import { useEffect, useRef, useState } from 'react'
import Icon from './components/Icon'
import {
  onMessage, notifyReady,
  listElemente, createWall, createDecke, createDach,
  createFenster, createTuer, createAussparung, createTreppe,
  createStuetze, createTraeger, createRaum,
  exportRaeume,
  openSwisstopo, openSwisstopoDialog, openOsmDialog,
  updateElement, deleteElement, regenerateAllElements,
} from './lib/rhinoBridge'

const labelXs = {
  fontSize: 10, fontWeight: 600, color: 'var(--text-muted)',
  letterSpacing: '0.06em', textTransform: 'uppercase',
}

function fmtNum(v) {
  if (v == null || v === '') return ''
  const n = Number(v)
  if (Number.isNaN(n)) return String(v)
  return Number(n.toFixed(4)).toString()
}

function ReferenzSelector({ value, onChange }) {
  const opts = [
    { code: 'left',  label: 'Links',  hint: 'Achse auf linker Aussenseite' },
    { code: 'mid',   label: 'Mittig', hint: 'Achse zentriert (Standard)' },
    { code: 'right', label: 'Rechts', hint: 'Achse auf rechter Aussenseite' },
  ]
  return (
    <div style={{ flex: 1, display: 'flex', gap: 2 }}>
      {opts.map(o => (
        <button
          key={o.code}
          onClick={() => onChange(o.code)}
          className={value === o.code ? 'btn-contained' : 'btn-outlined'}
          style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}
          title={o.hint}
        >
          {o.label}
        </button>
      ))}
    </div>
  )
}

// Pill-Button — kompakt, Icon + Label horizontal, abgerundet
function PillButton({ icon, label, hint, onClick, onContextMenu, disabled,
                      hasMenu, badge }) {
  return (
    <button
      onClick={onClick}
      onContextMenu={onContextMenu}
      disabled={disabled}
      title={hint}
      style={{
        display: 'flex', alignItems: 'center', gap: 6,
        padding: '5px 10px 5px 8px',
        background: 'var(--bg-input)',
        border: '1px solid var(--border-light)',
        borderRadius: 999,
        cursor: disabled ? 'not-allowed' : 'pointer',
        opacity: disabled ? 0.4 : 1,
        transition: 'background 0.1s, border-color 0.1s',
        fontSize: 11, fontWeight: 500,
        color: 'var(--text-primary)',
        whiteSpace: 'nowrap',
        position: 'relative',
      }}
      onMouseEnter={(e) => { if (!disabled) {
        e.currentTarget.style.background = 'var(--bg-item-hover)'
        e.currentTarget.style.borderColor = 'var(--accent)'
      }}}
      onMouseLeave={(e) => {
        e.currentTarget.style.background = 'var(--bg-input)'
        e.currentTarget.style.borderColor = 'var(--border-light)'
      }}
    >
      <Icon name={icon} size={14} style={{ color: 'var(--accent)' }} />
      <span>{label}</span>
      {hasMenu && (
        <Icon name="expand_more" size={12}
              style={{ color: 'var(--text-muted)', marginLeft: -2 }} />
      )}
      {badge && (
        <span style={{
          fontSize: 9, padding: '1px 5px', borderRadius: 8,
          background: 'var(--bg-section)', color: 'var(--text-muted)',
          marginLeft: 2,
        }}>{badge}</span>
      )}
    </button>
  )
}

// Vertikale Kategorie-Gruppe mit Label + Pills, die wrappen
function PillGroup({ label, children }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
      <span style={{
        fontSize: 9, color: 'var(--text-muted)',
        letterSpacing: '0.08em', textTransform: 'uppercase',
        fontWeight: 600,
      }}>
        {label}
      </span>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
        {children}
      </div>
    </div>
  )
}

// Popup-Menue (relativ positioniert) fuer Untertypen wie Treppen-Art
function PopupMenu({ items, onClose }) {
  useEffect(() => {
    const onDocClick = () => onClose()
    document.addEventListener('click', onDocClick)
    return () => document.removeEventListener('click', onDocClick)
  }, [onClose])
  return (
    <div style={{
      position: 'absolute',
      top: '100%', left: 0,
      marginTop: 4,
      background: 'var(--bg-section)',
      border: '1px solid var(--border)',
      borderRadius: 6,
      boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
      padding: 4,
      zIndex: 100,
      minWidth: 140,
    }}>
      {items.map((it, i) => (
        <button key={i}
          onClick={(e) => { e.stopPropagation(); it.onClick(); onClose() }}
          disabled={it.disabled}
          title={it.hint || ''}
          style={{
            display: 'flex', alignItems: 'center', gap: 6,
            width: '100%', padding: '6px 8px',
            background: 'transparent',
            border: 'none', borderRadius: 4,
            cursor: it.disabled ? 'not-allowed' : 'pointer',
            opacity: it.disabled ? 0.4 : 1,
            color: 'var(--text-primary)',
            fontSize: 11, textAlign: 'left',
          }}
          onMouseEnter={(e) => { if (!it.disabled) {
            e.currentTarget.style.background = 'var(--bg-item-hover)'
          }}}
          onMouseLeave={(e) => {
            e.currentTarget.style.background = 'transparent'
          }}>
          {it.icon && <Icon name={it.icon} size={12}
                            style={{ color: 'var(--accent)' }} />}
          <span style={{ flex: 1 }}>{it.label}</span>
          {it.badge && (
            <span style={{ fontSize: 9, color: 'var(--text-muted)' }}>{it.badge}</span>
          )}
        </button>
      ))}
    </div>
  )
}

const KIND_META = {
  wand:     { icon: 'view_week',     label: 'Wand',     color: '#a8b8c8' },
  decke:    { icon: 'layers',        label: 'Decke',    color: '#b8a890' },
  dach:     { icon: 'roofing',       label: 'Dach',     color: '#c89878' },
  fenster:  { icon: 'window',        label: 'Fenster',  color: '#90b8d0' },
  tuer:     { icon: 'sensor_door',   label: 'Tür',      color: '#c8a878' },
  treppe:   { icon: 'stairs',        label: 'Treppe',   color: '#a0c0a0' },
  stuetze:  { icon: 'square_foot',   label: 'Stütze',   color: '#5fa896' },
  traeger:  { icon: 'horizontal_rule', label: 'Träger', color: '#7fc8a8' },
  raum:     { icon: 'crop_free',     label: 'Raum',     color: '#a0a8b0' },
  aussparung: { icon: 'rectangle',  label: 'Aussparung', color: '#9090a0' },
}

const RAUM_RUNDUNGEN = ['exakt', '0.01', '0.1', '0.5', '1']
const RAUM_ALIGN = [
  { code: 'links',  label: 'Links',  icon: 'format_align_left' },
  { code: 'mid',    label: 'Mitte',  icon: 'format_align_center' },
  { code: 'rechts', label: 'Rechts', icon: 'format_align_right' },
]
const RAUM_SIA_KINDS = [
  { code: '',    label: '—',    color: 'transparent', hint: '' },
  { code: 'hnf', label: 'HNF',  color: '#e8a8a8', hint: 'Hauptnutzfläche' },
  { code: 'nnf', label: 'NNF',  color: '#e8c498', hint: 'Nebennutzfläche' },
  { code: 'vf',  label: 'VF',   color: '#e8d878', hint: 'Verkehrsfläche' },
  { code: 'ff',  label: 'FF',   color: '#a8c8e0', hint: 'Funktionsfläche' },
]

const PROFIL_META = {
  quadrat:  { label: 'Quadrat',  icon: 'square' },
  rechteck: { label: 'Rechteck', icon: 'rectangle' },
  rund:     { label: 'Rund',     icon: 'circle' },
  i_profil: { label: 'I-Profil', icon: 'view_column' },
  rohr:     { label: 'Rohr',     icon: 'radio_button_unchecked' },
}

function ElementList({ elements }) {
  // Gruppiert nach kind, dann nach geschoss-Reihenfolge wie sie reinkommen
  const grouped = {}
  for (const el of elements) {
    const k = el.kind || 'unknown'
    if (!grouped[k]) grouped[k] = []
    grouped[k].push(el)
  }
  const kindOrder = ['wand', 'decke', 'dach', 'fenster', 'tuer', 'aussparung',
                      'treppe', 'stuetze', 'traeger', 'raum']
  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10,
      background: 'var(--bg-section)',
      border: '1px solid var(--border)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <span style={{ ...labelXs, flex: 1 }}>Alle Elemente</span>
        <span style={{ fontSize: 9, color: 'var(--text-muted)' }}>{elements.length}</span>
      </div>
      {kindOrder.map(k => {
        const arr = grouped[k]
        if (!arr || arr.length === 0) return null
        const meta = KIND_META[k] || { icon: 'help', label: k, color: '#888' }
        return (
          <div key={k} style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
            <div style={{
              display: 'flex', alignItems: 'center', gap: 5,
              fontSize: 9, color: 'var(--text-muted)',
              letterSpacing: '0.06em', textTransform: 'uppercase',
              fontWeight: 600,
              paddingLeft: 2,
            }}>
              <Icon name={meta.icon} size={10}
                    style={{ color: meta.color }} />
              <span>{meta.label}</span>
              <span style={{ color: 'var(--text-muted)', opacity: 0.6 }}>·</span>
              <span style={{ opacity: 0.8 }}>{arr.length}</span>
            </div>
            {arr.map(el => (
              <ElementListRow key={el.id} el={el} meta={meta} />
            ))}
          </div>
        )
      })}
    </div>
  )
}

