/* ============================================================
   Wild Banana Portal - Design v3
   ------------------------------------------------------------
   Diese Datei enthält die GESAMTE optische Überarbeitung.
   Sie wird als letzte Schicht geladen und gewinnt daher bei
   gleicher Spezifität über die Inline-Styles in index.html.

   Rückbau: die eine <link>-Zeile in index.html entfernen.
   Es wird keine Funktion, keine Logik und keine Struktur berührt.

   Sieben Regeln (siehe Vault, Kundenportal-Intern/Status):
   1. Typo-Skala mit sechs Stufen, Fließtext 15px
   2. Nur drei Schriftgewichte: 400 / 500 / 700
   3. Versalien rationiert
   4. Drei Eckenradien: 4 / 10 / 16
   5. Abstände auf 4er-Raster
   6. Tiefe durch Material statt Gold-Leuchten
   7. Gold nur auf Primäraktion, Aktivzustand, Fokus
   ============================================================ */

:root {
    /* --- Flächen-Ebenen ------------------------------------
       Vier Stufen statt zwei. Der Helligkeitsunterschied ist
       bewusst klein: Tiefe entsteht durch Schatten und Lichtkante,
       die Fläche liefert nur den Untergrund dafür. */
    --s-0: #08090c;   /* Seitenhintergrund */
    --s-1: #101218;   /* Karte in der Fläche */
    --s-2: #171a22;   /* erhöht: Kartenkopf, Eingabefeld, Zeile */
    --s-3: #1e2129;   /* schwebend: Dialog, Aufklappmenü */

    /* --- Linien --------------------------------------------- */
    --line-1: #1e2129;   /* ruhende Kante */
    --line-2: #2b303b;   /* betonte Kante, Hover */

    /* --- Text ------------------------------------------------ */
    --t-1: #ffffff;   /* Titel, Werte */
    --t-2: #aeb4c0;   /* Fließtext */
    --t-3: #7d8492;   /* Beiläufiges, Metadaten */

    /* --- Radien: drei Werte, keine sechzehn ------------------ */
    --r-s: 4px;    /* Badges, Tags, kleine Eingaben */
    --r-m: 10px;   /* Buttons, Listenzeilen, Felder */
    --r-l: 16px;   /* Karten, Dialoge */

    /* --- Abstände: 4er-Raster ------------------------------- */
    --sp-1: 4px;
    --sp-2: 8px;
    --sp-3: 12px;
    --sp-4: 16px;
    --sp-5: 24px;
    --sp-6: 32px;
    --sp-7: 48px;

    /* --- Typo-Skala: sechs Stufen mit festen Rollen ---------- */
    --fs-1: 32px;   /* Seitentitel, Begrüßung */
    --fs-2: 24px;   /* Dialogtitel, große Zahl */
    --fs-3: 18px;   /* Kartenüberschrift */
    --fs-4: 15px;   /* Fließtext, Buttons, Eingaben */
    --fs-5: 13px;   /* Beiläufiges, Metadaten */
    --fs-6: 11px;   /* Label in Versalien, sparsam */

    /* --- Tiefe: zweischichtig, im Hintergrundton eingefärbt --
       Schicht 1 = Kontaktschatten (kurz, hart, "berührt den Grund")
       Schicht 2 = Streuschatten (weit, weich, "wirft Licht ab")
       Nicht reines Schwarz: das frisst die Farbe der Fläche auf. */
    --e-1: 0 1px 2px rgba(3,4,7,.6),   0 4px 12px -3px rgba(3,4,7,.58);
    --e-2: 0 2px 5px rgba(3,4,7,.72),  0 18px 40px -12px rgba(3,4,7,.8);
    --e-3: 0 8px 16px rgba(3,4,7,.78), 0 42px 84px -22px rgba(3,4,7,.9);

    /* --- Lichtkante: der eigentliche Materialtrick ------------
       Eine 1px helle Linie an der Oberkante liest das Auge als
       Lichtreflex. Deshalb wirkt die Karte körperlich, nicht flach. */
    --lip: inset 0 1px 0 rgba(255,255,255,.055);
    --lip-strong: inset 0 1px 0 rgba(255,255,255,.09);

    /* Gold bleibt, wird aber nur noch als Farbe verwendet,
       nie als Leuchten. */
    --gold: #ffe600;
    --online: #3fbf7f;   /* Online-Ampel: die einzige Zustandsfarbe neben Gold (Design-Labor 16) */
}

/* ============================================================
   1. GRUNDLAGE: Schrift, Fließtextgröße, Fläche
   ============================================================ */

body {
    background: var(--s-0);
    color: var(--t-2);
    font-size: var(--fs-4);
    line-height: 1.55;
    font-weight: 400;
}

/* Gewichte auf drei reduzieren. 800 und 900 wirken auf dunklem
   Grund ohnehin verwaschen, weil die Schrift optisch aufblüht. */
[style*="font-weight: 800"], [style*="font-weight:800"],
[style*="font-weight: 900"], [style*="font-weight:900"] {
    font-weight: 700;
}

/* ============================================================
   2. TIEFE STATT LEUCHTEN
   Alle Gold-Glows werden hier durch Material ersetzt.
   Reihenfolge entspricht dem Aufbau von index.html.
   ============================================================ */

/* --- Karten -------------------------------------------------
   Die Juli-Schicht in index.html setzt hier !important auf
   background, border und box-shadow. Deshalb steht es auch hier.
   Ihr Schatten war 0 18px 40px -30px: der negative Spread von
   -30px hat die Tiefe auf nahezu null zusammengezogen, sie war
   also codiert, aber nie sichtbar. Genau das wird hier behoben. */
.auth-container,
.banana-card,
.admin-card,
.stammdaten-card,
.client-stammdaten-section,
#admin-sidebar,
#admin-empty-state {
    background: linear-gradient(180deg, var(--s-2) 0%, var(--s-1) 62%) !important;
    border: 1px solid var(--line-1) !important;
    border-radius: var(--r-l);
    box-shadow: var(--lip), var(--e-2) !important;
}

.banana-card::before { display: none; }

.banana-card:hover,
.admin-card:hover {
    border-color: var(--line-2) !important;
    box-shadow: var(--lip-strong), var(--e-2) !important;
    transform: none;
}

.admin-client-item:hover,
.client-asset-item:hover {
    border-color: var(--line-2) !important;
    background: var(--s-3) !important;
    box-shadow: var(--lip-strong) !important;
}

.banana-card-header,
.admin-card-header {
    background: transparent !important;
    border-bottom: 1px solid var(--line-1) !important;
    padding: var(--sp-4) var(--sp-5);
    gap: var(--sp-3);
}

.banana-card:hover .banana-card-header { background: transparent; }

/* Die Kartentitel stehen im Markup als <h3>, nicht als <span>.
   Die urspruengliche Regel griff deshalb ins Leere und liess die
   Ueberschriften in 12px-Space-Mono-Versalien stehen - also genau
   in dem Etikettenstil, den Regel 3 abschaffen soll. Betrifft
   Kundenbereich und Admin-Cockpit gleichermassen. */
.banana-card-header span:not(.material-icons),
.admin-card-header span:not(.material-icons),
.banana-card-header h3,
.admin-card-header h3,
.client-stammdaten-section h3 {
    font-family: var(--font-body);
    font-size: var(--fs-3);
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--t-1);
}

.banana-card-body,
.admin-card-body { padding: var(--sp-5); }

/* --- Dialoge ------------------------------------------------ */
.modal-card,
.delete-modal-card,
.help-guide-card {
    background: var(--s-3) !important;
    border: 1px solid var(--line-2) !important;
    border-radius: var(--r-l);
    box-shadow: var(--lip-strong), var(--e-3) !important;
}

.modal-title, .delete-modal-title {
    font-size: var(--fs-2);
    font-weight: 500;
    letter-spacing: -0.01em;
}

/* --- Primärbutton: Gold als Fläche, nicht als Schein ------ */
.btn-primary,
.btn-client-upload,
.btn-stammdaten-save,
.btn-admin-save,
.modal-btn:not(.modal-btn-cancel) {
    background: var(--gold);
    color: #08090c;
    border: none;
    border-radius: var(--r-m);
    font-family: var(--font-body);
    font-size: var(--fs-4);
    font-weight: 700;
    letter-spacing: 0;
    text-transform: none;
    padding: var(--sp-3) var(--sp-5);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.35), var(--e-1);
    transition: transform .12s var(--ease), box-shadow .2s var(--ease), filter .2s var(--ease);
}

.btn-primary:hover,
.btn-client-upload:hover,
.btn-stammdaten-save:hover,
.btn-admin-save:hover,
.cockpit-savebar .btn-admin-save:hover,
.modal-btn:not(.modal-btn-cancel):hover {
    transform: translateY(-1px);
    filter: brightness(1.06);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.45), var(--e-2);
}

.btn-primary:active,
.btn-client-upload:active,
.btn-stammdaten-save:active,
.btn-admin-save:active {
    transform: translateY(0) scale(.985);
    box-shadow: inset 0 1px 2px rgba(0,0,0,.25);
}

/* Deaktiviert: neutral, nicht olivgrün */
.btn-primary:disabled,
.btn-client-upload:disabled,
.btn-stammdaten-save:disabled,
.btn-admin-save:disabled {
    background: #23262f;
    color: #5d6371;
    box-shadow: none;
    transform: none;
    filter: none;
    cursor: not-allowed;
}

/* --- Sekundärbutton ---------------------------------------- */
.btn-google,
/* --- Passwort-Modal: Felder statt Meldung -------------------- */
/* Die Modal-Karte zentriert ihren Inhalt. Beschriftete Eingabefelder
   brauchen linksbuendigen Text, sonst steht das Label mittig ueber
   dem Feld und liest sich wie eine Ueberschrift. Abstaende auf dem
   4er-Raster (16px). Kein neues Gold, kein neuer Radius - die Felder
   erben die vorhandene .form-group-Regel. */
.password-modal-form {
    width: 100%;
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    margin: var(--sp-4) 0;
}

.modal-btn-cancel,
.btn-header,
.admin-edit-toggle,
.btn-status-edit {
    background: var(--s-2);
    border: 1px solid var(--line-2);
    border-radius: var(--r-m);
    color: var(--t-1);
    font-size: var(--fs-4);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    box-shadow: var(--lip), var(--e-1);
}

.btn-google:hover,
.modal-btn-cancel:hover,
.btn-header:hover,
.admin-edit-toggle:hover {
    background: var(--s-3);
    border-color: #3a3f4d;
    box-shadow: var(--lip-strong), var(--e-1);
}

/* --- Eingabefelder: eingelassen statt aufgesetzt ------------- */
.form-group input,
.client-stammdaten-section input,
.client-stammdaten-section select,
.client-form-field input[type="text"],
.client-form-field select,
.client-form-field textarea,
.admin-field input,
.admin-field select,
.admin-field textarea,
#admin-search-input,
.delete-modal-input,
.livechat-feld,
.nachricht-antwort-feld {
    background: #0d0f14;
    border: 1px solid var(--line-1);
    border-radius: var(--r-m);
    color: var(--t-1);
    font-family: var(--font-body);
    font-size: var(--fs-4);
    padding: var(--sp-3) var(--sp-4);
    box-shadow: inset 0 1px 3px rgba(0,0,0,.45);
    transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}

.form-group input:hover,
.client-stammdaten-section input:hover,
.client-stammdaten-section select:hover,
.admin-field input:hover {
    border-color: var(--line-2);
}

/* Fokus: Gold als Ring, kein Streulicht. Der Ring ist die
   Ausnahme, in der Gold leuchten darf, weil er Zustand anzeigt. */
.form-group input:focus,
.client-stammdaten-section input:focus,
.client-stammdaten-section select:focus,
.client-form-field input[type="text"]:focus,
.client-form-field select:focus,
.client-form-field textarea:focus,
.admin-field input:focus,
.admin-field select:focus,
.admin-field textarea:focus,
#admin-search-input:focus,
.delete-modal-input:focus,
.livechat-feld:focus,
.nachricht-antwort-feld:focus {
    outline: none;
    border-color: var(--gold);
    box-shadow: inset 0 1px 3px rgba(0,0,0,.45), 0 0 0 3px rgba(255,230,0,.16);
}

/* --- Upload-Fläche ----------------------------------------- */
.banana-upload-zone,
.admin-upload-zone {
    background: #0d0f14;
    border: 1px dashed var(--line-2);
    border-radius: var(--r-m);
    padding: var(--sp-4) var(--sp-5);
    box-shadow: inset 0 1px 3px rgba(0,0,0,.4);
    transition: border-color .18s var(--ease), background .18s var(--ease);
}

.banana-upload-zone:hover,
.admin-upload-zone:hover {
    border-color: var(--gold);
    border-style: solid;
    background: #101319;
    box-shadow: inset 0 1px 3px rgba(0,0,0,.4);
}

/* --- Listenzeilen: Datei, Kunde ----------------------------- */
.client-asset-item,
.admin-client-item {
    background: var(--s-2);
    border: 1px solid var(--line-1);
    border-radius: var(--r-m);
    box-shadow: var(--lip);
    padding: var(--sp-3) var(--sp-4);
    transition: border-color .18s var(--ease), background .18s var(--ease);
}

.client-asset-item:hover,
.admin-client-item:hover {
    border-color: var(--line-2);
    background: var(--s-3);
    box-shadow: var(--lip-strong);
}

/* --- Update-Karte ------------------------------------------- */
.timeline-ticket {
    background: var(--s-2);
    border: 1px solid var(--line-1);
    border-radius: var(--r-m);
    box-shadow: var(--lip);
}

.unread-dot {
    background: var(--gold);
    box-shadow: none;
}

/* Kopfzeile des Tickets. Stand bis 2026-08-05 komplett als Inline-Stil
   in app.js und war damit fuer diese Schicht unerreichbar: 11px und
   10px Schrift (10 ausserhalb der Skala), 3px Radius (ausserhalb der
   drei), zwei fest verdrahtete Graustufen. */
.timeline-ticket-kopf {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--sp-2);
    margin-bottom: var(--sp-1);
}

.timeline-ticket-datum {
    font-size: var(--fs-5);
    font-weight: 400;
    color: var(--t-3);
}

/* Das Etikett meldet einen Zustand, kein Schmuck - Gold ist hier nach
   Regel 7 erlaubt.

   Kein text-transform: die Sammelregel "#client-dashboard *" hat 1-0-0
   und schlaegt eine reine Klasse (0-1-0) - ein uppercase hier waere tote
   Deklaration, die eine Absicht vortaeuscht, die nie wirkt (Fallstrick 11).
   Gemessen: das Etikett rendert als "Update". Das ist auch die richtige
   Entscheidung - Gold und Rahmen markieren den Zustand bereits, ein
   dritter Verstaerker widerspraeche Regel 3. Wer es doch in Versalien
   will, braucht "#client-dashboard .timeline-ticket-etikett". */
.timeline-ticket-etikett {
    font-size: var(--fs-6);
    font-weight: 700;
    color: var(--gold);
    border: 1px solid rgba(255,230,0,.3);
    border-radius: var(--r-s);
    padding: var(--sp-1) var(--sp-2);
    white-space: nowrap;
}

/* Der Meldungstext ist Fliesstext, also 15px nach Regel 1 - vorher 13px. */
.timeline-ticket-text {
    font-size: var(--fs-4);
    color: var(--t-2);
    line-height: 1.5;
    white-space: pre-wrap;
}

/* --- Datei-Warteschlange im Upload-Formular ------------------
   Stand bis 2026-08-05 vollstaendig als style.cssText in app.js, die
   Hover-Zustaende als onmouseenter/onmouseleave. Beides gehoert hierher:
   Inline schlaegt jede Klassenregel, und ein Hover ist Darstellung,
   keine Logik. */
.warteschlange-zeile {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
    padding: var(--sp-1) var(--sp-2);
    background: var(--s-2);
    border: 1px solid var(--line-1);
    border-radius: var(--r-m);
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
}

.warteschlange-name {
    font-size: var(--fs-5);
    color: var(--t-2);
    flex-shrink: 1;
    min-width: 50px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.warteschlange-name .material-icons {
    font-size: 14px;
    margin-right: var(--sp-1);
    vertical-align: -2px;
}

/* Kein text-transform: der Wert kommt bereits gross aus den Daten
   (item.category), und die Sammelregel "#client-dashboard *" wuerde
   eine reine Klasse ohnehin schlagen (Fallstrick 11).

   min-width statt fester width (korrigiert 2026-08-05, nach Ruecksprache
   mit Marco): der Klick-Umschalter in app.js wechselt nur zwischen
   BILDMATERIAL (110.67px) und LOGO/GRAFIKEN (122.64px) - 124px deckt
   genau diese beiden ohne Sprung ab. detectCategory() vergibt aber
   auch BRIEFING/DOKUMENT (150.28px, Fallback fuer jede Nicht-Bild-
   Nicht-Video-Datei) und VIDEOMATERIAL (120.4px) - beide werden nie
   angeklickt, springen also nie, und duerfen deshalb schmaler oder
   breiter als 124px sein. Eine feste width:156px (erste Fassung) war
   fuer den haeufigen Bildmaterial-Fall unnoetig breit und quetschte
   das Kurzbeschreibungs-Feld auf 18px zusammen - siehe Fallstrick 26. */
.warteschlange-kategorie {
    padding: var(--sp-1);
    background: var(--s-1);
    border: 1px solid var(--line-1);
    border-radius: var(--r-s);
    color: var(--gold);
    font-size: var(--fs-5);
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    text-align: center;
    box-sizing: border-box;
    min-width: 135px;
    flex-shrink: 0;
    user-select: none;
    transition: border-color .15s var(--ease);
}

.warteschlange-kategorie:hover { border-color: rgba(255,230,0,.4); }

.warteschlange-beschreibung {
    padding: var(--sp-1);
    font-size: var(--fs-5);
    background: var(--s-1);
    color: var(--t-1);
    border: 1px solid var(--line-1);
    border-radius: var(--r-s);
    flex-grow: 1;
    min-width: 60px;
    outline: none;
    transition: border-color .15s var(--ease);
}

.warteschlange-beschreibung:focus { border-color: rgba(255,230,0,.5); }

.warteschlange-loeschen {
    color: var(--t-3);
    cursor: pointer;
    padding: var(--sp-1);
    flex-shrink: 0;
    opacity: .85;
    line-height: 1;
    transition: transform .15s var(--ease), opacity .15s var(--ease);
}

.warteschlange-loeschen .material-icons { font-size: 15px; }

/* --- Fortsetzbarkeit nach Abbruch (Baustein 8, 2026-09-06) ---
   ⚠️ Der Balken liegt ABSOLUT auf der Unterkante der Zeile, nicht im
   Fluss. Eine Zeile ist exakt 40px hoch (gemessen, siehe unten); ein
   Balken im Fluss machte sie 43px hoch, und die ganze Liste - samt
   beider Karten im Grid - spraenge beim ersten Fortschritt.
   Deshalb braucht die Zeile hier position:relative. */
/* ⚠️ min-height: Ohne sie ist eine erledigte Zeile 30px hoch und eine offene
   40px (gemessen 2026-09-06) - die Hoehe kam bisher allein vom
   Beschreibungsfeld, und das faellt bei "schon da" weg. In einer
   wiederhergestellten Warteschlange stuenden erledigte und offene Zeilen dann
   unterschiedlich hoch untereinander, und die Liste ruckte bei jeder fertig
   werdenden Datei um 10px. 40px ist der gemessene Bestand, kein neuer Wert. */
.warteschlange-zeile { position: relative; min-height: 40px; }

.warteschlange-balken-spur {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 2px;
    border-radius: 0 0 var(--r-m) var(--r-m);
    overflow: hidden;
    pointer-events: none;
}

.warteschlange-balken {
    height: 100%;
    width: 0;
    background: var(--gold);
    transition: width .12s var(--ease);
}

/* Erledigt: die Zeile bleibt lesbar, tritt aber zurueck - sie ist
   Beleg, keine Aufgabe mehr. Gold waere hier falsch (Regel 7: Gold
   nur auf Primaeraktion, Aktivzustand, Fokus). */
.warteschlange-zeile--erledigt { opacity: .62; }

/* Kein Balken unter einer erledigten Zeile. Er stuende dort voll auf Gold und
   zoege genau den Blick auf sich, den die Zeile nicht mehr braucht - bei fuenf
   fertigen Dateien fuenf goldene Striche gegen Regel 7. Das Haekchen und
   "schon da" sagen dasselbe leiser. */
.warteschlange-zeile--erledigt .warteschlange-balken-spur { display: none; }

.warteschlange-fertig {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
    margin-left: auto;
    flex-shrink: 0;
    font-size: var(--fs-5);
    color: var(--t-3);
}

.warteschlange-fertig .material-icons { font-size: 15px; }

/* --- Banner: unterbrochener Upload --------------------------
   Steht ueber der Ablegeflaeche, weil es die Frage stellt, die vor
   jeder neuen Auswahl zu klaeren ist. */
.upload-fortsetzung {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    padding: var(--sp-3);
    margin-bottom: var(--sp-3);
    background: var(--s-2);
    border: 1px solid var(--line-2);
    border-radius: var(--r-m);
    box-shadow: var(--lip);
}

.upload-fortsetzung-kopf {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-5);
    color: var(--t-2);
    min-width: 0;
}

.upload-fortsetzung-kopf .material-icons {
    font-size: 18px;
    color: var(--gold);
    flex-shrink: 0;
}

.upload-fortsetzung-knoepfe {
    display: flex;
    gap: var(--sp-2);
    flex-shrink: 0;
}

.upload-fortsetzung-ja,
.upload-fortsetzung-nein {
    padding: var(--sp-1) var(--sp-3);
    border-radius: var(--r-s);
    font-size: var(--fs-5);
    font-weight: 500;
    cursor: pointer;
    transition: background .15s var(--ease), border-color .15s var(--ease);
}

/* Fortsetzen ist die Primaeraktion - deshalb traegt genau dieser
   Knopf das Gold, und der andere keins. */
.upload-fortsetzung-ja {
    background: var(--gold);
    border: 1px solid var(--gold);
    color: #0d0f14;
}

.upload-fortsetzung-ja:hover { background: #ffef4d; }

.upload-fortsetzung-nein {
    background: transparent;
    border: 1px solid var(--line-2);
    color: var(--t-3);
}

.upload-fortsetzung-nein:hover { border-color: var(--t-3); color: var(--t-2); }

.upload-fortsetzung-ja:disabled,
.upload-fortsetzung-nein:disabled { opacity: .5; cursor: not-allowed; }

/* Die Liste wuchs bis 165px (index.html, !important) mit jeder neuen
   Datei mit - und zog beide Karten im Grid gleich mit hoch, weil sie
   auf align-items:stretch stehen (Fallstrick 26b/27). Eine Zeile ist
   exakt 40px hoch, kein Abstand dazwischen (live gemessen).

   Zweiter Anlauf (:not(:empty), feste 120px erst AB der ersten Datei)
   loeste den Sprung zwischen 1/2/3 Dateien, aber nicht den Sprung
   zwischen 0 und 1 Datei - der blieb, weil die Box bei 0 Dateien immer
   noch unsichtbar war. Marco: "wäre es nicht sinnvoll gleich die beiden
   boxen größer zu machen, dass sie von anfang an die größe haben wie
   nach dem hochladen der 1. datei". Dritter Anlauf, jetzt ohne
   :not(:empty): die 120px sind immer da, auch mit null Dateien - die
   Flaeche haelt von der ersten Sekunde an die Groesse, die sie mit
   Inhalt hat, nichts bewegt sich mehr beim ersten Upload. Der
   Kopf-Chevron (#btn-toggle-warteschlange in app.js) zeigt aus
   demselben Grund jetzt ebenfalls dauerhaft "0 Dateien" statt sich
   erst ab der ersten Datei einzublenden. #id + !important, weil das
   Original (jetzt nur noch overflow-x im Inline-Rest) ebenso gesetzt
   war; die :empty-Randregel aus dem Markup wird hier bewusst
   ueberschrieben.

   Vierter Anlauf (2026-08-05): Marco meldete am fertigen Stand "sehr
   viel Luft in den Boxen" - richtig, denn bei null Dateien standen
   hier 120px reservierte Leere, und weil die rechte Karte damit die
   hoehere von beiden war, schob align-items:stretch das Gegenstueck
   links per margin-top:auto ein 134px-Loch zwischen Downloads und
   Neuigkeiten (gemessen: rechte Karte 605px natuerlicher Bedarf,
   linke nur 471px). Reservierung deshalb von drei auf zwei Zeilen:
   80px. Der Sprung-Schutz bleibt vollstaendig erhalten - die Hoehe
   ist weiterhin fest und ab der ersten Sekunde da, nur eben kleiner.
   Ab der dritten Datei wird intern gescrollt; die Gesamtzahl steht
   ohnehin im Kopf-Chevron. Eine Zeile waere noch kompakter gewesen,
   ist aber verworfen: dann verdeckt schon die zweite Datei die erste. */
#client-files-inline-list {
    height: 80px !important;
    overflow-y: auto !important;
    scrollbar-gutter: stable !important;
    margin: 0 !important;
    /* Firefox-Pfad (2026-08-06): die drei Zeilen darunter sind reines
       -webkit-*, wirken also nur in Chrome/Edge. In Firefox blieb der
       Balken deshalb grau/System-Standard, egal was dort steht - Marco
       sah kein Gold. scrollbar-color ist das Firefox-Gegenstueck:
       erster Wert Griff, zweiter Wert Spur. Einziger Ort im Projekt,
       der das setzt - bewusst nicht global, siehe Kommentar unten. */
    /* Marco (2026-08-06, gleicher Tag): "auto" zeigt in Firefox zusaetzlich
       Pfeil-Buttons oben/unten - wirkt klobiger als die Chromium-Fassung
       daneben. "thin" ist Firefoxs eigene duennere Stufe, keine Pfeile. */
    scrollbar-color: var(--gold) rgba(255,255,255,.05);
    scrollbar-width: thin;
}
/* Marco (2026-08-06): der 6px-Balken war gegen den dunklen Hintergrund
   kaum zu erkennen - nur der duenne Griff selbst war sichtbar, keine
   Rille drumherum, die "hier geht es weiter" signalisiert. Jetzt: auf
   die globale Standardbreite (8px) angehoben, dazu eine sichtbare
   Spur, damit der ganze scrollbare Bereich als solcher erkennbar ist,
   nicht nur der Griff bei Bewegung. Chromium-Pfad; das Firefox-
   Gegenstueck (scrollbar-color) steht oben an der Basisregel. */
