/* IBM Plex Sans (touvia-Hausschrift), SIL Open Font License 1.1.
   Liegt im Image, wird NICHT von Google Fonts o.ae. nachgeladen - siehe
   static/vendor/fonts/LICENSE. */
@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/static/vendor/fonts/ibm-plex-sans-latin-400-normal.woff2") format("woff2")
}

@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("/static/vendor/fonts/ibm-plex-sans-latin-500-normal.woff2") format("woff2")
}

@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("/static/vendor/fonts/ibm-plex-sans-latin-600-normal.woff2") format("woff2")
}

:root {
    /* Sagt dem Browser, in welcher Richtung er das zeichnet, was NICHT uns
       gehoert: Rollbalken, Auswahlfelder, Kalender, das Textfeld-Karussell.
       Ohne diese Angabe bleiben sie hell, waehrend die Seite dunkel ist. */
    color-scheme: light;

    /* ---------------------------------------------------------------------
       Farbrollen nach Material 3. Material denkt nicht in einzelnen Farben,
       sondern in Rollen: "primary" fuer Aktionen, "primary-container" fuer
       getoente Flaechen, "surface-container" in Stufen statt Schatten,
       "outline" fuer Raender. Abgeleitet sind sie hier aus der CI-Farbe -
       #3c8dbc bleibt unveraendert die Marke.

       Die alten Namen (--bg, --card, --ink, --line, --accent, ...) stehen
       weiter unten als Verweise auf die Rollen. Sie werden von rund 3000
       Zeilen benutzt - Kartenvorschau, Stileditor, Quelltextschau - und
       bleiben deshalb gueltig, statt ueberall angefasst zu werden. Neuer
       Code nimmt die Rollen. */
    --md-brand: #3c8dbc;              /* CI. Nur grosse Flaechen/grosse Schrift */
    /* Nachgemessen und nachgezogen: mit dem alten #2f7aa8 erreichte eine
       Knopfbeschriftung auf dem Grund einer Lade (--md-surface-container) nur
       4,29:1 und im Hinweiskasten 4,14:1 - beides unter den 4,5:1, die WCAG
       AA fuer normalen Text verlangt. #31749b haelt Farbton (202°) und
       Saettigung der CI-Farbe exakt und liegt auf ALLEN drei Gruenden
       darueber: Weiss 5,12:1, surface-container 4,68:1, primary-weich
       4,51:1. Weisse Schrift auf der Fuellung: 5,12:1. */
    --md-primary: #31749b;
    --md-on-primary: #fff;
    --md-primary-container: #cfe7f6;
    --md-on-primary-container: #08334a;
    --md-primary-weich: #e8f2f8;      /* noch leiser als der Container */
    --hb-treffer: #ffe680;            /* Suchtreffer im Handbuch, Text bleibt --ink */

    --md-surface: #fff;
    --md-surface-dim: #f4f6f8;        /* der Seitengrund */
    --md-surface-container: #f2f5f7;  /* Laden, Dialogkoepfe */
    --md-surface-container-hoch: #ecf0f4;
    --md-on-surface: #1c2733;
    --md-on-surface-variant: #5d6b7a; /* Nebentexte, 5,1:1 */

    /* --md-outline traegt Bedienelemente und erfuellt mit 3,1:1 die
       Kontrastvorgabe fuer Bedienelemente (WCAG 1.4.11). --md-outline-variant
       sind reine Trennlinien - dekorativ, ohne Kontrastpflicht. */
    --md-outline: #74808c;
    --md-outline-variant: #dfe5eb;

    /* #1a7f5a kam auf seinem eigenen Container nur auf 4,39:1 - die Marke
       "importiert" traegt normalen Text, keinen grossen. #187552: 5,00:1. */
    --md-ok: #187552;
    --md-ok-container: #e6f4ee;
    --md-warn: #8a6100;
    --md-warn-container: #fdf3e0;
    --md-error: #a32c2c;
    --md-error-container: #fbeaea;
    /* Schrift auf der GEFUELLTEN Fehlerflaeche. In der hellen Darstellung
       ist das Weiss; in der dunklen wird --md-error selbst hell, und Weiss
       darauf waere unlesbar - deshalb eine eigene Rolle statt eines festen
       Werts an den drei Knoepfen. */
    --md-on-error: #fff;

    /* Raender der drei Meldungsfarben. Sie sind kraeftiger als der Container
       und leiser als die Schriftfarbe - dazwischen gibt es in Material keine
       Rolle, also hier eine eigene. */
    --md-ok-outline: #b9e0cd;
    --md-warn-outline: #f0e0bd;
    --md-error-outline: #f0c9c9;

    /* Umgekehrte Flaeche: dunkel in der hellen Darstellung, hell in der
       dunklen. Traegt die Kurzhinweise an den Knoepfen und den Hinweis an
       der Karte - beide sollen sich vom Untergrund abheben, nicht mit ihm
       gehen. */
    --md-invers: #1c2733;
    --md-on-invers: #fff;

    /* Der Abdunkler hinter Dialogen, Laden und der Kartenvorschau. */
    --md-abdunkler: rgba(20, 35, 50, .55);
    --md-abdunkler-leicht: rgba(20, 35, 50, .45);

    /* Schatten in drei Staerken. In der dunklen Darstellung reicht Schwarz
       mit derselben Deckung nicht mehr - dort steht der Schatten auf einem
       fast ebenso dunklen Grund und verschwindet. Deshalb als Rolle. */
    --md-schatten-weich: rgba(0, 0, 0, .09);
    --md-schatten-mittel: rgba(0, 0, 0, .22);
    --md-schatten-hart: rgba(0, 0, 0, .30);

    /* Der Grund unter der Karte: was man sieht, bevor die Tiles da sind, und
       woraus der Meldungsschleier (.mstate) seine Flaeche nimmt. Beides ist
       UNSER Rahmen und folgt deshalb der Oberflaeche.
       Massstab und Legende folgen ihr bewusst NICHT: die liegen auf der Karte,
       direkt neben MapLibres eigenen Bedienelementen, und die kommen hell aus
       der Bibliothek. Zwei dunkle Kaesten zwischen weissen Zoomknoepfen saehen
       nach Versehen aus. Was die Karte selbst zeigt, waehlt ohnehin der Kunde
       im Menue "Karte" - dort steht auch ein dunkler Hintergrund. */
    --md-karte-grund: #eceff2;
    /* Das "Glas" von Massstab und Legende und die Schrift darauf. Sie ziehen
       seit dem 11.09.2026 mit der Darstellung mit - zusammen mit MapLibres
       eigenen Bedienelementen und der Hintergrundkarte, die im Dunkeln auf
       VersaTiles Shadow wechselt. Vorher blieb die Kartenecke bewusst hell,
       weil die Zoomknoepfe aus der Bibliothek hell sind; jetzt werden auch
       die eingefaerbt, und damit ist der Grund weggefallen.
       Was NICHT mitgeht, sind die Kurzhinweise - siehe --md-invers-rand. */
    --md-karte-glas: rgba(255, 255, 255, .82);
    --md-auf-karte-glas: #1c2733;
    /* Die Kurzhinweise sind hell wie dunkel dieselbe dunkle Flaeche mit
       weisser Schrift. Auf dunklem Grund braucht sie eine Kante, damit sie
       nicht mit ihm verschwimmt - hier ist keine noetig. */
    --md-invers-rand: transparent;
    /* Etwas kraeftiger als --md-outline-variant: die Karten im Inhaltsbereich
       stehen auf getoentem Grund und verschwaenden sonst darin. */
    --md-outline-karte: #d3dce4;

    /* Formenskala. Material stuft die Ecken, statt einen Radius ueberall zu
       wiederholen: je kleiner und dichter ein Element, desto kleiner der
       Radius. Knoepfe und Chips sind Kapseln, keine Rechtecke. */
    /* In rem, damit die Ecken mit der Schrift wachsen: auf einem 4K-Schirm
       ist alles andere groesser, und ein Radius, der als einziger stehen
       bleibt, laesst die Karten geknickt aussehen. */
    --md-form-xs: 0.25rem;
    --md-form-sm: 0.5rem;
    --md-form-md: 0.75rem;
    --md-form-lg: 1rem;
    --md-form-xl: 1.75rem;
    --md-form-voll: 999px;

    /* Erhebung. Sparsam: Tiefe entsteht hier ueber den Ton der Flaeche
       (surface-container), Schatten bleiben Menues und Dialogen vorbehalten. */
    --md-erhebung-1: 0 1px 2px rgba(16, 29, 41, .10), 0 1px 3px 1px rgba(16, 29, 41, .06);
    --md-erhebung-2: 0 1px 2px rgba(16, 29, 41, .10), 0 2px 6px 2px rgba(16, 29, 41, .08);
    --md-erhebung-3: 0 1px 3px rgba(16, 29, 41, .10), 0 4px 8px 3px rgba(16, 29, 41, .09);

    /* Materials Standardkurve: schnell an, weich aus. */
    --md-kurve: cubic-bezier(.2, 0, 0, 1);
    --md-kurz: .15s;
    --md-dauer: .25s;

    /* --------------------------------------------------------------- Verweise
       --brand ist die touvia-CI-Farbe und bleibt exakt so. Weisse Schrift
       darauf erreicht 3,67:1 - das genuegt fuer grosse Schrift (ab 3:1), nicht
       aber fuer normalen Fliesstext (4,5:1 nach WCAG AA / BITV). Deshalb fuer
       Links, Knoepfe und kleine Bedienelemente --accent: derselbe Farbton,
       etwas dunkler. Kunden aus der oeffentlichen Hand haben
       Barrierefreiheitsvorgaben - das sollte von vornherein passen. */
    --bg: var(--md-surface-dim);
    --card: var(--md-surface);
    --ink: var(--md-on-surface);
    --muted: var(--md-on-surface-variant);
    --line: var(--md-outline-variant);
    --brand: var(--md-brand);
    --accent: var(--md-primary);   /* siehe oben: 5,12:1 auf Weiss */
    --accent-soft: var(--md-primary-weich);
    /* Seitenmenue: aus dem geoCMS-Bildschirmfoto (12.09.2026, doppelt
       aufgeloest) pixelweise ausgemessen, damit das Menue dort und hier
       gleich aussieht. Der aktive Punkt traegt die CI-Farbe --md-brand -
       geoCMS benutzt denselben Ton. Die Farben sind in beiden Darstellungen
       dunkel; im Dunkeln nur eine Stufe tiefer als die Flaeche daneben,
       damit die Spalte noch als Spalte zu sehen ist. */
    --menue-flaeche: #222d32;
    --menue-text: #b8c7ce;
    --menue-aktiv: var(--md-brand);
    --menue-zeigen: rgba(255, 255, 255, .08);
    --menue-unter: #2c3b41;
    --menue-unter-text: #8aa4af;
    --ok: var(--md-ok);
    --ok-bg: var(--md-ok-container);
    --warn: var(--md-warn);
    --warn-bg: var(--md-warn-container);
    --err: var(--md-error);
    --err-bg: var(--md-error-container);
    --font: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

/* ======================================================================
   DUNKLE DARSTELLUNG

   Gilt NUR, wenn jemand sie im Seitenkopf einschaltet - die Seite folgt
   bewusst nicht der Systemeinstellung. Standard ist hell.

   Bis zum 11.09.2026 stand hier derselbe Satz Werte zweimal: einmal unter
   prefers-color-scheme fuer "das Geraet ist dunkel eingestellt", einmal hier.
   Mit der Entscheidung fuer den reinen Handschalter faellt der Zwilling weg,
   und mit ihm die Pflicht, beide gleich zu halten.

   Alle Werte sind wie die hellen nachgemessen (WCAG 2.1): Fliesstext und
   Nebentexte ueber 4,5:1 auf JEDER der vier Flaechenstufen, Rahmen ueber 3:1.
   ====================================================================== */

:root[data-thema="dunkel"] {
    color-scheme: dark;
    /* Flaechen. Material stuft sie auch im Dunkeln ueber den Ton, nicht ueber
       Schatten - je naeher am Betrachter, desto heller. Alle leicht ins
       Blaeuliche gezogen (Farbton 205°), damit die Oberflaeche zur CI-Farbe
       gehoert und nicht neben ihr steht. */
    --md-surface-dim: #0f151a;
    --md-surface: #161d24;
    --md-surface-container: #1b232b;
    --md-surface-container-hoch: #212b34;
    --md-on-surface: #e3e9ee;         /* 15,0:1 auf dem Seitengrund */
    --md-on-surface-variant: #a7b4c0; /* Nebentexte, 8,7:1 */

    --md-outline: #8b98a5;            /* Bedienelemente, 6,2:1 (noetig: 3:1) */
    --md-outline-variant: #333e48;
    --md-outline-karte: #2c353e;

    /* Die Marke wird dunkler statt heller: der Seitenkopf ist eine grosse
       Flaeche, und die CI-Farbe #3c8dbc waere darauf das hellste Stueck der
       ganzen Seite. Farbton und Saettigung bleiben, nur die Helligkeit geht
       herunter - Weiss darauf 7,6:1 statt 3,7:1 wie im Hellen. */
    --md-brand: #235974;

    /* Primaer dreht sich um: im Hellen ein dunkles Blau auf Weiss, im Dunkeln
       ein helles auf dunklem Grund. Deshalb kippt auch die Schrift DARAUF von
       Weiss auf Dunkel (--md-on-primary) - Weiss auf #7cc0e6 waere 2,0:1. */
    --md-primary: #7cc0e6;            /* 8,5:1 auf der Kartenflaeche */
    --md-on-primary: #04293c;         /* 7,6:1 auf der Fuellung */
    --md-primary-container: #17475f;
    --md-on-primary-container: #cbe6f8;
    --md-primary-weich: #16222c;
    --hb-treffer: #5a4a12;

    /* Meldungsfarben: heller Ton auf dunklem Container, spiegelbildlich zum
       Hellen. Auch hier kippt die Schrift auf der gefuellten Flaeche. */
    --md-ok: #6fd3a5;
    --md-ok-container: #12332a;
    --md-ok-outline: #1e5643;
    --md-warn: #e5b45f;
    --md-warn-container: #33290f;
    --md-warn-outline: #574311;
    --md-error: #f08a8a;
    --md-error-container: #3a1e1e;
    --md-error-outline: #63302f;
    --md-on-error: #3a1e1e;           /* 6,3:1 auf der Fuellung */

    /* --md-invers und --md-on-invers stehen bewusst NICHT hier: Ein Hinweis,
       der als einziges Element beim Umschalten die Richtung tauscht, waere
       Unruhe ohne Gewinn. Er bleibt dunkel mit weisser Schrift - nur die
       Kante oben kommt dazu. */

    /* Abdunkler und Schatten muessen kraeftiger werden: Schwarz mit 20 %
       Deckung ueber einer fast schwarzen Flaeche ist nichts. */
    --md-abdunkler: rgba(0, 0, 0, .66);
    --md-abdunkler-leicht: rgba(0, 0, 0, .55);
    --md-schatten-weich: rgba(0, 0, 0, .5);
    --md-schatten-mittel: rgba(0, 0, 0, .6);
    --md-schatten-hart: rgba(0, 0, 0, .72);
    --md-erhebung-1: 0 1px 2px rgba(0, 0, 0, .5), 0 1px 3px 1px rgba(0, 0, 0, .34);
    --md-erhebung-2: 0 1px 2px rgba(0, 0, 0, .5), 0 2px 6px 2px rgba(0, 0, 0, .38);
    --md-erhebung-3: 0 1px 3px rgba(0, 0, 0, .5), 0 4px 8px 3px rgba(0, 0, 0, .42);

    --md-karte-grund: #141a20;
    --md-karte-glas: rgba(18, 24, 30, .84);
    --md-auf-karte-glas: #e3e9ee;
    --md-invers-rand: color-mix(in srgb, #fff 18%, transparent);

    /* Seitenmenue: geoCMS kennt keine dunkle Fassung. Die Spalte ist ohnehin
       dunkel; hier nur eine Stufe tiefer als --md-surface (#161d24), damit
       sie neben dem Inhalt noch als Spalte zu erkennen ist. Schrift und
       aktiver Punkt bleiben die der hellen Darstellung (--md-brand kippt
       von selbst auf sein dunkles Blau). */
    --menue-flaeche: #0f161b;
    --menue-unter: #182129;
}

/* Material schreibt Roboto vor - hier bleibt IBM Plex Sans. Sie ist
   vianovis-Hausschrift, liegt bereits im Image und wird von nirgends
   nachgeladen. Uebernommen wird stattdessen Materials TYPOSKALA, auf Plex
   abgebildet: label-large 14/500 fuer Knoepfe, title-medium 16/600 fuer
   Ueberschriften, body-medium 14 fuer Erklaertexte. */

/* Bewegung ist Beiwerk. Wer sie abbestellt hat, bekommt sie nicht - und zwar
   ueberall, nicht nur dort, wo daran gedacht wurde. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important
    }
}

* {
    box-sizing: border-box
}

/* Schutz gegen eine Fehlerklasse, die hier schon einmal zugeschlagen hat:
   Ein eigenes "display:..." auf einem Element ueberschreibt die
   Voreinstellung des Browsers fuer [hidden] und fuer <dialog> ohne open -
   das Element bleibt dann sichtbar, obwohl der Code es versteckt hat. */
[hidden] {
    display: none !important
}

/* ================================================== Groesse der Schrift
   Zwei Dinge auf einmal, und sie gehoeren zusammen:

   1. KEINE feste Pixelgroesse mehr. "16px" ueberging die Schriftgroesse, die
      der Benutzer in seinem Browser eingestellt hat - wer schlecht sieht und
      dort 20px stehen hat, bekam trotzdem 16. Mit "1rem" gilt seine
      Einstellung wieder. Fuer Kunden aus der oeffentlichen Hand ist das
      keine Kuer, sondern BITV.

   2. Ab 1600px waechst die Grundgroesse mit der Bildschirmbreite. Auf einem
      4K-Schirm ohne Betriebssystem-Skalierung ist ein CSS-Pixel ein echter
      Pixel - die ganze Seite steht dann als Briefmarke in der Mitte.
      Gemessen: bei 3840px lagen 72% der Flaeche brach.

   Die Kurve beginnt bewusst erst bei 1600px und trifft dort exakt 1rem:
   unterhalb davon aendert sich damit GAR NICHTS, und alle Masse, die fuer
   Laptopbreiten gemessen sind (die Knopfreihe der Layerzeile, die Breite der
   Menuespalte), bleiben gueltig. Bei 3840px steht sie bei 22px.
     1600px -> 1rem + 0      = 16,00px
     1920px -> 1rem + 1,14px = 17,14px
     2560px -> 1rem + 3,43px = 19,43px
     3840px -> 1rem + 8,00px = 24,00px

   Der rem-Anteil ist der Grund, warum das den Zoom nicht kaputt macht:
   waere es reines vw, waere die Schrift an die Fensterbreite genagelt und
   der Browser-Zoom liefe ins Leere (WCAG 1.4.4 verlangt 200%). */
html {
    font-size: clamp(1rem, 1rem + (100vw - 1600px) * 0.00357, 1.5rem)
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font: 1rem/1.55 var(--font)
}

button,
input,
select {
    font-family: var(--font)
}

/* Kaestchen, Auswahlknoepfe und Regler zeichnet der Browser selbst - mit
   accent-color tut er es in unserer Farbe. Das ist der ganze Aufwand fuer
   Bedienelemente, die sonst nur mit viel eigenem Markup zu treffen waeren,
   und sie bleiben dabei die des Systems: vertraut und barrierefrei. */
input[type="checkbox"],
input[type="radio"],
input[type="range"],
progress {
    accent-color: var(--md-primary)
}

a {
    color: var(--accent)
}

/* Die Inhaltsspalte nimmt die ganze Breite neben dem Menue ein, wie der
   Inhaltsbereich im geoCMS (AdminLTE: content-wrapper). Bis zum 12.09.2026
   stand hier ein Deckel (clamp 67,5rem ... 90rem) aus der Zeit der
   zentrierten Seite ohne Menue; mit der dunklen Spalte links wirkte eine
   mittig gedeckelte Spalte daneben wie ein Loch (Entscheidung vom
   12.09.2026). Fliesstext behaelt seine Lesebreite (siehe die
   1600px-Regel darunter). */
.wrap {
    margin: 0 auto;
    padding: 0 1.25rem 4rem
}

/* Was Fliesstext ist, bekommt oberhalb 1600px eine Lesebreite. Eine Tabelle
   darf die ganze Spalte nehmen, ein ganzer Satz nicht: bei 1980px und 24px
   Schrift stuenden sonst 150 Zeichen in einer Zeile, und das Auge findet den
   Zeilenanfang nicht mehr wieder.
   Bewusst erst ab 1600px, damit sich auf Laptopbreiten nichts aendert. */
@media (min-width: 1600px) {
    .empty,
    .dok-summe {
        max-width: 105ch
    }
}

header {
    background: var(--brand);
    color: #fff;
    padding: 1rem 0 1rem
}

/* Der Kopf der Konsole laeuft ueber die ganze Breite und beginnt am linken
   Rand, ueber der Menuespalte - wie das Logo im geoCMS. Die Gastseiten
   (Anmeldung) behalten die zentrierte Spalte (.wrap). */
.kopf-innen {
    padding: 0 1.25rem
}

/* Unter dem Kopf zwei Spalten: links das Seitenmenue, rechts der Inhalt.
   Die Menuespalte ist so breit wie im geoCMS (520 px im doppelt
   aufgeloesten Foto). minmax(0, 1fr) statt 1fr, sonst darf die
   Inhaltsspalte nicht schmaler werden als ihre breiteste Tabelle und die
   Seite rollt seitlich. Die Mindesthoehe laesst die dunkle Spalte auch bei
   kurzem Inhalt bis unten reichen - 6rem sind ungefaehr der Kopf. */
.rahmen {
    display: grid;
    grid-template-columns: 18.75rem minmax(0, 1fr);
    min-height: calc(100vh - 6rem)
}

/* width: 100% ist noetig, kein Zierrat: Als Grid-Kind mit margin: auto
   wuerde .wrap sonst nur so breit wie sein Inhalt - die Uebersicht mit drei
   Karten stand deutlich schmaler da als die Layerliste daneben (gesehen am
   12.09.2026). Mit voller Breite greift wieder der max-width-Deckel, und
   die Spalte ist auf jedem Blatt gleich breit. */
.rahmen > .wrap {
    width: 100%;
    /* 1,1875rem = 19 CSS-px: der Abstand vom Kopf zur Brotkrumenzeile im
       geoCMS-Foto (38 px doppelt aufgeloest). */
    padding-top: 1.1875rem
}

/* ---------------------------------------------------------- Brotkrumen
   "Haus > Layer" ueber dem Inhalt, wie im geoCMS: verbindet das Menue links
   mit dem Inhalt rechts. Gemessen: Zeile 16 CSS-px hoch, Haus 16 px,
   alles in #444 - hier die Textfarbe der Seite, damit es im Dunkeln
   mitgeht. Der Pfeil ist derselbe wie in den Verwaltungskacheln. */
.pfad {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.375rem;
    margin: 0 0 1rem;
    font-size: 1rem;
    color: var(--ink)
}

.pfad-start {
    display: flex;
    padding: 0.25rem;
    margin: -0.25rem;
    border-radius: 0.25rem;
    color: inherit
}

.pfad-start .ic {
    width: 1.125rem;
    height: 1.125rem
}

.pfad-start:hover {
    color: var(--accent)
}

.pfad-start:focus-visible {
    outline: 3px solid var(--md-primary);
    outline-offset: 1px
}

.pfad-rest {
    display: contents
}

.pfad-trenner {
    display: flex;
    color: var(--muted)
}

.pfad-trenner .ic {
    width: 0.875rem;
    height: 0.875rem
}

.pfad-eltern {
    color: var(--muted)
}

.pfad-akt {
    font-weight: 500
}

/* Gastseiten haben keinen Rahmen: dort haelt der Kopf selbst den Abstand. */
header + .wrap {
    margin-top: 1.75rem
}

header .wrap {
    padding-bottom: 0
}

/* Der Schalter haengt am FENSTERRAND und nicht an der Inhaltsspalte: die ist
   auf 1080px gedeckelt und mittig, auf einem breiten Schirm saesse der Knopf
   sonst mitten im Kopf statt an dessen Ecke. Deshalb absolut im <header>.
   Der Innenabstand rechts an der Zeile haelt ihm den Platz frei, damit ein
   langer Organisationsname nicht darunterlaeuft. */
header {
    position: relative
}

/* Logo des Kunden links vor dem Text (Verwaltung, Erscheinungsbild).
   Direkt auf der Marke, ohne Platte und ohne Innenabstand (Wunsch vom
   11.09.2026): Der Kunde liefert das Logo so, wie es auf Blau stehen soll -
   meist als PNG oder SVG mit transparentem Grund. Nur der Abstand zum Text
   rechts bleibt (gap der Zeile). */
.kopf-zeile {
    display: flex;
    align-items: center;
    gap: 1rem
}

.kopf-logo {
    flex: 0 0 auto;
    height: 3.25rem;
    max-width: 14rem;
    object-fit: contain
}

.kopf-text {
    min-width: 0
}

/* Auf dem Telefon UEBER den Text statt daneben: nebeneinander blieben dem
   Titel bei 390 px noch 120 px, und "Stadt Musterstadt" brach mitten im
   Wort (overflow-wrap: anywhere an .brand). Kleiner ausserdem - der Kopf
   soll nicht die halbe Anzeige einnehmen. */
@media (max-width: 620px) {
    .kopf-zeile {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.5rem
    }

    .kopf-logo {
        height: 2.5rem;
        max-width: 10rem
    }
}

.kopf-zeile {
    /* 4,5rem = der Knopf (2,75) plus sein Abstand zum Rand (1) plus Luft.
       Gemessen bei 390px: mit 3,25rem endete der Textkasten acht Pixel HINTER
       dem Knopfanfang - ein langer Organisationsname waere daruntergelaufen.
       Der Knopf haengt am Fensterrand und nicht an der Inhaltsspalte, deshalb
       kann die Zeile ihren Platz nicht selbst ausrechnen. */
    padding-right: 4.5rem
}

.thema {
    position: absolute;
    right: 1rem;
    top: 50%;
    transform: translateY(-50%)
}

/* Diese drei Knoepfe stehen als einzige IMMER auf der Marke - in beiden
   Darstellungen, denn der Kopf ist hier wie dort eine gefuellte Flaeche.
   Deshalb sind sie hier die einzige Stelle, an der Weiss richtig bleibt und
   keine Rolle daraus wird. */
.thema .thema-seg {
    width: 2.75rem;
    min-width: 2.75rem;
    height: 2.75rem;
    min-height: 2.75rem;
    background: transparent;
    border-color: transparent;
    color: color-mix(in srgb, #fff 88%, transparent)
}

.thema .thema-seg .ic {
    width: 1.5rem;
    height: 1.5rem
}

.thema .thema-seg:hover:not(:disabled) {
    background: color-mix(in srgb, #fff 16%, transparent);
    border-color: transparent;
    color: #fff
}

/* Der uebliche Fokusrahmen ist --accent und verschwindet auf der Marke fast.
   Hier deshalb Weiss, mit Abstand, damit er auf der gefuellten Stellung nicht
   mit deren Flaeche verschwimmt. */
.thema .thema-seg:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px
}

/* Der Menueknopf links im Kopf: dieselbe Bauart wie der Themaschalter, aus
   demselben Grund weiss. Selektor mit .kopf-zeile davor, weil "button.ghost"
   sonst die Fuellung gewinnt (siehe .kopf-akt .benutzer-knopf). Unter 992 px
   ist er verborgen: dort gibt es bis Phase 3 nichts zu klappen. */
.kopf-zeile .menue-knopf {
    flex: 0 0 auto;
    width: 2.75rem;
    min-width: 2.75rem;
    height: 2.75rem;
    min-height: 2.75rem;
    background: transparent;
    border-color: transparent;
    color: color-mix(in srgb, #fff 88%, transparent)
}

.kopf-zeile .menue-knopf .ic {
    width: 1.5rem;
    height: 1.5rem
}

.kopf-zeile .menue-knopf:hover:not(:disabled) {
    background: color-mix(in srgb, #fff 16%, transparent);
    border-color: transparent;
    color: #fff
}

.kopf-zeile .menue-knopf:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px
}

/* Unter 992 px haengt der Knopf am linken Rand des Kopfes, wie die beiden
   Knoepfe rechts (.kopf-akt): Die Kopfzeile stapelt auf dem Telefon Logo
   ueber Text, und im Fluss stuende der Knopf dann als eigene Zeile obenauf.
   Die Kopfzeile der Konsole (.mit-menue) haelt ihm links den Platz frei;
   die Gastseiten haben keinen Knopf und brauchen den Platz nicht. */
@media (max-width: 992px) {
    .kopf-zeile .menue-knopf {
        position: absolute;
        left: 0.75rem;
        top: 50%;
        transform: translateY(-50%)
    }

    .kopf-zeile.mit-menue {
        padding-left: 4.25rem
    }
}

/* Auf dem Telefon stapelt die Kopfzeile Logo ueber Text (siehe oben, 620 px).
   Der Knopf rueckt dann in die Logozeile, und NUR das Logo weicht ihm aus -
   nicht die ganze Zeile: mit 4,25rem links und 7,5rem rechts blieben dem
   Titel bei 390 px noch 200 px, und "vianovis GmbH" brach um. Ohne Logo
   weicht stattdessen der Text aus (Knopf direkt vor .kopf-text). */
@media (max-width: 620px) {
    .kopf-zeile .menue-knopf {
        top: 0.875rem;
        transform: none
    }

    /* 1,25rem und nicht 0: das ist der Seitenabstand von .kopf-innen am
       selben Element - mit 0 lief der Titel ueber den linken Rand hinaus. */
    .kopf-zeile.mit-menue {
        padding-left: 1.25rem
    }

    .kopf-zeile.mit-menue .menue-knopf + .kopf-logo,
    .kopf-zeile.mit-menue .menue-knopf + .kopf-text {
        margin-left: 2.75rem
    }
}

/* Hier steht der Name des Kunden (KONTAKT_ORGANISATION), und der ist nicht
   in unserer Hand: "Gemeinsames Kommunalunternehmen
   Donau-Iller-Geodatenmanagement" ist fuer eine Kommune nichts Ungewoehnliches.
   Bei 2rem passt so ein Wortungetuem bei 320 px nicht mehr in die Zeile und
   schob den Kopf 65 px ueber den Rand. Gleiches Mittel wie bei .zustand
   weiter unten - lieber ein Trennstrich mitten im Wort als eine Seite, die
   sich seitlich schieben laesst. */
.brand {
    font-size: 2rem;
    font-weight: 600;
    letter-spacing: .2px;
    line-height: 1.1;
    overflow-wrap: anywhere
}

.brand span {
    opacity: .75;
    font-weight: 400
}

/* 19px/600 zaehlt als 'grosse Schrift' - damit reicht der Kontrast der
   CI-Farbe (3,67:1) auch nach WCAG AA aus. Volle Deckkraft, kein opacity. */
.sub {
    font-size: 1.1875rem;
    font-weight: 600;
    color: #fff
}

h2 {
    font-size: 1.125rem;
    margin: 2rem 0 0.75rem;
    font-weight: 600
}

/* Materials "outlined card": Rand statt Schatten. Schatten bleiben hier
   Dialogen und Menues vorbehalten - eine Seite, auf der alles schwebt, hat
   keine Rangfolge mehr. */
.card {
    background: var(--md-surface);
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-md);
    padding: 1.125rem 1.25rem;
    margin-bottom: 0.875rem
}

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 0.875rem
}

.kpi {
    display: block;
    font-size: 1.875rem;
    font-weight: 600;
    line-height: 1.1;
    font-variant-numeric: tabular-nums
}

.kpi-l {
    display: block;
    color: var(--muted);
    font-size: 0.875rem;
    margin-top: 0.1875rem
}

table {
    width: 100%;
    border-collapse: collapse
}

th,
td {
    text-align: left;
    padding: 0.5625rem 0.625rem;
    border-bottom: 1px solid var(--line);
    vertical-align: top;
    font-size: 0.9375rem;
    word-break: break-word;
}

th {
    color: var(--muted);
    font-weight: 600;
    font-size: 0.8125rem;
    text-transform: uppercase;
    letter-spacing: .4px
}

tr:last-child td {
    border-bottom: none
}

/* Tabellenzeilen abwechselnd weiss und hellgrau (Bill, 13.09.2026:
   "grundsaetzlich", nachdem das Handbuch Tabellen bekam). Nicht in den
   Layer- und Benutzerlisten (table.ebenen, table.liste): Das sind Listen
   mit Zeilenmenues und aufklappbaren Laden, keine Tabellen. Gruppenzeilen
   der Zustandstabelle (tr.gr) zaehlen nicht mit, sonst kippt das Muster
   nach jeder Gruppe. */
table:not(.ebenen):not(.liste) > tbody > tr:nth-child(even of :not(.gr)) > td,
table:not(.ebenen):not(.liste) > tbody > tr:nth-child(even of :not(.gr)) > th {
    background: var(--md-surface-container)
}

code {
    background: var(--accent-soft);
    padding: 0.125rem 0.375rem;
    border-radius: 0.3125rem;
    font: 0.8125rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
    word-break: break-all
}

.url-row {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap
}

/* ================================================== Symbole und Knopffamilie
   Die Symbole kommen als Inline-SVG aus app.py (ICON) bzw. app.js (ic()) und
   tragen KEINE eigene Farbe - Strichstaerke, Enden und Farbe setzt allein
   diese Datei. So haben Verwaltungskacheln, Knoepfe, Menuepunkte und Marken
   dieselbe Handschrift, auch wenn die Pfade an drei Stellen eingesetzt
   werden. */
/* Gefuellt statt umrissen. Presentation-Attribute im SVG wuerden hier nichts
   ausrichten - die Regel .ic gewinnt als CSS immer -, deshalb eine Klasse. */
.ic.voll {
    fill: currentColor;
    stroke: none
}

.ic {
    flex: 0 0 auto;
    width: 1.25rem;
    height: 1.25rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round
}

/* In Marken und Chips: kleiner, dafuer etwas kraeftiger - ein 1,8er Strich
   wirkt auf 15 px duenn und franst aus. */
.ic.klein {
    width: 0.9375rem;
    height: 0.9375rem;
    stroke-width: 2
}

/* Das getoente Quadrat vor einer Listenzeile, Kachel oder Karte. Materials
   "leading icon": es macht die Zeilen scanbar, ohne dass ein Wort mehr
   dasteht. */
.ic-kreis {
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--md-form-md);
    background: var(--md-primary-container);
    color: var(--md-on-primary-container);
    display: inline-flex;
    align-items: center;
    justify-content: center
}

.ic-kreis .ic {
    width: 1.375rem;
    height: 1.375rem
}

/* Die vier Knopfarten aus Material 3, auf die bestehenden Klassennamen
   gelegt (sie stecken in app.py UND app.js an rund achtzig Stellen):
     .primary  -> filled    - die eine Haupthandlung eines Abschnitts
     .copy     -> outlined  - alles Gleichrangige
     .ghost    -> text      - Nebensaechliches, Ruecknahmen
     .klapper.an -> tonal   - der Knopf, dessen Lade gerade offen steht
   Dazu .gefahr als Zusatz, der jede Art in die Fehlerfarbe dreht.

   Zustandsflaechen setzt color-mix() direkt auf den Hintergrund und NICHT
   als ::before-Ueberlagerung: beide Pseudoelemente sind hier schon vergeben,
   ::after traegt den Kurzhinweis und ::before dessen Pfeilspitze.

   Hoehe 36 px statt Materials 40: die Layerzeile traegt fuenf Knoepfe
   nebeneinander. 36 ist die "small"-Stufe der M3-Groessenskala und haelt die
   Zielflaeche mit dem Zeilenabstand ueber den 44 px, die auf dem Telefon
   noetig sind. */
button.copy,
a.copy,
button.primary,
button.ghost {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 2.25rem;
    padding: 0 1rem;
    border: 1px solid transparent;
    border-radius: var(--md-form-voll);
    background: transparent;
    font: 500 0.875rem/1.2 var(--font);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--md-kurz) var(--md-kurve),
                border-color var(--md-kurz) var(--md-kurve),
                color var(--md-kurz) var(--md-kurve),
                box-shadow var(--md-kurz) var(--md-kurve)
}