function ElementListRow({ el, meta }) {
  const secondary = (() => {
    if (el.kind === 'fenster' || el.kind === 'tuer')
      return `${fmtNum(el.breite)}×${fmtNum(el.hoehe)} m`
    if (el.kind === 'treppe')
      return `${el.nStufen} St · H ${fmtNum(el.ok - el.uk)} m`
    if (el.kind === 'dach' && el.neigung != null)
      return `d ${fmtNum(el.dicke)} · ${fmtNum(el.neigung)}°`
    if (el.kind === 'stuetze' || el.kind === 'traeger') {
      const pl = (PROFIL_META[el.profil] || {}).label || el.profil
      const dim = (el.profil === 'rund' || el.profil === 'rohr')
        ? `Ø${fmtNum(el.d)}`
        : `${fmtNum(el.b)}×${fmtNum(el.h)}`
      return `${pl} ${dim}`
    }
    if (el.kind === 'raum') {
      const label = el.nummer ? `${el.nummer} ${el.name}` : el.name
      return label || 'Raum'
    }
    if (el.kind === 'aussparung')
      return `${fmtNum(el.area)} m²`
    return `d ${fmtNum(el.dicke)} m`
  })()
  const tertiary = (() => {
    if (el.kind === 'fenster')
      return `Br ${fmtNum(el.brueest)}`
    if (el.kind === 'tuer' || el.kind === 'treppe')
      return ''
    if (el.kind === 'stuetze')
      return `H ${fmtNum(el.ok - el.uk)} m`
    if (el.kind === 'traeger')
      return `L ${fmtNum(el.axisLen)} m`
    if (el.kind === 'raum') {
      const siaInfo = RAUM_SIA_KINDS.find(s => s.code === (el.sia || ''))
      const tag = siaInfo && siaInfo.code ? `${siaInfo.label} · ` : ''
      return `${tag}${el.areaFmt} m²`
    }
    if (el.kind === 'aussparung')
      return `U ${fmtNum(el.umfang)} m`
    return `UK ${fmtNum(el.uk)} · OK ${fmtNum(el.ok)}`
  })()
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 8,
      padding: '5px 8px',
      background: el.selected ? 'var(--bg-item-active)' : 'transparent',
      border: '1px solid ' + (el.selected ? 'var(--accent)' : 'transparent'),
      borderLeft: '3px solid ' + (el.selected ? 'var(--accent)' : meta.color),
      borderRadius: 'var(--r)',
      fontSize: 10,
    }}>
      <span style={{
        fontFamily: 'DM Mono, monospace', fontWeight: 500,
        color: 'var(--text-primary)',
        minWidth: 40,
      }}>
        {el.geschossName || '—'}
      </span>
      <span style={{
        flex: 1, fontFamily: 'DM Mono, monospace',
        color: 'var(--text-primary)',
      }}>
        {secondary}
      </span>
      {tertiary && (
        <span style={{
          color: 'var(--text-muted)', fontFamily: 'DM Mono, monospace',
          fontSize: 9,
        }}>
          {tertiary}
        </span>
      )}
    </div>
  )
}


function NeuesElementSection({ noGeschoss, activeName }) {
  const [treppeMenuOpen, setTreppeMenuOpen] = useState(false)
  const [stuetzeMenuOpen, setStuetzeMenuOpen] = useState(false)
  const [traegerMenuOpen, setTraegerMenuOpen] = useState(false)
  const treppeWrapperRef = useRef(null)
  const dis = noGeschoss
  const baseHint = (label) =>
    noGeschoss ? 'Erst im Ebenen-Manager ein Geschoss aktivieren'
               : `${label} auf ${activeName}`

  const openTreppeMenu = (e) => {
    e.preventDefault()
    setTreppeMenuOpen(true)
  }
  const openStuetzeMenu = (e) => { e.preventDefault(); setStuetzeMenuOpen(true) }
  const openTraegerMenu = (e) => { e.preventDefault(); setTraegerMenuOpen(true) }

  const treppeItems = [
    { icon: 'stairs', label: 'Gerade Treppe',
      hint: 'Lauflinie mit 2 Punkten',
      onClick: () => createTreppe({ treppeArt: 'gerade' }) },
    { icon: 'turn_right', label: 'L-Treppe',
      hint: '3 Punkte: Start, Podest-Ecke, Ende',
      onClick: () => createTreppe({ treppeArt: 'l' }) },
    { icon: 'rotate_right', label: 'Wendeltreppe',
      hint: '3 Punkte: Mittelpunkt, Start-Lauflinie, End-Lauflinie',
      onClick: () => createTreppe({ treppeArt: 'wendel' }) },
  ]

  const profilItems = (factory) => [
    { icon: 'square',  label: 'Quadrat',
      hint: 'B × B', onClick: () => factory('quadrat') },
    { icon: 'rectangle', label: 'Rechteck',
      hint: 'B × H', onClick: () => factory('rechteck') },
    { icon: 'circle', label: 'Rund',
      hint: 'Durchmesser D', onClick: () => factory('rund') },
    { icon: 'view_column', label: 'I-Profil',
      hint: 'Stahl HEB-Stil — Flansch B, Höhe H, Wand t',
      onClick: () => factory('i_profil') },
    { icon: 'radio_button_unchecked', label: 'Rohr',
      hint: 'Hohlzylinder — D Aussen, t Wanddicke',
      onClick: () => factory('rohr') },
  ]

  const stuetzeItems = profilItems((profil) =>
    createStuetze({ profil }))
  const traegerItems = profilItems((profil) =>
    createTraeger({ profil }))

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 10,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--border)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 10 }}>
        <span style={labelXs}>Neues Element</span>
        <div style={{ flex: 1 }} />
        <span style={{ color: noGeschoss ? 'var(--danger)' : 'var(--text-muted)' }}>
          {noGeschoss ? 'Kein Geschoss aktiv' : 'auf'}
        </span>
        {!noGeschoss && (
          <span className="chip chip-accent" style={{
            fontSize: 10, fontFamily: 'DM Mono, monospace', fontWeight: 600,
          }}>
            {activeName}
          </span>
        )}
      </div>

      <PillGroup label="Konstruktion">
        <PillButton icon="view_week" label="Wand"
          hint={baseHint('Wand zeichnen')} disabled={dis}
          onClick={() => createWall({ geschoss: '' })} />
        <PillButton icon="layers" label="Decke"
          hint={baseHint('Decke zeichnen')} disabled={dis}
          onClick={() => createDecke({ geschoss: '' })} />
        <PillButton icon="roofing" label="Dach"
          hint={baseHint('Pultdach zeichnen — Traufe = 1. Kante')} disabled={dis}
          onClick={() => createDach({ geschoss: '' })} />
      </PillGroup>

      <PillGroup label="Öffnungen">
        <PillButton icon="window" label="Fenster"
          hint={dis ? baseHint('Fenster') :
            'Erst Wand-Achse wählen, dann Punkt darauf'} disabled={dis}
          onClick={() => createFenster({})} />
        <PillButton icon="sensor_door" label="Tür"
          hint={dis ? baseHint('Tür') :
            'Erst Wand-Achse wählen, dann Punkt darauf'} disabled={dis}
          onClick={() => createTuer({})} />
        <PillButton icon="rectangle" label="Aussparung"
          hint={dis ? baseHint('Aussparung') :
            'Outline auf einer Decke zeichnen — wird automatisch ausgeschnitten'}
          disabled={dis}
          onClick={() => createAussparung({})} />
      </PillGroup>

      <PillGroup label="Erschliessung">
        <div ref={treppeWrapperRef} style={{ position: 'relative' }}>
          <PillButton icon="stairs" label="Treppe" hasMenu
            hint={dis ? baseHint('Treppe') :
              'Klick: gerade Treppe · Rechtsklick: Typ wählen'}
            disabled={dis}
            onClick={() => createTreppe({ treppeArt: 'gerade' })}
            onContextMenu={openTreppeMenu} />
          {treppeMenuOpen && (
            <PopupMenu items={treppeItems}
                       onClose={() => setTreppeMenuOpen(false)} />
          )}
        </div>
      </PillGroup>

      <PillGroup label="Tragwerk">
        <div style={{ position: 'relative' }}>
          <PillButton icon="square_foot" label="Stütze" hasMenu
            hint={dis ? baseHint('Stütze') :
              'Klick: Quadrat-Stütze · Rechtsklick: Profil wählen'}
            disabled={dis}
            onClick={() => createStuetze({})}
            onContextMenu={openStuetzeMenu} />
          {stuetzeMenuOpen && (
            <PopupMenu items={stuetzeItems}
                       onClose={() => setStuetzeMenuOpen(false)} />
          )}
        </div>
        <div style={{ position: 'relative' }}>
          <PillButton icon="horizontal_rule" label="Träger" hasMenu
            hint={dis ? baseHint('Träger') :
              'Klick: Rechteck-Träger · Rechtsklick: Profil wählen'}
            disabled={dis}
            onClick={() => createTraeger({})}
            onContextMenu={openTraegerMenu} />
          {traegerMenuOpen && (
            <PopupMenu items={traegerItems}
                       onClose={() => setTraegerMenuOpen(false)} />
          )}
        </div>
      </PillGroup>

      <PillGroup label="Raeume">
        <PillButton icon="crop_free" label="Raum"
          hint={dis ? baseHint('Raum') :
            'Outline zeichnen · Stempel zeigt Name + Fläche'}
          disabled={dis}
          onClick={() => createRaum({})} />
      </PillGroup>

      <PillGroup label="Importer">
        <PillButton icon="download" label="Swisstopo"
          hint="Vollautomatischer Import via swisstopo STAC-API: Adresse suchen, Radius wählen, Gebäude + Terrain + Luftbild holen"
          onClick={() => openSwisstopoDialog()} />
        <PillButton icon="public" label="OSM"
          hint="OpenStreetMap-Daten via Overpass-API als 2D-Linien: Strassen, Gebäudeumrisse, Wasser, Grünflächen, Wege"
          onClick={() => openOsmDialog()} />
        <PillButton icon="map" label="Karte"
          hint="Öffnet map.geo.admin.ch im Browser zur visuellen Inspektion"
          onClick={() => openSwisstopo('both')} />
      </PillGroup>
    </div>
  )
}


