- Admin → Update: neuere Versionen (Tags) vom Git-Server anzeigen und per Knopf einspielen; ausgeführt vom Dienst auf dem Rechner (Docker/update-einrichten.sh, Docker/update-ausfuehren.sh), nur vorwärts, Fehler bei ausbleibendem Neustart, abgebrochene Läufe sperren nicht - Tests (npm test): Zusammenführen paralleler Änderungen, Rechteprüfung beim Speichern; SESSIONS_DIR lenkt den Datenordner für Testserver um - node_modules, Sicherungen und Uploads aus dem Repo genommen - cors und requireRole() entfernt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6553 lines
261 KiB
CSS
6553 lines
261 KiB
CSS
/* === 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; }
|
||
/* Kommentare mehrerer Gewerke, Darstellung „B – Überschrift mit Linie“
|
||
(Eriks Wahl): Gewerk in seiner Farbe, feine Linie, Verfasser und Zeit,
|
||
darunter der Text. Systemtext, nicht Teil des Kommentars. */
|
||
.kommentar-gewerke { display: flex; flex-direction: column; gap: 16px; }
|
||
.kommentar-block { display: flex; flex-direction: column; gap: 4px; }
|
||
.kommentar-kopf { display: flex; align-items: center; gap: 12px; }
|
||
.kommentar-gewerk { flex: none; font-size: 12px; font-weight: 500; letter-spacing: 0; color: var(--text-muted); }
|
||
.kommentar-gewerk.gewerk-elt { color: var(--gewerk-elt); }
|
||
.kommentar-gewerk.gewerk-hls { color: var(--gewerk-hls); }
|
||
.kommentar-gewerk.gewerk-arch { color: var(--gewerk-arch); }
|
||
.kommentar-linie { flex: 1; height: 1px; background: var(--line-soft); min-width: 16px; }
|
||
.kommentar-meta { flex: none; font-size: 12px; font-weight: 400; color: var(--text-subtle); white-space: nowrap; }
|
||
:root { --gewerk-elt: var(--accent); --gewerk-hls: var(--green); --gewerk-arch: var(--amber); }
|
||
.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; }
|
||
}
|
||
|
||
/* ===========================================================================
|
||
AUFGABEN IM WOCHENBERICHT (Eriks Wahl „B“, renderBerichtAufgaben() in
|
||
js/wochenbericht/wochenbericht.js): drei Spalten unter der Projektkarte,
|
||
Kopf wie „Verlauf der Vorwochen“ (.bv-kopf), rote Zahl in der Tagesordnung.
|
||
=========================================================================== */
|
||
.bericht-rail-ueber { font-family: var(--font-mono); font-size: 11px; color: var(--red); font-variant-numeric: tabular-nums; flex-shrink: 0; }
|
||
.bericht-aufgaben { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--line-soft); }
|
||
.ba-spalten { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin-top: 4px; }
|
||
@media (max-width: 1100px) { .ba-spalten { grid-template-columns: minmax(0, 1fr); } }
|
||
.ba-spalte { display: grid; gap: 2px; align-content: start; min-width: 0; }
|
||
.ba-spalte-kopf { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; color: var(--text-subtle);
|
||
padding: 0 8px 4px; border-bottom: 1px solid var(--line-soft); margin-bottom: 4px; }
|
||
.ba-zahl { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text-muted); }
|
||
.ba-spalte-kopf.is-rot .ba-zahl { color: var(--red); }
|
||
.ba-zeile { display: grid; grid-template-columns: 16px minmax(0, 1fr) auto 48px; gap: 8px; align-items: center;
|
||
min-height: 32px; padding: 0 8px; border-radius: 6px; font: inherit; font-size: 13px; color: var(--text);
|
||
background: none; border: 0; box-shadow: none; text-align: left; width: 100%; }
|
||
button.ba-zeile { cursor: pointer; transition: background-color var(--dur) var(--ease-out); }
|
||
button.ba-zeile:hover { background: var(--surface-sunk); }
|
||
button.ba-zeile:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
||
.ba-titel { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.ba-wer { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); white-space: nowrap; }
|
||
.ba-datum { font-family: var(--font-mono); font-size: 11px; color: var(--text-subtle); text-align: right; font-variant-numeric: tabular-nums; }
|
||
.ba-spalte-kopf.is-rot ~ .ba-zeile .ba-datum { color: var(--red); }
|
||
.ba-neu { font-size: 11px; color: var(--amber); margin-left: 6px; }
|
||
.ba-fort { font-family: var(--font-mono); font-size: 11px; color: var(--text-subtle); margin-left: 6px; }
|
||
.ba-haken { width: 14px; height: 14px; border-radius: 4px; border: 1px solid var(--line); display: grid; place-items: center; }
|
||
.ba-haken.is-an { background: var(--green); border-color: var(--green); }
|
||
.ba-haken.is-an::after { content: ""; width: 6px; height: 3px; border: solid var(--bg); border-width: 0 0 1.5px 1.5px; transform: translate(0, -1px) rotate(-45deg); }
|
||
.ba-haken.is-an ~ .ba-titel { color: var(--text-subtle); text-decoration: line-through; text-decoration-color: var(--line); }
|
||
.ba-raute { width: 9px; height: 9px; margin-left: 2px; background: var(--meilenstein); transform: rotate(45deg); }
|
||
.ba-leer, .ba-hinweis { font-size: 12px; color: var(--text-subtle); padding: 4px 8px; }
|
||
.ba-hinweis { padding: 8px 0 0; }
|
||
|
||
/* ===========================================================================
|
||
ADMIN → UPDATE (loadAdminUpdate() in js/admin/admin.js): Statuszeile wie
|
||
bei Teams, Liste der neueren Versionen, Fortschritt eines laufenden Updates.
|
||
=========================================================================== */
|
||
#adminUpdateInhalt { display: grid; gap: 12px; }
|
||
.update-version { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
||
.update-liste { display: grid; border: 1px solid var(--line-soft); border-radius: 8px; overflow: hidden; }
|
||
.update-eintrag { display: grid; grid-template-columns: 72px minmax(0, 1fr) auto; gap: 12px; align-items: baseline; padding: 8px 12px; font-size: 13px; }
|
||
.update-eintrag + .update-eintrag { border-top: 1px solid var(--line-soft); }
|
||
.update-eintrag .update-text { color: var(--text-muted); }
|
||
.update-eintrag .update-datum { font-family: var(--font-mono); font-size: 11px; color: var(--text-subtle); white-space: nowrap; }
|
||
.update-meldung { display: grid; gap: 4px; padding: 12px 16px; border-radius: 8px; background: var(--surface-sunk); font-size: 13px; }
|
||
.update-meldung > span { color: var(--text-muted); }
|
||
.update-meldung > button { justify-self: start; margin-top: 4px; }
|
||
.update-meldung.ist-ok { box-shadow: inset 3px 0 0 var(--green); }
|
||
.update-meldung.ist-fehler { box-shadow: inset 3px 0 0 var(--red); }
|
||
.update-meldung.ist-lauf { box-shadow: inset 3px 0 0 var(--accent); }
|
||
.update-schritte { list-style: none; margin: 4px 0; padding: 0; display: flex; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--text-subtle); }
|
||
.update-schritte li { display: inline-flex; align-items: center; gap: 6px; }
|
||
.update-schritte li::before { content: ""; width: 8px; height: 8px; border-radius: 50%; border: 1px solid var(--line); }
|
||
.update-schritte li.ist-fertig::before { background: var(--green); border-color: var(--green); }
|
||
.update-schritte li.ist-jetzt { color: var(--text); }
|
||
.update-schritte li.ist-jetzt::before { background: var(--accent); border-color: var(--accent); }
|
||
.update-log { margin: 4px 0 0; max-height: 200px; overflow: auto; padding: 8px; border-radius: 6px; background: var(--surface);
|
||
font-family: var(--font-mono); font-size: 11px; line-height: 16px; white-space: pre-wrap; color: var(--text-muted); }
|
||
@media (max-width: 560px) { .update-eintrag { grid-template-columns: 72px minmax(0, 1fr); } .update-eintrag .update-datum { grid-column: 2; } }
|