/* Umrissen: der Normalfall. Der Rand ist --md-outline und nicht die
   hellere Trennlinienfarbe - ein Bedienelement muss 3:1 erreichen. */
button.copy,
a.copy {
    border-color: var(--md-outline);
    color: var(--md-primary)
}

button.copy:hover,
a.copy:hover {
    background: color-mix(in srgb, var(--md-primary) 8%, transparent)
}

button.copy:active,
a.copy:active {
    background: color-mix(in srgb, var(--md-primary) 12%, transparent)
}

/* Gefuellt: hoechstens eine je Abschnitt. */
button.primary {
    background: var(--md-primary);
    border-color: var(--md-primary);
    color: var(--md-on-primary)
}

button.primary:hover {
    background: color-mix(in srgb, #fff 10%, var(--md-primary));
    box-shadow: var(--md-erhebung-1)
}

button.primary:active {
    background: color-mix(in srgb, #000 8%, var(--md-primary));
    box-shadow: none
}

/* Text: ohne Rand und ohne Flaeche, bis man darauf zeigt. */
button.ghost {
    color: var(--md-on-surface-variant);
    padding: 0 0.75rem
}

button.ghost:hover {
    background: color-mix(in srgb, var(--md-on-surface) 8%, transparent);
    color: var(--md-on-surface)
}

button.ghost:active {
    background: color-mix(in srgb, var(--md-on-surface) 12%, transparent)
}

/* Gefahr dreht die Farbrolle, nicht die Knopfart. Vorher faerbte
   "button.ghost:hover" JEDEN stillen Knopf rot - auch "Sachspalten wählen"
   und "Tiles erneuern" sahen beim Zeigen aus, als loeschten sie etwas. */
button.gefahr,
button.copy.gefahr,
a.copy.gefahr {
    color: var(--md-error);
    border-color: color-mix(in srgb, var(--md-error) 45%, transparent)
}

button.gefahr:hover,
button.copy.gefahr:hover {
    background: color-mix(in srgb, var(--md-error) 9%, transparent);
    border-color: var(--md-error);
    color: var(--md-error)
}

button.gefahr:active,
button.copy.gefahr:active {
    background: color-mix(in srgb, var(--md-error) 14%, transparent)
}

button.primary.gefahr {
    background: var(--md-error);
    border-color: var(--md-error);
    color: var(--md-on-error)
}

/* Reiner Symbolknopf: quadratische Zielflaeche, Beschriftung nur fuer
   Vorleseprogramme. Die Auskunft steckt weiter im aria-label - das setzt
   btn() ohnehin immer. */
button.ikon-nur,
a.ikon-nur {
    padding: 0;
    width: 2.25rem;
    min-width: 2.25rem;
    gap: 0;
    border-radius: var(--md-form-voll)
}

button.ikon-nur .kb,
a.ikon-nur .kb {
    position: absolute;
    width: 0.0625rem;
    height: 0.0625rem;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap
}

button:disabled,
button.primary:disabled,
button.copy:disabled {
    opacity: .5;
    cursor: default;
    box-shadow: none
}

/* Ein einheitlicher Fokusrahmen fuer die ganze Seite: 3 px in der
   Aktionsfarbe, 2 px Abstand - Materials Fokusindikator. Vorher waren es je
   nach Stelle 2 px, mal innen, mal aussen. */
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
    outline: 3px solid var(--md-primary);
    outline-offset: 2px
}

/* --- Layer-Zeile mit ausklappbarer Lade -------------------------
   Vorher standen fuenf gleichwertige Knoepfe nebeneinander, die auf zwei
   Zeilen umbrachen; drei davon kopierten irgendetwas, ohne dass der
   Unterschied erkennbar war. Sichtbar bleiben jetzt die beiden taeglichen
   Handgriffe, alles Uebrige liegt in der Lade - dafuer mit Erklaerung. */
/* Feste Aufteilung: ohne sie verteilt der Browser die Breite nach Inhalt -
   die lange Kachel-Adresse in der aufgeklappten Lade zog die Spalten
   auseinander, und die Knoepfe landeten am aeussersten Rand, weit weg vom
   Namen, zu dem sie gehoeren. */
table.ebenen,
table.liste {
    table-layout: fixed
}

.lz > td {
    vertical-align: middle
}

/* Die Zeile antwortet als Ganzes auf den Mauszeiger. Das ist nicht Zierrat:
   die Aktionen stehen rechtsbuendig, der Name links, und dazwischen liegt bei
   kurzen Namen viel Leere - der alte Einwand, dass die Knoepfe "weit weg vom
   Namen stehen, zu dem sie gehoeren". Die getoente Zeile ist die Klammer, die
   beides wieder zusammenbindet. Nur die sichtbare Zeile, nicht die Lade
   darunter: die hat ihren eigenen Ton. */
.lz:hover > td {
    background: color-mix(in srgb, var(--md-primary) 4%, transparent)
}

/* Die Nebenzeile traegt jetzt Layername UND Umfang - .ts verbietet sonst
   generell den Umbruch, was auf schmalen Anzeigen ueberliefe. */
.lz .ts {
    white-space: normal
}

/* Der Name mit seinem Symbol davor - Materials Listenzeile: fuehrendes
   Zeichen, Ueberschrift, Nebenzeile. Das Symbol unterscheidet auf einen
   Blick Tabellen von Rasterbildern; frueher stand dafuer ein Woertchen
   "Raster" hinter dem Namen und sonst nichts. */
.lz-name {
    display: flex;
    align-items: center;
    gap: 0.8125rem;
    min-width: 0
}

.lz-txt {
    min-width: 0
}

.lz-txt .ts {
    display: block
}

.lz-akt {
    /* Zwei Elemente: der gefuellte Vorschau-Knopf und die drei Punkte.
       Es waren einmal fuenf beschriftete Knoepfe, und das hiess bei zehn
       Layern fuenfzig Knoepfe auf einem Bildschirm - die Aktionen waren damit
       das Lauteste auf der Seite und die Layernamen das Leiseste. Mit nur
       einem Knopf je Zeile darf der auch gefuellt sein: ein Akzent je Zeile
       ist Materials Regel, fuenf gefuellte waeren eine blaue Wand.
       Die Zelle gibt damit 400px an die Namensspalte zurueck.
       table-layout:fixed macht diese Breite bindend, deshalb steht sie hier
       und nicht im Belieben des Inhalts.
       Bewusst KEIN display:flex hier: ein Flex-Container ist keine
       Tabellenzelle mehr, das wuerde die feste Spaltenaufteilung der
       Ebenentabelle aushebeln - die Reihe darin ist der Flex-Container. */
    width: 12.8125rem;
    text-align: right
}

/* Nicht veroeffentlichte Layer: Dort stehen VIER Knoepfe - Vorschau,
   umbenennen, loeschen und "Jetzt veroeffentlichen". In die 205px breite
   Aktionszelle passen die nicht; gemessen stapelten sie sich auf vier Zeilen,
   jede fuer sich rechtsbuendig. Diese eine Tabelle gibt die Spalten deshalb
   auf: der Name oben ueber die volle Breite, die Knoepfe darunter als
   Fusszeile nebeneinander.
   Nur hier und nicht in der Ebenentabelle - die kommt mit zwei Elementen je
   Zeile aus und braucht ihre feste Spaltenaufteilung. */
table.liste-offen,
table.liste-offen tbody,
table.liste-offen tr,
table.liste-offen > tbody > tr > td {
    display: block;
    width: auto
}

table.liste-offen .lz-akt {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 0.375rem;
    padding-top: 0.5rem;
    text-align: left
}

/* In einer Reihe mit gleichmaessigem Abstand traegt "gap" den Zwischenraum -
   der Aussenabstand von .lz-akt button ist fuer den Umbruchfall gedacht und
   verschoebe die Knoepfe hier gegeneinander. Auch der rechte: er hielt den
   letzten Knopf 6px von der Kante weg, waehrend der erste buendig an der
   linken stand - sichtbar, seit die Zeile beide Raender belegt. */
table.liste-offen .lz-akt button {
    margin: 0
}

.lz-reihe {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.2rem
}

/* ------------------------------------------------- Menue der drei Punkte
   Verankert an der Zelle, nicht am Fenster: kein Nachrechnen beim Scrollen,
   und es wandert von selbst mit seiner Zeile. */
.mehr {
    position: relative;
    flex: 0 0 auto
}

.mehr-knopf.offen,
.mehr-knopf.an {
    background: var(--md-primary-container);
    color: var(--md-on-primary-container)
}

.menue {
    position: absolute;
    z-index: 12;
    right: 0;
    top: calc(100% + 6px);
    width: 18.75rem;
    max-width: 78vw;
    padding: 0.375rem;
    background: var(--md-surface);
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-md);
    box-shadow: var(--md-erhebung-2);
    text-align: left;
    animation: mn-auf var(--md-kurz) var(--md-kurve)
}

/* Bei der letzten Layerzeile haengt das Menue sonst unter dem Fensterrand
   (app.js: menueUmschalten setzt die Klasse). */
.menue.nach-oben {
    top: auto;
    bottom: calc(100% + 6px)
}

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

.mn-eintrag {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    width: 100%;
    padding: 0.5625rem 0.75rem;
    border: none;
    background: transparent;
    border-radius: var(--md-form-sm);
    font: 500 0.875rem/1.35 var(--font);
    color: var(--md-on-surface);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--md-kurz) var(--md-kurve)
}

.mn-eintrag:hover {
    background: color-mix(in srgb, var(--md-on-surface) 8%, transparent)
}

/* Nach innen versetzt: das Menue ist nur 6px breiter als seine Eintraege,
   und ein Ring nach aussen laege halb auf dem Menuerand. */
.mn-eintrag:focus-visible {
    outline-offset: -2px
}

.mn-eintrag .ic {
    margin-top: 0.0625rem;
    color: var(--md-on-surface-variant)
}

.mn-txt {
    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
    min-width: 0
}

/* Der Halbsatz unter der Beschriftung ist der eigentliche Gewinn des Menues:
   frueher stand er im Kurzhinweis, und den bekommt nur, wer mit der Maus
   wartet - auf dem Telefon sieht ihn niemand. */
.mn-zu {
    font: 400 0.7812rem/1.35 var(--font);
    color: var(--md-on-surface-variant)
}

.menue hr {
    border: none;
    border-top: 1px solid var(--md-outline-variant);
    margin: 0.375rem 0.25rem
}

/* In der Liste dichter als sonst: fuenf Knoepfe nebeneinander. 34px ist
   Materials "extra small"-Stufe; mit 6px Aussenabstand bleibt die Zielflaeche
   ueber 44px hoch. */
.lz-akt button,
.lz-akt a {
    min-height: 2.125rem;
    padding: 0.3rem 0.625rem;
    gap: 0.3125rem;
    font-size: 0.8125rem;
    /* Luft, wenn die Knoepfe doch einmal auf zwei Zeilen stehen */
    margin-bottom: 0.25rem
}

.lz-akt .ic {
    width: 1.0625rem;
    height: 1.0625rem
}

.lz-akt .ic.chev {
    width: 0.8125rem;
    height: 0.8125rem
}

/* Klapp-Pfeil dreht sich beim Oeffnen - die einzige Rueckmeldung, die es
   braucht, und sie kostet kein Umbrechen.
   BEWUSST ein echtes <span> und kein ::after: der Hinweistext haengt an
   [data-tip]::after, und "button.klapper::after" waere spezifischer. Die
   Pfeilregel hat den Tooltip damit ueberschrieben - uebrig blieb ein winziger
   dunkler Kasten ohne Text. */
/* Der Klapp-Pfeil dreht sich beim Oeffnen. Frueher ein aus Rahmen gebautes
   Dreieck, jetzt Materials expand_more - dieselbe Handschrift wie alle
   anderen Zeichen. Er sitzt hinter der Beschriftung und ist bewusst kleiner
   als das fuehrende Symbol: er sagt "hier geht etwas auf", er benennt nichts. */
.ic.chev {
    width: 0.9375rem;
    height: 0.9375rem;
    margin-left: -0.0625rem;
    opacity: .75;
    transition: transform var(--md-kurz) var(--md-kurve)
}

button[aria-expanded="true"] .ic.chev {
    transform: rotate(180deg);
    opacity: 1
}

/* Der Knopf zur offenen Lade bleibt sichtbar gedrueckt. Ohne das ist bei
   vier Knoepfen nebeneinander nicht zu erkennen, welcher gerade den Inhalt
   darunter erzeugt - der Klapp-Pfeil allein ist dafuer zu klein. Dieselbe
   Farbe wie die Kante der Lade, damit beides als ein Stueck liest. */
/* Materials "filled tonal": der Knopf zur offenen Lade ist gefuellt, nicht
   nur umrissen. Ohne das ist bei fuenf Knoepfen nebeneinander nicht zu
   erkennen, welcher gerade den Inhalt darunter erzeugt - der Klapp-Pfeil
   allein ist dafuer zu klein. */
.lz-akt button.klapper.an {
    background: var(--md-primary-container);
    border-color: var(--md-primary-container);
    color: var(--md-on-primary-container);
    font-weight: 600
}

.lz-akt button.klapper.an:hover {
    background: color-mix(in srgb, var(--md-on-primary-container) 8%, var(--md-primary-container))
}

/* Die Lade gehoert sichtbar zur Zeile darueber: eingerueckt, abgesetzt,
   mit einer Kante an der linken Seite statt frei im Raum zu schweben. */
/* Die Lade gehoert sichtbar zur Zeile darueber: eingerueckt, abgesetzt,
   mit einer Kante an der linken Seite statt frei im Raum zu schweben.
   Der Ton ist Materials surface-container - Tiefe ueber die Flaeche, nicht
   ueber einen Schatten. */
tr.lade > td {
    background: var(--md-surface-container);
    border-left: 3px solid var(--md-primary);
    border-radius: 0 var(--md-form-md) var(--md-form-md) 0;
    padding: 0.625rem 1rem 0.875rem 1.125rem
}

/* Die Kopfzeile einer aufgeklappten Lade. Sie traegt den Rueckweg und sagt,
   welche der vier Laden man vor sich hat - beides hing frueher am Knopf in
   der Zeile, der sichtbar gedrueckt blieb. Seit die Laden aus dem Menue
   kommen, ist dieser Knopf beim Aufklappen schon wieder weg. */
.lade-kopf {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.25rem 0 0.75rem;
    margin-bottom: 0.25rem;
    border-bottom: 1px solid var(--md-outline-variant)
}

.lade-kopf strong {
    flex: 1 1 auto;
    font-size: 1rem;
    font-weight: 600
}

/* Kleiner als in einer Listenzeile: eine Ueberschrift, kein Eintrag. */
.lade-kopf .ic-kreis {
    width: 2rem;
    height: 2rem;
    border-radius: var(--md-form-sm)
}

.lade-kopf .ic-kreis .ic {
    width: 1.1875rem;
    height: 1.1875rem
}

.lade-x {
    flex: 0 0 auto
}

/* Die Lade haengt an ihrer Layerzeile und ist Teil derselben Liste - also
   dieselbe Knopfhoehe wie dort (34px, .lz-akt) und nicht die 36px, die sonst
   im Inhaltsbereich gelten. Gemessen standen "Stil bearbeiten" (36) und
   "Vorschau" (34) uebereinander und wirkten dadurch ungleich gewichtet,
   obwohl die Lade dem Knopf darueber untergeordnet ist. */