#client-files-inline-list::-webkit-scrollbar { width: 6px; }
#client-files-inline-list::-webkit-scrollbar-track { background: rgba(255,255,255,.05); border-radius: 99px; }
#client-files-inline-list::-webkit-scrollbar-thumb { background: var(--gold) !important; border-radius: 99px; }

/* --- Warteschlange: Kopfzeile --------------------------------
   Fund aus Marcos Aufzeichnung 224112: mehrere Dateien in der Liste
   zeigten nur die oberste Zeile plus einen 6px-Scrollbalken als
   einzigen Hinweis auf den Rest - leicht zu uebersehen. Die Zeile
   nennt deshalb dauerhaft Anzahl und Gesamtgroesse, auch bei null
   Dateien.

   Der Chevron zum Zusammenklappen ist am 2026-08-05 wieder entfallen
   (Marcos Aufzeichnung 234941, Fallstrick 35): Zuklappen gab genau
   die 80px frei, die der feste Deckel an der Liste reserviert -
   beide Karten und der gesamte Seiteninhalt darunter sprangen bei
   jedem Klick um 80px. Klappe und Sprung-Schutz arbeiteten
   gegeneinander. Seit die Liste eine feste Hoehe hat, loest der
   Deckel das urspruengliche Wachstumsproblem allein; die Klappe
   hatte keine Aufgabe mehr, nur noch ihre Nebenwirkung. Geblieben
   ist die Zusammenfassung als reine Textzeile - kein Knopf, kein
   Hover, kein Zeiger. */
.warteschlange-kopf {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-1);
    color: var(--t-2);
    font-size: var(--fs-5);
    font-weight: 500;
}
.warteschlange-loeschen:hover { transform: scale(1.2); opacity: 1; }

/* --- Hinweisleisten -----------------------------------------
   Der Versionsriegel (Baustein 12) haengt bewusst an derselben
   Regel und bekommt KEINE eigene Farbe: Rot ist in diesem System
   fuer Loeschaktionen reserviert, Gold laut Regel 7 fuer
   Primaeraktion, Aktivzustand und Fokus. Eine Hinweisleiste ist
   nichts davon - sie ist eine Hinweisleiste, und davon gibt es
   hier bereits drei. Die Dringlichkeit traegt der Wortlaut, nicht
   eine vierte Farbe. */
.guide-banner,
.stammdaten-warn-banner,
.pending-change-banner {
    background: rgba(255,230,0,.05);
    border: 1px solid rgba(255,230,0,.22);
    border-radius: var(--r-m);
    padding: var(--sp-3) var(--sp-4);
    font-size: var(--fs-5);
    font-weight: 400;
    line-height: 1.5;
    box-shadow: none;
}

/* Der Versionsriegel borgt sich die Leiste des Stammdaten-Banners und
   erbt damit dessen goldene Textfarbe (index.html, .stammdaten-warn-banner).
   ⚠️ Beim Nachmessen im Labor gegen den Nachbarn .guide-banner aufgefallen:
   Der schreibt grau, dieser golden. Fuer einen Satz ist Gold vertretbar, fuer
   vier Zeilen Fliesstext nicht — Regel 7 haelt Gold der Primaeraktion, dem
   Aktivzustand und dem Fokus vor, und ein Absatz ist keins davon. Das Symbol
   behaelt den Akzent, der Text bekommt die Fliesstextfarbe.
   Gilt nur fuer diese eine Leiste; der Stammdaten-Banner bleibt, wie er ist. */
.versions-banner {
    color: var(--t-2);
    /* Text links, Knopf rechts - dieselbe Bauform wie beim Freigabe-Banner
       darunter. `flex-wrap` ist Pflicht, nicht Feinschliff: Der Sperrtext
       laeuft ueber mehrere Zeilen, und auf einem Handy braucht der Knopf
       eine eigene. */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    flex-wrap: wrap;
}
.versions-banner .vb-text {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-2);
    /* Ohne `min-width: 0` bricht ein langer Text den Knopf aus der Leiste
       statt selbst umzubrechen - Flex-Kinder schrumpfen nicht unter ihre
       Inhaltsbreite. */
    min-width: 0;
    flex: 1 1 320px;
}
/* ⚠️ Ausdruecklich NUR im Textteil. Die Regel hiess bis zur Sichtpruefung
   `.versions-banner .wb-icon` und fasste damit auch das Symbol IM KNOPF —
   gold auf goldem Grund, also unsichtbar. Es belegte weiter seine 16 px:
   Eine Breitenmessung haette „da ist was" gemeldet, ein Kontrast-Blick
   nichts gefunden. Gleiche Lehre wie bei den Icon-Codepoints (CLAUDE.md):
   Messen belegt, DASS da eine Glyphe ist, nie dass man sie sieht. */
.versions-banner .vb-text .wb-icon { color: var(--gold); }

/* Gold ist hier regelkonform: In dieser Leiste IST „Portal neu öffnen" die
   Primaeraktion, und genau dafuer haelt Regel 7 die Farbe vor. Der Knopf
   borgt sich `.btn-client-upload`; das ist ein <a>, kein <button>, deshalb
   die drei Zeilen unten. */
.versions-banner .vb-knopf {
    width: auto;
    flex-shrink: 0;
    text-decoration: none;
    padding: var(--sp-2) var(--sp-4);
    cursor: pointer;
}

/* Der Freigabe-Banner braucht zusaetzlich einen Knopf rechts neben
   dem Text (die anderen beiden Hinweisleisten sind reiner Text) -
   das erfordert eigenes Layout on top der geteilten Optik oben.
   #pending-change-banner existierte nur auf einem nie gemergten
   Branch (siehe Kommentar in index.html); Farbe/Radius/Abstand
   kommen bewusst aus derselben Sammelregel wie die Nachbarbanner,
   nicht aus eigenen Rohwerten. */
.pending-change-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin-bottom: var(--sp-5);
}
.pending-change-banner.hidden { display: none !important; }
.pending-change-banner .pcb-text {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-2);
}
.pending-change-banner .wb-icon {
    font-size: 18px;
    flex-shrink: 0;
    margin-top: 1px;
}

/* Der schwebende Hilfe-Knopf stand hier bis 2026-09-04 und ist
   ersatzlos entfernt. Ausfuehrliche Begruendung im Abschnitt
   weiter unten, wo bis heute die Handy-Ausblendung stand. */

/* --- Scrollbalken: neutral, nie gold ------------------------ */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: #2b303b;
    border-radius: 99px;
}
::-webkit-scrollbar-thumb:hover { background: #3a4051; }

.banana-assets-list::-webkit-scrollbar-thumb,
.activity-feed::-webkit-scrollbar-thumb,
.admin-assets-list::-webkit-scrollbar-thumb,
#client-files-inline-list::-webkit-scrollbar-thumb,
#admin-client-list:hover::-webkit-scrollbar-thumb {
    background: #2b303b;
}

/* ============================================================
   3. TYPO-DISZIPLIN
   ============================================================ */

/* Versalien-Label: bleiben, werden aber leiser. Space Mono
   wird wieder Akzent statt Grundausstattung. */
/* .update-badge stand hier bis 2026-08-14 mit drin. Es ist aber kein
   Versalien-Label, sondern ein leerer Punkt (app.js:2345 erzeugt ein
   <span> ohne Text) - Schriftregeln greifen daran ins Leere. Siehe
   Abschnitt 15 fuer die ganze Geschichte. */
.client-asset-tag,
.client-sub,
.admin-sublabel,
.admin-workspace-head-tag,
.status-badge,
.client-asset-status-label {
    font-family: var(--font-mono);
    font-size: var(--fs-6);
    font-weight: 400;
    letter-spacing: .06em;
    color: var(--t-3);
}

/* Status- und Kategorie-Badges: ruhige Pille.
   .update-badge gehoerte hier bis 2026-08-14 dazu und war damit der
   Grund fuer den "gelben Klecks" - das Padding sprengte den Punkt zum
   Rechteck. Es hat jetzt seine eigene Regel in Abschnitt 15. */
.client-asset-tag,
.status-badge {
    background: rgba(255,255,255,.05);
    border: 1px solid var(--line-2);
    border-radius: var(--r-s);
    padding: 3px var(--sp-2);
    box-shadow: none;
    color: var(--t-2);
}

/* Metadaten */
.client-asset-date,
.client-asset-time,
.client-asset-size-label,
.admin-field-hint,
.help-guide-footer-text,
.nachricht-beitrag-zeit {
    font-size: var(--fs-5);
    color: var(--t-3);
    font-weight: 400;
}

/* Fließtext in Karten */
.modal-text,
.delete-modal-text,
.help-guide-item p,
.banana-empty-state-text {
    font-size: var(--fs-4);
    line-height: 1.6;
    color: var(--t-2);
    font-weight: 400;
}

/* Dateiname: der eigentliche Inhalt, darf gross sein */
.client-asset-title-line,
.timeline-attached-file-name {
    font-size: var(--fs-4);
    font-weight: 500;
    color: var(--t-1);
    letter-spacing: 0;
    text-transform: none;
}

/* ============================================================
   4. LEERE ZUSTAENDE
   ============================================================ */

.banana-empty-state,
.client-assets-empty,
.client-list-empty {
    padding: var(--sp-6) var(--sp-5);
    color: var(--t-3);
}

.banana-empty-state .material-icons {
    color: var(--t-3);
    opacity: .5;
}

/* ============================================================
   5. FOKUS UND BEWEGUNG
   ============================================================ */

:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

::selection { background: rgba(255,230,0,.25); color: #fff; }

/* Bewegung nur als Rückmeldung. Wer Bewegung abbestellt hat,
   bekommt keine. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
}

/* ============================================================
   6. ICONS: Phosphor Light statt Material Icons
   ------------------------------------------------------------
   Material Icons ist der Standardsatz von Google und liest sich
   deshalb als Stangenware. Phosphor Light hat eine eigene
   Handschrift und steht auf der Positivliste des Design-Skills.

   Wichtig fuer den Umbau: die Elemente behalten ihre Klasse
   .material-icons und bekommen .wb-icon dazu. Dadurch greifen
   alle bestehenden Groessen-, Farb- und Animationsregeln
   weiter, insbesondere das leichte Kippen des Kartenkopf-Icons
   beim Hover. Nur die Schrift und das Glyph werden getauscht.

   Die Schrift liegt lokal unter fonts/, kein CDN-Aufruf (DSGVO).
   ============================================================ */

@font-face {
    font-family: 'Phosphor-Light';
    src: url('fonts/phosphor-light.woff2') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: block;
}

.wb-icon {
    font-family: 'Phosphor-Light' !important;
    font-weight: normal;
    font-style: normal;
    line-height: 1;
    display: inline-block;
    vertical-align: middle;
    text-transform: none;
    letter-spacing: normal;
    white-space: nowrap;
    direction: ltr;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Das Kippen des Kartenkopf-Icons beim Hover, aus index.html
   uebernommen, damit es den Icon-Wechsel ueberlebt. */
.banana-card-header .wb-icon,
.admin-card-header .wb-icon {
    transition: transform .25s var(--ease);
}

/* Kleine Zeilen-Icons (Vorschau/Download in Datei- und
   Änderungslisten, Kunden- und Adminbereich gleichermaßen) - 12
   Stellen in app.js trugen dafuer identisch
   style="font-size:14px;vertical-align:middle" inline. Das
   vertical-align steckt schon in .wb-icon; hier bleibt nur die
   Groesse (2026-08-06). */
.icon-inline-sm {
    font-size: 14px;
}
.banana-card:hover .banana-card-header .wb-icon,
.admin-card:hover .admin-card-header .wb-icon {
    transform: scale(1.15) rotate(5deg);
}

.wb-icon.ph-paperclip::before { content: "\e39a"; }   /* attachment */
.wb-icon.ph-arrows-clockwise::before { content: "\e094"; }   /* autorenew */
.wb-icon.ph-cloud-arrow-up::before { content: "\e1ae"; }   /* backup */
.wb-icon.ph-identification-badge::before { content: "\e6f6"; }   /* badge */
.wb-icon.ph-check-circle::before { content: "\e184"; }   /* check_circle */
/* arrow-counter-clockwise-light, in der Ligaturtabelle von
   phosphor-light.woff2 nachgeschlagen UND die Glyphe angesehen
   (2026-09-06): Pfeil gegen den Uhrzeigersinn. Fortsetzen. */
.wb-icon.ph-arrow-counter-clockwise::before { content: "\e038"; }
.wb-icon.ph-x::before { content: "\e4f6"; }   /* close */
.wb-icon.ph-headset::before { content: "\e584"; }   /* contact_support */
.wb-icon.ph-trash::before { content: "\e4a6"; }   /* delete */
.wb-icon.ph-trash-simple::before { content: "\e4a8"; }   /* delete_forever */
.wb-icon.ph-download-simple::before { content: "\e20c"; }   /* download */
.wb-icon.ph-pencil-simple::before { content: "\e3b4"; }   /* edit */
.wb-icon.ph-note-pencil::before { content: "\e34c"; }   /* edit_note */
.wb-icon.ph-warning-circle::before { content: "\e4e2"; }   /* error */
.wb-icon.ph-caret-down::before { content: "\e136"; }   /* expand_more */
.wb-icon.ph-users-three::before { content: "\e68e"; }   /* groups */
.wb-icon.ph-question::before { content: "\e3e8"; }   /* help_outline */
.wb-icon.ph-clock-counter-clockwise::before { content: "\e1a0"; }   /* history */
.wb-icon.ph-hourglass::before { content: "\e2b2"; }   /* hourglass_empty */
.wb-icon.ph-tray::before { content: "\e4aa"; }   /* inbox */
.wb-icon.ph-info::before { content: "\e2ce"; }   /* info */
.wb-icon.ph-lock-simple::before { content: "\e308"; }   /* lock */
.wb-icon.ph-sign-out::before { content: "\e42a"; }   /* sign-out-light, Ligaturtabelle 2026-09-16 */
.wb-icon.ph-dots-three-vertical::before { content: "\e208"; }   /* more_vert */
.wb-icon.ph-bell::before { content: "\e0ce"; }   /* notifications */
.wb-icon.ph-bell-ringing::before { content: "\e5e8"; }   /* notifications_active */
.wb-icon.ph-bell-slash::before { content: "\e0d4"; }   /* bell-slash-light, Ligaturtabelle 2026-09-16 */
.wb-icon.ph-arrow-square-out::before { content: "\e5de"; }   /* open_in_new */
.wb-icon.ph-user-plus::before { content: "\e4d0"; }   /* person_add */
.wb-icon.ph-user-focus::before { content: "\e6fc"; }   /* person_search */
.wb-icon.ph-gift::before { content: "\e276"; }   /* redeem */
.wb-icon.ph-floppy-disk::before { content: "\e248"; }   /* save */
.wb-icon.ph-magnifying-glass::before { content: "\e30c"; }   /* search */
.wb-icon.ph-paper-plane-tilt::before { content: "\e398"; }   /* send */
.wb-icon.ph-chat-circle::before { content: "\e168"; }   /* Livechat */
.wb-icon.ph-caret-left::before { content: "\e138"; }   /* zurueck */
.wb-icon.ph-gear::before { content: "\e270"; }   /* settings */
.wb-icon.ph-sliders-horizontal::before { content: "\e434"; }   /* tune */
.wb-icon.ph-upload-simple::before { content: "\e4c0"; }   /* upload */
.wb-icon.ph-eye::before { content: "\e220"; }   /* visibility */
.wb-icon.ph-squares-four::before { content: "\e464"; }   /* apps */
.wb-icon.ph-film-strip::before { content: "\e792"; }   /* movie */
.wb-icon.ph-image::before { content: "\e2ca"; }   /* image */
.wb-icon.ph-tag::before { content: "\e478"; }   /* sell */
.wb-icon.ph-file-text::before { content: "\e23a"; }   /* description */
.wb-icon.ph-construction::before { content: "\ed46"; }   /* hard-hat-light */
/* Drei weitere Symbole, die im Design-Labor gebraucht werden und
   sich fuer das Portal anbieten (Archiv, Datei-Upload, offener Ordner). */
.wb-icon.ph-archive-box::before   { content: "\e00e"; }
.wb-icon.ph-file-arrow-up::before { content: "\e61e"; }
.wb-icon.ph-folder-open::before   { content: "\e256"; }
/* Wartehinweis in der Admin-Upload-Zone. Bewusst NICHT gold: Gold ist
   nach Regel 7 fuer Primaeraktion, Aktivzustand und Fokus reserviert -
   ein "noch nicht fertig" ist keins davon. Der Dateiname darueber
   traegt bereits Gold; stuende der Hinweis auch darin, laesen sich
   beide als eine Zeile. */
.admin-upload-pending {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-5);
    color: var(--t-3);
}
.admin-upload-pending[hidden] { display: none; }

/* Systemstatus in der Kopfzeile: Herz mit Pulslinie.
   Aufgeloest ueber die Ligaturtabelle von phosphor-light.woff2 SELBST:
   "heartbeat-light" -> Glyphe uniE2AC -> U+E2AC. Danach gross gerendert
   und angesehen.

   Bis 2026-09-04 stand hier \eaa2, und dort liegt "panorama-light" - ein
   Bild im Rahmen. Der Codepoint kam aus der Ligaturtabelle von
   material-icons.woff2 ("monitor_heart" -> U+EAA2). Die beiden Schriften
   belegen ihre Codepoints aber UNABHAENGIG voneinander; ein Material-Name
   fuehrt in Phosphor systematisch zur falschen Glyphe. Die Pruefung von
   damals ("Glyphe mit echten Konturen, 715 Bildpunkte gegen 0") war
   sauber gemessen und trotzdem wertlos: Bildpunkte belegen, dass DA eine
   Glyphe ist, nie WELCHE.

   Fuer das naechste Icon: Namen in der Ligaturtabelle DIESER Schrift
   nachschlagen, dann die Glyphe gross rendern und ansehen. Ein Skript,
   das alle .wb-icon-Regeln auf einmal prueft, steht im Fehler-Index
   (Eintrag 2026-09-04). */
.wb-icon.ph-heartbeat::before     { content: "\e2ac"; }

/* ============================================================
   7. LOGIN ALS AUFTRITT
   ------------------------------------------------------------
   Bis hierher stand eine saubere Box mittig auf schwarzem Nichts.
   Sauber, aber austauschbar. Der Login ist die erste Flaeche, die
   ein Neukunde sieht, und die einzige, die klein und kontrolliert
   genug fuer eine Inszenierung ist. Dashboard und Admin bekommen
   bewusst das Gegenteil: Ruhe. Der Abfall vom Auftritt in die Ruhe
   ist das, was Wertigkeit erzeugt.

   Umschaltung der Sektionen laeuft ausschliesslich ueber die Klasse
   .active, nie ueber inline gesetztes display. Deshalb kann hier
   gefahrlos auf Raster umgestellt werden.
   ============================================================ */

/* Die Buehne waechst mit dem Bildschirm. Eine feste Breite von
   1080px liess auf 1920px je 420px tote Flaeche links und rechts
   stehen, die Komposition wirkte als Insel. Jetzt skaliert der
   Abstand mit der Fensterbreite, die Karte bleibt bei 420px, weil
   ein Formular ab einer gewissen Breite nicht besser wird. */
/* Die beiden Spalten umschliessen ihren Inhalt und stehen als Paar
   in der Mitte. Vorher war die linke Spalte 1fr: sie dehnte sich ueber
   den ganzen Rest und drueckte Wortmarke und Karte an die
   gegenueberliegenden Bildschirmraender. Bei 1920px lagen dadurch rund
   600px Leere dazwischen, das Auge musste hin und her springen. */
#auth-section.active {
    display: grid;
    grid-template-columns: auto 420px;
    justify-content: center;
    align-items: center;
    gap: clamp(40px, 4.5vw, 96px);
    width: 100%;
    max-width: 1400px;
    margin: 0 auto;
    padding-inline: clamp(24px, 4vw, 64px);
    box-sizing: border-box;
}

/* Lichtquelle hinter der ganzen Buehne. Fest positioniert und
   klickdurchlaessig, damit sie nichts blockiert und beim Scrollen
   nicht mitrechnet. */
#auth-section.active::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(680px 520px at 32% 42%, rgba(255,230,0,.055), transparent 68%),
        radial-gradient(520px 480px at 78% 62%, rgba(90,120,255,.045), transparent 70%);
}

.auth-brand {
    align-self: center;
    animation: brandRise .7s var(--ease) both;
}

.auth-brand-mark {
    font-family: var(--font-display);
    font-size: clamp(48px, 6.4vw, 108px);
    font-weight: 600;
    line-height: .92;
    letter-spacing: -.03em;
    color: var(--t-1);
    margin-bottom: var(--sp-5);
}

.auth-brand-claim {
    font-family: var(--font-body);
    font-size: 17px;
    font-weight: 400;
    line-height: 1.6;
    color: var(--t-2);
    max-width: 34ch;
    margin: 0;
}

@keyframes brandRise {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Eine Wortmarke pro Seite. Traegt die Markenspalte sie, ist das
   Logo in der Karte eine Dopplung und geht weg. Unter 900px kehrt
   es zurueck, weil dort die Markenspalte fehlt. */
@media (min-width: 900px) {
    .auth-container .auth-logo { display: none; }
    .auth-container .auth-subtitle {
        font-family: var(--font-body);
        font-size: var(--fs-3);
        font-weight: 500;
        color: var(--t-1);
        text-transform: none;
        letter-spacing: 0;
        margin-bottom: var(--sp-5);
        text-align: left;
    }
}

/* Mobil und schmal: einspaltig, Markenspalte weg. */
@media (max-width: 899px) {
    #auth-section.active {
        display: flex;
        flex-direction: column;
        max-width: 460px;
    }
    .auth-brand { display: none; }
}

/* --- Feinschliff in der Karte ------------------------------- */

/* Regel 3: Versalien rationieren. Die Feldbeschriftungen standen in
   gesperrten Space-Mono-Versalien, obwohl im Markup normaler Text
   steht. Das ist reine Darstellung, also hier korrigierbar. */
.form-group label {
    font-family: var(--font-body) !important;
    font-size: var(--fs-5);
    font-weight: 500;
    text-transform: none !important;
    letter-spacing: 0 !important;
    color: var(--t-2);
}

/* Der Hinweis fuer Erstbesucher war das Lauteste nach dem Logo.
   Ein Hinweis darf nicht mit der Wortmarke konkurrieren. */
.auth-container .guide-banner {
    background: transparent;
    border: none;
    border-left: 2px solid rgba(255,230,0,.35);
    border-radius: 0;
    padding: var(--sp-1) 0 var(--sp-1) var(--sp-3);
    text-align: left;
    font-size: var(--fs-5);
    color: var(--t-3);
}
.auth-container .guide-banner strong { color: var(--t-2); font-weight: 500; }

/* Drei gleich laute Links am Fuss hatten keine Rangfolge. */
.footer-links { gap: var(--sp-2); }
.footer-links .auth-link.highlight { font-size: var(--fs-5); color: var(--t-2); }
.footer-links .switch-link { font-size: var(--fs-5); color: var(--t-3); }
.footer-links .help-link {
    font-size: var(--fs-6);
    color: var(--t-3);
    text-decoration: none;
    opacity: .8;
}
.footer-links .help-link:hover { color: var(--t-2); opacity: 1; }

/* Gestaffelter Einflug der Karte, einen Tick nach der Markenspalte. */
.auth-container { animation: brandRise .7s var(--ease) .12s both; }

/* --- Schwebender Hilfe-Button, entfernt am 2026-09-04 -------
   Hier standen drei Regeln fuer den Schweber: Ausblenden auf der
   Anmeldeseite (dort war er ueberfluessig, die Karte hat bereits
   "Brauchst du Hilfe?" im Fuss, beide riefen openSupportModal),
   und zweimal Farbe, weil er das einzige Element der Seite mit
   goldenem Text war - Regel 7 reserviert Gold fuer Primaeraktion,
   Aktivzustand und Fokus, nicht fuer einen schwebenden Nebenweg.

   Beide Korrekturen sind mit dem Element hinfaellig geworden.
   Der Schluss war schon damals derselbe, nur eine Stufe zu
   frueh abgebrochen: Wenn ein Element auf der Anmeldeseite eine
   zweite Tuer in denselben Raum ist, ist es das im eingeloggten
   Portal ebenso - dort steht die erste Tuer im Fuss des
   Wegweisers. Begruendung unten im Abschnitt, wo die
   Handy-Ausblendung stand. */

