Files
projektuebersicht/public/styles.css
T
erikandClaude Opus 5.5 e291ca8438 Neues Design „Bauplan“, Dashboard, Notizen-Bereich, Seitenleiste
- Gestaltung „Bauplan“: ruhigere Farben, Schriftskala, Abstände im
  4-px-Raster, Kalenderwochen und Nummern in Mono-Ziffern, Glas beruhigt
- Dashboard „Meine Aufgaben“: Kopf mit Kalenderwoche, Band „Diese Woche“
  (Mo–Fr), je Projekt Meine Rolle / Team / Notizen; offene Termine blau
  hervorgehoben; Monatskalender unverändert
- Neuer Bereich „Notizen“ in der Seitenleiste: Notizen aller eigenen
  Projekte und eigene Notizen ohne Projekt, später einem Projekt zuordenbar
- Seitenleiste neu gegliedert (Projekte, Team, Notizen, Studenten,
  Einstellungen/Admin unten), Admin-Punkte in Personen/Vorgaben/System
- Reiter im Projektkopf wie die Seitenleiste hervorgehoben
- Behoben: „&…“ in Kommentaren wuchs bei jedem Speichern;
  bestehende Kommentare werden beim Start repariert
- Behoben: Lösch-Rückfrage bei Notizen unlesbar bzw. blieb stehen,
  Cursor im Notiztitel verdeckt, Dropdown im Dunkelmodus gekachelt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 15:07:32 +01:00

