/* ITleague Events — Design-System
   Offizielle CI-Palette: Blau #1D86CC, Grün #2AA05F, Hellgrau #EBEFF1,
   Dunkelgrau #666666, Schwarz #333333, Weiß #FFFFFF.
   Typo: System-Sans (Titel/Body) + Monospace-Utility (Labels/Meta) — Signatur.
   DSGVO: keine externen Fonts/Assets, nur System-Fontstacks. Kein Build-Schritt.

   FARBEN AUS DER ITLEAGUE SKILLDB (kileague-skilldb/frontend/styles.css).
   Dieselbe CI-Grundlage, aber SEMANTISCH benannt statt nach der Farbe: „Fläche", „Rahmen",
   „gedämpfter Text". Genau das ist die Voraussetzung für die dunkle Darstellung — eine
   Regel, die „weiß" sagt, kann nicht dunkel werden; eine, die „Fläche" sagt, schon.
   Die alten Namen dieser Anwendung (--weiss, --schwarz, --grau …) leben weiter: Sie stehen
   weiter unten als BRÜCKE auf die semantischen Werte. Deshalb folgt die ganze Anwendung dem
   Umschalter, ohne dass 213 Regeln angefasst werden mussten. */

:root{
  color-scheme:light;   /* daran richten sich Rollbalken und Formularelemente aus */

  /* ===== Marken- & Basisfarben ===== */
  --blue:#1D86CC;
  --blue-hover:#1873B0;
  --blue-dark:#156BA5;
  /* DAS BLAU, DAS TEXT TRÄGT — und warum es nicht --blue ist (29.08.2026).
     Das Marken-Blau #1D86CC hält gegen Weiß nur 3,94:1, nötig sind 4,5:1. Das betraf drei
     Stellen auf einmal, weil sie alle an derselben Brücke hängen: den Hauptknopf (weiße
     Schrift auf der Fläche), jeden Verweis und jeden Link (Blau als Schrift auf Weiß).
     Auf Kartenflächen waren es sogar nur 3,73:1.
     --blue selbst bleibt unangetastet: Es ist die CI-Farbe und für Flächen, Grafik und
     Symbole richtig, wo 3:1 genügt. Geändert wird nur, welcher Ton TEXT trägt.
     Aufgefallen ist es der Tafel „Bausteine", die die Werte an den echten Bausteinen misst.
     NUR IN DER HELLEN DARSTELLUNG. Im Dunkelmodus liegt der Knopf bei 6,58:1 und der
     Verweis bei 5,83:1 — dort ist nichts zu holen. Und die Skala läuft dort in die ANDERE
     Richtung: --blue-hover ist im Dunkeln HELLER als --blue. Wer hier einfach „eine Stufe
     tiefer" schriebe, drehte den Hover im Dunkelmodus um. */
  /* ZWEITE RUNDE, DERSELBE TAG (29.08.2026): #1873B0 REICHTE NUR AUF WEISS.
     Der Ton oben wurde gegen Weiß (5,10) und gegen den Seitengrund (4,83) gemessen — und
     genau das war die Lücke: Ein Verweis steht nicht nur auf diesen beiden Flächen.
       --blau auf --blau-tint  #E7F0FA : 4,43:1  ← der Link im Hotelabsatz von .note
       --blau auf --surface-2  #EBEFF1 : 4,41:1  ← „Einloggen" im Anmeldekasten (ab 1040px)
     Beides IM BROWSER an https://events.itleague.de/veranstaltung.html?id=400 nachgemessen,
     nicht gerechnet: 390px breit für den ersten Fall, 1440px für den zweiten.
     #156BA5 hebt alle vier Flächen über die Grenze — 5,71 / 5,41 / 4,93 / 4,96 — und ist
     kein neuer Ton, sondern der, der als --blue-dark ohnehin schon in der Palette steht.

     UND WARUM DER HOVER-TON MITWANDERN MUSS: Er WAR #156BA5. Wäre er es geblieben, trüge
     --blau denselben Wert wie --blau-hover, und der Hauptknopf änderte beim Überfahren sein
     Aussehen nicht mehr — der Kontrast bliebe dabei tadellos. Für genau diese Falle stand
     die Wache „der Hover-Ton unterscheidet sich wirklich vom Ruhezustand" in
     tests/bausteine.test.js schon bereit; nachgestellt wurde es trotzdem, und sie wird rot.
     #136298 setzt die Reihe mit demselben Schritt fort, den #1873B0 → #156BA5 macht.
     NUR DIE HELLE PALETTE. Im Dunkeln laufen beide Namen auf eigene Werte und die Skala in
     die andere Richtung (Hover HELLER als Ruhe); dort ist nichts zu holen und nichts zu tun. */
  --blue-text:#156BA5;        /* weiß darauf 5,71:1 · als Schrift auf Weiß 5,71:1 */
  --blue-text-hover:#136298;  /* weiß darauf 6,51:1 */
  --green:#2AA05F;
  --green-hover:#238A51;
  /* DAS GRÜN, AN DEM TEXT HÄNGT — dieselbe Geschichte wie beim Blau, einen Tag später
     bemerkt (29.08.2026). Das CI-Grün #2AA05F hält gegen Weiß nur 3,34:1, nötig sind 4,5:1.
     Und weil Kontrast gegen Weiß in beide Richtungen dieselbe Zahl ist, traf es zwei
     Stellen auf einmal, die auf den ersten Blick nichts miteinander zu tun haben:
       weiße Initialen AUF der grünen Kachel  .tnl__ph in der Teilnehmerliste (15px/700)
       grüne Schrift AUF Weiß                 .sicht__wandel--hoch, die Steigerungszeile
     Beide im Browser gemessen: 3,34:1. Auf der Startseite (.card__ph, 42px) und im Profil
     (.profil__ph, 44px) fällt derselbe Ton NICHT durch — dort ist die Schrift groß, und für
     große Schrift gilt 3:1. Genau deshalb fiel es an der einen kleinen Stelle auf.
     #1E7A47 hält 5,34:1 gegen Weiß und ist kein neuer Ton: Es ist der Wert, der als
     --tint-green-text ohnehin schon in der Palette steht.
     --green SELBST BLEIBT UNANGETASTET. Es ist die CI-Farbe, es füllt den Balken der
     Sichtbarkeitskachel, es zeichnet die Linie an .callout--success — dort genügt 3:1 — und
     es ist der Ton des Hauszeichens: tests/konventionsdateien.test.js liest --gruen aus
     dieser Datei und hält jeden Bildpunkt von apple-touch-icon.png dagegen. Wer --green
     nachdunkelt, macht damit still das Symbol falsch.
     NUR DIE HELLE PALETTE. Im Dunkeln steht dasselbe Paar bei 7,46:1 bzw. 6,61:1; dort
     trägt --green-text deshalb denselben Wert wie --green. */
  --green-text:#1E7A47;
  --light:#EBEFF1;
  --gray:#666666;
  --black:#333333;
  --white:#FFFFFF;

  /* ===== Semantische Flächen & Text (Hell) ===== */
  --bg:#F7F9FA;              /* Seitenhintergrund */
  --surface:#FFFFFF;         /* Karten, Modals, Tabellen, Kopfleiste */
  --surface-2:#EBEFF1;       /* dezente Flächen, Chips, Hover */
  --surface-hover:#DDE4E8;   /* Hover auf dezenten Flächen */
  --hover-blue:#F0F7FC;      /* zarter blauer Hover (Listen, Nav) */
  --border:#E2E8EC;          /* Standard-Rahmen/Linien */
  --border-strong:#D4DCE1;   /* Rahmen interaktiver Elemente (Inputs, Chips) */
  --border-soft:#ECF0F2;     /* zarte innere Trennlinien */
  --text:#333333;            /* Haupttext */
  --text-soft:#4A4A4A;       /* etwas weicherer Fließtext */
  --muted:#666666;           /* gedämpfter Text */

  /* ===== Akzent-Tints (Paare: Fläche + Textfarbe) ===== */
  --tint-green:#E8F5E9;      --tint-green-text:#1E7A47;
  --tint-green-border:#BFE3CB; --tint-green-hover:#D8EDDD;
  /* #946700 statt #9A6B00 (29.08.2026): 4,55:1 statt 4,27 auf #FFF3DC. Ihre drei
     Geschwister liegen bei 4,75 / 5,34 / 4,96 — sie ist beim Nachdunkeln im Juli 2026
     als einzige uebersehen worden. Vier Prozent Helligkeit, mit blossem Auge nicht zu
     sehen. Im Dunkelmodus gilt weiter --tint-amber-text:#E3B667, dort stimmt es. */
  --tint-amber:#FFF3DC;      --tint-amber-text:#946700;
  --tint-amber-soft:#FFF7E8; --tint-amber-border:#F2DEB3;
  --tint-red:#FCE8E8;        --tint-red-text:#B0322E;
  --tint-red-strong:#B0322E; --tint-red-hover:#F6D6D6;
  --tint-blue:#E7F0FA;       --tint-blue-text:#156BA5;
  --tint-neutral:#F0F2F4;    --tint-neutral-text:#666666;
  /* Diese zwei Schriftfarben sind eine Nuance dunkler als in der SkillDB (dort #1873B0 und
     #777777), und das ist Absicht: Beide tragen ausschliesslich Plakettentext auf ihrer
     hellen Tint-Flaeche, und dort kamen sie auf 4,4:1 bzw. 4,0:1 — unter den 4,5:1, die
     kleiner Text braucht. Jetzt 5,0:1 und 5,1:1. In der dunklen Palette stehen ohnehin
     eigene Werte, die sind unberuehrt. Wer hier zurueckdreht, macht zwei Plaketten wieder
     schlecht lesbar. */

  /* ===== Sonstiges ===== */
  --star:#F5B301;
  /* DER NOCH NICHT ANGETIPPTE STERN IST EIN BEDIENZEICHEN, KEIN ZIERRAT (29.08.2026).
     Er steht auf /feedback.html im Bewertungsregler und ist das EINZIGE Zeichen, an dem
     sich dessen Zustand ablesen lässt — die <input type="radio"> darunter sind unsichtbar
     und ausdrücklich nicht deaktiviert. #D8DEE3 auf Weiß sind 1,36:1; die Grenze ist hier
     3:1 (WCAG 1.4.11 für Bedienzeichen, und dieselbe 3:1 gölte für große Schrift — das ★
     ist mit 28 bis 34px groß). Im Dunkeln waren es 2,05:1, also ebenfalls darunter.
     Beim Überfahren wurde er schon immer gut lesbar (--dunkelgrau, 5,74:1) — auf dem Telefon
     gibt es kein Überfahren, und genau dort wird der Bogen ausgefüllt.
     #868F97 hält 3,29:1 auf der Karte und 3,11:1 auf dem Seitengrund. Er bleibt damit
     deutlich blasser als der angetippte Stern (--blau) und als der überfahrene
     (--dunkelgrau); „noch nichts gewählt" ist weiterhin auf einen Blick zu sehen. */
  --star-off:#868F97;
  --hl-bg:#FFE873;
  --hl-text:#333333;
  --accent-cyan:#7CC5F0;
  --overlay:rgba(30,40,46,.55);

  --shadow:0 1px 3px rgba(51,51,51,.08), 0 4px 14px rgba(51,51,51,.06);
  --shadow-hover:0 4px 10px rgba(51,51,51,.10), 0 12px 28px rgba(51,51,51,.12);

  /* ===== Ergänzungen dieser Anwendung =====
     Vier Werte, die die SkillDB nicht kennt, weil es dort die Bausteine nicht gibt.
     Sie stehen deshalb in ALLEN drei Blöcken mit — sonst blieben sie im Dunkeln hell. */
  --shadow-modal:0 24px 60px rgba(31,42,51,.28);   /* der Dialog liegt höher als eine Karte */
  --ring:rgba(29,134,204,.20);                     /* Fokusring am Eingabefeld: DURCHSICHTIG,
                                                      damit er über jeder Fläche gleich wirkt */
  --accent-line:rgba(29,134,204,.30);              /* blaustichige Rahmenlinie (Karte im Hover,
                                                      Adressliste, Code in der Hinweiszeile) */
  --schach-grund:#FFFFFF;                          /* Schachbrett der Zuschnitt-Vorschau */
  --schach-feld:rgba(51,51,51,.10);
  /* Schrift AUF einer gesättigten Markenfläche (blauer Knopf, blaue/grüne Kachel).
     Hier Weiß, im Dunkeln TINTE — und das ist kein Versehen, sondern gemessen: Die dunkle
     Palette hellt Blau auf #3EA6E8 auf, damit es auf dunklem Grund noch leuchtet. Weiße
     Schrift darauf kommt nur noch auf 2,7:1 und ist damit unter jeder brauchbaren Grenze;
     dieselbe Fläche mit dunkler Tinte steht bei 6,6:1. Deshalb dreht sich das Paar mit der
     Darstellung um, statt in beiden Weiß zu bleiben. */
  --auf-farbe:#FFFFFF;

  --sans:"Helvetica Neue",Helvetica,Arial,system-ui,sans-serif;
  --mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;

  --radius:14px; --radius-sm:9px; --pill:999px;
  --maxw:1120px;
  /* Die Kopfleiste ist BREITER als die Lesebreite, und diese Zahl ist gemessen, nicht
     gerundet: Der schlimmste Fall des Menüs — angemeldet als Assistenz UND Admin, also
     „Meine Anmeldungen · Anmeldungen (Firma) · Sichtbarkeit · Profil · Admin · Feedback ·
     Name · Abmelden" plus Umschalter — braucht im Browser 1169,05px und passt damit ab
     1170px in EINE Zeile. 1177px sind diese 1170px plus rund sieben Pixel Luft für fremde
     Schriftschnitte: Auf einem Rechner ohne Helvetica Neue setzt der Browser Arial, und das
     misst ein paar Zehntel anders. Reicht die Luft einmal nicht, bricht trotzdem nichts um —
     dann kürzt .hr-user den Namen.

     NEU AUSGEMESSEN AM 27.08.2026, als „Profil" als ACHTER Punkt dazukam (Migration 0074,
     die Seite „Mein Profil"). Gemessen im Browser am breitesten Fall: Das Menü braucht
     903px statt 901px, der Bedarf steigt damit auf 1169,05px (Logo 194,05 + Abstand 16 +
     Menü 903 + Innenabstand 2 × 28), aufgerundet 1170px plus sieben Pixel Luft = 1177px.

     GENAU DESHALB HEISST DER PUNKT „Profil" UND NICHT „Mein Profil" — dieselbe Abwägung wie
     bei „Feedback" unten, und sie ist wieder gemessen und nicht geschätzt:
       „Mein Profil"  = 91,80px  ->  Menü 939px  ->  Grenze 1213px
       „Profil"       = 55,27px  ->  Menü 903px  ->  Grenze 1177px
     Mit „Mein Profil" hätte iPad Air (1180px) UND iPad Pro 11" (1194px) im Querformat in der
     Schublade gestanden — dieselben beiden Geräte, um derentwillen die Grenze am 20.08.2026
     gesenkt wurde und derentwegen der Feedback-Punkt sein zweites Wort verloren hat. Mit
     1177px behalten beide die einzeilige Leiste (3px bzw. 17px Luft). Die ÜBERSCHRIFT der
     Seite und ihr <title> heißen weiterhin „Mein Profil"; nur der Menüpunkt ist kurz.

     AUSGEMESSEN AM 21.08.2026, als „Feedback" als dritter Menüpunkt dazukam. Er kostet
     85,6px plus 10px Abstand; gegengerechnet spart die kürzere Beschriftung „Anmeldungen
     (Firma)" 41,6px gegenüber „Anmeldungen meiner Firma" (162,5px statt 204,1px). Unterm
     Strich wuchs der Bedarf von 1106px auf 1160px, die Grenze von 1113px auf 1167px
     (seit dem 27.08.2026 1177px, siehe oben).
     GENAU DESHALB HEISST DER PUNKT „Feedback" UND NICHT „Feedback geben". Auch das ist
     gemessen und nicht geschätzt: Mit „Feedback geben" wäre der Punkt 131px statt 85,6px
     breit, der Bedarf 1204,45px und die Grenze damit 1212px. Das hätte iPad Air (1180px)
     UND iPad Pro 11" (1194px) im Querformat in die Schublade geschickt. Beide Breiten
     stehen ausdrücklich in der Prüfliste von e2e/specs/darstellung.spec.js, damit das
     niemandem still passiert.

     GEMESSEN AN GENAU DIESEM MENÜ, und seit dem 21.08.2026 trägt es JEDE Seite mit Leiste:
     „Meine Anmeldungen" und „Anmeldungen (Firma)" sind eigene Seiten geworden und stehen
     deshalb auch auf der Veranstaltungsseite und auf einander im Menü; „Feedback" steht
     zusätzlich im Verwaltungsbereich. Wer einen Punkt hinzufügt oder eine Beschriftung
     verlängert, muss hier nachmessen. Der AKTIVE Punkt (aria-current) ist dabei bewusst
     gleich breit — er wird mit einem box-shadow ausgezeichnet und nicht mit Rahmen oder
     Fettschnitt.

     UNTER --maxw (1120px) HAT DIESER FALL NIE GEPASST — auf keinem Bildschirm, auch nicht
     auf einem 4K-Schirm, weil der Deckel und nicht das Fenster die Grenze setzte. Genau das
     war die doppelt hohe Kopfleiste, die der Auftraggeber gemeldet hat.

     --maxw selbst bleibt unangetastet: Das ist die LESEBREITE des Fließtextes (Begründung
     weiter unten bei body.admin) und hat mit dem Platzbedarf einer einzeiligen Leiste
     nichts zu tun. Der Preis ist, dass die Leiste auf breiten Schirmen 47px breiter ist als
     der Text darunter, also je Seite 23,5px weiter außen steht — sichtbar, aber harmlos
     gegen einen Kopf, der doppelt so hoch ist wie er sein müsste. (Die Zahl stand hier bis
     zum 21.08.2026 auf 65px und stimmte seit dem 20.08.2026 nicht mehr: Mit --maxw-kopf
     1113px war die Leiste sogar SCHMALER als der Text. Jetzt trägt der Satz wieder.) */
  --maxw-kopf:1177px;

  /* Die Höhe der klebenden Kopfleiste. GEMESSEN UND NICHT GESCHÄTZT (06.09.2026, Chromium
     auf veranstaltung.html, je für Gast, Mitglied und Admin): 67,4px oberhalb von 1176px,
     69px darunter — unterhalb der Grenze steht statt der einzeiligen Menüzeile der Burger
     (44 × 40px), und der ist höher. Genommen ist der GRÖSSERE der beiden Werte: Zu viel
     Abstand sieht man, zu wenig verdeckt die Überschrift.

     SIE WIRD NICHT GESETZT, SONDERN NACHGETRAGEN. Die Leiste ist so hoch, wie ihr Inhalt sie
     macht (header-inner: 14px Polsterung oben und unten um Logo beziehungsweise Burger); ein
     `height` hier würde sie beschneiden, statt sie zu beschreiben. Wer an der Polsterung, am
     Logo oder am Burger etwas ändert, muss diese Zahl deshalb neu messen — sie hängt heute
     an genau einer Stelle, dem scroll-margin-top der drei Sprungziele weiter unten.

     WOZU ÜBERHAUPT: Der Kalendereintrag führt mit #agenda, #anreise und #teilnehmer direkt
     auf die drei aufklappbaren Abschnitte. scrollIntoView setzt deren Oberkante an die
     Oberkante des Fensters — und dort steht diese Leiste. Ohne den Abstand landet die
     Überschrift des angesprungenen Abschnitts dahinter, und man sieht Inhalt ohne die Zeile,
     die sagt, worum es geht. */
  --kopf-hoehe:69px;
}

/* ===== Dunkle Palette =====
   Aus Wartungsgründen steht der Dunkel-Block ZWEIMAL, damit sowohl das automatische
   Systemfolgen (prefers-color-scheme) als auch die von Hand getroffene Wahl
   (data-theme="dark") funktioniert. Beide Blöcke MÜSSEN Wert für Wert identisch bleiben:
   Wer nur einen ändert, baut einen Unterschied zwischen „System steht auf dunkel" und „von
   Hand dunkel gewählt" ein, den beim Lesen niemand sieht.
   tests/darstellung.test.js vergleicht beide Blöcke und wird rot, wenn sie auseinanderlaufen. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    color-scheme:dark;
    --blue:#3EA6E8;
    --blue-hover:#5CB6EE;
    --blue-dark:#2C7FBE;
    /* Unverändert wie bisher: Der Dunkelmodus erfüllt jedes Paar schon (6,58 / 5,83). */
    --blue-text:#3EA6E8;
    --blue-text-hover:#5CB6EE;
    --green:#3FBE7C;
    --green-hover:#57CC90;
    /* Unverändert wie --green: Im Dunkeln hält das Paar längst (7,46 / 6,61). */
    --green-text:#3FBE7C;

    --bg:#15191D;
    --surface:#1E242A;
    --surface-2:#29323A;
    --surface-hover:#333D46;
    --hover-blue:#223140;
    --border:#38424B;
    --border-strong:#46525C;
    --border-soft:#2C353D;
    --text:#E6EAED;
    --text-soft:#C2CAD0;
    --muted:#98A3AC;

    --tint-green:#1E3A2B;     --tint-green-text:#7FD8A2;
    --tint-green-border:#2E5540; --tint-green-hover:#274A37;
    --tint-amber:#3A2F1A;     --tint-amber-text:#E3B667;
    --tint-amber-soft:#2F2716; --tint-amber-border:#4A3D22;
    --tint-red:#3A2222;       --tint-red-text:#E88C8C;
    --tint-red-strong:#C0392B; --tint-red-hover:#4A2A2A;
    --tint-blue:#1E3345;      --tint-blue-text:#6FB8EC;
    --tint-neutral:#2A3138;   --tint-neutral-text:#A6B0B8;

    --star:#F5B301;
    /* #4A555E lag auf --surface bei 2,05:1 — Begründung in der hellen Palette. 3,61:1. */
    --star-off:#6E7B86;
    --hl-bg:#665611;
    --hl-text:#FFE58A;
    --accent-cyan:#7CC5F0;
    --overlay:rgba(0,0,0,.66);

    --shadow:0 1px 3px rgba(0,0,0,.40), 0 4px 14px rgba(0,0,0,.32);
    --shadow-hover:0 4px 10px rgba(0,0,0,.45), 0 12px 28px rgba(0,0,0,.50);

    --shadow-modal:0 24px 60px rgba(0,0,0,.62);
    --ring:rgba(62,166,232,.32);
    --accent-line:rgba(62,166,232,.40);
    --schach-grund:#242B32;
    --schach-feld:rgba(255,255,255,.09);
    --auf-farbe:#15191D;
  }
}

/* Von Hand gewähltes Dunkel (identisch zum Block oben halten) */
:root[data-theme="dark"]{
  color-scheme:dark;
  --blue:#3EA6E8;
  --blue-hover:#5CB6EE;
  --blue-dark:#2C7FBE;
  /* Unverändert wie bisher — Begründung im Block darüber. */
  --blue-text:#3EA6E8;
  --blue-text-hover:#5CB6EE;
  --green:#3FBE7C;
  --green-hover:#57CC90;
  /* Unverändert wie --green — Begründung im Block darüber. */
  --green-text:#3FBE7C;

  --bg:#15191D;
  --surface:#1E242A;
  --surface-2:#29323A;
  --surface-hover:#333D46;
  --hover-blue:#223140;
  --border:#38424B;
  --border-strong:#46525C;
  --border-soft:#2C353D;
  --text:#E6EAED;
  --text-soft:#C2CAD0;
  --muted:#98A3AC;

  --tint-green:#1E3A2B;     --tint-green-text:#7FD8A2;
  --tint-green-border:#2E5540; --tint-green-hover:#274A37;
  --tint-amber:#3A2F1A;     --tint-amber-text:#E3B667;
  --tint-amber-soft:#2F2716; --tint-amber-border:#4A3D22;
  --tint-red:#3A2222;       --tint-red-text:#E88C8C;
  --tint-red-strong:#C0392B; --tint-red-hover:#4A2A2A;
  --tint-blue:#1E3345;      --tint-blue-text:#6FB8EC;
  --tint-neutral:#2A3138;   --tint-neutral-text:#A6B0B8;

  --star:#F5B301;
  /* #4A555E lag auf --surface bei 2,05:1 — Begründung in der hellen Palette. 3,61:1. */
  --star-off:#6E7B86;
  --hl-bg:#665611;
  --hl-text:#FFE58A;
  --accent-cyan:#7CC5F0;
  --overlay:rgba(0,0,0,.66);

  --shadow:0 1px 3px rgba(0,0,0,.40), 0 4px 14px rgba(0,0,0,.32);
  --shadow-hover:0 4px 10px rgba(0,0,0,.45), 0 12px 28px rgba(0,0,0,.50);

  --shadow-modal:0 24px 60px rgba(0,0,0,.62);
  --ring:rgba(62,166,232,.32);
  --accent-line:rgba(62,166,232,.40);
  --schach-grund:#242B32;
  --schach-feld:rgba(255,255,255,.09);
  --auf-farbe:#15191D;
}

/* ---------- Die Brücke: die Namen, auf denen diese Anwendung gebaut ist ----------
   styles.css hatte 213 Verwendungen von var(--…) und nur 13 feste Farbwerte. Statt 213
   Regeln umzuschreiben, zeigen die alten Namen hier auf die semantischen Werte. Der
   Umschalter tauscht dann OBEN einen Block aus, und alles darunter folgt von selbst.

   Die Zuordnung folgt der VERWENDUNG, nicht dem Namen — nachgesehen wurde jede einzelne:
   --grau hieß Hellgrau und ist der Seitengrund (also --bg, nicht --surface-2);
   --dunkelgrau ist an 42 Stellen gedämpfter Text (also --muted);
   --blau-dark stand nur an zwei Stellen, und die meinten Verschiedenes (siehe unten).

   Dieser Block steht bewusst NACH den Dunkel-Blöcken, ändert aber nichts an ihnen: Er
   definiert nur Namen, die dort nicht vorkommen. var() löst erst beim Benutzen auf — die
   Brücke zeigt also je nach Darstellung auf helle oder dunkle Werte. */
