Files
DOSSIER-STANDALONE/src/styles.css
T
karim 3b79e49cc6 App.tsx entschlackt: Kontextmenüs nach src/menus/, Views nach src/views/
Reiner Umzug (verhaltensgleich, byte-identische Funktionskörper): Kontextmenü-
Builder (buildMenuItems + layer/level/plan/viewport-Menüs) nach
src/menus/contextMenu.tsx; InlineEditor sowie Content/LevelPlanView/
SectionPlanView (der View-Router der Hauptansicht) nach src/views/. App.tsx
7130 → 5917 Zeilen. Setzt die in CONVENTIONS.md/state-architecture.md
vorgesehene, bisher nie umgesetzte Struktur endlich um (STATUS.md §4.3).

Dazu ein unabhängiger CSS-Fix: .plan-selected (Auswahl-Hervorhebung im 2D-Plan)
fehlte die fill-opacity, die zwei Regeln weiter unten bei .plan-marquee/
.tool-preview-fill schon gesetzt ist. Im Dunkel-Theme ist --accent-dim
transparent (zufällig unauffällig), im neuen Standard-Hell-Theme aber fast
deckend weiss — dadurch verdeckte die Auswahl die Schraffur der gewählten
Wand/Decke/des Raums vollständig.
2026-07-21 14:47:40 +02:00