6485 lines
255 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* === Design-Tokens === */
:root {
--accent: #0f5a85;
--accent-hover: #0d5079;
--accent-active: #0a4565;
--accent-soft: #e8f0f6;
--text: #16191d;
--text-muted: #4d545c;
--text-subtle: #69707a;
--bg: #f4f5f7;
--surface: #ffffff;
--surface-alt: #f9fafb;
--surface-sunk: #eef0f2;
--line: #dcdfe3;
--line-soft: #e9ebee;
--green: #1f7a3a;
--amber: #a64d00;
--red: #c4161c;
--red-hover: #d42a30;
--red-active: #a51217;
/* Flächen des Anwesenheits-Kacheln im Zustand "anwesend" — eigene Tokens,
weil eine feste rgba-Grünfläche im Dark Mode nicht trägt. */
--attend-bg: rgba(36,138,61,.10);
--attend-bg-hover: rgba(36,138,61,.16);
/* Kopfband der Wochenbericht-Karten je Änderungsart. Das Band trägt weiße
Schrift, deshalb etwas dunkler als --green (#248a3d käme nur auf
Kontrast 4,4 : 1 und bliebe damit unter den 4,5 : 1 für normalen Text). */
--band-neu: #0f5a85;
--band-fortschritt: #1f7a3a;
--meilenstein: #7a3fb8;
/* Gelbe Markierung neu hinzugekommener Protokoll-Punkte — bewusst eigenes
Token statt --band-neu (dort blau, bedeutet "neu kommentiertes Projekt"
im Wochenbericht — eine andere Bedeutung, die hier kollidieren würde). */
--protokoll-neu-bg: #fff6db;
--protokoll-neu-border: #e6b800;
/* Die drei Zustände eines Protokoll-Punkts (protokoll_punkte.status) —
eigene Bezeichner statt direkter --amber/--green/--accent-Nutzung an
jeder Stelle, damit sich die Farbzuordnung an einer Stelle ändern
ließe, ohne jede Verwendung einzeln anzufassen. Referenzieren andere
Tokens per var(), lösen sich also auch im Dark Mode korrekt auf, ohne
dort erneut definiert zu werden. --status-info-bg ist die Fläche einer
kompletten Info-Zeile in der Protokoll-Tabelle (color-mix statt fester
rgba-Werte, damit die Deckkraft in beiden Themes gleich wirkt). */
--status-offen: var(--amber);
--status-erledigt: var(--green);
--status-info: var(--accent);
--status-info-bg: color-mix(in srgb, var(--accent) 8%, transparent);
--r-sm: 6px;
--r-md: 8px;
--r-lg: 12px;
--r-xl: 16px;
--r-pill: 8px;
--sh-1: 0 1px 2px rgba(16,24,32,.05);
--sh-2: 0 2px 8px rgba(16,24,32,.06), 0 1px 2px rgba(16,24,32,.04);
--sh-3: 0 8px 24px rgba(16,24,32,.10), 0 2px 6px rgba(16,24,32,.05);
--ease: cubic-bezier(.4, 0, .2, 1);
/* === Bewegung (Stufe "Spürbar", Eriks Wahl aus dem Interaktions-
Vergleich) ============================================================
Die gesamte Rückmeldung der Oberfläche hängt an diesen fünf Werten.
Eine andere Stufe wäre ein Wechsel HIER, nicht in den Komponenten:
jede Regel weiter unten liest ausschliesslich über var().
--dur kurze Rückmeldung (Fläche, Farbe, Anheben, Druck)
--dur-lg grössere Zustandswechsel (Aufklappen, Dialog, Band)
--ease-out Ausschwingen: schnell los, weich aus. Bewusst anders als
das ältere --ease (symmetrisch), das für Bestandsregeln
unverändert bleibt.
--row-lift wie weit sich eine anklickbare Zeile beim Zeigen hebt
--press Stauchung beim Drücken (Knöpfe, Chips, Zeilen)
Grundregeln, die überall gelten: bewegt wird nur transform und
opacity (nie Höhe/Breite, das ruckelt), nichts läuft in Dauerschleife,
und jede Regung quittiert eine echte Handlung. Wer im Betriebssystem
"Bewegung reduzieren" gesetzt hat, bekommt am Dateiende alles auf
praktisch null gesetzt - dort genügt es, diese Variablen zu kippen. */
--dur: 150ms;
--dur-lg: 200ms;
--ease-out: cubic-bezier(.22, 1, .36, 1);
--row-lift: -1px;
--press: .97;
}
/* === Basis & Reset === */
* { box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI',
Roboto, Helvetica, Arial, sans-serif;
margin: 0;
background: var(--bg);
color: var(--text);
font-size: 13px;
line-height: 1.45;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
letter-spacing: 0;
font-variant-numeric: tabular-nums;
}
.container { padding: 20px 24px; }
/* Das hidden-Attribut muss immer gewinnen. Regeln wie button.menu-item
{ display:inline-flex } überstimmten sonst die Browser-Vorgabe, und z. B.
"+ Neues Projekt" blieb für Mitglieder sichtbar, obwohl renderUserBar()
es per hidden ausblendet. */
[hidden] { display: none !important; }
/* === App-Shell: globales Menüband oben + linke Navigations-Sidebar +
Hauptbereich (OpenProject-Anordnung: ein durchgehendes globales Menüband
trägt Marke, aktuellen Kontext [Sitzung] und alle Aktionen/Konto rechts;
die Sidebar darunter ist reine Modul-Navigation, sonst nichts) === */
.app-shell { display: flex; flex-direction: column; min-height: 100vh; }
.app-body { display: flex; flex: 1; min-height: 0; }
.app-main { flex: 1; min-width: 0; }
/* Generisches Icon-System (js/icons.js): Icons skalieren wie ein Zeichen mit
der umgebenden Schriftgröße (1.1em), erben die Textfarbe über currentColor. */
.icon { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.icon svg { width: 1.1em; height: 1.1em; display: block; }
/* === Globales Menüband (immer sichtbar, alle Breiten) === */
.app-topbar {
position: sticky;
top: 0;
z-index: 140;
display: flex;
align-items: center;
flex-wrap: wrap;
row-gap: 8px;
gap: 12px;
padding: 8px 20px;
background: rgba(255,255,255,.86);
-webkit-backdrop-filter: blur(16px);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--line-soft);
}
.mobile-nav-toggle { display: inline-flex; }
@media (min-width: 769px) { .app-topbar .mobile-nav-toggle { display: none; } }
.topbar-brand { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.topbar-logo { height: 28px; width: auto; object-fit: contain; }
.topbar-title {
font-size: 15px;
font-weight: 500;
margin: 0;
letter-spacing: 0;
white-space: nowrap;
}
.app-topbar-actions {
display: flex;
align-items: center;
gap: 6px;
margin-left: auto;
flex-wrap: wrap;
}
.toolbar-group { display: flex; align-items: center; gap: 4px; }
.toolbar-divider {
width: 1px;
height: 22px;
background: var(--line-soft);
flex-shrink: 0;
}
@media (max-width: 768px) {
.app-topbar-actions {
order: 4;
flex: 1 1 100%;
margin-left: 0;
overflow-x: auto;
padding-bottom: 2px;
}
}
.header-toggle {
position: relative;
z-index: 10;
padding: 6px 12px;
min-width: 36px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.theme-toggle-circle {
width: 34px;
height: 34px;
border-radius: 50%;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--surface-sunk);
border: 1px solid var(--line-soft);
color: var(--text);
cursor: pointer;
flex-shrink: 0;
transition: background .2s var(--ease), color .2s var(--ease), transform .1s var(--ease);
}
.theme-toggle-circle:hover { background: var(--line-soft); }
.theme-toggle-circle:active { transform: scale(.95); }
.icon-btn-danger:hover { background: var(--red); color: #fff; }
.icon-btn-danger:active { background: var(--red-active); }
/* Konto-Menü (Avatar-Knopf rechts im Menüband + Dropdown) */
.topbar-user { position: relative; }
.user-menu-dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
min-width: 220px;
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-lg);
box-shadow: var(--sh-3);
padding: 8px;
display: flex;
flex-direction: column;
gap: 2px;
z-index: 150;
opacity: 0;
visibility: hidden;
transform: translateY(-4px);
transition: opacity .15s var(--ease), transform .15s var(--ease), visibility .15s var(--ease);
}
.user-menu-dropdown.open {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.user-menu-info {
padding: 8px 12px 12px;
margin-bottom: 2px;
border-bottom: 1px solid var(--line-soft);
font-size: 12px;
color: var(--text-muted);
}
/* .menu-item: generischer Zeilen-Knopf/-Link (Text + optionales Icon) —
genutzt im Konto-Dropdown (dort über .user-menu-dropdown volle Breite) UND
als Basis für die Sidebar-Unterpunkte (siehe .sidebar-subitem). */
a.menu-item,
button.menu-item {
display: inline-flex;
align-items: center;
gap: 8px;
text-align: left;
background: transparent;
color: var(--text);
padding: 8px 12px;
border: none;
border-radius: var(--r-md);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: background .15s var(--ease);
}
a.menu-item:hover,
button.menu-item:hover { background: var(--surface-sunk); }
.user-menu-dropdown a.menu-item,
.user-menu-dropdown button.menu-item { display: flex; width: 100%; }
/* === Seiten-Navigation (links, reine Modul-Navigation) === */
.side-menu {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 260px;
max-width: 80vw;
background: var(--surface);
border-right: 1px solid var(--line-soft);
box-shadow: var(--sh-3);
z-index: 200;
transform: translateX(-100%);
transition: transform .3s var(--ease);
display: flex;
flex-direction: column;
}
.side-menu.open {
transform: translateX(0);
}
.side-menu-mobile-header {
display: flex;
justify-content: flex-end;
padding: 8px 8px 0;
}
@media (min-width: 769px) { .side-menu-mobile-header { display: none; } }
.sidebar-nav {
flex: 1;
min-height: 0;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 2px;
padding: 12px;
}
/* Kein Eintrag darf schrumpfen: Reicht die Höhe nicht (z. B. Admin
aufgeklappt), scrollt .sidebar-nav. Ohne das stauchte die Flex-Spalte das
zuletzt aufgeklappte Untermenü (overflow: hidden → Mindesthöhe 0) auf 0 px
— „Werkzeuge“ ließ sich bei offenem Admin scheinbar nicht aufklappen. */
.sidebar-nav > * { flex-shrink: 0; }
.sidebar-collapse-bar {
flex-shrink: 0;
display: none;
align-items: center;
justify-content: center;
width: 100%;
padding: 12px;
background: transparent;
border: none;
border-top: 1px solid var(--line-soft);
border-radius: 0;
color: var(--text-muted);
cursor: pointer;
transition: background .2s var(--ease), color .2s var(--ease);
}
.sidebar-collapse-bar:hover { background: var(--surface-sunk); color: var(--text); }
.sidebar-collapse-bar:active { transform: none; }
#sidebarCollapseIcon { transition: transform .2s var(--ease); }
/* === Wandtafel / Dashboard (js/wand/wand.js) ===
Touch-Dashboard für ein dauerhaft laufendes Display, gewerke- und
nutzerübergreifend — ersetzt den früheren Show Mode, der nur die gerade
angemeldete Sitzung großflächig klonte (keine Aggregation über Gewerke
oder Nutzer hinweg, siehe CLAUDE.md). Seitenleiste und Kopfzeile blendet
showTab() über body.wandtafel-aktiv aus: Das Kiosk-Konto ist ein
gewöhnliches Mitgliedskonto mit echten Schreibrechten — die Kachelfläche
soll die einzige Berührfläche sein, damit eine zufällige Berührung nicht
in einen bearbeitbaren Bereich wie "Projekte" führt.
Aufbau (Eriks Wahl aus dem Vergleich mehrerer Gesamtentwürfe, "Leitstand"-
Struktur, später in Eriks eigener Logo-Farbwelt umgesetzt): Kopf
(Marke "Dashboard" + Sitzung/Uhrzeit + Abwesend-Kürzel) → eine
Kennzahlen-Reihe aus vier Kacheln (Auslastung gesamt, Besprochen gesamt,
offene Aufgaben, überfällig) → eine Reihe aus drei Panels (Auslastung je
Gewerk, Besprochen je Gewerk, Aktuell). Verhält sich zusätzlich wie ein
fester Bildschirmausschnitt statt einer normal scrollenden Seite
(height:100vh + overflow:hidden auf dem Weg .app-main → .container →
#wand) — ein unbeaufsichtigtes Display soll von selbst nie scrollen
müssen; Kopf, Kennzahlen-Reihe und Panel-Reihe strecken sich über die
volle restliche Höhe.
AUSNAHME, auf Eriks Wunsch (echter Touch-Monitor, "ich kann mich vor die
Tafel stellen und scrollen"): unterhalb davon folgt die vollständige
Projektliste (wandProjektlisteHtml(), js/wand/wand.js), und NUR dieser
Bereich (#wandContent, Klasse .wand-scrollable, von renderWandboard()
gesetzt) scrollt — eine offene Übersicht bleibt weiter exakt einen
Bildschirm hoch, siehe .wand-uebersicht unten. Ohne Berührung kehrt die
Ansicht nach WAND_IDLE_MS von selbst an den Anfang zurück
(wandAktivitaetErkannt()/wandBeiUntaetigkeitZuruecksetzen()). */
body.wandtafel-aktiv .app-topbar,
body.wandtafel-aktiv .side-menu,
body.wandtafel-aktiv .side-menu-backdrop { display: none !important; }
body.wandtafel-aktiv .app-main { height: 100vh; overflow: hidden; }
body.wandtafel-aktiv .container {
height: 100%; overflow: hidden; display: flex; flex-direction: column; padding: 14px 20px;
background: var(--dash-bg);
}
body.wandtafel-aktiv #wand { display: flex !important; flex-direction: column; flex: 1; min-height: 0; }
/* Eigene Farbwelt der Wandtafel — bewusst GETRENNT von --accent/--surface der
übrigen App: Eriks Wahl ("Pastell Freundlich", aus dem Design-Vergleich der
Gesamtentwürfe) nutzt die zwei Farben des Firmenlogos (aus public/logo.ico
ausgelesen: #94b0cc helles, #116896 dunkles Blau) statt des generischen
App-Akzents. Jede Fläche (Kennzahlen, Panels) trägt eine eigene, davon
abgeleitete Tönung. Rot/Gelb bleiben als Warn-/Kritisch-Farben eigenständig
(müssen unabhängig von der Markenfarbe sofort erkennbar bleiben), nur
gedeckter gewählt, damit sie zum Blau passen statt dagegen zu stehen.
--dash-hover ist eine reine Hervorhebung beim Antippen (kein Ersatz einer
bestehenden Flächenfarbe), deshalb für beide Modi eigens abgestimmt statt
eines einzigen hartkodierten Werts. */
body.wandtafel-aktiv {
--dash-bg: #f7fafc;
--dash-surface: #ffffff;
--dash-ink: #116896;
--dash-ink-soft: #94b0cc;
--dash-text: #20282e;
--dash-text-muted: #5d7488;
--dash-text-subtle: #8a9aa6;
--dash-panel-1: #dde9f1;
--dash-panel-2: #cfe0ea;
--dash-panel-3: #e7eff5;
--dash-kpi-4: #f6e3e0;
--dash-warn: #9a5a1f;
--dash-crit: #b8463f;
--dash-line: #e7eef3;
--dash-hover: rgba(17, 104, 150, .07);
}
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) body.wandtafel-aktiv {
--dash-bg: #10181f; --dash-surface: #141c23;
--dash-ink: #7fc3dd; --dash-ink-soft: #4a7691;
--dash-text: #e7eef3; --dash-text-muted: #9fb3c0; --dash-text-subtle: #70838f;
--dash-panel-1: #16222c; --dash-panel-2: #141e27; --dash-panel-3: #131b22; --dash-kpi-4: #2a1f1c;
--dash-warn: #e8a15c; --dash-crit: #ef6f63;
--dash-line: #223039; --dash-hover: rgba(255, 255, 255, .06);
}
}
html[data-theme="dark"] body.wandtafel-aktiv {
--dash-bg: #10181f; --dash-surface: #141c23;
--dash-ink: #7fc3dd; --dash-ink-soft: #4a7691;
--dash-text: #e7eef3; --dash-text-muted: #9fb3c0; --dash-text-subtle: #70838f;
--dash-panel-1: #16222c; --dash-panel-2: #141e27; --dash-panel-3: #131b22; --dash-kpi-4: #2a1f1c;
--dash-warn: #e8a15c; --dash-crit: #ef6f63;
--dash-line: #223039; --dash-hover: rgba(255, 255, 255, .06);
}
.wand-kopfzeile {
flex-shrink: 0;
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
padding-bottom: 10px;
}
.wand-zurueck {
appearance: none;
border: none;
background: none;
color: var(--text-subtle);
font-size: 12px;
padding: 0;
cursor: pointer;
}
.wand-zurueck:hover { color: var(--text-muted); }
/* wandIdleCountdown (js/wand/wand.js): die verbleibende Zeit bis zum
automatischen Rücksprung in den Normalzustand (WAND_IDLE_MS) — Erik wollte
sehen können, wann das passiert, statt davon überrascht zu werden. Sitzt
bewusst AUSSERHALB von #wandContent, damit sie von dessen Neu-Rendern
(jeder Datenabgleich, jedes Auf-/Zuklappen) unberührt bleibt. */
.wand-idle-countdown { flex-shrink: 0; font-size: 11px; color: var(--text-subtle); font-variant-numeric: tabular-nums; }
/* Normalzustand: #wandContent selbst bleibt genau ein Bildschirm hoch, kein
Scrollen (das ist der Zustand, den eine offene Übersicht ebenfalls nutzt —
siehe .wand-uebersicht). Erst .wand-scrollable (von renderWandboard()
gesetzt, wenn KEINE Übersicht offen ist) schaltet auf einen normalen
Block-Scrollcontainer um, dessen erstes Kind (.wand-ambient) weiterhin
genau einen Bildschirm hoch ist — die Projektliste danach verlängert den
Inhalt darüber hinaus und erzeugt dadurch den Scrollbalken. */
.wand-grid-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--dash-bg); }
.wand-grid-wrap.wand-scrollable { display: block; overflow-y: auto; }
.wand-ambient { min-height: 100%; display: flex; flex-direction: column; }
/* ===== Kopf: Marke "Dashboard" + Sitzung/Uhrzeit + Abwesend-Mini ===== */
.dash-kopf { flex-shrink: 0; display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; }
.dash-marke { font-family: 'Outfit', -apple-system, system-ui, sans-serif; font-weight: 500; font-size: 19px; color: var(--dash-ink); }
.dash-kopf-rechts { display: flex; align-items: center; gap: 12px; }
.dash-datum { font-size: 13px; color: var(--dash-text-muted); background: var(--dash-panel-3); padding: 4px 12px; border-radius: 6px; }
.dash-abwesend-mini { display: flex; }
.dash-abwesend-mini.ist-klickbar { cursor: pointer; border-radius: var(--r-pill); }
.dash-abwesend-mini.ist-klickbar:hover { filter: brightness(.96); }
.dash-avatar-mini {
width: 25px; height: 25px; border-radius: 50%; background: var(--dash-panel-2); color: var(--dash-ink);
font-size: 11px; font-weight: 600; display: flex; align-items: center; justify-content: center;
margin-left: -7px; border: 2px solid var(--dash-bg);
}
.dash-avatar-mini:first-child { margin-left: 0; }
/* ===== Kennzahlen-Reihe (4 Kacheln) ===== */
.dash-kpi-reihe { flex-shrink: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 16px; }
.dash-kpi { border-radius: var(--r-xl); padding: 16px 20px; }
.dash-kpi:nth-child(1) { background: var(--dash-panel-1); }
.dash-kpi:nth-child(2) { background: var(--dash-panel-2); }
.dash-kpi:nth-child(3) { background: var(--dash-panel-3); }
.dash-kpi:nth-child(4) { background: var(--dash-kpi-4); }
.dash-kpi.ist-klickbar { cursor: pointer; }
.dash-kpi.ist-klickbar:hover { filter: brightness(.96); }
.dash-kpi-label { font-size: 11px; font-weight: 600; color: var(--dash-text-muted); margin-bottom: 8px; }
.dash-kpi-wert { font-family: 'Outfit', -apple-system, system-ui, sans-serif; font-weight: 500; font-size: 32px; color: var(--dash-ink); font-variant-numeric: tabular-nums; }
.dash-kpi-wert.ok { color: var(--dash-ink); }
.dash-kpi-wert.info { color: var(--dash-ink-soft); }
.dash-kpi-wert.warn { color: var(--dash-warn); }
.dash-kpi-wert.crit { color: var(--dash-crit); }
/* ===== Drei-Panel-Reihe: Auslastung / Besprochen / Aktuell =====
Dieselbe Kacheltechnik wie zuvor (space-evenly auf .dash-panel-body) —
der Inhalt verteilt sich gleichmäßig über die volle Höhe statt oben zu
kleben, damit ein dünn befülltes Panel neben einem vollen nicht leer
wirkt. */
.dash-grid { flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 1fr 1.3fr; gap: 12px; }
.dash-panel { border-radius: var(--r-xl); padding: 20px 20px; display: flex; flex-direction: column; min-height: 0; }
.dash-grid > .dash-panel:nth-child(1) { background: var(--dash-panel-1); }
.dash-grid > .dash-panel:nth-child(2) { background: var(--dash-panel-2); }
.dash-grid > .dash-panel:nth-child(3) { background: var(--dash-panel-3); }
.dash-panel-titel { flex-shrink: 0; font-size: 12px; font-weight: 600; color: var(--dash-ink); margin-bottom: 12px; }
.dash-panel-body { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: space-evenly; overflow: hidden; }
/* Meter-Zeilen: Auslastung je Gewerk, Wochenbericht-Fortschritt je Gewerk —
jede Zeile einzeln antippbar (wandZeigeUebersicht('auslastung'|'besprochen', g)). */
.wand-meter-zeile { display: flex; align-items: center; gap: 12px; }
.wand-meter-zeile.ist-klickbar { cursor: pointer; border-radius: var(--r-md); margin: 0 -6px; padding: 3px 6px; transition: background .15s var(--ease); }
.wand-meter-zeile.ist-klickbar:hover { background: var(--dash-hover); }
.wand-meter-label { flex: 0 0 90px; font-size: 13px; font-weight: 600; color: var(--dash-text); }
.wand-meter {
flex: 1; height: 14px; background: rgba(127, 127, 127, .18); border-radius: var(--r-pill);
overflow: hidden; display: flex; align-items: center;
}
.wand-meter-fill { height: 100%; border-radius: var(--r-pill); transition: width .3s var(--ease); }
.wand-meter-leer { font-size: 12px; color: var(--dash-text-subtle); padding-left: 4px; }
.wand-meter-wert {
flex: 0 0 76px; text-align: right; font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums;
font-family: 'Outfit', -apple-system, system-ui, sans-serif; color: var(--dash-ink);
}
.wand-einheit { font-size: 11px; font-weight: 400; color: var(--dash-text-subtle); margin-left: 2px; }
.wand-leer-klein { font-size: 11.5px; color: var(--dash-text-subtle); }
/* "Aktuell": flache, chronologisch sortierte Ereignisliste (wandAktuellInhalt(),
js/wand/wand.js) — ein farbiger Punkt statt einer Pille zeigt die Art
(Neu/Überfällig/Termin). Jeder Eintrag einzeln antippbar, springt direkt
ins betroffene Projekt. */
.dash-feed-eintrag {
display: flex; align-items: flex-start; gap: 8px; padding: 8px 2px; border-top: 1px solid var(--dash-line);
cursor: pointer; border-radius: var(--r-md); transition: background .15s var(--ease);
}
.dash-feed-eintrag:first-of-type { border-top: none; padding-top: 0; }
.dash-feed-eintrag:hover { background: var(--dash-hover); }
.dash-feed-punkt { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; margin-top: 6px; }
.dash-feed-punkt.neu { background: var(--dash-ink); }
.dash-feed-punkt.ueberfaellig { background: var(--dash-crit); }
.dash-feed-punkt.termin { background: var(--dash-warn); }
.dash-feed-text { flex: 1; font-size: 13px; line-height: 1.5; color: var(--dash-text); }
.dash-feed-zeit { display: block; font-size: 11px; color: var(--dash-text-subtle); margin-top: 2px; }
/* Vollbild-Übersichten (überfällige/offene Aufgaben, Auslastung/Besprochen
je Gewerk, Abwesenheiten) — ersetzen die Normalansicht vorübergehend
vollständig (wandUebersicht). Die Liste ist die EINE bewusste Ausnahme vom
"kein Scrollen"-Grundsatz der Wandtafel: ein gezielter Einstieg mit
potenziell vielen Zeilen, kein passiver Dauerzustand. In derselben
Logo-Farbwelt wie die Normalansicht, damit der Wechsel nicht wie ein
Bruch in eine andere App wirkt. */
.wand-uebersicht {
flex: 1; min-height: 0; display: flex; flex-direction: column;
background: var(--dash-surface); border: 1px solid var(--dash-line); border-radius: var(--r-xl);
box-shadow: var(--sh-2); padding: 20px 24px; color: var(--dash-text);
}
.wand-uebersicht-kopf { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; flex-shrink: 0; }
.wand-uebersicht-kopf h2 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -.01em; font-family: 'Outfit', -apple-system, system-ui, sans-serif; }
.wand-uebersicht-liste { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }
.wand-uebersicht-zeile {
display: flex; align-items: center; gap: 18px; padding: 13px 6px; border-top: 1px solid var(--dash-line);
transition: background .15s var(--ease);
}
.wand-uebersicht-zeile:first-child { border-top: none; }
.wand-uebersicht-zeile.ist-klickbar { cursor: pointer; }
.wand-uebersicht-zeile.ist-klickbar:hover { background: var(--dash-hover); }
.wand-uebersicht-projekt { flex: 0 0 280px; font-weight: 600; font-size: 14.5px; color: var(--dash-text); }
.wand-uebersicht-titel { flex: 1; font-size: 14.5px; color: var(--dash-text); }
/* Neutral, nicht fest rot: eine Übersichts-Zeile kann auch eine unauffällige
Angabe zeigen (Stunden, "noch offen", Abwesenheitsende) — .ist-rot
markiert gezielt die wirklich überfälligen Zeilen. */
.wand-uebersicht-seit { flex-shrink: 0; font-size: 13px; font-weight: 600; color: var(--dash-text-muted); }
.wand-uebersicht-seit.ist-rot { color: var(--dash-crit); }
.wand-leer { font-size: 13px; color: var(--dash-text-subtle); }
/* Vollständige Projektliste unterhalb der Kennzahlen-/Panel-Reihe
(wandProjektlisteHtml(), js/wand/wand.js) — nur sichtbar/erreichbar durch
Herunterscrollen auf
einem echten Touch-Monitor. Auf Eriks Wunsch optisch identisch mit der
echten Projektliste: Gruppenüberschrift und Zeilen sind exakt
.section-title/.board-list/.board-row aus der Projektliste selbst (keine
eigene Zeilenform hier) — nur der äußere Abstand zu den Kacheln darüber
ist eine Wandtafel-eigene Ergänzung. */
.wand-projektliste { margin-top: 22px; padding-bottom: 8px; }
.wand-projektliste .section-title:first-child { margin-top: 0; }
.side-menu-backdrop {
position: fixed;
inset: 0;
background: rgba(0,0,0,.35);
-webkit-backdrop-filter: blur(2px);
backdrop-filter: blur(2px);
z-index: 150;
opacity: 0;
visibility: hidden;
transition: opacity .3s var(--ease), visibility .3s var(--ease);
}
.side-menu.open ~ .side-menu-backdrop {
opacity: 1;
visibility: visible;
}
/* Ab 769px: Sidebar dauerhaft sichtbar (kein Off-Canvas mehr nötig) */
@media (min-width: 769px) {
#headerMenu.side-menu {
position: sticky;
/* top muss der tatsächlichen Höhe von .app-topbar entsprechen (beide
sticky, gleicher Scroll-Container) — sonst zieht die Sidebar bei
Seiten, die länger als ein Bildschirm sind, beim Herunterscrollen mit
top:0 unter das Menüband statt sich direkt darunter einzureihen, und
am unteren Rand bleibt eine sidebarlose Lücke in Höhe des Menübands
übrig (height: calc(100vh - 52px) reichte dann nicht bis nach unten). */
top: 52px;
height: calc(100vh - 52px);
transform: none;
box-shadow: none;
width: 260px;
flex: 0 0 260px;
border-right: 1px solid var(--line-soft);
}
#headerMenu.side-menu .header-toggle { display: none; }
.side-menu-backdrop { display: none; }
/* Umschalt-Leiste am unteren Sidebar-Rand nur auf Desktop-Breite sichtbar
(auf Mobile bleibt es beim reinen Off-Canvas-Menü ohne Zwischenzustand) */
.sidebar-collapse-bar { display: flex; }
/* Eingeklappte Sidebar: nur noch eine schmale Icon-Rail mit den 5
Navigationspunkten, alle Beschriftungen verschwinden */
#headerMenu.side-menu.collapsed {
width: 64px;
flex: 0 0 64px;
}
#headerMenu.side-menu.collapsed .sidebar-nav {
padding-left: 8px;
padding-right: 8px;
}
#headerMenu.side-menu.collapsed .nav-item .nav-label {
display: none;
}
#headerMenu.side-menu.collapsed .nav-item {
justify-content: center;
padding-left: 0;
padding-right: 0;
}
#headerMenu.side-menu.collapsed #sidebarCollapseIcon {
transform: rotate(180deg);
}
/* In der Icon-Rail ergeben aufgeklappte Unterlisten/Rubrik-Überschriften
ohne Textbeschriftung keinen Sinn — beides wird vollständig ausgeblendet. */
#headerMenu.side-menu.collapsed .nav-chevron,
#headerMenu.side-menu.collapsed .sidebar-section-label,
#headerMenu.side-menu.collapsed .sidebar-subnav {
display: none;
}
}
.toolbar {
display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
}
button.active {
background: var(--accent);
color: #fff;
border-color: var(--accent);
}
/* Hamburger-Knopf im globalen Menüband (nur < 769px sichtbar, öffnet die
Sidebar als Off-Canvas-Menü, siehe .mobile-nav-toggle weiter oben). */
.menu-corner-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background: var(--surface-sunk);
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
box-shadow: none;
color: var(--text);
cursor: pointer;
font-size: 19px;
transition: background .2s var(--ease);
}
.menu-corner-btn:hover { background: var(--line-soft); }
.menu-corner-btn:active { transform: none; }
/* Hauptnavigation (Sidebar) — ersetzt die alte horizontale .tabs-Leiste */
.sidebar-nav { gap: 2px; }
.nav-item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
text-align: left;
text-decoration: none;
background: transparent;
color: var(--text-muted);
padding: 8px 12px;
cursor: pointer;
border: none;
border-radius: var(--r-md);
font-size: 13px;
font-weight: 500;
transition: background .2s var(--ease), color .2s var(--ease);
}
.nav-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
flex-shrink: 0;
font-size: 15px;
}
.nav-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* .nav-item kann ein echter <a href>-Link sein statt eines Buttons — ohne
diese Regel würde ein bereits besuchtes Ziel (echte Browser-Historie) mit
der UA-Standardfarbe für besuchte Links abweichen. */
.nav-item:visited { color: var(--text-muted); }
.nav-item:hover,
.nav-item:visited:hover { background: var(--surface-sunk); color: var(--text); }
.nav-item.active {
background: var(--accent-soft);
color: var(--accent);
font-weight: 500;
}
/* .sidebar-section-label: kleine Rubrik-Überschrift oberhalb einer Gruppe von
Sidebar-Einträgen (z. B. "Dashboard", "Navigation"). */
.sidebar-section-label {
padding: 12px 12px 2px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0;
color: var(--text-muted);
}
/* .nav-group-toggle: Sidebar-Eintrag, der statt eines Tabs einen
auf-/zuklappbaren Unterbereich steuert (Einstellungen/Admin, siehe
toggleSidebarAccordion() in js/ui.js). */
.nav-chevron {
display: inline-flex;
align-items: center;
justify-content: center;
width: 14px;
flex-shrink: 0;
margin-left: auto;
transition: transform .2s var(--ease);
}
.nav-group-toggle.expanded .nav-chevron { transform: rotate(180deg); }
/* .sidebar-subnav: Liste der Aktionen unter einem aufgeklappten
.nav-group-toggle, per max-height animiert ein-/ausgeblendet. */
.sidebar-subnav {
display: flex;
flex-direction: column;
gap: 1px;
padding-left: 8px;
max-height: 0;
opacity: 0;
overflow: hidden;
transition: max-height .25s var(--ease), opacity .2s var(--ease);
}
.sidebar-subnav.open {
/* Reicht für die längste Gruppe (Admin, zwölf Punkte); die Animation
läuft über max-height, deshalb steht hier eine feste Zahl. 360px
schnitt seit „Rollen & Rechte“ die letzten Admin-Punkte ab. */
max-height: 640px;
opacity: 1;
}
.sidebar-subitem {
width: 100%;
padding-left: 36px;
font-size: 13px;
color: var(--text-muted);
}
.sidebar-subitem:hover { color: var(--text); }
/* .is-active: der gerade gezeigte Abschnitt der Gruppe „Einstellungen"
(showSettingsSection() in js/settings/settings.js). Die übrigen Gruppen
lösen Aktionen aus und kennen deshalb keinen dauerhaft aktiven Punkt. */
.sidebar-subitem.is-active {
color: var(--accent);
font-weight: 500;
background: var(--accent-soft);
}
.sidebar-subitem.danger { color: var(--red); }
.sidebar-subitem.danger:hover { background: var(--red); color: #fff; }
/* === Buttons & Formulare === */
button {
cursor: pointer;
border: none;
border-radius: 8px;
padding: 8px 16px;
font-size: 13px;
font-weight: 500;
font-family: inherit;
background: var(--accent);
color: #fff;
letter-spacing: 0;
transition: background .2s var(--ease), transform .1s var(--ease),
box-shadow .2s var(--ease);
}
button:hover { background: var(--accent-hover); }
button:active { background: var(--accent-active); transform: scale(.97); }
button:focus-visible {
outline: none;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
button.secondary {
background: rgba(0,0,0,.06);
color: var(--text);
}
button.secondary:hover { background: rgba(0,0,0,.10); }
button.secondary:active { background: rgba(0,0,0,.14); }
button.danger { background: var(--red); }
button.danger:hover { background: var(--red-hover); }
button.danger:active { background: var(--red-active); }
button.small {
padding: 4px 12px;
font-size: 11px;
}
select,
input[type=text],
input[type=email],
input[type=date] {
padding: 8px 12px;
border: 1px solid var(--line);
border-radius: var(--r-md);
font-size: 13px;
font-family: inherit;
color: var(--text);
background: var(--surface);
transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
select:focus,
input[type=text]:focus,
input[type=email]:focus,
input[type=date]:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding-right: 28px;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath fill='%236e6e73' d='M0 0h10L5 6z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
background-color: var(--surface);
}
.role-checkboxes {
display: flex;
gap: 12px;
flex-wrap: wrap;
align-items: center;
}
.role-checkboxes label {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: var(--text-muted);
}
input[type=checkbox] { accent-color: var(--accent); }
/* === Tabellen & Matrix === */
table {
border-collapse: separate;
border-spacing: 0;
width: 100%;
background: var(--surface);
font-size: 12px;
}
th,
td {
border: 1px solid var(--line-soft);
padding: 4px 8px;
text-align: center;
vertical-align: middle;
}
thead tr:nth-child(1) th {
background: var(--surface-sunk);
color: var(--text);
font-weight: 500;
border: 1px solid var(--line);
position: sticky;
top: 0;
z-index: 10;
}
tr:nth-child(even) td { background: var(--surface-alt); }
/* === Status-Felder in der Projektzeile === */
.status {
min-width: 240px;
position: relative;
vertical-align: top;
}
.status-edit {
width: 100%;
min-height: 34px;
border: 1px solid var(--line-soft);
border-radius: var(--r-sm);
padding: 4px 8px;
font-size: 11px;
font-family: inherit;
line-height: 1.4;
color: var(--text);
background: var(--surface);
text-align: left;
white-space: pre-wrap;
word-break: break-word;
transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.status-edit:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.status-edit.status-new { color: var(--green); }
.status-edit.status-duplicated { color: var(--text); }
.status-edit p { margin: 0 0 4px; }
.status-edit p:last-child { margin-bottom: 0; }
.status-edit b,
.status-edit strong { font-weight: 500; }
/* === Kommentar-Editor im Board und im Wochenbericht ======================
Ersetzt das frühere contenteditable-Feld samt execCommand-Leiste: Textfeld
mit Verfasser-Kopfzeile, ausdrücklichem Speichern und dem eingeklappten,
scrollbaren Verlauf der früheren Sitzungen. Aufgebaut von
renderCommentEditor() in js/board/board.js. */
.comment-block { display: flex; flex-direction: column; gap: 8px; }
.comment-editor {
border: 1px solid var(--line);
border-radius: var(--r-md);
background: var(--surface);
overflow: hidden;
}
.comment-editor-head,
.comment-head-line {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
padding: 6px 12px;
font-size: 11px;
color: var(--text-muted);
background: var(--surface-alt);
border-bottom: 1px solid var(--line-soft);
}
.comment-head-line { border: 1px solid var(--line-soft); border-radius: var(--r-md); }
.comment-who { display: flex; align-items: center; gap: 6px; color: var(--text); font-weight: 500; }
.comment-unknown { color: var(--text-subtle); font-weight: 400; font-style: normal; }
.comment-sep { color: var(--text-subtle); }
.comment-av {
width: 19px; height: 19px; border-radius: 50%;
background: var(--accent); color: #fff;
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 500; flex-shrink: 0;
}
.comment-kbd { margin-left: auto; font-size: 11px; color: var(--text-subtle); white-space: nowrap; }
/* .comment-field ist seit der Umstellung auf contenteditable (Eriks Wunsch
nach echter statt mit Sternchen markierter Fett-/Kursiv-/Farbschrift,
public/js/utils.js) ein <div>, kein <textarea> mehr — Selektor deshalb
klassenbasiert statt elementbasiert, resize entfällt (kein Äquivalent für
contenteditable), Platzhalter über :empty:before + data-placeholder statt
::placeholder (das Pseudo-Element gibt es nur für echte Formularfelder). */
.comment-editor .comment-field {
width: 100%;
display: block;
padding: 8px 12px;
font-family: inherit;
font-size: 12px;
line-height: 1.5;
color: var(--text);
background: var(--surface);
min-height: 62px;
}
.comment-editor .comment-field:focus { outline: none; box-shadow: none; }
.comment-editor .comment-field:empty::before { content: attr(data-placeholder); color: var(--text-subtle); }
/* .richtext-toolbar (js/utils.js, richtextToolbarHtml()): gemeinsame
Fett/Kursiv/Farbe-Werkzeugleiste für jedes Textfeld, das Eriks Wunsch nach
formatierbarem Text bedient (Board-Kommentar, Protokoll-Punkt-Text — siehe
.richtext-wrap dort). "Angedockte" Darstellung (Eriks Auswahl C aus dem
Formatierungsleiste-Mockup): eine schattierte Leiste, die direkt auf dem
Textfeld aufsitzt — eigene Fläche + untere Trennkante statt einzeln
umrandeter Knöpfe, damit Leiste und Feld wie EIN Kasten statt zwei lose
übereinanderstehende Elemente wirken. Bewusst KEIN zusätzlicher Rahmen um
den ganzen .richtext-wrap: das Protokoll-Textfeld soll weiterhin randlos in
seiner Tabellenzelle sitzen (siehe Kommentar bei .protokoll-thema-cell
weiter unten), der Board-Kommentar steckt ohnehin schon in der
umrandeten .comment-editor-Karte — die Leiste muss daher in beiden
Zusammenhängen funktionieren, ohne selbst Ecken abzurunden (mal steht sie
ganz oben in ihrer Karte, mal nicht). */
.richtext-toolbar {
display: flex; align-items: center; gap: 8px; padding: 4px 8px;
background: var(--surface-sunk); border-bottom: 1px solid var(--line-soft);
}
.richtext-btn-group { display: inline-flex; gap: 2px; }
.richtext-btn {
appearance: none; border: none; background: none; color: var(--text-muted);
width: 22px; height: 20px; border-radius: 4px; font-size: 11px; font-weight: 600; cursor: pointer; line-height: 1;
}
.richtext-btn:hover { background: var(--surface-alt); color: var(--text); }
/* "Standard": setzt die Formatierung zurück — Text statt Symbol, ein
Radiergummi-Zeichen wäre missverständlich. Bleibt auch ohne Markierung
voll sichtbar, weil er dann den ganzen Text zurücksetzt. */
.richtext-btn.richtext-btn-loeschen { width: auto; padding: 0 6px; font-weight: 500; }
.richtext-toolbar:not(.hat-auswahl) .richtext-btn.richtext-btn-loeschen { opacity: 1; }
.richtext-divider { width: 1px; height: 14px; background: var(--line); flex-shrink: 0; }
.richtext-farben { display: inline-flex; gap: 6px; }
.richtext-farbe-btn {
appearance: none; border: none; width: 14px; height: 14px; border-radius: 50%; cursor: pointer; padding: 0;
box-shadow: 0 0 0 1px rgba(0,0,0,.08) inset;
}
.richtext-farbe-btn:hover { outline: 2px solid var(--line); outline-offset: 1px; }
/* .richtext-hinweis: Fett/Kursiv/Farbe wirken nur auf MARKIERTEN Text — ohne
Markierung tut ein Klick nichts, und das war bisher nirgends ablesbar.
Solange nichts markiert ist, bleiben die Knöpfe blass und der Hinweis
fordert zum Markieren auf; .hat-auswahl (von einem selectionchange-
Lauscher gesetzt, siehe js/plan/plan-protokoll.js) dreht beides um. */
.richtext-hinweis { margin-left: auto; font-size: 11px; color: var(--text-subtle); white-space: nowrap; }
.richtext-toolbar:not(.hat-auswahl) .richtext-btn,
.richtext-toolbar:not(.hat-auswahl) .richtext-farbe-btn { opacity: .45; }
.richtext-toolbar.hat-auswahl { background: var(--accent-soft); }
.richtext-toolbar.hat-auswahl .richtext-hinweis { color: var(--accent); font-weight: 500; }
.comment-editor-foot {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-top: 1px solid var(--line-soft);
background: var(--surface-alt);
}
.comment-foot-spacer { margin-left: auto; }
.comment-dirty { font-size: 11px; color: var(--amber); white-space: nowrap; }
.comment-readonly {
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
padding: 8px 8px;
font-size: 12px;
line-height: 1.45;
background: var(--surface);
}
/* Verlauf: eingeklappt, mit Anzahl im Kopf; die Liste selbst scrollt */
.comment-history {
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
background: var(--surface);
overflow: hidden;
}
.comment-history > summary {
cursor: pointer;
list-style: none;
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
font-size: 12px;
color: var(--text);
user-select: none;
}
.comment-history > summary::-webkit-details-marker { display: none; }
.comment-history > summary:hover { background: var(--surface-alt); }
.comment-history-caret {
color: var(--text-subtle);
font-size: 11px;
transition: transform .18s var(--ease);
}
.comment-history[open] > summary .comment-history-caret { transform: rotate(90deg); }
.comment-history-count {
margin-left: auto;
font-size: 11px;
font-weight: 500;
background: var(--accent-soft);
color: var(--accent);
border-radius: 6px;
padding: 1px 8px;
font-variant-numeric: tabular-nums;
}
.comment-history-count.zero { background: var(--surface-sunk); color: var(--text-subtle); }
.comment-history-body { border-top: 1px solid var(--line-soft); padding: 4px 12px 8px; }
.comment-history-list {
max-height: 220px;
overflow-y: auto;
overscroll-behavior: contain;
}
.comment-history-entry { padding: 8px 0; }
.comment-history-entry + .comment-history-entry { border-top: 1px solid var(--line-soft); }
.comment-history-head {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
font-size: 11px;
}
.comment-history-week {
font-weight: 500;
color: var(--text);
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.comment-history-meta { color: var(--text-subtle); }
.comment-history-text { font-size: 12px; line-height: 1.45; color: var(--text-muted); }
.comment-history-text b,
.comment-history-text strong { font-weight: 500; color: var(--text); }
.comment-take {
margin-left: auto;
padding: 2px 8px;
font-size: 11px;
border-radius: 8px;
background: transparent;
color: var(--text-muted);
border: 1px solid var(--line);
flex-shrink: 0;
}
.comment-take:hover { background: var(--surface-sunk); color: var(--text); }
.status-toolbar {
display: none;
position: absolute;
bottom: 100%;
left: 0;
margin-bottom: 4px;
background: rgba(255,255,255,.9);
-webkit-backdrop-filter: saturate(180%) blur(16px);
backdrop-filter: saturate(180%) blur(16px);
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
padding: 4px;
gap: 2px;
box-shadow: var(--sh-2);
z-index: 10;
}
.status:focus-within .status-toolbar { display: flex; }
.status-toolbar button {
padding: 4px 8px;
font-size: 11px;
line-height: 1.2;
min-width: 24px;
border-radius: var(--r-sm);
background: transparent;
color: var(--text);
border: none;
}
.status-toolbar button:hover { background: rgba(0,0,0,.06); }
.status-toolbar button:active { background: rgba(0,0,0,.10); transform: none; }
.status-toolbar button.color-red { color: var(--red); }
.status-toolbar button.color-green { color: var(--green); }
.status-toolbar button.color-blue { color: var(--accent); }
.status-toolbar button.color-black {
color: var(--text);
font-weight: 500;
}
.locked-cell {
cursor: not-allowed !important;
background: var(--surface-sunk) !important;
opacity: .65;
}
.locked-cell input {
background: var(--surface-sunk) !important;
cursor: not-allowed !important;
border-color: var(--line-soft) !important;
}
.bar {
height: 5px;
border-radius: var(--r-pill);
background: var(--line-soft);
margin-top: 4px;
overflow: hidden;
}
.bar > div {
height: 100%;
background: var(--green);
border-radius: var(--r-pill);
transition: width .3s var(--ease);
}
/* === Projektboard (Listenansicht) ===
Eine Bubble-Card je Projektgruppe (.board-list) mit den Projekten als
Listenzeilen (.board-row) — ersetzt die frühere breite Matrix (eine Spalte
je Person/Leistungsphase). Jede Zeile zeigt nur das Nötigste auf einen
Blick (Kürzel-Avatare der Zugeordneten, aktive Phase, Status-Vorschau);
Klick auf die Zeile klappt darunter das Detailpanel (.board-drawer) mit
Status-Editor, vollständiger Rollen-Zuordnung und allen LPH-Balken auf. */
.board-list {
display: flex;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-xl);
box-shadow: var(--sh-2);
overflow: hidden;
}
.board-row { border-bottom: 1px solid var(--line-soft); }
.board-row:last-child { border-bottom: none; }
.board-row-main {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
cursor: pointer;
transition: background .15s var(--ease);
}
.board-row-main:hover { background: var(--surface-sunk); }
.board-chev {
flex-shrink: 0;
color: var(--text-subtle);
display: inline-flex;
transition: transform .2s var(--ease);
}
.board-row.open .board-chev { transform: rotate(90deg); }
.board-nr {
flex-shrink: 0;
width: 64px;
font-size: 12px;
color: var(--text-muted);
font-family: ui-monospace, 'SF Mono', monospace;
}
.board-name-wrap { flex: 1 1 260px; min-width: 0; }
.board-name {
font-weight: 500;
letter-spacing: 0;
cursor: pointer;
}
.board-name:hover { color: var(--accent); text-decoration: underline; }
.board-status-line {
margin-top: 4px;
font-size: 12px;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.board-newdot {
flex-shrink: 0;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--green);
}
.board-avatar-stack {
flex-shrink: 0;
display: flex;
flex-wrap: wrap;
gap: 4px;
max-width: 220px;
}
.board-avatar {
flex-shrink: 0;
width: 26px;
height: 26px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 600;
letter-spacing: 0;
color: var(--text-muted);
background: var(--surface-sunk);
border: 1px solid var(--line-soft);
cursor: pointer;
user-select: none;
transition: background .15s var(--ease), color .15s var(--ease), box-shadow .15s var(--ease);
}
.board-avatar.role-planung.state-1 { background: var(--green); border-color: var(--green); color: #fff; }
.board-avatar.role-projektleitung.state-1 { background: var(--red); border-color: var(--red); color: #fff; }
.board-avatar.role-bauüberwachung.state-1 { background: var(--accent); border-color: var(--accent); color: #fff; }
.board-avatar.role-planung.state-2 { background: transparent; color: var(--green); border-color: var(--green); }
.board-avatar.role-projektleitung.state-2 { background: transparent; color: var(--red); border-color: var(--red); }
.board-avatar.role-bauüberwachung.state-2 { background: transparent; color: var(--accent); border-color: var(--accent); }
.phase-pill {
flex-shrink: 0;
font-size: 11px;
font-weight: 500;
font-family: ui-monospace, 'SF Mono', monospace;
padding: 4px 12px;
border-radius: 6px;
background: var(--accent-soft);
color: var(--accent);
white-space: nowrap;
}
.phase-pill.muted { background: var(--surface-sunk); color: var(--text-subtle); font-family: inherit; font-weight: 500; }
/* --- Detailpanel (.board-drawer) --- */
.board-drawer {
padding: 20px 24px 24px;
background: var(--surface-alt);
border-top: 1px solid var(--line-soft);
}
.board-drawer h4 {
margin: 0 0 8px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0;
color: var(--text-muted);
}
/* Die Schublade liefert drei gleichrangige Blöcke (Kommentar, Leistungsphasen,
Beteiligte, siehe renderBoardDrawer() in js/board/board.js). Im Projektboard
stehen alle drei übereinander: Kommentar über die volle Breite (er teilte
sich zuvor eine schmale Spalte mit den gestapelten LPH-Zeilen, die ihn auf
wenig Höhe drückten — Eriks Rückmeldung "Darstellung der Kommentare ist zu
klein"), darunter die Leistungsphasen als EIN schmaler Streifen
(board-lph-leiste, dasselbe Muster wie in der dichten Wochenbericht-Karte)
statt als Liste, darunter die Beteiligten. */
.board-drawer-grid {
display: grid;
grid-template-columns: 1fr;
grid-template-areas: "kommentar" "lph" "beteiligte";
align-content: start;
gap: 16px;
}
.drawer-col-comment { grid-area: kommentar; }
.drawer-col-lph { grid-area: lph; }
.drawer-col-people { grid-area: beteiligte; }
/* Auswahl der beauftragten Leistungsphasen. Genutzt vom Honorar-Dialog der
Projektplan-Seite (renderHonorarEingaben(), js/plan/plan-honorar.js) und
vom Neues-Projekt-Modal (openNewProjModal(), js/load/load.js) — deshalb
hier im gemeinsamen Stylesheet statt inline in einer der beiden Seiten. */
.beauftragte-lp-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
gap: 4px 8px;
}
.beauftragte-lp-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--text);
white-space: nowrap;
}
/* --- Wochenbericht: immer aufgeklappte Projekt-Karten ("Neu kommentiert",
"Fortschritt aktualisiert") — im Unterschied zu .board-list/.board-row
(Akkordeon der "Unverändert"-Gruppe) ist hier jede Karte ein eigenständiger
Kasten, kein Container mit Trennlinien. */
.report-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.report-card {
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
box-shadow: var(--sh-1);
overflow: hidden;
}
/* Kopfband: durchgefärbt in der Farbe der Änderungsart, damit beim Scrollen
ohne Blick auf die Abschnitts-Überschrift erkennbar ist, worum es geht.
Die Farbe kommt aus --band-neu/--band-fortschritt und lässt sich dort in
einem Zug abschwächen, falls die Leiste zu kräftig wirkt. */
.report-card-head {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
padding: 8px 16px;
background: var(--surface-alt);
border-bottom: 1px solid var(--line-soft);
}
.report-card-head .board-name { cursor: pointer; }
.report-card-neu > .report-band { background: var(--band-neu); }
.report-card-fortschritt > .report-band { background: var(--band-fortschritt); }
.report-band {
border-bottom: none;
color: #fff;
}
.report-band .board-nr { color: rgba(255,255,255,.75); }
.report-band .board-name { color: #fff; font-weight: 500; }
.report-band .board-name:hover { text-decoration: underline; }
.report-band .tag-chip {
background: rgba(255,255,255,.22);
color: #fff;
border-color: transparent;
}
.report-band .report-reason-badge {
background: rgba(255,255,255,.22);
color: #fff;
}
/* Drei Spalten statt zwei: im Bericht stehen Leistungsphasen und Beteiligte
nebeneinander, wodurch die Karte flacher wird. Dasselbe Markup wie im
Projektboard, nur anders angeordnet. */
.report-card .board-drawer {
padding: 16px 16px 16px;
border-top: none;
background: var(--surface);
}
.report-card .board-drawer-grid {
grid-template-columns: 1.7fr 1fr 1fr;
grid-template-areas: "kommentar lph beteiligte";
gap: 20px;
}
.report-card .board-role-label { width: 58px; }
@media (max-width: 860px) {
.report-card .board-drawer-grid {
grid-template-columns: 1fr;
grid-template-areas: "kommentar" "lph" "beteiligte";
gap: 16px;
}
}
/* === Dichte Fassung der Karte (.board-drawer-dicht, Wochenbericht) ===
Fokus-Modus, zweite Fassung (Eriks Rückmeldung zum Entwurfsvergleich "A -
Aktenregal"): der Kommentar bleibt das größte, offene Element; Leistungs-
phasen und Beteiligte stehen darunter dauerhaft sichtbar nebeneinander
(.bericht-meta-grid, siehe renderBoardDrawer()s dichte Fassung in
js/board/board.js) statt in aufklappbaren Abschnitten versteckt zu sein -
"die Beteiligten sollen mit angezeigt werden". Die erste Fassung hatte
beide hinter Aufklappern verborgen (gegen die Rückmeldung "zu überladen"
bei allem gleichzeitig sichtbar); jetzt sind beide bewusst KOMPAKT
gehalten (Kästchen-Leiste bzw. schmale Rollenzeilen statt breiter
Balkenzeilen), damit sie nebeneinander Platz finden, ohne den Kommentar zu
überragen. Nur die Anwesenheit (renderAttendance(),
js/wochenbericht/wochenbericht.js) bleibt ein Aufklapper
(.bericht-disclosure, weiter unten) — sie ist eine Sitzungs-, keine
Projekt-Angabe und sitzt ohnehin außerhalb dieser Karte am Seitenfuß. */
.board-drawer-dicht { padding: 0; background: transparent; border-top: none; }
.bericht-meta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line-soft); }
@media (max-width: 860px) { .bericht-meta-grid { grid-template-columns: 1fr; gap: 16px; } }
.bericht-meta-label { font-size: 11px; font-weight: 600; letter-spacing: 0; color: var(--text-subtle); margin-bottom: 8px; }
.bericht-disclosure {
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
margin-top: 8px;
overflow: hidden;
background: var(--surface);
}
.bericht-disclosure summary {
list-style: none;
cursor: pointer;
padding: 12px 16px;
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
font-weight: 500;
color: var(--text-muted);
}
.bericht-disclosure summary::-webkit-details-marker { display: none; }
.bericht-disclosure summary:hover { background: var(--surface-alt); }
.bericht-disclosure summary .count {
font-size: 11px;
color: var(--text-subtle);
font-weight: 500;
margin-left: 2px;
}
.bericht-disclosure summary .chev {
margin-left: auto;
color: var(--text-subtle);
display: inline-flex;
transition: transform .15s var(--ease);
}
.bericht-disclosure[open] summary .chev { transform: rotate(90deg); }
.bericht-disclosure[open] summary { border-bottom: 1px solid var(--line-soft); }
.bericht-disclosure-body { padding: 12px 16px 12px; }
/* Der Kommentar bleibt das größte Element der dichten Fassung — etwas
größere Schrift als im Projektboard, wo er eine Spalte unter mehreren
teilt. Radius/Schatten wie eine freistehende Karte (Entwurf "Fokus-Modus"). */
.board-drawer-dicht .comment-editor .comment-field,
.board-drawer-dicht .comment-readonly { font-size: 13px; line-height: 1.6; min-height: 76px; }
/* .comment-editor hat bereits overflow:hidden (Basis-Regel) — ein größerer
Radius hier genügt, Kopf/Fuß werden automatisch mitgerundet. */
.board-drawer-dicht .comment-editor { border-radius: var(--r-lg); box-shadow: var(--sh-1); }
.board-lph-leiste {
display: flex;
gap: 4px;
align-items: stretch;
}
.board-lph-cell {
flex: 1 1 0;
min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 1px;
padding: 4px 2px 4px;
border: 1px solid var(--line-soft);
border-radius: var(--r-sm);
background: var(--surface-sunk);
/* Der Füllstand kommt als senkrechter Verlauf aus dem Inline-Stil
(renderBoardDrawer in js/board/board.js) — so bleibt der Balken erhalten,
den die Zeilenform als eigenes Element hatte. */
background-repeat: no-repeat;
}
.board-lph-cell-label {
font-size: 11px;
font-weight: 500;
color: var(--text-muted);
line-height: 1.2;
}
.board-lph-cell input {
width: 100%;
min-width: 0;
padding: 1px 2px;
border: none;
background: transparent;
text-align: center;
font-size: 12px;
font-variant-numeric: tabular-nums;
color: var(--text);
font-family: inherit;
}
.board-lph-cell input:focus {
outline: 1.5px solid var(--accent);
outline-offset: -1px;
border-radius: 4px;
}
.board-lph-cell.lp-aktiv {
border-color: var(--accent);
}
.board-lph-cell.lp-aktiv .board-lph-cell-label,
.board-lph-cell.lp-aktiv input { color: var(--accent); }
.board-lph-cell.locked-cell input { cursor: default; }
/* Das Rollen-Label steht NEBEN seinen Kürzeln. Über ihnen kostete es drei
zusätzliche Zeilen, und damit bestimmte die Beteiligten-Spalte die Höhe der
ganzen Karte (189 px gegenüber 130 für den Kommentar). Die feste Breite ist
so bemessen, dass "Bauüberwachung" hineinpasst — bei 52 px lief die
Beschriftung über die Kürzel.
Die Regeln hängen am Merkmal .board-drawer-dicht, nicht am Ort: Sie
standen zunächst unter .report-card, und mit dem Umbau des Wochenberichts
auf die Tagesordnung gab es diesen Container nicht mehr — die dichte
Karte fiel still auf die Board-Darstellung zurück. Das zusätzliche
.board-role-mini liefert die Spezifität, die nötig ist, um die dortige
Regel für .board-role-row zu schlagen. */
.board-drawer-dicht .board-role-mini .board-role-row {
display: flex;
align-items: flex-start;
gap: 8px;
margin-bottom: 4px;
}
.board-drawer-dicht .board-role-mini .board-role-label {
flex: 0 0 auto;
/* Bemessen am längsten Rollennamen: "BAUÜBERWACHUNG" braucht bei 10 px in
Versalien diese Breite — bei 88 px fehlte der letzte Buchstabe. */
width: 104px;
padding-top: 4px;
font-size: 11px;
line-height: 1.25;
letter-spacing: 0;
}
/* Schmale Fenster: Dort reicht die Breite für Label UND Kürzel nicht mehr,
also wieder übereinander — lieber drei Zeilen mehr als abgeschnittene
Beschriftungen. */
@media (max-width: 1100px) {
.board-drawer-dicht .board-role-mini .board-role-row { display: block; }
.board-drawer-dicht .board-role-mini .board-role-label {
width: auto;
padding-top: 0;
margin-bottom: 2px;
}
}
.report-reason-badge {
margin-left: auto;
font-size: 11px;
font-weight: 500;
padding: 4px 12px;
border-radius: 6px;
background: var(--accent-soft);
color: var(--accent);
white-space: nowrap;
}
/* Innerhalb einer Report-Karte braucht das Detailpanel nicht den
Zeilen-Einzug/die obere Trennlinie, die es im Board-Akkordeon ausrichtet. */
.report-card .board-drawer {
padding: 16px 20px 20px;
border-top: none;
}
/* .board-lph-row/-list/-label/-wert: reine Anzeige-Zeile für Leistungsphasen,
seit Variante A (Board-Drawer) nicht mehr für den Drawer selbst gebraucht
(siehe .board-lph-leiste/-cell dort), aber weiterhin für die
Fortschrittsliste im Projektbereich-Dashboard (#planLphList,
renderPlanInfo() in js/plan/plan-info.js) — eine großzügig bemessene,
eigene Fläche statt einer schmalen Drawer-Spalte, wo die gestapelte Form
kein Platzproblem hat. */
.board-lph-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 12px;
}
.board-lph-row {
display: grid;
grid-template-columns: 42px 1fr 50px;
align-items: center;
gap: 8px;
}
.board-lph-row.is-ansicht{ cursor: default; }
.board-lph-row .bar { flex: 1; margin-top: 0; }
.board-lph-label {
font-size: 11px;
font-family: ui-monospace, 'SF Mono', monospace;
color: var(--text-muted);
}
.board-lph-row.lp-aktiv .board-lph-label { color: var(--accent); font-weight: 500; }
/* Der Prozentwert einer Leistungsphase an der Stelle des Eingabefeldes. Die
Breite entspricht dem Feld, damit die Balken aller Zeilen gleich weit
reichen; tabular-nums hält die Ziffern in einer Spalte. */
.board-lph-wert{
min-width: 52px;
text-align: right;
font-size: 12px;
font-variant-numeric: tabular-nums;
color: var(--text-muted);
}
.board-role-mini { display: flex; flex-direction: column; gap: 8px; }
.board-role-mini .board-role-row { display: flex; align-items: center; gap: 8px; }
.board-role-mini .board-role-label {
width: 100px;
flex-shrink: 0;
font-size: 12px;
color: var(--text-muted);
}
.board-role-mini .board-role-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.section-title {
font-size: 19px;
font-weight: 500;
margin: 24px 0 8px;
color: var(--text);
letter-spacing: 0;
line-height: 1.2;
}
/* === Lade-/Auswertungsübersicht === */
.load-grid {
display: flex;
flex-direction: column;
gap: 24px;
width: 100%;
}
.load-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
align-items: start;
width: 100%;
}
/* === Auslastung: das Lastband ===
Eine Zeile je Person und Rolle; der Balken ist nach Projekten aufgeteilt.
Zuvor stand jede Person in einer eigenen .load-card mit ihrer Projektliste
klein darunter — man sah die Zahl, aber nicht, woraus sie besteht. Die
Frage nach einer Auslastung von 1,80 ist aber immer dieselbe: aus einem
großen Projekt oder aus fünf kleinen, und was davon lässt sich abgeben?
.load-card/.load-row/.load-num/.load-detail bleiben bestehen — das
Dashboard (js/dashboard/dashboard.js) und die Ressourcenansicht des
Projektbereichs (js/plan/plan-gantt.js) bauen darauf auf. */
.load-rolle { width: 100%; }
.load-rolle-titel {
font-size: 13px;
font-weight: 500;
margin: 0 0 6px;
color: var(--text-muted);
letter-spacing: 0;
}
.load-zeile {
display: flex;
align-items: center;
gap: 16px;
padding: 12px 4px;
border-bottom: 1px solid var(--line-soft);
}
.load-zeile:last-child { border-bottom: none; }
.load-zeile:hover { background: var(--surface); }
/* Wer — Name oben, Kürzel und Zusätze darunter. Feste Breite, damit die
Bänder aller Zeilen an derselben Stelle beginnen und vergleichbar sind. */
.load-wer {
flex: 0 0 200px;
min-width: 0;
}
.load-wer-name {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.load-wer-meta {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
font-size: 11px;
color: var(--text-subtle);
margin-top: 1px;
}
/* Das Band. Die Segmente füllen es von links; was frei bleibt, ist die
Restkapazität bis zum Skalenende (Vorgabe 8, firmenweit einstellbar). */
.load-band {
flex: 1;
display: flex;
align-items: center;
height: 22px;
min-width: 140px;
background: var(--surface-sunk);
border-radius: var(--r-sm);
overflow: hidden;
}
.load-seg {
display: flex;
align-items: center;
height: 100%;
padding: 0 8px;
min-width: 3px;
font-size: 11px;
font-weight: 500;
color: #fff;
white-space: nowrap;
overflow: hidden;
border-right: 1px solid rgba(255,255,255,.4);
transition: filter .15s var(--ease);
}
.load-seg:last-of-type { border-right: none; }
.load-seg:hover { filter: brightness(1.12); }
.load-seg span { overflow: hidden; }
.load-band-leer {
font-size: 11px;
color: var(--text-subtle);
padding-left: 8px;
}
.load-wert {
flex: 0 0 62px;
text-align: right;
font-size: 19px;
font-weight: 500;
letter-spacing: 0;
font-variant-numeric: tabular-nums;
}
.load-anbahnung {
font-size: 11px;
padding: 1px 8px;
border-radius: 6px;
background: var(--surface-sunk);
color: var(--text-muted);
white-space: nowrap;
}
/* Kopfzeile: was die Zahlen abdecken. Sie steht oben, weil die Aussagekraft
der ganzen Seite daran hängt — eine Auslastung aus drei von 29 Projekten
ist etwas anderes als eine aus 29. */
.load-kopf {
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
padding: 12px 16px;
background: var(--surface-sunk);
border-radius: var(--r-lg);
font-size: 13px;
}
.load-kopf-zahl strong {
font-size: 19px;
font-variant-numeric: tabular-nums;
}
.load-kopf-warnung {
color: var(--red);
font-weight: 500;
}
.load-kopf-warnung::before {
content: '';
display: inline-block;
width: 7px; height: 7px;
border-radius: 50%;
background: var(--red);
margin-right: 8px;
vertical-align: middle;
}
.load-luecken {
padding: 12px 16px;
border: 1px dashed var(--line);
border-radius: var(--r-lg);
font-size: 12px;
}
.load-luecke {
display: flex;
justify-content: space-between;
gap: 16px;
padding: 4px 0;
border-bottom: 1px solid var(--line-soft);
}
.load-luecke:last-child { border-bottom: none; }
.load-luecke b { color: var(--text-muted); font-weight: 500; white-space: nowrap; }
/* Summenzeile je Mensch: ein durchgehender Riegel statt der Projektsegmente.
Sie beantwortet die Frage, die die Rollenzeilen nicht beantworten können —
wer in drei Rollen arbeitet, hat trotzdem nur eine Arbeitswoche. */
.load-zeile-summe .load-wer-name { font-weight: 500; }
.load-seg-summe { border-right: none; }
/* Marke der vollen Kapazität im Band. Sie sitzt am rechten Rand, weil das
volle Band genau die Wochenkapazität ist; ein Riegel, der sie erreicht,
stößt sichtbar an. */
.load-kapazitaet {
position: absolute;
right: 0; top: 0; bottom: 0;
width: 2px;
background: var(--text-subtle);
opacity: .5;
}
.load-zeile-summe .load-band { position: relative; }
.load-einheit {
font-size: 11px;
font-weight: 500;
color: var(--text-subtle);
margin-left: 2px;
}
.load-legende-item em {
font-style: normal;
color: var(--text-subtle);
margin-left: 4px;
}
/* Legende der Projektfarben, nur für die tatsächlich vorkommenden Projekte.
Nötig, weil in schmalen Segmenten die Projektnummer keinen Platz findet. */
.load-legende {
display: flex;
flex-wrap: wrap;
gap: 6px 16px;
padding-top: 16px;
border-top: 1px solid var(--line-soft);
font-size: 11px;
color: var(--text-muted);
}
.load-legende-item {
display: inline-flex;
align-items: center;
gap: 6px;
}
.load-legende-item i {
width: 9px;
height: 9px;
border-radius: 4px;
flex: 0 0 auto;
}
@media (max-width: 720px) {
.load-wer { flex-basis: 128px; }
.load-zeile { gap: 8px; }
.load-wert { flex-basis: 52px; font-size: 15px; }
}
.load-row-title {
grid-column: 1 / -1;
width: 100%;
font-size: 13px;
font-weight: 500;
margin: 0 0 2px 0;
padding-top: 4px;
color: var(--text-muted);
letter-spacing: 0;
}
.load-card {
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-xl);
padding: 20px 24px;
min-width: 0;
width: 100%;
box-shadow: var(--sh-2);
transition: box-shadow .2s var(--ease), transform .2s var(--ease);
}
.load-card:hover {
box-shadow: var(--sh-3);
transform: translateY(-2px);
}
.load-card h3 {
margin: 0 0 4px;
font-size: 13px;
font-weight: 500;
letter-spacing: 0;
line-height: 1.25;
}
.load-num {
font-size: 30px;
font-weight: 500;
color: var(--text);
letter-spacing: 0;
line-height: 1.1;
}
.load-detail {
font-size: 11px;
color: var(--text-muted);
margin-top: 8px;
}
.load-detail div {
display: flex;
justify-content: space-between;
padding: 4px 0;
border-bottom: 1px solid var(--line-soft);
}
.load-detail div:last-child { border-bottom: none; }
/* === Modal, Form-Elemente & Listen === */
.modal-bg {
position: fixed;
inset: 0;
background: rgba(0,0,0,.35);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
display: none;
align-items: center;
justify-content: center;
/* Über Menüband (.app-topbar, 140) UND Seitenleiste (.side-menu, 200):
Mit dem früheren Wert 100 blieb die Navigation neben einem offenen Dialog
sichtbar und anklickbar — auch hinter dem "blockierenden" Login-Fenster —
und die Seitenleiste verdeckte bei mittleren Fensterbreiten die linke
Kante breiter Dialoge. */
z-index: 300;
}
.modal {
background: var(--surface);
padding: 28px;
border-radius: var(--r-xl);
max-width: 500px;
width: 90%;
max-height: 85vh;
overflow: auto;
box-shadow: var(--sh-3);
}
.modal h2 {
margin-top: 0;
font-size: 19px;
font-weight: 500;
letter-spacing: 0;
line-height: 1.15;
}
.form-row {
margin-bottom: 12px;
display: flex;
flex-direction: column;
gap: 4px;
}
.form-row label {
font-weight: 500;
font-size: 12px;
color: var(--text-muted);
}
/* Gesperrte Bedienelemente: Ohne eigene :disabled-Regeln schlagen die
Farbangaben oben die Browser-Voreinstellung, und ein deaktivierter Knopf
sieht exakt aus wie ein bedienbarer (z. B. in gesperrten Sitzungen oder
bei der eigenen Zeile in der Benutzerverwaltung). */
button:disabled,
input:disabled,
select:disabled,
textarea:disabled {
opacity: .45;
cursor: not-allowed;
box-shadow: none;
}
button:disabled:hover,
select:disabled:hover {
filter: none;
transform: none;
}
/* Warnband unten rechts, wenn das Speichern fehlschlägt (showSyncProblem() in
js/store.js). Liegt über allem, damit es auch neben einem offenen Dialog
sichtbar bleibt. */
.sync-problem-bar {
position: fixed;
right: 18px;
bottom: 18px;
z-index: 500;
max-width: 340px;
background: var(--red);
color: #fff;
font-size: 13px;
line-height: 1.45;
padding: 12px 16px;
border-radius: var(--r-md);
box-shadow: var(--sh-3);
}
/* Hinweisband gesperrter Sitzungen (renderBoard()/renderWochenbericht()).
Vorher standen die Farben als Inline-Angaben im Markup und waren damit im
Dunkelmodus grell weiß-rosa. */
.lock-notice {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
background: var(--surface-alt);
border: 1px solid var(--red);
color: var(--red);
border-radius: var(--r-md);
padding: 8px 12px;
margin-bottom: 12px;
font-size: 13px;
}
/* === LP-Aufwand-Maske als Matrix-Tabelle ===
Zeilen = Leistungsphasen, Spalten = Rollen + "amtierend" (Radio). */
.lp-matrix {
width: 100%;
border-collapse: collapse;
margin: 6px 0 12px;
}
.lp-matrix thead th {
font-size: 11px;
font-weight: 500;
color: var(--text-muted);
text-align: left;
padding: 4px 8px;
border-bottom: 1px solid var(--line-soft);
}
.lp-matrix th.lp-lph {
font-size: 12px;
font-weight: 500;
text-align: left;
padding: 6px 8px;
white-space: nowrap;
}
.lp-matrix td {
padding: 4px 8px;
}
.lp-matrix td input[type=text] {
width: 64px;
padding: 4px 8px;
font-size: 13px;
}
.lp-matrix tbody th.lp-lph,
.lp-matrix tbody td {
border-bottom: 1px solid var(--line-soft);
}
.lp-matrix tbody tr:last-child th.lp-lph,
.lp-matrix tbody tr:last-child td {
border-bottom: none;
}
.lp-matrix tbody tr:nth-child(even) th.lp-lph,
.lp-matrix tbody tr:nth-child(even) td {
background: var(--surface-alt);
}
/* LP-Aufwand- und Neues-Projekt-Modal etwas breiter für die Matrix-Tabelle */
#lpModal .modal,
#newProjModal .modal {
max-width: 640px;
}
/* .bubble-card: gemeinsame "Bubble-Card"-Optik für Projekt- und
Personen-Karten (Projekte-/Projektbeteiligte-Tab) — gleicher Radius/
Schatten wie .load-card (Dashboard/Auslastung), aber ohne Hover-Anhebung,
weil die Projekt-Karten Formularelemente enthalten (eine Anhebung beim
Überfahren wäre dort irritierend). */
.bubble-card {
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-xl);
padding: 20px 24px;
box-shadow: var(--sh-2);
transition: box-shadow .2s var(--ease);
}
.bubble-card:hover {
box-shadow: var(--sh-3);
}
.person-list-item,
.person-card {
display: flex;
flex-direction: column;
width: 100%;
}
.person-list-item {
align-items: center;
gap: 8px;
overflow-x: auto;
flex-direction: row;
}
.person-header {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 4px;
cursor: pointer;
overflow-x: auto;
transition: background .2s var(--ease);
}
.person-header:hover { background: var(--surface-sunk); }
/* Schaltflächen-Optik auch für Links: Ein <a class="secondary"> in einer
Knopfleiste sah sonst aus wie ein blauer Standardlink, weil .secondary
ausschließlich für <button> definiert war. */
a.secondary {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--surface-alt);
color: var(--text);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 6px 12px;
font-size: 13px;
font-weight: 500;
text-decoration: none;
transition: background .15s var(--ease), border-color .15s var(--ease);
}
a.secondary:hover { background: var(--surface-sunk); border-color: var(--text-subtle); }
/* Trennzeichen in der Board-Legende (Rollenfarben | Art der Zuordnung) */
.legend-sep { color: var(--text-subtle); }
/* Zugehörigkeit zur aktuellen Sitzung + Schaltfläche rechts in der Zeile
(renderPeople(), js/people/people.js). */
.person-week-state {
margin-left: auto;
font-size: 12px;
color: var(--green);
white-space: nowrap;
}
.person-header button { flex: 0 0 auto; }
.person-toggle {
flex: 0 0 auto;
font-size: 11px;
color: var(--text-subtle);
width: 14px;
text-align: center;
}
.person-toggle .icon {
transition: transform .2s var(--ease);
}
.person-card.open .person-toggle .icon {
transform: rotate(90deg);
}
.person-list-item span.drag-handle,
.person-header span.drag-handle {
flex: 0 0 auto;
color: var(--text-subtle);
cursor: grab;
}
.person-list-item input[type=text],
.person-header input[type=text] { flex: 0 0 auto; max-width: 25vw; }
.person-list-item input[type=text]:nth-of-type(1),
.person-header input[type=text]:nth-of-type(1) { max-width: 80px; }
.person-list-item input[type=text]:nth-of-type(2),
.person-header input[type=text]:nth-of-type(2) { max-width: 180px; }
.person-list-item select,
.person-header select,
.person-header .role-checkboxes {
flex: 0 0 auto;
min-width: 110px;
}
.person-list-item button,
.person-header button {
flex: 0 0 auto;
margin-left: auto;
white-space: nowrap;
}
/* .projects-sortable: Listen-Container je Projekt-Gruppe im Projekte-Tab
(projects.js) — eine Bubble-Card mit den Projekten als Listenzeilen
(kein Karten-Raster mehr, siehe .project-item). Der Container-Klassenname
bleibt unverändert, damit die Drag-and-Drop-Selektoren (handleDrop/
reorderProjects) weiter greifen. */
.projects-sortable {
display: flex;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-xl);
box-shadow: var(--sh-2);
overflow: hidden;
}
/* .people-readonly: Listen-Container im Projektbeteiligte-Tab (people.js) —
eine Bubble-Card mit den Personen als Listenzeilen (kein Karten-Raster
mehr, siehe .person-card). */
.people-readonly {
display: flex;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-xl);
box-shadow: var(--sh-2);
overflow: hidden;
}
/* .person-card: eine Listenzeile im Projektbeteiligte-Tab (ersetzt die
vormalige eigene Bubble-Card je Person) — der klickbare .person-header
bleibt die sichtbare Zeile, .person-absences klappt darunter auf. */
.person-card {
border-bottom: 1px solid var(--line-soft);
padding: 0 16px;
}
.person-card:last-child { border-bottom: none; }
/* .sidebar-nav-divider: trennt die Modul-Navigation von Einstellungen/Admin. */
.sidebar-nav-divider {
border: none;
border-top: 1px solid var(--line-soft);
margin: 8px 6px;
}
/* .project-item: eine Listenzeile im Projekte-Tab (ersetzt die vormalige
eigene Bubble-Card je Projekt) — überschreibt die Zeilen-Basis von
.person-list-item mit einer umbrechenden Zeile plus rechtsbündigen
Aktionen (.project-card-footer); das Karten-Attribut (data-project-id) und
die Drag-Handler bleiben unverändert. */
.project-item {
overflow-x: visible;
flex-wrap: wrap;
padding: 12px 16px;
border-bottom: 1px solid var(--line-soft);
border-radius: 0;
transition: background .15s var(--ease);
}
.project-item:last-child { border-bottom: none; }
.project-item:hover { background: var(--surface-sunk); }
.project-tags-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
flex: 1 1 200px;
min-width: 0;
}
.project-card-footer {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
}
/* Überschreibt die generische .person-list-item-button-Regel (margin-left:
auto auf jedem direkten Button), sonst würden sich die beiden Footer-
Buttons gegenseitig auseinanderschieben statt gruppiert zu bleiben — nur
der Footer als Ganzes soll rechtsbündig sein. */
.project-card-footer button { margin-left: 0; }
.tag-input { width: 110px; font-size: 12px; padding: 4px 8px; }
/* === Hinweise & Hilfsklassen === */
.legend {
font-size: 11px;
color: var(--text-muted);
margin: 8px 0;
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.legend span {
display: inline-flex;
align-items: center;
gap: 4px;
}
/* === Anwesenheit der Sitzung ============================================
Fokus-Modus: ein aufklappbarer Abschnitt (.bericht-disclosure, siehe
weiter oben) am Seitenfuß des Wochenberichts, UNABHÄNGIG vom gewählten
Projekt (eine Sitzungs-, keine Projekt-Angabe) - anders als Leistungs-
phasen/Beteiligte, die seit Eriks Rückmeldung zum Entwurfsvergleich wieder
dauerhaft sichtbar sind (.bericht-meta-grid, siehe weiter oben), bleibt
dies der einzige Aufklapper. Zustände bleiben binär (anwesend / nicht
anwesend, Tabelle attendance); der Zähler in der Kopfzeile macht sichtbar,
wenn noch niemand geklickt hat. */
#weekAttendance .bericht-disclosure { margin-top: 16px; }
.attendance-actions { display: flex; gap: 8px; margin-bottom: 8px; }
.attendance-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 12px 16px;
}
.attendance-tile {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
width: 84px;
padding: 8px 4px 8px;
border: 1px solid var(--line);
border-radius: var(--r-md);
background: var(--surface);
color: var(--text-muted);
font-family: inherit;
font-weight: 400;
cursor: pointer;
transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.attendance-tile:hover { border-color: var(--text-subtle); background: var(--surface-alt); }
.attendance-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.attendance-av {
width: 30px; height: 30px;
border-radius: 50%;
background: var(--surface-sunk);
color: var(--text-muted);
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 500;
transition: background .15s var(--ease), color .15s var(--ease);
}
.attendance-name {
font-size: 11px;
line-height: 1.25;
text-align: center;
word-break: break-word;
}
.attendance-note {
font-size: 11px;
color: var(--amber);
line-height: 1.2;
text-align: center;
}
/* Der Haken trägt den Zustand zusätzlich zur Farbe — nicht jede Person
unterscheidet Grün und Grau zuverlässig. */
.attendance-mark {
position: absolute;
top: 4px; right: 6px;
font-size: 11px;
font-weight: 600;
color: var(--green);
line-height: 1;
}
.attendance-tile.present {
border-color: var(--green);
background: var(--attend-bg);
color: var(--green);
font-weight: 500;
}
.attendance-tile.present:hover { background: var(--attend-bg-hover); border-color: var(--green); }
.attendance-tile.present .attendance-av { background: var(--green); color: #fff; }
.attendance-tile.locked-cell { cursor: not-allowed; opacity: .7; }
.attendance-tile.locked-cell:hover { border-color: var(--line); background: var(--surface); }
.attendance-tile.present.locked-cell:hover { background: var(--attend-bg); border-color: var(--green); }
/* Legende ist seit der Trennung reine Namenserklärung, nicht mehr klickbar */
.swatch {
width: 12px;
height: 12px;
border-radius: 4px;
display: inline-block;
box-shadow: inset 0 0 0 1px rgba(0,0,0,.06);
}
.table-wrap {
overflow: auto;
max-height: 75vh;
border-radius: var(--r-lg);
border: 1px solid var(--line-soft);
box-shadow: var(--sh-1);
background: var(--surface);
}
/* Fehlermeldung direkt am Formularfeld (Passwort-Dialog, Aufgaben-Dialog)
statt als alert() — der Fehler steht dort, wo er entstanden ist. */
.field-error { font-size: 12px; color: var(--red); }
.hint {
color: var(--text-subtle);
font-size: 12px;
margin: 8px 0;
}
.del-proj {
color: var(--red);
cursor: pointer;
font-weight: 500;
}
.lock-notice button { margin-left: 12px; }
input[type=text].edit-name {
border: 1px solid transparent;
background: transparent;
font-weight: 500;
width: 100%;
font-size: 12px;
padding: 4px 4px;
border-radius: var(--r-sm);
transition: background .15s var(--ease), border-color .15s var(--ease), box-shadow .15s var(--ease);
}
input[type=text].edit-name:hover { background: rgba(0,0,0,.03); }
input[type=text].edit-name:focus {
background: var(--surface);
border-color: var(--accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
outline: none;
}
/* === Scrollbars (WebKit) === */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
background: rgba(0,0,0,.2);
border-radius: var(--r-pill);
border: 2px solid transparent;
background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,.32); background-clip: content-box; }
/* ============================================================
=== DARK MODE ===
Automatisch nach Systemeinstellung ODER manuell per
<html data-theme="dark"> auslösbar.
============================================================ */
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) { color-scheme: dark; }
html:not([data-theme="light"]) {
/* „Bauplan“ (unslop-Überarbeitung, Eriks Wahl „volle Überarbeitung“):
Flächen fast neutral (Graphit, leicht kühl), die Farbe tragen der
Akzent (Logo-Blau) und die Inhalte — Terminplan, Auslastung, Status.
Ersetzt die frühere Logo-Blau-Fläche aus der Wandtafel; die Wandtafel
selbst behält ihre eigenen dash- und wt-Tokens. */
--accent: #86bfdf;
--accent-hover: #9dcbe5;
--accent-active: #6aa9cc;
--accent-soft: #1b2731;
--text: #eceef0;
--text-muted: #a5abb2;
--text-subtle: #878d95;
--bg: #121416;
--surface: #181a1d;
--surface-alt: #1c1f22;
--surface-sunk: #23262a;
--line: #2e3237;
--line-soft: #22262a;
--green: #4cc38a;
--amber: #e0a159;
--red: #e5484d;
--red-hover: #ee6a6e;
--red-active: #c23a3f;
--attend-bg: rgba(76,195,138,.14);
--attend-bg-hover: rgba(76,195,138,.22);
/* Auf Weiß gesetzt — deshalb dunkler als --band-neu/--green gebraucht
(Kontrastgrund wie im Hell-Modus): --band-neu ist das dunkle
Logo-Blau selbst (dasselbe #116896 wie am Dashboard). */
--band-neu: #0f5a85;
--band-fortschritt: #1f7a3a;
--meilenstein: #c19cf0;
--protokoll-neu-bg: rgba(255,214,10,.14);
--protokoll-neu-border: #ffd60a;
--sh-1: 0 1px 2px rgba(0,0,0,.4);
--sh-2: 0 2px 8px rgba(0,0,0,.45);
--sh-3: 0 12px 32px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.35);
}
}
html[data-theme="dark"] {
color-scheme: dark;
--accent: #86bfdf;
--accent-hover: #9dcbe5;
--accent-active: #6aa9cc;
--accent-soft: #1b2731;
--text: #eceef0;
--text-muted: #a5abb2;
--text-subtle: #878d95;
--bg: #121416;
--surface: #181a1d;
--surface-alt: #1c1f22;
--surface-sunk: #23262a;
--line: #2e3237;
--line-soft: #22262a;
--green: #4cc38a;
--amber: #e0a159;
--red: #e5484d;
--red-hover: #ee6a6e;
--red-active: #c23a3f;
--attend-bg: rgba(76,195,138,.14);
--attend-bg-hover: rgba(76,195,138,.22);
--band-neu: #0f5a85;
--band-fortschritt: #1f7a3a;
--protokoll-neu-bg: rgba(255,214,10,.14);
--protokoll-neu-border: #ffd60a;
--sh-1: 0 1px 2px rgba(0,0,0,.4);
--sh-2: 0 2px 8px rgba(0,0,0,.45);
--sh-3: 0 12px 32px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.35);
}
/* Gemeinsame Dark-Style-Regeln (System oder manuell) */
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) .app-topbar {
background: rgba(18,20,22,.84);
border-bottom: 1px solid var(--line);
}
html:not([data-theme="light"]) .toolbar-divider { background: var(--line); }
html:not([data-theme="light"]) .sidebar-nav-divider { border-top-color: var(--line); }
html:not([data-theme="light"]) .side-menu {
background: var(--surface);
border-right-color: var(--line);
}
html:not([data-theme="light"]) .side-menu-mobile-header,
html:not([data-theme="light"]) .sidebar-collapse-bar {
border-color: var(--line);
}
html:not([data-theme="light"]) .user-menu-dropdown {
background: var(--surface);
border-color: var(--line);
}
html:not([data-theme="light"]) .user-menu-info { border-bottom-color: var(--line); }
html:not([data-theme="light"]) a.menu-item:hover,
html:not([data-theme="light"]) button.menu-item:hover { background: rgba(255,255,255,.08); }
html:not([data-theme="light"]) .sidebar-collapse-bar:hover { background: rgba(255,255,255,.08); }
html:not([data-theme="light"]) .theme-toggle-circle {
background: var(--surface-sunk);
border-color: var(--line);
color: var(--text);
}
html:not([data-theme="light"]) .menu-corner-btn {
background: var(--surface-sunk);
border-color: var(--line);
color: var(--text);
}
html:not([data-theme="light"]) .menu-corner-btn:hover { background: var(--line); }
html:not([data-theme="light"]) .topbar-logo { filter: brightness(.92); }
html:not([data-theme="light"]) .nav-item:hover { background: rgba(255,255,255,.08); }
html:not([data-theme="light"]) .nav-item.active { background: var(--accent-soft); }
html:not([data-theme="light"]) button:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent); }
html:not([data-theme="light"]) button.secondary { background: rgba(255,255,255,.10); color: var(--text); }
html:not([data-theme="light"]) button.secondary:hover { background: rgba(255,255,255,.15); }
html:not([data-theme="light"]) button.secondary:active { background: rgba(255,255,255,.20); }
html:not([data-theme="light"]) button.danger { background: #d70015; }
html:not([data-theme="light"]) button.danger:hover { background: var(--red-hover); }
html:not([data-theme="light"]) button.danger:active { background: var(--red-active); }
html:not([data-theme="light"]) select,
html:not([data-theme="light"]) input[type=text],
html:not([data-theme="light"]) input[type=email],
html:not([data-theme="light"]) input[type=date] {
background-color: var(--surface-sunk);
border-color: var(--line);
color: var(--text);
}
html:not([data-theme="light"]) select:focus,
html:not([data-theme="light"]) input[type=text]:focus,
html:not([data-theme="light"]) input[type=email]:focus,
html:not([data-theme="light"]) input[type=date]:focus {
box-shadow: 0 0 0 3px rgba(10,132,255,.28);
}
html:not([data-theme="light"]) select {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath fill='%23a1a1a6' d='M0 0h10L5 6z'/%3E%3C/svg%3E");
}
html:not([data-theme="light"]) ::placeholder { color: var(--text-subtle); }
html:not([data-theme="light"]) .status-edit {
background: var(--surface-sunk);
border-color: var(--line);
}
html:not([data-theme="light"]) .status-edit:focus { box-shadow: 0 0 0 3px rgba(10,132,255,.28); }
html:not([data-theme="light"]) .status-edit.status-new { color: var(--green); }
html:not([data-theme="light"]) .status-toolbar {
background: rgba(44,44,46,.9);
border-color: var(--line);
}
html:not([data-theme="light"]) .status-toolbar button:hover { background: rgba(255,255,255,.10); }
html:not([data-theme="light"]) .status-toolbar button:active { background: rgba(255,255,255,.16); }
html:not([data-theme="light"]) .locked-cell { background: #38383a !important; opacity: .55; }
html:not([data-theme="light"]) .locked-cell input {
background: #38383a !important;
border-color: var(--line) !important;
color: var(--text-muted) !important;
}
html:not([data-theme="light"]) .bar { background: #38383a; }
html:not([data-theme="light"]) .board-avatar { border-color: var(--line); }
html:not([data-theme="light"]) .load-card { border-color: var(--line); }
html:not([data-theme="light"]) .bubble-card,
html:not([data-theme="light"]) .projects-sortable,
html:not([data-theme="light"]) .people-readonly,
html:not([data-theme="light"]) .board-list { border-color: var(--line); }
html:not([data-theme="light"]) .modal-bg { background: rgba(0,0,0,.6); }
html:not([data-theme="light"]) .modal { border: 1px solid var(--line); }
html:not([data-theme="light"]) .swatch { box-shadow: inset 0 0 0 1px rgba(255,255,255,.14); }
html:not([data-theme="light"]) .table-wrap { border-color: var(--line); }
html:not([data-theme="light"]) .del-proj { color: var(--red); }
html:not([data-theme="light"]) input[type=text].edit-name { border-color: transparent; background: transparent; }
html:not([data-theme="light"]) input[type=text].edit-name:hover { background: rgba(255,255,255,.06); }
html:not([data-theme="light"]) input[type=text].edit-name:focus {
background: var(--surface-sunk);
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(10,132,255,.28);
}
html:not([data-theme="light"]) ::-webkit-scrollbar-thumb { background: rgba(255,255,255,.22); background-clip: content-box; }
html:not([data-theme="light"]) ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.34); background-clip: content-box; }
}
html[data-theme="dark"] .app-topbar {
background: rgba(18,20,22,.84);
border-bottom: 1px solid var(--line);
}
html[data-theme="dark"] .toolbar-divider { background: var(--line); }
html[data-theme="dark"] .sidebar-nav-divider { border-top-color: var(--line); }
html[data-theme="dark"] .side-menu {
background: var(--surface);
border-right-color: var(--line);
}
html[data-theme="dark"] .side-menu-mobile-header,
html[data-theme="dark"] .sidebar-collapse-bar {
border-color: var(--line);
}
html[data-theme="dark"] .user-menu-dropdown {
background: var(--surface);
border-color: var(--line);
}
html[data-theme="dark"] .user-menu-info { border-bottom-color: var(--line); }
html[data-theme="dark"] a.menu-item:hover,
html[data-theme="dark"] button.menu-item:hover { background: rgba(255,255,255,.08); }
html[data-theme="dark"] .sidebar-collapse-bar:hover { background: rgba(255,255,255,.08); }
html[data-theme="dark"] .theme-toggle-circle {
background: var(--surface-sunk);
border-color: var(--line);
color: var(--text);
}
html[data-theme="dark"] .menu-corner-btn {
background: var(--surface-sunk);
border-color: var(--line);
color: var(--text);
}
html[data-theme="dark"] .menu-corner-btn:hover { background: var(--line); }
html[data-theme="dark"] .topbar-logo { filter: brightness(.92); }
html[data-theme="dark"] .nav-item:hover { background: rgba(255,255,255,.08); }
html[data-theme="dark"] .nav-item.active { background: var(--accent-soft); }
html[data-theme="dark"] button:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent); }
html[data-theme="dark"] button.secondary { background: rgba(255,255,255,.10); color: var(--text); }
html[data-theme="dark"] button.secondary:hover { background: rgba(255,255,255,.15); }
html[data-theme="dark"] button.secondary:active { background: rgba(255,255,255,.20); }
html[data-theme="dark"] button.danger { background: #d70015; }
html[data-theme="dark"] button.danger:hover { background: var(--red-hover); }
html[data-theme="dark"] button.danger:active { background: var(--red-active); }
html[data-theme="dark"] select,
html[data-theme="dark"] input[type=text],
html[data-theme="dark"] input[type=date] {
background-color: var(--surface-sunk);
border-color: var(--line);
color: var(--text);
}
html[data-theme="dark"] select:focus,
html[data-theme="dark"] input[type=text]:focus,
html[data-theme="dark"] input[type=date]:focus {
box-shadow: 0 0 0 3px rgba(10,132,255,.28);
}
html[data-theme="dark"] select {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath fill='%23a1a1a6' d='M0 0h10L5 6z'/%3E%3C/svg%3E");
}
html[data-theme="dark"] ::placeholder { color: var(--text-subtle); }
html[data-theme="dark"] .status-edit {
background: var(--surface-sunk);
border-color: var(--line);
}
html[data-theme="dark"] .status-edit:focus { box-shadow: 0 0 0 3px rgba(10,132,255,.28); }
html[data-theme="dark"] .status-edit.status-new { color: var(--green); }
html[data-theme="dark"] .status-toolbar {
background: rgba(44,44,46,.9);
border-color: var(--line);
}
html[data-theme="dark"] .status-toolbar button:hover { background: rgba(255,255,255,.10); }
html[data-theme="dark"] .status-toolbar button:active { background: rgba(255,255,255,.16); }
html[data-theme="dark"] .locked-cell { background: #38383a !important; opacity: .55; }
html[data-theme="dark"] .locked-cell input {
background: #38383a !important;
border-color: var(--line) !important;
color: var(--text-muted) !important;
}
html[data-theme="dark"] .bar { background: #38383a; }
html[data-theme="dark"] .board-avatar { border-color: var(--line); }
html[data-theme="dark"] .load-card { border-color: var(--line); }
html[data-theme="dark"] .bubble-card,
html[data-theme="dark"] .projects-sortable,
html[data-theme="dark"] .people-readonly,
html[data-theme="dark"] .board-list { border-color: var(--line); }
html[data-theme="dark"] .modal-bg { background: rgba(0,0,0,.6); }
html[data-theme="dark"] .modal { border: 1px solid var(--line); }
html[data-theme="dark"] .swatch { box-shadow: inset 0 0 0 1px rgba(255,255,255,.14); }
html[data-theme="dark"] .table-wrap { border-color: var(--line); }
html[data-theme="dark"] .del-proj { color: var(--red); }
html[data-theme="dark"] input[type=text].edit-name { border-color: transparent; background: transparent; }
html[data-theme="dark"] input[type=text].edit-name:hover { background: rgba(255,255,255,.06); }
html[data-theme="dark"] input[type=text].edit-name:focus {
background: var(--surface-sunk);
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(10,132,255,.28);
}
html[data-theme="dark"] ::-webkit-scrollbar-thumb { background: rgba(255,255,255,.22); background-clip: content-box; }
html[data-theme="dark"] ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.34); background-clip: content-box; }
/* === GANTT / Projektplan === */
.modal-wide {
max-width: 900px;
width: 92%;
}
.gantt-container {
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-lg);
padding: 12px 0;
box-shadow: var(--sh-1);
width: 100%;
box-sizing: border-box;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.gantt-chart {
width: 100%;
min-width: 0;
position: relative;
}
.gantt-row {
display: flex;
align-items: center;
position: relative;
height: 32px;
border-bottom: 1px solid var(--line-soft);
}
.gantt-row.gantt-header,
.gantt-row.gantt-days {
height: 26px;
border-bottom: 1px solid var(--line);
}
.gantt-timeline {
position: relative;
flex: 1 1 auto;
height: 100%;
min-width: 0;
overflow: hidden;
}
.gantt-row.gantt-project {
height: 34px;
}
.gantt-row.gantt-project.highlighted {
background: var(--accent-soft);
}
.gantt-row.gantt-vacation {
height: 22px;
background: transparent;
font-size: 11px;
color: var(--text-muted);
}
.gantt-label {
/* Breite per Griff veränderbar (ganttSpalteZiehen(), js/gantt.js). */
width: var(--gantt-label-w, 220px);
flex: 0 0 var(--gantt-label-w, 220px);
min-width: var(--gantt-label-w, 220px);
max-width: var(--gantt-label-w, 220px);
height: 100%;
align-self: stretch;
box-sizing: border-box;
padding: 0 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
font-size: 12px;
border-right: 1px solid var(--line-soft);
position: sticky;
left: 0;
z-index: 10;
background: var(--surface);
display: flex;
align-items: center;
}
.gantt-row.gantt-header .gantt-label,
.gantt-row.gantt-days .gantt-label {
background: var(--surface);
z-index: 15;
}
.gantt-row.gantt-project.highlighted .gantt-label {
background: var(--accent-soft);
}
.gantt-row.gantt-vacation .gantt-label {
font-weight: normal;
display: flex;
align-items: center;
gap: 6px;
background: var(--surface);
}
.gantt-row.gantt-todo .gantt-label {
background: var(--surface);
}
.gantt-vacation-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--amber);
}
.gantt-month,
.gantt-day {
position: absolute;
top: 0;
bottom: 0;
border-right: 1px solid var(--line-soft);
font-size: 11px;
color: var(--text-muted);
padding: 2px 4px;
white-space: nowrap;
overflow: hidden;
box-sizing: border-box;
}
.gantt-month {
background: var(--surface-sunk);
font-weight: 500;
}
.gantt-day {
display: flex;
align-items: center;
justify-content: center;
}
.gantt-day.weekend {
background: var(--surface-sunk);
opacity: .7;
}
.gantt-day.gantt-week {
font-weight: 500;
color: var(--text);
border-right: 1px solid var(--line);
}
/* Raster-Overlay: liegt über die gesamte Timeline-Breite (rechts der Labels).
Wochenend-Flächen zeichnen ohne z-index (Zeilen liegen darüber, sind aber
transparent); die Heute-Linie bekommt z-index, damit sie über den Balken,
aber unter den sticky Labels liegt. */
.gantt-grid-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 220px;
right: 0;
pointer-events: none;
}
.gantt-grid-weekend {
position: absolute;
top: 0;
bottom: 0;
background: var(--surface-sunk);
opacity: .55;
}
.gantt-grid-today {
position: absolute;
top: 0;
bottom: 0;
width: 0;
border-left: 2px solid var(--red);
opacity: .7;
z-index: 9;
}
.gantt-bar {
position: absolute;
top: 6px;
bottom: 6px;
border-radius: var(--r-sm);
overflow: hidden;
box-shadow: var(--sh-1);
}
.gantt-bar.project {
color: #fff;
display: flex;
align-items: center;
}
.gantt-bar .gantt-progress {
position: absolute;
top: 0;
left: 0;
bottom: 0;
background: rgba(0,0,0,.22);
pointer-events: none;
}
.gantt-bar-label {
position: relative;
z-index: 1;
padding: 0 8px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.gantt-bar.vacation {
top: 4px;
bottom: 4px;
background: repeating-linear-gradient(
45deg,
rgba(255, 159, 10, .55),
rgba(255, 159, 10, .55) 6px,
rgba(255, 159, 10, .35) 6px,
rgba(255, 159, 10, .35) 12px
);
border: 1px solid var(--amber);
border-radius: var(--r-pill);
}
.gantt-row.gantt-todo { height: 26px; font-size: 11px; }
.gantt-row.gantt-todo.done { opacity: .55; }
.gantt-row.gantt-milestone-row { height: 40px; font-size: 11px; background: transparent; }
.gantt-row.gantt-milestone-row .gantt-milestone { top: 6px; }
.gantt-row.gantt-milestone-row .gantt-milestone-label { top: 24px; max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
.gantt-row.gantt-todo-add {
height: 34px;
font-size: 12px;
background: var(--surface-alt);
position: relative;
}
.gantt-todo-add-sticky {
position: sticky;
left: 0;
z-index: 10;
display: flex;
align-items: center;
background: var(--surface-alt);
border-right: 1px solid var(--line-soft);
height: 100%;
align-self: stretch;
box-sizing: border-box;
}
.gantt-todo-add-sticky .gantt-label {
position: static;
border-right: none;
background: transparent;
flex: 0 0 220px;
width: 220px;
min-width: 220px;
max-width: 220px;
box-sizing: border-box;
display: flex;
align-items: center;
gap: 6px;
padding: 0 12px;
}
.gantt-bar.todo {
top: 5px;
bottom: 5px;
background: var(--accent);
border-radius: var(--r-sm);
}
.gantt-bar.todo.done {
background: var(--green);
}
.gantt-todo-add-controls {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: nowrap;
width: fit-content;
max-width: 100%;
flex-shrink: 0;
padding-right: 8px;
}
.gantt-todo-add input[type=text],
.gantt-todo-add input[type=date] {
padding: 4px 8px;
font-size: 12px;
}
.gantt-todo-add input[type=text] {
flex: 0 0 180px;
width: 180px;
min-width: 140px;
}
.gantt-todo-add input[type=date] {
flex: 0 0 120px;
width: 120px;
min-width: 120px;
}
.gantt-todo-add button {
flex: 0 0 auto;
padding: 4px 12px;
font-size: 12px;
}
.gantt-todo-add .gantt-timeline {
display: flex;
align-items: center;
gap: 8px;
padding: 0 8px;
overflow: hidden;
}
.gantt-todo-actions {
display: inline-flex;
gap: 4px;
margin-left: 6px;
}
/* === GANTT Abhängigkeiten (Dependencies) === */
.gantt-dependencies-svg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 8;
overflow: visible;
}
.gantt-dep-path {
fill: none;
stroke: var(--accent);
stroke-width: 2.2px;
stroke-linecap: round;
stroke-linejoin: round;
opacity: 0.85;
transition: stroke 0.2s, stroke-width 0.2s, opacity 0.2s;
}
.gantt-dep-path.conflict {
stroke: var(--red);
stroke-dasharray: 5 3;
}
.gantt-dep-path.done {
stroke: var(--green);
}
.gantt-dep-path.highlight {
stroke-width: 3.5px;
opacity: 1;
}
.gantt-dep-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 11px;
padding: 1px 6px;
border-radius: 6px;
background: var(--accent-soft);
color: var(--accent);
margin-left: 6px;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
}
.gantt-dep-badge.conflict {
background: rgba(215, 0, 21, 0.1);
color: var(--red);
border-color: rgba(215, 0, 21, 0.2);
}
.gantt-todo-actions button {
padding: 2px 8px;
font-size: 11px;
}
.gantt-milestone {
position: absolute;
top: 2px;
width: 0;
height: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 12px solid var(--red);
transform: translateX(-50%);
z-index: 2;
cursor: grab;
}
.gantt-milestone:active { cursor: grabbing; }
.gantt-milestone-label {
position: absolute;
top: 16px;
transform: translateX(-50%);
font-size: 11px;
color: var(--text-muted);
white-space: nowrap;
pointer-events: none;
z-index: 2;
}
.gantt-bar.project,
.gantt-bar.vacation { cursor: grab; }
.gantt-bar.project:active,
.gantt-bar.vacation:active { cursor: grabbing; }
.gantt-resize {
position: absolute;
top: 0;
bottom: 0;
width: 10px;
cursor: col-resize;
z-index: 3;
}
.gantt-resize-left { left: 0; }
.gantt-resize-right { right: 0; }
.gantt-resize:hover { background: rgba(255,255,255,.25); }
.gantt-form {
display: grid;
gap: 12px;
}
.gantt-form-row {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.gantt-form-row input[type=date] { flex: 1 1 140px; }
.gantt-form-row input[type=text] { flex: 2 1 200px; }
.gantt-modal-preview .gantt-chart {
min-width: auto;
}
/* === Datumsbereich-Picker (Von/Bis in einem Kalenderfenster) === */
.range-picker {
position: fixed;
z-index: 1100;
width: 316px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-3);
padding: 12px;
font-size: 13px;
}
.range-picker-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
}
.range-picker-title {
font-weight: 500;
font-size: 13px;
}
.range-picker-hint {
font-size: 11px;
color: var(--text-muted);
text-align: center;
margin-bottom: 6px;
}
.range-picker-grid {
display: grid;
grid-template-columns: 26px repeat(7, 1fr);
gap: 2px;
}
/* KW-Spalte: klein und gedämpft, damit die Tage die Hauptsache bleiben. */
.range-picker-kw {
display: grid; place-items: center;
font-size: 11px; font-weight: 500; color: var(--text-subtle);
border-right: 1px solid var(--line-soft);
font-variant-numeric: tabular-nums;
}
.range-picker-kw-kopf { border-right: 1px solid var(--line-soft); }
.range-picker-weekday {
text-align: center;
font-size: 11px;
font-weight: 500;
color: var(--text-muted);
padding: 2px 0;
}
.range-picker-day {
text-align: center;
padding: 4px 0;
border-radius: var(--r-sm);
cursor: pointer;
user-select: none;
transition: background .15s var(--ease);
}
.range-picker-day:hover { background: var(--surface-sunk); }
.range-picker-day.weekend { color: var(--text-subtle); }
.range-picker-day.today { outline: 1px solid var(--accent); outline-offset: -1px; }
.range-picker-day.in-range { background: var(--accent-soft); border-radius: 0; }
.range-picker-day.sel-start,
.range-picker-day.sel-end {
background: var(--accent);
color: #fff;
font-weight: 500;
}
.range-picker-footer {
display: flex;
justify-content: space-between;
gap: 8px;
margin-top: 8px;
}
/* Anzeige-Feld, das den Picker öffnet */
.range-picker-display {
cursor: pointer;
background: var(--surface);
min-width: 170px;
}
.person-absences {
padding: 8px 8px 8px 40px;
margin-top: 4px;
border-radius: var(--r-md);
background: var(--surface-alt);
}
.absence-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 8px;
}
.absence-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: 6px;
padding: 4px 12px;
font-size: 12px;
}
.absence-chip small {
color: var(--text-muted);
font-size: 11px;
}
.absence-form {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.absence-form input[type=date] { width: 130px; }
.absence-form select { min-width: 110px; }
/* Tag-Chips: editierbar in der Projekte-Liste (projects.js), read-only auf
Board-Zeilen, Dashboard-Aufgabenkarten und der Projektplan-Kopfzeile.
Farben ausschließlich über CSS-Variablen, die in beiden Dark-Mode-Blöcken
bereits passend umdefiniert sind — keine zusätzlichen Dark-Overrides nötig. */
.tag-chip-list {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.tag-chip {
display: inline-flex;
align-items: center;
gap: 4px;
background: var(--surface-sunk);
border: 1px solid var(--line-soft);
border-radius: 6px;
padding: 2px 8px;
font-size: 11px;
font-weight: 400;
color: var(--text-muted);
}
.tag-chip .tag-remove {
cursor: pointer;
font-weight: 500;
color: var(--text-subtle);
line-height: 1;
transition: color .15s var(--ease);
}
.tag-chip .tag-remove:hover { color: var(--red); }
/* === Dashboard ("Meine Aufgaben"-Tab, js/dashboard/dashboard.js) ===
Aufbau: Kopf — Band — zwei Zonen. Die frühere Anordnung stellte die
Uhrzeit als größte Zahl heraus, zeigte vier gleich große Kennzahlkacheln
(meist drei davon mit einer Null) und legte alles untereinander, sodass
der Urlaub eines Kollegen gleichrangig unter den eigenen offenen Aufgaben
stand. */
/* ---------- Kopf: Begrüßung links, Nebensachen rechts ---------- */
.dash-kopf {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 20px;
}
.dash-gruss {
margin: 0;
font-size: 24px;
font-weight: 500;
letter-spacing: 0;
}
.dash-gruss em {
font-style: normal;
font-weight: 400;
color: var(--text-muted);
}
.dash-kopf-rechts {
display: flex;
align-items: baseline;
gap: 12px;
}
.dash-sitzung {
font-size: 11px;
font-weight: 500;
padding: 2px 8px;
border-radius: 6px;
background: var(--surface-sunk);
color: var(--text-muted);
}
.dash-uhr {
font-size: 13px;
color: var(--text-muted);
font-variant-numeric: tabular-nums;
}
/* ---------- Band: der Befund als Satz ---------- */
.dash-band {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
padding: 16px 20px;
margin-bottom: 24px;
background: var(--surface-sunk);
border-radius: var(--r-lg);
}
/* Ohne Befund bleibt das Band farblos — eine Null in Alarmfarbe behauptet
ein Problem, das es nicht gibt. */
.dash-band.ist-ruhig {
background: transparent;
border: 1px solid var(--line-soft);
}
.dash-befund {
margin: 0;
flex: 1 1 300px;
min-width: 0;
font-size: 19px;
}
/* Die Zahl größer als der Satz: Sie ist die Nachricht, und ab 19 px fett
gilt für den Kontrast die Schwelle für große Schrift — die Ampelfarbe
erreicht auf der abgesetzten Bandfläche sonst nur 4,1:1 im dunklen Thema. */
.dash-befund b {
font-size: 1.15em;
font-weight: 500;
font-variant-numeric: tabular-nums;
}
.dash-befund b.ist-rot { color: var(--red); }
.dash-befund b.ist-ruhig { color: var(--green); }
.dash-band-zahlen { display: flex; gap: 20px; flex-wrap: wrap; }
.dash-zahl { display: flex; flex-direction: column; gap: 1px; min-width: 74px; }
.dash-zahl b {
font-size: 19px;
font-weight: 500;
line-height: 1.15;
font-variant-numeric: tabular-nums;
}
.dash-zahl span { font-size: 11px; color: var(--text-subtle); white-space: nowrap; }
.dash-zahl.ist-null b { color: var(--text-subtle); }
/* ---------- Zwei Zonen: links der Arbeitsstrom, rechts der Rand ---------- */
.dash-zonen {
display: grid;
grid-template-columns: minmax(0, 1fr) 300px;
gap: 24px;
align-items: start;
}
.dash-abschnitt { margin-bottom: 24px; }
.dash-abschnitt:last-child { margin-bottom: 0; }
.dash-abschnitt-kopf {
display: flex;
align-items: baseline;
gap: 8px;
margin-bottom: 8px;
}
.dash-abschnitt-titel {
margin: 0;
font-size: 12px;
font-weight: 500;
letter-spacing: 0;
color: var(--text-muted);
}
.dash-abschnitt-zahl {
font-size: 11px;
padding: 1px 8px;
border-radius: 6px;
background: var(--surface-sunk);
color: var(--text-muted);
font-variant-numeric: tabular-nums;
}
.dash-abschnitt-zusatz {
margin-left: auto;
font-size: 11px;
color: var(--text-subtle);
}
.dash-karte {
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-lg);
box-shadow: var(--sh-1);
overflow: hidden;
}
.dash-leer { padding: 16px 20px; font-size: 13px; color: var(--text-subtle); }
/* ---------- Meine Aufgaben: Zeilen statt halbseitiger Karten ---------- */
.dash-auf {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--line-soft);
text-decoration: none;
color: inherit;
transition: background .12s var(--ease);
}
.dash-auf:last-child { border-bottom: none; }
.dash-auf:hover { background: var(--surface-sunk); }
.dash-auf:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.dash-auf-text { flex: 1 1 auto; min-width: 0; }
.dash-auf-titel {
display: block;
font-size: 13px;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.dash-auf-meta {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin-top: 1px;
font-size: 11px;
color: var(--text-subtle);
}
.dash-auf-tag {
font-size: 11px;
padding: 0 6px;
border-radius: var(--r-sm);
background: var(--surface-sunk);
color: var(--text-muted);
}
.dash-auf-fortschritt { flex: 0 0 96px; display: flex; align-items: center; gap: 8px; }
.dash-auf-bar {
flex: 1;
height: 5px;
background: var(--surface-sunk);
border-radius: var(--r-pill);
overflow: hidden;
}
.dash-auf-bar i { display: block; height: 100%; background: var(--accent); border-radius: var(--r-pill); }
.dash-auf-pct {
min-width: 30px;
text-align: right;
font-size: 11px;
color: var(--text-subtle);
font-variant-numeric: tabular-nums;
}
.dash-auf-frist {
flex: 0 0 118px;
text-align: right;
font-size: 12px;
line-height: 1.3;
font-variant-numeric: tabular-nums;
}
.dash-auf-frist em { display: block; font-style: normal; font-size: 11px; color: var(--text-subtle); }
.dash-auf-frist.ist-ueberfaellig, .dash-auf-frist.ist-ueberfaellig em { color: var(--red); }
.dash-auf-frist.ist-bald { color: var(--amber); }
/* ---------- Rechte Zone: Freigaben und Abwesenheiten ---------- */
.dash-rand-zeile {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
border-bottom: 1px solid var(--line-soft);
}
.dash-rand-zeile:last-child { border-bottom: none; }
.dash-avatar {
flex: 0 0 auto;
width: 28px;
height: 28px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 11px;
font-weight: 500;
background: var(--surface-sunk);
color: var(--text-muted);
}
.dash-rand-text { flex: 1; min-width: 0; }
.dash-rand-name {
display: block;
font-size: 13px;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.dash-rand-meta {
display: block;
font-size: 11px;
color: var(--text-subtle);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.dash-chip {
flex: 0 0 auto;
font-size: 11px;
padding: 1px 8px;
border-radius: 6px;
background: var(--surface-sunk);
color: var(--text-muted);
white-space: nowrap;
}
/* Wer heute fehlt, ist die eigentliche Nachricht. */
.dash-chip.ist-heute { background: var(--red); color: #fff; }
@media (max-width: 1080px) {
.dash-zonen { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
.dash-auf-fortschritt { display: none; }
.dash-gruss { font-size: 19px; }
}
/* Farbe je Abwesenheitsart. Genutzt von .absence-chip (Projektbeteiligte,
Benutzerverwaltung, Projektbereich) und von .dash-chip im Dashboard.
.absence-chip-live ist entfallen — das Dashboard kennzeichnet eine heute
laufende Abwesenheit über .dash-chip.ist-heute. */
.absence-type-urlaub { background: rgba(36,138,61,.12); color: var(--green); border-color: transparent; }
.absence-type-krank { background: rgba(215,0,21,.10); color: var(--red); border-color: transparent; }
.absence-type-fortbildung { background: rgba(178,80,0,.12); color: var(--amber); border-color: transparent; }
/* Dark mode GANTT */
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) .gantt-container { border-color: var(--line); }
html:not([data-theme="light"]) .gantt-row { border-color: var(--line); }
html:not([data-theme="light"]) .gantt-row.gantt-header,
html:not([data-theme="light"]) .gantt-row.gantt-days { border-color: var(--line); }
html:not([data-theme="light"]) .gantt-month { background: var(--surface-sunk); }
html:not([data-theme="light"]) .gantt-day.weekend { background: var(--surface-sunk); }
html:not([data-theme="light"]) .gantt-row.gantt-project.highlighted { background: var(--accent-soft); }
html:not([data-theme="light"]) .person-absences { background: var(--surface-sunk); border-color: var(--line); }
html:not([data-theme="light"]) .absence-chip { background: var(--surface); border-color: var(--line); }
html:not([data-theme="light"]) .absence-type-urlaub { background: rgba(48,209,88,.16); }
html:not([data-theme="light"]) .absence-type-krank { background: rgba(255,69,58,.16); }
html:not([data-theme="light"]) .absence-type-fortbildung { background: rgba(255,159,10,.16); }
}
html[data-theme="dark"] .gantt-container { border-color: var(--line); }
html[data-theme="dark"] .gantt-row { border-color: var(--line); }
html[data-theme="dark"] .gantt-row.gantt-header,
html[data-theme="dark"] .gantt-row.gantt-days { border-color: var(--line); }
html[data-theme="dark"] .gantt-month { background: var(--surface-sunk); }
html[data-theme="dark"] .gantt-day.weekend { background: var(--surface-sunk); }
html[data-theme="dark"] .gantt-row.gantt-project.highlighted { background: var(--accent-soft); }
html[data-theme="dark"] .person-absences { background: var(--surface-sunk); border-color: var(--line); }
html[data-theme="dark"] .absence-chip { background: var(--surface); border-color: var(--line); }
html[data-theme="dark"] .absence-type-urlaub { background: rgba(48,209,88,.16); }
html[data-theme="dark"] .absence-type-krank { background: rgba(255,69,58,.16); }
html[data-theme="dark"] .absence-type-fortbildung { background: rgba(255,159,10,.16); }
/* === Bereich „Einstellungen" (js/settings/settings.js) ===
Karten in einem Raster: „Mein Konto" und „Honorar" sind schmal, die
Vorlagen-Pflege bekommt über .settings-card-wide die volle Breite, weil
dort eine Liste mit Bezeichnung, Versatz/Dauer und Lösch-Knopf steht. Die
Felder der beiden firmenweiten Abschnitte nutzen weiterhin die Klassen aus
plan-styles.css (.honorar-anteil-field, .template-admin-row …) — sie sind
mit den Masken aus den Projekt-Dialogen hierher gezogen und mussten dafür
nicht umbenannt werden. */
.settings-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 16px;
align-items: start;
/* Seit immer nur EIN Abschnitt sichtbar ist (showAdminSection()), füllt
eine einzelne Karte sonst die ganze Fensterbreite — Beschriftung links,
Eingabefeld ganz rechts. Die Grenze hält Feld und Bezeichnung beisammen
und lässt der Aufwands-Matrix trotzdem genug Platz. */
max-width: 1180px;
}
/* Ausnahme fuer breite Abschnitte (Kennzeichen wide in ADMIN_SECTIONS,
js/admin/admin.js): Die Benutzertabelle hat zehn Spalten und scrollte in
der begrenzten Breite quer. Sie darf das Fenster ausnutzen. */
.settings-grid.is-wide { max-width: none; }
.settings-card-wide { grid-column: 1 / -1; }
.settings-card {
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-lg);
padding: 16px 20px;
box-shadow: var(--sh-1);
}
.settings-card-title {
margin: 0 0 8px;
font-size: 13px;
font-weight: 500;
display: flex;
align-items: baseline;
gap: 8px;
flex-wrap: wrap;
}
/* Reichweite einer Einstellung — „firmenweit" heißt: wirkt bei allen. */
.settings-scope {
font-size: 11px;
font-weight: 500;
letter-spacing: 0;
color: var(--text-muted);
background: var(--surface-alt);
border-radius: 8px;
padding: 2px 8px;
}
.settings-kv { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
.settings-kv-row {
display: flex;
gap: 8px;
font-size: 13px;
padding: 4px 0;
border-bottom: 1px solid var(--line-soft);
}
.settings-kv-row:last-child { border-bottom: none; }
.settings-kv-key { width: 150px; flex-shrink: 0; color: var(--text-muted); }
.settings-warn { color: var(--amber); }
.settings-email-edit { display: flex; gap: 8px; align-items: center; flex: 1; flex-wrap: wrap; }
.settings-email-edit input[type=email] { flex: 1; max-width: 320px; min-width: 160px; }
.settings-inline-field {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
margin-bottom: 8px;
}
.settings-inline-field select { min-width: 120px; }
/* KI-Zugangsdaten (Einstellungen -> Mein Konto, js/settings/settings.js):
ein umrandeter Block je Anbieter statt eines durchgehenden Feld-Rasters —
ChatGPT und Claude sind zwei getrennte Zugänge, keine vier gleichrangigen
Felder. Design-Entwurf mit Erik abgestimmt (Artifact d7ab2945). */
.ai-provider { border: 1px solid var(--line-soft); border-radius: var(--r-md); padding: 16px 16px; }
.ai-provider + .ai-provider { margin-top: 12px; }
.ai-provider-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.ai-provider-info { min-width: 0; }
.ai-provider-mark {
width: 26px; height: 26px; border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 600; color: #fff; flex-shrink: 0; letter-spacing: 0;
}
.ai-provider-mark.chatgpt { background: #10a37f; }
.ai-provider-mark.claude { background: #c96442; }
.ai-provider-name { font-weight: 500; font-size: 13px; }
.ai-provider-sub { font-size: 12px; color: var(--text-subtle); }
/* Status-Pill: derselbe Punkt-plus-Text-Aufbau wie an anderen Stellen der
App (z. B. .protokoll-status-dot), hier als Pill neben dem Anbieternamen. */
.ai-status-chip {
margin-left: auto;
display: inline-flex; align-items: center; gap: 6px;
font-size: 12px; font-weight: 500;
padding: 4px 12px 4px 8px;
border-radius: 6px;
background: var(--surface-alt);
color: var(--text-muted);
flex-shrink: 0;
}
.ai-status-chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-subtle); flex-shrink: 0; }
.ai-status-chip.is-on { color: var(--green); }
.ai-status-chip.is-on .dot { background: var(--green); }
.ai-provider-row { display: flex; gap: 8px; }
.ai-provider-row input[type=password],
.ai-provider-row input[type=text] {
flex: 1; min-width: 0;
padding: 8px 12px;
font-size: 13px;
border: 1px solid var(--line);
border-radius: var(--r-sm);
background: var(--surface);
color: var(--text);
font-family: inherit;
}
.ai-provider-row input::placeholder { color: var(--text-subtle); }
.ai-provider-row input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
/* Häkchen-Knopf statt Textknopf "Speichern" — spricht für sich, direkt neben
dem Feld, das er betrifft. "Entfernen" bleibt ein leiser Text-Link, weil
er der seltenere, destruktivere Pfad ist. */
.ai-icon-btn {
flex-shrink: 0;
width: 30px; height: 30px; padding: 0;
border-radius: var(--r-sm);
border: 1px solid var(--accent);
background: var(--accent);
color: #fff;
display: flex; align-items: center; justify-content: center;
cursor: pointer;
transition: background .15s var(--ease), border-color .15s var(--ease);
}
.ai-icon-btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* flex-shrink:0 ist hier kein Stil-Detail, sondern die Abhilfe für einen
Chromium-Eigenheit: ein <svg> als Flex-Kind schrumpft ohne explizites
flex-shrink:0 auf Breite 0 (min-width:auto berechnet die Inhaltsgröße
eines SVG offenbar als 0), wodurch das Häkchen-Icon unsichtbar wurde. */
.ai-icon-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.ai-text-btn {
flex-shrink: 0;
border: none; background: none;
font-size: 12px; color: var(--text-subtle);
cursor: pointer; padding: 0 2px;
}
.ai-text-btn:hover { color: var(--red); }
.ai-text-btn:disabled { opacity: .4; cursor: default; }
.ai-text-btn:disabled:hover { color: var(--text-subtle); }
/* Cloud/Eigener-Server-Umschalter: ersetzt den früheren, eingeklappten
"API-URL"-Link. Ein eigener Server (LM Studio, Ollama) ist ein Kernfall,
kein Nebendetail — genau das wollte Erik als Erstes ausprobieren, als
er die Karte in echt benutzte — deshalb jetzt eine sichtbare Wahl statt
eines kleinen Links, den man erst entdecken muss. */
.ai-mode-toggle {
display: inline-flex; background: var(--surface-sunk); border-radius: 8px;
padding: 4px; gap: 2px; margin-top: 12px;
}
.ai-mode-toggle button {
border: none; background: none; padding: 4px 12px; border-radius: 6px;
font-size: 12px; font-weight: 500; color: var(--text-muted); cursor: pointer; font-family: inherit;
}
.ai-mode-toggle button.is-active { background: var(--surface); color: var(--text); font-weight: 500; box-shadow: var(--sh-1); }
.ai-url-panel { margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--line-soft); display: none; }
.ai-url-panel.is-open { display: block; }
.ai-url-panel label { display: block; font-size: 11px; color: var(--text-subtle); margin-bottom: 4px; }
.ai-url-panel .ai-provider-row { margin-bottom: 0; }
/* Zweites Label-/Zeilenpaar im selben Panel (Modellname, unter der
Server-Adresse): braucht Abstand nach oben, den das erste (am
Panel-Anfang) nicht haben darf — gleiches Muster wie .drawer-h4-zweit. */
.ai-url-panel label:nth-of-type(2) { margin-top: 12px; }
/* "Neu verbinden": prüft die gespeicherte Konfiguration wirklich (POST
/my-ai-settings/test), statt nur anzuzeigen, dass irgendein Schlüssel
gespeichert ist. Umrandet statt gefüllt, da kein primärer Speichern-
Knopf — sitzt direkt neben dem Status-Pill, den er betrifft. */
.ai-reconnect-btn {
flex-shrink: 0; width: 26px; height: 26px; padding: 0; border-radius: 50%;
border: 1px solid var(--line); background: var(--surface); color: var(--text-muted);
display: flex; align-items: center; justify-content: center; cursor: pointer;
transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}
.ai-reconnect-btn:hover { background: var(--surface-alt); color: var(--accent); border-color: var(--accent); }
.ai-reconnect-btn:disabled { opacity: .4; cursor: default; }
.ai-reconnect-btn:disabled:hover { background: var(--surface); color: var(--text-muted); border-color: var(--line); }
.ai-reconnect-btn svg { width: 13px; height: 13px; flex-shrink: 0; }
.ai-reconnect-btn.is-testing svg { animation: ai-reconnect-spin .9s linear infinite; }
@keyframes ai-reconnect-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
/* Fehler aus "Neu verbinden": bleibt stehen, bis der nächste Test läuft
oder erfolgreich ist — kein flüchtiger Hinweis. Dieselbe Lehre wie beim
Protokoll-Import-Banner: ein Toast neben einer sich gleichzeitig
ändernden Ansicht wird leicht übersehen. :empty blendet die Zeile aus,
solange kein Fehler ansteht, ohne eigenes hidden-Attribut in JS. */
.ai-test-error { font-size: 12px; color: var(--red); margin-top: 8px; }
.ai-test-error:empty { display: none; }
/* Bestätigung nach dem Speichern. Bewusst kein alert(): ein gelungener
Speichervorgang soll den Arbeitsfluss nicht anhalten. */
.settings-note {
margin-top: 12px;
font-size: 13px;
color: var(--green);
opacity: 0;
transition: opacity .25s var(--ease);
}
.settings-note.show { opacity: 1; }
/* Felder und Matrix der firmenweiten Einstellungen (js/settings/settings.js).
Die Aufwands-Matrix kann breiter werden als die Karte — sie scrollt dann in
ihrem eigenen Container, statt die Seite quer zu ziehen. */
.settings-field-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
gap: 8px 16px;
margin-bottom: 12px;
}
.settings-field {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: 13px;
}
.settings-field input { width: 88px; padding: 4px 8px; font-size: 13px; text-align: right; }
.settings-check {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
margin: 4px 0 2px;
}
.settings-subtitle {
margin: 16px 0 4px;
font-size: 13px;
font-weight: 500;
}
.settings-matrix-wrap { overflow-x: auto; margin-bottom: 8px; }
.settings-matrix { border-collapse: collapse; font-size: 12px; }
.settings-matrix th, .settings-matrix td {
padding: 4px 6px;
border-bottom: 1px solid var(--line-soft);
text-align: center;
}
.settings-matrix thead th { color: var(--text-muted); font-weight: 500; white-space: nowrap; }
.settings-matrix tbody th { text-align: left; font-weight: 500; white-space: nowrap; }
.settings-matrix input { width: 62px; padding: 4px 6px; font-size: 12px; text-align: right; }
.settings-holiday-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
/* Abweichende Stelle im Auslastungs-Tab (js/load/load.js) — steht nur da,
wenn die Person nicht voll beschäftigt ist. */
.load-capacity {
font-size: 11px;
font-weight: 500;
color: var(--text-muted);
background: var(--surface-alt);
border-radius: 6px;
padding: 1px 8px;
margin-left: 6px;
}
/* Benutzerverwaltung (Abschnitt „Benutzerverwaltung" des Bereichs #admin,
js/admin-users.js). Diese Regeln standen als <style>-Block in der frueheren
eigenstaendigen Seite public/admin-users.html; die Regeln der dortigen
Seiten-Huelle (.admin-page/.admin-header/.admin-body) sind mit ihr
entfallen, weil der Abschnitt jetzt in der normalen App-Huelle liegt. */
.admin-toolbar { margin-bottom: 12px; }
td.actions-cell { white-space: nowrap; width: 1%; text-align: right; }
/* Vor- und Nachname nebeneinander (Benutzer anlegen/bearbeiten), auf
schmalen Bildschirmen untereinander. */
.form-row-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
.form-row-pair > .form-row { min-width: 0; }
@media (max-width: 480px) { .form-row-pair { grid-template-columns: 1fr; } }
/* Zeilenaktionen der Benutzerverwaltung: „Bearbeiten" + Drei-Punkte-Menü
(js/admin-users.js, openUserActionMenu()). Das Menü hängt an <body> und
steht fixed, damit der quer scrollbare Tabellenrahmen es nicht abschneidet. */
.user-aktionen { display: inline-flex; gap: 6px; align-items: center; }
.user-kebab {
width: 30px; height: 30px; padding: 0;
display: inline-grid; place-items: center;
border: 1px solid transparent; border-radius: var(--r-sm);
background: transparent; color: var(--text-muted); cursor: pointer;
transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.user-kebab:hover { background: var(--surface-sunk); color: var(--text); }
.user-kebab[aria-expanded="true"] {
background: var(--accent-soft); color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
.user-kebab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.user-kebab svg { display: block; }
tr.is-menu-open { background: var(--surface-alt); }
.user-menu {
position: fixed; z-index: 250; min-width: 220px; max-width: 280px;
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-md); box-shadow: var(--sh-3); padding: 4px;
transform-origin: top right;
animation: user-menu-auf var(--dur) var(--ease-out);
}
.user-menu.nach-oben { transform-origin: bottom right; animation-name: user-menu-auf-oben; }
@keyframes user-menu-auf { from { opacity: 0; transform: scale(.96) translateY(-4px); } to { opacity: 1; transform: none; } }
@keyframes user-menu-auf-oben { from { opacity: 0; transform: scale(.96) translateY(4px); } to { opacity: 1; transform: none; } }
.user-menu-item {
all: unset; box-sizing: border-box;
display: flex; gap: 8px; align-items: flex-start; width: 100%;
padding: 8px 12px; border-radius: var(--r-sm);
color: var(--text); font-size: 13px; line-height: 1.35; cursor: pointer;
}
.user-menu-item:hover:not([aria-disabled="true"]), .user-menu-item:focus-visible { background: var(--surface-sunk); }
.user-menu-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.user-menu-ic { flex: none; width: 16px; height: 16px; margin-top: 1px; color: var(--text-muted); }
.user-menu-text { display: grid; gap: 1px; }
.user-menu-sub { font-size: 12px; color: var(--text-subtle); }
.user-menu-item.is-danger, .user-menu-item.is-danger .user-menu-ic { color: var(--red); }
.user-menu-item.is-danger:hover:not([aria-disabled="true"]),
.user-menu-item.is-danger:focus-visible { background: color-mix(in srgb, var(--red) 10%, transparent); }
.user-menu-item[aria-disabled="true"], .user-menu-item[aria-disabled="true"] .user-menu-ic { color: var(--text-subtle); cursor: not-allowed; }
.user-menu-trenner { border: 0; border-top: 1px solid var(--line-soft); margin: 4px 4px; }
.user-menu-confirm { padding: 12px 12px 8px; display: grid; gap: 8px; }
.user-menu-confirm p { margin: 0; font-size: 13px; line-height: 1.4; }
.user-menu-confirm-row { display: flex; gap: 6px; justify-content: flex-end; }
.badge-active { color: var(--green); font-weight: 500; }
.badge-inactive { color: var(--text-muted); }
.badge-warn { color: var(--amber); font-weight: 500; }
/* Das einmalig angezeigte Passwort laesst sich mit einem Klick markieren. */
#generatedPasswordBox {
display: none;
margin-bottom: 12px;
padding: 12px 16px;
border-radius: var(--r-md);
background: var(--surface-alt);
border: 1px solid var(--line-soft);
font-size: 13px;
}
#generatedPasswordBox b { user-select: all; }
/* === Verlauf: die Kacheln der Vorwochen (js/wochenbericht/wochenbericht.js)
Im wöchentlichen Teammeeting wird jedes Projekt angesehen; der Verlauf
zeigt auf Knopfdruck, wie der Stand in den Sitzungen davor war. Waagerecht
und scrollbar, weil der Bericht ohnehin sehr lang ist — eine Liste
untereinander hätte ihn je Projekt um ein Vielfaches verlängert. === */
.timeline-block {
grid-column: 1 / -1;
margin-top: 12px;
border-top: 1px solid var(--line-soft);
padding-top: 12px;
}
.timeline-block > summary {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 13px;
color: var(--text-muted);
list-style: none;
user-select: none;
}
.timeline-block > summary::-webkit-details-marker { display: none; }
/* Der Zähler schiebt sich sonst über die ganze Kartenbreite ans rechte Ende
(margin-left:auto von .comment-history-count) und steht dort verloren —
hier gehört er direkt an das Wort "Verlauf". */
.timeline-block > summary .comment-history-count { margin-left: 0; }
.timeline-block > summary:hover { color: var(--text); }
.timeline-block[open] > summary .comment-history-caret { transform: rotate(90deg); }
.timeline-body { padding-top: 12px; }
/* Die Schiene scrollt waagerecht; die Karte selbst darf dabei nicht breiter
werden, deshalb min-width:0 am Elternraster (siehe .board-drawer). */
.timeline-rail {
display: flex;
gap: 8px;
overflow-x: auto;
padding-bottom: 6px;
scroll-snap-type: x proximity;
}
.timeline-card {
flex: 0 0 190px;
scroll-snap-align: start;
display: flex;
flex-direction: column;
gap: 6px;
padding: 12px 12px;
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
background: var(--surface-alt);
font-size: 12px;
}
.timeline-card-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 6px;
}
.timeline-kw { font-weight: 500; white-space: nowrap; }
.timeline-datum { color: var(--text-muted); font-size: 11px; white-space: nowrap; }
/* Der Kommentar wird auf sechs Zeilen begrenzt, damit alle Kacheln einer
Schiene gleich hoch bleiben; der vollständige Text steht im Tooltip. */
/* Formatierter Kommentar (Absätze als <div>/<br>): statt line-clamp, das
mit Block-Kindern nicht greift, eine Höhe von sechs Zeilen. */
.timeline-text {
max-height: calc(1.45em * 6);
overflow: hidden;
overflow-wrap: anywhere;
line-height: 1.45;
min-height: 3em;
}
.timeline-text p { margin: 0; }
/* Gewerk über dem Kommentar: Systemtext, nicht Teil des Kommentars. */
.kommentar-gewerk {
font-size: 11px; font-weight: 600; letter-spacing: 0;
color: var(--text-subtle); margin-top: 4px;
}
.kommentar-gewerk:first-child { margin-top: 0; }
.timeline-leer { color: var(--text-muted); font-style: normal; }
.timeline-lph-list { display: flex; flex-direction: column; gap: 1px; }
.timeline-lph {
display: flex;
justify-content: space-between;
gap: 8px;
font-variant-numeric: tabular-nums;
color: var(--text-muted);
}
.timeline-termine { display: flex; flex-direction: column; gap: 1px; }
.timeline-termin { color: var(--text-muted); font-size: 12px; }
/* Gegenüber der Kachel DAVOR geänderte Werte. Dezent, aber auffindbar: Bei
zehn Leistungsphasen findet man die eine geänderte Zahl sonst nicht. */
.timeline-lph.is-changed,
.timeline-termin.is-changed { color: var(--accent); font-weight: 500; }
/* "übernehmen" in der Verlaufs-Kachel: übernimmt die einzige Funktion, die
der entfallene Aufklapper "Frühere Sitzungen" über das Anzeigen hinaus
hatte. Dezent, weil es in jeder Kachel steht und nicht mit dem Kommentar
um Aufmerksamkeit ringen soll. */
.timeline-take {
align-self: flex-start;
padding: 1px 8px;
border: 1px solid var(--line);
border-radius: 8px;
background: transparent;
color: var(--text-muted);
font-family: inherit;
font-size: 11px;
cursor: pointer;
}
.timeline-take:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.timeline-take:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Der Verfasser steht in der dichten Fassung in der Fußzeile neben den
Knöpfen statt in einer eigenen Kopfzeile darüber. */
/* Der Verfasser teilt sich die Fußzeile mit den Knöpfen. Er darf sie nicht
zweizeilig machen — deshalb bleibt er auf einer Zeile und wird lieber
gekürzt; der vollständige Eintrag steht ohnehin im Verlauf. */
.comment-foot-author {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 11px;
color: var(--text-subtle);
}
.comment-foot-author * { display: inline; white-space: nowrap; }
/* Das Verfasser-Kürzel klebte sonst am Namen: Der Avatar ist in der Fußzeile
inline gesetzt und bringt keinen eigenen Abstand mehr mit. */
.comment-foot-author .comment-av { margin-right: 4px; }
.comment-foot-author .comment-sep { margin: 0 4px; }
.timeline-people { display: flex; flex-wrap: wrap; gap: 4px; margin-top: auto; padding-top: 2px; }
.timeline-chip {
font-size: 11px;
font-weight: 500;
padding: 1px 6px;
border-radius: 6px;
background: var(--surface-sunk);
color: var(--text-muted);
}
/* === Wochenbericht als Tagesordnung, Fokus-Modus "Aktenregal" (Eriks Wahl
aus dem Entwurfsvergleich, js/wochenbericht/wochenbericht.js) ===
Links eine Leiste mit Nummer, Gruppe UND Name direkt sichtbar (keine
schmale Icon-Spalte mit Namen nur im Hover-Tooltip mehr), rechts das
gewählte Projekt: Kommentar (inkl. Scrollrad zum Verlauf), darunter
Leistungsphasen und Beteiligte dauerhaft sichtbar nebeneinander
(.bericht-meta-grid). Nur die Anwesenheit der Sitzung bleibt ein
aufklappbarer Abschnitt (.bericht-disclosure) - sie ist sitzungs-, nicht
projektbezogen und sitzt am Seitenfuß, unabhängig vom gewählten Projekt.
Ersetzt die vorherige ausgeschriebene Liste|Detail-Darstellung, die mit
Gewerke-Dropdown, Fortschrittsbalken, Suchfeld, mehreren Badges je Zeile
und allem gleichzeitig sichtbar als "zu überladen" zurückgemeldet wurde.
Eine ganze Fläche (.bericht-panel), nicht zwei lose nebeneinanderstehende
Karten — genau wie im freigegebenen Entwurf: eine Kopfleiste mit eigenem
Hintergrund/Trennlinie, darunter die Leiste als Spalte MIT dem Ergebnis
verbunden (Trennlinie statt eigenem Rahmen). */
.bericht-titelzeile { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.bericht-titelzeile button { margin-left: auto; }
.bericht-panel {
background: var(--surface); border: 1px solid var(--line-soft);
border-radius: var(--r-lg); box-shadow: var(--sh-1); overflow: hidden;
}
.bericht-topbar {
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
padding: 12px 20px; border-bottom: 1px solid var(--line-soft);
}
.bericht-gewerk-select {
font-size: 12px; font-weight: 500; padding: 4px 12px;
/* background-color statt background (Kurzform): die Kurzform setzt das vom
globalen select-Selektor gesetzte Pfeil-Icon (background-image/-repeat/
-position) auf die Vorgabewerte zurück, wodurch das 10x6px-Pfeilchen
über das ganze Feld kachelt statt einmal rechts zu sitzen - derselbe
Fehler wie zuvor bei .pc-select. */
border-radius: var(--r-sm); background-color: var(--surface-alt);
border: 1px solid var(--line); color: var(--text); cursor: pointer;
}
.bericht-ring {
width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
}
.bericht-ring span { width: 18px; height: 18px; border-radius: 50%; background: var(--surface); display: block; }
.bericht-fortschritt-text { font-size: 12px; color: var(--text-muted); font-weight: 500; white-space: nowrap; }
/* Reines Textlink, kein Knopf — im Entwurf steht an dieser Stelle nur der
Fortschritt, nicht noch eine zusätzliche Schaltfläche daneben. */
.bericht-reset-link {
font-size: 12px; font-weight: 500; border: none; background: none;
color: var(--text-subtle); cursor: pointer; padding: 0; text-decoration: underline;
text-decoration-color: var(--line);
}
.bericht-reset-link:hover { color: var(--text); }
.bericht-search-btn {
margin-left: auto; border: 1px solid var(--line); background: var(--surface-alt);
color: var(--text-subtle); font-size: 12px; padding: 4px 12px;
border-radius: 8px; cursor: pointer;
}
.bericht-search-btn:hover { color: var(--text); border-color: var(--text-subtle); }
.bericht-suche-input {
margin-left: auto; border: 1px solid var(--line); border-radius: 8px;
background: var(--surface-sunk); color: var(--text); font-size: 12px;
padding: 6px 12px; width: 170px;
}
.bericht-suche-input:focus { outline: none; border-color: var(--accent); background: var(--surface); }
/* ---------- Planung (Terminplan-Sandbox, js/planung/planung.js) ---------- */
.planung-row {
display: flex; align-items: center; gap: 12px;
padding: 12px 16px; border-radius: var(--r-md);
background: var(--surface); border: 1px solid var(--line-soft);
margin-bottom: 8px; cursor: pointer; transition: background .15s var(--ease);
}
.planung-row:hover { background: var(--surface-alt); }
.planung-row-icon {
width: 34px; height: 34px; border-radius: 8px; flex: 0 0 auto;
display: flex; align-items: center; justify-content: center; font-size: 13px;
}
.planung-row-icon.honorar { background: var(--accent-soft); color: var(--accent); }
.planung-row-icon.leer { background: var(--surface-sunk); color: var(--text-subtle); }
.planung-row-main { flex: 1; min-width: 0; }
.planung-row-name { font-size: 13px; font-weight: 500; margin: 0; }
.planung-row-meta { font-size: 12px; color: var(--text-muted); margin: 2px 0 0; }
.planung-row-stat { text-align: right; font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.planung-row-stat b { display: block; color: var(--text); font-size: 13px; font-variant-numeric: tabular-nums; }
.planung-choice-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.choice-card {
border: 1.5px solid var(--line); border-radius: var(--r-md); padding: 16px 16px 12px;
cursor: pointer; background: var(--surface-alt); transition: border-color .15s ease;
}
.choice-card:hover { border-color: var(--accent); }
.choice-card.is-gewaehlt { border-color: var(--accent); }
.choice-card-icon { font-size: 19px; margin-bottom: 8px; display: block; }
.choice-card-title { font-size: 13px; font-weight: 500; margin-bottom: 4px; }
.choice-card-desc { font-size: 12px; color: var(--text-muted); line-height: 1.4; }
.empty-hint { text-align: center; padding: 60px 20px; color: var(--text-subtle); font-size: 13px; }
.empty-hint .big { font-size: 15px; color: var(--text-muted); margin-bottom: 6px; display: block; }
/* ---------- Plan Checker (Studenten, js/planchecker/planchecker.js) ----------
Darstellung "Arbeitsfläche" (Eriks Wahl "B" aus dem Design-Vergleich): zwei
Spalten dauerhaft nebeneinander — links Upload/Konfiguration, rechts das
Ergebnis, damit ein erneutes Prüfen nach einer Korrektur das vorherige
Ergebnis nicht aus dem Blick verliert. Eigene .pc-*-Klassen statt
bestehender Komponenten (.board-drawer, .settings-card …): keine davon
passt auf eine Zwei-Spalten-Arbeitsfläche mit Ablagefläche + Ergebnisliste,
ein Zurechtbiegen hätte mehr Sonderfälle erzeugt als eine schlanke eigene
Regelgruppe. */
.pc-layout { display: grid; grid-template-columns: 340px 1fr; gap: 20px; align-items: start; }
@media (max-width: 860px) { .pc-layout { grid-template-columns: 1fr; } }
.pc-panel { background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: 20px; }
.pc-drop {
display: block; border: 1.5px dashed var(--line); border-radius: var(--r-md);
padding: 20px 16px; text-align: center; color: var(--text-muted);
background: var(--surface-alt); cursor: pointer; font-size: 13px;
transition: border-color .15s var(--ease), color .15s var(--ease), background .15s var(--ease);
}
.pc-drop:hover, .pc-drop.is-dragover { border-color: var(--accent); color: var(--text); background: var(--accent-soft); }
.pc-drop-name { font-weight: 500; color: var(--text); margin-top: 4px; font-size: 13px; word-break: break-word; }
.pc-section { margin-top: 16px; }
.pc-label { display: block; font-size: 12px; font-weight: 500; color: var(--text-muted); margin-bottom: 4px; }
.pc-merkmal-chip { font-size: 13px; padding: 4px 2px; color: var(--text-muted); }
.pc-zusatz { margin-top: 8px; }
.pc-zusatz .pc-input { width: 100%; }
/* background-color statt der background-Kurzschreibweise: Die Kurzschreibweise
setzt background-repeat/-position implizit auf ihre Vorgabewerte zurück
(repeat/0% 0%) und schlägt dabei wegen höherer Klassen-Spezifität die
globalen select-Regeln (background-repeat:no-repeat, siehe weiter oben),
die den eigenen SVG-Pfeil sonst nur einmal rechts zeigen — das gekachelte
Pfeil-Muster, das als "komisch" auffiel, war genau das. */
.pc-select, .pc-input {
border: 1px solid var(--line); border-radius: var(--r-md); background-color: var(--surface);
color: var(--text); font-size: 13px; font-family: inherit; padding: 8px 12px;
}
.pc-select:focus, .pc-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.pc-btn {
border: 1px solid var(--line); background: var(--surface); color: var(--text);
font-size: 13px; font-weight: 500; padding: 8px 16px; border-radius: var(--r-md); cursor: pointer;
}
.pc-btn:hover { background: var(--surface-alt); }
.pc-btn:disabled { opacity: .6; cursor: default; }
.pc-btn-primary { border: none; background: var(--accent); color: #fff; }
.pc-btn-primary:hover { background: var(--accent-hover); }
.pc-error { color: var(--red); margin: 8px 0 0; }
.pc-verdict {
display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
width: 20px; height: 20px; border-radius: 50%; font-size: 12px; font-weight: 600; color: #fff;
}
.pc-verdict.ok { background: var(--green); }
.pc-verdict.fail { background: var(--red); }
.pc-results { min-height: 420px; display: flex; flex-direction: column; }
.pc-results-empty { margin: auto; text-align: center; color: var(--text-subtle); padding: 40px 20px; }
.pc-results-empty .big { font-size: 13px; color: var(--text-muted); margin-bottom: 4px; display: block; }
.pc-result-head { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--line-soft); flex-wrap: wrap; }
.pc-result-head .filename { font-weight: 500; }
.pc-result-head .meta { color: var(--text-subtle); font-size: 12px; margin-left: auto; }
.pc-result-row { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.pc-result-row:last-child { border-bottom: none; }
.pc-result-row .titel { font-weight: 500; margin-bottom: 2px; font-size: 13px; }
.pc-result-row .begruendung { color: var(--text-muted); font-size: 13px; }
.pc-result-text { flex: 1 1 auto; min-width: 0; }
/* pc-result-shot: Screenshot des Planausschnitts, den die KI für das Merkmal
als Beleg gewählt hat (server.js, pruefePlanMitKI(), bildIndex) — nur
vorhanden, wenn die KI einen gültigen Bild-Index geliefert hat. Ein
<button>, kein <a> (öffnet #pcShotModal statt eines Links auf die
data:-URL, s. planCheckerZeigeScreenshot() — ein Link blieb wirkungslos,
da Chrome das Öffnen einer data:-URL in einem neuen Tab blockiert),
deshalb die Button-Vorgaben zurückgesetzt. */
.pc-result-shot { flex: 0 0 auto; display: block; background: none; border: none; padding: 0; cursor: pointer; }
.pc-result-shot img {
width: 110px; height: 80px; object-fit: cover; display: block;
border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--surface-alt);
}
.pc-result-shot:hover img { border-color: var(--accent); }
/* Entwurf "Aktenregal" (zweite Fassung des Fokus-Modus, Eriks Wahl aus dem
Entwurfsvergleich): die Leiste zeigt Nummer, Gruppe UND Name direkt statt
nur eine Nummer mit Name im Hover-Tooltip (die vorherige, schmale 64px-
Fassung) — man sieht die ganze Tagesordnung auf einen Blick, nicht nur
beim Drüberfahren. */
.bericht-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); }
@media (max-width: 860px) { .bericht-layout { grid-template-columns: 1fr; } }
.bericht-rail {
background: var(--surface-alt);
border-right: 1px solid var(--line-soft);
padding: 12px 8px; display: flex; flex-direction: column; gap: 2px;
position: sticky; top: 64px; max-height: calc(100vh - 64px); overflow-y: auto;
}
.bericht-rail-gruppe {
font-size: 11px; font-weight: 600; letter-spacing: 0;
color: var(--text-subtle); padding: 12px 8px 4px;
}
.bericht-rail-gruppe:first-child { padding-top: 2px; }
.bericht-rail-item {
position: relative; display: flex; align-items: center; gap: 8px;
padding: 6px 8px; border-radius: var(--r-sm); cursor: pointer; color: var(--text); font-size: 13px;
}
.bericht-rail-item:hover { background: var(--surface-sunk); }
.bericht-rail-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.bericht-rail-item.is-aktiv { background: var(--accent-soft); }
.bericht-rail-item.is-aktiv .bericht-rail-name { color: var(--accent); font-weight: 500; }
/* Abgehakt: zurückgenommen, aber erkennbar — man muss im Meeting noch sehen
können, was man schon besprochen hat. */
.bericht-rail-item.is-ab { opacity: .55; }
.bericht-rail-item.is-aktiv.is-ab { opacity: 1; }
.bericht-rail-num { font-size: 11px; color: var(--text-subtle); font-variant-numeric: tabular-nums; flex-shrink: 0; width: 16px; }
.bericht-rail-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.bericht-rail-dot.is-new, .bericht-rail-dot.is-status { background: var(--band-neu); }
.bericht-rail-dot.is-progress { background: var(--band-fortschritt); }
.bericht-rail-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Keine Breitenbegrenzung: Die Hauptspalte nutzt die ganze Bildschirmbreite
neben der Leiste (Eriks Wunsch). Früher hielt max-width: 640px sie als
schmale Lesespalte — auf breiten Monitoren blieb dabei die halbe Fläche
leer. */
.bericht-main { padding: 28px 36px 32px; min-width: 0; }
.bericht-head-line { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.bericht-head-nr {
font-family: ui-monospace, 'SF Mono', monospace; font-size: 12px;
color: var(--text-subtle);
}
.bericht-change-pill {
font-size: 11px; font-weight: 600; color: #fff; padding: 2px 8px; border-radius: 6px;
}
.bericht-change-pill.is-new, .bericht-change-pill.is-status { background: var(--band-neu); }
.bericht-change-pill.is-progress { background: var(--band-fortschritt); }
.bericht-h1 { font-size: 19px; font-weight: 500; letter-spacing: 0; margin: 0 0 4px; }
.bericht-sub { color: var(--text-subtle); font-size: 12px; margin-bottom: 16px; }
.bericht-abwesend-hinweis {
display: inline-flex; align-items: center; gap: 6px;
color: var(--amber); font-size: 12px; margin-bottom: 16px;
}
/* Verlauf der Vorwochen im Wochenbericht (Eriks Wahl "B"): oben die
schmale Zeile "Zuletzt KW …" (renderBerichtZuletzt()), unter der
Bearbeitungsmaske die Zeitleiste über die volle Breite
(renderBerichtVerlauf(), js/wochenbericht/wochenbericht.js) — dieselbe
Bildsprache wie der Verlauf im Projekt-Dashboard (.plan-verlauf-*). */
.bericht-zuletzt {
background: var(--surface-sunk); border-radius: var(--r-md);
padding: 8px 12px; margin-bottom: 12px; font-size: 13px; line-height: 1.5; color: var(--text-muted);
}
.bericht-zuletzt-kw { display: block; font-size: 11px; font-weight: 500; color: var(--text); margin-bottom: 2px; }
/* Lange Kommentare nicht endlos: höchstens fünf Zeilen, der Rest steht im Verlauf. */
.bericht-zuletzt-text { max-height: calc(1.5em * 5); overflow: hidden; overflow-wrap: anywhere; }
.bericht-zuletzt-text p { margin: 0; }
.bericht-verlauf { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--line-soft); }
.bv-kopf { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 6px; }
.bv-titel { font-size: 15px; font-weight: 500; margin: 0; color: var(--text); }
.bv-anzahl { font-size: 12px; color: var(--text-subtle); }
.bv-mehr, .bv-uebernehmen {
border: 0; background: transparent; font: inherit; font-size: 12px; font-weight: 500;
color: var(--accent); cursor: pointer; padding: 4px 8px; border-radius: var(--r-md);
}
.bv-mehr:hover, .bv-uebernehmen:hover { background: var(--surface-alt); }
.bv-liste { list-style: none; margin: 0; padding: 0; }
.bv-zeile {
display: grid; grid-template-columns: 120px 14px minmax(0, 1fr) minmax(0, 220px) auto;
column-gap: 12px; align-items: start; padding: 12px 0;
}
.bv-kw { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.bv-kw b { font-size: 13px; color: var(--text); }
.bv-kw span { font-size: 12px; color: var(--text-subtle); }
/* Punkt und Linie: die Linie läuft von Punkt zu Punkt über den Zeilenabstand */
.bv-punkt { position: relative; align-self: stretch; }
.bv-punkt::before {
content: ""; position: absolute; left: 1px; top: 4px; width: 12px; height: 12px;
border-radius: 50%; box-sizing: border-box; border: 2px solid var(--accent); background: var(--surface); z-index: 1;
}
.bv-zeile.ist-juengster .bv-punkt::before { background: var(--accent); }
.bv-punkt::after {
content: ""; position: absolute; left: 6px; top: 16px; bottom: -14px; width: 2px; background: var(--line);
}
.bv-zeile:last-child .bv-punkt::after { display: none; }
.bv-inhalt { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.bv-text { font-size: 13px; line-height: 1.5; color: var(--text); overflow-wrap: anywhere; }
.bv-text p { margin: 0; }
.bv-ruhe { font-size: 12px; color: var(--text-subtle); }
.bv-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.bv-chip {
font-size: 11px; font-weight: 500; padding: 2px 8px; border-radius: 6px; white-space: nowrap;
background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent);
}
.bv-uebernehmen { justify-self: end; }
@media (max-width: 1100px) {
.bv-zeile { grid-template-columns: 76px 14px minmax(0, 1fr); row-gap: 6px; }
.bv-chips, .bv-uebernehmen { grid-column: 3; }
.bv-uebernehmen { justify-self: start; padding-left: 0; }
.bv-chips:empty { display: none; }
}
.bericht-detail-fuss { display: flex; align-items: center; gap: 12px; margin-top: 20px; }
.bericht-fuss-spacer { flex: 1; }
.person-chip-label { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
.protokoll-text {
width: 100%; font-family: ui-monospace, 'SF Mono', monospace;
font-size: 12px; line-height: 1.5; resize: vertical;
}
/* === Projektbereich: Leistungsstand & Abrechnung ===========================
Karte im Dashboard des Projektbereichs (renderPlanLeistung() in
js/plan/plan-leistung.js): erbrachter Leistungswert aus Honorar, HOAI-Anteil
je Leistungsphase und Fortschritt der Phase. Ausschliesslich Design-Tokens,
damit die Karte in hellem und dunklem Thema ohne eigene Dark-Regeln traegt.
Die Tabelle nutzt .lp-matrix als Grundlage; .num/.strong sind hier eigene
Regeln, weil die gleichnamigen des Honorar-Dialogs auf .honorar-table
eingeschraenkt sind (plan-styles.css). */
.leistung-kpis {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
}
.leistung-kpi {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1 1 160px;
min-width: 150px;
padding: 12px 12px;
background: var(--surface-alt);
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
}
.leistung-kpi-label {
font-size: 11px;
font-weight: 500;
color: var(--text-muted);
letter-spacing: 0;
}
.leistung-kpi-value {
font-size: 19px;
font-weight: 500;
font-variant-numeric: tabular-nums;
line-height: 1.2;
}
.leistung-kpi-value.accent { color: var(--accent); }
.leistung-kpi-sub {
font-size: 12px;
color: var(--text-subtle);
font-variant-numeric: tabular-nums;
}
/* Fortschrittsbalken: derselbe Prozentwert wie die Kennzahl darueber, nur als
Flaeche — bei zehn Leistungsphasen ist die Gesamtlage sonst nur aus der
Tabelle zu erschliessen. */
.leistung-balken {
height: 7px;
border-radius: var(--r-pill);
background: var(--surface-sunk);
overflow: hidden;
margin: 0 0 12px;
}
.leistung-balken > span {
display: block;
height: 100%;
background: var(--accent);
border-radius: var(--r-pill);
transition: width .25s var(--ease);
}
.leistung-table { font-size: 13px; }
.leistung-table th.num,
.leistung-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.leistung-table td.strong { font-weight: 500; }
.leistung-table tfoot th,
.leistung-table tfoot td {
border-top: 1px solid var(--line);
padding: 6px 8px;
font-weight: 500;
background: var(--surface-alt);
}
/* Hinweis, wenn die Rechnung nicht moeglich ist (kein Honorar, keine
beauftragte Phase, keine Anteile). Bewusst als Text und nicht als "0 EUR":
unbekannt ist nicht dasselbe wie nichts erbracht. */
.leistung-leer {
font-size: 13px;
color: var(--text-muted);
padding: 12px 12px;
background: var(--surface-alt);
border-left: 2px solid var(--amber);
border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
/* === Auftragsstatus: Kennzeichen an der Projektzeile und Auswahlfeld in den
Pflegemasken (js/auftrag.js) ===
Die Farbflächen entstehen mit color-mix aus den Ampel-Tokens, damit sie im
hellen und im dunklen Erscheinungsbild von selbst mitwandern (feste rgba-
Flächen tragen im Dark Mode nicht, siehe --attend-bg). Die Schriftfarbe ist
dasselbe Token — es dreht je Erscheinungsbild ins jeweils kontrastreichere
Ende. */
.auftrag-chip {
display: inline-flex;
align-items: center;
border-radius: 6px;
padding: 1px 8px;
font-size: 11px;
font-weight: 500;
white-space: nowrap;
background: var(--surface-sunk);
border: 1px solid var(--line-soft);
color: var(--text-muted);
}
/* Anfrage: hereinkommend, noch nichts entschieden. */
.auftrag-chip-anfrage {
background: color-mix(in srgb, var(--accent) 12%, transparent);
border-color: color-mix(in srgb, var(--accent) 30%, transparent);
color: var(--accent);
}
/* Angebot: draußen, Entscheidung offen — dieselbe Warnfarbe wie die Ampel. */
.auftrag-chip-angebot {
background: color-mix(in srgb, var(--amber) 12%, transparent);
border-color: color-mix(in srgb, var(--amber) 30%, transparent);
color: var(--amber);
}
/* Beauftragt: der Regelfall. Wird an der Projektzeile normalerweise gar nicht
gezeigt (auftragsstatusChip() gibt dafür einen leeren String zurück), nur in
Masken, die den Status ausdrücklich ausweisen. */
.auftrag-chip-beauftragt {
background: color-mix(in srgb, var(--green) 12%, transparent);
border-color: color-mix(in srgb, var(--green) 30%, transparent);
color: var(--green);
}
/* Abgeschlossen: erledigt, tritt bewusst zurück. */
.auftrag-chip-abgeschlossen {
background: var(--surface-sunk);
border-color: var(--line);
color: var(--text-subtle);
}
/* Status-Chip einer Sitzung in der Admin-Liste "Termine & Sitzungen"
(renderAdminSitzungen(), js/admin/admin.js) — dasselbe Muster wie
.auftrag-chip: eine neutrale Grundform, drei eingefärbte Zustände. */
.sitzung-chip {
display: inline-flex;
align-items: center;
border-radius: 6px;
padding: 1px 8px;
font-size: 11px;
font-weight: 500;
white-space: nowrap;
background: var(--surface-sunk);
border: 1px solid var(--line-soft);
color: var(--text-muted);
}
.sitzung-chip.ist-geoeffnet {
background: color-mix(in srgb, var(--green) 12%, transparent);
border-color: color-mix(in srgb, var(--green) 30%, transparent);
color: var(--green);
}
.sitzung-chip.ist-entsperrt {
background: color-mix(in srgb, var(--accent) 12%, transparent);
border-color: color-mix(in srgb, var(--accent) 30%, transparent);
color: var(--accent);
}
.sitzung-chip.ist-gesperrt {
background: color-mix(in srgb, var(--amber) 12%, transparent);
border-color: color-mix(in srgb, var(--amber) 30%, transparent);
color: var(--amber);
}
/* .auftrag-select steht nur noch in den Projekteinstellungen und sieht dort
aus wie jedes andere Auswahlfeld (Gruppe direkt darunter): Größe, Rahmen
und Pfeil kommen aus der allgemeinen select-Regel. Die frühere kompakte
Fassung setzte `background:` als Kurzform und löschte damit den Pfeil —
das Feld wirkte wie ein loses Etikett statt wie eine Auswahl. */
.auftrag-select { width: 100%; }
/* ===========================================================
Ein Ort je Angabe — Anzeige statt Eingabe
===========================================================
Kommentar, Fortschritt und Zuordnung werden nur noch im Wochenbericht
erfasst. Die Projektliste und der Projektbereich zeigen dieselben Werte in
derselben Anordnung, aber ohne Eingabefelder (Klasse is-ansicht) — daneben
steht der Weg zur Bearbeitung (.drawer-verweis/.plan-verweis). */
/* Der Kommentar als Text: dieselbe Fläche wie der Editor, nur ruhiger. */
.status-edit.is-ansicht{
cursor: default;
background: var(--surface-sunk);
border-style: dashed;
}
.board-lph-cell.is-ansicht{ cursor: default; }
/* Ein Kürzel, das nicht angeklickt werden kann, zeigt das auch. */
.board-avatar.is-ansicht{ cursor: default; }
/* Der Verweis auf den Erfassungsort. Er steht unter der Karte, in einer
Zeile mit der Begründung, warum hier nichts einzugeben ist. */
.drawer-verweis{
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
padding: 8px 2px 2px;
}
.plan-verweis{
background: none;
border: 1px solid var(--line);
border-radius: 8px;
padding: 4px 12px;
font-size: 12px;
font-weight: 500;
color: var(--accent);
cursor: pointer;
}
.plan-verweis:hover{ background: var(--accent-soft); }
.plan-verweis:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
/* ===========================================================
Projektliste: die Listenoperationen des früheren Projekte-Tabs
=========================================================== */
/* Öffnen/Löschen am Zeilenende. Sichtbar erst beim Überfahren der Zeile,
damit die Liste im Ruhezustand ruhig bleibt — auf Tastatur-Fokus aber
ebenfalls, sonst wären die Knöpfe nicht erreichbar. */
.board-row-actions{
display: flex;
gap: 6px;
flex: 0 0 auto;
opacity: 0;
transition: opacity .12s;
}
.board-row:hover .board-row-actions,
.board-row.open .board-row-actions,
.board-row.is-menu-open .board-row-actions,
.board-row-actions:focus-within{ opacity: 1; }
/* Der Ziehgriff steht vor dem Aufklapp-Pfeil. */
.board-row .drag-handle{
flex: 0 0 auto;
cursor: grab;
color: var(--text-subtle);
display: inline-flex;
align-items: center;
}
.board-row .drag-handle:active{ cursor: grabbing; }
/* Die Gruppe ist eine Ablagefläche: sie behält eine Mindesthöhe, damit eine
leer geräumte Gruppe ein Ziel bleibt. */
.board-list.projects-sortable{ min-height: 34px; }
/* Der Verweis unter einer Anzeige-Karte im Projektbereich. */
.plan-verweis-row{
display: flex;
justify-content: flex-end;
padding-top: 12px;
}
.plan-verweis-row:empty{ display: none; }
/* Die Schlagwörter-Liste in den Projekteinstellungen darf umbrechen. */
#settingsProjTags{
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
}
#settingsProjTags .tag-input{ width: 110px; }
/* === Dashboard: Termine meiner Projekte ===
Die Aufgaben und Meilensteine der Projekte, denen man zugeordnet ist
(GET /my-dashboard, Feld projectDates). Bewusst eine Liste und keine
Kartenwand wie "Meine Aufgaben": Hier geht es um Reihenfolge in der Zeit,
und Termine, die untereinander stehen, lassen sich der Reihe nach
abarbeiten. */
/* Die Gruppen sitzen in derselben .dash-karte wie die eigenen Aufgaben
darüber — beides ist der eigene Arbeitsstrom und soll gleich aussehen.
Die Titelzeile ist deshalb ein abgesetzter Streifen innerhalb der Karte
und kein freistehender Abschnittstitel mehr. */
.fristen-gruppe + .fristen-gruppe { border-top: 1px solid var(--line); }
.fristen-gruppe-titel {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px 6px;
background: var(--surface-alt);
font-size: 11px;
font-weight: 500;
letter-spacing: 0;
color: var(--text-muted);
}
.fristen-gruppe-titel span {
font-size: 11px;
padding: 1px 8px;
border-radius: 6px;
background: var(--surface-sunk);
color: var(--text-muted);
letter-spacing: 0;
}
.fristen-gruppe.ist-ueberfaellig .fristen-gruppe-titel { color: var(--red); }
.fristen-gruppe.ist-ueberfaellig .fristen-gruppe-titel span {
background: var(--red);
color: #fff;
}
.fristen-gruppe.ist-bald .fristen-gruppe-titel { color: var(--amber); }
/* Eine Zeile: Datum — Art — Bezeichnung — Projekt — Zuständige.
Die Bezeichnung nimmt den freien Platz, alles andere hat feste Breiten,
damit die Spalten über alle Zeilen fluchten. */
.fristen-zeile {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 16px;
border-bottom: 1px solid var(--line-soft);
text-decoration: none;
color: inherit;
transition: background .12s var(--ease);
}
.fristen-zeile:last-child { border-bottom: none; }
.fristen-zeile:hover { background: var(--surface); }
.fristen-zeile:focus-visible {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.fristen-datum {
flex: 0 0 104px;
display: flex;
flex-direction: column;
line-height: 1.25;
font-variant-numeric: tabular-nums;
}
.fristen-datum b { font-size: 13px; font-weight: 500; }
.fristen-datum em {
font-style: normal;
font-size: 11px;
color: var(--text-subtle);
}
.ist-ueberfaellig .fristen-datum b,
.ist-ueberfaellig .fristen-datum em { color: var(--red); }
.ist-bald .fristen-datum b { color: var(--amber); }
.fristen-art {
flex: 0 0 auto;
font-size: 11px;
font-weight: 500;
padding: 2px 8px;
border-radius: 6px;
background: var(--surface-sunk);
color: var(--text-muted);
white-space: nowrap;
}
.fristen-art.ist-meilenstein {
background: var(--accent-soft);
color: var(--accent);
}
.fristen-titel {
flex: 1 1 auto;
min-width: 0;
font-size: 13px;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fristen-projekt {
flex: 0 0 auto;
max-width: 230px;
font-size: 12px;
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fristen-wer {
flex: 0 0 140px;
font-size: 11px;
color: var(--text-subtle);
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Die Warn-Kachel im Kennzahlenband. Eine Null bleibt neutral — "0
überfällig" ist eine gute Nachricht und darf nicht rot sein (siehe
renderStatTile()). */
.load-num-warnung { color: var(--red); }
@media (max-width: 860px) {
.fristen-projekt, .fristen-wer { display: none; }
.fristen-datum { flex-basis: 92px; }
}
/* Überfälliges Datum in der Aufgabenkarte des Dashboards — dieselbe
Kennzeichnung wie in der Terminliste darunter, damit die Kachel
"Überfällig" und die Listen zusammenpassen. */
.load-detail b.ist-ueberfaellig { color: var(--red); }
/* === Assistent "Neues Projekt" (js/projekt-wizard.js) ===
Ersetzt den früheren Dialog #newProjModal, der alle Felder auf einmal
zeigte. Sechs Schritte, und jeder muss vollständig sein, bevor es
weitergeht — genau die Lücken, die sonst entstehen (kein Honorar, keine
Phase, niemand zugeordnet), sind der Grund für Projekte, die später in
der Auslastung als "nicht bewertbar" erscheinen. */
.wizard-modal {
max-width: 820px;
width: 100%;
padding: 0;
overflow: hidden;
}
.wiz-kopf {
display: flex;
align-items: center;
gap: 12px;
padding: 20px 24px 16px;
border-bottom: 1px solid var(--line-soft);
}
.wiz-kopf h2 { margin: 0; flex: 1; }
.wiz-schliessen {
font-size: 19px;
line-height: 1;
padding: 2px 8px;
background: none;
border: none;
color: var(--text-subtle);
cursor: pointer;
}
.wiz-schliessen:hover { color: var(--text); background: none; }
/* Schrittleiste: erledigte Stationen tragen einen Haken, damit auch bei
sechs Schritten jederzeit erkennbar ist, was noch aussteht. */
.wiz-leiste {
display: flex;
align-items: center;
flex-wrap: wrap;
padding: 16px 24px 0;
}
.wiz-stufe {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--text-subtle);
}
.wiz-stufe b {
width: 24px; height: 24px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 11px;
font-weight: 500;
background: var(--surface-sunk);
color: var(--text-muted);
font-variant-numeric: tabular-nums;
}
.wiz-stufe.ist-aktiv { color: var(--text); font-weight: 500; }
.wiz-stufe.ist-aktiv b { background: var(--accent); color: #fff; }
.wiz-stufe.ist-fertig b { background: var(--green); color: #fff; }
.wiz-strich {
flex: 1 1 16px;
min-width: 10px;
height: 1px;
background: var(--line);
margin: 0 8px;
}
.wiz-inhalt {
padding: 20px 24px 24px;
min-height: 300px;
max-height: 58vh;
overflow-y: auto;
}
.wiz-titel { margin: 0 0 4px; font-size: 19px; font-weight: 500; letter-spacing: 0; }
.wiz-unter { margin: 0 0 20px; font-size: 13px; color: var(--text-muted); }
.wiz-zeile { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.wiz-pflicht { color: var(--red); }
/* Fußzeile: Der Weiter-Knopf bleibt gesperrt, solange etwas fehlt — und
daneben steht, WAS fehlt. Ein gesperrter Knopf ohne Begründung ist eine
Sackgasse. */
.wiz-fuss {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 24px;
border-top: 1px solid var(--line-soft);
background: var(--surface-alt);
}
.wiz-zaehler { font-size: 12px; color: var(--text-subtle); }
.wiz-fuss-rechts {
margin-left: auto;
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
justify-content: flex-end;
}
.wiz-fehlt { font-size: 12px; color: var(--amber); text-align: right; }
/* Auswahl-Chips (Gewerke, Phasen, Schlagwörter, Grundlage) */
.wiz-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.wiz-chip {
font: inherit;
font-size: 12px;
padding: 4px 12px;
cursor: pointer;
border: 1px solid var(--line);
border-radius: 6px;
background: var(--surface);
color: var(--text-muted);
}
.wiz-chip:hover { border-color: var(--accent); background: var(--surface); }
.wiz-chip[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #fff; }
.wiz-chip-gewerk { font-weight: 500; letter-spacing: .02em; padding: 6px 16px; }
/* Beteiligte je Rolle */
.wiz-rollen { display: flex; flex-direction: column; gap: 12px; }
.wiz-rolle { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.wiz-rolle-name {
flex: 0 0 116px;
font-size: 12px;
font-weight: 500;
padding-top: 8px;
color: var(--text-muted);
}
.wiz-person {
font: inherit;
font-size: 11px;
font-weight: 500;
cursor: pointer;
width: 34px; height: 34px;
border-radius: 50%;
border: 1px solid var(--line);
background: var(--surface);
color: var(--text-muted);
padding: 0;
}
.wiz-person[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #fff; }
.wiz-person[data-rolle="Projektleitung"][aria-pressed="true"] { background: var(--red); border-color: var(--red); }
.wiz-person[data-rolle="Bauüberwachung"][aria-pressed="true"] { background: var(--green); border-color: var(--green); }
/* Wer aus einem anderen Gewerk dazugeholt wurde: gestrichelter Rand, damit
die Herkunft sichtbar bleibt, ohne eine zweite Farbe einzuführen. */
.wiz-person.ist-fremd { border-style: dashed; border-width: 1.5px; }
.wiz-fremd { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line-soft); }
.wiz-fremd label {
display: block;
font-size: 12px;
font-weight: 500;
margin-bottom: 6px;
color: var(--text-muted);
}
.wiz-fremd select { width: 100%; max-width: 420px; }
.wiz-fremd-liste { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.wiz-fremd-chip {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11px;
padding: 4px 6px 4px 12px;
border-radius: 6px;
border: 1px dashed var(--line);
color: var(--text-muted);
}
.wiz-fremd-chip button {
font: inherit;
font-size: 13px;
line-height: 1;
cursor: pointer;
border: none;
background: none;
color: var(--text-subtle);
padding: 0 2px;
}
.wiz-fremd-chip button:hover { color: var(--red); background: none; }
/* Hinweis anstelle eines Schritts, der gerade nicht möglich ist */
.wiz-sperre {
padding: 20px 20px;
border: 1px dashed var(--line);
border-radius: var(--r-lg);
background: var(--surface-alt);
}
.wiz-sperre b { display: block; font-size: 13px; margin-bottom: 6px; }
.wiz-sperre p { margin: 0 0 8px; font-size: 13px; color: var(--text-muted); max-width: 58ch; }
.wiz-sperre p:last-child { margin-bottom: 0; }
.wiz-schalter { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
.wiz-schalter input { width: 16px; height: 16px; accent-color: var(--accent); }
/* Terminplan-Tabelle */
.wiz-plan {
margin-top: 16px;
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
overflow: hidden;
}
.wiz-plan-zeile {
display: grid;
grid-template-columns: 78px 62px 1fr 64px 170px;
gap: 8px;
padding: 8px 12px;
font-size: 13px;
border-bottom: 1px solid var(--line-soft);
font-variant-numeric: tabular-nums;
}
.wiz-plan-zeile:last-child { border-bottom: none; }
.wiz-plan-zeile span:nth-child(2),
.wiz-plan-zeile span:nth-child(3),
.wiz-plan-zeile span:nth-child(4) { text-align: right; }
.wiz-plan-kopf {
background: var(--surface-alt);
font-size: 11px;
font-weight: 500;
letter-spacing: 0;
color: var(--text-subtle);
}
.wiz-plan-summe { background: var(--surface-alt); font-weight: 500; }
/* Zusammenfassung */
.wiz-pruef { font-size: 13px; }
.wiz-pruef-zeile {
display: flex;
justify-content: space-between;
gap: 16px;
padding: 8px 0;
border-bottom: 1px solid var(--line-soft);
}
.wiz-pruef-zeile:last-child { border-bottom: none; }
.wiz-pruef-zeile span { color: var(--text-muted); flex: 0 0 auto; }
.wiz-pruef-zeile b { font-weight: 500; text-align: right; min-width: 0; overflow-wrap: anywhere; }
.wiz-pruef-zeile b.fehlt { color: var(--text-subtle); font-weight: 400; font-style: normal; }
/* Abschluss */
.wiz-fertig { text-align: center; padding: 44px 24px; }
.wiz-haken {
width: 52px; height: 52px;
border-radius: 50%;
margin: 0 auto 16px;
display: grid;
place-items: center;
font-size: 26px;
background: var(--green);
color: #fff;
}
.wiz-fertig h3 { margin: 0 0 8px; font-size: 19px; font-weight: 500; }
.wiz-fertig p { margin: 0 auto 20px; color: var(--text-muted); max-width: 46ch; font-size: 13px; }
@media (max-width: 760px) {
.wiz-zeile { grid-template-columns: 1fr; }
.wiz-stufe span { display: none; }
.wiz-plan-zeile { grid-template-columns: 66px 1fr 130px; }
.wiz-plan-zeile span:nth-child(2),
.wiz-plan-zeile span:nth-child(4) { display: none; }
}
/* ============================================================
BEWEGUNG - Stufe "Spürbar"
============================================================
Von Erik aus einem Dreier-Vergleich gewählt (dezent / spürbar /
lebendig). Die Werte stehen als Tokens oben in :root; hier wird
nur angewendet.
Bewusst NICHT enthalten: Eintritts-Animationen für Listen und
aufziehende Balken (das war Stufe 3). Die Oberfläche bewegt sich
ausschliesslich als Antwort auf Zeigen oder Klicken, nie von
selbst - bei einer Liste mit dreissig Projekten, die mehrmals
täglich neu gerendert wird, wäre alles andere Unruhe.
Die Wandtafel (body.wandtafel-aktiv) bleibt bewusst aussen vor:
ein Touch-Monitor kennt kein Hover, und die Tafel trägt ohnehin
ihre eigene Gestaltung (--dash-*).
============================================================ */
/* --- 1. Anklickbare Zeilen: anheben + Akzentleiste ---------------
Die Leiste entsteht als inset-Schatten, NICHT als ::before mit
position:absolute. Grund: .board-row ist draggable (Drag-and-Drop
der Projektliste, js/projects/projects.js). Ein zusätzliches
positioniertes Element bzw. ein Stapelkontext auf der Zeile kann
das Ziehbild und die Trefferflächen verschieben; ein inset-Schatten
kann das nicht, weil er weder Layout noch Stapelkontext anfasst.
Der Schatten wächst von 0 auf 3px, wirkt also wie eine von links
einfahrende Leiste. */
.board-row-main,
.bericht-rail-item,
.planung-row,
.dash-rand-zeile {
transition: background var(--dur) var(--ease-out),
box-shadow var(--dur) var(--ease-out),
transform var(--dur) var(--ease-out);
}
.board-row-main:hover,
.bericht-rail-item:hover,
.planung-row:hover,
.dash-rand-zeile:hover {
transform: translateY(var(--row-lift));
box-shadow: inset 3px 0 0 0 var(--accent);
}
/* Beim Drücken wieder absetzen: die Zeile geht unter den Finger
zurück, statt angehoben zu bleiben. */
.board-row-main:active,
.bericht-rail-item:active,
.planung-row:active,
.dash-rand-zeile:active {
transform: translateY(0);
transition-duration: 60ms;
}
/* Eine bereits ausgewählte Zeile trägt die Leiste dauerhaft - sonst
verschwände beim Wegfahren der Maus die einzige Markierung. */
.bericht-rail-item.is-aktiv { box-shadow: inset 3px 0 0 0 var(--accent); }
/* --- 2. Druckpunkt für alles Klickbare -------------------------
button:active trug schon scale(.97); hier kommen die übrigen
Bedienelemente auf denselben Wert, damit sich nicht das eine
Element gedrückt anfühlt und das Nachbarelement nicht. */
.board-avatar,
.person-chip,
.nav-item,
.attendance-tile,
.plan-verweis,
.tag-chip .tag-remove,
.richtext-btn,
.pc-btn {
transition: background var(--dur) var(--ease-out),
border-color var(--dur) var(--ease-out),
color var(--dur) var(--ease-out),
transform var(--dur) var(--ease-out);
}
.board-avatar:not(.is-ansicht):active,
.person-chip:active,
.nav-item:active,
.attendance-tile:active,
.plan-verweis:active,
.richtext-btn:active,
.pc-btn:active { transform: scale(var(--press)); }
/* --- 3. Dialoge: auftauchen statt aufblitzen -------------------
.modal-bg wird per JS von display:none auf flex geschaltet. Ein
display-Wechsel ist nicht übergangsfähig, eine ANIMATION dagegen
startet neu, sobald das Element wieder gerendert wird - deshalb
hier animation statt transition. Die Fläche blendet auf, der
Dialog kommt dabei minimal aus der Tiefe. */
.modal-bg { animation: modal-bg-auf var(--dur-lg) var(--ease-out); }
.modal-bg > .modal { animation: modal-auf var(--dur-lg) var(--ease-out); }
@keyframes modal-bg-auf { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-auf {
from { opacity: 0; transform: translateY(6px) scale(.985); }
to { opacity: 1; transform: none; }
}
/* --- 4. Tastatur-Fokus sichtbar halten ------------------------
Die Hebe-/Druck-Effekte oben reagieren auf Maus und Finger. Wer
mit der Tastatur arbeitet, braucht eine eigene, deutliche Marke;
ohne sie wäre die Oberfläche nach diesem Umbau für Tastatur-
Bedienung schlechter lesbar als vorher. */
.board-row-main:focus-visible,
.bericht-rail-item:focus-visible,
.planung-row:focus-visible,
.person-chip:focus-visible,
.board-avatar:focus-visible,
.nav-item:focus-visible {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
/* --- 5. Bewegung abbestellt ------------------------------------
Pflichtteil, in dieser Datei bisher gar nicht vorhanden. Weil die
Stufe an Variablen hängt, genügt es, sie zu kippen: ohne Hub und
ohne Stauchung bleibt reine Farb-Rückmeldung übrig. Die
transition-/animation-Zeilen fangen zusätzlich die rund sechzig
älteren Regeln ab, die ihre Dauer noch fest eingetragen haben. */
@media (prefers-reduced-motion: reduce) {
:root { --row-lift: 0px; --press: 1; --dur: 1ms; --dur-lg: 1ms; }
*, *::before, *::after {
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
transition-duration: 1ms !important;
scroll-behavior: auto !important;
}
}
/* =========================================================================
LOGIN-SEITE (#loginOverlay, Design "Kalenderwoche" — Eriks Wahl B)
Links Logo und Formular, rechts die laufende Kalenderwoche als große Zahl
mit ihren Arbeitstagen (renderLoginWoche(), js/auth.js). Farben über die
App-Tokens; nur die Zahl hat eigene Tokens (Logo-Hellblau im hellen,
gedämpft im dunklen Thema). Liegt über allem (z-index 400 > .modal-bg 300).
Das Logo ist ein blasses Wasserzeichen über die ganze Seite
(.login-wasserzeichen, Variante E), kein eigenes kleines Logo.
========================================================================= */
.login-seite {
--login-zahl: #94b0cc;
--login-jahr: #116896;
--login-wz: .11;
position: fixed; inset: 0; z-index: 400;
display: none; overflow-y: auto; overflow-x: hidden;
background: var(--bg); color: var(--text);
padding: 0 20px;
}
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) .login-seite { --login-zahl: #4f7695; --login-jahr: var(--accent); --login-wz: .07; }
}
html[data-theme="dark"] .login-seite { --login-zahl: #4f7695; --login-jahr: var(--accent); --login-wz: .07; }
/* Wasserzeichen: ganzes Zeichen sichtbar, an den Rändern angeschnitten. Die
Felder und Tageskacheln haben eigene Flächen und bleiben davor lesbar.
Der Rahmen ist genau so groß wie das Fenster und schneidet alles
Überstehende ab — sonst ragte das Logo über den Rand und machte die
Seite scrollbar. */
.login-wz-rahmen { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.login-wasserzeichen {
position: absolute; z-index: 0; left: 50%; top: 50%;
width: min(1500px, 98vw); height: auto; transform: translate(-44%, -47%);
opacity: var(--login-wz); filter: blur(1px);
pointer-events: none; user-select: none;
}
.login-raster {
position: relative; z-index: 1;
width: 100%; max-width: 1180px; min-height: 100%; margin: 0 auto;
display: grid; grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
gap: clamp(32px, 7vw, 108px); align-items: center;
padding-block: calc(48px + env(safe-area-inset-top, 0px)) calc(48px + env(safe-area-inset-bottom, 0px));
}
.login-titel { margin: 0 0 6px; font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-weight: 500; font-size: 30px; letter-spacing: -.01em; }
.login-unter { margin: 0 0 28px; color: var(--text-muted); }
.login-form { display: grid; gap: 16px; }
.login-feld { display: grid; gap: 8px; }
.login-feld label { font-size: 13.5px; font-weight: 500; color: var(--text); }
.login-feld input {
width: 100%; box-sizing: border-box; min-height: 46px; padding: 12px 12px;
font: inherit; font-size: 15px; color: var(--text);
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.login-feld input::placeholder { color: var(--text-subtle); opacity: 1; }
.login-feld input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
.login-feld.hat-fehler input { border-color: var(--red); }
.login-pw { position: relative; }
.login-pw input { padding-right: 96px; }
.login-pw-toggle {
position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
font: inherit; font-size: 12.5px; font-weight: 500; color: var(--accent);
background: transparent; border: 0; border-radius: var(--r-md); padding: 8px 12px; cursor: pointer;
}
.login-pw-toggle:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.login-pw-toggle:focus-visible, .login-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.login-caps { margin: 0; font-size: 12.5px; font-weight: 500; color: var(--amber); }
.login-fehler {
margin: 0; padding: 12px 12px; border-radius: var(--r-lg); font-size: 13.5px; line-height: 1.45;
color: var(--red); background: color-mix(in srgb, var(--red) 10%, transparent);
}
.login-btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
width: 100%; min-height: 48px; margin-top: 4px;
font: inherit; font-size: 15px; font-weight: 650; color: #fff;
background: var(--accent); border: 0; border-radius: var(--r-lg); cursor: pointer;
transition: filter var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .login-btn { color: #0b1c26; } }
html[data-theme="dark"] .login-btn { color: #0b1c26; }
.login-btn:hover { filter: brightness(1.07); }
.login-btn:active { transform: scale(var(--press)); }
.login-btn:disabled { cursor: progress; filter: saturate(.7); }
.login-spinner {
display: none; width: 15px; height: 15px; border-radius: 50%;
border: 2px solid color-mix(in srgb, currentColor 35%, transparent); border-top-color: currentColor;
animation: login-dreh .7s linear infinite;
}
.login-btn.laedt .login-spinner { display: block; }
@keyframes login-dreh { to { transform: rotate(360deg); } }
.login-hilfe { margin: 16px 0 0; font-size: 13px; color: var(--text-muted); }
.login-woche { justify-self: end; text-align: right; animation: login-auf .7s var(--ease-out) both; }
@keyframes login-auf { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.login-kw-label, .login-kw-jahr, .login-kw-zahl, .login-tage b { font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
.login-kw-label { display: block; font-weight: 500; font-size: 22px; color: var(--text-muted); letter-spacing: .02em; }
.login-kw-zahl {
display: block; font-weight: 300; font-size: clamp(150px, 21vw, 300px); line-height: .86;
letter-spacing: 0; color: var(--login-zahl); font-variant-numeric: tabular-nums;
}
.login-kw-jahr { display: block; font-weight: 500; font-size: 20px; color: var(--login-jahr); }
.login-tage { list-style: none; margin: 28px 0 0; padding: 0; display: flex; gap: 8px; justify-content: flex-end; }
.login-tage li {
display: grid; justify-items: center; gap: 2px; width: 58px; padding: 12px 0 8px;
border-radius: var(--r-lg); background: var(--surface); border: 1px solid var(--line);
}
.login-tage li span { font-size: 12px; font-weight: 500; color: var(--text-muted); }
.login-tage li b { font-weight: 500; font-size: 20px; font-variant-numeric: tabular-nums; }
.login-tage li.ist-heute { background: var(--accent); border-color: var(--accent); }
.login-tage li.ist-heute span, .login-tage li.ist-heute b { color: #fff; }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .login-tage li.ist-heute span, html:not([data-theme="light"]) .login-tage li.ist-heute b { color: #0b1c26; } }
html[data-theme="dark"] .login-tage li.ist-heute span, html[data-theme="dark"] .login-tage li.ist-heute b { color: #0b1c26; }
.login-datum { margin: 16px 0 0; color: var(--text-muted); font-size: 15px; }
@media (max-width: 880px) {
.login-raster { grid-template-columns: 1fr; gap: 28px; padding-block: calc(28px + env(safe-area-inset-top, 0px)) 48px; align-content: start; }
.login-woche { order: -1; justify-self: start; text-align: left; }
.login-kw-zahl { font-size: 110px; }
.login-tage { justify-content: flex-start; }
.login-tage li { width: auto; flex: 1; max-width: 58px; }
.login-wasserzeichen { width: 260vw; top: 0; transform: translate(-30%, -10%); }
}
@media (prefers-reduced-motion: reduce) { .login-woche { animation: none; } .login-spinner { animation: none; } }
/* ---------- Dashboard: Meine Projekte + Monatskalender (Entwurf B) ---------- */
.dash-zonen { grid-template-columns: minmax(0, 1fr) 340px; }
.dash-proj { margin-bottom: 8px; }
.dash-proj-kopf { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 12px 16px 4px; }
.dash-proj-kopf { align-items: center; padding-bottom: 12px; }
.dash-proj.ist-zu .dash-proj-kopf { padding-top: 12px; }
.dash-proj-toggle { flex: 1 1 260px; min-width: 0; display: flex; align-items: baseline; gap: 0; padding: 0; border: 0; background: none; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.dash-proj-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm); }
.dash-proj-titel { font-weight: 500; font-size: 13px; }
/* Die allgemeine Knopfregel (button:hover → Akzentfläche, :active →
Stauchen) ist spezifischer als eine einzelne Klasse und färbte den
Projektkopf beim Zeigen blau ein — der Titel verschwand darin. Diese
Knöpfe sind Textschalter, keine Aktionsknöpfe: Sie bekommen eine eigene,
ruhige Rückmeldung (Kopfzeile hellt auf, Pfeil und Titel in Akzentfarbe). */
button.dash-proj-toggle, button.dash-proj-toggle:hover, button.dash-proj-toggle:active,
button.dash-proj-alle, button.dash-proj-alle:hover, button.dash-proj-alle:active {
background: none; transform: none; box-shadow: none;
}
button.dash-kal-nav:hover { background: var(--surface-sunk); }
button.dash-kal-nav:active, button.dash-kal-tag:active { transform: none; }
.dash-proj-kopf, .dash-proj-sub { transition: background var(--dur, 150ms) var(--ease-out, ease); }
.dash-proj:has(.dash-proj-toggle:hover) .dash-proj-kopf,
.dash-proj:has(.dash-proj-toggle:hover) .dash-proj-sub { background: color-mix(in srgb, var(--accent) 4%, var(--surface)); }
.dash-proj-toggle:hover .dash-proj-titel,
.dash-proj-toggle:hover .dash-proj-chev { color: var(--accent); }
button.dash-proj-alle:hover { text-decoration: underline; }
.dash-proj-chev { display: inline-block; width: 16px; margin-right: 6px; font-size: 19px; line-height: 1; text-align: center; color: var(--text-subtle); transform: rotate(90deg); transition: transform var(--dur, 150ms) var(--ease-out, ease); }
.dash-proj.ist-zu .dash-proj-chev { transform: rotate(0deg); }
.dash-proj.ist-zu .dash-proj-inhalt { display: none; }
.dash-proj-kurz { display: none; gap: 6px; }
.dash-proj.ist-zu .dash-proj-kurz { display: flex; }
.dash-proj-zahl { font-size: 11px; padding: 1px 8px; border-radius: 6px; background: var(--surface-sunk); color: var(--text-muted); white-space: nowrap; }
.dash-proj-zahl.ist-rot { background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--red); font-weight: 500; }
/* Offene Aufgaben im Akzentblau (Eriks Wunsch); „0 offen“ bleibt grau. */
.dash-proj-zahl.ist-offen { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); font-weight: 500; }
.dash-proj-oeffnen { font-size: 12px; color: var(--accent); text-decoration: none; }
.dash-proj-oeffnen:hover { text-decoration: underline; }
.dash-proj-alle { margin-left: auto; padding: 2px 4px; border: 0; background: none; color: var(--accent); font: inherit; font-size: 12px; cursor: pointer; }
@media (prefers-reduced-motion: reduce) { .dash-proj-chev { transition: none; } }
.dash-proj-nr { font-family: ui-monospace, 'SF Mono', monospace; font-size: 11px; color: var(--text-subtle); font-weight: 500; margin-right: 8px; }
.dash-rollen { display: flex; gap: 4px; flex-wrap: wrap; }
.dash-rolle { font-size: 11px; font-weight: 500; padding: 1px 8px; border-radius: 6px; background: var(--accent-soft); color: var(--accent); }
.dash-rolle.ist-neben { background: transparent; border: 1px dashed var(--line); color: var(--text-muted); }
.dash-proj-sub { padding: 2px 16px 8px; }
.dash-proj-phase { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-muted); max-width: 320px; }
.dash-proj-phase .dash-auf-bar i { background: var(--green); }
.dash-proj-phase.is-leer { color: var(--text-subtle); }
.dash-proj-termine { border-top: 1px solid var(--line-soft); }
.dash-proj-mehr { display: block; padding: 8px 16px; font-size: 12px; color: var(--accent); text-decoration: none; border-top: 1px solid var(--line-soft); }
.dash-termin { display: flex; align-items: center; gap: 8px; padding: 8px 16px; border-bottom: 1px solid var(--line-soft); color: inherit; text-decoration: none; }
.dash-termin:last-child { border-bottom: none; }
a.dash-termin:hover { background: var(--surface-sunk); }
.dash-termin-art { flex: 0 0 22px; height: 22px; border-radius: 6px; display: grid; place-items: center; font-size: 11px; font-weight: 600; background: var(--surface-sunk); color: var(--text-muted); }
.dash-termin-art.ist-meins { background: var(--accent-soft); color: var(--accent); }
.dash-termin-art.ist-ms { background: color-mix(in srgb, var(--meilenstein) 14%, transparent); color: var(--meilenstein); }
.dash-termin-text { flex: 1; min-width: 0; }
.dash-termin-titel { display: block; font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-termin-meta { display: block; font-size: 11px; color: var(--text-subtle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-termin-datum { flex: 0 0 auto; text-align: right; font-size: 12px; font-variant-numeric: tabular-nums; }
.dash-termin-datum em { display: block; font-style: normal; font-size: 11px; color: var(--text-subtle); }
.dash-termin-datum.ist-ueberfaellig, .dash-termin-datum.ist-ueberfaellig em { color: var(--red); }
.dash-termin-datum.ist-bald { color: var(--amber); }
.dash-kal { padding: 12px 16px 12px; }
.dash-kal-kopf { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.dash-kal-kopf b { font-size: 13px; }
.dash-kal-nav { width: 26px; height: 26px; padding: 0; border: 1px solid var(--line); background: var(--surface); color: var(--text); border-radius: var(--r-sm); cursor: pointer; }
.dash-kal-raster { display: grid; grid-template-columns: 26px repeat(7, 1fr); gap: 2px; font-variant-numeric: tabular-nums; }
.dash-kal-wt { font-size: 11px; color: var(--text-subtle); text-align: center; padding-bottom: 4px; }
.dash-kal-kw { font-size: 11px; color: var(--text-subtle); display: grid; place-items: center; }
.dash-kal-tag { position: relative; aspect-ratio: 1; padding: 0; border: 0; border-radius: 8px; background: transparent; color: var(--text); font: inherit; font-size: 13px; cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; }
.dash-kal-tag:hover { background: var(--surface-sunk); }
.dash-kal-tag.ist-fremd, .dash-kal-tag.ist-frei { color: var(--text-subtle); }
.dash-kal-tag.ist-heute { color: var(--accent); font-weight: 600; box-shadow: inset 0 0 0 1.5px var(--accent); }
.dash-kal-tag.ist-gewaehlt { background: var(--accent); color: var(--surface); }
.dash-kal-tag:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.dash-kal-punkte { display: flex; gap: 2px; height: 5px; }
.dash-kal-punkte i, .dash-kal-legende i { width: 5px; height: 5px; border-radius: 50%; display: inline-block; background: var(--accent); }
.dash-kal-legende i { width: 7px; height: 7px; }
i.ist-fremd { background: color-mix(in srgb, var(--accent) 45%, transparent); }
i.ist-ms { background: var(--meilenstein); }
i.ist-rot { background: var(--red); }
i.ist-ab { background: var(--text-subtle); }
.dash-kal-tag.ist-gewaehlt .dash-kal-punkte i { background: currentColor; }
.dash-kal-legende { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 8px; font-size: 11px; color: var(--text-muted); }
.dash-kal-legende span { display: flex; align-items: center; gap: 4px; }
.dash-kal-liste { border-top: 1px solid var(--line-soft); }
.dash-kal-liste-titel { padding: 12px 16px 4px; font-size: 12px; font-weight: 500; color: var(--text-muted); }
.dash-kal-hinweis { padding: 2px 16px 6px; font-size: 12px; color: var(--amber); }
.dash-kal-liste .dash-termin { padding: 8px 16px; }
/* Griff zum Ändern der Spaltenbreite im GANTT (js/gantt.js). Ein breiter,
unsichtbarer Greifbereich über der Spaltengrenze; sichtbar wird er erst
beim Zeigen und Ziehen, wie die Spaltengrenze in Excel. */
.gantt-spalten-griff {
position: absolute; top: 0; right: -4px; width: 9px; height: 100%;
cursor: col-resize; z-index: 20; touch-action: none;
}
.gantt-spalten-griff::after {
content: ""; position: absolute; top: 3px; bottom: 3px; left: 3px; width: 3px;
border-radius: 4px; background: var(--accent); opacity: 0; transition: opacity .15s;
}
.gantt-spalten-griff:hover::after,
.gantt-spalte-zieht .gantt-spalten-griff::after { opacity: .7; }
.gantt-spalte-zieht, .gantt-spalte-zieht * { cursor: col-resize !important; user-select: none !important; }
/* ---------- Glocke: Benachrichtigungen (js/benachrichtigungen.js) ---------- */
.glocke { position: relative; }
#glockeBtn { position: relative; }
#glockeBtn.hat-neue { color: var(--accent); }
.glocke-zahl {
position: absolute; top: -3px; right: -3px; min-width: 17px; height: 17px; padding: 0 4px;
border-radius: 999px; background: var(--red); color: #fff; font-size: 11px; font-weight: 600;
line-height: 17px; text-align: center; box-shadow: 0 0 0 2px var(--bg);
}
.glocke-menue {
position: absolute; right: 0; top: calc(100% + 8px); z-index: 260;
width: min(360px, calc(100vw - 32px)); max-height: min(520px, 70vh); overflow: auto;
background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-lg);
box-shadow: var(--sh-3);
}
.glocke-kopf { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 12px 16px 8px; position: sticky; top: 0; background: inherit; }
.glocke-kopf b { font-size: 13px; }
.glocke-alle { padding: 2px 4px; border: 0; background: none !important; color: var(--accent); font: inherit; font-size: 12px; cursor: pointer; box-shadow: none !important; }
.glocke-alle:hover { text-decoration: underline; }
.glocke-leer { padding: 12px 16px 16px; font-size: 13px; color: var(--text-subtle); line-height: 1.5; }
.glocke-eintrag {
display: flex; align-items: flex-start; gap: 8px; width: 100%; padding: 12px 16px;
border: 0; border-top: 1px solid var(--line-soft); border-radius: 0; background: none; color: var(--text);
font: inherit; text-align: left; cursor: pointer; box-shadow: none;
}
.glocke-eintrag:hover { background: var(--surface-sunk); }
.glocke-eintrag:active { transform: none; }
.glocke-punkt { flex: 0 0 8px; height: 8px; margin-top: 4px; border-radius: 50%; background: transparent; }
.glocke-eintrag.ist-neu .glocke-punkt { background: var(--accent); }
.glocke-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.glocke-titel { font-size: 13px; font-weight: 500; }
.glocke-eintrag.ist-neu .glocke-titel { font-weight: 500; }
.glocke-meta { font-size: 11px; color: var(--text-subtle); }
.dash-neu .glocke-eintrag:first-child { border-top: 0; }
#dashNeu:empty { display: none; }
/* Löschen je Benachrichtigung: Zeile = Öffnen-Knopf + ×. */
.glocke-zeile { display: flex; align-items: stretch; border-top: 1px solid var(--line-soft); }
.glocke-zeile .glocke-eintrag { border-top: 0; flex: 1 1 auto; min-width: 0; }
.dash-neu .glocke-zeile:first-child { border-top: 0; }
.glocke-aktionen { display: flex; gap: 6px; }
.glocke-loeschen {
flex: 0 0 34px; padding: 0; border: 0; border-radius: 0; background: none !important; box-shadow: none !important;
color: var(--text-subtle); font-size: 19px; line-height: 1; cursor: pointer; opacity: .55;
}
.glocke-zeile:hover .glocke-loeschen, .glocke-loeschen:focus-visible { opacity: 1; }
.glocke-loeschen:hover { color: var(--red); }
@media (hover: none) { .glocke-loeschen { opacity: 1; } }
/* ===========================================================================
WANDTAFEL IN GLAS (js/wand/wand.js, wt*-Funktionen) — Eriks Wahl aus dem
Entwurf: derselbe Hintergrund wie die Glas-App (Lichtflächen in den
Logo-Blautönen + Logo als Wasserzeichen), Felder halbdeckend OHNE
backdrop-filter (Kiosk-Rechner), Schrift Outfit für Ziffern und
Überschriften. Größen in Container-Einheiten (cqw von #wandContent), damit
die Tafel auf jedem Bildschirm gleich aufgeteilt ist.
=========================================================================== */
body.wandtafel-aktiv {
--wt-tief: #0f5a85; --wt-hell: #94b0cc; --wt-grund: #e8eef3; --wt-mint: #7fc3b6;
--wt-tinte: #14212b; --wt-tinte-2: #4b5d6b; --wt-tinte-3: #6f8291;
--wt-gruen: #1f8a4c; --wt-gelb: #b4780a; --wt-rot: #c8242f; --wt-ms: #6d4bb3;
--wt-feld: rgba(255,255,255,.68); --wt-feld-2: rgba(255,255,255,.46);
--wt-kante: rgba(255,255,255,.85); --wt-kante-2: rgba(255,255,255,.4);
--wt-linie: rgba(20,45,65,.09);
--wt-schatten: 0 calc(1.2 * var(--u)) calc(3.4 * var(--u)) rgba(21,58,86,.12), 0 calc(.2 * var(--u)) calc(.6 * var(--u)) rgba(21,58,86,.06);
--wt-logo: .17;
--wt-zahl: 'Outfit', -apple-system, 'SF Pro Display', system-ui, sans-serif;
}
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) body.wandtafel-aktiv {
--wt-tief: #8cc9e6; --wt-hell: #2c5675; --wt-grund: #0b141b; --wt-mint: #2f6b62;
--wt-tinte: #e8f0f5; --wt-tinte-2: #a9bccb; --wt-tinte-3: #7790a2;
--wt-gruen: #5fd394; --wt-gelb: #f0c05e; --wt-rot: #ff7b72; --wt-ms: #c3a6ff;
--wt-feld: rgba(26,40,52,.72); --wt-feld-2: rgba(30,46,60,.5);
--wt-kante: rgba(255,255,255,.16); --wt-kante-2: rgba(255,255,255,.06); --wt-linie: rgba(255,255,255,.08);
--wt-schatten: 0 calc(1.2 * var(--u)) calc(3.4 * var(--u)) rgba(0,0,0,.4), 0 calc(.2 * var(--u)) calc(.6 * var(--u)) rgba(0,0,0,.3);
--wt-logo: .10;
}
}
html[data-theme="dark"] body.wandtafel-aktiv {
--wt-tief: #8cc9e6; --wt-hell: #2c5675; --wt-grund: #0b141b; --wt-mint: #2f6b62;
--wt-tinte: #e8f0f5; --wt-tinte-2: #a9bccb; --wt-tinte-3: #7790a2;
--wt-gruen: #5fd394; --wt-gelb: #f0c05e; --wt-rot: #ff7b72; --wt-ms: #c3a6ff;
--wt-feld: rgba(26,40,52,.72); --wt-feld-2: rgba(30,46,60,.5);
--wt-kante: rgba(255,255,255,.16); --wt-kante-2: rgba(255,255,255,.06); --wt-linie: rgba(255,255,255,.08);
--wt-schatten: 0 calc(1.2 * var(--u)) calc(3.4 * var(--u)) rgba(0,0,0,.4), 0 calc(.2 * var(--u)) calc(.6 * var(--u)) rgba(0,0,0,.3);
--wt-logo: .10;
}
/* Hintergrund: wie die Glas-App, auch wenn die Glas-Darstellung abgeschaltet ist. */
body.wandtafel-aktiv .container {
position: relative; isolation: isolate;
background:
radial-gradient(52% 48% at 8% 4%, color-mix(in srgb, var(--wt-hell) 72%, transparent), transparent 70%),
radial-gradient(42% 46% at 94% 10%, color-mix(in srgb, var(--wt-tief) 26%, transparent), transparent 70%),
radial-gradient(48% 55% at 72% 98%, color-mix(in srgb, var(--wt-mint) 30%, transparent), transparent 70%),
radial-gradient(38% 40% at 28% 84%, color-mix(in srgb, var(--wt-hell) 44%, transparent), transparent 70%),
var(--wt-grund);
}
body.wandtafel-aktiv .container::before {
content: ""; position: absolute; z-index: -1; left: -3vw; bottom: -11vw; width: 62vw; aspect-ratio: 256 / 216;
background: url("/logo.svg") no-repeat center / contain; opacity: var(--wt-logo); pointer-events: none;
}
/* #wandContent ist Abfrage-Container in Breite UND Höhe. Alle Maße der Tafel
laufen über --u: 1 % der Breite, aber nie mehr, als die Höhe bei 16:9
hergäbe. Auf einem flacheren Fenster (Browserleiste, 16:10-Laptop,
21:9-Monitor) schrumpft so alles gleichmäßig, statt unten abgeschnitten zu
werden; die Spalten füllen die Breite trotzdem über fr-Einheiten. */
body.wandtafel-aktiv .wand-grid-wrap { background: transparent; container-type: size; }
body.wandtafel-aktiv .wand-grid-wrap > * { --u: min(1cqw, 1.7778cqh); }
body.wandtafel-aktiv .wand-kopfzeile { padding-bottom: 6px; }
/* Genau einen Bildschirm hoch (nicht nur mindestens): Die Felder teilen
sich die Höhe, statt den Fuß unter den Rand zu schieben. Die Projektliste
folgt darunter und wird gewischt. */
.wand-ambient.wt { height: 100%; min-height: 0; color: var(--wt-tinte); font-size: calc(1.05 * var(--u)); gap: calc(1.2 * var(--u)); padding-bottom: calc(.4 * var(--u)); }
.wt-gewerke { overflow: hidden; }
.wt-feld { background: var(--wt-feld); border: 1px solid var(--wt-kante-2); border-radius: calc(1.5 * var(--u));
box-shadow: var(--wt-schatten), inset 0 1px 0 var(--wt-kante); }
.wt .ist-klickbar { cursor: pointer; }
.wt-kopf { flex-shrink: 0; display: flex; align-items: center; gap: calc(1.6 * var(--u)); padding: calc(.9 * var(--u)) calc(1.4 * var(--u)); }
.wt-marke { display: flex; align-items: center; gap: calc(.8 * var(--u)); }
.wt-marke img { width: calc(3.4 * var(--u)); height: auto; }
.wt-marke b { font-family: var(--wt-zahl); font-weight: 500; font-size: calc(2 * var(--u)); letter-spacing: -.01em; }
.wt-sitzung { font-size: calc(1 * var(--u)); font-weight: 650; padding: calc(.3 * var(--u)) calc(.9 * var(--u)); border-radius: calc(99 * var(--u)); background: color-mix(in srgb, var(--wt-tief) 12%, transparent); color: var(--wt-tief); }
.wt-abw { display: flex; align-items: center; gap: calc(.5 * var(--u)); margin-left: auto; font-size: calc(1 * var(--u)); color: var(--wt-tinte-2); cursor: pointer; }
.wt-abw.ist-leer { cursor: default; color: var(--wt-tinte-3); }
.wt-ava { width: calc(2.5 * var(--u)); height: calc(2.5 * var(--u)); border-radius: 50%; display: grid; place-items: center; font-size: calc(.85 * var(--u)); font-weight: 700;
background: var(--wt-feld); border: 1px solid var(--wt-kante-2); color: var(--wt-tinte-2); }
.wt-uhr { font-family: var(--wt-zahl); font-weight: 300; font-size: calc(3.4 * var(--u)); letter-spacing: -.02em; font-variant-numeric: tabular-nums; line-height: 1; text-align: right; }
.wt-uhr small { display: block; font-family: inherit; font-weight: 400; font-size: calc(.9 * var(--u)); color: var(--wt-tinte-3); letter-spacing: 0; margin-top: calc(.25 * var(--u)); }
.wt-kpis { flex-shrink: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: calc(1.2 * var(--u)); }
.wt-kpi { padding: calc(1.1 * var(--u)) calc(1.4 * var(--u)); display: flex; align-items: center; gap: calc(1.2 * var(--u)); transition: transform .15s; }
.wt-kpi.ist-klickbar:active { transform: scale(.98); }
.wt-wert { font-family: var(--wt-zahl); font-weight: 500; font-size: calc(4.2 * var(--u)); line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.wt-wert small { font-size: calc(2 * var(--u)); margin-left: calc(.15 * var(--u)); }
.wt-lab { font-size: calc(1.05 * var(--u)); color: var(--wt-tinte-2); line-height: 1.35; }
.wt-lab b { display: block; color: var(--wt-tinte); font-size: calc(1.15 * var(--u)); }
.wt-kpi.ist-rot .wt-wert { color: var(--wt-rot); }
.wt-ring { flex: none; width: calc(4.4 * var(--u)); height: calc(4.4 * var(--u)); border-radius: 50%;
background: conic-gradient(var(--c) calc(var(--p) * 1%), color-mix(in srgb, var(--wt-tinte-3) 20%, transparent) 0);
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - calc(.55 * var(--u))), #000 calc(100% - calc(.5 * var(--u))));
mask: radial-gradient(farthest-side, transparent calc(100% - calc(.55 * var(--u))), #000 calc(100% - calc(.5 * var(--u)))); }
.wt-felder { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0, .92fr) minmax(0, 2.08fr); gap: calc(1.2 * var(--u)); }
.wt-panel { padding: calc(1.1 * var(--u)) calc(1.3 * var(--u)); display: flex; flex-direction: column; min-height: 0; }
.wt-panel h2 { margin: 0 0 calc(.9 * var(--u)); font-family: var(--wt-zahl); font-size: calc(1.45 * var(--u)); font-weight: 500; letter-spacing: -.005em; color: var(--wt-tinte);
display: flex; align-items: baseline; gap: calc(.6 * var(--u)); }
.wt-panel h2 span { margin-left: auto; font-family: inherit; font-size: calc(.95 * var(--u)); font-weight: 400; color: var(--wt-tinte-3); }
.wt-gw { flex: 1 1 0; min-height: 0; display: grid; grid-template-columns: calc(5 * var(--u)) minmax(0, 1fr); gap: calc(1 * var(--u)); align-items: center; padding: calc(.9 * var(--u)) 0; border-top: 1px solid var(--wt-linie); }
.wt-gw:first-of-type { border-top: 0; }
.wt-gew { font-family: var(--wt-zahl); font-weight: 500; font-size: calc(1.7 * var(--u)); }
.wt-gew small { display: block; font-family: inherit; font-weight: 400; font-size: calc(.85 * var(--u)); color: var(--wt-tinte-3); }
.wt-gw-werte { display: flex; flex-direction: column; gap: calc(.5 * var(--u)); min-width: 0; }
.wt-gw-zeile { display: grid; grid-template-columns: calc(6.6 * var(--u)) minmax(0, 1fr) calc(4.6 * var(--u)); align-items: center; gap: calc(.8 * var(--u)); padding: calc(.25 * var(--u)) calc(.4 * var(--u)); margin: 0 -calc(.4 * var(--u)); border-radius: calc(.8 * var(--u)); }
.wt-gw-zeile:hover { background: color-mix(in srgb, var(--wt-tief) 7%, transparent); }
.wt-gw-lab { font-size: calc(.92 * var(--u)); color: var(--wt-tinte-3); }
.wt-balken { height: calc(1.1 * var(--u)); border-radius: calc(99 * var(--u)); background: color-mix(in srgb, var(--wt-tinte-3) 16%, transparent); overflow: hidden; display: block; }
.wt-balken i { display: block; height: 100%; border-radius: inherit; }
.wt-zahl { font-family: var(--wt-zahl); font-weight: 500; font-size: calc(1.6 * var(--u)); text-align: right; font-variant-numeric: tabular-nums; }
.wt-zahl.klein { font-size: calc(1.25 * var(--u)); color: var(--wt-tinte-2); }
/* Besprochen-Stand: immer EINE Reihe. Bei wenigen Projekten Kästchen, bei
vielen werden sie schmaler und bilden einen Segmentbalken (ein Segment je
Projekt) — umbrechende Kästchen liefen sonst bei ELT (25 Projekte) über
die Trennlinie zum nächsten Gewerk. */
.wt-punkte { display: flex; flex-wrap: nowrap; gap: calc(.22 * var(--u)); min-width: 0; }
.wt-punkte i { flex: 1 1 0; min-width: 2px; max-width: calc(.95 * var(--u)); height: calc(.95 * var(--u));
border-radius: calc(.25 * var(--u)); background: color-mix(in srgb, var(--wt-tinte-3) 22%, transparent); }
.wt-punkte i.ja { background: var(--wt-gruen); }
.wt-leer { font-size: calc(.95 * var(--u)); color: var(--wt-tinte-3); }
.wt-tage-raster { flex: 1; min-height: 0; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: calc(.8 * var(--u)); }
.wt-tag { display: flex; flex-direction: column; gap: calc(.55 * var(--u)); min-height: 0; padding: calc(.8 * var(--u)); border-radius: calc(1.1 * var(--u)); overflow: hidden;
background: color-mix(in srgb, var(--wt-feld) 55%, transparent); border: 1px solid var(--wt-kante-2); }
.wt-tag.ist-heute { background: color-mix(in srgb, var(--wt-tief) 9%, var(--wt-feld)); border-color: color-mix(in srgb, var(--wt-tief) 30%, transparent); }
.wt-tag-kopf { display: flex; align-items: baseline; gap: calc(.55 * var(--u)); padding-bottom: calc(.45 * var(--u)); border-bottom: 1px solid var(--wt-linie); }
.wt-tag-kopf b { font-family: var(--wt-zahl); font-weight: 500; font-size: calc(1.75 * var(--u)); letter-spacing: -.01em; }
.wt-tag-kopf span { font-size: calc(.95 * var(--u)); color: var(--wt-tinte-3); }
.wt-tag.ist-heute .wt-tag-kopf b { color: var(--wt-tief); }
.wt-ein { display: grid; grid-template-columns: calc(.35 * var(--u)) minmax(0, 1fr); gap: calc(.6 * var(--u)); font-size: calc(1 * var(--u)); line-height: 1.3; border-radius: calc(.5 * var(--u)); }
.wt-ein.ist-klickbar:hover { background: color-mix(in srgb, var(--wt-tief) 6%, transparent); }
.wt-ein > i { border-radius: calc(99 * var(--u)); background: var(--c); }
.wt-ein > div { min-width: 0; }
.wt-ein b { font-weight: 600; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.wt-ein span { display: block; color: var(--wt-tinte-3); font-size: calc(.88 * var(--u)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wt-ein.wt-ab b { font-weight: 500; color: var(--wt-tinte-2); }
.wt-ueber { --c: var(--wt-rot); } .wt-aufgabe { --c: var(--wt-tief); } .wt-ms { --c: var(--wt-ms); }
.wt-kommentar { --c: var(--wt-gruen); } .wt-ab { --c: var(--wt-tinte-3); }
.wt-gruppe { font-size: calc(.78 * var(--u)); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--c); margin-top: calc(.15 * var(--u)); }
.wt-tag-liste { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; gap: calc(.55 * var(--u)); }
.wt-mehr { flex-shrink: 0; font-size: calc(.9 * var(--u)); color: var(--wt-tief); font-weight: 600; cursor: pointer; }
.wt-legende { display: flex; flex-wrap: wrap; gap: calc(.4 * var(--u)) calc(1.4 * var(--u)); margin-top: calc(.8 * var(--u)); font-size: calc(.9 * var(--u)); color: var(--wt-tinte-2); }
.wt-legende span { display: flex; align-items: center; gap: calc(.45 * var(--u)); }
.wt-legende i { width: calc(.9 * var(--u)); height: calc(.9 * var(--u)); border-radius: calc(.3 * var(--u)); background: var(--c); }
.wt-fuss { flex-shrink: 0; display: flex; align-items: center; gap: calc(1 * var(--u)); padding: calc(.6 * var(--u)) calc(1.4 * var(--u)); font-size: calc(1.05 * var(--u)); color: var(--wt-tinte-2); }
.wt-pfeil { font-family: var(--wt-zahl); font-weight: 300; font-size: calc(1.7 * var(--u)); color: var(--wt-tief); }
@media (prefers-reduced-motion: reduce) { .wt-kpi { transition: none; } }
/* Hinweis nach dem Zusammenführen gleichzeitiger Änderungen (js/store.js,
zeigeHinweis()) — kein Fehler, deshalb nicht rot wie .sync-problem-bar. */
.sync-problem-bar.sync-hinweis { background: var(--accent); color: #fff; bottom: 70px; }
/* ===========================================================================
DUNKELMODUS NACHGESCHÄRFT (Eriks Wahl „A“ aus dem Kontrast-Vergleich):
Kalender auf „Meine Aufgaben“ und die Farben im Sitzungskommentar.
Alles über Tokens: :root = hell (unverändert bzw. gleichwertig), die
beiden Dunkel-Blöcke (System-Einstellung / ausdrücklich gewählt) setzen
die helleren Fassungen.
=========================================================================== */
:root {
/* „Meine Aufgabe“ und „Projektaufgabe“ waren derselbe Blauton (einmal
halbtransparent) — jetzt zwei klar verschiedene Farben. */
--kal-meine: var(--accent);
--kal-projekt: #138a6e;
--kal-abw: var(--text-subtle);
--kal-wt: var(--text-subtle);
--kal-frei: var(--text-subtle);
--kal-fremd: var(--text-subtle);
--kal-kw: var(--text-subtle);
--kal-legende: var(--text-muted);
--kal-punkt: 5px;
/* Farben der Kommentar-Werkzeugleiste (gespeichert als feste Werte,
siehe richtextToolbarHtml() in js/utils.js). */
--rt-rot: #d70015;
--rt-gruen: #248a3d;
--rt-blau: #0071e3;
--rt-orange: #b25000;
}
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) {
--meilenstein: #c19cf0;
--kal-projekt: #6fd6b4;
--kal-abw: #b4c3cf;
--kal-wt: #93aabb;
--kal-frei: #93aabb;
--kal-fremd: #6d8597;
--kal-kw: #8299aa;
--kal-legende: #b8c8d4;
--kal-punkt: 6px;
--rt-rot: #ff8a7f;
--rt-gruen: #5fd38d;
--rt-blau: #7cc0ff;
--rt-orange: #f2a65a;
--kommentar-ansicht-bg: #1b2730;
}
}
html[data-theme="dark"] {
--meilenstein: #c19cf0; /* fehlte hier bisher: blieb das dunkle Lila */
--kal-projekt: #6fd6b4;
--kal-abw: #b4c3cf;
--kal-wt: #93aabb;
--kal-frei: #93aabb;
--kal-fremd: #6d8597;
--kal-kw: #8299aa;
--kal-legende: #b8c8d4;
--kal-punkt: 6px;
--rt-rot: #ff8a7f;
--rt-gruen: #5fd38d;
--rt-blau: #7cc0ff;
--rt-orange: #f2a65a;
--kommentar-ansicht-bg: #1b2730;
}
/* Kalender */
.dash-kal-wt { color: var(--kal-wt); }
.dash-kal-kw { color: var(--kal-kw); }
.dash-kal-tag.ist-frei { color: var(--kal-frei); }
.dash-kal-tag.ist-fremd { color: var(--kal-fremd); }
.dash-kal-legende { color: var(--kal-legende); }
.dash-kal-punkte { height: var(--kal-punkt); }
.dash-kal-punkte i { width: var(--kal-punkt); height: var(--kal-punkt); background: var(--kal-meine); }
.dash-kal-legende i { background: var(--kal-meine); }
.dash-kal-punkte i.ist-fremd, .dash-kal-legende i.ist-fremd { background: var(--kal-projekt); }
.dash-kal-punkte i.ist-ms, .dash-kal-legende i.ist-ms { background: var(--meilenstein); }
.dash-kal-punkte i.ist-rot, .dash-kal-legende i.ist-rot { background: var(--red); }
.dash-kal-punkte i.ist-ab, .dash-kal-legende i.ist-ab { background: var(--kal-abw); }
.dash-kal-tag.ist-gewaehlt .dash-kal-punkte i { background: currentColor; }
/* Kommentar als Text (Projekt-Dashboard, Projektliste, Wochenbericht
gesperrt): heller Fließtext auf satter Fläche. */
.status-edit.is-ansicht { color: var(--text); background: var(--kommentar-ansicht-bg, var(--surface-sunk)); }
/* Farbige Stellen im Kommentar: Die Farbe steht als Inline-Stil im Text
(mal "#d70015", mal vom Browser als "rgb(215, 0, 21)" geschrieben, alt
auch <font color>). Im Dunkelmodus auf die helle Fassung umsetzen; im
hellen Modus sind die Tokens identisch mit den gespeicherten Werten.
:not([style*="background"]) hält Farbflächen (Werkzeugleiste, Gantt)
heraus. Gespeichert wird weiterhin der Originalwert, das PDF bleibt gleich. */
[style*="color"][style*="d70015" i]:not([style*="background"]),
[style*="color"][style*="215, 0, 21"]:not([style*="background"]),
font[color="#d70015" i] { color: var(--rt-rot) !important; }
[style*="color"][style*="248a3d" i]:not([style*="background"]),
[style*="color"][style*="36, 138, 61"]:not([style*="background"]),
font[color="#248a3d" i] { color: var(--rt-gruen) !important; }
[style*="color"][style*="0071e3" i]:not([style*="background"]),
[style*="color"][style*="0, 113, 227"]:not([style*="background"]),
font[color="#0071e3" i] { color: var(--rt-blau) !important; }
[style*="color"][style*="b25000" i]:not([style*="background"]),
[style*="color"][style*="178, 80, 0"]:not([style*="background"]),
font[color="#b25000" i] { color: var(--rt-orange) !important; }
/* Auslastung als Personenkarten mit Wochenvorschau (Eriks Wahl "B",
renderLoad() in js/load/load.js). Je Person: Projektanzahl, Wert dieser
Woche, zwölf Wochensäulen mit gestrichelter 100-%-Linie, Hinweis auf die
Spitze und die größten Projekte. */
.load-erklaerung { margin: 4px 0 12px; max-width: 900px; line-height: 1.5; }
.load-karten { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 12px; }
.load-karte {
background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-lg);
padding: 16px 20px; display: flex; flex-direction: column; gap: 8px; min-width: 0;
}
.load-karte.ist-ohne { opacity: .75; }
.load-karte-kopf { display: flex; align-items: center; gap: 12px; }
.load-ava {
width: 36px; height: 36px; flex: none; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 600; background: var(--accent); color: #fff;
}
.load-karte-wer { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.load-karte-wer b { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.load-karte-wer > span { font-size: 12px; color: var(--text-muted); }
.load-karte-wer .load-capacity { align-self: flex-start; margin: 2px 0 0; }
.load-jetzt { display: flex; flex-direction: column; align-items: flex-end; text-align: right; flex: none; }
.load-jetzt b { font-size: 24px; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.1; }
.load-jetzt span { font-size: 11px; color: var(--text-muted); }
.load-saeulen {
position: relative; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 4px;
align-items: end; height: 86px; padding-top: 4px;
}
.load-saeule { display: block; border-radius: 4px 4px 4px 4px; opacity: .85; min-height: 0; }
.load-saeule.ist-jetzt { opacity: 1; }
.load-saeule.ist-teil { background-image: repeating-linear-gradient(135deg, transparent 0 4px, rgba(255,255,255,.35) 4px 7px); }
.load-saeule.ist-abwesend { height: 100%; opacity: 1; background: repeating-linear-gradient(135deg, var(--surface-sunk) 0 5px, var(--line) 5px 10px); }
.load-linie { position: absolute; left: 0; right: 0; border-top: 1.5px dashed var(--text-muted); opacity: .55; pointer-events: none; }
.load-kws { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 4px; font-size: 11px; color: var(--text-muted); text-align: center; font-variant-numeric: tabular-nums; }
.load-kws .ist-jetzt { color: var(--accent); font-weight: 600; }
.load-hinweis { font-size: 13px; padding: 8px 12px; border-radius: 10px; line-height: 1.4; }
.load-hinweis.ist-rot { background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--red); }
.load-hinweis.ist-gelb { background: color-mix(in srgb, var(--amber) 12%, transparent); color: var(--amber); }
.load-hinweis.ist-ok { background: color-mix(in srgb, var(--green) 10%, transparent); color: var(--green); }
.load-hinweis.ist-leer { background: var(--surface-sunk); color: var(--text-muted); }
.load-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.load-chip {
display: inline-flex; align-items: center; min-height: 24px; max-width: 100%; padding: 0 8px; border-radius: 6px;
background: var(--surface-sunk); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.load-chip.ist-mehr { color: var(--text-muted); border: 0; font: inherit; font-size: 12px; cursor: pointer; }
.load-chip.ist-mehr:hover { color: var(--accent); }
.load-chips:not(.ist-offen) .load-chip.ist-weitere { display: none; }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .load-ava { color: #0b141b; } }
html[data-theme="dark"] .load-ava { color: #0b141b; }
/* =========================================================================
ROLLEN & RECHTE (Admin, js/admin/rollen.js) — Eriks Wahl „B“: links die
Rollen mit „+ Neue Rolle“ darunter, rechts die gewählte Rolle mit Schaltern
je Gruppe.
========================================================================= */
.rr-layout { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 16px; align-items: start; }
@media (max-width: 820px) { .rr-layout { grid-template-columns: 1fr; } }
.rr-rollen { display: flex; flex-direction: column; gap: 6px; }
.rr-rolle {
display: flex; flex-direction: column; gap: 2px; text-align: left; width: 100%;
border: 1px solid var(--line); background: var(--surface); color: var(--text);
border-radius: 12px; padding: 12px 12px; font: inherit; cursor: pointer;
}
.rr-rolle:hover { border-color: var(--accent); }
.rr-rolle.ist-aktiv { border-color: var(--accent); background: var(--accent-soft); }
.rr-rolle b { font-weight: 500; }
.rr-rolle small { color: var(--text-muted); font-size: 12px; }
.rr-balken { display: block; height: 4px; border-radius: 4px; background: var(--surface-sunk); overflow: hidden; margin-top: 4px; }
.rr-balken i { display: block; height: 100%; background: var(--accent); }
.rr-plus {
border: 1.5px dashed var(--line) !important; background: transparent; color: var(--accent);
border-radius: 12px; padding: 12px 12px; text-align: left; font: inherit; font-weight: 500; cursor: pointer; width: 100%;
}
.rr-plus:hover { border-color: var(--accent) !important; background: var(--accent-soft); }
.rr-neu { border: 1.5px solid var(--accent); border-radius: 12px; padding: 12px; display: flex; flex-direction: column; gap: 8px; background: var(--surface); }
.rr-neu input, .rr-neu select { width: 100%; box-sizing: border-box; }
.rr-neu label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--text-muted); }
.rr-neu-knoepfe { display: flex; gap: 6px; }
.rr-detail { border: 1px solid var(--line); border-radius: 14px; padding: 16px; display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.rr-detail-kopf { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rr-titel {
font: inherit; font-size: 19px; font-weight: 500; flex: 1 1 180px; min-width: 0;
border: 1px solid transparent !important; background: transparent !important; color: var(--text); border-radius: 8px; padding: 4px 8px;
}
.rr-titel:hover:not(:disabled), .rr-titel:focus { border-color: var(--line) !important; background: var(--surface-sunk) !important; }
.rr-titel:disabled { color: var(--text); opacity: 1; }
.rr-vergleich-wahl { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-muted); }
.rr-meldung { font-size: 13px; color: var(--text-muted); min-width: 80px; }
.rr-gruppen { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; align-items: start; }
.rr-gruppe { background: var(--surface-sunk); border-radius: 12px; padding: 12px; display: flex; flex-direction: column; gap: 4px; }
.rr-gruppe-kopf { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.rr-gruppe-kopf small { color: var(--text-muted); }
.rr-luecke { flex: 1; }
.rr-zeile { display: flex; align-items: center; gap: 8px; padding: 4px 0; }
.rr-name { flex: 1; min-width: 0; }
.rr-name small { display: block; color: var(--text-muted); font-size: 12px; }
.rr-schalter {
width: 36px; height: 20px; border-radius: 999px; border: 0 !important; padding: 0; flex: none; position: relative; cursor: pointer;
background: color-mix(in srgb, var(--text) 22%, transparent); transition: background var(--dur, 150ms);
}
.rr-schalter::after {
content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
background: #fff; transition: transform var(--dur, 150ms) var(--ease-out, ease);
}
.rr-schalter.ist-an { background: var(--accent); }
.rr-schalter.ist-an::after { transform: translateX(16px); }
.rr-schalter:disabled { opacity: .55; cursor: not-allowed; }
.rr-schalter:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rr-vergleich { font-size: 11px; color: var(--text-muted); min-width: 34px; text-align: right; }
.rr-vergleich.ist-anders { color: var(--accent); font-weight: 600; }
/* =========================================================================
RUNDGANG (js/einfuehrung.js) — abgedunkelte Ebene mit Aussparung um das
Ziel und eine Sprechblase. Liegt über allem (auch über Dialogen, 300),
aber unter der Anmeldeseite (400) bewusst nicht: der Rundgang startet erst
nach der Anmeldung.
========================================================================= */
.tour { position: fixed; inset: 0; z-index: 450; }
.tour-loch {
position: fixed; border-radius: 12px; pointer-events: none;
box-shadow: 0 0 0 9999px rgba(6, 14, 20, .62), 0 0 0 2px var(--accent);
transition: left .25s var(--ease-out, ease), top .25s var(--ease-out, ease), width .25s var(--ease-out, ease), height .25s var(--ease-out, ease);
}
.tour.ohne-ziel { background: rgba(6, 14, 20, .62); }
.tour.ohne-ziel .tour-loch { display: none; }
.tour-blase {
position: fixed; width: min(360px, calc(100vw - 24px)); box-sizing: border-box;
background: var(--surface); color: var(--text); border-radius: 16px; padding: 16px 20px;
box-shadow: var(--sh-3); display: flex; flex-direction: column; gap: 8px; outline: none;
transition: left .25s var(--ease-out, ease), top .25s var(--ease-out, ease);
}
html.glas .tour-blase { background: var(--glas-dicht, var(--surface)); -webkit-backdrop-filter: blur(24px) saturate(160%); backdrop-filter: blur(24px) saturate(160%); }
.tour-blase.ist-unten { left: 12px !important; right: 12px; width: auto; top: auto !important; bottom: calc(12px + env(safe-area-inset-bottom, 0px)); }
.tour-kopf { display: flex; align-items: center; justify-content: space-between; }
.tour-schritt { font-size: 11px; letter-spacing: 0; color: var(--text-muted); font-weight: 500; }
.tour-x { border: 0 !important; background: transparent; color: var(--text-muted); font-size: 19px; line-height: 1; padding: 0 4px; cursor: pointer; }
.tour-x:hover { color: var(--text); }
.tour-blase h3 { margin: 0; font-size: 19px; font-weight: 500; text-wrap: balance; }
.tour-text { font-size: 13px; line-height: 1.55; color: var(--text); }
.tour-hinweise { margin: 8px 0 0; padding-left: 20px; color: var(--text-muted); }
.tour-punkte { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 2px; }
.tour-punkte i { width: 6px; height: 6px; border-radius: 50%; background: var(--line); }
.tour-punkte i.ist-erledigt { background: color-mix(in srgb, var(--accent) 45%, transparent); }
.tour-punkte i.ist-aktiv { background: var(--accent); width: 16px; border-radius: 4px; }
.tour-knoepfe { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
.tour-luecke { flex: 1; }
.tour-skip { border: 0 !important; background: transparent; color: var(--text-muted); font: inherit; font-size: 13px; padding: 4px 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.tour-skip:hover { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .tour-loch, .tour-blase { transition: none; } }
/* ============================================================
ABWESENHEITEN (js/abwesenheit/abwesenheit.js) — Eriks Wahl
„Variante 2 im Stil der Projektübersicht“: nutzt .bericht-titelzeile,
.legend/.swatch, .board-list, .board-row(-main), .board-nr, .board-name,
.board-status-line, .phase-pill, .board-row-actions und .board-avatar
der Projektliste; hier nur, was es dort nicht gibt.
Artfarben sind mittlere Töne, in beiden Themen lesbar.
============================================================ */
:root {
--abw-urlaub: #2f7fb8;
--abw-krank: #c4553d;
--abw-fortbildung: #7a5fc0;
--abw-sonstiges: #7d8450;
--abw-verdeckt: #8d9aa6;
}
/* showTab() setzt display:block inline — Abstände deshalb über Geschwister. */
#abwesenheit > * + * { margin-top: 12px; }
#abwesenheit .legend { margin: 6px 0 0; }
.abw-u { --abw-c: var(--abw-urlaub); }
.abw-k { --abw-c: var(--abw-krank); }
.abw-f { --abw-c: var(--abw-fortbildung); }
.abw-s { --abw-c: var(--abw-sonstiges); }
.swatch.abw-u, .swatch.abw-k, .swatch.abw-f, .swatch.abw-s { background: var(--abw-c); }
.swatch.abw-verdeckt, .abw-balken.abw-verdeckt {
background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--abw-verdeckt) 50%, transparent) 0 3px, transparent 3px 6px);
box-shadow: inset 0 0 0 1px var(--abw-verdeckt);
}
/* Zwei Spalten: Neue Abwesenheit | Liste */
/* Beide Kacheln gleich breit und gleich hoch; die Überschrift steht in der
Kachel. Die Liste rechts bestimmt die Höhe nicht mit (height 0 +
min-height 100 %), sie scrollt innen, wenn die Erfassung kürzer ist. */
.abw-zwei { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: stretch; }
.abw-spalte { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.abw-spalte > .board-list { flex: 1; }
.abw-karten-titel { margin: 0 !important; }
.abw-listen-karte { height: 0; min-height: 100%; }
.abw-listen-kopf { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; padding: 16px 16px 12px; border-bottom: 1px solid var(--line-soft); }
.abw-listen-zeilen { flex: 1; min-height: 0; overflow-y: auto; }
.abw-karte { padding: 16px; gap: 12px; }
.abw-fuer { display: flex; align-items: center; gap: 8px; font-size: 13px; flex-wrap: wrap; }
.abw-kreis { cursor: default; }
.abw-fuer-wahl { margin-left: auto; border-radius: 8px; font-size: 13px; padding: 4px 12px; max-width: 220px; }
.abw-datumzeile { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: center; }
.abw-datum {
display: grid; text-align: left; gap: 0; padding: 12px 12px; cursor: pointer;
border: 1px solid var(--line-soft); border-radius: 14px; background: var(--surface-alt); color: var(--text);
font: inherit;
}
.abw-datum:hover { background: var(--surface-sunk); }
.abw-datum small { font-size: 11px; color: var(--text-subtle); letter-spacing: 0; font-weight: 500; }
.abw-datum b { font-size: 19px; font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-weight: 500; letter-spacing: 0; }
.abw-datum span { font-size: 12px; color: var(--text-muted); }
.abw-pfeil { color: var(--text-subtle); }
.abw-arten { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.abw-arten button {
display: grid; justify-items: center; gap: 4px; padding: 8px 6px; cursor: pointer; font: inherit; font-size: 13px;
border: 1px solid var(--line-soft); border-radius: 12px; background: var(--surface-alt); color: var(--text);
}
.abw-arten button i { width: 18px; height: 6px; border-radius: 4px; background: var(--abw-c); }
.abw-arten button.ist-an { background: color-mix(in srgb, var(--abw-c) 18%, transparent); border-color: var(--abw-c); font-weight: 500; }
.abw-unten { display: flex; gap: 8px; align-items: center; }
.abw-notiz { flex: 1; min-width: 0; border-radius: 8px; }
.abw-fehler { font-size: 13px; color: var(--red); }
.abw-warnung { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; border-radius: 12px; padding: 8px 12px; background: color-mix(in srgb, var(--amber) 15%, transparent); }
.abw-warnung::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--amber); margin-top: 6px; }
/* Liste: Zeilen der Projektliste, mit Punkt in Artfarbe und Kapsel */
.abw-zeile.ist-aktiv .board-row-main { background: var(--accent-soft); }
.abw-zeile-name { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.abw-punkt { width: 9px; height: 9px; border-radius: 50%; background: var(--abw-c); flex: none; }
.abw-tag { font-size: 11px; font-weight: 500; padding: 1px 8px; border-radius: 6px; border: 1px solid var(--line); color: var(--text-muted); }
.board-row-actions.abw-fragt { opacity: 1; }
.abw-leer { padding: 16px 16px; font-size: 13px; color: var(--text-muted); }
.abw-reiter { display: inline-flex; gap: 2px; padding: 4px; border-radius: 8px; background: var(--surface-sunk); }
.abw-reiter button { border: 0; background: none; padding: 4px 12px; border-radius: 6px; font: inherit; font-size: 12px; color: var(--text-muted); cursor: pointer; }
.abw-reiter button.ist-an { background: var(--surface); color: var(--text); font-weight: 500; box-shadow: 0 1px 3px rgba(0,0,0,.12); }
/* Team je Gewerk */
.abw-team-kopf {
margin-top: 24px !important; flex-wrap: wrap; gap: 8px;
/* Hinterlegt wie die Karten darunter (.board-list) */
background: var(--surface); border: 1px solid var(--line-soft);
border-radius: var(--r-xl); box-shadow: var(--sh-2);
padding: 12px 16px; margin-bottom: 12px;
}
.abw-team-steuer { display: inline-flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.abw-monat { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.abw-monat strong { min-width: 120px; text-align: center; font-weight: 500; }
.abw-rund { width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--line); background: var(--surface); color: var(--text); cursor: pointer; display: grid; place-items: center; padding: 0; flex: none; }
/* Eigener Hover: sonst färbt die allgemeine button:hover-Regel den Kreis
akzentblau. Pfeile als Symbol (icon()) statt der Schriftzeichen ‹ ›, die im
Kreis nicht mittig saßen. */
.abw-rund:hover { background: var(--surface-sunk); border-color: var(--text-subtle); }
.abw-rund:active { background: var(--surface-sunk); }
.abw-rund .icon { display: grid; place-items: center; }
.abw-rund svg { width: 16px; height: 16px; display: block; }
.abw-gewerk-kopf { display: flex; align-items: baseline; gap: 8px; }
.abw-gewerk-kopf small { color: var(--text-muted); font-size: 13px; }
#abwesenheit .abw-gewerk-kopf + .abw-tl-karte { margin-top: 8px; }
.abw-tl-karte { overflow-x: auto; }
.abw-tl { display: grid; min-width: 880px; font-size: 11px; }
.abw-tl > div { position: relative; height: 52px; border-bottom: 1px solid var(--line-soft); }
.abw-tl { margin-bottom: -1px; }
.abw-tl-tag, .abw-tl-kopf { height: 36px !important; }
.abw-tl-tag { display: grid; align-content: center; justify-items: center; line-height: 1.1; color: var(--text-subtle); font-variant-numeric: tabular-nums; }
.abw-tl-tag small { font-size: 11px; letter-spacing: 0; }
.abw-tl-tag b { font-size: 12px; color: var(--text-muted); font-weight: 500; }
.abw-tl-tag.ist-heute b { background: var(--red); color: #fff; border-radius: 6px; padding: 0 4px; }
.abw-tl-wer { position: sticky; left: 0; z-index: 3; display: flex; align-items: center; gap: 8px; padding: 0 16px; background: var(--surface); }
html.glas .abw-tl-wer { background: var(--glas-deckend, var(--surface)); }
.abw-tl-kopf { color: var(--text-subtle); font-size: 11px; }
.abw-tl-wer.ist-gewaehlt { box-shadow: inset 3px 0 0 var(--accent); }
.abw-tl-wer.ist-klickbar { cursor: pointer; transition: background var(--dur) var(--ease-out); }
.abw-tl-wer.ist-klickbar:hover { background-image: linear-gradient(var(--surface-alt), var(--surface-alt)); } /* Tönung über dem deckenden Grund, die Spalte ist sticky */
.abw-tl-wer.ist-klickbar:hover .abw-tl-wer-name { color: var(--accent); }
.abw-tl-wer.ist-klickbar:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.abw-tl-wer-text { display: flex; flex-direction: column; min-width: 0; }
.abw-tl-wer-name { font-weight: 500; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.abw-tl-wer-meta { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.abw-tl .ist-frei { background: color-mix(in srgb, var(--text-subtle) 7%, transparent); }
.abw-tl-zelle.ist-heute::after { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; background: var(--red); opacity: .5; z-index: 1; }
.abw-tl-zelle.ist-klickbar { cursor: pointer; }
.abw-tl-zelle.ist-klickbar:hover { background: var(--surface-sunk); }
.abw-balken {
position: absolute; top: 14px; bottom: 14px; left: 2px; z-index: 2; cursor: pointer;
display: flex; align-items: center; padding: 0 12px; white-space: nowrap; overflow: hidden;
border-radius: var(--r-pill); font-size: 11px; font-weight: 500; color: var(--text);
background: color-mix(in srgb, var(--abw-c) 20%, transparent); box-shadow: inset 0 0 0 1px var(--abw-c);
}
.abw-balken::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--abw-c); margin-right: 6px; }
.abw-balken.abw-verdeckt { cursor: default; }
.abw-balken.abw-verdeckt::before { display: none; }
@media (max-width: 980px) { .abw-zwei { grid-template-columns: 1fr; } .abw-listen-karte { height: auto; min-height: 0; } .abw-listen-zeilen { overflow: visible; } }
@media (max-width: 768px) {
.abw-arten { grid-template-columns: repeat(2, 1fr); }
}
/* Verdeckte Abwesenheit einer anderen Person: nur „abwesend“, neutral, nicht änderbar */
.abw-verdeckt-zeile { --abw-c: var(--text-subtle); }
.abw-verdeckt-zeile .board-row-main { cursor: default; }
/* ---------- Meine Aufgaben: eigene Reihenfolge der Projekte ----------
Griff vorn im Projektkopf (dashGriffDruck()/dashGriffTaste(),
js/dashboard/dashboard.js). */
.dash-proj-griff {
display: inline-flex; align-items: center; margin-right: -4px;
color: var(--text-subtle); cursor: grab; border-radius: var(--r-sm);
}
.dash-proj-griff:hover { color: var(--text); }
.dash-proj-griff:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dash-proj-griff svg { width: 14px; height: 14px; }
.dash-proj.ist-gezogen { opacity: .5; }
#dashReihenfolgeReset:not([hidden]) + #dashAlleKnopf { margin-left: 8px; }
/* Kürzel-Kreis einer Person mit mehreren Rollen (beteiligtenAvatarHtml(),
js/utils.js): Segmente in den Rollenfarben, weiße Schrift mit Schatten. */
.board-avatar.ist-mehrfach {
/* Das Verlaufsbild beginnt an der Außenkante des Rands und wiederholt sich
nicht — sonst kachelt es in den (durchsichtigen) Rand hinein, und links
und rechts erscheint je ein schmaler Streifen der jeweils anderen Farbe. */
background-color: transparent;
background-origin: border-box;
background-repeat: no-repeat;
color: #fff; border-color: transparent;
text-shadow: 0 0 2px rgba(0,0,0,.55), 0 0 1px rgba(0,0,0,.6);
}
/* „Weitere laden“ im Verlauf (weitereLadenKnopf(), js/store.js): holt je
fünf ältere Sitzungen nach — beim Start sind nur die jüngsten geladen. */
.verlauf-weitere-zeile, .plan-verlauf-mehr-zeile { display: flex; justify-content: center; margin-top: 8px; }
button.verlauf-weitere { white-space: nowrap; }
/* ---------- Einstellungen: Kalender in Outlook ---------- */
.kalender-adresse { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 8px 0 6px; }
.kalender-adresse input { flex: 1; min-width: 220px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
.kalender-adresse .button-link {
display: inline-flex; align-items: center; padding: 8px 16px; border-radius: 8px;
background: var(--accent); color: #fff; font-size: 13px; font-weight: 500; text-decoration: none; white-space: nowrap;
}
.kalender-adresse .button-link:hover { background: var(--accent-hover); }
.kalender-anleitung { margin: 6px 0 8px; font-size: 13px; }
.kalender-anleitung summary { cursor: pointer; color: var(--accent); }
.kalender-anleitung ol { margin: 8px 0; padding-left: 20px; display: grid; gap: 6px; }
.kalender-aktionen { display: flex; gap: 8px; }
/* ---------- Admin → Teams (loadAdminTeams(), js/admin/admin.js) ----------
Eigene Klassen statt .settings-field: das Adressfeld ist lang (≈ 300
Zeichen) und braucht eine breite, mehrzeilige Fläche. */
#adminTeamsInhalt { display: grid; gap: 12px; margin: 12px 0 6px; }
.teams-status {
display: flex; align-items: center; gap: 12px; padding: 12px 16px;
border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface-sunk);
}
.teams-punkt { width: 10px; height: 10px; border-radius: 50%; background: var(--text-subtle); flex: none; }
.teams-status.ist-an .teams-punkt { background: var(--green); box-shadow: 0 0 0 4px color-mix(in srgb, var(--green) 20%, transparent); }
.teams-status-text { display: grid; gap: 2px; flex: 1; min-width: 0; }
.teams-status-text b { font-size: 13px; }
.teams-status-text span { font-size: 12px; color: var(--text-muted); overflow-wrap: anywhere; }
button.teams-text-knopf, button.teams-text-knopf:hover, button.teams-text-knopf:active {
all: unset; cursor: pointer; font-size: 13px; color: var(--accent); white-space: nowrap; padding: 4px 2px;
}
button.teams-text-knopf:hover { text-decoration: underline; }
button.teams-text-knopf:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
button.teams-text-knopf.ist-gefahr { color: var(--red); }
.teams-feld[hidden] { display: none; }
.teams-feld { display: grid; gap: 6px; }
.teams-feld label { font-size: 12px; font-weight: 500; color: var(--text-muted); }
.teams-feld textarea {
width: 100%; box-sizing: border-box; resize: vertical; min-height: 64px;
padding: 12px 12px; border: 1px solid var(--line) !important; border-radius: var(--r-md) !important;
background: var(--surface) !important; color: var(--text); box-shadow: none !important;
font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; word-break: break-all;
}
.teams-feld textarea:focus { outline: none; border-color: var(--accent) !important; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent) !important; }
.teams-feld-fuss { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.teams-feld-hinweis { flex: 1; min-width: 200px; font-size: 12px; color: var(--text-subtle); }
.teams-feld-hinweis.ist-fehler { color: var(--red); }
/* Schalter statt nackter Checkbox */
.teams-schalter { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.teams-schalter input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.teams-schalter-bahn {
flex: none; width: 38px; height: 22px; border-radius: 12px; background: var(--line); position: relative;
transition: background var(--dur, 150ms);
}
.teams-schalter-bahn::after {
content: ''; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%;
background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3); transition: transform var(--dur, 150ms) var(--ease-out, ease);
}
.teams-schalter input:checked + .teams-schalter-bahn { background: var(--accent); }
.teams-schalter input:checked + .teams-schalter-bahn::after { transform: translateX(16px); }
.teams-schalter input:focus-visible + .teams-schalter-bahn { outline: 2px solid var(--accent); outline-offset: 2px; }
.teams-schalter input:disabled + .teams-schalter-bahn { opacity: .45; }
.teams-schalter:has(input:disabled) { cursor: default; }
.teams-schalter-text { display: grid; gap: 2px; }
.teams-schalter-text b { font-size: 13px; font-weight: 500; }
.teams-schalter-text span { font-size: 12px; color: var(--text-muted); }
.teams-schalter:has(input:disabled) .teams-schalter-text { opacity: .55; }
.teams-aktionen { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* ===========================================================================
BAUPLAN — unslop-Überarbeitung (Eriks Wahl „volle Überarbeitung“, 11/2026)
---------------------------------------------------------------------------
Richtung: Planköpfe und Raster eines Bauplans. Flächen fast neutral
(Graphit, leicht kühl), ein Akzent (Logo-Blau), Farbe nur in den
Inhalten (Terminplan, Auslastung, Status). Schriftskala 11/12/13/15/19/24,
Gewichte 400/500 (600 selten), keine gesperrten Großbuchstaben, Abstände
im 4px-Raster, Rundungen 4–8 (Bedienelemente) / 12–16 (Karten).
Kennzeichen: Kalenderwochen, Maße und Kennzahlen in gleich breiten
Monospace-Ziffern — überall dieselbe „KW 44“-Marke.
Die Durchgänge wurden auf styles.css/plan-styles.css/glas.css angewandt;
die Wandtafel (body.wandtafel-aktiv mit ihren dash- und wt-Tokens) ist ausgenommen.
Methode nach „unslop“ von Damir Sokolovsky (@heydamir), CC BY 4.0.
=========================================================================== */
:root { --font-mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace; }
/* Kennzeichen: KW-Marke */
.gantt-day.gantt-week span,
.range-picker-kw, .range-picker-kw-kopf,
.dash-kal-kw, .bv-kw, .timeline-kw, .plan-verlauf-kw, .bericht-zuletzt-kw, .load-kws {
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
font-weight: 400;
letter-spacing: 0;
}
.gantt-day.gantt-week span { font-size: 11px; color: var(--text-muted); }
.range-picker-kw, .dash-kal-kw { color: var(--text-subtle); font-size: 11px; }
/* Kennzahlen und Maße in denselben Ziffern */
.board-nr, .plan-header .projnr, .dash-proj-nr, .dash-auf-pct, .dash-termin-datum,
.gantt-dates, .plan-task-date, .load-zahl, .load-num, .kpi-value, .plan-kpi-tile .kpi-value,
.dash-band-zahl {
font-variant-numeric: tabular-nums;
}
.board-nr, .dash-proj-nr { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0; color: var(--text-muted); }
/* Navigation: ruhige Auswahl — Flächenstufe statt Akzentfläche */
.nav-item.active { background: var(--surface-sunk); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-soft); }
.nav-item.active .nav-icon { color: var(--accent); }
.nav-item { min-height: 32px; }
.sidebar-section-label { padding: 16px 12px 4px; color: var(--text-subtle); font-weight: 500; }
/* Dunkel: Hauptknöpfe in einem tieferen Logo-Blau, damit die weiße Schrift
trägt (auf dem hellen Akzent #86bfdf käme sie nur auf ~2:1). Der helle
Akzent bleibt für Auswahl, Links und Kennzahlen. */
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) { --knopf: #276f9c; --knopf-hover: #2f7aa8; --knopf-aktiv: #22628a; }
}
html[data-theme="dark"] { --knopf: #276f9c; --knopf-hover: #2f7aa8; --knopf-aktiv: #22628a; }
button { background: var(--knopf, var(--accent)); }
button:hover { background: var(--knopf-hover, var(--accent-hover)); }
button:active { background: var(--knopf-aktiv, var(--accent-active)); }
/* Bewegung: nur Farbe/Fläche/Transform, eine Kurve */
button { transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out),
transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out); }
/* ===========================================================================
MEINE AUFGABEN — Entwurf „Bauplan“ (Eriks Wahl, 11/2026)
---------------------------------------------------------------------------
Kopf mit Kalenderwoche statt Begrüßung, Band mit vier Kennzahlen, das Band
„Diese Woche“ (Mo–Fr) und je Projekt die Zeile „Meine Rolle | Team |
Notizen“. Der Monatskalender (.dash-kal-*) bleibt ausdrücklich unverändert.
=========================================================================== */
.dash-kopf { align-items: flex-end; margin-bottom: 16px; }
.dash-kopf-links { display: flex; flex-direction: column; gap: 4px; }
.dash-kopf-kw { font-family: var(--font-mono); font-size: 12px; color: var(--text-subtle); font-variant-numeric: tabular-nums; }
.dash-titel { margin: 0; font-size: 24px; line-height: 32px; font-weight: 500; letter-spacing: 0; }
.dash-kopf-rechts { align-items: center; gap: 8px; }
.dash-kopf-label { font-size: 12px; color: var(--text-subtle); }
.dash-sitzung { font-family: var(--font-mono); font-size: 12px; font-weight: 400; padding: 2px 8px; border: 1px solid var(--line-soft); background: var(--surface); color: var(--text); }
.dash-band { padding: 20px 24px; gap: 24px; background: var(--surface); border: 1px solid var(--line-soft); box-shadow: var(--sh-1); }
.dash-band.ist-ruhig { background: var(--surface); }
.dash-befund { font-size: 15px; line-height: 24px; color: var(--text-muted); }
.dash-befund b { font-size: 1em; }
.dash-band-zahlen { gap: 32px; }
.dash-zahl { gap: 2px; min-width: 88px; }
.dash-zahl span { font-size: 12px; }
.dash-zahl b { font-size: 24px; line-height: 32px; }
.dash-zahl.ist-rot b { color: var(--red); }
/* Diese Woche */
.dash-woche { padding: 16px 20px 16px; overflow: visible; }
.dash-woche-rahmen { overflow-x: auto; }
.dash-woche-raster { display: grid; grid-template-columns: repeat(5, minmax(140px, 1fr)); min-width: 700px;
border: 1px solid var(--line-soft); border-radius: var(--r-md); overflow: hidden; }
.dash-woche-tag { display: flex; flex-direction: column; min-width: 0; }
.dash-woche-tag + .dash-woche-tag { border-left: 1px solid var(--line-soft); }
.dash-woche-tag.ist-heute { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.dash-woche-kopf { display: flex; justify-content: space-between; align-items: baseline; padding: 8px 12px; border-bottom: 1px solid var(--line-soft); font-weight: 500; }
.dash-woche-tag.ist-heute .dash-woche-kopf > span:first-child { color: var(--accent); }
.dash-woche-datum { font-family: var(--font-mono); font-size: 12px; font-weight: 400; color: var(--text-subtle); }
.dash-woche-liste { display: flex; flex-direction: column; gap: 8px; padding: 12px; min-height: 120px; }
.dash-woche-eintrag { display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border-radius: 6px; text-decoration: none; color: var(--text);
background: var(--surface-sunk); border: 1px solid var(--line-soft); min-width: 0;
transition: background-color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out); }
.dash-woche-eintrag:hover { border-color: var(--line); }
.dash-woche-eintrag.ist-meins { background: color-mix(in srgb, var(--accent) 10%, var(--surface)); border-color: color-mix(in srgb, var(--accent) 22%, transparent); }
.dash-woche-eintrag.ist-ms { background: color-mix(in srgb, var(--meilenstein) 10%, var(--surface)); border-color: color-mix(in srgb, var(--meilenstein) 24%, transparent); }
.dash-woche-eintrag.ist-ueber { background: color-mix(in srgb, var(--red) 8%, var(--surface)); border-color: color-mix(in srgb, var(--red) 24%, transparent); }
.dash-woche-titel { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-woche-projekt { font-size: 12px; color: var(--text-subtle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-woche-leer { font-size: 12px; color: var(--text-subtle); }
.dash-woche-mehr { font-size: 12px; color: var(--text-muted); }
.dash-woche-legende { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 12px; font-size: 12px; color: var(--text-muted); }
.dash-woche-legende span { display: inline-flex; align-items: center; gap: 8px; }
.dash-woche-legende i { width: 12px; height: 4px; border-radius: 2px; background: var(--text-subtle); }
.dash-woche-legende i.ist-meins { background: var(--accent); }
.dash-woche-legende i.ist-ms { width: 8px; height: 8px; border-radius: 1px; transform: rotate(45deg); background: var(--meilenstein); }
.dash-woche-legende i.ist-ueber { background: var(--red); }
/* Projektblock: Meine Rolle | Team | Notizen */
.dash-proj-kopf .dash-proj-titel { font-size: 15px; font-weight: 500; }
.dash-proj-nr { font-family: var(--font-mono); }
.dash-proj-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); margin: 0 16px 12px;
border: 1px solid var(--line-soft); border-radius: var(--r-md); background: var(--surface-alt); }
.dash-proj-feld { display: flex; flex-direction: column; gap: 4px; padding: 12px 16px; min-width: 0; color: var(--text); text-decoration: none; }
.dash-proj-feld + .dash-proj-feld { border-left: 1px solid var(--line-soft); }
.dash-proj-feld-titel { font-size: 12px; color: var(--text-subtle); }
.dash-proj-feld-wert { display: flex; align-items: center; gap: 8px; min-width: 0; }
.dash-proj-feld-wert .board-avatar { flex: none; }
.dash-proj-team { display: inline-flex; flex: none; }
.dash-proj-team .board-avatar + .board-avatar { margin-left: -6px; }
.dash-proj-team .board-avatar { box-shadow: 0 0 0 2px var(--glas-deckend, var(--surface)); }
.dash-proj-namen { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-muted); }
.dash-proj-notizen { transition: background-color var(--dur) var(--ease-out); }
.dash-proj-notizen:hover { background: var(--surface-sunk); }
.dash-proj-notizen .icon { color: var(--text-muted); }
.dash-proj-notizen b { font-family: var(--font-mono); font-weight: 400; }
.dash-proj-zuletzt { font-size: 12px; color: var(--text-subtle); white-space: nowrap; }
@media (max-width: 640px) {
.dash-proj-feld + .dash-proj-feld { border-left: 0; border-top: 1px solid var(--line-soft); }
}
/* ===========================================================================
NOTIZEN (Seitenleiste, js/notizen/notizen.js) — nutzt die .notizen-*-Stile
des Projekt-Reiters (plan-styles.css), hier nur Kopf, Filter und Zuordnung.
=========================================================================== */
.mn-seite { display: flex; flex-direction: column; gap: 16px; }
.mn-kopf { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px; }
.mn-kopf-links { display: flex; flex-direction: column; gap: 4px; }
.mn-kopf-unter { font-size: 12px; color: var(--text-subtle); }
.mn-titel { margin: 0; font-size: 24px; line-height: 32px; font-weight: 500; }
.mn-filter, .mn-zuordnung { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-muted); }
/* Nur background-color — die Kurzschreibweise „background:“ setzte die Wiederholung zurück,
und der Dropdown-Pfeil der globalen select-Regel erschien im Dunkelmodus als Kachelmuster. */
.mn-filter select, .mn-zuordnung select { height: 32px; padding: 0 28px 0 8px; border: 1px solid var(--line); border-radius: 8px; background-color: var(--surface); color: var(--text); font: inherit; max-width: 360px; }
.mn-notizen { background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-lg); box-shadow: var(--sh-1); overflow: hidden; min-height: 520px; }
.mn-editor-zeile { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.mn-projekt-link { font-size: 13px; color: var(--accent); text-decoration: none; }
.mn-projekt-link:hover { text-decoration: underline; }
.mn-hinweis { font-size: 12px; color: var(--text-subtle); }
.mn-projekt { color: var(--text-muted); }
.mn-projekt.ist-ohne { color: var(--text-subtle); }
/* ===========================================================================
SEITENLEISTE — Gliederung nach Arbeitsbereich (Eriks Wahl „A“, 11/2026)
---------------------------------------------------------------------------
Gruppen (.sidebar-gruppe) statt loser Trennlinien: Eigenes, Projekte,
Team, Studenten; Einstellungen und Admin unten (.sidebar-unten). Einträge
fest 32 px, Icon-Spalte 18 px + 8 px Abstand, Rubrik 16 px über der Gruppe
und 4 px über ihren Einträgen. Unterpunkte (geöffnetes Projekt, Admin)
Admin-Unterpunkte beginnen auf der Linie der Beschriftungen (12 + 18 + 8 =
38 px), das geöffnete Projekt 8 px tiefer mit Führungslinie.
=========================================================================== */
.sidebar-nav { gap: 0; padding: 12px; }
.sidebar-gruppe { display: flex; flex-direction: column; gap: 2px; }
.sidebar-gruppe + .sidebar-gruppe { margin-top: 16px; }
.sidebar-gruppe.sidebar-unten { margin-top: auto; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.sidebar-gruppe > .sidebar-section-label { padding: 0 12px 4px; font-size: 12px; line-height: 16px; font-weight: 500; color: var(--text-subtle); }
.sidebar-nav .nav-item { min-height: 32px; height: 32px; padding: 0 12px; gap: 8px; font-weight: 400; color: var(--text-muted); }
.sidebar-nav .nav-item.active { font-weight: 500; color: var(--text); }
.sidebar-nav .nav-icon { width: 18px; font-size: 16px; }
/* Geöffnetes Projekt: eingezogen unter „Projekte“, mit feiner Führungslinie */
.nav-item.nav-unterpunkt { position: relative; padding-left: 46px; }
.nav-item.nav-unterpunkt .nav-icon { display: none; }
.nav-item.nav-unterpunkt::before { content: ""; position: absolute; left: 32px; top: 8px; bottom: 8px; width: 1px; background: var(--line); }
.nav-projnr { font-family: var(--font-mono); font-size: 12px; color: var(--text-subtle); }
.nav-item.active .nav-projnr { color: var(--accent); }
/* Admin: Unterpunkte auf der Beschriftungslinie, in drei Gruppen */
.sidebar-subnav { padding-left: 0; gap: 2px; }
.sidebar-subnav.open { max-height: 900px; }
/* Zwischenüberschrift: kleiner und heller als die Punkte, mit feiner Linie bis
zum Rand — hebt sich ab, ohne gesperrte Großbuchstaben. */
.sidebar-subgruppe { display: flex; align-items: center; gap: 8px; padding: 12px 12px 4px 38px; font-size: 11px; line-height: 16px; font-weight: 500; color: var(--text-subtle); }
.sidebar-subgruppe::after { content: ""; flex: 1; height: 1px; background: var(--line-soft); }
.sidebar-subgruppe:first-child { padding-top: 4px; }
/* Mit #adminSubnav, weil button.menu-item (Knopf-Grundregel) sonst Einzug und
Schriftstärke überschreibt — die Punkte standen dann ganz links und fett,
die Zwischenüberschriften wirkten in die Mitte gerutscht. */
#adminSubnav .sidebar-subitem { min-height: 32px; height: 32px; padding: 0 12px 0 38px; display: flex; align-items: center; border-radius: var(--r-md);
font-size: 13px; font-weight: 400; color: var(--text-muted); background: transparent; }
#adminSubnav .sidebar-subitem:hover { background: var(--surface-sunk); color: var(--text); }
#adminSubnav .sidebar-subitem.is-active { background: var(--surface-sunk); color: var(--text); font-weight: 500; box-shadow: inset 0 0 0 1px var(--line-soft); }
@media (min-width: 769px) {
#headerMenu.side-menu.collapsed .sidebar-gruppe + .sidebar-gruppe { margin-top: 8px; }
#headerMenu.side-menu.collapsed .nav-item.nav-unterpunkt { padding-left: 0; }
#headerMenu.side-menu.collapsed .nav-item.nav-unterpunkt::before { display: none; }
#headerMenu.side-menu.collapsed .nav-item.nav-unterpunkt .nav-icon { display: inline-flex; }
}