/*
  Die Oberfläche kommt von MudBlazor. Diese Datei enthält nur zwei Dinge:

  1. Eine Brücke für die Identity-Seiten, die noch aus dem .NET-Template stammen. Anmelden,
     Registrieren und die Kontoverwaltung sind auf MudBlazor umgestellt; die selteneren
     Unterseiten (Zwei-Faktor, Passkeys, Datenexport) benutzen weiterhin die Klassennamen des
     Templates. Statt zwanzig Seiten einzeln umzuschreiben, bilden die Regeln unten diese
     Klassen auf die MudBlazor-Tokens ab — damit sehen sie stimmig aus und wechseln das
     Aussehen korrekt zwischen hellem und dunklem Design mit.

  2. Die Fehleranzeige von Blazor.
*/

/* Die Schrift kommt von hier und nicht von Google.
   ------------------------------------------------------------------------------------
   Vorher stand im App.razor ein <link> auf fonts.googleapis.com. Die Lizenz der Schrift
   (SIL OFL 1.1) erlaubt beides — das Problem war der Weg: Jeder Besucher lud die Datei bei
   Google, und seine IP-Adresse ging dorthin, bevor er irgendetwas zugestimmt hatte. Als
   gehostetes Angebot mit Auftragsverarbeitungsvertrag je Mandant (§6.7) ist das keine
   Kleinigkeit; das LG München I hat 2022 genau dafür Schadenersatz zugesprochen.

   Dieselbe Entscheidung wie bei OpenLayers, die im Dockerfile schon begründet steht: Die
   Anwendung soll ohne Netzzugang zu Fremdservern auskommen.

   Zwei Dateien statt acht: IBM Plex Sans liegt bei Google als variable Schrift, eine Datei
   je Zeichensatz trägt alle Strichstärken von 100 bis 700. Latin deckt Deutsch ab,
   Latin-Ext den Rest Europas — die übrigen vier Zeichensätze (Kyrillisch, Griechisch,
   Vietnamesisch) braucht hier niemand. */

@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 100 700;
    font-display: swap;
    src: url('fonts/ibm-plex-sans-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                   U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 100 700;
    font-display: swap;
    src: url('fonts/ibm-plex-sans-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
                   U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
                   U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

html, body {
    font-family: 'IBM Plex Sans', 'Segoe UI', Helvetica, Arial, sans-serif;
}

/* ---------- Brücke: Formularfelder ---------- */

.form-control,
.form-select {
    width: 100%;
    padding: 10px 12px;
    font: inherit;
    color: var(--mud-palette-text-primary);
    background: transparent;
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--mud-default-borderradius);
    transition: border-color .15s ease;
}

.form-control:focus,
.form-select:focus {
    outline: none;
    border-color: var(--mud-palette-primary);
    box-shadow: 0 0 0 1px var(--mud-palette-primary);
}

.form-control:disabled {
    color: var(--mud-palette-text-disabled);
    background: var(--mud-palette-background-gray);
}

.form-floating {
    margin-bottom: 1rem;
}

.form-floating > label,
.form-label {
    display: block;
    margin-bottom: 4px;
    font-size: .8125rem;
    color: var(--mud-palette-text-secondary);
}

/* Das Template setzt das Feld vor das Label; ohne Bootstrap gehört es andersherum. */
.form-floating {
    display: flex;
    flex-direction: column-reverse;
}

.form-check-input,
.darker-border-checkbox {
    accent-color: var(--mud-palette-primary);
    width: 1rem;
    height: 1rem;
    margin-right: .5rem;
    vertical-align: middle;
}

/* ---------- Brücke: Schaltflächen ---------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    padding: 8px 16px;
    font: inherit;
    font-weight: 500;
    line-height: 1.75;
    text-decoration: none;
    cursor: pointer;
    border: 1px solid transparent;
    border-radius: var(--mud-default-borderradius);
    transition: background-color .15s ease, box-shadow .15s ease;
}

.btn-primary {
    color: var(--mud-palette-primary-text);
    background: var(--mud-palette-primary);
}

.btn-primary:hover {
    background: var(--mud-palette-primary-darken);
}

.btn-danger {
    color: var(--mud-palette-error-text);
    background: var(--mud-palette-error);
}

.btn-danger:hover {
    background: var(--mud-palette-error-darken);
}

.btn-lg {
    padding: 10px 22px;
    font-size: 1rem;
}

.btn-link,
a {
    color: var(--mud-palette-primary);
    background: none;
    border: none;
    text-decoration: none;
}

.btn-link:hover,
a:hover {
    text-decoration: underline;
}

.w-100 {
    width: 100%;
}

/* ---------- Brücke: Hinweise ---------- */

.alert {
    padding: 12px 16px;
    margin-bottom: 1rem;
    border: 1px solid;
    border-radius: var(--mud-default-borderradius);
}

.alert-success {
    color: var(--mud-palette-success);
    border-color: var(--mud-palette-success);
    background: transparent;
}

.alert-danger {
    color: var(--mud-palette-error);
    border-color: var(--mud-palette-error);
    background: transparent;
}

.alert-warning {
    color: var(--mud-palette-warning);
    border-color: var(--mud-palette-warning);
    background: transparent;
}

.alert-info {
    color: var(--mud-palette-info);
    border-color: var(--mud-palette-info);
    background: transparent;
}

/* ---------- Brücke: Raster und Listen ---------- */

.row {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
}

.row > [class^="col-"] {
    flex: 1 1 320px;
    min-width: 0;
}

.nav-pills {
    list-style: none;
    padding: 0;
    margin: 0;
}

.nav-pills .nav-link {
    display: block;
    padding: 8px 14px;
    border-radius: var(--mud-default-borderradius);
    color: var(--mud-palette-text-primary);
}

.nav-pills .nav-link:hover {
    background: var(--mud-palette-action-default-hover);
    text-decoration: none;
}

.nav-pills .nav-link.active {
    color: var(--mud-palette-primary-text);
    background: var(--mud-palette-primary);
}

/* ---------- Validierung ---------- */

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--mud-palette-success);
}

