:root {
  --bg: #1b1d22;
  --panel-bg: #24262c;
  --field-bg: #2f323a;
  --border: #3a3d46;
  --text: #eef0f3;
  --text-dim: #9aa0ab;
  --accent: #4f8cff;
  --accent-dim: #2c4a86;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", system-ui, sans-serif;
}

.app {
  display: flex;
  height: 100vh;
}

/* Easy/Pro-Modus: proOnly-Bereiche (Druckeinstellungen, Tools) nur im Profi-Modus, easyOnly-Bereiche
   (vereinfachte Stärke/Qualität-Regler) nur im Einfach-Modus - die zugrunde liegenden Felder bleiben
   in beiden Fällen im DOM (werden von den Presets/dem restlichen Code weiter genutzt), nur die Ansicht
   wechselt. */
.app:not(.proMode) .proOnly {
  display: none;
}

.app.proMode .easyOnly {
  display: none;
}

/* Sidebar gibt es nicht mehr - Modus-Umschalter sitzt jetzt kompakt direkt in der schwebenden Icon-Leiste
   (siehe .panelSectionsRow unten), keine volle Breite mehr wie im alten Sidebar-Kontext. */
.modeSwitch {
  display: flex;
  gap: 3px;
  flex: 0 0 auto;
}

.modeSwitchBtn {
  font: inherit;
  cursor: pointer;
  background: var(--field-bg);
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 9px;
  font-size: 12px;
  font-weight: 600;
}

.modeSwitchBtn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #0d1117;
}

/* Kleines, dezentes Label oben links im Viewer statt einer eigenen Titelzeile in der (jetzt entfernten)
   Sidebar - reine Beschriftung, keine Interaktion. */
.appTitle {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 12;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
  background: rgba(30, 32, 38, 0.7);
  padding: 4px 10px;
  border-radius: 6px;
  pointer-events: none;
}

/* Menüs sind jetzt keine Sidebar-Inhalte mehr, sondern eine schwebende Icon-Leiste MITTIG über dem
   3D-Viewer (.panelSectionsWrap lebt in .viewer, siehe index.html - .viewer hat position:relative).
   Klick/Hover auf ein Icon zeigt darunter ein schwebendes Popover-Panel mit dem Inhalt (JS toggelt
   [hidden], siehe initUI). PC = Hover (öffnet/schließt automatisch), Handy = Klick (kein Hover-Event
   auf Touch) - welcher Modus aktiv ist, entscheidet JS per matchMedia("(hover: hover)") direkt in der
   Verzweigung (kein CSS-Body-Klassen-Umweg), siehe die Tab-Wiring in initUI(). */
.panelSectionsWrap {
  position: absolute;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 15;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  /* "width: fit-content" statt nur max-width - erzwingt, dass die Box wirklich nur so breit ist wie ihr
     Inhalt (Icon-Leiste bzw. offenes Popover), nicht implizit die volle Viewer-Breite. War vorher nur
     max-width gesetzt, konnte die unsichtbare Box viel größer als das sichtbar Gezeichnete werden - dann
     zählte Hovern/Klicken weit neben dem Popover fälschlich noch als "drin" (gemeldeter Bug: riesiger
     Hoverbereich, Material schloss nicht obwohl man längst daneben klickte). */
  width: fit-content;
  max-width: calc(100vw - 24px);
  /* Schwebt direkt über dem 3D-Viewer - schnelles Doppelklicken (z.B. Material wählen, dann sofort ins
     Modell klicken) markierte sonst Text in der Leiste/im Popover statt zu orbiten/zu schließen. Felder
     (input/select) sind davon nicht betroffen - die haben ihr eigenes, unabhängiges Editier-Verhalten. */
  user-select: none;
  -webkit-user-select: none;
}

.panelSectionsRow {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 4px;
  max-width: 100%;
  background: rgba(30, 32, 38, 0.95);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 4px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}

.panelSectionTab {
  flex: 0 0 auto;
  width: 75px;
  font: inherit;
  cursor: pointer;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim);
  background: none;
  border: 1px solid transparent;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 7px 2px;
  text-align: center;
  white-space: normal;
  line-height: 1.15;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.panelSectionTab:hover {
  background: var(--field-bg);
  border-color: var(--border);
  color: var(--text);
}

.panelSectionTab.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #0d1117;
}

/* Führungs-Hinweis (siehe updateGuideHints() in app.js): solange kein Material gewählt ist, pulsiert immer
   nur EIN Tab - erst Modell, dann Material -, nicht beide gleichzeitig. Gleiche Warnfarbe wie
   .materialRequiredHint/.noPriceYet, damit "hier fehlt noch was" einheitlich aussieht. */
.panelSectionTab.guidePulse {
  animation: guideTabPulse 1.4s ease-in-out infinite;
}

@keyframes guideTabPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 184, 77, 0); border-color: transparent; }
  50% { box-shadow: 0 0 0 4px rgba(255, 184, 77, 0.35); border-color: #ffb84d; }
}

/* Modell-Tab-Beschriftung wechselt durch, wenn mehrere Modelle geladen sind (siehe updateModelTabLabel()
   in app.js: "Modell" -> "1 <Name>" -> "2 <Name>" -> ... -> zurück zu "Modell", je 3s). Clip schneidet
   lange Namen nicht einfach ab, sondern der innere Text wird bei Bedarf per Transform durchgescrollt. */
.panelSectionTabLabelClip {
  display: block;
  width: 100%;
  overflow: hidden;
  white-space: nowrap;
}

.panelSectionTabLabelText {
  display: inline-block;
  white-space: nowrap;
  transition: transform 1.1s ease-in-out;
}

