munin/static/style.css

1243 lines
43 KiB
CSS

/* Aussehen der Weboberfläche.
*
* ── Aufbau dieser Datei ──────────────────────────────────────────────────────
* 1. Grundwerte Farben, Abstände, Schriftgrößen, Breiten
* 2. Grundlagen body, Überschriften, Links, Code
* 3. Gerüst Kopfleisten und Inhaltsbereich
* 4. Bausteine Karten, Meldungen, Marken, Tabellen, Formulare, Knöpfe
* 5. Seiten das Wenige, das wirklich nur eine Seite betrifft
* 6. Schmale Geräte
*
* ── Zwei Regeln, die alles zusammenhalten ────────────────────────────────────
*
* ERSTENS: Abstände kommen ausschließlich aus der Skala (--s1 bis --s8). Keine
* frei gewählten Werte. Vorher standen hier 29 verschiedene Abstandswerte — das
* Ergebnis war, dass nichts zueinander ausgerichtet war und jede neue Seite
* wieder ein bisschen anders aussah.
*
* ZWEITENS: Die Breite richtet sich nach dem Inhalt, nicht nach dem Bildschirm.
* Eine Tabelle mit acht Spalten braucht Platz, ein Fließtext dagegen darf nicht
* über die ganze Breite laufen — ab etwa 75 Zeichen je Zeile findet das Auge den
* Zeilenanfang nicht mehr zuverlässig. Deshalb gibt es drei Breiten, und jede
* Seite wählt die passende Darstellung.
*/
/* ── 1. Grundwerte ────────────────────────────────────────────────────────── */
:root {
/* Farben: eine neutrale Grundfläche, ein Akzent, eine Warnfarbe. */
--grund: #faf9f7; /* Seitenhintergrund */
--flaeche: #ffffff; /* Karten, Tabellen, Eingabefelder */
--flaeche-leis: #f4f2ef; /* Tabellenköpfe, gedämpfte Bereiche */
--rahmen: #e4e1dc;
--rahmen-stark: #cfcbc4;
--text: #23211e;
--text-leise: #6d6862;
--akzent: #2c6e4e;
--akzent-flach: #ebf3ee;
--akzent-text: #205138;
--warn: #a13a2b;
--warn-flach: #fbeeeb;
/* Abstandsskala — alles Weitere ist ein Vielfaches davon. */
--s1: 0.25rem;
--s2: 0.5rem;
--s3: 0.75rem;
--s4: 1rem;
--s5: 1.5rem;
--s6: 2rem;
--s7: 3rem;
--s8: 4rem;
/* Schriftgrößen. */
--t-klein: 0.8125rem;
--t-meta: 0.875rem;
--t-normal: 1rem;
--t-gross: 1.125rem;
--t-h2: 1.25rem;
--t-h1: 1.625rem;
/* Breiten. Die Seite nutzt den Platz aus; nur der Fließtext begrenzt sich
* selbst (siehe Regel zwei oben). */
--breite-seite: 120rem; /* Seitenleiste plus Inhalt */
--breite-leiste: 15rem; /* Seitenleiste */
--breite-lesen: 44rem; /* Fließtext eines Eintrags */
--breite-schmal: 25rem; /* Anmeldung */
/* Höhe der Bedienelemente. Wichtig ist nicht der Wert, sondern dass Knopf
* und Eingabefeld denselben verwenden — sonst stehen sie in einer Zeile
* nebeneinander unterschiedlich hoch, und das fällt sofort auf. */
--hoehe-bedienelement: 2.5rem; /* Knöpfe, Eingabefelder, Auswahllisten: alle gleich hoch */
--hoehe-kopfleiste: 3.5rem;
--radius: 6px;
--radius-gross: 10px;
--schatten: 0 1px 2px rgb(0 0 0 / 0.04), 0 1px 3px rgb(0 0 0 / 0.06);
}
/* Dunkle Darstellung: nur die Farben werden getauscht, alle Regeln darunter
* gelten unverändert weiter. */
@media (prefers-color-scheme: dark) {
:root {
--grund: #1a1918;
--flaeche: #232120;
--flaeche-leis: #2b2927;
--rahmen: #383533;
--rahmen-stark: #4a4643;
--text: #e9e6e2;
--text-leise: #a09a94;
--akzent: #6fbf96;
--akzent-flach: #1f2d26;
--akzent-text: #8fd3ae;
--warn: #e08d7e;
--warn-flach: #2e2320;
--schatten: 0 1px 3px rgb(0 0 0 / 0.4);
}
}
*, *::before, *::after { box-sizing: border-box; }
/* ── 2. Grundlagen ───────────────────────────────────────────────────────── */
body {
margin: 0;
background: var(--grund);
color: var(--text);
font: var(--t-normal)/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3 { line-height: 1.3; font-weight: 600; margin: 0; }
h1 { font-size: var(--t-h1); letter-spacing: -0.01em; }
h2 { font-size: var(--t-h2); }
h3 { font-size: var(--t-gross); }
p { margin: 0 0 var(--s4); }
p:last-child { margin-bottom: 0; }
a { color: var(--akzent-text); text-decoration: none; }
a:hover { text-decoration: underline; }
/* Sichtbarer Fokusrahmen für alles, was per Tastatur erreichbar ist.
* :focus-visible statt :focus — so erscheint der Rahmen bei Tastaturbedienung,
* aber nicht bei jedem Mausklick. */
:focus-visible {
outline: 2px solid var(--akzent);
outline-offset: 2px;
border-radius: var(--radius);
}
code, pre {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.9em;
}
code {
background: var(--flaeche-leis);
border: 1px solid var(--rahmen);
padding: 0.1em 0.35em;
border-radius: 4px;
}
pre {
background: var(--flaeche-leis);
border: 1px solid var(--rahmen);
border-radius: var(--radius);
padding: var(--s4);
overflow-x: auto;
margin: 0;
}
pre code { background: none; border: none; padding: 0; }
/* Kleiner erklärender Text. Als Blockelement UND innerhalb von Beschriftungen
* verwendbar, deshalb kein eigenes display. */
.hilfe {
color: var(--text-leise);
font-size: var(--t-meta);
font-weight: normal;
}
/* Speicherort eines Workspace (Schema oder Datei) als blasse zweite Zeile in
* der Workspace-Übersicht. Festbreitenschrift, weil es ein Bezeichner zum
* Abtippen ist; markierbar, damit er sich kopieren lässt. */
.speicherort {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: var(--t-klein);
user-select: all;
}
/* ── 3. Gerüst ───────────────────────────────────────────────────────────── */
/* Schmale Kopfleiste über die ganze Breite. Sie trägt nur Marke und Abmelden —
* die eigentliche Navigation steht in der Seitenleiste. */
.kopfleiste {
background: var(--flaeche);
border-bottom: 1px solid var(--rahmen);
position: sticky;
top: 0;
z-index: 20;
}
.kopfleiste-innen {
max-width: var(--breite-seite);
margin-inline: auto;
padding-inline: var(--s5);
min-height: var(--hoehe-kopfleiste);
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--s4);
}
.marke-wort {
font-weight: 600;
color: var(--text);
letter-spacing: -0.01em;
}
.marke-wort:hover { text-decoration: none; }
/* Workspace-Umschalter in der Kopfleiste: Name des aktuellen Workspace,
* klickbar zur Auswahl-/Verwaltungsseite. Dezent gehalten — die Marke bleibt
* der optische Ankerpunkt, das hier ist Kontext, kein zweites Logo. */
.arbeitsbereich {
display: inline-flex;
align-items: center;
gap: var(--s1);
padding: var(--s1) var(--s2);
border-radius: var(--radius);
color: var(--text-leise);
font-size: var(--t-meta);
font-weight: 500;
}
/* Nur der Umschalter (Link) reagiert auf die Maus — beim Workspace-Admin
* steht der Name als reiner Text ohne Ziel. */
a.arbeitsbereich:hover {
background: var(--flaeche-leis);
color: var(--text);
text-decoration: none;
}
.arbeitsbereich-trenner { color: var(--rahmen-stark); font-weight: 400; }
.arbeitsbereich-name { color: inherit; }
.arbeitsbereich-pfeil { font-size: 0.7em; opacity: 0.6; }
/* Das Gerüst: Seitenleiste links, Inhalt rechts. */
.gerüst {
max-width: var(--breite-seite);
margin-inline: auto;
padding-inline: var(--s5);
display: grid;
grid-template-columns: var(--breite-leiste) minmax(0, 1fr);
gap: var(--s6);
align-items: start;
}
/* Seitenleiste.
*
* position: sticky hält sie beim Scrollen sichtbar. Der Wert für top entspricht
* der Höhe der Kopfleiste, damit sie genau darunter stehen bleibt und nicht
* dahinter verschwindet. */
.seitenleiste {
position: sticky;
top: var(--hoehe-kopfleiste);
max-height: calc(100dvh - var(--hoehe-kopfleiste));
overflow-y: auto;
padding-block: var(--s6);
display: flex;
flex-direction: column;
gap: var(--s5);
}
.nav-gruppe { display: flex; flex-direction: column; gap: var(--s1); }
.nav-titel {
font-size: var(--t-klein);
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text-leise);
margin-bottom: var(--s1);
padding-inline: var(--s2);
}
.nav-liste { list-style: none; margin: 0; padding: 0; }
.nav-liste a {
display: flex;
align-items: center;
gap: var(--s2);
padding: var(--s1) var(--s2);
border-radius: var(--radius);
color: var(--text);
font-size: var(--t-meta);
line-height: 1.5;
}
.nav-liste a:hover { background: var(--flaeche-leis); text-decoration: none; }
/* Der aktive Punkt wird durch Fläche markiert, nicht durch Farbe allein —
* so bleibt er auch bei eingeschränktem Farbsehen erkennbar. */
.nav-liste a.aktiv {
background: var(--akzent-flach);
color: var(--akzent-text);
font-weight: 500;
}
.nav-liste.leise a { color: var(--text-leise); }
/* Schlagwort und Anzahl: Name links, Zahl rechtsbündig. */
.nav-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-zahl { color: var(--text-leise); font-size: var(--t-klein); font-variant-numeric: tabular-nums; }
.nav-liste a.aktiv .nav-zahl { color: inherit; }
/* Inhaltsbereich. */
.inhalt { padding-block: var(--s6) var(--s8); min-width: 0; }
/* Fließtext auf Lesebreite. Nur der Text selbst — Überschrift, Metazeile und
* Tabellen dürfen die volle Breite nutzen. */
.lesebreite { max-width: var(--breite-lesen); }
/* Seitenkopf: Titel links, Aktionen rechts — auf einer Linie.
*
* align-items: center statt flex-start, damit der Knopf mittig zur Überschrift
* sitzt und nicht an deren Oberkante klebt. */
.seitenkopf {
display: flex;
align-items: center;
gap: var(--s4);
flex-wrap: wrap;
margin-bottom: var(--s5);
}
.seitenkopf-titel { flex: 1; min-width: 15rem; }
.seitenkopf-titel .hilfe { display: block; margin-top: var(--s1); }
.seitenkopf-aktionen { display: flex; gap: var(--s2); align-items: center; }
/* Abschnitt innerhalb einer Seite. */
.abschnitt { margin-top: var(--s7); }
.abschnitt > h2 { margin-bottom: var(--s2); }
.abschnitt > .hilfe { display: block; margin-bottom: var(--s4); }
/* ── 4. Bausteine ────────────────────────────────────────────────────────── */
/* Karte: der Standardbehälter für alles Gruppierte. */
.karte {
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius-gross);
padding: var(--s5);
box-shadow: var(--schatten);
}
.karte + .karte { margin-top: var(--s4); }
/* Kennzahlen (Nutzungsseite): eine Reihe kleiner Kacheln, die auf schmalen
* Bildschirmen umbrechen. auto-fit + minmax verteilt sie ohne Media-Query:
* so viele Spalten, wie mit mindestens 9rem Breite hineinpassen. */
.kennzahlen {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
gap: var(--s3);
}
.kennzahl {
display: flex;
flex-direction: column;
gap: var(--s1);
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius-gross);
padding: var(--s4);
}
.kennzahl-wert { font-size: var(--t-h2); font-weight: 600; font-variant-numeric: tabular-nums; }
.kennzahl-name { font-size: var(--t-meta); color: var(--text-leise); }
/* Kurze Listen ohne Aufzählungspunkte, etwa "Verweise ins Leere". */
.kompakt-liste { list-style: none; padding: 0; margin: 0; }
.kompakt-liste li { padding: var(--s1) 0; border-bottom: 1px solid var(--rahmen); }
.kompakt-liste li:last-child { border-bottom: none; }
/* Summenzeile am Ende einer Tabelle. */
table.tabelle tr.summe td { font-weight: 600; border-top: 2px solid var(--rahmen); }
/* Meldungen. */
.meldung {
padding: var(--s3) var(--s4);
border: 1px solid var(--rahmen);
border-left-width: 3px;
border-radius: var(--radius);
margin-bottom: var(--s5);
font-size: var(--t-meta);
}
.meldung.hinweis { background: var(--akzent-flach); border-left-color: var(--akzent); }
.meldung.fehler { background: var(--warn-flach); border-left-color: var(--warn); }
/* Leerzustand: nicht nur ein grauer Absatz, sondern ein erkennbarer Bereich mit
* einem Hinweis, was als Nächstes zu tun wäre. */
.leer {
text-align: center;
padding: var(--s7) var(--s5);
border: 1px dashed var(--rahmen-stark);
border-radius: var(--radius-gross);
color: var(--text-leise);
}
.leer p { margin-bottom: var(--s4); }
.leer p:last-child { margin-bottom: 0; }
/* Marken: eine einzige visuelle Familie für alle kleinen Auszeichnungen. */
.marke {
display: inline-flex;
align-items: center;
gap: var(--s1);
padding: 0.1rem var(--s2);
border: 1px solid var(--rahmen);
border-radius: 999px;
font-size: var(--t-klein);
line-height: 1.5;
color: var(--text-leise);
white-space: nowrap;
}
a.marke:hover {
border-color: var(--rahmen-stark);
color: var(--text);
text-decoration: none;
}
.marke.betont {
background: var(--akzent-flach);
border-color: transparent;
color: var(--akzent-text);
}
/* Etwas, um das sich jemand kümmern sollte, etwa ein seit Langem ungenutzter Key. */
.marke.warnung {
background: var(--warn-flach);
border-color: transparent;
color: var(--warn);
}
/* Metazeile unter einem Titel: Marken und Kleintext in einer Reihe. */
.meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--s2);
color: var(--text-leise);
font-size: var(--t-klein);
}
/* Knöpfe. Drei Ausprägungen, mehr braucht es nicht. */
button, .knopf {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--s2);
padding: var(--s2) var(--s4);
min-height: var(--hoehe-bedienelement);
background: var(--akzent);
color: #fff;
border: 1px solid transparent;
border-radius: var(--radius);
font: inherit;
font-size: var(--t-meta);
font-weight: 500;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
}
button:hover, .knopf:hover { filter: brightness(1.08); text-decoration: none; }
.knopf.zweitrangig, button.zweitrangig {
background: var(--flaeche);
color: var(--text);
border-color: var(--rahmen-stark);
}
.knopf.zweitrangig:hover, button.zweitrangig:hover {
background: var(--flaeche-leis);
filter: none;
}
button.warnung {
background: var(--flaeche);
color: var(--warn);
border-color: var(--warn);
}
button.warnung:hover { background: var(--warn-flach); filter: none; }
/* Ein Knopf, der wie ein Link aussieht — nötig, weil Abmelden ein POST sein muss
* (siehe CSRF-Schutz), aber wie ein Menüpunkt wirken soll. */
.wie-link {
background: none;
border: none;
padding: 0;
min-height: 0;
font: inherit;
font-size: var(--t-meta);
font-weight: normal;
color: var(--text-leise);
cursor: pointer;
}
.wie-link:hover { filter: none; text-decoration: underline; }
/* Rechte Seite der Kopfleiste: Hilfe und Abmelden, im selben leisen Ton.
* Doppelte Klasse, weil .reihe weiter unten sonst den Abstand überschreibt. */
.reihe.kopfleiste-aktionen { gap: var(--s5); }
.kopfleiste-link {
font-size: var(--t-meta);
color: var(--text-leise);
}
.kopfleiste-link:hover { color: var(--text); }
.kopfleiste-link.aktiv { color: var(--text); font-weight: 500; }
.reihe { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
form.reihe { display: inline-flex; }
/* In rechtsbündigen Tabellenspalten (z. B. Verlauf) sollen die Aktionen
* ebenfalls rechts stehen — ein Flex-Container ignoriert text-align. */
.rechts > .reihe { justify-content: flex-end; }
/* Formulare.
*
* Jedes Feld ist ein .feld mit Beschriftung, optionalem Hinweis und Bedienelement.
* Die Breite des BEDIENELEMENTS richtet sich nach dem erwarteten Inhalt statt
* immer 100 % zu sein: Ein Feld für einen Bezeichner, das über die halbe Seite
* läuft, sieht aus wie ein Fehler. Beschriftung und Hinweis dürfen breiter
* sein (bis zur Lesebreite) — sonst bricht ein Hinweis in eine schmale Spalte
* um und wirkt gedrückt (so war es bis 2026-10-16). */
.formular { display: flex; flex-direction: column; gap: var(--s5); }
.feld { display: flex; flex-direction: column; gap: var(--s1); }
.feld > label { font-weight: 500; font-size: var(--t-meta); }
.feld > .hilfe {
max-width: var(--breite-lesen);
line-height: 1.5;
margin-bottom: var(--s1);
}
input[type=text], input[type=search], input[type=password], input[type=number],
input[type=date], input[type=email], input[type=url], select, textarea {
width: 100%;
padding: var(--s2) var(--s3);
min-height: var(--hoehe-bedienelement);
background: var(--flaeche);
border: 1px solid var(--rahmen-stark);
border-radius: var(--radius);
color: var(--text);
font: inherit;
font-size: var(--t-meta);
}
/* Einzeilige Bedienelemente bekommen eine feste Höhe statt nur einer
* Mindesthöhe. Sonst sind Textfeld, Auswahlliste und Datumsfeld je nach
* Browser unterschiedlich hoch (beobachtet: 40, 36 und 42 Pixel), und in
* einer Zeile wie der Filterleiste stehen ihre Beschriftungen versetzt. */
input:is([type=text], [type=search], [type=password], [type=number], [type=date], [type=email], [type=url]),
select {
height: var(--hoehe-bedienelement);
padding-block: 0;
}
input::placeholder { color: var(--text-leise); opacity: 0.7; }
/* Dateiauswahl: kein Textfeld, aber derselbe Rahmen, damit sie im Formular
* nicht wie ein Fremdkörper wirkt. */
input[type=file] {
width: 100%;
padding: var(--s2);
border: 1px dashed var(--rahmen-stark);
border-radius: var(--radius);
background: var(--flaeche);
font-size: var(--t-meta);
}
input:focus, select:focus, textarea:focus {
border-color: var(--akzent);
outline: 2px solid var(--akzent-flach);
outline-offset: 0;
}
textarea {
resize: vertical;
line-height: 1.6;
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
min-height: 20rem;
}
/* Reiter "Schreiben"/"Vorschau" über dem Inhaltsfeld. */
.editor-reiter {
display: flex;
gap: var(--s1);
margin-bottom: var(--s2);
border-bottom: 1px solid var(--rahmen);
}
.editor-reiter-knopf {
padding: var(--s2) var(--s3);
background: none;
border: none;
border-bottom: 2px solid transparent;
border-radius: 0;
min-height: 0;
color: var(--text-leise);
font-weight: 500;
font-size: var(--t-meta);
cursor: pointer;
margin-bottom: -1px;
}
.editor-reiter-knopf:hover { color: var(--text); filter: none; }
.editor-reiter-knopf.aktiv {
color: var(--text);
border-bottom-color: var(--akzent);
}
/* Vorschau-Fläche: dieselbe Textdarstellung wie die Detailansicht
* (.eintrag-text), aber als Formularfeld gerahmt, damit der Wechsel
* zwischen Schreiben und Vorschau nicht die Höhe des Formulars springen
* lässt. */
.eintrag-text.vorschau-flaeche {
min-height: 20rem;
max-width: none;
padding: var(--s3);
background: var(--flaeche);
border: 1px solid var(--rahmen-stark);
border-radius: var(--radius);
/* Überschreibt den großzügigen margin-top von .eintrag-text (siehe
* weiter unten) — hier steckt es in einem Formularfeld, nicht unter
* einer Überschrift. Die doppelte Klasse hebt die Reihenfolge im
* Stylesheet auf: diese Regel gewinnt unabhängig davon, wo sie steht. */
margin-top: 0;
}
.vorschau-flaeche.vorschau-laedt { color: var(--text-leise); }
/* Vorschlagsliste der Autovervollständigung für [[…]] — direkt unter dem
* Feld statt an der Schreibmarke ausgerichtet, siehe autocomplete.js. */
.autovervollstaendigung {
list-style: none;
margin: var(--s2) 0 0;
padding: 0;
max-height: 12rem;
overflow-y: auto;
background: var(--flaeche);
border: 1px solid var(--rahmen-stark);
border-radius: var(--radius);
box-shadow: var(--schatten);
}
.autovervollstaendigung li {
display: flex;
align-items: baseline;
gap: var(--s2);
padding: var(--s2) var(--s3);
cursor: pointer;
font-size: var(--t-meta);
}
.autovervollstaendigung li:not(:last-child) { border-bottom: 1px solid var(--rahmen); }
.autovervollstaendigung li span { color: var(--text-leise); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.autovervollstaendigung li.aktiv { background: var(--akzent-flach); }
.autovervollstaendigung li.aktiv span { color: var(--akzent-text); }
/* Breiten der Bedienelemente nach Inhalt — nur das Eingabefeld wird
* begrenzt, nicht Beschriftung und Hinweis (siehe oben). */
.feld.kurz > :is(input, select) { max-width: 18rem; }
.feld.mittel > :is(input, select) { max-width: 28rem; }
.feld.lang > :is(input, select) { max-width: 40rem; }
/* Ankreuzfeld: Kästchen und Text nebeneinander, Text nicht fett. */
.ankreuz {
flex-direction: row;
align-items: flex-start;
gap: var(--s2);
}
/* Das Kästchen sitzt etwas tiefer als die Oberkante des Textes, damit es
* optisch auf der ersten Textzeile liegt statt darüber. */
.ankreuz input { width: auto; min-height: 0; margin-top: 0.3rem; }
.ankreuz label { font-weight: normal; }
/* Der Hinweis steht unter der Beschriftung, nicht in derselben Zeile. */
.ankreuz label .hilfe { display: block; margin-top: var(--s1); max-width: var(--breite-lesen); }
.formular-aktionen {
display: flex;
flex-wrap: wrap;
gap: var(--s2);
align-items: center;
padding-top: var(--s1);
}
/* Tabellen. */
.tabelle-huelle {
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius-gross);
box-shadow: var(--schatten);
overflow-x: auto; /* breite Tabellen scrollen in sich, nicht die Seite */
}
table.tabelle {
width: 100%;
border-collapse: collapse;
font-size: var(--t-meta);
}
.tabelle th, .tabelle td {
text-align: left;
padding: var(--s3) var(--s4);
border-bottom: 1px solid var(--rahmen);
vertical-align: middle;
}
.tabelle thead th {
background: var(--flaeche-leis);
color: var(--text-leise);
font-weight: 500;
font-size: var(--t-klein);
white-space: nowrap;
}
.tabelle tbody tr:last-child td { border-bottom: none; }
.tabelle tbody tr:hover { background: var(--flaeche-leis); }
.tabelle .zahl { text-align: right; font-variant-numeric: tabular-nums; }
.tabelle .schmal { width: 1%; white-space: nowrap; }
.tabelle .rechts { text-align: right; }
.tabelle tr.gedaempft { color: var(--text-leise); }
.tabelle tr.gedaempft td:first-child { text-decoration: line-through; }
/* ── 5. Seiten ───────────────────────────────────────────────────────────── */
/* Anmeldung: eigenständige, ruhige Seite ohne Navigation. */
.anmeldeseite {
min-height: 100dvh;
display: flex;
align-items: center;
justify-content: center;
/* Unten mehr Abstand als oben: Das hebt den Block etwas über die
* mathematische Mitte. Genau mittig wirkt er optisch zu tief. */
padding: var(--s5) var(--s5) 12vh;
}
.anmeldeseite .karte { width: 100%; padding: var(--s6); }
.anmeldeseite .hilfe { display: block; margin-bottom: var(--s5); }
/* Logo zentriert über der Anmeldekarte. */
.anmelde-logo { display: flex; justify-content: center; margin-bottom: var(--s2); }
/* 264px, damit auch die kleine Unterzeile im Logo ("MCP Server • Wiki /
* Knowledge Base") lesbar bleibt. max-width fängt schmale Handys ab. */
.anmelde-logo img { width: 264px; max-width: 100%; height: auto; }
/* Werkzeugleiste über der Eintragsliste: Suche und Filter. */
.werkzeugleiste {
display: flex;
gap: var(--s2);
margin-bottom: var(--s4);
}
.werkzeugleiste input[type=search] { flex: 1; }
/* Hinweis "Gibt es schon?" unter dem Titel eines neuen Eintrags. */
.aehnlich-hinweis {
margin-top: var(--s2);
padding: var(--s2) var(--s3);
border-left: 3px solid var(--akzent);
background: var(--flaeche-leis);
border-radius: var(--radius);
}
.aehnlich-hinweis ul { margin: var(--s1) 0 0; padding-left: var(--s4); }
.aehnlich-hinweis li { font-size: var(--t-meta); }
/* Eigenschaftsliste (Begriff: Wert), z.B. auf der Systemseite. */
.eigenschaften {
display: grid;
grid-template-columns: max-content 1fr;
gap: var(--s2) var(--s4);
margin: var(--s3) 0 0;
}
.eigenschaften dt { color: var(--text-leise); }
.eigenschaften dd { margin: 0; }
@media (max-width: 60rem) {
.eigenschaften { grid-template-columns: 1fr; gap: var(--s1); }
.eigenschaften dd { margin-bottom: var(--s2); }
}
/* Systemseite: Kasten "Zustand". Warnungen rot, Hinweise grün — dieselben
* Farben wie .meldung, aber als Liste, damit mehrere untereinander passen. */
.zustand { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s2); }
.zustand > li {
display: flex;
gap: var(--s3);
align-items: baseline;
padding: var(--s3) var(--s4);
border: 1px solid var(--rahmen);
border-left-width: 3px;
border-radius: var(--radius);
font-size: var(--t-meta);
line-height: 1.5;
}
.zustand > li.zustand-warnung { background: var(--warn-flach); border-left-color: var(--warn); }
.zustand > li.zustand-hinweis { background: var(--akzent-flach); border-left-color: var(--akzent); }
.zustand-art { font-weight: 600; flex: none; min-width: 5rem; max-width: 12rem; overflow-wrap: anywhere; }
.zustand-warnung .zustand-art { color: var(--warn); }
/* Überblick: mehrere Befunde eines Projekts in einer Karte. */
.zustand-punkte { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s1); flex: 1; min-width: 0; }
.zustand-punkte .dringend { font-weight: 600; }
.weiter-link { white-space: nowrap; font-weight: 400; }
.zustand-hinweis .zustand-art { color: var(--akzent-text); }
/* Systemseite: Balkendiagramme (internal/web/diagramm.go). Untereinander
* und höchstens so breit wie Fließtext: Das SVG skaliert samt Schrift mit
* seiner Breite — nebeneinander wären die Beschriftungen zu klein, über die
* ganze Seite die Balken unnötig flach. */
.diagramme {
display: grid;
gap: var(--s4);
max-width: var(--breite-lesen);
margin-bottom: var(--s2);
}
.diagramm-rahmen {
margin: 0;
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius-gross);
padding: var(--s4);
min-width: 0;
}
.diagramm-rahmen figcaption {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--s1) var(--s4);
margin-bottom: var(--s3);
font-size: var(--t-meta);
}
.legende { display: inline-flex; align-items: center; gap: var(--s1); color: var(--text-leise); }
.legende-farbe { display: inline-block; width: 0.75rem; height: 0.75rem; border-radius: 2px; }
.diagramm { display: block; width: 100%; height: auto; }
.diagramm-gitter { stroke: var(--rahmen); stroke-width: 1; }
.diagramm-text { fill: var(--text-leise); font-size: 11px; font-variant-numeric: tabular-nums; }
/* Unsichtbar, aber da: Über ihr erscheint der Tooltip eines Tages. */
.diagramm-treffer { fill: transparent; }
.diagramm-treffer:hover { fill: var(--flaeche-leis); }
.diagramm-leer { margin: var(--s5) 0; text-align: center; }
/* Die Farben gelten für die Balken (SVG: fill) und die Legende (HTML:
* background). Menschen in derselben Farbe wie Agenten, nur heller — beides
* ist "Schreiben". */
rect.balken-agent { fill: var(--akzent); }
rect.balken-mensch { fill: var(--akzent); fill-opacity: 0.4; }
rect.balken-suche { fill: var(--text-leise); }
.legende-farbe.balken-agent { background: var(--akzent); }
.legende-farbe.balken-mensch { background: var(--akzent); opacity: 0.4; }
.legende-farbe.balken-suche { background: var(--text-leise); }
/* Aufklappbarer Zusatz unter einer Tabelle oder einem Diagramm. */
.aufklapper { margin-top: var(--s3); }
.aufklapper > summary { cursor: pointer; color: var(--akzent-text); font-size: var(--t-meta); }
.aufklapper[open] > summary { margin-bottom: var(--s3); }
.abschnitt h3 { margin: var(--s5) 0 var(--s2); }
.ruhig-liste { margin: var(--s2) 0 0; padding-left: var(--s5); columns: 2 16rem; font-size: var(--t-meta); }
/* Warnender Text in einer Kennzahl. */
.warn-text { color: var(--warn); }
/* Filter über einer Tabelle, z.B. im Protokoll. Bricht auf schmalen
* Bildschirmen um. */
.filterleiste {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: var(--s2) var(--s3);
margin-bottom: var(--s4);
}
.filterleiste label {
display: flex;
flex-direction: column;
gap: var(--s1);
font-size: var(--t-meta);
color: var(--text-leise);
}
.filterleiste a { align-self: center; font-size: var(--t-meta); }
/* In der Filterleiste nehmen die Felder ihre natürliche Breite, nicht 100 %
* des Etiketts — sonst schrumpfen sie auf die Breite der Beschriftung. */
.filterleiste :is(input, select) { width: auto; min-width: 9rem; }
/* Filterlink neben der Suche, z.B. "3 veraltet". */
.werkzeugleiste .filter-link {
align-self: center;
white-space: nowrap;
font-size: var(--t-klein);
}
/* Eintragsliste. */
.eintraege {
list-style: none;
margin: 0;
padding: 0;
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius-gross);
box-shadow: var(--schatten);
overflow: hidden;
}
.eintraege li {
display: flex;
align-items: flex-start;
gap: var(--s3);
padding: var(--s4);
border-bottom: 1px solid var(--rahmen);
}
.eintraege li:last-child { border-bottom: none; }
.eintraege li:hover { background: var(--flaeche-leis); }
/* Auswahlkästchen der Sammelbearbeitung: etwas abgesetzt vom Titel, auf
* dessen erster Zeile ausgerichtet statt an der Oberkante der ganzen Zeile. */
.eintrag-auswahl {
display: flex;
align-items: center;
padding-top: 0.35rem;
}
.eintrag-inhalt { flex: 1; min-width: 0; }
/* Sammelleiste: erscheint unter der Liste, sobald Einträge angehakt sind.
* position: sticky hält sie beim Scrollen einer langen Liste in Sichtweite. */
.sammelleiste {
display: flex;
align-items: center;
gap: var(--s2);
flex-wrap: wrap;
margin-top: var(--s3);
padding: var(--s3) var(--s4);
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius-gross);
box-shadow: var(--schatten);
position: sticky;
bottom: var(--s4);
}
/* Papierkorb: "Alle von … wiederherstellen" steht mit etwas Abstand unter der
* Sammelleiste. */
.papierkorb-alle { margin-top: var(--s3); }
/* Für Screenreader lesbar, aber unsichtbar — etwa die Überschrift einer
* Spalte, die nur Kästchen enthält. display:none ginge nicht: Das würde den
* Text auch vor Screenreadern verstecken. */
.unsichtbar {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sammelleiste input[type=text] {
width: auto;
flex: 1 1 12rem;
min-height: 2rem;
padding: var(--s1) var(--s2);
}
.eintrag-titel {
display: block;
font-size: var(--t-gross);
font-weight: 500;
color: var(--text);
margin-bottom: var(--s2);
}
.eintrag-ausschnitt {
margin: var(--s2) 0 0;
color: var(--text-leise);
font-size: var(--t-meta);
/* Auf zwei Zeilen begrenzen, damit alle Zeilen der Liste gleich hoch bleiben. */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.eintrag-ausschnitt mark {
background: var(--akzent-flach);
color: var(--akzent-text);
padding: 0 0.15em;
border-radius: 3px;
}
/* Einzelner Eintrag: Fließtext in Lesebreite. */
.eintrag-text {
font-size: var(--t-gross);
line-height: 1.75;
margin-top: var(--s6);
}
.eintrag-text > :first-child { margin-top: 0; }
.eintrag-text h1, .eintrag-text h2, .eintrag-text h3 { margin: var(--s6) 0 var(--s3); }
.eintrag-text p, .eintrag-text ul, .eintrag-text ol { margin: 0 0 var(--s4); }
.eintrag-text ul, .eintrag-text ol { padding-left: var(--s5); }
.eintrag-text li { margin-bottom: var(--s1); }
.eintrag-text img { max-width: 100%; border-radius: var(--radius); }
.eintrag-text blockquote {
margin: var(--s4) 0;
padding: var(--s1) var(--s4);
border-left: 3px solid var(--rahmen-stark);
color: var(--text-leise);
}
.eintrag-text pre { margin-bottom: var(--s4); font-size: var(--t-meta); }
.eintrag-text table { border-collapse: collapse; width: 100%; font-size: var(--t-meta); }
.eintrag-text th, .eintrag-text td { border: 1px solid var(--rahmen); padding: var(--s2) var(--s3); }
.eintrag-text hr { border: none; border-top: 1px solid var(--rahmen); margin: var(--s6) 0; }
/* Checklisten (- [ ] / - [x]): Das Kästchen ersetzt den Aufzählungspunkt.
* :has() wählt nur Listenpunkte aus, die direkt ein Kästchen enthalten —
* gewöhnliche Aufzählungen behalten ihren Punkt. */
.eintrag-text li:has(> input[type="checkbox"]) { list-style: none; margin-left: calc(-1 * var(--s4)); }
.eintrag-text li > input[type="checkbox"] { margin: 0 var(--s2) 0 0; vertical-align: middle; }
.eintrag-text del { color: var(--text-leise); }
.rueckverweise { margin-top: var(--s7); }
.rueckverweise h2 { font-size: var(--t-meta); color: var(--text-leise); margin-bottom: var(--s3); }
.rueckverweise ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
/* Neu erzeugter Key: wird genau einmal angezeigt, also deutlich hervorheben. */
.key-anzeige { border-color: var(--akzent); background: var(--akzent-flach); }
.key-wert {
display: block;
margin: var(--s3) 0;
padding: var(--s3);
background: var(--flaeche);
border: 1px solid var(--rahmen-stark);
border-radius: var(--radius);
font-family: ui-monospace, Menlo, Consolas, monospace;
font-size: var(--t-meta);
word-break: break-all;
user-select: all; /* ein Klick markiert den ganzen Key */
}
.key-anleitung { margin-top: var(--s4); }
.key-anleitung summary {
cursor: pointer;
font-size: var(--t-meta);
color: var(--akzent-text);
}
.key-anleitung pre { margin-top: var(--s3); font-size: var(--t-klein); }
/* Fassungsvergleich. */
.diff-koepfe {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--s4);
margin-bottom: var(--s4);
}
.diff-kopf {
display: flex;
flex-direction: column;
gap: var(--s1);
padding: var(--s3) var(--s4);
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius);
}
.diff-huelle {
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius-gross);
box-shadow: var(--schatten);
overflow-x: auto;
}
.diff {
margin: 0;
padding: var(--s3) 0;
font-size: var(--t-meta);
line-height: 1.6;
white-space: pre-wrap;
word-break: break-word;
}
.diff span { display: block; padding: 0 var(--s4); }
.diff-hinzugefuegt { background: var(--akzent-flach); color: var(--akzent-text); }
.diff-entfernt { background: var(--warn-flach); color: var(--warn); }
/* Graph-Ansicht der Verweise. */
.graph-huelle {
background: var(--flaeche);
border: 1px solid var(--rahmen);
border-radius: var(--radius-gross);
box-shadow: var(--schatten);
overflow: hidden;
margin-bottom: var(--s4);
}
.graph-huelle svg {
display: block;
width: 100%;
/* Möglichst viel Fläche, aber auf kleinen und sehr hohen Bildschirmen
* in vernünftigen Grenzen. */
height: clamp(30rem, 75vh, 60rem);
cursor: grab;
touch-action: none;
}
.graph-huelle svg.graph-zieht { cursor: grabbing; }
.graph-kante { stroke: var(--rahmen-stark); stroke-width: 1.5; transition: opacity 0.15s; }
.graph-knoten-gruppe { transition: opacity 0.15s; }
.graph-punkt {
fill: var(--akzent-flach);
stroke: var(--akzent);
stroke-width: 1.5;
}
.graph-knoten-fehlend .graph-punkt {
fill: var(--flaeche);
stroke: var(--rahmen-stark);
stroke-dasharray: 3 2;
}
.graph-beschriftung {
font-size: 0.75rem;
fill: var(--text);
dominant-baseline: middle;
/* Beschriftungen sollen das Ziehen des Graphen nicht durch
* Textmarkierung stören. */
user-select: none;
/* Ein Rand in Hintergrundfarbe um jeden Buchstaben: Kanten, die hinter
* dem Text verlaufen, werden so unterbrochen, statt die Schrift zu
* durchkreuzen. paint-order malt den Rand zuerst, die Füllung darüber. */
paint-order: stroke;
stroke: var(--flaeche);
stroke-width: 3px;
stroke-linejoin: round;
}
/* Welche Namen Platz haben, entscheidet static/graph.js (beschrifte()). */
.graph-beschriftung-aus { display: none; }
.graph-knoten-fehlend .graph-beschriftung { fill: var(--text-leise); }
.graph-klickbar { cursor: pointer; }
.graph-klickbar:hover .graph-punkt { stroke-width: 2.5; }
.graph-klickbar:hover .graph-beschriftung { fill: var(--akzent-text); }
/* Hervorhebung beim Überfahren: der Knoten, seine Nachbarn und die
* verbindenden Kanten bleiben kräftig, der Rest tritt zurück. */
.graph-fokus .graph-knoten-gruppe:not(.graph-nachbar) { opacity: 0.2; }
.graph-fokus .graph-kante:not(.graph-kante-aktiv) { opacity: 0.1; }
.graph-kante-aktiv { stroke: var(--akzent); }
/* Seite "Alle Schlagworte": mehrspaltige Liste, die Spaltenzahl ergibt sich
* aus der verfügbaren Breite. */
.schlagwort-liste {
list-style: none;
margin: 0 0 var(--s5);
padding: 0;
columns: 14rem;
column-gap: var(--s5);
}
.schlagwort-liste li { break-inside: avoid; }
.schlagwort-liste a {
display: flex;
align-items: center;
gap: var(--s2);
padding: var(--s1) var(--s2);
border-radius: var(--radius);
color: var(--text);
font-size: var(--t-meta);
}
.schlagwort-liste a:hover { background: var(--flaeche-leis); text-decoration: none; }
.schlagwort-gruppe h2 {
font-size: var(--t-gross);
color: var(--text-leise);
border-bottom: 1px solid var(--rahmen);
padding-bottom: var(--s1);
margin-bottom: var(--s2);
}
/* Gefahrenzone: Löschvorgänge stehen abgesetzt am Seitenende, nicht zwischen
* den harmlosen Aktionen. Vorher steckte das Löschen eines Projekts samt
* Bestätigungsfeld in einer Tabellenzelle — zu eng und viel zu beiläufig für
* eine Aktion, die alle Einträge mitnimmt. */
.gefahrenzone { border-color: var(--warn); }
.gefahrenzone h2 { color: var(--warn); }
/* Hilfe: Themen als umbrechende Reiterleiste über dem Text, der Text selbst
* wie ein Eintrag in Lesebreite. */
.hilfe-navigation {
display: flex;
flex-wrap: wrap;
gap: var(--s2);
margin-top: var(--s5);
padding-bottom: var(--s4);
border-bottom: 1px solid var(--rahmen);
}
.hilfe-navigation a {
padding: var(--s1) var(--s3);
border: 1px solid var(--rahmen);
border-radius: var(--radius);
color: var(--text-leise);
font-size: var(--t-meta);
}
.hilfe-navigation a:hover { background: var(--flaeche-leis); color: var(--text); text-decoration: none; }
.hilfe-navigation a.aktiv {
background: var(--akzent-flach);
border-color: var(--akzent);
color: var(--akzent-text);
font-weight: 500;
}
.hilfe-text { max-width: var(--breite-lesen); }
/* Das kleine "?" neben Überschriften und Feldern (Partial "hilfelink").
* Bewusst unaufdringlich: ein Kreis in Textgröße, der erst beim Überfahren
* Farbe bekommt. Die Größe in rem (bezogen auf die Grundschrift, nicht auf
* die Umgebung), damit der Kreis neben einer großen h1 genauso klein bleibt
* wie neben einem Formular-Label. */
.hilfe-link {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.25rem;
height: 1.25rem;
margin-left: var(--s1);
border: 1px solid var(--rahmen);
border-radius: 50%;
font-size: 0.75rem;
font-weight: 600;
line-height: 1;
color: var(--text-leise);
vertical-align: middle;
}
.hilfe-link:hover {
border-color: var(--akzent);
color: var(--akzent-text);
text-decoration: none;
}
/* ── 6. Schmale Geräte ───────────────────────────────────────────────────── */
/* Unterhalb dieser Breite ist für zwei Spalten kein Platz mehr: Die Seitenleiste
* rückt über den Inhalt und wird flach — aus den Listen werden Zeilen, die bei
* Bedarf seitlich scrollen. So bleibt alles erreichbar, ohne dass eine
* ausklappbare Navigation samt JavaScript nötig wäre. */
@media (max-width: 60rem) {
.gerüst {
grid-template-columns: minmax(0, 1fr);
gap: 0;
padding-inline: var(--s4);
}
.kopfleiste-innen { padding-inline: var(--s4); gap: var(--s3); }
/* Kopfleiste: Auf schmalen Bildschirmen reicht "Hilfe", und ein langer
* Workspace-Name wird gekürzt statt die Leiste zu sprengen. */
.nur-breit { display: none; }
.reihe.kopfleiste-aktionen { gap: var(--s4); flex-wrap: nowrap; }
.kopfleiste-innen > .reihe:first-child { min-width: 0; flex-wrap: nowrap; }
.arbeitsbereich { min-width: 0; }
.arbeitsbereich-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.seitenleiste {
position: static;
max-height: none;
overflow: visible;
flex-direction: row;
flex-wrap: wrap;
gap: var(--s4);
padding-block: var(--s4);
border-bottom: 1px solid var(--rahmen);
}
/* Jede Gruppe wird zu einer Zeile: Überschrift links, Punkte daneben. */
.nav-gruppe {
flex-direction: row;
align-items: center;
gap: var(--s2);
min-width: 0;
flex: 1 1 100%;
}
.nav-titel { margin-bottom: 0; padding-inline: 0; white-space: nowrap; }
/* min-width: 0, damit die Zeile in sich scrollt, statt die ganze Seite
* zu verbreitern — ein Flex-Element gibt sonst nicht unter die Breite
* seines Inhalts nach. */
.nav-liste {
display: flex;
gap: var(--s1);
min-width: 0;
overflow-x: auto;
scrollbar-width: none; /* die Leiste scrollt, ohne einen Balken zu zeigen */
}
.nav-liste::-webkit-scrollbar { display: none; }
.nav-liste a { white-space: nowrap; }
/* Die Schlagwortliste kann lang werden — hier zählt jede Zeile. */
.schlagworte .nav-liste a { padding-inline: var(--s2); }
.inhalt { padding-block: var(--s5) var(--s7); }
.seitenkopf { gap: var(--s3); }
.seitenkopf-aktionen { width: 100%; }
.seitenkopf-aktionen .knopf { flex: 1; }
.karte { padding: var(--s4); }
.tabelle th, .tabelle td { padding: var(--s2) var(--s3); }
.feld.kurz > :is(input, select), .feld.mittel > :is(input, select),
.feld.lang > :is(input, select) { max-width: none; }
textarea { min-height: 14rem; }
.diff-koepfe { grid-template-columns: 1fr; }
}