:root{
  --blau:var(--blue-text);
  --gruen:var(--green);
  /* Zwei Grüntöne und nicht einer, seit dem 29.08.2026: --gruen ist die CI-Farbe (Flächen,
     Linien, das Hauszeichen), --gruen-text der Ton, an dem Text hängt. Wo beides zusammenfällt,
     steht --gruen-text — Begründung an --green-text oben. */
  --gruen-text:var(--green-text);
  --grau:var(--bg);
  --weiss:var(--surface);
  --schwarz:var(--text);
  --dunkelgrau:var(--muted);

  --line:var(--border);
  --line-soft:var(--border-soft);
  --shadow-sm:var(--shadow);
  --shadow-md:var(--shadow-hover);

  --blau-tint:var(--tint-blue);
  --blau-line:var(--accent-line);
  --gruen-tint:var(--tint-green);
  --danger:var(--tint-red-text);
  --danger-tint:var(--tint-red);
  --warn:var(--tint-amber-text);
  --warn-tint:var(--tint-amber);

  /* --blau-dark tat ZWEIERLEI und ist deshalb aufgeteilt: an .badge--pending war es die
     Schriftfarbe auf blauem Grund (im Dunkeln muss sie HELLER werden), an
     .btn--primary:hover die Füllung des gedrückten Knopfes (im Dunkeln ein anderer Blauton).
     Ein Wert für beides ginge in genau einer der beiden Darstellungen gut. */
  --blau-dark:var(--tint-blue-text);
  --blau-hover:var(--blue-text-hover);
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; background:var(--grau); color:var(--schwarz);
  font-family:var(--sans); font-size:15px; line-height:1.6;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
img{max-width:100%;display:block}
a{color:var(--blau);text-decoration:none}
a:hover{text-decoration:underline}
/* ---------- Verweise im Fließtext (29.08.2026) ----------
   Ein Verweis MITTEN IN EINEM SATZ war bis hierher allein an der Farbe zu erkennen. Wer
   Farben nicht unterscheidet, sah ihn gar nicht — WCAG 1.4.1 „Use of Color", gemeldet auf
   datenschutz.html. Die Farbe bleibt, wie sie ist (--blau trägt seit 1.71.0 mindestens
   4,85:1 auf jeder Fläche); hier kommt nur das ZWEITE Erkennungsmerkmal daneben dazu.

   GEZIELT UND NICHT ÜBER `a` INSGESAMT. Ein globales text-decoration:underline träfe auch
   das Logo, die Kopfleiste, die Fußzeile, die Reiter und jede Kachel, die als Ganzes ein
   Verweis ist. Dort steht kein Satz, in dem sich ein Verweis verstecken könnte — und eine
   unterstrichene Kachel sieht kaputt aus. Aufgezählt sind deshalb genau die Behälter, in
   denen dieses Haus Fließtext setzt: die Rechtstexte (.legal), die redaktionellen Blöcke
   einer Veranstaltung samt Hinweiskasten (.content, .callout), die Hinweiszeilen (.note)
   und der Beilagensatz im Meldeformular (.anliegen__beilage).

   `:not(.btn)` ist kein Zierrat: In .content steht der Knopf-Block als <a class="btn">
   mitten im Fließtext (js/blocks.js, Blocktyp „button"), und ein unterstrichener Knopf sähe
   aus wie ein Fehler. tests/bildmasse.test.js hält beide Richtungen fest.

   DIE LETZTEN DREI KAMEN AM 29.08.2026 DAZU, nachgereicht aus einer Prüfung. Die vier
   Behälter oben sind die des ÖFFENTLICHEN Fließtextes — der Verweis mitten im Satz steht
   aber auch dort, wo Zahlen erklärt werden:
     .dash__satz   „Am längsten offen: <a>ITLEK-0208</a> · 58,31 € · Security-Frühstück"
                   und „<a>1 bestätigt</a> · Plätze unbegrenzt" (js/admin-dashboard.js).
                   In derselben Kachel ist der <button class="btn--link"> daneben längst
                   unterstrichen — der echte Verweis war der einzige Blauton ohne Strich.
     .dash__liste  je Zeile ein Verweis, dahinter Wert und Veranstaltung als grauer Text.
     .list td      die Datumsspalte in „Meine Anmeldungen": schwarzes Datum, darunter
                   „In den Kalender eintragen" (js/anmeldungen-ansicht.js). Es ist der
                   einzige Verweis, den ein Mitglied dort überhaupt zu sehen bekommt.
   NICHT dabei ist .dash__zahl — die große Zahl einer Kachel ist ein Verweis, aber kein
   Stück Satz: Sie steht allein, dreißig Punkte groß, und ein Strich darunter sähe aus wie
   ein Fehler. Ebenso .dash__titel: Dort füllt der Verweis den ganzen Absatz. */
.legal a:not(.btn),
.content a:not(.btn),
.callout a:not(.btn),
.note a:not(.btn),
.dash__satz a:not(.btn),
.dash__liste a:not(.btn),
.list td a:not(.btn),
.anliegen__beilage a{text-decoration:underline}
/* UND DIE RUECKMELDUNG BEIM UEBERFAHREN — sie ist mit der Unterstreichung verloren
   gegangen und kommt hier zurueck. Bis zum 29.08.2026 war der Strich SELBST das
   Hover-Zeichen: Er erschien erst unter der Maus (a:hover oben). Seit er dauerhaft
   dasteht, aendert sich beim Ueberfahren nichts mehr — der Verweis ist im Ruhezustand
   besser erkennbar und unter der Maus stumm.

   EIN FARBWECHSEL UND NICHT DAS WEGNEHMEN DES STRICHS: Wer den Strich beim Hover
   entfernt, nimmt genau das Merkmal weg, das gerade erst dazugekommen ist — und zwar
   in dem Moment, in dem jemand hinsieht. Der dunklere Ton gibt etwas dazu, statt
   etwas wegzunehmen.

   --blau-hover STATT EINER EIGENEN FARBE: Es ist derselbe Ton, den der Hauptknopf
   beim Ueberfahren traegt. Eine zweite Hover-Farbe daneben waere ein zweites System.
   Kontrast gemessen (29.08.2026): #136298 liegt auf jeder benutzten Flaeche ueber
   5,5:1 — dunkler als der Ruhezustand, also kann er nur besser werden. */
.legal a:not(.btn):hover,
.content a:not(.btn):hover,
.callout a:not(.btn):hover,
.note a:not(.btn):hover,
.dash__satz a:not(.btn):hover,
.dash__liste a:not(.btn):hover,
.list td a:not(.btn):hover,
.anliegen__beilage a:hover{color:var(--blau-hover)}
h1,h2,h3{margin:0;color:var(--schwarz);font-weight:750;letter-spacing:-.02em;line-height:1.2}
hr{border:0;border-top:1px solid var(--line);margin:20px 0}
:focus-visible{outline:2px solid var(--blau);outline-offset:2px;border-radius:3px}
/* Schaltflächen erben die Textfarbe der Seite statt der Browser-Voreinstellung — nötig,
   sobald jemand die Darstellung von Hand übersteuert (System dunkel, Seite hell). */
button{color:inherit}

/* ---------- Die Fassungsleiste (21.08.2026) ----------
   „Es gibt eine neue Version" bzw. „diese Seite ist veraltet". Sie wird von
   frontend/js/fassung.js angelegt und steht als erstes Kind von <body>.

   IM FLUSS und ausdrücklich NICHT position:fixed. Eine schwebende Leiste läge über der
   klebenden Kopfleiste (z-index 50) und verdeckte dort das Menü, oder sie brauchte einen
   Abstand am <body>, den jede Seite mitführen müsste. Im Fluss schiebt sie die Kopfleiste
   einmal nach unten, rollt beim Scrollen aus dem Bild und fängt keinen einzigen Klick ab.
   Sie steht VOR <header> und ändert an dessen Klebrigkeit nichts: `top:0` misst am
   Sichtfenster, nicht am Vorgänger.

   Schmal und ruhig — sie informiert, sie drängt nicht. Die Farben kommen aus den
   Tint-Werten und tragen damit in beiden Darstellungen; ein fester Farbwert wäre in der
   dunklen Seite genau der helle Balken, den der Umschalter beseitigen soll.
   Blau für „neu" (eine Auskunft), Bernstein für „veraltet" (etwas geht gerade nicht). */
.fassungsleiste{
  background:var(--tint-blue);color:var(--tint-blue-text);
  font-size:13.5px;line-height:1.4;text-align:center;padding:8px 16px;
}
.fassungsleiste--veraltet{background:var(--tint-amber);color:var(--tint-amber-text)}
/* Der Knopf sieht aus wie ein Link und ist doch ein Knopf: Er führt nirgendwohin, er lädt
   diese Seite neu — ein <a href> wäre ein Versprechen auf ein Ziel. */
.fassungsleiste__knopf{
  background:none;border:0;padding:0;font:inherit;font-weight:700;
  color:inherit;text-decoration:underline;cursor:pointer;
}

/* ---------- Kopfleiste ----------
   Bis zum 20.08.2026 war das eine BLAUE Leiste mit weißen Großbuchstaben. Sie stammte aus
   dem Prototyp und war der einzige Ort im Haus, an dem eine gesättigte Fläche über die
   ganze Breite lief. Jetzt wie in der ITleague SkillDB: eine helle, klebende Leiste auf der
   Flächenfarbe mit einer Linie darunter — links das Logo, rechts das Menü.
   Der Zugewinn ist nicht nur Geschmack: Eine Leiste auf --surface wechselt mit dem
   Umschalter mit. Eine feste blaue Fläche mit fester weißer Schrift hätte in beiden
   Darstellungen gleich ausgesehen und die dunkle Seite oben abgeschnitten. */
header{
  position:sticky;top:0;z-index:50;
  background:var(--weiss);border-bottom:1px solid var(--line);
}
.header-inner{
  max-width:var(--maxw-kopf);margin:0 auto;
  padding:14px clamp(16px,4vw,28px);
  display:flex;align-items:center;justify-content:space-between;gap:16px;
}
.logo{display:inline-flex;align-items:center;gap:10px;text-decoration:none;flex-shrink:0}
.logo:hover{text-decoration:none}
/* 17px wie im Original — bei dieser Höhe steht das Wortbild auf derselben Grundlinie wie das
   Wort daneben. Angezeigt wird es damit 128 × 17.

   DIE DATEI IST SEIT DEM 29.08.2026 256 × 34 UND NICHT MEHR 1000 × 133. Sie wurde achtfach
   verkleinert dargestellt; 256 Punkte Breite sind genau die doppelte Punktdichte, mehr sieht
   bei 17 Punkten Höhe niemand. 11.301 → 3.598 Byte.
   DIE MASSE STEHEN AUCH IM HTML (width/height an jedem <img class="logo-img">) und tragen
   dasselbe Verhältnis wie die Datei: 256:34 = 128:17. Ohne sie reservierte der Browser vor
   dem Stylesheet keinen Platz — das war der Auslöser für „Avoid large layout shifts" auf der
   Startseite und für „Image elements do not have explicit width and height".
   tests/bildmasse.test.js rechnet Verhältnis und Herkunft nach. */
.logo-img{height:17px;width:auto;display:block}
/* Das Wort neben dem Logo. In der SkillDB steht dort „Skills", hier „Events". */
.logo-wort{font-size:18px;font-weight:700;color:var(--blau);letter-spacing:-.3px}
/* „Admin" neben dem Namen. Es ersetzt den früheren Titel „Admin — ITleague Events" und sagt
   dasselbe auf einen Blick: Man ist nicht in der öffentlichen Ansicht.

   SIE BLEIBT MONOSPACE, obwohl das Menü daneben seit dem 20.08.2026 in der Grundschrift
   steht — das ist eine Entscheidung und kein übersehener Rest. Sie ist keine Navigation,
   sondern eine Plakette, und Plaketten sind in dieser Anwendung an neunzehn Stellen
   Monospace-Versalien (Tabellenköpfe, Statusplaketten, Reiter, Fußzeile). Ginge sie mit,
   wäre sie die EINZIGE Plakette im Haus, die aus der Reihe fällt — eine Uneinheitlichkeit
   gegen achtzehn andere getauscht. Und der Unterschied zur Schrift daneben ist genau das,
   woran man auf einen Blick sieht, dass hier kein Menüpunkt steht. */
.logo-bereich{
  font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--dunkelgrau);border:1px solid var(--line);border-radius:var(--pill);padding:2px 8px;
}

/* min-width:0 an beiden Stellen ist KEIN Beiwerk: Ein Flex-Kind darf sonst nie unter seine
   Inhaltsbreite schrumpfen (min-width:auto). Ohne das könnte der Name unten nie gekürzt
   werden — die Leiste schöbe stattdessen über den Rand hinaus. */
.header-right{display:flex;align-items:center;gap:18px;min-width:0}
.hr-group{display:inline-flex;align-items:center;gap:10px}
/* Die Beschriftungen der Menüpunkte sind gesetzt und werden NICHT gekürzt (Entscheidung des
   Auftraggebers): Diese Gruppe gibt keinen Millimeter her. Nachgeben darf allein der Name. */
.hr-nav{flex:0 0 auto}
.hr-session{min-width:0}

/* Die Menüpunkte tragen seit dem 20.08.2026 die Typografie der ITleague SkillDB
   (dort .btn/.btn-ghost): Grundschrift, 14px, fett, gemischte Schreibung, kein Sperrsatz.
   Vorher standen sie in Monospace-Versalien wie Reiter, Plaketten und Fußzeile.

   DAS MENÜ IST DAMIT DIE AUSNAHME IM HAUS, und das ist so gewollt: Die Monospace-Signatur
   bleibt überall dort, wo sie etwas AUSZEICHNET (Plaketten, Tabellenköpfe, Reiter,
   Fußzeile). Das Menü zeichnet nichts aus, es benennt Ziele — und Ziele liest man in der
   Schrift, in der man auch den Rest der Seite liest.

   ZWEI SCHLÖSSER GEGEN DEN UMBRUCH, und beide sind Absicht:
   flex:0 0 auto hält den Menüpunkt auf seiner Inhaltsbreite — er wird also nie schmaler
   gedrückt, als sein Text lang ist, und kann schon deshalb nicht umbrechen. Das ist heute
   das tragende Schloss; nimmt man nur white-space:nowrap heraus, bricht nichts.
   white-space:nowrap ist das zweite: Es gilt auch dann noch, wenn der Menüpunkt einmal kein
   Flex-Kind mehr ist oder jemand an den flex-Werten dreht. Beide zusammen sind die
   Zusicherung: Ein Menüpunkt bricht NIE in zwei Zeilen. Passt die Zeile nicht mehr,
   übernimmt die Schublade (Grenze unten) — dazwischen gibt es nichts.
   tests/darstellung.test.js hält beide Zeilen fest, damit keine als „überflüssig" wegfällt. */
.hr-link{
  font-family:var(--sans);font-size:14px;font-weight:700;
  color:var(--dunkelgrau);padding:8px 10px;border-radius:var(--radius-sm);
  white-space:nowrap;flex:0 0 auto;
  transition:background .15s,color .15s;
}
.hr-link:hover{color:var(--schwarz);background:var(--hover-blue);text-decoration:none}
/* Der Punkt, der auf die Seite zeigt, auf der man steht (seit dem 21.08.2026: „Meine
   Anmeldungen" und „Anmeldungen meiner Firma" sind eigene Seiten und behalten ihren Punkt
   im Menü — er ist dort Wegweiser statt Weg).

   DIE AUSZEICHNUNG DARF DEN PUNKT WEDER BREITER NOCH HÖHER MACHEN, und das ist keine
   Kosmetik: --maxw-kopf (1177px) ist die GEMESSENE Breite, ab der die ganze Leiste in eine
   Zeile passt; wächst ein Punkt, bricht sie um oder rutscht in die Schublade. Deshalb ein
   box-shadow und kein border, und deshalb kein fetterer Schnitt — beides ginge in die
   Kasten­maße ein. e2e/specs/darstellung.spec.js misst genau das nach. */
.hr-link[aria-current="page"]{color:var(--blau);box-shadow:inset 0 -2px 0 var(--blau)}
.hr-link[aria-current="page"]:hover{color:var(--blau)}
/* „Feedback" ist ein KNOPF und kein Link — er öffnet einen Dialog, statt irgendwohin zu
   führen. Der Browser gibt Formularelementen eine eigene Fläche, einen Rahmen und eine
   eigene Zeilenhöhe (die UA-Kurzform `font:` setzt sie auf `normal` zurück); ohne diese
   drei Zeilen stünde er als grauer Kasten zwischen seinen Nachbarn und wäre ein paar Pixel
   niedriger als sie. Dieselbe Überlegung wie bei .footer__melden weiter unten.
   Die Schrift holt er sich aus .hr-link — deshalb hier KEIN `font:inherit`: Das nähme ihm
   Größe und Fettschnitt der Menüpunkte wieder weg. */
.hr-link--knopf{background:none;border:0;cursor:pointer;line-height:inherit}
/* „Anmelden" ist die eine Handlung, die ein Gast hier tun soll — deshalb gefüllt. */
.hr-link--primary{background:var(--blau);color:var(--auf-farbe)}
.hr-link--primary:hover{background:var(--blau-hover);color:var(--auf-farbe)}
/* Der angemeldete Name: kein Link, nur Auskunft — deshalb im Gegensatz zu den Menüpunkten
   NICHT fett. Er ist der längste und der einzige Brocken, dessen Länge wir nicht kennen
   (er kommt als Anspruch aus Entra, heute „Ingo Lücker | ITleague Ltd."). Deshalb ist er
   das einzige Element, das nachgibt: Wird es eng, kürzt ihn der Browser mit Auslassungs-
   punkten statt ihn umzubrechen. Der volle Name steht dann im title — app.js und admin.js
   setzen ihn dort mit. */
.hr-user{
  font-family:var(--sans);font-size:14px;
  color:var(--dunkelgrau);padding:8px 2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:0 1 auto;
}

/* ---------- Der Hell/Dunkel-Umschalter (☀ Hell · A Automatisch · ☾ Dunkel) ----------
   Drei Felder statt eines Kippschalters, und zwar mit ABSICHT: Ein Kippschalter kennt nur
   hell und dunkel und nimmt damit die dritte Möglichkeit weg, die die meisten wollen —
   dem System folgen. „A" ist die Vorgabe und der Zustand, in dem nichts gespeichert ist. */
.theme-switch{
  display:inline-flex;align-items:center;gap:0;flex-shrink:0;
  border:1px solid var(--border-strong);border-radius:10px;overflow:hidden;
}
.theme-seg{
  font-family:inherit;font-size:14px;line-height:1;
  padding:8px 10px;cursor:pointer;background:var(--weiss);color:var(--dunkelgrau);
  border:0;border-right:1px solid var(--border-strong);transition:background .12s,color .12s;
}
.theme-seg:last-child{border-right:none}
.theme-seg:hover{background:var(--hover-blue);color:var(--schwarz)}
.theme-seg--aktiv{background:var(--blau);color:var(--auf-farbe)}

/* Am Desktop gibt es weder Burger noch Verdunkelung; beide gehören zum Drawer unten. */
.burger{display:none}
.drawer-label{display:none}
.drawer-scrim{display:none}

/* ---------- Das Menü auf schmalen Fenstern: dieselbe Leiste wird zur Schublade ----------
   Kein zweites Markup für mobil — #nav ist am Desktop eine Zeile und hier eine von rechts
   einfahrende Schublade. Ein doppelter Menü-Aufbau hätte bedeutet, dass jeder neue
   Menüpunkt an ZWEI Stellen eingetragen werden muss, und die zweite vergisst man.

   DIE GRENZE IST GEMESSEN UND KEINE RUNDE ZAHL — 1176px ist 1177px minus eins, und 1177px
   ist die gemessene Einzeilenbreite aus --maxw-kopf oben (bis zum 21.08.2026 1113px; der
   Menüpunkt „Feedback" hat sie gehoben, die Messung steht dort). Bis zum 20.08.2026 stand
   hier 960px, begründet mit „der breiteste Fall bricht ab etwa 900px um". Das war zweifach
   falsch: Er brach bei JEDER Fensterbreite um, weil nicht das Fenster, sondern der Deckel
   von .header-inner die Grenze setzte — und 960px lag rund 290px zu niedrig, um ihn je in
   eine Zeile zu lassen.

   Die Zusicherung dahinter: Das Menü bricht NIE um. Oberhalb dieser Grenze steht es in
   einer Zeile, unterhalb übernimmt die Schublade — dazwischen gibt es nichts.
   Deshalb steht dieselbe Zahl in js/kopfleiste.js (DESKTOP): Liefen die beiden auseinander,
   gäbe es ein Fenster, in dem weder Zeile noch Schublade brauchbar ist.

   Nur mit --drawer: Die Rechts-, Bestätigungs- und Bewertungsseiten haben gar kein Menü,
   sondern nur den Umschalter. Ohne diese Bedingung schöbe die Regel ihn auf dem Telefon in
   eine Schublade, die niemand aufmachen kann — es gibt dort keinen Burger. */
@media (max-width:1176px){
  .burger{
    display:inline-flex;flex-direction:column;justify-content:center;gap:5px;
    width:44px;height:40px;padding:0 11px;
    background:transparent;border:1px solid var(--border-strong);border-radius:10px;
    cursor:pointer;position:relative;z-index:120;
  }
  .burger span{display:block;width:100%;height:2px;background:var(--schwarz);border-radius:2px;
    transition:transform .22s ease,opacity .18s ease}
  /* Offen wird aus den drei Strichen ein Kreuz — dasselbe Ding schließt wieder. */
  .burger[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
  .burger[aria-expanded="true"] span:nth-child(2){opacity:0}
  .burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

  /* Genau diese vier Seiten ohne Schublade (Impressum, Datenschutz, Bestätigung, Bewertung)
     sind der Grund für den Umbruch hier: Bei ihnen kann nichts „übernehmen", wenn es eng
     wird, und seit die Menüpunkte nowrap tragen, stünde „Zur App" auf einem Telefon über
     der Fensterkante (gemessen 417px Inhalt in einem 390px-Fenster). Mit flex-wrap rutscht
     stattdessen die rechte Gruppe unter das Logo. Das widerspricht der Zusicherung oben
     NICHT: Sie gilt den Menüpunkten, und die stehen auch hier je in einer Zeile — nur die
     Leiste selbst wird zweizeilig.
     Auf den Seiten MIT Schublade greift die Regel ins Leere: Dort ist sie ab hier
     position:fixed und damit aus dem Fluss. Und oberhalb von 1176px kann gar nichts
     umbrechen, weil es diese Regel dort nicht gibt. */
  .header-inner{flex-wrap:wrap}

  .header-right--drawer{
    position:fixed;top:0;right:0;bottom:0;width:82%;max-width:308px;
    flex-direction:column;align-items:stretch;gap:2px;
    background:var(--weiss);border-left:1px solid var(--line);
    box-shadow:var(--shadow-md);
    padding:72px 14px 22px;z-index:110;
    transform:translateX(100%);transition:transform .24s ease;overflow-y:auto;
  }
  .header-right--drawer.nav--open{transform:translateX(0)}
  .header-right--drawer .hr-group{flex-direction:column;align-items:stretch;gap:2px}
  .header-right--drawer .hr-session{border-top:1px solid var(--line);margin-top:8px;padding-top:10px}
  /* In der Schublade sind die Menüpunkte Zeilen und keine Wörter nebeneinander: volle
     Breite, linksbündig und hoch genug für einen Daumen.
     Und hier darf umbrochen werden — die Zusicherung „nie umbrechen" gilt der einzeiligen
     Leiste. In einer Schublade von höchstens 308px wäre ein erzwungenes nowrap das
     Gegenteil von hilfreich: Der Text stünde dann über den Rand hinaus. */
  .header-right--drawer .hr-link,
  .header-right--drawer .hr-user{
    display:block;width:100%;padding:12px 12px;color:var(--schwarz);
    white-space:normal;
  }
  .header-right--drawer .hr-link--primary{color:var(--auf-farbe);text-align:left}
  /* Ein <button> zentriert seinen Text von Haus aus. In der Zeile am Desktop fällt das nicht
     auf (er ist genau so breit wie sein Text), in der Schublade aber sehr wohl: Dort ist er
     display:block auf voller Breite, und „Feedback" stünde mittig zwischen linksbündigen
     Nachbarn. Derselbe Grund wie bei .hr-link--primary eine Zeile höher. */
  .header-right--drawer .hr-link--knopf{text-align:left}
  /* Der Name wird hier NICHT gekürzt: In der Schublade ist Höhe da, Breite nicht — ein
     langer Name darf also umbrechen statt in Auslassungspunkten zu enden. */
  .header-right--drawer .hr-user{
    color:var(--dunkelgrau);overflow:visible;text-overflow:clip;overflow-wrap:anywhere;
  }
  /* Die Überschrift über dem Umschalter — in der Zeile am Desktop wäre sie Ballast, in der
     Schublade sagt sie, wozu die drei Zeichen gut sind. */
  .header-right--drawer .drawer-label{display:block;font-size:11px;font-weight:700;letter-spacing:.6px;
    text-transform:uppercase;color:var(--dunkelgrau);margin:2px 2px 4px}
  .header-right--drawer .theme-switch{align-self:stretch}
  .header-right--drawer .theme-seg{flex:1;text-align:center;padding:11px 0;font-size:15px}

  .drawer-scrim{
    display:block;position:fixed;inset:0;background:var(--overlay);z-index:105;
    opacity:0;pointer-events:none;transition:opacity .24s ease;
  }
  .drawer-scrim.nav--open{opacity:1;pointer-events:auto}
  body.drawer-open{overflow:hidden}
}

.wrap{max-width:var(--maxw);margin:0 auto;padding:clamp(20px,4vw,36px) clamp(16px,4vw,28px)}

/* ---------- Platz für den Inhalt, der erst nach /api kommt (29.08.2026) ----------
   DER GEMESSENE FEHLER: Die Startseite malt zuerst Kopfleiste, Überschrift, Suchfeld — und
   dann sofort die Fußzeile, denn #eventGrid ist leer. Die Fußzeile steht damit auf halber
   Höhe im Bild. Eine Sekunde später kommt die Antwort von /api/public/events, die Kacheln
   schieben sie um mehrere hundert Punkte nach unten, und genau DAS meldet Lighthouse als
   „Avoid large layout shifts" und „Layout shift culprits". Gemessen am 29.08.2026 (Chrome,
   300 ms/780 kbit): index.html mobil 0,088 — Quelle in jedem einzelnen Eintrag:
   footer.footer. Dieselbe Bauart auf meine-anmeldungen, profil, sichtbarkeit,
   firmen-anmeldungen (je 0,106) und im Verwaltungsbereich (0,70).

   DIE ABHILFE IST HÖHE, DIE VON ANFANG AN DA IST. 100vh ist dabei kein gegriffener Wert,
   sondern die Grenze, ab der es überhaupt wirkt: Ein Sprung zählt nur, soweit er im
   SICHTBAREN Bereich stattfindet. Steht die Fußzeile schon beim ersten Bild unterhalb des
   Fensterrandes, rutscht sie später von unsichtbar nach unsichtbar — der Zähler bleibt bei
   null. Eine kleinere Reserve (etwa 60vh) ließe sie sichtbar am unteren Rand stehen und
   spart nur einen Teil des Sprungs.

   DAUERHAFT UND NICHT NUR WÄHREND DES LADENS: Eine Reserve, die nach dem Zeichnen wieder
   verschwindet, IST der Sprung — nur in die andere Richtung. Der Preis ist sichtbar und
   bewusst bezahlt: Auf einer kurzen Seite steht die Fußzeile jetzt eine Bildschirmhöhe
   unter dem Inhalt statt direkt darunter. Das ist der übliche Anblick einer Anwendung, und
   es kostet niemanden etwas — die Fußzeile trägt Impressum und Datenschutz, nichts, wonach
   jemand beim Lesen sucht.

   AUSGENOMMEN SIND DIE SEITEN OHNE NACHLADEN, und zwar als Ausnahme und nicht als Auswahl:
   Wer eine sechzehnte Seite baut, bekommt die Reserve, ohne daran zu denken. .legal sind die
   fünf Rechtstexte — Impressum, Datenschutz und seit dem 30.08.2026 die drei Seiten zu den
   Veranstaltungsbedingungen (Wortlaut, Fassungsarchiv, abgelöste V4.0); ihr Text steht
   jeweils vollständig im Dokument, sie laden nichts nach. .fb sind bestaetigen.html,
   feedback.html und seit dem 02.09.2026 verpflegung.html — die drei Seiten, die aus einer
   Mail heraus geöffnet werden: eine Karte in Lesebreite, und darunter steht nichts mehr,
   was springen könnte. Die beiden älteren Rechtstexte und die beiden älteren .fb-Seiten
   sind ohne Sprung gemessen (CLS 0,000), die drei neuen Rechtstexte und verpflegung.html
   sind derselben Bauart — und eine Bildschirmhöhe Leere unter einem Danke-Satz wäre nur
   Leere.
   tests/bildmasse.test.js hält beide Listen gegeneinander. */
main.wrap:not(.legal):not(.fb){min-height:100vh}

/* ---------- Der Verwaltungsbereich nutzt den ganzen Bildschirm ----------
   --maxw (1120px) ist eine LESEBREITE, keine Seitenbreite: Fliesstext wird ab etwa 90
   Zeichen je Zeile muehsam zu lesen, und die oeffentliche Seite besteht aus Text.
   Der Verwaltungsbereich besteht aus TABELLEN — die Anmeldungsliste hat elf Spalten.
   Dort war die Lesebreite schaedlich: Sie zwang die Tabelle in ihren Rollbereich, obwohl
   rechts und links Bildschirm frei blieb. Man scrollte also seitwaerts vor einem halb
   leeren Monitor — genau das hat der Auftraggeber am 30.07.2026 gemeldet, einen Tag
   nachdem der Rollbereich ueberhaupt erst eingezogen wurde.
   Der Rollbereich BLEIBT trotzdem: Er ist die Abhilfe fuer den schmalen Bildschirm, und
   dort greift er weiterhin. Hier faellt nur der kuenstliche Riegel auf breiten Geraeten. */
body.admin .wrap,
body.admin .footer{max-width:none}
/* Die Kopfleiste geht mit. Ohne diese Zeile stuende das Logo auf 1120px eingerueckt, waehrend
   die Tabelle darunter bis an den Bildschirmrand laeuft — es saehe aus, als haetten Leiste
   und Inhalt nichts miteinander zu tun. */
body.admin .header-inner{max-width:none}
/* Der Fliesstext behaelt seine Lesebreite. Die Erklaerkaesten ueber jeder Liste sind der
   einzige laengere Text im Verwaltungsbereich; ueber 2000px gezogen waeren sie
   unlesbar — und ein Erklaertext, den niemand liest, erklaert nichts. */
body.admin .note{max-width:var(--maxw)}
.wrap>h2{font-size:clamp(22px,3vw,30px)}

/* ---------- Tabs (editorial underline) ---------- */
.tabs{display:flex;gap:24px;margin:18px 0 6px;border-bottom:1px solid var(--line);overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}
.tab{
  appearance:none;background:none;border:0;cursor:pointer;
  padding:0 0 12px;margin-bottom:-1px;white-space:nowrap;
  font-family:var(--mono);font-size:12px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--dunkelgrau);border-bottom:2px solid transparent;transition:color .15s,border-color .15s;
}
.tab:hover{color:var(--schwarz)}
.tab--active{color:var(--blau);border-bottom-color:var(--blau)}
/* Der erste Reiter einer Gruppe traegt die Grenze (siehe admin.html). Ein senkrechter
   Strich und kein zusaetzlicher Abstand allein: Abstand hiesse in einer Leiste, die
   waagrecht SCROLLT (overflow-x oben), nur „hier ist mehr Platz" — die Gruppe waere am
   Rand des Sichtfensters nicht mehr zu erkennen. Der Strich geht bis zur Unterlinie der
   Leiste hinunter und bildet mit ihr ein T; das haelt ihn auch unter dem aktiven Reiter
   ruhig. Kein Umbruch moeglich, also sitzt er immer dort, wo er hingehoert. */