.panelSectionIcon {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Ersetzt das Modell-Tab-Icon, solange der Beschriftungs-Zyklus gerade eine nummerierte Position zeigt
   (siehe updateModelTabLabel() in app.js) - nimmt denselben Platz wie .panelSectionIcon ein.
   [hidden] MUSS hier explizit wiederholt werden: die eigene display:block-Regel unten hat dieselbe
   Spezifität wie die Browser-Standardregel "[hidden]{display:none}", gewinnt als Autor-Regel aber IMMER
   gegen die User-Agent-Regel - ohne das hier bliebe das Element bei .hidden=true unsichtbar, aber als
   leerer 18x18-Block im Flex-Stack stehen und schöbe das Icon darüber sichtbar nach oben. */
.panelSectionTabNumeral[hidden] {
  display: none;
}

.panelSectionTabNumeral {
  display: block;
  width: 18px;
  height: 18px;
  line-height: 18px;
  text-align: center;
  font-size: 10px; /* zeigt jetzt "1/3" statt nur "1" (siehe updateModelTabLabel) - kleiner, damit es reinpasst */
  font-weight: 700;
}

/* Preis-Tab ist doppelt so groß wie die anderen und zeigt den aktuellen Gesamtpreis direkt auf dem
   Button (immer sichtbar, live von updatePrice() aktualisiert, siehe #priceTabValue in app.js) - die
   Kostenaufschlüsselung (Rüstkosten/Material/Fertigung/...) bleibt wie bei den anderen Tabs nur im
   Popover, das erst bei Hover/Klick aufklappt. */
.panelSectionTabPrice {
  flex-direction: row;
  width: auto;
  min-width: 130px;
  padding: 10px 18px;
  gap: 10px;
}

.panelSectionTabPrice .panelSectionIcon {
  width: 32px;
  height: 32px;
}

.priceTabValue {
  font-size: 17px;
  font-weight: 700;
  white-space: nowrap;
}

/* Preis-Tab bleibt orange, solange kein Preis berechnet werden kann (Modell und/oder Material fehlt) -
   siehe updateGuideHints() in app.js. Kräftiger VOLLER Hintergrund statt transparentem Ton (gleiches
   Kontrast-Prinzip wie .panelSectionTab.active: satte Fläche + dunkler Text, nicht Farbe-auf-eigenem-Ton -
   Orange-Text auf blass-orangem Ton war kaum lesbar). */
.panelSectionTabPrice.noPriceYet {
  background: #ffb84d;
  border-color: #ffb84d;
  color: #2b1800;
}

.panelSectionsWrap > .panelSectionBody {
  /* Viewport-Einheit statt % vom (jetzt fit-content, also selbst vom Inhalt abhängigen) Wrap - keine
     zirkuläre Breitenberechnung. */
  width: min(440px, calc(100vw - 48px));
  background: rgba(30, 32, 38, 0.97);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
  max-height: min(70vh, 600px);
  overflow-y: auto;
}

.panelSectionsWrap > .panelSectionBody[hidden] {
  display: none;
}

/* Handy/schmaler Viewport: Icon-Leiste rutscht etwas höher, Tabs dürfen enger/kleiner werden - Sidebar
   gibt es nicht mehr, daher kein Umbruch von .app nötig. */
@media (max-width: 720px) {
  .panelSectionsWrap {
    top: 10px;
  }

  .panelSectionTab {
    width: 64px;
  }
}

.fileButtons {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 20px;
}

.currentFileName {
  margin: 4px 0 0;
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
}

/* STL kann jetzt überall im Fenster fallengelassen werden (nicht nur in einer kleinen Box) - dezentes
   Highlight ums ganze Fenster während des Ziehens statt eines eigenen Drop-Kastens. */
body.dragActive::after {
  content: "";
  position: fixed;
  inset: 0;
  border: 3px dashed var(--accent);
  pointer-events: none;
  z-index: 999;
}

.field {
  margin-bottom: 16px;
}

.field label {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--text-dim);
  margin-bottom: 6px;
}

.field select,
.field input[type="range"],
.field input[type="number"],
.field input[type="text"],
.field input[type="email"],
.field input[type="password"] {
  width: 100%;
}

.field select,
.field input[type="number"],
.field input[type="text"],
.field input[type="email"],
.field input[type="password"] {
  background: var(--field-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px;
  font-size: 14px;
}

.stats {
  border-top: 1px solid var(--border);
  padding-top: 14px;
  margin-bottom: 14px;
}

/* .priceRow wird per .hidden ein-/ausgeblendet (siehe updatePrice() in app.js, z.B. #priceInsertsRow) -
   dieselbe Falle wie bei .uploadToast/.panelSectionTabNumeral: die eigene display:flex-Regel unten
   überschreibt sonst die Browser-Standardregel "[hidden]{display:none}". */
.priceRow[hidden] {
  display: none;
}

.statRow, .priceRow, .priceTotal {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  padding: 4px 0;
}

.statRow span:last-child {
  color: var(--text-dim);
}

.supportBox {
  border-top: 1px solid var(--border);
  padding-top: 14px;
  margin-bottom: 14px;
}

.checkboxRow {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-dim);
  margin-bottom: 10px;
  cursor: pointer;
}

.supportStatus {
  font-size: 13px;
  font-weight: 600;
  padding: 8px 10px;
  border-radius: 6px;
  margin-bottom: 10px;
}

.supportStatus.ok {
  background: rgba(79, 200, 130, 0.15);
  color: #6fd89a;
}