/* ============================================================
   8. KUNDENBEREICH ALS RUHE
   ------------------------------------------------------------
   Das Gegenstueck zu Abschnitt 7. Der Login inszeniert, hier
   passiert das Gegenteil: nichts drae ngt sich vor, die Tiefe
   kommt aus Material, das Gold zieht sich auf Primaeraktion,
   Aktivzustand und Fokus zurueck.

   Gezaehlt auf der Flaeche, nicht geschaetzt:
   - Kopfzeile: Produktname 24px weiss vs. Kundenname 14px in
     Versalien - die Rangfolge stand auf dem Kopf
   - Begruessung 22px, Raster-Abstaende 20/22/30px: drei Werte,
     die es in der Skala gar nicht gibt
   - Leisten mit Radius 12px und 9px: zwei weitere der 16 Radien
   - Aufklapp-Kopf in Space-Mono-Versalien, Gewicht 700
   - Gold als Dekoration an fuenf Stellen: Leisten-Icons,
     Hover-Flaeche, Hover-Rand, Hover-Text, Leerzustands-Kreis
   ============================================================ */

/* --- 8.1 Kopfleiste: Rangfolge umdrehen ---------------------
   Der Kunde weiss, auf welchem Portal er ist - er hat sich
   gerade angemeldet. Was er wissen will, ist *sein* Bereich.
   Deshalb traegt jetzt der Kundenname die Kopfzeile und der
   Produktname tritt zurueck.
   Der Text "WILD BANANA AI - KUNDENPORTAL" steht als Versalie
   fest im Markup und bleibt in der Optikphase unangetastet
   (siehe Textliste im Vault). Nur die Lautstaerke aendert sich. */
/* Fallstrick 1: die Juli-Schicht setzt auf header und
   .header-center !important. Ohne Gegenstueck passiert hier
   sichtbar nichts. */
header {
    background: var(--s-1) !important;
    border-bottom: 1px solid var(--line-1) !important;
    padding: var(--sp-4) var(--sp-5);
    box-shadow: var(--lip);
    /* Spaltenabstand, ergaenzt 2026-09-16. Das Raster `1fr auto 1fr` hatte
       keinen: Im Master Dock ist die linke Gruppe breiter als ihre Spalte,
       und "Bauplatz oeffnen" stiess ohne Luft an "WILD BANANA AI -
       KUNDENPORTAL" (Marcos Screenshot, nachgemessen: 0 px). Symmetrisch,
       der Titel bleibt in der Kundenansicht mittig. */
    column-gap: var(--sp-5);
}

.header-left {
    font-size: var(--fs-3);
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--t-1);
    gap: var(--sp-2);
}

.header-center {
    font-size: var(--fs-5);
    font-weight: 400;
    letter-spacing: .04em;
    color: var(--t-3) !important;
    text-shadow: none !important;
}

.header-right { gap: var(--sp-3); }

/* Der Abmelden-Knopf hatte aus der Juli-Schicht padding:0 und
   aus Abschnitt 2 Flaeche, Rand und Radius dazubekommen. Das
   ergab eine farbige Box, die stramm am Text klebte. */
.header-right .btn-header {
    padding: var(--sp-2) var(--sp-3);
    border-bottom: 1px solid var(--line-2);
    color: var(--t-2);
}

.header-right .btn-header:hover {
    color: var(--t-1);
    transform: none;
    font-weight: 500;
}

#btn-logout:hover { color: #ff6b6b; }

.btn-help-circle {
    border-radius: var(--r-m);
    border: 1px solid var(--line-2);
    background: var(--s-2);
    color: var(--t-2);
    box-shadow: var(--lip);
    /* Der Statuslink traegt dieselbe Klasse, ist aber ein <a>:
       ohne das hier zeichnet der Browser einen Unterstrich unter
       das Icon und faerbt es in seiner Linkfarbe. */
    text-decoration: none;
}
.btn-help-circle:hover { background: var(--s-3); color: var(--t-1); }

/* --- 8.2 Begruessung und Raster ------------------------------
   Die Begruessung ist der Seitentitel und bekommt nach Regel 1
   die oberste Stufe. Das ist Hierarchie, nicht Auftritt: keine
   Animation, kein Leuchten, kein Verlauf - nur Groesse. */
.client-cockpit-head {
    margin: var(--sp-2) 0 var(--sp-5);
    gap: var(--sp-3);
}

.client-cockpit-head h2 {
    font-size: var(--fs-1);
    font-weight: 600;
    letter-spacing: -.02em;
    /* Clash Display hat ein schmales Leerzeichen; mit der negativen
       Laufweite darueber blieben bei 24px nur 3,23px uebrig (am Handy
       nachgemessen) - "Willkommen zurueck" las sich als ein Wort.
       0.12em bringt es auf 6,1px, also auf das uebliche Mass, ohne die
       enge Laufweite der Buchstaben anzutasten. */
    word-spacing: .12em;
    line-height: 1.15;
    color: var(--t-1);
}

.client-cockpit-grid {
    gap: var(--sp-5);
    margin-bottom: var(--sp-5);
}

.client-cockpit-foot {
    gap: var(--sp-3);
    margin-bottom: var(--sp-6);
}

/* --- 8.3 Die beiden Leisten ---------------------------------
   Verlauf und Benachrichtigung sind der Karte untergeordnet.
   Sie bekommen deshalb dieselbe Formsprache, aber die kleinere
   Tiefenstufe: sie liegen auf der Flaeche, sie schweben nicht. */
.client-collapse,
.client-notify-bar {
    background: var(--s-1);
    border: 1px solid var(--line-1);
    border-radius: var(--r-l);
    box-shadow: var(--lip), var(--e-1);
}

.client-collapse-head {
    font-family: var(--font-body);
    font-size: var(--fs-4);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-m);
    color: var(--t-1);
    gap: var(--sp-3);
}

.client-notify-row {
    font-size: var(--fs-4);
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-m);
    color: var(--t-2);
    gap: var(--sp-3);
}

.client-collapse-body { padding: var(--sp-1) var(--sp-4) var(--sp-4); }

/* Hover war goldene Flaeche plus goldener Rand plus goldene
   Schrift - drei Goldsignale fuer einen Nebenweg. Regel 7 laesst
   dafuer keinen Platz. Jetzt hebt sich die Zeile durch Material. */
.client-collapse-head:hover,
.client-notify-row:hover {
    background: var(--s-2);
    border-color: var(--line-2);
    color: var(--t-1);
}

.client-collapse-head:hover .chevron,
.client-notify-row:hover::after { color: var(--t-2); }

/* Die fuehrenden Icons der Leisten bleiben grau. Ein Icon, das
   nur benennt, worum es in der Zeile geht, ist Beschriftung und
   kein Zustand - und es traegt hier auch keine Bewegung. */
.client-collapse-head > .wb-icon:first-child,
.client-notify-row > .wb-icon { color: var(--t-3); font-size: 18px; }

/* Kartenkopf-Icons: Gold.

   Zwischenzeitlich standen sie auf Grau. Das ging auf eine Frage
   von mir zurueck, die auf einer falschen Zaehlung beruhte, und
   Marco hat es nach der Sichtung am angemeldeten Portal umgekehrt.

   Der Grund ist nicht nur Geschmack, sondern messbar: der Hover
   vergroessert das Icon um 15 Prozent und dreht es um 5 Grad - bei
   18px Kantenlaenge sind das 2,7px Zuwachs. Grau auf dunklem Grund
   verschluckt das. Gemessen laeuft die Bewegung in beiden Faellen
   identisch (transform-Matrix nachgewiesen), sichtbar ist sie nur
   mit Gold. Das Design-Labor zeigt sie so, und das Labor ist die
   Vorgabe.

   Gilt bewusst auch fuer das Admin-Cockpit, damit dort nicht als
   einzige Flaeche graue Kartenkoepfe stehen bleiben. */
.banana-card-header .wb-icon,
.banana-card-header .material-icons,
.admin-card-header .wb-icon,
.admin-card-header .material-icons {
    color: var(--gold);
}

/* Das Haekchen bestaetigt einen erreichten Zustand - nach Regel 7
   eine der drei erlaubten Goldstellen. */
#stammdaten-status-bar .status-bar-left .wb-icon,
#stammdaten-status-bar .status-bar-left .material-icons {
    color: var(--gold);
}

/* Dieses Chevron war die letzte Stelle im Kundenbereich, die
   noch eine Material-Icons-Ligatur zog - direkt neben den
   Phosphor-Chevrons der Aufklappleiste darueber. Zwei
   Icon-Handschriften auf einer Flaeche. Jetzt derselbe Satz.
   (\e136 = caret-down, identisch zur Zeile darueber.) */
.client-notify-row::after {
    content: "\e136";
    font-family: 'Phosphor-Light';
    font-size: 18px;
    color: var(--t-3);
    margin-left: var(--sp-3);
}

/* --- 8.4 Leerzustaende: kein Goldkreis -----------------------
   Ein leeres Portal ist der Normalfall am ersten Tag. Der
   Goldkreis machte das Nichts zum lautesten Element der Seite. */
.client-assets-empty .wb-icon,
.client-assets-empty .material-icons {
    background: rgba(255,255,255,.03);
    border: 1px solid var(--line-1);
    color: var(--t-3);
    box-shadow: var(--lip);
}

.client-assets-empty { font-size: var(--fs-5); }

/* --- 8.5 Kleinteile ------------------------------------------ */

/* Gewicht 800 gibt es nach Regel 2 nicht mehr, Radius 20px nach
   Regel 4 auch nicht. Das Gold bleibt: "NEU" ist ein Zustand. */
.client-new-badge {
    font-family: var(--font-mono);
    font-size: var(--fs-6);
    font-weight: 400;
    letter-spacing: .06em;
    border-radius: var(--r-s);
    padding: var(--sp-1) var(--sp-2);
}

#stammdaten-status-bar {
    border-radius: var(--r-m);
    padding: var(--sp-3) var(--sp-4);
    font-size: var(--fs-4);
}

/* Inline gesetzt im Markup, deshalb hier mit !important. */
#btn-save-notifications {
    font-size: var(--fs-4) !important;
    font-weight: 700 !important;
    text-transform: none !important;
    letter-spacing: 0 !important;
    border-radius: var(--r-m) !important;
    padding: var(--sp-3) var(--sp-5) !important;
}

.btn-client-upload { padding: var(--sp-2) var(--sp-4) !important; font-size: var(--fs-5) !important; }

/* Der Zaehler unter dem Absenden-Knopf: Marco wollte ihn optisch mit
   dem Leerzustand links ("Noch keine Neuigkeiten...") gleichziehen -
   der steht auf 13px/500 (--fs-5), nicht auf der urspruenglich hier
   gesetzten kleinsten Stufe 11px/400. Gemessen am angemeldeten Portal
   und uebernommen. Das Gold auf der Zahl faellt weiterhin weg, weil
   es ein Messwert ist und kein Zustand. */
#current-upload-count { color: var(--t-2) !important; font-weight: 500; }
#upload-counter-display { font-size: var(--fs-5) !important; font-weight: 500; color: var(--t-3); }

/* Die zwei Karten "Von uns fuer dich" und "Dateien an uns senden"
   stehen im Grid auf align-items:stretch (index.html) und damit
   gleich hoch - aber die Karte selbst gibt diese Hoehe nicht an
   ihren Body weiter (banana-card-body steht dort auf flex:none).
   Ohne das haengt der Leerzustand links oben, der Zaehler rechts
   unten, "versetzt" genau wie gemeldet.

   height:100% (erste Fassung) erzeugte einen Zirkelbezug: die Karte
   selbst steht auf height:auto (Inhalt bestimmt die Hoehe), ihr Body
   sollte gleichzeitig 100% DIESER noch unbestimmten Hoehe fuellen.
   Ergebnis bei echtem Inhalt (getestet mit echter Datei in der
   Warteschlange): Body wurde 60px hoeher als die eigene Karte, der
   Zaehler rutschte unten aus der sichtbaren Karte raus - "die MB-
   Anzeige ist ganz weg". flex:1 hat den Zirkelbezug nicht: es fuellt
   den vom Grid bereits fertig berechneten Rest, kein zweiter Durchlauf
   noetig. Siehe Fallstrick 26. */
.client-cockpit-grid .banana-card-body { flex: 1; }

/* Leerzustand links an den Kartenboden pinnen, per !important gegen
   das inline margin-top:37px (Fallstrick 14). */
#client-activity-feed { margin-top: auto !important; }

/* Klassischer Flex-Trick: zwei auto-Raender zentrieren den Knopf
   im uebrigen Platz zwischen Formular und Zaehler, waehrend der
   Zaehler selbst durch margin-bottom:auto am Knopf an den unteren
   Kartenrand gedrueckt wird - auf gleicher Hoehe wie der Leerzustand
   links, weil beide Karten-Bodies dasselbe Padding tragen. */
#btn-client-upload { margin-top: auto; margin-bottom: auto; }

/* Gleiche Unterkante der KAESTEN reicht nicht - Marco meint die
   TEXTE. Der Leerzustand links traegt padding:32px 24px, sein Text
   endet dadurch 35px ueber der eigenen Unterkante; der Zaehler rechts
   sass mit 2px praktisch buendig. Gemessen am angemeldeten Portal:
   Textunterkante 711 links gegen 744 rechts = 33px Versatz, genau der
   sichtbare Fehler. Derselbe Abstand unten am Zaehler bringt beide
   Textzeilen auf eine Linie - und zwar ueber das Token, nicht ueber
   eine abgelesene Zahl, damit es mitwandert, falls der Leerzustand
   je anders gepolstert wird. */
#upload-counter-display { margin-bottom: var(--sp-6); }

/* Zurueckgenommen (2026-08-05): eine Canvas-Alphakanal-Messung ausserhalb
   des echten DOM hatte hier faelschlich 2px Korrektur nahegelegt. Live im
   angemeldeten Portal gemessen (echter Rohtext-Knoten, den app.js beim
   Umschalten erzeugt) lag die Box-Mitte vorher bei 0,06px Differenz -
   praktisch perfekt. Die Korrektur hat ein kaum vorhandenes Problem
   ueberkorrigiert; Marcos Rueckmeldung "hängt jetzt tiefer" bestaetigte
   das. Siehe Fallstrick 26 fuer die Lehre: Canvas-Messung ausserhalb des
   echten Elements ersetzt keine Messung am echten, angemeldeten DOM. */

/* Das Textfeld "Worum geht's?" hatte einen goldenen Rand mit
   8 Prozent Deckkraft - Gold als Dekoration an einem Eingabefeld,
   und zugleich der letzte 8px-Radius auf der Flaeche. */
.client-form-field-boxed {
    border: 1px solid var(--line-1);
    border-radius: var(--r-m);
    background: rgba(255,255,255,.02);
    padding: var(--sp-2) var(--sp-4);
}

/* Der "aendern"-Knopf in der Benachrichtigungsleiste stand in
   11px-Versalien mit Gewicht 700 und wurde beim Hover gold.
   Die Juli-Schicht setzt hier durchgehend !important, deshalb
   steht es auch hier (Fallstrick 1). */
.btn-premium-text-link {
    font-family: var(--font-body) !important;
    font-size: var(--fs-5) !important;
    font-weight: 500 !important;
    text-transform: none !important;
    letter-spacing: 0 !important;
    color: var(--t-2) !important;
}
.btn-premium-text-link:hover { color: var(--t-1) !important; }
.btn-premium-text-link .wb-icon { font-size: 15px !important; }

.banana-upload-zone p { font-size: var(--fs-4); }
.banana-upload-zone p strong { font-size: var(--fs-4); font-weight: 500; color: var(--t-1); }

/* Ablageflaeche im Kundenbereich etwas flacher (2026-08-05, Teil der
   "zu viel Luft"-Runde). Bewusst ueber die ID und NICHT ueber die
   gemeinsame Klasse .banana-upload-zone/.admin-upload-zone: das
   Admin-Cockpit ist noch nicht ueberarbeitet und soll sich hier nicht
   still mitaendern. Gespart werden ~14px: 4px weniger Polster oben
   und unten, ein etwas kleineres Wolkensymbol mit engerem Abstand
   zur Zeile darunter. Die Klickflaeche bleibt komfortabel gross. */
#client-upload-zone { padding: var(--sp-3) var(--sp-5); }
#client-upload-zone .wb-icon { font-size: 22px; margin-bottom: var(--sp-1); }

/* Feldbeschriftungen im Kundenbereich wie im Login: keine
   gesperrten Versalien. */
.client-form-field label {
    font-family: var(--font-body);
    font-size: var(--fs-5);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    color: var(--t-2);
}

/* ============================================================
   9. VERSALIEN: SAMMELREGEL STATT EINZELJAGD
   ============================================================
   Befund 2026-08-05, ausgeloest durch Marcos Screenshot der
   angemeldeten Fläche: index.html setzt an 31 Stellen
   text-transform:uppercase, die Optikschicht hatte davon 13
   einzeln entschaerft. Der Rest stand weiter - "NEUIGKEITEN VON
   UNS", "AENDERN", die Filterchips.

   Einzelselektoren nachzuziehen heisst, denselben Fehler noch
   dreimal zu machen: die naechste Stelle, die jemand ergaenzt,
   steht wieder in Versalien. Deshalb umgekehrt - Versalien sind
   auf der Kundenflaeche grundsaetzlich aus, und die Ausnahmen
   stehen als benannte Liste darunter. Regel 3 gibt hoechstens
   acht Stellen frei; die Liste ist damit die Obergrenze, nicht
   der Startpunkt.

   Bewusst NICHT global (*): das wuerde auch das Admin-Cockpit
   treffen, das als eigene Flaeche noch aussteht. */
#client-dashboard *,
header * {
    text-transform: none;
}

/* Die Ausnahmen. Jede einzeln begruendet, keine Sammelposten.
   1-2  Zustand, kein Schmuck: "NEU" und der Status-Punkt.
   3-4  Datei-Etiketten - kurze Kennungen, die als Block gelesen
        werden, nicht als Satz. Space Mono traegt sie. */
#client-dashboard .client-new-badge,          /* 1 */
#client-dashboard .status-badge,              /* 2 */
#client-dashboard .client-asset-tag,          /* 3 */
#client-dashboard .client-asset-status-label  /* 4 */ {
    text-transform: uppercase;
    letter-spacing: .06em;
}

/* Der Neuigkeiten-Kopf war 10px, Gewicht 700, gesperrte
   Versalien - drei Verstaerker fuer eine Zwischenueberschrift.
   Jetzt eine Stufe der Skala und Schluss. */
.client-sub {
    font-family: var(--font-body);
    font-size: var(--fs-5);
    font-weight: 500;
    letter-spacing: 0;
    color: var(--t-2);
}

/* "Aendern" in der Stammdatenleiste: Bedienelemente, keine
   Etiketten.

   text-transform steht hier ausdruecklich mit drin. Die Sammelregel
   oben ist "#client-dashboard *" und damit nur 1-0-0; index.html
   setzt "#stammdaten-status-bar .btn-status-edit" mit 1-1-0 und
   gewinnt. Eine Sammelregel mit Universalselektor deckt also nur
   ab, was NICHT ueber eine ID adressiert wird - beim naechsten
   solchen Fall ist die Spezifitaet zuerst zu pruefen, nicht die
   Reihenfolge im Stylesheet. */
#stammdaten-status-bar .btn-status-edit,
.stammdaten-close-btn {
    font-family: var(--font-body);
    font-size: var(--fs-5);
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
}

/* ============================================================
   10. NACHTRAG AUS MARCOS SICHTUNG (2026-08-05)
   ============================================================ */

/* --- 10.1 Abgeschnittene Auswahlfelder -----------------------
   Befund aus Video 2: "Herr" stand nur zur Haelfte im Feld.
   index.html:325 setzt .client-stammdaten-section select auf
   height:38px. Abschnitt 3 vergroessert Innenabstand auf 12/16px
   und Schrift auf 15px - die feste Hoehe blieb stehen. Noetig
   waeren rund 49px, vorhanden 38px, also schneidet das Feld den
   eigenen Inhalt ab.

   Lehre fuer die restlichen Flaechen: wer Innenabstand oder
   Schriftgroesse anhebt, muss jede feste height im Basis-CSS
   mit aufloesen. Nur padding zu ueberschreiben reicht nicht. */
.client-stammdaten-section select,
.client-form-field select,
.admin-field select {
    height: auto;
    min-height: 0;
    line-height: 1.4;
}

/* --- 10.2 Der Speichern-Knopf ohne Bewegung ------------------
   Befund aus Video 3, gemessen: der Knopf in der Benachrichtigungs-
   leiste stand in allen 31 Einzelbildern exakt auf 281x87 an
   (153,381) - keine Regung. Der Stammdaten-Knopf aus Video 2 hebt
   sich dagegen um 2px.

   Ursache: #btn-save-notifications traegt seine Optik als
   Inline-Stil im Markup (index.html), und Inline schlaegt jede
   Klassenregel. Er stand deshalb nie in der Primaerknopf-Gruppe
   aus Abschnitt 2. !important ist hier die einzige Handhabe,
   solange der Inline-Stil im Markup bleibt. */
#btn-save-notifications {
    background: var(--gold) !important;
    color: #08090c !important;
    border: none !important;
    border-radius: var(--r-m) !important;
    font-family: var(--font-body) !important;
    font-size: var(--fs-4) !important;
    font-weight: 700 !important;
    letter-spacing: 0 !important;
    text-transform: none !important;
    padding: var(--sp-3) var(--sp-5) !important;
    width: auto !important;
    box-shadow: inset 0 1px 0 rgba(255,255,255,.35), var(--e-1) !important;
    transition: transform .12s var(--ease), box-shadow .2s var(--ease), filter .2s var(--ease) !important;
}

#btn-save-notifications:hover {
    transform: translateY(-1px) !important;
    filter: brightness(1.06) !important;
    box-shadow: inset 0 1px 0 rgba(255,255,255,.45), var(--e-2) !important;
}

#btn-save-notifications:active {
    transform: translateY(0) !important;
    filter: brightness(.97) !important;
}

/* --- 10.3 Gold in Massen zurueckgeben ------------------------
   Marco: "noch ein bisschen gelbe Akzente, aber nicht zu viel,
   sonst wirkt es wieder verspielt."

   Also keine Flaechen und kein Leuchten, sondern zwei Stellen,
   an denen Gold eine Aussage traegt statt zu schmuecken:
   eine Kante am Kartenkopf und die Ablageflaeche beim
   Hineinziehen. Beide zeigen einen Zustand - das deckt Regel 7
   ab. (Hier stand bis 2026-09-15 die tote Regel .banana-tab-btn
   .active; der aktive Filterchip setzt sein Gold inline in app.js.) */

/* Eine haarfeine Goldkante unter dem Kartenkopf. Sie faerbt
   nichts ein, sie zieht nur eine Linie unter die Ueberschrift. */
.banana-card-header {
    border-bottom: 1px solid var(--line-1) !important;
    box-shadow: inset 0 -1px 0 rgba(255,230,0,.14);
}

/* Beim Hineinziehen einer Datei darf die Ablageflaeche antworten. */
.banana-upload-zone:hover,
.banana-upload-zone.dragover {
    border-color: rgba(255,230,0,.45);
    background: rgba(255,230,0,.04);
}

/* --- 10.4 Chevron der Benachrichtigungsleiste ----------------
   Die alte Fassung setzte eine CSS-Variable --notify-open, die
   keine Regel je gelesen hat - das Chevron sah klickbar aus und
   drehte sich nie. Jetzt haengt es an einer Klasse, die derselbe
   Handler setzt, der auch Klappe und Beschriftung umstellt. */
.client-notify-row::after {
    transition: transform .2s var(--ease);
}

.client-notify-row.notify-open::after {
    transform: rotate(180deg);
}

/* Die ganze Zeile schaltet um, also darf sie das auch zeigen. */
.client-notify-row { cursor: pointer; }

/* --- 10.5 Inhalt der Klappe: bisher komplett Juli-Inline ------
   Punkt 6 der Sichtungsliste ("alle Benachrichtigungsfelder
   gegenpruefen") deckte auf: die Zeile selbst war laengst optisch
   ueberarbeitet, der aufklappbare Inhalt dahinter nie. Farbe
   #a4a9b6 statt Token, Frage bei 13px/600 statt Fliesstext-Skala,
   Abstaende 10/14px statt 4er-Raster. Alles per !important, weil
   Inline im Markup steht (Fallstrick 14). */
.notify-klappe-inhalt p {
    font-size: var(--fs-4) !important;
    font-weight: 700 !important;
    color: var(--t-2) !important;
    margin-bottom: var(--sp-4) !important;
}

.notify-klappe-inhalt label span {
    font-size: var(--fs-4) !important;
    color: var(--t-2) !important;
}

.notify-klappe-inhalt > div { gap: var(--sp-2) !important; }
.notify-klappe-inhalt label { gap: var(--sp-2) !important; }

#notification-warning-box { font-weight: 700 !important; }