.tab--gruppe{padding-left:12px;border-left:1px solid var(--line)}
/* Die Zahl am Reiter „Anliegen": wie viele Meldungen noch auf „neu" stehen. Sie ERSETZT die
   Benachrichtigung, die es bewusst nicht gibt (ein anonymes Freitextformular mit Mailversand
   wäre ein Relais in ein echtes Arbeitspostfach) — deshalb muss sie sichtbar sein, ohne dass
   jemand den Reiter öffnet. Sie steht nur da, wenn wirklich etwas offen ist; eine dauerhafte
   „0" wäre Lärm. */
.tab__zahl{
  display:inline-block;margin-left:6px;padding:1px 6px;border-radius:999px;
  background:var(--blau);color:var(--auf-farbe);
  font-size:10.5px;letter-spacing:0;vertical-align:1px;
}

/* ---------- Toolbar / Formularelemente ---------- */
.toolbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:20px 0}
input,select,textarea{font-family:var(--sans);font-size:14px;color:var(--schwarz)}
/* input[type=email] stand hier bis zum 31.07.2026 NICHT — die E-Mail-Felder der Anwendung
   waren als einzige ohne Rahmen und ohne Innenabstand. Aufgefallen ist es erst, als im
   Anmeldeformular zwei davon zwischen sieben normalen Feldern standen. */
input[type=search],input[type=text],input[type=email],input:not([type]),select,textarea{
  padding:10px 12px;border:1px solid var(--line);border-radius:var(--radius-sm);
  background:var(--weiss);transition:border-color .15s,box-shadow .15s;
}
/* DER PLATZHALTER BRAUCHT EINE EIGENE FARBE. Bis zum 29.08.2026 hatte er keine — dann nimmt
   der Browser seine eigene (rund #757575), und die bleibt in beiden Darstellungen dieselbe:
   color-scheme:dark hellt den Platzhalter NICHT mit auf. Auf dem hellen Feld geht das gerade
   noch durch, auf dem dunklen (#1E242A) kommt sie auf 3,40:1 und liegt damit unter den 4,5:1,
   die kleiner Text braucht. Das betraf JEDES Eingabefeld der Anwendung, nicht eine Stelle.
   Mit --dunkelgrau folgt der Platzhalter der Palette: hell 5,7:1, dunkel 6,1:1 auf der
   Feldfläche. tests/bausteine.test.js rechnet beide Werte aus der Palette nach.
   `opacity:1` steht ausdrücklich dabei: Firefox legt von sich aus eine Deckkraft unter 1
   darüber und machte den gerechneten Wert damit wieder schlechter. */
input::placeholder,textarea::placeholder{color:var(--dunkelgrau);opacity:1}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--blau);box-shadow:0 0 0 3px var(--ring)}
input[type=search]{min-width:240px}
label{font-size:14px}

/* ---------- Event-Grid + Karten ---------- */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:20px;margin-top:8px}
.card{
  display:flex;flex-direction:column;background:var(--weiss);
  border:1px solid var(--line-soft);border-radius:var(--radius);overflow:hidden;cursor:pointer;
  box-shadow:var(--shadow-sm);transition:transform .16s ease,box-shadow .16s ease,border-color .16s ease;
}
.card:hover{transform:translateY(-3px);box-shadow:var(--shadow-md);border-color:var(--blau-line)}
.card__img{width:100%;aspect-ratio:16/9;height:auto;object-fit:cover;background:var(--surface-2)}
.card__ph{
  display:flex;align-items:center;justify-content:center;aspect-ratio:16/9;height:auto;position:relative;
  color:var(--auf-farbe);font-family:var(--mono);font-weight:700;font-size:42px;letter-spacing:.04em;
}
/* Der Glanz liegt IMMER auf einer gesaettigten Marken-Flaeche (blau oder gruen) und
   bleibt deshalb in beiden Darstellungen echtes Weiss — die Flaeche darunter wechselt ja
   nicht die Helligkeit. */
.card__ph::after{content:"";position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,.16),transparent 55%)}
.card__ph--blau{background:var(--blau)}
/* --gruen-text und nicht --gruen: Auf dieser Fläche stehen Initialen in Weiß, und in der
   Teilnehmerliste (.tnl__ph) sind sie mit 15px klein. Mit dem CI-Grün waren das 3,34:1. */
.card__ph--gruen{background:var(--gruen-text)}
.card__body{padding:16px 16px 18px;display:flex;flex-direction:column;gap:8px;flex:1}
.card__eyebrow{font-family:var(--mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--dunkelgrau)}
.card__title{font-size:17.5px;font-weight:700;letter-spacing:-.01em;line-height:1.28}
.card__meta{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin-top:auto;font-size:13.5px;color:var(--dunkelgrau)}

/* ---------- Überschrift mit Umschalter daneben ---------- */
/* Der Knopf „Mich für mehrere Veranstaltungen gleichzeitig anmelden" steht rechtsbündig
   neben der Überschrift.
   flex-wrap ist kein Zierrat, sondern seit dem 27.08.2026 die halbe Miete: Die Beschriftung
   ist an dem Tag von 28 auf 53 Zeichen gewachsen (der Auftraggeber wollte, dass der Knopf
   genau verspricht, was damit geht). Damit passt er auch auf einem breiten Bildschirm nur
   knapp neben die Überschrift und auf einem Telefon gar nicht — ohne den Umbruch stünde er
   halb draußen. Wer die Beschriftung wieder kürzt, darf den Umbruch trotzdem stehen lassen:
   Er kostet nichts, solange nichts umbricht. */
.seitenkopf{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px}
.seitenkopf h2{margin:0}

/* ---------- Die Liste zum Ankreuzen (zweite Darstellung derselben Veranstaltungen) ---------- */
/* SIE SITZT IM SELBEN KASTEN WIE DIE KACHELN (#eventGrid) und tauscht nur die Klasse: Es ist
   dieselbe Liste, nur anders gezeichnet — so steht es im abgenommenen Entwurf („Keine neue
   Seite"). Deshalb hier auch kein margin-top-Sprung gegenüber .grid. */
.liste{display:block;margin-top:8px;border:1px solid var(--line-soft);border-radius:var(--radius);background:var(--weiss);overflow:hidden}
.termin{display:flex;align-items:center;flex-wrap:wrap;gap:10px;padding:10px 14px;border-bottom:1px solid var(--line-soft)}
.termin:last-child{border-bottom:none}
.termin input[type=checkbox]{width:16px;height:16px;accent-color:var(--blau);flex:0 0 auto}
/* Der Titel darf wachsen, Datum und Preis bleiben stehen — sonst rutscht der Preis bei einem
   langen Titel aus der Zeile. min-width:0 gehört zwingend dazu: Ein flex-Kind bricht sonst
   nicht um, sondern schiebt die Zeile breiter. */
.termin__wer{flex:1 1 240px;min-width:0;display:flex;flex-direction:column;gap:2px;font-size:14px;cursor:pointer}
.termin__titel{font-weight:600;color:var(--schwarz);overflow-wrap:anywhere}
.termin__datum{font-family:var(--mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--dunkelgrau)}
.termin__preis{flex:0 0 auto;font-size:13.5px;color:var(--dunkelgrau)}
/* Eine gesperrte Zeile wird gedämpft, aber NICHT versteckt — sie beantwortet die Frage,
   warum hier nichts anzukreuzen ist. Der Grund steht daneben, wo sonst der Preis steht. */
.termin--zu .termin__titel{color:var(--dunkelgrau);font-weight:500}
.termine__leiste{display:flex;justify-content:flex-end;margin:14px 0}

/* ---------- Badges (Mono, Status-Punkt) ---------- */
.badge{
  display:inline-flex;align-items:center;gap:6px;padding:3px 10px 3px 8px;border-radius:var(--pill);
  font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;white-space:nowrap;
}
.badge::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
.badge--ok,.badge--confirmed{background:var(--gruen-tint);color:var(--tint-green-text)}
.badge--pending{background:var(--blau-tint);color:var(--blau-dark)}
.badge--waitlist,.badge--waitlisted{background:var(--warn-tint);color:var(--warn)}
.badge--full,.badge--rejected,.badge--cancelled{background:var(--danger-tint);color:var(--danger)}
/* Die Zimmerangabe auf der Kachel (Anliegen 19, 01.09.2026). EIGENE NAMEN, obwohl die
   Farbpaare denen von .badge--confirmed und .badge--waitlisted gleichen: Wer spaeter die
   Wartelistenfarbe wechselt, soll nicht stillschweigend die Zimmerplakette mitziehen. Sie
   sagt etwas anderes aus und darf ihre Farbe unabhaengig behalten. */
.badge--zimmer-ja{background:var(--gruen-tint);color:var(--tint-green-text)}
.badge--zimmer-nein{background:var(--warn-tint);color:var(--warn)}
/* Die einzige Plakette ohne Signalfarbe. Als Flaeche der neutrale Ton und NICHT --line-soft:
   Das ist eine Rahmenfarbe, und im Dunkeln waere sie von der Karte darunter nicht zu
   unterscheiden gewesen — die Plakette haette dort schlicht gefehlt. */
.badge--kein_interesse{background:var(--tint-neutral);color:var(--tint-neutral-text)}
/* Die eigene Anmeldung auf einer Veranstaltungskachel: dieselbe Farbe wie sonst, aber als
   Umriss statt gefuellt. Grund: Neben ihr steht die Platz-Plakette, und „Noch 17 Plaetze"
   ist ebenfalls gruen — zwei gleich aussehende Plaketten nebeneinander unterscheidet man
   nur, indem man beide liest. Die Form trennt sie auf einen Blick: gefuellt = die
   Veranstaltung, Umriss = du. */
.badge--mine{background:transparent;box-shadow:inset 0 0 0 1px currentColor}

/* ---------- „E-Mail nicht bestätigt" an der Status-Zelle ---------- */
/* Das Merkmal einer offenen Anmeldung, deren Bestätigungslink noch nicht geklickt wurde
   (Double Opt-In). Es steht direkt unter der Status-Plakette.
   KEINE Plakette, obwohl es sich anböte — zweimal ausprobiert und zweimal verworfen:
   .badge verbietet den Umbruch, und dieser Text ist der längste im ganzen Haus. Mit Umbruch
   wird die Pille zu einem dreizeiligen Oval mit einem Punkt daneben; ohne Umbruch zwingt sie
   die Status-Spalte der Anmeldungsliste so weit auf, dass deren zwölf Spalten auf einem
   1920er Bildschirm wieder seitwärts rollen — genau die Beschwerde vom 30.07.2026, die ein
   Klickweg seither festhält.
   Die Schrift ist trotzdem die der Plaketten (Mono, 11px, versal): Damit gehört das Merkmal
   sichtbar zur selben Familie, ohne ihre Form zu erzwingen. Die Warnfarbe trägt es allein. */
.unbestaetigt{
  display:block;margin-top:5px;
  font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;
  line-height:1.35;color:var(--warn);
}
/* Derselbe Hinweis-Strich, nur in Rot: In der Teilnehmertabelle einer Veranstaltung steht er
   unter der Tarif-Auswahl, sobald eine BESTÄTIGTE Anmeldung keinen Tarif trägt — sie zählt
   dann mit 0,00 € ins Potenzial der Summenzeile darunter, und ohne diesen Strich müsste man
   alle Auswahlfelder durchsehen, um die Lücke zu finden. Rot und nicht bernstein wie
   .unbestaetigt darüber: Beide können in derselben Zeile stehen, und es sind zwei
   verschiedene Befunde.
   EINE KLASSE UND KEIN style-MERKMAL. Handbemalung mit color:var(--danger) gibt es in den
   Admin-Dateien nicht mehr — tests/aktionen-veranstaltungen.test.js wacht darüber, weil zwei
   Wege für dieselbe Farbe heißen, dass nur einer davon auffindbar ist. */
.tarif-fehlt{
  display:block;margin-top:5px;
  font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;
  line-height:1.35;color:var(--danger);
}

/* Suchtreffer */
mark.hl,.hl{background:var(--hl-bg);color:var(--hl-text);padding:0 2px;border-radius:3px;box-decoration-break:clone;-webkit-box-decoration-break:clone}

/* ---------- Buttons ---------- */
.btn{
  appearance:none;cursor:pointer;font-family:var(--sans);font-weight:600;font-size:14px;
  padding:9px 16px;border-radius:var(--radius-sm);border:1px solid var(--line);background:var(--weiss);color:var(--schwarz);
  transition:background .15s,border-color .15s,color .15s,transform .05s;
}
.btn:hover{border-color:var(--dunkelgrau)}
.btn:active{transform:translateY(1px)}
.btn--primary{background:var(--blau);border-color:var(--blau);color:var(--auf-farbe)}
.btn--primary:hover{background:var(--blau-hover);border-color:var(--blau-hover)}
.btn--ghost{background:transparent;border-color:var(--line);color:var(--dunkelgrau)}
.btn--ghost:hover{color:var(--schwarz);border-color:var(--dunkelgrau)}
.btn:disabled{opacity:.5;cursor:not-allowed}

/* ---------- Die Seite EINER Veranstaltung (veranstaltung.html) ----------

   HIER STECKT DIE EIGENTLICHE ANTWORT AUF DEN WUNSCH DES AUFTRAGGEBERS. Er lautete
   woertlich: „bisher oeffnen sich veranstaltung als popup. koennen wir es so machen, dass
   sich eine eigene Seite oeffnet? Dann haben wir mehr Platz." Der Dialog war auf 740px
   gedeckelt (.modal__box unten).

   EINE BREITERE SPALTE WAERE KEINE ANTWORT GEWESEN. Fliesstext wird ab etwa 90 Zeichen je
   Zeile muehsam zu lesen — eine Beschreibung ueber 1120px gezogen waere schlechter als im
   Dialog, nicht besser. Der Platz geht deshalb nicht in die BREITE des Textes, sondern in
   eine zweite SPALTE: Beschreibung und Agenda links, die Anmeldung rechts daneben.

   Der Gewinn ist nicht Geschmack, sondern messbar: Das Anmeldeformular hat seit dem
   31.07.2026 acht Pflichtfelder und ist laenger als ein Bildschirm. Im Dialog stand es
   UNTER der Beschreibung — wer sich anmelden wollte, musste erst an allem vorbeirollen,
   was er vielleicht gar nicht lesen wollte. Jetzt steht es von Anfang an im Bild.

   DIE GRENZE IST 1040px UND NICHT --maxw: Darunter ist eine 380px-Spalte neben einem Text
   kein Gewinn mehr, sondern zwei zu schmale Spalten. Unterhalb steht alles wieder
   untereinander — genau in der Reihenfolge des Quelltextes und damit genau so wie bisher
   im Dialog. Auf dem Telefon aendert sich also nichts.

   `position:sticky` steht bewusst NICHT am Anmeldeblock: Er ist hoeher als ein flaches
   Notebook-Fenster, und ein klebender Block, dessen Knopf unten abgeschnitten ist, waere
   nicht zu bedienen. */
.vst>h2{font-size:clamp(22px,3vw,30px);margin-bottom:6px}
.vst>.card__meta{margin-bottom:4px}
/* Der Weg zurueck zur Liste. Er steht GANZ OBEN im Kasten — dort, wo im Dialog „Schliessen"
   stand — und in jedem Zustand der Ansicht: beim Laden, im Fehlerfall und an der fertigen
   Veranstaltung. */
.vst__zurueck{margin:0 0 14px}

@media (min-width:1040px){
  .vst{
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,400px);
    column-gap:44px;
    align-items:start;
  }
  /* Voreinstellung: ueber beide Spalten. Das gilt fuer den Weg zurueck, den Titel und die
     Kopfdaten — sie gehoeren der ganzen Veranstaltung und nicht einer Spalte.
     Auch fuer alles, was die Seite in Sonderlagen selbst zeichnet („gibt es nicht mehr"):
     Dort gibt es keine zweite Spalte, und ein halbbreiter Absatz saehe nach einem Fehler aus. */
  .vst>*{grid-column:1 / -1}
  .vst>.vst__haupt{grid-column:1}
  /* Die zweite Spalte. Sie landet durch die Auto-Platzierung von selbst NEBEN .vst__haupt:
     Beide sind die letzten Kinder, die erste Huelle belegt Spalte 1 ihrer Zeile, und die
     zweite passt genau in die Luecke daneben. Deshalb duerfen die beiden Huellen auch nicht
     wegfallen — ohne sie waere jeder Absatz ein eigenes Rasterfeld (siehe zeichne() in
     js/veranstaltung-ansicht.js). */
  .vst>.vst__anmeldung{
    grid-column:2;
    background:var(--surface-2);border:1px solid var(--line-soft);border-radius:var(--radius);
    padding:4px 20px 20px;
  }
  /* Die Trennlinie trennt im Einspalter die Veranstaltung von der Anmeldung. Nebeneinander
     hat sie nichts mehr zu trennen — der Kasten tut es besser. */
  .vst>.vst__anmeldung>hr{display:none}
  /* Der erste Abschnitt braucht seine obere Trennlinie im Kasten nicht: Er steht schon oben. */
  .vst>.vst__anmeldung>.reg__group:first-of-type{border-top:0;margin-top:0}
}

/* ---------- Die drei aufklappbaren Abschnitte der Veranstaltungsseite ----------

   Agenda, „Anreise & Check-in" und die Teilnehmerliste. Der Wunsch des Auftraggebers,
   woertlich: „Ich haette gerne, dass man die Agenda auf- und zuklappen kann, ebenso die
   Anreise & CheckIn Part auf- und zuklappen. Und auch die Teilnehmerliste."

   ES IST EIN <details>/<summary> UND KEIN NACHGEBAUTER UMSCHALTER — die Begruendung steht
   bei klappAbschnitt() in js/veranstaltung-ansicht.js. Fuer das Stylesheet folgt daraus nur
   eines: Der Pfeil links ist HIER gezeichnet, weil `display:flex` am summary den
   eingebauten Marker in mehreren Browsern verschluckt. Ohne ihn saehe die Kopfzeile aus wie
   eine Ueberschrift und nicht wie etwas, das man anklicken kann.

   ALLE DREI STEHEN ZU (kein `open` im Markup) — die Wahl des Auftraggebers, damit die Seite
   wieder auf einen Bildschirm passt. */
.klapp{
  margin:18px 0 0;background:var(--weiss);
  border:1px solid var(--line-soft);border-radius:var(--radius);
}
.klapp__kopf{
  display:flex;align-items:center;flex-wrap:wrap;gap:2px 12px;
  padding:12px 16px;cursor:pointer;list-style:none;border-radius:var(--radius);
}
/* Beide Zeilen zusammen nehmen den eingebauten Marker weg: `list-style` fuer die
   Standard-Browser, die ::-webkit-Zeile fuer die aelteren WebKits. */
.klapp__kopf::-webkit-details-marker{display:none}
.klapp__kopf:hover{background:var(--hover-blue)}
.klapp__kopf:focus-visible{outline:2px solid var(--blau);outline-offset:-2px}
/* Der Pfeil: ein gedrehtes Winkelstueck, nach rechts wenn zu, nach unten wenn offen.
   `flex:0 0 auto`, damit er beim Umbruch der Kopfzeile nicht schrumpft. */
.klapp__kopf::before{
  content:"";flex:0 0 auto;width:7px;height:7px;margin-right:4px;
  border-right:2px solid var(--dunkelgrau);border-bottom:2px solid var(--dunkelgrau);
  transform:rotate(-45deg);transition:transform .15s ease;
}
.klapp[open]>.klapp__kopf::before{transform:rotate(45deg)}
/* Die Ueberschrift steht IM summary und traegt deshalb keinen eigenen Abstand — die
   Kopfzeile bringt ihn mit. Groesse wie .content h2, damit sie aussieht wie die
   Ueberschriften darueber. */
.klapp__titel{font-size:19px;margin:0}
/* Was hinter dem Klappen steckt, in derselben gedaempften Schrift wie die Kopfdaten der
   Veranstaltung (.card__meta). Bewusst OHNE Versalien: Hier stehen ganze Angaben
   („55 angemeldet · 48 zeigen ihren Namen", „Mit dem Auto · Mit der Bahn"), und die liest
   niemand gern in Grossbuchstaben. */
.klapp__meta{font-size:13.5px;color:var(--dunkelgrau);min-width:0;overflow-wrap:anywhere}
.klapp__inhalt{padding:0 16px 16px}
/* Der erste Absatz im aufgeklappten Teil braucht seinen oberen Abstand nicht: Die Kopfzeile
   hat ihren eigenen. Ohne diese Zeile klaffte beim Aufklappen eine Luecke. */
.klapp__inhalt>.content>:first-child{margin-top:0}

/* Der Teilnehmer-Abschnitt fuer die, die die Liste (noch) nicht sehen duerfen — ein Satz
   statt einer Liste (sperrBlock in js/veranstaltung-ansicht.js).

   ER KLAPPT AUSDRUECKLICH NICHT: Ein Hinweis hinter einer Klappe ist kein Hinweis — man
   muesste ihn erst aufklappen, um zu erfahren, dass es nichts aufzuklappen gibt. Deshalb
   dieselbe Huelle (.klapp), aber kein <details>, kein Pfeil, kein Zeigefinger. Der Abstand,
   den sonst .klapp__kopf und .klapp__inhalt zusammen bringen, steht hier am Kasten selbst. */
