Files
DOSSIER-STANDALONE/src/styles.css
T

5325 lines
121 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. */
/* Dark ist der feste Standard (Nutzer-Wunsch, unabhängig von OS/Browser-
Präferenz — vorher hing das an `prefers-color-scheme` und zeigte je nach
Umgebung fälschlich Light). Light bleibt als Opt-in erhalten (`[data-theme=
"light"]`) für ein späteres manuelles Umschalt-Feature. */
:root {
color-scheme: dark;
/* Gestufte Elevations-Palette (Wunsch: dunkler + mehr Tiefe). Tiefster
Grund hinten, Flächen werden nach vorn schrittweise heller — leicht
kühl-neutral, harmoniert mit dem Petrol-Akzent.
--bg Ebene 0 — App-Grund / Rinnen hinter allem (am dunkelsten)
--panel Ebene 1 — Oberleiste, Docks, Panel-Körper
--panel-2 Ebene 2 — angehobene Flächen: Panel-Köpfe, Hover-Zeilen
--input versenkt — Eingabefelder (dunkler als die Fläche → Inset) */
/* Zwei-Ton-Grundlage (Nutzer-Vorgabe): Fast-Schwarz hinten, dunkles
Neutralgrau für die Flächen. Neutral (kein Blaustich), mit dezenten
Elevations-Schritten für Tiefe. */
--bg: #0e0e0e;
--panel: #1d1d1d;
--panel-2: #262626;
--input: #0a0a0a;
--line: #2b2b2b;
--border: #383838;
/* --sheet bleibt bewusst hell (#f0f0f0): das Zeichenblatt ist immer
„Papier", unabhängig vom UI-Theme (Wunsch des Nutzers). */
--sheet: #f0f0f0;
--ink: #ededed;
--ink-2: #a8a8a8;
--muted: #6e6e6e;
--label: #c8c8c8;
/* Akzent NEUTRAL (kein Petrol-Grün, Nutzer-Vorgabe): helles Neutralgrau für
aktive/ausgewählte Flächen; weiße Schrift darauf bleibt lesbar. Hover/Dim
sind dezente weiße Aufhellungen. */
--accent: #4d4d4d;
--accent-light: #5e5e5e;
--accent-dim: rgba(255, 255, 255, 0.07);
--accent-border: rgba(255, 255, 255, 0.22);
--active: #6a6a6a;
--active-light: #7d7d7d;
--active-dim: rgba(255, 255, 255, 0.1);
--wall: #c8ccd2;
--swing: #7e8794;
--danger: #c85050;
--danger-light: #d86060;
/* Tiefere Schatten für klare Schichtung auf dunklem Grund. */
--shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5);
--shadow-2: 0 3px 12px rgba(0, 0, 0, 0.55);
--shadow-3: 0 12px 34px rgba(0, 0, 0, 0.62);
--overlay: rgba(0, 0, 0, 0.62);
--font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
sans-serif;
--font-mono: "DM Mono", ui-monospace, "SF Mono", monospace;
}
/* Light-Palette als Opt-in (aktuell von nichts gesetzt — bereit für einen
künftigen manuellen Theme-Umschalter in den Einstellungen). */
:root[data-theme="light"] {
color-scheme: light;
--bg: #e0dbd4;
--panel: #e0dbd4;
--panel-2: #d4cfc8;
--input: #ece8e2;
--line: #d4cfc8;
--border: #c8c2ba;
--sheet: #f0f0f0;
--ink: #1a1a18;
--ink-2: #555550;
--muted: #8a8580;
--label: #1a1a18;
--accent: #2f5d54;
--accent-light: #4a8a7c;
--accent-dim: #e6efed;
--accent-border: rgba(47, 93, 84, 0.35);
--active: #1a655a;
--active-light: #2f8275;
--active-dim: rgba(26, 101, 90, 0.12);
--wall: #2b3039;
--swing: #9aa3b2;
--danger: #8a1a1a;
--danger-light: #b03030;
--shadow-1: 0 1px 2px rgba(26, 26, 24, 0.08);
--shadow-2: 0 2px 8px rgba(26, 26, 24, 0.14);
--shadow-3: 0 6px 24px rgba(26, 26, 24, 0.18);
--overlay: rgba(26, 26, 24, 0.36);
}
* {
box-sizing: border-box;
}
html,
body,
#root {
height: 100%;
margin: 0;
/* Dokument selbst NIE scrollen/bouncen: das Zoom-Wheel im Viewport darf die
App-Chrome nicht mitziehen (macOS-Overscroll/Rubberband). Interne Panels
scrollen weiterhin über ihre eigenen overflow-Container. */
overflow: hidden;
overscroll-behavior: none;
}
/* Native-App-Verhalten: KEINE Textauswahl / „Alles markieren" in der App-Chrome.
user-select global aus; nur in echten Eingaben wieder an, damit Tippen und
Markieren im Feld weiter funktioniert. (Ctrl+A außerhalb von Eingaben wird in
main.tsx zusätzlich unterbunden.) */
html,
body,
#root {
-webkit-user-select: none;
user-select: none;
/* Kein Browser-eigenes Antippen/Highlight auf Touch. */
-webkit-tap-highlight-color: transparent;
}
input,
textarea,
[contenteditable],
[contenteditable] * {
-webkit-user-select: text;
user-select: text;
}
body {
font-family: var(--font);
color: var(--ink);
background: var(--bg);
-webkit-font-smoothing: antialiased;
}
.app {
display: flex;
flex-direction: column;
height: 100%;
}
/* ── Oberleiste ──────────────────────────────────────────────────────── */
/* Gruppen links nach rechts mit Trennern; die rechte Gruppe (Layout/
Ressourcen/Projekt) wird per margin-left:auto an den Rand geschoben. Bei
sehr schmalen Fenstern darf die Leiste horizontal scrollen, statt zu brechen. */
.topbar {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 14px;
height: 60px;
border-bottom: 1px solid var(--line);
/* Oberleiste auf Ebene 1 (angehoben über dem App-Grund) + weicher Schatten
nach unten → trennt sich klar von der Zeichenfläche (Tiefe). */
background: var(--panel);
box-shadow: var(--shadow-1);
position: relative;
z-index: 2;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
/* Dezente, normalerweise unsichtbare Scrollleiste (Firefox). */
scrollbar-width: thin;
scrollbar-color: transparent transparent;
}
/* Sehr ruhige eigene Scrollleiste (WebKit/Blink): Thumb nur beim Scrollen
(`is-scrolling`, von TopBar gesetzt) oder beim Überfahren sichtbar. */
.topbar::-webkit-scrollbar {
height: 6px;
}
.topbar::-webkit-scrollbar-track {
background: transparent;
}
.topbar::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 3px;
transition: background 0.25s ease;
}
.topbar.is-scrolling {
scrollbar-color: rgba(140, 140, 140, 0.45) transparent;
}
.topbar.is-scrolling::-webkit-scrollbar-thumb,
.topbar:hover::-webkit-scrollbar-thumb {
background: rgba(140, 140, 140, 0.4);
}
/* Eine Werkzeug-Gruppe der Oberleiste. */
.tb-group {
display: inline-flex;
align-items: center;
gap: 8px;
flex: 0 0 auto;
}
/* Vertikaler Trenner zwischen Gruppen. */
.tb-sep {
flex: 0 0 auto;
width: 1px;
height: 22px;
background: var(--border);
}
/* Beschriftetes Feld (Label + Control) in der Oberleiste. */
.tb-field {
display: inline-flex;
align-items: center;
gap: 6px;
}
.tb-label {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.03em;
color: var(--muted);
}
/* Kompaktes Dropdown der Oberleiste (Detailgrad, Massstab). */
.tb-select {
border: 1px solid var(--border);
background: var(--input);
color: var(--ink);
font-family: inherit;
font-size: 11px;
font-weight: 600;
padding: 3px 6px;
border-radius: 6px;
cursor: pointer;
}
.tb-select:hover {
border-color: var(--accent-border);
}
.tb-scale {
font-family: var(--font-mono);
min-width: 78px;
}
/* Gruppe aus Knöpfen (Zoom). */
/* Aktions-Pillen (100 % · Einpassen · Auswahl · PDF) — optisch eine Familie mit
den Dropdown-Triggern (.tb-dd-trigger): jede Aktion ist eine eigene gerahmte,
abgesetzte dunkle Pill mit demselben Akzent-Hover (Rand → accent-border,
Fläche → accent-dim, Text → accent). Sie bleiben Buttons (Aktion ≠ Auswahl);
nur die Optik ist angeglichen. */
.tb-btns {
display: inline-flex;
gap: 6px;
}
.tb-btn {
display: inline-flex;
align-items: center;
/* Gleiche stets-dunkle Fläche wie .tb-dd-trigger — einheitlicher Pillen-
Look über die ganze Oberleiste. */
border: 1px solid #4a4a4a;
background: #2c2c2c;
color: #e8e8e8;
font-family: inherit;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 3px 10px;
border-radius: 8px;
cursor: pointer;
outline: none;
transition: border-color 0.14s, background-color 0.14s, color 0.14s,
box-shadow 0.14s;
}
.tb-btn:hover:not(:disabled) {
border-color: var(--accent-border);
background-color: rgba(255, 255, 255, 0.1);
color: var(--accent);
}
.tb-btn:focus-visible {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent-dim);
}
.tb-btn:disabled {
color: #8a8a8a;
opacity: 0.55;
cursor: default;
}
/* Live-Zoom-Anzeige in der Oberleiste (monospace, fixe Breite). */
.tb-zoom {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 600;
color: var(--ink-2);
min-width: 44px;
text-align: right;
}
/* LIVE-Massstab 1:N + Zoom% kompakt übereinander (platzsparend). */
.tb-zoomstack {
display: inline-flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
gap: 1px;
line-height: 1.1;
}
.tb-zoomstack .tb-live-scale,
.tb-zoomstack .tb-zoom {
font-size: 11px;
min-width: 0;
}
/* Live-Papier-Massstab „1:N" in der Oberleiste (Akzent, monospace). Hebt sich
vom gewählten Massstab im Dropdown ab — dies ist der aktuelle Ist-Massstab. */
.tb-live-scale {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 700;
color: var(--accent);
min-width: 56px;
text-align: right;
}
/* Pillen-Toggle der Oberleiste (Referenzlinien). */
.tb-toggle {
border: 1px solid #4a4a4a;
background: #2c2c2c;
color: #e8e8e8;
font-family: inherit;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.03em;
padding: 6px 12px;
border-radius: 8px;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.tb-toggle:hover {
background: rgba(255, 255, 255, 0.1);
border-color: var(--accent-border);
color: var(--accent);
}
.tb-toggle.active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
/* ── DOSSIER-Grundprimitive: Segmentpille · Icon-Knopf · Zoom-Cluster ──────── */
/* Segmentpille (3er/4er): Aussencontainer rund, Zellen gleich hoch, interne
Trenner über border-left. Genutzt für Zoom-Aktionen, B/I/U, L/C/R. Gleiche
stets-dunkle Fläche wie .tb-dd-trigger/.tb-btn (einheitlicher Pillen-Look). */
.tb-seg {
display: inline-flex;
align-items: stretch;
height: 24px;
box-sizing: border-box;
border: 1px solid #4a4a4a;
border-radius: 8px;
overflow: hidden;
background: #2c2c2c;
flex: 0 0 auto;
}
/* Akzent-Rand, wenn ein Text-Ziel selektiert ist (Auswahl-Bewusstsein). */
.tb-seg-accent {
border-color: var(--accent);
}
.tb-seg-cell {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 1 1 0;
min-width: 26px;
padding: 0 6px;
border: none;
border-left: 1px solid #4a4a4a;
background: transparent;
color: #b0b0b0;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.tb-seg-cell:first-child {
border-left: none;
}
.tb-seg-cell:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.1);
color: #f0f0f0;
}
.tb-seg-cell.active {
background: var(--accent);
color: #fff;
}
.tb-seg-cell:disabled {
opacity: 0.4;
cursor: default;
}
/* Quadratischer Icon-Knopf (BarButton). */
.tb-iconbtn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
box-sizing: border-box;
padding: 0;
border: 1px solid #4a4a4a;
border-radius: 8px;
background: #2c2c2c;
color: #b0b0b0;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.tb-iconbtn:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.1);
border-color: var(--accent-border);
color: var(--accent);
}
.tb-iconbtn:disabled {
opacity: 0.4;
cursor: default;
}
/* Massstab/Zoom-Cluster (2×2-Raster): Stat-Pille links über beide Zeilen. */
.tb-zoomcluster {
/* Einfaches Flex: links die Stat-Pille, rechts der gestapelte
(Massstab-Dropdown + Zoom-Segment) Flex-Container. Früher ein 2-Zeilen-Grid
mit `grid-row: 1/span 2` an der Stat-Pille — das erzeugte aber eine leere
Phantom-Zeile (die rechte Seite ist EIN Flex-Container, keine zwei Grid-
Zeilen) und damit +6px Höhe, wodurch der Cluster höher war als die übrigen
zweizeiligen Kombos. */
display: inline-flex;
align-items: center;
gap: 6px;
}
.tb-stat {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 70px;
height: 50px;
box-sizing: border-box;
border: 1px solid var(--border);
border-radius: 14px;
background: var(--input);
/* Ring (Box-Shadow statt Border) für „am Massstab" — startet unsichtbar
(Alpha 0) und blendet weich ein/aus, statt die Fläche aufzuhellen. */
box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0);
transition: box-shadow 0.25s ease;
}
.tb-stat-scale {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 700;
line-height: 1.3;
color: var(--accent);
width: 100%;
text-align: center;
border-bottom: 1px solid var(--border);
}
.tb-stat-zoom {
font-family: var(--font-mono);
font-size: 11px;
line-height: 1.3;
color: var(--ink-2);
width: 100%;
text-align: center;
}
/* „am Massstab" (Live == gewählter Massstab): weisser Ring statt aufgehellter
Fläche — blendet dynamisch ein/aus (s. transition oben), Fläche bleibt
unverändert. */
.tb-stat-atscale {
box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.9);
}
.tb-zoomcluster-right {
display: inline-flex;
flex-direction: column;
align-items: stretch;
gap: 6px;
}
/* ── Text-Gruppe (immer sichtbar, 3×2) ───────────────────────────────────── */
.tb-textgroup {
flex-direction: column;
align-items: stretch;
gap: 6px;
}
.tb-textgroup-row {
display: inline-flex;
align-items: center;
gap: 6px;
}
/* „+ Text"-Knopf: Pille mit Icon + Label. */
/* Zeilenhöhe-Regler (ersetzt den früheren „+ Text"-Knopf — Text wird ein
eigenständiges Zeichenwerkzeug, AUDIT B1). Gleiche dunkle Pillen-Optik wie
die übrigen Oberleisten-Controls (.tb-dd-trigger/.tb-seg). */
.tb-lineheight {
display: inline-flex;
align-items: stretch;
height: 24px;
width: 80px;
box-sizing: border-box;
border: 1px solid #4a4a4a;
border-radius: 8px;
overflow: hidden;
background: #2c2c2c;
flex: 0 0 auto;
}
.tb-lineheight-btn {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 22px;
border: none;
background: transparent;
color: #b0b0b0;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.tb-lineheight-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #f0f0f0;
}
.tb-lineheight-value {
flex: 1 1 auto;
display: inline-flex;
align-items: center;
justify-content: center;
font-family: var(--font-mono);
font-size: 11px;
color: #e8e8e8;
border-left: 1px solid #4a4a4a;
border-right: 1px solid #4a4a4a;
}
/* Marken-Gruppe: Wortmarke + kleiner Icon-Stapel daneben (Settings/Ressourcen),
wie im DOSSIER-Rhino-Plugin (ToolbarApp.jsx: Logo + vertikal gestapelte
Settings-Icons direkt daneben, vor dem ersten Trenner). */
.tb-brandgroup {
display: flex;
align-items: center;
gap: 6px;
flex: 0 0 auto;
}
.brand {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 3px;
line-height: 1;
}
.brand-word {
font-weight: 800;
font-size: 15px;
letter-spacing: -0.01em;
color: var(--ink);
}
.brand-dot {
color: var(--accent);
}
.tag {
font-weight: 500;
font-size: 10px;
color: var(--muted);
border: 1px solid var(--border);
border-radius: 999px;
padding: 1px 7px;
}
/* Icon-Stapel neben der Wortmarke (Settings/Ressourcen) — schmal, dezent. */
.tb-brandicons {
display: flex;
flex-direction: column;
gap: 2px;
}
.tb-brandicon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 17px;
padding: 0;
border: none;
background: transparent;
border-radius: 4px;
color: var(--muted);
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.tb-brandicon .tb-ico {
font-size: 13px;
}
.tb-brandicon:hover {
background: var(--accent-dim);
color: var(--ink);
}
.tb-brandicon.active {
background: var(--accent-dim);
color: var(--accent);
}
.project-name {
color: var(--muted);
font-size: 13px;
}
/* Rechte Gruppe der Oberleiste: Layout-Menü + Ressourcen-Knopf + Projektname.
margin-left:auto schiebt sie an den rechten Rand. */
.topbar-right {
display: inline-flex;
align-items: center;
gap: 12px;
margin-left: auto;
padding-left: 10px;
flex: 0 0 auto;
}
/* „Ressourcen"-Eintrag der Oberleiste (öffnet die Manager-Schublade). */
.res-trigger {
border: 1px solid var(--border);
background: var(--input);
color: var(--ink-2);
font-family: inherit;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
padding: 6px 14px;
border-radius: 999px;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.res-trigger:hover {
background: var(--accent-dim);
border-color: var(--accent-border);
color: var(--accent);
}
.res-trigger.active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
/* Ansichtstyp-Umschalter (Geschoss). */
.view-toggle {
display: inline-flex;
border: 1px solid var(--border);
border-radius: 999px;
overflow: hidden;
background: var(--input);
}
.view-btn {
border: none;
background: transparent;
color: var(--muted);
font-family: inherit;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.03em;
padding: 6px 16px;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.view-btn + .view-btn {
border-left: 1px solid var(--border);
}
.view-btn:hover {
background: var(--accent-dim);
color: var(--ink);
}
.view-btn.active {
background: var(--accent);
color: #fff;
}
/* ── Ansichten als kompaktes Icon-Grid (DOSSIER-Stil, 3 oben / 3 unten) ──── */
.view-grid {
display: grid;
grid-template-columns: repeat(3, auto);
grid-auto-rows: 1fr;
gap: 3px;
flex: 0 0 auto;
}
.view-grid-4 {
grid-template-columns: repeat(4, auto);
}
.view-icon {
border: 1px solid #4a4a4a;
background: #2c2c2c;
color: #b0b0b0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 31px;
height: 22px;
padding: 0;
border-radius: 6px;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.view-icon:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.1);
color: #f0f0f0;
border-color: var(--accent-border);
}
.view-icon.active {
background: var(--accent);
color: #fff;
border-color: var(--accent);
}
.view-icon:disabled {
opacity: 0.4;
cursor: default;
}
/* Kamera-Button steckt im Popover-Wrapper → als Grid-Zelle behandeln. */
.view-grid .tb-popwrap {
display: inline-flex;
}
/* Material-Symbols-Icon in Oberleisten-Buttons (Referenzlinien, Linien-Modus,
Ressourcen) — auf die kompakte Icon-Button-Höhe skaliert, currentColor erbend,
sodass aktiv/hover die Farbe wie bei den View-Icons mitführt. */
.tb-ico {
font-size: 17px;
line-height: 1;
}
/* Linien-Modus-Segmente tragen jetzt Icons statt Text — Padding wie eine
Icon-Zelle (zentriert), damit beide Segmente gleich breit wirken. */
.view-btn-ico {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 4px 12px;
}
.view-btn-ico:disabled {
opacity: 0.4;
cursor: default;
}
/* Gestapelte Gruppe (Label-Felder untereinander, z. B. Ebenen-/Zeichn.-Komb.). */
.tb-group.tb-stack {
flex-direction: column;
align-items: stretch;
gap: 6px;
}
.tb-field-row {
justify-content: space-between;
gap: 8px;
}
/* Rumpf: Navigator + Inhalt nebeneinander. */
.body {
position: relative; /* Bezugsrahmen für schwebende Panels + Andock-Zonen. */
flex: 1;
display: flex;
min-height: 0;
}
/* ── Footer / Status-Leiste (unten) ──────────────────────────────────────
App-Shell ist eine Spalte: Oberleiste / Rumpf (flex:1) / Status-Leiste.
Die Leiste hat eine feste Höhe (~22 px) und schrumpft nicht. Links der
Werkzeug-Hinweis, rechts ein Cluster abgeleiteter View-/Modell-Werte. */
.statusbar {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
height: 22px;
padding: 0 12px;
border-top: 1px solid var(--line);
background: var(--panel-2);
font-size: 11px;
color: var(--ink-2);
user-select: none;
}
/* Werkzeug-Hinweis links (kontextueller Text). */
.sb-hint {
font-weight: 600;
letter-spacing: 0.02em;
color: var(--ink-2);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Rechter Wert-Cluster. */
.sb-cluster {
display: inline-flex;
align-items: center;
gap: 8px;
flex: 0 0 auto;
white-space: nowrap;
}
/* Ein Wert-Feld: kleine Beschriftung + Wert. */
.sb-field {
display: inline-flex;
align-items: baseline;
gap: 5px;
}
.sb-key {
font-size: 10px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--muted);
}
.sb-val {
font-family: var(--font-mono);
font-size: 11px;
color: var(--ink);
}
/* Vertikaler Trenner im Cluster. */
.sb-sep {
width: 1px;
height: 12px;
background: var(--border);
}
/* Renderer-Umschalter (WebGL2 / Nordstern) — kleine Segmentpille; ursprünglich
in der Statusleiste (22px-Leiste), jetzt im Einstellungs-Fenster. In einer
flex-column (.imp-section) würde sie sonst auf volle Breite gestreckt
werden (Standard-`align-items: stretch`) — bleibt daher inhaltsbreit. */
.sb-renderer-toggle {
display: inline-flex;
align-items: stretch;
align-self: flex-start;
height: 16px;
border: 1px solid var(--border);
border-radius: 999px;
overflow: hidden;
background: var(--input);
}
.sb-renderer-btn {
display: inline-flex;
align-items: center;
border: none;
border-left: 1px solid var(--border);
background: transparent;
color: var(--muted);
font-family: inherit;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 0 8px;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.sb-renderer-btn:first-child {
border-left: none;
}
.sb-renderer-btn:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--accent);
}
.sb-renderer-btn.active {
background: var(--accent);
color: #fff;
}
.sb-renderer-btn:disabled {
opacity: 0.4;
cursor: default;
}
/* Einzelner Umschalt-Button in der Statusleiste (z. B. Referenzlinien). */
.sb-toggle {
display: inline-flex;
align-items: center;
border: 1px solid var(--border);
border-radius: 5px;
background: transparent;
color: var(--muted);
font-family: inherit;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 2px 8px;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.sb-toggle:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--accent);
border-color: var(--accent);
}
.sb-toggle.active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
.sb-toggle:disabled {
opacity: 0.4;
cursor: default;
}
/* Generische Segment-Pille für die Statusleiste (Linien-Darstellung, Fang) —
gleiche Masse/Optik wie .sb-renderer-toggle, aber nicht renderer-spezifisch. */
.sb-seg {
display: inline-flex;
align-items: stretch;
height: 16px;
border: 1px solid var(--border);
border-radius: 999px;
overflow: hidden;
background: var(--input);
}
.sb-seg.disabled {
opacity: 0.4;
}
.sb-seg-btn {
display: inline-flex;
align-items: center;
border: none;
border-left: 1px solid var(--border);
background: transparent;
color: var(--muted);
font-family: inherit;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 0 8px;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.sb-seg-btn:first-child {
border-left: none;
}
.sb-seg-btn:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--accent);
}
.sb-seg-btn.active {
background: var(--accent);
color: #fff;
}
.sb-seg-btn:disabled {
opacity: 0.4;
cursor: default;
}
/* Caret-Segment (öffnet das Fang-Popover) — schmaler als die Text-Segmente. */
.sb-seg-caret {
padding: 0 6px;
font-size: 9px;
line-height: 1;
}
/* Fang-Kontrolle: Wrapper ist relativ positioniert, damit das Popover NACH
OBEN öffnen kann (die Leiste sitzt ganz unten im App-Shell). */
.sb-snap-wrap {
position: relative;
display: inline-flex;
}
.sb-snap-popover {
position: absolute;
right: 0;
bottom: 100%;
margin: 0 0 6px;
padding: 6px 0;
min-width: 170px;
background: var(--panel, #1c2128);
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
z-index: 200;
}
.sb-snap-row {
display: flex;
align-items: center;
gap: 8px;
padding: 3px 12px;
font-size: 12px;
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.sb-snap-row input[type="checkbox"] {
accent-color: var(--accent);
}
.sb-snap-row-num {
justify-content: space-between;
cursor: default;
}
.sb-snap-row-num input[type="number"] {
width: 64px;
}
/* ── Command-Line (Rhino-artig) — über der Statusleiste ──────────────────────
Dunkle, ruhige Leiste mit Prompt links, klickbaren Inline-Optionen und einer
monospace-Texteingabe rechts; das Autocomplete-Dropdown öffnet nach oben. */
.cmdline {
position: relative;
flex: 0 0 auto;
display: flex;
align-items: center;
gap: 10px;
height: 26px;
padding: 0 12px;
border-top: 1px solid var(--line);
background: var(--input);
font-size: 12px;
user-select: none;
}
.cmdline-prompt {
flex: 0 0 auto;
font-weight: 600;
color: var(--ink);
white-space: nowrap;
}
.cmdline-options {
display: inline-flex;
align-items: center;
gap: 6px;
flex: 0 0 auto;
}
.cmdline-option {
appearance: none;
border: 1px solid var(--border);
background: var(--panel-2);
color: var(--ink);
font: inherit;
font-size: 11px;
padding: 1px 7px;
border-radius: 4px;
cursor: pointer;
}
.cmdline-option:hover {
border-color: var(--accent-border);
background: var(--accent-dim);
}
.cmdline-option.active {
border-color: var(--accent);
background: var(--accent-dim);
color: var(--accent);
}
.cmdline-option-val {
color: var(--accent);
font-family: var(--font-mono);
}
/* Tastatur-Kürzel vor dem Options-Label (z. B. „U" der Kopiermodi). */
.cmdline-option-key {
display: inline-block;
margin-right: 5px;
padding: 0 4px;
border-radius: 3px;
background: var(--panel-3, var(--panel));
color: var(--ink-dim, var(--ink));
font-family: var(--font-mono);
font-size: 10px;
font-weight: 600;
}
.cmdline-option.active .cmdline-option-key {
background: var(--accent);
color: var(--bg, #fff);
}
/* Tab-Feld-Zyklus: kleine beschriftete Boxen (Länge/Winkel/…); aktives Feld
hervorgehoben (Akzent-Rand). Wert „—" = ungelockt (folgt der Maus). */
.cmdline-fields {
display: inline-flex;
align-items: center;
gap: 6px;
flex: 0 0 auto;
}
.cmdline-field {
display: inline-flex;
align-items: baseline;
gap: 5px;
padding: 1px 7px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--panel-2);
font-size: 11px;
white-space: nowrap;
}
.cmdline-field.active {
border-color: var(--accent-border);
background: var(--accent-dim);
}
.cmdline-field-label {
color: var(--muted);
}
.cmdline-field.active .cmdline-field-label {
color: var(--accent);
}
.cmdline-field-val {
font-family: var(--font-mono);
color: var(--ink);
min-width: 1.2em;
text-align: right;
}
/* Live (maus-folgender) Wert ruhiger als ein gelockter (getippter) Wert. */
.cmdline-field.live .cmdline-field-val {
color: var(--muted);
}
.cmdline-field.locked .cmdline-field-val {
color: var(--ink);
font-weight: 600;
}
.cmdline-input {
flex: 1 1 auto;
min-width: 80px;
appearance: none;
border: none;
background: transparent;
color: var(--ink);
font-family: var(--font-mono);
font-size: 12px;
outline: none;
user-select: text;
}
.cmdline-input::placeholder {
color: var(--muted);
}
/* Autocomplete-Dropdown öffnet nach OBEN (Leiste sitzt ganz unten). */
.cmdline-suggest {
position: absolute;
left: 12px;
bottom: 100%;
margin: 0 0 2px;
padding: 4px 0;
list-style: none;
min-width: 160px;
background: var(--panel-2);
border: 1px solid var(--border);
border-radius: 6px;
box-shadow: var(--shadow-2);
z-index: 50;
}
.cmdline-suggest-item {
padding: 3px 12px;
font-family: var(--font-mono);
font-size: 12px;
color: var(--ink);
cursor: pointer;
}
.cmdline-suggest-item:hover {
background: var(--accent-dim);
color: var(--accent);
}
/* ── Navigator (links) ───────────────────────────────────────────────── */
.navigator {
width: 252px;
flex: 0 0 252px;
display: flex;
flex-direction: column;
gap: 18px;
padding: 14px 8px;
border-right: 1px solid var(--line);
background: var(--panel);
overflow-y: auto;
}
.nav-group {
display: flex;
flex-direction: column;
}
.nav-group-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
padding: 2px 6px 6px;
}
.nav-group-title {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
.nav-group-actions {
display: inline-flex;
gap: 4px;
}
.nav-add {
border: 1px solid var(--border);
background: var(--input);
color: var(--ink-2);
font-family: inherit;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 3px 9px;
border-radius: 999px;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.nav-add:hover {
background: var(--accent-dim);
border-color: var(--accent-border);
color: var(--accent);
}
.nav-list {
display: flex;
flex-direction: column;
}
/* ── Zeichnungsebenen: einklappbare Kategorien (Geschosse/Schnitte/
Ansichten/2D-Zeichnungen) ──────────────────────────────────────────── */
.level-cat {
display: flex;
flex-direction: column;
}
.level-cat-head {
display: flex;
align-items: center;
gap: 6px;
min-height: 24px;
padding: 3px 10px 3px 6px;
background: var(--panel-2);
border-bottom: 1px solid var(--line);
cursor: pointer;
user-select: none;
}
.level-cat-head:hover {
background: var(--accent-dim);
}
.level-cat-chevron {
flex: 0 0 auto;
display: inline-flex;
font-size: 9px;
color: var(--muted);
transition: transform 0.12s;
transform: rotate(0deg);
}
.level-cat-chevron.open {
transform: rotate(90deg);
}
.level-cat-title {
flex: 1;
min-width: 0;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Blankes „+"-Glyph: reines Zeichen, keine Button-Chrome. */
.level-cat-add {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
font-size: 13px;
line-height: 1;
color: var(--muted);
cursor: pointer;
}
.level-cat-add:hover {
color: var(--accent-light);
}
/* Zeile (Zeichnungsebene). */
.nav-row {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
min-height: 26px;
padding: 2px 10px 2px 2px;
border-bottom: 1px solid var(--line);
background: transparent;
color: var(--label);
font-size: 12px;
text-align: left;
cursor: pointer;
user-select: none;
transition: background 0.12s;
}
.nav-row:hover {
background: var(--panel-2);
}
/* Aktive Zeile = Petrol-Pille; Trennlinie verschwindet. */
.nav-row.active {
background: var(--active-dim);
border-radius: 999px;
border-bottom-color: transparent;
color: var(--active-light);
}
.nav-row.active .nav-label {
font-weight: 600;
color: var(--active-light);
}
/* Ausgeblendete (nicht-aktive) Zeile: gedimmt. */
.nav-row.hidden {
opacity: 0.45;
}
.nav-label {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--label);
font-weight: 500;
}
.nav-elev {
flex: 0 0 auto;
font-family: var(--font-mono);
font-size: 10px;
color: var(--muted);
}
/* Augen-Schalter (Sichtbarkeit) — erbt Zeilenfarbe via currentColor. */
.eye {
flex: 0 0 auto;
width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--muted);
cursor: pointer;
border-radius: 50%;
padding: 0;
transition: background 0.12s, color 0.12s;
}
.eye:hover {
background: rgba(0, 0, 0, 0.06);
color: var(--ink);
}
.eye.is-on {
color: var(--ink);
}
.nav-row.active .eye,
.nav-row.active .eye.is-on {
color: var(--active-light);
}
/* ── Kategorie-Badges (Chips) ────────────────────────────────────────── */
.chip {
flex: 0 0 auto;
font-size: 9px;
font-weight: 600;
letter-spacing: 0.06em;
padding: 2px 8px;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--input);
color: var(--ink-2);
text-transform: uppercase;
}
.badge-floor {
background: var(--active-dim);
border-color: var(--accent-border);
color: var(--active);
}
.badge-section {
background: rgba(120, 90, 160, 0.14);
border-color: rgba(120, 90, 160, 0.35);
color: #6d4f93;
}
.badge-elevation {
background: rgba(40, 120, 110, 0.14);
border-color: rgba(40, 120, 110, 0.35);
color: #2f7a6c;
}
.badge-drawing {
background: rgba(181, 98, 30, 0.14);
border-color: rgba(181, 98, 30, 0.35);
color: #b5621e;
}
/* ── Ebenen-Baum (Kategorien) ────────────────────────────────────────── */
.cat-row {
display: flex;
align-items: center;
gap: 6px;
min-height: 24px;
padding: 1px 10px 1px 6px;
font-size: 12px;
color: var(--label);
border-bottom: 1px solid var(--line);
cursor: default;
user-select: none;
transition: background 0.12s;
}
.cat-row:hover {
background: var(--panel-2);
}
.cat-row {
cursor: pointer; /* Zeile ist als „aktive Ebene wählen" klickbar */
}
/* Aktive Ebene (Zeichen-Ziel): Akzentstreifen links + leichte Akzentfüllung. */
.cat-row.active {
background: var(--accent-dim);
box-shadow: inset 2px 0 0 var(--accent);
}
.cat-row.active .cat-name {
color: var(--text, #e6e6e6);
font-weight: 600;
}
.cat-row.hidden {
opacity: 0.45;
}
/* Gesperrt (Modus „Andere grau & gesperrt"): gedämpft, Cursor signalisiert
Nicht-Bearbeitbarkeit. Gilt für Ebenen- und Zeichnungsebenen-Zeilen. */
.cat-row.locked,
.nav-row.locked {
cursor: not-allowed;
}
.cat-row.locked .cat-name,
.nav-row.locked .nav-label {
font-style: italic;
}
.cat-code {
flex: 0 0 auto;
font-family: var(--font-mono);
font-size: 9px;
color: var(--muted);
width: 20px;
text-align: left;
}
.cat-swatch {
flex: 0 0 auto;
width: 12px;
height: 12px;
border-radius: 2px;
border: 1px solid var(--border);
}
.cat-name {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
}
/* ── Inhalt (rechts) ─────────────────────────────────────────────────── */
.content {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
min-width: 0;
}
/* Mitte-Spalte: Viewport (.content) + Befehlszeile (.cmdline) darunter. Nimmt in
der horizontalen .body den Platz ZWISCHEN den Docks ein und stapelt vertikal —
so ist die Befehlszeile nur so breit wie der Viewport, und die Docks reichen
über die volle Body-Höhe (inkl. der Befehlszeilen-Zeile). */
.center-col {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
min-height: 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;
}
/* ── Wand-/Deckenstil-Aufbau (Master-Detail) ────────────────────────────── */
/* Der Querschnitt-Swatch (WallTypeSwatch) ist frei skalierbar. */
.res-wallswatch {
border-radius: 3px;
}
/* Aufbau-Editor: die Schichten gestapelt, darunter „Schicht hinzufügen". */
.res-layers {
display: flex;
flex-direction: column;
gap: 8px;
}
.res-layer {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 8px;
padding: 8px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--input);
}
.res-layer-move {
display: flex;
flex-direction: column;
gap: 2px;
}
.res-layer-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 16px;
padding: 0;
border: 1px solid var(--border);
border-radius: 5px;
background: var(--panel-2);
color: var(--ink);
font-size: 9px;
line-height: 1;
cursor: pointer;
transition: background 0.12s, border-color 0.12s;
}
.res-layer-btn:hover:not(:disabled) {
background: var(--accent-dim);
border-color: var(--accent-border);
}
.res-layer-btn:disabled {
opacity: 0.35;
cursor: default;
}
.res-layer-fields {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
}
.res-layer-fields .res-field {
grid-template-columns: 76px 1fr;
gap: 8px;
}
/* ── Tabelle (saubere Liste, CONVENTIONS.md) ──────────────────────────────────
Ein CSS-Grid trägt die Spalten (gridTemplateColumns wird je Tab inline
gesetzt). Kopfzeile und Datenzeilen sind `display:contents`, damit ihre
Zellen direkte Grid-Kinder werden und exakt fluchten. */
.res-table {
display: grid;
align-items: center;
column-gap: 8px;
row-gap: 2px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--panel-2);
overflow: hidden;
}
/* Kopf- und Datenzeile lösen sich auf — ihre Zellen sind Grid-Kinder. */
.res-thead,
.res-tr {
display: contents;
}
/* Sticky Kopfzeile: jede Titel-Zelle klebt am oberen Rand des Scrollbereichs
(.res-body). Da Zellen einzelne Grid-Kinder sind, wird sticky je Zelle
gesetzt; ein durchgehender Hintergrund verhindert Durchscheinen. */
.res-th {
position: sticky;
top: 0;
z-index: 2;
align-self: stretch;
display: flex;
align-items: center;
padding: 7px 4px;
font-size: 9px;
font-weight: 700;
letter-spacing: 0.07em;
text-transform: uppercase;
color: var(--muted);
background: var(--panel-2);
border-bottom: 1px solid var(--border);
}
.res-th.num {
justify-content: flex-end;
}
/* Erste/letzte Kopfzelle randvoll, damit die Sticky-Leiste durchläuft. */
.res-th:first-child {
padding-left: 12px;
}
.res-th:last-child {
padding-right: 12px;
}
/* Datenzeilen: dezente Trennlinie, Hover hebt die ganze Zeile leicht hervor.
Die Trennlinie sitzt auf jeder Zelle, da die Zeile selbst kein Box hat. */
.res-td {
display: flex;
align-items: center;
padding: 5px 4px;
min-width: 0;
}
.res-td.num {
justify-content: flex-end;
}
.res-td:first-child {
padding-left: 12px;
}
.res-td:last-child {
padding-right: 12px;
}
.res-tr:hover .res-td {
background: var(--panel);
}
/* Verschneidungs-Rang (joinPriority) — die tragende Zelle hervorheben. */
.res-td.emphasis .res-input {
font-weight: 700;
color: var(--accent);
border-color: var(--accent-border);
background: var(--accent-dim);
}
/* Text-/Zahleneingabe (Pill). */
.res-input {
font-family: var(--font);
font-size: 12px;
color: var(--ink);
background: var(--input);
border: 1px solid var(--border);
border-radius: 999px;
padding: 5px 11px;
outline: none;
width: 100%;
transition: border-color 0.14s, box-shadow 0.14s, background 0.14s;
}
.res-input.mono {
font-family: var(--font-mono);
}
.res-input.num {
text-align: right;
}
.res-input:hover {
border-color: var(--accent);
background: var(--panel);
}
.res-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent-dim);
}
/* Pill-Dropdown — nativer Pfeil, Petrol-Hover. */
.res-select {
appearance: none;
-webkit-appearance: none;
font-family: var(--font);
font-size: 11px;
font-weight: 500;
letter-spacing: 0.02em;
color: var(--ink);
background-color: var(--input);
background-image: url("data:image/svg+xml;utf8,<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;
/* Feste Höhe statt nur Padding: Panels OHNE Darstellungsmodus-Dropdown
(nur Titel) hatten sonst einen niedrigeren Kopf als Panels MIT Dropdown
— beim Wechseln/Stapeln "sprang" die Kopfzeile sichtbar. box-sizing
zieht das Padding von der festen Höhe ab, align-items:center zentriert
Titel bzw. Titel+Dropdown gleichermassen. */
height: 40px;
box-sizing: border-box;
padding: 0 12px;
border-bottom: 1px solid var(--line);
/* Uniform mit dem Panel-Körper (Nutzer-Wunsch): kein separater hellerer
Kopf-Balken mehr, nur die Trennlinie grenzt Kopf/Körper ab. */
background: var(--panel);
}
.panel-title {
min-width: 0;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #f0f0f0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.panel-body {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 10px 8px 16px;
}
/* ── Darstellungsmodus-Dropdown (Panel-Kopf) ──────────────────────────────
Nutzt jetzt die gemeinsame Dropdown-Komponente (.tb-dd-trigger) für den
dunklen, abgerundeten Look; hier nur noch die Breitenbegrenzung + kompakte
Panel-Kopf-Typografie, die dem generischen Trigger fehlen. */
.display-mode {
flex: 0 0 auto;
max-width: 160px;
font-size: 10px;
letter-spacing: 0.02em;
}
/* ── Layout-Menü (Oberleiste: benannte Layouts sichern/laden) ─────────────── */
/* Kleines Pill-Dropdown im selben Stil wie .res-trigger; ein Aktions-Trigger,
daher steht der angezeigte Wert immer auf „Layout …". */
.layout-menu {
appearance: none;
-webkit-appearance: none;
font-family: var(--font);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
color: var(--ink-2);
background-color: var(--input);
background-image: url("data:image/svg+xml;utf8,<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;
/* Trigger trägt dieselbe stets-dunkle Fläche wie sein Popover (.ctx-menu) —
beide sollen wie EIN durchgehendes Element wirken statt heller Knopf +
dunkles Menü darunter. .ctx-menu hängt per Portal ausserhalb dieses
DOM-Zweigs, darum hier dieselben Werte dupliziert statt geerbt. */
border: 1px solid #4a4a4a;
background: #2c2c2c;
color: #e8e8e8;
font-family: inherit;
font-size: 11px;
font-weight: 600;
padding: 3px 8px 3px 8px;
border-radius: 8px;
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: rgba(255, 255, 255, 0.1);
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: #8a8a8a;
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: #8a8a8a;
}
.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);
}
/* Auswahlfelder (z. B. Schraffur) wie die Text-/Zahlenfelder gestaltet. */
.editor-control select {
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 select:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.4);
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.14);
}
/* Schraffur-Auswahl + kleine Vorschau nebeneinander. */
.editor-hatch {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
min-width: 0;
}
.editor-hatch select {
flex: 1 1 auto;
min-width: 0;
}
.editor-hatch svg {
flex: 0 0 auto;
}
/* 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;
}
/* Swatch + natives Farb-Input — EIGENER Klick-Bereich (öffnet die Palette).
Der Hex-Text daneben ist jetzt ein separates <input> (ColorHexField),
damit er unabhängig editierbar ist statt nur die Palette zu öffnen. */
.attr-color-swatch-wrap {
display: inline-flex;
align-items: center;
gap: 4px;
cursor: pointer;
}
.attr-color-swatch {
width: 16px;
height: 16px;
border-radius: 3px;
border: 1px solid var(--line);
flex: 0 0 auto;
}
.attr-color input[type="color"] {
width: 22px;
height: 18px;
padding: 0;
border: 1px solid var(--line);
border-radius: 3px;
background: transparent;
cursor: pointer;
}
.attr-color-hex {
font-variant-numeric: tabular-nums;
color: var(--ink-2);
font-size: 11px;
}
/* Editierbarer Hex-Code: sieht wie reiner Text aus, bis man klickt/fokussiert
— dann Rahmen + helle Schrift als Editier-Signal. */
.attr-color-hex-input {
width: 8.5ch;
color: var(--ink-2);
border: 1px solid transparent;
background: transparent;
border-radius: 3px;
padding: 1px 3px;
font-family: var(--font-mono);
cursor: text;
}
.attr-color-hex-input:hover:not(:disabled) {
border-color: var(--border);
}
.attr-color-hex-input:focus {
outline: none;
border-color: var(--accent);
background: var(--input);
color: var(--ink);
}
.attr-color input[type="color"]:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.attr-color-clear {
border: 1px solid var(--line);
background: transparent;
color: var(--muted);
border-radius: 3px;
width: 16px;
height: 16px;
line-height: 1;
padding: 0;
cursor: pointer;
flex: 0 0 auto;
}
.attr-color-clear:hover {
color: var(--text);
border-color: var(--accent-border);
}
/* ── Import-Dialog (DXF/DWG) ───────────────────────────────────────────────
Modal wie der ResourceManager: abgedunkeltes Overlay, zentriertes Fenster,
Klick auf den Hintergrund / Esc schließt. */
.imp-overlay {
position: fixed;
inset: 0;
z-index: 1100;
background: var(--overlay);
display: flex;
align-items: center;
justify-content: center;
}
.imp-dialog {
width: min(520px, 92vw);
max-height: 88vh;
display: flex;
flex-direction: column;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: var(--shadow-3);
overflow: hidden;
}
.imp-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
height: 48px;
flex: 0 0 auto;
border-bottom: 1px solid var(--line);
background: var(--bg);
}
.imp-head-title {
font-weight: 650;
font-size: 14px;
letter-spacing: 0.2px;
color: var(--ink);
}
.imp-close {
width: 28px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--muted);
font-size: 20px;
line-height: 1;
border-radius: 50%;
cursor: pointer;
transition: background 0.12s, color 0.12s;
}
.imp-close:hover {
background: var(--panel-2);
color: var(--ink);
}
.imp-body {
padding: 14px 16px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 14px;
}
.imp-file {
display: flex;
align-items: baseline;
gap: 8px;
font-size: 12px;
}
.imp-file-label {
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.04em;
font-size: 10px;
}
.imp-file-name {
color: var(--ink);
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.imp-section {
display: flex;
flex-direction: column;
gap: 7px;
}
.imp-section-title {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--ink-2);
}
.imp-summary {
margin: 0;
padding-left: 18px;
font-size: 12px;
color: var(--ink);
line-height: 1.6;
}
.imp-radio,
.imp-check {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--ink);
cursor: pointer;
}
.imp-radio input[disabled] + span {
color: var(--muted);
}
.imp-input,
.imp-select {
width: 100%;
font-family: inherit;
font-size: 13px;
padding: 6px 9px;
border: 1px solid var(--border);
border-radius: 7px;
background: var(--input);
color: var(--ink);
margin-left: 22px;
width: calc(100% - 22px);
}
.imp-note,
.imp-warn,
.imp-dwg-hint {
font-size: 12px;
line-height: 1.5;
color: var(--muted);
}
.imp-warn {
color: var(--accent);
}
.imp-dwg-hint {
color: var(--ink-2);
background: var(--panel-2);
border: 1px solid var(--border);
border-radius: 7px;
padding: 10px 12px;
}
.imp-foot {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 12px 16px;
flex: 0 0 auto;
border-top: 1px solid var(--line);
background: var(--bg);
}
.imp-btn {
font-family: inherit;
font-size: 13px;
font-weight: 600;
padding: 7px 18px;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--input);
color: var(--ink-2);
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.imp-btn:hover {
color: var(--ink);
border-color: var(--accent-border);
}
.imp-btn.primary {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
.imp-btn.primary:hover {
background: var(--accent-light);
border-color: var(--accent-light);
}
.imp-btn:disabled {
opacity: 0.45;
cursor: not-allowed;
}
/* ── Einstellungs-Fenster (SettingsDialog): Farbfelder + Akzent-Presets ────
Nutzt sonst dieselben imp-*-Klassen wie die anderen Dialoge. */
.settings-field-label {
font-size: 12px;
color: var(--ink-2);
}
.settings-color-field {
display: flex;
flex-direction: column;
gap: 7px;
}
.settings-color-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.settings-accent-presets {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.settings-accent-btn {
width: 22px;
height: 22px;
padding: 0;
border-radius: 6px;
border: 1px solid var(--line);
cursor: pointer;
transition: transform 0.1s, border-color 0.12s;
}
.settings-accent-btn:hover {
transform: scale(1.08);
}
.settings-accent-btn.active {
border: 2px solid var(--accent);
box-shadow: 0 0 0 2px var(--accent-dim);
}
.settings-num-input {
font-family: inherit;
font-size: 13px;
padding: 6px 9px;
border: 1px solid var(--border);
border-radius: 7px;
background: var(--input);
color: var(--ink);
width: 120px;
}
.settings-num-input:focus {
outline: none;
border-color: var(--accent-border);
}
/* ── App-weites Drop-Feedback ──────────────────────────────────────────────
Dezenter Rahmen + Hinweis, solange eine Datei über die App gezogen wird. */
.drop-feedback {
position: fixed;
inset: 0;
z-index: 1090;
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
background: var(--accent-dim);
border: 3px dashed var(--accent-border);
}
.drop-feedback-inner {
font-size: 16px;
font-weight: 650;
letter-spacing: 0.3px;
color: var(--accent);
background: var(--panel);
border: 1px solid var(--accent-border);
border-radius: 10px;
padding: 14px 22px;
box-shadow: var(--shadow-3);
}
/* ── 3D-Material-Zuweisung (Bauteil-Manager) ──────────────────────────────── */
/* Material-Zelle: Button mit kleinem Farb-Karten-Swatch + Name. */
.res-material-btn {
display: flex;
align-items: center;
gap: 6px;
text-align: left;
cursor: pointer;
overflow: hidden;
}
.res-material-swatch {
width: 16px;
height: 16px;
flex: 0 0 auto;
border-radius: 3px;
border: 1px solid var(--border);
background-size: cover;
background-position: center;
}
.res-material-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Modal-Overlay des Material-Pickers (über der Ressourcen-Schublade). */
.mat-overlay {
position: fixed;
inset: 0;
z-index: 1200;
display: flex;
align-items: center;
justify-content: center;
background: var(--overlay);
}
.mat-dialog {
width: min(680px, 92vw);
max-height: 86vh;
overflow: auto;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
box-shadow: var(--shadow-3);
padding: 16px 18px 18px;
}
.mat-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.mat-title {
font-weight: 600;
color: var(--ink);
}
.mat-hint {
color: var(--muted);
font-size: 12px;
margin-bottom: 10px;
}
.mat-section-title {
font-weight: 600;
color: var(--ink-2);
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.04em;
margin: 12px 0 8px;
}
.mat-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
gap: 8px;
}
.mat-tile {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 4px;
padding: 4px;
background: var(--panel-2);
border: 1px solid var(--border);
border-radius: 8px;
cursor: pointer;
}
.mat-tile:hover {
border-color: var(--accent-border);
}
.mat-tile.active {
border-color: var(--active);
box-shadow: 0 0 0 2px var(--active-dim);
}
.mat-thumb {
width: 100%;
aspect-ratio: 1 / 1;
border-radius: 5px;
background-size: cover;
background-position: center;
background-color: var(--input);
}
.mat-tile-name {
font-size: 11px;
color: var(--ink-2);
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mat-upload-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 8px;
}
.mat-upload-row {
display: flex;
align-items: center;
gap: 8px;
}
.mat-upload-label {
flex: 0 0 86px;
font-size: 12px;
color: var(--ink-2);
}
.mat-upload-ok {
color: var(--active);
font-weight: 700;
}
.mat-pick {
padding: 4px 10px;
}
.mat-actions {
display: flex;
gap: 8px;
margin-top: 16px;
}
.mat-danger {
color: var(--danger);
border-color: var(--danger);
}
/* ── Material-Picker: Modus-Umschalter + ambientCG-Browse ─────────────────── */
.mat-modes {
display: flex;
gap: 4px;
margin: 4px 0 12px;
padding: 3px;
background: var(--panel-2);
border: 1px solid var(--border);
border-radius: 8px;
}
.mat-mode-btn {
flex: 1 1 0;
padding: 6px 10px;
border: 1px solid transparent;
border-radius: 6px;
background: transparent;
color: var(--ink-2);
font-size: 12px;
cursor: pointer;
}
.mat-mode-btn:hover {
color: var(--ink);
}
.mat-mode-btn.active {
background: var(--panel);
border-color: var(--active);
color: var(--ink);
}
.mat-actions-browse {
align-items: center;
justify-content: space-between;
}
.mat-size-inline {
display: flex;
align-items: center;
gap: 8px;
}
.mat-size-inline .res-input {
width: 80px;
}
.mat-browse-bar {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
margin: 8px 0 10px;
}
.mat-search {
flex: 1 1 180px;
min-width: 140px;
}
.mat-browse-bar .res-select {
flex: 0 0 auto;
}
.mat-browse-grid {
min-height: 120px;
max-height: 46vh;
overflow: auto;
padding-right: 2px;
}
.mat-tile {
position: relative;
}
.mat-tile.loading {
opacity: 0.6;
}
.mat-tile-spinner {
position: absolute;
top: 50%;
left: 50%;
width: 22px;
height: 22px;
margin: -11px 0 0 -11px;
border: 3px solid var(--active-dim);
border-top-color: var(--active);
border-radius: 50%;
animation: mat-spin 0.7s linear infinite;
}
@keyframes mat-spin {
to {
transform: rotate(360deg);
}
}
.mat-browse-error {
color: var(--danger);
font-size: 12px;
padding: 8px 10px;
margin-bottom: 8px;
background: var(--panel-2);
border: 1px solid var(--danger);
border-radius: 6px;
}
.mat-browse-foot {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
}
.mat-browse-status {
color: var(--muted);
font-size: 12px;
}
/* ── Materialien-Tab: Bibliothek durchsuchen (Kugel-Vorschau, Filter) ────── */
.mat-lib-filterbar {
display: flex;
flex-wrap: wrap;
gap: 8px 10px;
align-items: center;
}
.mat-lib-filterbar .mat-search {
flex: 1 1 200px;
min-width: 160px;
}
.mat-lib-chips {
display: flex;
flex-wrap: wrap;
gap: 6px;
flex: 1 1 100%;
}
.mat-chip {
padding: 4px 11px;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--panel-2);
color: var(--ink-2);
font-size: 11.5px;
cursor: pointer;
transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.mat-chip:hover {
border-color: var(--accent-border);
color: var(--ink);
}
.mat-chip.active {
background: var(--active-dim);
border-color: var(--active);
color: var(--ink);
}
.mat-lib-selected {
font-size: 12px;
color: var(--ink-2);
}
.mat-lib-grid {
grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
gap: 10px;
}
.mat-sphere {
width: 100%;
aspect-ratio: 1 / 1;
border-radius: 999px;
background: var(--input);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.mat-sphere img {
width: 100%;
height: 100%;
object-fit: contain;
}
.mat-tile-badge {
align-self: center;
font-size: 9.5px;
letter-spacing: 0.02em;
color: var(--muted);
background: var(--panel);
border: 1px solid var(--border);
border-radius: 999px;
padding: 1px 7px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
/* Standort-Import-Dialog (ContextImportDialog) — nutzt das imp-*-Modal-Gerüst,
ergänzt nur die inneren Suchfelder/Checkboxen (cim-*-Namespace). */
.cim-search-row {
display: flex;
gap: 8px;
align-items: center;
}
.cim-input {
flex: 1 1 auto;
min-width: 0;
background: var(--input);
color: var(--ink);
border: 1px solid var(--border);
border-radius: 6px;
padding: 6px 8px;
font-size: 13px;
}
.cim-input:focus {
outline: none;
border-color: var(--accent);
}
.cim-input-num {
flex: 0 0 90px;
width: 90px;
}
.cim-results {
list-style: none;
margin: 8px 0 0;
padding: 0;
max-height: 168px;
overflow-y: auto;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--panel-2);
}
.cim-result {
padding: 6px 8px;
font-size: 12px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border-bottom: 1px solid var(--line);
}
.cim-result:last-child {
border-bottom: none;
}
.cim-result:hover {
background: var(--accent-dim);
}
.cim-result.selected {
background: var(--accent);
color: #fff;
}
.cim-radius-row {
display: flex;
align-items: center;
gap: 6px;
}
.cim-unit {
color: var(--muted);
font-size: 12px;
}
.cim-checks {
display: flex;
flex-direction: column;
gap: 6px;
}
.cim-check {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--ink);
cursor: pointer;
}
.cim-status {
color: var(--muted);
font-size: 12px;
padding: 2px 0;
}
/* ── Rich-Text-Editor ────────────────────────────────────────────── */
.rt-editor {
display: flex;
flex-direction: column;
border: 1px solid var(--border);
border-radius: 6px;
overflow: hidden;
background: var(--panel);
box-shadow: var(--shadow-1);
}
.rt-toolbar {
display: flex;
align-items: center;
gap: 4px;
padding: 5px 8px;
background: var(--panel-2);
border-bottom: 1px solid var(--border);
flex-wrap: wrap;
}
.rt-toolbar-sep {
width: 1px;
height: 18px;
background: var(--border);
margin: 0 2px;
}
.rt-toolbar-label {
display: flex;
align-items: center;
gap: 3px;
color: var(--ink-2);
font-size: 12px;
}
.rt-unit {
font-size: 11px;
color: var(--muted);
}
.rt-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 26px;
height: 26px;
padding: 0 5px;
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: var(--ink);
font-size: 13px;
font-family: var(--font);
cursor: pointer;
line-height: 1;
transition: background 120ms, border-color 120ms;
}
.rt-btn:hover {
background: var(--input);
border-color: var(--border);
}
.rt-btn--active {
background: var(--accent-dim);
border-color: var(--accent-border);
color: var(--accent-light);
}
.rt-btn--active:hover {
background: var(--accent-dim);
border-color: var(--accent);
}
.rt-size-input {
width: 48px;
height: 24px;
padding: 0 4px;
background: var(--input);
color: var(--ink);
border: 1px solid var(--border);
border-radius: 4px;
font-size: 12px;
font-family: var(--font-mono);
text-align: right;
appearance: textfield;
}
.rt-size-input:focus {
outline: none;
border-color: var(--accent);
}
.rt-size-input::-webkit-inner-spin-button,
.rt-size-input::-webkit-outer-spin-button {
opacity: 0.5;
}
.rt-color-input {
width: 26px;
height: 26px;
padding: 1px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--input);
cursor: pointer;
appearance: none;
-webkit-appearance: none;
}
.rt-color-input::-webkit-color-swatch-wrapper {
padding: 0;
}
.rt-color-input::-webkit-color-swatch {
border: none;
border-radius: 3px;
}
.rt-preset-select {
height: 26px;
padding: 0 6px;
background: var(--input);
color: var(--ink);
border: 1px solid var(--border);
border-radius: 4px;
font-size: 12px;
font-family: var(--font);
cursor: pointer;
}
.rt-preset-select:focus {
outline: none;
border-color: var(--accent);
}
.rt-surface {
flex: 1 1 auto;
min-height: 80px;
padding: 10px 12px;
background: var(--input);
color: var(--ink);
font-size: 13px;
font-family: var(--font);
line-height: 1.55;
outline: none;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-word;
}
.rt-surface:focus {
box-shadow: inset 0 0 0 1px var(--accent);
}
.rt-surface div {
min-height: 1em;
}
.rt-surface div + div {
margin-top: 0;
}
/* rt-btn active state — space-separated variant used by RichTextEditor */
.rt-btn.active {
background: var(--accent-dim);
border-color: var(--accent-border);
color: var(--accent-light);
}
.rt-btn.active:hover {
background: var(--accent-dim);
border-color: var(--accent);
}
/* ── Raum: Stempel-Griff, Objekt-Info-Steuerung, Stempel-Editor, Bilanz ── */
/* Ziehbare Stempel-Raute im Grundriss (Akzentfarbe). */
.plan-svg .plan-stamp-grip {
fill: var(--accent);
stroke: #ffffff;
stroke-width: 1;
vector-effect: non-scaling-stroke;
}
/* Objekt-Info: Textfeld + Aktions-Button (Raum-Abschnitt). */
.objinfo-text {
flex: 1;
min-width: 0;
background: var(--panel-2);
border: 1px solid var(--border);
color: var(--ink);
border-radius: 3px;
padding: 3px 6px;
font-size: 12px;
}
.objinfo-btn {
margin-top: 8px;
width: 100%;
background: var(--panel-2);
border: 1px solid var(--border);
color: var(--ink);
border-radius: 4px;
padding: 5px 8px;
font-size: 12px;
cursor: pointer;
}
.objinfo-btn:hover {
border-color: var(--accent);
}
/* Schwebendes Dialog-Fenster zum Bearbeiten eines Rich-Text-Dokuments
(Raum-Stempel / Textobjekt) — DOSSIER §4: zentriert, --shadow-3, radius 8,
Titel „Text bearbeiten", Kopf mit Schliessen-✕, Fuss Abbrechen/Übernehmen. */
.texteditor-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.28);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.texteditor-dialog {
display: flex;
flex-direction: column;
width: 560px;
max-width: calc(100vw - 32px);
height: 420px;
max-height: calc(100vh - 32px);
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: var(--shadow-3);
overflow: hidden;
}
.texteditor-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
}
.texteditor-title {
font-size: 13px;
font-weight: 600;
color: var(--ink);
}
.texteditor-x {
display: inline-flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
padding: 0;
border: 1px solid transparent;
border-radius: 999px;
background: transparent;
color: var(--muted);
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.texteditor-x:hover {
background: var(--accent-dim);
border-color: var(--accent-border);
color: var(--accent);
}
.texteditor-x .material-symbols-outlined {
font-size: 18px;
line-height: 1;
}
.texteditor-body {
flex: 1 1 auto;
min-height: 0;
display: flex;
padding: 12px;
overflow: auto;
}
.texteditor-body .rt-editor {
flex: 1 1 auto;
min-height: 0;
}
.texteditor-foot {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
padding: 10px 12px;
border-top: 1px solid var(--border);
}
.texteditor-btn {
border: 1px solid var(--border);
background: var(--input);
color: var(--ink);
font-family: inherit;
font-size: 12px;
font-weight: 600;
padding: 5px 14px;
border-radius: 999px;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.texteditor-btn:hover {
border-color: var(--accent-border);
background: var(--accent-dim);
color: var(--accent);
}
.texteditor-btn-primary {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
.texteditor-btn-primary:hover {
background: var(--accent);
border-color: var(--accent);
color: #fff;
filter: brightness(1.08);
}
/* Strukturierter Raum-Stempel-Editor (Feldmodell). */
.roomstamp-dialog {
height: auto;
}
.roomstamp-body {
flex-direction: column;
gap: 10px;
overflow: auto;
}
.roomstamp-field {
display: flex;
flex-direction: column;
gap: 4px;
}
.roomstamp-label {
font-size: 11px;
font-weight: 600;
color: var(--muted);
}
.roomstamp-input {
border: 1px solid var(--border);
background: var(--input);
color: var(--ink);
font-family: inherit;
font-size: 13px;
padding: 5px 8px;
border-radius: 6px;
}
.roomstamp-input:focus {
outline: none;
border-color: var(--accent-border);
}
.roomstamp-input:disabled {
opacity: 0.5;
}
.roomstamp-check {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--ink);
cursor: pointer;
}
/* Zeile mit Feld/Häkchen links und Ausrichtungs-Umschalter rechts. */
.roomstamp-row {
display: flex;
align-items: flex-end;
gap: 8px;
}
.roomstamp-row .roomstamp-field {
flex: 1;
}
.roomstamp-row .roomstamp-check {
flex: 1;
}
/* Drei-Wege-Umschalter (links/mitte/rechts) je Stempelzeile. */
.roomstamp-align {
display: inline-flex;
border: 1px solid var(--border);
border-radius: 6px;
overflow: hidden;
flex: none;
}
.roomstamp-align-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 28px;
border: none;
border-left: 1px solid var(--border);
background: transparent;
color: var(--ink-2);
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.roomstamp-align-btn:first-child {
border-left: none;
}
.roomstamp-align-btn:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--accent);
}
.roomstamp-align-btn.active {
background: var(--accent);
color: #fff;
}
.roomstamp-align-btn:disabled {
opacity: 0.4;
cursor: default;
}
.roomstamp-align .material-symbols-outlined {
font-size: 18px;
}
/* Flächenbilanz-Panel (SIA 416): Kopf, Raumliste, Summentabelle, Export. */
.balance-panel {
padding: 8px 10px;
font-size: 12px;
color: var(--ink);
overflow: auto;
}
.balance-empty {
color: var(--ink-2);
padding: 12px 4px;
font-size: 12px;
}
.balance-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.balance-title {
font-weight: 600;
}
.balance-export {
background: var(--accent);
color: #ffffff;
border: none;
border-radius: 4px;
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
}
.balance-export:hover {
filter: brightness(1.08);
}
.balance-section-label {
margin: 10px 0 4px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--ink-2);
}
.balance-table {
display: flex;
flex-direction: column;
gap: 1px;
}
.balance-row {
display: grid;
grid-template-columns: 1fr auto auto;
gap: 8px;
align-items: baseline;
padding: 3px 4px;
border-bottom: 1px solid var(--border);
}
.balance-row.aggregate {
font-weight: 600;
background: var(--panel-2);
}
.balance-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.balance-cat,
.balance-count {
color: var(--ink-2);
font-variant-numeric: tabular-nums;
}
.balance-area {
font-variant-numeric: tabular-nums;
text-align: right;
}
/* ── Fenster-Drag-Region (randloses Electron-Fenster) ────────────────────────
Ohne native Titelleiste (frame:false) muss die Oberleiste als Drag-Handle
dienen; alle interaktiven Elemente darin bekommen `no-drag` zurück, sonst
liesse sich nichts mehr anklicken. Wirkt nur unter Electron/Chromium
(WebKit/Firefox ignorieren `-webkit-app-region` ergebnislos, harmlos). */
.topbar {
-webkit-app-region: drag;
}
.topbar button,
.topbar select,
.topbar input,
.topbar a,
.topbar [role="button"],
.topbar [role="listbox"] {
-webkit-app-region: no-drag;
}
/* ── Custom Fenstersteuerung (WindowControls.tsx) — eigener Look statt OS-
Fensterknöpfen, ganz rechts in der Oberleiste (nur sichtbar unter
Electron/randlosem Fenster). */
.win-controls {
display: inline-flex;
align-items: center;
gap: 2px;
margin-left: 6px;
flex: 0 0 auto;
}
.win-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
padding: 0;
border: none;
background: transparent;
color: var(--muted);
border-radius: 6px;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.win-btn:hover {
background: var(--accent-dim);
color: var(--ink);
}
.win-btn-close:hover {
background: #e5484d;
color: #fff;
}