:root{
  --bg1:#eef1f7; --bg2:#e6eaf3;
  --card:#ffffff; --line:#e6e8ef; --line2:#eef0f5; --hover:#f5f7fb;
  --ink:#191c22; --ink2:#59616f; --ink3:#8a91a0;
  --blue:#2f6bff; --blue-ink:#1e57e8; --blue-soft:#e9f0ff; --active:#e6edfb;
  --green:#1f9d6b; --green-soft:#e6f5ee;
  /* Hetzner-Rot aus der Wortmarke. --red-ink ist die abgedunkelte Fassung für Text
     auf --red-soft: das Marken-Rot selbst hat auf dem hellen Feld zu wenig Kontrast. */
  --red:#D50C2D; --red-ink:#a5091f; --red-soft:#fdeaed;
  --radius:14px; --radius-sm:10px;
  --shadow:0 1px 2px rgba(16,24,40,.04),0 1px 3px rgba(16,24,40,.05);
  --rail:64px; --side:248px; --top:64px;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Inter,Helvetica,Arial,sans-serif;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  font-family:var(--font); color:var(--ink);
  background:linear-gradient(180deg,var(--bg1),var(--bg2));
  min-height:100vh; -webkit-font-smoothing:antialiased; font-size:14px;
}
button{font-family:inherit;cursor:pointer}
a{color:var(--blue-ink);text-decoration:none}
a:hover{text-decoration:underline}
svg{display:block}
/* Ein Icon *in* einem Link steht neben dem Text, nicht darunter: die Regel oben
   macht jedes SVG zum Block, was in Flex-Containern gewollt ist (dort ordnet der
   Container an) und in einem Textlink falsch — das Externer-Link-Zeichen brach in
   die nächste Zeile um. */
a svg{display:inline-block;vertical-align:-2px}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