6843 lines
157 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 + Neutralgrau), inkl.
Dark Mode. Der linke Navigator (Zeichnungsebenen + Ebenen-Baum) übernimmt
das DOSSIER-Look: aktive Zeile als neutrale Pille, nicht-aktive gedimmt,
ausgeblendete grau; Augen-Schalter, Farbtupfer, eingerückte Kinder. */
/* Hell (Standard) ist der feste Startzustand; Dunkel ist Opt-in über
`[data-theme="dark"]`, gesetzt/persistiert per Umschalter in den
Einstellungen (src/theme/themeMode.ts) — unabhängig von OS/Browser-
Präferenz, damit die App nicht je nach Umgebung unterschiedlich startet. */
:root {
color-scheme: light;
--bg: #e0dbd4;
--panel: #e0dbd4;
--panel-2: #d4cfc8;
--input: #ece8e2;
--line: #d4cfc8;
--border: #c8c2ba;
/* --sheet bleibt bewusst hell (#f0f0f0): das Zeichenblatt ist immer
„Papier", unabhängig vom UI-Theme (Wunsch des Nutzers). */
--sheet: #faf6ee;
--ink: #1a1a18;
--ink-2: #555550;
--muted: #8a8580;
--label: #1a1a18;
/* Akzent NEUTRAL (kein Petrol-Grün, Nutzer-Vorgabe). */
--accent: #4a453e;
--accent-light: #6b655c;
--accent-dim: #f5f2ec;
--accent-border: rgba(58, 53, 46, 0.2);
--active: #3f3a33;
--active-light: #5c564d;
--active-dim: rgba(58, 53, 46, 0.12);
/* Helle, randlose Feldfläche (Dropdowns/Pillen der Oberleiste, Mockup-
Konvention) — bewusst heller als --panel/--input, damit Felder als
eigene Fläche erkennbar sind, OHNE Rahmen zu brauchen. */
--field-bg: #f5f2ec;
--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);
--font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
sans-serif;
--font-mono: "DM Mono", ui-monospace, "SF Mono", monospace;
}
/* Dunkel-Palette als Opt-in (Einstellungen → Darstellung → Dunkelmodus). */
:root[data-theme="dark"] {
color-scheme: dark;
/* Gestufte Elevations-Palette: Tiefster Grund hinten, Flächen werden nach
vorn schrittweise heller — leicht kühl-neutral.
--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) */
--bg: #0e0e0e;
--panel: #1d1d1d;
--panel-2: #262626;
--input: #0a0a0a;
--line: #2b2b2b;
--border: #383838;
--sheet: #faf6ee;
--ink: #ededed;
--ink-2: #a8a8a8;
--muted: #6e6e6e;
--label: #c8c8c8;
--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);
/* Helle, randlose Feldfläche (Dropdowns/Pillen der Oberleiste) — angehoben
gegenüber --panel-2, analog zur hellen Palette. */
--field-bg: #2a2a2a;
--wall: #c8ccd2;
--swing: #7e8794;
--danger: #c85050;
--danger-light: #d86060;
/* Tiefere Schatten für klare Schichtung auf dunklem Grund. */
--shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5);
--shadow-2: 0 3px 12px rgba(0, 0, 0, 0.55);
--shadow-3: 0 12px 34px rgba(0, 0, 0, 0.62);
--overlay: rgba(0, 0, 0, 0.62);
}
* {
box-sizing: border-box;
}
html,
body,
#root {
height: 100%;
margin: 0;
/* 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: 8px;
padding: 0 10px;
/* EINE Zeile für Chrome + Ansichts-Controls (Mockup) — hoch genug für die
24px-Pillen der View-/Zoom-/Text-Steuerung. */
height: 40px;
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;
/* Die Leiste selbst scrollt NICHT — nur der mittlere View-Controls-Block
(.ribbon-views) scrollt bei schmalen Fenstern. So bleiben Marke, Menü und
die rechte Gruppe (Datei-Aktionen + Projekt) IMMER sichtbar. */
overflow: visible;
white-space: nowrap;
}
/* Tauri/macOS: native Ampeln (titleBarStyle "Overlay") liegen oben links über
dem Inhalt → Zeile links einrücken, damit `dossier.` frei bleibt (~3 Ampeln +
Rand). */
.topbar.topbar-native-inset {
padding-left: 88px;
}
/* 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) — flache Pille ohne
Rahmen (Mockup-Konvention): gefüllte Fläche in --field-bg, kein Rand. */
.tb-select {
border: none;
background: var(--field-bg);
color: var(--ink);
font-family: inherit;
font-size: 11px;
font-weight: 600;
padding: 3px 7px;
border-radius: 6px;
cursor: pointer;
}
.tb-select:hover {
background: var(--accent-dim);
}
.tb-select:disabled {
opacity: 0.45;
cursor: default;
}
.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): flache, randlose Pille in
--field-bg, Text/Rand-Akzent bei Hover (Mockup-Konvention — kein „stets
dunkler" Sonderlook mehr). */
.tb-btns {
display: inline-flex;
gap: 6px;
}
.tb-btn {
display: inline-flex;
align-items: center;
border: none;
background: var(--field-bg);
color: var(--ink-2);
font-family: inherit;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 3px 10px;
border-radius: 8px;
cursor: pointer;
outline: none;
transition: background-color 0.14s, color 0.14s, box-shadow 0.14s;
}
.tb-btn:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--ink);
}
.tb-btn:focus-visible {
box-shadow: 0 0 0 2px var(--accent-border);
}
.tb-btn:disabled {
opacity: 0.45;
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) — randlos, --field-bg. */
.tb-toggle {
border: none;
background: var(--field-bg);
color: var(--ink-2);
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;
}
.tb-toggle:hover {
background: var(--accent-dim);
color: var(--ink);
}
.tb-toggle.active {
background: var(--accent);
color: #fff;
}
/* ── DOSSIER-Grundprimitive: Segmentpille · Icon-Knopf · Zoom-Cluster ──────── */
/* Segmentpille (3er/4er): randloser Cluster OHNE interne Trennstriche
(Mockup-Konvention) — die Zellen sitzen als kleine Pillen auf --field-bg,
aktive Zelle in accent-dim. Gleiche 24px-Höhe wie die Dropdowns. */
.tb-seg {
display: inline-flex;
align-items: stretch;
height: 24px;
box-sizing: border-box;
border: none;
border-radius: 8px;
overflow: hidden;
background: var(--field-bg);
padding: 2px;
gap: 1px;
flex: 0 0 auto;
}
/* Akzent-Rand, wenn ein Text-Ziel selektiert ist (Auswahl-Bewusstsein). */
.tb-seg-accent {
box-shadow: inset 0 0 0 1px var(--accent);
}
.tb-seg-cell {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 1 1 0;
min-width: 22px;
padding: 0 6px;
border: none;
border-radius: 5px;
background: transparent;
color: var(--ink-2);
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.tb-seg-cell:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--ink);
}
.tb-seg-cell.active {
background: var(--accent-dim);
color: var(--ink);
}
.tb-seg-cell:disabled {
opacity: 0.4;
cursor: default;
}
/* Quadratischer Icon-Knopf (BarButton) — randlos, --field-bg. */
.tb-iconbtn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
box-sizing: border-box;
padding: 0;
border: none;
border-radius: 8px;
background: var(--field-bg);
color: var(--ink-2);
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.tb-iconbtn:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--ink);
}
.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;
}
/* Mockup: Massstab/Zoom als zwei kompakte, nebeneinanderliegende Readout-Pillen
(1:100 · 142%) statt einer grossen 2-zeiligen Stat-Kachel. */
.tb-stat {
display: inline-flex;
align-items: center;
gap: 6px;
height: 24px;
padding: 0 9px;
box-sizing: border-box;
border: none;
border-radius: 6px;
background: var(--field-bg);
box-shadow: 0 0 0 1.5px transparent;
transition: box-shadow 0.25s ease;
}
.tb-stat-scale {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 700;
color: var(--ink);
font-variant-numeric: tabular-nums;
}
.tb-stat-zoom {
font-family: var(--font-mono);
font-size: 11px;
color: var(--ink-2);
font-variant-numeric: tabular-nums;
}
/* „am Massstab" (Live == gewählter Massstab): dezenter Akzent-Ring. */
.tb-stat-atscale {
box-shadow: 0 0 0 1.5px var(--accent-border);
}
.tb-zoomcluster-right {
display: inline-flex;
flex-direction: row;
align-items: center;
gap: 6px;
}
/* ── Text-Gruppe (Mockup: EINE kompakte inline Zeile) ─────────────────────── */
.tb-textgroup {
flex-direction: row;
align-items: center;
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: none;
border-radius: 8px;
overflow: hidden;
background: var(--field-bg);
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: var(--ink-2);
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.tb-lineheight-btn:hover {
background: var(--accent-dim);
color: var(--ink);
}
.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: var(--ink);
border-left: 1px solid var(--border);
border-right: 1px solid var(--border);
}
/* 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 {
/* Als <button> gerendert (öffnet den Über-Dialog) — Button-Chrome zurücksetzen,
Optik bleibt exakt wie die frühere <span>. */
appearance: none;
background: none;
border: none;
padding: 0;
margin: 0;
cursor: pointer;
font-weight: 700;
font-size: 12px;
letter-spacing: -0.01em;
text-transform: lowercase;
color: var(--ink);
white-space: nowrap;
}
.brand-word:hover .brand-dot {
filter: brightness(1.25);
}
.brand-dot {
/* Petrolgrün wie im DOSSIER-Rhino-Plugin (fix, unabhängig vom Akzent). */
color: #0f766e;
}
/* Menüleiste (Bearbeiten/Ansicht/Fenster, AppMenuBar) — sitzt neben der
Wortmarke. Flache Text-Trigger statt der sonst üblichen Dropdown-Pille
(.tb-dd-trigger), damit sich die Leiste wie eine klassische Menüzeile
liest statt wie eine Reihe Wert-Auswahlfelder. */
.tb-menubar {
display: flex;
align-items: center;
gap: 2px;
flex: 0 0 auto;
}
.tb-menubar-item.tb-dd-trigger {
border: none;
background: none;
color: var(--ink-dim, #8a8a8a);
font-weight: 500;
padding: 3px 6px;
border-radius: 6px;
}
.tb-menubar-item.tb-dd-trigger:hover:not(:disabled) {
background-color: var(--accent-dim);
color: var(--accent);
}
.tb-menubar-item.tb-dd-trigger.active {
background-color: var(--accent-dim);
color: var(--accent);
box-shadow: none;
}
.tb-menubar-item .tb-dd-chevron {
display: none;
}
/* Export-Sammelmenü: Liste im Popover (fixed) über der Oberleiste. */
.tb-popover.tb-menu {
padding: 4px;
min-width: 200px;
display: flex;
flex-direction: column;
gap: 1px;
}
/* „Über"-Dialog (In-App, dunkel). */
.about-overlay {
position: fixed;
inset: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.45);
}
.about-dialog {
min-width: 320px;
max-width: 420px;
padding: 22px 24px;
border-radius: 10px;
background: var(--panel, #23262b);
border: 1px solid var(--border, #3a3f47);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
color: var(--ink, #e6e8ea);
/* Der Dialog ist ein DOM-Kind der `.topbar` (white-space: nowrap) — ohne
dieses Reset liefe der mehrzeilige Beschreibungstext einzeilig aus dem
Dialog heraus. Hier wieder normalen Zeilenumbruch erzwingen. */
white-space: normal;
}
.about-brand {
font-weight: 700;
font-size: 20px;
text-transform: lowercase;
letter-spacing: -0.01em;
}
.about-version {
margin-top: 2px;
font-size: 11px;
color: var(--muted, #9aa0a6);
}
.about-desc {
margin: 14px 0 0;
font-size: 12px;
line-height: 1.5;
color: var(--label, #c4c8cd);
}
.about-meta {
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 2px;
font-size: 11px;
line-height: 1.5;
color: var(--muted, #9aa0a6);
}
.about-sec-label {
margin-top: 16px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--muted, #9aa0a6);
}
.about-licenses {
margin: 6px 0 0;
padding-left: 16px;
font-size: 11px;
line-height: 1.6;
color: var(--label, #c4c8cd);
}
.about-close {
appearance: none;
margin-top: 18px;
padding: 6px 14px;
border-radius: 6px;
border: 1px solid var(--border, #3a3f47);
background: var(--input, #2b2f36);
color: var(--ink, #e6e8ea);
cursor: pointer;
font-size: 12px;
}
.about-close:hover {
background: var(--accent-dim, #33383f);
}
.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;
}
/* ── Ansichts-Umschalter + Blickwinkel (Mockup: viewtoggle + V/H/L/R/O) ────── */
.tb-viewtoggle,
.tb-views {
display: inline-flex;
align-items: center;
gap: 2px;
flex: 0 0 auto;
}
.tb-vt-btn,
.tb-view-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 26px;
height: 24px;
padding: 0;
border: none;
background: transparent;
color: var(--ink-2);
border-radius: 6px;
cursor: pointer;
flex: 0 0 auto;
transition: background 0.14s, color 0.14s;
}
.tb-view-btn {
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.02em;
}
.tb-vt-btn svg {
width: 15px;
height: 15px;
}
.tb-vt-btn:hover:not(:disabled),
.tb-view-btn:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--ink);
}
.tb-vt-btn.active,
.tb-view-btn.active {
background: var(--accent-dim);
color: var(--ink);
}
.tb-vt-btn:disabled,
.tb-view-btn:disabled {
opacity: 0.4;
cursor: default;
}
/* ── Ansichten als EINE kompakte Icon-Zeile (Mockup) ──────────────────────── */
.view-grid {
display: inline-flex;
flex-direction: row;
align-items: center;
gap: 2px;
flex: 0 0 auto;
}
.view-grid-4 {
/* keine Sonderbreite mehr — alle Ansichts-Icons in EINER Zeile. */
}
.view-icon {
border: none;
background: transparent;
color: var(--ink-2);
display: inline-flex;
align-items: center;
justify-content: center;
width: 26px;
height: 24px;
padding: 0;
border-radius: 6px;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.view-icon:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--ink);
}
.view-icon.active {
background: var(--accent-dim);
color: var(--ink);
}
.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;
}
/* Iso-Split-Button (IsoMenu): Haupt-Icon + schmaler Pfeil-Trigger, zusammen
exakt so breit wie ein normales .view-icon (31px) — behält die Grid-Spalten-
breite des Ansichts-Rasters bei. */
.view-icon-split {
display: inline-flex;
}
.view-icon-split-main {
width: 24px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.view-icon-caret {
width: 12px;
height: 24px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--muted);
border-radius: 0 6px 6px 0;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.view-icon-caret:hover:not(:disabled) {
background: var(--accent-dim);
color: var(--ink);
}
.view-icon-caret.active {
background: var(--accent-dim);
color: var(--ink);
}
.view-icon-caret:disabled {
opacity: 0.4;
cursor: default;
}
/* 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;
}
/* Mockup: EINE kompakte Zeile — die früher gestapelten Gruppen (Kombinationen,
Detailgrad/Darstellung) laufen jetzt inline nebeneinander. */
.tb-group.tb-stack {
flex-direction: row;
align-items: center;
gap: 6px;
}
.tb-field-row {
align-items: center;
gap: 5px;
}
/* Feld-Labels sehr dezent halten (Mockup zeigt kompakte Pillen ohne grosse
Beschriftungen). */
.tb-field .tb-label {
font-size: 9.5px;
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* 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(--line);
}
/* 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(--line);
border-radius: 999px;
overflow: hidden;
background: var(--field-bg);
}
.sb-renderer-btn {
display: inline-flex;
align-items: center;
border: none;
border-left: 1px solid var(--line);
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(--active-dim);
color: var(--active-light);
}
.sb-renderer-btn.active {
background: var(--active-dim);
color: var(--active-light);
}
.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(--line);
border-radius: 999px;
background: transparent;
color: var(--muted);
font-family: inherit;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 2px 10px;
cursor: pointer;
transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.sb-toggle:hover:not(:disabled) {
background: var(--active-dim);
color: var(--active-light);
border-color: var(--line);
}
.sb-toggle.active {
background: var(--active-dim);
border-color: transparent;
color: var(--active-light);
}
.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(--line);
border-radius: 999px;
overflow: hidden;
background: var(--field-bg);
}
.sb-seg.disabled {
opacity: 0.4;
}
.sb-seg-btn {
display: inline-flex;
align-items: center;
border: none;
border-left: 1px solid var(--line);
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(--active-dim);
color: var(--active-light);
}
.sb-seg-btn.active {
background: var(--active-dim);
color: var(--active-light);
}
.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);
border: 1px solid var(--line);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
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;
}
/* Einheitlicher „+"-Knopf für neue Ebene / neue Zeichnungsebene: rundes
Kreis-Icon (kein Pill), dezent, accent-dim beim Überfahren. */
.nav-add,
.level-cat-add {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 20px;
height: 20px;
padding: 0;
border: none;
border-radius: 50%;
background: var(--field-bg);
color: var(--ink-2);
font-family: inherit;
font-size: 15px;
line-height: 1;
font-weight: 500;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.nav-add:hover,
.level-cat-add:hover {
background: var(--accent-dim);
color: var(--ink);
}
/* Zahnrad in der Panel-Kopfzeile (Zeichnungsebenen/Ebenen) → Einstellungen.
Dezent, rund, wie der „+"-Knopf daneben. */
.nav-settings {
flex: 0 0 auto;
width: 20px;
height: 20px;
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;
}
.nav-settings:hover {
background: var(--accent-dim);
color: var(--ink);
}
.nav-settings .material-symbols-outlined {
font-size: 15px;
}
/* Sichtbarkeits-Umschalter im Panel-Körper (Ebenen/Zeichnungsebenen): kleine
„Sichtbarkeit"-Beschriftung über dem Modus-Dropdown. */
.panel-vis {
display: flex;
flex-direction: column;
gap: 5px;
padding: 8px 10px 12px;
}
.panel-vis-label {
font-size: 9.5px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
.panel-vis .display-mode {
max-width: none;
width: 100%;
}
.nav-list {
display: flex;
flex-direction: column;
}
/* ── Zeichnungsebenen: einklappbare Kategorien (Geschosse/Schnitte/
Ansichten/2D-Zeichnungen) ──────────────────────────────────────────── */
.level-cat {
display: flex;
flex-direction: column;
}
/* Randlos, kein Kasten mehr (Mockup-Konvention) — reiner uppercase-Text wie
.field-label, Chevron/Plus bleiben (Funktion), nur ohne Fläche. */
.level-cat-head {
display: flex;
align-items: center;
gap: 6px;
min-height: 24px;
padding: 6px 10px 3px 6px;
background: transparent;
border-bottom: none;
cursor: pointer;
user-select: none;
border-radius: 6px;
}
.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;
}
/* Drag&Drop-Ziel: überfahrene Ordnerzeile (Layouts-Baum) hervorheben. */
.nav-row.drag-over {
background: var(--active-dim);
box-shadow: inset 0 0 0 1.5px var(--active-light);
border-bottom-color: transparent;
}
/* Wurzel-Ablagefläche (leerer Bereich der Baumliste) beim Ziehen markieren. */
.nav-list.drag-over-root {
box-shadow: inset 0 0 0 1.5px var(--active-light);
border-radius: 6px;
}
.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);
}
/* Footer-Bar des Ausschnitte-Panels: fixiert am Panel-Boden, zeigt die
Kenndaten des angewählten Ausschnitts (Massstab/Kombis/Overrides + Rename). */
.viewsnap-footer {
margin-top: auto;
flex: 0 0 auto;
position: sticky;
bottom: 0;
padding: 6px 10px 8px;
border-top: 1px solid var(--line);
background: var(--panel-2);
}
.viewsnap-footer-name {
display: flex;
align-items: center;
gap: 4px;
margin-bottom: 5px;
}
.viewsnap-footer-title {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
font-weight: 600;
color: var(--active-light);
}
.viewsnap-footer-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 2px 8px;
margin: 0;
font-size: 11px;
}
.viewsnap-footer-grid dt {
color: var(--muted);
white-space: nowrap;
}
.viewsnap-footer-grid dd {
margin: 0;
color: var(--label);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 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): identisch zur Zeichnungsebenen-Auswahl
(.nav-row.active) — helle Pille (active-dim) statt Akzentstreifen. */
.cat-row.active {
background: var(--active-dim);
border-radius: 999px;
border-bottom-color: transparent;
}
.cat-row.active .cat-name {
color: var(--active-light);
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;
}
/* Zahnrad — öffnet die Ebeneneinstellungen (natives Fenster unter Tauri, s.
native/layerSettingsWindow.ts). Nur bei Zeilen-Hover eingeblendet, damit
die Liste nicht überladen wirkt (anders als das Auge, das den Zustand
dauerhaft zeigt). */
.cat-settings {
flex: 0 0 auto;
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--muted);
cursor: pointer;
border-radius: 50%;
padding: 0;
opacity: 0;
transition: opacity 0.12s, background 0.12s, color 0.12s;
}
.cat-row:hover .cat-settings {
opacity: 1;
}
.cat-settings:hover {
background: rgba(0, 0, 0, 0.06);
color: var(--ink);
}
.cat-settings .material-symbols-outlined {
font-size: 15px;
}
/* ── 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. */
/* Geschoss-/Dokument-Tabs: keine Ordnerreiter mehr, sondern die gleiche helle
Pillen-Sprache wie Panels/Umschalter — flache Chips, aktiver Tab = helle
Füllung (field-bg) mit Akzent-Unterstrich statt gerahmter Reiter. */
.drawing-tabs {
display: flex;
align-items: center;
gap: 4px;
padding: 6px 10px;
border-bottom: 1px solid var(--line);
background: var(--panel);
overflow-x: auto;
scrollbar-width: thin;
}
.drawing-tab {
border: none;
background: transparent;
color: var(--muted);
font-family: inherit;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.01em;
padding: 5px 14px;
border-radius: 8px;
cursor: pointer;
white-space: nowrap;
transition: background 0.14s, color 0.14s, box-shadow 0.14s;
}
.drawing-tab:hover {
background: var(--accent-dim);
color: var(--ink);
}
.drawing-tab.active {
background: var(--active-dim);
color: var(--active-light);
}
.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.
`fill-opacity` erzwingt die Transparenz unabhängig vom Theme — im Hell-Theme
ist `--accent-dim` fast deckend weiss (Nutzung als UI-Hintergrundfarbe an
~70 anderen Stellen), würde ohne eigene Opacity die Schraffur der gewählten
Wand/Decke/des Raums vollständig verdecken (nur im Dunkel-Theme zufällig
unauffällig, da dort selbst schon rgba mit niedriger Alpha). Gleiches Muster
wie .plan-marquee/.tool-preview-fill weiter unten. */
.plan-svg .plan-selected {
fill: var(--accent-dim);
fill-opacity: 0.35;
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.5;
}
.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;
}
/* Cursor-HUD (Massband „L: 3.118m W: 60.000°"). Themen-abhängig: im Light-
Mode eine helle Papier-Pille mit dunkler Schrift, im Dark-Mode die dunkle
Pille wie die Dropdown-Trigger. Basis = Light; Dark unten überschrieben. */
.plan-svg .plan-hud-box {
fill: #fbf8f2;
fill-opacity: 0.96;
stroke: #cfc9c0;
stroke-width: 1;
}
.plan-svg .plan-hud-text {
fill: #2a2926;
font-family: var(--mono, ui-monospace, monospace);
font-weight: 600;
}
/* Aktives Tab-Feld im HUD: leicht abgesenkte Pille, dunklerer Text. */
.plan-svg .plan-hud-active {
fill: #000000;
fill-opacity: 0.07;
}
.plan-svg .plan-hud-text.active {
fill: #111110;
}
/* Gelockter (getippter) Wert: voll dunkel + fett — hebt sich vom Live-Grau ab. */
.plan-svg .plan-hud-text.locked {
fill: #000000;
font-weight: 700;
}
:root[data-theme="dark"] .plan-svg .plan-hud-box {
fill: #2c2c2c;
fill-opacity: 0.94;
stroke: #4a4a4a;
}
:root[data-theme="dark"] .plan-svg .plan-hud-text {
fill: #e8e8e8;
}
:root[data-theme="dark"] .plan-svg .plan-hud-active {
fill: #ffffff;
fill-opacity: 0.16;
}
:root[data-theme="dark"] .plan-svg .plan-hud-text.active,
:root[data-theme="dark"] .plan-svg .plan-hud-text.locked {
fill: #ffffff;
}
/* Lineale (oben + links, VW-Stil): helle Papier-Leisten, Tinte-Ticks,
gelber Cursor-Marker. */
/* .plan-ruler-overlay: eigenständiges Sibling-SVG des Lineals (s. PlanView.tsx
„screen-fix" Kommentar) — dieselben Regeln gelten daher für BEIDE Elternteile. */
.plan-svg .plan-ruler-strip,
.plan-ruler-overlay .plan-ruler-strip {
fill: #f7f6f3;
fill-opacity: 0.96;
}
.plan-svg .plan-ruler-border,
.plan-ruler-overlay .plan-ruler-border {
stroke: #b9b5ae;
stroke-width: 1;
vector-effect: non-scaling-stroke;
}
.plan-svg .plan-ruler-tick,
.plan-ruler-overlay .plan-ruler-tick {
stroke: #6d6a64;
stroke-width: 1;
vector-effect: non-scaling-stroke;
}
.plan-svg .plan-ruler-text,
.plan-ruler-overlay .plan-ruler-text {
fill: #3c3a36;
font-family: var(--mono, ui-monospace, monospace);
font-weight: 500;
}
.plan-svg .plan-ruler-cursor,
.plan-ruler-overlay .plan-ruler-cursor {
fill: #e8c33c;
fill-opacity: 0.9;
}
.plan-svg .plan-ruler-corner,
.plan-ruler-overlay .plan-ruler-corner {
fill: #f0efec;
stroke: #b9b5ae;
stroke-width: 1;
vector-effect: non-scaling-stroke;
}
/* Weicher Winkel-Snap: gestrichelte Führungslinie (dezent) + Winkel-Badge in
derselben dunklen Dropdown-Pille wie das Cursor-HUD. */
.plan-svg .plan-angle-guide-line {
stroke: #8a8a8a;
stroke-width: 1;
stroke-dasharray: 5 4;
opacity: 0.8;
}
/* Horizontale 0°-Referenz des Winkelbogens (dezentes Lachsrot, VW-Anlehnung). */
.plan-svg .plan-angle-ref-line {
stroke: #cf8d7f;
stroke-width: 1;
stroke-dasharray: 7 5;
opacity: 0.8;
}
/* Gestrichelter Winkelbogen von der Referenz zur Strahlrichtung. */
.plan-svg .plan-angle-arc {
fill: none;
stroke: #8a8a8a;
stroke-width: 1;
stroke-dasharray: 5 4;
opacity: 0.8;
}
.plan-svg .plan-angle-badge-box {
fill: #2c2c2c;
fill-opacity: 0.94;
stroke: #4a4a4a;
stroke-width: 1;
}
.plan-svg .plan-angle-badge-text {
fill: #e8e8e8;
font-family: var(--mono, ui-monospace, monospace);
font-weight: 600;
}
/* ── 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;
}
/* Eigenes natives Fenster (native/resourceWindow.ts) statt schwebendes
Overlay: füllt die ganze Fensterfläche, kein Rahmen/Schatten/rundes Eck
nötig (das IST schon das ganze Fenster), kein Move-Cursor auf der
Kopfzeile (das OS-Fenster übernimmt Verschieben/Grössenändern). */
.res-window.res-window-embedded {
position: static;
inset: 0;
width: 100%;
height: 100%;
border: none;
border-radius: 0;
box-shadow: none;
}
.res-window.res-window-embedded .res-head {
cursor: default;
/* Platz für die macOS-Ampeln (titleBarStyle "overlay", s. windowChrome.ts) —
dieselbe Einrückung wie alle übrigen Fenster-Köpfe (76px). */
padding-left: 88px;
}
.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: 40px;
flex: 0 0 auto;
border-bottom: 1px solid var(--line);
background: var(--panel);
}
.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 — Pille wie .tb-dd-trigger statt Ordnerlasche (Mockup-
Konvention): accent-dim-Hintergrund im aktiven Zustand, sonst transparent,
kein Rahmen/Verschmelzen mit dem Rand. */
.res-tabs {
display: flex;
gap: 2px;
padding: 10px 16px 0;
flex: 0 0 auto;
border-bottom: 1px solid var(--line);
}
.res-tab-btn {
border: none;
background: transparent;
color: var(--muted);
font-family: inherit;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.02em;
padding: 7px 14px;
border-radius: 7px;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.res-tab-btn:hover {
color: var(--ink-2);
}
.res-tab-btn.active {
background: var(--accent-dim);
color: var(--ink);
}
/* 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: flex;
align-items: stretch;
min-height: 320px;
}
/* Seitenleiste — nur noch eine Trennlinie (border-right), keine eigene Box
mit Rahmen/Radius/Hintergrund (Mockup-Konvention: liest sich als EIN
Fenster statt Karte-in-Karte). */
.res-md-list {
display: flex;
flex-direction: column;
gap: 2px;
width: 240px;
flex: 0 0 auto;
padding: 0 12px 0 0;
border-right: 1px solid var(--line);
max-height: 62vh;
overflow-y: auto;
}
.res-md-row {
display: flex;
align-items: center;
gap: 9px;
width: 100%;
padding: 6px 8px;
border: none;
border-radius: 7px;
background: transparent;
color: var(--ink-2);
font-family: inherit;
font-size: 12px;
text-align: left;
cursor: pointer;
transition: background 0.12s, color 0.12s;
}
.res-md-row:hover {
background: var(--accent-dim);
color: var(--ink);
}
.res-md-row.active {
background: var(--accent-dim);
color: var(--ink);
font-weight: 600;
}
.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) — keine eigene Box mehr, sitzt direkt auf der
Panel-Fläche (Mockup-Konvention). */
.res-md-detail {
flex: 1 1 auto;
min-width: 0;
min-height: 300px;
padding: 0 0 0 16px;
}
.res-md-detail-inner {
display: flex;
flex-direction: column;
gap: 14px;
padding: 2px 0 4px;
}
.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: none;
border-radius: 10px;
background: var(--field-bg);
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) ──────────────────────────────────────────────── */
/* Tab-Leiste: Icon-Pillen (Mockup-Konvention) statt Ordnerlaschen — aktive
Pille in accent-dim, keine Verschmelzung mit dem Körper, kein eigener
Tray-Hintergrund. */
.tabstrip {
display: flex;
gap: 2px;
flex: 0 0 auto;
padding: 6px 8px 5px;
border-bottom: 1px solid var(--line);
background: var(--panel);
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
}
.tabstrip::-webkit-scrollbar {
display: none;
}
.tab {
flex: 0 0 auto;
width: 30px;
height: 26px;
display: flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--ink-2);
font-family: inherit;
padding: 0;
border-radius: 7px;
cursor: pointer;
transition: background 0.14s, color 0.14s;
}
.tab svg {
flex: 0 0 auto;
}
.tab:hover {
color: var(--ink);
}
.tab.active {
background: var(--accent-dim);
color: var(--ink);
}
/* ── 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: var(--ink);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Aktions-Slot rechts in der Panel-Kopfzeile (headerActions + Darstellungs-
modus) — bündelt panel-eigene Aktionen in DER EINEN Kopfzeile. */
.panel-head-actions {
display: inline-flex;
align-items: center;
gap: 6px;
flex: 0 0 auto;
}
.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;
/* Randlose, helle Feldfläche (Mockup-Konvention) — das geöffnete Popover
(.ctx-menu) bleibt eine eigene, davon unabhängige dunkle Schwebe-Fläche. */
border: none;
background: var(--field-bg);
color: var(--ink);
font-family: inherit;
font-size: 11px;
font-weight: 600;
padding: 3px 8px 3px 8px;
border-radius: 8px;
cursor: pointer;
outline: none;
transition: background-color 0.14s, color 0.14s, box-shadow 0.14s;
}
.tb-dd-trigger:hover:not(:disabled) {
background-color: var(--accent-dim);
}
.tb-dd-trigger.active,
.tb-dd-trigger:focus-visible {
box-shadow: 0 0 0 2px var(--accent-border);
}
.tb-dd-trigger:disabled {
opacity: 0.45;
cursor: default;
}
.tb-dd-trigger-label {
flex: 1 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: left;
}
.tb-dd-chevron {
flex: 0 0 auto;
color: var(--muted);
}
.tb-dd-trigger:hover:not(:disabled) .tb-dd-chevron {
color: var(--ink);
}
/* 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 {
/* Theme-abhängig: helle Fläche im Light-Modus, dunkle im Dark-Modus
(folgt der App-Palette statt fix dunkel zu sein). */
--ctx-surface: var(--panel-2);
--ctx-border: var(--border);
--ctx-text: var(--ink);
--ctx-muted: var(--muted);
--ctx-hover: var(--accent-dim);
--ctx-hover-text: var(--ink);
--ctx-danger: var(--danger);
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: var(--shadow-3);
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, folgt der App-Palette (hell im Light-
Modus). 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: var(--panel-2);
--ctx-border: var(--border);
--ctx-text: var(--ink);
--ctx-muted: var(--muted);
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: var(--shadow-3);
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: randlos, --field-bg (Mockup-Konvention) —
das ist der Standard-Look (natives Ebeneneinstellungen-Fenster u. a.).
Der ältere Canvas-Popover (.ctx-editor, InlineEditor in App.tsx) bekommt
weiter unten seine eigene dunkle Schwebe-Optik zurück (gleiche Familie
wie .ctx-menu). */
.editor-control input[type="text"],
.editor-control input[type="number"] {
width: 100%;
padding: 5px 8px;
background: var(--field-bg);
border: none;
border-radius: 7px;
color: var(--ink);
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;
box-shadow: 0 0 0 2px var(--accent-border);
}
/* Auswahlfelder (z. B. Schraffur) wie die Text-/Zahlenfelder gestaltet. */
.editor-control select {
width: 100%;
padding: 5px 8px;
background: var(--field-bg);
border: none;
border-radius: 7px;
color: var(--ink);
font-family: inherit;
font-size: 13px;
}
.editor-control select:focus {
outline: none;
box-shadow: 0 0 0 2px var(--accent-border);
}
/* 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: none;
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);
}
/* ── Werkzeug-Palette ─────────────────────────────────────────────────────────
Einheitlicher Knopf-Look für Werkzeuge UND Befehle (`.tp-item`), zwei
Darstellungen: Liste (Icon + Name als Zeilen) oder Symbole (kompaktes
Icon-Raster). Umschalter oben (`.tp-viewtoggle`), Rubriken 2D · 3D ·
Bearbeiten (`.tp-section`). */
.tools-panel {
display: flex;
flex-direction: column;
gap: 10px;
padding: 8px;
overflow-y: auto;
height: 100%;
min-height: 0;
}
/* Anzeige-Umschalter (segmentiert). */
.tools-panel .tp-head {
position: sticky;
top: -8px;
z-index: 1;
margin: -8px -8px 0;
padding: 8px 8px 6px;
background: var(--panel, var(--bg));
border-bottom: 1px solid var(--line);
}
.tools-panel .tp-viewtoggle {
display: flex;
gap: 4px;
}
.tools-panel .tp-viewbtn {
flex: 1 1 0;
padding: 5px 8px;
border: none;
border-radius: 7px;
background: transparent;
color: var(--muted);
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: background 0.12s, color 0.12s;
}
.tools-panel .tp-viewbtn:hover {
background: var(--active-dim);
color: var(--active-light);
}
/* Aktiv = helle Pille (active-dim), ohne Akzentstreifen. */
.tools-panel .tp-viewbtn.active {
background: var(--active-dim);
color: var(--active-light);
}
/* Rubrik (2D / 3D / Bearbeiten). */
.tools-panel .tp-section {
display: flex;
flex-direction: column;
gap: 4px;
}
.tools-panel .tp-section-title {
font-size: 9.5px;
font-weight: 700;
letter-spacing: 0.09em;
text-transform: uppercase;
color: var(--muted);
padding: 0 4px;
}
/* Gemeinsamer Knopf. Basis = Listenzeile (Icon + Name). */
.tools-panel .tp-item {
display: flex;
align-items: center;
gap: 9px;
padding: 5px 8px;
border: 1px solid transparent;
border-radius: 7px;
background: transparent;
color: var(--label);
cursor: pointer;
font-size: 12px;
text-align: left;
transition: background 0.12s, color 0.12s;
}
.tools-panel .tp-item:hover:not(.disabled) {
background: var(--accent-dim);
color: var(--ink);
}
/* Aktiv = identisch zur Zeichnungsebenen-Auswahl (.nav-row.active): helle
Pille (active-dim) + active-light-Schrift, kein dunkler Akzent/Streifen. */
.tools-panel .tp-item.active {
background: var(--active-dim);
color: var(--active-light);
}
.tools-panel .tp-item.active .tp-label {
font-weight: 600;
}
.tools-panel .tp-item.disabled {
opacity: 0.4;
cursor: not-allowed;
}
.tools-panel .tp-item svg {
flex: 0 0 auto;
}
.tools-panel .tp-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Auswahl-Werkzeug: immer volle Zeile. */
.tools-panel .tp-select .tp-item {
width: 100%;
}
/* Listen-Darstellung: Knöpfe als volle Zeilen untereinander. */
.tools-panel .tp-body {
display: flex;
flex-direction: column;
gap: 1px;
}
/* Symbol-Darstellung: kompaktes Icon-Raster, Namen ausgeblendet. */
.tools-panel .tp-body.icons {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(30px, 1fr));
gap: 3px;
}
.tools-panel .tp-body.icons .tp-item {
justify-content: center;
aspect-ratio: 1 / 1;
padding: 0;
}
.tools-panel .tp-body.icons .tp-item svg {
width: 15px;
height: 15px;
}
.tools-panel .tp-body.icons .tp-label {
display: none;
}
/* ── 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-group {
margin-bottom: 6px;
}
.site-panel .site-group-head {
display: flex;
align-items: center;
gap: 6px;
padding: 3px 6px;
font-size: 11px;
font-weight: 600;
color: var(--ink-2);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.site-panel .site-group-name {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.site-panel .site-group-count {
flex: 0 0 auto;
color: var(--muted);
font-weight: 400;
}
.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-row-selected {
background: var(--accent-dim);
outline: 1px solid var(--accent);
}
.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;
}
/* „Typ bearbeiten"-Knopf (⚙) neben dem Öffnungstyp-Dropdown. */
.objinfo-typebtn {
margin-left: 4px;
width: 22px;
height: 22px;
padding: 0;
border: 1px solid var(--border, #444);
border-radius: 4px;
background: transparent;
color: var(--fg, inherit);
cursor: pointer;
font-size: 12px;
line-height: 1;
}
.objinfo-typebtn:hover {
background: var(--hover, rgba(127, 127, 127, 0.18));
}
/* Mess-Werkzeug: Live-Messwerte im Objekt-Info-Panel. */
.objinfo-measure {
margin: 2px 2px 8px;
}
.objinfo-measure-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 2px 10px;
align-items: baseline;
margin: 2px 0 4px;
}
.objinfo-measure-key {
color: var(--label);
font-size: 11px;
}
.objinfo-measure-val {
text-align: right;
font-variant-numeric: tabular-nums;
font-size: 12px;
}
.objinfo-measure-hint {
color: var(--label);
font-size: 10px;
opacity: 0.75;
}
/* Bezugspunkt-Zeile: Würfel (fix quadratisch) links, X/Y/Z-Spalte rechts. */
.objinfo-refrow {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 6px;
}
.objinfo-cube {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 3px;
/* Immer quadratisch, wächst NICHT mit der Panelbreite (Nutzer-Wunsch). */
flex: 0 0 auto;
width: 64px;
height: 64px;
aspect-ratio: 1 / 1;
padding: 3px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--input);
}
.objinfo-xyzcol {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1 1 auto;
min-width: 0;
}
.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;
}
.objinfo-input-narrow {
flex: 0 0 auto;
width: 48px;
min-width: 48px;
}
/* ── Element-Abschnitte IM Attribute-Panel dem .attr-Grid-Look angleichen ──────
Die aus ObjectInfo verschobenen Sektionen (Wand/Decke/Öffnung/Treppe/Raum)
nutzen weiter die `objinfo-*`-Klassen; hier bekommen sie — NUR innerhalb
`.attr-panel` — denselben sauberen Look wie das obere Attribut-Grid: Sektion-
Balken, Zeilentrenner, einheitliche Label-Spalte, linksbündig füllende Felder.
Das eigenständige ObjectInfo-Panel (Bezugspunkt/Masse) bleibt unberührt. */
.attr-panel .objinfo-section-label {
color: var(--label);
font-weight: 700;
text-transform: uppercase;
font-size: 10px;
letter-spacing: 0.05em;
margin: 8px 0 2px;
padding: 3px 6px;
background: var(--panel-2);
border-radius: 3px;
}
/* Sektion-Balken trennen bereits → die dünnen Extra-Striche entfallen. */
.attr-panel .objinfo-sep {
display: none;
}
/* Zeile: Label-Spalte | Wert/Control, mit dezentem Zeilentrenner (wie .attr-*). */
.attr-panel .objinfo-field {
display: flex;
align-items: center;
gap: 6px;
padding: 2px 2px 2px 2px;
border-bottom: 1px solid var(--line);
}
.attr-panel .objinfo-flabel {
flex: 0 0 104px;
width: 104px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 8px;
color: var(--label);
}
/* Werte/Controls füllen die Zelle linksbündig (statt rechtsbündig). */
.attr-panel .objinfo-fval {
flex: 1 1 auto;
text-align: left;
}
.attr-panel .objinfo-input {
text-align: left;
}
.attr-panel .objinfo-field .tb-dd-trigger {
flex: 1 1 auto;
min-width: 0;
justify-content: space-between;
}
/* Segment-Umschalter + verschachtelte Felder füllen ebenfalls die Wert-Spalte. */
.attr-panel .objinfo-segment,
.attr-panel .objinfo-subfield {
flex: 1 1 auto;
min-width: 0;
}
/* ── 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: auto;
max-width: 140px;
}
/* ── Attribute-Palette (AttributesPanel) ─────────────────────────────── */
.attr-panel {
overflow-y: auto;
height: 100%;
min-height: 0;
font-size: 12px;
}
.attr-empty {
color: var(--muted);
padding: 8px 4px;
text-align: center;
}
/* OCS-artiges Eigenschaften-Grid: EIN durchgehendes Zwei-Spalten-Grid über alle
Sektionen (Label | Wert), damit die Wertfelder über Stift/Füllung/Maße hinweg
in EINER Spalte bündig stehen. Sektion-Balken spannen beide Spalten. Zeilen mit
dezenten Trennlinien; Wertfelder füllen ihre Zelle linksbündig über die volle
Panel-Breite — einheitlicher Look für Texte/Zahlen/Dropdowns. */
.attr-section-label {
grid-column: 1 / -1;
color: var(--label);
font-weight: 700;
text-transform: uppercase;
font-size: 10px;
letter-spacing: 0.05em;
margin: 8px 0 2px;
padding: 3px 6px;
background: var(--panel-2);
border-radius: 3px;
}
.attr-section-label:first-child {
margin-top: 0;
}
.attr-grid {
display: grid;
grid-template-columns: minmax(80px, max-content) 1fr;
align-items: stretch;
gap: 0;
padding: 0;
}
.attr-key {
color: var(--label);
white-space: nowrap;
display: flex;
align-items: center;
padding: 3px 10px 3px 2px;
border-bottom: 1px solid var(--line);
}
.attr-val {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 6px;
min-width: 0;
padding: 2px 0;
border-bottom: 1px solid var(--line);
}
/* Wertfelder (Dropdown-Trigger, Zahlenfeld) füllen die Wert-Spalte — der
Dropdown wächst in die verfügbare Breite (kein fixes width mehr im TSX). */
.attr-val .tb-dd-trigger {
flex: 1 1 auto;
min-width: 0;
justify-content: space-between;
}
.attr-readonly {
color: var(--label);
opacity: 0.85;
padding-left: 6px;
}
.attr-num,
.attr-select {
width: 100%;
text-align: left;
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 = EINZIGES sichtbares Quadrat; das native Farb-Input liegt unsichtbar
(opacity 0) exakt darüber und öffnet beim Klick die native Palette — vorher
standen Swatch UND natives Input sichtbar nebeneinander ("zwei Farbfelder",
Nutzer-Report). Kein separates Hex-Textfeld mehr daneben. */
.attr-color-swatch-wrap {
position: relative;
display: inline-flex;
width: 16px;
height: 16px;
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"] {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
padding: 0;
border: none;
opacity: 0;
cursor: pointer;
}
.attr-color input[type="color"]:disabled {
cursor: not-allowed;
}
.attr-color-swatch-wrap:has(input:disabled) {
cursor: not-allowed;
opacity: 0.4;
}
.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;
}
/* Eigenes natives Fenster (native/settingsWindow.ts) statt Overlay-Dialog:
füllt die ganze Fensterfläche, kein Rahmen/Schatten/rundes Eck nötig. */
.imp-dialog.imp-dialog-embedded {
width: 100%;
max-height: 100vh;
height: 100vh;
border: none;
border-radius: 0;
box-shadow: none;
overflow-y: auto;
}
.imp-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
height: 40px;
flex: 0 0 auto;
border-bottom: 1px solid var(--line);
background: var(--panel);
}
.imp-dialog-embedded .imp-head {
/* Platz für die macOS-Ampeln (titleBarStyle "overlay", windowChrome.ts) —
dieselbe Einrückung wie alle übrigen Fenster-Köpfe (76px). */
padding-left: 88px;
}
.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;
}
.cim-geo-anchor {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 6px 8px;
border-radius: 6px;
background: var(--panel-2);
border: 1px solid var(--line);
font-size: 11px;
color: var(--ink-2);
}
/* Roh-Import-Hinweis (kein Bezug gesetzt) — mehrzeilig, kein Button daneben. */
.cim-geo-hint {
display: block;
line-height: 1.5;
font-family: inherit;
font-size: 12px;
color: var(--muted);
}
.imp-linklike {
background: none;
border: none;
padding: 0;
color: var(--accent);
cursor: pointer;
font-size: 11px;
text-decoration: underline;
flex: 0 0 auto;
}
.imp-linklike:hover {
opacity: 0.8;
}
.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);
}
/* Segment-Auswahl (A4/A3/Frei · Hoch/Quer) in den Layout-Erstell-Dialogen. */
.imp-seg {
display: inline-flex;
gap: 0;
border: 1px solid var(--border);
border-radius: 7px;
overflow: hidden;
}
.imp-seg-btn {
font-family: inherit;
font-size: 12px;
padding: 6px 14px;
border: none;
border-right: 1px solid var(--border);
background: var(--input);
color: var(--ink);
cursor: pointer;
}
.imp-seg-btn:last-child {
border-right: none;
}
.imp-seg-btn:hover {
background: var(--panel-2);
}
.imp-seg-btn.active {
background: var(--accent);
color: #fff;
}
/* Freie-Grösse-Felder (Breite × Höhe). */
.imp-size-row {
display: flex;
align-items: flex-end;
gap: 10px;
}
.imp-size-field {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 11px;
color: var(--muted);
}
.imp-size-x {
padding-bottom: 6px;
color: var(--muted);
}
.imp-input,
.imp-select {
width: 100%;
font-family: inherit;
font-size: 13px;
padding: 6px 9px;
border: none;
border-radius: 7px;
background: var(--field-bg);
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(--panel);
}
.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;
}
/* Riskante Aktion (z. B. Lock erzwingen): dezente rote Kontur statt Fläche —
warnt, ohne wie der primäre Bestätigen-Button auszusehen. */
.imp-btn.danger {
color: var(--ctx-danger);
border-color: var(--ctx-danger);
}
.imp-btn.danger:hover {
background: rgba(216, 96, 96, 0.14);
color: var(--ctx-danger);
border-color: var(--ctx-danger);
}
/* ── Fenster-/Tür-Einstellungsdialog (OpeningEditorDialog) ──────────────────
Drei Zonen (Sidebar · Panel · Ansicht) unter einer Stil-Leiste. Nutzt sonst
die imp-*-Klassen (Overlay/Head/Foot). */
.oed-dialog {
width: min(880px, 96vw);
max-height: 90vh;
display: flex;
flex-direction: column;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: var(--shadow-3);
overflow: hidden;
}
.oed-stylebar {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
border-bottom: 1px solid var(--line);
background: var(--bg);
}
.oed-stylebar-spacer {
flex: 1 1 auto;
}
.oed-style-label {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--ink-2);
}
.oed-style-select {
min-width: 160px;
}
.oed-main {
display: grid;
grid-template-columns: 150px 1fr 250px;
min-height: 320px;
flex: 1 1 auto;
overflow: hidden;
}
.oed-sidebar {
display: flex;
flex-direction: column;
padding: 8px;
gap: 2px;
border-right: 1px solid var(--line);
overflow-y: auto;
}
.oed-cat {
text-align: left;
font-family: inherit;
font-size: 13px;
padding: 7px 10px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--ink);
cursor: pointer;
}
.oed-cat:hover {
background: var(--panel-2);
}
.oed-cat.active {
background: var(--accent);
color: #fff;
}
.oed-panel {
display: flex;
flex-direction: column;
gap: 10px;
padding: 14px 16px;
overflow-y: auto;
}
.oed-preview {
display: flex;
flex-direction: column;
gap: 8px;
padding: 14px 16px;
border-left: 1px solid var(--line);
background: var(--bg);
}
.oed-preview-title {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--ink-2);
}
.oed-elev {
flex: 1 1 auto;
min-height: 0;
max-height: 320px;
}
.oed-field {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
font-size: 13px;
}
.oed-flabel {
color: var(--ink);
flex: 0 0 auto;
}
.oed-input,
.oed-select {
font-family: inherit;
font-size: 13px;
padding: 5px 8px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--input);
color: var(--ink);
min-width: 120px;
}
.oed-input {
width: 110px;
text-align: right;
}
.oed-select-sm {
min-width: 0;
width: 100%;
padding: 3px 6px;
font-size: 12px;
}
.oed-hint {
font-size: 12px;
color: var(--muted);
line-height: 1.5;
}
.oed-hint-warn {
color: var(--ctx-danger);
}
.oed-check {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--ink);
cursor: pointer;
}
.oed-btn {
font-family: inherit;
font-size: 12px;
padding: 6px 12px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--input);
color: var(--ink);
cursor: pointer;
}
.oed-btn:hover {
background: var(--panel-2);
}
.oed-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.oed-table th {
text-align: left;
font-weight: 600;
color: var(--muted);
padding: 4px 6px;
border-bottom: 1px solid var(--line);
}
.oed-table td {
padding: 3px 6px;
border-bottom: 1px solid var(--line);
}
.oed-td-num {
color: var(--muted);
width: 20px;
}
.oed-td-dash {
color: var(--muted);
}
.oed-rowbtn {
border: none;
background: transparent;
color: var(--muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
padding: 0 4px;
}
.oed-rowbtn:hover:not(:disabled) {
color: var(--ctx-danger);
}
.oed-rowbtn:disabled {
opacity: 0.35;
cursor: default;
}
.oed-rowactions {
display: flex;
gap: 8px;
margin-top: 4px;
}
/* ── 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: none;
border-radius: 7px;
background: var(--field-bg);
color: var(--ink);
width: 120px;
}
.settings-num-input:focus {
outline: none;
box-shadow: 0 0 0 2px 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-check-row {
justify-content: space-between;
cursor: default;
}
.cim-check-row select.cim-input {
width: auto;
min-width: 160px;
}
.cim-check-with-box {
display: flex;
align-items: center;
gap: 8px;
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;
/* Ganz links in der randlosen Titelleiste (macOS-Konvention). */
margin-right: 4px;
flex: 0 0 auto;
}
.win-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 26px;
height: 22px;
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;
}
/* ── Ribbon (2D · 3D · BIM · Ansichten) ────────────────────────────────────
Die Tab-REITER (`.ribbon-tabs`) sitzen in der TopBar-Zeile; hier folgt nur der
INHALT (`.ribbon`/`.ribbon-body`) des aktiven Tabs darunter. So gibt es genau
zwei Bänder: TopBar-Zeile (Chrome + Tabs) oben, Ribbon-Inhalt darunter. */
.ribbon {
display: flex;
flex-direction: column;
background: var(--panel);
border-bottom: 1px solid var(--line);
position: relative;
z-index: 1;
flex: 0 0 auto;
}
/* Tab-Reiter — leben in der TopBar-Zeile: volle Zeilenhöhe, Aktiv-Unterstrich
sitzt an der Unterkante (verbindet optisch mit dem Inhalt darunter). */
.ribbon-tabs {
display: flex;
align-self: stretch;
align-items: stretch;
gap: 2px;
}
.ribbon-tab {
appearance: none;
border: none;
background: transparent;
color: var(--muted);
font-size: 10.5px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
padding: 0 10px;
display: flex;
align-items: center;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: color 0.12s, border-color 0.12s;
}
/* Quick-Access-Leiste (OCS-Stil) — kleine Icon-Knöpfe in der TopBar-Zeile. */
.tb-qa {
display: inline-flex;
align-items: center;
gap: 1px;
flex: 0 0 auto;
}
.tb-qa-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
padding: 0;
border: none;
background: transparent;
border-radius: 4px;
color: var(--muted);
cursor: pointer;
transition: background 0.12s, color 0.12s;
}
.tb-qa-btn:hover:not(:disabled) {
background: var(--panel-2);
color: var(--label);
}
.tb-qa-btn.active {
background: var(--accent-dim);
color: var(--accent);
}
.tb-qa-btn:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.tb-qa-btn .tb-ico {
font-size: 16px;
}
.tb-qa-sep {
width: 1px;
height: 15px;
background: var(--line);
margin: 0 3px;
flex: 0 0 auto;
}
/* Kamera-Gruppe der Quick-Access-Leiste (zusätzlich zum „Ansichten"-Ribbon-Tab):
dieselben `.view-icon`/`.view-icon-split`-Bausteine, nur ohne eigenen Rand um
die Gruppe — die 22px-Höhe passt bereits in die 26px hohe Chrome-Zeile. */
.tb-qa-camera {
display: inline-flex;
align-items: center;
gap: 2px;
flex: 0 0 auto;
}
.ribbon-tab:hover {
color: var(--label);
}
.ribbon-tab.active {
color: var(--ink);
border-bottom-color: var(--accent-border, var(--accent));
}
.ribbon-body {
display: flex;
align-items: stretch;
gap: 0;
padding: 4px 8px;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: thin;
scrollbar-color: transparent transparent;
height: 60px;
flex-shrink: 0;
}
.ribbon-body::-webkit-scrollbar {
height: 6px;
}
.ribbon-body::-webkit-scrollbar-thumb {
background: transparent;
}
.ribbon-body:hover::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 3px;
}
/* Eine Gruppe: Buttons oben, kleiner Gruppentitel darunter; rechts ein dezenter
Trenner (wie OCS-Ribbon-Gruppen). */
.ribbon-group {
display: flex;
flex-direction: column;
padding: 0 8px;
border-right: 1px solid var(--line);
}
.ribbon-group:last-child {
border-right: none;
}
.ribbon-items {
display: flex;
align-items: flex-start;
gap: 2px;
flex: 1;
}
.ribbon-group-title {
text-align: center;
color: var(--muted);
font-size: 9px;
letter-spacing: 0.05em;
text-transform: uppercase;
padding-top: 2px;
}
/* Ein Ribbon-Button: Icon oben, Label darunter (kompakt, wie eine Palette). */
.ribbon-btn {
appearance: none;
border: 1px solid transparent;
background: transparent;
color: var(--label);
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
width: 52px;
padding: 4px 2px 3px;
border-radius: 5px;
cursor: pointer;
font-size: 10px;
line-height: 1.15;
transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.ribbon-btn span {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ribbon-btn:hover:not(:disabled) {
background: var(--panel-2);
border-color: var(--line);
}
.ribbon-btn.active {
background: var(--accent-dim, var(--panel-2));
border-color: var(--accent-border, var(--accent));
color: var(--ink);
}
.ribbon-btn:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.ribbon-empty {
display: flex;
align-items: center;
color: var(--muted);
font-size: 11px;
font-style: italic;
padding: 0 8px;
}
/* Custom-Bar-Picker („+ Hinzufügen") — kompakter, unauffälliger Trigger, der
sich neben die Custom-Buttons stellt. */
.ribbon-add.tb-dd-trigger {
align-self: center;
height: 24px;
font-size: 11px;
color: var(--muted);
border-style: dashed;
}
.ribbon-add.tb-dd-trigger:hover {
color: var(--label);
}
/* „Ansichten"-Tab: die aus der TopBar gemergten Cluster (View-Grid, Kombis,
Detail, Zoom, Darstellung) in EINER Zeile — nutzt die bestehenden `.tb-*`-
Stile unverändert weiter. */
/* ViewRibbonTab-Wurzel — sitzt jetzt inline in der Oberleiste (kein eigenes
Band). NUR dieser mittlere Block scrollt horizontal, wenn das Fenster zu
schmal wird — die rechte Gruppe (Datei-Aktionen + Projekt) bleibt sichtbar. */
.ribbon-views {
display: inline-flex;
align-items: center;
gap: 8px;
flex: 1 1 auto;
min-width: 0;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
}
.ribbon-views::-webkit-scrollbar {
display: none;
}
/* ── Layout-Blatt im Haupt-Viewport (DOSSIER A3, Phase 2b) ─────────────────────
Das Blatt liegt auf einem dunklen „Tisch"; oben eine schlanke Werkzeugleiste,
darunter die pan-/zoombare Blattfläche mit dem weissen Papier. Griffe/HUD sind
Overlays. */
.layout-sheet-pane {
display: flex;
flex-direction: column;
}
.layout-sheet-toolbar {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 10px;
border-bottom: 1px solid var(--line, #2b2b2b);
background: var(--panel, #1d1d1d);
flex: 0 0 auto;
z-index: 3;
}
.layout-sheet-name {
font-size: 11px;
font-weight: 600;
color: var(--fg, #ddd);
margin-left: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 40%;
}
.layout-sheet-stage {
position: relative;
flex: 1;
min-height: 0;
min-width: 0;
overflow: hidden;
/* Dunkler „Tisch" mit dezentem Verlauf, damit das weisse Blatt abhebt. */
background:
radial-gradient(circle at 50% 40%, #2a2a2a 0%, #161616 70%, #101010 100%);
touch-action: none;
user-select: none;
}
.layout-sheet-paper {
position: absolute;
background: #ffffff;
box-shadow: 0 4px 22px rgba(0, 0, 0, 0.55);
}
.layout-sheet-vp {
position: absolute;
overflow: hidden;
background: #ffffff;
outline: 1px solid rgba(0, 0, 0, 0.35);
}
.layout-sheet-vp.selected {
outline: 1.5px solid #3b82f6;
z-index: 2;
}
.layout-sheet-vp-missing {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
color: #b00;
text-align: center;
padding: 4px;
}
/* Resize-Griffe (8 Stück) — kleine blaue Quadrate am Rand des gewählten VP. */
.layout-sheet-grip {
position: absolute;
width: 9px;
height: 9px;
margin: -5px 0 0 -5px; /* auf den Ankerpunkt zentrieren */
background: #ffffff;
border: 1.5px solid #3b82f6;
border-radius: 2px;
z-index: 4;
}
/* Ghost-Rechteck beim Aufziehen einer neuen Platzierung. */
.layout-sheet-ghost {
position: absolute;
border: 1.5px dashed #3b82f6;
background: rgba(59, 130, 246, 0.12);
z-index: 4;
pointer-events: none;
}
/* HUD (Eigenschaften des gewählten VP) unten links. */
.layout-sheet-hud {
position: absolute;
left: 12px;
bottom: 12px;
z-index: 5;
display: flex;
flex-direction: column;
gap: 5px;
padding: 8px 10px;
min-width: 150px;
background: color-mix(in srgb, var(--panel-2, #262626) 92%, transparent);
border: 1px solid var(--line, #2b2b2b);
border-radius: 6px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}
/* Ausschnitt-Bindung nach dem Aufziehen — mittig oben. */
.layout-sheet-bind {
position: absolute;
left: 50%;
top: 16px;
transform: translateX(-50%);
z-index: 6;
display: flex;
flex-direction: column;
gap: 6px;
padding: 8px 10px;
min-width: 220px;
background: color-mix(in srgb, var(--panel-2, #262626) 96%, transparent);
border: 1px solid var(--line, #2b2b2b);
border-radius: 6px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}
.layout-sheet-hud-title {
font-size: 11px;
font-weight: 600;
color: var(--fg, #ddd);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 220px;
}
.layout-sheet-hud-row {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--muted, #6e6e6e);
}
.layout-sheet-hud-row > span:first-child {
flex: 0 0 54px;
}
.layout-sheet-hud-scale {
display: flex;
align-items: center;
gap: 2px;
flex: 1;
}
.layout-sheet-hud-scale input {
width: 64px;
}
.layout-sheet-hud-size {
font-size: 10px;
color: var(--muted, #6e6e6e);
}
.layout-sheet-hud-actions {
display: flex;
gap: 5px;
}
/* Leer-Hinweis (keine Ausschnitte / keine Viewports). */
.layout-sheet-empty {
position: absolute;
left: 50%;
bottom: 14px;
transform: translateX(-50%);
z-index: 3;
max-width: 70%;
padding: 6px 12px;
text-align: center;
font-size: 11px;
color: var(--muted, #9a9a9a);
background: color-mix(in srgb, var(--panel, #1d1d1d) 80%, transparent);
border-radius: 5px;
pointer-events: none;
}
/* ── Toast (nicht-blockierende Kurzmeldungen, Ersatz für window.alert) ──────
Oben rechts, gestapelt, über allem (auch Modals) — window.alert war im
WKWebView ohnehin lautlos wirkungslos, siehe state/notifySlice.ts. */
.toast-stack {
position: fixed;
top: 16px;
right: 16px;
z-index: 2000;
display: flex;
flex-direction: column;
gap: 8px;
max-width: min(360px, calc(100vw - 32px));
pointer-events: none;
}
.toast-item {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 10px 12px;
background: var(--panel);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
border-radius: 8px;
box-shadow: var(--shadow-3);
color: var(--ink);
font-size: 12px;
line-height: 1.4;
cursor: pointer;
pointer-events: auto;
animation: toast-in 0.15s ease-out;
}
.toast-item.toast-warning {
border-left-color: #d8a83c;
}
.toast-item.toast-error {
border-left-color: var(--danger);
}
.toast-message {
flex: 1 1 auto;
white-space: pre-wrap;
}
.toast-close {
flex: 0 0 auto;
border: none;
background: transparent;
color: var(--ink-2);
font-size: 15px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
.toast-close:hover {
color: var(--ink);
}
@keyframes toast-in {
from {
opacity: 0;
transform: translateY(-6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}