.klapp--hinweis{padding:12px 16px 16px}
.klapp--hinweis>.klapp__titel{margin-bottom:6px}
.klapp--hinweis>.note{margin:0;max-width:62ch}

/* ---------- Die drei Sprungziele aus dem Kalendereintrag (Anliegen 24) ----------

   #agenda, #anreise und #teilnehmer führen aus dem Kalendereintrag direkt auf diese drei
   Abschnitte (js/sprungziele.js). Der Sprung setzt ihre Oberkante an die Oberkante des
   Fensters — dort steht aber die klebende Kopfleiste (header, position:sticky, top:0).
   Ohne diese Zeile verschwindet die Überschrift des angesprungenen Abschnitts dahinter, und
   der Betrachter sieht Inhalt ohne die Zeile, die sagt, worum es geht. Genau das Gegenteil
   von „den Abschnitt vor Augen haben", wofür der Sprung gebaut ist.

   DIE HÖHE STEHT HIER UND NICHT IM SKRIPT: Sie ist eine Eigenschaft der Leiste, und die
   Leiste steht in dieser Datei. Ein Rechenwert in sprungziele.js wäre die zweite Fassung
   derselben Zahl und liefe beim nächsten Umbau der Kopfleiste still davon.

   DIE 12px SIND DIE POLSTERUNG DER KLAPPZEILE (.klapp__kopf oben) und keine gegriffene
   Zugabe: So viel Luft hat die Überschrift im Kasten auch nach oben, und so viel bekommt der
   Kasten damit zur Leiste.

   DREI KENNUNGEN UND KEINE KLASSE — dieselbe Entscheidung wie bei der Tabelle ZIELE in
   js/sprungziele.js: Was anspringbar ist, soll man aufzählen und nicht erraten. Ein
   `.klapp{scroll-margin-top:…}` träfe auch jeden Abschnitt, den niemand anspringen kann.
   Wer ein viertes Ziel aufnimmt, trägt es dort UND hier ein. */
#agendaKlapp,#anreiseKlapp,#teilnehmerKlapp{scroll-margin-top:calc(var(--kopf-hoehe) + 12px)}

/* ---------- Die Teilnehmerliste als Kachelraster ----------

   Der Auftraggeber, woertlich: „Und auch die Teilnehmerliste. Die finde ich derzeit ja total
   schwer zu lesen." Vorher stand dort ein nacktes Raster aus „Name · Firma" ohne Trennung
   und ohne Bild — bei 55 Zeilen eine Wand.

   DIE REGELN STEHEN HIER UND NICHT MEHR ALS style="…" IM ERZEUGTEN TEXT. Ein Inline-Stil
   laesst sich weder ueberschreiben noch wiederverwenden; dieselbe Entscheidung wie bei der
   Liste der Startseite (.termin, .liste) seit 1.61.0.

   `minmax(min(220px,100%),1fr)` UND NICHT `minmax(220px,1fr)`: Das min() ist der Unterschied
   zwischen „bricht auf dem Telefon um" und „schiebt die Seite seitlich hinaus". In einem
   Kasten, der schmaler als 220px ist, waere die Spur sonst breiter als ihr Platz. */
.tnl{
  list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(auto-fill,minmax(min(220px,100%),1fr));gap:10px;
}
.tnl__zelle{margin:0;min-width:0}
/* Die Kachel selbst. Sie ist mit Foto ein <button> und ohne Foto ein <div> — deshalb setzt
   diese Regel Schrift, Farbe und Ausrichtung ausdruecklich zurueck: Ein Knopf erbt beides
   nicht von allein, und die beiden Bauarten muessen gleich aussehen. */
.tnl__kachel{
  display:flex;align-items:center;gap:10px;width:100%;min-width:0;
  padding:8px 10px;border:1px solid var(--line-soft);border-radius:var(--radius-sm);
  background:var(--weiss);color:var(--schwarz);font:inherit;text-align:left;
}
/* NUR die Kachel mit Foto reagiert — sie ist die einzige, die etwas zu zeigen hat. Eine
   Kachel ohne Foto, die auf Zeigen und Klicken antwortet, verspraeche ein Bild, das es
   nicht gibt. */
.tnl__kachel--foto{cursor:pointer;transition:border-color .16s ease,box-shadow .16s ease}
.tnl__kachel--foto:hover{border-color:var(--blau-line);box-shadow:var(--shadow-sm)}
.tnl__kachel--foto:focus-visible{outline:2px solid var(--blau);outline-offset:2px}
.tnl__bild{flex:0 0 auto;width:44px;height:44px;border-radius:50%;object-fit:cover}
/* DIE GRAUE FLAECHE GILT NUR FUER DAS <img> und ausdruecklich nicht fuer den Platzhalter
   daneben — sie ist der Grund, auf dem ein noch nicht geladenes Foto steht. Stuende sie an
   .tnl__bild, gewaenne sie gegen .card__ph--blau/--gruen (gleiche Spezifitaet, aber weiter
   unten in der Datei), und die Initialen saessen auf Grau statt auf der Markenflaeche.
   Genau so war es eine Fassung lang, und man sah es erst auf dem Bildschirmfoto. */
img.tnl__bild{background:var(--surface-2)}
/* Der Platzhalter mit den Initialen. Die FARBE kommt aus .card__ph--blau/--gruen und damit
   aus derselben Quelle wie die Kachel der Startseite (js/app.js, cardHtml) — ein zweites
   Farbpaar liefe beim naechsten Feilen an der Marke von jenem weg. Die FORM gehoert dagegen
   hierher: rund und klein statt 16:9 und gross, denn hier steht ein Mensch. */
.tnl__ph{
  display:flex;align-items:center;justify-content:center;
  color:var(--auf-farbe);font-family:var(--mono);font-weight:700;font-size:15px;letter-spacing:.04em;
}
.tnl__wer{min-width:0;display:flex;flex-direction:column;gap:1px}
/* overflow-wrap:anywhere ist kein Zierrat: Eine lange Firmenschreibweise ohne Leerzeichen
   schoebe die Kachel sonst breiter als ihre Spur, und das Raster verzoege sich. */
.tnl__name{font-size:14.5px;font-weight:600;line-height:1.3;overflow-wrap:anywhere}
.tnl__firma{font-size:13px;color:var(--dunkelgrau);line-height:1.3;overflow-wrap:anywhere}
.tnl__verborgen{margin-top:12px;max-width:62ch}

/* ---------- Das Foto gross ----------
   Nur Breite und Bild: Verdunkelung, Rollverhalten und Schatten bringt .modal/.modal__box
   mit. Genau darum ist es diese Bauform und keine zweite. */
/* NIE HOCHRECHNEN. Hier stand bis zum 28.08.2026 `width:100%` am Bild, und das zwang jedes
   Foto auf die volle Kastenbreite — auch eines, das kleiner ist. Aufgefallen ist es am
   ersten echten Bestand: Die aus der ITleague-Skilldatenbank uebernommenen Fotos sind
   samt und sonders auf 256 Punkte begrenzt (gemessen an allen, auch am groessten mit
   17,8 kB). Auf 560 Punkte gezogen sah das erwartbar verwaschen aus, und der Auftraggeber
   hat es sofort bemerkt.
   `width:auto` mit `max-width:100%` zeigt ein kleines Bild in seiner echten Groesse —
   scharf statt gross — und begrenzt ein grosses weiterhin auf den Kasten. Der Kasten legt
   sich mit `fit-content` um das Bild, damit ein 256er nicht in einer halbleeren Flaeche
   von 560 Punkten steht.
   `min-width` ist GEWAEHLT und nicht gemessen: Darunter wuerde die Leiste mit Name und
   Schliessen-Knopf umbrechen, und ein Portraet ist schmaler als sein eigener Name. */
.tnl__gross{width:fit-content;min-width:min(280px,92vw);max-width:min(560px,96vw)}
.tnl__gross-bild{
  display:block;width:auto;max-width:100%;max-height:70vh;object-fit:contain;
  border-radius:var(--radius-sm);background:var(--surface-2);
}
.tnl__gross-hinweis{margin:10px 0 0;font-size:14px;color:var(--dunkelgrau)}
/* Die beiden Blaetterknoepfe und die Positionsangabe dazwischen (28.08.2026).
   MINDESTENS 44 PUNKTE IN BEIDE RICHTUNGEN. Das ist nicht frei gewaehlt, sondern das Mass,
   das das Haus ueberall dort ansetzt, wo ein Finger trifft: .fb__check und .fb__price
   tragen es mit derselben Begruendung, der Burger misst 44x40, und genau 44 Punkte ist auch
   das Foto in der Teilnehmerkachel breit (.tnl__bild). Der Auftraggeber hat die Knoepfe
   ausdruecklich fuer Maus UND Finger verlangt — ein Pfeil in Textgroesse waere mit der Maus
   zu treffen und mit dem Daumen nicht.
   UNTER DEM BILD UND NICHT DARUEBER GELEGT: Ein Knopf auf dem Bild verdeckt ein Gesicht, und
   bei den 256 Punkten, die der echte Bestand misst (Begruendung an .tnl__gross-bild), waere
   das ein Sechstel der Breite je Knopf. */
.tnl__blaettern{display:flex;align-items:center;justify-content:center;gap:10px;margin:12px 0 0}
.tnl__pfeil{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:44px;min-height:44px;padding:0;font-size:22px;line-height:1;
}
/* Die Mindestbreite ist GEWAEHLT und nicht gemessen: Sie haelt die beiden Knoepfe ruhig,
   solange die Angabe „9 von 41" lang ist. Wird die Zahl dreistellig, ruecken sie um ein
   halbes Zeichen auseinander — das ist die bewusste Naeherung und kein Fehler. */
.tnl__stand{
  min-width:8ch;text-align:center;
  font-family:var(--mono);font-size:12.5px;letter-spacing:.06em;color:var(--dunkelgrau);
}
.tnl__gross-leiste{
  display:flex;gap:12px;align-items:center;justify-content:space-between;
  margin:12px 0 0;flex-wrap:wrap;
}
.tnl__gross-wer{font-size:14.5px;min-width:0;overflow-wrap:anywhere}

/* ---------- Die Detailansicht einer Veranstaltung im Verwaltungsbereich ----------

   Sie war bis zum 27.08.2026 ein Dialog und hat mit ihm die Regeln von .modal__box
   verloren — darunter die Groesse der Ueberschrift (dort clamp(20px,3vw,26px)). Der Titel
   bekommt hier die Groesse, die jede andere Ansicht des Verwaltungsbereichs traegt
   (.wrap>h2): Sie IST jetzt eine Seitenueberschrift und keine Dialogueberschrift mehr.

   NUR DIREKTE KINDER (#eventBox>h2). Ohne diese Einschraenkung traefe die Regel auch die
   <h2> in der Blockvorschau des Bearbeiten-Formulars — die gehoeren dem Renderer
   (.content h2, 19px), und eine Regel mit id gewaenne dort gegen ihn.

   EIN DECKEL AUF DIE BREITE STEHT HIER AUSDRUECKLICH NICHT. Der ganze Umbau geschah, weil
   die Teilnehmertabelle mit ihren acht Spalten im Dialog auf 740px gedrueckt war und
   dauerhaft in ihrem Rollbereich stand. body.admin .wrap laesst den Bildschirm frei; wer
   hier eine max-width nachtraegt, nimmt genau das wieder zurueck. */
#eventBox>h2{font-size:clamp(22px,3vw,30px);margin-bottom:10px}

/* ---------- Modal ---------- */
.modal{
  /* 60 und damit UEBER der Kopfleiste (50). Das ist kein beliebiger Wert: Bis v1.24.0 lag
     die Leiste auf 20 und der Dialog auf 40 — der Dialog deckte sie zu. Die neue Leiste kam
     auf 50 und gewann damit gegen genau denselben Dialog: Der Titel jeder geoeffneten
     Veranstaltung stand hinter der Leiste. Wer hier herunterdreht, baut das wieder ein.
     Die Overlays in admin.js liegen auf 70 und muessen ueber dem Dialog bleiben, aus dem
     sie geoeffnet werden — deshalb 60 und nicht mehr. */
  position:fixed;inset:0;z-index:60;display:none;align-items:flex-start;justify-content:center;
  padding:clamp(12px,4vw,40px);background:var(--overlay);overflow:auto;
}
@supports (backdrop-filter:blur(2px)){.modal{backdrop-filter:blur(3px)}}
.modal--open{display:flex;animation:fade .18s ease}
/* DIE HUELLE DES GROSSEN TEILNEHMERFOTOS IST KEINE ZWEITE BAUFORM: Sie traegt die Klasse
   `modal` mit und holt sich damit JEDEN Wert von oben — Verdunkelung, Ebene, Rollverhalten.
   Diese eine Zeile macht daraus nur „offen".

   WARUM SIE NICHT EINFACH `modal--open` HEISST: Diese Klasse gehoert dem Dialog des
   AUFRUFERS (#detailModal), und js/veranstaltung-ansicht.js darf ihn nicht anfassen — das
   haelt tests/veranstaltung-ansicht.test.js fest, damit sich dieselbe Ansicht in einen
   Dialog UND in eine eigene Seite setzen laesst. Ein eigener Name haelt diese Wache scharf,
   ohne dass hier ein zweiter Satz Werte entsteht.

   SIE HAT KEINEN EIGENEN ZUSTAND: Offen ist, DASS es sie gibt — die Ansicht zeichnet sie
   beim Klick und nimmt sie beim Schliessen wieder weg. */
.tnl__huelle{display:flex;animation:fade .18s ease}
@keyframes fade{from{opacity:0}to{opacity:1}}
.modal__box{
  background:var(--weiss);border-radius:16px;max-width:740px;width:100%;min-width:0;
  padding:clamp(20px,4vw,32px);box-shadow:var(--shadow-modal);
}
.modal__box h2{font-size:clamp(20px,3vw,26px);margin-bottom:10px}

/* Detail-Inhalt (gerenderte Blocks) */
.content{font-size:15.5px;line-height:1.65;color:var(--schwarz)}
.content h2{font-size:19px;margin:18px 0 6px}
/* Die Unterüberschrift. SIE FEHLTE BIS ZUM 27.08.2026 GANZ und fiel damit auf die
   Grundregel `h1,h2,h3{margin:0}` zurück — sie klebte oben an der Überschrift darüber und
   unten an ihrer eigenen Liste. Aufgefallen ist es erst am ersten echten Verwender: den
   Anreisehinweisen, wo „Mit dem Auto“ unter „Anreise & Check-in“ steht. Vorher war der Fall
   unerreichbar, weil der Editor jede Überschrift als Ebene 2 festschrieb.
   Abstand OBEN größer als unten (16 zu 4): Eine Unterüberschrift gehört sichtbar zu dem,
   was UNTER ihr steht — steht sie in der Mitte, sucht der Auge sich die Zuordnung selbst.
   Kleiner als h2, aber nicht so klein wie Fließtext: Sie muss als Überschrift lesbar
   bleiben, sonst ist die Stufe umsonst. */
.content h3{font-size:16.5px;margin:16px 0 4px}
.content p{margin:0 0 12px}
.content ul,.content ol{margin:0 0 12px;padding-left:20px}
.content li{margin:2px 0}
.content figure{margin:14px 0}
.content img{border-radius:10px}
/* ---------- Das redaktionelle Bild hält seinen Platz frei (29.08.2026) ----------
   OHNE DIESE ZEILE IST DAS BILD IM BESCHREIBUNGSTEXT NULL PUNKTE HOCH, bis die Datei da
   ist, und wächst dann auf einen Schlag auf ihre volle Höhe. Alles darunter — der nächste
   Absatz, die Aufzählung, die Bildunterschrift und auf dem Telefon der ganze Anmeldeblock —
   springt mit. Gemessen am 29.08.2026 an einer Veranstaltung mit einem Bild-Block (900 ×
   1200, Datei um 4 s verzögert, 300 ms/780 kbit): mobil CLS 0,333 in EINEM Eintrag. Es war
   der größte verbliebene Sprung einer öffentlichen Seite.

   `aspect-ratio: auto 4/3` IST DER KERN: Das Schlüsselwort `auto` heißt „nimm das
   Verhältnis der Datei, SOBALD du es kennst" — die 4/3 gelten nur bis dahin. Das Bild wird
   also NICHT verzerrt und nicht beschnitten; reserviert wird lediglich ein Kasten, solange
   niemand etwas Besseres weiß. Ein Bild-Block trägt seit dem 29.08.2026 zusätzlich `w`/`h`
   im gespeicherten Inhalt (js/blocks.js schreibt daraus width/height ans <img>); wo die
   Maße dastehen, ist die Reserve exakt und diese Näherung nie in Gebrauch. Sie ist für die
   Bilder da, die vorher schon in der Datenbank standen, und für die von Hand eingetragene
   fremde Adresse.

   DESHALB `:not([width])`, UND DAS IST GEMESSEN, NICHT VERMUTET: width/height am <img>
   wirken nur als Darstellungs-Hinweis des Browsers — JEDE aspect-ratio aus dem Stylesheet
   schlägt sie. Ohne diese Einschränkung stand die Näherung 4/3 also auch über einem Bild,
   dessen echte Maße danebenstanden: gemessen CLS 0,058 statt 0,000. Ein einfacher Selektor
   und kein `:not([width][height])`, weil älteres Safari zusammengesetzte Selektoren in
   :not() nicht kennt; blocks.js schreibt die beiden ohnehin nur PAARWEISE, und
   tests/bildmasse.test.js hält das fest.

   4/3 UND NICHT 3/2 ODER 16/9 — eine bewusste Näherung, kein gemessener Wert: Über die
   Mischung aus Quer- und Hochformat gerechnet ist der mittlere Fehlgriff bei 4/3 am
   kleinsten (Querformate liegen dicht daneben, Hochformate sind selten). Wer hier etwas
   anderes einträgt, macht nichts kaputt — es wird nur der Sprung wieder größer.

   `width:100%` MUSS DABEISTEHEN, gemessen: Ein noch ladendes <img> ohne Breitenangabe ist
   in Chrome 0 × 0, das Verhältnis allein rechnet an nichts. Der Preis: Ein Bild, das
   SCHMALER ist als seine Spalte, wird jetzt auf die Spaltenbreite hochgezogen statt in
   seiner Originalgröße zu stehen. Das ist die Zusage, die der Editor ohnehin macht („Die
   Größe oben gilt für den Bildschirm") — und über den Zuschnitt hochgeladene Bilder sind
   mit bis zu 1600 Punkten immer breiter als die Textspalte. */
.content figure img{width:100%;height:auto}
.content figure img:not([width]){aspect-ratio:auto 4/3}
/* Bildgrößen im Fließtext (Feld `width` am Bild-Block, drei Stufen — siehe blocks.js).
   PROZENTE UND KEINE PIXEL: Eine feste Breite in Pixeln bricht die Seite auf dem Telefon,
   und der Redakteur sieht das an seinem großen Bildschirm nie. Der Anteil steht am <figure>,
   nicht am <img> — sonst rutschte die Bildunterschrift unter dem verkleinerten Bild auf die
   volle Textbreite. Die Spezifität (zwei Klassen + Element) muss über `.content figure`
   liegen, sonst bliebe der Außenabstand des allgemeinen Selektors stehen und die Zentrierung
   käme nie an. */
.content figure.content__bild--mittel{width:66%;margin-left:auto;margin-right:auto}
.content figure.content__bild--klein{width:33%;margin-left:auto;margin-right:auto}

/* ---------- Anmeldeformular im Veranstaltungs-Fenster ---------- */
/* Seit die Rechnungsanschrift dazugehört, sind es acht Pflichtfelder statt drei. Ohne
   sichtbare Gruppierung wäre das eine Feldwüste, in der niemand mehr weiß, welche Adresse
   gerade gemeint ist — die eigene oder die der Buchhaltung. <fieldset>/<legend> gruppiert
   sichtbar UND für Vorlesesoftware; der Rahmen des Browsers ist dabei im Weg, deshalb steht
   hier eine Trennlinie oben statt eines Kastens ringsum. */
.reg__group{border:0;border-top:1px solid var(--line-soft);padding:0;margin:18px 0 0;min-width:0}
.reg__legend{padding:0;margin:14px 0 10px;font-size:15px;font-weight:700;letter-spacing:-.01em}
.reg__group .note{margin:0 0 12px}
.reg__fields{display:flex;flex-wrap:wrap;gap:10px}
/* Die Beschriftung steht ÜBER dem Feld und nicht als Platzhalter darin: Ein Platzhalter
   verschwindet beim Tippen, und in einem Block aus PLZ, Ort und Land ist genau dann die
   Orientierung weg. Die drei Felder darüber behalten ihre Platzhalter — sie sind einzeln
   eindeutig und stehen dort seit jeher. */
.reg__field{flex:1 1 200px;min-width:0;font-size:13.5px;color:var(--dunkelgrau)}
.reg__field--voll{flex:1 1 100%}

/* ---------- Adressvorschläge am Feld „Straße und Hausnummer" ----------
   Die Hülle steht als eigenes Element UNTER dem Feld (adresse.js hängt sie neben das
   <label>) und trägt zwei Dinge: die aufklappende Liste und die Namensnennung.
   `position:relative` ist der ganze Trick — die Liste liegt absolut darin und schiebt
   damit die Felder darunter nicht auseinander, wenn sie aufgeht. */
.adr{flex:1 1 100%;position:relative;margin:-4px 0 0}
.adr__liste{
  position:absolute;top:0;left:0;right:0;z-index:5;max-height:264px;overflow-y:auto;
  background:var(--weiss);border:1px solid var(--blau-line);border-radius:var(--radius-sm);
  box-shadow:var(--shadow-md);
}
.adr__eintrag{padding:8px 12px;cursor:pointer;border-bottom:1px solid var(--line-soft);font-size:14px;color:var(--schwarz)}
.adr__eintrag:last-child{border-bottom:0}
/* Der Zeiger tönt den Eintrag nur ein. Die TASTATUR bekommt zusätzlich den Balken links —
   dieselbe Auszeichnung, die in dieser Anwendung .note und .callout tragen. Sie muss
   deutlicher sein als das Überfahren mit der Maus: Wer mit den Pfeiltasten wandert, hat
   keinen Zeiger, an dem er sich festhält, und muss trotzdem erkennen, was die Eingabetaste
   gleich übernimmt. Als inset-Schatten und nicht als border — ein Rahmen verschöbe den
   Text bei jedem Tastendruck um drei Pixel. */
.adr__eintrag:hover{background:var(--hover-blue)}
.adr__eintrag--aktiv{background:var(--blau-tint);box-shadow:inset 3px 0 0 var(--blau)}
.adr__strasse{display:block;font-weight:600}
.adr__ort{display:block;font-size:13px;color:var(--dunkelgrau)}
/* Die Namensnennung der ODbL. Klein und unaufdringlich, aber DAUERHAFT sichtbar — sie ist
   Pflicht und nicht an das Aufklappen der Liste gebunden. */
.adr__quelle{margin:2px 0 0;font-family:var(--mono);font-size:10.5px;letter-spacing:.04em;color:var(--dunkelgrau)}

/* ---------- Inhaltsblöcke: Hinweisbox, Schaltfläche, zwei Spalten ---------- */
/* Gleiche Bauform wie .note (linke Akzentlinie), nur je Tonalität eingefärbt.
   Der Abstand ist bewusst identisch zu `.content p` — der Renderer darf die Box
   als <p> oder <div> ausgeben, ohne dass der Rhythmus springt. */
.callout{
  display:block;background:var(--blau-tint);border-left:3px solid var(--blau);
  padding:10px 14px;border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  margin:0 0 12px;font-size:14.5px;color:var(--schwarz);
}
/* Der Renderer legt den Text als <p> in die Box — dessen Aussenabstand muss weg,
   sonst steht unten in der Box eine leere Zeile. */
.callout>*:first-child{margin-top:0}
.callout>*:last-child{margin-bottom:0}
.callout--info{background:var(--blau-tint);border-left-color:var(--blau)}
.callout--warn{background:var(--warn-tint);border-left-color:var(--warn)}
.callout--success{background:var(--gruen-tint);border-left-color:var(--gruen)}

/* Absatz um die Schaltfläche; die Schaltfläche selbst nutzt .btn .btn--primary.
   Der Selektor steht zusätzlich als p.content__cta, sonst gewinnt das allgemeine
   `.content p` (höhere Spezifität) und der Abstand wäre weg. */
.content__cta,p.content__cta{margin:18px 0}
.content__cta .btn{display:inline-block;text-decoration:none}
.content__cta .btn:hover{text-decoration:none}

/* Zwei Spalten nebeneinander — unter 640px untereinander (siehe Media-Query). */
.cols{display:flex;flex-wrap:wrap;gap:20px;margin:0 0 12px}
.cols__col{flex:1 1 220px;min-width:0}
.cols__col>*:last-child{margin-bottom:0}

/* Zeitzeilen der Agenda: Zeit links, Programmpunkt rechts — unter 640px untereinander
   (siehe Media-Query). Ausdrücklich KEINE <table>: Es sind keine Daten mit Spaltenköpfen,
   und eine Tabelle im Dialog hat schon einmal zum waagerechten Schieben geführt (v1.13.0). */
.schedule{margin:0 0 12px}
.schedule__row{
  display:flex;gap:14px;align-items:baseline;
  padding:7px 0;border-bottom:1px solid var(--line-soft);
}
.schedule__row:last-child{border-bottom:0}
/* Feste Breite und Monospace, damit die Zeiten untereinander fluchten, auch wenn sie
   verschieden lang sind („09:00“ neben „ganztägig“). Bewusst OHNE nowrap: Die Zeit ist
   freier Text, und eine besonders lange Angabe soll umbrechen statt in den Programmpunkt
   zu laufen. */
.schedule__time{
  flex:0 0 104px;font-family:var(--mono);font-size:12.5px;letter-spacing:.03em;
  color:var(--dunkelgrau);overflow-wrap:anywhere;
}
.schedule__item{flex:1 1 200px;min-width:0}
.schedule__title{display:block}
.schedule__note{display:block;font-size:13px;color:var(--dunkelgrau)}

/* ---------- Inhalts-Editor (Admin): Live-Vorschau ---------- */
.editorPreview__label{
  margin:18px 0 6px;font-family:var(--mono);font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--dunkelgrau);
}
.editorPreview{
  background:var(--weiss);border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:14px 16px;min-height:64px;
}
.editorPreview>*:last-child{margin-bottom:0}