.invalid {
    outline: 1px solid var(--mud-palette-error);
}

.validation-message,
.text-danger {
    display: block;
    margin-top: 4px;
    font-size: .8125rem;
    color: var(--mud-palette-error);
}

.text-secondary {
    color: var(--mud-palette-text-secondary);
}

h1:focus {
    outline: none;
}

/* ---------- Fehleranzeige von Blazor ---------- */

#blazor-error-ui {
    color: var(--mud-palette-dark-text);
    background: var(--mud-palette-warning);
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, .2);
    display: none;
    left: 0;
    padding: .8rem 1.4rem .8rem 1.4rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: .75rem;
    top: .5rem;
}

.blazor-error-boundary {
    padding: 1rem;
    color: var(--mud-palette-error-text);
    background: var(--mud-palette-error);
    border-radius: var(--mud-default-borderradius);
}

/* ---------- Kartenbedienung ----------
   Die Vorgaben von OpenLayers sind rund 22 px groß und damit auf einem Plan, an dem man
   längere Zeit arbeitet, unangenehm zu treffen — erst recht auf einem Touchgerät. Hier auf
   40 px gebracht und farblich an MudBlazor angeglichen.
*/

.ol-control {
    background: transparent;
    padding: 0;
    border-radius: var(--mud-default-borderradius);
}

.ol-control button {
    width: 40px;
    height: 40px;
    margin: 0 0 6px 0;
    font-size: 22px;
    font-weight: 400;
    line-height: 1;
    color: var(--mud-palette-text-primary);
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
    cursor: pointer;
    transition: background-color .12s ease, color .12s ease;
}

.ol-control button:hover,
.ol-control button:focus-visible {
    color: var(--mud-palette-primary-text);
    background: var(--mud-palette-primary);
    outline: none;
}

.ol-zoom {
    top: 12px;
    left: 12px;
}

.ol-full-screen {
    top: 12px;
    right: 12px;
    left: auto;
}

/* Maßstabsleiste: gut lesbar statt Kleingedrucktes. */
.ol-scale-line {
    left: 12px;
    bottom: 12px;
    padding: 4px 8px;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
}

.ol-scale-line-inner {
    font-size: 12px;
    font-weight: 500;
    color: var(--mud-palette-text-primary);
    border: 2px solid var(--mud-palette-text-primary);
    border-top: none;
    padding-bottom: 2px;
}

/* Namensnennung ist Lizenzpflicht (basemap.at CC BY, OSM ODbL) — lesbar, nicht versteckt. */
.ol-attribution {
    right: 12px;
    bottom: 12px;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    padding: 3px 8px;
}

.ol-attribution ul {
    font-size: 11px;
    color: var(--mud-palette-text-secondary);
    text-shadow: none;
}

.ol-attribution a {
    color: var(--mud-palette-primary);
}

/* ---------- Ablagefläche für den Datei-Upload ----------
   Das native Dateifeld bleibt im Formular (der Upload läuft als klassischer POST, nicht über
   die SignalR-Leitung), wird hier aber unsichtbar gemacht. Sichtbar ist nur diese Fläche.
*/

.plan-upload-zone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    position: relative;
    padding: 24px 16px;
    text-align: center;
    cursor: pointer;
    border: 2px dashed var(--mud-palette-lines-inputs);
    border-radius: var(--mud-default-borderradius);
    background: var(--mud-palette-background-gray);
    transition: border-color .15s ease, background-color .15s ease;
}

.plan-upload-zone:hover,
.plan-upload-zone:focus-visible {
    border-color: var(--mud-palette-primary);
    outline: none;
}

/* Während eine Datei über der Fläche schwebt. */
.plan-upload-zone.plan-upload-aktiv {
    border-color: var(--mud-palette-primary);
    border-style: solid;
    background: var(--mud-palette-primary-hover);
}

/* Sobald eine Datei gewählt ist: durchgezogener Rahmen statt Einladung zum Ablegen. */
.plan-upload-zone.plan-upload-gefuellt {
    border-style: solid;
    border-color: var(--mud-palette-primary);
}

.plan-upload-text {
    font-size: .875rem;
    font-weight: 500;
    line-height: 1.35;
    color: var(--mud-palette-text-primary);
    overflow-wrap: anywhere;
}

.plan-upload-hinweis {
    font-size: .75rem;
    color: var(--mud-palette-text-secondary);
}

/* Bleibt bedienbar für Tastatur und Screenreader, ist aber nicht zu sehen —
   display:none würde die Formularübermittlung in manchen Browsern stören. */
.plan-upload-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

/* Die Planvorschau auf der Kartenseite: dieselbe Karte wie auf der Zeichenfläche.

   Eine feste Höhe, weil eine Karte keine eigene hat — sie füllt, was man ihr gibt. Hoch genug,
   dass ein Gelände darin zu erkennen ist, und niedrig genug, dass die Liste daneben nicht
   allein auf dem Schirm steht. */
.plan-vorschaukarte {
    width: 100%;
    height: 520px;
    border-radius: var(--mud-default-borderradius);
    background: var(--mud-palette-background-gray);
}