.supportStatus.warn {
  background: rgba(230, 90, 90, 0.15);
  color: #ff8888;
}

.resetBtn {
  width: 100%;
  background: var(--field-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px;
  font-size: 13px;
  cursor: pointer;
}

.resetBtn:hover {
  border-color: var(--accent);
}

.resetBtn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.6);
}
.resetBtn:disabled:hover {
  border-color: var(--border);
}

.primaryBtn {
  background: var(--accent);
  border-color: var(--accent);
  color: #0d1117;
  font-weight: 600;
}

.primaryBtn:hover {
  border-color: var(--accent);
  filter: brightness(1.08);
}

.linkBtn {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  font-size: 13px;
  cursor: pointer;
  text-decoration: underline;
}

.authBar {
  border-bottom: 1px solid var(--border);
  padding-bottom: 14px;
  margin-bottom: 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.authBar .resetBtn {
  width: auto;
}

.authComingSoon {
  font-size: 12px;
  color: var(--text-dim);
  font-style: italic;
}

.authBarLoggedIn {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.authEmail {
  font-size: 12px;
  color: var(--text-dim);
  word-break: break-all;
}

.modalOverlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  padding: 16px;
}

.modalOverlay[hidden] {
  display: none;
}

.modalCard {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 22px;
  width: 100%;
  max-width: 360px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
}

.modalCard h2 {
  margin: 0 0 12px;
  font-size: 17px;
}

.modalHint {
  font-size: 12px;
  color: var(--text-dim);
  margin: 0 0 14px;
}

/* Kleine Kopfzeile mit ⓘ-Icon direkt daneben (ersetzt die entfernte Tutorial-Tour, siehe TOOLTIPS in
   app.js) - z.B. über der Datei-Auswahl oder der Preis-Aufschlüsselung. */
.fieldLabelHint {
  display: flex;
  align-items: center;
  gap: 4px;
}

.modalError {
  font-size: 12px;
  color: #ff8888;
  background: rgba(230, 90, 90, 0.15);
  border-radius: 6px;
  padding: 8px 10px;
  margin: 0 0 10px;
}

/* Preis/Stückzahl nochmal als Übersicht im Bestell-Modal - man verliert beim Ausfüllen der Kontaktdaten
   sonst leicht den Bezug dazu, was man da eigentlich bestellt. */
.orderSummary {
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 4px 12px;
  margin: 0 0 16px;
}

.orderSummary .priceRow:last-child {
  font-weight: 600;
}

.orderSummary .priceRow:last-child span:last-child {
  color: var(--accent);
}

/* Hebt sich farblich ab - klarer Hinweis, kein normaler Fließtext (materialRequiredHint = eigene Klasse
   zusätzlich zu .modalHint, damit .modalHint's Standardfarbe hier gezielt überschrieben werden kann). */
.materialRequiredHint {
  color: #ffb84d;
  font-weight: 600;
}

/* Immer sichtbarer Hinweis zur nächsten Rabattstufe, dezenter als der Material-Hinweis (reine Zusatzinfo,
   kein Blocker). */
#nextDiscountHint {
  color: var(--text-dim);
  margin: 10px 0 0;
}

.fieldRow {
  display: flex;
  gap: 10px;
}

.fieldRow .field {
  flex: 1;
  min-width: 0;
}

.modalActions {
  display: flex;
  gap: 10px;
  margin-top: 4px;
}

.modalActions .resetBtn {
  flex: 1;
}

.modalSwitch {
  text-align: center;
  font-size: 12px;
  color: var(--text-dim);
  margin: 14px 0 0;
}

.ordersList {
  max-height: 50vh;
  overflow-y: auto;
  margin-bottom: 14px;
}

.orderItem {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 8px;
  font-size: 13px;
}

.orderItem .orderMeta {
  color: var(--text-dim);
  font-size: 12px;
  margin: 4px 0 8px;
}

.orderItem .resetBtn {
  padding: 6px;
  font-size: 12px;
}

.priceBox {
  border-top: 1px solid var(--border);
  padding-top: 14px;
}

.priceRow span:last-child {
  color: var(--text-dim);
}

.priceTotal {
  border-top: 1px solid var(--border);
  margin-top: 8px;
  padding-top: 10px;
  font-size: 18px;
  font-weight: 600;
}

.priceTotal span:last-child {
  color: var(--accent);
}

/* Weitere Bauteile derselben Bestellung (mehrere Dateien auf einmal geladen) - siehe orderItems/
   renderOrderItemsList() in app.js. Nur die INAKTIVEN Positionen stehen hier als Zeile; die aktive nutzt
   das normale Stückzahl-Feld darüber. */