/* Die Vorschaukarte im Bearbeitungsformular. Sie ahmt nach, wie LinkedIn und WhatsApp einen
   geteilten Link zeigen — deshalb Bild oben, Quelle klein, Titel fett, eine Zeile Text.
   Das Maß 1200/630 ist das des Hausbilds und damit bindend (api/src/lib/vorschau.js).
   `overflow-wrap` an Titel und Zeile, weil eine nackte Adresse in der Kurzbeschreibung ein
   einziges langes Wort ist: Die Karte schneidet (overflow:hidden) und verschluckte sie sonst
   wortlos — ausgerechnet in dem Baustein, der zeigen soll, was ankommt. */
.vorschau { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; }
.vorschau__karte { flex: 1 1 320px; max-width: 420px; border: 1px solid var(--border);
  border-radius: 8px; overflow: hidden; background: var(--surface); }
.vorschau__bild { display: block; width: 100%; aspect-ratio: 1200 / 630; object-fit: cover; }
.vorschau__text { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; }
.vorschau__quelle { font-size: 12px; color: var(--muted); text-transform: lowercase; }
.vorschau__titel { font-size: 15px; line-height: 1.3; overflow-wrap: anywhere; }
.vorschau__zeile { font-size: 13px; color: var(--muted); overflow-wrap: anywhere; }
.vorschau__herkunft { flex: 1 1 260px; display: grid; grid-template-columns: auto 1fr;
  gap: 4px 12px; margin: 0; font-size: 13px; }
.vorschau__herkunft dt { color: var(--muted); }
.vorschau__herkunft dd { margin: 0; }

/* ---------- Inhalts-Editor (Admin): Werkzeugleiste über einem Textfeld ----------
   Der Kasten steht im Layout an der Stelle, an der vorher das Feld allein stand (die
   Flex-Maße werden ihm im Markup mitgegeben) — deshalb ohne eigene Breite und ohne
   eigenen Außenabstand. */
.mkfeld{display:block;min-width:0}
.mkfeld__leiste{display:flex;flex-wrap:wrap;gap:4px;margin:0 0 4px}
/* Klein und ruhig: Die Leiste steht über JEDEM Textfeld eines Blocks und darf den Editor
   nicht dominieren. Die Mindestbreite gilt dem Paar F/K — ein fettes „F“ ist breiter als
   ein kursives „K“, und ohne sie stünden die beiden ungleich nebeneinander. */
.mkfeld__knopf{
  padding:2px 8px;min-width:30px;font-size:13px;line-height:1.5;
  border-radius:var(--radius-sm);
}
.mkfeld__knopf:hover{color:var(--blau);border-color:var(--blau-line)}
/* Zeitzeilen: Leiste NEBEN dem Feld statt darüber. Eine Zeitzeile besteht aus drei
   Einzeilern; zwei Leisten übereinander verdoppelten ihre Höhe, und aus der kompakten
   Tabelle würde eine Wand aus Knöpfen. Auf schmalen Fenstern rutscht die Leiste durch
   flex-wrap von selbst über das Feld. */
.mkfeld--knapp{display:flex;flex-wrap:wrap;align-items:center;gap:4px}

/* ---------- Die Leiste zeigt sich nur am Feld, in dem gearbeitet wird ----------
   Auch NEBEN dem Feld blieb es eine Wand: Eine Agenda mit vier Zeitzeilen hat acht
   Textfelder, also acht Leisten und 24 Knoepfe auf einmal. Dazu steht die Leiste links von
   ihrem Feld und sah dadurch aus, als gehoerte sie zur Uhrzeit daneben. Beides ist erledigt,
   sobald immer nur EINE sichtbar ist — die am Feld, in dem der Blinkbalken steht.

   visibility statt display: Der Platz bleibt reserviert, damit beim Wechsel von Feld zu Feld
   nichts springt. Ein Editor, in dem die Zeilen huepfen, waehrend man sie ausfuellt, ist
   schlimmer als ein paar Knoepfe zu viel.

   :focus-within und nicht :focus am Feld — der Knopf liegt INNERHALB der Huelle, und beim
   Klick darauf wandert der Fokus vom Textfeld zu ihm. Mit :focus verschwaende die Leiste
   genau in dem Augenblick, in dem sie gedrueckt wird.

   Nur in den Zeitzeilen (--knapp). In Absatz, Hinweiskasten und Spalten gibt es je Block nur
   EIN Feld; dort ist die Leiste keine Wand, und sie dauerhaft zu zeigen erspart das Raten,
   ob es sie ueberhaupt gibt. */
.mkfeld--knapp > .mkfeld__leiste{visibility:hidden}
.mkfeld--knapp:focus-within > .mkfeld__leiste{visibility:visible}
/* Wer mit dem Zeiger ueber die Zeile faehrt, sieht ebenfalls, dass es sie gibt — sonst
   entdeckt die Leiste niemand, der nicht ohnehin schon im Feld steht. */
.mkfeld--knapp:hover > .mkfeld__leiste{visibility:visible}
.mkfeld--knapp .mkfeld__leiste{margin:0;flex:0 0 auto}
.mkfeld--knapp [data-mk-feld]{flex:1 1 160px;min-width:0}

/* ---------- Die Pfeile einer Zeitzeile (↑ ↓ ✕) ----------
   Dieselben drei Zeichen tragen auch die Bloecke, und beide Saetze stehen fast auf derselben
   Hoehe. Unterschieden waren sie bis zum 09.08.2026 allein ueber title/aria-label — also gar
   nicht fuer den, der hinsieht statt zu lesen. Diese hier gehoeren zur ZEILE und nicht zum
   Block; das sollen sie auch aussehen.

   KLEINER AUSSEHEN HEISST NICHT KLEINER TREFFEN. Zurueckgenommen sind Schrift, Rahmen und
   Farbe — die Tippflaeche bleibt bei 30 Punkten in beide Richtungen. Unter 640px wird die
   Tabelle zur Karte, und „✕" ist die Aktion, bei der ein Fehlgriff wehtut: Er nimmt eine
   Zeile heraus, die der Redakteur gerade getippt hat.

   Der Rahmen wird durchsichtig statt weggenommen: Ohne border-width aendert der Knopf beim
   Ueberfahren seine Masse, und die ganze Zeile zuckte.

   Steht nach .btn/.btn--ghost, damit Farbe und Masse ohne !important gewinnen. */
.zeilenknopf{
  padding:4px 5px;min-width:30px;min-height:30px;
  font-size:12px;line-height:1;
  background:transparent;border-color:transparent;color:var(--dunkelgrau);opacity:.7;
}
.zeilenknopf:hover,.zeilenknopf:focus-visible{
  opacity:1;border-color:var(--line);color:var(--schwarz);
}

.switch{display:flex;align-items:center;gap:10px;margin:14px 0}
.switch input{width:16px;height:16px;accent-color:var(--blau)}

/* ---------- hidden bedeutet hidden ----------
   Das hidden-Attribut wird vom Browser nur mit `[hidden]{display:none}` umgesetzt — einer
   Regel ohne Klasse und damit mit der schwächsten Spezifität, die es gibt. JEDE eigene
   Klasse, die `display` setzt, schlägt sie: Das Element trägt hidden und steht trotzdem da.

   Diese Falle hat in diesem Projekt VIER Mal zugeschlagen (Kollegen-Hinweis, Sortierung
   auf Mobilgeräten, Vorlagen-Warnung, und zuletzt die Adress-Warnung am
   Schaltflächen-Block in admin.js — dort war sie dauerhaft sichtbar, auch bei gültiger
   Adresse). Statt sie ein fünftes Mal an einer einzelnen Stelle zu umgehen, steht hier
   ein für alle Mal: hidden gewinnt.

   Das !important ist hier kein Notnagel, sondern der Zweck. Es gibt keinen Fall, in dem
   jemand ein Element ausdrücklich versteckt und es trotzdem sehen will. */
[hidden]{display:none !important}

/* ---------- Notiz-/Hinweiszeile (nicht zu verwechseln mit dem Block .callout) ---------- */
.note{
  display:block;background:var(--blau-tint);border-left:3px solid var(--blau);
  padding:10px 14px;border-radius:0 var(--radius-sm) var(--radius-sm) 0;font-size:14px;color:var(--schwarz);
}
/* Auszeichnungs-Beispiele in Hinweiszeilen (z. B. **fett**) — als Code kenntlich. */
.note code{font-family:var(--mono);font-size:12.5px;background:var(--weiss);border:1px solid var(--blau-line);border-radius:4px;padding:1px 5px}

/* ---------- Admin-Tabelle ---------- */
table.list{width:100%;border-collapse:collapse;font-size:14px;background:var(--weiss);box-shadow:var(--shadow-sm);border-radius:var(--radius-sm);overflow:hidden}
table.list th{
  font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;
  color:var(--dunkelgrau);text-align:left;padding:11px 12px;border-bottom:1px solid var(--line);background:var(--weiss);
}
.list th[data-sort]{cursor:pointer;user-select:none;white-space:nowrap}
.list th[data-sort]:hover{text-decoration:underline}
table.list td{padding:12px;border-bottom:1px solid var(--line-soft);vertical-align:top}
table.list tbody tr:last-child td{border-bottom:0}
table.list tbody tr:hover{background:var(--hover-blue)}
table.list small{color:var(--dunkelgrau)}
.sort-mobile{display:none}

/* Eigener Rollbereich je Tabelle.
   Ohne ihn schiebt eine zu breite Tabelle die GANZE Seite nach rechts: Man scrollt dann das
   Dokument statt der Tabelle, die Kopfzeile der Anwendung wandert mit weg, und die letzte
   Spalte („Aktion") bleibt trotzdem unerreichbar. Unter 640px greift die Kartenliste weiter
   unten — dazwischen gab es bis hierher gar nichts.
   -webkit-overflow-scrolling wie bei .tabs (Zeile 70) für den Schwung auf Touchgeräten.
   Das tabindex="0" steht am Element im HTML: Ein Rollbereich ohne Fokus ist ohne Maus nicht
   zu bedienen. Den sichtbaren Rahmen holt er sich aus :focus-visible und nur dort — bei
   einem Mausklick soll die halbe Seite nicht eingerahmt sein. */
.table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}

/* Die Spalte „Veranstaltung": ein MINDESTMASS, keine Höchstbreite.
   Der Titel „ITleague SUMMIT Herbst 2026 (2 Tage Dresden) – Der Treffpunkt für die TOP
   Systemhäuser!" stand über acht Zeilen in einer 119px schmalen, hohen Säule — die Zelle
   war also zu ENG, nicht zu breit. Die automatische Tabellenbreite verteilt den Platz nach
   dem längsten Wort einer Spalte, und „Systemhäuser!" ist kürzer als eine E-Mail-Adresse;
   ohne Untergrenze bekommt ausgerechnet die Spalte mit dem längsten Text den wenigsten
   Platz.
   Eine Höchstbreite hilft hier NICHT: max-width wirkt im automatischen Tabellenlayout auf
   ein <td> überhaupt nicht (gemessen in Chromium: 119px bei jedem Wert von 200 bis 360).
   min-width wirkt.
   Die 240px sind gemessen, nicht geraten — genau dieser Titel: 119px → 8 Zeilen,
   160px → 5, 200px → 4, 240px → 3, darüber wird die Zeile nicht mehr niedriger, weil dann
   eine andere Zelle die Zeilenhöhe bestimmt. Zeilenhöhe damit 249px → 136px. */
.list td[data-col="event"]{min-width:240px}

/* ---------- Der Klappbereich im Mitgliederbereich (21.08.2026) ----------
   Er hiess bis zum 02.09.2026 „Vergangene Anmeldungen"; seither stehen dort auch stornierte
   Anmeldungen zu kommenden Terminen, und er heisst „Nicht mehr aktuelle Anmeldungen". Die
   KLASSE bleibt `.vergangene` — sie ist kein sichtbarer Text, und ein Umbenennen zoege vier
   Klickwege ohne Gewinn mit.
   Der Auftraggeber, wörtlich: „Auf beiden Seiten ‚Meine Anmeldungen' und auch ‚Anmeldungen
   (Firma)' sollen nur die Anmeldungen für aktuell laufende Veranstaltungen bzw. kommende
   direkt sichtbar sein. Die anderen also bereits in der Vergangenheit liegenden sollen
   aufgeklappt werden können."
   Natives <details>/<summary> wie bei .ptPeople, .invSkip und .logk — kein JS zum Auf- und
   Zuklappen nötig, und der Klapp-Pfeil ist der des Browsers.
   DIE TRENNLINIE IST KEINE ZIERDE: Darunter folgt eine ZWEITE Tabelle mit derselben
   Kopfzeile. Ohne sie sähe sie aus wie die Fortsetzung der ersten, und die Zusammenfassung
   dazwischen wäre eine Zeile, die niemand als Grenze liest. Abstand und Linie deshalb wie
   beim Protokoll-Ausschnitt (.logk), der aus demselben Grund eine hat. */
.vergangene{margin-top:18px;padding-top:14px;border-top:1px solid var(--line-soft)}
.vergangene>summary{cursor:pointer;user-select:none;font-weight:600;padding:2px 0}
/* Luft zwischen der Zusammenfassung und der Tabelle darunter — sonst klebt die Kopfzeile
   der zweiten Tabelle unmittelbar am aufgeklappten Text. */
.vergangene .table-scroll{margin-top:12px}

/* ---------- Der Buchungsweg an der Zimmerfrage ----------
   Anliegen 8, gemeldet am 24.08.2026 von einem Partner: „Nein, ich möchte mir mein Zimmer
   über das Zimmerkontingent buchen -> Hinweis zum Buchungslink oder Zimmerbuchung über die
   Email des Hotels". Wer ankreuzt, dass ihm noch ein Zimmer fehlt, bekommt den Weg dorthin
   gleich mit (events.accommodation_note).
   ZUGEKLAPPT IST ER EINE ZEILE, und das ist der Grund für <details>: In der Firmenübersicht
   steht er an JEDER Zeile — bei acht Kolleginnen zum selben Summit stünde derselbe Absatz
   sonst achtmal ausgeschrieben in der Tabelle.
   Natives <details>/<summary> wie bei .vergangene, .ptPeople und .logk — kein JS zum Auf-
   und Zuklappen; die Ansicht setzt bei „Nein" nur `open`.
   Die Breite hält die Spalte zusammen: Ein Hotelabsatz ohne Grenze zöge die Zimmerspalte
   über den halben Bildschirm. Auf dem Telefon fällt sie weg (dort ist die Tabelle eine
   Kartenliste, siehe unten). */
.zimmerweg{margin-top:6px;max-width:280px}
.zimmerweg>summary{cursor:pointer;user-select:none;font-size:13px;padding:2px 0}
.zimmerweg .note{margin-top:6px;padding:8px 12px;font-size:13px}

/* ---------- Die Stornoangaben auf der Veranstaltungsseite (25.08.2026) ----------
   Ein .note-Kasten mit mehreren Absätzen statt einer Zeile: Der Wortlaut ist ein gepflegter
   Rechtstext mit eigenen Absätzen, und darüber stehen Frist und Gebühr. Die Abstände müssen
   hier stehen, weil es im Haus keine Grundregel für <p> gibt — sonst gälten die weiten
   Browservorgaben (1em oben UND unten), und der Kasten stünde doppelt so hoch wie nötig.
   Der Wortlaut steht etwas abgesetzt: Er ist die Zusage, die beiden Zeilen darüber sind die
   Zahlen dazu — ohne den Absatz läse sich beides als eine Aufzählung.
   KEINE eigene Farbe: Die trägt .note, und bei abgelaufener Frist hängt die Ansicht
   .note--warn an — dann wird der ganze Kasten bernstein. Eine dritte Farbquelle liefe
   irgendwann gegen die beiden, und welche gewinnt, entschiede allein die Zeilennummer. */
.vst__storno{margin-top:14px}
.vst__storno>p{margin:0 0 6px}
.vst__storno-text{margin-top:10px}

/* ---------- Der Editor in der Zeile (Unternehmensliste, 25.08.2026) ----------
   Der Auftraggeber, wörtlich: „Wenn ich in der Unternehmensliste auf bearbeiten klicke,
   dann wird mir der Dialog zur Bearbeitung immer ganz oben in der Seite angezeigt. Wenn ich
   sehr weit nach unten scrollen musste, muss ich dann immer hin und her scrollen."
   Das Formular ist deshalb eine eigene Zeile unmittelbar unter der angeklickten, über die
   volle Breite (colspan aus Columns.spannweite).
   DER BLAUE STREIFEN IST KEINE ZIERDE: Ohne ihn ist die Editorzeile eine weitere Zeile
   unter vielen, und in einer Liste mit vierzig Firmen sieht man nicht mehr, zu welcher das
   Formular gehört. Er zeigt an, dass diese Zeile an der darüber hängt — dieselbe Sprache
   wie .note, die ihren Rand aus demselben Grund links trägt.
   KEIN :hover: Die Datenzeilen färben sich beim Überfahren, weil man sie anklickt. Ein
   Formular klickt man nicht an, und eine mitwandernde Färbung ließe es so aussehen. */
tr.pt-editor>td{background:var(--blau-tint);border-left:3px solid var(--blau);padding:14px 16px}
table.list tbody tr.pt-editor:hover>td{background:var(--blau-tint)}
/* Die Trennlinie zwischen der Firma und ihrem Formular fällt weg — sie gehören zusammen.
   Über :has, weil die Linie am UNTEREN Rand der Datenzeile sitzt und CSS sonst nicht
   rückwärts wählen kann. Fehlt :has im Browser, bleibt eine Haarlinie stehen; mehr nicht. */
table.list tbody tr:has(+tr.pt-editor)>td{border-bottom:0}

.pager{display:flex;gap:10px;align-items:center;margin:16px 0;flex-wrap:wrap;font-family:var(--mono);font-size:12px;letter-spacing:.04em;color:var(--dunkelgrau)}
.pager .btn{padding:6px 12px}

/* ---------- Responsive ---------- */
@media (max-width:640px){
  .grid{grid-template-columns:1fr}
  .toolbar{gap:8px}
  input[type=search]{min-width:0;width:100%}
  .modal__box{padding:18px}
  .tabs{gap:16px}
  /* Zwei-Spalten-Block: auf dem Telefon untereinander statt gequetscht nebeneinander. */
  .cols{flex-direction:column;gap:12px}
  /* Bilder: hier IMMER volle Breite, egal welche Stufe gewählt ist. Ein Drittel von einem
     320px-Telefon sind hundert Pixel — ein Briefmarkenbild, auf dem nichts zu erkennen ist. */
  .content figure.content__bild--mittel,
  .content figure.content__bild--klein{width:100%}
  /* Zeitzeile: die Zeit steht über dem Programmpunkt. Nebeneinander blieben von 320px
     nach Abzug der Zeitspalte keine 200px für den Text — er bräche nach jedem Wort um. */
  .schedule__row{flex-direction:column;gap:2px}
  /* BEIDE Kinder müssen ihre flex-Vorgabe verlieren, nicht nur die Uhrzeit. Sobald die Zeile
     senkrecht steht, ist flex-basis die HÖHE — .schedule__item{flex:1 1 200px} von oben
     machte daraus 200px Leerraum unter jedem Programmpunkt. Auf dem Telefon klaffte deshalb
     zwischen zwei Zeitzeilen ein halber Bildschirm. Wer hier eine der beiden Zeilen
     wegnimmt, baut das wieder ein. */
  .schedule__time{flex:0 0 auto}
  .schedule__item{flex:0 0 auto}
  /* Admin-Tabellen als gestapelte Karten (kein horizontales Abschneiden mehr) */
  table.list{display:block;width:100%;box-shadow:none;background:transparent;border-radius:0;font-size:13px}
  table.list tbody{display:block}
  table.list thead{display:none}
  table.list tr{display:block;background:var(--weiss);border:1px solid var(--line);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);margin-bottom:12px;padding:2px 2px}
  table.list td{display:block;border-bottom:1px solid var(--line-soft);padding:9px 14px;vertical-align:middle;overflow-wrap:anywhere}
  table.list tr td:last-child{border-bottom:0}
  table.list td[data-label]::before{content:attr(data-label);display:block;font-family:var(--mono);font-size:10px;letter-spacing:.05em;text-transform:uppercase;color:var(--dunkelgrau);margin-bottom:3px}
  /* textarea steht seit dem 27.08.2026 mit dabei: Das Notizfeld der Teilnehmertabelle ist
     mehrzeilig geworden. Ohne diese Zeile bliebe es auf der Karte bei seinen 200px stehen —
     also genau so schmal wie das einzeilige Feld, das ersetzt werden sollte. */
  table.list td select,table.list td input[type=text],table.list td input[type=email],table.list td textarea{width:100%;max-width:none}
  /* Der Buchungsweg ebenso: Auf der Karte gibt es keine Spalte, die er sprengen könnte. */
  .zimmerweg{max-width:none}
  /* Hier ist die Tabelle eine Kartenliste — ein Rollbalken um Karten wäre sinnlos, und ein
     Mindestmaß von 240px sprengte auf einem 320px-Telefon die Karte. */
  .table-scroll{overflow-x:visible}
  .list td[data-col="event"]{min-width:0}
  .sort-mobile{display:flex;gap:8px;align-items:flex-end;margin:0 0 14px}
  .sort-mobile label{font-family:var(--mono);font-size:10px;letter-spacing:.05em;text-transform:uppercase;color:var(--dunkelgrau)}
  .sort-mobile select{font-size:14px}
  .sort-mobile [data-sort-dir]{padding:8px 12px}
}

/* ---------- Reduced Motion ---------- */
@media (prefers-reduced-motion:reduce){
  *{transition:none!important;animation:none!important}
  .card:hover{transform:none}
}

/* ---------- Rechtsseiten (Impressum / Datenschutz) ---------- */
.legal{max-width:820px}
.legal h3{font-size:17px;margin:28px 0 8px}
.legal p{margin:0 0 12px;font-size:15px;line-height:1.65}
.legal ul{margin:0 0 12px;padding-left:20px}
.legal li{margin:3px 0}
.legal table.list{margin:12px 0 18px}
.legal__lead{font-size:16px;color:var(--dunkelgrau)}
.legal__hint{color:var(--dunkelgrau);font-size:13.5px}
.legal__meta{
  margin-top:28px;font-family:var(--mono);font-size:11.5px;
  letter-spacing:.07em;text-transform:uppercase;color:var(--dunkelgrau);
}
.legal dl{margin:0 0 12px}
.legal dt{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--dunkelgrau);margin-top:14px;
}
.legal dd{margin:3px 0 0;font-size:15px;line-height:1.55}

/* ---------- Feedback-Seite (öffentlich, ohne Login) ---------- */
.fb{max-width:640px}
.fb__card{
  background:var(--weiss);border:1px solid var(--line-soft);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);padding:clamp(18px,4vw,28px);margin-top:14px;
}
.fb__card p{margin:0 0 12px}
.fb__card p:last-child{margin-bottom:0}
.fb__card .fb__event{font-size:18px;font-weight:700;letter-spacing:-.01em;margin:16px 0 0}
.fb__card .fb__meta{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--dunkelgrau);margin:3px 0 0;
}
.fb__card .fb__hint{color:var(--dunkelgrau);font-size:14px}
.fb__field{border:0;padding:0;margin:22px 0 0;min-width:0}
.fb__label{display:block;font-size:14px;font-weight:600;padding:0;margin:0 0 6px}
.fb__field textarea{width:100%;resize:vertical}
.fb__stars{display:flex;gap:2px}
.fb__star{
  position:relative;display:inline-flex;
  width:clamp(44px,14vw,56px);height:clamp(44px,14vw,56px);border-radius:var(--radius-sm);cursor:pointer;
}
.fb__star input{position:absolute;inset:0;width:100%;height:100%;opacity:0;margin:0;cursor:pointer}
.fb__starIcon{
  display:flex;align-items:center;justify-content:center;width:100%;height:100%;
  font-size:clamp(28px,8vw,34px);line-height:1;color:var(--star-off);transition:color .12s;
}
.fb__star:hover .fb__starIcon{color:var(--dunkelgrau)}
.fb__star--on .fb__starIcon{color:var(--blau)}
.fb__star input:focus-visible + .fb__starIcon{outline:2px solid var(--blau);outline-offset:-3px;border-radius:var(--radius-sm)}
.fb__card .fb__ratingOut{
  min-height:18px;margin:4px 0 0;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--dunkelgrau);
}
.fb__card .fb__privacy{margin:22px 0 0}
.fb__card .fb__actions{margin:18px 0 0}
.note--err{background:var(--danger-tint);border-left-color:var(--danger)}
/* Kein Fehler, sondern ein Umstand, der auffallen soll — etwa eine noch unbestätigte
   Anmeldung im Rechnungsdialog. Rot wäre falsch: Es spricht nichts dagegen, nur soll es
   niemand übersehen. Gegenstück zu .callout--warn, damit beide Bausteine dieselbe Skala haben. */
.note--warn{background:var(--warn-tint);border-left-color:var(--warn)}
/* Der Zähler unter der Kurzbeschreibung, sobald es eng wird — nur die Zahl in Rot, nicht
   der ganze Kasten.
   SIE HIESS BIS ZUM 22.08.2026 .note--warnung UND STAND DIREKT UNTER .note--warn. Zwei
   Namen, die sich um zwei Buchstaben unterscheiden, mit völlig verschiedener Wirkung (dort
   der ganze Kasten bernstein, hier nur die Zahl rot) — ein Vertipper zwischen beiden gibt
   keine Fehlermeldung, sondern nur ein anderes Bild.
   SIE MUSS HIER STEHEN UND NICHT BEI DER VORSCHAUKARTE: `.note` setzt selbst eine Farbe und
   hat dieselbe Spezifität — steht diese Regel VOR ihr, gewinnt `.note`, und die Warnung
   bleibt unsichtbar. Genau so gebaut; aufgefallen ist es erst auf dem Bildschirmfoto, denn
   der Klickweg prüfte nur die Klasse und war grün. Er prüft jetzt auch die Farbe. */
.note--eng{color:var(--tint-red-text);font-weight:600}

/* Zusatzabschnitte im Bewertungsformular (Anmeldungen / Angebote).
   Jeder Abschnitt setzt sich per Trennlinie und Abstand vom Rest ab;
   die Ankreuzzeilen sind bewusst gross genug fuer Daumen (min. 44px). */