export default function ElementeApp() {
  const [state, setState] = useState({
    elements: [], geschosse: [], selection: null,
    activeGeschoss: '', activeGeschossName: '',
  })
  // Defaults werden vom Backend (sticky) verwaltet — letzte Werte aus
  // dem Rhino-Prompt bleiben fuer den naechsten Klick erhalten.

  useEffect(() => {
    onMessage('STATE', (s) => setState(prev => ({ ...prev, ...s })))
    notifyReady()
  }, [])

  const elements = state.elements || []
  const geschosse = state.geschosse || []
  const selected = elements.find(el => el.id === state.selection)
  const activeName = state.activeGeschossName || ''
  const noGeschoss = !state.activeGeschoss

  return (
    <div style={{
      display: 'flex', flexDirection: 'column',
      height: '100vh', overflow: 'hidden',
      background: 'var(--bg-base)', color: 'var(--text-primary)',
      fontFamily: 'var(--font)', fontSize: 11,
    }}>
      {/* Header */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 6,
        padding: '8px 10px', borderBottom: '1px solid var(--border)',
        flexShrink: 0,
      }}>
        <span style={{ flex: 1, fontWeight: 600 }}>Elemente</span>
        <span className="chip" style={{ fontSize: 8 }}>{elements.length}</span>
        <button
          onClick={() => exportRaeume()}
          className="btn-icon-tonal"
          disabled={!elements.some(e => e.kind === 'raum')}
          title="Raumliste als CSV exportieren"
        >
          <Icon name="download" size={14} />
        </button>
        <button
          onClick={() => regenerateAllElements()}
          className="btn-icon-tonal"
          disabled={elements.length === 0}
          title="Alle Elemente neu generieren (z.B. nach Geschoss-Änderung)"
        >
          <Icon name="sync" size={14} />
        </button>
        <button
          onClick={() => listElemente()}
          className="btn-icon-tonal"
          title="Aktualisieren"
        >
          <Icon name="refresh" size={14} />
        </button>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden', padding: 8 }}>
        {/* Element-Toolbar: kategorisierte Pills */}
        <NeuesElementSection
          noGeschoss={noGeschoss}
          activeName={activeName}
        />


        {/* Properties */}
        {selected ? (
          selected.kind === 'wand' ? (
            <WallProperties wall={selected} geschosse={geschosse}
              materials={state.materials || []}
              onUpdate={(p) => updateElement(selected.id, p)}
              onDelete={() => { if (window.confirm('Wand löschen?')) deleteElement(selected.id) }} />
          ) : selected.kind === 'decke' ? (
            <DeckenProperties decke={selected} geschosse={geschosse}
              onUpdate={(p) => updateElement(selected.id, p)}
              onDelete={() => { if (window.confirm('Decke löschen?')) deleteElement(selected.id) }} />
          ) : selected.kind === 'dach' ? (
            <DachProperties dach={selected} geschosse={geschosse}
              onUpdate={(p) => updateElement(selected.id, p)}
              onDelete={() => { if (window.confirm('Dach löschen?')) deleteElement(selected.id) }} />
          ) : selected.kind === 'treppe' ? (
            <TreppeProperties treppe={selected} geschosse={geschosse}
              onUpdate={(p) => updateElement(selected.id, p)}
              onDelete={() => { if (window.confirm('Treppe löschen?')) deleteElement(selected.id) }} />
          ) : (selected.kind === 'stuetze' || selected.kind === 'traeger') ? (
            <TragwerkProperties el={selected}
              onUpdate={(p) => updateElement(selected.id, p)}
              onDelete={() => {
                const lbl = (KIND_META[selected.kind] || {}).label || 'Element'
                if (window.confirm(`${lbl} löschen?`)) deleteElement(selected.id)
              }} />
          ) : selected.kind === 'raum' ? (
            <RaumProperties raum={selected} geschosse={geschosse}
              hatchPatterns={state.hatchPatterns}
              onUpdate={(p) => updateElement(selected.id, p)}
              onDelete={() => { if (window.confirm('Raum löschen?')) deleteElement(selected.id) }} />
          ) : selected.kind === 'aussparung' ? (
            <AussparungProperties aussp={selected}
              onDelete={() => { if (window.confirm('Aussparung löschen?')) deleteElement(selected.id) }} />
          ) : (
            <OeffnungProperties oeff={selected}
              onUpdate={(p) => updateElement(selected.id, p)}
              onDelete={() => {
                const label = selected.kind === 'fenster' ? 'Fenster' : 'Tür'
                if (window.confirm(`${label} löschen?`)) deleteElement(selected.id)
              }} />
          )
        ) : (
          <div style={{
            padding: '20px 16px', textAlign: 'center',
            color: 'var(--text-muted)', fontSize: 11,
            border: '1px dashed var(--border)',
            borderRadius: 'var(--r-lg)',
            background: 'var(--bg-section)',
            marginBottom: 8,
          }}>
            <Icon name="touch_app" size={24} style={{ color: 'var(--text-muted)', opacity: 0.5 }} />
            <div style={{ marginTop: 6 }}>Kein Element selektiert.</div>
            <div style={{ marginTop: 4, fontSize: 10 }}>
              Eine Wand-Achse oder Decken-Outline in Rhino auswählen.
            </div>
          </div>
        )}

        {/* Liste aller Elemente */}
        {elements.length > 0 && (
          <ElementList elements={elements} />
        )}
      </div>
    </div>
  )
}

function NumberField({ label, value, onCommit, width, step }) {
  const [raw, setRaw] = useState(String(value))
  useEffect(() => { setRaw(String(value)) }, [value])
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
      <span style={{ fontSize: 10, color: 'var(--text-muted)',
                      width: width || 60 }}>{label}</span>
      <input type="text" value={raw}
             onChange={(e) => setRaw(e.target.value)}
             onBlur={() => {
               const v = parseFloat(raw)
               if (!Number.isNaN(v) && v !== value) onCommit(v)
               else setRaw(String(value))
             }}
             onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
             style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
    </div>
  )
}

function TragwerkProperties({ el, onUpdate, onDelete }) {
  const meta = KIND_META[el.kind] || { icon: 'square_foot', label: 'Element' }
  const profil = el.profil || 'quadrat'
  const profilOpts = ['quadrat', 'rechteck', 'rund', 'i_profil', 'rohr']
  const isRound = (profil === 'rund' || profil === 'rohr')
  const showH = (profil === 'rechteck' || profil === 'i_profil')
  const showT = (profil === 'rohr' || profil === 'i_profil')
  const isStuetze = (el.kind === 'stuetze')
  const [zRaw, setZRaw] = useState(el.zOver || '')
  useEffect(() => { setZRaw(el.zOver || '') }, [el.id, el.zOver])

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--accent)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name={meta.icon} size={13}
              style={{ color: 'var(--accent)', marginRight: 6 }} />
        <span style={{ ...labelXs, flex: 1, color: 'var(--accent)' }}>
          {meta.label} · {el.geschossName}
        </span>
        <button className="btn-icon-sm btn-icon-danger" onClick={onDelete}
                title="Löschen">
          <Icon name="delete" size={12} />
        </button>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>
          Profil
        </span>
        <select value={profil}
                onChange={(e) => onUpdate({ profil: e.target.value })}
                style={{ flex: 1, fontSize: 11 }}>
          {profilOpts.map(p => (
            <option key={p} value={p}>
              {(PROFIL_META[p] || {}).label || p}
            </option>
          ))}
        </select>
      </div>

      {!isRound && (
        <NumberField label="Breite" value={el.b}
                      onCommit={(v) => onUpdate({ b: v })} />
      )}
      {showH && (
        <NumberField label="Höhe" value={el.h}
                      onCommit={(v) => onUpdate({ h: v })} />
      )}
      {isRound && (
        <NumberField label="Durchm." value={el.d}
                      onCommit={(v) => onUpdate({ d: v })} />
      )}
      {showT && (
        <NumberField label="Wanddicke" value={el.t}
                      onCommit={(v) => onUpdate({ t: v })} />
      )}
      <NumberField label="Drehung°" value={el.angle}
                    onCommit={(v) => onUpdate({ angle: v })} />

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>
          {isStuetze ? 'Höhe' : 'OK über UK'}
        </span>
        <input type="text" value={zRaw}
                placeholder={`auto (${fmtNum(el.ok - el.uk)} m)`}
                onChange={(e) => setZRaw(e.target.value)}
                onBlur={() => {
                  const v = zRaw.trim()
                  if (v === '') {
                    if ((el.zOver || '') !== '') onUpdate({ zOver: '' })
                  } else {
                    const n = parseFloat(v)
                    if (!Number.isNaN(n)) onUpdate({ zOver: n })
                  }
                }}
                onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
                style={{ flex: 1, fontSize: 11,
                          fontFamily: 'DM Mono, monospace' }} />
      </div>

      <div style={{
        display: 'flex', justifyContent: 'space-between',
        fontSize: 10, color: 'var(--text-muted)',
        fontFamily: 'DM Mono, monospace',
        paddingTop: 4, borderTop: '1px dashed var(--border)',
      }}>
        <span>UK {fmtNum(el.uk)} · OK {fmtNum(el.ok)}</span>
        {!isStuetze && <span>L {fmtNum(el.axisLen)} m</span>}
      </div>
    </div>
  )
}