@media (max-width: 960px) {
    /* Untereinander statt nebeneinander: Dann teilt sich die Karte die Höhe mit allem
       anderen, und 520 Pixel wären die halbe Seite. */
    .plan-vorschaukarte {
        height: 360px;
    }
}

/* Beschriftungen, die stehenbleiben, wo schon Text steht.
   ------------------------------------------------------------------------------------
   MudBlazor hebt die Beschriftung eines Feldes in drei Fällen an: wenn die Komponente
   selbst einen Wert kennt (.mud-shrink), wenn das Feld den Fokus hat, und wenn der Browser
   es automatisch ausgefüllt hat (:-webkit-autofill).

   Die Anmeldeseiten rendern statisch — ohne Blazor-Laufzeit im Browser. Die Komponente
   erfährt also nie, dass jemand etwas eingetippt hat, und .mud-shrink bleibt aus. Solange
   das Feld den Fokus hat, fällt das nicht auf; sobald er weiterwandert, steht die
   Beschriftung quer über dem eingetippten Kennwort.

   Hier fehlt der vierte Fall: Der Browser weiß, ob das Feld leer ist, und über
   :placeholder-shown lässt er sich danach fragen. Voraussetzung ist ein Platzhalter am
   Feld — ohne ihn trifft die Bedingung nie zu. Deshalb tragen die Felder der Anmeldeseiten
   Placeholder=" ".

   Die beiden Regeln sind Abschriften der MudBlazor-eigenen, nur mit anderer Bedingung:
   die erste hebt die Beschriftung, die zweite schlägt ihr das Loch in die Umrandung. */
.mud-input.mud-input-outlined:has(input:not(:placeholder-shown))
~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined {
    transform: translate(14px, -6px) scale(0.75);
    max-width: calc((100% - 14px) / .75);
}

.mud-input.mud-input-outlined:has(input:not(:placeholder-shown))
> .mud-input-outlined-border legend {
    width: auto;
    padding: 0 5px;
}

/* Die Anmeldewege über dem Formular — externe Anbieter und Passkey. Ein Zeichen links, die
   Beschriftung mittig, alle gleich breit: so sehen Anmeldemasken überall aus, und genau
   deshalb versteht man sie ohne Erklärung.

   Umrandung und Farben kommen von MudBlazor, die Aufteilung von hier. app.css steht nach
   MudBlazor.min.css, deshalb genügt dieselbe Spezifität. */
.plan-anmeldeweg {
    width: 100%;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 16px;

    /* MudBlazor setzt Schaltflächen in Versalien. „ANMELDEN MIT MICROSOFT" schreit, und
       der Anbietername gehört so geschrieben, wie der Anbieter ihn schreibt. */
    text-transform: none;
}

/* Nimmt den übrigen Platz, damit die Beschriftung mittig steht und nicht am Zeichen klebt. */
.plan-anmeldeweg-text {
    flex: 1;
    text-align: center;
}

/* Das Zeichen behält seine Größe, auch wenn die Beschriftung länger wird. */
.plan-anmeldeweg-zeichen {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
}

.plan-anmeldeweg-zeichen > svg {
    width: 100%;
    height: 100%;
}

/* Trennlinie mit Beschriftung in der Mitte: Die beiden Striche entstehen als Pseudoelemente
   und teilen sich den Platz, der neben dem Text übrig bleibt. */
.plan-anmeldetrenner {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 20px 0;
    font-size: .75rem;
    color: var(--mud-palette-text-secondary);
}

.plan-anmeldetrenner::before,
.plan-anmeldetrenner::after {
    content: "";
    flex: 1;
    border-top: 1px solid var(--mud-palette-divider);
}

/* Registrieren links, Kennwort vergessen rechts — auf schmalen Schirmen untereinander. */
.plan-anmeldefuss {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 8px 16px;
    margin-top: 24px;
}

/* Der Lizenzverweis steht mittig und leiser als die beiden darüber: Er muss da sein, aber
   er ist nicht der Weg, den hier jemand sucht. */
.plan-anmeldefuss-leise {
    justify-content: center;
    margin-top: 12px;
    opacity: .7;
}

/* Der Stand der Übertragung, an der Stelle der Schaltfläche. Siehe sendeFormular
   in plan-map.js. */
