Files
projektuebersicht/public/styles.css
T

4822 lines
139 KiB
CSS

/* === Design-Tokens === */
:root {
--accent: #0071e3;
--accent-hover: #0077ed;
--accent-active: #006ad1;
--accent-soft: #e8f1fd;
--text: #1d1d1f;
--text-muted: #6e6e73;
--text-subtle: #86868b;
--bg: #f5f5f7;
--surface: #ffffff;
--surface-alt: #fafafc;
--surface-sunk: #f0f0f2;
--line: #d2d2d7;
--line-soft: #e8e8ed;
--green: #248a3d;
--amber: #b25000;
--red: #d70015;
--red-hover: #e8112d;
--red-active: #b8000f;
/* 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: #0071e3;
--band-fortschritt: #22823a;
/* 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;
--r-sm: 6px;
--r-md: 8px;
--r-lg: 12px;
--r-xl: 16px;
--r-pill: 980px;
--sh-1: 0 1px 2px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.06);
--sh-2: 0 2px 8px rgba(0,0,0,.06), 0 1px 3px rgba(0,0,0,.04);
--sh-3: 0 8px 28px rgba(0,0,0,.12), 0 2px 8px rgba(0,0,0,.06);
--ease: cubic-bezier(.4, 0, .2, 1);
}
/* === 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.005em;
}
.container { padding: 20px 24px; }
/* === 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,.82);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
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: 600;
margin: 0;
letter-spacing: -0.01em;
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 10px;
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 10px 10px;
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 10px;
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;
}
.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); }
/* === Show Mode === */
.show-mode-overlay {
position: fixed;
inset: 0;
background: var(--bg);
z-index: 1000;
display: flex;
flex-direction: column;
}
.show-mode-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 24px;
background: var(--surface);
border-bottom: 1px solid var(--line-soft);
font-size: 18px;
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.01em;
}
.show-mode-content {
flex: 1;
overflow: auto;
padding: 24px;
}
.show-mode-content .container {
padding: 0;
max-width: 1400px;
margin: 0 auto;
}
.show-mode-content .section-title {
margin-top: 0;
}
.show-mode-paused {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
background: var(--surface);
border: 1px solid var(--line-soft);
padding: 12px 24px;
border-radius: var(--r-pill);
box-shadow: var(--sh-3);
font-weight: 600;
z-index: 1001;
pointer-events: none;
}
/* Kompakte Darstellung im Show Mode */
.show-mode-content {
font-size: 12px;
}
.show-mode-content .status {
min-width: 160px !important;
}
.show-mode-content .status-edit {
min-height: 24px !important;
padding: 3px 5px !important;
font-size: 10px !important;
}
.show-mode-content .bar {
height: 3px !important;
margin-top: 2px !important;
}
.show-mode-content .load-grid {
gap: 14px;
}
/* Im Show Mode enger: Die Auslastung wird dort auf eine Wandfläche geworfen,
auf der alle Rollen zusammen sichtbar bleiben sollen. */
.show-mode-content .load-zeile {
padding: 7px 4px !important;
}
.show-mode-content .load-wer-name {
font-size: 12px !important;
}
.show-mode-content .load-wert {
font-size: 16px !important;
}
.show-mode-loading {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 24px;
color: var(--text-muted);
}
/* Show Mode: Auslastungs-Steuerelemente und Überschrift ausblenden */
.show-mode-overlay .show-mode-content #load .section-title,
.show-mode-overlay .show-mode-content #load .toolbar {
display: none !important;
}
/* Show Mode: Bearbeitung deaktivieren */
.show-mode-content .board-avatar,
.show-mode-content .board-lph-row input,
.show-mode-content .status-edit {
pointer-events: none !important;
cursor: default !important;
}
.show-mode-content .del-proj {
pointer-events: none !important;
}
.show-mode-content input[type=text].edit-name,
.show-mode-content input[type=checkbox] {
pointer-events: none !important;
}
.show-mode-content button {
pointer-events: none !important;
}
.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: 18px;
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: 9px 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: 600;
}
/* .sidebar-section-label: kleine Rubrik-Überschrift oberhalb einer Gruppe von
Sidebar-Einträgen (z. B. "Dashboard", "Navigation"). */
.sidebar-section-label {
padding: 10px 12px 2px;
font-size: 11px;
font-weight: 700;
letter-spacing: .04em;
text-transform: uppercase;
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 („Einstellungen", sieben Punkte); die
Animation läuft über max-height, deshalb steht hier eine feste Zahl. */
max-height: 360px;
opacity: 1;
}
.sidebar-subitem {
width: 100%;
padding-left: 34px;
font-size: 12.5px;
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: 600;
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: var(--r-pill);
padding: 7px 16px;
font-size: 13px;
font-weight: 500;
font-family: inherit;
background: var(--accent);
color: #fff;
letter-spacing: -0.01em;
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 rgba(0,113,227,.35);
}
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: 3px 10px;
font-size: 11px;
}
select,
input[type=text],
input[type=email],
input[type=date] {
padding: 7px 10px;
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 rgba(0,113,227,.18);
}
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: 5px;
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: 5px 7px;
text-align: center;
vertical-align: middle;
}
thead tr:nth-child(1) th {
background: var(--surface-sunk);
color: var(--text);
font-weight: 600;
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: 5px 7px;
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 rgba(0,113,227,.18);
}
.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: 600; }
/* === 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: 10px; }
.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 10px;
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: italic; }
.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: 9px; font-weight: 600; flex-shrink: 0;
}
.comment-kbd { margin-left: auto; font-size: 10px; color: var(--text-subtle); white-space: nowrap; }
.comment-editor textarea {
width: 100%;
display: block;
border: none;
border-radius: 0;
padding: 8px 10px;
font-family: inherit;
font-size: 12px;
line-height: 1.5;
color: var(--text);
background: var(--surface);
resize: vertical;
min-height: 62px;
}
.comment-editor textarea:focus { outline: none; box-shadow: none; }
.comment-editor textarea::placeholder { color: var(--text-subtle); }
.comment-editor-foot {
display: flex;
align-items: center;
gap: 7px;
padding: 7px 10px;
border-top: 1px solid var(--line-soft);
background: var(--surface-alt);
}
.comment-foot-spacer { margin-left: auto; }
.comment-dirty { font-size: 10.5px; color: var(--amber); white-space: nowrap; }
.comment-readonly {
border: 1px solid var(--line-soft);
border-radius: var(--r-md);
padding: 7px 9px;
font-size: 11.5px;
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: 7px 10px;
font-size: 11.5px;
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: 9px;
transition: transform .18s var(--ease);
}
.comment-history[open] > summary .comment-history-caret { transform: rotate(90deg); }
.comment-history-count {
margin-left: auto;
font-size: 10.5px;
font-weight: 600;
background: var(--accent-soft);
color: var(--accent);
border-radius: var(--r-pill);
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 10px 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: 3px;
font-size: 10.5px;
}
.comment-history-week {
font-weight: 600;
color: var(--text);
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.comment-history-meta { color: var(--text-subtle); }
.comment-history-text { font-size: 11.5px; line-height: 1.45; color: var(--text-muted); }
.comment-history-text b,
.comment-history-text strong { font-weight: 600; color: var(--text); }
.comment-take {
margin-left: auto;
padding: 2px 9px;
font-size: 10px;
border-radius: var(--r-pill);
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: 3px;
gap: 2px;
box-shadow: var(--sh-2);
z-index: 10;
}
.status:focus-within .status-toolbar { display: flex; }
.status-toolbar button {
padding: 3px 7px;
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: 600;
}
.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: 14px;
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: 11.5px;
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: 600;
letter-spacing: -0.01em;
cursor: pointer;
}
.board-name:hover { color: var(--accent); text-decoration: underline; }
.board-status-line {
margin-top: 3px;
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: 10px;
font-weight: 700;
letter-spacing: -0.02em;
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: 600;
font-family: ui-monospace, 'SF Mono', monospace;
padding: 4px 10px;
border-radius: var(--r-pill);
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: 18px 20px 22px 94px;
background: var(--surface-alt);
border-top: 1px solid var(--line-soft);
}
.board-drawer h4 {
margin: 0 0 8px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .05em;
color: var(--text-muted);
}
/* Die Schublade liefert drei gleichrangige Blöcke (Kommentar, Leistungsphasen,
Beteiligte, siehe renderBoardDrawer() in js/board/board.js). Im Projektboard
stehen Kommentar und Leistungsphasen wie bisher übereinander in der linken
Spalte, die Beteiligten rechts daneben. */
.board-drawer-grid {
display: grid;
grid-template-columns: 1.3fr 1fr;
grid-template-areas:
"kommentar beteiligte"
"lph beteiligte";
align-content: start;
gap: 16px 24px;
}
.drawer-col-comment { grid-area: kommentar; }
.drawer-col-lph { grid-area: lph; }
.drawer-col-people { grid-area: beteiligte; }
@media (max-width: 720px) {
.board-drawer-grid {
grid-template-columns: 1fr;
grid-template-areas: "kommentar" "lph" "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 10px;
}
.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: 10px;
}
.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: 10px;
flex-wrap: wrap;
padding: 9px 14px;
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: 600; }
.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: 14px 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: 22px;
}
.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) ===
Gleiche Angaben, gleiche Bedienelemente, enger gesetzt. Den Ausschlag gibt
die Leistungsphasen-Leiste: als Zeilen untereinander bestimmten bis zu zehn
Phasen die Höhe der ganzen Karte — bei 29 Projekten wurde der Bericht damit
rund 17 Bildschirmseiten lang. Nebeneinander braucht dieselbe Information
eine Zeile. */
.board-drawer-dicht .board-drawer-grid {
/* Die Beteiligten brauchen so viel Breite wie der Kommentar: Sobald ihre
Kürzel umbrechen, bestimmt ihre Spalte die Höhe der ganzen Karte. Mit
1.45/1.15/1 waren es 14,2 Bildschirmseiten, mit gleich breiter
Beteiligten-Spalte 11,6 — mehr Breite bringt darüber hinaus nichts,
weil dann ohnehin nichts mehr umbricht. */
/* Die rechte Spalte bekommt eine MINDESTBREITE: Zehn Phasenfelder in einer
reinen fr-Spalte wurden 27 px schmal, und "100" passte nicht mehr ins
Eingabefeld — 79 Werte waren abgeschnitten. Unter 620 px Containerbreite
gibt die Mindestbreite nach und die Spalten rutschen untereinander. */
grid-template-columns: minmax(0, 1fr) minmax(340px, 1fr);
grid-template-areas: "kommentar kompakt";
gap: 18px;
}
@media (max-width: 860px) {
.board-drawer-dicht .board-drawer-grid {
grid-template-columns: 1fr;
grid-template-areas: "kommentar" "kompakt";
gap: 14px;
}
}
.drawer-col-kompakt { grid-area: kompakt; min-width: 0; }
/* Die zweite Überschrift in derselben Spalte braucht Abstand nach oben, den
die erste (am Spaltenanfang) nicht haben darf. */
.drawer-h4-zweit { margin-top: 12px; }
.board-lph-leiste {
display: flex;
gap: 3px;
align-items: stretch;
}
.board-lph-cell {
flex: 1 1 0;
min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 1px;
padding: 3px 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: 10px;
font-weight: 600;
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: 11.5px;
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: 3px;
}
.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: 7px;
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: 10px;
line-height: 1.25;
letter-spacing: .02em;
text-transform: uppercase;
}
/* 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: 600;
padding: 3px 10px;
border-radius: var(--r-pill);
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-list {
display: flex;
flex-direction: column;
gap: 7px;
margin-top: 14px;
}
.board-lph-row {
display: grid;
grid-template-columns: 42px 1fr 50px;
align-items: center;
gap: 10px;
}
.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 input {
width: 100%;
border: 1px solid var(--line-soft);
border-radius: var(--r-sm);
padding: 3px 6px;
text-align: center;
font-size: 11px;
font-family: inherit;
background: var(--surface);
color: var(--text);
}
.board-lph-row.lp-aktiv .board-lph-label { color: var(--accent); font-weight: 600; }
.board-lph-row.lp-aktiv input { border-color: var(--accent); font-weight: 600; }
.board-role-mini { display: flex; flex-direction: column; gap: 10px; }
.board-role-mini .board-role-row { display: flex; align-items: center; gap: 10px; }
.board-role-mini .board-role-label {
width: 100px;
flex-shrink: 0;
font-size: 11.5px;
color: var(--text-muted);
}
.board-role-mini .board-role-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.section-title {
font-size: 17px;
font-weight: 600;
margin: 24px 0 10px;
color: var(--text);
letter-spacing: -0.02em;
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: 14px;
font-weight: 600;
margin: 0 0 6px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: .04em;
}
.load-zeile {
display: flex;
align-items: center;
gap: 16px;
padding: 11px 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 7px;
min-width: 3px;
font-size: 10px;
font-weight: 600;
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: 9px;
}
.load-wert {
flex: 0 0 62px;
text-align: right;
font-size: 19px;
font-weight: 600;
letter-spacing: -.02em;
font-variant-numeric: tabular-nums;
}
.load-anbahnung {
font-size: 10px;
padding: 1px 7px;
border-radius: var(--r-pill);
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 14px;
background: var(--surface-sunk);
border-radius: var(--r-lg);
font-size: 13px;
}
.load-kopf-zahl strong {
font-size: 17px;
font-variant-numeric: tabular-nums;
}
.load-kopf-warnung {
color: var(--red);
font-weight: 600;
}
.load-kopf-warnung::before {
content: '';
display: inline-block;
width: 7px; height: 7px;
border-radius: 50%;
background: var(--red);
margin-right: 7px;
vertical-align: middle;
}
.load-luecken {
padding: 12px 14px;
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: 600; }
.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: 3px;
}
/* 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: 14px;
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: 2px;
flex: 0 0 auto;
}
@media (max-width: 720px) {
.load-wer { flex-basis: 128px; }
.load-zeile { gap: 10px; }
.load-wert { flex-basis: 52px; font-size: 16px; }
}
.load-row-title {
grid-column: 1 / -1;
width: 100%;
font-size: 14px;
font-weight: 600;
margin: 0 0 2px 0;
padding-top: 4px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: .04em;
}
.load-card {
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-xl);
padding: 20px 22px;
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: 14px;
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.25;
}
.load-num {
font-size: 30px;
font-weight: 600;
color: var(--text);
letter-spacing: -0.03em;
line-height: 1.1;
}
.load-detail {
font-size: 11px;
color: var(--text-muted);
margin-top: 10px;
}
.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: 20px;
font-weight: 600;
letter-spacing: -0.02em;
line-height: 1.15;
}
.form-row {
margin-bottom: 14px;
display: flex;
flex-direction: column;
gap: 5px;
}
.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: 11px 15px;
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: 10px;
flex-wrap: wrap;
background: var(--surface-alt);
border: 1px solid var(--red);
color: var(--red);
border-radius: var(--r-md);
padding: 9px 13px;
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: 600;
text-align: left;
padding: 6px 8px;
white-space: nowrap;
}
.lp-matrix td {
padding: 3px 8px;
}
.lp-matrix td input[type=text] {
width: 64px;
padding: 4px 7px;
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 22px;
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: 3px 8px; }
/* === Hinweise & Hilfsklassen === */
.legend {
font-size: 11px;
color: var(--text-muted);
margin: 10px 0;
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.legend span {
display: inline-flex;
align-items: center;
gap: 5px;
}
/* === Anwesenheitsblock der Sitzung ======================================
Eigener Block oben im Projektboard (renderAttendance(), js/board/board.js).
Zustände sind bewusst binär (anwesend / nicht anwesend, Tabelle attendance);
der Zähler in der Kopfzeile macht sichtbar, wenn noch niemand geklickt hat. */
.attendance-block {
background: var(--surface);
border: 1px solid var(--line-soft);
border-radius: var(--r-lg);
box-shadow: var(--sh-1);
margin-top: 16px;
overflow: hidden;
}
.attendance-bar {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
padding: 9px 14px;
background: var(--surface-alt);
border-bottom: 1px solid var(--line-soft);
}
.attendance-title { font-weight: 600; font-size: 13px; }
.attendance-count {
font-size: 11.5px;
color: var(--text-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.attendance-spacer { margin-left: auto; }
.attendance-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 12px 14px;
}
.attendance-tile {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
width: 84px;
padding: 9px 5px 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: 600;
transition: background .15s var(--ease), color .15s var(--ease);
}
.attendance-name {
font-size: 10.5px;
line-height: 1.25;
text-align: center;
word-break: break-word;
}
.attendance-note {
font-size: 9.5px;
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: 700;
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: 11.5px; color: var(--red); }
.hint {
color: var(--text-subtle);
font-size: 12px;
margin: 8px 0;
}
.del-proj {
color: var(--red);
cursor: pointer;
font-weight: 600;
}
.lock-notice button { margin-left: 12px; }
input[type=text].edit-name {
border: 1px solid transparent;
background: transparent;
font-weight: 600;
width: 100%;
font-size: 12px;
padding: 3px 5px;
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 rgba(0,113,227,.18);
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"]) {
--accent: #0a84ff;
--accent-hover: #2f95ff;
--accent-active: #0060df;
--accent-soft: #10263f;
--text: #f5f5f7;
--text-muted: #a1a1a6;
--text-subtle: #86868b;
--bg: #000000;
--surface: #1c1c1e;
--surface-alt: #232326;
--surface-sunk: #2c2c2e;
--line: #3a3a3c;
--line-soft: #2c2c2e;
--green: #30d158;
--amber: #ff9f0a;
--red: #ff453a;
--red-hover: #ff453a;
--red-active: #a3000f;
--attend-bg: rgba(48,209,88,.16);
--attend-bg-hover: rgba(48,209,88,.24);
--band-neu: #0a6fd0;
--band-fortschritt: #1e7a38;
--protokoll-neu-bg: rgba(255,214,10,.14);
--protokoll-neu-border: #ffd60a;
--sh-1: 0 1px 2px rgba(0,0,0,.5), 0 1px 3px rgba(0,0,0,.4);
--sh-2: 0 2px 10px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.4);
--sh-3: 0 12px 40px rgba(0,0,0,.7), 0 2px 10px rgba(0,0,0,.5);
}
}
html[data-theme="dark"] {
color-scheme: dark;
--accent: #0a84ff;
--accent-hover: #2f95ff;
--accent-active: #0060df;
--accent-soft: #10263f;
--text: #f5f5f7;
--text-muted: #a1a1a6;
--text-subtle: #86868b;
--bg: #000000;
--surface: #1c1c1e;
--surface-alt: #232326;
--surface-sunk: #2c2c2e;
--line: #3a3a3c;
--line-soft: #2c2c2e;
--green: #30d158;
--amber: #ff9f0a;
--red: #ff453a;
--red-hover: #ff453a;
--red-active: #a3000f;
--attend-bg: rgba(48,209,88,.16);
--attend-bg-hover: rgba(48,209,88,.24);
--band-neu: #0a6fd0;
--band-fortschritt: #1e7a38;
--protokoll-neu-bg: rgba(255,214,10,.14);
--protokoll-neu-border: #ffd60a;
--sh-1: 0 1px 2px rgba(0,0,0,.5), 0 1px 3px rgba(0,0,0,.4);
--sh-2: 0 2px 10px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.4);
--sh-3: 0 12px 40px rgba(0,0,0,.7), 0 2px 10px rgba(0,0,0,.5);
}
/* Gemeinsame Dark-Style-Regeln (System oder manuell) */
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) .app-topbar {
background: rgba(28,28,30,.75);
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"]) .show-mode-overlay { background: var(--bg); }
html:not([data-theme="light"]) .show-mode-header {
background: var(--surface);
border-bottom-color: var(--line);
}
html:not([data-theme="light"]) .show-mode-paused {
background: var(--surface);
border-color: var(--line);
}
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 rgba(10,132,255,.45); }
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"]) .board-lph-row input {
background: var(--surface-sunk);
border-color: var(--line);
color: var(--text);
}
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(28,28,30,.75);
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"] .show-mode-overlay { background: var(--bg); }
html[data-theme="dark"] .show-mode-header {
background: var(--surface);
border-bottom-color: var(--line);
}
html[data-theme="dark"] .show-mode-paused {
background: var(--surface);
border-color: var(--line);
}
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 rgba(10,132,255,.45); }
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"] .board-lph-row input {
background: var(--surface-sunk);
border-color: var(--line);
color: var(--text);
}
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 {
width: 220px;
flex: 0 0 220px;
min-width: 220px;
max-width: 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: 10px;
color: var(--text-muted);
padding: 2px 4px;
white-space: nowrap;
overflow: hidden;
box-sizing: border-box;
}
.gantt-month {
background: var(--surface-sunk);
font-weight: 600;
}
.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: 600;
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-bar.todo.critical {
background: var(--red);
box-shadow: 0 0 0 2px rgba(215,0,21,.25);
}
.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 7px;
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;
filter: drop-shadow(0 0 3px rgba(0, 113, 227, 0.4));
}
.gantt-dep-badge {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 10px;
padding: 1px 6px;
border-radius: var(--r-pill);
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 rgba(0, 113, 227, 0.2);
}
.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 7px;
font-size: 10px;
}
.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: 10px;
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: 288px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-3);
padding: 10px;
font-size: 13px;
}
.range-picker-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
}
.range-picker-title {
font-weight: 600;
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: repeat(7, 1fr);
gap: 2px;
}
.range-picker-weekday {
text-align: center;
font-size: 10px;
font-weight: 600;
color: var(--text-muted);
padding: 2px 0;
}
.range-picker-day {
text-align: center;
padding: 5px 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: 600;
}
.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: var(--r-pill);
padding: 3px 10px;
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: var(--r-pill);
padding: 2px 8px;
font-size: 11px;
font-weight: 400;
color: var(--text-muted);
}
.tag-chip .tag-remove {
cursor: pointer;
font-weight: 600;
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: 22px;
font-weight: 600;
letter-spacing: -.02em;
}
.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: 600;
padding: 2px 9px;
border-radius: var(--r-pill);
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: 22px;
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: 17px;
}
/* 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: 600;
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: 600;
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: 10px;
margin-bottom: 8px;
}
.dash-abschnitt-titel {
margin: 0;
font-size: 11.5px;
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
color: var(--text-muted);
}
.dash-abschnitt-zahl {
font-size: 11px;
padding: 1px 7px;
border-radius: var(--r-pill);
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 18px; font-size: 13px; color: var(--text-subtle); }
/* ---------- Meine Aufgaben: Zeilen statt halbseitiger Karten ---------- */
.dash-auf {
display: flex;
align-items: center;
gap: 14px;
padding: 11px 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: 13.5px;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.dash-auf-meta {
display: flex;
align-items: center;
gap: 7px;
flex-wrap: wrap;
margin-top: 1px;
font-size: 11px;
color: var(--text-subtle);
}
.dash-auf-tag {
font-size: 10px;
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: 10.5px; 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: 10px;
padding: 10px 14px;
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: 10.5px;
font-weight: 600;
background: var(--surface-sunk);
color: var(--text-muted);
}
.dash-rand-text { flex: 1; min-width: 0; }
.dash-rand-name {
display: block;
font-size: 12.5px;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.dash-rand-meta {
display: block;
font-size: 10.5px;
color: var(--text-subtle);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.dash-chip {
flex: 0 0 auto;
font-size: 10px;
padding: 1px 7px;
border-radius: var(--r-pill);
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 18px;
box-shadow: var(--sh-1);
}
.settings-card-title {
margin: 0 0 10px;
font-size: 14px;
font-weight: 600;
display: flex;
align-items: baseline;
gap: 10px;
flex-wrap: wrap;
}
/* Reichweite einer Einstellung — „firmenweit" heißt: wirkt bei allen. */
.settings-scope {
font-size: 11px;
font-weight: 500;
letter-spacing: .04em;
text-transform: uppercase;
color: var(--text-muted);
background: var(--surface-alt);
border-radius: var(--r-pill);
padding: 2px 8px;
}
.settings-kv { display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; }
.settings-kv-row {
display: flex;
gap: 10px;
font-size: 12.5px;
padding: 5px 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: 12.5px;
margin-bottom: 10px;
}
.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: 14px 16px; }
.ai-provider + .ai-provider { margin-top: 12px; }
.ai-provider-head { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.ai-provider-info { min-width: 0; }
.ai-provider-mark {
width: 26px; height: 26px; border-radius: 7px;
display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700; color: #fff; flex-shrink: 0; letter-spacing: -.02em;
}
.ai-provider-mark.chatgpt { background: #10a37f; }
.ai-provider-mark.claude { background: #c96442; }
.ai-provider-name { font-weight: 600; font-size: 13px; }
.ai-provider-sub { font-size: 11.5px; 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: 11.5px; font-weight: 500;
padding: 3px 10px 3px 8px;
border-radius: var(--r-pill);
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: 7px 10px;
font-size: 12.5px;
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: 11.5px; 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: var(--r-pill);
padding: 3px; gap: 2px; margin-top: 12px;
}
.ai-mode-toggle button {
border: none; background: none; padding: 5px 13px; border-radius: var(--r-pill);
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: 600; 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: 5px; }
.ai-url-panel .ai-provider-row { margin-bottom: 0; }
/* "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: 11.5px; 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: 14px;
font-size: 12.5px;
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: 10px;
font-size: 12.5px;
}
.settings-field input { width: 88px; padding: 4px 7px; font-size: 12.5px; text-align: right; }
.settings-check {
display: flex;
align-items: center;
gap: 8px;
font-size: 12.5px;
margin: 4px 0 2px;
}
.settings-subtitle {
margin: 16px 0 4px;
font-size: 12.5px;
font-weight: 600;
}
.settings-matrix-wrap { overflow-x: auto; margin-bottom: 10px; }
.settings-matrix { border-collapse: collapse; font-size: 12px; }
.settings-matrix th, .settings-matrix td {
padding: 3px 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: 3px 6px; font-size: 12px; text-align: right; }
.settings-holiday-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
/* 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: var(--r-pill);
padding: 1px 7px;
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: 14px; }
td.actions-cell { white-space: nowrap; }
td.actions-cell button { margin: 2px; }
.badge-active { color: var(--green); font-weight: 600; }
.badge-inactive { color: var(--text-muted); }
.badge-warn { color: var(--amber); font-weight: 600; }
/* Das einmalig angezeigte Passwort laesst sich mit einem Klick markieren. */
#generatedPasswordBox {
display: none;
margin-bottom: 14px;
padding: 12px 14px;
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: 10px;
}
.timeline-block > summary {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 12.5px;
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: 10px; }
/* 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: 10px;
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: 10px 11px;
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: 600; 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. */
.timeline-text {
display: -webkit-box;
-webkit-line-clamp: 6;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.45;
min-height: 3em;
}
.timeline-leer { color: var(--text-muted); font-style: italic; }
.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: 11.5px; }
/* 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: 600; }
/* "ü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: var(--r-pill);
background: transparent;
color: var(--text-muted);
font-family: inherit;
font-size: 10.5px;
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: 3px; margin-top: auto; padding-top: 2px; }
.timeline-chip {
font-size: 10.5px;
font-weight: 600;
padding: 1px 6px;
border-radius: var(--r-pill);
background: var(--surface-sunk);
color: var(--text-muted);
}
/* === Wochenbericht als Tagesordnung (js/wochenbericht/wochenbericht.js) ===
Links die vollständige Projektliste, rechts das gewählte Projekt. Die
frühere Abfolge gestapelter Karten war bei 29 Projekten 18 Bildschirmseiten
lang; hier bleibt die Liste stehen und man sieht jederzeit, wo man steht
und was noch kommt. */
.bericht-titelzeile { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.bericht-titelzeile button { margin-left: auto; }
.bericht-kopf {
display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
margin-bottom: 12px;
}
.bericht-gewerk-select { font-size: 12.5px; padding: 5px 10px; min-width: 170px; }
/* ---------- Planung (Terminplan-Sandbox, js/planung/planung.js) ---------- */
.planung-row {
display: flex; align-items: center; gap: 14px;
padding: 13px 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: 9px; flex: 0 0 auto;
display: flex; align-items: center; justify-content: center; font-size: 14px;
}
.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: 14px; font-weight: 600; 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: 10px; }
.choice-card {
border: 1.5px solid var(--line); border-radius: var(--r-md); padding: 14px 14px 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: 18px; margin-bottom: 8px; display: block; }
.choice-card-title { font-size: 13px; font-weight: 600; margin-bottom: 3px; }
.choice-card-desc { font-size: 11.5px; 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; }
.bericht-fortschritt { display: flex; align-items: center; gap: 9px; margin-left: auto; }
.bericht-fortschritt-text { font-size: 12.5px; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bericht-fortschritt-bar { width: 120px; height: 6px; border-radius: 3px; background: var(--surface-sunk); overflow: hidden; }
.bericht-fortschritt-bar i { display: block; height: 100%; background: var(--green); transition: width .25s var(--ease); }
.bericht-split {
display: grid;
grid-template-columns: 272px minmax(0, 1fr);
gap: 16px;
align-items: start;
}
@media (max-width: 900px) { .bericht-split { grid-template-columns: 1fr; } }
.bericht-liste-spalte {
background: var(--surface); border-radius: var(--r-md); box-shadow: var(--sh-1);
overflow: hidden;
/* Die Liste bleibt beim Scrollen im Blick — sie IST die Tagesordnung. */
position: sticky; top: 64px;
}
.bericht-suche { padding: 8px 9px; border-bottom: 1px solid var(--line-soft); }
.bericht-suche input { width: 100%; padding: 5px 9px; font-size: 12.5px; }
.bericht-liste { max-height: min(62vh, 620px); overflow-y: auto; }
.bericht-gruppe {
font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
color: var(--text-subtle); padding: 9px 11px 3px;
}
.bericht-zeile {
display: flex; align-items: center; gap: 7px;
padding: 5px 10px; cursor: pointer;
border-left: 2px solid transparent;
font-size: 12.5px;
}
.bericht-zeile:hover { background: var(--surface-alt); }
.bericht-zeile:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.bericht-zeile.is-aktiv { background: var(--accent-soft); border-left-color: var(--accent); }
/* Abgehakt: zurückgenommen, aber lesbar — man muss im Meeting noch erkennen
können, was man besprochen hat. */
.bericht-zeile.is-ab .bericht-zeile-name,
.bericht-zeile.is-ab .bericht-zeile-nr { color: var(--text-subtle); }
.bericht-zeile-nr {
font-family: ui-monospace, 'SF Mono', monospace; font-size: 11px;
color: var(--text-muted); flex-shrink: 0;
}
.bericht-zeile-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bericht-haken {
width: 17px; height: 17px; flex-shrink: 0; padding: 0;
display: inline-flex; align-items: center; justify-content: center;
border: 1.5px solid var(--line); border-radius: 50%;
background: transparent; color: transparent;
font-size: 10px; line-height: 1; cursor: pointer;
}
.bericht-haken:hover:not(:disabled) { border-color: var(--green); color: var(--green); }
.bericht-haken.ab { background: var(--green); border-color: var(--green); color: #fff; }
.bericht-haken:disabled { cursor: default; opacity: .5; }
/* Änderungs-Kennzeichen: nur noch ein Punkt in der Liste — die Gruppierung
nach Änderungsart ist entfallen, weil im Meeting ohnehin jedes Projekt
besprochen wird. Die ausformulierte Begründung steht im Detail. */
/* Die Zustandsklassen tragen ein "is-"-Präfix: Ohne es hieß eine davon
schlicht "status" und traf die Regel .status { min-width: 240px } weiter
oben — der 7-px-Punkt wurde 240 px breit und quetschte den Projektnamen
auf null. */
.bericht-punkt { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.bericht-punkt.is-new, .bericht-punkt.is-status { background: var(--band-neu); }
.bericht-punkt.is-progress { background: var(--band-fortschritt); }
.bericht-abwesend {
font-size: 10px; font-weight: 600; color: var(--amber);
background: color-mix(in srgb, var(--amber) 15%, transparent);
border-radius: var(--r-pill); padding: 0 5px; flex-shrink: 0;
}
.bericht-detail {
background: var(--surface); border-radius: var(--r-md); box-shadow: var(--sh-1);
padding: 15px 17px 16px; min-width: 0;
}
.bericht-detail-kopf { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.bericht-detail-kopf h3 { margin: 0; font-size: 16px; font-weight: 600; }
.bericht-detail-zaehler {
font-family: ui-monospace, 'SF Mono', monospace; font-size: 11px;
color: var(--text-subtle); font-variant-numeric: tabular-nums;
}
.bericht-detail-meta { color: var(--text-muted); font-size: 12px; margin: 2px 0 12px; }
.bericht-kennz {
font-size: 11px; border-radius: var(--r-pill); padding: 1px 9px; color: #fff;
}
.bericht-kennz.is-new, .bericht-kennz.is-status { background: var(--band-neu); }
.bericht-kennz.is-progress { background: var(--band-fortschritt); }
.bericht-abwesend-hinweis {
font-size: 12.5px; color: var(--amber);
background: color-mix(in srgb, var(--amber) 10%, transparent);
border-radius: var(--r-sm); padding: 6px 10px; margin-bottom: 10px;
}
/* Der Kommentar der Vorwoche steht ÜBER dem Eingabefeld: "Was hatten wir
letzte Woche vereinbart?" ist die häufigste Frage im Meeting. */
.bericht-vorwoche {
display: flex; gap: 9px; align-items: baseline;
padding: 7px 10px; margin-bottom: 11px;
background: var(--surface-alt); border-left: 2px solid var(--line);
border-radius: 0 var(--r-sm) var(--r-sm) 0;
font-size: 12.5px;
}
.bericht-vorwoche-label { font-weight: 600; color: var(--text-muted); white-space: nowrap; flex-shrink: 0; }
.bericht-vorwoche-text { color: var(--text-muted); }
.bericht-detail-fuss {
display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line-soft);
}
.bericht-fuss-spacer { flex: 1; }
.person-chip-label { display: inline-flex; align-items: center; gap: 5px; 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: 10px;
margin-bottom: 12px;
}
.leistung-kpi {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1 1 160px;
min-width: 150px;
padding: 10px 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);
text-transform: uppercase;
letter-spacing: .03em;
}
.leistung-kpi-value {
font-size: 19px;
font-weight: 600;
font-variant-numeric: tabular-nums;
line-height: 1.2;
}
.leistung-kpi-value.accent { color: var(--accent); }
.leistung-kpi-sub {
font-size: 11.5px;
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 14px;
}
.leistung-balken > span {
display: block;
height: 100%;
background: var(--accent);
border-radius: var(--r-pill);
transition: width .25s var(--ease);
}
.leistung-table { font-size: 12.5px; }
.leistung-table th.num,
.leistung-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.leistung-table td.strong { font-weight: 600; }
.leistung-table tfoot th,
.leistung-table tfoot td {
border-top: 1px solid var(--line);
padding: 6px 8px;
font-weight: 600;
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: 12.5px;
color: var(--text-muted);
padding: 10px 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: var(--r-pill);
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: var(--r-pill);
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 {
font-size: 12px;
padding: 3px 8px;
border: 1px solid var(--line);
border-radius: var(--r-sm);
background: var(--surface);
color: var(--text);
max-width: 100%;
}
.auftrag-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* ===========================================================
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;
}
/* 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-lph-row.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: 10px;
padding: 8px 2px 2px;
}
.plan-verweis{
background: none;
border: 1px solid var(--line);
border-radius: var(--r-pill);
padding: 3px 12px;
font-size: 12px;
font-weight: 600;
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-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: 10px;
}
.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: 9px;
padding: 9px 16px 6px;
background: var(--surface-alt);
font-size: 10.5px;
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
color: var(--text-muted);
}
.fristen-gruppe-titel span {
font-size: 10px;
padding: 1px 7px;
border-radius: var(--r-pill);
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: 14px;
padding: 9px 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: 600; }
.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: 10px;
font-weight: 600;
padding: 2px 8px;
border-radius: var(--r-pill);
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: 13.5px;
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: 18px 24px 14px;
border-bottom: 1px solid var(--line-soft);
}
.wiz-kopf h2 { margin: 0; flex: 1; }
.wiz-schliessen {
font-size: 20px;
line-height: 1;
padding: 2px 9px;
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: 600;
background: var(--surface-sunk);
color: var(--text-muted);
font-variant-numeric: tabular-nums;
}
.wiz-stufe.ist-aktiv { color: var(--text); font-weight: 600; }
.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 22px;
min-height: 300px;
max-height: 58vh;
overflow-y: auto;
}
.wiz-titel { margin: 0 0 3px; font-size: 19px; font-weight: 600; letter-spacing: -.02em; }
.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: 14px 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: 11.5px; 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: 5px 11px;
cursor: pointer;
border: 1px solid var(--line);
border-radius: var(--r-pill);
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: 600; letter-spacing: .02em; padding: 6px 14px; }
/* 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: 11.5px;
font-weight: 600;
padding-top: 7px;
color: var(--text-muted);
}
.wiz-person {
font: inherit;
font-size: 11px;
font-weight: 600;
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: 11.5px;
font-weight: 600;
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: 10px; }
.wiz-fremd-chip {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11px;
padding: 3px 6px 3px 10px;
border-radius: var(--r-pill);
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: 18px 20px;
border: 1px dashed var(--line);
border-radius: var(--r-lg);
background: var(--surface-alt);
}
.wiz-sperre b { display: block; font-size: 14px; 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: 9px; font-size: 13.5px; 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: 10px;
padding: 7px 12px;
font-size: 12.5px;
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: 10.5px;
font-weight: 600;
letter-spacing: .05em;
text-transform: uppercase;
color: var(--text-subtle);
}
.wiz-plan-summe { background: var(--surface-alt); font-weight: 600; }
/* Zusammenfassung */
.wiz-pruef { font-size: 13.5px; }
.wiz-pruef-zeile {
display: flex;
justify-content: space-between;
gap: 18px;
padding: 7px 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: italic; }
/* 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: 20px; font-weight: 600; }
.wiz-fertig p { margin: 0 auto 20px; color: var(--text-muted); max-width: 46ch; font-size: 13.5px; }
@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; }
}