tr.lade button,
tr.lade a.copy {
    min-height: 2.125rem;
    font-size: 0.8125rem
}

.adr {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 0.625rem 0;
    border-bottom: 1px solid var(--line)
}

.adr:last-of-type {
    border-bottom: 0
}

.adr-txt {
    flex: 0 0 30%;
    min-width: 10.625rem;
    display: flex;
    flex-direction: column;
    gap: 0.125rem
}

.adr-zweck {
    color: var(--muted);
    font-size: 0.7812rem;
    line-height: 1.35
}

/* Die Adresse ist Beiwerk, nicht der Hauptdarsteller: ein leiser Kasten,
   der nicht lauter ist als seine eigene Beschriftung. min-width:0 ist noetig,
   damit ein langer Wert das Flex-Element schrumpfen laesst, statt die ganze
   Zeile aufzublaehen. */
.adr-wert {
    flex: 1;
    min-width: 0;
    overflow-x: auto;
    white-space: nowrap;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 0.375rem;
    padding: 0.3125rem 0.5625rem;
    font-size: 0.75rem;
    color: var(--muted)
}

/* Mehrzeilige Werte (das MapLibre-Style-Fragment): Zeilenumbrueche bleiben,
   die Hoehe ist begrenzt, gescrollt wird im Kasten. */
.adr-wert.mehrzeilig {
    white-space: pre;
    max-height: 13.75rem;
    overflow: auto
}

.adr-wert::-webkit-scrollbar {
    height: 0.375rem
}

.adr-btn {
    flex: 0 0 auto
}

/* Der Kopierknopf traegt nur noch sein Zeichen. Die lange Adresse daneben
   braucht jede Bildschirmbreite, die zu holen ist - und "kopieren" neben
   einem Kopiersymbol sagt nichts, was das Symbol nicht schon sagt. */
.adr .adr-btn {
    width: 2.125rem;
    min-width: 2.125rem;
    min-height: 2.125rem
}

/* ------------------------------------------------- Lade "Einstellungen"
   Alles hier stand frueher im Fuss der Einbinden-Lade, als Reihe von sechs
   gleich aussehenden Knoepfen. Was sie tun, stand nur im Kurzhinweis - und
   den bekommt nur, wer mit der Maus wartet.
   Jetzt eine Material-Listenzeile je Sache: fuehrendes Zeichen, Ueberschrift,
   ein Satz dazu, Aktionen rechts. Der Satz ist der eigentliche Gewinn. */
.einst {
    display: flex;
    flex-direction: column;
    gap: 0.125rem
}

.es-zeile {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 0.8125rem 0.125rem;
    border-bottom: 1px solid var(--md-outline-variant)
}

.es-zeile:last-child {
    border-bottom: none
}

.es-txt {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.125rem
}

.es-txt strong {
    font-weight: 600;
    font-size: 0.9375rem
}

.es-txt .ts {
    white-space: normal;
    font-size: 0.8125rem;
    line-height: 1.45
}

.es-akt {
    flex: 0 0 auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.5rem
}

/* Das Unumkehrbare steht unten und ist als solches gekennzeichnet - nicht
   ausgegraut, nicht versteckt, aber auch nicht wie die anderen. */
.es-gefahr {
    margin-top: 0.375rem;
    padding: 0.875rem 0.875rem 0.875rem 0.75rem;
    border: 1px solid color-mix(in srgb, var(--md-error) 22%, transparent);
    border-radius: var(--md-form-md);
    background: color-mix(in srgb, var(--md-error-container) 55%, transparent)
}

.es-gefahr .ic-kreis {
    background: var(--md-error-container);
    color: var(--md-error)
}

@media (max-width: 720px) {
    .es-zeile {
        flex-wrap: wrap
    }

    .es-txt {
        flex: 1 1 60%
    }

    .es-akt {
        flex: 1 1 100%;
        justify-content: flex-start;
        padding-left: 3.375rem
    }
}

/* Auf schmalen Anzeigen untereinander statt gequetscht nebeneinander */
@media (max-width: 720px) {
    .adr {
        flex-wrap: wrap
    }

    .adr-txt {
        flex: 1 1 100%
    }
}

/* Telefon: kein schwebendes Menue, sondern ein Bottom Sheet - so kennt man
   es von dort, und es braucht keine Positionsrechnerei auf einem Bildschirm,
   auf dem ohnehin nichts danebenpasst. Genau der Einwand, an dem schwebende
   Menues hier frueher gescheitert sind (app.js: menueUmschalten). */
@media (max-width: 620px) {
    .menue {
        position: fixed;
        z-index: 25;
        inset: auto 0 0 0;
        width: auto;
        max-width: none;
        padding: .5rem .5rem calc(.5rem + env(safe-area-inset-bottom));
        border: none;
        border-radius: var(--md-form-xl) var(--md-form-xl) 0 0;
        box-shadow: var(--md-erhebung-3);
        animation: mn-hoch var(--md-dauer) var(--md-kurve)
    }

    .menue.nach-oben {
        bottom: 0;
        top: auto
    }

    /* Der Greifbalken oben: das Zeichen, an dem ein Bottom Sheet als solches
       erkannt wird. */
    .menue::before {
        content: "";
        display: block;
        width: 2.125rem;
        height: 0.25rem;
        margin: 0.125rem auto 0.625rem;
        border-radius: var(--md-form-voll);
        background: var(--md-outline-variant)
    }

    .mn-eintrag {
        padding: 0.8125rem 0.875rem
    }

    @keyframes mn-hoch {
        from {
            transform: translateY(100%)
        }
    }

    /* Der Schleier dahinter. Er faengt keine Klicks ab - das Schliessen
       erledigt der Verteiler in app.js -, er nimmt der Seite nur die
       Aufmerksamkeit. */
    body.menue-sheet {
        overflow: hidden
    }

    body.menue-sheet::after {
        content: "";
        position: fixed;
        inset: 0;
        z-index: 24;
        background: var(--md-abdunkler-leicht);
        pointer-events: none
    }
}

/* Telefon: die Listen der Layer und der noch nicht veroeffentlichten
   Tabellen stapeln, statt drei Spalten nebeneinander zu quetschen.
   Der Grund: eine Tabellenspalte kann nie schmaler werden als ihr laengstes
   Wort, und Namen wie "bp_grunddaten_postgre" haben keine Trennstelle. Die
   Tabelle schob sich damit ueber den Bildschirmrand und nahm die ganze Seite
   mit nach rechts. Erzwingt man den Umbruch stattdessen mitten im Wort,
   bleiben von der Namensspalte drei Zeichen je Zeile uebrig - schlimmer als
   der Ueberhang. Als Bloecke bekommt jede Angabe die volle Breite:
   Name, darunter die Objektzahl, darunter die Knoepfe. */
@media (max-width: 620px) {

    table.liste,
    table.liste tbody,
    table.liste tr,
    table.liste td,
    table.ebenen,
    table.ebenen tbody,
    table.ebenen tr,
    table.ebenen td {
        display: block;
        width: auto
    }

    table.liste tr,
    table.ebenen tr {
        padding: 0.625rem 0;
        border-bottom: 1px solid var(--line)
    }

    table.liste tr:last-child,
    table.ebenen tr:last-child {
        border-bottom: none
    }

    table.liste td,
    table.ebenen td {
        padding: 0.125rem 0;
        border-bottom: none
    }

    /* Knoepfe unter den Namen, linksbuendig - rechtsbuendig passt nur zur
       Spaltenansicht. Der Einzug ist die Breite des fuehrenden Symbols samt
       Abstand (40 + 13): so beginnt die Knopfreihe unter dem NAMEN und nicht
       unter dem Symbol, und die Zeile liest sich als ein Block. */
    .lz-akt {
        width: auto;
        text-align: left
    }

    /* Die Aktionen bleiben rechts, auch wenn die Zeile stapelt. Ich hatte sie
       am 09.09.2026 linksbuendig unter den Namen gesetzt, weil sie sich so als
       ein Block lesen - im Gebrauch ist das falsch herum: der Daumen liegt
       rechts, und in jeder Zeile an derselben Stelle zu greifen ist mehr wert
       als die saubere Kante zum Text. Zurueckgedreht am 10.09.2026.
       .lz-reihe ist ein Flex-Container; das text-align der Zelle erreicht ihn
       nicht, deshalb steht es hier ausdruecklich. */
    .lz-reihe {
        justify-content: flex-end
    }

    .lz-akt button {
        margin: 0.1875rem 0.375rem 0.1875rem 0
    }

    /* Tabellen, die auf dem Telefon in Spalten unlesbar werden: die
       Dienst-Adressen (Dienst | Adresse), die Dokumentenlisten (Datei |
       Fundstelle) und das Import-Protokoll (Zeit | Marke | Meldung).
       Beim Protokoll stand hier einmal, Spalten seien dort richtig, weil die
       langen Dateinamen die Tabelle aufblaehen. Das galt fuer schmale Marken
       ohne Symbol - gemessen am 10.09.2026 blieben der Meldungsspalte 24px,
       und das ist keine Spalte mehr, sondern ein Strich. */
    table.stapelt,
    table.stapelt tbody,
    table.stapelt tr,
    table.stapelt td,
    table.stapelt th,
    .protokoll table,
    .protokoll tbody,
    .protokoll tr,
    .protokoll td {
        display: block;
        width: auto
    }

    table.stapelt tr,
    .protokoll tr {
        padding: 0.625rem 0;
        border-bottom: 1px solid var(--line)
    }

    table.stapelt tr:last-child,
    .protokoll tr:last-child {
        border-bottom: none
    }

    table.stapelt td,
    table.stapelt th,
    .protokoll td {
        padding: 0.125rem 0;
        border-bottom: none
    }

    /* Die Spaltenueberschrift "Dienst | Adresse" benennt gestapelt nichts
       mehr - jede Angabe steht ohnehin bei ihrem Wert. Die ganze ZEILE weg
       und nicht nur die Zellen: sonst bleibt ihr Innenabstand samt Trennlinie
       als leerer Streifen ueber der ersten Angabe stehen.
       ":has(th)" trifft dabei nur echte Kopfzeilen - die Dokumentenlisten
       haben keine, und ihre erste Zeile ist eine Datenzeile. */
    table.stapelt tr:has(th) {
        display: none
    }

    /* Im Protokoll steht die Zeit ueber der Meldung und tritt zurueck.
       Sie behaelt ihr nowrap: "05.09.2026," und "22:10" gehoeren zusammen. */
    .protokoll td.ts {
        font-size: 0.75rem;
        white-space: nowrap
    }

    .url-row {
        justify-content: flex-start !important
    }

    /* Im Import-Protokoll bleibt es bei Spalten (Zeit, Status, Meldung) -
       dort sind es die langen Dateinamen, die die Tabelle aufblaehen.
       Weil die Meldungsspalte breit ist, faellt ein Umbruch mitten im
       Namen dort nicht stoerend auf. */
    .protokoll td {
        overflow-wrap: anywhere
    }
}

/* Materials Chips: Radius 8 statt voller Kapsel - das unterscheidet sie auf
   den ersten Blick von den Knoepfen, die jetzt Kapseln sind. Mit Symbol,
   damit ein Zustand nicht allein an der Farbe haengt (WCAG 1.4.1). */
.tag {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    padding: 0.1875rem 0.5625rem;
    border-radius: var(--md-form-sm);
    font-size: 0.7812rem;
    font-weight: 500;
    white-space: nowrap;
    vertical-align: 1px
}

.t-ok {
    background: var(--ok-bg);
    color: var(--ok)
}

.t-err {
    background: var(--err-bg);
    color: var(--err)
}

.t-warn {
    background: var(--warn-bg);
    color: var(--warn)
}

.t-info {
    background: var(--accent-soft);
    color: var(--accent)
}

.t-sys {
    background: var(--md-surface-container-hoch);
    color: var(--muted)
}

/* "nur mit Anmeldung" an einer Layerzeile. Bewusst NICHT in Warn- oder
   Fehlerfarbe: ein beschraenkter Zugang ist kein Missstand, sondern eine
   getroffene Entscheidung. Dieselbe Flaeche wie die Aktionsfarbe, damit die
   Marke ruhig bleibt und trotzdem auffaellt. */
.t-zu {
    background: var(--accent-soft);
    color: var(--accent)
}

/* ------------------------------------------------------- Dialog "Zugang" */
.zd-feld {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin: 0.875rem 0
}

.zd-feld span {
    font-size: 0.8125rem;
    font-weight: 600
}

/* Ein Halbsatz UNTER dem Feld (z. B. "Die eigene Rolle ändert jemand
   anderes") ist keine Beschriftung - ohne diese Regel erbte er ihre
   Fettschrift (gesehen am 14.09.2026 im Dialog "Benutzer ändern"). */
.zd-feld .zd-unter {
    font-weight: 400;
    color: var(--muted)
}

.zd-feld input {
    font: inherit;
    padding: 0.4375rem 0.625rem;
    border: 1px solid var(--line);
    border-radius: var(--md-form-sm);
    background: var(--md-surface);
    color: var(--ink)
}

/* Das Passwort ist hier das einzige, was zaehlt - es steht gross, in fester
   Breite (damit l und 1 unterscheidbar bleiben) und mit dem Kopierknopf
   daneben. Umbrechen darf es: auf dem Telefon passen 23 Zeichen sonst nicht. */
.zd-pw {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.625rem;
    margin: 0.875rem 0;
    padding: 0.75rem 0.875rem;
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-md);
    background: var(--accent-soft)
}

/* Zu kurz - in Fehlerfarbe, aber ohne Ausrufezeichen: Es ist eine Auskunft
   waehrend des Tippens, keine Zurechtweisung. */
.zd-kurz {
    color: var(--err)
}

.zd-pw code {
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: .04em;
    color: var(--ink);
    word-break: break-all
}

.ts {
    color: var(--muted);
    font-size: 0.8125rem;
    white-space: nowrap;
    font-variant-numeric: tabular-nums
}

/* Auf Telefonbreite bricht die Nebenzeile um. Sie traegt normalerweise kurze
   Angaben - daher das nowrap oben -, in den Laden "Stil" und "Template" aber
   ganze Saetze ("von Hand angelegt - hier nur zum Ansehen", die Reichweite
   einer Vorlagenstufe). Gemessen am 10.09.2026: ein solcher Satz war 430px
   breit und schob die ganze Seite auf 500px hinaus.
   Die Ausnahme steht BEWUSST hier und nicht oben im grossen Telefon-Block:
   der steht in dieser Datei vor .ts, und bei gleicher Spezifitaet gewinnt die
   spaetere Regel - dort kam die Ausnahme nie an. */
@media (max-width: 620px) {
    .ts {
        white-space: normal
    }

    /* Ausser der Zeitspalte im Protokoll: "05.09.2026," und "22:10" gehoeren
       zusammen. */
    .protokoll td.ts {
        white-space: nowrap
    }
}

.empty {
    color: var(--muted);
    padding: 0.375rem 0
}

/* Der Kasten war bisher nur an seiner Farbe zu erkennen. Das Zeichen setzt
   das CSS und nicht das Markup: die Kaesten entstehen an zehn Stellen als
   fertiges HTML, und ein Symbol je Stelle einzupflegen waere zehnmal die
   Gelegenheit, eines zu vergessen. Gebacken als Daten-URI, weil ein
   Hintergrundbild kein currentColor kennt - die Farbe des Kastens steht
   ohnehin fest. */
.hint {
    position: relative;
    background: var(--md-primary-weich);
    border-left: 3px solid var(--md-primary);
    padding: 0.75rem 0.9375rem 0.75rem 2.8125rem;
    border-radius: 0 var(--md-form-md) var(--md-form-md) 0;
    margin-top: 0.75rem;
    font-size: 0.9062rem
}

/* Absolut und NICHT als Flex-Element: der Kasten enthaelt freien Fliesstext
   mit <em> und <strong> darin, und in einem Flex-Container wird jedes dieser
   Kinder ein eigenes Element - der Satz zerfaellt dann in Spalten. */
.hint::before {
    content: "";
    position: absolute;
    left: 15px;
    top: 14px;
    width: 1.1875rem;
    height: 1.1875rem;
    background: no-repeat center/contain url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2331749b' stroke-width='1.8' stroke-linecap='round'><circle cx='12' cy='12' r='9'/><path d='M12 11v5.5M12 7.6v.9'/></svg>")
}

/* Verwaltungsleiste als Kacheln. Icons sind inline eingebettete SVGs - keine
   Icon-Bibliothek und keine zusaetzliche Anfrage. */
.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 0.75rem
}

.tile {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 0.9375rem 1rem;
    background: var(--md-surface);
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-lg);
    text-decoration: none;
    color: var(--md-on-surface);
    transition: background-color var(--md-kurz) var(--md-kurve),
                border-color var(--md-kurz) var(--md-kurve),
                box-shadow var(--md-kurz) var(--md-kurve)
}

/* Kein transform mehr beim Zeigen: Material hebt eine Flaeche ueber Ton und
   Erhebung an, nicht indem es sie verschiebt. */
.tile:hover {
    border-color: var(--md-outline);
    background: color-mix(in srgb, var(--md-primary) 5%, var(--md-surface));
    box-shadow: var(--md-erhebung-1)
}

/* display:block ist hier wesentlich - als Inline-Spans klebten Titel und
   Beschreibung ohne Umbruch aneinander. */
.tile .tt {
    display: block;
    font-weight: 600;
    font-size: 0.9375rem;
    line-height: 1.3
}

.tile .td {
    display: block;
    color: var(--muted);
    font-size: 0.8125rem;
    margin-top: 0.1875rem;
    line-height: 1.4
}

.tile .prod {
    color: var(--accent);
    font-weight: 600
}

/* Die globale code-Regel bricht mitten im Wort um (sinnvoll bei langen URLs,
   stoerend bei einem kurzen Ordnernamen in der Kachel). */
.tile code {
    word-break: normal;
    white-space: nowrap;
    font-size: 0.75rem;
    padding: 0.0625rem 0.25rem
}

.tile .ar {
    display: flex;
    margin-left: auto;
    padding-left: 0.625rem;
    color: var(--md-on-surface-variant);
    transition: transform var(--md-kurz) var(--md-kurve),
                color var(--md-kurz) var(--md-kurve)
}

.tile:hover .ar {
    color: var(--md-primary);
    transform: translateX(2px)
}

footer {
    color: var(--muted);
    font-size: 0.8125rem;
    margin-top: 2.25rem;
    text-align: center
}

.offline {
    background: var(--err-bg);
    color: var(--err);
    border: 1px solid var(--md-error-outline);
    padding: 0.75rem 0.9375rem;
    margin-bottom: 1.375rem;
    border-radius: 0.5rem
}

/* Kurze Bestaetigung nach dem Kopieren. Nur Farbe, keine Groessenaenderung -
   die Breite haelt das JS ohnehin fest. Das Symbol wechselt mit auf den
   Haken (app.js: copyText); bei einem reinen Symbolknopf ist das die einzige
   sichtbare Rueckmeldung. */
button.copy.ok {
    border-color: var(--md-ok);
    color: var(--md-ok);
    background: color-mix(in srgb, var(--md-ok) 10%, transparent)
}

/* Materials Snackbar: flache Ecken (Radius 4), Erhebung 3, und sie faehrt
   von unten herein statt aufzublitzen. Die Farbe bleibt: gruen/rot tragen
   hier eine Aussage und sind nicht Dekor. */
.bn {
    position: fixed;
    left: 50%;
    /* Der Ruhezustand traegt die Verschiebung selbst - die Animation endet
       sonst und der Kasten springt nach links. */
    transform: translateX(-50%);
    bottom: 26px;
    z-index: 9;
    padding: 0.875rem 1.25rem;
    border-radius: var(--md-form-xs);
    font-size: 0.9375rem;
    max-width: min(38.75rem, 92vw);
    box-shadow: var(--md-erhebung-3);
    display: none;
    animation: bn-auf var(--md-dauer) var(--md-kurve)
}

@keyframes bn-auf {
    from {
        opacity: 0;
        transform: translate(-50%, 14px)
    }

    to {
        opacity: 1;
        transform: translate(-50%, 0)
    }
}

.bn.ok {
    background: var(--ok-bg);
    color: var(--ok);
    border: 1px solid var(--md-ok-outline)
}

.bn.err {
    background: var(--err-bg);
    color: var(--err);
    border: 1px solid var(--md-error-outline)
}

@media(max-width:560px) {
    .kpi {
        font-size: 1.5625rem
    }

    th,
    td {
        padding: 0.5rem 0.375rem;
        font-size: 0.875rem
    }
}

/* ---- Kartenvorschau ---------------------------------------------------- */
.ov {
    position: fixed;
    inset: 0;
    background: var(--md-abdunkler);
    z-index: 20;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 1.25rem
}

.ov.on {
    display: flex
}

/* Im Vollbild fuellt die Vorschau samt Kopfzeile den Bildschirm (app.js:
   vollbildSchalten) - kein Rand, keine Rundung, die Karte nimmt den Rest. */
.ov:fullscreen {
    padding: 0;
    background: var(--card)
}

.ov:fullscreen .mw {
    width: 100%;
    height: 100%;
    max-width: none;
    border-radius: 0;
    box-shadow: none
}

.mw {
    background: var(--card);
    /* 28px - die Formenstufe, die Material fuer Dialoge vorsieht
       (--md-form-xl). Der Vorschaudialog stand als einziger noch auf den
       12px von vorher, weil er beim Material-Durchgang ausgespart war. */
    border-radius: var(--md-form-xl);
    width: min(62.5rem, 100%);
    height: min(88vh, 47.5rem);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    box-shadow: 0 18px 50px var(--md-schatten-hart)
}

.mh {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.875rem 1.125rem;
    border-bottom: 1px solid var(--line);
    flex-wrap: wrap
}

.mh h3 {
    margin: 0;
    font-size: 1.0625rem;
    font-weight: 600
}

.mh .sp {
    flex: 1
}

/* Umschalter "Vorschau / Wie im Portal". Zwei zusammengeschobene Knoepfe,
   von denen einer gefuellt ist - keine Auswahlliste: bei genau zwei
   Moeglichkeiten sieht man so beide auf einmal und braucht einen Klick
   statt zweier. Erscheint nur bei veroeffentlichten Layer; bei den anderen
   gibt es keinen Dienst, den man zeigen koennte. */
.mmodus {
    display: inline-flex;
    border: 1px solid var(--line);
    border-radius: 0.4375rem;
    overflow: hidden
}

/* Gemessen standen im Dialogkopf drei Groessen nebeneinander: 27px die
   Segmente, 29px die Kartenknoepfe, 36px "Vollbild" und der Schliesser.
   Jetzt tragen alle die 36px der uebrigen Knoepfe - eine Leiste, eine Hoehe,
   und die Zielflaeche der Segmente waechst dabei von 27 auf 36px. */
.mmodus .seg {
    border: 0;
    min-height: 2.125rem;
    background: var(--card);
    color: var(--muted);
    padding: 0.3125rem 0.75rem;
    font-size: 0.8125rem;
    cursor: pointer
}

.mmodus .seg + .seg {
    border-left: 1px solid var(--line)
}

.mmodus .seg:hover {
    background: var(--accent-soft);
    color: var(--accent)
}

.mmodus .seg.an {
    background: var(--accent);
    color: var(--md-on-primary);
    font-weight: 600
}

/* Hintergrundkarte und 3D in der Kopfzeile der Vorschau. Das Auswahlfeld
   sieht aus wie die Segmentknoepfe daneben, damit die Kopfzeile aus einem
   Guss bleibt. */
.mkarte {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8125rem;
    color: var(--muted);
    /* Eigene Zeile unter Titel und Umschalter, immer - sonst bricht die
       Kopfzeile je nach Fensterbreite an wechselnder Stelle um, und mal
       steht der Deckkraft-Regler allein unten, mal die Kartenwahl. */
    flex-basis: 100%;
    order: 10
}

.mkarte select {
    border: 1px solid var(--line);
    border-radius: 0.4375rem;
    background: var(--card);
    color: var(--ink);
    padding: 0.3125rem 0.5rem;
    font-size: 0.8125rem;
    font-family: inherit;
    max-width: 11.25rem;
    cursor: pointer
}

.mkarte .seg3d {
    border: 1px solid var(--line);
    min-height: 2.125rem;
    border-radius: 0.4375rem;
    background: var(--card);
    color: var(--muted);
    padding: 0.3125rem 0.625rem;
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer
}

.mkarte .seg3d:hover:not(:disabled) {
    background: var(--accent-soft);
    color: var(--accent)
}

.mkarte .seg3d.an {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--md-on-primary)
}

.mkarte .seg3d:disabled {
    opacity: .45;
    cursor: default
}

.mkarte .mkarte-lbl {
    margin-left: 0.625rem
}

/* Deckkraft-Regler: laesst den Kunden sehen, was UNTER des Layers liegt -
   beim Luftbild der eigentliche Zweck (passt der Ausschnitt?), bei Flaechen
   die Hintergrundkarte. Sitzt in der Kopfzeile der Vorschau, damit er beim
   Schieben nicht vom eigenen Finger verdeckt wird. */
.mopa {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8125rem;
    color: var(--muted)
}

/* .mopa steht auf display:flex - ohne diese Zeile bliebe die Gruppe im
   Editor sichtbar, weil "display" die Voreinstellung des Browsers fuer
   [hidden] schlaegt. */
.mopa[hidden] {
    display: none
}

.mopa input[type=range] {
    width: 6.875rem;
    accent-color: var(--accent);
    cursor: pointer
}

/* Feste Breite und Ziffern gleicher Laufweite: sonst wackelt die ganze
   Kopfzeile beim Schieben, weil "5 %" schmaler ist als "100 %". */
.mopa output {
    min-width: 2.625rem;
    text-align: right;
    font-variant-numeric: tabular-nums
}

/* Hinweis am Mauszeiger (app.js: hoverZeigen). Rechts unter dem Zeiger,
   damit er das Objekt nicht verdeckt; ohne Zeigerereignisse, sonst flackert
   er, sobald die Maus ihn selbst trifft. */
