Files
DOSSIER-STANDALONE/src/styles.css
T
karim 683242ba78 ResourceManager: schwebendes, nicht-modales Fenster statt Modal
Der Ressourcen-Editor ist von einem blockierenden Modal (dunkler Backdrop,
Canvas gesperrt) zu einem frei schwebenden Fenster geworden: verschiebbar
ueber die Kopfzeile (Pointer-Capture-Drag), unten rechts resizable, KEIN
Backdrop — der Plan bleibt bedienbar, sodass Schraffur-/Linien-Aenderungen
live am Modell sichtbar sind. Der Ressourcen-Button bleibt Toggle, Esc
schliesst weiter. FloatingPanel-Bausteine wiederverwendet; die interne
Tab-/Master-Detail-Struktur unveraendert.
2026-07-04 01:30:13 +02:00

5032 lines
113 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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. */
:root {
color-scheme: light;
/* Flächen */
--bg: #e0dbd4;
--panel: #e0dbd4;
--panel-2: #d4cfc8;
--input: #ece8e2;
--line: #d4cfc8;
--border: #c8c2ba;
/* Zeichenblatt-Hintergrund (Plan) — bewusst fix, unabhängig von der Panel-
Farbe: helles „Papier" im Light-Mode. */
--sheet: #f0f0f0;
/* Schrift */
--ink: #1a1a18;
--ink-2: #555550;
--muted: #8a8580;
--label: #1a1a18;
/* Petrol-Akzent + aktiver Marker */
--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);
/* Plan-/3D-Töne */
--wall: #2b3039;
--swing: #9aa3b2;
/* Warn-/Gefahr-Akzente (z. B. Löschen). */
--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);
--font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
sans-serif;
--font-mono: "DM Mono", ui-monospace, "SF Mono", monospace;
}
@media (prefers-color-scheme: dark) {
: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) wie im Light-Mode: das Zeichenblatt
ist immer „Papier", unabhängig vom UI-Theme (Wunsch des Nutzers). */
--ink: #ededed;
--ink-2: #a8a8a8;
--muted: #6e6e6e;
--label: #c8c8c8;
/* Akzent NEUTRAL (kein Petrol-Grün mehr, 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);
}
}
* {
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: 0 14px;
height: 56px;
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;
border: 1px solid var(--border);
background: var(--input);
color: var(--ink);
font-family: inherit;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 3px 10px;
border-radius: 999px;
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: var(--accent-dim);
color: var(--accent);
}
.tb-btn:focus-visible {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent-dim);
}
.tb-btn:disabled {
color: var(--muted);
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 var(--border);
background: var(--input);
color: var(--ink-2);
font-family: inherit;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.03em;
padding: 6px 12px;
border-radius: 999px;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.tb-toggle:hover {
background: var(--accent-dim);
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. */
.tb-seg {
display: inline-flex;
align-items: stretch;
height: 24px;
box-sizing: border-box;
border: 1px solid var(--border);
border-radius: 999px;
overflow: hidden;
background: var(--input);
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 var(--border);
background: transparent;
color: var(--ink-2);
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.tb-seg-cell:first-child {
border-left: none;
}
.tb-seg-cell:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--accent);
}
.tb-seg-cell.active {
background: var(--accent);
color: #fff;
}
.tb-seg-cell:disabled {
opacity: 0.4;
cursor: default;
}
/* Quadratischer Icon-Knopf (BarButton) — Export-Pillen (PDF/DXF). */
.tb-iconbtn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
box-sizing: border-box;
padding: 0;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--input);
color: var(--ink-2);
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.tb-iconbtn:hover:not(:disabled) {
background: var(--accent-dim);
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 {
display: grid;
grid-template-columns: auto auto;
gap: 4px 6px;
align-items: center;
}
.tb-stat {
grid-row: 1 / span 2;
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);
}
.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): Akzent-Hervorhebung. */
.tb-stat-atscale {
background: var(--accent-dim);
border-color: var(--accent);
}
.tb-stat-atscale .tb-stat-scale {
border-bottom-color: var(--accent-border);
}
.tb-stat-atscale .tb-stat-zoom {
color: var(--accent);
}
.tb-zoomcluster-right {
display: inline-flex;
align-items: center;
gap: 6px;
}
/* ── Text-Gruppe (immer sichtbar, 3×2) ───────────────────────────────────── */
.tb-textgroup {
flex-direction: column;
align-items: stretch;
gap: 4px;
}
.tb-textgroup-row {
display: inline-flex;
align-items: center;
gap: 6px;
}
/* „+ Text"-Knopf: Pille mit Icon + Label. */
.tb-addtext {
display: inline-flex;
align-items: center;
gap: 4px;
width: 80px;
height: 24px;
box-sizing: border-box;
padding: 0 8px;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--input);
color: var(--ink-2);
font-family: inherit;
font-size: 11px;
font-weight: 600;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.tb-addtext:hover:not(:disabled) {
background: var(--accent-dim);
border-color: var(--accent-border);
color: var(--accent);
}
.tb-addtext:disabled {
opacity: 0.5;
cursor: default;
}
.tb-addtext-label {
flex: 1 1 auto;
text-align: left;
}
.brand {
display: flex;
align-items: center;
gap: 8px;
font-weight: 650;
font-size: 16px;
letter-spacing: 0.2px;
}
.logo {
color: var(--accent);
font-size: 18px;
}
.tag {
font-weight: 500;
font-size: 11px;
color: var(--muted);
border: 1px solid var(--border);
border-radius: 999px;
padding: 2px 8px;
}
.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 var(--border);
background: var(--input);
color: var(--ink-2);
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: var(--accent-dim);
color: var(--ink);
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: 4px;
}
.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 / Engine) in der Statusleiste — kleine
Segmentpille, an die 22px-Leiste angepasst (siehe .tb-seg für das größere
Vorbild in der Oberleiste). */
.sb-renderer-toggle {
display: inline-flex;
align-items: stretch;
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 <div class="viewport">) ── */
.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 <g> 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;
}
/* ── 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,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M0 0l5 6 5-6z' fill='%23888'/></svg>");
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;
padding: 9px 12px;
border-bottom: 1px solid var(--line);
/* Panel-Kopf leicht angehoben (Ebene 2) → setzt sich vom Körper ab. */
background: var(--panel-2);
}
.panel-title {
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;
}
.panel-body {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 10px 8px 16px;
}
/* ── Darstellungsmodus-Dropdown (kompaktes Pill, vgl. .res-select) ────────── */
.display-mode {
flex: 0 0 auto;
appearance: none;
-webkit-appearance: none;
max-width: 160px;
font-family: var(--font);
font-size: 10px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--ink-2);
background-color: var(--input);
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M0 0l5 6 5-6z' fill='%23888'/></svg>");
background-repeat: no-repeat;
background-position: right 8px center;
border: 1px solid var(--border);
border-radius: 999px;
padding: 4px 22px 4px 10px;
cursor: pointer;
outline: none;
text-overflow: ellipsis;
transition: border-color 0.14s, background-color 0.14s, color 0.14s,
box-shadow 0.14s;
}
.display-mode:hover {
border-color: var(--accent);
color: var(--accent);
background-color: var(--panel);
}
.display-mode:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent-dim);
}
/* ── 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,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M0 0l5 6 5-6z' fill='%23888'/></svg>");
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 <select>, deren offene Optionsliste das Betriebssystem
blau/eckig rendert. Der Trigger sieht aus wie .tb-select bzw. .layout-menu
(Pill); das geöffnete Popover nutzt die .ctx-menu-Tokens (dunkel, rund,
Akzent-Hover) — siehe Regeln unten (.tb-dd-menu erbt von .ctx-menu). */
.tb-dd-trigger {
display: inline-flex;
align-items: center;
gap: 8px;
border: 1px solid var(--border);
background: var(--input);
color: var(--ink);
font-family: inherit;
font-size: 11px;
font-weight: 600;
padding: 3px 8px 3px 8px;
border-radius: 999px;
cursor: pointer;
outline: none;
transition: border-color 0.14s, background-color 0.14s, color 0.14s,
box-shadow 0.14s;
}
.tb-dd-trigger:hover:not(:disabled) {
border-color: var(--accent-border);
background-color: var(--accent-dim);
color: var(--accent);
}
.tb-dd-trigger.active,
.tb-dd-trigger:focus-visible {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent-dim);
}
.tb-dd-trigger:disabled {
color: var(--muted);
opacity: 0.55;
cursor: default;
}
.tb-dd-trigger-label {
flex: 1 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: left;
}
.tb-dd-chevron {
flex: 0 0 auto;
color: var(--muted);
}
.tb-dd-trigger:hover:not(:disabled) .tb-dd-chevron {
color: var(--accent);
}
/* Monospace-Variante (Massstab) — ziffernbündig wie das frühere .tb-scale. */
.tb-dd-mono .tb-dd-trigger-label {
font-family: var(--font-mono);
}
/* Popover des Dropdowns: erbt die dunkle Kontextmenü-Fläche; eine Häkchen-
Spalte links hält die Labels bündig. Tastatur-Fokus = derselbe Akzent-Hover
wie Maus-Hover (.is-active). */
.tb-dd-menu {
max-width: 360px;
}
.tb-dd-item.is-active:not(:disabled) {
background: var(--ctx-hover);
color: var(--ctx-hover-text);
}
.tb-dd-item.danger.is-active:not(:disabled) {
background: rgba(216, 96, 96, 0.16);
color: var(--ctx-danger);
}
.tb-dd-check-slot {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
width: 14px;
color: var(--ctx-hover-text);
}
.tb-dd-check {
display: block;
}
/* ── Kontextmenü (generisch, wiederverwendbar) ───────────────────────────────
1:1 nach DOSSIER (docs/design/context-menu.md): fixe Position mit
Rand-Clamp im JS, min-width 200px, Radius 13px, weicher Schatten, DUNKEL
(immer dunkle Fläche — DOSSIER-Kontextmenü ist themenunabhängig dunkel).
Mount-Animation scale(.94) translateY(-5px) 100ms; Item-Hover = Akzent;
danger = rote Schrift; divider = 1px Trenner; Titel oben caps/10px/gedämpft.
z-index 300 (unter Modals/Overlays). */
/* Material-Symbols-Icon-Font: aktiviert die Ligaturen (sonst erscheint der
Ligaturname „content_copy" als roher Text). Größe/Farbe setzen die jeweiligen
Icon-Klassen (.ctx-item-icon, .view-icon …). */
.material-symbols-outlined {
font-family: "Material Symbols Outlined";
font-weight: normal;
font-style: normal;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
font-feature-settings: "liga";
-webkit-font-feature-settings: "liga";
-webkit-font-smoothing: antialiased;
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
}
.ctx-menu {
/* Eigene, stets dunkle Palette (unabhängig vom Light/Dark-Theme). */
--ctx-surface: #2c2c2c;
--ctx-border: #4a4a4a;
--ctx-text: #e8e8e8;
--ctx-muted: #8a8a8a;
/* Hover NEUTRAL (kein Petrol-Grün mehr): dezente weiße Aufhellung, heller Text. */
--ctx-hover: rgba(255, 255, 255, 0.1);
--ctx-hover-text: #f0f0f0;
--ctx-danger: #d86060;
position: fixed;
z-index: 300;
min-width: 168px;
max-width: 300px;
padding: 4px;
background: var(--ctx-surface);
border: 1px solid var(--ctx-border);
border-radius: 10px;
box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0, 0, 0, 0.4);
color: var(--ctx-text);
font-family: var(--font);
font-size: 11.5px;
user-select: none;
transform-origin: top left;
animation: ctx-pop 0.1s ease-out;
}
@keyframes ctx-pop {
from {
opacity: 0;
transform: scale(0.94) translateY(-5px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
/* Titel (Ebenenname/-code) — caps, 10px, gedämpft. */
.ctx-title {
padding: 5px 10px 4px;
font-size: 9px;
font-weight: 600;
letter-spacing: 0.07em;
text-transform: uppercase;
color: var(--ctx-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Eintrag. */
.ctx-item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 5px 10px;
border: none;
border-radius: 7px;
background: transparent;
color: var(--ctx-text);
font-family: inherit;
font-size: 11.5px;
text-align: left;
cursor: pointer;
transition: background 0.1s, color 0.1s;
}
.ctx-item:hover:not(:disabled),
.ctx-item:focus-visible:not(:disabled) {
background: var(--ctx-hover);
color: var(--ctx-hover-text);
outline: none;
}
.ctx-item:disabled {
color: var(--ctx-muted);
opacity: 0.55;
cursor: default;
}
/* Destruktive Aktion (Löschen): rote Schrift. */
.ctx-item.danger {
color: var(--ctx-danger);
}
.ctx-item.danger:hover:not(:disabled),
.ctx-item.danger:focus-visible:not(:disabled) {
background: rgba(216, 96, 96, 0.16);
color: var(--ctx-danger);
}
/* Icon-Slot (fluchtende Labels, auch wenn die Icon-Font fehlt). */
.ctx-item-icon {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 14px;
font-size: 13px;
line-height: 1;
color: var(--ctx-muted);
}
/* Material-Symbol im Menü-Icon-Slot exakt auf Slot-Größe begrenzen (sonst wirkt
die Glyphe größer als der Text daneben). */
.ctx-item-icon .material-symbols-outlined {
font-size: 14px;
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 20;
}
.ctx-item:hover:not(:disabled) .ctx-item-icon {
color: inherit;
}
.ctx-item.danger .ctx-item-icon {
color: inherit;
}
.ctx-item-label {
flex: 1 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Tastenkürzel rechtsbündig, gedämpft, monospace. */
.ctx-item-shortcut {
flex: 0 0 auto;
margin-left: 16px;
font-family: var(--font-mono);
font-size: 11px;
color: var(--ctx-muted);
}
/* Trenner. */
.ctx-divider {
height: 1px;
margin: 5px 8px;
background: var(--ctx-border);
}
/* ── Inline-Editor (Ebene / Geschoss) ──────────────────────────────────────
Kleiner schwebender Editor am Anker, gleiche dunkle Palette wie das
Kontextmenü. Aus einem Menüpunkt geöffnet; schließt über Außenklick/Esc.
z-index 301 (knapp über dem Kontextmenü, unter Modals). */
.ctx-editor {
--ctx-surface: #2c2c2c;
--ctx-border: #4a4a4a;
--ctx-text: #e8e8e8;
--ctx-muted: #8a8a8a;
position: fixed;
z-index: 301;
min-width: 240px;
max-width: 320px;
background: var(--ctx-surface);
border: 1px solid var(--ctx-border);
border-radius: 13px;
box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0, 0, 0, 0.4);
color: var(--ctx-text);
font-family: var(--font);
font-size: 13px;
user-select: none;
animation: ctx-pop 0.1s ease-out;
}
.ctx-editor-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 9px 10px 7px 14px;
border-bottom: 1px solid var(--ctx-border);
}
.ctx-editor-title {
font-size: 10px;
font-weight: 600;
letter-spacing: 0.07em;
text-transform: uppercase;
color: var(--ctx-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ctx-editor-close {
flex: 0 0 auto;
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
border-radius: 6px;
background: transparent;
color: var(--ctx-muted);
font-size: 17px;
line-height: 1;
cursor: pointer;
transition: background 0.1s, color 0.1s;
}
.ctx-editor-close:hover {
background: rgba(255, 255, 255, 0.08);
color: var(--ctx-text);
}
.ctx-editor-body {
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px 14px 14px;
}
/* Eine Editor-Zeile: Beschriftung links, Steuerelement rechts. */
.editor-field {
display: grid;
grid-template-columns: 92px 1fr;
align-items: center;
gap: 10px;
}
.editor-label {
font-size: 12px;
color: var(--ctx-muted);
}
.editor-control {
display: flex;
align-items: center;
min-width: 0;
}
/* Text-/Zahlenfelder im Editor: dunkel, volle Breite der Steuer-Spalte. */
.editor-control input[type="text"],
.editor-control input[type="number"] {
width: 100%;
padding: 5px 8px;
background: #1f1f1f;
border: 1px solid var(--ctx-border);
border-radius: 7px;
color: var(--ctx-text);
font-family: inherit;
font-size: 13px;
}
.editor-control input[type="number"] {
font-family: var(--font-mono);
text-align: right;
}
.editor-control input:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.4);
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.14);
}
/* Farbwähler als kompakter Swatch. */
.editor-control input[type="color"] {
width: 40px;
height: 26px;
padding: 0;
border: 1px solid var(--ctx-border);
border-radius: 7px;
background: transparent;
cursor: pointer;
}
/* Zahl + Einheit nebeneinander (mm/m). */
.editor-num {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
}
.editor-unit {
flex: 0 0 auto;
font-size: 11px;
color: var(--ctx-muted);
}
/* Hinweis im Editor (z. B. Zeichnung hat keine Höhen). */
.editor-note {
font-size: 12px;
line-height: 1.4;
color: var(--ctx-muted);
}
/* ════════════════════════════════════════════════════════════════════════
Panel-Drag: schwebende Fenster, Andock-Zonen, Geist + Einfüge-Caret.
════════════════════════════════════════════════════════════════════════ */
/* ── Schwebendes Panel-Fenster ────────────────────────────────────────────
Frei positioniert über der Mitte. Viewport-Koordinaten (position:fixed),
passend zu den clientX/Y der Drag-Gesten — so landet ein gelöstes Panel
exakt unter dem Cursor (ohne Oberleisten-Versatz). Dunkles DOSSIER-Fenster:
Titelleiste (verschieben), Inhalt im PanelFrame, Greifer unten rechts
(Größe). z-index über den Docks, unter Ressourcen-Overlay/Kontextmenü
(FloatingPanel setzt 500 + z inline). */
.floating-panel {
position: fixed;
display: flex;
flex-direction: column;
min-width: 220px;
min-height: 160px;
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;
}
/* Titelleiste = Verschiebegriff. */
.floating-titlebar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
flex: 0 0 auto;
height: 32px;
padding: 0 6px 0 12px;
background: var(--bg);
border-bottom: 1px solid var(--line);
cursor: move;
touch-action: none;
user-select: none;
}
.floating-title {
min-width: 0;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.floating-actions {
display: inline-flex;
align-items: center;
gap: 2px;
flex: 0 0 auto;
}
.floating-btn {
width: 24px;
height: 24px;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--muted);
font-size: 15px;
line-height: 1;
border-radius: 6px;
cursor: pointer;
transition: background 0.12s, color 0.12s;
}
.floating-btn:hover {
background: var(--accent-dim);
color: var(--accent);
}
/* Der PanelFrame im Fenster füllt den Rest. */
.floating-panel .panel-frame {
flex: 1;
min-height: 0;
}
/* Greifer unten rechts (Größe ändern). */
.floating-resize {
position: absolute;
right: 0;
bottom: 0;
width: 16px;
height: 16px;
cursor: nwse-resize;
touch-action: none;
background:
linear-gradient(
135deg,
transparent 0 50%,
var(--border) 50% 60%,
transparent 60% 72%,
var(--border) 72% 82%,
transparent 82%
);
}
/* ── Rand-Andock-Zonen (während eines Drags) ──────────────────────────────
Schmale Streifen am linken/rechten Rand der .body. Reagieren auf Zeiger
(Trefferfläche für die Drop-Auflösung), zeigen aber nur einen dezenten
Petrol-Schimmer; „active" (überfahren) hebt sie hervor. */
.drop-zone {
position: absolute;
top: 0;
bottom: 0;
width: 64px;
z-index: 480;
background: var(--accent-dim);
border: 1px dashed var(--accent-border);
opacity: 0.5;
pointer-events: auto;
transition: opacity 0.12s, background 0.12s;
}
.drop-zone-left {
left: 0;
border-left: none;
}
.drop-zone-right {
right: 0;
border-right: none;
}
.drop-zone.active {
opacity: 1;
background: var(--accent);
border-color: var(--accent);
}
/* ── Drag-Overlay (Geist + Einfüge-Caret) ─────────────────────────────────
Liegt fix über allem und ignoriert Zeiger, damit die Trefferprüfung die
darunterliegenden Tab-Leisten/Zonen weiter erreicht. */
.drag-overlay {
position: fixed;
inset: 0;
z-index: 900;
pointer-events: none;
}
/* Geist-Etikett am Cursor (zeigt den Panel-Titel). */
.drag-ghost {
position: fixed;
padding: 5px 12px;
background: var(--accent);
color: #fff;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
border-radius: 6px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
white-space: nowrap;
opacity: 0.95;
}
/* Einfüge-Caret in der überfahrenen Tab-Leiste (Position aus panelDrag). */
.tab-caret {
position: fixed;
width: 2px;
background: var(--accent);
border-radius: 1px;
box-shadow: 0 0 0 1px var(--accent-dim);
}
/* Neue-Gruppe-Caret an der Ober-/Unterkante eines Gruppen-Körpers (horizontal):
zeigt, dass beim Ablegen eine NEUE gestapelte Gruppe entsteht. */
.group-caret {
position: fixed;
height: 3px;
transform: translateY(-1.5px);
background: var(--accent);
border-radius: 2px;
box-shadow: 0 0 0 1px var(--accent-dim);
}
/* Tools palette */
.tools-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 6px;
overflow-y: auto;
height: 100%;
min-height: 0;
}
.tools-panel .tool-row {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 8px;
border-radius: 6px;
border: 1px solid transparent;
background: transparent;
color: var(--label);
cursor: pointer;
font-size: 12px;
text-align: left;
}
.tools-panel .tool-row:hover {
background: var(--accent-dim);
}
.tools-panel .tool-row.active {
background: var(--accent);
color: #fff;
}
.tools-panel .tool-row.disabled {
opacity: 0.4;
cursor: not-allowed;
}
.tools-panel .tool-row svg {
flex: 0 0 auto;
}
.tools-panel .tools-sep {
height: 1px;
background: var(--line);
margin: 6px 2px;
}
.tools-panel .tools-field {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 4px 8px;
font-size: 12px;
}
.tools-panel .tools-snap-row {
display: flex;
align-items: center;
gap: 8px;
padding: 3px 8px;
font-size: 12px;
cursor: pointer;
user-select: none;
}
.tools-panel .tools-snap-row input {
accent-color: var(--accent);
}
.tools-panel .tools-field input,
.tools-panel .tools-field select {
width: 90px;
}
/* ── Gelände-/Kontext-Palette (SitePanel) ─────────────────────────────── */
.site-panel {
display: flex;
flex-direction: column;
gap: 6px;
padding: 8px;
overflow-y: auto;
height: 100%;
min-height: 0;
font-size: 12px;
}
.site-panel .site-actions,
.site-panel .site-terrain-row {
display: flex;
align-items: center;
gap: 6px;
}
.site-panel .site-btn {
flex: 1 1 auto;
padding: 5px 8px;
border-radius: 6px;
border: 1px solid var(--line);
background: var(--panel-2);
color: var(--ink);
cursor: pointer;
font-size: 12px;
text-align: center;
}
.site-panel .site-btn:hover {
background: var(--accent-dim);
border-color: var(--accent-border);
}
.site-panel .site-select {
flex: 1 1 auto;
min-width: 0;
}
.site-panel .site-terrain-src {
flex: 1 1 auto;
min-width: 0;
color: var(--ink-2);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.site-panel .site-sep {
height: 1px;
background: var(--line);
margin: 2px 0;
}
.site-panel .site-empty {
color: var(--ink-2);
padding: 8px 4px;
line-height: 1.4;
}
.site-panel .site-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.site-panel .site-row {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 6px;
border-radius: 5px;
}
.site-panel .site-row:hover {
background: var(--accent-dim);
}
.site-panel .site-name {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.site-panel .site-badge {
flex: 0 0 auto;
font-size: 10px;
padding: 1px 6px;
border-radius: 8px;
background: var(--panel-2);
color: var(--ink-2);
border: 1px solid var(--line);
}
.site-panel .site-badge-terrainMesh {
color: var(--accent);
border-color: var(--accent-border);
}
.site-panel .site-remove {
flex: 0 0 auto;
width: 18px;
height: 18px;
line-height: 1;
border: none;
background: transparent;
color: var(--ink-2);
cursor: pointer;
border-radius: 4px;
font-size: 14px;
}
.site-panel .site-remove:hover {
background: var(--accent-dim);
color: var(--ink);
}
.site-panel .site-hint {
margin-top: auto;
padding: 8px 4px 2px;
color: var(--ink-2);
font-size: 11px;
line-height: 1.4;
border-top: 1px solid var(--line);
}
/* ── Objekt-Info-Palette (ObjectInfoPanel) ───────────────────────────── */
.objinfo-panel {
display: flex;
flex-direction: column;
gap: 3px;
padding: 8px;
overflow-y: auto;
height: 100%;
min-height: 0;
font-size: 12px;
}
.objinfo-empty {
color: var(--muted);
font-size: 12px;
padding: 8px 4px;
text-align: center;
}
.objinfo-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 8px;
}
.objinfo-kind {
font-weight: 600;
color: var(--label);
}
.objinfo-cat {
color: var(--muted);
font-size: 11px;
}
.objinfo-sep {
height: 1px;
background: var(--line);
margin: 6px 2px;
}
.objinfo-section-label {
color: var(--label);
font-weight: 700;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.04em;
margin: 2px 2px 4px;
}
.objinfo-cube {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 3px;
width: 64px;
height: 64px;
margin: 0 auto 6px;
padding: 3px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--input);
}
.objinfo-anchor {
display: flex;
align-items: center;
justify-content: center;
padding: 0;
border: none;
background: transparent;
border-radius: 3px;
cursor: pointer;
}
.objinfo-anchor:hover {
background: var(--accent-dim);
}
.objinfo-anchor .objinfo-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--muted);
}
.objinfo-anchor:hover .objinfo-dot {
background: var(--accent-light);
}
.objinfo-anchor.active {
background: var(--accent);
}
.objinfo-anchor.active .objinfo-dot {
background: #fff;
width: 8px;
height: 8px;
}
.objinfo-field {
display: flex;
align-items: center;
gap: 8px;
padding: 3px 4px;
}
.objinfo-flabel {
flex: 0 0 auto;
width: 56px;
color: var(--label);
}
.objinfo-fval {
flex: 1 1 auto;
text-align: right;
font-variant-numeric: tabular-nums;
color: var(--label);
}
.objinfo-muted {
color: var(--muted);
}
.objinfo-input {
flex: 1 1 auto;
width: 0;
min-width: 0;
text-align: right;
font-variant-numeric: tabular-nums;
}
.objinfo-unit {
flex: 0 0 auto;
color: var(--muted);
font-size: 11px;
}
/* ── Wand-Abschnitt (Object-Info) ─────────────────────────────────────── */
/* Referenzlinie-Dropdown rechts oben im Kopf. */
.objinfo-head-ref {
display: flex;
align-items: center;
gap: 6px;
}
.objinfo-head-ref-label {
color: var(--muted);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* Segment-Umschalter (Aufbau-Typ, UK/OK-Modus). */
.objinfo-segment {
display: flex;
flex: 1 1 auto;
gap: 2px;
background: var(--input);
border: 1px solid var(--border);
border-radius: 4px;
padding: 2px;
}
.objinfo-seg-btn {
flex: 1 1 0;
padding: 3px 6px;
border: none;
background: transparent;
color: var(--muted);
border-radius: 3px;
cursor: pointer;
font-size: 11px;
white-space: nowrap;
}
.objinfo-seg-btn:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--label);
}
.objinfo-seg-btn.active {
background: var(--accent);
color: #fff;
}
.objinfo-seg-btn:disabled {
opacity: 0.4;
cursor: default;
}
/* Checkbox-Zeile (Verknüpfung oberes Geschoss). */
.objinfo-check-field {
justify-content: space-between;
}
.objinfo-check {
flex: 0 0 auto;
accent-color: var(--accent);
cursor: pointer;
}
/* UK/OK-Block: Segment-Zeile + Unterzeile (Dropdown bzw. Z-Feld). */
.objinfo-wall-anchor {
display: flex;
flex-direction: column;
gap: 2px;
}
.objinfo-subfield {
padding-left: 56px;
}
.objinfo-subfield .tb-dd-trigger {
width: 100%;
}
/* ── Attribute-Palette (AttributesPanel) ─────────────────────────────── */
.attr-panel {
display: flex;
flex-direction: column;
gap: 4px;
padding: 8px;
overflow-y: auto;
height: 100%;
min-height: 0;
font-size: 12px;
}
.attr-empty {
color: var(--muted);
padding: 8px 4px;
text-align: center;
}
.attr-title {
font-weight: 600;
color: var(--label);
padding: 0 2px 4px;
}
.attr-section-label {
color: var(--label);
font-weight: 700;
text-transform: uppercase;
font-size: 10px;
letter-spacing: 0.05em;
margin-top: 6px;
padding: 0 2px;
}
.attr-grid {
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
gap: 4px 10px;
padding: 2px;
}
.attr-key {
color: var(--label);
white-space: nowrap;
}
.attr-val {
display: flex;
align-items: center;
justify-content: flex-end;
min-width: 0;
}
.attr-readonly {
color: var(--label);
opacity: 0.85;
}
.attr-num,
.attr-select {
width: 110px;
background: var(--panel-2);
color: var(--label);
border: 1px solid var(--line);
border-radius: 4px;
padding: 3px 6px;
font-size: 12px;
}
.attr-num:disabled,
.attr-select:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.attr-num:focus,
.attr-select:focus {
outline: none;
border-color: var(--accent-border);
}
.attr-color {
display: inline-flex;
align-items: center;
gap: 6px;
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(--muted);
font-size: 11px;
}
.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;
}
/* ── 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;
}