.fb__section{margin-top:28px;padding-top:22px;border-top:1px solid var(--line)}
.fb__sectionTitle{font-size:16px;font-weight:700;letter-spacing:-.01em;margin:0 0 8px}
.fb__card .fb__sectionLead{margin:0 0 12px;font-size:14px}
.fb__card .fb__sectionHint{margin:12px 0 0}
.fb__field input[type=text]{width:100%}
.fb__label--sub{margin:16px 0 6px;font-weight:600;color:var(--dunkelgrau)}
.fb__checks{display:flex;flex-direction:column;gap:2px}
.fb__check{
  display:flex;align-items:flex-start;gap:12px;min-height:44px;
  padding:10px 12px;margin:0 -12px;border-radius:var(--radius-sm);
  cursor:pointer;transition:background .12s;
}
.fb__check:hover{background:var(--hover-blue)}
.fb__check:focus-within{background:var(--hover-blue)}
.fb__check input{flex:0 0 auto;width:20px;height:20px;margin:1px 0 0;accent-color:var(--blau);cursor:pointer}
.fb__checkBody{min-width:0}
.fb__checkTitle{display:block;font-size:14.5px;font-weight:600;line-height:1.35;overflow-wrap:anywhere}
.fb__checkMeta{
  display:block;margin-top:3px;overflow-wrap:anywhere;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--dunkelgrau);
}
.fb__checkDesc{display:block;margin-top:3px;font-size:13.5px;line-height:1.5;color:var(--dunkelgrau);overflow-wrap:anywhere}
/* Hinweiszeile unter einer Ankreuzzeile (z. B. „bereits angemeldet") — bewusst in
   CI-Blau, damit sie in der gedämpften Zeile trotzdem gelesen wird. */
.fb__checkNote{display:block;margin-top:3px;font-size:13.5px;font-weight:600;line-height:1.5;color:var(--blau);overflow-wrap:anywhere}
/* Gesperrte Zeile: nicht wählbar, deshalb kein Hover/Klick-Feedback und
   zurückgenommene Schrift. Der Hinweis bleibt gut lesbar. */
.fb__check--done{cursor:default}
.fb__check--done:hover{background:transparent}
.fb__check--done:focus-within{background:transparent}
.fb__check--done input{cursor:not-allowed;opacity:.6}
.fb__check--done .fb__checkTitle,
.fb__check--done .fb__checkMeta{color:var(--dunkelgrau)}

/* Tarifauswahl unter einer Veranstaltung: eingerueckt bis unter den Titel,
   Zeilen ebenfalls min. 44px hoch. Solange die Veranstaltung nicht angehakt ist,
   sind die Radios deaktiviert — dann steht die Auswahl dezent ausgegraut da. */
.fb__prices{display:flex;flex-direction:column;gap:2px;padding-left:32px;margin:2px 0 6px}
.fb__price{
  display:flex;align-items:flex-start;gap:10px;min-height:44px;
  padding:9px 12px;margin:0 -12px;border-radius:var(--radius-sm);
  cursor:default;opacity:.5;transition:background .12s,opacity .12s;
}
.fb__price input{flex:0 0 auto;width:18px;height:18px;margin:2px 0 0;accent-color:var(--blau)}
.fb__priceLabel{display:block;font-size:13.5px;line-height:1.5;color:var(--dunkelgrau);overflow-wrap:anywhere}
.fb__prices--on .fb__price{opacity:1;cursor:pointer}
.fb__prices--on .fb__price input{cursor:pointer}
.fb__prices--on .fb__priceLabel{color:var(--schwarz)}
.fb__prices--on .fb__price:hover{background:var(--hover-blue)}
.fb__prices--on .fb__price:focus-within{background:var(--hover-blue)}

/* ---------- Feedback-Liste (Verwaltungs-Modal) ---------- */
.feedback__head{
  margin:0 0 10px;font-family:var(--mono);font-size:12px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--dunkelgrau);
}
.feedback__item{
  background:var(--weiss);border:1px solid var(--line);border-radius:var(--radius-sm);
  box-shadow:var(--shadow-sm);padding:10px 14px;margin-bottom:10px;
}
.feedback__rating{display:flex;align-items:center;gap:8px;margin:0}
/* Sterne in Listen/Tabellen — gleiche CI-Farbe wie im Bewertungsformular. */
/* Kommentarspalte der Bewertungsliste: lange Texte umbrechen, statt die Spalte zu dehnen. */
.fbcomment{max-width:360px;overflow-wrap:anywhere}

.stars{color:var(--blau);letter-spacing:1px}

.feedback__stars{color:var(--blau);font-size:16px;letter-spacing:2px;line-height:1}
.feedback__score{font-family:var(--mono);font-size:11px;letter-spacing:.06em;color:var(--dunkelgrau)}
.feedback__meta{margin:3px 0 0;font-size:13px;color:var(--dunkelgrau);overflow-wrap:anywhere}
.feedback__text{margin:8px 0 0;font-size:14.5px;white-space:pre-wrap;overflow-wrap:anywhere}

/* ---------- Footer ---------- */
.footer{
  max-width:var(--maxw);margin:0 auto;padding:22px clamp(16px,4vw,28px) 36px;
  border-top:1px solid var(--line);
  font-family:var(--mono);font-size:11.5px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--dunkelgrau);
}
.footer a{color:var(--dunkelgrau)}
.footer a:hover{color:var(--blau)}
/* Versionsnummer in der Fußzeile: sieht aus wie die Nachbarlinks, ist aber ein Knopf —
   sie führt nirgendwo hin, sondern öffnet die Neuerungen. `font:inherit` holt die
   Monospace-Auszeichnung der Fußzeile zurück, die der Browser Formularelementen nimmt. */
.footer__version{
  background:none;border:0;padding:0;cursor:pointer;
  font:inherit;letter-spacing:inherit;text-transform:inherit;color:var(--dunkelgrau);
}
.footer__version:hover{color:var(--blau);text-decoration:underline}
/* „Fehler oder Wunsch melden" in der Fußzeile — derselbe Bau wie die Versionsnummer: sieht
   aus wie die Nachbarlinks, ist aber ein Knopf.
   SEIT DEM 21.08.2026 GIBT ES IHN NUR NOCH AUF ZWEI SEITEN: bestaetigen.html und
   feedback.html, den einzigen ohne Menü. Überall sonst ist der Meldeweg als Menüpunkt
   „Feedback" nach oben gezogen (.hr-link--knopf). Die Regel lautet: entweder der Menüpunkt
   ODER dieser Knopf, nie beides und nie keins — sonst fiele der Meldeweg ausgerechnet dort
   weg, wo Menschen ohne Konto stranden. tests/anliegen-melden.test.js hält das fest. */
.footer__melden{
  background:none;border:0;padding:0;cursor:pointer;
  font:inherit;letter-spacing:inherit;text-transform:inherit;color:var(--dunkelgrau);
}
.footer__melden:hover{color:var(--blau);text-decoration:underline}

/* ---------- Anliegen melden (Dialog) ---------- */
/* Schmaler als der Vorgabewert von .modal__box (740px): Drei Felder brauchen keine
   Textbreite, und ein zu breites Textfeld lädt zu Zeilen ein, die niemand mehr liest. */
.anliegen__box{max-width:min(560px,94vw)}
.anliegen__arten{border:0;margin:18px 0 0;padding:0;display:grid;gap:8px}
.anliegen__arten>legend{
  padding:0;margin:0 0 8px;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.07em;text-transform:uppercase;color:var(--dunkelgrau);
}
/* Die ganze Zeile ist anklickbar (das <label> umschließt den Knopf) — auf dem Telefon ist
   ein Radiofeld allein zu klein für einen Daumen. */
.anliegen__art{
  display:flex;gap:10px;align-items:flex-start;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--radius-sm);padding:10px 12px;background:var(--weiss);
}
.anliegen__art:hover{border-color:var(--dunkelgrau)}
.anliegen__art small{color:var(--dunkelgrau)}
.anliegen__label{
  display:block;margin:20px 0 6px;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.07em;text-transform:uppercase;color:var(--dunkelgrau);
}
.anliegen__text{width:100%;min-height:140px;resize:vertical}
.anliegen__zaehler{margin:6px 0 0;font-size:13px;color:var(--dunkelgrau)}
/* Zu lang ist kein Fehler des Menschen, sondern eine Grenze — aber sie muss auffallen,
   solange der Absendeknopf deswegen gesperrt ist. */
.anliegen__zaehler--zuviel{color:var(--danger);font-weight:600}

/* ---- Das Bildschirmfoto ---- */
/* DIE WARNUNG IST NICHT WEGZUKLICKEN und steht auch dann noch da, wenn schon ein Bild
   angehängt ist — die Begründung steht im Kopf von js/anliegen-melden.js: Ein
   Bildschirmfoto nimmt fremde Namen und Adressen VERSEHENTLICH mit, und ein Hinweis, der
   nach dem Anhängen verschwindet, käme genau dann nicht mehr vor, wenn er etwas bewirken
   könnte.
   Amber und nicht Rot: Hier ist nichts schiefgegangen. Rot an dieser Stelle läse sich wie
   eine Absage — und wer melden will, ist ohnehin schon verärgert. */
.anliegen__warnung{
  margin:8px 0 0;font-size:13px;line-height:1.5;color:var(--warn);
  background:var(--warn-tint);border-left:3px solid var(--warn);
  padding:9px 12px;border-radius:0 var(--radius-sm) var(--radius-sm) 0;
}
.anliegen__anhang{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:10px 0 0}
.anliegen__einfuegen{font-size:13px;color:var(--dunkelgrau)}
/* Wer ein Bild anhängt, muss SEHEN, was er anhängt — sonst prüft er die Warnung oben an
   etwas, das er sich nur vorstellt. Die Höhe ist gedeckelt, damit ein Hochkantfoto den
   Dialog nicht über drei Bildschirmlängen zieht. */
.anliegen__vorschau{margin:12px 0 0}
.anliegen__bild{
  display:block;max-width:100%;max-height:220px;
  border:1px solid var(--line);border-radius:var(--radius-sm);
}
.anliegen__bild-zeile{
  display:flex;gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap;margin:8px 0 0;
}
.anliegen__bild-info{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--dunkelgrau);
}

/* ---- Wer meldet ---- */
/* Der Kasten ist im Markup LEER und wird gefüllt, sobald die Anmeldelage feststeht. Er
   bekommt deshalb keinen Abstand von sich aus — sonst klaffte vor der Antwort eine Lücke,
   die aussähe, als fehle dort etwas. */
.anliegen__wer-satz{margin:18px 0 0;font-size:13px;color:var(--dunkelgrau)}
.anliegen__feld{width:100%}

.anliegen__beilage{margin:10px 0 0;font-size:13px;color:var(--dunkelgrau);overflow-wrap:anywhere}
.anliegen__meldung{margin:14px 0 0;font-size:14px}
.anliegen__meldung--err{
  background:var(--danger-tint);border-left:3px solid var(--danger);
  padding:10px 14px;border-radius:0 var(--radius-sm) var(--radius-sm) 0;
}
.anliegen__aktionen{display:flex;gap:10px;justify-content:flex-end;margin:20px 0 0;flex-wrap:wrap}

/* ---- Der Hinweis auf die Freischaltung ---- */
/* Er steht NUR bei Wunsch und Idee da (js/anliegen-melden.js) und ist deshalb blau und nicht
   amber: Hier ist nichts schiefgegangen und nichts zu befürchten — es ist eine Auskunft
   darüber, was mit dem Text passiert. Amber an dieser Stelle läse sich wie eine Warnung vor
   dem eigenen Wunsch, und genau das soll er nicht sein.
   Der erste Satz steht fett (was passiert), der zweite normal (was NICHT passiert) — wer nur
   die erste Zeile überfliegt, hat trotzdem das Wichtigste gelesen. */
.anliegen__frei-hinweis{
  margin:12px 0 0;font-size:13px;line-height:1.5;color:var(--blau-dark);
  background:var(--blau-tint);border-left:3px solid var(--blau);
  padding:9px 12px;border-radius:0 var(--radius-sm) var(--radius-sm) 0;
}

/* ---------- Die zweite Ansicht des Dialogs: die Wunschliste ---------- */
/* SIE GIBT ES NUR FÜR MITGLIEDER, und für alle anderen entsteht sie gar nicht erst — es gibt
   deshalb bewusst keinen „gesperrten" Zustand dieser Klassen. Die Begründung steht im Kopf
   von js/anliegen-melden.js: Ein ausgegrauter Reiter erzählt einem Gast nur, dass es etwas
   gibt, das er nicht sehen darf. */
.anliegen__reiter-zeile{display:flex;gap:6px;margin:16px 0 0;border-bottom:1px solid var(--line)}
.anliegen__reiter{
  background:none;border:0;border-bottom:2px solid transparent;margin-bottom:-1px;
  padding:8px 12px;cursor:pointer;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--dunkelgrau);
}
.anliegen__reiter:hover{color:var(--blau)}
.anliegen__reiter--aktiv{color:var(--blau);border-bottom-color:var(--blau)}
.anliegen__liste{margin:18px 0 0}
.anliegen__leer{margin:16px 0 0;font-size:14px;color:var(--dunkelgrau)}

/* Ein Wunsch als eigener Kasten. Die Liste wird überflogen, nicht gelesen — deshalb steht
   oben die Marke und nicht der Text. */
.anliegen__wunsch{
  border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:12px 14px;margin:12px 0 0;background:var(--weiss);
}
.anliegen__wunsch-kopf{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.anliegen__marke{
  display:inline-block;padding:1px 7px;border-radius:var(--pill);
  font-family:var(--mono);font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;
  background:var(--tint-neutral);color:var(--tint-neutral-text);border:1px solid var(--line);
  white-space:nowrap;
}
/* Die Lage trägt die Farbe, und GRÜN GIBT ES NUR EINMAL — dieselbe Regel wie bei den
   Versandlagen im Verwaltungsreiter: „umgesetzt" ist die einzige Lage, in der wirklich etwas
   fertig ist. „abgelehnt" bleibt grau und bekommt einen gestrichelten Rahmen statt Rot: Es
   ist eine Entscheidung und kein Fehler — Rot läse sich wie eine Störung und wäre nach drei
   Tagen weggelesen. „neu" und „gesehen" tragen die Grundfarbe; sie sagen nichts, was eine
   eigene Farbe verdiente. */
.anliegen__marke--geplant{background:var(--blau-tint);color:var(--blau-dark);border-color:var(--blau-line)}
.anliegen__marke--umgesetzt{background:var(--gruen-tint);color:var(--tint-green-text);border-color:var(--tint-green-border)}
.anliegen__marke--abgelehnt{border-style:dashed}
.anliegen__wunsch-wann{margin-left:auto;font-size:12px;color:var(--dunkelgrau)}
/* pre-wrap wie im Verwaltungsreiter, und aus demselben Grund: Die Absätze des Melders
   bleiben erhalten. overflow-wrap gegen die lange Zeichenkette ohne Leerzeichen. */
.anliegen__wunsch-text{margin:10px 0 0;white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.55}
.anliegen__wunsch-antwort{
  margin:10px 0 0;padding:9px 12px;font-size:14px;line-height:1.5;
  background:var(--surface-2);border-left:3px solid var(--blau);
  border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  white-space:pre-wrap;overflow-wrap:anywhere;
}
.anliegen__stimmzeile{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:12px 0 0}
.anliegen__stimmknopf{padding:5px 13px;font-size:13px}
/* Die eigene Stimme ist ohne Lesen zu erkennen: Wer die Liste überfliegt, will sehen, wo er
   schon dabei ist, ohne jeden Knopf zu entziffern. */
.anliegen__stimmknopf--dafuer{
  background:var(--gruen-tint);color:var(--tint-green-text);border-color:var(--tint-green-border);
}
.anliegen__stimmzahl{font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--dunkelgrau)}
/* Ist entschieden, steht hier kein Knopf mehr, sondern dieser Satz — ein Knopf, der nur eine
   Abfuhr erzeugen kann, wäre eine Falle. */
.anliegen__stimme-zu{font-size:12.5px;color:var(--dunkelgrau)}

/* ---------- Anliegen (Verwaltungsreiter) ---------- */
/* pre-wrap, damit die Absätze des Melders erhalten bleiben — ein Fehlerbericht ist oft eine
   Aufzählung, und in einer Zeile zusammengeschoben ist er kaum noch zu lesen.
   overflow-wrap:anywhere gegen die lange Zeichenkette ohne Leerzeichen (eine eingefügte
   Adresse, eine Kennung): Ohne sie zieht eine einzige Meldung die ganze Tabelle breit. */
.anl__text{white-space:pre-wrap;overflow-wrap:anywhere;max-width:52ch}
/* Version und Seite sind technische Werte und sollen als solche aussehen. */
.anl__wert{font-family:var(--mono);font-size:12px;overflow-wrap:anywhere}
.anl__summe{font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--dunkelgrau)}

/* ---- Woher Name und Adresse stammen ---- */
/* DIE WICHTIGSTE ANZEIGE DES REITERS, und sie ist FARBIG, weil sie in einer Tabelle sonst
   untergeht: Eine belegte und eine frei erfundene Adresse stehen hier untereinander und
   sehen ohne den Marker gleich aus.
   Amber für „ungeprüft" und nicht Rot: Die Meldung ist in Ordnung, nur die Adresse ist es
   möglicherweise nicht. Rot hieße „hier ist etwas kaputt" und wäre nach drei Tagen
   weggelesen — dann fiele auch die echte Warnung nicht mehr auf. */
.anl__quelle{
  display:inline-block;margin-top:4px;padding:1px 7px;border-radius:var(--pill);
  font-family:var(--mono);font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;
  border:1px solid transparent;white-space:nowrap;
}
.anl__quelle--konto{background:var(--gruen-tint);color:var(--tint-green-text);border-color:var(--tint-green-border)}
.anl__quelle--selbst{background:var(--warn-tint);color:var(--warn);border-color:var(--tint-amber-border)}
.anl__quelle--unbekannt{background:var(--tint-neutral);color:var(--tint-neutral-text);border-color:var(--line)}

/* ---- Das Bildschirmfoto in der Zeile ---- */
/* Klein in der Liste, groß auf Klick. Der Knopf ist bewusst nur der Rahmen um das Bild —
   das Bild selbst ist die Schaltfläche, alles andere wäre eine zweite Stelle zum Treffen. */
.anl__bildzeile{margin:8px 0 0}
.anl__bildknopf{
  display:inline-block;padding:0;border:1px solid var(--line);border-radius:var(--radius-sm);
  background:none;cursor:pointer;line-height:0;overflow:hidden;
}
.anl__bildknopf:hover{border-color:var(--blau)}
.anl__bildknopf:disabled{cursor:default;border-style:dashed;line-height:1.4;padding:6px 10px}
.anl__vorschau{display:block;max-width:180px;max-height:110px;object-fit:cover}
.anl__ohnebild{font-size:12px;color:var(--dunkelgrau)}

/* ---- Das Bild groß ---- */
/* z-index 70 wie die Overlays in admin.js: über der Kopfleiste (50) und über den
   Dialogen (60). Das Schachbrett dahinter, weil ein Bildschirmfoto durchsichtig sein kann
   (siehe .zuschnitt__grund) — auf weißem Grund sähe man den Unterschied nicht. */
.anl__gross{
  position:fixed;inset:0;z-index:70;background:var(--overlay);
  display:flex;align-items:center;justify-content:center;padding:16px;
}
.anl__gross-kasten{
  background:var(--weiss);border-radius:var(--radius);padding:14px;
  max-width:min(1100px,96vw);max-height:92vh;overflow:auto;box-shadow:var(--shadow-modal);
}
.anl__gross-bild{display:block;max-width:100%;max-height:78vh;border-radius:var(--radius-sm)}
.anl__gross-hinweis{margin:0;font-size:14px;color:var(--dunkelgrau)}
.anl__gross-leiste{display:flex;gap:12px;align-items:center;justify-content:space-between;margin:12px 0 0;flex-wrap:wrap}

/* ---- Die Spalte „Vorgang": was schon geschehen ist ---- */
/* Beide Marken teilen sich die Form mit .anl__quelle — eine Plakette in Versalien, klein und
   einfarbig. Das ist Absicht: Wer eine der drei kennt, liest die anderen beiden ohne
   Nachdenken. Unterschieden werden sie über die FARBE, und die trägt hier die ganze
   Aussage. */
.anl__marken{display:flex;flex-wrap:wrap;gap:5px;margin:0 0 8px}
.anl__frei,.anl__versand{
  display:inline-block;padding:1px 7px;border-radius:var(--pill);
  font-family:var(--mono);font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;
  border:1px solid transparent;white-space:nowrap;
}
.anl__frei--an{background:var(--blau-tint);color:var(--blau-dark);border-color:var(--blau-line)}
.anl__frei--aus{background:var(--tint-neutral);color:var(--tint-neutral-text);border-color:var(--line)}

/* DIE VIER VERSANDLAGEN — und GRÜN GIBT ES NUR EINMAL.
   „bestätigt" ist die einzige Lage, in der wirklich etwas angekommen ist; sie allein trägt
   den Haken und die grüne Fläche. „angestoßen, nicht bestätigt" ist amber und ausdrücklich
   NICHT grün: Die Mail ist unterwegs ODER verloren, und das ist eine Aufgabe. Genau hier
   setzt die Vorlage (SkillDB) ihren grünen Haken — vor dem Versand, ohne Quittung. */
.anl__versand--bestaetigt{background:var(--gruen-tint);color:var(--tint-green-text);border-color:var(--tint-green-border)}
.anl__versand--unterwegs{background:var(--warn-tint);color:var(--warn);border-color:var(--tint-amber-border)}
.anl__versand--fehler{background:var(--danger-tint);color:var(--danger);border-color:var(--danger)}
.anl__versand--keiner{background:var(--tint-neutral);color:var(--tint-neutral-text);border-color:var(--line)}
.anl__bearbeiten{padding:5px 11px;font-size:12.5px}

/* ---- Das Bearbeitungsfenster ---- */
/* z-index 70 wie die übrigen Overlays des Verwaltungsbereichs: über der Kopfleiste (50) und
   über den Dialogen (60). align-items:flex-start und overflow:auto, weil dieses Fenster
   höher wird als der Bildschirm — mit `center` läge sein Kopf dann außerhalb und wäre nicht
   mehr zu erreichen. */
.anl__vorgang{
  position:fixed;inset:0;z-index:70;background:var(--overlay);
  display:flex;align-items:flex-start;justify-content:center;padding:clamp(12px,4vw,40px);overflow:auto;
}
.anl__vorgang-kasten{
  background:var(--weiss);border-radius:var(--radius);padding:clamp(18px,3vw,26px);
  max-width:720px;width:100%;min-width:0;box-shadow:var(--shadow-modal);
}
.anl__vorgang-titel{margin:0 0 4px;font-size:19px}
/* Der gemeldete Text ist Freitext von außen und bekommt deshalb eine eigene Fläche: Er
   soll sich sichtbar von allem abheben, was WIR geschrieben haben. */
.anl__vorgang-meldung{
  margin:12px 0 0;max-width:none;padding:12px 14px;
  background:var(--surface-2);border-radius:var(--radius-sm);border:1px solid var(--line-soft);
}

/* ---- Freischalten ---- */
.anl__freizeile{margin:18px 0 0;padding:12px 14px;border:1px solid var(--line);border-radius:var(--radius-sm)}
.anl__schalter{display:flex;gap:9px;align-items:center;font-weight:600}
.anl__schalter input{width:17px;height:17px;flex:0 0 auto}
.anl__freizeile .note{margin:6px 0 0}

/* ---- Die Vorschau der öffentlichen Fassung ---- */
/* SIE IST DIE ZWEITE SICHERUNGSSCHICHT und darf deshalb nicht aussehen wie ein weiterer
   Absatz. Der blaue Rand links sagt „das hier ist ein Zitat aus einer anderen Ansicht" —
   es ist die einzige Stelle im Fenster, die zeigt, was ANDERE sehen würden.

   Sie heißt __oeffentlich und NICHT __vorschau: Diesen Namen trägt oben schon die kleine
   Bildvorschau in der Zeile (.anl__vorschau am <img>). Zwei Bedeutungen unter einem
   Klassennamen wären nicht nur unsauber — ein Klickweg, der `.anl__vorschau` sucht, fände
   dann zwei Elemente und wüsste nicht, welches gemeint ist. */
.anl__oeffentlich{
  margin:18px 0 0;padding:14px 16px;border-radius:var(--radius-sm);
  background:var(--blau-tint);border-left:3px solid var(--blau);
}
.anl__oeffentlich--keine{background:var(--tint-neutral);border-left-color:var(--line)}
.anl__oeffentlich-titel{
  margin:0 0 6px;font-family:var(--mono);font-size:11px;font-weight:700;
  letter-spacing:.07em;text-transform:uppercase;color:var(--dunkelgrau);
}
.anl__oeffentlich .anl__text{max-width:none;margin:10px 0 0;font-size:14.5px}
.anl__oeffentlich-zeile{margin:10px 0 0;font-size:13px;color:var(--dunkelgrau)}
.anl__oeffentlich-stand{font-family:var(--mono);font-size:12px;color:var(--schwarz)}
/* Die Antwort ist im Blick der Mitglieder ein eigener Absatz unter dem Wunsch — hier
   ebenso, sonst liest sie sich wie eine Fortsetzung des maskierten Textes. */
.anl__oeffentlich-antwort{margin:14px 0 0}
.anl__oeffentlich .note{margin:6px 0 0}

/* ---- Die drei Textfelder ---- */
.anl__feld{display:block;margin:18px 0 0}
.anl__feld-name{display:block;margin:0 0 5px;font-weight:600;font-size:14px}
.anl__feld textarea,.anl__feld select{width:100%;box-sizing:border-box}
.anl__feld textarea{resize:vertical;line-height:1.5}
/* Der Satz, wer das Feld liest, steht DIREKT darunter und dicht dran — mit dem üblichen
   Abstand einer .note hinge er zwischen zwei Feldern und gehörte optisch zu keinem. */
.anl__feldhinweis{margin:5px 0 0}

/* ---- Versandzustand und Empfänger ---- */
.anl__versandkasten{margin:20px 0 0;padding:12px 14px;border-radius:var(--radius-sm);border:1px solid var(--line)}
.anl__versandkasten--bestaetigt{background:var(--gruen-tint);border-color:var(--tint-green-border)}
.anl__versandkasten--unterwegs{background:var(--warn-tint);border-color:var(--tint-amber-border)}
.anl__versandkasten--fehler{background:var(--danger-tint);border-color:var(--danger)}
.anl__versandkasten-kopf{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:0}
.anl__versandkasten .note{margin:6px 0 0}
/* Der Klartext des Servers. Er ist das, was aus einer stillen Lücke eine Aufgabe macht —
   ohne ihn stünde dort „hat nicht geklappt". */
.anl__versandkasten-grund{margin:8px 0 0;font-family:var(--mono);font-size:12.5px;color:var(--danger);overflow-wrap:anywhere}