.mtip {
    position: absolute;
    z-index: 5;
    pointer-events: none;
    transform: translate(14px, 14px);
    border: 1px solid var(--md-invers-rand);
    background: color-mix(in srgb, var(--md-invers) 92%, transparent);
    color: var(--md-on-invers);
    padding: 0.3125rem 0.5625rem;
    border-radius: 0.375rem;
    font-size: 0.7812rem;
    line-height: 1.35;
    max-width: 17.5rem;
    box-shadow: 0 4px 14px var(--md-schatten-mittel)
}

.mtip.links {
    transform: translate(calc(-100% - 14px), 14px)
}

.mtip.oben {
    transform: translate(14px, calc(-100% - 14px))
}

.mtip.links.oben {
    transform: translate(calc(-100% - 14px), calc(-100% - 14px))
}

.mtip strong {
    display: block;
    font-weight: 600
}

/* Weitere Objekte an derselben Stelle: mit Trennlinie, die Bezeichnung
   etwas leiser als die des obersten. */
.mtip .mtip-weitere {
    margin-top: 0.25rem;
    padding-top: 0.25rem;
    border-top: 1px solid color-mix(in srgb, var(--md-on-invers) 18%, transparent)
}

.mtip .mtip-weitere strong {
    font-weight: 500
}

.mtip .mtip-mehr {
    display: block;
    margin-top: 0.3125rem;
    color: color-mix(in srgb, var(--md-on-invers) 60%, transparent);
    font-size: 0.6875rem
}

/* Spaltenname als Ueberschrift ueber dem Wert: klein, in Kapitaelchen-Art
   mit etwas Sperrung - so liest er sich als Beschriftung, nicht als Text. */
.mtip .mtip-spalte {
    display: block;
    color: color-mix(in srgb, var(--md-on-invers) 62%, transparent);
    font-size: 0.6562rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    margin-bottom: 0.0625rem
}

.mc {
    flex: 1;
    position: relative;
    background: var(--md-karte-grund);
    /* Wieviel von der Karte gerade verdeckt ist. Das Panel des Stileditors
       liegt ueber ihr (.stp, position:absolute, z-index 3) - alles, was in
       der Kartenecke sitzt, muss davor ausweichen, sonst liegt es darunter
       und ist unsichtbar. EINE Angabe fuer alle drei Ecken-Elemente
       (Massstabszahl, Quellenangabe, Massstabsbalken): drei getrennte
       Rechnungen waeren drei Gelegenheiten, es unterschiedlich falsch zu
       machen. */
    --karte-rechts: 0px;
    --karte-unten: 0px
}

.mc:has(.stp:not([hidden])) {
    --karte-rechts: min(25rem, 92%)
}

/* Schmal liegt das Panel nicht rechts, sondern unten ueber 62 % der Hoehe
   (siehe die 620px-Regel weiter unten). */
@media (max-width: 620px) {
    .mc:has(.stp:not([hidden])) {
        --karte-rechts: 0px;
        --karte-unten: 62%
    }
}

/* Die Bedienelemente von MapLibre sitzen im Kartencontainer, der die ganze
   Flaeche einnimmt - auch den Teil unter dem Panel. Sie weichen mit aus. */
.mc .maplibregl-ctrl-bottom-right {
    right: var(--karte-rechts);
    bottom: var(--karte-unten)
}

/* Aufgeklappt braucht die Quellenangabe die ganze Ecke und laege sonst
   hinter der Massstabszahl, die links daneben steht. Sie rueckt deshalb eine
   Zeile nach oben - eingeklappt (nur das (i), 24px) bleibt sie unten neben
   der Zahl. */
.mc .maplibregl-ctrl-attrib.maplibregl-compact-show {
    margin-bottom: 2.125rem
}

.mc .maplibregl-ctrl-bottom-left {
    bottom: var(--karte-unten)
}

#map {
    position: absolute;
    inset: 0
}

.mf {
    padding: 0.5625rem 1.125rem;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: 0.8125rem;
    display: flex;
    gap: 0.875rem;
    flex-wrap: wrap;
    align-items: center
}

/* Materials Icon-Button: runde Zielflaeche, Zustandsflaeche beim Zeigen,
   kein Rahmen. In den Kaestchen-Dialogen traegt er nur noch das Kreuz - die
   Beschriftung bleibt fuer Vorleseprogramme im aria-label stehen. */
.mclose {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4375rem;
    border: 1px solid var(--md-outline);
    background: transparent;
    border-radius: var(--md-form-voll);
    min-height: 2.25rem;
    padding: 0 0.875rem;
    font: 500 0.875rem/1.2 var(--font);
    cursor: pointer;
    color: var(--md-on-surface-variant);
    transition: background-color var(--md-kurz) var(--md-kurve),
                color var(--md-kurz) var(--md-kurve)
}

.mclose:hover {
    background: color-mix(in srgb, var(--md-on-surface) 8%, transparent);
    color: var(--md-on-surface)
}

.mclose.ikon-nur {
    width: 2.5rem;
    min-width: 2.5rem;
    padding: 0;
    gap: 0;
    border-color: transparent
}

.mstate {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 1.625rem;
    color: var(--muted);
    font-size: 0.9375rem;
    background: var(--md-karte-grund);
    z-index: 2
}

.mstate.hide {
    display: none
}

.warnbar {
    background: var(--warn-bg);
    color: var(--warn);
    padding: 0.5rem 1.125rem;
    font-size: 0.8438rem;
    border-bottom: 1px solid var(--md-warn-outline)
}

.maplibregl-ctrl-attrib {
    font-size: 0.6875rem
}

/* ---- Sachdaten des angeklickten Objekts -------------------------------- */
/* Bewusst ein Panel IN der Karte statt eines zweiten Modals ueber dem
   Vorschaufenster: geschachtelte Dialoge sind auf kleinen Geraeten kaum
   bedienbar, und ein Panel kann bei vielen Attributen scrollen. */
.dp {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(20.625rem, 80%);
    background: var(--card);
    border-left: 1px solid var(--line);
    z-index: 3;
    display: flex;
    flex-direction: column;
    transform: translateX(101%);
    transition: transform .18s ease;
    box-shadow: -4px 0 16px var(--md-schatten-weich)
}

.dp.on {
    transform: translateX(0)
}

.dph {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6875rem 0.875rem;
    border-bottom: 1px solid var(--line)
}

.dph strong {
    font-size: 0.9375rem;
    flex: 1
}

/* Der Schliesser eines Panels. Trug frueher ein Mal-Zeichen (&times;) - das
   ist ein Schriftzeichen und wechselt mit der Schrift Strichstaerke und Groesse.
   Jetzt dasselbe Symbol wie ueberall sonst, aus dem Register. */
.dpx {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    flex: 0 0 auto;
    border: none;
    background: none;
    border-radius: var(--md-form-voll);
    cursor: pointer;
    color: var(--md-on-surface-variant);
    padding: 0;
    transition: background-color var(--md-kurz) var(--md-kurve),
                color var(--md-kurz) var(--md-kurve)
}

.dpx .ic {
    width: 1.1875rem;
    height: 1.1875rem
}

.dpx:hover {
    background: color-mix(in srgb, var(--md-on-surface) 8%, transparent);
    color: var(--md-on-surface)
}

/* Blaetterleiste bei uebereinanderliegenden Objekten. Die Pfeile sitzen
   aussen, die Zaehlung mittig - so bleibt der Text an derselben Stelle
   stehen, egal ob "1 von 2" oder "10 von 12" darin steht. */
/* Zustandstabelle im Bereich "Info". Vier Spalten, von links nach rechts
   abnehmend wichtig: Teil, Zustand, Version, Begruendung. Die Versionsspalte
   mit gleicher Ziffernlaufweite, sonst zappeln die Zahlen untereinander. */
.zustand {
    width: 100%;
    border-collapse: collapse
}

.zustand th {
    text-align: left;
    font-weight: 600;
    white-space: nowrap
}

.zustand td:nth-child(3) {
    font-variant-numeric: tabular-nums
}

.zustand th,
.zustand td {
    padding: 0.4375rem 0.625rem;
    border-bottom: 1px solid var(--line);
    vertical-align: top;
    font-size: 0.9062rem
}

.zustand tr:last-child th,
.zustand tr:last-child td {
    border-bottom: none
}

/* Zwischenueberschrift zwischen den Bloecken (siehe app.py: GRUPPEN).
   Bewusst OHNE eigene Hintergrundflaeche: eine graue Zeile mitten in einer
   Tabelle liest sich wie ein weiterer Eintrag. Abstand darueber und die
   ohnehin vorhandene Trennlinie darunter reichen.
   Und bewusst NICHT in Grossbuchstaben: die Namensspalte ist es schon (siehe
   die th-Grundregel weiter oben), zwei gleich gesetzte Ebenen untereinander
   waeren keine Gliederung. Der Kopf hebt sich stattdessen durch die volle
   Textfarbe und mehr Gewicht ab. */
.zustand tr.gr td {
    padding: 1.375rem 0.625rem 0.4375rem;
    font-size: 0.9375rem;
    font-weight: 700;
    color: var(--ink)
}

/* Der erste Block sitzt direkt unter der Kartenkante - der Abstand darueber
   waere dort doppelt. */
.zustand tr.gr:first-child td {
    padding-top: 0.125rem
}

/* Auf Telefonbreiten stapeln, wie table.ebenen und table.liste weiter oben.
   Der Grund ist derselbe: Die Namensspalte steht auf "white-space: nowrap"
   (in der Spaltenansicht richtig, sonst zerfaellt "Datenbank (PostGIS)" in
   drei Zeilen), und die Versionen daneben sind lang - "PostgreSQL 17.5
   (Debian 17.5-1.pgdg110+1), PostGIS 3.5.2" hat keine brauchbare
   Trennstelle. Bei 320 px stand die Tabelle 105 px ueber den Rand hinaus,
   und jede neue Zeile machte es schlimmer.

   Dieser Block steht bewusst HIER und nicht im grossen 620er-Block weiter
   oben: Die Grundregeln von .zustand stehen darueber und haben dieselbe
   Spezifitaet - sie wuerden Rahmen und Innenabstand einfach zurueckholen.
   Genau so gesehen: Trennlinien zwischen den ZELLEN statt zwischen den
   Zeilen. */
@media (max-width: 620px) {

    .zustand,
    .zustand tbody,
    .zustand tr,
    .zustand th,
    .zustand td {
        display: block;
        width: auto
    }

    .zustand tr {
        padding: 0.5625rem 0;
        border-bottom: 1px solid var(--line)
    }

    .zustand tr:last-child {
        border-bottom: none
    }

    /* Gestapelt traegt die Zeile selbst den Abstand und die Trennlinie -
       die Zelle darf hier also keinen eigenen Vorlauf mehr haben, sonst
       stehen Kopf und erster Eintrag doppelt weit auseinander. */
    .zustand tr.gr td {
        padding: 0 0 0.125rem
    }

    .zustand tr.gr {
        padding: 1rem 0 0;
        border-bottom: none
    }

    .zustand tr.gr:first-child {
        padding-top: 0
    }

    .zustand th,
    .zustand td {
        padding: 0.125rem 0;
        border-bottom: none;
        white-space: normal;
        overflow-wrap: anywhere
    }
}

.dpn {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.625rem;
    border-bottom: 1px solid var(--line);
    background: var(--bg);
    color: var(--muted);
    font-size: 0.7812rem
}

.dpn span {
    flex: 1;
    text-align: center
}

.dpn button {
    border: 1px solid var(--line);
    background: var(--card);
    border-radius: 0.3125rem;
    /* Feste Groesse: ein Chevron ist schmal, ohne das waere die Trefferflaeche
       auf dem Telefon kleiner als eine Fingerkuppe. */
    width: 1.75rem;
    height: 1.625rem;
    font-size: 1.0625rem;
    line-height: 1;
    cursor: pointer;
    color: var(--muted);
    padding: 0
}

.dpn button:hover {
    border-color: var(--accent);
    color: var(--accent)
}

.dpb {
    flex: 1;
    overflow: auto;
    padding: 0.25rem 0
}

/* Rahmen fuer die GeoServer-Ausgabe. Fuellt den Panelbereich; scrollen tut
   der Inhalt darin selbst. Die Hoehe laesst sich nicht am Inhalt ausrichten -
   ein abgeschotteter Rahmen gibt seine Groesse nicht nach aussen. */
#dpi {
    width: 100%;
    height: 100%;
    min-height: 13.75rem;
    border: 0;
    background: #fff;
    display: block
}

.dpb table {
    width: 100%;
    border-collapse: collapse
}

.dpb th,
.dpb td {
    text-align: left;
    padding: 0.4375rem 0.875rem;
    font-size: 0.875rem;
    border-bottom: 1px solid var(--line);
    vertical-align: top
}

.dpb th {
    color: var(--muted);
    font-weight: 600;
    width: 42%;
    font-size: 0.7812rem;
    text-transform: none;
    letter-spacing: 0;
    word-break: break-word
}

.dpb td {
    word-break: break-word
}

.dpb .leer {
    color: var(--muted);
    font-style: italic
}

/* ---- Import-Protokoll: aeltere Eintraege eingeklappt -------------------- */
/* Ohne Skript nicht darstellbar - deshalb wird der Knopf erst gar nicht
   ausgeliefert, wenn es nichts zum Ausklappen gibt (siehe app.py). */
.protokoll tr.extra {
    display: none
}

.protokoll.offen tr.extra {
    display: table-row
}

/* Die letzte sichtbare Zeile soll keine Trennlinie tragen. Welche das ist,
   markiert app.py - eine Zeilennummer hier waere eine stille Kopplung an
   LOG_SICHTBAR. Im ausgeklappten Zustand greift wieder die globale Regel
   fuer tr:last-child. */
.protokoll:not(.offen) tr.letzte-sichtbar td {
    border-bottom: none
}

.klapp {
    padding-top: 0.75rem
}

/* ---- Abschnitt "Ihre Dokumente" ---------------------------------------- */
/* Die Zusammenfassung steht ueber den Listen und traegt die Zahlen, die den
   Kunden wirklich interessieren - die Listen darunter sind die Arbeitsliste. */
.dok-summe {
    font-size: 0.9375rem;
    color: var(--muted);
    padding-bottom: 0.25rem
}

.dok-summe strong {
    color: var(--ink);
    font-weight: 600
}

/* Zwischenueberschrift innerhalb einer Karte: kleiner als h2, mit Abstand
   nach oben, damit sich die Listen optisch voneinander abheben. */
.card h3 {
    font-size: 0.9375rem;
    font-weight: 600;
    margin: 1.25rem 0 0.375rem;
    display: flex;
    align-items: center;
    gap: 0.5rem
}

.card h3 + table {
    margin-bottom: 0.125rem
}

/* Verweis auf ein Dokument (PDF & Co.). Bewusst deutlich als Link erkennbar:
   in einer Attributtabelle rechnet niemand mit klickbaren Werten. */
.dpb a.dok {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 2px;
    display: inline-block;
    word-break: break-all
}

.dpb a.dok::before {
    content: "📄";
    margin-right: 0.3125rem;
    text-decoration: none
}

.dpb a.dok:hover {
    color: var(--brand)
}

/* Abwechselnde Zeilenhintergruende - bei vielen Attributen bleibt so die
   Zuordnung Schluessel/Wert ueber die Zeile hinweg lesbar. */
.dpb tr:nth-child(even) th,
.dpb tr:nth-child(even) td {
    background: var(--bg);
}

/* Die globale Regel "tr:last-child td" nimmt nur der Wertzelle die Linie -
   im Sachdaten-Panel ist der Schluessel aber ein th, dessen Linie blieb
   stehen und endete auf halber Breite. */
.dpb tr:last-child th,
.dpb tr:last-child td {
    border-bottom: none;
}

.dpf {
    padding: 0.5625rem 0.875rem;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: 0.8125rem;
    display: flex;
    align-items: center;
    gap: 0.625rem
}

.dpf button {
    border: 1px solid var(--line);
    background: var(--md-surface);
    border-radius: 0.3125rem;
    padding: 0.1875rem 0.5625rem;
    font-size: 0.7812rem;
    cursor: pointer;
    color: var(--muted)
}

.dpf button:hover {
    border-color: var(--accent);
    color: var(--accent)
}

.maplibregl-canvas-container canvas {
    outline: none
}

@media(max-width:620px) {
    .dp {
        top: auto;
        left: 0;
        width: 100%;
        height: min(52%, 20rem);
        border-left: none;
        border-top: 1px solid var(--line);
        transform: translateY(101%)
    }

    .dp.on {
        transform: translateY(0)
    }
}

/* Maßstabszahl, unten rechts in der Kartenecke. Sie stand früher links über
   dem Maßstabsbalken von MapLibre, weil Nenner und Entfernung zusammen erst
   ein Bild ergeben - die Zahl ist aber die Angabe, in der SLD denkt
   (Min-/MaxScaleDenominator), und sie soll ohne Suchen dastehen. Fett und
   ohne Rahmen: Auf einer Karte ist jeder Rahmen eine Linie mehr, die mit den
   Daten konkurriert; der halbdurchsichtige Grund trägt die Lesbarkeit.
   Position in rem und nicht in px: Die Bedienelemente von MapLibre werden
   über "zoom" mit der Grundschrift skaliert (siehe weiter unten), mit festen
   Pixeln säße die Zahl auf einem 4K-Schirm im Weg. */
.mscale {
    position: absolute;
    /* Auf gleicher Hoehe wie der Massstabsbalken von MapLibre links unten
       (gemessen: 10px ueber der Kante, 22px hoch) - beide sagen etwas ueber
       denselben Ausschnitt und sollen auf einer Linie liegen.
       Rechts daneben bleibt Platz fuer das (i) der Quellenangabe: 24px breit
       mit 10px Abstand zur Kante, also 44px insgesamt. */
    right: calc(var(--karte-rechts) + 2.75rem);
    bottom: calc(var(--karte-unten) + 0.625rem);
    z-index: 2;
    background: var(--md-karte-glas);
    border-radius: 0.3125rem;
    padding: 0.125rem 0.4375rem;
    font-size: 0.7188rem;
    font-weight: 700;
    color: var(--md-auf-karte-glas);
    font-variant-numeric: tabular-nums;
    pointer-events: none
}


/* ---- Stileditor -------------------------------------------------------- */
/* Sitzt an derselben Stelle wie das Sachdaten-Panel und ist etwas breiter:
   Hier steht ein Verlauf mit ganzen Sätzen, keine Wertetabelle. Die Karte
   füllt weiterhin das Fenster - in einem Stileditor ist das Bild das, was
   beurteilt wird. */
.stp {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(25rem, 92%);
    background: var(--card);
    border-left: 1px solid var(--line);
    z-index: 3;
    display: flex;
    flex-direction: column;
    box-shadow: -4px 0 16px var(--md-schatten-weich)
}

/* Ohne diese Zeile bliebe das Panel sichtbar: "display:flex" oben schlägt
   die Voreinstellung des Browsers für [hidden]. */
.stp[hidden] {
    display: none
}


.stpb {
    flex: 1;
    overflow: auto;
    padding: 0.75rem 0.875rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem
}

/* Leere Abschnitte hinterlassen in einer Spalte mit "gap" trotzdem ihren
   Abstand - vier davon (Legende, Vorschläge, Grenzen, Feinschliff) sind je
   nach Zustand leer, und der Verlauf rutschte dann grundlos nach unten. */
.stp-legende:empty,
.stp-vorschlaege:empty,
.stp-grenzen:empty,
.stp-fein:empty {
    display: none
}

/* Was gerade gilt - die Frage, die man bei einem geerbten Stil immer hat. */
.stp-jetzt {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 0.5rem;
    padding: 0.625rem 0.75rem
}

.stp-jetzt h4 {
    margin: 0 0 0.375rem;
    font-size: 0.8125rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--muted)
}

.stp-satz {
    margin: 0;
    font-size: 0.875rem;
    line-height: 1.5
}

.stp-liste {
    margin: 0.4375rem 0 0;
    padding-left: 1.125rem;
    font-size: 0.8438rem;
    line-height: 1.5;
    color: var(--muted)
}

.stp-verlauf {
    display: flex;
    flex-direction: column;
    gap: 0.625rem
}

/* Der Wunsch des Kunden rechts, die Antwort links - dieselbe Anordnung wie
   in jedem Nachrichtenverlauf, damit man beides ohne Nachdenken auseinander
   hält. */
.stp-du {
    align-self: flex-end;
    max-width: 92%;
    background: var(--accent-soft);
    border: 1px solid var(--line);
    border-radius: 0.625rem 0.625rem 0.125rem 0.625rem;
    padding: 0.5rem 0.6875rem;
    font-size: 0.875rem;
    line-height: 1.45
}

.stp-ki {
    max-width: 96%;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 0.625rem 0.625rem 0.625rem 0.125rem;
    padding: 0.5625rem 0.75rem;
    font-size: 0.875rem;
    line-height: 1.5
}

.stp-warte {
    font-size: 0.8438rem;
    color: var(--muted);
    padding: 0.25rem 0.125rem
}

/* Ein Fehlschlag ist keine Katastrophe: Der Entwurf wird geprüft und
   verworfen, BEVOR er angezeigt wird - die Darstellung des Kunden war nie in
   Gefahr. Deshalb ein Hinweis in Bernstein und kein rotes Feld. */
.stp-fehl {
    background: var(--warn-bg);
    border: 1px solid var(--md-warn-outline);
    border-radius: 0.625rem;
    padding: 0.5625rem 0.75rem;
    font-size: 0.8438rem;
    line-height: 1.5;
    color: var(--warn)
}

.stp-vorschlaege {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem
}

.stp-vs {
    border: 1px dashed var(--line);
    background: var(--card);
    color: var(--accent);
    border-radius: 1.25rem;
    padding: 0.3125rem 0.6875rem;
    font-size: 0.8125rem;
    cursor: pointer;
    text-align: left
}

.stp-vs:hover:not(:disabled) {
    border-style: solid;
    background: var(--accent-soft)
}

.stp-vs:disabled {
    opacity: .5;
    cursor: default
}

/* Die Legende, gezeichnet von GeoServer selbst - also genau so, wie sie
   später im Portal des Kunden steht. Sie liegt AUF der Karte, oben links:
   Welche Farbe welchen Wert meint, liest man neben den Farben selbst.
   Heller Grund, weil GeoServer schwarze Schrift auf durchsichtigem Grund
   liefert - im dunklen Erscheinungsbild wäre sie sonst unlesbar.
   Gedeckelt auf 40 % der Kartenhöhe: Eine Klassifikation mit zwanzig Klassen
   liefert ein hohes Bild, und die Legende soll die Karte begleiten und nicht
   verdecken. Darüber wird gescrollt. */
.mlegende {
    position: absolute;
    top: 0.625rem;
    left: 0.625rem;
    z-index: 2;
    max-width: 45%;
    max-height: 40%;
    overflow: auto;
    background: var(--md-karte-glas);
    color: var(--md-auf-karte-glas);
    border-radius: 0.5rem;
    padding: 0.375rem 0.5rem;
    /* Sie ist Auskunft, kein Bedienelement - Klicks gehören der Karte.
       Der Rollbalken braucht sie allerdings, sobald gescrollt werden muss. */
    box-shadow: 0 1px 4px var(--md-schatten-mittel)
}

.mlegende[hidden] {
    display: none
}

/* GeoServer zeichnet die Legende in doppelter Groesse (dpi:180, siehe
   app.py: legenden_url). "zoom" halbiert sie fuer das Layout, ohne die
   Bildpunkte wegzuwerfen - dieselbe Technik wie bei den MapLibre-Elementen
   weiter unten. Ergebnis: gleiche Flaeche, scharfe Beschriftung. */
.mlegende img {
    display: block;
    zoom: .5;
    max-width: 100%
}

/* Maßstabsgrenzen. Derselbe Kasten wie beim Feinschliff - beides sind
   Eigenschaften des Entwurfs, die man direkt ausprobieren kann. */
.stp-grenzen h4 {
    margin: 0 0 0.125rem;
    font-size: 0.8125rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--muted)
}

.stp-g {
    border: 1px solid var(--line);
    border-radius: 0.5rem;
    padding: 0.5rem 0.625rem;
    margin-bottom: 0.4375rem
}

.stp-g-kopf {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.4375rem;
    font-size: 0.8438rem;
    margin-bottom: 0.375rem
}

.stp-g-wert {
    font-size: 0.7812rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap
}

.stp-g-zeile {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem
}

/* Der Stand steht unter den Knöpfen und nicht in ihnen: Was der Knopf
   verspricht, ist eine Ansage über die Karte - was gerade gilt, ist eine
   Messung. Beides in einer Zeile zu mischen wäre genau die Verwechslung,
   die dieser Kasten auflösen soll. */
.stp-g-stand {
    margin: 0.375rem 0 0;
    font-size: 0.7812rem;
    color: var(--muted)
}

.stp-g-stand.an {
    color: var(--ok)
}

.stp-g-stand.aus {
    color: var(--warn)
}

/* Der Feinschliff. Die KI bringt den Kunden auf 90 % - die letzten 10 %
   macht ein Farbwähler: kein Warten, keine Kosten, keine Anfrage nach
   draußen. */
.stp-fein h4 {
    margin: 0 0 0.125rem;
    font-size: 0.8125rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--muted)
}

.stp-fein-txt {
    margin: 0 0 0.5rem;
    font-size: 0.7812rem;
    color: var(--muted)
}

/* Ein Block je Regel. Er fasst zusammen, was im Stil ohnehin zusammen
   gehoert - Flaeche und Rand eines Polygons sind EINE Regel - und traegt
   deren Titel als Ueberschrift. Der Titel ist ein Eingabefeld, weil genau er
   in der Legende des Kunden steht: "Gray Polygon with Black Outline" aus der
   GeoServer-Vorlage war bisher nur im SLD-Quelltext zu beheben. */