/* ============================================================
   11. ADMIN-COCKPIT (2026-08-06)
   ============================================================
   Die Flaeche, die Marco selbst taeglich benutzt, war als
   einzige noch komplett unangetastete Juli-Schicht: 119
   Admin-Selektoren im <style>-Block von index.html, darunter
   font-weight 900 auf dem Panel-Titel, 800 auf dem Speichern-
   Knopf, gesperrte Versalien an jeder Beschriftung und
   Gold-Leuchten (box-shadow: 0 0 25px) an jedem Hover und
   Fokus.

   Abschnitt 9 hatte die Versalien-Sammelregel bewusst auf
   #client-dashboard und header begrenzt, weil diese Flaeche
   noch ausstand. Das wird hier nachgeholt - gleiches Muster:
   erst alles aus, dann die Ausnahmen einzeln benannt.

   design-v3.css laedt NACH dem <style>-Block (index.html:1049
   gegen :13), bei gleicher Spezifitaet gewinnt also diese
   Schicht. !important steht deshalb nur dort, wo die
   Juli-Schicht es selbst gesetzt hat. */

/* --- 11.1 Versalien: Sammelregel wie auf der Kundenflaeche --- */
#admin-dashboard * {
    text-transform: none;
}

/* Die Ausnahmen, jede begruendet - Zustaende und kurze
   Kennungen, die als Block gelesen werden, nicht als Satz.
   Dieselbe Liste wie im Kundenbereich, damit beide Flaechen
   dieselbe Sprache sprechen. */
#admin-dashboard .status-badge,
#admin-dashboard .update-badge,
#admin-dashboard .client-asset-tag,
#admin-dashboard .admin-workspace-head-tag {
    text-transform: uppercase;
    letter-spacing: .06em;
}

/* --- 11.2 Gewichte: 900 und 800 gibt es nicht -------------- */
/* Regel 2 kennt 400/500/600. Der Panel-Titel stand auf 900 und
   trug zusaetzlich text-shadow - zwei Verstaerker fuer eine
   Ueberschrift, die schon durch Groesse und Position traegt. */
#admin-dashboard .admin-card-header h3,
#admin-dashboard .admin-sidebar-header h3,
#admin-dashboard .admin-client-item h4,
#admin-dashboard .delete-modal-title {
    font-weight: 600;
    text-shadow: none;
}

/* --- 11.2b Kopfzeilen-Angleichung an Kunden-Dock (Marco, 2026-08-06) ---
   Kunden-Dock (.admin-sidebar-header h3) war beim Schrift-Rutsch auf
   var(--font-body)/fs-3 versehentlich ausgenommen (Abschnitt 3 oben
   nennt nur .admin-card-header h3), lief also weiter in 13px Space
   Mono, waehrend Stammdaten und Projekt-Updates auf 18px General Sans
   sprangen. Marco will die Kartenkoepfe gleich hoch UND gleiche
   Schrift/Groesse, mit Kunden-Dock als Vorbild - hier zurueckgeglichen
   statt umgekehrt, damit die Kopfzeile schmal und ruhig bleibt.

   Hoehe bewusst per Konstruktion statt per Messung: das Browser-Pane
   liefert fuer diese Flaeche durchgaengig Layout 0 (Fallstrick 41),
   eine Messung waere hier wertlos. Rechnung, an Kunden-Dock
   ausgerichtet: #admin-sidebar hat 18px Innenabstand, die Icon-Zeile
   ist 20px hoch, die Textmitte liegt also 28px unter der Boxoberkante.
   Die Kartenkoepfe bekommen dieselben 18px und dieselbe 20px-Zeile
   (--head-line) und landen damit ebenfalls bei 28px. Alle Elemente in
   der Zeile werden auf diese 20px gedeckelt - auch der Bearbeiten-
   Knopf, der sonst als einziger die mittlere Karte hoeher zieht. */
#admin-dashboard {
    --head-line: 20px;
    --head-pad: 18px;
}

#admin-dashboard .admin-card-header h3,
#admin-dashboard .admin-sidebar-header h3 {
    font-family: var(--font-mono);
    font-size: 13px;
    letter-spacing: .5px;
    line-height: var(--head-line);
}

#admin-dashboard .admin-card-header .material-icons,
#admin-dashboard .admin-sidebar-header .material-icons {
    font-size: 20px;
    line-height: var(--head-line);
}

#admin-dashboard .admin-card-header {
    padding-top: var(--head-pad);
    padding-bottom: var(--head-pad);
    min-height: calc(var(--head-line) + 2 * var(--head-pad));
    box-sizing: border-box;
}

/* Der Knopf sass als einziges Element der Zeile ueber der 20px-Zeile
   (Rahmen + 4px Polsterung + 18px Textzeile) und zog damit genau die
   mittlere Karte hoeher - das war Marcos "das in der Mitte passt
   nicht". Feste Hoehe statt Polsterung haelt ihn in der Zeile. */
#admin-dashboard .admin-edit-toggle {
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .5px;
    height: var(--head-line);
    line-height: 1;
    padding: 0 9px;
    box-sizing: border-box;
}

/* Der Knopftext steht als eigener <span>, und Abschnitt 3 trifft mit
   ".admin-card-header span:not(.material-icons)" (0-2-0) genau diesen
   span direkt - eine Regel auf dem Knopf vererbt sich dagegen nicht
   dorthin. Deshalb blieb "Bearbeiten" in General Sans 18px stehen,
   obwohl der Knopf selbst laengst auf Mono 13px stand. Gemessen, nicht
   vermutet: computed style meldete "General Sans" / 18px / 500,
   waehrend alle h3 daneben auf "Space Mono" / 13px / 600 standen. */
#admin-dashboard .admin-card-header .edit-label {
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .5px;
    line-height: 1;
}

/* --- 11.2c Auswahl in der Kundenliste (Marco, 2026-08-06) ---
   Zwei Anlaeufe vorher missverstanden: Marcos "das Gelb in dem Feld nervt,
   der gelbe Ring drumherum ist okay" meinte die goldene FLAECHE der Karte
   (.admin-client-item.selected, index.html:~514, background rgba(255,230,0,.08)
   plus inset-Goldbalken links) - nicht den Punkt. Der Punkt war beim ersten
   Anlauf faelschlich entfernt worden.

   Endstand, wie Marco ihn benannt hat:
   - Karte innen wie jede andere Zeile, nur der goldene Rahmen bleibt
   - der Punkt darueber wird satt gefuellt, sobald die Zeile ausgewaehlt ist

   ⚠️ KORRIGIERT 2026-08-14: Hier stand, der Punkt werde "gruen", mit einer
   langen Begruendung, Gruen sei eine bewusste Ausnahme von Regel 7. Das war
   falsch dokumentiert - im gesamten Stylesheet kam nie ein Gruen vor, der
   Code darunter setzte immer var(--gold). Marco hat am 2026-08-14 entschieden:
   Gold bleibt, der Kommentar wird nachgezogen (nicht umgekehrt). Die Auswahl
   ist am goldenen Rahmen erkennbar; der gefuellte Punkt bestaetigt sie, ohne
   eine achte Farbe einzufuehren. Kein Glow (Regel 6): satte Flaeche, den
   Schein nimmt Abschnitt 11.3. :not(.visible), damit ein echter neuer Upload
   weiterhin Vorrang hat und nicht ueberschrieben wird.

   Spezifitaet: index.html:~514 ist .admin-client-item.selected (0-2-0) ohne
   !important, die Praefixe hier liegen mit 1-2-0 sauber darueber. */
#admin-dashboard .admin-client-item.selected {
    background: var(--s-2);
    border-color: var(--gold);
    box-shadow: var(--lip);
}

/* Seit 2026-08-14 ohne #admin-dashboard-Praefix, aus demselben Grund
   wie der Punkt selbst (Abschnitt 13.1): der Pruefstand liegt
   ausserhalb dieses Knotens und zeigte den Zustand sonst falsch.
   .admin-client-item kommt nur im Kunden-Dock vor, die Regel greift
   also nirgends zusaetzlich. Spezifitaet 0-3-0 liegt weiter ueber dem
   .update-badge aus Abschnitt 13.1 (0-1-0). */
.admin-client-item.selected .update-badge:not(.visible) {
    background: var(--gold);
    border-color: var(--gold);
    box-shadow: none;
}

#admin-dashboard .admin-field label,
#admin-dashboard .admin-sublabel,
#admin-dashboard .admin-workspace-head-tag,
#admin-dashboard .status-badge,
#admin-dashboard .btn-admin-save,
#admin-dashboard .btn-danger-delete,
#admin-dashboard .btn-premium-text-link {
    font-weight: 500;
}

/* --- 11.3 Kein Gold-Leuchten (Regel 6) --------------------- */
/* Tiefe kommt aus Material, nicht aus Schein. Die Juli-Schicht
   legte auf jeden Hover und jeden Fokus einen 0-0-25px-Schein;
   auf einer Arbeitsflaeche, die stundenlang offen ist, ist das
   Unruhe ohne Informationswert. Der Fokus bleibt sichtbar -
   ueber Rahmen und den dezenten Ring aus Abschnitt 5, nicht
   ueber Leuchten. */
#admin-dashboard #admin-search-input:focus,
#admin-dashboard .admin-field input:focus,
#admin-dashboard .admin-field select:focus,
#admin-dashboard .admin-field textarea:focus,
#admin-dashboard .btn-admin-save:hover,
#admin-dashboard .btn-admin-invite:hover,
#admin-dashboard .btn-danger-delete:hover,
#admin-dashboard .btn-asset-admin-delete:hover,
#admin-dashboard .update-badge.visible {
    box-shadow: none;
    text-shadow: none;
}

/* Fokus bekommt denselben Ring wie ueberall sonst - erkennbar,
   aber ohne Schein. */
#admin-dashboard #admin-search-input:focus,
#admin-dashboard .admin-field input:focus,
#admin-dashboard .admin-field select:focus,
#admin-dashboard .admin-field textarea:focus {
    border-color: var(--gold);
    box-shadow: inset 0 1px 3px rgba(0,0,0,.45), 0 0 0 3px rgba(255,230,0,.16);
}

/* Der Speichern-Knopf ist die Primaeraktion der Flaeche und
   darf gold bleiben (Regel 7) - er hob sich aber zusaetzlich
   um 2px an und leuchtete dabei. Die Hebung bleibt, halbiert
   und ohne Schein, wie beim Primaerbutton im Kundenbereich. */
#admin-dashboard .btn-admin-save:hover {
    transform: translateY(-1px);
    filter: brightness(1.06);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.45), var(--e-2);
}

/* Die beiden Admin-Scrollbalken leuchteten beim Hover weiter:
   Abschnitt 2 setzt fuer sie zwar den Griff auf neutral, aber
   nur die Farbe - der box-shadow aus index.html (:511 und :579)
   blieb dadurch stehen, und bei .admin-assets-list gewinnt die
   :hover-Fassung dort ohnehin ueber die Spezifitaet. Deshalb
   hier ausdruecklich beide Eigenschaften. */
#admin-client-list:hover::-webkit-scrollbar-thumb,
.admin-assets-list:hover::-webkit-scrollbar-thumb,
.admin-assets-list::-webkit-scrollbar-thumb:hover {
    background: #3a4051;
    box-shadow: none;
}

/* Der Upload-Fortschrittsbalken trug seine gesamte Optik inline
   im Markup und war damit fuer jede Regel unerreichbar
   (Fallstrick 14) - inklusive eines 8px-Goldscheins. Jetzt liegt
   hier alles ausser der Breite, die app.js waehrend des Uploads
   setzt (app.js:2527). Gold bleibt: ein Fortschritt ist Funktion,
   kein Schmuck (Regel 7) - nur eben ohne Leuchten (Regel 6). */
#admin-upload-progress-bar {
    height: 100%;
    background: var(--gold);
    transition: width .1s ease;
}

/* --- 11.5 Suchfeld: Platz fuer die Lupe -------------------- */
/* Die Sammelregel fuer Eingabefelder (Abschnitt 1, Zeile 276)
   nimmt #admin-search-input mit auf und setzt padding auf
   sp-3/sp-4 - damit fiel der linke Einzug von 38px auf 16px,
   waehrend das Lupensymbol absolut bei 11-29px stehenblieb.
   Der Platzhalter "Kunde suchen..." begann dadurch mitten im
   Symbol. Marco im Screenshot 2026-08-06 als angeschnittenes
   Zeichen vor dem Text sichtbar. Der Einzug kommt hier zurueck;
   die uebrigen Felder behalten die Sammelregel. */
#admin-search-input {
    padding-left: 38px;
}

/* --- 11.6 Beschriftungen: Mono war an Versalien gebunden ---- */
/* Der Sammelblock in index.html:650 gibt Space Mono an alle
   "technischen Uppercase-Labels" - so steht es dort woertlich
   im Kommentar. Mit 11.1 sind die Versalien weg, damit ist die
   Begruendung fuer die Schreibmaschinenschrift entfallen: uebrig
   blieben gesperrte Kleinbuchstaben, die neben der Kundenflaeche
   fremd wirken (dort tragen dieselben Beschriftungen laengst
   --font-body, siehe "Feldbeschriftungen im Kundenbereich wie im
   Login"). Beide Flaechen sprechen jetzt dieselbe Sprache.
   Die Etiketten mit erlaubten Versalien behalten Mono - dort
   traegt es weiterhin, siehe Ausnahmeliste in 11.1. */
#admin-dashboard .admin-field label,
#admin-dashboard .admin-sublabel,
#admin-dashboard .client-form-field label {
    font-family: var(--font-body);
    font-size: var(--fs-5);
    font-weight: 500;
    letter-spacing: 0;
    color: var(--t-3);
}

/* --- 11.4 Radien (Regel 4: 4 / 10 / 16) -------------------- */
/* .update-badge ist hier bis 2026-08-14 mitgelaufen und bekam dadurch
   4px Radius statt eines Kreises. Regel 4 gilt fuer Flaechen, nicht
   fuer einen Punkt - der Kreis ist keine vierte Radiusstufe, sondern
   die Form des Elements. Eigene Regel in Abschnitt 15. */
#admin-dashboard .admin-workspace-head-tag,
#admin-dashboard .status-badge {
    border-radius: var(--r-s);
}

/* ============================================================
   12. AUSGEWAEHLTER KUNDENNAME IN DER KOPFZEILE (2026-08-06)
   ============================================================
   Stand bis heute gross im Master-Dock-Workspace
   (.cockpit-title-wrap, 22px/900/Textschatten) und schob dort
   als eigene Zeile die beiden Karten nach unten - Marco im
   Screenshot: "die Boxen sind wegen dem Kundennamen so nach
   unten verrueckt". Auf seinen Wunsch in die Kopfzeile verlegt,
   neben "Master Dock". Gleiche ID wie zuvor (#admin-panel-mid-
   title), die Live-Bindung in app.js:2261 (Firestore-Snapshot)
   ist unveraendert - nur der Ort im Markup hat sich geaendert.
   Die alten Regeln dafuer waren auf "#admin-dashboard
   #admin-panel-mid-title" gescoped; das <header> liegt ausserhalb
   von #admin-dashboard, deshalb eine eigene, vollstaendige Regel
   statt eines Nachtrags zu den alten Selektoren. */
#admin-panel-mid-title {
    font-size: var(--fs-3);
    font-weight: 500;
    font-family: var(--font-body);
    color: var(--gold);
    text-shadow: none;
    margin: 0;
}

/* Trennzeichen vor dem Namen, nur wenn er tatsaechlich sichtbar
   ist (also ein Kunde ausgewaehlt wurde) - kein "Master Dock ·"
   ins Leere. [hidden] entfernt das Element bereits aus dem
   Layout; ::before greift nur am sichtbaren Element. */
#admin-panel-mid-title::before {
    content: "·";
    margin-right: var(--sp-2);
    color: var(--t-3);
    font-weight: 400;
}


/* ============================================================
   ABSCHNITT 12 - RESPONSIVE: TABLET UND HANDY
   ------------------------------------------------------------
   Bis hierher regelte die Optikschicht nur den Anmeldebereich
   responsiv (die 899/900er-Haltestelle weiter oben). Alles
   andere - Kopfzeile, Kunden-Dock, Cockpit - haing noch an den
   Media Queries aus index.html, und die stammen aus dem
   ABGELOESTEN Design: sie sprechen --bg-card und --accent-neon
   an, brechen bei 480/600/768/900/950/1200 und widersprechen
   sich zwischen 900 und 950. Dieser Abschnitt zieht das zusammen.

   ZWEI Haltestellen, mehr nicht:

     max-width: 1023px   "nicht Desktop"  - Tablet UND Handy
     max-width:  599px   "Handy"          - verschaerft, was
                                            oben schon gilt

   Der Handy-Block steht bewusst NACH dem Tablet-Block: gleiche
   Spezifitaet, spaeter im Stylesheet, also gewinnt er. Wer eine
   Regel nur fuer Handys will, schreibt sie unten; wer sie fuer
   beide will, oben. Es gibt keinen dritten Ort.

   Warum diese Datei und nicht index.html: die Optikschicht wird
   als letzte geladen und gewinnt bei gleicher Spezifitaet. Die
   alten Queries bleiben unangetastet stehen (Rueckbau = die eine
   <link>-Zeile entfernen, laut CLAUDE.md), werden hier aber
   ueberstimmt, wo sie falsch liegen.

   AUSNAHME, bewusst stehen gelassen: der Anmeldebereich bricht
   weiter bei 900px, nicht bei 1023. Diese Grenze ist inhaltlich
   bestimmt (ab dort passen Markenspalte und Karte nebeneinander)
   und war die einzige verifiziert funktionierende responsive
   Flaeche. Sie ohne Not zu verschieben waere Risiko ohne Gewinn.
   ============================================================ */


/* --- 12.1 Tablet und schmaler ------------------------------- */

@media (max-width: 1023px) {

    /* Aussenrand: 40px seitlich sind auf einem Tablet verschenkt,
       auf einem Handy fressen sie ein Fuenftel der Breite. */
    main { padding: var(--sp-5) var(--sp-4); }

    /* --- Ein Scrollkontext statt drei (2026-08-17) ---------------
       `.banana-assets-list` (index.html:404) deckelt jede Liste auf
       400px mit eigenem Scrollbereich. Ab 1024px richtig: dort
       stehen die Karten im Raster nebeneinander und sollen nicht
       auseinanderlaufen. Darunter stapelt alles ohnehin, und dann
       scrollt der Kunde in einer Liste, die in einer Karte sitzt,
       die in der Seite scrollt.

       🔴 Warum das kein Schoenheitsfehler ist: Die Liste zeigt
       KEINEN dauerhaften Scrollbalken. `scrollbar-width: none` ist
       gesetzt, und der 6px-Goldbalken aus `::-webkit-scrollbar`
       ist auf Touch-Geraeten ein Overlay, das nur waehrend des
       Scrollens kurz erscheint. Wer fuenf Slots hat und drei
       sieht, hat keinen Anhaltspunkt, dass es weitergeht - er
       haelt die Liste fuer vollstaendig und vermisst den Slot, den
       er eigentlich befuellen wollte. Genau darauf bin ich am
       selben Tag selbst hereingefallen, als ein Screenshot dieser
       Liste abgeschnitten aussah.

       ⚠️ Diese Regel stand zuerst im 599px-Block. Falsch, und die
       Messung hat es gezeigt: bei **768px waren 2 von 5 Slots
       unerreichbar** (578px noetig bei 400px sichtbar). Ein Tablet
       ist genauso ein Touch-Geraet, die Overlay-Balken verhalten
       sich dort gleich - der Deckel gehoert also an dieselbe
       Grenze wie das gestapelte Layout selbst.

       Zwei Dinge muessen zusammenkommen; nur die Liste
       freizugeben reicht NICHT (siehe den Irrweg-Kommentar bei
       `.client-media-slots` weiter unten):

       (1) Die Liste darf wachsen.
       (2) Ihre Karte muss mitwachsen duerfen. Die Karten im
           Cockpit-Gitter tun das laengst (`height: auto !important`
           in index.html:1057) - die Medien-Slot-Karte liegt
           ausserhalb des Gitters und braucht dieselbe Freigabe,
           sonst schneidet ihr `overflow: hidden` genau das ab, was
           die Liste an Hoehe gewinnt.

       `!important` ist noetig, weil die Regeln in index.html es
       ebenfalls tragen; diese Datei laedt spaeter und gewinnt bei
       gleicher Spezifitaet. */
    .banana-assets-list {
        max-height: none !important;
        overflow-y: visible !important;
    }

    .client-media-slots {
        height: auto !important;
    }
    .client-media-slots .banana-card-body {
        flex: none !important;
        overflow: visible !important;
    }

    /* Kopfzeile bleibt EINE Zeile. Die Juli-Schicht stapelt sie
       ab 768px in drei Reihen (gemessen: 145px hoch) - auf einem
       768er-Tablet ist das verschenkter Platz, denn Titel und
       Abmelden passen dort bequem nebeneinander. */
    header {
        grid-template-columns: 1fr auto 1fr;
        padding: var(--sp-3) var(--sp-4);
        gap: var(--sp-3);
    }
    .header-right {
        justify-self: end;
        width: auto;
        justify-content: flex-end;
    }

    /* Kunden-Dock: gestapelt ist die Liste kein Seitenstreifen
       mehr, sondern steht ueber dem Inhalt. Mit sticky und
       calc(100vh - 140px) muesste man erst einen ganzen Schirm
       Kundenliste durchscrollen, bevor ueberhaupt Inhalt kommt.
       Also: mitscrollen statt kleben, und hoechstens halbhoch. */
    .master-dock-grid {
        grid-template-columns: 1fr;
        gap: var(--sp-4);
        margin-top: var(--sp-5);
    }
    #admin-sidebar {
        position: static;
        top: auto;
        height: auto;
        max-height: 46vh;
    }

    /* Kundenflaeche einspaltig. Stand so schon in index.html bei
       900px - hier auf die gemeinsame Haltestelle gezogen, damit
       es nur noch eine Zahl gibt. */
    .client-cockpit-grid { grid-template-columns: 1fr; }
}


/* --- 12.2 Handy --------------------------------------------- */

@media (max-width: 599px) {

    main { padding: var(--sp-4) var(--sp-3); }


    /* Die Markenzeile "WILD BANANA AI - KUNDENPORTAL" ist auf dem
       Handy die teuerste Zeile: sie kostet eine ganze Reihe und
       sagt jemandem, der die Seite gerade offen hat, nichts Neues.
       Sie weicht, und die Kopfzeile faellt von drei Reihen auf
       eine - Kundenname links, Abmelden rechts. */
    /* Bis 2026-09-03 stand hier 'grid-template-columns: 1fr auto' -
       eine Reihe, Name links, Knoepfe rechts. Am echten Geraet
       aufgefallen (Marco, Chrome/Android), danach am Pruefstand bei
       375px nachgemessen: die rechte Gruppe braucht 331px von 335px
       verfuegbaren. Die 1fr-Spalte des Namens fiel damit auf 0px.
       Sie war trotzdem 251px breit und lag QUER UEBER den Knoepfen -
       weil .header-left aus index.html 'justify-self: start' traegt
       und ein Grid-Kind damit seine Inhaltsbreite behaelt, statt in
       die Spur zu schrumpfen. Die Ellipse darunter kam nie zum Zug:
       sie greift erst, wenn das Element ueberhaupt schmaler wird.

       Ergebnis auf dem Handy: der Firmenname unlesbar hinter den
       Knoepfen, der Abmelden-Knopf halb ausserhalb.

       Vier Bedienelemente (Status, Hilfe, Passwort, Abmelden) passen
       auf 375px nicht neben einen Namen. Die Annahme im Absatz oben -
       'Kundenname links, Abmelden rechts' - stammt aus einer Zeit mit
       zwei Knoepfen und traegt seither nicht mehr. Also zwei Reihen:
       Name oben, Knoepfe darunter ueber die volle Breite. */
    header {
        grid-template-columns: 1fr;
        padding: var(--sp-3);
        gap: var(--sp-2);
    }
    .header-center { display: none; }

    /* 'justify-self: stretch' hebt das 'start' aus index.html auf.
       Ohne das bleibt die Zeile inhaltsbreit und die Ellipse tot -
       min-width:0 allein reicht dafuer NICHT, das war der Fehler. */
    .header-left {
        justify-self: stretch;
        min-width: 0;
        font-size: var(--fs-4);
    }
    .header-left > #portal-title-display,
    .header-left > #admin-panel-mid-title {
        flex: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }

    /* Zweite Reihe: die Knoepfe verteilen sich ueber die volle Breite,
       statt sich am rechten Rand zu draengen. Hebt 'justify-self: end'
       und 'width: auto' aus der 768px-Haltestelle auf. */
    .header-right {
        justify-self: stretch;
        width: 100%;
        justify-content: space-between;
        gap: var(--sp-2);
    }

    /* Regel 1 gilt auch quer: 32px Begruessung sind auf 375px
       Breite keine Hierarchie mehr, sondern ein Umbruch. Eine
       Stufe runter - bleibt in der Skala, kein Sonderwert. */
    .client-cockpit-head h2 { font-size: var(--fs-2); }

    /* DER Lesbarkeitsdefekt: die Stammdaten im Lese-Modus standen
       bis 375px hinunter zweispaltig, also zwei Spalten zu je rund
       140px. Der Bearbeiten-Modus (.admin-field-grid) hatte diese
       Regel seit jeher, der Lese-Modus nie - deshalb faellt es
       erst auf, wenn man nichts bearbeitet. */
    .stammdaten-card.locked .admin-card-body {
        grid-template-columns: 1fr;
        column-gap: 0;
    }

    /* Speicherbalken: Hinweis und Knopf nebeneinander ergibt auf
       dem Handy einen Knopf von der Breite eines Daumennagels. */
    .cockpit-savebar {
        flex-wrap: wrap;
        gap: var(--sp-3);
    }
    .cockpit-savebar .btn-admin-save { width: 100% !important; }

    /* Kundenliste weiter kuerzen - auf einem Handy ist auch
       46vh noch ein halber Bildschirm vor dem eigentlichen Inhalt. */
    #admin-sidebar { max-height: 38vh; }
}