function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns }) {
  const [name, setName] = useState(raum.name || 'Raum')
  const [nummer, setNummer] = useState(raum.nummer || '')
  const [txtH, setTxtH] = useState(String(raum.txtH || 0.20))
  useEffect(() => {
    setName(raum.name || 'Raum')
    setNummer(raum.nummer || '')
    setTxtH(String(raum.txtH || 0.20))
  }, [raum.id, raum.name, raum.nummer, raum.txtH])

  // Aktueller Wert von raum_fuellung: "" | "Solid" | "Hatch1" | … | "ByLayer"
  const fuell = raum.fuellung || ''
  const patternList = hatchPatterns || []

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--accent)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name="crop_free" size={13}
              style={{ color: 'var(--accent)', marginRight: 6 }} />
        <span style={{ ...labelXs, flex: 1, color: 'var(--accent)' }}>
          Raum · {raum.geschossName}
        </span>
        <button className="btn-icon-sm btn-icon-danger" onClick={onDelete}
                title="Löschen">
          <Icon name="delete" size={12} />
        </button>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>Geschoss</span>
        <select value={raum.geschoss}
                onChange={(e) => onUpdate({ geschoss: e.target.value })}
                style={{ flex: 1, fontSize: 11 }}>
          {geschosse.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>Nummer</span>
        <input type="text" value={nummer}
               onChange={(e) => setNummer(e.target.value)}
               onBlur={() => {
                 if (nummer !== (raum.nummer || '')) onUpdate({ nummer })
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11,
                         fontFamily: 'DM Mono, monospace' }} />
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>Name</span>
        <input type="text" value={name}
               onChange={(e) => setName(e.target.value)}
               onBlur={() => {
                 const v = (name || 'Raum').trim()
                 if (v !== raum.name) onUpdate({ name: v })
                 else setName(v)
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11 }} />
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>Typ</span>
        <select value={raum.sia || ''}
                onChange={(e) => onUpdate({ sia: e.target.value })}
                title="SIA 416 Flaechenklassifikation"
                style={{ flex: 1, fontSize: 11 }}>
          {RAUM_SIA_KINDS.map(s => (
            <option key={s.code} value={s.code}>
              {s.code === '' ? '—'
                : `${s.label} — ${s.hint}`}
            </option>
          ))}
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>Füllung</span>
        <select value={fuell}
                onChange={(e) => onUpdate({ fuellung: e.target.value })}
                title="Hatch-Pattern im Normalmodus. Bei aktivem SIA-Modus wird klassifizierten Raeumen automatisch Solid forciert."
                style={{ flex: 1, fontSize: 11 }}>
          <option value="">Keine</option>
          <option value="ByLayer">Ebene (folgt Layer-Farbe)</option>
          {patternList.length > 0 && <option disabled>──────</option>}
          {patternList.map(p => (
            <option key={p} value={p}>{p}</option>
          ))}
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>Rundung</span>
        <select value={raum.rundung || ''}
                onChange={(e) => onUpdate({ rundung: e.target.value })}
                style={{ flex: 1, fontSize: 11 }}
                title="Rundung der Flächenangabe. Leer = Default aus Mass-Style.">
          <option value="">Aus Mass-Style</option>
          <option value="exakt">Exakt (2 Nachkommastellen)</option>
          <option value="0.01">auf 0.01 m²</option>
          <option value="0.1">auf 0.1 m²</option>
          <option value="0.5">auf 0.5 m²</option>
          <option value="1">auf 1 m²</option>
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>Ausrichtung</span>
        <div style={{ flex: 1, display: 'flex', gap: 2 }}>
          {RAUM_ALIGN.map(a => (
            <button key={a.code}
              onClick={() => onUpdate({ align: a.code })}
              className={(raum.align || 'mid') === a.code ? 'btn-contained' : 'btn-outlined'}
              style={{ flex: 1, padding: '3px 4px', fontSize: 10,
                        display: 'flex', alignItems: 'center',
                        justifyContent: 'center', gap: 3 }}
              title={a.label}>
              <Icon name={a.icon} size={12} />
            </button>
          ))}
        </div>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 60 }}>Texthöhe</span>
        <input type="text" value={txtH}
               onChange={(e) => setTxtH(e.target.value)}
               onBlur={() => {
                 const v = parseFloat(txtH)
                 if (v > 0 && v !== raum.txtH) onUpdate({ txtH: v })
                 else setTxtH(String(raum.txtH))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11,
                         fontFamily: 'DM Mono, monospace' }} />
      </div>

      <div style={{
        display: 'flex', justifyContent: 'space-between',
        fontSize: 10, color: 'var(--text-muted)',
        fontFamily: 'DM Mono, monospace',
        paddingTop: 4, borderTop: '1px dashed var(--border)',
      }}>
        <span>Flaeche: <strong style={{ color: 'var(--text-primary)' }}>
          {raum.areaFmt} m²</strong></span>
        <span>Umfang: {fmtNum(raum.umfang)} m</span>
      </div>
    </div>
  )
}

function AussparungProperties({ aussp, onDelete }) {
  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--accent)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name="rectangle" size={13}
              style={{ color: 'var(--accent)', marginRight: 6 }} />
        <span style={{ ...labelXs, flex: 1, color: 'var(--accent)' }}>
          Aussparung · {aussp.geschossName}
        </span>
        <button className="btn-icon-sm btn-icon-danger" onClick={onDelete}
                title="Löschen">
          <Icon name="delete" size={12} />
        </button>
      </div>
      <div style={{
        display: 'flex', justifyContent: 'space-between',
        fontSize: 10, color: 'var(--text-muted)',
        fontFamily: 'DM Mono, monospace',
        paddingTop: 4, borderTop: '1px dashed var(--border)',
      }}>
        <span>Fläche: {fmtNum(aussp.area)} m²</span>
        <span>Umfang: {fmtNum(aussp.umfang)} m</span>
      </div>
      <div style={{
        fontSize: 9, color: 'var(--text-muted)', fontStyle: 'italic',
        lineHeight: 1.4,
      }}>
        Outline in Rhino editieren (Punkte ziehen, _Reshape, …) — die
        Eltern-Decke wird automatisch nachgerechnet.
      </div>
    </div>
  )
}