.anl__ziel{margin:14px 0 0;padding:12px 14px;border-radius:var(--radius-sm);border:1px solid var(--line)}
.anl__ziel--ungeprueft{background:var(--warn-tint);border-color:var(--tint-amber-border)}
.anl__ziel--ohne{background:var(--tint-neutral)}
.anl__ziel-satz{margin:0;font-size:14px}
.anl__ziel .note{margin:6px 0 0}

/* ---- Die Absage des Servers ---- */
/* Sie steht ZWISCHEN den Feldern und den Knöpfen: dort, wo der Blick nach einem Klick
   hinfällt. Und sie hat einen zweiten Satz, der sagt, dass nichts gespeichert wurde — sonst
   tippt jemand seinen Text noch einmal, den er längst vor sich hat. */
.anl__vorgang-fehler{
  margin:16px 0 0;padding:12px 14px;border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  background:var(--danger-tint);border-left:3px solid var(--danger);
}
.anl__fehlersatz{margin:0;font-size:14px;color:var(--danger);font-weight:600}
.anl__fehlerzusatz{margin:6px 0 0;font-size:13px;color:var(--schwarz)}

.anl__vorgang-leiste{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;margin:20px 0 0}

/* ---- Hinweiszeilen INNERHALB der getönten Kästen ---- */
/* .note ist im ganzen Haus eine blau getönte Zeile mit blauem Rand links. In einem Kasten,
   der selbst schon getönt ist (die Vorschau blau, der Fehlversand rot, die ungeprüfte
   Adresse amber), säße damit eine zweite Tönung in der ersten — und ausgerechnet im roten
   Kasten stünde der erklärende Satz blau. Die Farbe trägt hier der Kasten; der Satz darin
   ist nur noch Text. Die Hinweise unter den drei EINGABEFELDERN behalten ihre Tönung: Dort
   ist sie der Grund, warum man sie liest. */
.anl__oeffentlich .note,.anl__versandkasten .note,.anl__ziel .note,.anl__freizeile .note{
  background:none;border-left:0;padding:0;border-radius:0;font-size:13px;color:var(--dunkelgrau);
}

/* ---------- Neuerungen (Dialog) ---------- */
/* Der Inhalt kommt aus CHANGELOG.md: # wird h2 (der Titel des Dialogs), ## h3 (eine
   Fassung), ### h4 (eine Rubrik darin). Ohne eigene Größen stünden h2 und h3 fast gleich
   groß nebeneinander — die Gliederung der Datei wäre dann nicht mehr zu sehen. */
.changelog h2{font-size:clamp(20px,3vw,26px);margin:0 0 10px}
.changelog h3{font-size:17px;margin:26px 0 4px;padding-top:14px;border-top:1px solid var(--line)}
.changelog h4{
  margin:18px 0 4px;font-family:var(--mono);font-size:11.5px;font-weight:700;
  letter-spacing:.07em;text-transform:uppercase;color:var(--dunkelgrau);
}
.changelog ul{margin:0 0 14px;padding-left:20px}
.changelog li{margin:0 0 8px}

/* Personen je Firma (Reiter Unternehmen): aufklappbare Liste hinter der Anzahl.
   Native <details>/<summary> — kein JS zum Auf- und Zuklappen noetig. */
.ptPeople>summary{cursor:pointer;user-select:none}
.ptPeople__list{list-style:none;margin:6px 0 0;padding:0}
.ptPeople__list li{padding:2px 0;white-space:nowrap}
/* Sammellauf: die Uebersprungenen. Aufklappbar aus demselben Grund wie oben — und die
   Zusammenfassung traegt IMMER die Zahl, damit auch die zugeklappte Liste sagt, wie viele
   nicht dabei sind. */
.invSkip>summary{cursor:pointer;user-select:none;font-weight:600}
.invSkip ul{margin:8px 0 0;padding-left:20px}
/* Button, der wie ein Link aussieht — fuer Sprungziele innerhalb einer Tabellenzelle. */
.btn--link{background:none;border:0;padding:0;font:inherit;color:var(--blau);cursor:pointer;text-align:left;text-decoration:underline}
.btn--link:hover{text-decoration:none}

/* ---------- Aktionen in Listenzeilen (31.07.2026) ----------
   Bis dahin standen dort umrandete Knoepfe. Bei acht Aktionen je Zeile fraßen sie die
   halbe Tabellenbreite und alle sahen gleich wichtig aus. Jetzt: Text mit Verlinkung.

   ES BLEIBT EIN <button>, auch wenn es wie ein Link aussieht. Das ist kein Versehen:
   Ein <a> ohne Ziel ist mit der Tastatur nicht erreichbar und wird vorgelesen als
   „Link, ohne Ziel“. Diese Dinge FUEHREN NICHT irgendwohin, sie TUN etwas — deshalb
   Schaltflaeche im Aufbau und Verlinkung im Aussehen.

   DREI STUFEN, und die zweite und dritte sind kein Schmuck: Waeren alle Aktionen
   derselbe blaue Text, saehe „Endgueltig loeschen“ genauso harmlos aus wie „Bearbeiten“.
   Das Entschlacken darf die Gefahr nicht mit wegraeumen. */
.btn--link-primary{font-weight:600;text-decoration:none}
.btn--link-primary:hover{text-decoration:underline}
/* Rot und ohne Unterstreichung im Ruhezustand — es soll auffallen, ohne zu schreien.
   Die Rueckfrage davor bleibt in JEDEM Fall bestehen; die Farbe ersetzt sie nicht. */
.btn--link-danger{color:var(--danger);text-decoration:none}
.btn--link-danger:hover{text-decoration:underline}
/* Die Aktionen einer Zeile untereinander, mit ruhigem Abstand statt Knopfrahmen. */
.aktionen{display:flex;flex-direction:column;align-items:flex-start;gap:4px;font-size:13.5px}

/* WAS DAS ENTSCHLACKEN SONST GEKOSTET HAETTE — auf dem Telefon wird die Tabelle zur Karte,
   und aus fingergrossen Knoepfen waeren Textzeilen von rund 18px Hoehe mit 4px Abstand
   geworden. Wer daneben tippt, loescht im schlimmsten Fall die Nachbarzeile: „Loeschen"
   steht direkt unter „Bearbeiten". Mit Maus ist das kein Thema, mit dem Daumen schon.
   Deshalb hier zurueck auf eine Flaeche, die man trifft — das Aussehen bleibt Text. */
@media (max-width:640px){
  .aktionen{gap:2px}
  .aktionen .btn--link{min-height:34px;display:flex;align-items:center;padding:2px 0}
}

/* ---------- Der Protokoll-Ausschnitt im Dialog (LogView.abschnitt) ----------
   Der Reiter „Log" hat die ganze Bildschirmbreite und zeigt dort seine sechs Spalten.
   Im Fenster einer Anmeldung sind es rund 700px — dieselbe Tabelle stand dort in einem
   Rollbereich, und man schob jede Zeile von Hand hin und her, um sie zu lesen. Gemeldet vom
   Auftraggeber am 31.07.2026. Hier steht deshalb ein BLOCK je Eintrag: Zeitpunkt und Art
   oben mit dem Ergebnis rechts, der Vorgang als Hauptzeile, der Handelnde darunter. Nichts
   steht mehr nebeneinander, also gibt es auch nichts mehr zu schieben.
   Natives <details>/<summary> wie beim Export-Block und der Spaltenauswahl — kein JS zum
   Auf- und Zuklappen noetig, und der Klapp-Pfeil ist der des Browsers. */
.logk{margin-top:18px;padding-top:14px;border-top:1px solid var(--line-soft)}
.logk>summary{cursor:pointer;user-select:none;padding:2px 0}
/* Die Ueberschrift bleibt eine Ueberschrift — die Gliederung ist fuer Vorleseprogramme da.
   Als Block schoebe sie den Klapp-Pfeil auf eine eigene Zeile darueber. */
.logk>summary>h3{display:inline;font-size:15px}
.logk small{color:var(--dunkelgrau)}
.logk__erklaerung{margin:8px 0 0;font-size:12.5px}
.logk__liste{margin:10px 0 0}
.logk__nachsatz{margin:8px 0 0;font-size:12.5px}
/* Ein Eintrag. Die Trennlinie zwischen den Bloecken tritt an die Stelle der Zeilenlinien. */
.logk__eintrag{padding:8px 0;border-top:1px solid var(--line-soft)}
.logk__eintrag:first-child{border-top:0;padding-top:0}
/* Kopfzeile des Blocks: links Zeitpunkt und Art, rechts das Ergebnis. flex-wrap ist Pflicht,
   nicht Vorsicht: .badge verbietet den Umbruch, und „Von Outlook angenommen" ist lang genug,
   um im schmalen Dialog neben dem Zeitpunkt keinen Platz mehr zu finden. Ohne wrap schoebe
   die Plakette den Kasten auf. */
.logk__kopf{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline;justify-content:space-between;margin:0}
.logk__kopf>small{font-family:var(--mono);font-size:11px;letter-spacing:.04em}
/* Nichts darf breiter werden als der Kasten: Adressen, Gruende und Betreffs sind lange
   Zeichenketten, in denen es keine Stelle zum Umbrechen gibt. `anywhere` bricht sie
   trotzdem um, statt den Dialog in die Breite zu ziehen — sonst waere der waagerechte
   Rollbalken nur von der Tabelle an den Dialog gewandert. */
.logk__vorgang{margin:3px 0 0;font-size:14px;overflow-wrap:anywhere}
.logk__wer{margin:3px 0 0;font-size:12.5px;color:var(--dunkelgrau);overflow-wrap:anywhere}

/* ---------- Der Grund im Zuschnitt-Fenster (09.08.2026) ----------
   Vorher schwarz. Ein Logo mit durchsichtigem Hintergrund stand damit im Fenster auf
   Schwarz — ausgerechnet der Eindruck, den der Erhalt der Durchsicht beseitigen soll. Die
   Vorschau log also ueber das Ergebnis.
   Das Schachbrett ist die eingefuehrte Art, Durchsicht zu zeigen; jeder erkennt es. Fuer
   ein DECKENDES Bild aendert sich nichts: Es liegt vollflaechig darueber, der Grund ist
   unsichtbar.
   DREI ORTE, und alle drei halten dasselbe Versprechen: das Fenster des Bild-Blocks, die
   Vorschau des angehaengten Bildschirmfotos (js/anliegen-melden.js) und dessen grosse
   Ansicht im Verwaltungsreiter (js/admin-anliegen.js). An allen dreien bleibt die
   Durchsicht erhalten, wenn das Bild sie hat. NICHT beim Titelbild — dort wird ohnehin
   immer nach JPEG gewandelt, und ein Schachbrett waere ein Versprechen, das die Ausgabe
   nicht haelt. */
.zuschnitt__grund{
  background-color:var(--schach-grund);
  background-image:
    linear-gradient(45deg,var(--schach-feld) 25%,transparent 25%,transparent 75%,var(--schach-feld) 75%),
    linear-gradient(45deg,var(--schach-feld) 25%,transparent 25%,transparent 75%,var(--schach-feld) 75%);
  background-size:18px 18px;
  background-position:0 0,9px 9px;
}

/* ---------- Der Überblick im Verwaltungsbereich (23.08.2026) ----------

   Der erste Reiter, und damit das Erste, was der Auftraggeber mehrmals täglich sieht. Er
   folgt dem bestehenden System und bringt KEINEN zweiten Stil mit: dieselben Kartenflächen
   wie .card, dieselbe Mono-Auszeichnung wie .card__eyebrow, dieselben CI-Farben. Der einzige
   neue Baustein ist der Balken — und der ist aus zwei divs, nicht aus einem Diagramm-Paket.

   Die ZAHL ist der Verweis, nicht ein Text daneben: „Zahlen, die man nicht anklicken kann,
   sind Deko." Deshalb ist .dash__zahl selbst ein <a> und trägt keine Unterstreichung — sie
   soll wie eine Zahl aussehen und sich wie ein Verweis verhalten. */
.dash{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;margin-top:8px;
  /* Jede Kachel so hoch, wie ihr Inhalt sie macht. Gleich hohe Kacheln je Reihe wären die
     übliche Wahl, hier aber die schlechtere: „Wartet auf Entscheidung" trägt drei Namen und
     zöge die drei Kacheln daneben auf dieselbe Höhe — dreimal so viel Leerraum wie Inhalt. */
  align-items:start}
.dash__kachel{
  display:flex;flex-direction:column;gap:6px;background:var(--weiss);
  border:1px solid var(--line-soft);border-radius:var(--radius);
  padding:16px 18px 18px;box-shadow:var(--shadow-sm);
}
/* Die nächste Veranstaltung nimmt zwei Spalten — sie trägt Titel, Termin, Ort und den
   Balken, und in einer Spalte bräche der Titel auf fünf Zeilen um. Bei nur einer Spalte
   (schmaler Bildschirm) läuft die Regel ins Leere, weil das Gitter dann ohnehin einspaltig
   ist. */
.dash__kachel--weit{grid-column:span 2}
@media (max-width:640px){.dash__kachel--weit{grid-column:auto}}
.dash__marke{
  margin:0;font-family:var(--mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--dunkelgrau);
}
/* Mono und groß: Die Zahl ist die Aussage der Kachel. Die Ziffern der Mono-Schrift sind
   gleich breit — zwei Kacheln nebeneinander zeigen ihre Zahlen damit auf derselben Höhe und
   in derselben Breite, egal ob dort 4 oder 47 steht. */
.dash__zahl{
  /* margin:0, weil die Zahl auch als <p> auftritt (der Zustand „nicht nachgesehen"): Der
     Vorgabe-Abstand eines Absatzes ist bei 38px Schriftgrad ein Loch von 38px. */
  display:inline-block;align-self:flex-start;margin:0;
  font-family:var(--mono);font-size:38px;font-weight:700;line-height:1.05;letter-spacing:-.02em;
  color:var(--blau);text-decoration:none;
}
.dash__zahl:hover{color:var(--blau-hover);text-decoration:none}
/* Der Zustand „nicht nachgesehen": ein Fragezeichen in gedämpftem Grau, ausdrücklich keine
   0 und ausdrücklich kein Verweis — es gibt ja keine Liste, auf die er führen könnte. */
.dash__zahl--leer{color:var(--dunkelgrau)}
/* Ein Geldbetrag steht nicht in Blau: Er ist keine Liste, auf die man klickt, sondern ein
   Ergebnis. Mono, damit Beträge untereinander an derselben Stelle das Komma haben. */
.dash__geld{
  margin:0;font-family:var(--mono);font-size:32px;font-weight:700;line-height:1.1;
  letter-spacing:-.02em;color:var(--schwarz);
}
.dash__titel{margin:0;font-size:17.5px;font-weight:700;letter-spacing:-.01em;line-height:1.28;overflow-wrap:anywhere}
.dash__satz{margin:0;font-size:13.5px;color:var(--dunkelgrau);overflow-wrap:anywhere}
.dash__satz--stark{color:var(--schwarz);font-weight:600}
/* Der Hinweis, dass etwas fehlt oder unbekannt ist. Warnfarbe und nicht Rot: Es ist kein
   Fehler, sondern eine Lücke — und die Kachel daneben zeigt weiter, was bekannt ist. */
.dash__warnung{margin:0;font-size:13px;line-height:1.45;color:var(--warn);overflow-wrap:anywhere}
/* Die Aufzählung unter einer Zahl. Jede Zeile ist ein Verweis links und ihr Wert rechts —
   der Wert steht in Mono, damit die Zahlen untereinander fluchten. */
.dash__liste{list-style:none;margin:4px 0 0;padding:0;display:flex;flex-direction:column;gap:2px}
.dash__liste li{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 8px;
  padding:5px 0;border-top:1px solid var(--line-soft);font-size:13.5px;
}
.dash__liste li:first-child{border-top:0}
/* .dash__name steht für eine Zeile, deren erstes Element KEIN Verweis ist: eine offene
   Rechnung ohne Stripe-Adresse. Ohne dieselbe Breitenangabe rutschte der Betrag an genau
   dieser einen Zeile nach links, und die Spalte wäre gebrochen. */
.dash__liste a,.dash__liste .dash__name{flex:1 1 140px;min-width:0;overflow-wrap:anywhere}
.dash__liste small{flex:1 1 100%;color:var(--dunkelgrau);font-size:12.5px;overflow-wrap:anywhere}
/* „erst ab … abrechenbar" — der Grund, warum eine Zahl dasteht, an die heute niemand
   herangehen soll. Der Selektor ist ABSICHTLICH doppelt qualifiziert: `.dash__liste small`
   direkt darüber hat die Spezifität 0-1-1 und schlüge eine einfache Klasse (0-1-0) — die
   Warnfarbe hätte dann nie gegriffen, und der einzige Hinweis darauf wäre das Aussehen
   gewesen. Genau das ist in diesem Projekt schon einmal passiert, samt Test, der die
   Klasse prüfte statt die Wirkung. */
.dash__liste small.dash__nochnicht{color:var(--warn);font-weight:600}
.dash__wert{margin-left:auto;font-family:var(--mono);font-size:12px;letter-spacing:.04em;color:var(--dunkelgrau);white-space:nowrap}
/* „nicht abgeglichen" statt eines Betrags — die Lücke steht an der Stelle, an der sonst die
   Zahl steht, und sie ist die einzige Stelle der Liste, an der KEINE Auskunft steht.
   Warnfarbe wie .dash__warnung: kein Fehler, sondern eine Lücke.
   Der Selektor ist ABSICHTLICH doppelt qualifiziert. `.dash__wert` direkt darüber setzt das
   Grau und hat dieselbe Spezifität (0-1-0) — eine einfache Klasse gewänne nur über die
   Reihenfolge im Stylesheet und verlöre stillschweigend, sobald jemand die Regeln umstellt.
   Genau diese Sorte Fehler gab es hier schon zweimal (siehe .dash__nochnicht darüber). */
.dash__wert.dash__wert--unbekannt{color:var(--warn);font-weight:600}
/* „seit 12 Tagen offen" — wie lange eine Rechnung schon aussteht (06.09.2026). Ein <small>
   in der Liste und damit eine eigene Zeile: `.dash__liste small` weiter oben gibt ihm
   flex-basis:100%, Farbe und Größe. Warum eine eigene und nicht dieselbe: Die Kachel ist im
   Gitter 314 px breit, Nummer und Betrag füllen sie schon aus — nachgemessen bricht ein
   dritter Teil dort in jedem Fall um. Ein Umbruch, den das Stylesheet bestimmt, sieht an
   jeder Zeile gleich aus; einer, den die Textlänge bestimmt, nicht.
   KEINE eigene Farbe, auch nicht bei „Datum unbekannt" (anders als `.dash__wert--unbekannt`
   direkt darüber): Am Tag der Auslieferung trägt keine einzige Rechnung ein Datum, und sechs
   orange Zeilen untereinander läsen sich wie ein Ausfall. Wie viele es sind, sagt die
   Staffelzeile über der Liste.
   Tabellenziffern, damit die Zahlen der Zeilen untereinander fluchten — dieselbe Absicht wie
   bei der Mono-Schrift an `.dash__wert`, nur ohne den Bruch im Schriftbild. */
.dash__liste small.dash__alter{font-variant-numeric:tabular-nums}
/* Bei den Personen steht der Name oben und die Veranstaltung darunter — deshalb bricht die
   Zeile dort immer um (siehe .dash__liste small mit flex-basis 100%). */
.dash__liste--personen li{align-items:flex-start}
/* Der Balken. Zwei divs: die Bahn und die Füllung. Die Breite kommt als Prozentwert am
   style-Attribut, weil sie aus den Daten stammt — eine Klasse je möglichem Prozentwert wäre
   Unsinn. Voll ist er grün, solange Platz ist blau: dieselbe Bedeutung wie an den Plaketten
   („ausgebucht" ist kein Fehler, sondern das Ziel). */
.dash__balken{margin-top:2px;height:8px;border-radius:var(--pill);background:var(--surface-2);overflow:hidden}
.dash__balken-fuellung{height:100%;background:var(--blau);border-radius:var(--pill)}
.dash__balken-fuellung--voll{background:var(--gruen)}
/* Die ruhige Zeile am Fuß. Sie ersetzt die Kacheln, die eine 0 zeigen würden, und BELEGT
   damit, dass nachgesehen wurde — eine fehlende Kachel allein wäre von einer nie gebauten
   nicht zu unterscheiden. Bewusst schmal, grau und ohne Rahmen: Sie soll gelesen werden
   können, aber nichts an sich ziehen. */
.dash__still{
  margin:16px 0 0;padding:10px 14px;border-radius:var(--radius-sm);
  background:var(--surface-2);color:var(--dunkelgrau);font-size:13.5px;overflow-wrap:anywhere;
}

/* ---------- Sichtbarkeit im Mitgliederbereich (24.08.2026) ----------

   Was eine Firma von ihren SISTRIX-Werten sieht: je Kennzahl eine Kachel mit der aktuellen
   Zahl, der Einordnung in die Gruppe, der Verlaufslinie und den Werten als Tabelle.

   KEIN DIAGRAMM-PAKET, und das ist keine Sparsamkeit: Das Frontend nimmt keine
   Abhängigkeiten auf (CLAUDE.md). Die Linie ist ein <path>, dessen Koordinaten
   js/sichtbarkeit-ansicht.js selbst rechnet — genauso wie der Balken im Überblick aus zwei
   divs besteht und nicht aus einer Bibliothek.

   Die Kacheln folgen .dash__kachel: dieselbe Fläche, derselbe Rahmen, dieselbe
   Mono-Auszeichnung, dieselbe große Zahl. Ein zweiter Stil für dieselbe Sache wäre der
   Anfang zweier Systeme. */
.sicht{display:flex;flex-direction:column;gap:16px;margin-top:8px}
/* Der Hinweis, der die Löcher in der Kurve erklärt (Fensterregel,
   api/src/lib/sichtbarkeit.js). Er steht ganz oben und eine Spur größer als der Fließtext:
   Ohne ihn sähe eine Firma, deren Fenster gerade zu ist, eine Kurve, die im August aufhört —
   und das sieht aus wie ein ausgefallener Abruf, nicht wie Absicht.
   BLAU UND AUSDRÜCKLICH NICHT .note--err ODER .note--warn: Hier ist nichts schiefgegangen
   und niemandem ist etwas vorzuwerfen — wer schon bezahlt hat, wartet nur auf den Abgleich.
   Ein Alarmton machte aus einer Auskunft eine Anklage. */
.sicht__fenster{margin:12px 0 0;font-size:14.5px;line-height:1.55}
/* Firma und Domain als Mono-Zeile unter der Überschrift — dieselbe Auszeichnung wie
   .dash__marke: eine Herkunftsangabe, kein Text zum Lesen. */
.sicht__firma{
  margin:6px 0 0;font-family:var(--mono);font-size:11.5px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--dunkelgrau);overflow-wrap:anywhere;
}
.sicht__block{
  display:flex;flex-direction:column;gap:6px;background:var(--weiss);
  border:1px solid var(--line-soft);border-radius:var(--radius);
  padding:16px 18px 18px;box-shadow:var(--shadow-sm);
}
.sicht__titel{margin:0;font-size:17.5px;font-weight:700;letter-spacing:-.01em;line-height:1.28;overflow-wrap:anywhere}
/* Mono und groß wie .dash__zahl: gleich breite Ziffern, damit die drei Kacheln untereinander
   ihre Werte an derselben Stelle zeigen. Schwarz und ausdrücklich NICHT blau — hier führt
   kein Verweis weiter, und Blau heißt in diesem Haus „anklickbar". */
.sicht__zahl{
  margin:0;font-family:var(--mono);font-size:38px;font-weight:700;line-height:1.05;
  letter-spacing:-.02em;color:var(--schwarz);
}
.sicht__stand{margin:0;font-size:12.5px;color:var(--dunkelgrau)}
.sicht__wandelzeile{margin:0;font-size:13.5px;overflow-wrap:anywhere}
.sicht__wandel{color:var(--dunkelgrau)}
/* Die Richtung steht IMMER auch im Vorzeichen (+ oder −). Die Farbe ist die Zugabe, nicht
   die Aussage: Wer Grün und Rot nicht unterscheidet, liest dasselbe. Deshalb ist Rot hier
   auch kein Alarm, sondern schlicht die andere Richtung. */
.sicht__wandel--hoch{color:var(--gruen-text);font-weight:600}
.sicht__wandel--runter{color:var(--danger);font-weight:600}
.sicht__rang{margin:0;font-size:13.5px;color:var(--schwarz);overflow-wrap:anywhere}
/* Die Linie füllt die Kachelbreite, die Höhe steht fest. Ohne height wäre ein <svg> im Fluss
   150px hoch (die Vorgabe des Browsers) und das Seitenverhältnis nicht mehr das, für das die
   Koordinaten gerechnet wurden.
   preserveAspectRatio="none" im Markup zieht den viewBox waagerecht mit — deshalb trägt der
   Pfad vector-effect="non-scaling-stroke", sonst würde die Strichstärke mitgezerrt.
   overflow:visible, weil der Höchst- und der Tiefstwert GENAU auf dem Rand des viewBox
   liegen: Ohne sie wäre dort die halbe Strichstärke abgeschnitten. */
.sicht__linie{width:100%;height:120px;display:block;margin:6px 0 2px;overflow:visible}
/* stroke-linecap:round ist hier NICHT Zierde. Bei genau EINEM Messpunkt besteht der Pfad aus
   zweimal derselben Stelle, und erst die runde Kappe macht daraus einen sichtbaren Punkt.
   Ohne sie wäre die Kachel einer Firma nach ihrem ersten Lauf leer — und leer sieht aus wie
   „keine Daten". */