/* --- 12.3 Fingerbedienung ----------------------------------- */
/* Haengt bewusst am Zeigergeraet, nicht an der Breite: ein
   Touch-Notebook hat 1400px und trotzdem keinen Mauszeiger.
   44px ist das uebliche Mindestmass fuer eine Fingerkuppe.

   Der Papierkorb wird SICHTBAR groesser, nicht per unsichtbarer
   Trefferflaeche. Er sitzt in der anklickbaren Kundenkarte - eine
   unsichtbare 44er-Flaeche ueber einem 26er-Knopf wuerde Klicks
   schlucken, die der Karte galten. Bei einer loeschenden Aktion
   ist "man sieht, was man trifft" wichtiger als ein unveraenderter
   Knopf. */
@media (pointer: coarse) {

    /* Der Kontaktweg im Wegweiser ist auf dem Handy der einzige
       Weg zum Support - der schwebende Knopf ist dort weg
       (Abschnitt 22). Also volle Zielgroesse. */
    .help-guide-contact-btn { min-height: 44px; }

    .client-delete-btn {
        width: 40px;
        height: 40px;
    }
    .client-delete-btn .material-icons { font-size: 20px; }

    .btn-help-circle {
        min-width: 44px;
        min-height: 44px;
    }

    .header-right .btn-header { min-height: 44px; }

    .btn-admin-invite {
        min-width: 44px;
        min-height: 44px;
    }

    /* Nachgezogen 2026-08-17 bei der 375px-Abnahme: der Knopf stand bei
       33px, obwohl er die einzige Aktion der Flaeche ist - der Kunde
       kommt ohne ihn an keinen Slot. Er fehlte hier schlicht, weil die
       Slot-Zeile (15.08.) juenger ist als dieser Block. Breite bleibt
       unangetastet, sie kommt vom Text ("Neues Video vorschlagen"). */
    .media-slot-replace {
        min-height: 44px;
    }

    /* Nachgezogen 2026-09-01 bei der 375px-Abnahme der Nachrichtenkarte,
       gleiche Klasse von Fehler wie bei `.media-slot-replace` darueber und
       aus demselben Grund uebersehen: der Block ist aelter als die Knoepfe.
       Gemessen standen alle drei bei 33px. Sie sind die Primaeraktion ihrer
       Flaeche - ohne sie geht weder eine Datei noch eine Nachricht raus.
       Breite bleibt unangetastet, sie kommt vom Text. */
    .btn-client-upload,
    .btn-stammdaten-save,
    .btn-admin-save {
        min-height: 44px;
    }

    /* Nachgezogen in der Schlusspruefung 2026-09-03: dritter Fall derselben
       Sorte, aus demselben Grund - der Block ist aelter als der Ausloeser.
       Gemessen stand er bei 44px (bei 375px sind es 68px, weil er dort auf
       zwei Zeilen umbricht). Die Zahl entstand aber aus Polsterung plus
       Zeilenkasten, nicht aus einer Zusage. Hier steht sie.
       Breite bleibt unangetastet, sie kommt vom Text. */
    .nachricht-verlauf-ausloeser {
        min-height: 44px;
    }
}

/* ============================================================
   ABSCHNITT 13 - KUNDENZEILE IM DOCK (2026-08-14)
   ------------------------------------------------------------
   Marco am 2026-08-14 am Master Dock: "da passt was nicht mit der
   anzeige". Am Screenshot waren drei Fehler zu sehen, alle in der
   Zeile des ausgewaehlten Kunden. Am nachgebauten DOM mit diesem
   Stylesheet gemessen (nicht geschaetzt):

   1. Der Papierkorb hing 18,9px UNTER der Karte. Die Aktions-Ecke
      (.admin-client-item-actions, index.html:527) ist absolut
      positioniert; sie war fuer EINEN 9px-Punkt gebaut. Seit dem
      Meldungs-Chip vom 13.08. stehen dort bis zu drei Elemente
      uebereinander - 72px hoch in einer 66,2px hohen Karte. Absolut
      positioniert waechst die Karte nicht mit.
   2. Der Punkt lag 47,3px UEBER dem Firmennamen. Das <h4> ist
      306px breit (volle Kartenbreite) und weiss nichts von der
      darueberliegenden Spalte, also griff die Ellipse zu spaet.
   3. Der Punkt war kein Punkt mehr, sondern ein 18x9px grosser
      "gelber Klecks": index.html:521 will 9x9px rund, dieses
      Stylesheet zog .update-badge aber in die Text-Badge-Gruppe
      (padding 3px 8px + Rahmen + 4px Radius). Bei box-sizing:
      border-box erzwingt Padding+Rahmen 18px Mindestbreite.

   Woher der Fehler kam - fuer die naechste Runde wichtiger als der
   Fix selbst: Im Pruefstand steht .update-badge mit dem TEXT "NEU"
   und laborseitig auf width/height:auto gezwungen
   (design-labor.html:30-47, 300). Dort SIEHT es aus wie eine
   Textpille und wurde folgerichtig wie eine gestylt. In Produktion
   ist es ein LEERES <span> mit festen 9x9px (app.js:2345). Das
   Labor kann eine Komponente also glaubhaft als etwas anderes
   zeigen, als sie im Produkt ist - ein Blick ins Labor ersetzt die
   Messung am echten, angemeldeten DOM nicht (vgl. Fallstrick 26).

   Marcos Entscheidungen dazu am 2026-08-14: Layout "Variante C"
   (Chip rechtsbuendig neben der E-Mail, alle Karten gleich hoch)
   und Gold statt der nie umgesetzten Gruen-Idee (siehe 11.2c).
   ============================================================ */

/* --- 13.1 Der Punkt ist wieder ein Punkt ------------------- */
/* Eigene Regel statt Mitfahren in der Badge-Gruppe. 8px statt der
   alten 9 hebt ihn aufs 4er-Raster; der Kreis ist keine vierte
   Radiusstufe im Sinn von Regel 4, sondern die Form des Elements.

   Bewusst NICHT auf #admin-dashboard gescoped, obwohl der Punkt nur
   dort vorkommt: das Labor liegt ausserhalb dieses Knotens
   (design-labor.html:189, .lab-wrap). Eine gescopete Regel haette
   dort weiter die alte Pille gezeigt - und genau diese Luecke
   zwischen Labor und Produkt hat den Fehler erzeugt. Eine
   Komponente, eine Definition. */
.update-badge {
    width: var(--sp-2);
    height: var(--sp-2);
    min-width: 0;
    padding: 0;
    border: 1px solid var(--line-2);
    border-radius: 50%;
    background: transparent;
    box-shadow: none;
    flex-shrink: 0;
}

/* Der echte neue Upload ist das eigentliche Signal und muss satt
   gefuellt sein. Vorher lag hier 10% Gold - als Pille lesbar, als
   8px-Punkt praktisch unsichtbar. Kein Schein und kein Puls: die
   Regeln aus index.html:522 (0 0 10px Gold plus badgePulse) sind
   Regel 6 zuwider, deshalb hier explizit zurueckgenommen statt sich
   auf die Teil-Entschaerfungen in 11.3 und index.html:698 zu
   verlassen - die greifen nur innerhalb von #admin-dashboard. */
.update-badge.visible {
    background: var(--gold);
    border-color: var(--gold);
    box-shadow: none;
    animation: none;
}

/* --- 13.2 Zeile als echte Reihe, nicht als Overlay --------- */
/* Kein position:absolute mehr: Textspalte und Aktionsspalte teilen
   sich die Breite im Fluss. Damit kann nichts mehr aus der Karte
   herauslaufen - auch nicht auf Touch, wo der Papierkorb per
   @media (pointer: coarse) auf 44px waechst. */
#admin-dashboard .admin-client-item {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

/* min-width:0 ist der Punkt, an dem die Ellipse des Firmennamens
   endlich gegen die TATSAECHLICH freie Breite rechnet. Ohne das
   behaelt die Flex-Spalte ihre Inhaltsbreite und schiebt die
   Aktionen hinaus. */
#admin-dashboard .admin-client-item-text {
    flex: 1 1 auto;
    min-width: 0;
}

#admin-dashboard .admin-client-item-zeile2 {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-1);
}

#admin-dashboard .admin-client-item-zeile2 p {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
}

/* Der Chip darf die Zeile nicht hoeher machen, sonst stehen Karten mit
   Meldung 8px hoeher als die ohne - und "alle Karten gleich hoch" war
   genau der Grund, aus dem Variante C gewaehlt wurde. Mit line-height:1
   und 2px Luft oben/unten kommt er auf dieselben 17px wie die
   E-Mail-Zeile. Die 2px sind bewusst neben dem 4er-Raster: es ist
   Innenluft eines 11px-Labels, kein Layout-Abstand — dieselbe Ausnahme,
   die die Pillen-Regel in Abschnitt 3 mit ihren 3px schon macht. */
#admin-dashboard .admin-client-item-zeile2 .client-asset-tag {
    flex-shrink: 0;
    padding-top: 2px;
    padding-bottom: 2px;
    line-height: 1;
}

/* Punkt oben, Papierkorb darunter - align-self:flex-start haelt die
   Spalte an der Textoberkante, sonst zentriert die Karte sie. */
#admin-dashboard .admin-client-item-actions {
    position: static;
    align-self: flex-start;
    flex-shrink: 0;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
}

/* --- 13.3 Papierkorb dauerhaft rot (Marco, 2026-08-14) ------ */
/* ⚠️ Bewusste UMKEHR einer frueheren Entscheidung. index.html:834
   trug den Kommentar "Rot erst beim Hover: destruktiv, aber nicht
   dauernd laut" - der Knopf war absichtlich neutral und wurde erst
   beim Zeigen rot. Marco am 2026-08-14, mit Verweis auf den roten
   Papierkorb an den bereitgestellten Dateien: "ich moechte dort
   bitte diesen muelleimer". Der Kommentar in index.html ist
   entsprechend nachgezogen - Code und Kommentar sollen sich nicht
   widersprechen (siehe 11.2c, wo genau das vier Tage lang der Fall
   war).

   Uebernommen wird die Optik von .btn-asset-admin-delete
   (index.html:606) - dieselbe Aktion, dieselbe Farbe, damit im
   Dock nicht zwei verschiedene Loeschknoepfe stehen. Ohne den
   Schein des Originals: Regel 6 verbietet das Leuchten, und 11.3
   nimmt es dem Asset-Knopf ohnehin schon.

   Die Groesse bleibt bei 26px (nicht 34px wie beim Asset-Knopf):
   34px machten die Aktionsspalte 50px hoch und damit die
   ausgewaehlte Karte sichtbar hoeher als die uebrigen - das
   widerspraeche der Zusage "alle Karten gleich hoch" aus
   Variante C. Die Fingerbedienung hebt ihn weiterhin auf 44px
   (Abschnitt 12.3). */
.client-delete-btn {
    border-color: rgba(255,51,102,.4);
    color: var(--error-red);
}

/* --- 13.4 Kleiner und kraeftiger (Marco, 2026-08-14) --------
   Zwei Rueckmeldungen direkt nacheinander: "der wirkt noch
   ziemlich blass" bei voll deckendem var(--error-red) auf dem
   Glyph - die Blaesse kommt nicht von der Farbe, sondern von der
   duennen Strichstaerke. Es gibt im Projekt nur EINE Phosphor-
   Schnittweite (fonts/phosphor-light.woff2, design-v3.css:795) -
   kein zusaetzliches Schriftschnitt-Gewicht zum Umschalten, ohne
   eine neue Font-Datei einzufuehren. Statt eines neuen Assets:
   -webkit-text-stroke zieht den vorhandenen Strich dicker, rein
   optisch, ohne Font-Wechsel und ohne Schein (Regel 6 bleibt
   sauber, das ist kein box-shadow/text-shadow). Dazu Rahmen und
   Ruhe-Flaeche angehoben - vorher fast unsichtbare .4 Rahmen-
   Deckkraft auf transparentem Grund. text-stroke ist WebKit-
   Sondersyntax (Chrome/Edge/Safari); ohne Unterstuetzung bleibt
   die Kontur so duenn wie zuvor, keine Verschlechterung.
   Icon-Groesse 14px -> 9px ("um ein drittel kleiner").

   ⚠️ KORRIGIERT, gleiche Nachricht: Hier stand "Knopf bleibt bei
   26px" - Marco meinte mit "ein drittel kleiner" den GANZEN Knopf,
   nicht nur das Glyph darin ("der ganze button sollte kleiner
   werden, nicht nur der mülleimer"). 26px * 2/3 = 17,3px, auf 17px
   gerundet. Icon bleibt bei 9px mit Kontur (das war schon richtig
   fuer "mehr Farbe"), nur der Rahmen darum schrumpft mit.

   Nebeneffekt, positiv: Die Aktionsspalte (Punkt 8px + Abstand 8px
   + Knopf) kommt jetzt auf 33px statt 42px - deutlich unter der
   43px hohen Textspalte. Die in 13.2 hingenommene 0,8px-Differenz
   der ausgewaehlten Karte (siehe dortiger Kommentar) verschwindet
   damit von selbst, ohne dass hier gezielt danach gesucht wurde. */
.client-delete-btn .material-icons {
    -webkit-text-stroke: .4px currentColor;
}

/* --- 13.5 Ruhezustand golden, nicht rot (Marco, per Aufzeichnung
   2026-08-14 111607.mp4) ------------------------------------
   Video zeigt einen DRITTEN, bis dahin unangefassten Papierkorb:
   #btn-delete-deliverable / .btn-deliverable-delete
   (index.html:1631, "aktuelle Datei" im Bereitstellen-Panel). Der
   wirkt in Ruhe golden und rahmenlos, erst beim Hover ein voll
   gefuelltes rotes Quadrat - Marco: "den mülleimer bitte genauso
   bauen wie den anderen mülleimer im video (so wie den gelben)".

   Nachgemessen, WARUM er dort golden erscheint: index.html:609
   setzt `.btn-deliverable-delete { color: #FF3366 }` (rot), aber
   index.html:575 `.admin-current-asset .material-icons { color:
   var(--accent-neon) }` trifft das Icon-<span> direkt (Abstammungs-
   selektor, nicht nur Vererbung) und gewinnt gegen die geerbte rote
   Farbe - unabhaengig von Deklarationsreihenfolge, weil eine direkt
   treffende Regel eine nur geerbte Eigenschaft immer schlaegt. Ob
   das am 05.08. Absicht oder Zufall war, ist nicht mehr feststellbar
   - Marco hat das Ergebnis heute ausdruecklich als Zielbild benannt,
   das genuegt als Entscheidung. Der Deliverable-Knopf selbst bleibt
   unangetastet (Marcos Dauerregel "alles andere NICHT anfassen").

   Uebertragen auf .client-delete-btn: kein Rahmen, keine Flaeche in
   Ruhe (beides aus 13.3/13.4 hier zurueckgenommen), Icon-Farbe
   golden. Erst :hover fuellt rot - unveraendert aus 13.3, das war
   bereits richtig.

   ⚠️ Groesse hier noch als "unangetastet, eigene bestaetigte
   Entscheidung" notiert - das galt nur bis zur naechsten Nachricht,
   siehe 13.6. */
.client-delete-btn {
    border: none;
    background: transparent;
    color: var(--gold);
}

.client-delete-btn:hover {
    background: var(--error-red);
    border-color: var(--error-red);
    color: #fff;
    box-shadow: none;
}

/* --- 13.6 Doch dieselbe Groesse wie das Vorbild (Marco, direkt
   im Anschluss) -------------------------------------------------
   "der der im berholzkasten soll auch die selbe größe wie der
   andere gelbe mülleimer haben" - 13.4 hatte den Knopf eigenstaendig
   auf 17px/9px verkleinert, ohne am Vorbild (.btn-deliverable-
   delete, index.html:609) zu messen. Jetzt woertlich uebernommen,
   nicht nur die Farbe wie in 13.5, sondern auch die Box-Mechanik:
   kein fester width/height mehr (der hätte hier gegen den Inhalt
   gewonnen), stattdessen padding:2px wie das Vorbild - die Box
   ergibt sich aus Icon + Padding, exakt wie dort. border-radius
   ebenfalls auf 4px gezogen (Vorbild-Wert), vorher 6px aus der
   Basisregel (index.html:841, fuer den frueheren 26px-Knopf
   gedacht). -webkit-text-stroke aus 13.4 bewusst BEIBEHALTEN,
   obwohl das Vorbild keine Kontur hat: bei 16px Phosphor-Light
   ist der Strich dick genug, ohne Kontur wirkt es identisch: 0px
   sichtbarer Unterschied in der Gegenprobe, also unschaedlich
   stehen gelassen statt einer weiteren Regel zum Zuruecknehmen. */
.client-delete-btn {
    width: auto;
    height: auto;
    padding: 2px;
    border-radius: 4px;
}

.client-delete-btn .material-icons {
    font-size: 16px;
}

/* ============================================================
   14. MEDIEN-SLOTS (2026-08-15)
   ------------------------------------------------------------
   Feature 2 des Staging-Systems: feste Stellen der Kundenwebsite,
   deren Inhalt der Kunde selbst tauscht.

   Zwei Flaechen, ein Bauteil: die Zeile sieht im Kundenbereich
   und im Master Dock gleich aus. Sie ist bewusst NICHT von
   .banana-assets-list abgeleitet - eine Dateizeile zeigt eine
   Datei, eine Slot-Zeile zeigt eine STELLE, die auch leer sein
   kann. Der Leerzustand ist hier ein regulaerer Zustand, kein
   Fehler, und braucht eigene Gestalt.

   Regel 7 beachtet: Gold traegt hier ausschliesslich der
   Vorschlagen-Knopf (Primaeraktion der Flaeche, .media-slot-replace
   - hiess bis 2026-08-16 "Austauschen"). Der Zustand
   "noch nichts hinterlegt" ist bewusst grau - ein offener Slot
   ist keine Warnung, nur eine Feststellung.
   ============================================================ */

/* ⚠️ Lehrstueck vom 2026-08-17, in zwei Schritten. Hier stand
   kurzzeitig `height: auto !important; min-height: 640px`, weil
   die Karte im Screenshot unten abgeschnitten aussah, sobald
   eine Absage die Zeile hoeher machte. Wieder entfernt, und die
   Begruendung lohnt sich zu kennen:

   **Erster Fehler - falsche Ursache.** Die Begrenzung kam nicht
   von `.banana-card`, sondern von `.banana-assets-list
   { max-height: 400px }`. Die Karte freizugeben half deshalb
   nichts: die Liste blieb gedeckelt und scrollte weiter, die
   Karte stand bei Absagen nur 35px hoeher als alle anderen. Es
   war also kein falscher, sondern ein **unvollstaendiger**
   Eingriff - er behandelte (2) ohne (1).

   **Zweiter Fehler - falsches Messgeraet.** Dass es ueberhaupt
   nach einem Defekt aussah, lag am Screenshot: kopfloses Chrome
   laeuft mit `--hide-scrollbars`, genau das Signal "hier geht es
   weiter" war ausgeblendet. **Ein abgeschnittener Rand im Bild
   ist erst dann ein Befund, wenn scrollHeight gegen clientHeight
   es bestaetigt.**

   Aufgeloest ist beides jetzt im `max-width: 1023px`-Block
   weiter oben: dort faellt der Deckel der Liste UND die Karte
   darf mitwachsen. Nur zusammen ergibt das einen Effekt - und
   nur unterhalb von 1024px, weil der Deckel im Raster daneben
   seinen Sinn hat. */
.client-media-slots {
    margin-top: var(--sp-5);
}

.client-media-slots-intro {
    margin: 0 0 var(--sp-4);
    font-size: var(--fs-5);
    color: var(--t-3);
    line-height: 1.55;
}

.client-media-slots-intro strong {
    color: var(--t-2);
    font-weight: 500;
}

/* --- Die Zeile ---------------------------------------------- */
/* flex-wrap + die min-width unten: Seit der Knopf "Neues Video
   vorschlagen" heisst (176px statt 103px fuer "Austauschen"), frisst
   er bei schmalen Zeilen das Label auf - gemessen 2026-08-16: unter
   ~382px Zeilenbreite bleibt vom Namen der Stelle nichts uebrig, und
   genau der sagt dem Kunden, WAS er da aendert. Statt eines Media
   Query containerbasiert geloest: die Karte kann in jeder
   Spaltenbreite stehen, der Viewport sagt darueber nichts aus. */
.media-slot-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3);
    background: var(--s-2);
    border: 1px solid var(--line-1);
    border-radius: var(--r-m);
    margin-bottom: var(--sp-2);
}

.media-slot-row:last-child { margin-bottom: 0; }

/* --- Vorschau: feste Box, damit die Zeilen nicht tanzen ------
   object-fit: cover statt contain - ein Ausschnitt liest sich
   als Bild, ein eingepasstes Bild mit Rand liest sich als Fehler. */
