Beteiligten-Zuordnung verlagert: p.assign ist jetzt ein Stammdatum und nur noch in den Projekteinstellungen editierbar (planCycleAssign()). Wochenbericht und Projektboard zeigen sie nur noch an, mit Verweis dorthin (springZuEinstellungen()). cycleAssign() ist damit entfallen. Neu "TODOs aus Protokoll": ein externes Besprechungsprotokoll als PDF wird per KI eingelesen und daraus in einem zweiten KI-Schritt Aufgaben abgeleitet. Eigener Bereich in der Aufgaben-/GANTT-Leiste, bewusst unabhaengig von den gespeicherten Protokollen - nichts davon wird abgelegt. Drei Schritte in einem Dialog mit fester Fusszeile; die Vorschlagsliste als kompakte, aufklappbare Zeilen, weil ein echter Import leicht 20 Vorschlaege liefert. Sichtbarkeit je Aufgabe im GANTT: neue Spalte plan_todos.show_in_gantt (Vorgabe 1, idempotente Migration) und eine Checkbox im Aufgaben-Dialog. Eine terminierte Aufgabe kann damit gezielt aus dem Terminplan herausgehalten werden, bleibt sonst aber eine vollwertige Aufgabe. plan-build-gantt.js und plan-drag.js pruefen dieselbe Bedingung, damit Zeilenzahl und Umsortierung nicht auseinanderlaufen. KI-Timeout dritte Stufe: ein abweichender, aber entfernter Endpunkt (Drittanbieter-Proxy) bekommt 2 Minuten statt der 45 Sekunden der echten Anbieter-API. Gemessen: die Punkte-Erkennung eines realen Protokolls brauchte dort 39 Sekunden, lag also stets knapp unter dem alten Limit und brach bei jeder Schwankung ab. Bewegung: Hover- und Klick-Rueckmeldung als Token-Satz in :root, angewandt auf die anklickbaren Zeilen, Schalter und Dialoge. Die Akzentleiste ist ein inset-Schatten statt eines positionierten Elements, damit Drag-and-Drop unberuehrt bleibt; prefers-reduced-motion war bisher gar nicht vorhanden und kippt jetzt einfach die Tokens. Plan Checker: optionaler Projektabgleich des Plankopfs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1118 lines
51 KiB
CSS
1118 lines
51 KiB
CSS
/* ===========================================================================
|
|
Projektbereich — Stile des Projektplan-Bereichs
|
|
---------------------------------------------------------------------------
|
|
Diese Regeln standen früher als <style>-Block in der eigenständigen Seite
|
|
public/project-plan.html. Seit der Projektbereich ein normaler Bereich der
|
|
Hauptseite ist (Test.html, Bereich #plan — er wird wie jeder andere über
|
|
showTab() eingeblendet), liegen sie in einer eigenen Datei, die Test.html
|
|
zusaetzlich zu styles.css laedt. Die Klassennamen sind unveraendert
|
|
(.plan-page, .plan-nav, .plan-section, .plan-kpi-*, .gantt-*, …), damit die
|
|
Module unter public/js/plan/ nichts davon wissen muessen.
|
|
=========================================================================== */
|
|
|
|
.plan-page { display: flex; flex-direction: column; }
|
|
.plan-header {
|
|
display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
|
|
padding: 12px 24px; background: var(--surface); border-bottom: 1px solid var(--line-soft);
|
|
}
|
|
.plan-header h1 { font-size: 16px; margin: 0; font-weight: 600; white-space: nowrap; }
|
|
.plan-header .plan-id {
|
|
font-family: ui-monospace, 'SF Mono', monospace; font-size: 12.5px; color: var(--text-muted); white-space: nowrap;
|
|
}
|
|
.plan-header .subtitle { color: var(--text-muted); font-size: 12.5px; white-space: nowrap; }
|
|
.plan-header-spacer { flex: 1; min-width: 12px; }
|
|
.plan-header-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
|
|
.plan-body { flex: 1; display: flex; gap: 0; }
|
|
.plan-main { flex: 1; display: flex; flex-direction: column; background: var(--bg); min-width: 0; }
|
|
.plan-main-header {
|
|
padding: 0 0 14px; display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
|
|
}
|
|
.plan-main-content { flex: 1; padding: 20px 20px 20px; min-width: 0; }
|
|
.todo-row:last-child { margin-bottom: 0; }
|
|
/* .plan-avatar-stack: leicht überlappende Avatar-Kreise der Beteiligten in
|
|
der kompakten Titelzeile (siehe .board-avatar in styles.css für die
|
|
Kreis-/Farb-Definitionen selbst). */
|
|
.plan-avatar-stack { display: flex; flex-shrink: 0; }
|
|
.plan-avatar-stack .board-avatar { margin-left: -7px; border: 2px solid var(--surface); }
|
|
.plan-avatar-stack .board-avatar:first-child { margin-left: 0; }
|
|
.todo-row-item.done { opacity: .55; text-decoration: line-through; }
|
|
/* Farben aus den Theme-Variablen: `background: var(--text)` mit fester
|
|
weißer Schrift ergab im Dunkelmodus weiß auf weiß. */
|
|
/* Die Regeln der früheren TODO-Liste (.todo-item, .todo-list-main, .milestone-row …)
|
|
sind entfallen: Der Bereich "Aufgaben" rendert seit dem Umbau .plan-task-*
|
|
(siehe weiter unten und js/plan/plan-tasks.js). */
|
|
|
|
.plan-toast {
|
|
position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%) translateY(20px);
|
|
background: var(--surface); color: var(--text); border: 1px solid var(--line);
|
|
padding: 10px 18px; border-radius: var(--r-pill);
|
|
font-size: 13px; opacity: 0; pointer-events: none; transition: opacity .25s, transform .25s;
|
|
z-index: 1000; box-shadow: var(--sh-3);
|
|
}
|
|
.plan-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
.plan-resource-grid {
|
|
display: grid !important;
|
|
grid-template-columns: repeat(auto-fill, minmax(140px, max-content)) !important;
|
|
gap: 10px !important;
|
|
justify-content: start !important;
|
|
}
|
|
.plan-resource-card {
|
|
padding: 8px 10px !important;
|
|
min-height: 0 !important;
|
|
width: 140px !important;
|
|
max-width: 140px !important;
|
|
}
|
|
.plan-resource-card h3 {
|
|
font-size: 12px !important;
|
|
margin-bottom: 2px !important;
|
|
}
|
|
.plan-resource-clickable { cursor: pointer; transition: box-shadow .15s, transform .15s; }
|
|
.plan-resource-clickable:hover { box-shadow: var(--sh-2); transform: translateY(-1px); }
|
|
.todo-depends-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
max-height: 180px;
|
|
overflow-y: auto;
|
|
border: 1px solid var(--line-soft);
|
|
border-radius: var(--r-md);
|
|
padding: 8px;
|
|
background: var(--surface-alt);
|
|
}
|
|
.todo-depends-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 5px 6px;
|
|
border-radius: var(--r-sm);
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
transition: background .15s;
|
|
}
|
|
.todo-depends-item:hover { background: var(--surface-sunk); }
|
|
.todo-depends-item input { flex-shrink: 0; }
|
|
.todo-depends-group {
|
|
font-size: 11px;
|
|
color: var(--text-muted);
|
|
margin-top: 6px;
|
|
margin-bottom: 2px;
|
|
text-transform: uppercase;
|
|
letter-spacing: .03em;
|
|
}
|
|
.todo-depends-group:first-child { margin-top: 0; }
|
|
/* Im eingeklappten Bereich trägt bereits das <details> den Rahmen */
|
|
.todo-depends-list.flat {
|
|
border: none;
|
|
background: transparent;
|
|
padding: 0;
|
|
max-height: 168px;
|
|
}
|
|
/* Vorgänger, die einen Zirkelbezug erzeugen würden (siehe
|
|
cyclicDependencyIds() in js/plan/plan-modals.js) */
|
|
.todo-depends-item.is-blocked { color: var(--text-subtle); cursor: not-allowed; opacity: .6; }
|
|
.todo-depends-item.is-blocked:hover { background: transparent; }
|
|
.todo-depends-note {
|
|
margin-left: auto;
|
|
font-size: 10.5px;
|
|
color: var(--amber);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* === TODO-Modal (Stufe B) =================================================
|
|
Kopfzeile mit Tastaturhinweis, Fehlermeldung am Feld, Dauer-Badge,
|
|
Zuständige als Personen-Chips, Status-Segment und eingeklappte
|
|
Abhängigkeiten. Befüllt von openTodoModal() in js/plan/plan-modals.js. */
|
|
|
|
.modal-head {
|
|
display: flex;
|
|
align-items: baseline;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
margin-bottom: 18px;
|
|
}
|
|
.modal-head h2 { margin: 0; }
|
|
.modal-kbd { font-size: 11px; color: var(--text-subtle); white-space: nowrap; }
|
|
|
|
/* Etwas breiter als die 500 px der Standard-Maske, damit die vier
|
|
Schaltflächen der Fußzeile in eine Zeile passen (gleiche Ausnahme wie
|
|
#lpModal/#newProjModal in styles.css). */
|
|
#todoModal .modal { max-width: 560px; }
|
|
|
|
/* Fußzeile bleibt stehen, wenn die Abhängigkeiten aufgeklappt sind */
|
|
.modal-footer {
|
|
position: sticky;
|
|
bottom: -28px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
margin: 20px -28px -28px;
|
|
padding: 16px 28px;
|
|
border-top: 1px solid var(--line-soft);
|
|
background: var(--surface-alt);
|
|
border-radius: 0 0 var(--r-xl) var(--r-xl);
|
|
}
|
|
|
|
/* Validierungshinweis direkt am Feld statt im alert() */
|
|
/* .field-error steht in styles.css: der Passwort-Dialog (js/auth.js) nutzt
|
|
sie auch ausserhalb des Projektbereichs. */
|
|
.modal input.is-error,
|
|
.modal textarea.is-error {
|
|
border-color: var(--red);
|
|
box-shadow: 0 0 0 3px rgba(215,0,21,.15);
|
|
}
|
|
|
|
.field-inline { display: flex; align-items: center; gap: 8px; }
|
|
.field-inline .range-picker-display { flex: 1; }
|
|
.field-badge {
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
color: var(--text-muted);
|
|
background: var(--surface-sunk);
|
|
border-radius: var(--r-pill);
|
|
padding: 3px 9px;
|
|
white-space: nowrap;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* Zuständige: Kürzel + Name, gleiche Lesart wie die Board-Spalten */
|
|
.person-chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
.person-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
background: var(--surface);
|
|
color: var(--text-muted);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--r-pill);
|
|
padding: 4px 12px 4px 4px;
|
|
font-size: 12.5px;
|
|
font-weight: 500;
|
|
transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
|
|
}
|
|
.person-chip:hover { background: var(--surface); border-color: var(--text-subtle); }
|
|
.person-chip-ini {
|
|
width: 21px;
|
|
height: 21px;
|
|
border-radius: 50%;
|
|
background: var(--surface-sunk);
|
|
color: var(--text-muted);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
}
|
|
.person-chip[aria-pressed="true"] {
|
|
background: var(--accent-soft);
|
|
border-color: var(--accent);
|
|
color: var(--accent);
|
|
}
|
|
.person-chip[aria-pressed="true"]:hover { background: var(--accent-soft); }
|
|
.person-chip[aria-pressed="true"] .person-chip-ini { background: var(--accent); color: #fff; }
|
|
|
|
/* Hover-Fläche im Status-Segment: eigene Abstufung, weil das Segment auf
|
|
--surface-sunk sitzt. Gleiche Drei-Zustands-Logik wie in styles.css —
|
|
:root deckt hell ab, die Media Query das System-Dunkel (außer bei
|
|
ausdrücklich hell), [data-theme="dark"] die manuelle Umschaltung. */
|
|
:root { --seg-hover: rgba(0,0,0,.06); }
|
|
@media (prefers-color-scheme: dark) {
|
|
html:not([data-theme="light"]) { --seg-hover: rgba(255,255,255,.10); }
|
|
}
|
|
html[data-theme="dark"] { --seg-hover: rgba(255,255,255,.10); }
|
|
|
|
/* Status: ersetzt das frühere Paar aus "Erledigt" und Fortschrittsregler */
|
|
.segmented {
|
|
display: inline-flex;
|
|
background: var(--surface-sunk);
|
|
border-radius: var(--r-pill);
|
|
padding: 2px;
|
|
gap: 2px;
|
|
width: fit-content;
|
|
}
|
|
.segmented button {
|
|
background: transparent;
|
|
color: var(--text-muted);
|
|
padding: 5px 14px;
|
|
font-size: 12.5px;
|
|
border-radius: var(--r-pill);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
.segmented button:hover { background: var(--seg-hover); }
|
|
.segmented button[aria-pressed="true"] {
|
|
background: var(--surface);
|
|
color: var(--text);
|
|
font-weight: 600;
|
|
box-shadow: var(--sh-1);
|
|
}
|
|
.segmented-dot {
|
|
width: 7px; height: 7px; border-radius: 50%;
|
|
background: currentColor; opacity: .45;
|
|
}
|
|
.segmented button[aria-pressed="true"] .segmented-dot { opacity: 1; }
|
|
.segmented button[data-status="offen"][aria-pressed="true"] .segmented-dot { background: var(--text-subtle); }
|
|
.segmented button[data-status="arbeit"][aria-pressed="true"] .segmented-dot { background: var(--amber); }
|
|
.segmented button[data-status="fertig"][aria-pressed="true"] .segmented-dot { background: var(--green); }
|
|
|
|
.progress-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
|
|
.progress-row[hidden] { display: none; }
|
|
.progress-row input[type=range] { flex: 1; accent-color: var(--accent); }
|
|
.progress-row output {
|
|
font-variant-numeric: tabular-nums;
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
min-width: 42px;
|
|
text-align: right;
|
|
}
|
|
|
|
/* Abhängigkeiten: selten gebraucht, daher eingeklappt mit Anzahl im Kopf */
|
|
.fold {
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--r-md);
|
|
overflow: hidden;
|
|
background: var(--surface);
|
|
}
|
|
.fold > summary {
|
|
cursor: pointer;
|
|
list-style: none;
|
|
padding: 9px 11px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
font-size: 12.5px;
|
|
user-select: none;
|
|
}
|
|
.fold > summary::-webkit-details-marker { display: none; }
|
|
.fold > summary:hover { background: var(--surface-alt); }
|
|
.fold-caret {
|
|
color: var(--text-subtle);
|
|
font-size: 10px;
|
|
transition: transform .18s var(--ease);
|
|
}
|
|
.fold[open] > summary .fold-caret { transform: rotate(90deg); }
|
|
.fold-count {
|
|
margin-left: auto;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
background: var(--accent-soft);
|
|
color: var(--accent);
|
|
border-radius: var(--r-pill);
|
|
padding: 1px 9px;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.fold-count.zero { background: var(--surface-sunk); color: var(--text-subtle); }
|
|
.fold-body {
|
|
padding: 9px 11px 11px;
|
|
border-top: 1px solid var(--line-soft);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 7px;
|
|
}
|
|
.fold-search { position: relative; }
|
|
.fold-search input { width: 100%; padding-left: 28px; }
|
|
.fold-search-icon {
|
|
position: absolute;
|
|
left: 9px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: var(--text-subtle);
|
|
font-size: 13px;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* Drag-Handle für TODO- und Meilenstein-Zeilen direkt im GANTT */
|
|
.gantt-row.gantt-todo,
|
|
.gantt-row.gantt-milestone-row { cursor: default; }
|
|
.gantt-row.gantt-todo .gantt-label,
|
|
.gantt-row.gantt-milestone-row .gantt-label { display: flex; align-items: center; gap: 8px; }
|
|
.gantt-todo-drag {
|
|
color: var(--text-subtle); cursor: grab; font-size: 13px; line-height: 1;
|
|
user-select: none; flex-shrink: 0;
|
|
}
|
|
.gantt-row.gantt-todo.dragging,
|
|
.gantt-row.gantt-milestone-row.dragging { opacity: .5; }
|
|
.gantt-todo-drag:active { cursor: grabbing; }
|
|
.gantt-row.gantt-milestone-row .gantt-todo-drag { color: var(--text-subtle); }
|
|
|
|
/* Cockpit: KPI-Kacheln über dem GANTT (Gesamtfortschritt, offene Aufgaben,
|
|
nächster Meilenstein, Team-Auslastung) — siehe renderPlanKpis() in
|
|
js/plan/plan-gantt.js. */
|
|
.plan-kpi-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 12px;
|
|
margin-bottom: 16px;
|
|
}
|
|
.plan-kpi-tile {
|
|
background: var(--surface);
|
|
border: 1px solid var(--line-soft);
|
|
border-radius: var(--r-lg);
|
|
padding: 12px 16px;
|
|
box-shadow: var(--sh-1);
|
|
}
|
|
.plan-kpi-tile .kpi-label {
|
|
font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em;
|
|
color: var(--text-subtle); font-weight: 600; margin-bottom: 4px;
|
|
}
|
|
.plan-kpi-tile .kpi-value {
|
|
font-size: 22px; font-weight: 700; letter-spacing: -0.02em; color: var(--text);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.plan-kpi-tile .kpi-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
|
|
.plan-kpi-tile.warn .kpi-value { color: var(--amber); }
|
|
|
|
/* Kompakt-Karten im Dashboard (Ressourcen-Auslastung, Dateien & Freigaben,
|
|
Verlauf) — springen in den zugehörigen Bereich bzw. öffnen den Verlauf,
|
|
siehe showPlanSection()/openHistoryModal(). */
|
|
.plan-card-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
|
|
gap: 14px;
|
|
margin-top: 18px;
|
|
}
|
|
.plan-mini-card {
|
|
background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-lg);
|
|
padding: 14px 16px; cursor: pointer; transition: box-shadow .15s, transform .15s;
|
|
}
|
|
.plan-mini-card:hover { box-shadow: var(--sh-2); transform: translateY(-1px); }
|
|
.plan-mini-card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
|
.plan-mini-card-title { font-size: 13px; font-weight: 600; }
|
|
.plan-mini-card-expand { font-size: 11px; color: var(--accent); font-weight: 500; flex-shrink: 0; }
|
|
.plan-mini-card-preview {
|
|
font-size: 12.5px; color: var(--text-muted); line-height: 1.5;
|
|
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
|
}
|
|
.plan-mini-chip-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
|
|
.plan-mini-chip { font-size: 10.5px; padding: 2px 8px; border-radius: var(--r-pill); background: var(--surface-sunk); color: var(--text-muted); }
|
|
.plan-mini-chip.warn { background: rgba(178,80,0,.12); color: var(--amber); }
|
|
.plan-heat-row { display: flex; gap: 4px; }
|
|
.plan-heat-cell { flex: 1; height: 18px; border-radius: 4px; background: var(--accent); }
|
|
|
|
@media (max-width: 760px) {
|
|
.plan-kpi-row { grid-template-columns: repeat(2, 1fr); }
|
|
}
|
|
|
|
/* --- Geister-Balken: Termine der Vorwoche (plan-build-gantt.js) ---
|
|
Bewusst ohne Füllung und ohne Zeiger-Ereignisse: sie sind reine
|
|
Referenz-Anzeige und dürfen Drag/Resize des echten Balkens darunter
|
|
nicht abfangen. */
|
|
.gantt-bar.ghost {
|
|
background: transparent;
|
|
border: 1.5px dashed var(--text-subtle);
|
|
opacity: .7;
|
|
pointer-events: none;
|
|
box-shadow: none;
|
|
}
|
|
.gantt-milestone.ghost {
|
|
background: transparent;
|
|
border: 1.5px dashed var(--text-subtle);
|
|
opacity: .7;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* --- Urlaubs-Kollision (plan-build-gantt.js) --- */
|
|
.gantt-bar.todo.conflict-absence {
|
|
outline: 2px solid var(--amber);
|
|
outline-offset: 1px;
|
|
}
|
|
.gantt-conflict-badge {
|
|
color: var(--amber);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
margin-left: 4px;
|
|
cursor: help;
|
|
}
|
|
|
|
/* Hinweisband über dem GANTT (renderPlanWarnings in plan-gantt.js) */
|
|
.plan-warning-band {
|
|
background: var(--surface);
|
|
border: 1px solid var(--line-soft);
|
|
border-left: 3px solid var(--amber);
|
|
border-radius: var(--r-md);
|
|
padding: 10px 14px;
|
|
margin-bottom: 12px;
|
|
}
|
|
.plan-warning-head {
|
|
display: flex; align-items: center; gap: 7px;
|
|
font-size: 12.5px; font-weight: 600; color: var(--amber);
|
|
}
|
|
.plan-warning-list {
|
|
margin: 6px 0 0; padding-left: 26px;
|
|
font-size: 12.5px; color: var(--text-muted); line-height: 1.55;
|
|
}
|
|
|
|
/* Projektübergreifende Auslastung in der Ressourcen-Karte */
|
|
.plan-resource-other {
|
|
font-size: 10px; margin-top: 4px; font-weight: 600; color: var(--amber);
|
|
cursor: help;
|
|
}
|
|
.plan-resource-other.muted { color: var(--text-subtle); font-weight: 400; cursor: default; }
|
|
|
|
/* --- Verlaufs-Modal (plan-history.js) --- */
|
|
.history-list {
|
|
display: flex; flex-direction: column; gap: 10px;
|
|
max-height: 58vh; overflow-y: auto;
|
|
}
|
|
.history-entry {
|
|
border: 1px solid var(--line-soft); border-radius: var(--r-md);
|
|
background: var(--surface-alt); padding: 10px 12px;
|
|
}
|
|
.history-entry.current { border-color: var(--accent); background: var(--surface); }
|
|
.history-entry-head {
|
|
display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px;
|
|
}
|
|
.history-week { font-size: 12.5px; font-weight: 600; }
|
|
.history-date { font-size: 11.5px; color: var(--text-subtle); }
|
|
.history-badge {
|
|
font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: var(--r-pill);
|
|
background: var(--accent-soft); color: var(--accent);
|
|
}
|
|
.history-badge.muted { background: var(--surface-sunk); color: var(--text-subtle); }
|
|
.history-entry-text { font-size: 12.5px; line-height: 1.5; color: var(--text); }
|
|
.history-entry-text p { margin: 0 0 4px; }
|
|
.history-entry-text p:last-child { margin-bottom: 0; }
|
|
|
|
/* --- Vorlagen-Modal (plan-templates.js) --- */
|
|
.template-head-grid {
|
|
display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
|
|
}
|
|
.template-admin-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
|
|
.template-admin-row {
|
|
display: flex; align-items: center; gap: 8px;
|
|
font-size: 12.5px; padding: 4px 6px;
|
|
background: var(--surface-alt); border-radius: var(--r-sm);
|
|
}
|
|
.template-admin-title { flex: 1; min-width: 0; }
|
|
.template-add-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
.template-add-row input[type=text] { flex: 1 1 200px; min-width: 140px; }
|
|
.template-add-row input[type=number] { width: 70px; }
|
|
|
|
/* --- Honorar-Terminplan (plan-honorar.js) --- */
|
|
.honorar-check { display: flex; align-items: center; gap: 7px; font-size: 12.5px; cursor: help; }
|
|
.honorar-plan-wrap { overflow-x: auto; margin-top: 4px; }
|
|
.honorar-table { font-size: 12.5px; }
|
|
.honorar-table th.num, .honorar-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
.honorar-table td.nowrap { white-space: nowrap; }
|
|
.honorar-table input[type=number] { width: 58px; padding: 3px 6px; font-size: 12px; text-align: center; }
|
|
.honorar-table tfoot th, .honorar-table tfoot td {
|
|
border-top: 2px solid var(--line); font-weight: 600; background: var(--surface-alt);
|
|
}
|
|
|
|
.honorar-anteil-grid {
|
|
display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
|
|
gap: 8px; margin-bottom: 10px;
|
|
}
|
|
.honorar-anteil-field { display: flex; align-items: center; gap: 6px; font-size: 12px; }
|
|
.honorar-anteil-field span { width: 46px; flex-shrink: 0; color: var(--text-muted); }
|
|
.honorar-anteil-field input { width: 100%; min-width: 0; padding: 4px 6px; font-size: 12px; text-align: right; }
|
|
.honorar-admin-foot { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
|
|
.honorar-satz-field { display: flex; align-items: center; gap: 6px; font-size: 12.5px; }
|
|
.honorar-satz-field input { width: 78px; padding: 4px 7px; font-size: 12.5px; text-align: right; }
|
|
.honorar-sum { font-size: 12.5px; font-weight: 600; }
|
|
.honorar-sum.ok { color: var(--green); }
|
|
.honorar-sum.warn { color: var(--amber); }
|
|
|
|
/* Beauftragte Leistungsphasen im Projektinfo */
|
|
@media (max-width: 620px) {
|
|
.template-head-grid { grid-template-columns: 1fr; }
|
|
}
|
|
|
|
/* Dateien-Modal (Upload, Freigabe, Kommentare) */
|
|
/* Upload-Maske des Bereichs "Dateien": oben ausgerichtet, damit
|
|
Dateiauswahl und Prüferliste in der breiten Karte auf gleicher Höhe
|
|
beginnen (im früheren Modal war sie unten ausgerichtet). */
|
|
.plan-files-upload { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start; margin-bottom: 4px; }
|
|
.plan-files-upload .form-row { margin-bottom: 0; }
|
|
.plan-files-upload #fileUploadReviewers { width: 240px; }
|
|
.files-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; max-height: 50vh; overflow-y: auto; }
|
|
.file-item {
|
|
border: 1px solid var(--line-soft); border-radius: var(--r-md);
|
|
background: var(--surface-alt); padding: 10px 12px;
|
|
}
|
|
.file-item-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
.file-item-name { font-weight: 500; font-size: 13px; word-break: break-all; }
|
|
.file-item-meta { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
|
|
.file-status-badge {
|
|
font-size: 11px; padding: 2px 8px; border-radius: var(--r-pill); color: #fff; flex-shrink: 0;
|
|
}
|
|
.file-status-badge.pending, .file-reviewer-chip.pending { background: #d18b00; }
|
|
.file-status-badge.approved, .file-reviewer-chip.approved { background: var(--green); }
|
|
.file-status-badge.rejected, .file-reviewer-chip.rejected { background: var(--red); }
|
|
.file-reviewer-list { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
|
|
.file-reviewer-chip { font-size: 11px; padding: 2px 8px; border-radius: var(--r-pill); color: #fff; }
|
|
.file-item-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
|
|
.file-comments { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line-soft); }
|
|
.file-comment-list { display: flex; flex-direction: column; gap: 6px; max-height: 160px; overflow-y: auto; }
|
|
.file-comment { font-size: 12px; background: var(--surface-sunk); border-radius: var(--r-sm); padding: 6px 8px; }
|
|
.file-comment-date { color: var(--text-subtle); font-size: 11px; margin-left: 6px; }
|
|
.file-comment-form { display: flex; gap: 6px; margin-top: 6px; }
|
|
.file-comment-form input { flex: 1; }
|
|
|
|
/* === Projektbereich: Bereichs-Navigation und Bereiche ====================
|
|
Die Seite ist der EIGENE BEREICH eines Projekts und gliedert sich in fünf
|
|
Unterbereiche (Dashboard, Terminplan, Aufgaben, Dateien, Einstellungen),
|
|
zwischen denen die Leiste .plan-nav umschaltet (showPlanSection() in
|
|
js/plan/plan-sections.js). Alle Bereiche liegen gleichzeitig im DOM und
|
|
werden nur ein-/ausgeblendet. */
|
|
.plan-nav {
|
|
display: flex;
|
|
gap: 4px;
|
|
padding: 0 24px;
|
|
background: var(--surface);
|
|
border-bottom: 1px solid var(--line-soft);
|
|
overflow-x: auto;
|
|
}
|
|
.plan-nav-item {
|
|
background: transparent;
|
|
color: var(--text-muted);
|
|
border: none;
|
|
border-bottom: 2px solid transparent;
|
|
border-radius: 0;
|
|
padding: 10px 14px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
}
|
|
.plan-nav-item:hover { color: var(--text); background: var(--surface-alt); }
|
|
.plan-nav-item.is-active {
|
|
color: var(--accent);
|
|
border-bottom-color: var(--accent);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.plan-section { display: none; }
|
|
.plan-section.is-active { display: block; }
|
|
|
|
/* Kartenraster der Bereiche (Dashboard, Einstellungen) */
|
|
.plan-panel-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 14px;
|
|
margin-top: 16px;
|
|
}
|
|
.plan-panel {
|
|
background: var(--surface);
|
|
border: 1px solid var(--line-soft);
|
|
border-radius: var(--r-lg);
|
|
padding: 14px 16px;
|
|
}
|
|
.plan-panel-wide { grid-column: 1 / -1; }
|
|
.plan-panel-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
margin: 0 0 10px;
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 8px;
|
|
}
|
|
.plan-panel-count {
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
color: var(--text-subtle);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.settings-static {
|
|
font-size: 13px;
|
|
color: var(--text-muted);
|
|
padding: 6px 0;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* --- Aufgaben-Bereich: Listendarstellung von TODOs und Meilensteinen
|
|
(renderPlanTasks() in js/plan/plan-tasks.js). Ein Klick auf eine Zeile
|
|
öffnet denselben Dialog wie im GANTT (openTodoModal/openMilestoneModal). */
|
|
.plan-task-list { display: flex; flex-direction: column; gap: 6px; }
|
|
.plan-task-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 8px 10px;
|
|
background: var(--surface-alt);
|
|
border: 1px solid var(--line-soft);
|
|
border-radius: var(--r-md);
|
|
cursor: pointer;
|
|
transition: border-color .15s, background .15s;
|
|
}
|
|
.plan-task-row:hover { border-color: var(--accent); }
|
|
.plan-task-row.is-done .plan-task-title { text-decoration: line-through; color: var(--text-subtle); }
|
|
.plan-task-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--text-subtle); }
|
|
.plan-task-dot.arbeit { background: var(--amber); }
|
|
.plan-task-dot.fertig { background: var(--green); }
|
|
.plan-task-title { flex: 1; font-size: 13px; font-weight: 500; min-width: 0; }
|
|
.plan-task-meta {
|
|
font-size: 11.5px;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.plan-task-meta.overdue { color: var(--red); font-weight: 600; }
|
|
.plan-task-no-schedule { color: var(--text-subtle); font-style: italic; }
|
|
.plan-task-assignees { display: flex; flex-shrink: 0; }
|
|
.plan-task-assignees .board-avatar { margin-left: -7px; border: 2px solid var(--surface-alt); }
|
|
.plan-task-assignees .board-avatar:first-child { margin-left: 0; }
|
|
.plan-task-empty { font-size: 12.5px; color: var(--text-subtle); padding: 10px 0; }
|
|
|
|
/* --- Dashboard: Liste der nächsten Termine (renderPlanDashboard()) --- */
|
|
.plan-upcoming-list { display: flex; flex-direction: column; gap: 7px; }
|
|
.plan-upcoming-item { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; }
|
|
.plan-upcoming-date {
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
font-variant-numeric: tabular-nums;
|
|
min-width: 82px;
|
|
}
|
|
.plan-upcoming-item.overdue .plan-upcoming-date { color: var(--red); font-weight: 600; }
|
|
.plan-upcoming-kind {
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
letter-spacing: .04em;
|
|
color: var(--text-subtle);
|
|
}
|
|
.plan-dash-team { display: flex; flex-direction: column; gap: 7px; }
|
|
.plan-dash-team-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }
|
|
|
|
/* === Bereich "Protokolle" (js/plan/plan-protokoll.js) ===
|
|
Listenansicht (.protokoll-row je Protokoll) und Detailansicht
|
|
(.protokoll-field-grid Kopf, Teilnehmer- und Punkte-Block). Der
|
|
Punkte-Block ist ZWEIGETEILT (.protokoll-split): links eine schmale
|
|
Liste aller Punkte nach Gruppen (.protokoll-liste), rechts der
|
|
Schreibbereich für den gerade ausgewählten (.protokoll-schreiben) —
|
|
Begründung bei .protokoll-split weiter unten. Gruppierung bleibt freier
|
|
Text, keine feste Liste, siehe CLAUDE.md "### Protokolle".
|
|
|
|
Das Neu-Kennzeichen ist seit dieser Umstellung ein schlichter Chip
|
|
(.protokoll-chip-neu, Gelb-Token --protokoll-neu-bg/-border — bewusst
|
|
nicht --band-neu, das im Wochenbericht schon blau "neu kommentiert"
|
|
bedeutet) statt einer Einfärbung ganzer Zeilen und Felder; der Status
|
|
eines Punkts erscheint als farbiger Punkt im Listeneintrag
|
|
(.protokoll-punkt-dot) und als Umschalter im Kopf des Schreibbereichs.
|
|
Damit entfällt das frühere Gegeneinander von Zeilen-Gelb und
|
|
Feldrahmen. */
|
|
|
|
.protokoll-list-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
|
|
|
|
.protokoll-row {
|
|
display: grid;
|
|
grid-template-columns: 64px 1fr 140px 170px 100px;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 11px 4px;
|
|
border-bottom: 1px solid var(--line-soft);
|
|
cursor: pointer;
|
|
transition: background .12s var(--ease);
|
|
}
|
|
.protokoll-row:last-child { border-bottom: none; }
|
|
.protokoll-row:hover { background: var(--surface-alt); }
|
|
.protokoll-nr { font-variant-numeric: tabular-nums; font-weight: 650; color: var(--text-muted); font-size: 13px; }
|
|
.protokoll-betreff { font-weight: 560; font-size: 13px; }
|
|
.protokoll-datum { display: block; font-weight: 400; color: var(--text-subtle); font-size: 11.5px; margin-top: 2px; }
|
|
.protokoll-verfasser { color: var(--text-muted); font-size: 12.5px; }
|
|
.protokoll-punkte-status { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-muted); }
|
|
.protokoll-status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--amber); flex-shrink: 0; }
|
|
.protokoll-punkte-status.ist-leer .protokoll-status-dot { background: var(--green); }
|
|
.protokoll-teilnehmer-count { color: var(--text-subtle); font-size: 12.5px; text-align: right; }
|
|
|
|
.protokoll-back-link { display: flex; align-items: center; gap: 6px; color: var(--text-muted); font-size: 13px;
|
|
margin-bottom: 14px; cursor: pointer; width: fit-content; }
|
|
.protokoll-back-link:hover { color: var(--text); }
|
|
|
|
.protokoll-import-banner { background: var(--protokoll-neu-bg); border: 1px solid var(--protokoll-neu-border);
|
|
border-radius: var(--r-md); padding: 10px 14px; font-size: 12.5px; color: var(--text); margin-bottom: 14px; }
|
|
|
|
/* KI-Erkennung angefordert, aber fehlgeschlagen (kein Schlüssel, Netzwerk-
|
|
fehler, unerwartete Antwort …) — eigene, wärmere Farbe als der neutrale
|
|
Import-Hinweis darüber, damit ein echter Fehlschlag nicht im selben Ton
|
|
wie eine normale Information untergeht. Rein clientseitig für GENAU den
|
|
Import, der ihn ausgelöst hat (currentProtokoll._kiFallbackHinweis,
|
|
js/plan/plan-protokoll.js) — beim erneuten Öffnen des Protokolls vom
|
|
Server aus ist er wieder weg, das einmalige Ereignis ist dann bereits
|
|
zur Kenntnis genommen. */
|
|
.protokoll-ki-fallback-banner { background: color-mix(in srgb, var(--amber) 10%, transparent); border: 1px solid var(--amber);
|
|
border-radius: var(--r-md); padding: 10px 14px; font-size: 12.5px; color: var(--text); margin-bottom: 14px; }
|
|
|
|
/* Ladehinweis im Import-Dialog (#protokollImportLoading, Test.html): ohne
|
|
ihn wirkte der Dialog während eines länger laufenden KI-Aufrufs (bis zu
|
|
KI_TIMEOUT_MS_LOKAL = 30 Minuten bei einem eigenen Server, server.js) wie
|
|
hängengeblieben — Felder und Knöpfe werden während der Anfrage zusätzlich
|
|
gesperrt (setzeProtokollImportLadezustand(), plan-protokoll.js), damit
|
|
kein zweiter Import gleichzeitig losgeschickt wird.
|
|
[hidden] MUSS hier explizit auf display:none gesetzt werden: das
|
|
HTML-Attribut hidden bewirkt das nur über eine UA-Regel niedrigster
|
|
Priorität, die eigene display:flex-Regel direkt darunter hätte sie sonst
|
|
überschrieben und den Kasten dauerhaft sichtbar gemacht — genau das ist
|
|
beim ersten Versuch ohne diese Zeile passiert. */
|
|
.protokoll-import-loading[hidden] { display: none; }
|
|
.protokoll-import-loading { display: flex; align-items: center; gap: 10px; margin-top: 12px;
|
|
padding: 10px 12px; background: var(--surface-alt); border: 1px solid var(--line-soft);
|
|
border-radius: var(--r-md); font-size: 12.5px; color: var(--text-muted); }
|
|
.protokoll-import-spinner { width: 15px; height: 15px; flex-shrink: 0; border-radius: 50%;
|
|
border: 2px solid var(--line); border-top-color: var(--accent);
|
|
animation: protokoll-import-spin .8s linear infinite; }
|
|
@keyframes protokoll-import-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
|
|
|
/* "TODOs aus Protokoll" (js/plan/plan-protokoll-todos.js): die drei Segmente
|
|
oben im Dialog zeigen den Fortschritt durch Hochladen → Punkte →
|
|
Aufgaben — erledigte/aktuelle Schritte gefüllt, kommende leer. Entwurf
|
|
"A — Schritt-Dialog" (Taste-Skill-Vergleich, Eriks Wahl). */
|
|
.protokoll-todos-steps { display: flex; gap: 5px; margin: 12px 0 16px; }
|
|
.protokoll-todos-steps .seg { flex: 1; height: 4px; border-radius: 2px; background: var(--line-soft); }
|
|
.protokoll-todos-steps .seg.done { background: var(--accent); }
|
|
|
|
/* Schritt 3 ("Aufgaben bestätigen"), Entwurfsrichtung "B — Kompakte Zeilen"
|
|
aus einem zweiten Taste-Skill-Vergleich (Eriks Wahl): eine Zeile je
|
|
Vorschlag statt einer ausführlichen Karte — ein echter Testlauf lieferte
|
|
bereits 19 Vorschläge aus einem Protokoll, als Karten wäre das nur noch
|
|
Scrollen gewesen. Termin/Zuständig stehen als Abzeichen schon in der
|
|
eingeklappten Zeile; Beschreibung und die volle Bearbeitung (Termin-Feld,
|
|
Zuständigen-Auswahl) erscheinen erst nach Antippen der Zeile
|
|
(.protokoll-todo-vorschlag.open). */
|
|
.pt-sel-bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
|
.pt-sel-count { font-size: 12.5px; color: var(--text-muted); }
|
|
.pt-sel-count b { color: var(--accent); font-weight: 700; }
|
|
.pt-sel-actions button { appearance: none; border: none; background: none; font: inherit; font-size: 11.5px;
|
|
font-weight: 650; color: var(--accent); cursor: pointer; padding: 2px 3px; }
|
|
.pt-sel-actions button:hover { text-decoration: underline; }
|
|
|
|
.protokoll-todo-vorschlag { border-bottom: 1px solid var(--line-soft); }
|
|
.protokoll-todo-vorschlag:last-child { border-bottom: none; }
|
|
.pt-row-main { display: flex; align-items: center; gap: 9px; padding: 8px 6px; cursor: pointer; }
|
|
.protokoll-todo-vorschlag.off .pt-row-main { opacity: .45; }
|
|
.pt-row-main .pt-vorschlag-check { flex-shrink: 0; accent-color: var(--accent); cursor: pointer; }
|
|
.pt-row-main .pt-vorschlag-titel { flex: 1; min-width: 0; border: none; background: none; font: inherit;
|
|
font-weight: 650; font-size: 12.8px; color: var(--text); padding: 2px 0;
|
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.pt-row-badges { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
|
|
.pt-badge-date { font-size: 10.5px; font-weight: 650; padding: 2px 8px; border-radius: 999px;
|
|
background: var(--accent-soft); color: var(--accent); white-space: nowrap; }
|
|
/* Pille statt perfektem Kreis: ein Kürzel ist 2-4 Zeichen lang (initialsForName(),
|
|
js/utils.js), ein starrer 18px-Kreis hätte "THK" o. Ä. abgeschnitten. */
|
|
.pt-badge-person { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
|
|
background: var(--accent); color: var(--surface);
|
|
font-size: 9px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
|
.pt-chevron { color: var(--text-subtle); font-size: 10.5px; width: 12px; text-align: center;
|
|
transition: transform .15s; flex-shrink: 0; }
|
|
.protokoll-todo-vorschlag.open .pt-chevron { transform: rotate(90deg); }
|
|
.pt-row-detail { display: none; padding: 0 6px 12px; }
|
|
.protokoll-todo-vorschlag.open .pt-row-detail { display: block; }
|
|
.protokoll-todo-vorschlag-beschreibung { margin: 2px 0 8px 28px; }
|
|
.protokoll-todo-vorschlag-felder { margin-left: 28px; display: flex; flex-direction: column; gap: 6px; }
|
|
|
|
.protokoll-detail-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px;
|
|
padding-bottom: 16px; border-bottom: 1px solid var(--line-soft); margin-bottom: 16px; }
|
|
.protokoll-detail-meta { color: var(--text-muted); font-size: 12.5px; }
|
|
|
|
.protokoll-field-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 20px; }
|
|
.protokoll-field { display: flex; flex-direction: column; gap: 5px; }
|
|
.protokoll-field label { font-size: 11.5px; color: var(--text-subtle); text-transform: uppercase; letter-spacing: .04em; }
|
|
|
|
.protokoll-block { margin-top: 20px; }
|
|
.protokoll-block-head { margin-bottom: 10px; display: flex; align-items: center; justify-content: space-between; }
|
|
.protokoll-block-head h4 { margin: 0; font-size: 13.5px; font-weight: 650; }
|
|
|
|
.protokoll-teilnehmer-row { display: grid; grid-template-columns: 1fr 160px 100px 24px; gap: 10px; align-items: center;
|
|
padding: 7px 0; border-bottom: 1px solid var(--line-soft); font-size: 13px; }
|
|
.protokoll-teilnehmer-row:last-of-type { border-bottom: none; }
|
|
.protokoll-anwesend-toggle { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-muted); }
|
|
.protokoll-anwesend-toggle input { accent-color: var(--green); }
|
|
.protokoll-row-remove { color: var(--text-subtle); text-align: center; cursor: pointer; opacity: .6; flex-shrink: 0; }
|
|
.protokoll-row-remove:hover { opacity: 1; color: var(--red); }
|
|
|
|
/* ---------- Punkte: links Liste, rechts Schreibbereich ----------
|
|
Eriks Auswahl „Liste + Schreibbereich" aus dem Protokoll-schreiben-Mockup,
|
|
Ablösung der früheren durchgehenden Tabelle (eine Zeile je Punkt mit allen
|
|
Feldern nebeneinander). Begründung dort und im Kopf von
|
|
renderPunkteBlockHtml() (js/plan/plan-protokoll.js): ein Protokollpunkt ist
|
|
ein kleiner Fließtext, keine Tabellenzelle — in der Spalte blieb für den
|
|
Text weder Breite noch Höhe, Thema und Text klebten übereinander und die
|
|
Formatierungsleiste saß eingeklemmt dazwischen. */
|
|
.protokoll-split { display: grid; grid-template-columns: 290px minmax(0, 1fr); gap: 14px; align-items: start; }
|
|
|
|
.protokoll-liste {
|
|
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
|
|
overflow: hidden; box-shadow: var(--sh-1);
|
|
}
|
|
.protokoll-liste-kopf {
|
|
padding: 9px 12px; background: var(--surface-alt); border-bottom: 1px solid var(--line);
|
|
font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-subtle);
|
|
}
|
|
/* Gruppen-Kopfzeile: Farbbalken links aus dem Gruppennamen gehasht
|
|
(protokollGruppeFarbe(), plan-protokoll.js — dasselbe Prinzip wie
|
|
projektFarbe() in der Auslastung), damit eine Gruppe ihre Farbe behält,
|
|
egal an welcher Position sie steht. Der Name selbst ist das Eingabefeld:
|
|
umbenennen wirkt auf alle Punkte der Gruppe. */
|
|
.protokoll-liste-gruppe {
|
|
display: flex; align-items: center; gap: 7px; padding: 8px 10px 8px 11px;
|
|
background: var(--surface-sunk); border-bottom: 1px solid var(--line-soft);
|
|
border-left: 3px solid var(--protokoll-gruppe-farbe, var(--accent));
|
|
}
|
|
.protokoll-liste-gruppe + .protokoll-liste-gruppe,
|
|
.protokoll-liste-zeile + .protokoll-liste-gruppe,
|
|
.protokoll-liste-neu + .protokoll-liste-gruppe { border-top: 1px solid var(--line); }
|
|
/* Dieselbe !important-Begründung wie bei den übrigen randlosen Feldern
|
|
weiter unten: die Dark-Mode-Fassung von input[type=text] (styles.css)
|
|
hängt ein html-Element vor den Selektor und gewinnt sonst. */
|
|
.protokoll-liste-gruppe input.protokoll-gruppe-heading {
|
|
flex: 1; min-width: 0; font-size: 12px; font-weight: 700; letter-spacing: .01em;
|
|
border: none !important; background: none !important; border-radius: 4px !important;
|
|
color: var(--text) !important; padding: 2px 3px !important;
|
|
}
|
|
.protokoll-liste-gruppe input.protokoll-gruppe-heading::placeholder { color: var(--text-subtle); font-weight: 550; }
|
|
.protokoll-liste-gruppe input.protokoll-gruppe-heading:focus { outline: none; }
|
|
.protokoll-liste-gruppe input.protokoll-gruppe-heading:focus-visible { box-shadow: 0 0 0 2px var(--accent-soft) inset; }
|
|
.protokoll-liste-gruppe .count {
|
|
font-size: 10px; font-variant-numeric: tabular-nums; color: var(--text-subtle);
|
|
background: var(--surface); border: 1px solid var(--line); border-radius: 980px; padding: 0 6px; flex-shrink: 0;
|
|
}
|
|
|
|
.protokoll-liste-zeile {
|
|
display: grid; grid-template-columns: 30px minmax(0, 1fr) auto; gap: 2px 8px; align-items: baseline;
|
|
padding: 8px 11px; cursor: pointer; border-bottom: 1px solid var(--line-soft);
|
|
border-left: 3px solid transparent;
|
|
}
|
|
.protokoll-liste-zeile:hover { background: var(--surface-alt); }
|
|
.protokoll-liste-zeile.ist-aktiv { background: var(--accent-soft); border-left-color: var(--accent); }
|
|
/* Jedes Kind bekommt seinen Platz im Raster AUSDRÜCKLICH zugewiesen. Mit
|
|
bloßer Auto-Platzierung zog der Status-Punkt (der als einziger eine
|
|
grid-row trug) die erste Spalte an sich, Nummer und Titel rutschten je
|
|
eine Spalte weiter und der Titel landete rechtsbündig in der schmalen
|
|
auto-Spalte — die Unterzeile blieb dann auf zwei Zeichen abgeschnitten. */
|
|
.protokoll-liste-zeile .nr {
|
|
grid-column: 1; grid-row: 1;
|
|
font-variant-numeric: tabular-nums; font-size: 11.5px; font-weight: 700; color: var(--text-muted);
|
|
}
|
|
.protokoll-liste-zeile.ist-aktiv .nr { color: var(--accent); }
|
|
.protokoll-liste-zeile .titel {
|
|
grid-column: 2; grid-row: 1;
|
|
font-size: 12.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.protokoll-liste-zeile .titel.ist-leer { font-weight: 500; font-style: italic; color: var(--text-subtle); }
|
|
.protokoll-liste-zeile .sub {
|
|
grid-column: 2; grid-row: 2; font-size: 10.5px; color: var(--text-subtle);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
/* Das Neu-Kennzeichen steht rechts unter dem Status-Punkt statt in einer
|
|
eigenen dritten Zeile — sonst wäre jeder Eintrag der Liste eine Zeile
|
|
höher, nur für einen Chip. */
|
|
.protokoll-liste-zeile .protokoll-chip-neu { grid-column: 3; grid-row: 2; justify-self: end; }
|
|
.protokoll-punkt-dot {
|
|
grid-column: 3; grid-row: 1; justify-self: end; align-self: center;
|
|
width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0;
|
|
}
|
|
.protokoll-punkt-dot[data-status="offen"] { background: var(--status-offen); }
|
|
.protokoll-punkt-dot[data-status="erledigt"] { background: var(--status-erledigt); }
|
|
.protokoll-punkt-dot[data-status="info"] { background: var(--status-info); }
|
|
|
|
.protokoll-liste-neu {
|
|
padding: 8px 11px 8px 14px; color: var(--accent); font-size: 12px; font-weight: 600; cursor: pointer;
|
|
border-bottom: 1px solid var(--line-soft);
|
|
}
|
|
.protokoll-liste-neu:hover { background: var(--surface-alt); color: var(--accent-hover); }
|
|
.protokoll-liste-neu.ist-gruppe { border-bottom: none; border-top: 1px solid var(--line); }
|
|
.protokoll-liste-leer { padding: 14px 12px; font-size: 12.5px; color: var(--text-subtle); }
|
|
|
|
/* ---------- Schreibbereich ---------- */
|
|
.protokoll-schreiben {
|
|
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
|
|
overflow: hidden; box-shadow: var(--sh-1);
|
|
}
|
|
.protokoll-schreiben.ist-leer { padding: 28px 22px; color: var(--text-muted); font-size: 13px; }
|
|
.protokoll-schreiben.ist-leer p { margin: 0 0 6px; }
|
|
.protokoll-schreiben.ist-leer p:last-child { margin-bottom: 0; }
|
|
|
|
.protokoll-schreiben-kopf {
|
|
display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
|
|
padding: 9px 16px; background: var(--surface-alt); border-bottom: 1px solid var(--line-soft);
|
|
}
|
|
.protokoll-schreiben-kopf .nr { font-variant-numeric: tabular-nums; font-weight: 700; font-size: 13px; }
|
|
.protokoll-schreiben-kopf .gruppe {
|
|
font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-subtle);
|
|
}
|
|
.protokoll-schreiben-kopf .rechts { margin-left: auto; display: flex; align-items: center; gap: 9px; }
|
|
|
|
.protokoll-feld-label {
|
|
display: block; font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
|
|
color: var(--text-subtle); margin-bottom: 5px;
|
|
}
|
|
.protokoll-schreiben-feld { padding: 13px 16px; }
|
|
.protokoll-schreiben-feld + .protokoll-schreiben-feld { border-top: 1px solid var(--line-soft); }
|
|
|
|
/* Thema und Text sitzen randlos in ihrem beschrifteten Abschnitt — der
|
|
Abschnitt selbst (Linie + Label) sagt schon, wo man schreibt, ein
|
|
zusätzlicher Feldrahmen gäbe zwei Kästen umeinander. !important wie
|
|
überall hier gegen die Dark-Mode-Fassung von input[type=text]
|
|
(html:not([data-theme="light"]) …, styles.css), die sonst an Spezifität
|
|
gewinnt und ein abgerundetes Eingabefeld-Rechteck durchscheinen lässt. */
|
|
.protokoll-schreiben-feld input.protokoll-punkt-thema {
|
|
font: inherit; font-size: 17px; font-weight: 650; letter-spacing: -.01em; width: 100%; line-height: 1.3;
|
|
border: none !important; background: none !important; border-radius: 4px !important;
|
|
color: var(--text) !important; padding: 1px 2px !important; display: block;
|
|
}
|
|
.protokoll-schreiben-feld input.protokoll-punkt-thema::placeholder { font-weight: 500; color: var(--text-subtle); }
|
|
.protokoll-schreiben-feld .protokoll-punkt-text {
|
|
font: inherit; font-size: 13.5px; line-height: 1.65; color: var(--text);
|
|
min-height: 130px; max-width: 66ch; padding: 1px 2px; border-radius: 4px;
|
|
}
|
|
.protokoll-schreiben-feld .protokoll-punkt-text:empty::before { content: attr(data-placeholder); color: var(--text-subtle); }
|
|
.protokoll-schreiben-feld input.protokoll-punkt-thema:focus,
|
|
.protokoll-schreiben-feld .protokoll-punkt-text:focus { outline: none; }
|
|
.protokoll-schreiben-feld input.protokoll-punkt-thema:focus-visible,
|
|
.protokoll-schreiben-feld .protokoll-punkt-text:focus-visible { box-shadow: 0 0 0 2px var(--accent-soft) inset; }
|
|
/* ist-geaendert: NUR das geänderte Feld eines fortgeschriebenen Punkts gelb
|
|
markiert (Eriks Wunsch) — derselbe Gelb-Ton wie das Neu-Kennzeichen, hier
|
|
aber feldweise, weil der Punkt selbst ja derselbe geblieben ist, nur sein
|
|
Inhalt nicht. */
|
|
.protokoll-schreiben-feld input.protokoll-punkt-thema.ist-geaendert,
|
|
.protokoll-schreiben-feld .protokoll-punkt-text.ist-geaendert {
|
|
background: var(--protokoll-neu-bg) !important; padding: 3px 6px !important; margin: 0 -4px;
|
|
}
|
|
|
|
/* Die Formatierungsleiste sitzt zwischen Thema und Text — sie gehört zum
|
|
Textfeld darunter, nicht zum Thema darüber (Thema ist reiner Text ohne
|
|
Auszeichnung). */
|
|
.protokoll-schreiben .richtext-toolbar { border-top: 1px solid var(--line-soft); }
|
|
|
|
.protokoll-schreiben-raster {
|
|
display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
|
|
padding: 13px 16px; border-top: 1px solid var(--line-soft);
|
|
}
|
|
.protokoll-schreiben-raster input {
|
|
font: inherit !important; font-size: 13px !important; width: 100%; min-width: 0 !important;
|
|
color: var(--text) !important; background: var(--surface) !important;
|
|
border: 1px solid var(--line) !important; border-radius: var(--r-sm) !important; padding: 6px 9px !important;
|
|
}
|
|
.protokoll-schreiben-raster input:focus {
|
|
outline: none; border-color: var(--accent) !important; box-shadow: 0 0 0 3px var(--accent-soft);
|
|
}
|
|
.protokoll-schreiben-fuss {
|
|
padding: 9px 16px; border-top: 1px solid var(--line-soft); background: var(--surface-alt);
|
|
font-size: 11px; color: var(--text-subtle);
|
|
}
|
|
.protokoll-schreiben-fuss .hint { font-size: 11px; }
|
|
|
|
/* ---------- Chips, Verschieben, Entfernen ---------- */
|
|
.protokoll-chip-neu {
|
|
display: inline-block; width: fit-content; background: var(--protokoll-neu-border); color: #3d2e00;
|
|
font-size: 9.5px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; padding: 1px 6px;
|
|
border-radius: var(--r-pill);
|
|
}
|
|
/* Gnadenrunde eines erledigten Punkts — bewusst Fließtext statt
|
|
Großbuchstaben-Pille: der Hinweistext ist zu lang dafür. */
|
|
.protokoll-chip-entfaellt {
|
|
display: inline-block; max-width: 100%;
|
|
background: color-mix(in srgb, var(--red) 15%, transparent); color: var(--red);
|
|
font-size: 9.5px; font-weight: 700; line-height: 1.3; padding: 2px 7px; border-radius: var(--r-sm);
|
|
}
|
|
.protokoll-gruppe-move, .protokoll-punkt-move { display: inline-flex; gap: 4px; flex-shrink: 0; }
|
|
/* button.secondary (styles.css) bringt eine eigene, leicht deckende Fläche
|
|
mit, die hier wie ein grauer Fleck wirkte. !important nötig: die
|
|
Dark-Mode-Fassung von button.secondary hängt ein html-Element vor den
|
|
Selektor und gewinnt sonst an Spezifität. */
|
|
.protokoll-gruppe-move button, .protokoll-punkt-move button {
|
|
padding: 1px 6px; line-height: 1.4; font-size: 11px;
|
|
background: transparent !important; color: var(--text-subtle) !important;
|
|
}
|
|
.protokoll-gruppe-move button:hover:not(:disabled),
|
|
.protokoll-punkt-move button:hover:not(:disabled) { color: var(--text) !important; }
|
|
.protokoll-gruppe-move button:disabled, .protokoll-punkt-move button:disabled { opacity: .3; cursor: default; }
|
|
|
|
/* ---------- Status-Umschalter ----------
|
|
Senkrecht gestapelt in der Grundform; im Kopf des Schreibbereichs ist
|
|
Platz, dort liegt er waagerecht. */
|
|
.protokoll-status-switch {
|
|
display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 7px; padding: 2px;
|
|
background: var(--surface-sunk); gap: 1px; width: 56px;
|
|
}
|
|
.protokoll-status-switch.ist-waagerecht { flex-direction: row; width: auto; border-radius: 980px; }
|
|
.protokoll-status-switch button {
|
|
appearance: none; border: none; background: none; font: inherit; font-size: 10.5px; font-weight: 700;
|
|
letter-spacing: .01em; padding: 4px 0; border-radius: 5px; cursor: pointer; color: var(--text-subtle);
|
|
text-align: center; transition: background .12s, color .12s;
|
|
}
|
|
.protokoll-status-switch.ist-waagerecht button { padding: 3px 11px; border-radius: 980px; }
|
|
.protokoll-status-switch button:hover:not(.is-active) { color: var(--text); }
|
|
.protokoll-status-switch button:disabled { cursor: default; }
|
|
.protokoll-status-switch button:disabled:hover { color: var(--text-subtle); }
|
|
.protokoll-status-switch button.offen.is-active { background: var(--status-offen); color: #fff; }
|
|
.protokoll-status-switch button.erledigt.is-active { background: var(--status-erledigt); color: #fff; }
|
|
.protokoll-status-switch button.info.is-active { background: var(--status-info); color: #fff; }
|
|
|
|
@media (max-width: 900px){
|
|
.protokoll-row { grid-template-columns: 1fr; gap: 4px; }
|
|
.protokoll-field-grid { grid-template-columns: 1fr; }
|
|
/* Unter 900px stehen Liste und Schreibbereich untereinander — nebeneinander
|
|
bliebe für den Text keine lesbare Zeilenlänge mehr. */
|
|
.protokoll-split { grid-template-columns: minmax(0, 1fr); }
|
|
.protokoll-schreiben-raster { grid-template-columns: 1fr; }
|
|
}
|
|
|
|
.add-row-btn { display: flex; align-items: center; gap: 6px; color: var(--accent); font-size: 13px;
|
|
font-weight: 600; cursor: pointer; padding: 8px 0; width: fit-content; }
|
|
.add-row-btn:hover { color: var(--accent-hover); }
|
|
|
|
.legend-swatch { width: 13px; height: 13px; border-radius: 4px; flex-shrink: 0; display: inline-block; }
|
|
.legend-swatch.neu { background: var(--protokoll-neu-bg); border: 1.5px solid var(--protokoll-neu-border); }
|
|
.legend-swatch.info { background: var(--status-info-bg); border: 1.5px solid var(--status-info); }
|
|
|
|
/* ============================================================
|
|
BEWEGUNG - Stufe "Spürbar", Teil Projektbereich
|
|
============================================================
|
|
Ergänzung zum gleichnamigen Block am Ende von styles.css; die
|
|
Tokens (--dur, --ease-out, --row-lift, --press) stehen dort in
|
|
:root und gelten hier mit. Erfasst die Zeilenarten, die es nur
|
|
im Projektbereich gibt.
|
|
============================================================ */
|
|
|
|
/* Anklickbare Zeilen: anheben + einfahrende Akzentleiste, exakt wie
|
|
die Projektliste. Auch hier als inset-Schatten statt ::before -
|
|
die Aufgabenzeilen des GANTT sind ziehbar (plan-drag.js), ein
|
|
Stapelkontext auf der Zeile wäre dort riskant. */
|
|
.todo-depends-item,
|
|
.pt-row-main,
|
|
.plan-task-row {
|
|
transition: background var(--dur) var(--ease-out),
|
|
box-shadow var(--dur) var(--ease-out),
|
|
transform var(--dur) var(--ease-out);
|
|
}
|
|
.todo-depends-item:hover,
|
|
.pt-row-main:hover,
|
|
.plan-task-row:hover {
|
|
transform: translateY(var(--row-lift));
|
|
box-shadow: inset 3px 0 0 0 var(--accent);
|
|
}
|
|
.todo-depends-item:active,
|
|
.pt-row-main:active,
|
|
.plan-task-row:active { transform: translateY(0); transition-duration: 60ms; }
|
|
|
|
/* Die Protokoll-Zeile ist der eine Sonderfall: sie trägt bereits einen
|
|
eigenen linken Rand (border-left: 3px solid transparent, oben), den
|
|
der aktive Punkt auf die Akzentfarbe setzt. Hier NICHT zusätzlich den
|
|
inset-Schatten verwenden - beides zusammen ergäbe 6 px Akzent statt 3.
|
|
Stattdessen wird genau dieser vorhandene Rand animiert, das Anheben
|
|
bleibt identisch zu allen anderen Zeilen. */
|
|
.protokoll-liste-zeile {
|
|
transition: background var(--dur) var(--ease-out),
|
|
border-left-color var(--dur) var(--ease-out),
|
|
transform var(--dur) var(--ease-out);
|
|
}
|
|
.protokoll-liste-zeile:hover {
|
|
transform: translateY(var(--row-lift));
|
|
border-left-color: var(--accent);
|
|
}
|
|
.protokoll-liste-zeile:active { transform: translateY(0); transition-duration: 60ms; }
|
|
|
|
/* Bereichs-Reiter des Projektbereichs: Druckpunkt wie jeder andere
|
|
Schalter. Die Unterstreichung des aktiven Reiters bleibt wie sie ist. */
|
|
.plan-nav-item { transition: color var(--dur) var(--ease-out),
|
|
border-color var(--dur) var(--ease-out),
|
|
transform var(--dur) var(--ease-out); }
|
|
.plan-nav-item:active { transform: scale(var(--press)); }
|
|
|
|
/* Aufklapp-Pfeil der Vorschlagszeile ("TODOs aus Protokoll"): dreht
|
|
sich jetzt mit derselben Dauer wie der Pfeil der Projektliste
|
|
(.board-chev), statt mit einem eigenen Wert. */
|
|
.pt-chevron { transition: transform var(--dur) var(--ease-out); }
|