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