.stp-rb {
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-md);
    background: var(--md-surface-container);
    padding: 0.5625rem 0.625rem 0.375rem;
    margin-bottom: 0.5rem
}

.stp-rb-kopf {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem
}

/* Der Titel nimmt den Platz, der Legendenschalter nur den, den er braucht. */
.stp-rb-kopf .stp-rb-titel {
    flex: 1;
    min-width: 0
}

.stp-rb-leg {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    flex: 0 0 auto;
    font-size: 0.7188rem;
    color: var(--muted);
    cursor: pointer;
    white-space: nowrap
}

.stp-rb-leg input {
    accent-color: var(--accent);
    cursor: pointer;
    margin: 0
}

/* Der Kasten fuer einen Stil, der noch nicht beschriftet. Gestrichelt, weil
   hier noch nichts ist - er legt erst etwas an. */
.stp-rb-neu {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    border-style: dashed;
    background: transparent
}

.stp-rb-neu select {
    flex: 1;
    min-width: 6rem;
    font: inherit;
    font-size: 0.8125rem;
    height: 2.125rem;
    padding: 0 0.25rem;
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-xs);
    background: var(--card);
    color: var(--ink)
}

.stp-rb-neu button {
    flex: 0 0 auto
}

.stp-rb-kopf label {
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted)
}

.stp-rb-titel {
    font: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    min-height: 2.125rem;
    padding: 0 0.5rem;
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-sm);
    background: var(--card);
    color: var(--ink);
    width: 100%;
    box-sizing: border-box
}

.stp-rb-titel:focus {
    outline: 2px solid var(--accent);
    outline-offset: -1px
}

/* Ohne Titel faellt die Legende auf den technischen <Name> zurueck - der
   Platzhalter sagt das, statt ein leeres Feld unkommentiert zu lassen. */
.stp-rb-titel::placeholder {
    font-weight: 400;
    color: var(--muted);
    opacity: 1
}

/* Die Farbkaesten liegen jetzt IM Block und brauchen dort keinen eigenen
   Ton mehr - der Block traegt ihn. */
.stp-rb .stp-r {
    background: var(--card)
}

.stp-rb .stp-r:last-child {
    margin-bottom: 0
}

.stp-r {
    border: 1px solid var(--line);
    border-radius: 0.5rem;
    padding: 0.5rem 0.625rem;
    margin-bottom: 0.4375rem
}

.stp-r-kopf {
    display: flex;
    align-items: baseline;
    gap: 0.4375rem;
    font-size: 0.8438rem;
    margin-bottom: 0.3125rem
}


.stp-r-zeile {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.25rem
}

.stp-r-zeile input[type=range] {
    flex: 1;
    accent-color: var(--accent);
    cursor: pointer;
    min-width: 3.75rem
}

.stp-r-zeile input[type=color] {
    width: 2.125rem;
    height: 1.625rem;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 0.3125rem;
    background: none;
    cursor: pointer
}

/* Der Hexwert neben dem Farbfeld. Ziffern gleicher Laufweite, weil hier
   sechs Stellen verglichen werden - "#0f6e8c" gegen "#0f6e8b" liest sich
   in einer Proportionalschrift schlecht. */
.stp-r-zeile .stp-hex {
    width: 4.75rem;
    flex: none;
    height: 1.625rem;
    padding: 0 0.375rem;
    border: 1px solid var(--line);
    border-radius: 0.3125rem;
    background: var(--card);
    color: var(--ink);
    font: inherit;
    font-size: 0.7812rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    text-transform: lowercase
}

.stp-r-zeile .stp-hex:focus {
    outline: 2px solid var(--accent);
    outline-offset: 1px
}

/* Auswahlfeld im Feinschliff - Punktform, Schrift, beschriftete Spalte.
   Es nimmt den Platz des Reglers ein, den es an dieser Stelle ersetzt. */
.stp-r-zeile select {
    flex: 1;
    min-width: 0;
    font: inherit;
    font-size: 0.8125rem;
    height: 1.625rem;
    padding: 0 0.25rem;
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-xs);
    background: var(--card);
    color: var(--ink)
}

/* Ein Wert, den der Server so nicht kennt (etwa "Arial" als Schrift). Nicht
   rot: Es ist kein Fehler, sondern eine Abweichung, die man sehen soll. */
.stp-r-zeile select.stp-fremd {
    border-color: var(--md-warn);
    background: var(--md-warn-container)
}

/* Fett und kursiv. Die Klickflaeche ist das ganze Etikett und nicht nur das
   Kaestchen - ein 13px-Quadrat zu treffen ist auf dem Telefon Gluecksspiel. */
.stp-r-haken {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    margin-right: 0.75rem;
    font-size: 0.7812rem;
    color: var(--muted);
    cursor: pointer;
    min-height: 1.625rem
}

.stp-r-haken input {
    accent-color: var(--accent);
    cursor: pointer;
    margin: 0
}

.stp-r-lbl {
    font-size: 0.7812rem;
    color: var(--muted);
    min-width: 2.625rem
}

/* Feste Breite und Ziffern gleicher Laufweite, damit beim Schieben nicht die
   ganze Zeile wackelt - dieselbe Überlegung wie beim Deckkraft-Regler oben. */
.stp-r-zeile output {
    font-size: 0.7812rem;
    color: var(--muted);
    min-width: 3.25rem;
    text-align: right;
    font-variant-numeric: tabular-nums
}

.stpe {
    display: flex;
    gap: 0.4375rem;
    align-items: flex-end;
    padding: 0.625rem 0.75rem 0.375rem;
    border-top: 1px solid var(--line)
}

.stpe textarea {
    flex: 1;
    resize: none;
    font: inherit;
    font-size: 0.875rem;
    line-height: 1.4;
    padding: 0.4375rem 0.5625rem;
    border: 1px solid var(--line);
    border-radius: 0.5rem;
    background: var(--md-surface);
    color: var(--ink)
}

.stpe textarea:focus {
    outline: 2px solid var(--accent);
    outline-offset: -1px
}

.stpgo {
    border: 1px solid var(--accent);
    background: var(--accent);
    color: var(--md-on-primary);
    border-radius: 0.5rem;
    width: 2.25rem;
    height: 2.25rem;
    font-size: 1.0625rem;
    cursor: pointer;
    flex: none
}

.stpgo:disabled,
.stpe textarea:disabled {
    opacity: .55;
    cursor: default
}

/* Der Datenschutzhinweis steht dort, wo er gelesen wird - direkt über dem
   Knopf, der die Übertragung auslöst - und benennt konkret, was übertragen
   wird, nicht "Daten". */
.stphinweis {
    margin: 0;
    padding: 0 0.75rem 0.5rem;
    font-size: 0.7188rem;
    line-height: 1.4;
    color: var(--muted)
}

/* Werkzeugzeile ueber die volle Dialogbreite. Sie sass frueher im Panel
   rechts (rund 500px) und stapelte die fuenf Knoepfe auf drei Zeilen; ueber
   die ganze Breite passen sie in eine. Links der Zustand, rechts die
   Aktionen - dazwischen schiebt der Zwischenraum sie auseinander. */
.stpf {
    border-top: 1px solid var(--line);
    padding: 0.5rem 1.125rem;
    display: flex;
    flex-direction: row;
    align-items: center;
    /* Umbrechen statt abschneiden. Ohne das lagen auf 390px "SLD
       bearbeiten", "Verwerfen" und "Uebernehmen" ausserhalb des Dialogs -
       und weil .mw fuer den Radius auf overflow:hidden steht, waren sie
       schlicht weg, die Hauptaktion eingeschlossen. Kein fester
       Umbruchpunkt: Wieviel Platz die Knoepfe brauchen, haengt davon ab,
       wieviele gerade sichtbar sind (drei ohne Entwurf, fuenf mit einem). */
    flex-wrap: wrap;
    row-gap: 0.5rem;
    gap: 0.75rem
}

.stpf[hidden] {
    display: none
}

/* Sagt, WELCHEN der beiden Staende die Karte zeigt - die Marke links sagt,
   OB es einen Entwurf gibt. Sie darf schrumpfen, bevor die Knoepfe es tun:
   der Satz ist Auskunft, die Knoepfe sind Bedienung. */
.stpf .ts {
    flex: 0 1 auto;
    /* Sie darf schrumpfen, aber nicht bis zur Unkenntlichkeit: Sonst
       presst sie sich auf wenige Zeichen zusammen, statt die Knoepfe in die
       naechste Zeile zu lassen. */
    min-width: 9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap
}

/* margin-left:auto schiebt die Gruppe nach rechts, solange sie neben die
   Auskunft passt. "0 1 auto" statt "0 0 auto" ist der Punkt: Ohne das
   Schrumpfen nimmt die Gruppe immer ihre volle Inhaltsbreite ein, bricht
   INNEN also nie um - auf 390px stand "Uebernehmen" dann ausserhalb des
   Dialogs. */
/* Die Layerliste ist die Referenz der Seite - 34px. Im Dialog galten bisher
   36px, die Normalgroesse des Inhaltsbereichs; nebeneinander gehalten war der
   Dialog dadurch der lautere von beiden, obwohl er dieselben Handgriffe
   anbietet. */
.mh button,
.stpf button {
    min-height: 2.125rem
}

.stpf .stpknoepfe {
    margin-left: auto;
    flex: 0 1 auto;
    min-width: 0
}

/* Drei Zustände, immer sichtbar: was ist veröffentlicht, was ist nur ein
   Entwurf, und wie komme ich zurück. */
.stpmarke {
    font-size: 0.7812rem;
    font-weight: 600;
    padding: 0.1875rem 0.5625rem;
    border-radius: 1.25rem;
    align-self: center;
    flex: 0 0 auto;
    white-space: nowrap
}

.stpmarke.live {
    background: var(--ok-bg);
    color: var(--ok)
}

.stpmarke.entwurf {
    background: var(--warn-bg);
    color: var(--warn)
}

/* Woher die Werte des Feinschliffs stammen. Dieselben zwei Farben wie die
   Marke unten bei den Knoepfen - es ist dieselbe Auskunft, nur dort, wo man
   sie braucht: an den Werten. */
.stp-quelle {
    margin-left: 0.5rem;
    padding: 0.125rem 0.5rem;
    border-radius: var(--md-form-sm);
    font-size: 0.6875rem;
    font-weight: 500;
    vertical-align: 0.0625rem
}

.stp-quelle.live {
    background: var(--md-ok-container);
    color: var(--md-ok)
}

.stp-quelle.entwurf {
    background: var(--md-warn-container);
    color: var(--md-warn)
}

/* Der Hinweis auf einen liegengebliebenen Entwurf in der Stil-Lade. Warnfarben,
   aber kein Alarm: es ist kein Fehler, dass ein Entwurf daliegt - es ist nur
   der Grund, warum Lade und Editor verschiedene Werte zeigen. */
.dar-entwurf {
    display: flex;
    gap: 0.5rem;
    align-items: flex-start;
    margin: 0.5rem 0 0;
    padding: 0.625rem 0.75rem;
    border-radius: var(--md-form-sm);
    background: var(--md-warn-container);
    color: var(--md-warn);
    font-size: 0.8125rem;
    line-height: 1.45
}

.dar-entwurf .ic {
    flex: 0 0 auto;
    margin-top: 0.125rem
}

.dar-entwurf strong {
    font-weight: 600
}

.stpknoepfe {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    justify-content: flex-end
}

/* Textknoepfe stehen dichter als umrissene: Ohne Rahmen traegt der
   Zwischenraum die Trennung, und 16px Innenabstand liessen die Zeile
   unnoetig breit werden. */
.stpknoepfe button.ghost {
    padding: 0 0.625rem
}

/* Knopfzeile in der Lade. Sie stand als schmale Spalte RECHTS neben dem
   Inhalt und stapelte ihre Knoepfe deshalb untereinander - bei zwei Knoepfen
   zwei Zeilen, wo eine gereicht haette, und der Inhalt daneben verlor die
   Breite. Jetzt eine volle Zeile UNTER dem Inhalt: nebeneinander, rechts
   ausgerichtet, mit Trennlinie darueber wie bei einem Dialogfuss.
   "flex: 1 0 100%" in einer umbrechenden .dar-zeile ist der ganze Trick -
   dafuer musste am Markup nichts geaendert werden. */
.dar-akt {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    justify-content: flex-end;
    align-items: center;
    flex: 1 0 100%;
    margin-top: 0.6875rem;
    padding-top: 0.6875rem;
    border-top: 1px solid var(--md-outline-variant)
}

/* Ein Dialogfuss ordnet auch ueber die SEITE, nicht nur ueber die Form: der
   Hauptknopf haengt rechts, das Stillere und das Gefaehrliche am anderen
   Ende links - so verschwimmt "loeschen" nicht mit "Jetzt veroeffentlichen"
   zu einer Reihe gleichrangiger Angebote. "schub" traegt der LETZTE Knopf
   der linken Gruppe und schiebt alles Folgende von sich weg.
   Warum am letzten linken und nicht am ersten rechten: die Zeile darf
   umbrechen. Ein "margin-left: auto" weiter rechts wirkt nach dem Umbruch
   nur noch auf seiner eigenen Zeile, und die letzte Zeile - dort steht dann
   der Hauptknopf - stuende plotzlich links. So bleibt die Zeile auf
   "flex-end": jede Zeile fuer sich rechtsbuendig, und der eine Aussenabstand
   holt die linke Gruppe an ihren Rand zurueck. Gemessen bei 1440, 900, 620
   und 390px.
   Zwei geschachtelte Flex-Kaesten waeren der andere Weg und der schlechtere:
   die haetten als Block umgebrochen, die linke Gruppe also als Klotz. */
.dar-akt .schub,
table.liste-offen .lz-akt .schub {
    margin-right: auto
}

/* Nur für Vorlesesoftware: Das Eingabefeld trägt seine Beschriftung sichtbar
   im Platzhalter, braucht aber trotzdem ein echtes <label>. */
.nur-sr {
    position: absolute;
    width: 0.0625rem;
    height: 0.0625rem;
    padding: 0;
    margin: -0.0625rem;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0
}

@media(max-width:620px) {
    .stp {
        top: auto;
        left: 0;
        width: 100%;
        height: 62%;
        border-left: none;
        border-top: 1px solid var(--line)
    }
}

/* ---- Erklärende Kurzhinweise an Bedienelementen ------------------------- */
/* Reines CSS, kein Skript. Erscheinen bei Mauszeiger UND bei Tastaturfokus,
   damit sie nicht nur der Maus vorbehalten sind. Auf Touchgeräten gibt es
   kein "hover" - dort tragen die Hinweistexte unter den Tabellen und der
   Bestätigungsdialog beim Zurückziehen die Erklärung. */
[data-tip] {
    position: relative;
}

[data-tip]::after,
[data-tip]::before {
    opacity: 0;
    visibility: hidden;
    transition: opacity .13s ease, transform .13s ease;
    pointer-events: none;
    position: absolute;
    bottom: calc(100% + 9px);
    left: 50%;
    transform: translate(-50%, 4px);
    z-index: 30;
}

[data-tip]::after {
    content: attr(data-tip);
    border: 1px solid var(--md-invers-rand);
    width: max-content;
    max-width: 16.25rem;
    white-space: normal;
    text-align: left;
    line-height: 1.4;
    background: var(--md-invers);
    color: var(--md-on-invers);
    font-size: 0.7812rem;
    font-weight: 400;
    padding: 0.5rem 0.6875rem;
    border-radius: 0.4375rem;
    box-shadow: 0 4px 14px var(--md-schatten-mittel);
}

/* Pfeilspitze zum Bedienelement hin */
[data-tip]::before {
    content: "";
    bottom: calc(100% + 3px);
    border: 6px solid transparent;
    border-top-color: var(--ink);
}

[data-tip]:hover::after,
[data-tip]:hover::before,
[data-tip]:focus-visible::after,
[data-tip]:focus-visible::before {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
}

/* Knoepfe in der obersten Zeile eines Dialogs: Der Hinweis sitzt sonst
   ueber ihnen und damit ausserhalb des Dialogs - und weil .mw auf
   "overflow: hidden" steht (der Radius braucht das), war er schlicht
   abgeschnitten. Gemessen: 27px oberhalb der Dialogkante. Dort also
   darunter statt darueber, samt umgedrehter Pfeilspitze. */
[data-tip][data-tip-unten]::after,
[data-tip][data-tip-unten]::before {
    bottom: auto;
    top: calc(100% + 9px);
    transform: translate(-50%, -4px)
}

[data-tip][data-tip-unten]::before {
    top: calc(100% + 3px);
    border-top-color: transparent;
    border-bottom-color: var(--ink)
}

[data-tip][data-tip-unten]:hover::after,
[data-tip][data-tip-unten]:hover::before,
[data-tip][data-tip-unten]:focus-visible::after,
[data-tip][data-tip-unten]:focus-visible::before {
    transform: translate(-50%, 0)
}

/* Rechts ausgerichtet UND darunter: die Verschiebung muss dann ohne den
   waagerechten Anteil auskommen, sonst zieht sie den Hinweis wieder in die
   Mitte zurueck. */
[data-tip][data-tip-pos="links"][data-tip-unten]::after,
[data-tip][data-tip-pos="links"][data-tip-unten]::before {
    transform: translate(0, -4px)
}

[data-tip][data-tip-pos="links"][data-tip-unten]:hover::after,
[data-tip][data-tip-pos="links"][data-tip-unten]:hover::before,
[data-tip][data-tip-pos="links"][data-tip-unten]:focus-visible::after,
[data-tip][data-tip-pos="links"][data-tip-unten]:focus-visible::before {
    transform: translate(0, 0)
}

/* Am rechten Rand würde ein mittig zentrierter Hinweis aus dem Fenster
   ragen - dort nach links ausrichten. */
[data-tip][data-tip-pos="links"]::after {
    left: auto;
    right: 0;
    transform: translate(0, 4px);
}

[data-tip][data-tip-pos="links"]:hover::after,
[data-tip][data-tip-pos="links"]:focus-visible::after {
    transform: translate(0, 0);
}

/* Und am LINKEN Rand spiegelbildlich: der Menueknopf sitzt in der linken
   oberen Ecke, ein mittig zentrierter Hinweis war dort zur Haelfte
   abgeschnitten ("in- oder ausklappen", gesehen am 12.09.2026 bei 390px). */
[data-tip][data-tip-pos="rechts"]::after {
    left: 0;
    right: auto;
    transform: translate(0, 4px);
}

[data-tip][data-tip-pos="rechts"]:hover::after,
[data-tip][data-tip-pos="rechts"]:focus-visible::after {
    transform: translate(0, 0);
}

[data-tip][data-tip-pos="rechts"][data-tip-unten]::after,
[data-tip][data-tip-pos="rechts"][data-tip-unten]::before {
    transform: translate(0, -4px)
}

[data-tip][data-tip-pos="rechts"][data-tip-unten]:hover::after,
[data-tip][data-tip-pos="rechts"][data-tip-unten]:hover::before,
[data-tip][data-tip-pos="rechts"][data-tip-unten]:focus-visible::after,
[data-tip][data-tip-pos="rechts"][data-tip-unten]:focus-visible::before {
    transform: translate(0, 0)
}

/* Bei ausgeschaltetem Knopf (läuft gerade) nicht mehr anzeigen */
[data-tip]:disabled::after,
[data-tip]:disabled::before {
    display: none;
}

@media (max-width: 620px) {
    [data-tip]::after {
        max-width: 12.5rem;
    }
}

/* Upload-Bereich: bewusst schlank - eine Drop-Zone, keine Technik-Anzeige
   (Chunk-Groesse, Parallelitaet, Kamera/Video wie beim Filebrowser-eigenen
   Dialog). */
.upload-zone {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    text-align: center;
    padding: 1.875rem 1rem;
    border: 2px dashed var(--md-outline-variant);
    border-radius: var(--md-form-lg);
    cursor: pointer;
    transition: border-color var(--md-kurz) var(--md-kurve),
                background-color var(--md-kurz) var(--md-kurve)
}

.upload-zone:hover,
.upload-zone:focus-visible {
    border-color: var(--md-primary);
    background: color-mix(in srgb, var(--md-primary) 5%, transparent)
}

/* Beim Ziehen deutlicher als beim blossen Zeigen: durchgezogener Rand und
   die getoente Flaeche - hier faellt gleich etwas hinein. */
.upload-zone.over {
    border-style: solid;
    border-color: var(--md-primary);
    background: var(--md-primary-weich)
}

.upload-ic {
    display: flex;
    width: 3.5rem;
    height: 3.5rem;
    align-items: center;
    justify-content: center;
    border-radius: var(--md-form-lg);
    background: var(--md-primary-container);
    color: var(--md-on-primary-container)
}

.upload-ic .ic {
    width: 1.75rem;
    height: 1.75rem
}

.upload-txt {
    font-weight: 600;
    font-size: 0.9375rem
}

.upload-hint {
    color: var(--muted);
    font-size: 0.8125rem
}

#upload-liste {
    display: flex;
    flex-direction: column;
    gap: 0.625rem
}

#upload-liste:not(:empty) {
    margin-top: 0.875rem
}

.upload-row {
    display: grid;
    /* vierte Spalte: Abbrechen-Knopf. Bleibt nach Abschluss leer stehen,
       damit die Zeilen darueber und darunter nicht verrutschen. */
    grid-template-columns: minmax(120px, 1fr) minmax(90px, 160px) minmax(140px, 1.4fr) 92px;
    align-items: center;
    gap: 0.625rem;
    font-size: 0.8438rem
}

.upload-name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap
}

.upload-bar {
    height: 0.4375rem;
    border-radius: 0.25rem;
    background: var(--accent-soft);
    overflow: hidden
}

.upload-fill {
    height: 100%;
    width: 0;
    background: var(--accent);
    transition: width .2s
}

.upload-stop {
    justify-self: end;
    padding: 0.1875rem 0.5625rem;
    font-size: 0.7812rem
}

.upload-row.ok .upload-fill {
    background: var(--ok)
}

.upload-row.err .upload-fill {
    background: var(--err)
}

.upload-row.ok .upload-status {
    color: var(--ok)
}

.upload-row.err .upload-status {
    color: var(--err)
}

.upload-status {
    color: var(--muted)
}

@media (max-width: 560px) {
    .upload-row {
        grid-template-columns: 1fr;
        gap: 0.25rem
    }
}

/* Sandbox-Reset: bewusst deutlich als Gefahrenbereich abgesetzt, nicht wie
   die uebrigen, ruhigen Karten - dieser Abschnitt existiert ueberhaupt nur
   auf Vorfuehr-/Testumgebungen. */
.card.gefahr {
    border-color: var(--err);
    background: var(--err-bg)
}

.card.gefahr p {
    margin: 0 0 0.75rem
}

#reset-bestaetigung {
    flex: 1 1 15rem;
    min-width: 0;
    padding: 0.375rem 0.625rem;
    border: 1px solid var(--line);
    border-radius: 0.375rem;
    font-size: 0.875rem;
    background: var(--card);
    color: var(--ink)
}

#reset-bestaetigung:focus-visible {
    outline: 2px solid var(--err);
    outline-offset: 1px
}

/* Ein stiller Knopf in Fehlerfarbe - OHNE Rahmen. Materials Aufteilung fuer
   eine Flaeche ist ein Akzent und der Rest still; neben dem gefuellten
   "Jetzt veroeffentlichen" bzw. "Uebernehmen" waere ein zweiter Rahmen ein
   zweiter Anspruch auf dieselbe Aufmerksamkeit. Die Warnung traegt das Wort
   und die Farbe, nicht die Umrandung. */
button.ghost.gefahr {
    color: var(--err);
    /* "button.gefahr" weiter oben faerbt den Rahmen ein - das gilt fuer den
       umrissenen Knopf und ist dort richtig. Der stille braucht ihn
       ausdruecklich zurueckgenommen, sonst gewinnt die allgemeinere Regel
       ueber die Reihenfolge. */
    border-color: transparent
}

button.ghost.gefahr:hover:not(:disabled) {
    background: var(--err);
    border-color: var(--err);
    color: var(--md-on-error)
}

#reset-status {
    margin-top: 0.625rem;
    font-size: 0.8438rem;
    color: var(--err);
    min-height: 1.4em
}

/* --------------------------------------------------- Darstellung (Stil/Vorlage)
   Zeilenaufbau wie bei .adr in der Einbinden-Lade: Bezeichnung links,
   Inhalt in der Mitte, Aktion rechts. Bewusst dieselbe Anmutung - es ist
   dieselbe Art von Angabe, nur einen Layer tiefer im Kartendienst. */
.dar-zeile {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.875rem;
    padding: 0.6875rem 0;
    border-top: 1px solid var(--line)
}

.dar-zeile:first-child {
    border-top: none;
    padding-top: 0.125rem
}

.dar-lbl {
    flex: 0 0 8.25rem;
    font-weight: 600;
    font-size: 0.875rem;
    padding-top: 0.125rem
}

.dar-inh {
    flex: 1 1 auto;
    min-width: 0
}

/* Telefon: Beschriftung UEBER den Inhalt statt daneben. Nebeneinander bleiben
   dem Inhalt bei 390px keine 180px - der Stilname, die Legende und die
   Vorlagenkette standen dort als Buchstabenkolonne.
   Der Block steht BEWUSST hier und nicht oben im grossen Telefon-Block: der
   steht in dieser Datei vor .dar-zeile, und bei gleicher Spezifitaet gewinnt
   die spaetere Regel. Dort blieb die Beschriftung 8,25rem HOCH (im
   Spaltenmodus wird aus der Breite eine Hoehe) und 22px schmal - gemessen am
   10.09.2026. */
@media (max-width: 620px) {
    .dar-zeile {
        flex-direction: column;
        align-items: stretch;
        gap: 0.25rem;
        padding: 0.75rem 0
    }

    .dar-lbl {
        flex: 0 0 auto;
        padding-top: 0
    }

    /* Die Knopfzeile darf umbrechen statt sich zu quetschen. */
    .dar-akt {
        flex-wrap: wrap
    }
}