.media-slot-thumb {
    width: 64px;
    height: 48px;
    flex-shrink: 0;
    border-radius: var(--r-s);
    background: var(--s-3);
    border: 1px solid var(--line-1);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.media-slot-thumb img,
.media-slot-thumb video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.media-slot-thumb .material-icons {
    font-size: 20px;
    color: var(--t-3);
}

/* --- Beschriftung -------------------------------------------- */
.media-slot-info {
    flex: 1;
    /* 144px = 36x4, bleibt auf dem 4er-Raster. Unterschreitet die
       Spalte das, bricht der Knopf in die naechste Zeile, statt das
       Label per Ellipsis wegzukuerzen. Vorher stand hier 0 - damit
       gewann immer der Knopf. */
    min-width: 144px;
}

/* Das Label darf umbrechen, die Dateizeile darunter nicht ---
   Bis 2026-08-17 stand hier dasselbe `nowrap` wie bei
   .media-slot-meta. Bei 375px hat die Spalte 154px, und
   "Produktbild Startseite" braucht 155px - es fehlte genau EIN
   Zeichen, sichtbar blieb "Produktbild Startseit…". Die Grenze
   liegt also bei rund 20 Zeichen, und Marco vergibt die Namen
   selbst: der naechste Slot kann laenger heissen.

   Warum das schwerer wiegt als bei der Zeile darunter: Das
   Label ist die EINZIGE Stelle, die sagt, welche Stelle der
   Website gemeint ist. Ein gekuerzter Dateiname ist laestig,
   ein gekuerztes Label macht die Zeile mehrdeutig - bei zwei
   Slots "Produktbild Startseite oben" und "... unten" wuerde
   der Kunde beide gleich lesen und ins falsche Feld laden.

   Deckel bei zwei Zeilen mit Absicht: Umbruch ohne Grenze
   liesse ein versehentlich langer Name die Zeile beliebig hoch
   ziehen. Darueber greift wieder eine Ellipsis.

   Kapazitaet bei 154px nachgemessen, nicht geschaetzt: rund
   **33 Zeichen** mit Leerzeichen, 39 als ein Wort. Die Grenze
   ist dabei nicht scharf, sie haengt am Umbruchpunkt - "Galerie-
   bild 3 auf der Leistungsseite" (36) passt, "Produktbild
   Startseite oben rechts" (34) nicht, weil dort ein langes Wort
   ungluecklich faellt. Statt 20 Zeichen wie vorher also grob
   die anderthalbfache Laenge, und alle heute vergebenen Namen
   passen mit Abstand.

   `overflow-wrap: anywhere` fuer den Fall, dass jemand einen
   Namen ohne Leerzeichen vergibt: sonst schoebe ein einziges
   langes Wort die Spalte auf. */
.media-slot-label {
    font-size: var(--fs-4);
    font-weight: 500;
    color: var(--t-1);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.media-slot-meta {
    font-size: var(--fs-5);
    color: var(--t-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- Die Absage: eigene Komponente, nicht die Meta-Zeile ------
   Bis 2026-08-17 lief die Ablehnungsbegruendung ueber
   .media-slot-meta - und damit gegen deren white-space:nowrap.
   Bei der 375px-Abnahme gemessen: von 103 Zeichen blieben 23
   sichtbar, exakt das Praefix "Noch nicht uebernommen: ", vom
   Grund selbst NULL. Der Kunde erfuhr also, DASS abgelehnt
   wurde, nie WARUM - auf einer Flaeche, deren ganzer Zweck die
   Rueckmeldung ist. Am Desktop faellt das nicht auf, dort passt
   die Zeile.

   white-space:normal ist der Kern: eine Begruendung ist
   Fliesstext, kein Dateiname, und darf umbrechen. Die Zeile
   waechst dadurch von 134,5px auf 235,4px - das ist der Preis
   dafuer, dass der Grund lesbar wird, und er ist es wert.

   Farbe --t-2 statt --t-3: das hier ist eine Nachricht an den
   Kunden, keine Metadatenzeile. --t-3 ist fuer Beilaeufiges.
   Kein Gold (Regel 7 haelt es fuer Primaeraktion, Aktivzustand
   und Fokus frei) und kein Rot: Rot gibt es in den Tokens
   nicht, und es erklaerte die Absage zum Stoerfall. Sie ist ein
   regulaerer Schritt im Ablauf. Der Randstrich setzt sie von
   der Dateizeile darueber ab - ohne ihn liest sich beides als
   eine zerfallene Zeile.

   Variante mit mitlaufendem Kopf ("Noch nicht uebernommen: Das
   Bild ist...") wurde am 2026-08-17 verworfen. Sie spart KEINEN
   Platz - nachgemessen exakt gleich hoch, weil der Kopf bei
   154px Spaltenbreite ohnehin umbricht. Die Entscheidung fiel
   also rein optisch: getrennter Kopf ist schneller zu fassen. */
.media-slot-absage {
    margin-top: var(--sp-1);
    padding-left: var(--sp-2);
    border-left: 2px solid var(--line-2);
    font-size: var(--fs-5);
    line-height: 1.5;
    color: var(--t-2);
    white-space: normal;
}

.media-slot-absage b {
    display: block;
    font-weight: 500;
    color: var(--t-1);
}

/* --- Aktionen ------------------------------------------------ */
.media-slot-actions {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex-shrink: 0;
    /* Haelt den Knopf rechtsbuendig - auch dann, wenn er nach dem
       Umbruch allein in der zweiten Zeile steht. */
    margin-left: auto;
}

.media-slot-replace {
    background: var(--gold);
    color: #08090c;
    border: none;
    border-radius: var(--r-m);
    padding: var(--sp-2) var(--sp-3);
    font-family: inherit;
    font-size: var(--fs-5);
    font-weight: 500;
    cursor: pointer;
}

.media-slot-replace:disabled {
    opacity: .5;
    cursor: default;
}

/* --- Die Kennung: technisch, also monospace und leise --------
   Sie steht im Master Dock, weil Marco sie in die Website
   eintraegt. Im Kundenbereich hat sie nichts zu suchen. */
.media-slot-key {
    font-family: "Space Mono", monospace;
    font-size: var(--fs-6);
    color: var(--t-3);
    background: var(--s-3);
    border: 1px solid var(--line-1);
    border-radius: var(--r-s);
    padding: 1px var(--sp-1);
}

.admin-slot-id-hint {
    color: var(--t-3);
    font-weight: 400;
}

/* Das Kennungsfeld ist eine Anzeige, kein Eingabefeld - es sieht
   auch so aus. Ohne das wirkt es wie ein Feld, das sich nur nicht
   anfassen laesst, und genau das erzeugt die Frage "warum nicht?". */
#admin-slot-id-preview {
    font-family: "Space Mono", monospace;
    color: var(--t-3);
    cursor: default;
}

/* ═══════════════════════════════════════════════════════════════════
   Nachricht an uns (Kundenkarte, 2026-09-01)
   ───────────────────────────────────────────────────────────────────
   Bewusst wenige eigene Regeln: Textfeld, Knopf und Verlaufsliste
   erben vollstaendig von .client-form-field / .btn-client-upload /
   .banana-assets-list. Neu ist nur, was es vorher nicht gab - der
   Zeichenzaehler und die Zwischenueberschrift ueber dem Verlauf.
   Alles ueber Tokens, keine abgelesenen Zahlen.
   ═══════════════════════════════════════════════════════════════════ */

/* Das Textfeld darf wachsen, aber nicht die Karte sprengen. Horizontal
   fest: eine Textarea, die der Nutzer breiter zieht, bricht das Raster. */
#client-message-text {
    width: 100%;
    resize: vertical;
    min-height: 120px;
    max-height: 420px;
    line-height: 1.5;
}

/* Zaehler rechtsbuendig unter dem Feld - dort sucht ihn das Auge, und
   er bleibt aus dem Weg, solange niemand ans Limit kommt. */
.client-message-footer {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--sp-2);
    margin-bottom: var(--sp-4);
}

#client-message-counter {
    font-size: var(--fs-5);
    color: var(--t-2);
    font-variant-numeric: tabular-nums;
}

/* Ab 90 Prozent wird der Zaehler gold - Hinweis, keine Warnung. Rot waere
   eine Fehlermeldung fuer etwas, das noch gar kein Fehler ist. */
#client-message-counter.is-nah-am-limit { color: var(--gold); }

.client-message-sublabel {
    margin-top: var(--sp-6);
    margin-bottom: var(--sp-3);
    font-size: var(--fs-5);
    color: var(--t-2);
}

/* Der Verlauf braucht KEINEN eigenen Hoehendeckel: `.banana-assets-list`
   (index.html:404) setzt bereits max-height:400px und overflow-y:auto, beides
   mit !important. Hier stand bis zur Sichtpruefung am 2026-09-01 ein eigenes
   max-height:320px - gemessen wurden trotzdem 400px, die Regel tat also nichts
   und behauptete das Gegenteil. Geblieben ist allein die waagerechte Sperre,
   die der Deckel nicht abdeckt. */
#client-messages-list {
    overflow-x: hidden;
}

/* Eine gesendete Nachricht im Verlauf. Der goldene Streifen links ist
   dasselbe Zeichen wie im ausgelieferten Fragebogen: hier steht etwas
   von dir. */
.client-message-item {
    border: 1px solid var(--line-1);
    border-left: 3px solid var(--gold);
    border-radius: var(--r-m);
    background: rgba(255,255,255,.02);
    padding: var(--sp-3) var(--sp-4);
    margin-bottom: var(--sp-3);
}

.client-message-item-kopf {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--sp-3);
    margin-bottom: var(--sp-2);
    /* Bei 375px passen Zustand und Datum nicht nebeneinander. Ohne Umbruch
       zerlegt es den Zustandstext mitten im Wort, waehrend das Datum daneben
       einzeilig stehen bleibt - gemessen 40px Kopfhoehe statt 20. Mit wrap
       rutscht das Datum sauber in die zweite Zeile. */
    flex-wrap: wrap;
}

.client-message-item-datum {
    font-size: var(--fs-5);
    color: var(--t-2);
    white-space: nowrap;
}

/* Zustand der Nachricht. "Beantwortet" ist bewusst zurueckhaltend und
   nicht gruen gefeiert - es ist der Normalfall, kein Erfolgserlebnis. */
.client-message-item-zustand {
    font-size: var(--fs-5);
    color: var(--t-2);
    /* Gehoert zum Umbruch darueber: der Zustand ist eine feste Wendung, die
       ganz bleiben muss, sonst bricht flex-wrap ihn trotzdem auf. */
    white-space: nowrap;
}
.client-message-item-zustand.ist-erledigt { color: var(--gold); }

/* Umbruch erzwingen: eine ohne Leerzeichen durchgetippte Zeile wuerde
   die Karte sonst horizontal aufreissen. */
.client-message-item-text {
    font-size: var(--fs-4);
    color: var(--t-1);
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* ============================================================
   15. REGELTREUE NACHGEZOGEN (2026-09-02)
   ------------------------------------------------------------
   Befund aus der Sitzung vom 2026-09-02, jede Stelle in der
   Quelle belegt statt im DOM gezaehlt. Alles additiv; in
   index.html wurde nichts geloescht, der Rueckbau bleibt die
   eine <link>-Zeile.
   ============================================================ */

/* --- 15.1 Scrollbalken: die Sammelregel aus Abschnitt 2 verlor
   ------------------------------------------------------------
   Abschnitt 2 setzt "Scrollbalken: neutral, nie gold" bereits -
   aber OHNE !important. Die Juli-Schicht schreibt dieselben
   Selektoren MIT !important (index.html 408, 409, 415, 467, 490,
   527, 604). Bei !important gegen nicht-!important gewinnt die
   Juli-Zeile unabhaengig von Spezifitaet und Reihenfolge; die
   v3-Regel war also seit dem 05.08. wirkungslos.

   Sichtbar war das am Goldbalken samt Leuchten in jeder
   .banana-assets-list - der letzte Regel-7-Verstoss im Portal
   und die letzte Stelle mit #fff333.

   Merksatz: Eine Ueberschreibung, die man nicht am Ergebnis
   prueft, ist eine Absichtserklaerung. !important schlaegt
   Spezifitaet, und die Juli-Schicht benutzt es reichlich. */
.banana-assets-list::-webkit-scrollbar-thumb,
.activity-feed::-webkit-scrollbar-thumb,
.admin-assets-list::-webkit-scrollbar-thumb,
.banana-card-body::-webkit-scrollbar-thumb,
.upload-file-list::-webkit-scrollbar-thumb,
#client-files-inline-list::-webkit-scrollbar-thumb,
#client-messages-list::-webkit-scrollbar-thumb {
    background: var(--line-2) !important;
    box-shadow: none !important;
    border-radius: 99px !important;
}

.banana-assets-list::-webkit-scrollbar-thumb:hover,
.activity-feed::-webkit-scrollbar-thumb:hover,
.admin-assets-list:hover::-webkit-scrollbar-thumb,
.admin-assets-list::-webkit-scrollbar-thumb:hover,
.banana-card-body::-webkit-scrollbar-thumb:hover,
#client-files-inline-list::-webkit-scrollbar-thumb:hover,
#admin-client-list:hover::-webkit-scrollbar-thumb,
#client-messages-list::-webkit-scrollbar-thumb:hover {
    background: #3a4051 !important;
    box-shadow: none !important;
}

/* --- 15.2 "Aendern" in der Stammdatenleiste: Radius 6px --------
   Abschnitt 10 hat diese Schaltflaeche bereits ueber
   "#stammdaten-status-bar .btn-status-edit" (1-1-0) eingefangen
   und Schriftgroesse, Gewicht, Laufweite und Versalien geheilt -
   den border-radius aber ausgelassen. index.html 281 setzt 6px,
   die Sammelregel in Abschnitt 2 traegt nur 0-1-0 und verliert.
   Dieselbe Spezifitaetsfalle wie damals, eine Eigenschaft
   weiter. */
#stammdaten-status-bar .btn-status-edit {
    border-radius: var(--r-m);
}

/* --- 15.3 Loeschdialog: Radius 25px und Gewicht 600 -----------
   Nur diese beiden - die Schriftgroesse stand nicht im Befund,
   siehe den Block darunter.

   index.html 641/643/685 setzt beide Knoepfe auf 25px und 600.
   Gleiche Spezifitaet wie hier, v3 laedt spaeter und gewinnt -
   ohne !important. */
.delete-modal-btn-cancel,
.delete-modal-btn-confirm {
    border-radius: var(--r-m);
    /* 13px, nicht 15px. Hier stand zuerst --fs-4 - eine
       Vergroesserung, die auf keinem Befund steht: index.html
       641/643 setzt 13px, und 13px IST --fs-5, also die Skala.
       Der Befund listete fuer diese Knoepfe Radius und Gewicht,
       nicht die Groesse. Uebernommen wird deshalb der Wert, den
       sie schon hatten - aber ueber den Token statt als Literal,
       damit die Absicht dasteht und eine spaetere Aenderung an
       der Skala die Knoepfe mitnimmt.

       Merksatz: Wer beim Heilen eines Verstosses eine zweite
       Eigenschaft mitzieht, aendert etwas, das niemand geprueft
       hat - und kann hinterher nicht sagen, ob es besser wurde. */
    font-size: var(--fs-5);
}
.delete-modal-btn-cancel  { font-weight: 500; }
.delete-modal-btn-confirm { font-weight: 700; }

/* Ueber die Befundliste hinaus, deshalb hier ausdruecklich
   vermerkt: index.html 644 legt auf den Bestaetigen-Knopf ein
   rotes Leuchten (0 0 20px). Regel 6 verlangt Tiefe durch
   Material, nicht durch Schein - fuer Rot gilt das genauso wie
   fuer Gold. Ersetzt durch den Materialschatten; die Farbe
   allein traegt die Warnung. Wenn das Leuchten gewollt war,
   ist dieser Block die eine Stelle zum Zuruecknehmen. */
.delete-modal-btn-confirm:hover:not(:disabled) {
    box-shadow: var(--e-2);
}

/* --- 15.4 Hilfe-Fenster: 17px und Gewicht 600 -----------------
   index.html 1091 setzt die Ueberschrift auf 17px/600, 1102 die
   Hervorhebungen im Fliesstext auf 600. Acht solcher <strong>
   stehen im Text. 17px liegt zwischen --fs-3 und --fs-4 und
   gehoert damit auf keine Stufe.

   Anmerkung zur Zaehlung: Das Fenster steht im Ruhezustand auf
   opacity 0. In einer DOM-Messung, die nur die eigene Deckkraft
   jedes Elements prueft, erscheinen seine Kinder trotzdem als
   sichtbar - hier lag der Zaehlfehler vom selben Tag. Die
   Verstoesse sind echt, der Kunde sieht sie beim Klick auf das
   Fragezeichen, aber sie stehen nicht auf der Ruheflaeche. */
.help-guide-head h3 {
    font-size: var(--fs-3);
    font-weight: 700;
}
.help-guide-item p strong {
    font-weight: 700;
}

/* --- 15.5 Was hier bewusst NICHT steht -----------------------
   .icon-inline-sm und .warteschlange-name .material-icons
   tragen 14px. Das sind Glyphengroessen, keine Schriftstufen -
   die Typo-Skala regelt Text, nicht Icons. Die 14px stammen aus
   dem Icon-Durchgang vom 2026-08-06 und sind dort begruendet.
   Eine Messung, die Icons als Text zaehlt, meldet hier einen
   Verstoss, der keiner ist. */


/* ============================================================
   16. DER DECKEL WAR SCHON DA (2026-09-02,
       berichtigt in der Schlusspruefung 2026-09-03)
   ------------------------------------------------------------
   Hier standen zwei Regeln, die nie gewirkt haben. Sie sind
   ersatzlos entfernt. Der Abschnitt bleibt als Beleg stehen,
   damit die "Luecke" nicht ein weiteres Mal geschlossen wird.

   Der Messwert, der zu ihnen fuehrte, stimmt weiter: eine
   Kundennachricht mit 3.332 Zeichen rendert 549px hoch, das
   Feld erlaubt 4.000 Zeichen.

   Falsch war die Schlussfolgerung, #client-messages-list sei
   als einzige Liste ungedeckelt. Das Element traegt BEIDE
   Selektoren (index.html:1483), und .banana-assets-list
   deckelt es seit dem 05.08. auf 400px - mit !important
   (index.html:404). Dagegen verliert eine ID-Regel ohne
   !important unabhaengig von Spezifitaet und Reihenfolge:
   Wichtung wird in der Kaskade VOR Spezifitaet ausgewertet.
   Weil beide denselben Wert nannten, war nichts zu sehen.

   Dasselbe fuer die 1023px-Regel: .banana-assets-list wird
   ab Zeile ~2062 im selben Medienbereich laengst mit
   !important freigegeben; die ID-Regel kam dort nie an.

   ⚠️ Das ist derselbe Fehler, den Abschnitt 15.1 am SELBEN Tag
   beschrieben und behoben hat, und derselbe, den der Kommentar
   bei Zeile ~2809 am 2026-09-01 schon einmal abgeraeumt hatte.
   Dritter Fall in einer Datei. Sein Merksatz gilt: eine
   Ueberschreibung, die man nicht am Ergebnis prueft, ist eine
   Absichtserklaerung.

   Wer hier kuenftig einen Deckel SETZEN will, schreibt ihn wie
   Abschnitt 19: mit !important und einer Spezifitaet ueber
   0-1-0. Wer ihn LOESEN will, muss index.html:404 schlagen -
   ergaenzen reicht nicht.
   ============================================================ */

/* ============================================================
   17. LEERZUSTAENDE WERDEN ZEILEN (2026-09-02)
   ------------------------------------------------------------
   Befund: Die Seite ist leer 2.443px hoch und mit drei
   Downloads, drei Medien-Slots und drei Nachrichten 2.744px.
   301px Unterschied - der Leerzustand kostet fast so viel Platz
   wie der Vollzustand. Das ist der Grund, warum die Flaeche
   unuebersichtlich wirkt, nicht die Optik.

   Ursache, gemessen statt geraten: .client-assets-empty
   (index.html 1067/1068) traegt 26px Polsterung oben und unten
   und darueber einen 60x60px grossen Iconblock mit 12px Abstand.
   Ein Leerzustand ist damit rund 140px hoch - fuer einen Satz,
   der sagt, dass nichts da ist. Bei fuenf solchen Stellen auf
   der Kundenflaeche ist das der halbe Bildschirm.

   Abschnitt 8.4 hat 2026-08-05 bereits den Goldkreis aus diesen
   Zustaenden genommen; die Geometrie blieb unberuehrt. Hier wird
   sie nachgezogen: aus dem zentrierten Block wird eine ruhige
   Zeile - Icon links, Satz daneben, linksbuendig.

   Bewusst OHNE :has() und ohne Textaenderung. .client-assets-
   empty steht ausschliesslich dann im DOM, wenn die Liste leer
   ist - eine Bedingung im Selektor ist also gar nicht noetig.
   Der Wortlaut bleibt, wie er ist; Texte gehoeren in den
   eigenen Durchgang. Gilt gleichermassen fuer das Admin-Dock,
   das dieselbe Klasse an neun Stellen benutzt. */
.client-assets-empty {
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: var(--sp-3);
    text-align: left;
    padding: var(--sp-3) var(--sp-2);
    line-height: 1.45;
    /* index.html 457 setzt min-height: 100px. Ohne diese Zeile
       greift die neue Polsterung, der Kasten bleibt aber 100px
       hoch - gemessen am 2026-09-02: 152px wurden zu 100px statt
       zu 43px. Ein Mindestmass, das man nicht mitnimmt, macht
       jede Polsterungsaenderung wirkungslos. */
    min-height: 0;
}

/* Der Iconblock war ein 60px hoher Kasten mit eigener Zeilenhoehe
   und automatischem Rand - genau der Teil, der die Hoehe machte.
   Als Glyphe in Zeilengroesse traegt er dieselbe Information. */
.client-assets-empty .material-icons,
.client-assets-empty .wb-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    line-height: 1;
    font-size: 16px;
    margin: 0;
    flex: none;
}

/* Nicht jeder Leerzustand hat ein Icon: "Noch keine Neuigkeiten"
   (index.html 1364) und die sieben Zustaende im Admin-Dock sind
   reiner Text. Ohne Ausgleich beginnen bebilderte und unbebilderte
   Zeilen an verschiedenen Kanten - in der Karte "Von uns fuer dich"
   stehen genau diese beiden Faelle direkt untereinander, der
   Versatz war 57px. Der Einzug haelt beide auf einer Fluchtlinie.

   :has() traegt hier kein Risiko: faellt es aus, steht wieder der
   Versatz von heute da, nichts bricht. */
.client-assets-empty:not(:has(> .material-icons)):not(:has(> .wb-icon)) {
    padding-left: calc(var(--sp-2) + 28px + var(--sp-3));
}

/* Die Neuigkeitenleiste zentriert ihre Leerzustaende zusaetzlich
   ueber eine eigene Regel (index.html 465). Ohne diese Zeile
   bliebe genau dort der alte, mittige Block stehen - und die
   Karte "Von uns fuer dich" haette einen aufgeraeumten und einen
   unaufgeraeumten Zustand direkt untereinander. */
.activity-feed .client-assets-empty {
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
}

/* Der 37px-Abstand zwischen Downloads und Neuigkeiten steht als
   Inline-Stil im Markup (index.html 1363) und ist damit nicht
   von hier aus erreichbar - er bleibt stehen. Im leeren Zustand
   trennt er jetzt zwei Zeilen statt zweier Bloecke, was zu viel
   ist; die 24px des Rasters reichen. Erreichbar nur ueber den
   Nachbarn, deshalb dieser Umweg. */
#client-downloads-list:has(> .client-assets-empty) + .activity-feed {
    margin-top: var(--sp-5) !important;
}


/* ============================================================
   18. DIE FESTE KARTENHOEHE FAELLT (2026-09-02)
   ------------------------------------------------------------
   Aus dem Pruefstand, Abschnitt 13. Marco hat A und C gewaehlt;
   B (align-items: start) bleibt draussen, D (Raster umordnen)
   ist widerlegt.

   Gemessen bei 1.132px Rahmenbreite, Kundenflaeche im
   Leerzustand: 2.001px -> 1.592px. Die 409px kommen
   vollstaendig aus 18.1; 18.2 aendert die Hoehe um null und ist
   eine reine Optikfrage.
   ============================================================ */

/* --- 18.1 "Bilder & Videos" war auf 640px genagelt -----------
   index.html 356 setzt .banana-card { height: 640px !important }
   auf ALLE Karten. Zurueckgenommen wird das in index.html 1057
   nur fuer ".client-cockpit-grid .banana-card" und
   ".client-cockpit-foot .banana-card". Die Medien-Karte liegt in
   keinem von beiden - sie haengt direkt in #client-gated-content -
   und behielt die feste Hoehe: 488px Luft bei 152px Inhalt, im
   Leerzustand wie im vollen, fuer jeden Kunden.

   Unterhalb 1024px war sie laengst befreit (weiter oben in dieser
   Datei, ".client-media-slots { height: auto !important }" im
   1023px-Block). Diese Regel zieht den Desktop nach; die
   Media-Query bleibt stehen, weil sie dort auch das Scrollen
   aufhebt.

   ⚠️ Der Befund vom selben Tag nannte als Ursache den
   "max-height: 400px" der Slot-Liste. Das war falsch: max-height
   begrenzt, es reserviert nicht. Gegenprobe am laufenden Portal -
   max-height aufheben aendert keinen Pixel, height aufheben
   aendert 409.

   Merksatz: Eine Obergrenze und eine feste Hoehe sehen im
   Ergebnis gleich aus - eine Karte, die sich nicht ruehrt. Nur
   die eine laesst sich unterschreiten. Wer die Ursache nicht
   gegenprueft, repariert die falsche Eigenschaft.

   Das !important ist noetig, nicht Gewohnheit: die Regel in
   index.html traegt selbst eines. */
.client-media-slots {
    height: auto !important;
}

/* --- 18.2 Leere Karten verlieren Rahmen und Fuellung ---------
   Eine Karte, die nichts enthaelt als den Satz, dass sie nichts
   enthaelt, soll nicht aussehen wie ein Behaelter. Sie behaelt
   ihre Ueberschrift und ihre Zeile, verliert aber den Rahmen,
   den Verlauf und den Schatten.

   Das spart keinen Pixel - die Karte steht weiter im Raster und
   wird von ihrer Nachbarin auf deren Hoehe gehalten. Gewonnen ist
   Ruhe, nicht Platz. Wer die Hoehe will, muss die Karte aus der
   Rasterzeile nehmen, und genau das kostet mehr, als es bringt
   (Variante D im Labor: +150px).

   ZUR BEDINGUNG: "leer" heisst hier, dass die Karte einen
   Leerzustand zeigt UND keine einzige echte Zeile enthaelt. Die
   drei Zeilenklassen kommen aus app.js:
     .client-asset-item  - ein Download          (app.js 2310)
     .timeline-ticket    - eine Neuigkeit        (app.js 2440)
     .media-slot-row     - ein Medien-Slot       (app.js 4657)

   Der erste Entwurf pruefte nur auf .client-assets-empty. Das
   war zu grob: "Von uns fuer dich" traegt ZWEI Listen -
   Downloads und Neuigkeiten. Ist nur eine davon leer, zeigt die
   Karte einen Leerzustand und trotzdem Inhalt; sie haette den
   Rahmen verloren, waehrend eine Neuigkeit darin steht.

   Merksatz: "Enthaelt einen Leerzustand" ist nicht dasselbe wie
   "ist leer". Bei zwei Listen in einer Karte faellt der
   Unterschied auseinander.

   Die beiden letzten Ausschluesse halten die Karten fern, die nie
   leer sein koennen: "Dateien an uns senden" traegt immer die
   Uploadzone, "Nachricht an uns" immer das Eingabefeld.

   #client-gated-content grenzt auf die Kundenflaeche ein. Das
   Admin-Dock benutzt .admin-card und waere ohnehin nicht
   getroffen - die Eingrenzung sagt es trotzdem, damit ein
   spaeterer Umbau dort nicht versehentlich hier landet.

   Das !important ist noetig: die Sammelregel fuer .banana-card
   weiter oben in dieser Datei traegt selbst eines. */
#client-gated-content .banana-card:has(.client-assets-empty):not(:has(.client-asset-item)):not(:has(.timeline-ticket)):not(:has(.media-slot-row)):not(:has(textarea)):not(:has(.banana-upload-zone)) {
    background: none !important;
    border-color: transparent !important;
    box-shadow: none !important;
}
#client-gated-content .banana-card:has(.client-assets-empty):not(:has(.client-asset-item)):not(:has(.timeline-ticket)):not(:has(.media-slot-row)):not(:has(textarea)):not(:has(.banana-upload-zone)) > .banana-card-header {
    /* Die Trennlinie BLEIBT stehen - sie sortiert Ueberschrift
       gegen Inhalt, und bei einer Karte ohne Rahmen tut sie das
       sogar noetiger als bei einer mit. Hier stand zuerst
       border-bottom-color: transparent; das nahm die Struktur
       mit, die man behalten will.

       Weg muss stattdessen die Lichtkante darueber:
       box-shadow rgba(255,230,0,.14) 0 -1px 0 inset, ein
       goldener Reflex an der Unterkante des Kartenkopfs. Auf der
       gerahmten Karte ist der Material - Regel 6, Licht auf einer
       echten Kante. Auf der entkleideten ist es ein Reflex auf
       einer Kante, die es nicht mehr gibt, und damit schlichtes
       Deko-Gold: Regel 7. Es war die letzte goldene Linie, die
       dort noch stand.

       Gefunden erst am Bild, nicht an der Messung. Der Wert stand
       die ganze Zeit im computed style - dass er stoert, sieht
       man erst, wenn der Rahmen drumherum fehlt.

       Merksatz: Nimmt man einem Bauteil seine Kanten, bleiben die
       Lichter zurueck, die zu ihnen gehoerten. Wer nur nach Rahmen
       und Fuellung sucht, uebersieht sie. */
    box-shadow: none !important;
}