.topbar{
  position:fixed;top:0;left:0;right:0;height:var(--top);z-index:40;
  display:flex;align-items:center;gap:20px;padding:0 22px;
  background:linear-gradient(180deg,#f3f5fa,#eef1f7);
}
/* Die Wortmarke bringt ihre eigenen Proportionen mit; das Feld richtet sie nur
   aus. Feste Breite/Höhe am <img>, damit die Kopfzeile beim Laden nicht springt. */
.topbar .logo{
  flex:none;display:flex;align-items:center;height:34px;
}
.topbar .logo img{display:block;height:23px;width:auto}
.topbar .logo:hover{text-decoration:none;opacity:.8}
.topbar .search{
  flex:1;max-width:620px;margin:0 auto;position:relative;
}
.topbar .search input{
  width:100%;height:40px;border:1px solid var(--line);border-radius:999px;background:#fff;
  padding:0 44px;font-size:14px;color:var(--ink);outline:none;
}
.topbar .search input:focus{border-color:#c7d3ee;box-shadow:0 0 0 3px rgba(47,107,255,.12)}
.topbar .search .mag{position:absolute;left:16px;top:11px;color:var(--ink3)}
.topbar .search .kbd{position:absolute;right:16px;top:11px;color:var(--ink3);font-size:12px}
.topright{display:flex;align-items:center;gap:16px;color:var(--ink2)}
.topright .ic{width:30px;height:30px;display:grid;place-items:center;border-radius:8px}
.topright .ic:hover{background:#e6ebf4}
.avatar{text-decoration:none;width:34px;height:34px;border-radius:50%;background:#bfebe6;color:#0a6b60;font-weight:700;display:grid;place-items:center;font-size:13px}

.shell{display:flex;padding-top:var(--top);min-height:100vh}

.gside{width:288px;flex:none;padding:18px}
.gcard{background:#fff;border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);padding:12px;min-height:calc(100vh - var(--top) - 36px);display:flex;flex-direction:column}
.navitem{
  display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:10px;color:var(--ink);
  font-weight:500;border:none;background:none;width:100%;text-align:left;font-size:14px;
}
.navitem:hover{background:var(--hover)}
.navitem.active{background:var(--active);font-weight:600}
.navitem .ci{color:var(--ink2);flex:none}
.navitem.active .ci{color:var(--blue-ink)}
.gcard .spacer{flex:1}

.rail{
  width:var(--rail);flex:none;padding:16px 0;margin:18px 0 18px 18px;
  background:#fff;border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);
  display:flex;flex-direction:column;align-items:center;gap:6px;height:calc(100vh - var(--top) - 36px);position:sticky;top:calc(var(--top) + 18px)
}
.railbtn{width:38px;height:38px;border-radius:10px;display:grid;place-items:center;border:none;background:none;color:var(--ink2)}
.railbtn:hover{background:var(--hover)}
.railbtn.active{background:var(--active);color:var(--blue-ink)}
.rail .rspacer{flex:1}

.pside{width:var(--side);flex:none;padding:18px 14px 18px 14px}
.pcard{background:#fff;border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);overflow:hidden}
.phead{padding:0}
/* Kopf der Projektleiste: Motiv-Fliese, Name, Kurz-ID.

   Auf `.pcard` eingegrenzt, und das ist eine Fehlerbehebung, keine Kosmetik:
   `.pname` stand hier als nackte Klasse und traf damit *jedes* .pname im Panel —
   auch das in der Projektkachel und das in der Organisationskachel. Die erbten
   `padding:16px 16px 6px`, während die Zeile mit dem Organisationsnamen direkt
   darunter nur das Polster ihres Elternteils hatte. Ergebnis: der Projektname saß
   16px weiter rechts als die Zeile darunter — ein Versatz, den man sieht und dem
   man im Kachel-CSS nicht ansieht, woher er kommt.

   Beide Nutzer dieser Regeln — die Projekt-Seitenleiste und die Profilleiste —
   liegen in einer `.pcard`, also ist das die richtige Grenze. */
.pcard .pname{padding:16px 16px 6px;display:flex;align-items:center;gap:11px;min-width:0}
.pcard .pname h3{margin:0;font-size:16px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pcard .pname .pid{font-family:var(--mono);font-size:11.5px;color:var(--ink3)}
.pcard .pname .pmono{width:38px;height:38px;flex:none;border-radius:10px;display:grid;place-items:center;
  background:var(--hover);color:var(--ink2);font-weight:700;font-size:13px}
.pnav{padding:8px 8px 12px}
.pgroup{margin-top:10px}
.pgroup > .lbl{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;color:var(--ink2);font-size:12px;font-weight:700;letter-spacing:.02em;text-transform:none}
.pnav .navitem{padding:8px 10px;font-size:13.5px}

.main{flex:1;min-width:0;padding:22px 30px 60px}
/* Der Abstand zur Überschrift war 6px — Brotkrume und H1 klebten aneinander und
   lasen sich wie eine zweizeilige Überschrift. 14px plus die 2px der .h1row ergeben
   16px: genug, dass der Pfad als eigene Zeile erkennbar ist. */
.crumbs{color:var(--ink2);font-size:13px;margin-bottom:14px;display:flex;gap:8px;align-items:center}
.crumbs a{color:var(--ink2)}
.crumbs b{color:var(--ink)}
.h1row{display:flex;align-items:center;justify-content:space-between;margin:2px 0 20px}
h1{font-size:27px;margin:0;letter-spacing:-.3px}
.card{background:#fff;border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow)}
.card.pad{padding:22px 24px}
/* `pad` auch **ohne** `card`: ein Block innerhalb einer Karte, der Abstand zum Rand
   braucht — der Inhalt eines Reiters etwa, der unter der Reiterleiste in derselben
   Karte steht.

   Die Regel fehlte, und fünf Vorlagen benutzten die Klasse trotzdem
   (E-Mails, Backups, Sicherheit, Installationen, Speicher). Ihr Inhalt klebte
   dadurch an der Kartenkante: Überschrift, Tabellenzeilen und Fließtext liefen
   randlos von Kante zu Kante, und die Seite sah aus, als sei das Layout verrutscht.
   Genau so ist es gemeldet worden. */
.pad{padding:22px 24px}
/* Nur für senkrecht gestapelte Karten im Inhaltsbereich. Ohne die Einschränkung
   trifft die Regel auch Karten in einem Raster oder Flex-Layout, wo der Abstand
   schon aus gap kommt — dort verschiebt sie die erste Kachel gegen alle anderen. */
.main > .card + .card{margin-top:18px}

.btn{height:38px;padding:0 16px;border-radius:9px;border:1px solid var(--line);background:#fff;color:var(--ink);font-weight:600;font-size:13.5px;display:inline-flex;align-items:center;gap:8px}
.btn:hover{background:var(--hover)}
.btn.primary{background:var(--blue);border-color:var(--blue);color:#fff}
.btn.primary:hover{background:var(--blue-ink)}
/* Ein abgeschalteter Knopf sah bis 2026-09-06 aus wie ein benutzbarer — voll gefärbt,
   mit Hover-Reaktion, und beim Klick geschah nichts. Das liest sich nicht als „geht
   gerade nicht", sondern als kaputte Plattform, und der Kunde klickt mehrmals.
   Zentral hier und nicht je Seite: `disabled` heißt überall dasselbe. */
.btn:disabled,.btn[disabled]{opacity:.5;cursor:not-allowed}
.btn:disabled:hover,.btn[disabled]:hover{background:#fff}
.btn.primary:disabled:hover,.btn.primary[disabled]:hover{background:var(--blue)}

.toolbar{display:flex;align-items:center;gap:10px;padding:14px 16px;flex-wrap:wrap}
.toolbar .grow{flex:1;min-width:120px}
.tsearch{position:relative;flex:1;min-width:160px}
.tsearch input{width:100%;height:40px;border:1px solid var(--line);border-radius:9px;padding:0 14px 0 40px;outline:none;font-size:14px}
.tsearch input:focus{border-color:#c7d3ee;box-shadow:0 0 0 3px rgba(47,107,255,.12)}
.tsearch .mag{position:absolute;left:13px;top:11px;color:var(--ink3)}

.tabs{display:flex;gap:4px;padding:10px 12px 0;border-bottom:1px solid var(--line2);background:linear-gradient(180deg,#eef2f8,#fff);border-radius:16px 16px 0 0}
.tab{padding:11px 16px;border:none;background:none;color:var(--ink2);font-weight:600;font-size:14px;border-bottom:2px solid transparent;margin-bottom:-1px}
.tab:hover{color:var(--ink)}
.tab.active{color:var(--ink);border-bottom-color:var(--blue);background:#fff;border-radius:8px 8px 0 0}

.row{display:flex;align-items:center;gap:14px;padding:16px 18px;border-top:1px solid var(--line2)}
.row:first-child{border-top:none}
.list{padding:6px 0}
.listwrap{padding:6px 8px 10px}
.ricon{width:34px;height:34px;border-radius:8px;display:grid;place-items:center;flex:none;background:#f1f4f9}
.rmain{flex:1;min-width:0}
.rmain .t{font-weight:600;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.rmain .s{color:var(--ink2);font-size:12.5px;margin-top:1px}
.rmeta{color:var(--ink2);font-size:13px;display:flex;align-items:center;gap:8px}
.dots{color:var(--ink3);font-weight:800;letter-spacing:1px;padding:6px 8px;border-radius:8px}
.dots:hover{background:var(--hover)}
.badge{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:600;padding:3px 9px;border-radius:999px}
.badge.blue{background:var(--blue-soft);color:var(--blue-ink)}
.badge.gray{background:#eef0f4;color:var(--ink2)}
.badge.green{background:var(--green-soft);color:var(--green)}
/* Hetzner-Rot, und nur hier: als Bedeutung („kaputt"), nicht als Dekoration. Ohne
   diese Stufe hatte die Phase `error` dieselbe graue Plakette wie „wird angelegt" —
   zwei Zustände, die man auseinanderhalten muss, sahen gleich aus. */
.badge.red{background:var(--red-soft);color:var(--red-ink)}
/* Bernstein für den Zustand dazwischen: nicht kaputt, aber auch nicht fertig. Gebraucht
   für Domains im Zustand „Bestand" — sie funktionieren, brauchen aber noch einen
   Nachweis. Grau hätte sie wie „vorbereitet" aussehen lassen, und genau diese beiden
   muss man auseinanderhalten. */
.badge.amber{background:#fff8e8;color:#7a5a12}
/* Ein Hinweis unter einer Zeile, der nicht übersehen werden darf — der Satz, warum eine
   Prüfung nicht durchging. Kein Flash: er gehört an die Zeile, auf die er sich bezieht,
   und er bleibt beim nächsten Aufruf stehen, solange der Grund gilt. */
.s.warn{color:#7a5a12}
/* Adresse als zwei Felder mit einem @ dazwischen: der Name wird getippt, die Domain
   gewählt. Sie sitzen in einer Zeile, damit sie als eine Angabe gelesen werden und
   nicht als zwei unabhängige Felder. */
.addrsplit{display:flex;align-items:center;gap:8px}
.addrsplit input{flex:1 1 auto;min-width:0}
.addrsplit select{flex:0 1 auto;max-width:60%}
.addrsplit .at{color:var(--ink2);font-weight:600}
/* Der TXT-Eintrag zum Abschreiben: Beschriftung links, Wert rechts auf voller Breite.
   Der Wert ist 47 Zeichen und darf nicht umbrechen wie ein Satz — wer ihn falsch
   überträgt, sucht den Fehler danach bei seinem DNS-Anbieter. */
.txtrecord{display:grid;grid-template-columns:64px 1fr;gap:4px 12px;margin:10px 0;
  padding:10px 12px;background:#f7f8fa;border:1px solid var(--line,#e6e8ee);border-radius:8px}
.txtrecord dt{color:var(--ink2);font-size:12px;align-self:center}
.txtrecord dd{margin:0;overflow-wrap:anywhere;user-select:all}
/* Der Hinweistext unter dem Eintrag braucht Luft nach unten: darunter beginnt beim
   Plattform-Administrator sofort ein Formular, und ohne Abstand las sich dessen
   Beschriftung wie die Fortsetzung des Satzes. */
.txtrecord ~ .s{max-width:760px;margin-bottom:14px}
.count{text-align:center;color:var(--ink2);font-size:13px;padding:14px}

/* ── Was direkt in einer Karte steht, braucht ihren Einzug ──

   Eine `.card` hat selbst kein Padding; den Einzug bringt jeder Abschnitt mit —
   `.row` 18px, `.tabs` 12px, `.count` 14px. Ein Formular oder ein Hinweistext, der
   ohne Abschnitt dazwischen direkt in der Karte (oder in einer `.list` neben den
   Zeilen) steht, hat keinen: er klebt links UND rechts auf der Kante. Auf
   /domains betraf das drei Stellen — das Feld „Domain hinzufügen", der Absatz
   darunter und das Rücknahme-Formular in der Liste.

   18px, weil das der Einzug der Zeilen darüber ist. Eine eigene Zahl wäre eine
   zweite Kante, und zwei Kanten in einer Karte sieht man sofort, ohne sagen zu
   können warum.

   `:not(.pad)`, weil eine Karte mit `pad` ihren Einzug schon hat — dort käme er
   sonst doppelt. */
.card:not(.pad) > .wform,
.list > .wform{padding:0 18px 10px}
.card:not(.pad) > .s,
.list > .s{padding:0 18px 16px}

.grid2{display:grid;grid-template-columns:1fr 1fr;gap:26px 40px}
.field .k{color:var(--ink2);font-size:13px;margin-bottom:4px}
.field .v{font-size:14.5px;display:flex;align-items:center;gap:8px}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;background:#f3f5f9;padding:2px 7px;border-radius:6px;font-size:13px}
.cp{color:var(--ink3);cursor:pointer}
.cp:hover{color:var(--ink)}
.sectitle{font-size:17px;margin:0 0 4px;display:flex;align-items:center;justify-content:space-between}

.bar{height:14px;border-radius:999px;background:#eef1f6;overflow:hidden}
.bar > span{display:block;height:100%;background:var(--green);border-radius:999px}
/* Der Balken der Verbrauchsanzeige färbt sich nach demselben Zustand, aus dem auch die
   Warnmail entsteht — nicht nach einer zweiten Schwelle im Stylesheet. Zwei Bewertungen
   derselben Zahl wären der Fall, in dem die Oberfläche „gelb" zeigt, während der Kunde
   eine Mail mit „kritisch" im Postfach hat. */
.bar > span.warning{background:#c9931f}
.bar > span.critical{background:var(--red)}

.empty{padding:60px 20px;text-align:center;color:var(--ink2)}
.empty .eic{width:56px;height:56px;border-radius:14px;display:grid;place-items:center;margin:0 auto 14px;color:var(--ink3)}
.empty h3{color:var(--ink);margin:0 0 6px;font-size:18px}


/* ── Titelbilder ──
   Zwei Quellen, ein Aussehen: entweder ein hochgeladenes Bild (.coverimg) oder
   ein aus der Kurz-ID gerechnetes Motiv (.coverart, siehe CoverArt). Beide
   füllen ihr Feld vollständig und werden beschnitten, statt sich zu verzerren —
   ein gestauchtes Kundenlogo ist schlimmer als ein angeschnittenes.

   Die width/height-Zeile zählt auch für das SVG: ohne sie bringt ein SVG mit
   viewBox seine eigene Größe mit und füllt das Feld nicht aus. */
.coverimg,.coverart{display:block;width:100%;height:100%;object-fit:cover}

/* ── Projektkacheln ──
   Vorher stand hier ein nachgebauter Website-Screenshot aus dem Prototyp:
   Oliv-Gradient, giftgelbe Schrift auf dunklem Balken, Georgia kursiv — und damit
   eine Behauptung darüber, wie die Seite des Kunden aussieht. Der wurde zu Recht
   entfernt.

   Das Bild ist jetzt wieder da, behauptet aber nichts: es ist entweder eines, das
   jemand selbst hinterlegt hat, oder ein abstraktes Motiv mit genau einer Aufgabe
   — dieses Projekt in einer Liste von dreißig wiedererkennbar zu machen, bevor
   der erste Name gelesen ist. */
/* Seitliches Polster wie in der Werkzeugleiste darüber (.toolbar: 16px). Es waren
   8px, und damit standen die Kacheln sichtbar weiter außen als die Knöpfe, die
   sie sortieren — zwei Kanten in einer Karte, wo eine hingehört. */
.projgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(246px,1fr));gap:18px;padding:8px 16px 14px}
.projcard{
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  color:var(--ink);text-decoration:none;
  transition:border-color .14s ease,box-shadow .14s ease,transform .14s ease;
}
.projcard:hover{border-color:#cfd6e4;box-shadow:0 6px 18px rgba(16,24,40,.09);
  transform:translateY(-2px);text-decoration:none}
/* Sichtbarer Tastaturfokus: die Kachel ist ein Link und muss ohne Maus auffindbar sein. */
.projcard:focus-visible{outline:2px solid var(--blue);outline-offset:2px}

/* Das Bildfeld. aspect-ratio statt fester Höhe, damit die Kachel in jeder
   Rasterbreite dieselben Proportionen behält — 16:9, weil die Kacheln breiter als
   hoch sind und ein quadratisches Feld die halbe Karte fräße. */
.pcover{position:relative;display:block;aspect-ratio:16/9;overflow:hidden;background:var(--hover)}
/* Abdunkelung nach unten. Auf der Kachel fällt sie kaum auf, aber es ist dieselbe
   Schicht wie im Projektkopf, wo weiße Schrift darauf liegt — eine Regel für
   beide, damit ein helles Kundenbild nicht an einer Stelle Text verschluckt. */
.pcover::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(10,14,22,0) 58%,rgba(10,14,22,.20))}
.pcover .coverimg,.pcover .coverart{transition:transform .35s ease}
.projcard:hover .pcover .coverimg,.projcard:hover .pcover .coverart{transform:scale(1.045)}
@media (prefers-reduced-motion:reduce){
  .projcard{transition:none}
  .pcover .coverimg,.pcover .coverart{transition:none}
  .projcard:hover .pcover .coverimg,.projcard:hover .pcover .coverart{transform:none}
}

.projcard .pbody{display:flex;flex-direction:column;gap:2px;min-width:0;padding:12px 14px 10px}
.projcard .pname{font-weight:600;font-size:14.5px;line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.projcard .porg{font-size:12.5px;color:var(--ink2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Haarlinie über der Fußzeile: trennt, was der Mensch benannt hat, von dem, was
   das System vergibt. */
.projcard .pmeta{display:flex;align-items:center;justify-content:space-between;gap:8px;
  margin-top:auto;padding:9px 14px 11px;border-top:1px solid var(--line2)}
.projcard .pid{font-family:var(--mono);font-size:12px;color:var(--ink3)}
.table{width:100%;border-collapse:collapse}
.table td{padding:12px 14px;border-top:1px solid var(--line2);font-size:13.5px}
.table tr:first-child td{border-top:none}
.table td.k{color:var(--ink2);width:150px}
.hint{font-size:12.5px;color:var(--ink2)}
.tabinput{flex:1;height:44px;border:1px solid var(--line);border-radius:9px;padding:0 14px;background:#f7f9fc}
/* ── Schmale Fenster: der Inhalt steht UNTER der Navigation, nicht neben ihr ──

   Hier stand nur `.gside{width:100%}`. Die Seitenleiste wurde damit breit, aber
   `.shell` blieb eine Flex-Zeile ohne Umbruch — der Inhalt rutschte also nicht
   darunter, sondern daneben und aus dem Bild. Auf einem Telefon sah man die
   Navigation und sonst nichts; gemessen bei 390 px Körperbreite stand `.main` bei
   `left: 390px`, also vollständig außerhalb.

   `flex-direction:column` ist die eigentliche Umstellung, die Breite von `.gside`
   folgt daraus. Und `.gcard` muss seine Bildschirmhöhe abgeben: nebeneinander ist
   eine Navigationsspalte über die volle Höhe richtig, untereinander wäre sie eine
   leere Karte, an der man erst vorbeirollt, bevor der Inhalt anfängt. */
@media(max-width:900px){
  .shell{flex-direction:column}
  .gside{width:100%} .grid2{grid-template-columns:1fr}
  .gcard{min-height:0}
  .rail{display:none} .pside{display:none}

  /* ── Und nichts darf die Seite breiter machen, als der Bildschirm ist ──

     Das steht hier nicht der Ordnung halber. Eine `position:fixed`-Leiste richtet
     sich nach dem Layout-Viewport, und den zieht **jedes** überbreite Element auf:
     solange die Servertabelle 816 px belegte, war die Kopfzeile 847 px breit und
     „Abmelden" lag außerhalb — obwohl an der Kopfzeile selbst nichts fehlte.
     Gemessen: mit diesen drei Regeln fällt die Seitenbreite von 847 auf 390, und
     erst dadurch wird der Abmelde-Weg erreichbar.

     Die Tabelle wird selbst zum Rollbereich und nicht ihre `.card`: in den Karten
     hängen die „•••"-Menüs, die ein `overflow` an der Karte abschneiden würde. */
  .main table.table{display:block;overflow-x:auto;max-width:100%}
  .tabs{flex-wrap:wrap}
  .pid{overflow-wrap:anywhere}
}

/* ── Die Kopfzeile bricht um, statt hinauszulaufen ──

   `.topbar` ist `position:fixed` und war ebenfalls eine Zeile ohne Umbruch. Was
   nicht hineinpasste, lag rechts außerhalb — und weil die Leiste fixiert ist,
   kam man auch nicht dorthin. Gemessen bei 390 px: „Abmelden" lag bei 415–445 px.
   Der Weg aus der Sitzung war auf dem Telefon schlicht nicht erreichbar.

   Umbrechen und nicht ausblenden: die Rolle („Super-Admin") ändert, was jemand
   sieht, und die angemeldete Adresse sagt, als wer er es sieht. Beides wegzulassen
   wäre der bequeme Weg — er nimmt aber genau die zwei Angaben weg, wegen derer die
   Leiste überhaupt etwas anzeigt. Zwei Zeilen kosten 44 px Höhe, und die gibt
   `--top` an alle weiter, die sich darauf verlassen (`.shell` schiebt ihren Inhalt
   darunter, `.gcard` und `.rail` rechnen ihre Höhe daraus). Deshalb wird hier die
   Variable geändert und nicht die Höhe an einer einzelnen Regel. */
@media(max-width:700px){
  :root{--top:108px}
  .topbar{
    height:auto;min-height:var(--top);flex-wrap:wrap;align-content:center;
    padding:10px 14px;gap:6px 12px;
  }
  .topright{width:100%;min-width:0;gap:12px}
  /* Die Adresse bekommt den Rest der Zeile und kürzt sich selbst (sie hat bereits
     `text-overflow:ellipsis`). Ohne `min-width:0` gäbe ein Flex-Kind seine
     Inhaltsbreite nicht her und schöbe Abmelden und Avatar wieder hinaus. */
  .topbar .who{max-width:none;flex:1 1 auto;min-width:0}
  .topright .ic,.topright .avatar{flex:none}
}

/* Flash-Meldungen nach einem Schreibvorgang.

   Drei Stufen, weil sie drei verschiedene Dinge bedeuten: success = erledigt,
   error = die Eingabe war das Problem (der Nutzer kann es beheben), warning =
   die Plattform war das Problem (kann er nicht). Alle gleich einzufaerben wuerde
   genau diese Unterscheidung wegwerfen. */
.flash{padding:12px 16px;border-radius:9px;margin:0 0 16px;font-size:14px;border:1px solid}
.flash-success{background:#eefaf3;border-color:#bfe8d2;color:#1c6b45}
.flash-error{background:#fdeeee;border-color:#f2c9c9;color:#8b2020}
.flash-warning{background:#fff8e8;border-color:#f0dcae;color:#7a5a12}
/* Projekt im Abbau: Anlege-Formulare und -Knöpfe tragen `data-creates` und verschwinden.
   Die API weist sie ohnehin mit 409 ab; siehe layouts/project.html.twig. */
.main.is-deleting [data-creates]{display:none !important}

/* Formularzeilen in den Karten. */
.wform{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;margin:0 0 6px}
/* Eingabefeld und Knopf nebeneinander sollen auf einer Höhe stehen.
   `.wform` richtet an der UNTERKANTE aus. Das stimmt, solange unter dem
   Eingabefeld nichts mehr kommt — trägt ein Feld darunter noch einen Hinweis oder
   (beim Passwort) Stärkebalken und Hinweiszeile, rutscht der Knopf um deren Höhe
   nach unten. Gemessen: 4 px bei den Cronjobs, 29 px beim Passwort.
   Der Knopf richtet sich in diesen Fällen an der OBERKANTE aus. Von oben ist der
   Abstand fest — eine Beschriftungszeile, in beiden Feldarten 18 px —, von unten
   wächst er mit jeder Zeile, die ein Hinweis umbricht. Deshalb hielten die
   handgeschriebenen `margin-top`-Werte an den einzelnen Formularen nicht. */
.wform{--fieldlabel:18px}
.wform:has(.pwfield) > .btn,
.wform:has(.wfield > .hint) > .btn{align-self:flex-start;margin-top:var(--fieldlabel)}
/* Dasselbe gilt für die Felder untereinander: ein Hinweis unter EINEM Feld
   („Mindestens 12 Zeichen.") schob bei Ausrichtung an der Unterkante dessen
   Beschriftung höher als die der Nachbarn. Mit Hinweis also alles an die Oberkante —
   die Beschriftungen sind einzeilig und stehen dann auf einer Linie. */
.wform:has(.wfield > .hint),
.wform:has(.pwfield){align-items:flex-start}
.wfield{display:flex;flex-direction:column;gap:4px}
.wfield label{font-size:12px;color:var(--ink2)}
/* Häkchen und Auswahlknöpfe sind ausgenommen.
   Ohne die Ausnahme galt `height:40px;padding:0 12px` auch für sie: das Häkchen wurde
   ein 40 Pixel hoher, gestreckter Kasten neben einer umgebrochenen Beschriftung — eine
   Regel für Texteingaben, angewendet auf etwas, das keine ist. Zweimal aufgefallen
   (Zusatzleistung im Anlege-Formular, „danach aufhören“ bei den Filterregeln), deshalb
   hier an der Wurzel und nicht ein drittes Mal als Ausnahme daneben. */
.wfield input:not([type=checkbox]):not([type=radio]),.wfield select{height:40px;border:1px solid var(--line);border-radius:9px;padding:0 12px;background:#f7f9fc;font-size:14px}
.wfield input[type=checkbox],.wfield input[type=radio]{width:15px;height:15px;margin:0;flex:0 0 auto;accent-color:var(--blue)}
.wfield input[type=text],.wfield input[type=email],.wfield input[type=password]{min-width:220px}
.wfield-narrow input{width:110px}
/* Ein Datumsfeld braucht mehr als 110px: der Browser zeichnet dort tt.mm.jjjj plus
   Kalendersymbol, und bei der schmalen Breite steht sichtbar „tt.mm.j" — was wie ein
   Fehler aussieht und keiner ist. */
.wfield-narrow input[type=date]{width:160px}
/* Eingaben in einer Schlüssel/Wert-Tabelle (z. B. die PHP-Einstellungen) tragen
   denselben Stil wie ein `.wfield`-Feld — sonst stehen auf einer Seite zwei
   Sorten Eingabefelder, und das liest sich wie ein halbfertiges Formular. Gleiche
   Höhe in beiden Spalten, damit Name und Wert nebeneinander nicht springen. */
.kvtable td{padding:5px 8px;vertical-align:middle}
.kvtable input[type=text]{box-sizing:border-box;width:100%;height:40px;border:1px solid var(--line);border-radius:9px;padding:0 12px;background:#f7f9fc;font-size:14px;font-family:inherit}
.kvtable input[type=text]:focus{border-color:var(--blue);outline:none}
.kvtable input[type=text].mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}
/* Ein schreibgeschützter Schlüssel (memory_limit …) sieht gedämpft aus, damit klar
   ist, dass hier der Wert daneben geändert wird, nicht der Name. */
.kvtable input[readonly]{background:#eef1f6;color:var(--ink2);cursor:default}

/* Ordnerwahl: der gewählte Pfad neben dem Knopf, auf gleicher Höhe. */
.dirpick{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.dirpick-value{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  height:40px;display:flex;align-items:center;padding:0 12px;border:1px solid var(--line);
  border-radius:9px;background:#f7f9fc}
.dirpick > .btn{flex:0 0 auto}

/* Der Lichtkasten selbst. `hidden` blendet ihn aus; sichtbar liegt er über allem. */
.lightbox[hidden]{display:none}
.lightbox{position:fixed;inset:0;z-index:100;background:rgba(20,28,44,.45);
  display:flex;align-items:center;justify-content:center;padding:20px}
.lightbox-panel{background:#fff;border-radius:14px;width:min(560px,100%);max-height:80vh;
  display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,.28)}
.lightbox-head{display:flex;align-items:center;justify-content:space-between;
  padding:14px 16px;border-bottom:1px solid var(--line)}
.lightbox-title{font-weight:600;font-size:15px}
.dirpick-close{font-size:20px;line-height:1}
.dirpick-crumb{padding:10px 16px;font:13px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;
  color:var(--ink2);border-bottom:1px solid var(--line2);word-break:break-all}
.dirpick-list{overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:2px}
.dirpick-item{display:flex;align-items:center;justify-content:space-between;gap:8px;
  width:100%;text-align:left;padding:9px 12px;border:1px solid transparent;border-radius:9px;
  background:none;font:inherit;font-size:14px;color:var(--ink);cursor:pointer}
.dirpick-item:hover{background:#f2f5fb;border-color:var(--line)}
.dirpick-up{color:var(--ink2)}
.dirpick-empty{padding:12px;color:var(--ink2);font-size:13px}
.lightbox-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;padding:12px 16px;border-top:1px solid var(--line)}
.dirpick-current{font-size:13px;color:var(--ink2)}
.dirpick-current code{font-size:12.5px}

/* Auslastung: CPU und Arbeitsspeicher einer App, mit einem Balken für den Speicher. */
.usagebox{margin-top:16px;padding:14px 16px;border:1px solid var(--line);border-radius:11px;background:#f7f9fc;max-width:520px}
.usagehead{font-weight:600;font-size:14px;margin-bottom:8px}
.usagerow{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:3px 0}
.usagelabel{color:var(--ink2);font-size:13px}
.usageval{font-variant-numeric:tabular-nums;font-size:14px;font-weight:600}
.usagebar{height:8px;border-radius:6px;background:#e4e9f2;overflow:hidden;margin:6px 0 2px}
.usagebar-fill{height:100%;background:var(--blue);border-radius:6px}

/* Leistungsanalyse: die Kurven je App, CPU und Speicher nebeneinander. */
.perfrow{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:10px}
@media (max-width:820px){.perfrow{grid-template-columns:1fr}}
.perfrow-3{grid-template-columns:repeat(3,1fr)}
@media (max-width:1100px){.perfrow-3{grid-template-columns:1fr}}
.perfmetric-head{font-size:13px;color:var(--ink2);margin-bottom:4px;display:flex;align-items:baseline;gap:8px}
.perfnow{font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
.perfchart{width:100%}
.perfsvg{width:100%;height:150px;display:block}
.perfgrid{stroke:#e4e9f2;stroke-width:1}
.perflimit{stroke:#d98a2b;stroke-width:1;stroke-dasharray:4 3}
.perfaxis{fill:var(--ink2);font-size:10px;font-family:inherit}
.perfline{fill:none;stroke-width:1.6;vector-effect:non-scaling-stroke}
.perfline-cpu{stroke:var(--blue)}
.perfline-mem{stroke:#2ba36b}
.perfarea{stroke:none;opacity:.12}
.perfarea-cpu{fill:var(--blue)}
.perfarea-mem{fill:#2ba36b}
.perfempty{color:var(--ink2);font-size:13px;padding:20px 0}
/* Fluss der Reconciler: kompakte Karten in einem Raster, je eine Dauer-Kurve. */
.flowgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-top:10px}
@media (max-width:900px){.flowgrid{grid-template-columns:1fr}}
.flowcard{margin:0}
.flowhead{display:flex;align-items:baseline;justify-content:space-between;gap:8px}
.flowname{font-weight:600;font-size:14px}
.flowmeta{display:flex;align-items:center;gap:8px}
.flowchart{width:100%;margin-top:4px}
.flowchart .perfsvg{height:120px}
.flowfoot{margin-top:2px}
.flowfail{fill:#c0392b}
/* Rückstau: Kacheln je Ressourcenart mit ihrer Wartezahl. */
.backlogrow{display:flex;flex-wrap:wrap;gap:12px}
.backlogitem{flex:1 1 120px;min-width:110px;text-align:center;padding:12px 10px;border:1px solid var(--line);border-radius:11px;background:#f7f9fc}
.backlognum{font-size:26px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--ink2)}
.backloglabel{font-size:12.5px;color:var(--ink2);margin-top:2px}
.backlog-pending{background:#fff6ec;border-color:#e8c79a}
.backlog-pending .backlognum{color:#b3610d}
.btn-danger{color:#8b2020}
.rowform{display:inline}
.linkbtn{background:none;border:none;padding:0;font:inherit;color:var(--ink2);cursor:pointer;text-decoration:underline}
.linkbtn:hover{color:#8b2020}
/* DNS-Records: eine Tabelle pro Zone. Records sind kurz und maschinenlesbar –
   die Zeilendarstellung der übrigen Listen würde sie unnötig auseinanderziehen. */
.zone{border:1px solid var(--line);border-radius:12px;padding:8px 12px 12px;margin:0 0 14px}
.dnstable{width:100%;border-collapse:collapse;margin:4px 0 12px;font-size:13px}
.dnstable th{text-align:left;font-weight:500;color:var(--ink2);font-size:12px;padding:6px 8px;border-bottom:1px solid var(--line)}
.dnstable td{padding:7px 8px;border-bottom:1px solid var(--line);vertical-align:middle}
.dnstable tr:last-child td{border-bottom:none}
.dnstable .mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
/* Ein Befund-Etikett gehört auf eine Zeile. Ohne das umbricht „in Ordnung" in der
   schmalen Spalte zu „in / Ordnung" — und zwar nur in manchen Tabellen, je nachdem wie
   breit die Nachbarspalte gerade ist. Zwei Tabellen untereinander sehen dann aus, als
   zeigten sie Verschiedenes. */
.dnstable .badge{white-space:nowrap}
/* Ein langer TXT-Record (DKIM ist ~400 Zeichen) darf die Tabelle nicht sprengen. */
.dnstable .val{max-width:420px;overflow-wrap:anywhere}
.dnstable .tright{text-align:right;white-space:nowrap}
/* Die Eingabefelder in der DNS-Zeile tragen dieselben Bausteine wie überall im
   Panel — Rahmen, Radius, Hintergrund, Schrift —, nur kompakter (34px statt 40),
   damit sie in die Tabellenzeile passen. Ohne das standen dort nackte Browser-
   Felder neben den gestylten Formularen des Panels; die Breite regelt weiter das
   inline `width` je Feld (Wert breit, TTL/Prio schmal). */
.dnstable input:not([type=checkbox]):not([type=radio]){height:34px;border:1px solid var(--line);border-radius:9px;padding:0 10px;background:#f7f9fc;font-size:13px;font-family:inherit}
.dnstable input:focus{outline:none;border-color:var(--blue)}
/* Icon-Buttons in der DNS-Zeile: kompakt, ohne Rahmen, das Icon trägt die
   Handlung. Bedeutung steckt in title/aria-label, nicht im sichtbaren Text. */
.iconbtn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink2);cursor:pointer;vertical-align:middle}
.dnsactions{display:inline-flex;gap:6px;justify-content:flex-end;align-items:center}
.iconbtn:hover{color:var(--ink);border-color:var(--ink3)}
.iconbtn-danger:hover{color:#8b2020;border-color:#e2b4b4}
/* Schreibformulare: ein Feld, das eine ganze Zeile braucht (Schlüssel, langer Wert). */
.wfield-wide{flex:1 1 100%}
.wfield textarea{border:1px solid var(--line);border-radius:9px;padding:10px 12px;background:#f7f9fc;font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;resize:vertical}

/* Formular als Raster statt als umbrechende Reihe.

   `.wform` ist eine flex-Reihe mit `align-items:flex-end`. Bei zwei oder drei Feldern
   sieht das aus wie eine Werkzeugleiste; bei fünf staffeln sich die Kanten, weil jedes
   Feld seine eigene Mindestbreite mitbringt, und der Knopf steht plötzlich neben einem
   Beschriftungstext statt neben einem Eingabefeld. Genau so sah die E-Mail-Seite aus.

   Im Raster liegen Spalten und Grundlinien fest, unabhängig davon, wie viele Felder
   ein Formular hat. `min-width:0` überstimmt die Mindestbreiten aus `.wfield input` —
   die sind für die flex-Reihe gedacht und würden hier die Spalten aufsprengen. */
.fgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 20px;max-width:760px}
.fgrid .wfield{min-width:0}
/* `width:100%` gilt für Texteingaben — nicht für Häkchen. Ein Häkchen über die ganze
   Spaltenbreite ist der Grund, warum es im Anlege-Formular in der Mitte stand und die
   Beschriftung am rechten Rand: dieselbe Klasse Fehler wie die 40-Pixel-Höhe darüber. */
.fgrid .wfield input:not([type=checkbox]):not([type=radio]),.fgrid .wfield select,.fgrid .wfield textarea{width:100%;min-width:0}
/* Ein Feld über die ganze Breite: für das, was lang wird (mehrere Ziele). */
.fgrid .wfield-full{grid-column:1 / -1}
/* Die Knopfzeile steht auf eigener Zeile und nicht in der Feldspalte: neben einem
   Eingabefeld sieht ein Knopf aus, als gehörte er zu diesem einen Feld. */
.fgrid .fgrid-actions{grid-column:1 / -1;display:flex;align-items:center;gap:14px;margin-top:2px}
@media (max-width:720px){.fgrid{grid-template-columns:minmax(0,1fr)}}

/* Passwortfeld mit Prüfung im Feld (components/_password.html.twig, scripts/password.js).

   Warum es eigene Regeln braucht und nicht `.wfield` reicht: hier liegen drei Dinge
   übereinander, die zusammengehören — das Feld mit zwei Symbolen darin, ein Balken und
   eine Zeile Text, die sich beim Tippen ändert. Als `.wfield` mit Beiwerk daneben
   sprangen die Symbole beim Umbruch unter das Feld und der Text landete in der
   Nachbarspalte. */
.pwfield{display:flex;flex-direction:column;gap:4px;min-width:0}
/* Beschriftung links, „Generieren" rechts auf derselben Zeile: der Knopf gehört zum
   Feld, nicht unter das Feld — dort läse er sich wie eine Fußnote. */
.pwhead{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.pwhead label{font-size:12px;color:var(--ink2)}
.pwgen{font-size:12px;color:var(--blue);cursor:pointer;user-select:none;white-space:nowrap}
.pwgen:hover{text-decoration:underline}
.pwinput{position:relative;display:flex;align-items:center}
/* Rechts Platz für zwei Symbole, damit ein langes Passwort nicht darunter läuft. */
.pwinput input{width:100%;min-width:0;height:40px;border:1px solid var(--line);border-radius:9px;padding:0 68px 0 12px;background:#f7f9fc;font-size:14px}
.pwicon{position:absolute;display:flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:7px;color:var(--ink2);cursor:pointer}
.pwicon:hover{background:rgba(0,0,0,.05);color:var(--ink)}
.pwicon.on{color:var(--blue)}
.pwicon[data-pwtoggle]{right:36px}
.pwicon[data-pwcopy]{right:7px}
/* Der Balken: Weg zur Regel, keine Bewertung. Standardmäßig unsichtbar breit 0 —
   ein leeres Feld hat nichts zu melden. */
.pwbar{height:3px;border-radius:999px;background:var(--line2);overflow:hidden;margin-top:2px}
.pwbar > span{display:block;height:100%;width:0;border-radius:999px;background:var(--line2);transition:width .12s linear}
.pwbar > span.weak{background:var(--red)}
.pwbar > span.ok{background:var(--green)}
.pwnote{font-size:12px;color:var(--ink2);min-height:16px}
.pwnote.bad{color:var(--red-ink)}
.pwnote.good{color:var(--green)}
/* Der rote Rahmen erst, wenn wirklich etwas Falsches drinsteht — nicht bei einem
   leeren Feld. Ein Formular, das rot aufleuchtet, bevor jemand getippt hat, liest sich
   wie ein Vorwurf. */
.pwfield.invalid .pwinput input{border-color:var(--red);background:var(--red-soft)}

/* Zusatzleistung: ein Häkchen mit Erklärung und Preis, als Kasten und nicht als Zeile.

   Der Kasten macht die Entscheidung sichtbar teuer — sie kostet Geld und läuft
   monatlich weiter. Ein Häkchen in einer Reihe mit „Spamschutz" wäre optisch dasselbe
   wie eine kostenlose Einstellung. */
.addonbox{grid-column:1 / -1;display:flex;flex-direction:column;gap:4px;min-width:0}
.addonbox-label{font-size:12px;color:var(--ink2)}
.addon{display:block;border:1px solid var(--line);border-radius:11px;padding:12px 14px;cursor:pointer;background:#fbfcfe}
.addon input[type=checkbox]{width:15px;height:15px;margin:0;flex:0 0 auto;accent-color:var(--blue)}
.addon:hover{border-color:var(--line2)}
/* Hervorhebung ohne Skript: `:has` fragt den Zustand des Häkchens im Label ab. Eine
   Klasse per JavaScript wäre ein zweiter Zustand, der beim Zurückspringen im Browser
   nicht mehr zum Häkchen passt. */
.addon:has(input:checked),.addon.chosen{border-color:var(--blue);background:#f2f7ff}
.addon-top{display:flex;align-items:center;gap:9px}
.addon-top .t{font-size:14px;font-weight:500}
.addon .d{font-size:12px;color:var(--ink2);margin:5px 0 0;padding-left:23px}
.addon .p{font-size:12px;color:var(--ink2);margin-top:8px;padding-left:23px}
.addon .p b{color:var(--ink)}

/* Adressliste mit festen Spalten.

   Vorher war jede Zeile eine flex-Reihe, in der nur stand, was diese eine Adresse
   hatte: bei einem Postfach der Speicher, bei einem Alias das Ziel — an verschiedenen
   Stellen, mit einer großen Lücke bis zum Pfeil am Rand. Nichts lag untereinander, und
   zwei Zeilen sahen aus wie zwei verschiedene Listen.

   Mit Spalten steht dieselbe Angabe immer an derselben Stelle. Schmal wird daraus
   wieder eine Reihe: drei Spalten auf einem Telefon sind unlesbar. */
.mlist .row{display:grid;grid-template-columns:34px minmax(0,1fr) 120px minmax(0,240px) 20px;align-items:center;gap:14px}
.mlist .rmeta{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mlist .dots{justify-self:end;padding:0}
@media (max-width:980px){
  .mlist .row{grid-template-columns:34px minmax(0,1fr) 20px}
  /* Weg, nicht umgebrochen: die Angaben stehen auf der Detailseite, und eine dritte
     Zeile Kleingedrucktes je Eintrag macht die Liste unlesbarer als das Weglassen. */
  .mlist .rmeta{display:none}
}
.sub{font-size:14px;font-weight:600;margin:18px 0 8px}
/* SSH-Schlüssel: die volle Zeile abschneiden statt umbrechen – untereinander sind
   Schlüssel an Typ und Kommentar zu unterscheiden, der base64-Rumpf trägt nichts. */
.keyrow{display:flex;gap:12px;align-items:center;padding:6px 0;border-bottom:1px solid var(--line)}
.keytext{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;color:var(--ink2)}
/* Anmeldeseite: eine Karte auf leerem Grund, ohne Rail und Seitenleiste — vor der
   Anmeldung gibt es keine Navigation, die etwas anzeigen könnte.
   flex:1, weil .shell ein Flex-Container für Rail und Seitenleiste ist: ohne das
   bleibt die Karte in der Breite des ersten Kindes und landet links unten. */
.loginwrap{flex:1;display:grid;place-items:center;padding:24px}
.logincard{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:32px;width:100%;max-width:380px;display:flex;flex-direction:column;gap:14px}
.loginlogo{display:block;height:31px;width:auto;margin:0 0 6px}
.loginbtn{height:42px;margin-top:4px}
.loginhint{margin:6px 0 0;font-size:12.5px;color:var(--ink2);line-height:1.5}

/* Die Fehlerseiten (404, 503): dasselbe zentrierte, schlanke Layout wie die
   Anmeldung, damit sie erkennbar zur Plattform gehören, aber mit einem großen
   Fehlercode als Blickfang. Zentriert und ohne Navigation — sie stehen für sich. */
.errorwrap{flex:1;display:grid;place-items:center;padding:24px}
.errorcard{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:36px 32px;width:100%;max-width:440px;display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px}
.errorcode{font-size:64px;line-height:1;font-weight:700;letter-spacing:-2px;color:var(--blue);margin:8px 0 0}
.errortitle{font-size:22px;margin:0;letter-spacing:-.3px}
.errortext{margin:0;color:var(--ink2);line-height:1.6;max-width:340px}
.errorcard .btn{margin-top:8px}
/* ── Zweite Anmeldestufe ─────────────────────────────────────────────────────
   Der QR-Code und der Schlüssel nebeneinander: wer scannen kann, sieht links
   alles, was er braucht; wer nicht scannen kann, findet rechts den Weg zum
   Abtippen, ohne erst danach suchen zu müssen. */
.mfasetup{display:flex;gap:22px;flex-wrap:wrap;align-items:flex-start;margin-top:14px}
/* Weißer Rand um den Code: viele Scanner brauchen die Ruhezone, und auf einer
   getönten Karte fehlt sie sonst. */
.mfaqr{display:block;background:#fff;padding:10px;border:1px solid var(--line);border-radius:var(--radius-sm)}
.mfasecret{display:inline-block;font-family:var(--mono);font-size:15px;letter-spacing:1px;
  background:var(--hover);border:1px solid var(--line);border-radius:8px;padding:8px 12px;
  user-select:all;word-break:break-all}

/* Die Wahl zwischen den Verfahren. Zwei gleich große Felder nebeneinander, weil es
   eine Entscheidung ist und keine Liste mit einer Voreinstellung — und weil der
   Hinweis, dass der Mailweg schwächer ist, neben der Alternative stehen muss, um
   etwas zu bewirken. */
.mfachoice{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-top:16px}
.mfaopt{display:flex;flex-direction:column;gap:8px;height:100%;
  border:1px solid var(--line);border-radius:var(--radius-sm);padding:16px;background:var(--card)}
.mfaopt .t{font-weight:600;font-size:15px}
.mfaopt .s{font-size:13px;line-height:1.5;color:var(--ink2);flex:1}
.mfaopt .btn{align-self:flex-start;margin-top:4px}

/* Die Wiederherstellungscodes. Monospace und großzügig, weil sie abgetippt oder
   ausgedruckt werden — und in einem Raster, damit man beim Abhaken die Zeile nicht
   verliert. */
.reccodes{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:8px;margin-top:14px}
.reccodes code{font-family:var(--mono);font-size:14px;letter-spacing:.5px;text-align:center;
  background:#fff;border:1px solid var(--line);border-radius:8px;padding:9px 6px;user-select:all}
@media print{
  /* Ausgedruckt zählt nur die Liste. Ohne diese Regel druckt jemand die
     Seitenleiste und die Navigation mit — und legt ein Blatt ab, auf dem die Codes
     unten rechts stehen. */
  .rail,.side,.pside,.topbar,.toolbar,form{display:none !important}
  .reccodes code{border:1px solid #000}
}

/* ── Einladungskarte ─────────────────────────────────────────────────────────
   Die Seite hinter dem Link aus der Einladungsmail benutzt bewusst die Karte der
   Anmeldung: der Besucher kommt aus einer Mail und soll erkennen, dass er bei der
   Anwendung gelandet ist, von der die Mail sprach. Eine eigene Gestaltung an genau
   der Stelle, an der nach einem Passwort gefragt wird, sähe nach einer fremden
   Seite aus.

   Etwas breiter als die Anmeldung: hier stehen Organisation, Rolle und Zugang
   untereinander, und bei 380 Pixeln bricht die Werteliste in zwei Zeilen. */
.loginwrap .logincard:has(.invitetitle){max-width:440px}
.invitetitle{font-size:21px;line-height:28px;margin:0;letter-spacing:-.3px}
.invitelead{margin:0;font-size:14px;line-height:22px;color:var(--ink2)}

/* Die Angaben, zu denen zugesagt wird. Als Raster mit Beschriftung links, weil ein
   Wert ohne Namen Rauschen ist — „Administrator" allein sagt nicht, wovon. */
.invitefacts{display:grid;grid-template-columns:auto 1fr;gap:6px 14px;margin:2px 0;
  padding:14px 16px;background:var(--hover);border:1px solid var(--line);border-radius:var(--radius-sm)}
.invitefacts dt{font-size:12px;color:var(--ink3);align-self:center}
.invitefacts dd{margin:0;font-size:14px;font-weight:600;color:var(--ink);
  overflow-wrap:anywhere}

/* Ablehnen steht unter der Karte, nicht darin: es ist die gegenteilige Handlung
   zu „annehmen", und direkt daneben wird sie verklickt. Erreichbar, nicht
   angeboten. */
.invitedecline{margin-top:14px;text-align:center}
.invitedecline .linkbtn{font-size:13px;color:var(--ink3)}

/* Vor- und Nachname nebeneinander. Untereinander wäre das Formular vier Felder
   hoch, und ein Anmeldeformular, das nach unten aus dem Bild läuft, sieht nach
   mehr Aufwand aus, als es ist. */
.invitenames{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.invitenames .wfield input{min-width:0;width:100%}

/* Angemeldete Adresse in der Kopfzeile. Abgeschnitten statt umgebrochen: die
   Kopfzeile hat eine feste Höhe, und eine lange Adresse darf sie nicht sprengen. */
.topbar .who{text-decoration:none;font-size:13px;color:var(--ink2);max-width:200px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}

/* ── Organisationen ─────────────────────────────────────────────────────────
   Dieselbe Kachelsprache wie die Projekte (Bild, Name, Haarlinie über der
   Fußzeile) mit einem Unterschied, der aus der Sache kommt: eine Projektkachel ist
   ein Link, eine Organisationskachel enthält Formulare. Ein <a> darf keine
   Schaltflächen umschließen, also ist das hier ein <div> — und weil nichts
   anklickbar ist, was nicht aussieht wie ein Knopf, fehlt auch der Hover-Effekt.

   Drei Zonen statt einer losen Reihe: Kopf (Logo, Name, Größe), beschriftete
   Werte, Fußzeile mit den Aktionen. Vorher stand zwischen Kopf und Aktionen eine
   nackte UUID ohne ein Wort dazu, was sie ist. */
.orggrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;padding:8px 16px 14px}
.orgcard{
  display:flex;flex-direction:column;
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
}
.orgbody{display:flex;flex-direction:column;gap:12px;padding:14px 14px 12px}
.ohead{display:flex;align-items:center;gap:12px;min-width:0}
.obody{display:flex;flex-direction:column;gap:2px;min-width:0}
.oname{font-weight:600;font-size:14.5px;line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osub{font-size:12.5px;color:var(--ink2)}

/* Beschriftete Werte. Zwei Spalten, die Beschriftung so breit wie ihr längstes
   Wort — nicht als Tabelle, weil es keine ist: zwei Angaben zu *einem* Ding. */
.ometa{margin:0;display:grid;grid-template-columns:auto minmax(0,1fr);gap:4px 12px;font-size:12.5px}
.ometa dt{color:var(--ink2)}
.ometa dd{margin:0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Die UUID gekürzt statt umgebrochen: sie ist ein Griff zum Kopieren, keine Zeile
   zum Lesen — vollständig steht sie in der Listenansicht und im title. */
.ometa dd .pid{font-family:var(--mono);font-size:11.5px;color:var(--ink3)}

.orgactions{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  margin-top:auto;padding:10px 14px 12px;border-top:1px solid var(--line2)}

/* ── Logo einer Organisation ────────────────────────────────────────────────
   Die Fliese *ist* der Knopf: ein Klick öffnet die Dateiauswahl, weil die Fläche
   das <label> des Dateifelds ist, und die Auswahl schickt sofort ab (nav.js).

   Hell und gerahmt, anders als das Projekt-Titelbild: dort füllt ein Bild eine
   Fläche, hier steht eine Marke auf einem Feld. Ein Logo mit transparentem Rand
   braucht einen hellen Grund, sonst verschwindet die halbe Wortmarke. */
.ologoform{margin:0;flex:none;display:flex;align-items:center;gap:10px}
.ologo{position:relative;display:block;width:52px;height:52px;flex:none;
  border:1px solid var(--line);border-radius:11px;overflow:hidden;background:#fff;cursor:pointer}
.ologo.sm{width:30px;height:30px;border-radius:8px}
.ologo:hover{border-color:#cfd6e4}
.ologo:has(.filepick:focus-visible){outline:2px solid var(--blue);outline-offset:2px}
/* Das hochgeladene Logo ganz zeigen, nicht beschneiden — siehe CoverExtension.
   Das Polster ist der Rand, den ein Logo braucht, um nicht an der Kante zu kleben;
   ein knapp gesetztes Wortbild sähe sonst aus wie abgeschnitten. */
.logoimg{width:100%;height:100%;object-fit:contain;background:#fff;padding:5px}
.ologo.sm .logoimg{padding:3px}
/* Das Zeichen in der Ecke sagt: hier kann man klicken. Leise, weil es in einem
   Raster von dreißig Kacheln dreißigmal vorkommt — und sichtbar, weil ein
   Hinweis, der erst beim Überfahren erscheint, auf einem Tablet nie erscheint. */
.ologobadge{position:absolute;right:0;bottom:0;display:grid;place-items:center;
  width:17px;height:17px;border-radius:7px 0 0 0;
  background:rgba(12,16,24,.5);color:#fff;opacity:.7;transition:opacity .12s ease}
.ologo.sm .ologobadge{width:14px;height:14px;border-radius:5px 0 0 0}
.ologo:hover .ologobadge,.ologo:has(.filepick:focus-visible) .ologobadge{opacity:1}
@media (prefers-reduced-motion:reduce){.ologobadge{transition:none}}

/* Logo und Name in einer Tabellenzelle. */
.ocell{display:flex;align-items:center;gap:10px;min-width:0}

/* Listenansicht: zum Vergleichen. Kopfzeile, weil eine Spalte mit Zahlen ohne
   Überschrift nicht sagt, was sie zählt. */
.otable th{padding:10px 14px;text-align:left;font-size:12px;font-weight:600;
  color:var(--ink2);border-bottom:1px solid var(--line);white-space:nowrap}
.otable td{vertical-align:middle}
/* Die Aktionen einer Zeile in einem Flex-Behälter, wie im Raster: ein <form> und
   ein <span> nebeneinander in einer Tabellenzelle haben sonst keinen Abstand, und
   „Logo entfernen" klebte an „Löschen erst ohne Projekte". Vorher trug das ein
   margin-right am <details> allein — das reichte, solange es genau zwei Dinge
   waren. */
.rowactions{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
/* Die Ausgabe eines Composer-Laufs. Sie ist lang, sie hat Zeilenumbrüche, und sie ist
   das, was ein Kunde dem Support vorliest — also umbruchtreu, scrollbar und mit einer
   Höhe, die den Rest der Seite nicht verdrängt. */
.runlog{margin:8px 0 0;padding:10px 12px;max-height:320px;overflow:auto;
        background:#f6f7f9;border:1px solid var(--line);border-radius:8px;
        font-family:var(--mono);font-size:12px;line-height:1.5;white-space:pre-wrap;
        word-break:break-word}
.otable .disclose{display:inline-block}

/* Aufklappen ohne JavaScript: <details> bringt der Browser mit, und ein
   Umbenennen-Formular pro Kachel dauerhaft offen würde die Liste erschlagen. */
.disclose summary{cursor:pointer;color:var(--ink2);font-size:13px;
  text-decoration:underline;text-underline-offset:2px}
.disclose summary:hover{color:var(--ink)}
.disclose summary::marker{content:''}
.disclose form{display:flex;gap:8px;align-items:center;margin-top:8px;flex-wrap:wrap}
.disclose input[type=text]{height:36px;min-width:150px;flex:1;
  border:1px solid var(--line);border-radius:8px;padding:0 10px;background:#f7f9fc;font-size:13.5px}
.disclose .btn{height:36px}



/* ── App-Auswahl („App anlegen", Schritt 1) ────────────────────────────────────
   Kachelraster nach dem Vorbild: Icon auf farbigem Feld, darunter Name und
   Kategorie. Ein Raster und keine Liste, weil hier ein Wiedererkennungswert zählt —
   Leute suchen das Logo, nicht die Zeile. */
.appgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:16px}
.appcard{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:12px;
  overflow:hidden;background:var(--card);color:inherit;text-decoration:none;transition:border-color .12s}
.appcard:hover{border-color:var(--blue);text-decoration:none}
.appicon{position:relative;height:120px;display:grid;place-items:center;background:var(--blue-soft);color:var(--blue-ink)}
.appmeta{padding:12px 14px;border-top:1px solid var(--line2)}
.appmeta .t{font-weight:600}
.appmeta .s{color:var(--ink2);font-size:12.5px;margin-top:2px}
.badge.popular{background:#fce7f6;color:#a01a72}
/* Die Marke sitzt oben rechts auf dem Icon-Feld, wie im Vorbild. */
.appicon .badge{position:absolute;top:8px;right:8px}
/* Was noch nicht anlegbar ist, wird nicht versteckt und nicht ausgegraut bis zur
   Unlesbarkeit: es ist auswählbar, und der nächste Schritt sagt, was fehlt. Nur das
   Icon-Feld verliert seine Farbe — genug, um die Gruppe auf einen Blick zu trennen. */
.appcard.soon .appicon{background:#f2f4f8;color:var(--ink2)}

/* ── Kacheln mit echtem Markenlogo ──

   Neutraler Grund statt der blauen Tönung: die Tönung ist der Hintergrund für ein
   einfarbiges Strichsymbol, das ihre Farbe aufnimmt. Ein Markenlogo bringt seine
   eigene Farbe mit, und PostgreSQL-Blau auf Hellblau ist kaum noch zu sehen.

   Und die Dämpfung für „noch nicht anlegbar" muss auch hier tragen: über die Farbe
   allein geht das bei einem Bild nicht, also über Sättigung und Deckkraft. Ohne das
   sähe eine Kachel, die man nicht anklicken kann, munterer aus als eine, die man
   kann. */
.appicon.haslogo{background:#fff}
.appcard.soon .appicon.haslogo{background:#f7f8fb}
.applogo{display:block;width:40px;height:40px}
.appcard.soon .applogo{filter:grayscale(1);opacity:.45}
.appcard.soon .soonbadge{top:auto;bottom:8px;right:8px}
/* Kachel mit echtem Bild statt Symbol.
   Der Dienstkatalog bringt eine Vorschau mit (beim Kachelproxy eine Pseudo-Kachel), und
   die soll die Fläche füllen statt darin zu schwimmen. `cover` und nicht `contain`: die
   Bilder sind quadratisch, die Fläche ist breiter — mit `contain` stünden links und
   rechts graue Balken, und die Kachel sähe kaputt aus statt bebildert.

   `position:absolute` und nicht `height:100%`: `.appicon` ist ein Grid mit
   `place-items:center`, das Bild wird darin also NICHT gestreckt — eine Prozenthöhe hat
   dann keinen Bezug und fällt auf die Eigengröße zurück. Gemessen: 214px statt 120px, und
   das Bild lag über Name und Preis. Auf dem Bildschirm war die Kachel dann eine reine
   Bildfläche ohne Text, im HTML stand alles richtig da.

   `overflow:hidden` als Gürtel dazu: ein `cover`-Bild darf unter keinen Umständen aus
   seiner Fläche laufen. */
.appicon{overflow:hidden}
.appicon.hasimg{padding:0;background:#fff}
.appicon.hasimg img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
/* Eine Ankündigung wird gedämpft, aber **nicht** entfärbt.
   `.appcard.soon` blasst sonst nur den Symbol-Hintergrund aus, und den verdeckt ein Bild
   vollständig — die Kachel sähe aus wie eine buchbare. Graustufen wären hier trotzdem
   falsch: bei VersaTiles ist gerade das Aussehen der Vektorkachel die Aussage, und eine
   graue Vorschau nähme dem Eintrag seinen Zweck. Etwas Transparenz reicht; das
   Kennzeichen darüber sagt den Rest. */
.appcard.soon .appicon.hasimg img{opacity:.6}
.appcard.soon:hover .appicon.hasimg img{opacity:.85}

/* ── Anlege-Formular (Schritt 2) ── */
/* Zwei Felder nebeneinander, wie im Vorbild (Name + Version, Benutzer + Passwort).
   Umbruch statt Quetschen: auf schmalen Fenstern ist untereinander richtig. */
.fieldrow{display:flex;gap:14px;flex-wrap:wrap;align-items:flex-end}
/* Wie bei `.wform`: trägt ein Feld einen Hinweis darunter, an der Oberkante
   ausrichten — sonst steht dessen Beschriftung höher als die der anderen. */
.fieldrow:has(.wfield > .hint),
.fieldrow:has(.pwfield){align-items:flex-start}
.fieldrow .wfield input,.fieldrow .wfield select{width:100%}
/* Auswahl aus zwei Möglichkeiten als zwei Flächen und nicht als Radiogruppe im
   Fließtext: die zweite ist hier abgeschaltet, und eine abgeschaltete Fläche ist als
   solche erkennbar, ein abgeschalteter Punkt nicht. */
.choicerow{display:flex;gap:10px;flex-wrap:wrap}
.choice{display:flex;align-items:center;gap:8px;padding:10px 16px;border:1px solid var(--line);
  border-radius:9px;background:#f7f9fc;font-size:14px}
.choice.chosen{border-color:var(--blue);background:var(--blue-soft);color:var(--blue-ink);font-weight:600}
.choice.off{color:var(--ink3);background:#f2f4f8}
.choice.off input{cursor:not-allowed}
/* Eine Auswahlzeile innerhalb eines Formularfelds. `min-content` als Breite hätte den
   Text Wort für Wort umbrochen; sie darf so breit werden wie das Feld und dort normal
   umbrechen. */
.wfield > .choice{justify-content:flex-start}
/* Knopfleiste am Fuß eines Formulars: getrennt durch eine Linie, damit „Zurück" und
   „Anlegen" nicht wie zwei weitere Felder aussehen. */
.formfoot{display:flex;gap:10px;justify-content:flex-end;align-items:center;
  margin-top:22px;padding-top:16px;border-top:1px solid var(--line2)}

/* ── Projektkopf ─────────────────────────────────────────────────────────────
   Das Titelbild als Kopf der Projektseite, mit Name und Kurz-ID darauf.

   Warum das Bild und nicht nur die Überschrift: in einem Panel mit dreißig
   Projekten sehen alle Unterseiten identisch aus, und die einzige Antwort auf
   „bin ich hier im richtigen Projekt" war bisher eine Zeile Text in der
   Seitenleiste. Eine Farbfläche erkennt man, bevor man liest — und wenn jemand
   sein eigenes Bild hinterlegt hat, erkennt man es noch schneller. */
.phero{position:relative;overflow:hidden;background:var(--card);
  border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);margin:0 0 18px}
.phero .herocover{position:relative;height:180px;background:var(--hover)}
/* Der Verlauf ist hier nicht Dekoration, sondern die Lesbarkeitsgarantie: welches
   Foto jemand hochlädt, weiß niemand, und weiße Schrift auf einem hellen Himmel
   ist unlesbar. Von unten nach oben, weil der Text unten liegt. */
.phero .herocover::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(10,14,22,0) 28%,rgba(10,14,22,.52))}
.phero .heroinfo{position:absolute;left:24px;right:24px;bottom:16px;z-index:1;
  display:flex;align-items:flex-end;justify-content:space-between;gap:14px;flex-wrap:wrap}
.phero .heroname{margin:0;font-size:24px;line-height:1.2;color:#fff;letter-spacing:-.2px;
  text-shadow:0 1px 12px rgba(0,0,0,.35)}
.phero .heroid{display:block;margin-top:5px;font-family:var(--mono);font-size:12.5px;
  color:rgba(255,255,255,.82)}
/* Bild wechseln und Bild entfernen: zwei kleine Zeichen oben rechts im Bild.
   Vorher war das eine eigene Leiste unter dem Bild samt Erklärtext — auf jeder
   Projektseite ein Streifen Aufmerksamkeit für etwas, das man einmal im Leben
   eines Projekts tut.

   Auf dem Bild und nicht darunter heißt: unbekannter Untergrund. Deshalb bringt
   der Knopf seinen eigenen mit — dunkel, halbtransparent, mit heller Kante. Das
   liest sich auf einem hellen Himmel genauso wie auf einer dunklen Fläche. */
.phero .heroactions{position:absolute;top:12px;right:12px;z-index:2;display:flex;gap:8px}
.phero .heroactions form{margin:0;display:flex;gap:8px}
.iconbtn{width:34px;height:34px;flex:none;padding:0;border-radius:9px;
  display:grid;place-items:center;cursor:pointer;color:#fff;
  border:1px solid rgba(255,255,255,.30);background:rgba(12,16,24,.42);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
  transition:background .12s ease}
.iconbtn:hover{background:rgba(12,16,24,.66)}
@media (prefers-reduced-motion:reduce){.iconbtn{transition:none}}

/* Das Dateifeld liegt unsichtbar *im* Knopf: ein Klick auf die Fläche öffnet die
   Auswahl, weil die Fläche das <label> ist. Nicht display:none und nicht
   visibility:hidden — beides nimmt dem Feld den Platz im Tab-Lauf, und dann ist
   das Bild ohne Maus nicht mehr wechselbar. */
.filepick{position:absolute;width:1px;height:1px;padding:0;margin:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}
/* Der Fokus liegt am versteckten Feld, sichtbar sein muss aber die Fläche. :has()
   verlegt die Anzeige dorthin, wo der Nutzer sie sucht. */
.iconbtn:has(.filepick:focus-visible),
.iconbtn:focus-visible{outline:2px solid #fff;outline-offset:2px}

/* Die Fliese in der Projekt-Seitenleiste: dasselbe Motiv, quadratisch, an der
   Stelle, an der vorher das graue Monogramm saß. Dieselbe Größe wie dort, damit
   der Kopf der Leiste nicht springt. */
.pcard .pname .ptile{width:38px;height:38px;flex:none;border-radius:10px;overflow:hidden;
  background:var(--hover);display:block}

/* ── Umschalter Raster / Liste ───────────────────────────────────────────────
   Zwei Knöpfe in einer Fassung statt zweier freistehender: sie sind eine
   Entscheidung mit zwei Antworten, und genau eine davon gilt. Vorher waren es
   zwei Links auf `#` — sie sahen aus wie eine Wahl und waren keine. */
.segbar{display:inline-flex;border:1px solid var(--line);border-radius:9px;overflow:hidden;background:#fff}
.segbar a{display:inline-flex;align-items:center;gap:7px;height:36px;padding:0 13px;
  font-size:13.5px;font-weight:600;color:var(--ink2);text-decoration:none}
.segbar a + a{border-left:1px solid var(--line)}
.segbar a:hover{background:var(--hover);text-decoration:none;color:var(--ink)}
.segbar a.on{background:var(--active);color:var(--blue-ink)}

/* ── Projektliste ────────────────────────────────────────────────────────────
   Die zweite Ansicht derselben Daten, zum Vergleichen statt zum Wiedererkennen.
   Das Motiv bleibt als kleine Fliese dabei: ohne sie wäre die Liste eine andere
   Welt als das Raster, und die Farbe, die man sich gemerkt hat, wäre weg. */
.projlist{padding:6px 0}
/* Seitliches Polster wie in der Werkzeugleiste und im Raster: 16px. Die Zeilen
   stehen unter derselben Leiste, also an derselben Kante. */
.projrow{display:flex;align-items:center;gap:14px;padding:12px 16px;
  border-top:1px solid var(--line2);color:var(--ink);text-decoration:none}
.projrow:first-child{border-top:none}
.projrow:hover{background:var(--hover);text-decoration:none}
.projrow .rthumb{width:52px;height:34px;flex:none;border-radius:8px;overflow:hidden;background:var(--hover)}
.projrow .rmain .t{font-weight:600}
.projrow .rmain .s{color:var(--ink2);font-size:12.5px;margin-top:1px}
.projrow .rmeta{gap:12px}
@media(max-width:700px){
  .projrow .rmeta .pid{display:none}
}

/* ── Server-Seite: Karten statt Tabelle ─────────────────────────────────────────
   Die Tabelle hatte neun Spalten und die Wartungsformulare in der letzten. Bei 390 px
   blieb ein Rollbereich, in dem man die Spalte „Aktion" nie sah. Eine Karte je Server
   bricht von selbst um: zwei Spalten, wo Platz ist, sonst eine. */
.srvsum{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,180px),1fr));gap:14px;margin:0 0 18px}
.srvtile{display:flex;align-items:center;gap:12px;padding:14px 16px;min-width:0}
.srvtile .ico{width:42px;height:42px;border-radius:12px;background:var(--blue-soft);color:var(--blue-ink);display:grid;place-items:center;flex:none}
.srvtile b{display:block;font-size:22px;line-height:1.1;font-variant-numeric:tabular-nums}
.srvtile span{font-size:12.5px;color:var(--ink2)}
.srvalert{display:flex;gap:12px;align-items:flex-start;padding:13px 16px;margin:0 0 18px;
  border-radius:14px;background:#fff8e8;border:1px solid #ecd9a8;color:#5f4608}
.srvalert svg{flex:none;margin-top:2px;color:#9a6b00}
.srvalert p{margin:0}
.srvgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,440px),1fr));gap:18px;align-items:start}
.srvcard{padding:20px;display:flex;flex-direction:column;gap:14px;min-width:0}
.srvhead{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.srvicon{width:46px;height:46px;border-radius:13px;background:var(--hover);color:var(--ink2);display:grid;place-items:center;flex:none}
.srvname{flex:1 1 150px;min-width:0}
.srvname h3{margin:0;font-size:17px;overflow-wrap:anywhere}
.srvwhere{display:flex;align-items:center;gap:5px;color:var(--ink2);font-size:12.5px}
.badge.dot::before{content:'';width:7px;height:7px;border-radius:50%;background:currentColor}
.chips{display:flex;flex-wrap:wrap;gap:6px}
.chip{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;
  font-size:12.5px;font-weight:600;background:#eef0f4;color:var(--ink2);white-space:nowrap}
.chip.green{background:var(--green-soft);color:var(--green)}
.chip.amber{background:#fff8e8;color:#7a5a12}
.chip.red{background:var(--red-soft);color:var(--red-ink)}
.chip.blue{background:var(--blue-soft);color:var(--blue-ink)}
.chip.wrap{white-space:normal}
.specs{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px 14px}
.spec{display:flex;align-items:center;gap:8px;min-width:0}
.spec svg{flex:none;color:var(--ink3)}
.addr{display:flex;align-items:center;gap:10px;padding:7px 8px 7px 12px;border-radius:10px;
  background:#f7f9fc;border:1px solid var(--line2)}
.addr svg{flex:none;color:var(--ink3)}
.addr code{flex:1;min-width:0;overflow-wrap:anywhere;font-family:var(--mono);font-size:13.5px}
.addr .btn{height:34px;padding:0 12px;font-size:13px}
.meters{display:grid;gap:12px}
.meterhead{display:flex;justify-content:space-between;gap:8px;font-size:12.5px;font-weight:600;margin-bottom:5px}
.meterhead span:last-child{color:var(--ink2);font-weight:500;font-variant-numeric:tabular-nums}
.meterbar{height:8px;border-radius:99px;background:#e4e9f2;overflow:hidden}
.meterfill{height:100%;background:var(--blue);border-radius:99px}
.meterfill.hot{background:#d98a2b}
.srvactions{display:flex;flex-wrap:wrap;gap:8px}
.srvactions form{margin:0}
.srvacc{display:grid;gap:8px}
details.acc{border:1px solid var(--line);border-radius:12px;background:#fff}
details.acc > summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:10px;
  min-height:46px;padding:6px 14px;font-weight:600;font-size:13.5px;border-radius:12px;color:var(--ink)}
details.acc > summary::-webkit-details-marker{display:none}
details.acc > summary:hover{background:var(--hover)}
details.acc > summary > svg:first-child{flex:none;color:var(--ink2)}
details.acc .accsum{flex:1;min-width:0;display:flex;flex-wrap:wrap;gap:2px 8px;align-items:baseline}
details.acc .accsum small{font-weight:400;color:var(--ink2);font-size:12.5px}
details.acc .chev{flex:none;color:var(--ink3);transition:transform .15s}
details.acc[open] > summary .chev{transform:rotate(180deg)}
.accbody{padding:4px 14px 16px;display:flex;flex-direction:column;gap:14px}
.accbody form{margin:0;display:flex;flex-direction:column;gap:12px}
.accbody label{display:flex;flex-direction:column;gap:5px;font-size:12.5px;font-weight:600;color:var(--ink2)}
.accbody label.check{flex-direction:row;align-items:flex-start;gap:10px;font-weight:500;color:var(--ink);font-size:13.5px}
.accbody label.check > span{display:flex;flex-direction:column;gap:3px}
.accbody input:not([type=checkbox]),.accbody select{height:40px;width:100%;min-width:0;border:1px solid var(--line);
  border-radius:9px;padding:0 12px;background:#f7f9fc;font-size:14px;font-weight:400;color:var(--ink)}
.accbody input[type=checkbox]{width:18px;height:18px;margin:1px 0 0;flex:none;accent-color:var(--blue)}
.accbody .btnrow{display:flex;flex-wrap:wrap;gap:8px}
.accbody .btn{align-self:flex-start}
.fgrid2{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,170px),1fr));gap:12px}
.mtlist{display:grid;grid-template-columns:auto 1fr;gap:6px 14px;margin:0;font-size:13.5px}
.mtlist dt{color:var(--ink2)}
.mtlist dd{margin:0;min-width:0;overflow-wrap:anywhere}
.mtwarn{color:var(--red-ink)}
details.accdanger{border-color:#f0c4cb;background:#fff9fa}
details.accdanger > summary > svg:first-child{color:var(--red-ink)}

/* Betriebssicht: Kennzahlen mit Icon, Kacheln mit Rand. */
.perfmetric{border:1px solid var(--line2);border-radius:14px;padding:12px 14px;min-width:0}
.perfmetric-head{align-items:center}
.perfmetric-head svg{color:var(--ink3)}
.perfmetric-head .perfnow{margin-left:auto;font-size:18px}
.perfrow-3{grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr))}
.backlogicon{display:flex;justify-content:center;color:var(--ink3);margin-bottom:4px}
.backlog-pending .backlogicon{color:#b3610d}
.flowname{display:flex;align-items:center;gap:8px}
.flowname svg{color:#6a5acd;flex:none}
.sechead{display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:flex-start}
.sechead svg{color:var(--blue-ink);flex:none}

@media(max-width:700px){
  .srvgrid{grid-template-columns:minmax(0,1fr)}
  .srvcard{padding:16px}
  .srvactions .btn,.srvactions form{flex:1 1 100%}
  .srvactions .btn{justify-content:center;width:100%}
  .accbody .btn{align-self:stretch;justify-content:center}
  .accbody .btnrow .btn{flex:1 1 100%}
  .mtlist{grid-template-columns:1fr;gap:0}
  .mtlist dd{margin-bottom:8px}
}

/* Bestellformular: Abstand unter der Überschrift */
.sechead + .wform{margin-top:14px}

/* Hinweise in Formular-Labels nicht fett */
.accbody label .hint,.accbody label.check{font-weight:400}
.accbody label.check > span:first-child,.accbody label.check{font-size:14px}

/* ── Kopfzeile wie im Entwurf: weiß mit Haarlinie, Rolle als Chip neben der
   Wortmarke, Konto rechts (Adresse, Avatar, Abmelden als Knopf). ── */
.topbar{background:#fff;border-bottom:1px solid #e1e6f0}
.topbar .chip{flex:none}
.topright{margin-left:auto}
.topright .avatar{width:40px;height:40px;background:#c9efe3;color:#0e6b4a;font-size:14px}
.topright .btn.logout{width:auto;height:44px;padding:0 12px;border-radius:12px;display:inline-flex;align-items:center;justify-content:center}
.topright .btn.logout:hover{background:#f4f6fb}

/* ── Navigation auf dem Telefon ──────────────────────────────────────────────────

   Bis hierher galt unter 900 px `.rail{display:none} .pside{display:none}` — und sonst
   nichts. Auf einer Projektseite gab es damit **keine** Navigation: kein Menü, keine
   Leiste, kein Knopf. Gemessen bei 390 px als e2e-user-1 in p-bf6bb5: von „Apps" kam
   man nur über den Zurück-Knopf des Browsers zu „Datenbanken".

   Jetzt liegen die Leisten in einer Schublade (`.navdrawer`), die der Menüknopf in der
   Kopfzeile öffnet. Auf dem Desktop ist die Schublade `display:contents` — sie erzeugt
   keinen Kasten, und Rail, globale Leiste und Projektleiste stehen wie bisher direkt in
   `.shell`.

   **Nur mit Skript.** Die Klasse `js-nav` setzt base.html.twig vor dem ersten Zeichnen.
   Fehlt das Skript, bleibt es beim alten Verhalten mit einer Ausnahme: die
   Projektleiste steht dann untereinander statt gar nicht. Eine lange Seite ist besser
   als eine ohne Weg hinaus. */
/* Verdeckte Werte (Passwörter) mit „Anzeigen" und „Kopieren" — siehe nav.js. */
.secret{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap;max-width:100%}
.secret input{min-width:0;max-width:100%;height:34px;border:1px solid var(--line);border-radius:8px;padding:0 10px;
  font-family:var(--mono);font-size:12.5px;background:#fff;color:var(--ink)}
.secret .btn{height:34px}
/* Ein Symbol in einer Unterzeile steht neben dem Text, nicht darüber (die globale Regel
   macht jedes SVG zum Block). */
.rmain .s > svg{display:inline-block;vertical-align:-2px}

.navdrawer{display:contents}
.navdrawer .gside.drawer-only{display:none}
.navtoggle{display:none;flex:none;width:44px;height:44px;margin-left:-6px;border:none;border-radius:12px;
  background:none;color:var(--ink);place-items:center}
.navtoggle:hover{background:#f4f6fb}
.navtoggle .ic-close{display:none}
.navbackdrop{display:none}

@media(max-width:900px){
  html:not(.js-nav) .pside{display:block;width:auto}

  .js-nav .navtoggle:not([hidden]){display:grid}
  .js-nav .navdrawer{
    display:block;position:fixed;z-index:39;top:var(--top);left:0;bottom:0;
    width:min(340px,88vw);overflow-y:auto;overscroll-behavior:contain;
    background:linear-gradient(180deg,var(--bg1),var(--bg2));
    box-shadow:0 10px 30px rgba(16,24,40,.18);
    transform:translateX(-105%);visibility:hidden;
    transition:transform .2s ease,visibility 0s linear .2s;
  }
  .js-nav.nav-open .navdrawer{transform:none;visibility:visible;transition:transform .2s ease}
  .js-nav.nav-open .navtoggle .ic-open{display:none}
  .js-nav.nav-open .navtoggle .ic-close{display:block}
  /* In der Schublade stehen die Leisten untereinander und in voller Breite. Die Rail
     bleibt weg: sie ist eine Spalte aus Symbolen ohne Beschriftung, und dafür steht in
     der Schublade die beschriftete globale Leiste. */
  .js-nav .navdrawer .rail{display:none}
  .js-nav .navdrawer .gside,
  .js-nav .navdrawer .gside.drawer-only{display:block;width:auto;padding:14px 14px 0}
  .js-nav .navdrawer .pside{display:block;width:auto;padding:14px}
  .js-nav .navdrawer .gcard{min-height:0}
  /* Der Hintergrund hinter der offenen Schublade. Ein Tipp darauf schließt sie — das
     erwartet man auf dem Telefon, und ohne ihn läge der Inhalt bedienbar unter einer
     Schublade, die ihn halb verdeckt. */
  .js-nav.nav-open .navbackdrop{display:block;position:fixed;inset:var(--top) 0 0 0;z-index:38;
    background:rgba(16,24,40,.28)}
  .js-nav.nav-open body{overflow:hidden}
}