.dar-kopf {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem
}

/* Lange Pfade und Stilnamen duerfen umbrechen - sonst schiebt ein
   workspaces/<ws>/<store>/<layer>/content.ftl die Zeile auf dem Telefon
   ueber den Rand hinaus. */
.dar-kopf code {
    font-size: 0.8125rem;
    word-break: break-all
}

.dar-txt {
    margin: 0.25rem 0 0;
    color: var(--muted);
    font-size: 0.8438rem;
    line-height: 1.5
}

.dar-keine {
    color: var(--muted);
    font-style: italic;
    font-size: 0.8438rem
}

.dar-warte {
    margin: 0.125rem 0;
    color: var(--muted);
    font-size: 0.8438rem
}

/* Der Hinweis auf einen unveraenderten GeoServer-Stil. Warnfarben, aber
   bewusst kein Fehlerrot: es ist kein Fehler, nur selten gewollt. */
.tag.std {
    background: var(--warn-bg);
    color: var(--warn)
}

/* ------------------------------------------ Suchkette der Sachdaten-Vorlage
   Vier Stufen, von "nur diesen Layer" bis "der ganze GeoServer". Die
   Reihenfolge IST die Information: GeoServer nimmt die erste, die es findet.
   Deshalb eine Liste mit sichtbarer Rangfolge und nicht vier gleichwertige
   Kaesten - und deshalb der durchgehende Strich links, der sie als eine
   Leiter lesbar macht. */
.vk {
    list-style: none;
    margin: 0.125rem 0 0;
    padding: 0 0 0 0.9375rem;
    border-left: 2px solid var(--line);
    display: flex;
    flex-direction: column;
    gap: 0.5625rem
}

.vk li {
    position: relative
}

/* Der Punkt auf der Leiter. Gefuellt heisst: diese Datei gibt es. */
.vk li::before {
    content: "";
    position: absolute;
    left: -20px;
    top: 7px;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    border: 2px solid var(--muted);
    background: var(--bg);
    opacity: .5
}

.vk-aktiv::before {
    border-color: var(--brand);
    background: var(--brand);
    opacity: 1;
    box-shadow: 0 0 0 3px var(--accent-soft)
}

.vk-da::before {
    border-color: var(--warn);
    background: var(--warn);
    opacity: 1
}

.vk-kopf {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem
}

.vk-kopf code {
    font-size: 0.7812rem;
    word-break: break-all
}

/* Stufen ohne Datei treten deutlich zurueck. Sie stehen oben, weil das die
   Suchreihenfolge ist - aber sie sind nicht die Auskunft, sondern nur der
   Hinweis, WO noch etwas liegen koennte. Bekaemen sie dieselbe Anmutung wie
   die wirksame Vorlage, laege das Auge zuerst auf drei Dateien, die es gar
   nicht gibt. Bewusst nicht bis zur Unlesbarkeit ausgegraut: genau diese
   Zeilen erklaeren spaeter die Wahl der Reichweite beim Uebernehmen. */
.vk-leer .vk-kopf code {
    color: var(--muted);
    background: transparent;
    padding-left: 0;
    padding-right: 0;
    font-size: 0.75rem
}

.vk-leer .vk-txt {
    font-size: 0.7812rem;
    opacity: .8
}

/* Die wirksame Vorlage traegt die Auskunft und bekommt das Gewicht dazu. */
.vk-aktiv .vk-kopf code {
    font-weight: 600
}

.vk-aktiv .vk-txt {
    color: var(--ink)
}

.vk-txt {
    margin: 0.125rem 0 0;
    color: var(--muted);
    font-size: 0.8125rem;
    line-height: 1.45
}

.vk li .copy {
    margin-top: 0.375rem
}

/* Die wirksame Vorlage - dieselbe Farbe wie jede andere bejahende Auskunft
   in der Oberflaeche. */
.tag.akt {
    background: var(--ok-bg);
    color: var(--ok)
}

/* Eine Vorlage, die daliegt und trotzdem nie zum Zug kommt, weil eine
   engere Stufe gewinnt. Der verwirrendste Fall ueberhaupt - wer ihn nicht
   sieht, sucht seine Aenderung in der falschen Datei. Warnfarben, kein
   Fehlerrot: es ist kein Fehler. */
.tag.verdeckt {
    background: var(--warn-bg);
    color: var(--warn)
}

/* ------------------------------------------------------- Quelltextschau
   Natives <dialog>: Escape, Fokusfalle und Verdunkelung kommen vom Browser. */
#qd {
    width: min(55rem, 94vw);
    /* max-height, nicht height: eine kurze Vorlage soll einen kleinen Kasten
       ergeben und nicht 86% des Bildschirms mit Leere fuellen. Zusammen mit
       der Flex-Spalte unten waechst der Kasten bis zu dieser Grenze und
       scrollt erst danach innen. */
    max-height: 86vh;
    padding: 0;
    border: none;
    border-radius: 0.75rem;
    background: var(--card);
    color: var(--ink);
    box-shadow: 0 18px 48px var(--md-schatten-hart);
    overflow: hidden
}

/* display AUSSCHLIESSLICH im geoeffneten Zustand setzen.
   Ein <dialog> ohne das open-Attribut ist per Voreinstellung des Browsers
   display:none. Ein pauschales "display:flex" auf #qd hebelt genau diese
   Voreinstellung aus - der Kasten stand dann dauerhaft unten auf der Seite
   herum, als halb abgeschnittenes Modal. Dieselbe Falle gilt fuer jedes
   Element mit hidden-Attribut, siehe die Schutzregel weiter unten.

   Spalte mit fester Kopfzeile und scrollendem Rest: ohne das waechst der
   Kasten mit dem Quelltext, und gescrollt wird dann die SEITE dahinter
   statt des Inhalts. */
#qd[open] {
    display: flex;
    flex-direction: column
}

/* Solange die Quelltextschau offen ist, steht die Seite dahinter still.
   <dialog> allein verhindert das Durchscrollen nicht - die Klasse setzt
   app.js beim Oeffnen und raeumt sie im close-Ereignis wieder weg (auch bei
   Escape, das nicht ueber den Schliessen-Knopf laeuft). */
html.modal-offen {
    overflow: hidden
}

#qd::backdrop {
    background: var(--md-abdunkler)
}

.qd-kopf {
    display: flex;
    align-items: center;
    gap: 0.8125rem;
    padding: 1rem 1.125rem;
    border-bottom: 1px solid var(--md-outline-variant)
}

.qd-kopf .ts {
    margin-left: auto
}

/* Mit Symbol im Kopf sitzt der Titel in einer eigenen Spalte, die Nebenzeile
   (der Layername) darunter - nicht als dritte Angabe daneben. */
.qd-titel {
    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
    min-width: 0
}

.qd-titel strong {
    font-size: 1.0625rem;
    font-weight: 600
}

.qd-titel .ts {
    margin-left: 0
}

.qd-kopf .mclose {
    margin-left: auto
}

/* Der Hilfe-Verweis steht zwischen Hinweis und Schliessen-Knopf -
   nicht ganz aussen, damit er nicht mit dem Schliessen verwechselt
   wird. */
.qd-kopf .qd-hilfe {
    flex: 0 0 auto
}

.qd-kopf {
    flex: 0 0 auto
}

#qdp {
    flex: 1 1 auto;
    /* Ohne min-height:0 waechst ein Flex-Element ueber seinen Anteil hinaus,
       statt zu scrollen - der klassische Grund, warum genau diese
       Konstruktion sonst nicht funktioniert. */
    min-height: 0;
    display: flex;
    overflow: hidden
}

/* Der Rohtext-Notnagel, falls CodeMirror einmal nicht geladen ist. Lieber
   ungefaerbt lesbar als ein leerer Kasten. */
.qd-roh {
    margin: 0;
    padding: 0.875rem 1rem;
    flex: 1 1 auto;
    overflow: auto;
    overscroll-behavior: contain;
    font-size: 0.7812rem;
    line-height: 1.55;
    white-space: pre;
    tab-size: 2
}

/* ------------------------------------------- CodeMirror in der Palette
   Die mitgelieferten Themes von CodeMirror bleiben aussen vor: Sie bringen
   ihre eigene Farbwelt mit und stuenden neben dem uebrigen Dashboard wie
   ein Fremdkoerper. Stattdessen genau die Handvoll Regeln, die noetig ist,
   und jede Farbe aus der bestehenden Palette - die ist auf Kontrast
   geprueft, eine neue waere es nicht. */
#qdp .CodeMirror {
    flex: 1 1 auto;
    height: auto;
    min-height: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.7812rem;
    line-height: 1.55;
    color: var(--ink);
    background: var(--card);
    tab-size: 2
}

#qdp .CodeMirror-scroll {
    overscroll-behavior: contain
}

.CodeMirror-gutters {
    background: var(--bg);
    border-right: 1px solid var(--line)
}

.CodeMirror-linenumber {
    color: var(--muted)
}

.CodeMirror-cursor {
    border-left: 2px solid var(--ink)
}

.CodeMirror-selected,
.CodeMirror-focused .CodeMirror-selected {
    background: var(--accent-soft)
}

/* Die aktuelle Zeile eine Spur getoent: der Ton der frueheren Reiterflaeche
   (#f0f6fa hell, #16232c dunkel), jetzt ueber die Primaerfarbe gemischt,
   damit er in beiden Darstellungen ohne eigene Variable stimmt. */
.CodeMirror-activeline-background {
    background: color-mix(in srgb, var(--md-primary) 6%, var(--md-surface))
}

/* Zusammengehoerige Tags: beide Enden gleichzeitig hervorgehoben. Bei einem
   SLD mit fuenf Verschachtelungsebenen ist das die schnellste Auskunft
   darueber, wo man sich befindet. */
.CodeMirror-matchingtag {
    background: var(--accent-soft);
    outline: 1px solid var(--brand)
}

.CodeMirror-foldmarker {
    color: var(--accent);
    text-shadow: none;
    font-family: inherit;
    line-height: 1
}

.CodeMirror-foldgutter-open:after,
.CodeMirror-foldgutter-folded:after {
    color: var(--muted)
}

/* Die Zeile, die der Pruefkette missfallen hat. GeoServers
   Schema-Validator nennt sie beim Namen ("line 12, column 34") - app.js
   zieht sie heraus und setzt diese Klasse. */
.cm-fehlerzeile {
    background: var(--err-bg)
}

/* ------------------------------------------------- Farben im Quelltext
   Zwei Familien, und der Unterschied ist die eigentliche Information:
   ALLES Freemarker (Direktiven, ${...}, <#-- -->) ist rot, alles XML/SLD
   blau. Damit sieht man einer Vorlage auf einen Blick an, was Schablone ist
   und was spaeter tatsaechlich ausgegeben wird. */
.cm-s-default .cm-tag,
#qdp .cm-tag { color: var(--accent) }
#qdp .cm-attribute { color: var(--warn) }
#qdp .cm-string { color: var(--ok) }
#qdp .cm-comment { color: var(--muted); font-style: italic }
#qdp .cm-meta { color: var(--muted) }
#qdp .cm-bracket { color: var(--muted) }
#qdp .cm-error { color: var(--err) }

/* Die drei aus dem Freemarker-Overlay (siehe FTL_OVERLAY in app.js).
   CodeMirror stellt den zurueckgegebenen Namen mit "cm-" voran. */
#qdp .cm-ftl-d { color: var(--err); font-weight: 600 }
#qdp .cm-ftl-a { color: var(--err) }
#qdp .cm-ftl-k { color: var(--muted); font-style: italic }

/* ------------------------------------------------ Ausgangspunkte (Vorlage)
   Ueber dem Editor, nicht darunter: Die Wahl des Ausgangspunkts kommt VOR
   dem Schreiben. Dieselben Knoepfe wie die Vorschlaege im Stilassistenten -
   es ist dieselbe Geste, nur fuer Vorlagen. */
.qd-bausteine {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4375rem;
    padding: 0.5625rem 1rem;
    border-bottom: 1px solid var(--line);
    background: var(--bg)
}

.qdb-t {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--muted)
}

.qd-hilfe {
    font-size: 0.7812rem;
    white-space: nowrap
}

.qd-hilfe::after {
    content: " \2197";
    text-decoration: none
}

/* ------------------------------------------------ Vorschau der Vorlage
   Unter dem Editor, nicht daneben: Der Quelltext ist breit (Zeilen bis
   100 Zeichen), die gerenderte Sachdatentabelle ist schmal und hoch.
   Nebeneinander bekaeme beides zu wenig Platz. Feste Hoehe, damit der
   Editor darueber nicht bei jeder Vorschau springt. */
.qd-vorschau {
    flex: 0 0 auto;
    border-top: 1px solid var(--line);
    background: var(--bg);
    display: flex;
    flex-direction: column;
    max-height: 40vh
}

.qd-vk {
    padding: 0.4375rem 1rem 0.3125rem;
    font-size: 0.75rem;
    color: var(--muted)
}

#qdvi {
    flex: 1 1 auto;
    width: 100%;
    min-height: 8.125rem;
    border: none;
    border-top: 1px solid var(--line);
    background: var(--card)
}

/* ---------------------------------------------------- Fuss des Editors
   Nur da, wenn auch bearbeitet wird - aus der Lade heraus ist der Dialog
   reine Schau und der Fuss bleibt versteckt (app.js setzt das Attribut). */
.qd-fuss {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    padding: 0.6875rem 1rem;
    border-top: 1px solid var(--line);
    background: var(--bg)
}

.qd-knoepfe {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.5rem
}

.qd-meld {
    margin: 0;
    font-size: 0.8125rem;
    min-height: 1em;
    /* Die Meldung des Validators kann lang werden. Sie darf den Kasten
       nicht sprengen, aber auch nicht die Knoepfe verdraengen. */
    flex: 1 1 16em;
    max-height: 4.6em;
    overflow: auto
}

.qd-meld.fehler { color: var(--err) }
.qd-meld.warte { color: var(--muted) }
.qd-meld.ok { color: var(--ok) }

/* --------------------------------------------------------- Legendenbild
   Kommt von GeoServer, nicht vom Assistenten. Steht deshalb in der
   Stil-Zeile und traegt eine eigene Beschriftung, damit die Herkunft klar
   ist. Schmal gehalten: bei den mitgelieferten Stilen ist es ein einzelnes
   Kaestchen, bei einer Klassifikation eine Liste - beides muss passen. */
.legende {
    margin: 0.625rem 0 0;
    display: inline-block;
    max-width: 100%;
    padding: 0.5625rem 0.6875rem;
    border: 1px solid var(--line);
    border-radius: 0.5rem;
    background: var(--bg)
}

/* Wie bei der Legende auf der Karte: GeoServer zeichnet sie in doppelter
   Groesse (dpi:180, siehe app.py: legenden_url), "zoom" halbiert sie fuers
   Layout. Ohne diese Zeile stand sie hier seit der Umstellung doppelt so
   gross wie zuvor - dasselbe Bild, nur ungehalbiert. */
.legende img {
    display: block;
    zoom: .5;
    max-width: 100%;
    height: auto
}

.legende figcaption {
    margin-top: 0.4375rem;
    font-size: 0.7188rem;
    color: var(--muted)
}

/* ------------------------------------------------------- Stilassistent (KI)
   Das ist das Verkaufsargument dieser Funktion und darf NICHT aussehen wie
   die uebrigen Hinweiskaesten der Seite - vorher war es einer, und damit war
   ausgerechnet das Wertvollste auf der Seite das Unscheinbarste.
   Eigene Karte mit gefuellter Kopfleiste statt blassem Kasten.

   Fuellfarbe ist --accent (#31749b, 5,12:1) und bewusst NICHT die CI-Farbe
   --brand (#3c8dbc): Weiss auf --brand erreicht nur 3,67:1 und reisst die
   BITV-Grenze fuer normalen Text. Hausregel im Dashboard: --brand fuer
   Flaechen ohne Text, --accent sobald Text darauf steht. */
.ki-karte {
    margin: 0.875rem 0 0.375rem;
    border: 1px solid var(--accent);
    border-radius: 0.625rem;
    overflow: hidden
}

.ki-kopf {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5625rem 0.875rem;
    background: var(--accent);
    color: var(--md-on-primary)
}

.ki-titel {
    font-weight: 600;
    font-size: 0.9062rem
}

/* Das Etikett sagt geradeheraus, was hier passiert - es soll man sehen. */
.ki-badge {
    margin-left: auto;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: .12em;
    padding: 0.1875rem 0.625rem;
    border-radius: 1.25rem;
    background: color-mix(in srgb, var(--md-on-primary) 22%, transparent);
    border: 1px solid color-mix(in srgb, var(--md-on-primary) 50%, transparent)
}

/* Am Eingabefeld des Assistenten steht sie auf hellem Grund und braucht
   deshalb die getoente Flaeche statt des weissen Schleiers, der fuer die
   frueher gefuellte Kopfleiste gedacht war. Kein margin-left:auto: sie sitzt
   direkt vor dem Feld, zu dem sie gehoert, und nicht am anderen Ende. */
.stpe .ki-badge {
    margin: 0 0 0.375rem 0;
    align-self: center;
    background: var(--md-primary-container);
    border-color: transparent;
    color: var(--md-on-primary-container)
}

.ki-inhalt {
    padding: 0.8125rem 0.9375rem
}

.ki-satz {
    margin: 0;
    font-size: 0.9688rem;
    line-height: 1.55;
    max-width: 72ch
}

.ki-liste {
    margin: 0.625rem 0 0;
    padding-left: 1.25rem;
    font-size: 0.875rem;
    line-height: 1.55;
    max-width: 72ch
}

.ki-liste li {
    margin: 0.1875rem 0
}

.ki-fuss {
    padding: 0.5625rem 0.9375rem;
    border-top: 1px solid var(--line);
    background: var(--bg);
    font-size: 0.7812rem;
    color: var(--muted)
}

.ki-warte {
    margin: 0;
    font-size: 0.8438rem;
    color: var(--muted)
}

/* Scheitert der Assistent, bleibt nur eine schmale Zeile uebrig - eine
   grosse beworbene Karte mit einer Fehlermeldung darin waere schlechte
   Werbung fuer genau die Funktion, die sie herausstellen soll. */
.ki-fehler {
    margin: 0.75rem 0 0.25rem;
    font-size: 0.8125rem;
    color: var(--muted)
}

/* ============================================================ Seitenmenue
   Eine dunkle Spalte links, dem touvia.geoCMS nachgebaut und nicht
   geschaetzt - alle Werte sind aus dem Bildschirmfoto vom 12.09.2026
   ausgemessen (doppelt aufgeloest, die Zahlen hier sind die Haelfte):
     Spaltenbreite     520 px -> 260 CSS-px (16,25rem)
                       seit 13.09.2026: 18,75rem (300 px) - Platz fuer
                       Beschriftungen mit Verb ("Benutzer verwalten")
     Eintrag hoch       82 px ->  41 CSS-px (2,5625rem) - Mindesthoehe
     Abstand dazwischen 14 px ->   7 CSS-px (0,4375rem)
     Einzug seitlich    16 px ->   8 CSS-px (0,5rem)
     Ecken               8 px ->   4 CSS-px
     Symbol ab          46 px ->  23 CSS-px vom Rand
     Schrift            ~15 CSS-px
     Flaeche            #222d32   Text #b8c7ce
     aktiv              #3c8dbc   (= CI-Farbe), Schrift weiss
     Unterpunkte        #2c3b41   Text #8aa4af, halb so hoch

   Warum links und nicht mehr Reiter oben: Die Konsole ist eine
   eigenstaendige Anwendung im eigenen Fenster (entschieden am 12.09.2026);
   die Reiter kamen von der Idee, sie in geoCMS einzubetten, wo links schon
   ein Menue steht. Die Form des geoCMS-Menues bleibt, damit sich
   Bestandskunden zurechtfinden.

   Bis zum Einklappen (Phase 2) und dem Ausfahren auf dem Telefon (Phase 3)
   ist das Menue unter 992 px eine umbrechende Zeile ueber dem Inhalt -
   besser als neun Zeilen Menue vor der ersten Tabelle. */
.seitenmenue {
    background: var(--menue-flaeche);
    color: var(--menue-text)
}

/* Die Liste klebt beim Rollen am oberen Rand und rollt bei Bedarf in sich
   selbst, waehrend die dunkle Flaeche (das <nav>) mit der Seite mitwaechst. */
.sm-liste {
    list-style: none;
    margin: 0;
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.4375rem;
    position: sticky;
    top: 0;
    max-height: 100vh;
    overflow-y: auto
}

.sm-liste li {
    margin: 0
}

.sm-eintrag {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 2.5625rem;
    padding: 0.4375rem 0.75rem 0.4375rem 0.9375rem;
    border-radius: 0.25rem;
    color: inherit;
    text-decoration: none;
    font: 500 0.9375rem var(--font);
    white-space: nowrap;
    transition: background-color var(--md-kurz) var(--md-kurve),
                color var(--md-kurz) var(--md-kurve)
}

.sm-eintrag .ic {
    width: 1.125rem;
    height: 1.125rem;
    opacity: .9
}

.sm-text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis
}

a.sm-eintrag:hover,
button.sm-eintrag:hover {
    background: var(--menue-zeigen);
    color: #fff
}

/* Der Klappknopf ueber einem Untermenue (menue.py: "unter") ist ein echter
   <button>: keine Seite, nur Auf- und Zuklappen. Er sieht aus wie die
   Links daneben; der Pfeil rechts zeigt den Zustand und dreht sich beim
   Oeffnen nach unten. */
button.sm-eintrag {
    width: 100%;
    background: none;
    border: 0;
    color: inherit;
    text-align: left;
    cursor: pointer
}

.sm-pfeil {
    margin-left: auto;
    width: 1rem;
    height: 1rem;
    opacity: .7;
    transition: transform var(--md-kurz) var(--md-kurve)
}

button.sm-eintrag[aria-expanded="true"] .sm-pfeil {
    transform: rotate(90deg)
}

/* Ein Link nach draussen (menue.py: "url") traegt rechts das Symbol dafuer -
   der Kunde soll sehen, dass ein neuer Tab aufgeht. */
.sm-hinaus {
    margin-left: auto;
    width: 0.875rem;
    height: 0.875rem;
    opacity: .6
}

/* Der aktive Punkt: blaue Kapsel, weisse Schrift - so im geoCMS. Ueber
   aria-current statt einer eigenen Klasse, damit das, was Vorleseprogramme
   als "aktuelle Seite" melden, und das, was man sieht, dieselbe Quelle hat. */
.sm-eintrag[aria-current="page"] {
    background: var(--menue-aktiv);
    color: #fff;
    font-weight: 600
}

.sm-eintrag[aria-current="page"] .ic {
    opacity: 1
}

/* Der Klappknopf, wenn eines seiner Kinder die offene Seite ist (app.js
   setzt data-offen): leicht aufgehellt, damit der Weg "Verwaltung >
   Benutzer verwalten" auch im Menue zu sehen ist, nicht nur in den
   Brotkrumen. */
.sm-eltern > .sm-eintrag[data-offen] {
    background: var(--menue-zeigen)
}

/* Weiss statt --md-primary: auf der dunklen Flaeche waere das Blau der
   hellen Darstellung kaum zu sehen. */
.sm-eintrag:focus-visible {
    outline: 3px solid #fff;
    outline-offset: -3px
}

/* Unterpunkte (menue.py: "unter"): auf der etwas helleren Flaeche unter
   dem Klappknopf, wie im geoCMS, eingerueckt und mit derselben Hoehe. Zu
   ist die Liste ueber das hidden-Attribut (app.js: untermenue). */
.sm-unter {
    list-style: none;
    margin: 0.25rem -0.5rem 0;
    padding: 0.25rem 0.5rem;
    background: var(--menue-unter);
    display: flex;
    flex-direction: column;
    gap: 0.125rem
}

/* Waehrend der Fahrt (app.js: untermenue setzt die Klasse fuer die Dauer
   der Animation): die Eintraege duerfen nicht unter der schrumpfenden
   Liste herausschauen. Nur waehrenddessen, nicht immer - sonst schnitte die
   Liste den Fokusring ihrer Eintraege an. */
.sm-unter.sm-fahrt {
    overflow: hidden
}

.sm-unter .sm-eintrag {
    padding-left: 2rem;
    font-size: 0.875rem
}

/* Marke am Menuepunkt: die Zahl rechts, in einer vollen Kapsel (Materials
   Badge). Auf der dunklen Flaeche eine Aufhellung statt einer eigenen
   Farbe; die Warnfarben bleiben, sie sind auch dort lesbar. */
.tm {
    font-size: 0.75rem;
    font-weight: 600;
    min-width: 1.25rem;
    text-align: center;
    padding: 0.0625rem 0.4375rem;
    border-radius: var(--md-form-voll);
    background: var(--md-surface-container-hoch);
    color: var(--md-on-surface);
    font-variant-numeric: tabular-nums
}

.sm-eintrag .tm {
    margin-left: auto;
    background: rgba(255, 255, 255, .16);
    color: #fff
}

.sm-eintrag[aria-current="page"] .tm {
    background: rgba(255, 255, 255, .28)
}

.tm.warn,
.sm-eintrag .tm.warn {
    background: var(--warn-bg);
    color: var(--warn)
}

/* Eingeklappt (html[data-menue="mini"], siehe menueKlappen in app.js):
   nur noch die Symbole auf 4,6rem - AdminLTEs "sidebar-mini". Zeigen oder
   Tastaturfokus fahren die Liste voruebergehend auf volle Breite aus, UEBER
   den Inhalt statt ihn zu schieben (deshalb die eigene Flaeche und der
   Schatten an der Liste, die sonst das <nav> traegt). Die Marke wird zum
   Punkt in der Ecke: eine Zahl ohne Beschriftung sagt nichts, ein Punkt
   sagt "hier ist etwas". Nur oberhalb 992 px - darunter ist das Menue eine
   Zeile, und die klappt nicht. z-index 15: ueber dem Inhalt, unter den
   Dialogen (.ov: 20). */