/* Faellt :has() aus, steht wieder die Karte von heute da - mit
   Rahmen, aber ohne Schaden. Das war die Bedingung dafuer, das
   hier ueberhaupt in CSS zu loesen, statt app.js eine Klasse
   setzen zu lassen: der Rueckbau muss die eine <link>-Zeile
   bleiben. */

/* ═══════════════════════════════════════════════════════════════════
   Verlauf eines Vorgangs (Rueckkanal-Dialog, 2026-09-02)
   ───────────────────────────────────────────────────────────────────
   zeichneVerlauf() (app.js) bedient Kunden- und Admin-Seite mit
   denselben Klassen - was hier steht, gilt auf beiden Seiten gleich.

   Kein eigener Hoehendeckel: den setzt der umgebende Bereich.
   Kundenseite (.banana-assets-list, 400px) ist am 2026-09-03
   entschieden - Abschnitt 19 laesst ihn fallen, sobald ein Verlauf
   offen ist. Master Dock (.admin-assets-list, 280px, index.html:602)
   ist seit dem 2026-09-03 ebenfalls entschieden, aber ANDERS: dort
   faellt der Deckel ganz, siehe Abschnitt 21. Der Satz, der hier
   stand ("bewusst offen; vor der Abnahme des Docks zu messen"),
   hat sich bei genau dieser Abnahme eingeloest - Marco kam an sein
   eigenes Antwortfeld nicht heran, weil es mit im 280px-Ausschnitt
   lag.

   Eingabefeld und Fokusring haengen bewusst NICHT hier, sondern an
   den bestehenden Sammelregeln fuer Eingabefelder (Abschnitt 1, ab
   Zeile ~281) - .nachricht-antwort-feld ist dort mit aufgenommen,
   damit es exakt wie jedes andere Feld im Portal aussieht, statt
   eine zweite, leicht abweichende Kopie zu pflegen. Ebenso
   .nachricht-beitrag-zeit in der Metadaten-Sammelregel (Zeile ~725).

   Kein Gold auf dem Beitrag selbst (Regel 7): ein Listeneintrag im
   Verlauf ist weder Primaeraktion noch Aktivzustand noch Fokus. Der
   Antwort-Knopf bekommt sein Gold regulaer ueber .btn-client-upload
   (app.js setzt beide Klassen zusammen). */

.nachricht-verlauf {
    margin-top: var(--sp-3);
}

.nachricht-verlauf-liste {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.nachricht-beitrag {
    border: 1px solid var(--line-1);
    border-radius: var(--r-m);
    background: rgba(255,255,255,.02);
    padding: var(--sp-3) var(--sp-4);
}

/* Der eigene Beitrag hebt sich ab - nachgetragen in der Schlusspruefung
   2026-09-03. Bis dahin setzte app.js `.ist-eigener` und `.ist-fremder`,
   ohne dass es dazu irgendeine Regel gab: eigener und fremder Beitrag
   sahen identisch aus, und in einem Dialog ist genau das die eine
   Unterscheidung, auf die es ankommt. Die Absenderzeile allein trug sie
   nicht - sie ist bei beiden --t-2/500.

   Tiefe durch Material (Regel 6), nicht durch Farbe: der eigene Beitrag
   liegt eine Stufe hoeher und traegt die betonte Kante. Kein Gold
   (Regel 7) - ein Absender ist weder Primaeraktion noch Aktivzustand
   noch Fokus.

   Bewusst weiter rgba statt eines --s-Tokens, wie bei .nachricht-beitrag
   darueber: zeichneVerlauf bedient Kunden- und Adminseite, und die
   Untergruende dort sind verschieden. Eine Aufhellung passt sich an,
   ein fester Wert wuerde auf einer der beiden Seiten falsch sitzen.

   ⚠️ ABGELOEST am 2026-09-15: Hier stand "Bewusst OHNE Einrueckung",
   begruendet mit Marcos Satz vom 2026-09-03 ("das ist doch gar nicht
   buendig"). Mit dem Livechat hat Marco sich ausdruecklich fuer
   Sprechblasen links/rechts entschieden (Entwurf
   docs/superpowers/specs/2026-09-15-livechat-dock-design.md). Die Regeln
   dieses Abschnitts zeichnet im Produkt niemand mehr, sie bleiben fuer die
   Labor-Abschnitte 14 bis 16 stehen. Der Chat steht in Abschnitt 25.

   `.ist-fremder` bekommt hier bewusst KEINE Regel und wird von app.js
   auch nicht mehr gesetzt - der fremde Beitrag IST der Grundzustand
   darueber. Eine zweite Klasse, die nur wiederholt, was schon gilt,
   waere die zahme Variante desselben Fehlers, um den es in diesem
   Abschnitt geht. */
.nachricht-beitrag.ist-eigener {
    background: rgba(255,255,255,.05);
    border-color: var(--line-2);
}

.nachricht-beitrag-absender {
    font-size: var(--fs-5);
    font-weight: 500;
    color: var(--t-2);
    margin-bottom: var(--sp-1);
}

/* Wortlaut ist der eigentliche Inhalt des Eintrags - gleiche Rolle
   wie .client-asset-title-line (Abschnitt 1, Zeile ~747), deshalb
   dieselben Werte: volle Textfarbe statt gedaempft. */
.nachricht-beitrag-text {
    font-size: var(--fs-4);
    font-weight: 400;
    color: var(--t-1);
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.nachricht-antwort-box {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    margin-top: var(--sp-4);
}

/* Nur das Layout hier - Optik (Hintergrund, Rahmen, Fokusring) kommt
   aus der Sammelregel fuer Eingabefelder, siehe Kopfkommentar oben. */
.nachricht-antwort-feld {
    width: 100%;
    resize: vertical;
    min-height: 80px;
}

.nachricht-antwort-knopf {
    align-self: flex-end;
}


/* ============================================================
   19. DER DECKEL FAELLT, WENN EIN VERLAUF OFFEN IST (2026-09-03)
   ------------------------------------------------------------
   #client-messages-list ist seit dem 05.08. auf 400px gedeckelt,
   ueber .banana-assets-list (index.html:404) - siehe Abschnitt 16.
   Der Rueckkanal-Dialog macht daraus ein Problem: gemessen im
   Design-Labor (Sektion 14, 1280px) ergeben drei Vorgaenge mit je
   drei Beitraegen 2.382px Inhalt bei 400px sichtbarer Hoehe.
   1.982px lagen unsichtbar darunter - `scrollbar-width: none`
   (index.html:404) zeigt davon NICHTS an. Das ist derselbe Fehler,
   den "Ein Scrollkontext statt drei" (Zeile ~2021) am 2026-08-17
   fuer die Medien-Slots beschrieben hat, nur eine Karte weiter.

   Zwei Fassungen standen zur Wahl. Die verworfene: den Verlauf in
   ein Overlay legen. Sie loest es nicht - am Pruefstand blieben
   139px der Liste weiterhin verdeckt, und das Overlay bringt bei
   84vh einen ZWEITEN Scrollbereich mit, dazu Fokusfang, Escape und
   aria-modal. Ein zweiter Scrollkontext ist genau das, was der
   Abschnitt von 2026-08-17 abgeschafft hat.

   Gewaehlt: der Verlauf klappt auf, und solange einer offen ist,
   faellt der Deckel ganz. Zugeklappt bleibt die Liste kurz wie
   bisher, aufgeklappt ist NICHTS verdeckt - kein Rest, der sich
   still versteckt. Das ist der "Aufklapper pro Eintrag", den der
   Kopf von Abschnitt 16 als "braucht app.js" ausdruecklich
   vertagt hat; app.js setzt die Klasse in renderClientMessages.

   Warum !important, und warum ID MIT Klasse: der Deckel, der hier
   fallen soll, kommt aus index.html:404 und traegt selbst
   !important. Eine Regel ohne !important verliert dagegen
   unabhaengig von Spezifitaet - genau so sind die beiden toten
   Regeln in Abschnitt 16 entstanden (dort in der Schlusspruefung
   am 2026-09-03 entfernt). `#client-messages-list.hat-offenen-
   verlauf` steht mit 1-1-0 ueber `.banana-assets-list` (0-1-0)
   und schlaegt damit index.html:404 ebenso wie die 1023px-
   Freigabe bei Zeile ~2062.
   ============================================================ */

#client-messages-list.hat-offenen-verlauf {
    max-height: none !important;
    overflow-y: visible !important;
}

/* [hidden] allein reicht nicht ueberall: sobald irgendeine Regel
   dem Element ein display gaebe, gewaenne sie. Hier explizit, damit
   der zugeklappte Zustand nicht von einer spaeteren Regel
   aufgehebelt wird. */
.nachricht-verlauf[hidden] {
    display: none;
}

/* ============================================================
   20. ZWEI KNOEPFE, DIE SICH NICHT VERSTECKEN LIESSEN
       (Marco am 2026-09-03 am Preview-Channel)
   ------------------------------------------------------------
   Marco als Testkunde angemeldet: "warum hat ein kunde einen
   papierkorb neben seinem namen? der kunde kann sich nicht selbst
   loeschen!" Er hatte recht - und app.js tat bereits das Richtige.

   `[hidden]` ist eine Regel des User-Agent-Stylesheets. JEDE
   Autorenregel mit `display` schlaegt sie, unabhaengig von
   Spezifitaet und Reihenfolge. index.html:857 gibt
   `.client-delete-btn` ein `display: flex` - damit lief
   `deleteBtn.hidden = true` (app.js:1024) ins Leere. Am
   Preview-Channel gemessen: Attribut gesetzt, computed display
   weiterhin `flex`.

   ⚠️ Der Reset in app.js:1024 stammt vom 2026-08-08 und wurde
   eingebaut, WEIL Marco den Knopf damals schon einmal beim Kunden
   sah. Er hat also einen Monat lang nichts bewirkt und das
   Gegenteil behauptet. Derselbe Fehler wie in Abschnitt 16, nur
   umgekehrt herum: dort eine Regel ohne Wirkung, hier ein
   Attribut ohne Wirkung. Der Merksatz aus Abschnitt 15.1 deckt
   beide: eine Ueberschreibung, die man nicht am Ergebnis prueft,
   ist eine Absichtserklaerung.

   Ein systematischer Durchlauf ueber alle `[hidden]`-Elemente der
   geladenen Seite fand GENAU ZWEI Faelle - beide hier. Der zweite
   ist `#btn-help-guide` (.btn-help-circle): den blendet
   `setHelpGuideVisible(false)` im Master Dock aus, wo die
   Kunden-Hilfe nichts zu suchen hat. Auch das wirkte nicht.

   Hier statt in index.html, weil der Rueckbau der Optik die eine
   <link>-Zeile bleiben soll (CLAUDE.md). Spezifitaet 0-2-0 gegen
   0-1-0 - kein !important noetig.
   ============================================================ */
.client-delete-btn[hidden],
.btn-help-circle[hidden],
.btn-admin-bauplatz[hidden] {
    display: none;
}

/* Master Dock: "Bauplatz oeffnen" in der Kopfzeile neben Name und Status
   (2026-09-16). Optik von .btn-header (Abschnitt oben: Flaeche s-2, Rahmen,
   Radius m) - nur die Innenabstaende fehlen dort, weil index.html der
   Grundklasse padding: 0 gibt (fuer die randlosen Kopfzeilen-Links rechts).
   [hidden] steht in der Liste direkt darueber: .btn-header traegt
   display: flex, das schlaegt das Attribut sonst (siehe Kommentar dort).

   Nachgezogen am selben Abend nach Marcos Screenshot: Der Knopf sah neben
   "Livechat" fremd aus - Space Mono statt General Sans (nackter Textknoten,
   Loesung im Markup, siehe index.html), 32 statt 42 px hoch (kleines Icon,
   halber Innenabstand), weiss statt t-2. Die Werte stammen jetzt 1:1 aus
   `.header-left .livechat-knopf` (Abschnitt 25) - beide Knoepfe stehen im
   Dock nebeneinander und muessen gleich aussehen. margin-left entfaellt:
   .header-left hat selbst gap 8 px, Livechat traegt ebenfalls keinen. */
.btn-admin-bauplatz {
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    border-bottom: 1px solid var(--line-2);
    color: var(--t-2);
    white-space: nowrap;
}

.btn-admin-bauplatz:hover { color: var(--t-1); }

/* Text nur, wo er Platz hat. Gemessen in der nachgestellten Dock-Kopfzeile
   (Kunde "Foliendesign Matthias Koellner", "Noch nie online", Livechat,
   Etikett): mit Text ragte die Symbolgruppe rechts bei 1024 px 104 px ueber
   den Kopf, bei 1200 px passte es auf 0 px genau, bei 1280 px blieben 23 px.
   Laengere Woerter im Firmennamen verbreitern die linke Gruppe weiter -
   deshalb erst ab 1440 px mit Text. Darunter nur das Symbol, der Name steht
   im title (Maus) und im aria-label (Screenreader). Der Kopf waechst ab
   1500 px nicht mehr (max-width), was bei 1919 px passt, passt also ueberall
   darueber. */
@media (max-width: 1439px) {
    .btn-admin-bauplatz-text { display: none; }
}

@media (pointer: coarse) {
    .btn-admin-bauplatz { min-height: 44px; }
}

/* Der Ausloeser ist eine Listenzeile, keine Aktion: Bauart und
   Werte von .nachricht-beitrag (oben) uebernommen, damit er sich in
   den Vorgang einreiht statt ihn zu ueberstimmen. Kein Gold
   (Regel 7) - Aufklappen ist weder Primaeraktion noch
   Aktivzustand. Der Fokusring kommt aus Abschnitt 5 und braucht
   hier nichts. */
.nachricht-verlauf-ausloeser {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    width: 100%;
    margin-top: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--line-1);
    border-radius: var(--r-m);
    background: rgba(255,255,255,.02);
    color: var(--t-2);
    font-size: var(--fs-4);
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: border-color .2s ease, color .2s ease;
}

.nachricht-verlauf-ausloeser:hover {
    border-color: var(--line-2);
    color: var(--t-1);
}

/* Aufgeklappt ist ein Aktivzustand - dort darf Gold stehen
   (Regel 7). Nur die Kante, nicht die Flaeche: die Zeile bleibt
   ein Listeneintrag und soll nicht zur Primaeraktion werden. */
.nachricht-verlauf-ausloeser[aria-expanded="true"] {
    border-color: var(--gold);
    color: var(--t-1);
}

/* ============================================================
   21. DER DECKEL IM MASTER DOCK FAELLT GANZ
       (Marco bei der Abnahme des Rueckkanal-Dialogs, 2026-09-03)
   ------------------------------------------------------------
   Marco kam im Dock nicht an sein eigenes Antwortfeld und musste
   blind scrollen. #admin-nachrichten-list erbt .admin-assets-list
   (index.html:602) mit max-height:280px UND scrollbar-width:none;
   der Verlauf hing dort - anders als auf der Kundenseite - ohne
   Aufklapper dauerhaft offen, das Antwortfeld mit drin.

   Der Kopf von Abschnitt 19 hatte diesen Fall ausdruecklich
   offengelassen ("Master Dock ist NICHT entschieden ... vor der
   Abnahme des Docks zu messen"). Gemessen wurde er jetzt, im
   Pruefstand, Sektion 15 - zwei Vorgaenge a drei Beitraegen bei
   1280px:

     Ist-Zustand      2.296px Inhalt, 280px sichtbar
                      -> 2.016px verdeckt (88%), BEIDE
                         Antwortfelder unter der Kante.
     Aufklapper, zu     426px  -> 146px verdeckt
     Aufklapper, offen 1.457px -> 0px verdeckt

   Entschieden hat Marco zwei Dinge auf einmal:

   (1) Aufklapper wie auf der Kundenseite. Steht in app.js,
       renderAdminNachrichten - gleiche Bauart wie Aufgabe 9.

   (2) Der Deckel faellt hier GANZ, nicht nur bei offenem Verlauf
       wie in Abschnitt 19. Grund: zugeklappt braucht ein Vorgang
       213px, fuenf also rund 1.065px - jeder feste Deckel, auch
       ein hoeherer, verdeckte den Rest wieder lautlos. Ein
       Scrollkontext statt zwei, dasselbe Prinzip wie am
       2026-08-17. Die Dock-Spalte scrollt ohnehin.

   Nur diese eine Liste, nicht .admin-assets-list als Ganzes: die
   Slot- und Datei-Listen daneben behalten ihren Deckel, sie
   bestehen aus gleich hohen Zeilen und verdecken nichts
   Bedienbares.

   Kein !important noetig - anders als bei #client-messages-list
   in Abschnitt 19, wo index.html:404 selbst eins traegt.
   .admin-assets-list traegt keins, und eine ID (1-0-0) schlaegt
   eine Klasse (0-1-0). Nachgemessen, nicht angenommen.
   ============================================================ */

#admin-nachrichten-list {
    max-height: none;
    overflow-y: visible;
}

/* ============================================================
   22. DER SCHWEBENDE HILFE-KNOPF VERLAESST DAS HANDY
       (2026-09-03)

   Marco meldete, dass "Hilfe & Kontakt" auf dem Handy Text
   verdeckt. Am Pruefstand bei 375x812 mit angemeldetem
   Testkunden nachgemessen, nicht geschaetzt: Der Knopf ist
   115 x 35 px, sitzt fest bei unten 25 / rechts 25 und traf
   beim Durchscrollen der Seite unter anderem den Absenden-
   Knopf der Upload-Karte (#btn-client-upload, Ueberlappung
   nachgerechnet), die Absage-Begruendung an Bildstelle 4, die
   Nachrichtenkarte und mehrere Kartenueberschriften. Auf einer
   Flaeche von 375 px Breite ist jede feste Ecke irgendwann die
   falsche.

   Drei Auswege standen als Bildvergleich nebeneinander:
   nur ein Symbol (44 x 44 = 1.936 statt 4.026 px2 - halbiert
   das Problem, loest es nicht), eine Leiste ueber die volle
   Breite (375 x 48 = 18.000 px2 - mehr Flaeche, dafuer immer
   dieselbe Stelle, und sie verdeckt mitten im Scrollverlauf
   genauso) und Ausblenden beim Scrollen (frei nur in
   Bewegung, im Stillstand unveraendert). Keiner davon bringt
   die Verdeckung auf null.

   Also der vierte Weg, von Marco gewaehlt: auf dem Handy faellt
   der Knopf weg. Er oeffnet ein Fenster mit genau einer Zeile -
   der Support-Adresse (app.js: openSupportModal). Fuer diesen
   Inhalt schwebt er dauerhaft ueber dem Portal. Der Weg dorthin
   steht seit heute im Fuss des Wegweisers, den die Kopfzeile auf
   jedem Geraet oeffnet (#btn-help-guide-contact). In die
   Kopfzeile selbst passt er nicht: die vier Knoepfe dort
   brauchen bei 375px bereits 331 von 335 px (Abschnitt oben,
   @media max-width 599px).

   Breakpoint 599px, nicht die 480px aus dem <style>-Block von
   index.html: 599 ist der Handy-Schnitt dieser Datei, und der
   Kopfzeilen-Umbau, auf den die Begruendung sich stuetzt, haengt
   an genau diesem Wert. Die 480er-Regel dort bleibt unangetastet -
   sie regelt Polsterung und Abstand des Knopfes und laeuft
   unterhalb von 599 ins Leere, weil das Element dann nicht mehr
   angezeigt wird. Diese Datei wird nach dem <style>-Block geladen
   (index.html:1116), das !important hier schlaegt das dortige.

   Auf dem Desktop bleibt alles, wie es war.

   ------------------------------------------------------------
   NACHTRAG 2026-09-04: Der Knopf verlaesst auch den Desktop.

   Der Satz darueber hat nicht gehalten. Marco meldete dieselbe
   Verdeckung bei 1280x900, ueber "Neues Bild vorschlagen". Am
   Pruefstand nachgemessen: der Knopf sitzt bei 1280 px Breite
   fest auf x 1126-1245, y 840-875 und traegt z-index 999999 -
   er liegt damit ueber allem, auch ueber dem Wegweiser-Dialog
   (z-index 10000). Der Inhaltsbereich reicht bei dieser Breite
   bis x 1250, die Ecke ist also bespielte Flaeche und keine
   leere Marge. Groessere Flaeche heisst nur seltener getroffen,
   nicht nie.

   Der Grund von gestern galt auf dem Desktop unveraendert: Der
   Knopf oeffnet ein Fenster mit einer Zeile, und derselbe Weg
   steht im Fuss des Wegweisers, den die Kopfzeile auf JEDEM
   Geraet oeffnet. Ein zweiter Weg zum selben Ziel, der Inhalt
   verdeckt, rechtfertigt seinen Dauerplatz nicht - das war schon
   die Lehre von gestern, sie wurde nur auf das Handy begrenzt.

   Statt die Ausblendung auf alle Breiten auszudehnen, ist das
   Element ganz entfernt (index.html, app.js, die Farbregeln in
   Abschnitt 7 dieser Datei). Ein dauerhaft verstecktes Element
   im Markup ist Ballast, den der naechste Leser erst wieder
   verstehen muss. openSupportModal bleibt: Anmeldeseite und
   Wegweiser rufen es weiter auf.
   ============================================================ */

/* Der Kontaktweg im Fuss des Wegweisers. Aussehen wie der
   Schweber im eingeloggten Portal: dunkle Flaeche, neutraler
   Text, Gold erst beim Beruehren - Regel 7, er ist ein Nebenweg
   und keine Primaeraktion. */
.help-guide-contact-btn {
    margin-left: auto;
    flex-shrink: 0;
    background: var(--s-3);
    color: var(--t-1);
    border: 1px solid var(--line-2);
    border-radius: var(--r-m);
    padding: var(--sp-2) var(--sp-4);
    font-family: inherit;
    font-size: var(--fs-4);
    font-weight: 500;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease, background .15s ease;
}

.help-guide-contact-btn:hover {
    background: #262a34;
    color: var(--gold);
}

.help-guide-contact-btn:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* Unter 599px steht der Knopf auf eigener Zeile: Text und Knopf
   nebeneinander lassen bei 375px weniger als 120px fuer den
   Satz uebrig. */
@media (max-width: 599px) {
    .help-guide-footer {
        flex-wrap: wrap;
    }
    /* Ohne diese Zeile schiebt der Umbruch den Textblock unter das
       Headset-Symbol, statt ihn schrumpfen zu lassen: flex-basis auto
       plus min-width auto heisst, der Block beansprucht seine
       Inhaltsbreite und wandert in die naechste Zeile, sobald die
       nicht mehr passt. Am Pruefstand bei 375px gesehen, nicht
       hergeleitet - dieselbe Mechanik wie beim Firmennamen in der
       Kopfzeile am selben Tag.
       Die Basis muss 0 sein, nicht auto: mit 'flex: 1 1 auto'
       beansprucht der Block weiter seine Inhaltsbreite (gemessen
       254px bei 258px Platz) und bricht um, statt zu schrumpfen -
       bei flex-wrap geht der Umbruch dem Schrumpfen vor. Erster
       Versuch mit 'auto' war wirkungslos und am Bild nachgeprueft. */
    .help-guide-footer-text {
        flex: 1 1 0;
        min-width: 0;
    }
    .help-guide-contact-btn {
        margin-left: 0;
        width: 100%;
    }
}

/* ============================================================
   23. ONLINE-AMPEL UND LESEBESTAETIGUNG (2026-09-12)
   ------------------------------------------------------------
   Punkt vor dem Firmennamen im Master Dock, Statuszeile in der
   Kopfzeile, "Gelesen" am Vorgang, "Neue Antwort" beim Kunden.
   Inaktiv ist ein grauer Ring, nicht Rot: Rot gehoert den
   Loeschaktionen. Der Kreis ist die Form des Elements, keine
   vierte Radiusstufe - dieselbe Begruendung wie beim
   .update-badge in Abschnitt 15. Gewaehlt von Marco im
   Design-Labor, Abschnitt 16.
   ============================================================ */

.praesenz-punkt {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-right: var(--sp-2);
    vertical-align: middle;
    background: transparent;
    border: 1px solid var(--t-3);
    flex-shrink: 0;
}

.praesenz-status {
    font-family: var(--font-body);
    font-size: var(--fs-5);
    font-weight: 400;
    color: var(--t-3);
    margin-left: var(--sp-3);
}

.nachricht-gelesen {
    font-size: var(--fs-5);
    color: var(--t-3);
    margin-left: var(--sp-2);
}