.orderItemsList {
  margin: 4px 0 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.orderItemRow {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
}

/* Deutlich markierte Zeile der gerade aktiven Position - macht auf einen Blick klar, dass hier mehrere
   Modelle liegen und welches davon man gerade sieht/bearbeitet (siehe renderOrderItemsList in app.js). */
.orderItemRowActive {
  background: var(--accent-dim);
  border-color: var(--accent);
}

.orderItemRowActive .orderItemNameBtn {
  color: var(--text);
  text-decoration: none;
  font-weight: 600;
  cursor: default;
}

.orderItemNameBtn {
  flex: 1;
  min-width: 0;
  text-align: left;
  background: none;
  border: none;
  color: var(--accent);
  font-size: 12px;
  cursor: pointer;
  text-decoration: underline;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0;
}

.orderItemQtyInput {
  width: 54px;
  background: var(--panel-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px;
  font-size: 12px;
}

.orderItemPrice {
  font-size: 12px;
  color: var(--text-dim);
  white-space: nowrap;
  min-width: 60px;
  text-align: right;
}

.orderItemRemove {
  appearance: none;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}

.orderItemRemove:hover {
  color: #ff8888;
}

.orderItemsGrandTotal {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  font-weight: 600;
  padding: 6px 8px 0;
  border-top: 1px solid var(--border);
  margin-top: 4px;
}

.infoIcon {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  margin: 0 0 0 4px;
  width: 16px;
  height: 16px;
  line-height: 16px;
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: 50%;
}

.infoIcon:hover,
.infoIcon:focus-visible {
  color: var(--accent);
  outline: none;
}

.tooltipPopover {
  position: fixed;
  z-index: 1000;
  max-width: 240px;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.12s, transform 0.12s;
  pointer-events: none;
}

.tooltipPopover.visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.tooltipIcon {
  color: var(--accent);
  margin-bottom: 6px;
}

.tooltipIcon svg {
  width: 26px;
  height: 26px;
}

.tooltipText {
  margin: 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--text);
}

/* Verstecktes Perf-Debug-Panel - 5x auf "Pro" klicken (siehe initEasyProMode in app.js), zeigt Dreieckszahl/
   BVH-Status/Analyse-Dauern. Unten links, dezent, monospace für Zahlen-Spalten. */
.debugPanel {
  position: fixed;
  left: 12px;
  bottom: 12px;
  z-index: 4000;
  width: 220px;
  background: rgba(20, 22, 26, 0.95);
  border: 1px solid var(--accent-dim);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 11px;
  font-family: "Consolas", "Courier New", monospace;
  color: var(--text-dim);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}

.debugPanelHeader {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: 6px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.debugPanelRow {
  display: flex;
  justify-content: space-between;
  padding: 2px 0;
}

.debugPanelRow span:last-child {
  color: var(--text);
}

/* Verstecktes Icon zum Öffnen des Bohrungs-Debug-Popovers - bewusst NICHT in der normalen Menüleiste
   (panelSectionsRow), nur hier im ohnehin versteckten Debug-Panel erreichbar (5x-Klick auf "Pro"). */
.debugPanelHoleBtn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  margin-top: 8px;
  padding: 6px 0;
  background: none;
  border: 1px solid var(--accent-dim);
  border-radius: 6px;
  color: var(--text-dim);
  cursor: pointer;
}

.debugPanelHoleBtn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.debugPanelHoleBtn .panelSectionIcon {
  width: 18px;
  height: 18px;
}

.viewer {
  flex: 1;
  position: relative;
}

#viewerCanvas {
  width: 100%;
  height: 100%;
}

#viewerCanvas canvas {
  display: block;
}