@media (min-width: 993px) {
    .rahmen {
        transition: grid-template-columns var(--md-kurz) var(--md-kurve)
    }

    html[data-menue="mini"] .rahmen {
        grid-template-columns: 4.6rem minmax(0, 1fr)
    }

    html[data-menue="mini"] .sm-liste {
        width: 4.6rem;
        transition: width var(--md-kurz) var(--md-kurve)
    }

    html[data-menue="mini"] .sm-eintrag {
        position: relative
    }

    html[data-menue="mini"] .seitenmenue:not(:hover):not(:focus-within) .sm-text,
    html[data-menue="mini"] .seitenmenue:not(:hover):not(:focus-within) .sm-pfeil,
    html[data-menue="mini"] .seitenmenue:not(:hover):not(:focus-within) .sm-unter {
        display: none
    }

    html[data-menue="mini"] .seitenmenue:not(:hover):not(:focus-within) .sm-eintrag .tm {
        position: absolute;
        top: 0.4375rem;
        right: 0.4375rem;
        width: 0.5rem;
        height: 0.5rem;
        min-width: 0;
        padding: 0;
        border-radius: 50%;
        font-size: 0;
        background: #fff
    }

    html[data-menue="mini"] .seitenmenue:not(:hover):not(:focus-within) .sm-eintrag .tm.warn {
        background: var(--warn)
    }

    html[data-menue="mini"] .seitenmenue:hover .sm-liste,
    html[data-menue="mini"] .seitenmenue:focus-within .sm-liste {
        width: 18.75rem;
        background: var(--menue-flaeche);
        box-shadow: 0.25rem 0 1rem var(--md-schatten-mittel);
        z-index: 15
    }
}

/* Unter 992 px (die Grenze von AdminLTE): eine Spalte, das Menue als
   Schublade, die von links ueber die Seite faehrt (body.menue-offen, siehe
   schublade() in app.js). Ueber die volle Hoehe, auch ueber den Kopf - so
   macht es AdminLTE, und so bleibt die Schublade gleich breit wie die
   Spalte am Schreibtisch. Dahinter der Schleier aus dem Bottom-Sheet-Muster
   (body.menue-sheet weiter oben), derselbe Ton, eine Stufe darunter.

   visibility mit Verzoegerung: geschlossen soll die Schublade weder zu
   sehen noch per Tabulator erreichbar sein - aber erst, wenn sie zu Ende
   gefahren ist, sonst verschwindet sie beim Schliessen schlagartig. */
@media (max-width: 992px) {
    .rahmen {
        grid-template-columns: minmax(0, 1fr);
        min-height: 0
    }

    .seitenmenue {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        width: 18.75rem;
        max-width: 85vw;
        overflow-y: auto;
        z-index: 23;
        transform: translateX(-100%);
        visibility: hidden;
        transition: transform var(--md-kurz) var(--md-kurve),
                    visibility 0s linear var(--md-kurz)
    }

    body.menue-offen .seitenmenue {
        transform: none;
        visibility: visible;
        transition-delay: 0s;
        box-shadow: 0.25rem 0 1.5rem var(--md-schatten-mittel)
    }

    body.menue-offen {
        overflow: hidden
    }

    body.menue-offen::before {
        content: "";
        position: fixed;
        inset: 0;
        z-index: 22;
        background: var(--md-abdunkler-leicht)
    }

    /* Die Liste klebt nicht mehr - die Schublade selbst rollt. */
    .sm-liste {
        position: static;
        max-height: none;
        overflow: visible
    }
}

/* Der Inhaltsbereich ist weiss, die Blaetter liegen darin. Bewusst KEIN
   display hier und auch nicht am Rahmen darum: .blatt wird ueber das
   hidden-Attribut versteckt, und ein display an dieser Stelle wuerde genau
   das aushebeln (siehe die Schutzregel ganz oben). */
.inhalt {
    background: var(--md-surface);
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-md)
}

.blatt {
    padding: 1.375rem 1.5rem
}

/* Weisse Karten auf weissem Grund brauchen einen deutlicheren Rand, sonst
   verschwinden sie im Inhaltsbereich. */
.blatt .card {
    border-color: var(--md-outline-karte)
}

/* Das erste <h2> eines Blattes braucht keinen Abstand nach oben - der
   Innenabstand des Blattes setzt ihn schon. */
.blatt > h2:first-child,
.handbuch > h2:first-child {
    margin-top: 0
}

/* Das Kundenhandbuch im Bereich "Hilfe": aus Markdown erzeugter Fliesstext
   (app.py: hilfe_html). Die Grundregeln der Seite gelten weiter - h2, table,
   code -, hier kommt nur dazu, was ein laengerer Text braucht: Zeilenlaenge,
   Absatzabstaende, Listen, Zitate, Codebloecke. Keine eigene Zeilenlaenge:
   die Breite der Inhaltsspalte (.wrap) reicht als Mass, wie ueberall. */
.handbuch {
    line-height: 1.6
}

/* Lange Adressen als Link (<https://docs.geoserver.org/...> in Abschnitt
   11) duerfen ueberall brechen: ohne das war das Blatt "Hilfe" auf dem
   Telefon 629 statt 390 px breit und rollte seitlich (gemessen am
   13.09.2026). Nur in Fliesstext und Listen - in Tabellen rollt der Rahmen. */
.handbuch p a,
.handbuch li a {
    overflow-wrap: anywhere
}

/* Ueberschriften groesser und in der CI-Farbe: Ein Handbuch liest man
   nicht wie eine Layerliste, man springt darin - die Kapitelanfaenge
   muessen beim Rollen ins Auge fallen. Deshalb auch die Linie unter den
   Kapiteln (h3 = "## " im Markdown) und der grosse Abstand davor. Die
   Farbe ist --md-primary, nicht --md-brand: das kraeftigere Blau haelt auf
   Weiss 5,1:1 und liest sich damit auch in Fliesstextgroesse. */
.handbuch h2 {
    font-size: 1.75rem;
    line-height: 1.25;
    font-weight: 600;
    color: var(--md-primary);
    margin: 0 0 1rem
}

.handbuch h3 {
    font-size: 1.375rem;
    line-height: 1.3;
    font-weight: 600;
    color: var(--md-primary);
    margin: 3rem 0 1rem;
    padding-bottom: 0.375rem;
    border-bottom: 2px solid var(--md-primary-container)
}

.handbuch h4 {
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--ink);
    margin: 2rem 0 0.5rem
}

/* Der Vorspann direkt unter dem Titel, etwas groesser und leiser -
   damit die Seite nicht mit einer Wand aus gleichem Text beginnt. */
.handbuch > h2:first-child + details + p,
.handbuch > h2:first-child + p {
    font-size: 1.0625rem;
    color: var(--muted)
}

.handbuch p,
.handbuch ul,
.handbuch ol {
    margin: 0 0 0.875rem
}

.handbuch ul,
.handbuch ol {
    padding-left: 1.5rem
}

.handbuch li {
    margin: 0.25rem 0
}

.handbuch li > ul,
.handbuch li > ol {
    margin-bottom: 0.25rem
}

.handbuch blockquote {
    margin: 0 0 0.875rem;
    padding: 0.5rem 1rem;
    border-left: 3px solid var(--md-primary);
    background: var(--md-primary-weich);
    border-radius: 0 0.375rem 0.375rem 0
}

.handbuch blockquote p:last-child {
    margin-bottom: 0
}

.handbuch pre {
    margin: 0 0 0.875rem;
    padding: 0.75rem 1rem;
    background: var(--md-surface-container);
    border: 1px solid var(--line);
    border-radius: 0.375rem;
    overflow-x: auto
}

/* Der Block traegt den Hintergrund, nicht die Zeilen darin. */
.handbuch pre code {
    background: none;
    padding: 0;
    word-break: normal
}

.handbuch hr {
    border: none;
    border-top: 1px solid var(--line);
    margin: 1.5rem 0
}

/* Tabellen: die Adress-Tabellen sind breiter als ein Telefon. Der Rahmen
   rollt seitlich, die Seite nicht. */
.handbuch .scroll-x {
    overflow-x: auto;
    margin: 0 0 0.875rem;
    border: 1px solid var(--md-outline-karte);
    border-radius: 0.5rem
}

.handbuch .scroll-x table {
    margin: 0
}

/* Woerter und Adressen in Tabellenzellen am Stueck: die globalen Regeln
   (td: break-word, code: break-all) sind fuer die Layerlisten gedacht, in
   denen ein langer Tabellenname die Spalte nicht sprengen darf. Hier
   brachen damit "GeoServer" und "Konsole" in der schmalen ersten Spalte zu
   "GeoSer/ver", weil sich die Adress-Spalte beliebig zusammendruecken
   liess. Lieber breiter, und der Rahmen rollt seitlich. */
.handbuch td,
.handbuch th {
    word-break: normal;
    /* Und eine Mindestbreite: sonst quetscht der Browser auf dem Telefon die
       letzte Spalte auf drei Woerter je Zeile zusammen, um die Tabelle in den
       Rahmen zu zwingen - jede Zeile wird turmhoch. Mit Mindestbreite wird
       die Tabelle breiter als der Rahmen, und der rollt (scroll-x). */
    min-width: 9rem
}

/* Die letzte Spalte ist die erklaerende - ihr etwas mehr, damit auf dem
   Telefon nicht jede Zeile zum Turm wird. */
.handbuch td:last-child,
.handbuch th:last-child {
    min-width: 14rem
}

.handbuch td code {
    white-space: nowrap;
    word-break: normal
}

/* Tabellenkoepfe im Handbuch sind Woerter, keine Spaltenmarken - die
   Versalien der Layerlisten passen hier nicht. */
.handbuch th {
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.875rem;
    color: var(--ink)
}

/* ---- Leiste, Suche, Kapitel (Plan "Das Handbuch zum Nachschlagen",
   13.09.2026) ----------------------------------------------------------
   Ab 993 px steht die Leiste links neben dem Text und bleibt beim Rollen
   stehen (sticky); darunter steht sie ueber dem Text, das Suchfeld sichtbar,
   die Kapitelliste als zugeklapptes <details> - app.js setzt "open" nach der
   Breite. Dieselbe Grenze wie das Seitenmenue (992 px). */
.hb {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.25rem
}

@media (min-width: 993px) {
    .hb {
        grid-template-columns: 14rem minmax(0, 1fr);
        gap: 2.5rem
    }

    .hb-leiste {
        position: sticky;
        top: 1rem;
        align-self: start;
        max-height: calc(100vh - 2rem);
        overflow-y: auto;
        padding-right: 0.25rem
    }

    .hb-kapitel > summary {
        display: none
    }
}

.hb-suche {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.625rem;
    border: 1px solid var(--md-outline);
    border-radius: 0.5rem;
    background: var(--md-surface-container)
}

.hb-suche:focus-within {
    border-color: var(--md-primary);
    box-shadow: 0 0 0 2px var(--md-primary-container)
}

.hb-suche .ic {
    flex: none;
    color: var(--muted)
}

.hb-suche input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    outline: none
}

/* Der Browser-eigene Loeschknopf weicht dem eigenen, der auch die
   Hervorhebung zuruecknimmt. */
.hb-suche input::-webkit-search-cancel-button {
    display: none
}

.hb-leeren {
    flex: none;
    display: inline-flex;
    border: 0;
    background: none;
    padding: 0.125rem;
    color: var(--muted);
    cursor: pointer;
    border-radius: 0.25rem
}

.hb-leeren:hover {
    color: var(--ink)
}

.hb-leeren:focus-visible {
    outline: 2px solid var(--md-primary)
}

.hb-treffer {
    margin: 0.5rem 0 0;
    font-size: 0.8125rem;
    color: var(--muted)
}

.hb-kapitel {
    margin-top: 0.75rem
}

.hb-kapitel > summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--md-primary);
    padding: 0.375rem 0
}

.hb-kapitel nav ul {
    list-style: none;
    margin: 0;
    padding: 0
}

.hb-kapitel nav ul ul {
    padding-left: 0.875rem
}

.hb-kapitel nav li {
    margin: 0
}

.hb-kapitel nav a {
    display: block;
    padding: 0.25rem 0.5rem;
    border-left: 3px solid transparent;
    border-radius: 0 0.25rem 0.25rem 0;
    color: var(--ink);
    text-decoration: none;
    font-size: 0.875rem;
    line-height: 1.35
}

.hb-kapitel nav ul ul a {
    font-size: 0.8125rem;
    color: var(--muted)
}

.hb-kapitel nav a:hover {
    background: var(--md-surface-container)
}

/* Das Kapitel, das gerade im Bild ist (app.js: hbSpion). aria-current statt
   Klasse, wie im Seitenmenue. */
.hb-kapitel nav a[aria-current="true"] {
    border-left-color: var(--md-primary);
    color: var(--md-primary);
    background: var(--md-primary-weich);
    font-weight: 600
}

/* Suchtreffer: Farbe auf dem Wort, Text bleibt, wie er ist. Der erste
   Treffer nach Enter bekommt zusaetzlich den Rahmen. */
mark.hb-mark {
    background: var(--hb-treffer);
    color: inherit;
    padding: 0 0.05em;
    border-radius: 2px
}

mark.hb-mark.aktiv {
    outline: 2px solid var(--md-primary);
    outline-offset: 1px
}

/* Vor/zurueck am Kapitelende (app.py: kapitel_gliedern). */
.hb-weiter {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    margin: 2rem 0 0;
    padding-top: 0.75rem;
    border-top: 1px solid var(--line);
    font-size: 0.875rem
}

.hb-weiter a {
    text-decoration: none
}

.hb-weiter a:hover {
    text-decoration: underline
}

.hb-weiter .hb-zurueck::before {
    content: "\2190\00a0"
}

.hb-weiter .hb-vor {
    text-align: right
}

.hb-weiter .hb-vor::after {
    content: "\00a0\2192"
}

.hb-weiter .hb-oben {
    color: var(--muted);
    white-space: nowrap
}

/* Der schwebende Knopf "Nach oben" unten rechts (Materials kleiner FAB:
   40 px, Radius 12, Erhebung 3, Primaerfarbe). Er liegt im Blatt "Hilfe"
   und verschwindet mit ihm ([hidden] am Blatt); app.js (hbSpion) zeigt ihn
   per Klasse "an", sobald der Anfang aus dem Bild gerollt ist. Zustand
   ueber Klasse statt [hidden], damit Ein- und Ausblenden fahren koennen.
   z-index 9 wie die Snackbar: ueber dem Inhalt, unter der Hilfe-Lade (15)
   und unter der Schublade des Seitenmenues (23). "button.hb-hoch", nicht
   ".hb-hoch": die Masse von button.ikon-nur (das die Beschriftung
   versteckt) sollen hier nicht gewinnen. */
button.hb-hoch {
    position: fixed;
    right: 1.5rem;
    bottom: 1.5rem;
    z-index: 9;
    width: 2.5rem;
    min-width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    gap: 0;
    border: 0;
    border-radius: var(--md-form-md);
    background: var(--md-primary);
    color: var(--md-on-primary);
    box-shadow: var(--md-erhebung-3);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.5rem);
    transition: opacity var(--md-kurz) var(--md-kurve),
                transform var(--md-kurz) var(--md-kurve),
                visibility 0s linear var(--md-kurz)
}

button.hb-hoch.an {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s
}

button.hb-hoch:hover {
    background: var(--md-brand)
}

button.hb-hoch:focus-visible {
    outline: 3px solid var(--md-on-surface);
    outline-offset: 2px
}

/* Der Anfang des Handbuchs bekommt nach "Nach oben" den Fokus (app.js:
   hbNachOben) - ein Ring um das ganze Blatt waere dabei nur Laerm. */
#hb-anfang:focus {
    outline: none
}

/* Der Kapitelwechsel traegt in der Mitte "Zum Anfang" und "Frage zu diesem
   Kapitel" (mailto an den Support, app.py: frage_adresse). */
.hb-mitte {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem 1rem;
    justify-content: center;
    text-align: center
}

.hb-frage {
    color: var(--muted)
}

/* Spruenge aus dem Handbuch in die Konsole: [Daten-Upload oeffnen](#daten)
   in der Vorlage, hier als Knopf (app.py: spruenge_markieren). Ein Link
   bleibt es - zeigeReiter oeffnet das Blatt ueber den Anker. */
.hb-sprung {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    margin: 0 0.25rem 0.25rem 0;
    border: 1px solid var(--md-primary);
    border-radius: var(--md-form-sm);
    color: var(--md-primary);
    font-weight: 600;
    font-size: 0.875rem;
    text-decoration: none;
    line-height: 1.3
}

.hb-sprung::after {
    content: "\2192"
}

.hb-sprung:hover {
    background: var(--md-primary-weich)
}

.hb-sprung:focus-visible {
    outline: 2px solid var(--md-primary);
    outline-offset: 2px
}

/* Adressen mit Kopierknopf (app.py: adressen_kopierbar): der Knopf haengt
   dicht am Code, wie unter "Dienste einbinden". */
.hb-adr {
    display: inline-flex;
    align-items: center;
    gap: 0.125rem;
    max-width: 100%;
    vertical-align: middle
}

.hb-adr code {
    min-width: 0
}

.hb-kopie {
    flex: none
}

/* Begriffe aus dem Kapitel "Begriffe" (app.py: begriffe_markieren): das
   erste Vorkommen je Kapitel erklaert sich beim Zeigen. Gepunktet, nicht
   wie ein Link - es fuehrt nirgendwohin. */
abbr.hb-begriff {
    text-decoration: underline dotted var(--md-primary);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
    cursor: help
}

/* Das Fragezeichen rechts in der Brotkrumenzeile und im Ladenkopf. */
.pfad-hilfe {
    margin-left: auto
}

.lade-hilfe {
    flex: 0 0 auto
}

/* Die Hilfe-Lade (app.py: hilfe_lade_html, app.js: hilfeOeffnen): fest am
   rechten Rand ueber der Seite, unter den nativen Dialogen und der
   Vorschau (z 20), ueber den Zeilenmenues. Nur ab 993 px - darunter springt
   das Fragezeichen ins Blatt "Hilfe". */
.hb-lade {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(27rem, 92vw);
    display: flex;
    flex-direction: column;
    padding: 1rem 1.25rem 1rem;
    background: var(--md-surface);
    border-left: 1px solid var(--md-outline-variant);
    box-shadow: -8px 0 24px rgba(0, 0, 0, 0.14);
    z-index: 15
}

.hb-lade .lade-kopf {
    flex: none
}

.hb-lade-nr {
    font-weight: 400;
    color: var(--muted)
}

.hb-lade-inhalt {
    flex: 1 1 auto;
    overflow-y: auto;
    padding-right: 0.25rem;
    font-size: 0.9375rem
}

.hb-lade-inhalt h3,
.hb-lade-inhalt h4 {
    margin-top: 0.25rem;
    font-size: 1.125rem;
    border: 0;
    padding: 0
}

.hb-lade-fuss {
    flex: none;
    margin: 0;
    padding-top: 0.75rem;
    border-top: 1px solid var(--line);
    font-size: 0.875rem
}

/* Bilder des Handbuchs (app.py: markdown_html, Route /hilfe/bild/): mit
   Rand und Unterschrift, beim Klick gross im Dialog (app.js). */
.hb-figur {
    margin: 1rem 0 1.25rem
}

/* max-width auf JEDEM Bild des Handbuchs, nicht nur in der <figure>: Ein
   Bild in einem Listenpunkt bekommt keinen Rahmen, war aber 2044 px breit
   und schob die ganze Seite auf 2653 px (gesehen am 13.09.2026, Bill:
   "ragen rechts raus"). */
.handbuch img,
.hb-lade-inhalt img {
    max-width: 100%;
    height: auto
}

.hb-figur img,
.hb-lade-inhalt img.hb-bild,
.handbuch img.hb-bild {
    display: block;
    max-width: 100%;
    height: auto;
    border: 1px solid var(--md-outline-karte);
    border-radius: 0.5rem;
    cursor: zoom-in
}

.hb-figur figcaption {
    margin-top: 0.375rem;
    font-size: 0.8125rem;
    color: var(--muted)
}

.hb-bild-dialog {
    padding: 0;
    border: 0;
    background: transparent;
    max-width: 96vw;
    max-height: 96vh;
    overflow: visible
}

.hb-bild-dialog::backdrop {
    background: rgba(0, 0, 0, 0.6)
}

.hb-bild-dialog img {
    display: block;
    max-width: 96vw;
    max-height: 92vh;
    border-radius: 0.5rem;
    background: var(--md-surface)
}

.hb-bild-zu {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    background: var(--md-surface)
}

/* Das "#" an jeder Ueberschrift: die Adresse der Stelle, zum Weitergeben
   ("schauen Sie in Abschnitt 6.4" wird ein Link). Erst beim Zeigen oder per
   Tastatur sichtbar - im Lesefluss stoert es sonst. */
.hb-anker {
    margin-left: 0.375rem;
    font-weight: 400;
    font-size: 0.875em;
    color: var(--muted);
    text-decoration: none;
    opacity: 0
}

.handbuch h3:hover > .hb-anker,
.handbuch h4:hover > .hb-anker,
.hb-anker:focus-visible {
    opacity: 1
}

/* Ausdruck: "darf ausgedruckt werden" steht im Handbuch - dann ohne
   Seitenkopf, Menue, Leiste und Kapitelwechsel, jedes Kapitel auf einer
   neuen Seite, aeussere Links mit ihrer Adresse. Kopf und Menue gehen bei
   jedem Ausdruck der Konsole weg; die dunkle Spalte will niemand auf Papier. */
@media print {
    header,
    .seitenmenue,
    .pfad,
    .hb-leiste,
    .hb-weiter,
    .hb-anker,
    .hb-sprung,
    .hb-kopie,
    .hb-lade,
    .pfad-hilfe,
    .handbuch-stand {
        display: none !important
    }

    .rahmen,
    .hb {
        display: block
    }

    .inhalt {
        border: 0
    }

    .hb-kap + .hb-kap {
        break-before: page
    }

    .handbuch a[href^="http"]::after {
        content: " (" attr(href) ")";
        font-size: 0.85em;
        color: var(--muted)
    }

    mark.hb-mark {
        background: none;
        font-weight: 600
    }
}

.handbuch-stand {
    margin: 1.5rem 0 0;
    font-size: 0.875rem;
    color: var(--muted)
}

/* Ohne markdown-Paket (nur ausserhalb des Images): der Quelltext, aber
   lesbar. */
.handbuch-roh {
    white-space: pre-wrap;
    font: 0.875rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace
}

/* Die Kennzahlen im Blatt "Uebersicht" fuehren in den Bereich, um den es
   geht - als Knopf, nicht als Kaestchen. */
.kpi-karte {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    width: 100%;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: background-color var(--md-kurz) var(--md-kurve),
                border-color var(--md-kurz) var(--md-kurve),
                box-shadow var(--md-kurz) var(--md-kurve)
}

.kpi-txt {
    min-width: 0
}

/* Sie sind Knoepfe und sollen sich auch so anfuehlen: Zustandsflaeche und
   eine Stufe Erhebung beim Zeigen. */
.kpi-karte:hover {
    border-color: var(--md-outline);
    background: color-mix(in srgb, var(--md-primary) 5%, var(--md-surface));
    box-shadow: var(--md-erhebung-1)
}

/* Die Fehlerkarte traegt ihre Aussage in der Farbe, sobald es etwas zu
   melden gibt - eine 0 in Rot waere Alarm ohne Anlass. */
.kpi-karte.warn .ic-kreis {
    background: var(--md-error-container);
    color: var(--md-error)
}


/* Die kleinen Dialoge: Spaltenauswahl fuer die Vektor-Tiles (#td),
   Dienste-Freigabe je Layer (#fd), Zugang je Layer (#zd), Passwort aendern
   (#pd), Zugaenge der Konsole und angemeldete Geraete (#bd), weiteren Stil
   anlegen oder umbenennen (#sd). Derselbe Rahmen
   wie die Quelltextschau (#qd), nur mit einer Liste oder Feldern statt eines
   Editors. #bd traegt zwei Aufgaben, wie #zd alles rund um den Zugang je
   Layer traegt: Titel und Fuss setzt app.js je Schritt.
   WICHTIG: Ein <dialog> ohne eigene Regel bekommt den Browser-Rahmen -
   schwarzer Strich, eckige Ecken, weisser Grund. Ein neuer Dialog gehoert
   deshalb in diese Aufzaehlung, sonst faellt er sichtbar aus der Reihe.
   Genau das war #zd passiert: Der Zugangs-Dialog stand seit dem 10.09.2026
   mit schwarzem Kasten in der Seite und ist am 11.09.2026 mit
   hereingenommen worden - und #sd am 13.09.2026 gleich noch einmal. */
/* Materials Basisdialog: Radius 28 - die groesste Stufe der Formenskala.
   Ein Dialog ist die eine Flaeche, die deutlich ueber allem anderen liegt;
   die Ecken sagen das mit. */
#td,
#fd,
#zd,
#pd,
#gd,
#bd,
#sd {
    width: min(35rem, 94vw);
    max-height: 86vh;
    padding: 0;
    border: none;
    border-radius: var(--md-form-xl);
    background: var(--md-surface);
    color: var(--md-on-surface);
    box-shadow: var(--md-erhebung-3);
    overflow: hidden
}

/* Etwas breiter: hier steht zu jedem Kaestchen ein ganzer Satz, in der
   Spaltenauswahl nur ein Spaltenname. */
#fd,
#gd {
    width: min(42.5rem, 94vw)
}

#td[open],
#fd[open],
#zd[open],
#pd[open],
#gd[open],
#bd[open],
#sd[open] {
    display: flex;
    flex-direction: column
}

