/* Buero-Visualisierung (Phase 2, statische Szene). Bewusst ohne UI-Framework, siehe Visualization.md Abschnitt 5. Farbschema hell/freundlich (F9: "Zeichentrick"-Anmutung). */ :root { --color-floor: #eef2f7; --color-wall: #cfe0f0; --color-wall-side: #bcd3ea; --color-door: #8fb8dd; --color-board-frame: #94622f; --color-note: #fde68a; --color-note-border: #d9b34f; --color-note-stack-badge: #d97706; --color-monitor-frame: #2b2f38; --color-monitor-screen: #10131a; --color-monitor-placeholder: #1a2436; --color-desk-table: #b98a54; --color-desk-chair: #6b7280; --color-desk-screen-idle: #3a3f4b; --color-text: #1f2937; --color-text-inverse: #f8fafc; } * { box-sizing: border-box; } /* Die Szene soll immer vollstaendig sichtbar sein - auch im normalen Fenstermodus, ohne Scrollen. Dazu spannt der body eine Flex-Spalte ueber die Viewport-Hoehe auf: Header nimmt seine natuerliche Hoehe, #office-scene bekommt den Rest, und das SVG skaliert sich per aspect-ratio in diesen Rest hinein (siehe unten). */ html { height: 100%; } body { margin: 0; font-family: "Segoe UI", Verdana, Arial, sans-serif; background: #dbe7f3; color: var(--color-text); /* height (nicht min-height): Nur eine definite Hoehe laesst die prozentualen max-height-Angaben weiter unten aufloesen. dvh rechnet mobile Browserleisten mit, vh ist der Fallback fuer aeltere Engines. */ height: 100vh; height: 100dvh; display: flex; flex-direction: column; overflow: hidden; } header.office-header { flex: 0 0 auto; padding: 12px 24px; display: flex; align-items: baseline; justify-content: space-between; gap: 16px; } header.office-header h1 { margin: 0; font-size: 1.4rem; } /* Rein kosmetischer Countdown bis zum naechsten Codeberg-Sync (Issue #12), rechtsbuendig anstelle des frueheren erklaerenden Fliesstexts. Tabular-nums verhindert Wackeln der Breite, wenn sich die Ziffernzahl aendert (z.B. 9s -> 10s). */ .reload-countdown { color: #475569; font-size: 0.9rem; font-variant-numeric: tabular-nums; white-space: nowrap; } /* Warnbanner, wenn die API nicht erreichbar ist und die Szene auf Demo-Daten zurueckfaellt (siehe setApiBanner() in js/office-render.js). */ .api-banner { flex: 0 0 auto; padding: 10px 24px; background: #dc2626; color: #ffffff; font-weight: 700; text-align: center; letter-spacing: 0.02em; } /* Muss nach der Regel oben stehen: ohne diese Zeile wuerde das hidden-Attribut vom flex-Wert ueberstimmt und der Banner waere dauerhaft sichtbar. */ .api-banner[hidden] { display: none; } /* Reine Vorwarnung (Anmeldung laeuft demnaechst ab, der Agent arbeitet noch): gelb statt rot, damit das Rot dem tatsaechlichen Stillstand vorbehalten bleibt. Dunkle Schrift, weil Weiss auf Gelb kaum zu lesen ist. */ .api-banner.api-banner-warning { background: #facc15; color: #422006; } #office-scene { flex: 1 1 auto; /* min-height: 0 hebt die Flex-Default-Mindesthoehe (min-content) auf - sonst kann das Kind den Container nicht unterschreiten und die Szene laeuft aus dem Bild. */ min-height: 0; display: flex; align-items: center; justify-content: center; padding: 0 16px 16px; } /* Das SVG soll sich an der jeweils knapperen Achse ausrichten. Welche das ist, haengt vom Seitenverhaeltnis des Fensters ab - daher die beiden Media-Queries unten. Die max-*-Grenzen sind das Sicherheitsnetz fuer den Grenzbereich (die Header-Hoehe geht in das Verhaeltnis der Szenenflaeche ein, in die Media-Query aber nicht): Dort wird die Elementbox notfalls leicht gestaucht - der Szeneninhalt bleibt dank preserveAspectRatio unverzerrt und vollstaendig sichtbar. */ .office-svg { max-width: 100%; max-height: 100%; aspect-ratio: 1920 / 1080; display: block; border-radius: 12px; box-shadow: 0 10px 30px rgba(15, 23, 42, 0.25); } /* Fenster breiter als 16:9 -> die Hoehe ist der Engpass. */ @media (min-aspect-ratio: 16 / 9) { .office-svg { height: 100%; width: auto; } } /* Fenster schmaler als 16:9 -> die Breite ist der Engpass. */ @media (max-aspect-ratio: 16 / 9) { .office-svg { width: 100%; height: auto; } } @media (max-height: 640px) { header.office-header { padding: 8px 16px; } header.office-header h1 { font-size: 1.1rem; } } .floor { fill: var(--color-floor); } .wall { fill: var(--color-wall); } .wall-side { fill: var(--color-wall-side); } .door { fill: var(--color-door); stroke: #5f86ab; stroke-width: 3; transition: fill 0.4s ease-in-out; } /* Phase 6 (F2): kurzes Aufleuchten, waehrend ein "user"-Tuerbesuch die Tuer passiert. */ .door-active { fill: #d7ecff; } .wall-label { fill: #f8fafc; font-size: 22px; font-weight: 600; } /* Kanban-Board */ .board-frame { fill: #7a5230; stroke: var(--color-board-frame); stroke-width: 6; } .board-column-label { fill: var(--color-text-inverse); font-size: 16px; font-weight: 600; } .board-column-divider { stroke: rgba(255, 255, 255, 0.25); stroke-width: 2; } .note { fill: var(--color-note); stroke: var(--color-note-border); stroke-width: 2; } /* Zettel in der Farbe seines Projekts (Issue #27): die Fuellung kommt als Inline-Style aus der Projektkonfiguration, die Kontur muss dann neutral sein - die gelbe Standardkontur passt nur zum gelben Standardzettel. */ .note-colored { stroke: rgba(15, 23, 42, 0.35); } .note-stack-badge { fill: var(--color-note-stack-badge); stroke: #92400e; stroke-width: 2; } .note-stack-count { fill: #ffffff; font-size: 20px; font-weight: 700; } /* Monitor-Wand */ .monitor-frame { fill: var(--color-monitor-frame); } .monitor-screen { fill: var(--color-monitor-screen); } .monitor-screen-placeholder { fill: var(--color-monitor-placeholder); } .monitor-event-line { font-family: "Consolas", "Courier New", monospace; font-size: 13px; /* Grundfarbe fuer Schweregrade ohne eigene Farbe (z.B. info) - ohne sie stuende der Text schwarz auf dem dunklen Monitor. */ fill: #cbd5e1; } /* Kopfzeile eines Events (Zeit + System): tritt gegenueber der Meldung zurueck. */ .monitor-event-meta { opacity: 0.75; } .severity-warning, .severity-warning .monitor-event-line { fill: #facc15; } .severity-error, .severity-error .monitor-event-line { fill: #f87171; } .system-tile-bg { fill: #1e293b; } .system-tile-mark { fill: none; stroke: #64748b; stroke-linecap: round; stroke-linejoin: round; } .status-ok .system-tile-mark { stroke: #4ade80; } .status-error .system-tile-mark { stroke: #f87171; } .monitor-placeholder-text { fill: #64748b; font-size: 15px; } /* Schreibtische */ .desk-table { fill: var(--color-desk-table); stroke: #8a6238; stroke-width: 3; } .desk-screen { fill: var(--color-desk-screen-idle); stroke: #1f2430; stroke-width: 2; } .desk-screen.pose-working { fill: #7dd3fc; } /* Blockierter Platz (Claude-Anmeldung abgelaufen): der Bildschirm traegt den Zustand mit, damit er auch dann am Platz sichtbar bleibt, wenn der Avatar gerade nicht dort steht. */ .desk-screen.pose-auth_expired { fill: #7f1d1d; stroke: #f87171; } .desk-screen.pose-auth_expiring { fill: #713f12; stroke: #facc15; } .desk-chair { fill: var(--color-desk-chair); } .person-head { fill: #f3d9b1; stroke: #d8b98a; stroke-width: 1.5; } .person-body { fill: var(--role-color, #94a3b8); } .desk-name { fill: #334155; font-size: 14px; font-weight: 600; } .desk-name-empty { fill: #94a3b8; font-style: italic; font-weight: 400; } /* Der Name eines Agenten am Schreibtisch steht am Tisch, nicht am Avatar (updateAgentsLayer() blendet .avatar-name am Platz aus) - der Problemzustand faerbt deshalb hier. */ .desk-name.desk-name-problem-error { fill: #b91c1c; font-weight: 700; } .desk-name.desk-name-problem-warning { fill: #a16207; font-weight: 700; } .desk-deco-star { fill: #fbbf24; } .desk-deco-flower circle { fill: #f9a8d4; } .desk-deco-flower-center { fill: #fde047 !important; } .desk-deco-check { fill: none; stroke: #22c55e; stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; } .desk-deco-gear circle, .desk-deco-gear rect { fill: #a78bfa; } /* Heller Kern, damit der gefuellte Kreis als Zahnrad-Ring lesbar wird (wie beim Bluetenkern). */ .desk-deco-gear-center { fill: #ede9fe !important; } .desk-empty .desk-chair { opacity: 0.5; } /* Klickbare Bereiche (Phase 3/4: Board-Spalten + Monitore oeffnen ein Modal) */ .clickable { cursor: pointer; } .board-column-hit { fill: transparent; pointer-events: all; } /* Trefferflaeche des PO-Platzes (Issue #27): macht den ganzen Platz klickbar, nicht nur die gefuellten Moebelstuecke - ein Klick daneben landete sonst auf dem Boden. */ .po-desk-hit { fill: transparent; pointer-events: all; } /* Konfig-Zahnrad (Issue #27 am PO-Platz, Issue #70 am Systeme-Monitor): zeigt ueberhaupt erst, dass sich hier etwas einstellen laesst. Aussehen ist ueberall gleich, nur der Ausloeser des Hovers unterscheidet sich - beim PO der ganze Schreibtisch, beim Monitor das Symbol selbst (der Monitor traegt schon eine eigene Klick-Aktion). */ .config-hint-bg { fill: #ffffff; stroke: #94a3b8; stroke-width: 1.5; opacity: 0.85; } .config-hint-tooth, .config-hint-hub { stroke: #475569; stroke-width: 2; stroke-linecap: round; fill: none; } .po-desk:hover .config-hint-bg, .system-config-hint:hover .config-hint-bg { fill: #e0f2fe; stroke: #0284c7; opacity: 1; } .po-desk:hover .config-hint-tooth, .po-desk:hover .config-hint-hub, .system-config-hint:hover .config-hint-tooth, .system-config-hint:hover .config-hint-hub { stroke: #0284c7; } .system-config-hint-hit { fill: transparent; pointer-events: all; } /* Reload-Symbol neben der Board-Ueberschrift (Issue #51): stoesst ein komplettes Neueinlesen des Kanban-Boards an, analog zum Zahnrad am PO-Platz. */ .board-reload-hint-bg { fill: #ffffff; stroke: #94a3b8; stroke-width: 1.5; opacity: 0.85; } .board-reload-hint-arrow { fill: none; stroke: #475569; stroke-width: 2; stroke-linecap: round; } .board-reload-hint-arrowhead { fill: #475569; } .board-reload-hint-hit { fill: transparent; pointer-events: all; } .board-reload-hint:hover .board-reload-hint-bg { fill: #e0f2fe; stroke: #0284c7; opacity: 1; } .board-reload-hint:hover .board-reload-hint-arrow { stroke: #0284c7; } .board-reload-hint:hover .board-reload-hint-arrowhead { fill: #0284c7; } .just-updated .board-frame, .just-updated .monitor-frame { animation: office-flash 0.9s ease-out; } @keyframes office-flash { 0% { filter: drop-shadow(0 0 0 rgba(250, 204, 21, 0)); } 25% { filter: drop-shadow(0 0 12px rgba(250, 204, 21, 0.9)); } 100% { filter: drop-shadow(0 0 0 rgba(250, 204, 21, 0)); } } /* Kaffee-Ecke (Phase 5, Raum-Prop zwischen den Schreibtischreihen) */ .coffee-table { fill: #8a6238; stroke: #6b4a28; stroke-width: 2; } .coffee-icon { font-size: 20px; } /* Agenten-Avatare (Phase 5: bewegliche Schicht, siehe office-render.js) */ .agent-avatar { transition: transform 1.1s ease-in-out; } .agent-avatar .person-head { fill: #f3d9b1; stroke: #d8b98a; stroke-width: 1.5; } .agent-avatar .person-body { fill: var(--role-color, #94a3b8); } .agent-avatar.pose-walking .person-leg { fill: #475569; transform-box: fill-box; transform-origin: top center; animation: office-leg-swing 0.6s ease-in-out infinite alternate; } .agent-avatar.pose-walking .person-leg-right { animation-delay: 0.3s; } @keyframes office-leg-swing { from { transform: rotate(-18deg); } to { transform: rotate(18deg); } } /* Blockierter Agent (AGENT_PROBLEM_STATES): der Koerper verliert seine Rollenfarbe, damit die Figur als "steht still" lesbar wird, ohne aus der Szene zu verschwinden. */ .agent-avatar.pose-auth_expired .person-body, .agent-avatar.pose-auth_expiring .person-body { fill: #94a3b8; } .person-prop-lock-body { fill: #f87171; stroke: #7f1d1d; stroke-width: 1.5; } .person-prop-lock-shackle { fill: none; stroke: #f87171; stroke-width: 3; stroke-linecap: round; } .pose-auth_expiring .person-prop-lock-body { fill: #facc15; stroke: #713f12; } .pose-auth_expiring .person-prop-lock-shackle { stroke: #facc15; } /* Nur der harte Ausfall pulsiert - eine pulsierende Vorwarnung waere auf einem Dauermonitor Rauschen, das man nach einer Stunde nicht mehr sieht. */ .agent-avatar.pose-auth_expired .person-prop-lock { animation: office-lock-pulse 1.4s ease-in-out infinite; } @keyframes office-lock-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } } .person-prop-cup { fill: #f8fafc; stroke: #94a3b8; } .person-prop-newspaper { fill: #f8fafc; stroke: #94a3b8; stroke-width: 1; } .person-prop-speech { fill: #f8fafc; stroke: #94a3b8; stroke-width: 1; } .avatar-name { fill: #334155; font-size: 13px; font-weight: 600; text-anchor: middle; } /* KPI-Sparklines (Phase 4, F10: kein Chart-Lib, eigenes SVG-Zeichnen) */ .kpi-tile-label { fill: var(--color-text-inverse); font-size: 13px; font-weight: 600; } /* Kombiniert notiert, weil .kpi-tile-label weiter unten steht als .severity-* und die Farbe sonst bei gleicher Spezifitaet ueber die Reihenfolge zurueckgewinnt. */ .kpi-tile-label.severity-error { fill: #f87171; } .kpi-tile-label.severity-warning { fill: #facc15; } .kpi-sparkline-bg { fill: rgba(255, 255, 255, 0.04); } .kpi-sparkline-line { fill: none; stroke: #7dd3fc; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; } /* Modal (Phase 3/4: Board-/Monitor-Detailansicht, Visualization.md 4.5) */ .office-modal { border: none; border-radius: 12px; padding: 0; width: min(640px, 92vw); max-height: 80vh; box-shadow: 0 20px 50px rgba(15, 23, 42, 0.4); } .office-modal::backdrop { background: rgba(15, 23, 42, 0.55); } .office-modal-form { display: flex; flex-direction: column; max-height: 80vh; } .office-modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid #e2e8f0; } .office-modal-title { margin: 0; font-size: 1.1rem; } .office-modal-close { border: none; background: transparent; font-size: 1.4rem; line-height: 1; cursor: pointer; color: #64748b; } .office-modal-body { padding: 16px 20px; overflow-y: auto; } .office-modal-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; } .office-modal-item { border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px 12px; } .office-modal-item p { margin: 6px 0 0; color: #475569; font-size: 0.9rem; } .office-modal-item-heading { display: flex; align-items: center; gap: 8px; } .office-modal-badge { font-size: 0.75rem; padding: 2px 8px; border-radius: 999px; background: #e2e8f0; color: #1f2937; } .office-modal-item.severity-error { border-color: #fca5a5; background: #fef2f2; } .office-modal-item.severity-warning { border-color: #fde68a; background: #fffbeb; } .office-modal-item.status-error { border-color: #fca5a5; background: #fef2f2; } .office-modal-empty { color: #64748b; font-style: italic; } .kpi-modal-sparkline { width: 100%; height: 80px; margin-top: 8px; background: #10131a; border-radius: 6px; } /* Projekte-Modal (Issue #27: Klick auf den PO -> Projekte des Codeberg-Sync verwalten) */ .office-modal-error { margin: 0 0 10px; padding: 8px 12px; border-radius: 6px; background: #fef2f2; border: 1px solid #fca5a5; color: #991b1b; font-size: 0.9rem; } .repo-config-item { display: flex; align-items: center; gap: 10px; } .repo-config-swatch { flex: none; width: 16px; height: 16px; border-radius: 4px; border: 1px solid rgba(15, 23, 42, 0.15); } .repo-config-slug { flex: 1; font-weight: 600; color: #1f2937; } .repo-config-actions { display: flex; gap: 4px; flex: none; } .repo-config-form { flex-wrap: wrap; } .repo-config-form-fields { display: flex; flex: 1; gap: 8px; min-width: 0; } .repo-config-form-fields input[type="text"] { flex: 1; min-width: 0; padding: 6px 8px; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 0.9rem; } .repo-config-form-fields input[type="color"] { flex: none; width: 36px; height: 32px; padding: 2px; border: 1px solid #cbd5e1; border-radius: 6px; cursor: pointer; } .icon-button { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: #475569; cursor: pointer; } .icon-button:hover { background: #f1f5f9; border-color: #e2e8f0; color: #1f2937; } .icon-button-svg { width: 18px; height: 18px; } .repo-config-add-button { margin-top: 8px; color: #16a34a; } .repo-config-add-button:hover { background: #f0fdf4; border-color: #bbf7d0; color: #15803d; } /* Issue #53: Ampel-Symbole (Schreibrechte/Webhook) rechts neben den Bearbeiten-/ Loeschen-Symbolen einer Projektzeile - Tooltip (title) traegt den Pruefgrund. */ .repo-config-status { display: flex; gap: 2px; flex: none; } .repo-config-status-icon { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; cursor: help; } .repo-config-status-icon.is-ok { color: #16a34a; } .repo-config-status-icon.is-fail { color: #dc2626; } /* Hosts-Modal (Issue #70) - teilt sich Zeilen-, Formular- und Symbolstile mit dem Projekte-Modal; eigene Regeln braucht nur, was es dort nicht gibt. */ .host-config-subheading { margin: 18px 0 4px; font-size: 0.95rem; color: #1f2937; } .host-config-agent { flex: none; padding: 2px 8px; border-radius: 999px; background: #f1f5f9; color: #475569; font-size: 0.8rem; }