function WallProperties({ wall, geschosse, materials, onUpdate, onDelete }) {
  const [dicke, setDicke] = useState(String(wall.dicke))
  const [ukOver, setUkOver] = useState(wall.ukOverride)
  const [okOver, setOkOver] = useState(wall.okOverride)
  useEffect(() => {
    setDicke(String(wall.dicke))
    setUkOver(wall.ukOverride)
    setOkOver(wall.okOverride)
  }, [wall.id, wall.dicke, wall.ukOverride, wall.okOverride])

  const ukAuto = ukOver === '' || ukOver == null
  const okAuto = okOver === '' || okOver == null

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--accent)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name="view_week" size={13} style={{ color: 'var(--accent)', marginRight: 6 }} />
        <span style={{ ...labelXs, flex: 1, color: 'var(--accent)' }}>
          Wand · {wall.geschossName}
        </span>
        <button className="btn-icon-sm btn-icon-danger" onClick={onDelete} title="Löschen">
          <Icon name="delete" size={12} />
        </button>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Geschoss</span>
        <select value={wall.geschoss}
                onChange={(e) => onUpdate({ geschoss: e.target.value })}
                style={{ flex: 1, fontSize: 11 }}>
          {geschosse.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Aufbau</span>
        <div style={{ flex: 1, display: 'flex', gap: 2 }}>
          <button
            onClick={() => onUpdate({ layered: false })}
            className={!wall.layered ? 'btn-contained' : 'btn-outlined'}
            style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}
            title="Eine homogene Wand-Schicht (Standard)">Solid</button>
          <button
            onClick={() => onUpdate({ layered: true })}
            className={wall.layered ? 'btn-contained' : 'btn-outlined'}
            style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}
            title="Mehrere Schichten mit individuellen Dicken und Farben"
            >Mehrschichtig</button>
        </div>
      </div>

      {!wall.layered && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Dicke</span>
          <input type="text" value={dicke}
                 onChange={(e) => setDicke(e.target.value)}
                 onBlur={() => {
                   const v = parseFloat(dicke)
                   if (v > 0 && v !== wall.dicke) onUpdate({ dicke: v })
                   else setDicke(String(wall.dicke))
                 }}
                 onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
                 style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        </div>
      )}

      {wall.layered && (
        <LayersEditor layers={wall.layers || []}
                       materials={materials}
                       onChange={(layers) => onUpdate({ layers })} />
      )}

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Referenz</span>
        <ReferenzSelector value={wall.referenz || 'mid'}
                          onChange={(v) => onUpdate({ referenz: v })} />
      </div>

      <AutoOverrideField label="UK" auto={ukAuto} autoValue={wall.uk}
        rawValue={ukOver} onChangeRaw={setUkOver}
        onToggle={() => onUpdate({ ukOverride: ukAuto ? wall.uk : '' })}
        onCommit={() => {
          if (ukAuto) return
          const v = parseFloat(ukOver)
          if (!Number.isNaN(v)) onUpdate({ ukOverride: v })
        }} />

      <AutoOverrideField label="OK" auto={okAuto} autoValue={wall.ok}
        rawValue={okOver} onChangeRaw={setOkOver}
        onToggle={() => onUpdate({ okOverride: okAuto ? wall.ok : '' })}
        onCommit={() => {
          if (okAuto) return
          const v = parseFloat(okOver)
          if (!Number.isNaN(v)) onUpdate({ okOverride: v })
        }} />
    </div>
  )
}

function LayersEditor({ layers, onChange, materials }) {
  const total = (layers || []).reduce((s, l) => s + (parseFloat(l.dicke) || 0), 0)
  const updateAt = (idx, patch) => {
    const next = (layers || []).map((l, i) => (i === idx ? { ...l, ...patch } : l))
    onChange(next)
  }
  const removeAt = (idx) => {
    const next = (layers || []).filter((_, i) => i !== idx)
    onChange(next.length ? next : [{ name: 'Schicht 1', dicke: 0.20, color: '#cccccc', material: '' }])
  }
  const addLayer = () => {
    const idx = (layers || []).length + 1
    onChange([ ...(layers || []),
      { name: `Schicht ${idx}`, dicke: 0.05, color: '#dddddd', material: '' } ])
  }
  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 4,
      padding: '6px 8px',
      background: 'var(--bg-input)',
      border: '1px solid var(--border-light)',
      borderRadius: 'var(--r)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 4,
                     fontSize: 9, color: 'var(--text-muted)',
                     letterSpacing: '0.06em', textTransform: 'uppercase',
                     fontWeight: 600 }}>
        <span style={{ flex: 1 }}>Schichten (links → rechts)</span>
        <span style={{ fontFamily: 'DM Mono, monospace', fontSize: 10,
                        color: 'var(--text-primary)', textTransform: 'none' }}>
          Σ {total.toFixed(3)} m
        </span>
      </div>
      {(layers || []).map((ly, i) => (
        <LayerRow key={i} layer={ly}
                   materials={materials}
                   onChange={(patch) => updateAt(i, patch)}
                   onRemove={() => removeAt(i)} />
      ))}
      <button onClick={addLayer} className="btn-outlined"
        style={{ padding: '3px 6px', fontSize: 10, marginTop: 2 }}
        title="Neue Schicht unten anfügen">
        + Schicht
      </button>
    </div>
  )
}

function LayerRow({ layer, materials, onChange, onRemove }) {
  const [name, setName] = useState(layer.name || '')
  const [dicke, setDicke] = useState(String(layer.dicke || 0))
  useEffect(() => {
    setName(layer.name || '')
    setDicke(String(layer.dicke || 0))
  }, [layer.name, layer.dicke])
  // Wenn ein Material gewaehlt ist, kommt die Farbe von dort — der Color-
  // Picker wird ausgegraut und zeigt die Material-Farbe an.
  const matList = materials || []
  const matName = layer.material || ''
  const matDef = matList.find(m => m.name === matName)
  const effectiveColor = matDef ? matDef.color : (layer.color || '#cccccc')
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
      <input type="color"
             value={effectiveColor}
             onChange={(e) => onChange({ color: e.target.value })}
             disabled={!!matDef}
             title={matDef
               ? `Farbe aus Material "${matDef.name}"`
               : 'Schicht-Farbe'}
             style={{
               width: 22, height: 22, padding: 0, border: 'none',
               background: 'transparent',
               cursor: matDef ? 'not-allowed' : 'pointer',
               opacity: matDef ? 0.5 : 1,
             }} />
      <select value={matName}
              onChange={(e) => {
                const m = matList.find(x => x.name === e.target.value)
                if (m) onChange({ material: m.name, color: m.color })
                else onChange({ material: '' })
              }}
              title="Material aus Bibliothek (steuert Farbe + Section-Hatch)"
              style={{ width: 92, fontSize: 10, padding: '2px 4px' }}>
        <option value="">(eigene)</option>
        {matList.map(m => (
          <option key={m.name} value={m.name}>{m.name}</option>
        ))}
      </select>
      <input type="text" value={name}
             placeholder="Name"
             onChange={(e) => setName(e.target.value)}
             onBlur={() => { if (name !== layer.name) onChange({ name }) }}
             onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
             style={{ flex: 1, fontSize: 10, padding: '2px 4px' }} />
      <input type="text" value={dicke}
             onChange={(e) => setDicke(e.target.value)}
             onBlur={() => {
               const v = parseFloat(dicke)
               if (v > 0 && v !== layer.dicke) onChange({ dicke: v })
               else setDicke(String(layer.dicke))
             }}
             onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
             style={{ width: 56, fontSize: 10, padding: '2px 4px',
                      fontFamily: 'DM Mono, monospace' }}
             title="Dicke in Metern" />
      <button onClick={onRemove} className="btn-icon-sm btn-icon-danger"
              title="Schicht entfernen" style={{ width: 18, height: 18 }}>
        <Icon name="close" size={10} />
      </button>
    </div>
  )
}