#td::backdrop,
#fd::backdrop,
#zd::backdrop,
#pd::backdrop,
#gd::backdrop,
#bd::backdrop,
#sd::backdrop {
    background: var(--md-abdunkler)
}

.td-rumpf {
    padding: 0.75rem 1rem;
    overflow: auto;
    flex: 1 1 auto
}

/* .ts ist sonst einzeilig (Nebenzeile beim Layernamen) - der Erklaertext im
   Dialog soll umbrechen. */
.td-rumpf p.ts {
    white-space: normal;
    margin: 0 0 0.375rem
}

.td-alle {
    display: flex;
    gap: 0.5rem;
    margin: 0.5rem 0
}

.td-liste {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.25rem 1rem
}

.td-spalte {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.1875rem 0;
    cursor: pointer;
    min-width: 0
}

.td-spalte code {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap
}

/* Dienste-Freigabe: EINE Spalte mit Erklaerung unter dem Namen. Die
   Spaltenauswahl daneben hat zwei schmale Spalten, weil dort je ein Wort
   steht - hier gehoert zu jedem Haekchen ein ganzer Satz. */
.fd-liste {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.75rem
}

.fd-dienst {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.125rem 0.5rem;
    align-items: start;
    cursor: pointer
}

.fd-dienst input {
    margin-top: 0.1875rem
}

.fd-dienst .ts {
    grid-column: 2;
    white-space: normal
}

/* Ohne Karte gibt es keine Objektinfo - das Kaestchen wird dann gesperrt und
   soll auch so aussehen. */
.fd-dienst input:disabled,
.fd-dienst:has(input:disabled) {
    cursor: default;
    opacity: .55
}

.td-fuss {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    padding: 0.875rem 1.125rem;
    border-top: 1px solid var(--md-outline-variant);
    flex: 0 0 auto
}

/* Materials Basisdialog: die Knoepfe unten rechts, der abweisende links vom
   bestaetigenden. Sie tragen hier den Rueckweg - deshalb hat die Kopfzeile
   dieser Dialoge kein X (anders als Vorschau, Stileditor und
   Quelltextschau, wo unten nichts steht). */
.td-akt {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-left: auto
}

@media (max-width: 600px) {
    .td-liste {
        grid-template-columns: 1fr
    }
}


/* Die Bedienelemente der Karte kommen aus maplibre-gl.css und sind dort in
   festen Pixeln bemessen - Zoomknoepfe, Massstabsbalken, Quellenangabe. Auf
   einem 4K-Schirm blieben sie als einzige winzig, waehrend alles um sie herum
   mitgewachsen ist.
   "zoom" je Bedienelement, mit Ausnahme des Massstabsbalkens: es skaliert
   Layout UND Klickflaeche und trifft auch die Hintergrundbilder der Symbole, an
   die man sonst einzeln heranmuesste. Der Faktor ist die Grundschrift geteilt
   durch ihren Ausgangswert - bei 16px kommt 1 heraus, es aendert sich also
   unterhalb von 1600px nichts. */
.maplibregl-ctrl:not(.maplibregl-ctrl-scale) {
    zoom: calc(1rem / 16px)
}

/* Der Balken misst Kartenpixel. Ihn mitzuskalieren verlaengert die gezeigte
   Strecke, ohne seine Meterangabe anzupassen (09.09.2026). Nur seine Schrift
   waechst mit; die von MapLibre berechnete Breite bleibt unangetastet. */
.maplibregl-ctrl-scale {
    font-size: 0.625rem;
    padding: 0 0.3125rem
}

/* ------------------------------------ MapLibres Bedienelemente einfaerben
   Die Bibliothek bringt sie in Weiss mit dunklen Zeichen mit. In der dunklen
   Darstellung ziehen sie jetzt mit - zusammen mit unserem Massstab, der
   Legende und der Hintergrundkarte (app.js: basemapStandard waehlt im
   Dunkeln VersaTiles Shadow). Die Werte kommen aus den Rollen, nicht aus
   einem [data-thema]-Block: so gilt fuer diese Elemente dieselbe Regel wie
   fuer alles andere. */
.maplibregl-ctrl-group {
    background: var(--md-surface-container-hoch);
    box-shadow: 0 0 0 2px var(--md-schatten-weich)
}

.maplibregl-ctrl-group button + button {
    border-top-color: var(--md-outline-variant)
}

.maplibregl-ctrl-group button:not(:disabled):hover {
    background-color: var(--md-surface-container)
}

.maplibregl-ctrl-scale {
    background-color: var(--md-karte-glas);
    color: var(--md-auf-karte-glas);
    border-color: var(--md-auf-karte-glas)
}

.maplibregl-ctrl-attrib,
.maplibregl-ctrl-attrib a {
    color: var(--md-auf-karte-glas)
}

.maplibregl-ctrl-attrib.maplibregl-compact {
    background: var(--md-karte-glas)
}

/* Die EINE Stelle, an der eine Rolle nicht reicht. Die Zeichen der Knoepfe
   sind Hintergrundbilder - bei MapLibre eingebettete SVG mit fest
   eingetragenem dunklem Fuellwert, bei unserem Haus-Knopf ebenso (siehe
   .ctrl-daten unten). An die Farbe kommt man von aussen nicht heran; das
   Umkehren der Helligkeit dreht sie alle auf einmal.
   hue-rotate holt den Farbton zurueck, den invert() mitdreht - die Zeichen
   sind Graustufen, aber der Kompass traegt Rot. */
:root[data-thema="dunkel"] .maplibregl-ctrl-group button .maplibregl-ctrl-icon,
:root[data-thema="dunkel"] .maplibregl-ctrl-group button.ctrl-daten {
    filter: invert(1) hue-rotate(180deg)
}

/* Kartenknopf "Zu den Daten" (app.js: zoomZuDaten) - ein Haus, wie der
   "Home"-Knopf in QGIS und den Desktop-GIS. Der erste Entwurf war ein Rahmen
   mit Punkt und sah aus wie ein Vollbild-Symbol. */
.maplibregl-ctrl button.ctrl-daten {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 29'><path d='M14.5 6.5 5.5 14h2.5v8h5v-5h3v5h5v-8h2.5z' fill='%23333'/></svg>");
    background-repeat: no-repeat;
    background-position: center
}

/* ======================================================================
   ANMELDUNG

   Drei Dinge: der Benutzer im Seitenkopf, die Seiten VOR der Anmeldung
   (Anmelden, Passwort vergessen, neues Passwort setzen) und der Hinweis,
   wenn eine Sitzung ablaeuft, waehrend die Seite offen liegt.

   Die Gastseiten benutzen bewusst dieselbe app.css wie die Konsole und kein
   eigenes Stylesheet: Wer dort landet, soll sehen, wo er ist - gleicher Kopf,
   gleiche Schrift, gleiche Farben, gleicher Dunkelschalter.
   ====================================================================== */

/* Zwei Knoepfe im Kopf statt einem. Der Behaelter traegt jetzt die
   Positionierung am FENSTERRAND, die vorher .thema selbst hatte - deshalb
   dort zurueckgenommen.

   MITTIG UEBER top/bottom UND NICHT ueber transform: translateY(-50%), wie
   es .thema vorher tat. Ein transform macht das Element zum Bezugsrahmen
   fuer alles Darunterliegende mit position: fixed - und das Benutzermenue
   ist unter 620px genau das (Bottom Sheet, inset: auto 0 0 0). Mit dem
   transform klebte es als schmaler Streifen in der oberen rechten Ecke statt
   am unteren Rand; am 11.09.2026 im Bildschirmfoto bei 400px gesehen. */
.kopf-akt {
    position: absolute;
    right: 1rem;
    top: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 0.25rem
}

.kopf-akt .thema {
    position: static;
    transform: none
}

/* Platz fuer zwei Knoepfe: 2x2,75rem + Abstand + 1rem Rand + Luft. Steht
   nach .kopf-zeile weiter oben und gewinnt bei gleicher Spezifitaet. */
.kopf-zeile {
    padding-right: 7.5rem
}

/* ------------------------------------------------ Erscheinungsbild
   Zwei Textfelder nebeneinander, darunter Logo und Favicon als zwei Karten.
   Die Logo-Vorschau liegt auf der Marke, so wie sie im Kopf erscheint. */
.ers-zeile,
.ers-bilder {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 1rem
}

.ers-bilder {
    gap: 1rem;
    margin-top: 1rem
}

.ers-bild {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.75rem;
    border: 1px solid var(--line);
    border-radius: 0.5rem
}

/* Genau wie im Kopf: das Logo auf der Marke, ohne Platte. Die Vorschau soll
   zeigen, was der Kopf zeigt - ein Logo, das hier auf dem Blau verschwindet,
   verschwindet dort auch. */
.ers-vorschau {
    align-self: flex-start;
    height: 3.25rem;
    max-width: calc(100% - 1rem);
    object-fit: contain;
    background: var(--brand);
    padding: 0.5rem;
    border-radius: 0.375rem
}

/* Das Favicon auf Dunkelgrau, dem Ton des Seitenmenues: Die Reiterleiste
   von Chrome, Firefox und Co. ist heute meist dunkel - ein Favicon, das
   hier auf Dunkel verschwindet, verschwindet dort auch (Rueckmeldung vom
   13.09.2026). */
.ers-vorschau.favicon {
    height: 2rem;
    width: 2rem;
    padding: 0.25rem;
    background: var(--menue-flaeche)
}

@media (max-width: 620px) {
    .ers-zeile,
    .ers-bilder {
        grid-template-columns: 1fr
    }
}

/* Das Kuerzel ("mm") statt eines Symbols: Es sagt, WER angemeldet ist, und
   genau das ist die Frage, die dieser Knopf beantwortet. Rund und gefuellt,
   damit er sich vom Themaschalter daneben unterscheidet.

   SELEKTOR MIT .kopf-akt DAVOR, und das ist kein Zierrat: "button.ghost"
   weiter oben setzt background: transparent und ist mit Element PLUS Klasse
   spezifischer als eine Klasse allein - ein schlichtes ".benutzer-knopf"
   verlor den Kampf um die Fuellung, und das Kuerzel stand nackt im Kopf
   (gesehen am 11.09.2026 im Bildschirmfoto). Dieselbe Bauart wie
   ".thema .thema-seg" darueber, aus demselben Grund. */
.kopf-akt .benutzer-knopf {
    width: 2.75rem;
    min-width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border-radius: 50%;
    background: color-mix(in srgb, #fff 20%, transparent);
    border-color: transparent;
    color: #fff;
    font-weight: 600;
    font-size: 0.875rem;
    letter-spacing: .02em
}

.kopf-akt .benutzer-knopf:hover:not(:disabled) {
    background: color-mix(in srgb, #fff 34%, transparent);
    border-color: transparent;
    color: #fff
}

/* Der uebliche Fokusrahmen ist --accent und verschwindet auf der Marke fast -
   hier deshalb Weiss, wie beim Themaschalter. */
.kopf-akt .benutzer-knopf:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px
}

/* HIER STAND EINE REGEL FUER .benutzer-menue .menue - absichtlich weg:
   ".menue" bringt right: 0 und eine Breite schon mit, das Menue haengt also
   ohnehin rechts am Knopf. Die zusaetzliche Regel war nicht nur unnoetig,
   sie war schaedlich: Mit zwei Klassen ist sie spezifischer als die
   Telefonregel weiter oben (.menue, inset: auto 0 0 0), und ihr
   "left: auto" hat das Bottom Sheet unter 620px in die obere rechte Ecke
   gezogen - im Bildschirmfoto vom 11.09.2026 lag es halb ueber dem Kopf. */

/* Wer angemeldet ist, steht im Menue noch einmal ausgeschrieben - das
   Kuerzel am Knopf ist zweideutig, sobald zwei Personen dieselben
   Anfangsbuchstaben haben. */
.mn-kopf {
    padding: 0.625rem 0.875rem;
    border-bottom: 1px solid var(--line);
    font-size: 0.8125rem;
    color: var(--muted);
    line-height: 1.5
}

.mn-kopf strong {
    display: block;
    color: var(--ink);
    font-size: 0.9375rem;
    overflow-wrap: anywhere
}

.mn-kopf .ts {
    white-space: normal
}

/* ---------------------------------------------------- Seiten vor der Anmeldung */
.gast {
    display: flex;
    justify-content: center
}

.gast-karte {
    width: 100%;
    max-width: 26rem;
    background: var(--md-surface);
    border: 1px solid var(--md-outline-variant);
    border-radius: var(--md-form-md);
    padding: 1.5rem
}

.gast-kopf {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1.25rem
}

.gast-kopf strong {
    display: block;
    font-size: 1.125rem;
    line-height: 1.3
}

/* Auf diesen Seiten stehen ganze Saetze in .ts - der Standard dort ist
   nowrap (fuer kurze Angaben in Tabellen), und ein Satz haette die Seite
   seitlich hinausgeschoben. */
.gast .ts,
.gast-karte .ts {
    white-space: normal;
    line-height: 1.55
}

.gast-form {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin: 1rem 0 0.5rem
}

.gast-feld {
    display: flex;
    flex-direction: column;
    gap: 0.3125rem;
    margin-bottom: 0.875rem
}

.gast-feld span {
    font-size: 0.8125rem;
    font-weight: 600
}

/* 2,75rem hoch und 16px Schrift: Auf einem Telefon zoomt Safari sonst beim
   Hineintippen in das Feld, und die halbe Seite ist weg. */
.gast-feld input {
    font: inherit;
    font-size: 1rem;
    min-height: 2.75rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--md-outline);
    border-radius: var(--md-form-sm);
    background: var(--md-surface);
    color: var(--ink)
}

.gast-feld input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
    border-color: var(--accent)
}

/* Das Auge im Passwortfeld (app.py: passwort_feld). Das Feld bekommt
   rechts Platz fuer den Knopf; der Knopf liegt darueber, 2,75rem hoch wie
   das Feld, damit er auf dem Telefon eine echte Trefferflaeche hat. */
.pw-zeile {
    position: relative;
    display: flex
}

.pw-zeile input {
    flex: 1 1 auto;
    min-width: 0;
    padding-right: 2.75rem
}

/* top UND bottom, nicht min-height: button.ghost bringt eine eigene Hoehe
   mit und ist spezifischer - so fuellt der Knopf die Feldhoehe trotzdem. */
.pw-zeile .pw-auge {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 2.75rem;
    height: auto;
    min-height: 0;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--md-form-sm);
    color: var(--md-on-surface-variant)
}

/* Auge auf, solange das Passwort verborgen ist; Auge zu, wenn es zu sehen
   ist - der Knopf zeigt immer die NAECHSTE Wirkung. */
.pw-auge[aria-pressed="false"] .ic:nth-of-type(2),
.pw-auge[aria-pressed="true"] .ic:nth-of-type(1) {
    display: none
}

/* Ohne JavaScript taete der Knopf nichts - dann lieber keiner. */
html:not(.js) .pw-auge {
    display: none
}

html:not(.js) .pw-zeile input {
    padding-right: 0.75rem
}

.gast-kasten {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0.25rem 0 1rem;
    font-size: 0.875rem
}

.gast-kasten input {
    width: 1.125rem;
    height: 1.125rem;
    accent-color: var(--accent)
}

.gast-knopf {
    width: 100%;
    min-height: 2.75rem
}

.gast-link {
    display: inline-block;
    margin-top: 0.75rem;
    color: var(--accent);
    font-size: 0.875rem
}

/* Fehler und Bestaetigung sitzen UEBER dem Formular und nicht daneben: Wer
   sich vertippt hat, liest zuerst oben. role="alert" im Markup sorgt dafuer,
   dass ein Vorleseprogramm die Meldung auch ohne Neuladen bekommt. */
.gast-fehler,
.gast-ok {
    margin: 0 0 1rem;
    padding: 0.625rem 0.75rem;
    border-radius: var(--md-form-sm);
    font-size: 0.875rem;
    line-height: 1.5
}

.gast-fehler {
    background: var(--err-bg);
    color: var(--md-on-error-container, var(--ink));
    border-left: 3px solid var(--err)
}

.gast-ok {
    background: var(--ok-bg);
    color: var(--ink);
    border-left: 3px solid var(--ok)
}

/* ------------------------------------------------- Sitzung ist abgelaufen */
/* Bewusst kein <dialog>: Der Hinweis kommt aus einem fetch heraus, das
   jederzeit laufen kann - auch waehrend ein anderer Dialog offen ist, und
   zwei showModal() nacheinander wirft. */
.sitzung-weg {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.25rem;
    background: color-mix(in srgb, #000 45%, transparent)
}

.sitzung-weg-karte {
    width: 100%;
    max-width: 24rem;
    background: var(--md-surface);
    color: var(--ink);
    border-radius: var(--md-form-md);
    box-shadow: var(--md-erhebung-3, 0 8px 24px rgba(0, 0, 0, .3));
    padding: 1.25rem 1.375rem
}

.sitzung-weg-karte strong {
    display: block;
    font-size: 1.0625rem;
    margin-bottom: 0.5rem
}

.sitzung-weg-karte .ts {
    white-space: normal;
    line-height: 1.55
}

/* Ein <a> und kein <button>: Es fuehrt auf eine andere Seite. Die
   Knopf-Regeln oben sind element-gebunden (button.primary), deshalb hier
   eigens. */
.sitzung-weg-karte a.primary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 2.5rem;
    margin-top: 1rem;
    padding: 0 1.25rem;
    border-radius: var(--md-form-voll);
    background: var(--md-primary);
    color: var(--md-on-primary);
    font: 500 0.875rem/1.2 var(--font);
    text-decoration: none
}

/* ---------------------------------------------------------- Zugangsliste */
/* Kopfzeile des Bereichs: Zahl links, Anlegen rechts. Auf Telefonbreite
   untereinander - ein Knopf, der neben einer Zahl klemmt, trifft niemand. */
.zug-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.625rem;
    margin-bottom: 0.75rem
}

/* In der Zugangsliste stehen zwei Marken hintereinander (Rolle und Zustand),
   anders als in der Layerliste, wo eine allein hinter dem Namen steht. */
.lz-txt .tag + .tag {
    margin-left: 0.375rem
}

/* Auswahlfelder gibt es sonst nur im Stileditor; im Dialog sollen sie wie
   die Textfelder daneben aussehen (.zd-feld input). */
.zd-feld select {
    font: inherit;
    padding: 0.4375rem 0.625rem;
    border: 1px solid var(--line);
    border-radius: var(--md-form-sm);
    background: var(--md-surface);
    color: var(--ink)
}

/* Geraeteliste im Dialog: eine Zeile je offener Anmeldung, keine Spalten -
   Browser, Zeit und Adresse sind verschieden lang und stapeln besser. */
table.geraete {
    width: 100%;
    border-collapse: collapse
}

table.geraete td {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--line);
    vertical-align: top
}

table.geraete tr:last-child td {
    border-bottom: none
}

table.geraete .ts {
    white-space: normal
}


/* ------------------------------------------------------------ Layergruppen
   Eine Gruppe steht in derselben Liste wie ein Layer (Zeile .lz), darunter
   eingerueckt ihre Mitglieder in Kartenreihenfolge: oben ist oben. Die
   Mitgliedszeilen sind bewusst leiser als die Gruppenzeile - sie sind
   Inhalt, keine eigenen Eintraege mit Aktionen. */
.tag.t-gr {
    background: var(--md-secondary-container);
    color: var(--md-on-secondary-container)
}

.gz > td {
    padding-top: 0;
    padding-bottom: 0;
    background: color-mix(in srgb, var(--md-primary) 3%, transparent)
}

.gz-zeile {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.375rem 0 0.375rem 3.3125rem;
    min-width: 0
}

.gz-pos {
    flex: 0 0 2.75rem;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--md-on-surface-variant)
}

.ic-kreis.gz-ic {
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--md-form-sm)
}

.ic-kreis.gz-ic .ic {
    width: 1rem;
    height: 1rem
}

.gz-name {
    font-size: 0.875rem;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0
}

.gr-fuss {
    display: flex;
    justify-content: flex-end;
    padding: 0.75rem 0 0.25rem
}

.mn-eintrag.gefahr,
.mn-eintrag.gefahr .ic {
    color: var(--md-error)
}

/* Der Dialog: Titel, Art, links die Layer zum Ankreuzen, rechts die
   Reihenfolge mit Pfeilen. Pfeile statt Ziehen - Tastatur und Telefon
   koennen das, Ziehen nicht ohne Weiteres. */
.gd-feld {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin: 0.25rem 0 0.5rem
}

.gd-feld span {
    font-size: 0.8125rem;
    font-weight: 600
}

.gd-feld input {
    font: inherit;
    padding: 0.4375rem 0.625rem;
    border: 1px solid var(--line);
    border-radius: var(--md-form-sm);
    background: var(--md-surface);
    color: var(--ink)
}

.gd-name {
    margin: 0 0 0.875rem
}

.gd-modus {
    display: grid;
    gap: 0.625rem;
    margin: 0 0 1rem;
    padding: 0;
    border: none
}

.gd-modus legend,
.gd-ueber {
    display: block;
    font-size: 0.8125rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
    padding: 0
}

.gd-spalten {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    align-items: start
}

.gd-liste {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    max-height: 16rem;
    overflow: auto;
    padding-right: 0.25rem
}

.gd-layer {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3125rem 0.25rem;
    border-radius: var(--md-form-sm);
    cursor: pointer;
    min-width: 0
}

.gd-layer:hover {
    background: color-mix(in srgb, var(--md-on-surface) 6%, transparent)
}

.gd-layer > span:not(.tag) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0
}

.gd-layer .ic.klein {
    color: var(--md-on-surface-variant);
    flex: 0 0 auto
}

.gd-reihe {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.125rem
}

.gd-reihe li {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.1875rem 0.25rem;
    border-radius: var(--md-form-sm);
    min-width: 0
}

.gd-reihe li.gd-leer {
    white-space: normal;
    padding: 0.5rem 0.25rem
}

.gd-pos {
    flex: 0 0 2.75rem;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--md-on-surface-variant)
}

.gd-mname {
    flex: 1 1 auto;
    font-size: 0.875rem;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0
}

.gd-pfeile {
    display: flex;
    gap: 0.125rem;
    flex: 0 0 auto
}

.gd-pfeile button {
    padding: 0.25rem
}

.gd-pfeile button:disabled {
    opacity: .3;
    cursor: default
}

.ic.hoch {
    transform: rotate(-90deg)
}

.ic.runter {
    transform: rotate(90deg)
}

@media (max-width: 600px) {
    .gd-spalten {
        grid-template-columns: 1fr
    }

    .gz-zeile {
        padding-left: 0.5rem
    }
}

/* Zugeklappter Abschnitt in der Einbinden-Lade ("Fuer eigene Webkarten"):
   eine Zeile wie die Listenzeilen darueber, mit Klapp-Pfeil statt Symbol.
   Offen erscheint der Inhalt eingerueckt darunter. */
.lade-mehr {
    margin-top: 0.5rem;
    border-top: 1px solid var(--md-outline-variant)
}

.lade-mehr summary {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 0.8125rem 0.125rem;
    cursor: pointer;
    list-style: none;
    border-radius: var(--md-form-sm)
}

.lade-mehr summary::-webkit-details-marker {
    display: none
}

.lade-mehr summary:hover {
    background: color-mix(in srgb, var(--md-on-surface) 5%, transparent)
}

.lade-mehr summary .ic.chev {
    flex: 0 0 auto;
    color: var(--md-on-surface-variant);
    transition: transform var(--md-kurz) var(--md-kurve)
}

.lade-mehr[open] summary .ic.chev {
    transform: rotate(90deg)
}

.lm-txt {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0
}

.lm-txt strong {
    font-weight: 600;
    font-size: 0.9375rem
}

.lm-txt .ts {
    white-space: normal;
    font-size: 0.8125rem
}

.lm-inhalt {
    padding: 0 0 0.5rem 0.125rem
}

/* Sprungziel aus den Laden (data-ziel): der Fokus landet auf der
   Ueberschrift, damit Vorleseprogramme dort weiterlesen - ohne Ring, der
   saehe nach einem Fehler aus; und mit Luft nach oben beim Anspringen. */
h2[tabindex]:focus,
h2[tabindex]:focus-visible {
    outline: none
}

h2[id] {
    scroll-margin-top: 1.25rem
}


/* ------------------------------------------------- Stil-Lade: eine Zeile je Stil
   Seit den weiteren Stilen (13.09.2026) eine Liste statt einer Zeile. Der
   Standardstil steht oben und ist markiert; jede Zeile traegt Bezeichnung,
   technischen Namen, Legende und ihre Aktionen. */
.st-liste {
    display: flex;
    flex-direction: column
}

.st-zeile {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.875rem;
    padding: 0.75rem 0;
    border-top: 1px solid var(--line)
}

.st-zeile:first-child {
    border-top: none;
    padding-top: 0.125rem
}

.st-inh {
    flex: 1 1 18rem;
    min-width: 0
}

.st-bez {
    font-size: 0.9375rem
}

.st-akt {
    flex: 0 1 auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.375rem;
    align-self: flex-end;
    margin-left: auto
}

.st-fuss {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--line)
}

.st-fuss .dar-txt {
    margin: 0;
    flex: 1 1 22rem
}

/* Der gewaehlte Stil eines Gruppenmitglieds, an seiner Zeile. */
.gz-stil code {
    font-size: 0.75rem
}

/* Die Stilwahl im Gruppendialog, neben dem Layernamen. */
.gd-stil {
    font: inherit;
    font-size: 0.8125rem;
    padding: 0.25rem 0.375rem;
    border: 1px solid var(--line);
    border-radius: var(--md-form-sm);
    background: var(--md-surface);
    color: var(--ink);
    max-width: 12rem
}

/* Die Stilwahl in der Vorschau, gleiche Anmutung wie die Kartenwahl. */
.mstil select {
    max-width: 14rem
}

@media (max-width: 620px) {
    .st-akt {
        margin-left: 0;
        justify-content: flex-start
    }
}