/* Die Farbe bleibt Gold aus .ist-erledigt - "Neue Antwort" ist
   derselbe Zustand, nur noch nicht gesehen. Das Gewicht traegt
   den Unterschied, keine weitere Farbe. */
.client-message-item-zustand.ist-ungelesen { font-weight: 500; }

.praesenz-punkt.ist-aktiv  { background: var(--online); border-color: var(--online); }
.praesenz-status.ist-aktiv { color: var(--online); }

/* ============================================================
   24. MASTER DOCK MIT REITERN (2026-09-15)
   ------------------------------------------------------------
   Drei Reiter statt acht Kaesten untereinander: Kunde, Website,
   Dateien. Gold nur auf dem aktiven Reiter (Regel 7, Aktivzustand),
   als Unterstrich, nicht als Flaeche. Der Zaehler ist weiss auf
   dunkel wie ein Abzeichen, nicht gold - er ist weder Aktion noch
   Aktivzustand. Entschieden von Marco an Mockups (Variante A).
   ============================================================ */

.dock-reiter {
    display: flex;
    gap: var(--sp-1);
    margin-bottom: var(--sp-4);
    border-bottom: 1px solid var(--line-1);
    overflow-x: auto;
    scrollbar-width: none;
}

.dock-reiter-knopf {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 44px;
    padding: var(--sp-2) var(--sp-4);
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    font-family: var(--font-body);
    font-size: var(--fs-4);
    font-weight: 500;
    color: var(--t-3);
    white-space: nowrap;
    cursor: pointer;
}

.dock-reiter-knopf:hover { color: var(--t-1); }

.dock-reiter-knopf.is-aktiv {
    color: var(--t-1);
    box-shadow: inset 0 -2px 0 var(--gold);
}

.dock-reiter-knopf:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
}

.dock-reiter-knopf[hidden],
.dock-reiter-inhalt[hidden],
.dock-reiter-zaehler[hidden] { display: none; }

.dock-reiter-zaehler,
.livechat-zaehler {
    display: inline-block;
    min-width: 20px;
    height: 20px;
    padding: 0 var(--sp-1);
    border-radius: var(--r-m);
    background: var(--t-1);
    color: var(--s-0);
    font-size: var(--fs-6);
    font-weight: 700;
    line-height: 20px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------
   24.1 Reiter "Kunde" kompakt (2026-09-16)
   ------------------------------------------------------------
   Abnahme Punkt 1: Marco musste im Reiter Kunde zu viel scrollen
   (1.204 px bei 1.077 px Reiterbreite). An Skizzen und im Labor
   (Abschnitt 19) gewaehlt: Variante C - Stammdaten ueber die volle
   Breite vierspaltig, Notizen und Benachrichtigung darunter
   nebeneinander, die zwei Notizfelder ebenfalls nebeneinander.
   Ergebnis 738 px. Spalte 4 ist breiter, weil dort die Login-E-Mail
   steht (272 px Text passten in eine gleich breite Spalte nicht).

   Container- statt Media-Query: Wie breit der Reiter ist, haengt
   an der Kundenliste daneben, nicht allein am Fenster. Drei Stufen,
   im Labor bei 620/800/959/1000/1077 px nachgemessen:
   - ab 1.000 px wie oben (vierspaltig, Notizfelder nebeneinander)
   - 720 bis 999 px Aufbau wie C, Stammdaten aber zweispaltig; vier
     Spalten liessen dort Firmenname und Login-E-Mail ueberlaufen
   - darunter unveraendert das bisherige Raster

   Hintergrund der Einspaltigkeit (Fehler aus Aufgabe 7): Seit die
   Stammdaten allein in ihrer Spalte stehen, sind sie deren LETZTE
   Karte. index.html ".cockpit-col > .admin-card:last-child
   .admin-card-body" setzt dann display:flex (0-4-0) und schlaegt
   das Zwei-Spalten-Raster des Lese-Modus (0-3-0). Unter 720 px
   bleibt das bewusst so - zwei Spalten liefen dort ueber.
   ------------------------------------------------------------ */

.dock-kunde-breit { container-type: inline-size; }

@container (min-width: 720px) {
    .dock-kunde-breit > .cockpit-grid { grid-template-columns: minmax(0, 1fr); }

    .dock-kunde-breit > .cockpit-grid > .cockpit-col:last-child {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }

    .dock-kunde-breit .stammdaten-card.locked .admin-card-body {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

@container (min-width: 1000px) {
    .dock-kunde-breit .stammdaten-card.locked .admin-card-body {
        grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1.4fr);
    }

    .dock-kunde-breit > .cockpit-grid > .cockpit-col:last-child > .admin-card:first-child .admin-card-body {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: var(--sp-3);
    }
}


/* ============================================================
   24.2 Stammdaten: Verwerfen und Speichern im Kartenkopf (2026-09-16)
   ------------------------------------------------------------
   Labor 20, Variante A (von Marco gewaehlt). Vorher stand hier im
   Bearbeiten-Modus nur "Fertig", das nichts speicherte.
   Regel 7: Gold nur auf der Primaeraktion Speichern. Die Basis in
   index.html faerbt im Bearbeiten-Modus JEDEN .admin-edit-toggle gold,
   Verwerfen wird deshalb ausdruecklich neutral gesetzt.
   ============================================================ */
/* [hidden] gegen display:inline-flex der Knopfregeln durchsetzen */
.admin-edit-toggle[hidden],
.stamm-aktionen[hidden] { display: none !important; }

.stamm-aktionen {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
}
.stamm-aktionen .admin-edit-toggle { margin-left: 0; white-space: nowrap; }
/* Die Kartenkopf-Regel faerbt jedes Symbol gold. Hier folgt das Symbol der
   Knopfschrift: Verwerfen neutral, Speichern gold, gesperrt grau. */
.stamm-aktionen .admin-edit-toggle .material-icons { color: inherit; }

.stammdaten-card:not(.locked) .admin-edit-toggle.stamm-verwerfen {
    color: var(--t-1);
    border-color: var(--line-2);
}
.stammdaten-card:not(.locked) .admin-edit-toggle.stamm-speichern {
    color: var(--gold);
    border-color: rgba(255,230,0,.4);
}
.stammdaten-card:not(.locked) .admin-edit-toggle.stamm-speichern:disabled {
    color: var(--t-3);
    border-color: var(--line-1);
    cursor: not-allowed;
}

/* Schmaler Reiter (Handy): nur die Symbole. Gemessen wird der Container
   .dock-kunde-breit, also der ganze Reiter - die Stammdatenkarte steht seit
   24.1 ueber die volle Breite, Reiter- und Kartenbreite fallen zusammen.
   Name fuer Screenreader und Tooltip: aria-label/title im Markup. */
@container (max-width: 479px) {
    .stamm-aktionen .edit-label { display: none; }
}


/* ============================================================
   25. LIVECHAT (2026-09-15)
   ------------------------------------------------------------
   Eine Seitenleiste fuer Kundenportal und Master Dock, geoeffnet
   ueber den Knopf in der Kopfzeile (Portal: rechts; Dock: neben der
   Praesenzanzeige des Kunden). Kein schwebender Knopf: den
   schwebenden Hilfe-Knopf hat Abschnitt 22 aus demselben Grund
   entfernt (verdeckte am Handy Text).

   Sprechblasen: eigene rechts eine Stufe hoeher mit betonter
   Lichtkante (Regel 6), fremde links auf der Kartenflaeche mit
   Kante. Kein Gold in Blasen - nur der Sendeknopf ist gold
   (Primaeraktion). Die abgeflachte Ecke an der Absenderseite ist
   --r-s, keine vierte Radiusstufe.

   Unter 768px: Vollbild wie ein Messenger, Zurueck-Pfeil statt X,
   Knopf in der Kopfzeile nur mit Symbol und Zaehler.
   ============================================================ */

.livechat-knopf {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
}

.livechat-knopf[aria-expanded="true"] {
    color: var(--t-1);
    box-shadow: var(--lip-strong), inset 0 -2px 0 var(--gold);
}

/* Im Dock steht der Knopf in .header-left neben #admin-praesenz-status
   (Entwurf 1.2, app.js livechatKnopfPlatzieren). Die Regeln fuer
   `.header-right .btn-header` greifen dort nicht - dieselben Werte
   deshalb hier, damit der Knopf an beiden Plaetzen gleich aussieht. */
.header-left .livechat-knopf {
    padding: var(--sp-2) var(--sp-3);
    border-bottom: 1px solid var(--line-2);
    color: var(--t-2);
}

.header-left .livechat-knopf:hover { color: var(--t-1); }

/* ⚠️ Fund Review 2026-09-15: dieselbe Spezifitaet (zwei Klassen) wie die
   Regel oben, aber quellenspaeter - ueberschrieb die Farbe des offenen
   Knopfs im Dock zurueck auf Grau (--t-2). Eine Regel mit einem Selektor
   mehr gewinnt zuverlaessig, unabhaengig von der Reihenfolge. Der Gold-
   Unterstrich (box-shadow) kommt weiterhin aus der Regel oben - dort
   greift kein Konflikt, weil .header-left .livechat-knopf keinen eigenen
   box-shadow setzt. */
.header-left .livechat-knopf[aria-expanded="true"] { color: var(--t-1); }

@media (pointer: coarse) {
    .header-left .livechat-knopf { min-height: 44px; }
}

.livechat-knopf[hidden],
.livechat-zaehler[hidden],
.livechat[hidden],
.livechat-scrim[hidden],
.livechat-hinweis[hidden],
.livechat-aktion[hidden],
.livechat-tippt[hidden],
.livechat-zeichen[hidden],
.livechat-punkt[hidden] { display: none; }

.livechat-scrim {
    position: fixed;
    inset: 0;
    z-index: 900;
    background: rgba(8, 9, 12, .55);
}

.livechat {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 901;
    width: min(380px, 100vw);
    display: flex;
    flex-direction: column;
    background: var(--s-1);
    border-left: 1px solid var(--line-2);
    box-shadow: var(--e-3);
}

.livechat-kopf {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-4);
    background: var(--s-2);
    border-bottom: 1px solid var(--line-1);
    box-shadow: var(--lip);
}

.livechat-kopf-text {
    flex: 1;
    min-width: 0;
}

.livechat-titel {
    margin: 0;
    font-family: var(--font-body);
    font-size: var(--fs-4);
    font-weight: 500;
    color: var(--t-1);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.livechat-untertitel {
    display: flex;
    align-items: center;
    font-size: var(--fs-5);
    color: var(--t-3);
}

.livechat-ton,
.livechat-schliessen,
.livechat-zurueck {
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    background: none;
    border: none;
    border-radius: var(--r-m);
    color: var(--t-2);
    cursor: pointer;
}

.livechat-ton:hover,
.livechat-schliessen:hover,
.livechat-zurueck:hover {
    background: var(--s-3);
    color: var(--t-1);
}

/* Glocke aus: zurueckgenommen, das Symbol (durchgestrichen) traegt die Aussage. */
.livechat-ton[aria-pressed="false"]:not(:hover) { color: var(--t-3); }
.livechat-ton[hidden] { display: none; }

.livechat-zurueck { display: none; }

/* ⚠️ UMGEZOGEN am 2026-09-15 (Fund Review Aufgabe 6): .livechat-aktion sass
   im Kopf neben dem Kundennamen, max-width:45% liess von "Baeckerei
   Hoffmann" nur noch 133px stehen. Beide Zeilen stehen jetzt auf derselben
   Flaeche unter dem Kopf wie eine zweite, klickbare .livechat-hinweis-Zeile -
   der Kopf zeigt nur noch Name, Status und Schliessen-Knopf. Keine Rahmen
   und kein Radius mehr noetig (Regel 7 dadurch gegenstandslos): eine volle
   Zeile ist kein Badge. */
.livechat-hinweis,
.livechat-aktion {
    display: block;
    width: 100%;
    padding: var(--sp-2) var(--sp-4);
    background: var(--s-2);
    border-bottom: 1px solid var(--line-1);
    font-family: var(--font-body);
    font-size: var(--fs-5);
    color: var(--t-2);
    text-align: left;
}

.livechat-aktion {
    border-left: none;
    border-right: none;
    border-top: none;
    text-decoration: underline;
    cursor: pointer;
}

.livechat-aktion:hover { color: var(--t-1); }

.livechat-verlauf {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    padding: var(--sp-4);
}

.livechat-tag {
    align-self: center;
    margin: var(--sp-3) 0 var(--sp-1);
    font-size: var(--fs-5);
    color: var(--t-3);
}

.livechat-leer,
.livechat-fehler {
    margin: auto 0;
    text-align: center;
    font-size: var(--fs-5);
    color: var(--t-3);
}

.livechat-fehler { color: var(--t-2); }

.livechat-eintrag {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    max-width: 82%;
}

.livechat-eintrag.ist-eigene {
    align-self: flex-end;
    align-items: flex-end;
}

.livechat-eintrag.ist-fremde { align-self: flex-start; }

.livechat-blase {
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--r-l);
    font-size: var(--fs-4);
    line-height: 1.45;
    color: var(--t-1);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.ist-eigene .livechat-blase {
    background: var(--s-3);
    box-shadow: var(--lip-strong);
    border-bottom-right-radius: var(--r-s);
}

.ist-fremde .livechat-blase {
    background: var(--s-2);
    border: 1px solid var(--line-1);
    border-bottom-left-radius: var(--r-s);
}

.livechat-eintrag.ist-fehler .livechat-blase { opacity: .6; }

.livechat-zeit,
.livechat-gelesen {
    padding: 0 var(--sp-1);
    font-size: var(--fs-6);
    color: var(--t-3);
    font-variant-numeric: tabular-nums;
}

.livechat-erneut {
    display: inline-flex;
    align-items: center;
    padding: 0 var(--sp-1);
    background: none;
    border: none;
    font-family: var(--font-body);
    font-size: var(--fs-6);
    color: var(--t-2);
    text-decoration: underline;
    cursor: pointer;
}

/* Fund Review 2026-09-15: ~17px hoch, unter dem Mindestmass fuer
   Fingerbedienung. Nur bei grobem Zeiger groesser - am Rechner bleibt die
   Zeile knapp, wie die uebrigen Metadaten der Blase. */
@media (pointer: coarse) {
    .livechat-erneut { min-height: 44px; }
}

.livechat-tippt {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 0 var(--sp-4) var(--sp-2);
    font-size: var(--fs-5);
    color: var(--t-3);
}

.livechat-tippt-punkte {
    display: inline-flex;
    gap: var(--sp-1);
}

.livechat-tippt-punkte i {
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--t-3);
    animation: livechat-tippt 1.2s infinite;
}

.livechat-tippt-punkte i:nth-child(2) { animation-delay: .2s; }
.livechat-tippt-punkte i:nth-child(3) { animation-delay: .4s; }

@keyframes livechat-tippt {
    0%, 80%, 100% { opacity: .3; }
    40% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .livechat-tippt-punkte i { animation: none; opacity: .7; }
}

.livechat-eingabe {
    display: flex;
    align-items: flex-end;
    gap: var(--sp-2);
    padding: var(--sp-3);
    padding-bottom: max(var(--sp-3), env(safe-area-inset-bottom));
    background: var(--s-1);
    border-top: 1px solid var(--line-1);
}

/* Nur das Layout - Flaeche, Rand und Fokusring kommen aus der
   Sammelregel fuer Eingabefelder (Abschnitt 1). Die Sammelregel setzt
   padding: var(--sp-3) var(--sp-4) (12px oben/unten) - das treibt eine
   einzeilige Textarea per box-sizing:border-box (index.html Zeile 65) auf
   knapp 49px, waehrend .livechat-senden fest 44px hoch ist (Fund Review
   2026-09-15). Fix: senkrechtes Polster auf --sp-2 (8px) verkleinert, dann
   greift min-height:44px als Boden und beide Elemente sind gleich hoch. */
.livechat-feld {
    flex: 1;
    min-height: 44px;
    max-height: 160px;
    padding-top: var(--sp-2);
    padding-bottom: var(--sp-2);
    resize: none;
}

.livechat-senden {
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    display: grid;
    place-items: center;
    background: var(--gold);
    color: var(--s-0);
    border: none;
    border-radius: var(--r-m);
    cursor: pointer;
}

.livechat-senden:disabled {
    background: #23262f;
    color: #5d6371;
    cursor: not-allowed;
}

.livechat-zeichen {
    padding: 0 var(--sp-3) var(--sp-2);
    text-align: right;
    font-size: var(--fs-6);
    color: var(--t-3);
    font-variant-numeric: tabular-nums;
}

@media (max-width: 767px) {
    .livechat {
        width: 100vw;
        border-left: none;
    }
    .livechat-scrim { display: none; }
    .livechat-zurueck { display: grid; }
    .livechat-schliessen { display: none; }
    .livechat-knopf-text { display: none; }
    body.livechat-offen { overflow: hidden; }

    /* Abnahme Punkt 4 (2026-09-16): Mit dem Livechat-Knopf brauchte die
       Kopfzeile am Handy 377 px bei 311 px Platz. Die Seite wurde dadurch
       409 px breit, der Chat (100vw) lag 34 px verschoben und das
       Eingabefeld unter dem Bildschirmrand. Passwort und Abmelden zeigen
       hier deshalb nur ihr Symbol, wie die Hilfe-Knoepfe daneben. */
    .btn-header-text { display: none; }
    .header-right .btn-header-symbol { display: inline-block; }
}

.btn-header-symbol { display: none; font-size: 20px; line-height: 1; }

/* ============================================================
   26. MASTER DOCK AM HANDY: KOPFZEILE UND SEITENBREITE (2026-09-16)
   ============================================================
   Abnahme Punkt 4 am Handy (Marco, 360 px), drei Befunde:
   1. Der Dock-Kopf quetschte Firmenname und Online-Status auf 0 px.
   2. Die Symbole standen verstreut und ungleich gross (34/46 px).
   3. Die Seite war 412 statt 360 px breit.

   Zu 3: .cockpit-grid in index.html verlangt minmax(380px, 1fr) -
   auf einem 360-px-Geraet ist schon die erste Spalte breiter als der
   Bildschirm. Nicht neu mit dem Livechat, betraf auch main.
   min(380px, 100%) laesst die Spalte unter 380 px auf die Breite des
   Rasters schrumpfen; darueber aendert sich nichts.

   Zu 1 und 2: Labor 22, Variante A, Runde 4 (Marco 2026-09-16, nach
   Livegang). Drei Zeilen: Firmenname / Online-Status + Etikett / Chat
   links, Symbole rechts. Runde 3 hatte vier Zeilen mit fester
   200-px-Spalte fuer vier Symbole - im echten Dock sind es nur drei
   (Hilfe-Knopf aus), links blieb Leerraum, Kopf 185 px. Jetzt Spalten
   auto / auto / Rest: Online-Status und Etikett stehen dicht
   nebeneinander.

   "Dock mit Kunde" erkennt die Regel an einem sichtbaren
   #admin-panel-mid-title statt an einer Klasse aus app.js: den Titel
   blenden drei Wege aus (Kundenlogin, Kunde geloescht, Abmelden), und
   eine Klasse muesste jeder dieser Wege mitpflegen. :has() ist hier
   ungefaehrlich - faellt es aus, bleibt die Kopfzeile aus 12.2. */
.cockpit-grid { grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr)); }

/* --- 26.1 Chat und Bauplatz als eine Gruppe (Labor 23, 2026-09-16) ---
   Live mit 53bc85b stand "Bauplatz oeffnen" am Handy im selben Feld wie
   der Chat, verschoben um eine feste Chatbreite (42 px + Luecke). Mit
   Zaehler ist der Chat 68 px breit - das Symbol lag 18 px auf ihm und
   verdeckte die Zahl. Die Abnahme war ohne Zaehler gemessen.
   Jetzt steht der Bauplatz-Knopf im Markup in .dock-kopf-aktionen, und
   livechatKnopfPlatzieren() setzt den Chat im Dock davor. Die Luecke
   waechst mit, egal wie breit der Chat ist. Folge am Desktop: Reihenfolge
   Online, Etikett, Chat, Bauplatz (vorher Chat vor dem Etikett).
   Ohne sichtbaren Inhalt (Kundenansicht: Chat rechts, Bauplatz hidden)
   faellt die Gruppe ganz weg, sonst kostete sie eine Luecke. */
.dock-kopf-aktionen { display: inline-flex; align-items: center; gap: var(--sp-2); flex-shrink: 0; }
.dock-kopf-aktionen:not(:has(> :not([hidden]))) { display: none; }

/* --- 26.2 Dock mit Kunde bis 1023 px: drei Zeilen -----------------
   Bis Labor 23 galt das nur bis 599 px. Zwischen 600 und 1024 px stand
   darueber das Desktop-Raster 1fr auto 1fr, und die Symbole ragten aus
   dem Kopf (gemessen im schwierigsten Fall: 600 px 258 px, 700 px 158 px,
   768 px 254 px, 1024 px 30 px), der Firmenname brach dreizeilig um.
   Marco waehlte Variante A: der Handy-Aufbau bis 1023 px (Kopf 136 px),
   darueber eine Zeile (26.3). Die Markenzeile weicht, solange ein Kunde
   offen ist - sie sagt im eigenen Dock nichts Neues (vgl. 12.2). */
@media (max-width: 1023px) {
    header:has(#admin-panel-mid-title:not([hidden])) {
        grid-template-columns: auto auto minmax(0, 1fr);
        align-items: center;
        column-gap: var(--sp-2);
        row-gap: var(--sp-2);
    }
    header:has(#admin-panel-mid-title:not([hidden])) .header-left { display: contents; }
    header:has(#admin-panel-mid-title:not([hidden])) #portal-title-display,
    header:has(#admin-panel-mid-title:not([hidden])) .header-center { display: none; }
    header:has(#admin-panel-mid-title:not([hidden])) #admin-panel-mid-title {
        grid-column: 1 / 4; grid-row: 1;
        min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    header:has(#admin-panel-mid-title:not([hidden])) #admin-panel-mid-title::before { display: none; }
    header:has(#admin-panel-mid-title:not([hidden])) #admin-praesenz-status {
        grid-column: 1; grid-row: 2; margin-left: 0;
        min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    header:has(#admin-panel-mid-title:not([hidden])) #admin-status-badge { grid-column: 2; grid-row: 2; justify-self: start; }
    header:has(#admin-panel-mid-title:not([hidden])) .dock-kopf-aktionen { grid-column: 1 / 3; grid-row: 3; justify-self: start; }
    header:has(#admin-panel-mid-title:not([hidden])) .header-right {
        grid-column: 3; grid-row: 3;
        width: auto; justify-self: end; justify-content: flex-end; gap: var(--sp-2);
    }
}

/* --- 26.3 Dock mit Kunde ab 1024 px: eine Zeile -------------------
   Vorher 1fr auto 1fr mit Markenzeile: Die linke Gruppe war breiter als
   ihre Spalte, der Firmenname brach bis 1919 px dreizeilig um (Kopf
   117 px), die Marke stand bis zu 183 px neben der Mitte. Jetzt links
   alles, rechts die Symbole, Kopf 75 px. Reicht der Platz nicht, kuerzt
   nur der Firmenname mit Ellipse. "Master Dock" und die Texte von Chat
   und Bauplatz erst ab 1440 px - bei 1024 px war der Name sonst gekuerzt. */
@media (min-width: 1024px) {
    header:has(#admin-panel-mid-title:not([hidden])) { grid-template-columns: minmax(0, 1fr) auto; }
    header:has(#admin-panel-mid-title:not([hidden])) .header-center { display: none; }
    header:has(#admin-panel-mid-title:not([hidden])) .header-left { min-width: 0; justify-self: stretch; }
    header:has(#admin-panel-mid-title:not([hidden])) #admin-panel-mid-title {
        min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    header:has(#admin-panel-mid-title:not([hidden])) .header-left > :not(#admin-panel-mid-title) { flex-shrink: 0; white-space: nowrap; }
}
@media (min-width: 1024px) and (max-width: 1439px) {
    header:has(#admin-panel-mid-title:not([hidden])) #portal-title-display,
    header:has(#admin-panel-mid-title:not([hidden])) #admin-panel-mid-title::before { display: none; }
}
@media (max-width: 1439px) {
    header:has(#admin-panel-mid-title:not([hidden])) .livechat-knopf-text { display: none; }
}

@media (max-width: 599px) {
    /* Marco Runde 1 (Labor 22): der Chat wurde auf die Spaltenbreite
       gestreckt. Bauplatz gleich gross daneben; beide stehen seit 26.1 in
       der Gruppe, die Platzierung sitzt dort. Name fuer Screenreader:
       aria-label im Markup. */
    header:has(#admin-panel-mid-title:not([hidden])) #btn-livechat,
    header:has(#admin-panel-mid-title:not([hidden])) #btn-admin-bauplatz {
        width: auto; min-height: 44px; padding: 0 var(--sp-2); gap: 6px;
    }

    /* Alle Symbole gleich gross - in jeder Kopfzeile, auch beim Kunden. */
    /* :not([hidden]) - sonst holt display:inline-flex ausgeblendete Knoepfe zurueck. */
    header .header-right > .btn-help-circle:not([hidden]),
    header .header-right > .btn-header:not(.livechat-knopf):not([hidden]) {
        width: 44px; height: 44px; min-width: 44px; min-height: 44px;
        padding: 0; margin: 0;
        display: inline-flex; align-items: center; justify-content: center;
        border-radius: var(--r-m); box-sizing: border-box;
    }
}