.viewerToolbar {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 10;
  display: none;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.viewerToolbar.visible {
  display: flex;
}

/* Bestell-Upload-Hinweis - NUR während uploadOrder() läuft sichtbar (siehe setUploadStatus() in app.js,
   startet "hidden" und wird ausschließlich von dort ein-/ausgeblendet). position:fixed + Bildschirmmitte
   statt kleinem Pill oben, bewusst groß/auffällig - fixed statt absolute, damit die Mitte sich auf den
   GANZEN Bildschirm bezieht, unabhängig von der Größe/Verschachtelung von .viewer.
   [hidden] MUSS hier explizit wiederholt werden - dieselbe Falle wie bei .panelSectionTabNumeral: die
   eigene display:flex-Regel unten hat dieselbe Spezifität wie die Browser-Standardregel
   "[hidden]{display:none}", gewinnt als Autor-Regel aber IMMER gegen die User-Agent-Regel - ohne das hier
   blieb die Box dauerhaft sichtbar stehen, auch wenn app.js sie längst per .hidden=true ausgeblendet hatte. */
.uploadToast[hidden] {
  display: none;
}

.uploadToast {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 500;
  display: flex;
  align-items: center;
  gap: 16px;
  background: rgba(20, 22, 28, 0.95);
  color: var(--text);
  border-radius: 14px;
  padding: 26px 36px;
  font-size: 19px;
  font-weight: 600;
  text-align: center;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
  max-width: min(90vw, 520px);
}

.uploadToast.ok {
  color: #6fd89a;
}

.uploadToast.warn {
  color: #ff8888;
}


/* Schicht-Vorschau-Regler teilt sich jetzt die Box mit der Legende (siehe .legendBox oben) statt eigene
   schwebende Box am Rand zu sein - hier nur noch das Innen-Layout (Label + Regler nebeneinander), Rahmen/
   Hintergrund kommt von .legendBox. Nimmt den ganzen restlichen Platz in der Box ein (flex:1), Regler
   selbst füllt das wiederum aus - dadurch automatisch so lang wie in der Box Platz ist, unabhängig davon
   wie breit die Icon-Reihe gerade ist (Easy/Pro-Modus haben unterschiedlich viele Tabs). */
.sliceSideControl {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 auto;
  min-width: 0;
}

.sliceSideControl[hidden] {
  display: none;
}

.sliceSideControl label {
  display: flex;
  gap: 6px;
  font-size: 12px;
  color: var(--text-dim);
  white-space: nowrap;
  flex-shrink: 0;
}

.sliceSideControl input[type="range"] {
  flex: 1 1 auto;
  min-width: 60px;
}

.toolButtons {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

/* Box IST der Aktivierungs-Button fürs Tool (Klick startet den Modus) - Animation läuft nur beim Hovern,
   nicht dauerhaft, und ist bewusst kompakt gehalten. */
.toolActivator {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  font: inherit;
  cursor: pointer;
  background: rgba(36, 38, 44, 0.6);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 10px 4px 4px;
  transition: border-color 0.15s, background 0.15s;
}

.toolActivator:hover {
  border-color: var(--accent-dim);
}

.toolActivator.active {
  background: rgba(255, 140, 26, 0.16);
  border-color: #ff8c1a;
}

.toolDemoSvg {
  width: 64px;
  height: auto;
  display: block;
  overflow: visible;
  flex-shrink: 0;
}

.toolActivatorLabel {
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  text-align: left;
}

/* Einfacher isometrischer Würfel statt nur einer flachen Fläche - deutlicher als 3D-Bauteil erkennbar.
   Loch/Markierung sitzt auf der Deckfläche (nicht der Seite) - auf einer Seitenfläche sah der Klickpunkt
   schräg/falsch aus. */
.toolDemoCubeTop { fill: rgba(79, 140, 255, 0.35); stroke: var(--accent-dim); stroke-width: 1; }
.toolDemoCubeLeft { fill: rgba(79, 140, 255, 0.12); stroke: var(--accent-dim); stroke-width: 1; }
.toolDemoCubeRight { fill: rgba(79, 140, 255, 0.22); stroke: var(--accent-dim); stroke-width: 1; }

/* "Material für alle übernehmen": 3 kleine Würfel färben sich beim Hovern nacheinander ein (orange, wie die
   übrige Warnfarbe fürs "hier fehlt/ändert sich was") - macht bildlich klar, was der Klick tut, ohne echte
   Filamentfarben in CSS nachbilden zu müssen. */
.applyAllCube .toolDemoCubeTop, .applyAllCube .toolDemoCubeLeft, .applyAllCube .toolDemoCubeRight {
  transition: fill 0.3s ease;
}
.applyMaterialActivator:hover .applyAllCube1 .toolDemoCubeTop { fill: rgba(255, 140, 26, 0.65); transition-delay: 0s; }
.applyMaterialActivator:hover .applyAllCube1 .toolDemoCubeLeft { fill: rgba(255, 140, 26, 0.35); transition-delay: 0s; }
.applyMaterialActivator:hover .applyAllCube1 .toolDemoCubeRight { fill: rgba(255, 140, 26, 0.5); transition-delay: 0s; }
.applyMaterialActivator:hover .applyAllCube2 .toolDemoCubeTop { fill: rgba(255, 140, 26, 0.65); transition-delay: 0.15s; }
.applyMaterialActivator:hover .applyAllCube2 .toolDemoCubeLeft { fill: rgba(255, 140, 26, 0.35); transition-delay: 0.15s; }
.applyMaterialActivator:hover .applyAllCube2 .toolDemoCubeRight { fill: rgba(255, 140, 26, 0.5); transition-delay: 0.15s; }
.applyMaterialActivator:hover .applyAllCube3 .toolDemoCubeTop { fill: rgba(255, 140, 26, 0.65); transition-delay: 0.3s; }
.applyMaterialActivator:hover .applyAllCube3 .toolDemoCubeLeft { fill: rgba(255, 140, 26, 0.35); transition-delay: 0.3s; }
.applyMaterialActivator:hover .applyAllCube3 .toolDemoCubeRight { fill: rgba(255, 140, 26, 0.5); transition-delay: 0.3s; }

/* Ruhezustand: Cursor sitzt sichtbar oben links (Hinweis "hier gehts los"), Effekte sind aus - erst beim
   Hovern der ganzen Box läuft die Animation los (siehe .toolActivator:hover unten). */
.toolDemoCursor,
.toolDemoCursorInsert {
  fill: #f2f2f2;
  stroke: #0d1117;
  stroke-width: 0.6;
}

.toolDemoClick {
  fill: none;
  stroke: #f2f2f2;
  opacity: 0;
}

.toolDemoComment,
.toolDemoInsert {
  opacity: 0;
}

.toolDemoDot { fill: #ffb84d; }
.toolDemoLine { stroke: #ffb84d; stroke-width: 1; }
.toolDemoBubble { fill: rgba(255, 184, 77, 0.25); stroke: #ffb84d; stroke-width: 1; }
.toolDemoHole { fill: #14161a; stroke: var(--accent-dim); stroke-width: 1; }
.toolDemoInsertBody { fill: #b8860b; }
.toolDemoInsertTop { fill: #d4a017; }
.toolDemoInsertBottom { fill: #96700a; }
.toolDemoScaleFrame { fill: none; stroke: #66d9ff; stroke-width: 1.2; stroke-dasharray: 3 2; }
.toolDemoScaleHandle { fill: #66d9ff; }

/* Würfel startet klein (Ruhezustand = kleinste Größe im Loop) und wächst erst beim Hovern auf volle
   Größe - zeigt "so sieht Ziehen am Rahmen aus" statt nur ein statisches Icon. Eigener Cursor/Click statt
   der generischen .toolDemoCursor/.toolDemoClick, weil Ziel-Punkt (Eck-Handle unten rechts) + Timing
   anders sind als bei Kommentar/Einschmelzmutter. */
.toolDemoScaleCube { transform-origin: 45px 30px; transform: scale(0.45); }
.toolDemoScaleCursor { fill: #f2f2f2; stroke: #0d1117; stroke-width: 0.6; }
.toolDemoScaleClick { fill: none; stroke: #f2f2f2; opacity: 0; }

.toolActivator:hover .toolDemoCursor,
.toolActivator:hover .toolDemoCursorInsert {
  animation: toolDemoCursorMove 3s ease-in-out infinite;
}

.toolActivator:hover .toolDemoClick {
  animation: toolDemoClickPulse 3s ease-in-out infinite;
}

.toolActivator:hover .toolDemoComment {
  animation: toolDemoCommentPop 3s ease-in-out infinite;
}

.toolActivator:hover .toolDemoInsert {
  animation: toolDemoInsertDrop 3s ease-in-out infinite;
}

#scaleModeBtn:hover .toolDemoScaleCube {
  animation: toolDemoScaleCubeGrow 3s ease-in-out infinite;
}

#scaleModeBtn:hover .toolDemoScaleCursor {
  animation: toolDemoScaleCursorDrag 3s ease-in-out infinite;
}

#scaleModeBtn:hover .toolDemoScaleClick {
  animation: toolDemoClickPulse 3s ease-in-out infinite;
}

@keyframes toolDemoCursorMove {
  0%, 8%    { transform: translate(0, 0); opacity: 1; }
  35%, 60%  { transform: translate(43px, 16px); opacity: 1; }
  75%, 100% { transform: translate(43px, 16px); opacity: 0; }
}

/* Kurzer Puls genau im Moment, in dem der Cursor "landet" - macht den Klick selbst sichtbar */
@keyframes toolDemoClickPulse {
  0%, 32%   { opacity: 0; r: 1px; }
  38%       { opacity: 0.9; r: 3px; stroke-width: 1.5; }
  50%, 100% { opacity: 0; r: 10px; stroke-width: 0.5; }
}

@keyframes toolDemoCommentPop {
  0%, 35%   { opacity: 0; }
  45%, 80%  { opacity: 1; }
  95%, 100% { opacity: 0; }
}

/* Hülse als echter Zylinder (Deckel-Ellipse + Mantel + Boden-Ellipse), fällt von oben ins Loch */
@keyframes toolDemoInsertDrop {
  0%, 38%   { opacity: 0; transform: translateY(-14px); }
  50%, 80%  { opacity: 1; transform: translateY(0); }
  95%, 100% { opacity: 0; transform: translateY(0); }
}

/* Würfel wächst genau in dem Moment, in dem der Cursor unten am Eck-Handle "zieht" (siehe
   toolDemoScaleCursorDrag) - hält kurz bei voller Größe, schrumpft am Loop-Ende wieder zurück (0%=100%
   identisch, kein Sprung beim Neustart der Endlosschleife). */
@keyframes toolDemoScaleCubeGrow {
  0%, 32%   { transform: scale(0.45); }
  70%, 85%  { transform: scale(1); }
  100%      { transform: scale(0.45); }
}

/* Cursor läuft zum unteren Eck-Handle, "klickt" dort (siehe toolDemoClickPulse-Trigger auf
   .toolDemoScaleClick, gleiches Timing-Fenster), zieht dann noch ein Stück weiter nach außen (Drag-
   Geste) während der Würfel wächst, verschwindet gegen Loop-Ende. */
@keyframes toolDemoScaleCursorDrag {
  0%, 8%    { transform: translate(0, 0); opacity: 1; }
  32%, 40%  { transform: translate(62px, 51px); opacity: 1; }
  68%, 78%  { transform: translate(70px, 59px); opacity: 1; }
  90%, 100% { transform: translate(70px, 59px); opacity: 0; }
}

/* Ladeanzeige während des (bei komplexen Teilen teuren) Hülsen-Scans - selbe Bildsprache wie der
   Tool-Aktivierungs-Button, aber als Endlos-Loop statt hover-gesteuertem Einzelzyklus, damit sichtbar
   bleibt "läuft noch" statt eingefroren zu wirken. */
.holeScanLoading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  background: rgba(36, 38, 44, 0.92);
  border: 1px solid var(--accent-dim);
  border-radius: 10px;
  padding: 14px 22px;
  color: var(--text-dim);
  font-size: 13px;
  pointer-events: none;
}

.holeScanLoading[hidden] {
  display: none;
}

.holeScanSpinnerSvg {
  width: 70px;
}

.holeScanSpinnerInsert {
  animation: holeScanInsertDrop 1.1s ease-in-out infinite;
}

@keyframes holeScanInsertDrop {
  0%   { opacity: 0; transform: translateY(-14px); }
  35%  { opacity: 1; transform: translateY(0); }
  70%  { opacity: 1; transform: translateY(0); }
  85%  { opacity: 0; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-14px); }
}

#insertStatus {
  position: absolute;
  top: 64px;
  left: 16px;
  z-index: 10;
  max-width: 320px;
  margin: 0;
}

.insertSizeLabel {
  display: inline-flex;
  align-items: center;
  background: rgba(36, 38, 44, 0.95);
  color: var(--text-dim);
  border: 1px solid var(--accent-dim);
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}

.insertSizeLabel.ok {
  color: #6fd89a;
  border-color: #6fd89a;
}

.insertSizeLabel.warn {
  color: #ff8888;
  border-color: #ff8888;
}

/* Legende + Schicht-Vorschau-Regler teilen sich jetzt EINE Box über der Icon-Leiste (statt zwei getrennte
   schwebende Kästen) - .legendBox trägt den Rahmen/Hintergrund, .legend und .sliceSideControl selbst sind
   nur noch schlanke Innen-Layouts ohne eigenes Styling. Box ist per align-self:stretch genau so breit wie
   die Icon-Reihe darunter (siehe .panelSectionsRow). :has() zeigt die Box nur, wenn WIRKLICH einer der
   beiden Inhalte sichtbar ist - kein JS nötig, um die zwei unabhängigen hidden-Zustände zu verrechnen. */
.legendBox {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  align-self: stretch;
  background: rgba(30, 32, 38, 0.95);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px 16px;
  min-height: 44px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}

.legendBox:has(> #legend:not([hidden])),
.legendBox:has(> #sliceField:not([hidden])) {
  display: flex;
}

.legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* display:flex oben überschreibt sonst das [hidden]-Standardverhalten (gleiches Muster/derselbe Bug wie
   bei .holeScanLoading) - explizit erzwingen, sonst bleibt eine leere Box sichtbar wenn nichts zu zeigen ist. */
.legend[hidden] {
  display: none;
}

.legendSwatch {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  cursor: default;
  transition: transform 0.15s, filter 0.15s;
}

/* Farbe kommt je Eintrag über die CSS-Variable --legend-color rein (Inline-Style pro SVG, siehe
   updateLegend() in app.js) - eine Form pro Bedeutung (Wand/Füllung/Stütze/Hülse-ok/Hülse-zu-klein) statt
   nur einer Farbfläche, damit auf einen Blick erkennbar ist WAS gemeint ist. */
.legendIconSvg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: var(--legend-color);
  stroke-width: 1.3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Kleine Animation beim Hovern, gleiches Prinzip wie bei den Tool-Aktivierungs-Buttons: ruht im Normalzustand,
   läuft nur beim Drüberfahren - Swatch pulsiert kurz auf statt starr dazustehen. */
.legendSwatch:hover {
  animation: legendSwatchPulse 0.9s ease-in-out infinite;
}

@keyframes legendSwatchPulse {
  0%, 100% { transform: scale(1); filter: brightness(1); }
  50% { transform: scale(1.3); filter: brightness(1.4); }
}

/* "Fläche anklicken -> wird zur Standfläche"-Demo-Icon, jetzt Icon des "Standfläche wählen"-Tool-Buttons
   (index.html, Tools-Panel) statt eigener schwebender Box im Viewer. Kleine Demo-Animation beim Hovern,
   gleiches Prinzip wie die anderen Tool-Aktivierungs-Buttons (Trigger: .toolActivator:hover): Cursor klickt
   die OBERE Fläche an, der ganze Würfel hebt sich ein Stück an, dreht sich einmal um 180° (zeigt dabei die
   angeklickte Fläche unten), landet mit einem kurzen Plumps (breitgedrückt, federt zurück). Bewusst nur EIN
   Dreh, kein zweiter zum glatten Zurückspringen - der Loop-Neustart springt hart von 180° auf 0° zurück,
   das ist hier gewollt. */
.orientHintCubeTop {
  fill: rgba(79, 140, 255, 0.35);
  stroke: var(--accent-dim);
  stroke-width: 1;
}

.orientHintCursor {
  fill: #f2f2f2;
  stroke: #0d1117;
  stroke-width: 0.6;
  opacity: 0;
}

.orientHintClick {
  fill: none;
  stroke: #f2f2f2;
  opacity: 0;
}

.toolActivator:hover .orientHintCursor {
  animation: orientHintCursorShow 2.6s ease-in-out infinite;
}

.toolActivator:hover .orientHintClick {
  animation: orientHintClickPulse 2.6s ease-in-out infinite;
}

.toolActivator:hover .orientHintCubeTop {
  animation: orientHintFaceHighlight 2.6s ease-in-out infinite;
}

.toolActivator:hover .orientHintCube {
  animation: orientHintLiftSpin 2.6s ease-in-out infinite;
  transform-origin: 45px 30px; /* Würfel-Mitte - hebt sich an, dreht um sich selbst, kein Kanten-Kippen mehr */
}

@keyframes orientHintCursorShow {
  0%, 8%    { opacity: 0; }
  13%, 30%  { opacity: 1; }
  38%, 100% { opacity: 0; }
}

@keyframes orientHintClickPulse {
  0%, 15%   { opacity: 0; r: 1px; }
  21%       { opacity: 0.9; r: 3px; stroke-width: 1.5; }
  32%, 100% { opacity: 0; r: 9px; stroke-width: 0.5; }
}

/* Obere Fläche färbt sich beim Klick ein, bleibt während des Anhebens/Drehens sichtbar eingefärbt. */
@keyframes orientHintFaceHighlight {
  0%, 14%   { fill: rgba(79, 140, 255, 0.35); }
  22%, 80%  { fill: rgba(255, 184, 77, 0.85); }
  94%, 100% { fill: rgba(79, 140, 255, 0.35); }
}

/* Nur EIN 180°-Dreh (kein zweiter zum glatten Zurückspringen) - Loop-Neustart springt hart zurück auf 0°,
   das ist hier gewollt. Landung mit kurzem Plumps: breitgedrückt, federt leicht zurück, dann Ruhe. */
@keyframes orientHintLiftSpin {
  0%, 18%   { transform: translateY(0) rotate(0deg) scale(1, 1); }
  38%       { transform: translateY(-11px) rotate(180deg) scale(1, 1); }
  62%       { transform: translateY(-11px) rotate(180deg) scale(1, 1); }
  74%       { transform: translateY(0) rotate(180deg) scale(1.2, 0.78); }
  85%       { transform: translateY(0) rotate(180deg) scale(0.94, 1.07); }
  100%      { transform: translateY(0) rotate(180deg) scale(1, 1); }
}

.commentLayer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}

.commentLine {
  position: absolute;
  height: 2px;
  background: #ffb84d;
  transform-origin: 0 50%;
  pointer-events: none;
}

.commentBubble {
  position: absolute;
  max-width: 200px;
  background: var(--field-bg);
  border: 1px solid #ffb84d;
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 12px;
  color: var(--text);
  pointer-events: auto;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}

.commentBubble textarea {
  width: 100%;
  min-height: 50px;
  resize: vertical;
  background: var(--panel-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px;
  font-size: 12px;
  font-family: inherit;
}

.commentBubbleActions {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 6px;
}

.commentBubbleActions button {
  appearance: none;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 11px;
  cursor: pointer;
  padding: 2px 4px;
}

.commentBubbleActions button:hover {
  color: var(--accent);
}

.commentBubbleActions .commentDeleteBtn:hover {
  color: #ff8888;
}

/* ---- Material Vor-/Nachteil-Chips (direkt bei der Auswahl sichtbar) ---- */

.materialTraits {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.traitChip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  padding: 4px 8px 4px 6px;
  border-radius: 999px;
  border: 1px solid transparent;
  white-space: nowrap;
}

.traitChip svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
}

/* Neutral statt grün - grün sah bei JEDEM Material (das hat immer ein paar Vorteile) wie "schon
   ausgewählt" aus, genau wie die echte .selected-Markierung anderswo (z.B. .materialCard.selected,
   .traitCloudChip.selected). Nachteile (.con) bleiben zur Abgrenzung farbig (orange). */
.traitChip.pro {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--border);
  color: var(--text-dim);
}

.traitChip.con {
  background: rgba(230, 150, 60, 0.13);
  border-color: rgba(230, 150, 60, 0.35);
  color: #ffb84d;
}

.traitFinder {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  background: var(--field-bg);
}

.traitCloud {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.traitCloudChip {
  cursor: pointer;
  font: inherit;
  transition: opacity 0.15s;
}

.traitCloudChip.selected {
  background: rgba(79, 200, 130, 0.35);
  border-color: #6fd89a;
  font-weight: 600;
}

.traitCloudChip.disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

#traitFinderReset {
  font-size: 12px;
  font-weight: 400;
}

.materialGraph {
  position: relative;
  margin-top: 8px;
}

.materialGraphLines {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

/* Standard (blau): Eigenschaft(en) angeklickt -> Linie zu passenden Materialien ("Suche"). variantOwn
   (orange, durchgezogen): Material angeklickt -> Linie zu DESSEN eigenen Eigenschaften ("Zugehörigkeit").
   Eigene Farbe/Strich pro Richtung, damit beide Linienarten bei gleichzeitiger Auswahl klar unterscheidbar
   bleiben statt sich optisch zu vermischen. */
.materialGraphLine {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-dasharray: 5 5;
  opacity: 0.55;
  animation: materialGraphFlow 1s linear infinite;
  transition: opacity 0.2s;
}

.materialGraphLine.variantOwn {
  stroke: #ffb84d;
  stroke-dasharray: none;
  opacity: 0.65;
}


@keyframes materialGraphFlow {
  to { stroke-dashoffset: -20; }
}

.materialCards {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 14px;
  position: relative;
}

.materialCard {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: inherit;
  cursor: pointer;
  background: var(--panel-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 7px 8px 7px 12px;
  font-size: 13px;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
}

.materialCard:hover {
  border-color: var(--accent-dim);
}

.materialCard.selected {
  background: var(--accent);
  border-color: var(--accent);
  color: #0d1117;
  font-weight: 600;
}

.materialCard.dimmed {
  opacity: 0.3;
}

.radarChart {
  width: 100%;
  max-width: 340px;
  display: block;
  margin: 8px auto;
}

.radarLegend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  justify-content: center;
  margin-top: 2px;
  font-size: 11px;
  color: var(--text-dim);
}

.radarLegendItem {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  transition: color 0.15s;
}

.radarLegendItem.highlighted {
  color: var(--text);
  font-weight: 700;
}

.radarLegendItem i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}

.radarRing {
  fill: none;
  stroke: var(--border);
  stroke-width: 1;
}

.radarSpoke {
  stroke: var(--border);
  stroke-width: 1;
}

.radarShape {
  fill: rgba(79, 140, 255, 0.35);
  stroke: var(--accent);
  stroke-width: 2.5;
}

.radarLabel {
  fill: var(--text);
  font-size: 12px;
  font-weight: 600;
  text-anchor: middle;
}

.radarValue {
  fill: var(--text-dim);
  font-size: 11px;
  text-anchor: middle;
}

/* Verbunden statt loses Modal: Popover mit Pfeil zur gehoverten Material-Karte, slidet beim Erscheinen
   rein (gleiches Prinzip wie .tooltipPopover - hidden+visible-Klasse zusammen, kein Extra-Trick nötig,
   das funktioniert im Rest der App schon zuverlässig so). */
.radarPopover {
  position: fixed;
  z-index: 1500;
  width: 380px;
  max-width: calc(100vw - 32px);
  background: var(--panel-bg);
  border: 1px solid var(--accent-dim);
  border-radius: 10px;
  padding: 16px 18px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5);
  opacity: 0;
  transform: translateX(-10px);
  transition: opacity 0.18s, transform 0.18s;
  pointer-events: none;
}

.radarPopover.visible {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}

.radarPopoverTitle {
  font-weight: 600;
  font-size: 15px;
  margin-bottom: 4px;
}

/* Popover sitzt jetzt immer rechts vom Panel (siehe showRadarPopover) - Pfeil zeigt deshalb immer nach
   links zurück zum Panel, keine Rechts-Variante mehr nötig. */
.radarPopoverArrow {
  position: absolute;
  top: 50%;
  left: -7px;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  background: var(--panel-bg);
  border-left: 1px solid var(--accent-dim);
  border-bottom: 1px solid var(--accent-dim);
  transform: rotate(45deg);
}
