/* === 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 -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 in einer
Knopfleiste sah sonst aus wie ein blauer Standardlink, weil .secondary
ausschließlich für