.plan-upload-fortschritt {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.plan-upload-balken {
    height: 6px;
    border-radius: 3px;
    overflow: hidden;
    background: var(--mud-palette-action-disabled-background);
}

.plan-upload-balken > div {
    width: 0;
    height: 100%;
    background: var(--mud-palette-primary);
    /* Kurz genug, dass der Balken dem Hochladen folgt, und nicht ihm hinterherläuft. */
    transition: width .2s linear;
}

.plan-upload-stand {
    font-size: .75rem;
    color: var(--mud-palette-text-secondary);
    overflow-wrap: anywhere;
}

/* Eine abgewiesene Datei: Der Balken bleibt als roter Strich stehen, damit die Meldung
   darunter nicht in der Luft hängt. */
.plan-upload-fortschritt.plan-upload-fehler .plan-upload-balken > div {
    background: var(--mud-palette-error);
}

.plan-upload-fortschritt.plan-upload-fehler .plan-upload-stand {
    color: var(--mud-palette-error);
}

/* --- Passpunkt-Assistent (§5.3) --------------------------------------------------- */

/* --- Die Darstellungsgröße und die Karten ------------------------------------------

   Die Oberfläche lässt sich verkleinern (siehe Anzeigegroesse). Gesetzt wird das als `zoom`
   am <html>; daneben steht derselbe Wert als --plan-zoom, weil CSS den Zoom nicht selbst
   auslesen kann.

   Für alles, was sich am Inhalt bemisst, genügt der Zoom: Schrift, Abstände und Listen
   schrumpfen mit, und genau das ist der Zweck. Eine Karte ist der Gegenfall — sie soll den
   frei gewordenen Platz *bekommen*, nicht ihn ebenfalls hergeben.

   Der Haken steckt in `100vh`: Es bleibt unter Zoom in CSS-Pixeln gleich und wird nur kleiner
   gemalt. Eine Karte auf `100vh - 420px` verlöre damit ein Fünftel ihrer Fläche. Bei einem
   Zoom z füllt die Karte den Rest des Fensters erst, wenn ihre CSS-Höhe 100vh/z − Kopfhöhe
   beträgt: Der Kopf über ihr misst in CSS-Pixeln unverändert, belegt aber nur noch das
   z-fache an Papier. Bei z = 1 steht dort wieder genau der alte Wert.
   ---------------------------------------------------------------------------------- */

/* Beide Karten gleich hoch, damit sich dieselbe Stelle links und rechts auf einer Linie
   wiederfindet. Die Höhe folgt dem Fenster, ist nach unten aber begrenzt: Unter etwa
   340 px trifft man eine Hallenecke nicht mehr zuverlässig — und zwar 340 px auf dem
   Bildschirm, weshalb auch diese Grenze den Zoom herausrechnet. */
.plan-einmess-karte {
    width: 100%;
    height: calc(100vh / var(--plan-zoom, 1) - 420px);
    min-height: calc(340px / var(--plan-zoom, 1));
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    background: var(--mud-palette-background-gray);
    /* Es wird gezielt geklickt, nicht gezogen — das Fadenkreuz sagt das an. */
    cursor: crosshair;
}

/* Die Zeichenkarte (§8). Höher als die Einmesskarten: Hier wird gearbeitet, nicht verglichen.

   Das Maß steht als Variable da, weil es zweimal gebraucht wird: Die Registerspalte links ist
   genau so hoch wie die Karte rechts. Zwei gleichlautende Rechnungen wären zwei Stellen, die
   auseinanderlaufen können — und man sähe es erst daran, dass die Spalten nicht mehr auf einer
   Linie enden. */
:root {
    --plan-zeichenhoehe: calc(100vh / var(--plan-zoom, 1) - 300px);
    --plan-zeichenhoehe-mindestens: calc(480px / var(--plan-zoom, 1));
}

.plan-zeichenkarte {
    width: 100%;
    height: var(--plan-zeichenhoehe);
    min-height: var(--plan-zeichenhoehe-mindestens);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    background: var(--mud-palette-background-gray);
}

/* Die Registerspalte neben der Karte — Layer, Eigenschaften, Objekte, Karte.

   Sie bekommt dieselbe Höhe und behält sie: Was nicht hineinpasst, rollt im Register, statt
   die Spalte wachsen zu lassen. Vorher richtete sich ihre Höhe nach dem Inhalt, und der
   wechselt ständig — eine ausgeklappte Objektartliste, ein Symbolvorrat, ein gewähltes Objekt
   mit seinen Maßen. Mal endete die Spalte weit über der Karte, mal ragte sie darunter hinaus.

   Die Kette aus flex und min-height:0 muss bis zu den Panels durchgreifen. Ein Flexelement
   schrumpft von sich aus nicht unter seinen Inhalt; ohne das min-height:0 wüchse die Spalte
   trotz fester Höhe und schöbe ihren Inhalt über den Rand. */
.plan-registerspalte {
    height: var(--plan-zeichenhoehe);
    min-height: var(--plan-zeichenhoehe-mindestens);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.plan-registerspalte .mud-tabs {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.plan-registerspalte .mud-tabs-panels {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* Die Vorschau auf der Kartenseite — Orthofoto wie Geländeausschnitt.

   Dieselbe Rechnung wie oben. Sie stand zuvor als Inline-Stil an beiden Karten und rechnete
   den Zoom nicht heraus: Bei 80 % blieb die Karte auf ihrer alten CSS-Höhe und wurde nur
   kleiner gemalt — statt den Platz zu bekommen, den das Verkleinern frei macht. Der Abzug ist
   hier kleiner als bei der Zeichenfläche, weil über der Karte nur die Kennzahlenleiste steht
   und keine Werkzeugleiste. */
.plan-kartenvorschau {
    width: 100%;
    height: calc(100vh / var(--plan-zoom, 1) - 340px);
    min-height: calc(420px / var(--plan-zoom, 1));
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    background: var(--mud-palette-background-gray);
}

/* Farbtupfer vor dem Layernamen — dieselbe Farbe, in der der Layer in der Karte zeichnet. */
.plan-layer-farbe {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    flex: 0 0 auto;
}

/* Farbtupfer in der Stilleiste. Bewusst klein und dicht: Sie sind Schnellzugriff, der
   vollständige Farbwähler steht daneben. */
.plan-farbtupfer {
    width: 18px;
    height: 18px;
    border-radius: 3px;
    cursor: pointer;
    border: 2px solid transparent;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15);
}

.plan-farbtupfer:hover {
    transform: scale(1.15);
}

.plan-farbtupfer-aktiv {
    border-color: var(--mud-palette-text-primary);
}

/* Vorschau im Symbolkatalog. Kariert, damit sich Durchsichtigkeit von Weiß unterscheiden
   lässt — genau der Unterschied, auf den es bei einem Planzeichen ankommt. */
.plan-symbol-vorschau {
    height: 84px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    background-color: var(--mud-palette-background-gray);
    background-image:
        linear-gradient(45deg, rgba(0, 0, 0, .06) 25%, transparent 25%),
        linear-gradient(-45deg, rgba(0, 0, 0, .06) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, rgba(0, 0, 0, .06) 75%),
        linear-gradient(-45deg, transparent 75%, rgba(0, 0, 0, .06) 75%);
    background-size: 12px 12px;
    background-position: 0 0, 0 6px, 6px -6px, -6px 0;
}

.plan-symbol-vorschau img {
    max-width: 100%;
    max-height: 76px;
    object-fit: contain;
}

/* Auswahlliste der Symbole in der Werkzeugleiste. */
/* Die Objektartwahl im Eigenschaften-Register.

   Dieselbe Idee wie beim Symbolvorrat darunter, nur mit Namen statt Bildern: Was zur Wahl
   steht, steht offen da. Ein Auswahlfeld verlangte erst einen Klick zum Aufklappen und
   verdeckte dabei den Rest der Spalte.

   Ohne eigenen Deckel, mit Absicht. Sie hatte einmal einen von 240 px und rollte darin — und
   darunter blieb die halbe Spalte leer, während man in einem Fensterchen scrollte. Zwei
   ineinanderliegende Rollbereiche sind ohnehin der schlechtere Handgriff: Das Register rollt
   bereits, wenn es eng wird (siehe .plan-registerspalte), und das genügt. Eine Liste, die in
   die Spalte passt, braucht so gar keinen Bildlauf.

   Der Symbolvorrat behält seinen Deckel: Er wächst in die Breite und bleibt bei dreißig
   Bildern vier Reihen hoch, während diese Liste je Eintrag eine Zeile zulegt. */
.plan-objektartwahl {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 2px;
}

.plan-objektart-wahl {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 3px 6px;
    border: 2px solid transparent;
    border-radius: 4px;
    cursor: pointer;
    line-height: 1.3;
}

.plan-objektart-wahl:hover {
    background: var(--mud-palette-action-default-hover);
}

.plan-objektart-wahl-aktiv {
    border-color: var(--mud-palette-primary);
}

/* Der Name darf gekürzt werden, die Kategorie rechts nicht umbrechen: In einer schmalen Spalte
   ist „Rettungswege & Zufahrten · Sicherheit" sonst drei Zeilen hoch. */
.plan-objektart-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.plan-objektart-rand {
    flex: 0 0 auto;
    color: var(--mud-palette-text-secondary);
    font-size: 11px;
    white-space: nowrap;
}

/* Die Layerwahl — in der Werkzeugleiste wie über der Objekttabelle.

   Die Breite hängt an dieser Hülle und nicht am Feld selbst: MudSelect legt seinen Style auf
   den inneren Teil, und der wuchs zwar auf die Mindestbreite, während die Hülle darum vom
   Flex-Container auf 161 px gequetscht wurde — der Inhalt stand dann über seinen eigenen Rand
   hinaus. `flex: 0 0 auto` sagt der Reihe, dass hier nichts zu holen ist. */
.plan-layerwahl {
    flex: 0 0 auto;
    min-width: 230px;
}

/* Im Dialog bleibt der Deckel. Dort gibt es keine Spalte, die mitwächst — ein Fenster mit
   dreißig Bildern untereinander schöbe seine eigenen Knöpfe aus dem Bild. */
.mud-dialog .plan-symbolraster {
    max-height: 200px;
    overflow-y: auto;
}

/* Der Symbolvorrat im Eigenschaften-Register.

   Ohne eigenen Deckel, wie die Objektartliste darüber: Er hatte einen von 200 px und rollte
   darin, während unter Farbe, Strich und Füllung Platz frei blieb — zwei ineinanderliegende
   Rollbereiche, und man scrollte im kleineren. Das Register rollt bereits, wenn es eng wird
   (siehe .plan-registerspalte), und das genügt. Was hineinpasst, braucht so gar keinen
   Bildlauf. */
.plan-symbolraster {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    /* Sonst schneidet der Rand den Rahmen des aktiven Symbols an. */
    padding: 2px;
}

.plan-symbol-wahl {
    width: 46px;
    height: 46px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid transparent;
    border-radius: 4px;
    cursor: pointer;
    padding: 3px;
}

.plan-symbol-wahl:hover {
    background: var(--mud-palette-action-default-hover);
}

.plan-symbol-wahl-aktiv {
    border-color: var(--mud-palette-primary);
}

.plan-symbol-wahl img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* Der Farbwähler in der Stilleiste.
   Sein eigenes Eingabefeld bleibt ausgeblendet: Geöffnet wird über das Palettensymbol
   daneben, und der Hexwert steht im Dialog. Ausgeblendet wird ausdrücklich nur das
   Eingabefeld, und zwar über den unmittelbaren Nachfahren: Der Dialog steckt im selben
   Wurzelelement, und sein Hexfeld ist ebenfalls ein .mud-input-control — ohne das > war es
   mit ausgeblendet. */
.plan-farbwaehler > .mud-input-control {
    display: none;
}

/* Der Dialog des Farbwählers soll seine natürliche Breite behalten, egal was um ihn herum
   an Begrenzungen steht. */
.mud-picker-color-dialog,
.mud-picker-container .mud-picker-color-view {
    min-width: 260px;
}

/* Objekttabelle unter der Karte. Begrenzt und scrollend: Ein Layer mit hundert Objekten darf
   die Seite nicht ins Unendliche ziehen.

   840 statt der früheren 280 Pixel: Mit den Gruppenzeilen für Kategorie und Objektart kostet
   jede Gruppe zwei Zeilen, und auf einem Drittel Bildschirmhöhe standen am Ende mehr
   Überschriften als Objekte. Es bleibt eine Höchsthöhe — eine kurze Liste schrumpft mit.

   Gescrollt wird in MudBlazors eigenem Behälter und nicht im <div> darum herum. Das ist keine
   Feinheit, sondern die Bedingung für die stehende Überschrift darunter: `position: sticky`
   bezieht sich auf den nächsten scrollenden Vorfahren. Lag das Scrollen außen, war der
   nächste Vorfahre der `mud-table-container` — der selbst nie scrollt, weil sein Inhalt genau
   hineinpasst. Die Überschrift klebte dann an etwas, das stillsteht, und wanderte mit den
   Zeilen nach oben davon. */
.plan-objekttabelle .mud-table-container {
    max-height: 840px;
    overflow-y: auto;
}

/* Die Spaltenüberschriften bleiben beim Scrollen stehen.

   Die Regel steht auf den Zellen und nicht auf der Zeile: `position: sticky` auf einem <tr>
   wirkt in Firefox und Safari nicht, und ein <thead> lässt sich gar nicht kleben. Der
   Hintergrund ist Pflicht — ohne ihn scrollen die Zeilen sichtbar durch die Überschrift. */
.plan-objekttabelle thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--mud-palette-surface);
}

/* Die beiden Gruppenzeilen: Kategorie, darunter eingerückt ihre Objektarten. */
.plan-objekttabelle .plan-gruppe > td {
    background: var(--mud-palette-background-grey);
    font-weight: 600;
    letter-spacing: 0.02em;
}

.plan-objekttabelle .plan-gruppe-art > td {
    padding-left: 32px;
    font-weight: 500;
    color: var(--mud-palette-text-secondary);
}

/* Die Tabelle hebt die Zeile unter dem Zeiger hervor. Eine Überschrift ist aber nichts, was
   sich anfassen lässt — sie darf sich auch nicht so anfühlen. */
.plan-objekttabelle .plan-gruppe:hover > td {
    background: var(--mud-palette-background-grey);
}

/* Die Anzahl hinter dem Gruppennamen. */
.plan-objekttabelle .plan-gruppe-zahl {
    margin-left: 8px;
    font-weight: 400;
    color: var(--mud-palette-text-disabled);
}

/* Die Zeile des gewählten Objekts — dieselbe Hervorhebung wie in der Layerliste. */
.plan-objekttabelle .plan-zeile-gewaehlt > td {
    background: var(--mud-palette-action-default-hover);
}

/* Der Name eines Layers in der Liste.

   Das min-width:0 ist wieder der eigentliche Punkt: Ein Flexelement schrumpft von sich aus
   nicht unter seine eigene Inhaltsbreite. „Rettungswege & Zufahrten" schob deshalb Zähler und
   Knöpfe aus der Spalte, statt sich kürzen zu lassen. Der MudTooltip um den Link herum ist
   selbst ein Flexelement — deshalb steht die Regel auch auf ihm. */
.plan-layername,
.plan-layername.mud-typography {
    /* Breite vom Elternteil nehmen, nicht vom Inhalt: Nur dann kann „…" überhaupt greifen.
       Ein Link ist von Haus aus inline und damit so breit wie sein Text — die Kürzung liefe
       ins Leere, das Wort ragte einfach über den Rand. */
    display: block;
    width: 100%;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Der MudTooltip legt sich als eigenes Element um den Namen. Er ist das Flexelement in der
   Zeile, also muss die Regel auch auf ihm stehen. */
.mud-tooltip-root:has(.plan-layername) {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
}

/* Der wählbare Teil einer Layerzeile: Farbtupfer, Name und Zähler.

   Er nimmt den freien Platz der Zeile ein, damit auch daneben noch gewählt wird. Häkchen und
   Knöpfe stehen außerhalb — sie tun etwas anderes.

   Ein echtes <button> und kein <div> mit role: Damit kommt die Tastaturbedienung von selbst
   mit, statt dass Eingabe- und Leertaste von Hand nachgereicht werden müssten. Dafür muss
   das Knopfaussehen zurückgenommen werden — sonst steht hier ein grauer Kasten mit Rand. */
.plan-layerzeile {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 0;
    min-width: 0;
    padding: 2px 4px;
    border: none;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    border-radius: var(--mud-default-borderradius);
    cursor: pointer;
}

/* Dass sich hier etwas anklicken lässt, stand dem Namen nicht an: Er ist nicht
   unterstrichen und trägt keinen Rand. Der Schimmer beim Darüberfahren sagt es. */
.plan-layerzeile:hover {
    background: var(--mud-palette-action-default-hover);
}

.plan-layerzeile:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

/* Zähler und Knöpfe behalten ihre Breite — gekürzt wird der Name. */
.plan-nicht-schrumpfen {
    flex: 0 0 auto;
}

/* Dieselbe Liste im Register neben der Karte. Höher als die Tabelle unten, weil hier nur
   der Name steht und damit mehr Zeilen auf dieselbe Höhe passen. */
.plan-objektspalte {
    max-height: 420px;
    overflow-y: auto;
}

/* Die Maße eines Objekts im Nur-Lese-Modus: zwei schmale Spalten statt der Regler.

   Die Beschriftung links wird nicht umgebrochen — in der schmalen Seitenspalte rutschte
   „Umfang / Länge" sonst auf zwei Zeilen und die Werte daneben verrutschten gegeneinander. */
.plan-lesewerte td {
    border: 0;
    padding: 2px 8px 2px 0;
    font-size: .8125rem;
}

.plan-lesewerte td:first-child {
    white-space: nowrap;
    width: 1%;
}

/* Felder, die in der schmalen Eigenschaftenspalte die volle Breite nehmen sollen.

   Das min-width:0 ist der eigentliche Punkt: Ein Flexelement schrumpft von sich aus nicht
   unter seine eigene Inhaltsbreite. Ein langer Layername schob das Auswahlfeld deshalb über
   den Rand der Spalte hinaus, statt den Text zu kürzen. */
.plan-register .plan-volle-breite {
    width: 100%;
    min-width: 0;
}

/* Der Name des gewählten Objekts über den Eigenschaften — zugleich das Feld, in dem er sich
   ändern lässt. Es sieht aus wie eine Überschrift und ist keine: Ein eigenes Feld weiter unten
   wäre dieselbe Angabe an zwei Stellen. */
.plan-objektname {
    flex: 1 1 120px;
    min-width: 0;
    margin-top: 0;
}

/* Die Zeile darf nicht springen, sobald etwas gewählt ist: kein Innenabstand, den ein
   Textfeld sonst mitbringt, und die Schrift der Überschrift, die hier vorher stand. */
.plan-objektname .mud-input {
    margin-top: 0;
}

.plan-objektname input {
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.57;
    text-overflow: ellipsis;
}

/* Und der Text darin wird abgeschnitten statt übergeschoben. */
.plan-register .plan-volle-breite .mud-input-slot {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Innenabstand der Registerinhalte. Als Attribut auf MudTabs weist der Analyzer das zu Recht
   zurück (MUD0002) — hier gehört es ohnehin hin. */
.plan-register .mud-tabs-panels {
    padding: 8px;
}

/* Die Reiter zeigen nur ihr Sinnbild: Zu viert nähmen die Wörter in einer schmalen Spalte ein
   Vielfaches der Breite, und die Sinnbilder sind eindeutig genug.

   Die Beschriftung bleibt trotzdem im Markup stehen und wird nur auf Schriftgröße null
   gesetzt, statt sie wegzulassen. Der Unterschied ist nicht kosmetisch: Ein Reiter ohne Text
   hat für einen Screenreader keinen Namen und heißt dann „Registerkarte 2". Auf null gesetzte
   Schrift wird weiterhin vorgelesen — sie ist nur nicht zu sehen. Was sie sagt, erfährt auch
   die Maus, über den Hinweis beim Darüberfahren.

   Der schmale Innenabstand ist nicht Geschmackssache: Mit MudBlazors Vorgabe von 12 px je
   Seite brauchen vier Reiter mehr Platz, als die linke Spalte hat — dann blendet MudTabs
   Blätterpfeile ein, und sichtbar sind noch zwei Reiter. Mit 6 px passen alle vier nebeneinander,
   auch auf dem schmalsten Fenster, in dem die Spalte noch neben der Karte steht. */
.plan-register .mud-tab {
    font-size: 0;
    min-width: 0;
    padding-left: 6px;
    padding-right: 6px;
}

/* Die Ausrichtungszeile: Auswahlfeld und Drehknopf nebeneinander in einer schmalen Spalte.

   Das min-width:0 muss bis auf die innere Hülle des Feldes durchgreifen — MudBlazor setzt dort
   eine eigene Mindestbreite, und ein Flexelement schrumpft ohnehin nicht von sich aus unter
   seinen Inhalt. Ohne das ragt der Drehknopf aus dem Kasten. */
.plan-ausrichtung,
.plan-ausrichtung .mud-input-control,
.plan-ausrichtung .mud-input {
    flex: 1 1 auto;
    min-width: 0;
}

.plan-register .mud-tab .mud-icon-root {
    font-size: 1.5rem;
}

/* Der Kartenname in der Statusleiste. Lange Dateinamen — orthofoto_bd_2026_final_v3 — dürfen
   die Kennzahlen daneben nicht aus der Zeile schieben; jenseits von 220 px wird gekürzt.
   min-width:0 am Elternteil ist dafür Voraussetzung: Ein Flex-Kind schrumpft sonst nicht unter
   seine Inhaltsbreite, und text-overflow käme nie zum Zug. */
.plan-kartentitel {
    display: block;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Luft im Registerinhalt der Kartenspalte. MudTabs nimmt für den Panel-Inhalt kein
   Klassenattribut entgegen — der Analyzer weist PanelClass als MUD0002 zurück. */
.plan-kartenregister .mud-tabs-panels {
    padding: 12px;
}

/* Nur der offene Reiter trägt seinen Namen.

   Hier stand jeder Reiter stumm da, weil „Anlegen" und „Karten" samt Sinnbild nicht
   nebeneinander passten. „Anlegen" ist inzwischen ein Pluszeichen in der Kartenliste, es sind
   nur noch zwei — und trotzdem bleibt es knapp: In einer Spalte von einem Viertel Seitenbreite
   messen beide beschriftet 214 Pixel bei 214 Pixel Platz. Das passt, aber ohne einen Pixel
   Luft, und genau dann schiebt MudTabs Blätterpfeile davor; ein Wechsel kostete zwei Klicks
   statt einem. Nur der offene beschriftet braucht 168.

   Die Beschriftung bleibt bei allen im Markup stehen und wird nur auf Schriftgröße null
   gesetzt: Ein Reiter ohne Text hat für einen Screenreader keinen Namen und hieße
   „Registerkarte 2". Was ein stummer bedeutet, erfährt die Maus über den Hinweis beim
   Darüberfahren. */
.plan-kartenregister .mud-tab {
    font-size: 0;
}

.plan-kartenregister .mud-tab.mud-tab-active {
    font-size: .875rem;
}

.plan-kartenregister .mud-tab .mud-icon-root {
    font-size: 1.5rem;
}

/* --- Die Bildablage ----------------------------------------------------------------------

   Ein Bild am Objekt ist etwas anderes als ein Symbol: Es liegt nicht im Plan, sondern in den
   Eigenschaften, und es wird angesehen statt gedruckt. Deshalb kein 46-Pixel-Raster wie beim
   Symbolvorrat — ein Foto in Briefmarkengröße ist nicht wiederzuerkennen. */

.plan-bildwahl {
    display: flex;
    flex-direction: column;
    gap: 6px;
    width: 100%;
}

/* Der Name daneben darf die Zeile nicht sprengen: Ein Dateiname aus der Kamera ist lang, und
   die Knöpfe rechts sind wichtiger als sein Ende. */
.plan-bildname {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* Der Aufnahmeknopf. Ein Beschriftungselement und kein Knopf, weil das native Dateifeld darin
   liegt (siehe .plan-upload-input) — anders lässt sich der Dateidialog nicht öffnen, ohne
   über JavaScript zu gehen. Sieht aus wie die Symbolknöpfe daneben. */
.plan-bildzugabe {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    cursor: pointer;
    color: var(--mud-palette-action-default);
}

.plan-bildzugabe:hover {
    background: var(--mud-palette-action-default-hover);
}

/* Das gewählte Bild. Höhe gedeckelt, Breite voll: In der Spalte steht sonst ein Hochformat,
   das Farbe, Strich und alles Weitere nach unten schiebt. */
.plan-bildschau {
    display: block;
    line-height: 0;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    overflow: hidden;
    background: var(--mud-palette-background-gray);
}

.plan-bildschau img {
    width: 100%;
    max-height: 180px;
    object-fit: contain;
}

/* Das Raster der Ablage. Größer als beim Symbolvorrat und ohne eigenen Deckel: Das Register
   rollt bereits, wenn es eng wird (siehe .plan-registerspalte), und zwei ineinanderliegende
   Rollbereiche sind einer zu viel. */
.plan-bildraster {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    /* Sonst schneidet der Rand den Rahmen des gewählten Bildes an. */
    padding: 2px;
}

/* Im Dialog bleibt der Deckel: Dort gibt es keine Spalte, die mitwächst. */
.mud-dialog .plan-bildraster {
    max-height: 220px;
    overflow-y: auto;
}

.plan-bild-wahl {
    position: relative;
    width: 72px;
    height: 72px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid transparent;
    border-radius: 4px;
    cursor: pointer;
    overflow: hidden;
    background: var(--mud-palette-background-gray);
}

.plan-bild-wahl:hover {
    border-color: var(--mud-palette-lines-default);
}

.plan-bild-wahl-aktiv,
.plan-bild-wahl-aktiv:hover {
    border-color: var(--mud-palette-primary);
}

/* Ausgefüllt statt eingepasst: Im Raster zählt das Wiedererkennen, und ein Foto mit weißen
   Balken links und rechts erkennt man schlechter als seinen Ausschnitt. Groß steht es
   darüber, vollständig hinter dem Klick. */
.plan-bild-wahl img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Das Löschzeichen in der Ecke.

   Blass und nicht versteckt: Erst beim Darüberfahren zu erscheinen wäre zurückhaltender, aber
   auf einem Tablet gibt es kein Darüberfahren — der Knopf wäre dort schlicht nicht zu
   erreichen, und das Aufräumen der Ablage ginge nur noch am Schreibtisch. Ein halb
   durchsichtiges Kreuz von zweiundzwanzig Punkten macht aus dem Vorrat noch keine
   Abrissliste. */
.plan-bild-weg {
    position: absolute;
    top: 0;
    right: 0;
    display: flex;
    opacity: .45;
    transition: opacity .15s ease;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 0 2px 0 4px;
    cursor: pointer;
    color: #fff;
    background: rgba(0, 0, 0, .55);
}

.plan-bild-wahl:hover .plan-bild-weg,
.plan-bild-weg:focus-visible {
    opacity: 1;
}

.plan-bild-weg:hover {
    opacity: 1;
    background: var(--mud-palette-error);
}

/* Die Kacheln der Startseite.
   ------------------------------------------------------------------------------------
   Der Anker trägt die Höhe und gibt Farbe und Unterstreichung ab; die Karte darin bleibt
   eine gewöhnliche MudPaper. Andersherum — Klickbehandlung auf der Karte — verlöre man
   Tastatur, Mittelklick und die Statuszeile des Browsers. */

.plan-startkachel {
    display: block;
    height: 100%;
    color: inherit;
    text-decoration: none;
}

.plan-startkachel .mud-paper {
    transition: border-color .15s, box-shadow .15s;
}

.plan-startkachel:hover .mud-paper {
    border-color: var(--mud-palette-primary);
    box-shadow: var(--mud-elevation-2);
}

/* Der Rahmen gehört an die Karte und nicht an den Anker: Am Anker säße er außen um einen
   Kasten ohne eigene Kanten und liefe an den abgerundeten Ecken daneben. */
.plan-startkachel:focus-visible {
    outline: none;
}

.plan-startkachel:focus-visible .mud-paper {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

/* Die Lizenzseite.
   ------------------------------------------------------------------------------------
   Vorformatiert, weil die Hinweisdatei ihre Spalten selbst setzt — die Punktreihen vor den
   Lizenznamen stehen nur in einer Schrift mit fester Breite gerade. Umbruch trotzdem
   erlaubt: Auf einem Telefon wäre waagrechtes Scrollen über vierhundert Zeilen eine
   Zumutung. */

.plan-lizenztext {
    margin: 0;
    font-family: ui-monospace, "Cascadia Mono", "Consolas", monospace;
    font-size: .8125rem;
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: var(--mud-palette-text-primary);
}
