/* Erscheinungsbild nach DOSSIER ("RAPPORT": Creme + Petrol-Grün), inkl. Dark Mode. Der linke Navigator (Zeichnungsebenen + Ebenen-Baum) übernimmt das DOSSIER-Look: aktive Zeile als Petrol-Pille, nicht-aktive gedimmt, ausgeblendete grau; Augen-Schalter, Farbtupfer, eingerückte Kinder. */ /* Dark ist der feste Standard (Nutzer-Wunsch, unabhängig von OS/Browser- Präferenz — vorher hing das an `prefers-color-scheme` und zeigte je nach Umgebung fälschlich Light). Light bleibt als Opt-in erhalten (`[data-theme= "light"]`) für ein späteres manuelles Umschalt-Feature. */ :root { color-scheme: dark; /* Gestufte Elevations-Palette (Wunsch: dunkler + mehr Tiefe). Tiefster Grund hinten, Flächen werden nach vorn schrittweise heller — leicht kühl-neutral, harmoniert mit dem Petrol-Akzent. --bg Ebene 0 — App-Grund / Rinnen hinter allem (am dunkelsten) --panel Ebene 1 — Oberleiste, Docks, Panel-Körper --panel-2 Ebene 2 — angehobene Flächen: Panel-Köpfe, Hover-Zeilen --input versenkt — Eingabefelder (dunkler als die Fläche → Inset) */ /* Zwei-Ton-Grundlage (Nutzer-Vorgabe): Fast-Schwarz hinten, dunkles Neutralgrau für die Flächen. Neutral (kein Blaustich), mit dezenten Elevations-Schritten für Tiefe. */ --bg: #0e0e0e; --panel: #1d1d1d; --panel-2: #262626; --input: #0a0a0a; --line: #2b2b2b; --border: #383838; /* --sheet bleibt bewusst hell (#f0f0f0): das Zeichenblatt ist immer „Papier", unabhängig vom UI-Theme (Wunsch des Nutzers). */ --sheet: #f0f0f0; --ink: #ededed; --ink-2: #a8a8a8; --muted: #6e6e6e; --label: #c8c8c8; /* Akzent NEUTRAL (kein Petrol-Grün, Nutzer-Vorgabe): helles Neutralgrau für aktive/ausgewählte Flächen; weiße Schrift darauf bleibt lesbar. Hover/Dim sind dezente weiße Aufhellungen. */ --accent: #4d4d4d; --accent-light: #5e5e5e; --accent-dim: rgba(255, 255, 255, 0.07); --accent-border: rgba(255, 255, 255, 0.22); --active: #6a6a6a; --active-light: #7d7d7d; --active-dim: rgba(255, 255, 255, 0.1); --wall: #c8ccd2; --swing: #7e8794; --danger: #c85050; --danger-light: #d86060; /* Tiefere Schatten für klare Schichtung auf dunklem Grund. */ --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5); --shadow-2: 0 3px 12px rgba(0, 0, 0, 0.55); --shadow-3: 0 12px 34px rgba(0, 0, 0, 0.62); --overlay: rgba(0, 0, 0, 0.62); --font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --font-mono: "DM Mono", ui-monospace, "SF Mono", monospace; } /* Light-Palette als Opt-in (aktuell von nichts gesetzt — bereit für einen künftigen manuellen Theme-Umschalter in den Einstellungen). */ :root[data-theme="light"] { color-scheme: light; --bg: #e0dbd4; --panel: #e0dbd4; --panel-2: #d4cfc8; --input: #ece8e2; --line: #d4cfc8; --border: #c8c2ba; --sheet: #f0f0f0; --ink: #1a1a18; --ink-2: #555550; --muted: #8a8580; --label: #1a1a18; --accent: #2f5d54; --accent-light: #4a8a7c; --accent-dim: #e6efed; --accent-border: rgba(47, 93, 84, 0.35); --active: #1a655a; --active-light: #2f8275; --active-dim: rgba(26, 101, 90, 0.12); --wall: #2b3039; --swing: #9aa3b2; --danger: #8a1a1a; --danger-light: #b03030; --shadow-1: 0 1px 2px rgba(26, 26, 24, 0.08); --shadow-2: 0 2px 8px rgba(26, 26, 24, 0.14); --shadow-3: 0 6px 24px rgba(26, 26, 24, 0.18); --overlay: rgba(26, 26, 24, 0.36); } * { box-sizing: border-box; } html, body, #root { height: 100%; margin: 0; } /* Native-App-Verhalten: KEINE Textauswahl / „Alles markieren" in der App-Chrome. user-select global aus; nur in echten Eingaben wieder an, damit Tippen und Markieren im Feld weiter funktioniert. (Ctrl+A außerhalb von Eingaben wird in main.tsx zusätzlich unterbunden.) */ html, body, #root { -webkit-user-select: none; user-select: none; /* Kein Browser-eigenes Antippen/Highlight auf Touch. */ -webkit-tap-highlight-color: transparent; } input, textarea, [contenteditable], [contenteditable] * { -webkit-user-select: text; user-select: text; } body { font-family: var(--font); color: var(--ink); background: var(--bg); -webkit-font-smoothing: antialiased; } .app { display: flex; flex-direction: column; height: 100%; } /* ── Oberleiste ──────────────────────────────────────────────────────── */ /* Gruppen links nach rechts mit Trennern; die rechte Gruppe (Layout/ Ressourcen/Projekt) wird per margin-left:auto an den Rand geschoben. Bei sehr schmalen Fenstern darf die Leiste horizontal scrollen, statt zu brechen. */ .topbar { display: flex; align-items: center; gap: 10px; padding: 6px 14px; height: 60px; border-bottom: 1px solid var(--line); /* Oberleiste auf Ebene 1 (angehoben über dem App-Grund) + weicher Schatten nach unten → trennt sich klar von der Zeichenfläche (Tiefe). */ background: var(--panel); box-shadow: var(--shadow-1); position: relative; z-index: 2; overflow-x: auto; overflow-y: hidden; white-space: nowrap; /* Dezente, normalerweise unsichtbare Scrollleiste (Firefox). */ scrollbar-width: thin; scrollbar-color: transparent transparent; } /* Sehr ruhige eigene Scrollleiste (WebKit/Blink): Thumb nur beim Scrollen (`is-scrolling`, von TopBar gesetzt) oder beim Überfahren sichtbar. */ .topbar::-webkit-scrollbar { height: 6px; } .topbar::-webkit-scrollbar-track { background: transparent; } .topbar::-webkit-scrollbar-thumb { background: transparent; border-radius: 3px; transition: background 0.25s ease; } .topbar.is-scrolling { scrollbar-color: rgba(140, 140, 140, 0.45) transparent; } .topbar.is-scrolling::-webkit-scrollbar-thumb, .topbar:hover::-webkit-scrollbar-thumb { background: rgba(140, 140, 140, 0.4); } /* Eine Werkzeug-Gruppe der Oberleiste. */ .tb-group { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; } /* Vertikaler Trenner zwischen Gruppen. */ .tb-sep { flex: 0 0 auto; width: 1px; height: 22px; background: var(--border); } /* Beschriftetes Feld (Label + Control) in der Oberleiste. */ .tb-field { display: inline-flex; align-items: center; gap: 6px; } .tb-label { font-size: 11px; font-weight: 600; letter-spacing: 0.03em; color: var(--muted); } /* Kompaktes Dropdown der Oberleiste (Detailgrad, Massstab). */ .tb-select { border: 1px solid var(--border); background: var(--input); color: var(--ink); font-family: inherit; font-size: 11px; font-weight: 600; padding: 3px 6px; border-radius: 6px; cursor: pointer; } .tb-select:hover { border-color: var(--accent-border); } .tb-scale { font-family: var(--font-mono); min-width: 78px; } /* Gruppe aus Knöpfen (Zoom). */ /* Aktions-Pillen (100 % · Einpassen · Auswahl · PDF) — optisch eine Familie mit den Dropdown-Triggern (.tb-dd-trigger): jede Aktion ist eine eigene gerahmte, abgesetzte dunkle Pill mit demselben Akzent-Hover (Rand → accent-border, Fläche → accent-dim, Text → accent). Sie bleiben Buttons (Aktion ≠ Auswahl); nur die Optik ist angeglichen. */ .tb-btns { display: inline-flex; gap: 6px; } .tb-btn { display: inline-flex; align-items: center; /* Gleiche stets-dunkle Fläche wie .tb-dd-trigger — einheitlicher Pillen- Look über die ganze Oberleiste. */ border: 1px solid #4a4a4a; background: #2c2c2c; color: #e8e8e8; font-family: inherit; font-size: 11px; font-weight: 600; letter-spacing: 0.02em; padding: 3px 10px; border-radius: 8px; cursor: pointer; outline: none; transition: border-color 0.14s, background-color 0.14s, color 0.14s, box-shadow 0.14s; } .tb-btn:hover:not(:disabled) { border-color: var(--accent-border); background-color: rgba(255, 255, 255, 0.1); color: var(--accent); } .tb-btn:focus-visible { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); } .tb-btn:disabled { color: #8a8a8a; opacity: 0.55; cursor: default; } /* Live-Zoom-Anzeige in der Oberleiste (monospace, fixe Breite). */ .tb-zoom { font-family: var(--font-mono); font-size: 12px; font-weight: 600; color: var(--ink-2); min-width: 44px; text-align: right; } /* LIVE-Massstab 1:N + Zoom% kompakt übereinander (platzsparend). */ .tb-zoomstack { display: inline-flex; flex-direction: column; align-items: flex-end; justify-content: center; gap: 1px; line-height: 1.1; } .tb-zoomstack .tb-live-scale, .tb-zoomstack .tb-zoom { font-size: 11px; min-width: 0; } /* Live-Papier-Massstab „1:N" in der Oberleiste (Akzent, monospace). Hebt sich vom gewählten Massstab im Dropdown ab — dies ist der aktuelle Ist-Massstab. */ .tb-live-scale { font-family: var(--font-mono); font-size: 12px; font-weight: 700; color: var(--accent); min-width: 56px; text-align: right; } /* Pillen-Toggle der Oberleiste (Referenzlinien). */ .tb-toggle { border: 1px solid #4a4a4a; background: #2c2c2c; color: #e8e8e8; font-family: inherit; font-size: 11px; font-weight: 600; letter-spacing: 0.03em; padding: 6px 12px; border-radius: 8px; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .tb-toggle:hover { background: rgba(255, 255, 255, 0.1); border-color: var(--accent-border); color: var(--accent); } .tb-toggle.active { background: var(--accent); border-color: var(--accent); color: #fff; } /* ── DOSSIER-Grundprimitive: Segmentpille · Icon-Knopf · Zoom-Cluster ──────── */ /* Segmentpille (3er/4er): Aussencontainer rund, Zellen gleich hoch, interne Trenner über border-left. Genutzt für Zoom-Aktionen, B/I/U, L/C/R. Gleiche stets-dunkle Fläche wie .tb-dd-trigger/.tb-btn (einheitlicher Pillen-Look). */ .tb-seg { display: inline-flex; align-items: stretch; height: 24px; box-sizing: border-box; border: 1px solid #4a4a4a; border-radius: 8px; overflow: hidden; background: #2c2c2c; flex: 0 0 auto; } /* Akzent-Rand, wenn ein Text-Ziel selektiert ist (Auswahl-Bewusstsein). */ .tb-seg-accent { border-color: var(--accent); } .tb-seg-cell { display: inline-flex; align-items: center; justify-content: center; flex: 1 1 0; min-width: 26px; padding: 0 6px; border: none; border-left: 1px solid #4a4a4a; background: transparent; color: #b0b0b0; cursor: pointer; transition: background 0.14s, color 0.14s; } .tb-seg-cell:first-child { border-left: none; } .tb-seg-cell:hover:not(:disabled) { background: rgba(255, 255, 255, 0.1); color: #f0f0f0; } .tb-seg-cell.active { background: var(--accent); color: #fff; } .tb-seg-cell:disabled { opacity: 0.4; cursor: default; } /* Quadratischer Icon-Knopf (BarButton). */ .tb-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; box-sizing: border-box; padding: 0; border: 1px solid #4a4a4a; border-radius: 8px; background: #2c2c2c; color: #b0b0b0; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .tb-iconbtn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.1); border-color: var(--accent-border); color: var(--accent); } .tb-iconbtn:disabled { opacity: 0.4; cursor: default; } /* Massstab/Zoom-Cluster (2×2-Raster): Stat-Pille links über beide Zeilen. */ .tb-zoomcluster { /* Einfaches Flex: links die Stat-Pille, rechts der gestapelte (Massstab-Dropdown + Zoom-Segment) Flex-Container. Früher ein 2-Zeilen-Grid mit `grid-row: 1/span 2` an der Stat-Pille — das erzeugte aber eine leere Phantom-Zeile (die rechte Seite ist EIN Flex-Container, keine zwei Grid- Zeilen) und damit +6px Höhe, wodurch der Cluster höher war als die übrigen zweizeiligen Kombos. */ display: inline-flex; align-items: center; gap: 6px; } .tb-stat { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 70px; height: 50px; box-sizing: border-box; border: 1px solid var(--border); border-radius: 14px; background: var(--input); /* Ring (Box-Shadow statt Border) für „am Massstab" — startet unsichtbar (Alpha 0) und blendet weich ein/aus, statt die Fläche aufzuhellen. */ box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0); transition: box-shadow 0.25s ease; } .tb-stat-scale { font-family: var(--font-mono); font-size: 11px; font-weight: 700; line-height: 1.3; color: var(--accent); width: 100%; text-align: center; border-bottom: 1px solid var(--border); } .tb-stat-zoom { font-family: var(--font-mono); font-size: 11px; line-height: 1.3; color: var(--ink-2); width: 100%; text-align: center; } /* „am Massstab" (Live == gewählter Massstab): weisser Ring statt aufgehellter Fläche — blendet dynamisch ein/aus (s. transition oben), Fläche bleibt unverändert. */ .tb-stat-atscale { box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.9); } .tb-zoomcluster-right { display: inline-flex; flex-direction: column; align-items: stretch; gap: 6px; } /* ── Text-Gruppe (immer sichtbar, 3×2) ───────────────────────────────────── */ .tb-textgroup { flex-direction: column; align-items: stretch; gap: 6px; } .tb-textgroup-row { display: inline-flex; align-items: center; gap: 6px; } /* „+ Text"-Knopf: Pille mit Icon + Label. */ /* Zeilenhöhe-Regler (ersetzt den früheren „+ Text"-Knopf — Text wird ein eigenständiges Zeichenwerkzeug, AUDIT B1). Gleiche dunkle Pillen-Optik wie die übrigen Oberleisten-Controls (.tb-dd-trigger/.tb-seg). */ .tb-lineheight { display: inline-flex; align-items: stretch; height: 24px; width: 80px; box-sizing: border-box; border: 1px solid #4a4a4a; border-radius: 8px; overflow: hidden; background: #2c2c2c; flex: 0 0 auto; } .tb-lineheight-btn { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 22px; border: none; background: transparent; color: #b0b0b0; cursor: pointer; transition: background 0.14s, color 0.14s; } .tb-lineheight-btn:hover { background: rgba(255, 255, 255, 0.1); color: #f0f0f0; } .tb-lineheight-value { flex: 1 1 auto; display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: 11px; color: #e8e8e8; border-left: 1px solid #4a4a4a; border-right: 1px solid #4a4a4a; } /* Marken-Gruppe: Wortmarke + kleiner Icon-Stapel daneben (Settings/Ressourcen), wie im DOSSIER-Rhino-Plugin (ToolbarApp.jsx: Logo + vertikal gestapelte Settings-Icons direkt daneben, vor dem ersten Trenner). */ .tb-brandgroup { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; } .brand { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; line-height: 1; } .brand-word { font-weight: 800; font-size: 15px; letter-spacing: -0.01em; color: var(--ink); } .brand-dot { color: var(--accent); } .tag { font-weight: 500; font-size: 10px; color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 1px 7px; } /* Icon-Stapel neben der Wortmarke (Settings/Ressourcen) — schmal, dezent. */ .tb-brandicons { display: flex; flex-direction: column; gap: 2px; } .tb-brandicon { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 17px; padding: 0; border: none; background: transparent; border-radius: 4px; color: var(--muted); cursor: pointer; transition: background 0.14s, color 0.14s; } .tb-brandicon .tb-ico { font-size: 13px; } .tb-brandicon:hover { background: var(--accent-dim); color: var(--ink); } .tb-brandicon.active { background: var(--accent-dim); color: var(--accent); } .project-name { color: var(--muted); font-size: 13px; } /* Rechte Gruppe der Oberleiste: Layout-Menü + Ressourcen-Knopf + Projektname. margin-left:auto schiebt sie an den rechten Rand. */ .topbar-right { display: inline-flex; align-items: center; gap: 12px; margin-left: auto; padding-left: 10px; flex: 0 0 auto; } /* „Ressourcen"-Eintrag der Oberleiste (öffnet die Manager-Schublade). */ .res-trigger { border: 1px solid var(--border); background: var(--input); color: var(--ink-2); font-family: inherit; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; padding: 6px 14px; border-radius: 999px; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .res-trigger:hover { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent); } .res-trigger.active { background: var(--accent); border-color: var(--accent); color: #fff; } /* Ansichtstyp-Umschalter (Geschoss). */ .view-toggle { display: inline-flex; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; background: var(--input); } .view-btn { border: none; background: transparent; color: var(--muted); font-family: inherit; font-size: 12px; font-weight: 600; letter-spacing: 0.03em; padding: 6px 16px; cursor: pointer; transition: background 0.14s, color 0.14s; } .view-btn + .view-btn { border-left: 1px solid var(--border); } .view-btn:hover { background: var(--accent-dim); color: var(--ink); } .view-btn.active { background: var(--accent); color: #fff; } /* ── Ansichten als kompaktes Icon-Grid (DOSSIER-Stil, 3 oben / 3 unten) ──── */ .view-grid { display: grid; grid-template-columns: repeat(3, auto); grid-auto-rows: 1fr; gap: 3px; flex: 0 0 auto; } .view-grid-4 { grid-template-columns: repeat(4, auto); } .view-icon { border: 1px solid #4a4a4a; background: #2c2c2c; color: #b0b0b0; display: inline-flex; align-items: center; justify-content: center; width: 31px; height: 22px; padding: 0; border-radius: 6px; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .view-icon:hover:not(:disabled) { background: rgba(255, 255, 255, 0.1); color: #f0f0f0; border-color: var(--accent-border); } .view-icon.active { background: var(--accent); color: #fff; border-color: var(--accent); } .view-icon:disabled { opacity: 0.4; cursor: default; } /* Kamera-Button steckt im Popover-Wrapper → als Grid-Zelle behandeln. */ .view-grid .tb-popwrap { display: inline-flex; } /* Material-Symbols-Icon in Oberleisten-Buttons (Referenzlinien, Linien-Modus, Ressourcen) — auf die kompakte Icon-Button-Höhe skaliert, currentColor erbend, sodass aktiv/hover die Farbe wie bei den View-Icons mitführt. */ .tb-ico { font-size: 17px; line-height: 1; } /* Linien-Modus-Segmente tragen jetzt Icons statt Text — Padding wie eine Icon-Zelle (zentriert), damit beide Segmente gleich breit wirken. */ .view-btn-ico { display: inline-flex; align-items: center; justify-content: center; padding: 4px 12px; } .view-btn-ico:disabled { opacity: 0.4; cursor: default; } /* Gestapelte Gruppe (Label-Felder untereinander, z. B. Ebenen-/Zeichn.-Komb.). */ .tb-group.tb-stack { flex-direction: column; align-items: stretch; gap: 6px; } .tb-field-row { justify-content: space-between; gap: 8px; } /* Rumpf: Navigator + Inhalt nebeneinander. */ .body { position: relative; /* Bezugsrahmen für schwebende Panels + Andock-Zonen. */ flex: 1; display: flex; min-height: 0; } /* ── Footer / Status-Leiste (unten) ────────────────────────────────────── App-Shell ist eine Spalte: Oberleiste / Rumpf (flex:1) / Status-Leiste. Die Leiste hat eine feste Höhe (~22 px) und schrumpft nicht. Links der Werkzeug-Hinweis, rechts ein Cluster abgeleiteter View-/Modell-Werte. */ .statusbar { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 12px; height: 22px; padding: 0 12px; border-top: 1px solid var(--line); background: var(--panel-2); font-size: 11px; color: var(--ink-2); user-select: none; } /* Werkzeug-Hinweis links (kontextueller Text). */ .sb-hint { font-weight: 600; letter-spacing: 0.02em; color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* Rechter Wert-Cluster. */ .sb-cluster { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; white-space: nowrap; } /* Ein Wert-Feld: kleine Beschriftung + Wert. */ .sb-field { display: inline-flex; align-items: baseline; gap: 5px; } .sb-key { font-size: 10px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); } .sb-val { font-family: var(--font-mono); font-size: 11px; color: var(--ink); } /* Vertikaler Trenner im Cluster. */ .sb-sep { width: 1px; height: 12px; background: var(--border); } /* Renderer-Umschalter (WebGL2 / Nordstern) — kleine Segmentpille; ursprünglich in der Statusleiste (22px-Leiste), jetzt im Einstellungs-Fenster. In einer flex-column (.imp-section) würde sie sonst auf volle Breite gestreckt werden (Standard-`align-items: stretch`) — bleibt daher inhaltsbreit. */ .sb-renderer-toggle { display: inline-flex; align-items: stretch; align-self: flex-start; height: 16px; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; background: var(--input); } .sb-renderer-btn { display: inline-flex; align-items: center; border: none; border-left: 1px solid var(--border); background: transparent; color: var(--muted); font-family: inherit; font-size: 10px; font-weight: 600; letter-spacing: 0.02em; padding: 0 8px; cursor: pointer; transition: background 0.14s, color 0.14s; } .sb-renderer-btn:first-child { border-left: none; } .sb-renderer-btn:hover:not(:disabled) { background: var(--accent-dim); color: var(--accent); } .sb-renderer-btn.active { background: var(--accent); color: #fff; } .sb-renderer-btn:disabled { opacity: 0.4; cursor: default; } /* Einzelner Umschalt-Button in der Statusleiste (z. B. Referenzlinien). */ .sb-toggle { display: inline-flex; align-items: center; border: 1px solid var(--border); border-radius: 5px; background: transparent; color: var(--muted); font-family: inherit; font-size: 10px; font-weight: 600; letter-spacing: 0.02em; padding: 2px 8px; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .sb-toggle:hover:not(:disabled) { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); } .sb-toggle.active { background: var(--accent); border-color: var(--accent); color: #fff; } .sb-toggle:disabled { opacity: 0.4; cursor: default; } /* Generische Segment-Pille für die Statusleiste (Linien-Darstellung, Fang) — gleiche Masse/Optik wie .sb-renderer-toggle, aber nicht renderer-spezifisch. */ .sb-seg { display: inline-flex; align-items: stretch; height: 16px; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; background: var(--input); } .sb-seg.disabled { opacity: 0.4; } .sb-seg-btn { display: inline-flex; align-items: center; border: none; border-left: 1px solid var(--border); background: transparent; color: var(--muted); font-family: inherit; font-size: 10px; font-weight: 600; letter-spacing: 0.02em; padding: 0 8px; cursor: pointer; transition: background 0.14s, color 0.14s; } .sb-seg-btn:first-child { border-left: none; } .sb-seg-btn:hover:not(:disabled) { background: var(--accent-dim); color: var(--accent); } .sb-seg-btn.active { background: var(--accent); color: #fff; } .sb-seg-btn:disabled { opacity: 0.4; cursor: default; } /* Caret-Segment (öffnet das Fang-Popover) — schmaler als die Text-Segmente. */ .sb-seg-caret { padding: 0 6px; font-size: 9px; line-height: 1; } /* Fang-Kontrolle: Wrapper ist relativ positioniert, damit das Popover NACH OBEN öffnen kann (die Leiste sitzt ganz unten im App-Shell). */ .sb-snap-wrap { position: relative; display: inline-flex; } .sb-snap-popover { position: absolute; right: 0; bottom: 100%; margin: 0 0 6px; padding: 6px 0; min-width: 170px; background: var(--panel, #1c2128); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45); z-index: 200; } .sb-snap-row { display: flex; align-items: center; gap: 8px; padding: 3px 12px; font-size: 12px; cursor: pointer; user-select: none; white-space: nowrap; } .sb-snap-row input[type="checkbox"] { accent-color: var(--accent); } .sb-snap-row-num { justify-content: space-between; cursor: default; } .sb-snap-row-num input[type="number"] { width: 64px; } /* ── Command-Line (Rhino-artig) — über der Statusleiste ────────────────────── Dunkle, ruhige Leiste mit Prompt links, klickbaren Inline-Optionen und einer monospace-Texteingabe rechts; das Autocomplete-Dropdown öffnet nach oben. */ .cmdline { position: relative; flex: 0 0 auto; display: flex; align-items: center; gap: 10px; height: 26px; padding: 0 12px; border-top: 1px solid var(--line); background: var(--input); font-size: 12px; user-select: none; } .cmdline-prompt { flex: 0 0 auto; font-weight: 600; color: var(--ink); white-space: nowrap; } .cmdline-options { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; } .cmdline-option { appearance: none; border: 1px solid var(--border); background: var(--panel-2); color: var(--ink); font: inherit; font-size: 11px; padding: 1px 7px; border-radius: 4px; cursor: pointer; } .cmdline-option:hover { border-color: var(--accent-border); background: var(--accent-dim); } .cmdline-option.active { border-color: var(--accent); background: var(--accent-dim); color: var(--accent); } .cmdline-option-val { color: var(--accent); font-family: var(--font-mono); } /* Tastatur-Kürzel vor dem Options-Label (z. B. „U" der Kopiermodi). */ .cmdline-option-key { display: inline-block; margin-right: 5px; padding: 0 4px; border-radius: 3px; background: var(--panel-3, var(--panel)); color: var(--ink-dim, var(--ink)); font-family: var(--font-mono); font-size: 10px; font-weight: 600; } .cmdline-option.active .cmdline-option-key { background: var(--accent); color: var(--bg, #fff); } /* Tab-Feld-Zyklus: kleine beschriftete Boxen (Länge/Winkel/…); aktives Feld hervorgehoben (Akzent-Rand). Wert „—" = ungelockt (folgt der Maus). */ .cmdline-fields { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; } .cmdline-field { display: inline-flex; align-items: baseline; gap: 5px; padding: 1px 7px; border: 1px solid var(--border); border-radius: 4px; background: var(--panel-2); font-size: 11px; white-space: nowrap; } .cmdline-field.active { border-color: var(--accent-border); background: var(--accent-dim); } .cmdline-field-label { color: var(--muted); } .cmdline-field.active .cmdline-field-label { color: var(--accent); } .cmdline-field-val { font-family: var(--font-mono); color: var(--ink); min-width: 1.2em; text-align: right; } /* Live (maus-folgender) Wert ruhiger als ein gelockter (getippter) Wert. */ .cmdline-field.live .cmdline-field-val { color: var(--muted); } .cmdline-field.locked .cmdline-field-val { color: var(--ink); font-weight: 600; } .cmdline-input { flex: 1 1 auto; min-width: 80px; appearance: none; border: none; background: transparent; color: var(--ink); font-family: var(--font-mono); font-size: 12px; outline: none; user-select: text; } .cmdline-input::placeholder { color: var(--muted); } /* Autocomplete-Dropdown öffnet nach OBEN (Leiste sitzt ganz unten). */ .cmdline-suggest { position: absolute; left: 12px; bottom: 100%; margin: 0 0 2px; padding: 4px 0; list-style: none; min-width: 160px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 6px; box-shadow: var(--shadow-2); z-index: 50; } .cmdline-suggest-item { padding: 3px 12px; font-family: var(--font-mono); font-size: 12px; color: var(--ink); cursor: pointer; } .cmdline-suggest-item:hover { background: var(--accent-dim); color: var(--accent); } /* ── Navigator (links) ───────────────────────────────────────────────── */ .navigator { width: 252px; flex: 0 0 252px; display: flex; flex-direction: column; gap: 18px; padding: 14px 8px; border-right: 1px solid var(--line); background: var(--panel); overflow-y: auto; } .nav-group { display: flex; flex-direction: column; } .nav-group-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 2px 6px 6px; } .nav-group-title { font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); } .nav-group-actions { display: inline-flex; gap: 4px; } .nav-add { border: 1px solid var(--border); background: var(--input); color: var(--ink-2); font-family: inherit; font-size: 10px; font-weight: 600; letter-spacing: 0.02em; padding: 3px 9px; border-radius: 999px; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .nav-add:hover { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent); } .nav-list { display: flex; flex-direction: column; } /* ── Zeichnungsebenen: einklappbare Kategorien (Geschosse/Schnitte/ Ansichten/2D-Zeichnungen) ──────────────────────────────────────────── */ .level-cat { display: flex; flex-direction: column; } .level-cat-head { display: flex; align-items: center; gap: 6px; min-height: 24px; padding: 3px 10px 3px 6px; background: var(--panel-2); border-bottom: 1px solid var(--line); cursor: pointer; user-select: none; } .level-cat-head:hover { background: var(--accent-dim); } .level-cat-chevron { flex: 0 0 auto; display: inline-flex; font-size: 9px; color: var(--muted); transition: transform 0.12s; transform: rotate(0deg); } .level-cat-chevron.open { transform: rotate(90deg); } .level-cat-title { flex: 1; min-width: 0; font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* Blankes „+"-Glyph: reines Zeichen, keine Button-Chrome. */ .level-cat-add { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; font-size: 13px; line-height: 1; color: var(--muted); cursor: pointer; } .level-cat-add:hover { color: var(--accent-light); } /* Zeile (Zeichnungsebene). */ .nav-row { display: flex; align-items: center; gap: 6px; width: 100%; min-height: 26px; padding: 2px 10px 2px 2px; border-bottom: 1px solid var(--line); background: transparent; color: var(--label); font-size: 12px; text-align: left; cursor: pointer; user-select: none; transition: background 0.12s; } .nav-row:hover { background: var(--panel-2); } /* Aktive Zeile = Petrol-Pille; Trennlinie verschwindet. */ .nav-row.active { background: var(--active-dim); border-radius: 999px; border-bottom-color: transparent; color: var(--active-light); } .nav-row.active .nav-label { font-weight: 600; color: var(--active-light); } /* Ausgeblendete (nicht-aktive) Zeile: gedimmt. */ .nav-row.hidden { opacity: 0.45; } .nav-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--label); font-weight: 500; } .nav-elev { flex: 0 0 auto; font-family: var(--font-mono); font-size: 10px; color: var(--muted); } /* Augen-Schalter (Sichtbarkeit) — erbt Zeilenfarbe via currentColor. */ .eye { flex: 0 0 auto; width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; border: none; background: transparent; color: var(--muted); cursor: pointer; border-radius: 50%; padding: 0; transition: background 0.12s, color 0.12s; } .eye:hover { background: rgba(0, 0, 0, 0.06); color: var(--ink); } .eye.is-on { color: var(--ink); } .nav-row.active .eye, .nav-row.active .eye.is-on { color: var(--active-light); } /* ── Kategorie-Badges (Chips) ────────────────────────────────────────── */ .chip { flex: 0 0 auto; font-size: 9px; font-weight: 600; letter-spacing: 0.06em; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border); background: var(--input); color: var(--ink-2); text-transform: uppercase; } .badge-floor { background: var(--active-dim); border-color: var(--accent-border); color: var(--active); } .badge-section { background: rgba(120, 90, 160, 0.14); border-color: rgba(120, 90, 160, 0.35); color: #6d4f93; } .badge-elevation { background: rgba(40, 120, 110, 0.14); border-color: rgba(40, 120, 110, 0.35); color: #2f7a6c; } .badge-drawing { background: rgba(181, 98, 30, 0.14); border-color: rgba(181, 98, 30, 0.35); color: #b5621e; } /* ── Ebenen-Baum (Kategorien) ────────────────────────────────────────── */ .cat-row { display: flex; align-items: center; gap: 6px; min-height: 24px; padding: 1px 10px 1px 6px; font-size: 12px; color: var(--label); border-bottom: 1px solid var(--line); cursor: default; user-select: none; transition: background 0.12s; } .cat-row:hover { background: var(--panel-2); } .cat-row { cursor: pointer; /* Zeile ist als „aktive Ebene wählen" klickbar */ } /* Aktive Ebene (Zeichen-Ziel): Akzentstreifen links + leichte Akzentfüllung. */ .cat-row.active { background: var(--accent-dim); box-shadow: inset 2px 0 0 var(--accent); } .cat-row.active .cat-name { color: var(--text, #e6e6e6); font-weight: 600; } .cat-row.hidden { opacity: 0.45; } /* Gesperrt (Modus „Andere grau & gesperrt"): gedämpft, Cursor signalisiert Nicht-Bearbeitbarkeit. Gilt für Ebenen- und Zeichnungsebenen-Zeilen. */ .cat-row.locked, .nav-row.locked { cursor: not-allowed; } .cat-row.locked .cat-name, .nav-row.locked .nav-label { font-style: italic; } .cat-code { flex: 0 0 auto; font-family: var(--font-mono); font-size: 9px; color: var(--muted); width: 20px; text-align: left; } .cat-swatch { flex: 0 0 auto; width: 12px; height: 12px; border-radius: 2px; border: 1px solid var(--border); } .cat-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500; } /* ── Inhalt (rechts) ─────────────────────────────────────────────────── */ .content { flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; } /* Dokument-Tabs „Zeichnungen/Blätter" — Kopfzeile über der Zeichenfläche. Leiste auf Ebene 1 (--panel), der aktive Tab hebt sich auf Ebene 2 an und verschmilzt unten mit dem Grund der Zeichenfläche darunter (--bg), wie bei klassischen Dokument-Tabs. */ .drawing-tabs { display: flex; align-items: flex-end; gap: 2px; padding: 6px 10px 0; border-bottom: 1px solid var(--line); background: var(--panel); overflow-x: auto; scrollbar-width: thin; } .drawing-tab { border: 1px solid transparent; border-bottom: none; background: transparent; color: var(--muted); font-family: inherit; font-size: 12px; font-weight: 600; letter-spacing: 0.01em; padding: 7px 16px; border-radius: 8px 8px 0 0; cursor: pointer; white-space: nowrap; transition: background 0.14s, color 0.14s, border-color 0.14s; } .drawing-tab:hover { background: var(--panel-2); color: var(--ink); } .drawing-tab.active { background: var(--bg); border-color: var(--line); color: var(--ink); /* Nahtloser Übergang: die Unterkante verschwindet im Zeichenflächen-Grund. */ position: relative; top: 1px; } .drawing-tab-label { pointer-events: none; } /* Eine Ansicht füllt die Restfläche. */ .pane { position: relative; flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; overflow: hidden; } .pane-title { position: absolute; top: 12px; left: 12px; z-index: 2; font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); background: var(--accent-dim); border: 1px solid var(--accent-border); padding: 4px 9px; border-radius: 999px; } .placeholder { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; color: var(--muted); background: var(--panel); } .placeholder strong { color: var(--ink); font-size: 16px; } .placeholder span { font-size: 13px; max-width: 360px; text-align: center; line-height: 1.5; } /* Freie 2D-Zeichenfläche. */ .drawing-area { position: relative; flex: 1; min-height: 0; background: var(--bg); overflow: hidden; } .drawing-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; } /* ── 3D-Viewport (Contract: Viewport3D rendert
) ── */ .viewport { position: relative; flex: 1; min-height: 0; overflow: hidden; } /* Anzeigegröße per CSS festnageln, entkoppelt von der Renderauflösung — verhindert die ResizeObserver-Wachstumsschleife auf HiDPI-Displays. */ .viewport canvas { position: absolute; inset: 0; display: block; width: 100% !important; height: 100% !important; } .viewport-fallback { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; padding: 24px; text-align: center; color: var(--muted); background: var(--panel); } .viewport-fallback strong { color: var(--ink); font-size: 15px; } .viewport-fallback span { max-width: 320px; font-size: 13px; line-height: 1.5; } .viewport-fallback code { font-family: var(--font-mono); background: var(--panel-2); padding: 1px 5px; border-radius: 4px; font-size: 12px; } /* ── Grundriss (SVG) ─────────────────────────────────────────────────── */ .plan-legend { position: absolute; bottom: 12px; left: 12px; z-index: 2; font-size: 11px; font-weight: 600; color: var(--muted); background: var(--accent-dim); border: 1px solid var(--accent-border); padding: 4px 9px; border-radius: 999px; } .plan-svg { flex: 1; min-height: 0; width: 100%; /* Die GESAMTE Zeichenfläche ist Papier (hell), nicht nur der Modell-Bereich: das SVG-Element-Backing füllt den sichtbaren Bereich unabhängig von Pan/Zoom (die .plan-bg-Rechteckfläche deckt nur die Modellgrenzen ab). */ background: var(--sheet); } .plan-bg { fill: var(--sheet); } /* CAD-typische Ecken: alle Plan-Striche gehren (miter) statt an dicken Ecken eine Stufe/Butt-Cap-Lücke zu zeigen. Der Miter-Grenzwert verhindert überlange Spitzen bei sehr spitzen Winkeln (dann kappen die Enden rund). Gilt für Poché-Umrisse (polygon), Wand-/Tür-/2D-Linien und Bögen gleichermaßen. */ .plan-svg polygon, .plan-svg polyline, .plan-svg line, .plan-svg path { stroke-linejoin: miter; stroke-linecap: round; stroke-miterlimit: 8; } /* Tür-/Fenster- + Referenz-Linien: NUR Farbe + fill hier. Strichstärke und Strichmuster kommen aus dem Modell in mm Papier und werden in PlanView inline gesetzt (vector-effect:non-scaling-stroke → papierkonstant beim Zoomen). Das Zeichenblatt ist IMMER hell (--sheet) → Plan-Tinte bleibt fix dunkel, unabhängig vom UI-Theme (sonst unsichtbar auf Weiß im Dark-Mode). */ .plan-svg .door-leaf { stroke: #1a1a1a; fill: none; } .plan-svg .door-swing { stroke: #1a1a1a; fill: none; } /* Anschlag-/Rahmenstriche der Tür (nur im Detailgrad „fein"). */ .plan-svg .door-frame { stroke: #1a1a1a; fill: none; } /* Glaslinien des Fensters (dünn, gedämpft — die Verglasung im Grundriss). */ .plan-svg .window-glass { stroke: #1a1a1a; fill: none; } /* Referenzlinie = Wand-Achslinie (Toggle „Referenzlinien"), gestrichelt. */ .plan-svg .wall-axis { stroke: var(--accent); fill: none; opacity: 0.85; } /* Auswahl-Hervorhebung (Links-Klick): Akzentkontur + leichte Füllung. */ .plan-svg .plan-selected { fill: var(--accent-dim); stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; } /* Hervorhebung selektierter 2D-Zeichenelemente (Mehrfach/Marquee): Akzentkontur über der Geometrie, keine Füllung. */ .plan-svg .plan-sel-draw { fill: none; stroke: var(--accent); stroke-width: 2.5; vector-effect: non-scaling-stroke; } /* Auswahl-Rechteck (Marquee, Links-Drag): gestrichelte Akzentkontur, leichte Füllung. Strichstärke + Strichmuster papierkonstant (non-scaling-stroke). Die Default-Variante (links→rechts, „einschließend") ist durchgezogen; die crossing-Variante (rechts→links, „berührend") gestrichelt — wie in CAD üblich. */ .plan-svg .plan-marquee { fill: var(--accent-dim); fill-opacity: 0.35; stroke: var(--accent); stroke-width: 1.25; vector-effect: non-scaling-stroke; } .plan-svg .plan-marquee-crossing { stroke-dasharray: 5 4; } /* Editier-Griffe (Grips) des selektierten Elements: gefüllte Akzent-Quadrate mit hellem Rand. Ziehbar (Treffer in PlanView per Bildschirm-Pixel). */ .plan-svg .plan-grip { fill: var(--accent-light); stroke: #fff; stroke-width: 1.5; vector-effect: non-scaling-stroke; } /* Kanten-/Seiten-Griffe (Edge Grips): dreieckiger Anfasser je Seite, Spitze nach außen. Gleiche Akzent-/Rand-Farben wie die Eckpunkt-Griffe. */ .plan-svg .plan-edge-grip { fill: var(--accent); stroke: #fff; stroke-width: 1.5; vector-effect: non-scaling-stroke; } /* ── Freie 2D-Zeichengeometrie (Drawing2D) ─────────────────────────────── Farbe kommt aus dem Modell (Kategorie/LineStyle) und wird in PlanView inline als stroke gesetzt; hier nur der Fallback + fill:none. */ .plan-svg .draw2d { /* Plan-Tinte fix dunkel: KEINE CSS-`stroke`-Regel hier — eine CSS-Property überstimmt sonst das inline gesetzte `stroke`-Presentation-Attribut, sodass im Dark-Mode `var(--ink)` (#edeef0, hell) gewänne und 2D-Linien auf dem hellen Papier (#f0f0f0) unsichtbar würden. Die Strichfarbe kommt deshalb ausschließlich aus dem Modell (Kategorie/LineStyle), inline in PlanView gesetzt, theme-unabhängig (generatePlan: dunkler Plan-Tinte-Default). */ fill: none; } /* Kontext-Konturen (importierte Höhenlinien): dezente Referenz-Linien. Die Strichfarbe wird in generatePlan inline gesetzt (gedämpftes Grau); hier nur fill:none + leichte Transparenz, damit sie hinter den Bauteilen ruhig bleiben. */ .plan-svg .context-line { fill: none; opacity: 0.7; } /* ── Zeichenwerkzeug-Overlay (Live-Vorschau, Stützpunkte, Snap, HUD) ───── Immer obenauf, nicht klickbar (pointer-events:none am in PlanView). */ .plan-svg .tool-preview-line { stroke: #8a8a8a; fill: none; stroke-width: 1.4; opacity: 0.95; } .plan-svg .tool-preview-fill { fill: var(--accent-dim); fill-opacity: 0.35; stroke: var(--accent-light); stroke-width: 1.25; } .plan-svg .tool-vertex { fill: var(--accent); stroke: #fff; stroke-width: 1; } .plan-svg .snap-glyph { fill: none; stroke: #ffcc44; stroke-width: 1.4; } .plan-svg .snap-glyph.snap-fill { fill: #ffcc44; } .plan-svg .snap-helper-line { stroke: #ffcc44; stroke-width: 1; stroke-dasharray: 4 4; opacity: 0.8; } .plan-svg .tool-hud { fill: var(--accent-light); paint-order: stroke; stroke: rgba(0, 0, 0, 0.55); stroke-width: 3px; font-family: var(--mono, ui-monospace, monospace); dominant-baseline: middle; } /* ── Modusleiste der Transformation (über der Mitte) ─────────────────────── */ .transform-bar { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 60; display: flex; align-items: center; gap: 10px; padding: 6px 10px; background: var(--panel, #1c2128); border: 1px solid var(--accent-border, #355); border-radius: 10px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45); font-size: 12px; user-select: none; } .transform-bar .tf-op { font-weight: 700; color: var(--accent-light, #6db5a4); } .transform-bar .tf-sep { width: 1px; align-self: stretch; background: var(--line, #333); } .transform-bar .tf-modes { display: flex; gap: 4px; } .transform-bar .tf-mode { display: inline-flex; align-items: center; gap: 5px; padding: 3px 8px; border-radius: 6px; border: 1px solid transparent; background: transparent; color: inherit; cursor: pointer; } .transform-bar .tf-mode:hover { background: var(--accent-dim); } .transform-bar .tf-mode.active { background: var(--accent); color: #fff; } .transform-bar .tf-key { display: inline-grid; place-items: center; width: 16px; height: 16px; border-radius: 4px; background: rgba(255, 255, 255, 0.14); font-weight: 700; font-size: 10px; } .transform-bar .tf-hint { opacity: 0.8; } .transform-bar .tf-cancel { border: none; background: transparent; color: inherit; cursor: pointer; opacity: 0.7; font-size: 13px; } .transform-bar .tf-cancel:hover { opacity: 1; } /* ── Snap-Menü (TopBar-Popover) ──────────────────────────────────────────── */ .snap-menu { position: relative; display: inline-flex; } .snap-popover { position: fixed; z-index: 200; min-width: 200px; padding: 6px; background: var(--panel, #1c2128); border: 1px solid var(--line, #333); border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45); display: flex; flex-direction: column; gap: 2px; } .snap-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; font-size: 12px; border-radius: 5px; cursor: pointer; user-select: none; } .snap-row:hover { background: var(--accent-dim); } .snap-row input[type="checkbox"] { accent-color: var(--accent); } .snap-master { font-weight: 600; } .snap-field { justify-content: space-between; } .snap-field input, .snap-field select { width: 84px; } .snap-sep { height: 1px; margin: 4px 2px; background: var(--line, #333); } /* ── Kamera-Einstellungen (Popover in der Ansicht-3D-Gruppe) ─────────── */ .tb-popwrap { position: relative; display: inline-flex; } .tb-popover { position: fixed; z-index: 200; min-width: 200px; padding: 10px; background: var(--panel, #1c2128); border: 1px solid var(--line, #333); border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45); } .tb-poprow { display: flex; flex-direction: column; gap: 6px; } .tb-poprow input[type="range"] { width: 100%; accent-color: var(--accent); } /* ── Fehlerseite (ErrorBoundary) ─────────────────────────────────────── */ .error-screen { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; height: 100%; padding: 40px; text-align: center; } .error-screen pre { background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 12px 16px; color: #b42318; font-size: 13px; max-width: 90%; overflow: auto; } .error-screen button { border: 1px solid var(--accent); background: var(--accent); color: white; border-radius: 999px; padding: 8px 18px; font-size: 14px; cursor: pointer; } /* ── Ressourcen-Manager (schwebendes Fenster / Overlay) ─────────────────── Drei Tabs (Bauteile / Schraffuren / Linien). Dunkles DOSSIER-Look: abgesetzte Sektionen, Pill-Inputs, Petrol-Akzent für aktive Elemente. Schwebt ÜBER allem (hoher z-index, über dem Kontextmenü ~300) mit abgedunkeltem Hintergrund; Klick auf den Hintergrund schließt. */ .res-overlay { position: fixed; inset: 0; z-index: 1000; background: var(--overlay); display: flex; justify-content: flex-end; } .res-drawer { width: min(880px, 94vw); height: 100%; display: flex; flex-direction: column; background: var(--panel); border-left: 1px solid var(--border); box-shadow: var(--shadow-3); } /* Frei schwebendes, NICHT-modales Ressourcen-Fenster (kein Backdrop): schwebt über dem Grundriss, der dahinter bedienbar bleibt. Position/Größe kommen als Inline-Style; die Kopfzeile (res-head) ist der Verschiebegriff. */ .res-window { position: fixed; z-index: 700; display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow-3, 0 12px 32px rgba(0, 0, 0, 0.45)); overflow: hidden; } .res-window .res-head { cursor: move; touch-action: none; user-select: none; } /* Greifer unten rechts: nutzt .floating-resize (siehe unten), sitzt aber im overflow:hidden-Fenster korrekt in der Ecke. */ .res-window .res-resize { z-index: 1; } /* Kopf der Schublade. */ .res-head { display: flex; align-items: center; justify-content: space-between; padding: 0 16px; height: 52px; flex: 0 0 auto; border-bottom: 1px solid var(--line); background: var(--bg); } .res-head-title { display: inline-flex; align-items: center; gap: 9px; font-weight: 650; font-size: 15px; letter-spacing: 0.2px; color: var(--ink); } .res-head-icon { display: inline-flex; color: var(--accent); } .res-close { width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border: none; background: transparent; color: var(--muted); font-size: 22px; line-height: 1; border-radius: 50%; cursor: pointer; transition: background 0.12s, color 0.12s; } .res-close:hover { background: var(--panel-2); color: var(--ink); } /* Tab-Leiste. */ .res-tabs { display: flex; gap: 4px; padding: 10px 16px 0; flex: 0 0 auto; border-bottom: 1px solid var(--line); background: var(--bg); } .res-tab-btn { border: 1px solid transparent; border-bottom: none; background: transparent; color: var(--muted); font-family: inherit; font-size: 12px; font-weight: 600; letter-spacing: 0.03em; padding: 8px 16px; border-radius: 8px 8px 0 0; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .res-tab-btn:hover { background: var(--accent-dim); color: var(--ink); } .res-tab-btn.active { background: var(--panel); border-color: var(--line); color: var(--accent); margin-bottom: -1px; } /* Scrollbarer Körper. */ .res-body { flex: 1; min-height: 0; overflow-y: auto; padding: 14px 16px 20px; } .res-tab { display: flex; flex-direction: column; gap: 12px; } .res-hint { font-size: 11px; font-style: italic; color: var(--muted); line-height: 1.4; } /* ── Wandstile: ein Block je Wandtyp (Überschrift + Schicht-/Fugen-Tabelle) ─── */ .res-wallstyle { display: flex; flex-direction: column; gap: 6px; } .res-wallstyle-head { font-size: 12px; font-weight: 600; color: var(--text); padding: 2px 2px 0; } /* Innerste Schicht: keine innere Fuge → gedämpfter Platzhalter statt Dropdown. */ .res-joint-none { color: var(--muted); padding-left: 4px; } /* ── Wand-/Deckenstil-Aufbau (Master-Detail) ────────────────────────────── */ /* Der Querschnitt-Swatch (WallTypeSwatch) ist frei skalierbar. */ .res-wallswatch { border-radius: 3px; } /* Aufbau-Editor: die Schichten gestapelt, darunter „Schicht hinzufügen". */ .res-layers { display: flex; flex-direction: column; gap: 8px; } .res-layer { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 8px; padding: 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--input); } .res-layer-move { display: flex; flex-direction: column; gap: 2px; } .res-layer-btn { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 16px; padding: 0; border: 1px solid var(--border); border-radius: 5px; background: var(--panel-2); color: var(--ink); font-size: 9px; line-height: 1; cursor: pointer; transition: background 0.12s, border-color 0.12s; } .res-layer-btn:hover:not(:disabled) { background: var(--accent-dim); border-color: var(--accent-border); } .res-layer-btn:disabled { opacity: 0.35; cursor: default; } .res-layer-fields { display: flex; flex-direction: column; gap: 6px; min-width: 0; } .res-layer-fields .res-field { grid-template-columns: 76px 1fr; gap: 8px; } /* ── Tabelle (saubere Liste, CONVENTIONS.md) ────────────────────────────────── Ein CSS-Grid trägt die Spalten (gridTemplateColumns wird je Tab inline gesetzt). Kopfzeile und Datenzeilen sind `display:contents`, damit ihre Zellen direkte Grid-Kinder werden und exakt fluchten. */ .res-table { display: grid; align-items: center; column-gap: 8px; row-gap: 2px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel-2); overflow: hidden; } /* Kopf- und Datenzeile lösen sich auf — ihre Zellen sind Grid-Kinder. */ .res-thead, .res-tr { display: contents; } /* Sticky Kopfzeile: jede Titel-Zelle klebt am oberen Rand des Scrollbereichs (.res-body). Da Zellen einzelne Grid-Kinder sind, wird sticky je Zelle gesetzt; ein durchgehender Hintergrund verhindert Durchscheinen. */ .res-th { position: sticky; top: 0; z-index: 2; align-self: stretch; display: flex; align-items: center; padding: 7px 4px; font-size: 9px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); background: var(--panel-2); border-bottom: 1px solid var(--border); } .res-th.num { justify-content: flex-end; } /* Erste/letzte Kopfzelle randvoll, damit die Sticky-Leiste durchläuft. */ .res-th:first-child { padding-left: 12px; } .res-th:last-child { padding-right: 12px; } /* Datenzeilen: dezente Trennlinie, Hover hebt die ganze Zeile leicht hervor. Die Trennlinie sitzt auf jeder Zelle, da die Zeile selbst kein Box hat. */ .res-td { display: flex; align-items: center; padding: 5px 4px; min-width: 0; } .res-td.num { justify-content: flex-end; } .res-td:first-child { padding-left: 12px; } .res-td:last-child { padding-right: 12px; } .res-tr:hover .res-td { background: var(--panel); } /* Verschneidungs-Rang (joinPriority) — die tragende Zelle hervorheben. */ .res-td.emphasis .res-input { font-weight: 700; color: var(--accent); border-color: var(--accent-border); background: var(--accent-dim); } /* Text-/Zahleneingabe (Pill). */ .res-input { font-family: var(--font); font-size: 12px; color: var(--ink); background: var(--input); border: 1px solid var(--border); border-radius: 999px; padding: 5px 11px; outline: none; width: 100%; transition: border-color 0.14s, box-shadow 0.14s, background 0.14s; } .res-input.mono { font-family: var(--font-mono); } .res-input.num { text-align: right; } .res-input:hover { border-color: var(--accent); background: var(--panel); } .res-input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); } /* Pill-Dropdown — nativer Pfeil, Petrol-Hover. */ .res-select { appearance: none; -webkit-appearance: none; font-family: var(--font); font-size: 11px; font-weight: 500; letter-spacing: 0.02em; color: var(--ink); background-color: var(--input); background-image: url("data:image/svg+xml;utf8,"); background-repeat: no-repeat; background-position: right 10px center; border: 1px solid var(--border); border-radius: 999px; padding: 5px 26px 5px 12px; cursor: pointer; outline: none; width: 100%; min-width: 0; text-overflow: ellipsis; transition: border-color 0.14s, background-color 0.14s, box-shadow 0.14s; } .res-select:hover { border-color: var(--accent); background-color: var(--panel); } .res-select:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); } /* Farb-Swatch mit unsichtbarem nativem Picker darüber. */ .res-color { position: relative; display: block; width: 46px; height: 28px; border-radius: 999px; border: 1px solid var(--border); overflow: hidden; cursor: pointer; } .res-color:hover { border-color: var(--accent); } .res-color-swatch { position: absolute; inset: 0; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.18); pointer-events: none; } .res-color input[type="color"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; border: none; padding: 0; margin: 0; cursor: pointer; background: transparent; } /* Linienstil-Vorschau (füllt die Vorschau-Zelle). */ .res-line-preview { width: 100%; min-width: 80px; height: 26px; background: var(--input); border: 1px solid var(--border); border-radius: 999px; padding: 0 8px; } /* ── Motiv-Editor (Custom-Linie / Kachel-Motiv) ─────────────────────────── */ .motif-editor { display: flex; flex-direction: column; gap: 8px; width: 100%; } .motif-canvas { width: 100%; height: auto; background: var(--input); border: 1px solid var(--border); border-radius: 6px; touch-action: none; cursor: crosshair; } .motif-frame { fill: none; stroke: var(--border); stroke-width: 1; } .motif-grid { stroke: var(--border); stroke-width: 0.5; opacity: 0.5; } .motif-axis { stroke: var(--ink-2); stroke-width: 0.75; stroke-dasharray: 3 3; opacity: 0.7; } .motif-path { stroke: var(--accent-light); stroke-width: 1.75; stroke-linejoin: round; stroke-linecap: round; } .motif-handle { fill: var(--input); stroke: var(--accent-light); stroke-width: 1.5; cursor: grab; } .motif-handle:hover { fill: var(--accent-light); } .motif-handle.is-drag { fill: var(--accent-light); cursor: grabbing; } .motif-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } .motif-len { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-2); } .motif-len input { width: 62px; } .motif-hint { font-size: 11px; color: var(--ink-2); opacity: 0.85; flex: 1 1 120px; line-height: 1.3; } .motif-preview { width: 100%; height: auto; background: var(--input); border: 1px solid var(--border); border-radius: 6px; } /* Löschen-Knopf (Mülleimer in der letzten Spalte, zentriert). */ .res-delete { flex: 0 0 auto; width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--border); background: var(--input); color: var(--muted); border-radius: 50%; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .res-delete:hover { background: var(--danger-light); border-color: var(--danger-light); color: #fff; } /* „+ Neu"-Knopf je Tab. */ .res-add-row { display: flex; padding-top: 2px; } .res-add { display: inline-flex; align-items: center; gap: 6px; border: 1px dashed var(--border); background: var(--input); color: var(--ink-2); font-family: inherit; font-size: 12px; font-weight: 600; letter-spacing: 0.02em; padding: 7px 16px; border-radius: 999px; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .res-add:hover { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent); } .res-add-plus { font-size: 15px; line-height: 1; } /* Leerer Zustand — überspannt alle Spalten der Tabelle. */ .res-empty { grid-column: 1 / -1; padding: 26px 16px; text-align: center; font-size: 12px; color: var(--muted); background: var(--panel-2); } /* ── Master-Detail (Schraffuren / Linien): Liste links, Detail rechts ──────── */ .res-md { display: grid; grid-template-columns: 240px 1fr; gap: 14px; align-items: start; min-height: 320px; } .res-md-list { display: flex; flex-direction: column; gap: 2px; padding: 6px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel-2); max-height: 62vh; overflow-y: auto; } .res-md-row { display: flex; align-items: center; gap: 9px; width: 100%; padding: 6px 8px; border: 1px solid transparent; border-radius: 8px; background: transparent; color: var(--ink); font-family: inherit; font-size: 12px; text-align: left; cursor: pointer; transition: background 0.12s, border-color 0.12s; } .res-md-row:hover { background: var(--accent-dim); } .res-md-row.active { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent); } .res-md-row-thumb { flex: 0 0 auto; display: inline-flex; } .res-md-row-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 550; } /* Linien-Zeile: Name oben schmal, Swatch daneben rechts. */ .res-md-row-line .res-line-preview { flex: 0 0 auto; width: 150px; min-width: 0; } .res-md-listfoot { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 4px 2px; } .res-md-empty { padding: 22px 14px; text-align: center; font-size: 12px; color: var(--muted); } /* Detail-Panel (rechts). */ .res-md-detail { border: 1px solid var(--border); border-radius: 10px; background: var(--panel-2); min-height: 300px; } .res-md-detail-inner { display: flex; flex-direction: column; gap: 14px; padding: 14px 16px 18px; } .res-md-head { display: flex; align-items: center; gap: 10px; } .res-md-rename { flex: 1 1 auto; font-size: 13px; font-weight: 600; } .res-md-preview { display: flex; align-items: center; justify-content: center; padding: 16px; border: 1px solid var(--border); border-radius: 10px; background: var(--input); color: var(--ink); } .res-md-preview .res-line-preview { border: none; background: transparent; } /* Detail-Felder: Label links, Steuerelement rechts. */ .res-fields { display: flex; flex-direction: column; gap: 8px; } .res-field { display: grid; grid-template-columns: 120px 1fr; align-items: center; gap: 12px; } .res-field-label { font-size: 12px; color: var(--muted); } .res-field-control { display: flex; align-items: center; min-width: 0; } .res-field-control > * { min-width: 0; } /* Segmentierter Umschalter (Pillen-Gruppe). */ .res-seg { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--border); border-radius: 999px; background: var(--input); } .res-seg-btn { border: none; background: transparent; color: var(--muted); font-family: inherit; font-size: 11px; font-weight: 600; letter-spacing: 0.02em; padding: 4px 14px; border-radius: 999px; cursor: pointer; transition: background 0.12s, color 0.12s; } .res-seg-btn:hover { color: var(--ink); } .res-seg-btn.active { background: var(--accent); color: #fff; } /* Schraffur-Swatch (kleine Vorschau in Liste + große im Detail). */ .res-swatch { border-radius: 3px; } /* Modularer Linien-Segment-Editor (Strich / Punkt / Lücke). */ .line-seg-editor { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; } .line-seg-row { display: flex; align-items: center; gap: 4px; } .line-seg-num { width: 56px; display: inline-flex; align-items: center; } .line-seg-dotlabel { color: var(--muted); font-variant-numeric: tabular-nums; padding-left: 4px; } .line-seg-add { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; } /* ══ Panel-Chrome (Andockrahmen: Docks, Tabs, Panel-Rahmen) ═══════════════════ Der erweiterbare Rahmen: links/rechts je ein Dock mit Tab-Leiste und dem aktiven Panel. Greift dieselben Tokens wie die nav- und res-Klassen (dunkles DOSSIER- Look: Petrol-Akzent, Pill-Form, dezente Trennlinien). Die Breite trägt jedes Dock als CSS-Variable --dock-w (vom Dock-Component inline gesetzt). */ /* ── Dock (angedockte Spalte) ────────────────────────────────────────────── */ .dock { position: relative; flex: 0 0 var(--dock-w, 280px); width: var(--dock-w, 280px); display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--panel); overflow: hidden; } /* Trennkante zum Inhalt: links rechts, rechts links. */ .dock-left { border-right: 1px solid var(--line); } .dock-right { border-left: 1px solid var(--line); } /* Während des Ziehens: keine Text-/Cursor-Flacker, weicher Cursor überall. */ .dock.resizing { user-select: none; cursor: col-resize; } /* Ziehgriff an der inneren Kante (eine schmale, unsichtbare Greiffläche mit sichtbarer Linie auf Hover/Drag). Liegt über dem Rand des Docks. Cursor = col-resize (horizontales Verschieben der Dock-Breite). */ .dock-resize { position: absolute; top: 0; bottom: 0; width: 7px; z-index: 5; cursor: col-resize; background: transparent; transition: background 0.12s; touch-action: none; } .dock-left .dock-resize { right: -3px; } .dock-right .dock-resize { left: -3px; } .dock-resize:hover, .dock.resizing .dock-resize { background: var(--accent-border); } /* ── Gestapelte Gruppen im Dock ──────────────────────────────────────────── */ /* Vertikaler Stapel der Gruppen; füllt den Dock-Körper. */ .dock-stack { flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; } /* Eine Gruppe = eigener Tab-Stapel (TabStrip + aktives Panel). Die Höhe ergibt sich aus flex-grow (= group.weight, inline gesetzt); flex-basis 0, damit nur die Gewichte zählen. */ .dock-group { flex: 1 1 0; display: flex; flex-direction: column; min-height: 0; min-width: 0; overflow: hidden; } /* Trennlinie zwischen gestapelten Gruppen (außer der ersten). */ .dock-group + .dock-group, .dock-group-resize + .dock-group { border-top: 1px solid var(--line); } /* Splitter zwischen zwei Gruppen: schmale Greiffläche, Cursor row-resize. */ .dock-group-resize { flex: 0 0 7px; margin: -3px 0; z-index: 4; cursor: row-resize; background: transparent; transition: background 0.12s; touch-action: none; } .dock-group-resize:hover { background: var(--accent-border); } /* ── Tab-Leiste (Dock-Kopf) ──────────────────────────────────────────────── */ .tabstrip { display: flex; gap: 4px; flex: 0 0 auto; padding: 8px 8px 0; border-bottom: 1px solid var(--line); background: var(--bg); overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; } /* Dünne, dezente Scrollleiste, wenn viele Tabs umlaufen (WebKit). */ .tabstrip::-webkit-scrollbar { height: 5px; } .tabstrip::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; } .tab { flex: 0 0 auto; width: 34px; height: 30px; display: flex; align-items: center; justify-content: center; border: 1px solid transparent; border-bottom: none; background: transparent; color: var(--muted); font-family: inherit; padding: 0; border-radius: 8px 8px 0 0; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .tab svg { flex: 0 0 auto; } .tab:hover { background: var(--accent-dim); color: var(--ink); } .tab.active { background: var(--panel); border-color: var(--line); color: var(--accent); margin-bottom: -1px; } /* ── Panel-Rahmen (Kopf + scrollbarer Körper) ────────────────────────────── */ .panel-frame { flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--panel); } .panel-frame-empty { /* Platzhalter, wenn kein Panel aktiv ist — nur Fläche, kein Inhalt. */ background: var(--panel); } .panel-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex: 0 0 auto; /* Feste Höhe statt nur Padding: Panels OHNE Darstellungsmodus-Dropdown (nur Titel) hatten sonst einen niedrigeren Kopf als Panels MIT Dropdown — beim Wechseln/Stapeln "sprang" die Kopfzeile sichtbar. box-sizing zieht das Padding von der festen Höhe ab, align-items:center zentriert Titel bzw. Titel+Dropdown gleichermassen. */ height: 40px; box-sizing: border-box; padding: 0 12px; border-bottom: 1px solid var(--line); /* Uniform mit dem Panel-Körper (Nutzer-Wunsch): kein separater hellerer Kopf-Balken mehr, nur die Trennlinie grenzt Kopf/Körper ab. */ background: var(--panel); } .panel-title { min-width: 0; font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #f0f0f0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .panel-body { flex: 1; min-height: 0; overflow-y: auto; padding: 10px 8px 16px; } /* ── Darstellungsmodus-Dropdown (Panel-Kopf) ────────────────────────────── Nutzt jetzt die gemeinsame Dropdown-Komponente (.tb-dd-trigger) für den dunklen, abgerundeten Look; hier nur noch die Breitenbegrenzung + kompakte Panel-Kopf-Typografie, die dem generischen Trigger fehlen. */ .display-mode { flex: 0 0 auto; max-width: 160px; font-size: 10px; letter-spacing: 0.02em; } /* ── Layout-Menü (Oberleiste: benannte Layouts sichern/laden) ─────────────── */ /* Kleines Pill-Dropdown im selben Stil wie .res-trigger; ein Aktions-Trigger, daher steht der angezeigte Wert immer auf „Layout …". */ .layout-menu { appearance: none; -webkit-appearance: none; font-family: var(--font); font-size: 11px; font-weight: 600; letter-spacing: 0.04em; color: var(--ink-2); background-color: var(--input); background-image: url("data:image/svg+xml;utf8,"); background-repeat: no-repeat; background-position: right 10px center; border: 1px solid var(--border); border-radius: 999px; padding: 6px 26px 6px 14px; cursor: pointer; outline: none; transition: border-color 0.14s, background-color 0.14s, color 0.14s, box-shadow 0.14s; } .layout-menu:hover { border-color: var(--accent-border); background-color: var(--accent-dim); color: var(--accent); } .layout-menu:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); } /* ── Eigenes Dropdown (Oberleiste) — Trigger + Popover im Kontextmenü-Stil ─── Ersetzt die nativen (ColorHexField), damit er unabhängig editierbar ist statt nur die Palette zu öffnen. */ .attr-color-swatch-wrap { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; } .attr-color-swatch { width: 16px; height: 16px; border-radius: 3px; border: 1px solid var(--line); flex: 0 0 auto; } .attr-color input[type="color"] { width: 22px; height: 18px; padding: 0; border: 1px solid var(--line); border-radius: 3px; background: transparent; cursor: pointer; } .attr-color-hex { font-variant-numeric: tabular-nums; color: var(--ink-2); font-size: 11px; } /* Editierbarer Hex-Code: sieht wie reiner Text aus, bis man klickt/fokussiert — dann Rahmen + helle Schrift als Editier-Signal. */ .attr-color-hex-input { width: 8.5ch; color: var(--ink-2); border: 1px solid transparent; background: transparent; border-radius: 3px; padding: 1px 3px; font-family: var(--font-mono); cursor: text; } .attr-color-hex-input:hover:not(:disabled) { border-color: var(--border); } .attr-color-hex-input:focus { outline: none; border-color: var(--accent); background: var(--input); color: var(--ink); } .attr-color input[type="color"]:disabled { opacity: 0.4; cursor: not-allowed; } .attr-color-clear { border: 1px solid var(--line); background: transparent; color: var(--muted); border-radius: 3px; width: 16px; height: 16px; line-height: 1; padding: 0; cursor: pointer; flex: 0 0 auto; } .attr-color-clear:hover { color: var(--text); border-color: var(--accent-border); } /* ── Import-Dialog (DXF/DWG) ─────────────────────────────────────────────── Modal wie der ResourceManager: abgedunkeltes Overlay, zentriertes Fenster, Klick auf den Hintergrund / Esc schließt. */ .imp-overlay { position: fixed; inset: 0; z-index: 1100; background: var(--overlay); display: flex; align-items: center; justify-content: center; } .imp-dialog { width: min(520px, 92vw); max-height: 88vh; display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-3); overflow: hidden; } .imp-head { display: flex; align-items: center; justify-content: space-between; padding: 0 16px; height: 48px; flex: 0 0 auto; border-bottom: 1px solid var(--line); background: var(--bg); } .imp-head-title { font-weight: 650; font-size: 14px; letter-spacing: 0.2px; color: var(--ink); } .imp-close { width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; border: none; background: transparent; color: var(--muted); font-size: 20px; line-height: 1; border-radius: 50%; cursor: pointer; transition: background 0.12s, color 0.12s; } .imp-close:hover { background: var(--panel-2); color: var(--ink); } .imp-body { padding: 14px 16px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; } .imp-file { display: flex; align-items: baseline; gap: 8px; font-size: 12px; } .imp-file-label { color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; font-size: 10px; } .imp-file-name { color: var(--ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .imp-section { display: flex; flex-direction: column; gap: 7px; } .imp-section-title { font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-2); } .imp-summary { margin: 0; padding-left: 18px; font-size: 12px; color: var(--ink); line-height: 1.6; } .imp-radio, .imp-check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink); cursor: pointer; } .imp-radio input[disabled] + span { color: var(--muted); } .imp-input, .imp-select { width: 100%; font-family: inherit; font-size: 13px; padding: 6px 9px; border: 1px solid var(--border); border-radius: 7px; background: var(--input); color: var(--ink); margin-left: 22px; width: calc(100% - 22px); } .imp-note, .imp-warn, .imp-dwg-hint { font-size: 12px; line-height: 1.5; color: var(--muted); } .imp-warn { color: var(--accent); } .imp-dwg-hint { color: var(--ink-2); background: var(--panel-2); border: 1px solid var(--border); border-radius: 7px; padding: 10px 12px; } .imp-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 16px; flex: 0 0 auto; border-top: 1px solid var(--line); background: var(--bg); } .imp-btn { font-family: inherit; font-size: 13px; font-weight: 600; padding: 7px 18px; border-radius: 999px; border: 1px solid var(--border); background: var(--input); color: var(--ink-2); cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .imp-btn:hover { color: var(--ink); border-color: var(--accent-border); } .imp-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; } .imp-btn.primary:hover { background: var(--accent-light); border-color: var(--accent-light); } .imp-btn:disabled { opacity: 0.45; cursor: not-allowed; } /* ── Einstellungs-Fenster (SettingsDialog): Farbfelder + Akzent-Presets ──── Nutzt sonst dieselben imp-*-Klassen wie die anderen Dialoge. */ .settings-field-label { font-size: 12px; color: var(--ink-2); } .settings-color-field { display: flex; flex-direction: column; gap: 7px; } .settings-color-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; } .settings-accent-presets { display: flex; flex-wrap: wrap; gap: 6px; } .settings-accent-btn { width: 22px; height: 22px; padding: 0; border-radius: 6px; border: 1px solid var(--line); cursor: pointer; transition: transform 0.1s, border-color 0.12s; } .settings-accent-btn:hover { transform: scale(1.08); } .settings-accent-btn.active { border: 2px solid var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); } .settings-num-input { font-family: inherit; font-size: 13px; padding: 6px 9px; border: 1px solid var(--border); border-radius: 7px; background: var(--input); color: var(--ink); width: 120px; } .settings-num-input:focus { outline: none; border-color: var(--accent-border); } /* ── App-weites Drop-Feedback ────────────────────────────────────────────── Dezenter Rahmen + Hinweis, solange eine Datei über die App gezogen wird. */ .drop-feedback { position: fixed; inset: 0; z-index: 1090; pointer-events: none; display: flex; align-items: center; justify-content: center; background: var(--accent-dim); border: 3px dashed var(--accent-border); } .drop-feedback-inner { font-size: 16px; font-weight: 650; letter-spacing: 0.3px; color: var(--accent); background: var(--panel); border: 1px solid var(--accent-border); border-radius: 10px; padding: 14px 22px; box-shadow: var(--shadow-3); } /* ── 3D-Material-Zuweisung (Bauteil-Manager) ──────────────────────────────── */ /* Material-Zelle: Button mit kleinem Farb-Karten-Swatch + Name. */ .res-material-btn { display: flex; align-items: center; gap: 6px; text-align: left; cursor: pointer; overflow: hidden; } .res-material-swatch { width: 16px; height: 16px; flex: 0 0 auto; border-radius: 3px; border: 1px solid var(--border); background-size: cover; background-position: center; } .res-material-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* Modal-Overlay des Material-Pickers (über der Ressourcen-Schublade). */ .mat-overlay { position: fixed; inset: 0; z-index: 1200; display: flex; align-items: center; justify-content: center; background: var(--overlay); } .mat-dialog { width: min(680px, 92vw); max-height: 86vh; overflow: auto; background: var(--panel); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow-3); padding: 16px 18px 18px; } .mat-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; } .mat-title { font-weight: 600; color: var(--ink); } .mat-hint { color: var(--muted); font-size: 12px; margin-bottom: 10px; } .mat-section-title { font-weight: 600; color: var(--ink-2); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; margin: 12px 0 8px; } .mat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; } .mat-tile { display: flex; flex-direction: column; align-items: stretch; gap: 4px; padding: 4px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; cursor: pointer; } .mat-tile:hover { border-color: var(--accent-border); } .mat-tile.active { border-color: var(--active); box-shadow: 0 0 0 2px var(--active-dim); } .mat-thumb { width: 100%; aspect-ratio: 1 / 1; border-radius: 5px; background-size: cover; background-position: center; background-color: var(--input); } .mat-tile-name { font-size: 11px; color: var(--ink-2); text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .mat-upload-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 8px; } .mat-upload-row { display: flex; align-items: center; gap: 8px; } .mat-upload-label { flex: 0 0 86px; font-size: 12px; color: var(--ink-2); } .mat-upload-ok { color: var(--active); font-weight: 700; } .mat-pick { padding: 4px 10px; } .mat-actions { display: flex; gap: 8px; margin-top: 16px; } .mat-danger { color: var(--danger); border-color: var(--danger); } /* ── Material-Picker: Modus-Umschalter + ambientCG-Browse ─────────────────── */ .mat-modes { display: flex; gap: 4px; margin: 4px 0 12px; padding: 3px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; } .mat-mode-btn { flex: 1 1 0; padding: 6px 10px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--ink-2); font-size: 12px; cursor: pointer; } .mat-mode-btn:hover { color: var(--ink); } .mat-mode-btn.active { background: var(--panel); border-color: var(--active); color: var(--ink); } .mat-actions-browse { align-items: center; justify-content: space-between; } .mat-size-inline { display: flex; align-items: center; gap: 8px; } .mat-size-inline .res-input { width: 80px; } .mat-browse-bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 8px 0 10px; } .mat-search { flex: 1 1 180px; min-width: 140px; } .mat-browse-bar .res-select { flex: 0 0 auto; } .mat-browse-grid { min-height: 120px; max-height: 46vh; overflow: auto; padding-right: 2px; } .mat-tile { position: relative; } .mat-tile.loading { opacity: 0.6; } .mat-tile-spinner { position: absolute; top: 50%; left: 50%; width: 22px; height: 22px; margin: -11px 0 0 -11px; border: 3px solid var(--active-dim); border-top-color: var(--active); border-radius: 50%; animation: mat-spin 0.7s linear infinite; } @keyframes mat-spin { to { transform: rotate(360deg); } } .mat-browse-error { color: var(--danger); font-size: 12px; padding: 8px 10px; margin-bottom: 8px; background: var(--panel-2); border: 1px solid var(--danger); border-radius: 6px; } .mat-browse-foot { display: flex; align-items: center; gap: 10px; margin-top: 10px; } .mat-browse-status { color: var(--muted); font-size: 12px; } /* ── Materialien-Tab: Bibliothek durchsuchen (Kugel-Vorschau, Filter) ────── */ .mat-lib-filterbar { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; } .mat-lib-filterbar .mat-search { flex: 1 1 200px; min-width: 160px; } .mat-lib-chips { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 100%; } .mat-chip { padding: 4px 11px; border: 1px solid var(--border); border-radius: 999px; background: var(--panel-2); color: var(--ink-2); font-size: 11.5px; cursor: pointer; transition: background 0.12s, color 0.12s, border-color 0.12s; } .mat-chip:hover { border-color: var(--accent-border); color: var(--ink); } .mat-chip.active { background: var(--active-dim); border-color: var(--active); color: var(--ink); } .mat-lib-selected { font-size: 12px; color: var(--ink-2); } .mat-lib-grid { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 10px; } .mat-sphere { width: 100%; aspect-ratio: 1 / 1; border-radius: 999px; background: var(--input); display: flex; align-items: center; justify-content: center; overflow: hidden; } .mat-sphere img { width: 100%; height: 100%; object-fit: contain; } .mat-tile-badge { align-self: center; font-size: 9.5px; letter-spacing: 0.02em; color: var(--muted); background: var(--panel); border: 1px solid var(--border); border-radius: 999px; padding: 1px 7px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } /* Standort-Import-Dialog (ContextImportDialog) — nutzt das imp-*-Modal-Gerüst, ergänzt nur die inneren Suchfelder/Checkboxen (cim-*-Namespace). */ .cim-search-row { display: flex; gap: 8px; align-items: center; } .cim-input { flex: 1 1 auto; min-width: 0; background: var(--input); color: var(--ink); border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; font-size: 13px; } .cim-input:focus { outline: none; border-color: var(--accent); } .cim-input-num { flex: 0 0 90px; width: 90px; } .cim-results { list-style: none; margin: 8px 0 0; padding: 0; max-height: 168px; overflow-y: auto; border: 1px solid var(--border); border-radius: 6px; background: var(--panel-2); } .cim-result { padding: 6px 8px; font-size: 12px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-bottom: 1px solid var(--line); } .cim-result:last-child { border-bottom: none; } .cim-result:hover { background: var(--accent-dim); } .cim-result.selected { background: var(--accent); color: #fff; } .cim-radius-row { display: flex; align-items: center; gap: 6px; } .cim-unit { color: var(--muted); font-size: 12px; } .cim-checks { display: flex; flex-direction: column; gap: 6px; } .cim-check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink); cursor: pointer; } .cim-status { color: var(--muted); font-size: 12px; padding: 2px 0; } /* ── Rich-Text-Editor ────────────────────────────────────────────── */ .rt-editor { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; background: var(--panel); box-shadow: var(--shadow-1); } .rt-toolbar { display: flex; align-items: center; gap: 4px; padding: 5px 8px; background: var(--panel-2); border-bottom: 1px solid var(--border); flex-wrap: wrap; } .rt-toolbar-sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; } .rt-toolbar-label { display: flex; align-items: center; gap: 3px; color: var(--ink-2); font-size: 12px; } .rt-unit { font-size: 11px; color: var(--muted); } .rt-btn { display: inline-flex; align-items: center; justify-content: center; min-width: 26px; height: 26px; padding: 0 5px; border: 1px solid transparent; border-radius: 4px; background: transparent; color: var(--ink); font-size: 13px; font-family: var(--font); cursor: pointer; line-height: 1; transition: background 120ms, border-color 120ms; } .rt-btn:hover { background: var(--input); border-color: var(--border); } .rt-btn--active { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent-light); } .rt-btn--active:hover { background: var(--accent-dim); border-color: var(--accent); } .rt-size-input { width: 48px; height: 24px; padding: 0 4px; background: var(--input); color: var(--ink); border: 1px solid var(--border); border-radius: 4px; font-size: 12px; font-family: var(--font-mono); text-align: right; appearance: textfield; } .rt-size-input:focus { outline: none; border-color: var(--accent); } .rt-size-input::-webkit-inner-spin-button, .rt-size-input::-webkit-outer-spin-button { opacity: 0.5; } .rt-color-input { width: 26px; height: 26px; padding: 1px; border: 1px solid var(--border); border-radius: 4px; background: var(--input); cursor: pointer; appearance: none; -webkit-appearance: none; } .rt-color-input::-webkit-color-swatch-wrapper { padding: 0; } .rt-color-input::-webkit-color-swatch { border: none; border-radius: 3px; } .rt-preset-select { height: 26px; padding: 0 6px; background: var(--input); color: var(--ink); border: 1px solid var(--border); border-radius: 4px; font-size: 12px; font-family: var(--font); cursor: pointer; } .rt-preset-select:focus { outline: none; border-color: var(--accent); } .rt-surface { flex: 1 1 auto; min-height: 80px; padding: 10px 12px; background: var(--input); color: var(--ink); font-size: 13px; font-family: var(--font); line-height: 1.55; outline: none; overflow-y: auto; white-space: pre-wrap; word-break: break-word; } .rt-surface:focus { box-shadow: inset 0 0 0 1px var(--accent); } .rt-surface div { min-height: 1em; } .rt-surface div + div { margin-top: 0; } /* rt-btn active state — space-separated variant used by RichTextEditor */ .rt-btn.active { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent-light); } .rt-btn.active:hover { background: var(--accent-dim); border-color: var(--accent); } /* ── Raum: Stempel-Griff, Objekt-Info-Steuerung, Stempel-Editor, Bilanz ── */ /* Ziehbare Stempel-Raute im Grundriss (Akzentfarbe). */ .plan-svg .plan-stamp-grip { fill: var(--accent); stroke: #ffffff; stroke-width: 1; vector-effect: non-scaling-stroke; } /* Objekt-Info: Textfeld + Aktions-Button (Raum-Abschnitt). */ .objinfo-text { flex: 1; min-width: 0; background: var(--panel-2); border: 1px solid var(--border); color: var(--ink); border-radius: 3px; padding: 3px 6px; font-size: 12px; } .objinfo-btn { margin-top: 8px; width: 100%; background: var(--panel-2); border: 1px solid var(--border); color: var(--ink); border-radius: 4px; padding: 5px 8px; font-size: 12px; cursor: pointer; } .objinfo-btn:hover { border-color: var(--accent); } /* Schwebendes Dialog-Fenster zum Bearbeiten eines Rich-Text-Dokuments (Raum-Stempel / Textobjekt) — DOSSIER §4: zentriert, --shadow-3, radius 8, Titel „Text bearbeiten", Kopf mit Schliessen-✕, Fuss Abbrechen/Übernehmen. */ .texteditor-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.28); display: flex; align-items: center; justify-content: center; z-index: 1000; } .texteditor-dialog { display: flex; flex-direction: column; width: 560px; max-width: calc(100vw - 32px); height: 420px; max-height: calc(100vh - 32px); background: var(--panel); border: 1px solid var(--border); border-radius: 8px; box-shadow: var(--shadow-3); overflow: hidden; } .texteditor-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; border-bottom: 1px solid var(--border); } .texteditor-title { font-size: 13px; font-weight: 600; color: var(--ink); } .texteditor-x { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; padding: 0; border: 1px solid transparent; border-radius: 999px; background: transparent; color: var(--muted); cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .texteditor-x:hover { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent); } .texteditor-x .material-symbols-outlined { font-size: 18px; line-height: 1; } .texteditor-body { flex: 1 1 auto; min-height: 0; display: flex; padding: 12px; overflow: auto; } .texteditor-body .rt-editor { flex: 1 1 auto; min-height: 0; } .texteditor-foot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--border); } .texteditor-btn { border: 1px solid var(--border); background: var(--input); color: var(--ink); font-family: inherit; font-size: 12px; font-weight: 600; padding: 5px 14px; border-radius: 999px; cursor: pointer; transition: background 0.14s, color 0.14s, border-color 0.14s; } .texteditor-btn:hover { border-color: var(--accent-border); background: var(--accent-dim); color: var(--accent); } .texteditor-btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; } .texteditor-btn-primary:hover { background: var(--accent); border-color: var(--accent); color: #fff; filter: brightness(1.08); } /* Strukturierter Raum-Stempel-Editor (Feldmodell). */ .roomstamp-dialog { height: auto; } .roomstamp-body { flex-direction: column; gap: 10px; overflow: auto; } .roomstamp-field { display: flex; flex-direction: column; gap: 4px; } .roomstamp-label { font-size: 11px; font-weight: 600; color: var(--muted); } .roomstamp-input { border: 1px solid var(--border); background: var(--input); color: var(--ink); font-family: inherit; font-size: 13px; padding: 5px 8px; border-radius: 6px; } .roomstamp-input:focus { outline: none; border-color: var(--accent-border); } .roomstamp-input:disabled { opacity: 0.5; } .roomstamp-check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink); cursor: pointer; } /* Zeile mit Feld/Häkchen links und Ausrichtungs-Umschalter rechts. */ .roomstamp-row { display: flex; align-items: flex-end; gap: 8px; } .roomstamp-row .roomstamp-field { flex: 1; } .roomstamp-row .roomstamp-check { flex: 1; } /* Drei-Wege-Umschalter (links/mitte/rechts) je Stempelzeile. */ .roomstamp-align { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; flex: none; } .roomstamp-align-btn { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; border: none; border-left: 1px solid var(--border); background: transparent; color: var(--ink-2); cursor: pointer; transition: background 0.14s, color 0.14s; } .roomstamp-align-btn:first-child { border-left: none; } .roomstamp-align-btn:hover:not(:disabled) { background: var(--accent-dim); color: var(--accent); } .roomstamp-align-btn.active { background: var(--accent); color: #fff; } .roomstamp-align-btn:disabled { opacity: 0.4; cursor: default; } .roomstamp-align .material-symbols-outlined { font-size: 18px; } /* Flächenbilanz-Panel (SIA 416): Kopf, Raumliste, Summentabelle, Export. */ .balance-panel { padding: 8px 10px; font-size: 12px; color: var(--ink); overflow: auto; } .balance-empty { color: var(--ink-2); padding: 12px 4px; font-size: 12px; } .balance-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .balance-title { font-weight: 600; } .balance-export { background: var(--accent); color: #ffffff; border: none; border-radius: 4px; padding: 4px 10px; font-size: 12px; cursor: pointer; } .balance-export:hover { filter: brightness(1.08); } .balance-section-label { margin: 10px 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-2); } .balance-table { display: flex; flex-direction: column; gap: 1px; } .balance-row { display: grid; grid-template-columns: 1fr auto auto; gap: 8px; align-items: baseline; padding: 3px 4px; border-bottom: 1px solid var(--border); } .balance-row.aggregate { font-weight: 600; background: var(--panel-2); } .balance-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .balance-cat, .balance-count { color: var(--ink-2); font-variant-numeric: tabular-nums; } .balance-area { font-variant-numeric: tabular-nums; text-align: right; } /* ── Fenster-Drag-Region (randloses Electron-Fenster) ──────────────────────── Ohne native Titelleiste (frame:false) muss die Oberleiste als Drag-Handle dienen; alle interaktiven Elemente darin bekommen `no-drag` zurück, sonst liesse sich nichts mehr anklicken. Wirkt nur unter Electron/Chromium (WebKit/Firefox ignorieren `-webkit-app-region` ergebnislos, harmlos). */ .topbar { -webkit-app-region: drag; } .topbar button, .topbar select, .topbar input, .topbar a, .topbar [role="button"], .topbar [role="listbox"] { -webkit-app-region: no-drag; } /* ── Custom Fenstersteuerung (WindowControls.tsx) — eigener Look statt OS- Fensterknöpfen, ganz rechts in der Oberleiste (nur sichtbar unter Electron/randlosem Fenster). */ .win-controls { display: inline-flex; align-items: center; gap: 2px; margin-left: 6px; flex: 0 0 auto; } .win-btn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; padding: 0; border: none; background: transparent; color: var(--muted); border-radius: 6px; cursor: pointer; transition: background 0.14s, color 0.14s; } .win-btn:hover { background: var(--accent-dim); color: var(--ink); } .win-btn-close:hover { background: #e5484d; color: #fff; }