function DeckenProperties({ decke, geschosse, onUpdate, onDelete }) {
  const [dicke, setDicke] = useState(String(decke.dicke))
  const [ukOver, setUkOver] = useState(decke.ukOverride)
  const [okOver, setOkOver] = useState(decke.okOverride)
  useEffect(() => {
    setDicke(String(decke.dicke))
    setUkOver(decke.ukOverride)
    setOkOver(decke.okOverride)
  }, [decke.id, decke.dicke, decke.ukOverride, decke.okOverride])

  const ukAuto = ukOver === '' || ukOver == null
  const okAuto = okOver === '' || okOver == null

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--accent)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name="layers" size={13} style={{ color: 'var(--accent)', marginRight: 6 }} />
        <span style={{ ...labelXs, flex: 1, color: 'var(--accent)' }}>
          Decke · {decke.geschossName}
        </span>
        <button className="btn-icon-sm btn-icon-danger" onClick={onDelete} title="Löschen">
          <Icon name="delete" size={12} />
        </button>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Geschoss</span>
        <select value={decke.geschoss}
                onChange={(e) => onUpdate({ geschoss: e.target.value })}
                style={{ flex: 1, fontSize: 11 }}>
          {geschosse.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Dicke</span>
        <input type="text" value={dicke}
               onChange={(e) => setDicke(e.target.value)}
               onBlur={() => {
                 const v = parseFloat(dicke)
                 if (v > 0 && v !== decke.dicke) onUpdate({ dicke: v })
                 else setDicke(String(decke.dicke))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
      </div>

      <AutoOverrideField label="UK" auto={ukAuto} autoValue={decke.uk}
        rawValue={ukOver} onChangeRaw={setUkOver}
        onToggle={() => onUpdate({ ukOverride: ukAuto ? decke.uk : '' })}
        onCommit={() => {
          if (ukAuto) return
          const v = parseFloat(ukOver)
          if (!Number.isNaN(v)) onUpdate({ ukOverride: v })
        }} />

      <AutoOverrideField label="OK" auto={okAuto} autoValue={decke.ok}
        rawValue={okOver} onChangeRaw={setOkOver}
        onToggle={() => onUpdate({ okOverride: okAuto ? decke.ok : '' })}
        onCommit={() => {
          if (okAuto) return
          const v = parseFloat(okOver)
          if (!Number.isNaN(v)) onUpdate({ okOverride: v })
        }} />
    </div>
  )
}

function DachProperties({ dach, geschosse, onUpdate, onDelete }) {
  const [dicke,   setDicke]   = useState(String(dach.dicke))
  const [neigung, setNeigung] = useState(String(dach.neigung ?? 30))
  const [eaveIdx, setEaveIdx] = useState(String(dach.eaveIdx ?? 0))
  const [ukOver,  setUkOver]  = useState(dach.ukOverride)
  useEffect(() => {
    setDicke(String(dach.dicke))
    setNeigung(String(dach.neigung ?? 30))
    setEaveIdx(String(dach.eaveIdx ?? 0))
    setUkOver(dach.ukOverride)
  }, [dach.id, dach.dicke, dach.neigung, dach.eaveIdx, dach.ukOverride])
  const dachTyp = dach.dachTyp || 'pult'

  const ukAuto = ukOver === '' || ukOver == null

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--accent)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name="roofing" size={13} style={{ color: 'var(--accent)', marginRight: 6 }} />
        <span style={{ ...labelXs, flex: 1, color: 'var(--accent)' }}>
          {dachTyp === 'sattel' ? 'Satteldach' : dachTyp === 'walm' ? 'Walmdach' : 'Pultdach'} · {dach.geschossName}
        </span>
        <button className="btn-icon-sm btn-icon-danger" onClick={onDelete} title="Löschen">
          <Icon name="delete" size={12} />
        </button>
      </div>

      {/* Dach-Typ */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Typ</span>
        <select value={dachTyp}
                onChange={(e) => onUpdate({ dachTyp: e.target.value })}
                style={{ flex: 1, fontSize: 11 }}>
          <option value="pult">Pult — Geneigte Fläche, Traufe = 1. Kante</option>
          <option value="sattel">Sattel — Rechteck-Outline (4 Ecken)</option>
          <option value="walm">Walm — Rechteck-Outline (4 Ecken)</option>
          <option value="mansarde">Mansarde — Rechteck-Outline (4 Ecken)</option>
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Geschoss</span>
        <select value={dach.geschoss}
                onChange={(e) => onUpdate({ geschoss: e.target.value })}
                style={{ flex: 1, fontSize: 11 }}>
          {geschosse.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Dicke</span>
        <input type="text" value={dicke}
               onChange={(e) => setDicke(e.target.value)}
               onBlur={() => {
                 const v = parseFloat(dicke)
                 if (v > 0 && v !== dach.dicke) onUpdate({ dicke: v })
                 else setDicke(String(dach.dicke))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Neigung</span>
        <input type="text" value={neigung}
               onChange={(e) => setNeigung(e.target.value)}
               onBlur={() => {
                 const v = parseFloat(neigung)
                 if (!Number.isNaN(v) && v >= 0 && v < 90) onUpdate({ neigung: v })
                 else setNeigung(String(dach.neigung ?? 30))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>°</span>
      </div>

      {dachTyp === 'pult' && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
                title="Index der Traufkante (0 = Kante zwischen 1. und 2. Punkt)">
            Traufe
          </span>
          <input type="text" value={eaveIdx}
                 onChange={(e) => setEaveIdx(e.target.value)}
                 onBlur={() => {
                   const v = parseInt(eaveIdx, 10)
                   if (!Number.isNaN(v) && v >= 0) onUpdate({ eaveIdx: v })
                   else setEaveIdx(String(dach.eaveIdx ?? 0))
                 }}
                 onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
                 style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
          <span style={{ fontSize: 9, color: 'var(--text-muted)' }}>Kante</span>
        </div>
      )}

      {/* Mansarde-spezifisch: Variante + untere Neigung + Knick-Hoehe */}
      {dachTyp === 'mansarde' && (
        <>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Variante</span>
            <select value={dach.dachVariante || 'walm'}
                    onChange={(e) => onUpdate({ dachVariante: e.target.value })}
                    style={{ flex: 1, fontSize: 11 }}>
              <option value="walm">Walm — Knick auf allen 4 Seiten (Pariser Stil)</option>
              <option value="giebel">Giebel — Knick nur an langen Seiten (DACH-Standard)</option>
              <option value="walm_giebel">Walm + Giebel — Unten Walm, oben Giebel</option>
            </select>
          </div>
          <MansardeFields dach={dach} onUpdate={onUpdate} />
        </>
      )}

      <AutoOverrideField label="Basis" auto={ukAuto} autoValue={dach.uk}
        rawValue={ukOver} onChangeRaw={setUkOver}
        onToggle={() => onUpdate({ ukOverride: ukAuto ? dach.uk : '' })}
        onCommit={() => {
          if (ukAuto) return
          const v = parseFloat(ukOver)
          if (!Number.isNaN(v)) onUpdate({ ukOverride: v })
        }} />
    </div>
  )
}

function MansardeFields({ dach, onUpdate }) {
  const [nu, setNu] = useState(String(dach.neigungUnten ?? 60))
  const [kh, setKh] = useState(String(dach.knickH ?? 2.0))
  useEffect(() => {
    setNu(String(dach.neigungUnten ?? 60))
    setKh(String(dach.knickH ?? 2.0))
  }, [dach.id, dach.neigungUnten, dach.knickH])
  return (
    <>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
              title="Untere (steile) Neigung — bis zum Knick">
          Steil
        </span>
        <input type="text" value={nu}
               onChange={(e) => setNu(e.target.value)}
               onBlur={() => {
                 const v = parseFloat(nu)
                 if (!Number.isNaN(v) && v > 0 && v < 90) onUpdate({ neigungUnten: v })
                 else setNu(String(dach.neigungUnten ?? 60))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>°</span>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
              title="Höhe über Traufe wo der Knick sitzt">
          Knick H
        </span>
        <input type="text" value={kh}
               onChange={(e) => setKh(e.target.value)}
               onBlur={() => {
                 const v = parseFloat(kh)
                 if (!Number.isNaN(v) && v > 0) onUpdate({ knickH: v })
                 else setKh(String(dach.knickH ?? 2.0))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
      </div>
    </>
  )
}

const SIMS_OPTIONS = [
  { code: 'ohne',     label: 'ohne' },
  { code: 'schmal',   label: 'schmal' },
  { code: 'standard', label: 'standard' },
  { code: 'breit',    label: 'breit' },
]

const RAHMEN_POS_OPTIONS = [
  { code: 'aussen', label: 'aussen', hint: 'Rahmen bündig mit Aussenfläche' },
  { code: 'mid',    label: 'mittig', hint: 'Rahmen mittig im Wandquerschnitt' },
  { code: 'innen',  label: 'innen',  hint: 'Rahmen bündig mit Innenfläche' },
]

const OEFF_REFERENZ_OPTIONS = [
  { code: 'links',  label: 'Links',  hint: 'Klick-Punkt am linken Rand — Öffnung extendiert nach rechts (+tan der Wand-Achse)' },
  { code: 'mid',    label: 'Mittig', hint: 'Klick-Punkt mittig in der Öffnung (Standard)' },
  { code: 'rechts', label: 'Rechts', hint: 'Klick-Punkt am rechten Rand — Öffnung extendiert nach links (-tan)' },
]

function SollRow({ label, value, unit, soll, sollKey, onUpdateSoll, readOnly }) {
  // soll[sollKey] = [lo, hi, on]
  const lo = soll?.[sollKey]?.[0] ?? 0
  const hi = soll?.[sollKey]?.[1] ?? 0
  const on = soll?.[sollKey]?.[2] ?? true
  const inRange = value >= lo && value <= hi
  const valueColor = !on ? 'var(--text-muted)'
                    : inRange ? 'var(--accent)'
                    : '#c87050'
  const [loStr, setLoStr] = useState(String(lo))
  const [hiStr, setHiStr] = useState(String(hi))
  useEffect(() => {
    setLoStr(String(lo))
    setHiStr(String(hi))
  }, [lo, hi])

  const commit = (k, val, setBack, def) => {
    const v = parseFloat(val)
    if (!Number.isNaN(v) && v > 0) {
      const next = [...soll[sollKey]]
      next[k] = v
      onUpdateSoll({ ...soll, [sollKey]: next })
    } else setBack(String(def))
  }

  const toggle = () => {
    const next = [...soll[sollKey]]
    next[2] = !next[2]
    onUpdateSoll({ ...soll, [sollKey]: next })
  }

  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10,
                  fontFamily: 'DM Mono, monospace' }}>
      <input type="checkbox" checked={on} onChange={toggle}
             title="Validierung ein/aus"
             style={{ margin: 0, cursor: 'pointer' }} />
      <span style={{ color: 'var(--text-muted)', width: 30 }}>{label}</span>
      <span style={{ color: valueColor, width: 56 }}>
        {fmtNum(value)} {unit}
      </span>
      {readOnly ? (
        <span style={{ color: 'var(--text-muted)', marginLeft: 'auto', fontSize: 9 }}>
          {fmtNum(lo)}–{fmtNum(hi)}
        </span>
      ) : (
        <>
          <span style={{ color: 'var(--text-muted)', marginLeft: 'auto', fontSize: 9 }}>Soll</span>
          <input type="text" value={loStr}
                 onChange={(e) => setLoStr(e.target.value)}
                 onBlur={() => commit(0, loStr, setLoStr, lo)}
                 onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
                 disabled={!on}
                 style={{
                   width: 38, fontSize: 9, fontFamily: 'DM Mono, monospace',
                   padding: '1px 3px', background: 'transparent',
                   border: '1px solid var(--border-light)',
                   color: on ? 'var(--text-primary)' : 'var(--text-muted)',
                 }} />
          <span style={{ color: 'var(--text-muted)', fontSize: 9 }}>–</span>
          <input type="text" value={hiStr}
                 onChange={(e) => setHiStr(e.target.value)}
                 onBlur={() => commit(1, hiStr, setHiStr, hi)}
                 onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
                 disabled={!on}
                 style={{
                   width: 38, fontSize: 9, fontFamily: 'DM Mono, monospace',
                   padding: '1px 3px', background: 'transparent',
                   border: '1px solid var(--border-light)',
                   color: on ? 'var(--text-primary)' : 'var(--text-muted)',
                 }} />
        </>
      )}
    </div>
  )
}

const DEFAULT_TREPPE_SOLL = {
  s:  [0.15, 0.20, true],
  a:  [0.21, 0.35, true],
  sa: [0.60, 0.65, true],
}

function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
  const [breite, setBreite] = useState(String(treppe.breite ?? 1.0))
  const [nStufen, setNStufen] = useState(String(treppe.nStufen ?? 15))
  const [laufD, setLaufD] = useState(String(treppe.laufD ?? 0.18))
  const [hStr, setHStr] = useState('')
  useEffect(() => {
    setBreite(String(treppe.breite ?? 1.0))
    setNStufen(String(treppe.nStufen ?? 15))
    setLaufD(String(treppe.laufD ?? 0.18))
  }, [treppe.id, treppe.breite, treppe.nStufen, treppe.laufD])

  const H = (treppe.ok ?? 0) - (treppe.uk ?? 0)
  const N = treppe.nStufen ?? 15
  const L = treppe.laufLen ?? 0
  const S = N > 0 ? H / N : 0
  const A = N > 0 ? L / N : 0
  const sa = 2 * S + A
  const soll = treppe.soll || DEFAULT_TREPPE_SOLL
  const hasHOver = treppe.hOver != null && treppe.hOver !== ''
  useEffect(() => {
    setHStr(hasHOver ? String(treppe.hOver) : fmtNum(H))
  }, [treppe.id, treppe.hOver, H, hasHOver])

  const allOK = (
    (!soll.s[2]  || (S  >= soll.s[0]  && S  <= soll.s[1])) &&
    (!soll.a[2]  || (A  >= soll.a[0]  && A  <= soll.a[1])) &&
    (!soll.sa[2] || (sa >= soll.sa[0] && sa <= soll.sa[1]))
  )

  const onUpdateSoll = (newSoll) => {
    onUpdate({ soll: newSoll })
  }
  const onCommitH = () => {
    const v = parseFloat(hStr)
    if (!Number.isNaN(v) && v > 0 && Math.abs(v - H) > 1e-5) {
      // User hat H ueberschrieben → Ziel auf "eigene"
      onUpdate({ hOver: v, geschossEnd: '' })
    }
  }
  const onClearHOver = () => {
    onUpdate({ hOver: '' })
  }

  const ref = treppe.treppeReferenz ?? 'mid'
  const REF_OPTIONS = [
    { code: 'links',  label: 'Links' },
    { code: 'mid',    label: 'Mittig' },
    { code: 'rechts', label: 'Rechts' },
  ]
  const modus = treppe.treppeModus ?? 'flach'
  const MODUS_OPTIONS = [
    { code: 'massiv',      label: 'massiv',      hint: 'Block bis zum Boden — wie eine Mauer unter der Treppe' },
    { code: 'flach',       label: 'flach',       hint: 'Schräge Plattenunterseite parallel zum Treppenlauf (realistisch)' },
    { code: 'plattenrand', label: 'gestuft',     hint: 'Plattenunterseite folgt den Stufen, vertikal versetzt' },
  ]

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--accent)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name="stairs" size={13} style={{ color: 'var(--accent)', marginRight: 6 }} />
        <span style={{ ...labelXs, flex: 1, color: 'var(--accent)' }}>
          Treppe · {treppe.geschossName} → {treppe.geschossEndName || '(auto)'}
        </span>
        <button className="btn-icon-sm btn-icon-danger" onClick={onDelete} title="Löschen">
          <Icon name="delete" size={12} />
        </button>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Start</span>
        <select value={treppe.geschoss}
                onChange={(e) => onUpdate({ geschoss: e.target.value })}
                style={{ flex: 1, fontSize: 11 }}>
          {geschosse.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Ziel</span>
        <select
          value={hasHOver ? '__custom__' : (treppe.geschossEnd || '')}
          onChange={(e) => {
            const v = e.target.value
            if (v === '__custom__') {
              // Eigene Hoehe — falls noch nicht gesetzt, mit aktuellem H starten
              onUpdate({ hOver: H, geschossEnd: '' })
            } else {
              onUpdate({ geschossEnd: v, hOver: '' })
            }
          }}
          style={{ flex: 1, fontSize: 11 }}>
          <option value="">(auto: Start + Höhe)</option>
          {geschosse.filter(g => g.id !== treppe.geschoss)
            .map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
          <option value="__custom__">eigene Höhe</option>
        </select>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Breite</span>
        <input type="text" value={breite}
               onChange={(e) => setBreite(e.target.value)}
               onBlur={() => {
                 const v = parseFloat(breite)
                 if (!Number.isNaN(v) && v >= 0.3) onUpdate({ breite: v })
                 else setBreite(String(treppe.breite))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Stufen</span>
        <input type="text" value={nStufen}
               onChange={(e) => setNStufen(e.target.value)}
               onBlur={() => {
                 const v = parseInt(nStufen, 10)
                 if (Number.isFinite(v) && v >= 2 && v <= 40) onUpdate({ nStufen: v })
                 else setNStufen(String(treppe.nStufen))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>×</span>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Lage</span>
        <div style={{ flex: 1, display: 'flex', gap: 2 }}>
          {REF_OPTIONS.map(o => (
            <button key={o.code}
              onClick={() => onUpdate({ treppeReferenz: o.code })}
              className={ref === o.code ? 'btn-contained' : 'btn-outlined'}
              style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}>
              {o.label}
            </button>
          ))}
        </div>
      </div>

      <div style={{ height: 1, background: 'var(--border-light)', margin: '2px 0' }} />

      {/* Unterseite-Modus */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
              title="Form der Treppen-Unterseite">
          Unten
        </span>
        <div style={{ flex: 1, display: 'flex', gap: 2 }}>
          {MODUS_OPTIONS.map(o => (
            <button key={o.code}
              onClick={() => onUpdate({ treppeModus: o.code })}
              className={modus === o.code ? 'btn-contained' : 'btn-outlined'}
              style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}
              title={o.hint}>
              {o.label}
            </button>
          ))}
        </div>
      </div>

      {/* Lauf-Plattendicke (nur fuer flach + plattenrand relevant) */}
      {modus !== 'massiv' && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
                title="Dicke der Lauf-Platte (Materialdicke unter den Stufen)">
            Platte
          </span>
          <input type="text" value={laufD}
                 onChange={(e) => setLaufD(e.target.value)}
                 onBlur={() => {
                   const v = parseFloat(laufD)
                   if (!Number.isNaN(v) && v > 0) onUpdate({ laufD: v })
                   else setLaufD(String(treppe.laufD))
                 }}
                 onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
                 style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
          <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
        </div>
      )}

      {/* Schrittmass-Tabelle: H (editierbar), S, A, 2S+A mit on/off + range */}
      <div style={{
        display: 'flex', flexDirection: 'column', gap: 3,
        padding: '6px 8px', borderRadius: 4,
        background: allOK ? 'rgba(95,168,150,0.10)' : 'rgba(200,112,80,0.08)',
        border: '1px solid ' + (allOK ? 'rgba(95,168,150,0.4)' : 'rgba(200,112,80,0.35)'),
        fontSize: 10, fontFamily: 'DM Mono, monospace',
      }}>
        {/* H — editierbar; aendert Hoehe und kippt Ziel auf "eigene" */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ width: 16 }} />
          <span style={{ color: 'var(--text-muted)', width: 30 }}>H</span>
          <input type="text" value={hStr}
                 onChange={(e) => setHStr(e.target.value)}
                 onBlur={onCommitH}
                 onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
                 style={{
                   width: 56, fontSize: 10, fontFamily: 'DM Mono, monospace',
                   padding: '1px 3px', background: 'transparent',
                   border: '1px solid ' + (hasHOver ? 'var(--accent)' : 'var(--border-light)'),
                   color: 'var(--text-primary)',
                 }} />
          <span style={{ color: 'var(--text-muted)' }}>m</span>
          {hasHOver && (
            <button onClick={onClearHOver}
              title="Zurück zu Geschoss-Höhe"
              style={{
                marginLeft: 'auto', fontSize: 9,
                background: 'transparent', border: 'none',
                color: 'var(--text-muted)', cursor: 'pointer',
                textDecoration: 'underline',
              }}>auto</button>
          )}
        </div>
        <SollRow label="S"     value={S}   unit="m" soll={soll} sollKey="s"
                  onUpdateSoll={onUpdateSoll} />
        <SollRow label="A"     value={A}   unit="m" soll={soll} sollKey="a"
                  onUpdateSoll={onUpdateSoll} />
        <SollRow label="2S+A"  value={sa}  unit="m" soll={soll} sollKey="sa"
                  onUpdateSoll={onUpdateSoll} />
      </div>
    </div>
  )
}

function OeffnungProperties({ oeff, onUpdate, onDelete }) {
  const isFenster = oeff.kind === 'fenster'
  const label = isFenster ? 'Fenster' : 'Tür'
  const icon = isFenster ? 'window' : 'sensor_door'
  const [breite, setBreite] = useState(String(oeff.breite ?? (isFenster ? 1.2 : 0.9)))
  const [hoehe, setHoehe] = useState(String(oeff.hoehe ?? (isFenster ? 1.4 : 2.1)))
  const [brueest, setBrueest] = useState(String(oeff.brueest ?? 0.9))
  const [rahmenB, setRahmenB] = useState(String(oeff.rahmenB ?? 0.06))
  const [rahmenTiefe, setRahmenTiefe] = useState(String(oeff.rahmenTiefe ?? 0.08))
  useEffect(() => {
    setBreite(String(oeff.breite ?? (isFenster ? 1.2 : 0.9)))
    setHoehe(String(oeff.hoehe ?? (isFenster ? 1.4 : 2.1)))
    setBrueest(String(oeff.brueest ?? 0.9))
    setRahmenB(String(oeff.rahmenB ?? 0.06))
    setRahmenTiefe(String(oeff.rahmenTiefe ?? 0.08))
  }, [oeff.id, oeff.breite, oeff.hoehe, oeff.brueest, oeff.rahmenB,
      oeff.rahmenTiefe, isFenster])

  const commit = (key, val, setBack, def) => {
    const v = parseFloat(val)
    if (!Number.isNaN(v) && v > 0) onUpdate({ [key]: v })
    else setBack(String(def))
  }

  const fluegel = oeff.fluegel ?? 1
  const rahmenPos = oeff.rahmenPos ?? 'mid'
  const simsAus = oeff.simsAus ?? 'ohne'
  const simsIn  = oeff.simsIn ?? 'ohne'

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 8,
      padding: 10, marginBottom: 8,
      background: 'var(--bg-section)',
      border: '1px solid var(--accent)',
      borderRadius: 'var(--r-lg)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name={icon} size={13} style={{ color: 'var(--accent)', marginRight: 6 }} />
        <span style={{ ...labelXs, flex: 1, color: 'var(--accent)' }}>
          {label} · {oeff.geschossName}
        </span>
        <button className="btn-icon-sm btn-icon-danger" onClick={onDelete} title="Löschen">
          <Icon name="delete" size={12} />
        </button>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Breite</span>
        <input type="text" value={breite}
               onChange={(e) => setBreite(e.target.value)}
               onBlur={() => commit('breite', breite, setBreite, oeff.breite)}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>Höhe</span>
        <input type="text" value={hoehe}
               onChange={(e) => setHoehe(e.target.value)}
               onBlur={() => commit('hoehe', hoehe, setHoehe, oeff.hoehe)}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
              title={isFenster
                ? 'Brüstungshöhe über UK der Wand'
                : 'Türschwelle / Höhe über UK der Wand'}>
          {isFenster ? 'Brüst.' : 'Schw.'}
        </span>
        <input type="text" value={brueest}
               onChange={(e) => setBrueest(e.target.value)}
               onBlur={() => {
                 const v = parseFloat(brueest)
                 if (!Number.isNaN(v) && v >= 0) onUpdate({ brueest: v })
                 else setBrueest(String(oeff.brueest))
               }}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
      </div>

      {/* Referenz-Lage: wo sitzt der Klick-Punkt in der Oeffnung */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
              title="Lage des Klick-Punkts in der Öffnung">
          Ref
        </span>
        <div style={{ flex: 1, display: 'flex', gap: 2 }}>
          {OEFF_REFERENZ_OPTIONS.map(o => (
            <button key={o.code}
              onClick={() => onUpdate({ oeffReferenz: o.code })}
              className={(oeff.oeffReferenz || 'mid') === o.code ? 'btn-contained' : 'btn-outlined'}
              style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}
              title={o.hint}>
              {o.label}
            </button>
          ))}
        </div>
      </div>

      <div style={{ height: 1, background: 'var(--border-light)', margin: '2px 0' }} />

      {/* Rahmen-Profil (Breite × Tiefe) — beide Felder gleich breit */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
              title="Rahmen-Profil: Breite (in Wandflaeche) × Tiefe (entlang Wandnormale)">
          Rahmen
        </span>
        <input type="text" value={rahmenB}
               onChange={(e) => setRahmenB(e.target.value)}
               onBlur={() => commit('rahmenB', rahmenB, setRahmenB, oeff.rahmenB)}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               title="Profil-Breite (sichtbar in der Fassade)"
               style={{ flex: '1 1 0', minWidth: 0, width: 0, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>×</span>
        <input type="text" value={rahmenTiefe}
               onChange={(e) => setRahmenTiefe(e.target.value)}
               onBlur={() => commit('rahmenTiefe', rahmenTiefe, setRahmenTiefe, oeff.rahmenTiefe)}
               onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
               title="Rahmen-Tiefe (Lage in der Wand)"
               style={{ flex: '1 1 0', minWidth: 0, width: 0, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
        <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
      </div>

      {/* Rahmen-Lage im Wandquerschnitt */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
              title="Lage des Rahmens im Wandquerschnitt">
          Lage
        </span>
        <div style={{ flex: 1, display: 'flex', gap: 2 }}>
          {RAHMEN_POS_OPTIONS.map(o => (
            <button key={o.code}
              onClick={() => onUpdate({ rahmenPos: o.code })}
              className={rahmenPos === o.code ? 'btn-contained' : 'btn-outlined'}
              style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}
              title={o.hint}>
              {o.label}
            </button>
          ))}
        </div>
      </div>

      {/* Fluegel-Anzahl — nur fuer Fenster (Tueren haben ein einzelnes Tuerblatt) */}
      {isFenster && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
                title="Anzahl Flügel (vertikale Unterteilung)">
            Flügel
          </span>
          <div style={{ flex: 1, display: 'flex', gap: 2 }}>
            {[1, 2, 3, 4].map(n => (
              <button key={n}
                onClick={() => onUpdate({ fluegel: n })}
                className={fluegel === n ? 'btn-contained' : 'btn-outlined'}
                style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}>
                {n}
              </button>
            ))}
          </div>
        </div>
      )}

      {/* Sims-Stile (aussen / innen) — nur fuer Fenster */}
      {isFenster && (
        <>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
                  title="Aussensims — Platte unter Öffnung, ragt aussen heraus">
              Sims a.
            </span>
            <select value={simsAus}
                    onChange={(e) => onUpdate({ simsAus: e.target.value })}
                    style={{ flex: 1, fontSize: 11 }}>
              {SIMS_OPTIONS.map(o =>
                <option key={o.code} value={o.code}>{o.label}</option>)}
            </select>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}
                  title="Innensims — Platte unter Öffnung, ragt innen heraus">
              Sims i.
            </span>
            <select value={simsIn}
                    onChange={(e) => onUpdate({ simsIn: e.target.value })}
                    style={{ flex: 1, fontSize: 11 }}>
              {SIMS_OPTIONS.map(o =>
                <option key={o.code} value={o.code}>{o.label}</option>)}
            </select>
          </div>
        </>
      )}

      {/* Glas-Toggle: bei Tueren ersetzt Glas das Tuerblatt (verglaste Tuer) */}
      <div style={{ display: 'flex', gap: 4 }}>
        <button
          onClick={() => onUpdate({ glas: !oeff.glas })}
          className={oeff.glas ? 'btn-contained' : 'btn-outlined'}
          style={{ flex: 1, padding: '3px 6px', fontSize: 10 }}
          title={isFenster ? 'Glasscheibe sichtbar' : 'Verglaste Tür (statt Türblatt)'}>
          {isFenster ? 'Glas' : 'Verglast'} {oeff.glas ? '✓' : ''}
        </button>
      </div>
    </div>
  )
}


// Wiederverwendete UI fuer UK/OK-Felder mit Auto/Custom-Toggle
function AutoOverrideField({ label, auto, autoValue, rawValue, onChangeRaw, onToggle, onCommit }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
      <span style={{ fontSize: 10, color: 'var(--text-muted)', width: 50 }}>{label}</span>
      <button onClick={onToggle}
              className={auto ? 'btn-contained' : 'btn-outlined'}
              style={{ padding: '3px 8px', fontSize: 10 }}
              title={auto ? 'Folgt Geschoss' : 'Eigener Wert'}>
        {auto ? 'Auto' : 'Custom'}
      </button>
      <input type="text"
             value={auto ? fmtNum(autoValue) : rawValue}
             disabled={auto}
             onChange={(e) => onChangeRaw(e.target.value)}
             onBlur={onCommit}
             onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
             style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace',
                      opacity: auto ? 0.6 : 1 }} />
    </div>
  )
}