.sicht__pfad{fill:none;stroke:var(--blau);stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
@media (max-width:640px){
  /* Auf dem Telefon steht die Kachel auf ganzer Breite; 38px Ziffern brächen dort um. */
  .sicht__zahl{font-size:32px}
  .sicht__linie{height:96px}
}

/* ---------- Die Zeitfenster im Verwaltungsbereich (07.09.2026) ---------- */
/* Der Satz über der Einzelansicht, in der Sprache des BETREIBERS. Er steht bewusst über und
   nicht anstelle des Hinweises, den der Partner sieht: Diese Ansicht beantwortet „was sieht
   dieser Partner gerade?", und dazu gehört sein eigener Wortlaut. */
.sicht__betreiber{margin:12px 0 0}
/* Die Zeiträume in der Tabellenzelle: eine Liste ohne Aufzählungszeichen, weil die Zelle
   schmal ist und ein Punkt je Zeile hier nur Platz kostet. */
.sicht__zeitraeume{list-style:none;margin:2px 0 0;padding:0;display:flex;flex-direction:column;gap:6px}
.sicht__zeitraum{font-size:13.5px;line-height:1.35;white-space:nowrap}
/* Ein VERWEHRTER Zeitraum ist kein Alarm: Er ist die andere Hälfte derselben Auskunft, und
   der Grund steht in Worten daneben. Deshalb nur gedämpft und durchgestrichen — wer Farben
   nicht unterscheidet, liest am „verwehrt:" darunter dasselbe. */
.sicht__zeitraum--verwehrt{color:var(--dunkelgrau);text-decoration:line-through}
.sicht__herkunft{font-size:12px;white-space:normal}
/* Der Freischaltungs-Abschnitt unter der Auswahl — zugeklappt eine Zeile, aufgeklappt ein
   Formular. Natives <details>, kein eigenes JavaScript zum Auf- und Zuklappen. */
.freiblock{
  margin:12px 0 0;background:var(--weiss);border:1px solid var(--line-soft);
  border-radius:var(--radius);padding:10px 14px;
}
.freiblock>summary{cursor:pointer;font-weight:600;font-size:14px}
.freiform{display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end;margin:12px 0 0}
.freifeld{font-size:13px;color:var(--dunkelgrau)}
/* Der Grund braucht mehr Platz als ein Monat: Er ist ein ganzer Satz und geht dem Partner zu. */
.freifeld--weit{flex:1;min-width:260px}
.freifeld--weit input{width:100%}
/* input[type=month] fällt nicht unter die Feldregel weiter oben (die zählt die Typen einzeln
   auf) — hier steht sie deshalb noch einmal für diese beiden Felder. */
.freifeld input[type=month]{
  padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:var(--weiss);
}
/* Der Satz unter dem Grund-Feld. Er ist der Grund, warum niemand „Kulanz, weil er sich
   beschwert hat" hineinschreibt: Der Grund steht dem Partner als Bezeichnung seines
   Zeitraums gegenüber. */
.freihinweis{margin:10px 0 0}
/* Die Meldung des Formulars — Fehler wie Bestätigung. Sie steht IM Abschnitt, direkt über
   den Feldern, auf die sie sich bezieht, und der Abschnitt bleibt dafür aufgeklappt: Hinter
   einer zugeklappten Klappe wäre sie „genau so ein unsichtbarer Grund" (die Hausregel steht
   wörtlich in js/admin-partners.js). Ein Kurzhinweis wäre hier das Falsche — er verschwände
   nach ein paar Sekunden, und die getippten Werte stünden weiter unkommentiert da. */
.freimeldung{margin:10px 0 0}
/* Gelungen: grün statt rot, sonst derselbe Kasten. Die Farbe ist die Zugabe — der Satz sagt
   selbst, was passiert ist, und wer Grün und Rot nicht unterscheidet, liest dasselbe. */
.freimeldung--ok{background:var(--gruen-tint);border-left-color:var(--gruen)}
.freiliste{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:6px}
.freizeile{font-size:13.5px;line-height:1.4;overflow-wrap:anywhere}
.freizeitraum{font-family:var(--mono);font-size:12.5px}
.freigrund{color:var(--dunkelgrau)}

/* ---------- Das Beispiel der Erinnerungsmail (Kachel „Erinnerung", 27.08.2026) ---------- */
/* Die Mail ist REINER TEXT (siehe api/src/lib/mailer.js), und genau so muss sie hier stehen:
   Ihre Absätze und Zeilenumbrüche sind der ganze Aufbau, den sie hat. Ein <p> würde sie zu
   einem Block zusammenziehen — der Betrachter sähe eine andere Mail als der Empfänger.
   pre-wrap statt pre: Die Zeilen der Vorlage sind nicht umgebrochen, ohne Umbruch schöbe der
   Kalenderlink das Fenster waagerecht auf. overflow-wrap fängt genau diesen langen Link. */
.mailbeispiel{
  margin:10px 0 0;padding:14px 16px;
  background:var(--surface-2);border-radius:var(--radius-sm);
  font-family:var(--mono);font-size:13px;line-height:1.6;color:var(--schwarz);
  white-space:pre-wrap;overflow-wrap:anywhere;
}
/* Der Betreff steht ÜBER dem Kasten und nicht darin: In der Mail ist er auch nicht Teil des
   Textes, und wer ihn ändern will, findet ihn im Vorlagenformular als eigenes Feld. */
.mailbeispiel__betreff{margin:14px 0 0;font-size:14px;overflow-wrap:anywhere}

/* ---------- Die Seite „Mein Profil" (Migration 0074) ----------

   Der Wunsch des Auftraggebers, woertlich, auf die Frage, wo jemand sein Foto hinterlegt:
   „Eigene Seite ‚mein profil' und dort koennen auch telefon, Mobil, Mail-Adresse und
   Position hinterlegt werden. Ebenso LinkedIn Profil".

   DIESER ABSCHNITT ENTHAELT KEINE ZWEITE FASSUNG DES PLATZHALTERS. Die Initialen ohne Foto
   tragen dieselben Klassen wie die Kachel der Teilnehmerliste (.tnl__ph) und dieselben
   Farben wie die Kachel der Startseite (.card__ph--blau/--gruen). Was hier dazukommt, ist
   ausschliesslich die GROESSE — gross und rund statt klein und rund, denn hier steht der
   Mensch selbst und nicht ein Eintrag in einer Liste. Ein zweites Farbpaar oder eine zweite
   Form liefe beim naechsten Feilen an der Marke von jenen weg, und dieselbe Person saehe
   sich auf zwei Seiten verschieden. */
.profil__name{margin:2px 0 0;font-size:15px;color:var(--dunkelgrau)}
/* Das Foto und seine beiden Handgriffe stehen untereinander und LINKSBUENDIG — wie alles
   andere auf dieser Seite. Mittig stand es zuerst; auf dem Bildbeleg schwamm es dann in der
   Mitte einer sonst linksbuendigen Spalte, und der Hinweistext darunter zog eine breite
   zentrierte Insel auf. Eine Ausrichtung je Baustein ist keine Gestaltung, sondern ein
   Versehen mit Absicht. */
.profil__foto{
  display:flex;flex-direction:column;align-items:flex-start;gap:12px;
  margin:18px 0 24px;
}
/* 132px sind GEWAEHLT und nicht gemessen — eine bewusste Naeherung, damit sie niemand
   spaeter fuer eine ausgerechnete Zahl haelt: Bei 44px (Teilnehmerkachel) erkennt man ein
   Gesicht gerade eben, hier soll man PRUEFEN koennen, ob das hochgeladene Bild taugt.
   Deutlich groesser lohnt nicht — ein Portraet aus dem Ablageort ist selten schaerfer, und
   die Seite soll auf einen Bildschirm passen. */
.profil__bild{
  flex:0 0 auto;width:132px;height:132px;border-radius:50%;object-fit:cover;
}
/* Wie bei .tnl__bild gilt die graue Flaeche NUR fuer das <img>: Sie ist der Grund, auf dem
   ein noch nicht geladenes Foto steht. Stuende sie an .profil__bild, gewaenne sie gegen
   .card__ph--blau/--gruen (gleiche Spezifitaet, aber weiter unten in der Datei), und die
   Initialen saessen auf Grau statt auf der Markenflaeche. */
img.profil__bild{background:var(--surface-2)}
/* NUR DIE GROESSE. Ausrichtung, Schriftfamilie, Farbe und Sperrsatz kommen aus .tnl__ph,
   der Untergrund aus .card__ph--blau/--gruen. Wer hier eine Farbe oder ein display
   ergaenzt, macht daraus die dritte Fassung desselben Platzhalters. */
.profil__ph{font-size:44px}
.profil__fotoAktionen{display:flex;flex-wrap:wrap;gap:10px}
.profil__fotoHinweis{max-width:62ch;margin:0}

/* ---------- Der Widerspruch gegen die Anzeige des Fotos (Migration 0075) ----------

   DAS BILD BLEIBT STEHEN, ABER ES SIEHT ANDERS AUS. Die Person soll ihr eigenes Foto
   weiterhin sehen — wer nicht sehen darf, was er verbirgt, kann es nicht beurteilen. Genau
   deshalb braucht der Zustand aber ein Zeichen: Ein unveraendert dastehendes Foto saehe aus,
   als haette der Haken nichts bewirkt.

   GRAU UND BLASS, UND ZUSAETZLICH EIN SATZ DANEBEN (js/profil-ansicht.js, VERBORGEN_MARKE).
   Beides zusammen, weil jedes fuer sich falsch verstanden wuerde: Die graue Flaeche allein
   saehe nach einem kaputten oder halb geladenen Bild aus, der Satz allein ginge unter.

   NUR AM <img>: Ueber den Initialen waere die Blende falsch — sie sind kein Foto, sondern
   der Name in Kurzform und stehen in der Teilnehmerliste weiterhin. Die Ansicht setzt den
   Modifikator ohnehin nur, wenn es ein Bild gibt; die Einschraenkung hier ist die zweite
   Haelfte derselben Aussage und kostet nichts. */
.profil__foto--verborgen img.profil__bild{filter:grayscale(1);opacity:.5}
/* Der Satz unter dem Bild. Er traegt KEINE eigene Farbe: Ausgezeichnet ist er ueber die
   Schriftstaerke, alles Weitere kommt aus der Grauskala des Hauses — ein eigenes Farbpaar
   waere die Fassung, die beim naechsten Feilen an den Farben stehen bliebe. */
.profil__verborgen{margin:0;font-size:13.5px;font-weight:600;color:var(--dunkelgrau);max-width:62ch}
/* Der Haken selbst und sein erklaerender Satz. Der Abstand darueber trennt ihn vom Hinweis
   zum Foto: Es sind zwei verschiedene Aussagen — wo das Foto steht, und dass es gar nicht
   stehen soll. .switch bringt Ausrichtung und Kaestchen mit (Bauform des Hauses). */
.profil__haken{margin:14px 0 0;align-items:flex-start}
.profil__haken input{margin-top:3px}
.profil__hakenHinweis{max-width:62ch;margin:6px 0 0}

/* Die Felder. Eine Spalte auf schmalen Fenstern, zwei ab 640px — mehr waeren es nicht wert:
   Vier kurze Angaben und eine Adresse ergeben keine dritte Spalte, und ein Formular, das
   ueber die ganze Breite laeuft, ist schlechter zu lesen als eines in Lesebreite. */
.profil__felder{
  display:grid;grid-template-columns:1fr;gap:14px;max-width:760px;
}
@media (min-width:640px){.profil__felder{grid-template-columns:1fr 1fr}}
.profil__feld{display:flex;flex-direction:column;gap:4px;min-width:0}
/* Die E-Mail-Adresse und ihr Hinweissatz stehen ueber die ganze Breite: Der Satz erklaert,
   warum das Feld fest ist, und ein erklaerender Satz in einer halben Spalte bricht in fuenf
   Zeilen um. */
.profil__feld--fest{grid-column:1/-1}
.profil__hinweis{grid-column:1/-1;margin:0;max-width:70ch}
.profil__label{font-size:13.5px;font-weight:600;color:var(--dunkelgrau)}
.profil__eingabe{width:100%;min-width:0}
/* Ein festes Feld sieht auch fest aus. `readonly` allein aendert nichts am Aussehen — dann
   saehe es aus wie ein Feld, in das man tippen kann, und der Mensch fragte sich, warum
   nichts passiert. Der Zeiger sagt es zusaetzlich. */
.profil__eingabe[readonly]{background:var(--surface-2);color:var(--dunkelgrau);cursor:not-allowed}
/* Der Verweis steht dicht unter seinem Feld und in derselben Spalte — er gehoert zu
   ihm und nicht an das Ende des Formulars. */
.profil__linkedin{margin:2px 0 0;font-size:13.5px}
.profil__aktionen{margin:18px 0 0}
/* Die Rueckmeldung nach dem Speichern. Sie steht in einem aria-live-Bereich (siehe
   js/profil-ansicht.js): Ein Vorleseprogramm sagt „Gespeichert." sonst nie an, und der
   Mensch davor haette keine Bestaetigung.

   NUR DER ABSTAND STEHT HIER. Aussehen und Farbe kommen aus den Bausteinen des Hauses:
   .note fuer die ruhige Auskunft, .note--err fuer die Absage. Ein eigenes Farbpaar daneben
   waere eine zweite Fassung derselben Skala — und die eine, die beim naechsten Feilen an
   den Farben stehen bliebe. */
.profil__meldung{margin:12px 0 0}

/* ---------- Ernaehrung und Allergien (Migration 0077) ----------

   DERSELBE KASTEN AN DREI STELLEN: „Mein Profil", das Mitglieder-Anmeldeformular und das
   offene Anmeldeformular. Er traegt deshalb die Bauform des Hauses (.reg__group mit
   Legende, .switch fuer die Kaestchen) und bringt hier nur mit, was ihm eigen ist. Ein
   zweites Aussehen daneben liefe beim naechsten Feilen an den Formularen weg.

   DIE VIER FORMEN STEHEN NEBENEINANDER, solange Platz ist, und untereinander, wenn nicht.
   Vier kurze Woerter in vier Zeilen waeren eine Liste, wo eine Reihe genuegt — und auf einem
   Telefon ist die Reihe wiederum unlesbar. Der Zeilenumbruch entscheidet das selbst.

   .switch bringt margin:14px 0 mit; zwischen vier Kaestchen in einer Reihe waere das ein
   Loch. Deshalb hier die engere Fassung — sie gilt NUR fuer die Formen, nicht fuer den
   Einwilligungshaken darunter, der als eigene Aussage seinen Abstand behaelt. */
.ern__formen{display:flex;flex-wrap:wrap;gap:4px 22px;margin:2px 0 12px}
.ern__form{margin:4px 0}
.ern__notiz{display:flex;flex-direction:column;gap:4px;max-width:62ch}
.ern__label{font-size:13.5px;font-weight:600;color:var(--dunkelgrau)}
/* Zwei Zeilen hoch und von Hand aufziehbar: Es ist ein Hinweis und kein Aufsatz — aber wer
   drei Allergien hat, soll sie sehen koennen, waehrend er sie tippt. */
.ern__notiz textarea{width:100%;resize:vertical;line-height:1.5}
/* Der Einwilligungshaken. Oben buendig wie im Profil (.profil__haken): Der Satz laeuft
   ueber zwei Zeilen, und ein senkrecht zentriertes Kaestchen stuende dann in der Mitte des
   Textes statt am Anfang. */
.ern__haken{margin:14px 0 0;align-items:flex-start}
.ern__haken input{margin-top:3px}
.ern__hakenHinweis{max-width:62ch;margin:6px 0 0}
/* Der Satz „vorbelegt aus deinem Profil" steht ueber den Kaestchen und gehoert zur Legende
   — deshalb dichter an ihr als der uebliche Notenabstand von .reg__group .note. */
.ern__vorbelegt{margin:2px 0 10px;max-width:70ch}

/* ---------- Die Spalte „Foto" der Personenliste (Verwaltungsbereich) ----------

   Der Wunsch des Auftraggebers, woertlich: „Dann sehe ich sofort ob ein Foto hinterlegt ist
   oder nicht. Unter dem Foto soll die Pixelangabe stehe und die Dateigroesse."

   DREI ZUSTAENDE, DIE SICH BEIM UEBERFLIEGEN UNTERSCHEIDEN MUESSEN — daran haengt der ganze
   Nutzen der Spalte. Deshalb bekommt jeder eine eigene Randart und nicht bloss eine eigene
   Farbe: Ein Rand ist auch auf einem schlechten Bildschirm und fuer ein farbschwaches Auge
   zu sehen, eine Farbe allein nicht.
     durchgezogen + Bild   ein Foto liegt
     gestrichelt, leer     kein Foto — die Luecke, nach der gesucht wird
     durchgezogen + Strich  Widerspruch: es gibt etwas, aber es wird nicht gezeigt

   AUSDRUECKLICH KEINE INITIALEN wie im Bearbeiten-Formular (.profil__ph). Initialen in einem
   Kreis sehen beim Ueberfliegen aus wie ein Foto, und genau das Ueberfliegen ist der Zweck
   dieser Spalte. Das ist der eine Fall, in dem eine zweite Bauform des Platzhalters richtig
   ist — sie beantwortet eine andere Frage.

   44 PUNKTE, WIE DIE KACHEL DER TEILNEHMERLISTE (.tnl__bild): gross genug, um ein falsches
   Gesicht zu erkennen, klein genug, dass die Zeilenhoehe der Tabelle nicht kippt. */
.pe-foto{display:flex;flex-direction:column;align-items:flex-start;gap:3px}
.pe-foto__ring{
  flex:0 0 auto;width:44px;height:44px;border-radius:50%;box-sizing:border-box;
  display:flex;align-items:center;justify-content:center;overflow:hidden;
}
/* Waehrend gemessen wird. Ruhiges Grau und KEIN gestrichelter Rand: Der gestrichelte gehoert
   der Aussage „kein Foto", und die waere hier falsch — es liegt ja eines, es ist nur noch
   nicht da. Ein blinkender oder wandernder Zustand waere in einer Liste mit hundert Zeilen
   hundertfaches Flimmern. */
.pe-foto__ring--laedt{background:var(--surface-2);border:1px solid var(--border-soft)}
/* Die graue Flaeche gilt NUR fuer den Ring mit Bild — sie ist der Grund, auf dem ein noch
   nicht geladenes Foto steht. Dieselbe Ueberlegung wie bei img.tnl__bild weiter oben. */
.pe-foto__ring--bild{background:var(--surface-2)}
.pe-foto__ring--leer{border:2px dashed var(--border-strong)}
.pe-foto__ring--widerspruch{border:2px solid var(--border-strong);background:var(--surface-2);position:relative}
/* Der Strich durch den Ring. Er steht als ::after und nicht als Zeichen im Markup: Ein
   Vorleseprogramm laese ein Zeichen mit vor, und die Zelle sagt daneben schon „Widerspruch"
   — zweimal dasselbe waere schlechter als einmal. */
.pe-foto__ring--widerspruch::after{
  content:"";position:absolute;left:50%;top:-2px;bottom:-2px;width:2px;margin-left:-1px;
  background:var(--border-strong);transform:rotate(45deg);
}
.pe-foto__bild{width:100%;height:100%;object-fit:cover;display:block}
/* Die beiden Zahlenzeilen und das Zustandswort. Die Farbe kommt aus `table.list small`
   (Grauskala des Hauses); hier stehen nur Groesse und Zeilenhoehe, damit zwei Zeilen unter
   einem 44-Punkte-Ring die Zeilenhoehe der Tabelle nicht auftreiben.
   tabular-nums, damit die Zahlen untereinander stehen und nicht tanzen. */
.pe-foto__zahlen{font-size:12px;line-height:1.25;font-variant-numeric:tabular-nums}
.pe-foto__zustand{font-size:12px;line-height:1.25}

/* ---------- Der Reiter „Bausteine" (Verwaltungsbereich) ----------

   Die Nachschlagetafel des Hauses: Farben, Maße, Schriften, Bausteine, Technik.

   HIER STEHT SO WENIG WIE MÖGLICH, UND DAS IST DER PUNKT. Alles, was der Reiter ZEIGT,
   sind die Bausteine dieser Datei selbst (.btn, .badge, .callout, .klapp, .card,
   table.list …). Die Regeln hier bauen nur das Regal, in dem sie liegen: Raster, Kästen,
   Beschriftungen. Ein eigener Farb- oder Schriftton in diesem Block wäre ein zweites
   System neben dem, das die Tafel beschreibt — und ausgerechnet die Seite, die das
   System zeigt, folgte ihm dann nicht.

   Die Farbfläche eines Feldes bekommt ihren Wert als style-Merkmal aus dem JavaScript
   (js/admin-bausteine.js). Das ist die eine Stelle, an der es nicht anders geht: Der Wert
   ist ja genau das, was gezeigt werden soll, und er wird zur Laufzeit aus dem Stylesheet
   gelesen. */
.bst{margin-top:10px}
.bst__h2{font-size:clamp(22px,3vw,30px);margin:0 0 10px}
/* Die Abschnittsüberschriften stehen weit auseinander: Die Tafel ist lang, und ohne Luft
   davor liest sie sich als eine einzige Wand. */
.bst__titel{font-size:20px;margin:40px 0 10px}
.bst__gruppe{margin:20px 0 0}
/* Die Gruppenzeile in der Mono-Auszeichnung des Hauses — dieselbe Schrift, in der die
   Tabellenköpfe und .card__eyebrow stehen. */
.bst__gruppe-titel{
  margin:0 0 8px;
  font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--dunkelgrau);
}
.bst__raster{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}
/* Die Gruppenzeile IM Raster, über die ganze Breite. Damit laufen die Felder durch, statt
   je Gruppe ein eigenes halbleeres Regal zu bilden: Die meisten Gruppen haben zwei oder drei
   Mitglieder, nebeneinander passen sechs. */
.bst__gruppe-titel--reihe{grid-column:1/-1;margin:14px 0 0}
.bst__gruppe-titel--reihe:first-child{margin-top:0}
.bst__raster--breit{grid-template-columns:repeat(auto-fill,minmax(230px,1fr))}
.bst__feld{
  background:var(--weiss);border:1px solid var(--line-soft);
  border-radius:var(--radius-sm);overflow:hidden;
}
/* Die Fläche liegt auf dem Schachbrett des Hauses (.zuschnitt__grund), damit man einer
   halbdurchsichtigen Farbe ANSIEHT, dass sie halbdurchsichtig ist. Auf Weiß sähe --overlay
   aus wie ein hellerer Vollton, und die Tafel behauptete etwas Falsches. */
.bst__flaeche{height:52px;border-bottom:1px solid var(--line-soft)}
.bst__farbe{width:100%;height:100%}
.bst__text{padding:8px 10px}
/* Namen und Werte müssen sich markieren und kopieren lassen — echter Text, kein Bild.
   `user-select:text` steht ausdrücklich da: Der Kasten liegt in einer Tafel, und eine
   spätere Regel, die das Auswählen irgendwo abschaltet, nähme ihm sonst genau den Zweck.
   overflow-wrap, weil ein Schattenwert länger ist als jede Spalte. */
.bst__name{display:block;font-family:var(--mono);font-size:12px;color:var(--schwarz);overflow-wrap:anywhere;user-select:text}
.bst__wert{display:block;margin-top:2px;font-family:var(--mono);font-size:11.5px;color:var(--dunkelgrau);overflow-wrap:anywhere;user-select:text}
.bst__bruecke{display:block;margin-top:3px;font-size:11.5px;color:var(--dunkelgrau)}
.bst__stapel{margin:8px 0 0}
/* Jedes Maß zweimal: als Eckenrundung und als Balkenbreite. Welche Lesart gemeint ist,
   sagt keine Zahl — das Bild sagt es. */
.bst__mass{display:flex;flex-direction:column;gap:8px;margin-top:10px}
.bst__mass-radius{width:56px;height:56px;background:var(--blau-tint);border:1px solid var(--blau-line)}
.bst__mass-balken{height:10px;max-width:100%;background:var(--blau);border-radius:var(--pill)}
/* Schatten auf dem SEITENGRUND und nicht auf der Karte — das ist die Paarung, in der die
   Anwendung sie wirklich benutzt: eine Karte, die auf der Seite liegt.
   HIER STAND, das sei so gewählt, „weil die Tafel sonst nichts zeigte". Für die helle
   Darstellung stimmt das, für die dunkle nicht: Dort ist --grau (21,25,29) sogar dunkler als
   der Beispielkasten --weiss (30,36,42), und die dunklen Schattenwerte sind reines Schwarz.
   Ein schwarzer Schatten auf fast schwarzem Grund hebt sich an seiner stärksten Stelle um
   rund 1,10:1 ab — die fünf Beispiele sahen dort identisch aus, und die Tafel sagte es nicht.
   Umstellen hilft nicht: In der dunklen Palette gibt es keinen hellen Grund, auf dem ein
   schwarzer Schatten etwas hergäbe. Deshalb MISST js/admin-bausteine.js die Abhebung jetzt
   und schreibt sie neben jedes Beispiel. */
.bst__schattenfeld{background:var(--grau);padding:22px 18px;border-radius:var(--radius-sm)}
.bst__schatten{width:96px;height:58px;background:var(--weiss);border-radius:var(--radius-sm)}
/* Die Spalte ist so breit wie ihre MESSUNG und nicht wie das Kästchen: „Abhebung 1,15:1 ·
   kaum sichtbar" braucht gut das Doppelte der 96px, und ohne feste Breite standen die fünf
   Beispiele ungleich weit auseinander — je nachdem, wie lang ihr Messwert gerade ausfällt. */
.bst__schattenfeld .bst__spalte{width:200px}
/* Und sie stehen oben bündig: .bst__reihe zentriert sonst senkrecht, und die eine Spalte
   ohne den Zusatz „kaum sichtbar" hinge dann eine Zeile tiefer als ihre vier Nachbarn. */
.bst__schattenfeld .bst__reihe{align-items:flex-start}
/* Ein Beispielkasten. Er hat die Fläche einer Karte, aber ausdrücklich KEINEN Zeigefinger
   und keinen Hover: Hier wird nichts angeklickt, hier wird nachgesehen. */
.bst__probe{
  margin:16px 0 0;padding:14px 16px 16px;
  background:var(--weiss);border:1px solid var(--line-soft);border-radius:var(--radius);
}
.bst__probe-titel{
  margin:0 0 12px;
  font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--dunkelgrau);
}
.bst__reihe{display:flex;flex-wrap:wrap;gap:14px;align-items:center}
.bst__spalte{display:flex;flex-direction:column;gap:6px;align-items:flex-start;min-width:0}
/* Der Klassenname neben dem Beispiel — man will ihn abschreiben können. */
.bst__klasse{
  font-family:var(--mono);font-size:11.5px;color:var(--dunkelgrau);
  overflow-wrap:anywhere;user-select:text;
}
/* Eine einzelne Karte soll nicht über die ganze Breite laufen: .grid füllt sonst mit einer
   Karte eine Spalte von 1120px, und das Beispiel sähe aus wie ein Banner. */
.bst__kartenfeld{max-width:340px}
.bst__fliess{margin:0 0 10px;max-width:78ch}
.bst__fliess:last-child{margin-bottom:0}
.bst__fliess--gross{font-size:17px}
.bst__fliess-zahl{font-family:var(--mono);font-size:13px;color:var(--dunkelgrau)}
.bst__liste{margin:0;padding-left:20px;max-width:78ch}
.bst__liste li{margin-bottom:8px}
.bst__liste li:last-child{margin-bottom:0}
