// Interaktiver Loxone-App-Nachbau fuers Phone-Mockup (Task F2) — Loxone-Gegenstueck zu
// ha-app-demo.jsx, gleiche Architektur (vanilla imperative DOM, <script type="text/babel" src>,
// Factory auf window). Konsumiert window.SM_LOX (lox-app-data.js). Loxone-Dark-Look nach
// work/research-app-ui/LOXONE-APP-SPEC.md (§1/§3/§5).
window.LoxAppDemo = (function () {
  const SVG_NS = 'http://www.w3.org/2000/svg';
  const ROOM_ORDER = ['Wohnzimmer', 'Küche', 'Bad', 'Schlafzimmer', 'Energie', 'Zutritt'];

  function injectCss() {
    if (document.getElementById('lox-app-css')) return;
    const style = document.createElement('style');
    style.id = 'lox-app-css';
    style.textContent = `
.lox-app{ position:absolute; inset:0; z-index:1; overflow-y:auto; -webkit-overflow-scrolling:touch;
  background:#101010; color:#fff; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; }
.lox-app *{ box-sizing:border-box; }
.lox-app__header{ background:#8CFF70; }
.lox-app__header-bar{ height:52px; display:flex; align-items:center; gap:10px; padding:0 14px; }
.lox-app__icon-btn{ appearance:none; border:none; background:none; padding:6px; margin:-6px; display:flex;
  align-items:center; justify-content:center; color:#141414; cursor:pointer; line-height:1; }
.lox-app__hamburger-bars{ display:flex; flex-direction:column; gap:4px; width:18px; }
.lox-app__hamburger-bar{ display:block; height:2px; width:100%; background:#141414; border-radius:1px; }
.lox-app__wordmark{ flex:1; font:800 15px/1 -apple-system,sans-serif; letter-spacing:.06em; color:#141414; }
.lox-app__header-caption{ text-align:center; font:700 10px/1 -apple-system,sans-serif; letter-spacing:.14em;
  color:rgba(20,20,20,.62); padding:0 0 9px; text-transform:uppercase; }
.lox-app__chips{ display:flex; gap:8px; padding:12px 14px 4px; flex-wrap:wrap; }
.lox-app__chip{ display:inline-flex; align-items:center; gap:6px; background:#1c1c1c; border-radius:100px;
  padding:7px 12px; }
.lox-app__chip-value{ font:600 12px/1 -apple-system,sans-serif; color:#fff; }
.lox-app__section{ padding:6px 14px 4px; }
.lox-app__section-title{ font:400 13px/1 -apple-system,sans-serif; color:#a0a0a0; padding:6px 2px 10px; }
.lox-app__fav-grid{ display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.lox-app__fav-card{ background:#1c1c1c; border-radius:20px; padding:14px; display:flex; flex-direction:column;
  gap:8px; cursor:pointer; min-height:92px; }
.lox-app__fav-card.wide{ grid-column:1 / -1; }
.lox-app__fav-top{ display:flex; align-items:flex-start; justify-content:space-between; gap:8px; }
.lox-app__fav-tag{ font:400 11px/1.2 -apple-system,sans-serif; color:#a0a0a0; text-align:right; }
.lox-app__fav-state{ font:700 17px/1.25 -apple-system,sans-serif; color:#fff; }
.lox-app__fav-sub{ font:400 12px/1.3 -apple-system,sans-serif; color:#a0a0a0; }
.lox-app__fav-ctrl{ margin-top:auto; display:flex; align-items:center; justify-content:space-between;
  background:#131313; border-radius:14px; padding:6px 8px; }
.lox-pill{ position:relative; width:13px; height:31px; border-radius:999px; background:#131313; flex:0 0 13px;
  cursor:pointer; overflow:hidden; }
.lox-pill.is-off{ background:#fff; }
.lox-pill__fill{ position:absolute; left:0; right:0; bottom:0; border-radius:999px; background:#FEE502; }
.lox-mood-chip{ display:inline-flex; align-items:center; gap:3px; border:none; background:transparent;
  color:#a0a0a0; cursor:pointer; padding:4px; }
.lox-climate-mini{ display:flex; align-items:center; justify-content:space-between; background:#131313;
  border-radius:14px; padding:6px 10px; margin-top:auto; }
.lox-climate-mini-btn{ appearance:none; width:26px; height:26px; border-radius:50%; border:none; background:#2a2a2a;
  color:#fff; font:600 15px/1 -apple-system,sans-serif; cursor:pointer; }
.lox-climate-mini-val{ font:600 13px/1 -apple-system,sans-serif; color:#0FA4FF; }
.lox-app__room{ padding:8px 14px 2px; }
.lox-app__room-title{ display:flex; align-items:center; gap:4px; font:700 15px/1 -apple-system,sans-serif;
  color:#fff; padding:10px 2px 8px; cursor:pointer; }
.lox-app__row{ display:flex; align-items:center; gap:12px; background:#1c1c1c; border-radius:16px;
  padding:12px 14px; margin-bottom:8px; cursor:pointer; }
.lox-app__row-icon{ width:26px; height:26px; flex:0 0 26px; display:flex; align-items:center; justify-content:center; }
.lox-app__row-text{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.lox-app__row-title{ font:700 14px/1.2 -apple-system,sans-serif; color:#fff; }
.lox-app__row-sub{ font:400 12px/1.2 -apple-system,sans-serif; color:#a0a0a0; }
.lox-app__row-trailing{ flex:0 0 auto; display:flex; align-items:center; }
.lox-tabbar{ position:absolute; left:14px; right:14px; bottom:14px; height:54px; background:#000000;
  border-radius:27px; display:flex; align-items:center; justify-content:space-around;
  box-shadow:0 10px 24px rgba(0,0,0,.5); z-index:2; }
.lox-tabbar__btn{ appearance:none; border:none; background:transparent; color:#a0a0a0; display:flex;
  align-items:center; justify-content:center; width:38px; height:38px; border-radius:50%; cursor:pointer; }
.lox-tabbar__btn.active{ background:#2b2b2b; color:#fff; }
.lox-view{ position:absolute; inset:0; z-index:6; background:#101010; overflow-y:auto;
  transform:translateX(100%); transition:transform .28s ease; display:flex; flex-direction:column; }
.lox-view.open{ transform:translateX(0); }
.lox-view.modal{ background:#0B0F14; }
.lox-view__bar{ position:sticky; top:0; height:56px; display:flex; align-items:center; gap:10px;
  padding:0 10px; background:inherit; z-index:1; }
.lox-view__back{ width:40px; height:40px; border-radius:50%; background:rgba(255,255,255,.08); border:0;
  cursor:pointer; display:flex; align-items:center; justify-content:center; color:#fff; }
.lox-view__title{ font:700 19px/1 -apple-system,sans-serif; color:#fff; }
.lox-view__body{ flex:1; padding:10px 18px 32px; }
@media (prefers-reduced-motion: reduce){ .lox-view{ transition:none; } }
.lox-light-readout{ font:700 40px/1 -apple-system,sans-serif; color:#fff; text-align:center; margin-top:10px; }
.lox-light-caption{ font:400 13px/1 -apple-system,sans-serif; color:#a0a0a0; text-align:center; margin-top:6px; }
.lox-light-glow{ width:120px; height:120px; margin:20px auto 8px; border-radius:50%; display:flex;
  align-items:center; justify-content:center; transition:opacity .2s; }
.lox-light-slider{ width:56px; height:220px; border-radius:28px; margin:16px auto 0; position:relative;
  background:#131313; }
.lox-light-slider-fill{ position:absolute; left:0; right:0; bottom:0; border-radius:28px; background:#FEE502; }
.lox-light-slider-thumb{ position:absolute; left:50%; width:34px; height:5px; border-radius:3px; background:#fff;
  box-shadow:0 1px 4px rgba(0,0,0,.5); transform:translate(-50%,50%); }
.lox-power-row{ display:flex; justify-content:center; margin-top:22px; }
.lox-power-btn{ width:52px; height:52px; border-radius:50%; border:0; cursor:pointer; display:flex;
  align-items:center; justify-content:center; background:#1c1c1c; color:#fff; }
.lox-power-btn.is-on{ background:#FEE502; color:#141414; }
.lox-blindwin{ position:relative; width:100%; aspect-ratio:4/3; border-radius:16px; overflow:hidden;
  background:linear-gradient(#1b2a1c,#0d130d); border:1px solid rgba(255,255,255,.08); margin:14px 0; }
.lox-blind{ position:absolute; left:0; top:0; width:100%;
  background:repeating-linear-gradient(#2c2c2c 0 6px, #1c1c1c 6px 8px); box-shadow:0 2px 10px rgba(0,0,0,.45);
  transition:height .12s linear; }
.lox-cover-readout{ font:700 34px/1 -apple-system,sans-serif; color:#fff; text-align:center; margin-top:6px; }
.lox-cover-caption{ font:400 13px/1 -apple-system,sans-serif; color:#a0a0a0; text-align:center; margin-top:6px; }
.lox-cover-slider{ width:56px; height:180px; border-radius:28px; background:#131313; margin:14px auto 0;
  position:relative; }
.lox-cover-slider-fill{ position:absolute; left:0; right:0; bottom:0; border-radius:28px; background:#73E0FF; }
.lox-cover-slider-thumb{ position:absolute; left:50%; width:34px; height:5px; border-radius:3px; background:#fff;
  box-shadow:0 1px 4px rgba(0,0,0,.5); transform:translate(-50%,50%); }
.lox-seg-row{ display:flex; gap:8px; margin-top:18px; }
.lox-seg-btn{ flex:1; background:#1c1c1c; border:0; border-radius:14px; padding:14px 0; display:flex;
  align-items:center; justify-content:center; color:#fff; cursor:pointer; }
.lox-seg-btn.stop-btn{ font:700 12px/1 -apple-system,sans-serif; letter-spacing:.06em; }
.lox-seg-btn.is-active{ background:#8CFF70; color:#101010; }
.lox-climate-readout{ font:700 60px/1 -apple-system,sans-serif; color:#0FA4FF; text-align:center; margin-top:6px; }
.lox-climate-caption{ font:400 13px/1 -apple-system,sans-serif; color:#a0a0a0; text-align:center; margin-top:4px; }
.lox-climate-card{ background:#1c1c1c; border-radius:16px; padding:12px 16px; margin-top:20px; display:flex;
  align-items:center; justify-content:space-between; }
.lox-climate-stepper-btn{ width:44px; height:44px; border-radius:50%; background:#131313; border:0; color:#fff;
  font:600 22px/1 -apple-system,sans-serif; cursor:pointer; }
.lox-climate-stepper-value{ font:700 20px/1 -apple-system,sans-serif; color:#fff; }
.lox-climate-chips{ display:flex; gap:8px; margin-top:14px; }
.lox-climate-chip{ flex:1; padding:10px 0; border-radius:100px; background:#1c1c1c; border:0; color:#fff;
  font:600 12px/1 -apple-system,sans-serif; cursor:pointer; }
.lox-climate-chip.is-active{ background:#29CC00; color:#0d1117; }
.lox-media-art{ width:150px; height:150px; margin:10px auto; border-radius:16px;
  background:linear-gradient(135deg,#1c1c1c,#274127); box-shadow:0 10px 26px rgba(0,0,0,.45); }
.lox-media-title{ font:700 17px/1.3 -apple-system,sans-serif; color:#8CFF70; text-align:center; }
.lox-media-artist{ font:400 13px/1.3 -apple-system,sans-serif; color:#a0a0a0; text-align:center; margin-bottom:14px; }
.lox-media-ctrl{ display:flex; align-items:center; justify-content:center; gap:26px; margin:14px 0 18px; }
.lox-media-btn{ border:0; background:transparent; color:#fff; cursor:pointer; padding:6px; }
.lox-media-vol-row{ display:flex; align-items:center; gap:10px; padding:0 6px; }
.lox-media-vol{ flex:1; height:6px; background:#131313; border-radius:3px; position:relative; cursor:pointer; }
.lox-media-vol-fill{ position:absolute; left:0; top:0; bottom:0; background:#8CFF70; border-radius:3px; }
.lox-lock-btn{ display:block; width:118px; height:118px; margin:14px auto 10px; border-radius:50%; border:0;
  cursor:pointer; font-size:42px; color:#fff; }
.lox-lock-caption{ font:700 17px/1 -apple-system,sans-serif; color:#fff; text-align:center; margin-bottom:16px; }
.lox-access-list-title{ font:700 13px/1 -apple-system,sans-serif; color:#fff; margin:10px 4px 4px; }
.lox-access-row{ display:flex; justify-content:space-between; padding:10px 4px;
  border-bottom:1px solid rgba(255,255,255,.06); font:400 14px/1.2 -apple-system,sans-serif; }
.lox-intercom-cam{ width:100%; border-radius:14px; overflow:hidden; background:#000; position:relative;
  margin-top:6px; }
.lox-intercom-live{ position:absolute; top:10px; left:10px; display:flex; align-items:center; gap:6px;
  background:rgba(0,0,0,.55); color:#fff; font:600 11px/1 -apple-system,sans-serif; padding:4px 10px;
  border-radius:100px; }
.lox-intercom-actions{ display:flex; justify-content:center; gap:36px; margin:20px 0 16px; }
.lox-intercom-action{ display:flex; flex-direction:column; align-items:center; gap:6px; }
.lox-intercom-action button{ width:62px; height:62px; border-radius:50%; border:0; cursor:pointer;
  display:flex; align-items:center; justify-content:center; }
.lox-intercom-action span{ font:500 12px/1 -apple-system,sans-serif; color:#a0a0a0; }
`;
    document.head.appendChild(style);
  }

  function svgIcon(d, color, size) {
    const svg = document.createElementNS(SVG_NS, 'svg');
    svg.setAttribute('viewBox', '0 0 24 24');
    svg.setAttribute('width', String(size));
    svg.setAttribute('height', String(size));
    svg.setAttribute('fill', 'none');
    svg.setAttribute('stroke', color || '#fff');
    svg.setAttribute('stroke-width', '1.8');
    svg.setAttribute('stroke-linecap', 'round');
    svg.setAttribute('stroke-linejoin', 'round');
    const path = document.createElementNS(SVG_NS, 'path');
    path.setAttribute('d', d);
    svg.appendChild(path);
    return svg;
  }

  const mk = (tag, css, text) => {
    const n = document.createElement(tag);
    if (css) n.style.cssText = css;
    if (text != null) n.textContent = text;
    return n;
  };

  function buildHeader() {
    const header = document.createElement('div');
    header.className = 'lox-app__header';

    const bar = document.createElement('div');
    bar.className = 'lox-app__header-bar';

    const menuBtn = document.createElement('button');
    menuBtn.type = 'button';
    menuBtn.className = 'lox-app__icon-btn lox-app__hamburger';
    menuBtn.setAttribute('aria-label', 'Menü');
    const bars = document.createElement('span');
    bars.className = 'lox-app__hamburger-bars';
    for (let i = 0; i < 3; i++) bars.appendChild(document.createElement('span')).className = 'lox-app__hamburger-bar';
    menuBtn.appendChild(bars);

    const wordmark = document.createElement('div');
    wordmark.className = 'lox-app__wordmark';
    wordmark.textContent = 'LOXONE';

    const searchBtn = document.createElement('button');
    searchBtn.type = 'button';
    searchBtn.className = 'lox-app__icon-btn';
    searchBtn.setAttribute('aria-label', 'Suche');
    searchBtn.appendChild(svgIcon(window.SM_LOX.ICON.search, '#141414', 20));

    bar.appendChild(menuBtn);
    bar.appendChild(wordmark);
    bar.appendChild(searchBtn);

    const caption = document.createElement('div');
    caption.className = 'lox-app__header-caption';
    caption.textContent = 'Home';

    header.appendChild(bar);
    header.appendChild(caption);
    return { header, menuBtn };
  }

  function buildChips() {
    const row = document.createElement('div');
    row.className = 'lox-app__chips';
    const ICON = window.SM_LOX.ICON;
    (window.SM_LOX.chips || []).forEach((chip) => {
      const pill = document.createElement('div');
      pill.className = 'lox-app__chip';
      const d = ICON[chip.icon];
      if (d) pill.appendChild(svgIcon(d, '#fff', 15));
      const val = document.createElement('span');
      val.className = 'lox-app__chip-value';
      val.textContent = chip.v;
      pill.appendChild(val);
      row.appendChild(pill);
    });
    return row;
  }

  // Pill toggle (LOXONE-APP-SPEC §3.1) — off:white solid, on:yellow bottom-anchored fill on
  // the dark strip color (documented fallback for representing brightness level in §3.1).
  function pillToggle(e, onToggle) {
    const pill = document.createElement('div');
    pill.className = 'lox-pill' + (e.on ? '' : ' is-off');
    if (e.on) {
      const fill = document.createElement('div');
      fill.className = 'lox-pill__fill';
      fill.style.height = Math.max(6, e.brightness) + '%';
      pill.appendChild(fill);
    }
    pill.addEventListener('click', (ev) => { ev.stopPropagation(); onToggle(); });
    return pill;
  }

  function sensorGlyph(e, ICON) {
    return e.device === 'car' ? ICON.car : ICON.energy;
  }

  function tileIconIsToggle(e) { return e.type === 'light' || e.type === 'lock'; }

  function domainIcon(e, ICON) {
    switch (e.type) {
      case 'light': return { d: ICON.bulb, color: e.on ? '#FEE502' : '#a0a0a0' };
      case 'cover': return { d: ICON.blind, color: e.moving ? '#73E0FF' : (e.position > 0 ? '#fff' : '#a0a0a0') };
      case 'climate': return { d: ICON.heat, color: '#0FA4FF' };
      case 'media': return { d: ICON.audio, color: e.state === 'playing' ? '#8CFF70' : '#a0a0a0' };
      case 'lock': return { d: ICON.lock, color: e.locked ? '#fff' : '#E33246' };
      case 'intercom': return { d: ICON.phone, color: '#fff' };
      case 'energy': return { d: sensorGlyph(e, ICON), color: e.device === 'pv' ? '#8CFF70' : '#a0a0a0' };
      default: return { d: ICON.bulb, color: '#a0a0a0' };
    }
  }

  function stateText(e) {
    switch (e.type) {
      case 'light': return e.on ? e.brightness + ' %' : 'Aus';
      case 'cover': return e.position > 0 ? 'Offen · ' + e.position + ' %' : 'Geschlossen';
      case 'climate': return e.current.toFixed(1) + ' °';
      case 'media': return e.state === 'playing' ? 'Wiedergabe' : 'Pause';
      case 'lock': return e.locked ? 'Verriegelt' : 'Entriegelt';
      case 'intercom': return e.mute ? 'Stumm' : 'Bereit';
      case 'energy': return e.state || (e.value + (e.unit ? ' ' + e.unit : ''));
      default: return '';
    }
  }

  // ---- Favorites grid card (LOXONE-APP-SPEC §2 "A") ----
  function favCard(id, ctx) {
    const { state, ICON, openDetail, dispatchSignature } = ctx;
    const e = state[id];
    const sig = () => { if (dispatchSignature && e.signature) dispatchSignature(id); };

    const card = document.createElement('div');
    card.className = 'lox-app__fav-card' + (e.type === 'media' ? ' wide' : '');
    card.dataset.entity = id;

    if (e.type === 'media') {
      const art = document.createElement('div');
      art.style.cssText = 'width:48px;height:48px;flex:0 0 48px;border-radius:8px;background:linear-gradient(135deg,#1c1c1c,#274127);margin-right:10px;';
      const stack = document.createElement('div');
      stack.style.cssText = 'flex:1;min-width:0;';
      const title = mk('div', 'font:600 13px -apple-system,sans-serif;color:#8CFF70;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;', e.track.title);
      const artist = mk('div', 'font:400 12px -apple-system,sans-serif;color:#a0a0a0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;', e.track.artist + ' · ' + e.tag);
      stack.appendChild(title); stack.appendChild(artist);
      card.style.flexDirection = 'row';
      card.style.alignItems = 'center';
      card.appendChild(art);
      card.appendChild(stack);
      card.addEventListener('click', () => openDetail(id));
      return card;
    }

    const top = document.createElement('div');
    top.className = 'lox-app__fav-top';
    const icon = domainIcon(e, ICON);
    const iconBox = document.createElement('div');
    iconBox.appendChild(svgIcon(icon.d, icon.color, 22));
    const tag = document.createElement('div');
    tag.className = 'lox-app__fav-tag';
    tag.textContent = e.tag || e.room;
    top.appendChild(iconBox);
    top.appendChild(tag);

    const stateEl = document.createElement('div');
    stateEl.className = 'lox-app__fav-state';
    stateEl.textContent = stateText(e);
    if (e.type === 'lock' && !e.locked) stateEl.style.color = '#E33246';

    const sub = document.createElement('div');
    sub.className = 'lox-app__fav-sub';
    sub.textContent = e.name;

    card.appendChild(top);
    card.appendChild(stateEl);
    card.appendChild(sub);

    if (tileIconIsToggle(e)) {
      iconBox.style.cursor = 'pointer';
      iconBox.addEventListener('click', (ev) => {
        ev.stopPropagation();
        if (e.type === 'light') ctx.setLight(id, { on: !e.on, brightness: (!e.on && e.brightness === 0) ? 100 : e.brightness });
        else if (e.type === 'lock') ctx.setLock(id, !e.locked);
      });
    }
    // sig(): beim Antippen eines Favoriten sofort die CGI-Buehne auf diese Szene schalten (wie HA),
    // damit im Neubau auch der Loxone-Tap direkt das Mit/Ohne-Video zeigt, nicht erst in der Deep-View.
    card.addEventListener('click', () => { sig(); openDetail(id); });

    // Optional control row (§2: only light/lock favorites get the pill; pure-status cards
    // like cover/climate/energy/media are tap-through only, per spec).
    if (e.type === 'light') {
      const ctrl = document.createElement('div');
      ctrl.className = 'lox-app__fav-ctrl';
      const pill = pillToggle(e, () => {
        ctx.setLight(id, { on: !e.on, brightness: (!e.on && e.brightness === 0) ? 100 : e.brightness });
        sig();
      });
      const mood = document.createElement('button');
      mood.type = 'button';
      mood.className = 'lox-mood-chip';
      mood.appendChild(svgIcon('M9 9a3 3 0 1 0 0 6a3 3 0 1 0 0-6ZM15 9a3 3 0 1 0 0 6a3 3 0 1 0 0-6Z', '#a0a0a0', 14));
      mood.appendChild(svgIcon(ICON.down, '#a0a0a0', 12));
      mood.addEventListener('click', (ev) => { ev.stopPropagation(); openDetail(id); });
      ctrl.appendChild(pill);
      ctrl.appendChild(mood);
      card.appendChild(ctrl);
    } else if (e.type === 'lock') {
      const ctrl = document.createElement('div');
      ctrl.className = 'lox-app__fav-ctrl';
      const pill = pillToggle({ on: !e.locked, brightness: 100 }, () => { ctx.setLock(id, !e.locked); });
      ctrl.appendChild(pill);
      ctrl.appendChild(mk('span', 'font:400 11px -apple-system,sans-serif;color:#a0a0a0;padding:0 6px;', e.locked ? 'Verriegelt' : 'Entriegelt'));
      card.appendChild(ctrl);
    } else if (e.type === 'climate') {
      const mini = document.createElement('div');
      mini.className = 'lox-climate-mini';
      const minus = document.createElement('button');
      minus.type = 'button'; minus.className = 'lox-climate-mini-btn'; minus.textContent = '−';
      minus.addEventListener('click', (ev) => { ev.stopPropagation(); ctx.setClimate(id, e.target - 0.5); });
      const val = document.createElement('span');
      val.className = 'lox-climate-mini-val';
      val.textContent = e.target.toFixed(1) + '°';
      const plus = document.createElement('button');
      plus.type = 'button'; plus.className = 'lox-climate-mini-btn'; plus.textContent = '+';
      plus.addEventListener('click', (ev) => { ev.stopPropagation(); ctx.setClimate(id, e.target + 0.5); });
      mini.appendChild(minus); mini.appendChild(val); mini.appendChild(plus);
      card.appendChild(mini);
    }

    return card;
  }

  // ---- Full-width room row (LOXONE-APP-SPEC §2 "B") ----
  function roomRow(id, ctx) {
    const { state, ICON, openDetail, dispatchSignature } = ctx;
    const e = state[id];
    const sig = () => { if (dispatchSignature && e.signature) dispatchSignature(id); };

    const row = document.createElement('div');
    row.className = 'lox-app__row';
    row.dataset.entity = id;

    const icon = domainIcon(e, ICON);
    const iconBox = document.createElement('div');
    iconBox.className = 'lox-app__row-icon';
    iconBox.appendChild(svgIcon(icon.d, icon.color, 22));

    const textStack = document.createElement('div');
    textStack.className = 'lox-app__row-text';
    const nameEl = mk('div', null, e.name);
    nameEl.className = 'lox-app__row-title';
    const stateEl = mk('div', null, stateText(e));
    stateEl.className = 'lox-app__row-sub';
    if (e.type === 'lock' && !e.locked) stateEl.style.color = '#E33246';
    textStack.appendChild(nameEl);
    textStack.appendChild(stateEl);

    const trailing = document.createElement('div');
    trailing.className = 'lox-app__row-trailing';
    if (e.type === 'light') {
      trailing.appendChild(pillToggle(e, () => {
        ctx.setLight(id, { on: !e.on, brightness: (!e.on && e.brightness === 0) ? 100 : e.brightness });
        sig();
      }));
    } else if (e.type === 'lock') {
      trailing.appendChild(pillToggle({ on: !e.locked, brightness: 100 }, () => ctx.setLock(id, !e.locked)));
    } else {
      trailing.appendChild(svgIcon(ICON.chevronRight, '#a0a0a0', 18));
    }

    row.appendChild(iconBox);
    row.appendChild(textStack);
    row.appendChild(trailing);
    row.addEventListener('click', (ev) => {
      if (trailing.contains(ev.target)) return; // pill handles its own click
      openDetail(id);
    });
    return row;
  }

  function buildFavorites(ctx) {
    const section = document.createElement('div');
    section.className = 'lox-app__section';
    section.appendChild(mk('div', null, 'Favorites')).className = 'lox-app__section-title';
    const grid = document.createElement('div');
    grid.className = 'lox-app__fav-grid';
    Object.entries(ctx.state).filter(([, e]) => e.fav).forEach(([id]) => grid.appendChild(favCard(id, ctx)));
    section.appendChild(grid);
    return section;
  }

  function buildRoomSection(room, ctx) {
    const entries = Object.entries(ctx.state).filter(([, e]) => e.room === room);
    if (!entries.length) return null;
    const wrap = document.createElement('div');
    wrap.className = 'lox-app__room';
    const title = document.createElement('div');
    title.className = 'lox-app__room-title';
    title.appendChild(mk('span', null, room));
    title.appendChild(svgIcon(ctx.ICON.chevronRight, '#fff', 15));
    wrap.appendChild(title);
    entries.forEach(([id]) => wrap.appendChild(roomRow(id, ctx)));
    return wrap;
  }

  function buildRooms(ctx) {
    const frag = document.createDocumentFragment();
    ROOM_ORDER.forEach((room) => { const s = buildRoomSection(room, ctx); if (s) frag.appendChild(s); });
    return frag;
  }

  function mount(host, opts) {
    opts = opts || {};
    injectCss();

    const container = document.createElement('div');
    container.className = 'lox-app';

    const SM_LOX = window.SM_LOX || { state: {}, ICON: {}, C: {}, chips: [] };
    const ICON = SM_LOX.ICON;

    const sectionsWrap = document.createElement('div');
    sectionsWrap.className = 'lox-app__sections';
    sectionsWrap.style.paddingBottom = '76px'; // Platz fuer die floatende Tabbar

    // ---- Deep-View-Router (.lox-view, auf host montiert wie .ha-view — s. ha-app-demo.jsx) ----
    const viewEl = document.createElement('div');
    viewEl.className = 'lox-view';
    const viewBar = document.createElement('div');
    viewBar.className = 'lox-view__bar';
    const backBtn = document.createElement('button');
    backBtn.type = 'button';
    backBtn.className = 'lox-view__back';
    backBtn.setAttribute('aria-label', 'Zurück');
    backBtn.appendChild(svgIcon(ICON.back, '#fff', 22));
    const viewTitle = document.createElement('div');
    viewTitle.className = 'lox-view__title';
    viewBar.appendChild(backBtn);
    viewBar.appendChild(viewTitle);
    const viewBody = document.createElement('div');
    viewBody.className = 'lox-view__body';
    viewEl.appendChild(viewBar);
    viewEl.appendChild(viewBody);

    let closeTimer = null;
    let homeDirty = false;
    let lightRefs = null, coverRefs = null, climateRefs = null;

    function openView(view) {
      if (closeTimer) { clearTimeout(closeTimer); closeTimer = null; }
      viewEl.classList.toggle('modal', !!view.modal);
      viewTitle.textContent = view.title || '';
      viewBody.innerHTML = '';
      viewBody.removeAttribute('style');
      view.build(viewBody);
      viewEl.scrollTop = 0;
      requestAnimationFrame(() => { viewEl.classList.add('open'); });
    }
    function closeView() {
      viewEl.classList.remove('open');
      if (homeDirty) { homeDirty = false; refresh(); }
      lightRefs = null; coverRefs = null; climateRefs = null;
      const reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      if (reduced) viewBody.innerHTML = '';
      else closeTimer = setTimeout(() => { viewBody.innerHTML = ''; closeTimer = null; }, 280);
    }
    backBtn.addEventListener('click', closeView);
    const onViewKeydown = (ev) => { if (ev.key === 'Escape' && viewEl.classList.contains('open')) closeView(); };
    document.addEventListener('keydown', onViewKeydown);

    // ---- Level-Konventionen (muessen zur linken "Mit/Ohne"-Buehne passen, s. ha-app-demo.jsx) ----
    function lightLevel(e) { return e.on ? (1 - e.brightness / 100) : 1; }
    function coverLevel(e) { return e.position / 100; }
    function climateLevel(e) { return (24 - e.target) / 6; }

    function paintLightView() {
      if (!lightRefs) return;
      const e = SM_LOX.state[lightRefs.id];
      lightRefs.readout.textContent = e.on ? e.brightness + ' %' : 'Aus';
      lightRefs.glow.style.opacity = e.on ? e.brightness / 100 : 0;
      lightRefs.fill.style.height = e.brightness + '%';
      lightRefs.thumb.style.bottom = e.brightness + '%';
      lightRefs.powerBtn.classList.toggle('is-on', e.on);
    }
    function setLight(id, patch) {
      const e = SM_LOX.state[id];
      if (!e) return;
      const prevLevel = lightLevel(e);
      if (patch.on !== undefined) e.on = patch.on;
      if (patch.brightness !== undefined) e.brightness = Math.max(0, Math.min(100, Math.round(patch.brightness)));
      if (lightRefs && lightRefs.id === id) paintLightView();
      if (opts.actionTarget && e.signature === 'licht') {
        const level = lightLevel(e);
        opts.actionTarget.dispatchEvent(new CustomEvent('sm-hero-action', { detail: { ex: 'licht', level, prevLevel } }));
      }
      refresh();
    }

    function paintCoverView() {
      if (!coverRefs) return;
      const e = SM_LOX.state[coverRefs.id];
      coverRefs.readout.textContent = e.position + ' %';
      coverRefs.caption.textContent = e.position > 0 ? 'Offen' : 'Geschlossen';
      coverRefs.blind.style.height = (100 - e.position) + '%';
      coverRefs.fill.style.height = e.position + '%';
      coverRefs.thumb.style.bottom = e.position + '%';
    }
    function setCover(id, level) {
      const e = SM_LOX.state[id];
      if (!e) return;
      level = Math.max(0, Math.min(1, level));
      const prevLevel = coverLevel(e);
      e.position = Math.round(level * 100);
      if (coverRefs && coverRefs.id === id) paintCoverView();
      if (opts.actionTarget && e.signature === 'rollladen') {
        opts.actionTarget.dispatchEvent(new CustomEvent('sm-hero-action', { detail: { ex: 'rollladen', level, prevLevel } }));
      }
      refresh();
    }
    let coverTweenRaf = null;
    function tweenCover(id, targetLevel, duration) {
      if (coverTweenRaf) cancelAnimationFrame(coverTweenRaf);
      const startLevel = SM_LOX.state[id].position / 100;
      const start = performance.now();
      const step = (now) => {
        const t = Math.min(1, (now - start) / (duration || 1200));
        const eased = t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
        setCover(id, startLevel + (targetLevel - startLevel) * eased);
        coverTweenRaf = t < 1 ? requestAnimationFrame(step) : null;
      };
      coverTweenRaf = requestAnimationFrame(step);
    }
    function stopCoverTween() { if (coverTweenRaf) { cancelAnimationFrame(coverTweenRaf); coverTweenRaf = null; } }

    function paintClimateView() {
      if (!climateRefs) return;
      const e = SM_LOX.state[climateRefs.id];
      climateRefs.readout.textContent = e.current.toFixed(1) + '°';
      climateRefs.caption.textContent = 'Soll ' + e.target.toFixed(1) + '°';
      climateRefs.stepperValue.textContent = e.target.toFixed(1) + '°';
      climateRefs.chipEls.forEach((chip, i) => chip.classList.toggle('is-active', Math.abs(climateRefs.presets[i] - e.target) < 0.05));
    }
    function setClimate(id, target) {
      const e = SM_LOX.state[id];
      if (!e) return;
      const prevLevel = climateLevel(e);
      target = Math.max(18, Math.min(24, target));
      e.target = Math.round(target * 10) / 10;
      if (climateRefs && climateRefs.id === id) paintClimateView();
      if (opts.actionTarget && e.signature === 'heizung') {
        const level = climateLevel(e);
        opts.actionTarget.dispatchEvent(new CustomEvent('sm-hero-action', { detail: { ex: 'heizung', level, prevLevel } }));
      }
      refresh();
    }

    function setLock(id, locked) {
      const e = SM_LOX.state[id];
      if (!e) return;
      e.locked = locked;
      refresh();
    }

    // ---- Deep view: Lighting ----
    function openLightView(id) {
      const e = SM_LOX.state[id];
      openView({ title: e.name, build(body) {
        const readout = mk('div', null, ''); readout.className = 'lox-light-readout';
        const caption = mk('div', null, e.tag || e.room); caption.className = 'lox-light-caption';
        const glow = document.createElement('div');
        glow.className = 'lox-light-glow';
        glow.style.background = 'radial-gradient(circle, #FEE502 0%, transparent 70%)';
        glow.appendChild(svgIcon(ICON.bulb, '#fff', 40));

        const slider = document.createElement('div');
        slider.className = 'lox-light-slider';
        const fill = document.createElement('div'); fill.className = 'lox-light-slider-fill';
        const thumb = document.createElement('div'); thumb.className = 'lox-light-slider-thumb';
        slider.appendChild(fill); slider.appendChild(thumb);

        const powerRow = document.createElement('div'); powerRow.className = 'lox-power-row';
        const powerBtn = document.createElement('button');
        powerBtn.type = 'button'; powerBtn.className = 'lox-power-btn';
        powerBtn.appendChild(svgIcon(ICON.power, 'currentColor', 22));
        powerBtn.addEventListener('click', () => {
          const en = SM_LOX.state[id];
          const willOn = !en.on;
          setLight(id, { on: willOn, brightness: (willOn && en.brightness === 0) ? 100 : en.brightness });
        });
        powerRow.appendChild(powerBtn);

        body.appendChild(readout); body.appendChild(caption); body.appendChild(glow);
        body.appendChild(slider); body.appendChild(powerRow);

        lightRefs = { id, readout, glow, fill, thumb, powerBtn };
        paintLightView();

        function brightnessFromClientY(clientY) {
          const rect = slider.getBoundingClientRect();
          return Math.round(Math.max(0, Math.min(1, 1 - (clientY - rect.top) / rect.height)) * 100);
        }
        let dragging = false;
        slider.addEventListener('pointerdown', (ev) => {
          dragging = true; slider.setPointerCapture(ev.pointerId);
          const b = brightnessFromClientY(ev.clientY);
          setLight(id, { brightness: b, on: b > 0 });
          ev.stopPropagation();
        });
        slider.addEventListener('pointermove', (ev) => {
          if (!dragging) return;
          const b = brightnessFromClientY(ev.clientY);
          setLight(id, { brightness: b, on: b > 0 });
        });
        const endDrag = () => { dragging = false; };
        slider.addEventListener('pointerup', endDrag);
        slider.addEventListener('pointercancel', endDrag);
      } });
    }

    // ---- Deep view: Shading ----
    function openCoverView(id) {
      const e = SM_LOX.state[id];
      openView({ title: e.name, build(body) {
        const readout = mk('div', null, ''); readout.className = 'lox-cover-readout';
        const caption = mk('div', null, ''); caption.className = 'lox-cover-caption';
        const win = document.createElement('div'); win.className = 'lox-blindwin';
        const blind = document.createElement('div'); blind.className = 'lox-blind';
        win.appendChild(blind);

        const slider = document.createElement('div'); slider.className = 'lox-cover-slider';
        const fill = document.createElement('div'); fill.className = 'lox-cover-slider-fill';
        const thumb = document.createElement('div'); thumb.className = 'lox-cover-slider-thumb';
        slider.appendChild(fill); slider.appendChild(thumb);

        const segRow = document.createElement('div'); segRow.className = 'lox-seg-row';
        const upBtn = document.createElement('button'); upBtn.type = 'button'; upBtn.className = 'lox-seg-btn';
        upBtn.appendChild(svgIcon(ICON.up, '#fff', 18));
        upBtn.addEventListener('click', () => tweenCover(id, 1));
        const stopBtn = document.createElement('button'); stopBtn.type = 'button'; stopBtn.className = 'lox-seg-btn stop-btn';
        stopBtn.textContent = 'STOP';
        stopBtn.addEventListener('click', stopCoverTween);
        const downBtn = document.createElement('button'); downBtn.type = 'button'; downBtn.className = 'lox-seg-btn';
        downBtn.appendChild(svgIcon(ICON.down, '#fff', 18));
        downBtn.addEventListener('click', () => tweenCover(id, 0));
        segRow.appendChild(upBtn); segRow.appendChild(stopBtn); segRow.appendChild(downBtn);

        body.appendChild(readout); body.appendChild(caption); body.appendChild(win);
        body.appendChild(slider); body.appendChild(segRow);

        coverRefs = { id, readout, caption, blind, fill, thumb };
        paintCoverView();

        function levelFromClientY(clientY) {
          const rect = slider.getBoundingClientRect();
          return Math.max(0, Math.min(1, 1 - (clientY - rect.top) / rect.height));
        }
        let dragging = false;
        slider.addEventListener('pointerdown', (ev) => {
          dragging = true; stopCoverTween(); slider.setPointerCapture(ev.pointerId);
          setCover(id, levelFromClientY(ev.clientY));
          ev.stopPropagation();
        });
        slider.addEventListener('pointermove', (ev) => { if (dragging) setCover(id, levelFromClientY(ev.clientY)); });
        const endDrag = () => { dragging = false; };
        slider.addEventListener('pointerup', endDrag);
        slider.addEventListener('pointercancel', endDrag);
      } });
    }

    // ---- Deep view: Climate ----
    function openClimateView(id) {
      const e = SM_LOX.state[id];
      openView({ title: e.name, build(body) {
        const readout = mk('div', null, ''); readout.className = 'lox-climate-readout';
        const caption = mk('div', null, ''); caption.className = 'lox-climate-caption';

        const card = document.createElement('div'); card.className = 'lox-climate-card';
        const minus = document.createElement('button'); minus.type = 'button'; minus.className = 'lox-climate-stepper-btn';
        minus.textContent = '−';
        minus.addEventListener('click', () => setClimate(id, SM_LOX.state[id].target - 0.5));
        const stepperValue = mk('div', null, ''); stepperValue.className = 'lox-climate-stepper-value';
        const plus = document.createElement('button'); plus.type = 'button'; plus.className = 'lox-climate-stepper-btn';
        plus.textContent = '+';
        plus.addEventListener('click', () => setClimate(id, SM_LOX.state[id].target + 0.5));
        card.appendChild(minus); card.appendChild(stepperValue); card.appendChild(plus);

        const presets = [18, 21, 23];
        const presetLabels = ['Frost 18°', 'Eco 21°', 'Comfort 23°'];
        const chipsRow = document.createElement('div'); chipsRow.className = 'lox-climate-chips';
        const chipEls = presets.map((p, i) => {
          const chip = document.createElement('button');
          chip.type = 'button'; chip.className = 'lox-climate-chip'; chip.textContent = presetLabels[i];
          chip.addEventListener('click', () => setClimate(id, p));
          chipsRow.appendChild(chip);
          return chip;
        });

        body.appendChild(readout); body.appendChild(caption); body.appendChild(card); body.appendChild(chipsRow);

        climateRefs = { id, readout, caption, stepperValue, chipEls, presets };
        paintClimateView();
      } });
    }

    // ---- Deep view: Audio ----
    function openMediaView(id) {
      const e = SM_LOX.state[id];
      const render = (body) => {
        body.innerHTML = '';
        body.appendChild(mk('div', null)).className = 'lox-media-art';
        body.appendChild(mk('div', null, e.track.title)).className = 'lox-media-title';
        body.appendChild(mk('div', null, e.track.artist)).className = 'lox-media-artist';
        const ctr = document.createElement('div'); ctr.className = 'lox-media-ctrl';
        const btn = (d, big, fn) => {
          const b = document.createElement('button'); b.type = 'button'; b.className = 'lox-media-btn';
          b.appendChild(svgIcon(d, '#fff', big ? 30 : 22));
          b.addEventListener('click', fn);
          return b;
        };
        ctr.appendChild(btn(ICON.back, false, () => {}));
        ctr.appendChild(btn(e.state === 'playing' ? ICON.pause : ICON.play, true, () => {
          e.state = e.state === 'playing' ? 'paused' : 'playing'; render(body); refresh();
        }));
        ctr.appendChild(btn(ICON.next, false, () => {}));
        body.appendChild(ctr);

        const volRow = document.createElement('div'); volRow.className = 'lox-media-vol-row';
        volRow.appendChild(svgIcon(ICON.audio, '#a0a0a0', 16));
        const track = document.createElement('div'); track.className = 'lox-media-vol';
        const fill = document.createElement('div'); fill.className = 'lox-media-vol-fill'; fill.style.width = e.volume + '%';
        track.appendChild(fill);
        const setVol = (ev) => {
          const r = track.getBoundingClientRect();
          e.volume = Math.round(Math.max(0, Math.min(1, (ev.clientX - r.left) / r.width)) * 100);
          fill.style.width = e.volume + '%';
        };
        track.addEventListener('pointerdown', setVol);
        volRow.appendChild(track);
        body.appendChild(volRow);
      };
      openView({ title: e.name, build: render });
    }

    // ---- Deep view: Access (lock + intercom) ----
    function openLockView(id) {
      const e = SM_LOX.state[id];
      const render = (body) => {
        body.innerHTML = '';
        const big = document.createElement('button');
        big.type = 'button'; big.className = 'lox-lock-btn';
        big.style.background = e.locked ? '#1c1c1c' : '#3a1518';
        big.textContent = e.locked ? '🔒' : '🔓';
        big.addEventListener('click', () => { setLock(id, !e.locked); render(body); });
        body.appendChild(big);
        body.appendChild(mk('div', null, e.locked ? 'Verriegelt' : 'Entriegelt')).className = 'lox-lock-caption';
        body.appendChild(mk('div', null, 'Tür & Fenster')).className = 'lox-access-list-title';
        Object.values(SM_LOX.state).filter((x) => x.type === 'lock').forEach((x) => {
          const row = document.createElement('div'); row.className = 'lox-access-row';
          row.appendChild(mk('span', 'color:#fff;', x.name));
          const st = mk('span', 'color:' + (x.locked ? '#8CFF70' : '#E33246') + ';font-weight:600;', x.locked ? 'Verriegelt' : 'Entriegelt');
          row.appendChild(st);
          body.appendChild(row);
        });
      };
      openView({ title: e.name, build: render });
    }
    function openIntercomView(id) {
      const e = SM_LOX.state[id];
      const lockEntity = Object.entries(SM_LOX.state).find(([, x]) => x.type === 'lock');
      const render = (body) => {
        body.innerHTML = '';
        const cam = document.createElement('div'); cam.className = 'lox-intercom-cam';
        const img = document.createElement('img');
        img.src = 'assets/renders/erklaerung-premium-poster.webp';
        img.style.cssText = 'width:100%;display:block;aspect-ratio:4/3;object-fit:cover;';
        cam.appendChild(img);
        const live = document.createElement('div'); live.className = 'lox-intercom-live';
        live.appendChild(mk('span', 'width:8px;height:8px;border-radius:50%;background:#E33246;display:inline-block;'));
        live.appendChild(mk('span', null, 'LIVE'));
        cam.appendChild(live);
        body.appendChild(cam);

        const actions = document.createElement('div'); actions.className = 'lox-intercom-actions';
        const mkAction = (d, color, label, fn) => {
          const wrap = document.createElement('div'); wrap.className = 'lox-intercom-action';
          const btn = document.createElement('button'); btn.type = 'button'; btn.style.background = color;
          btn.appendChild(svgIcon(d, '#101010', 24));
          btn.addEventListener('click', fn);
          wrap.appendChild(btn); wrap.appendChild(mk('span', null, label));
          return wrap;
        };
        actions.appendChild(mkAction(ICON.phone, '#69C350', 'Connect', () => setLock(lockEntity[0], false)));
        actions.appendChild(mkAction(ICON.clock, '#F7B55C', 'Activity', () => {}));
        body.appendChild(actions);

        const muteRow = document.createElement('div'); muteRow.className = 'lox-app__row';
        muteRow.style.cursor = 'default';
        muteRow.appendChild(mk('div', null, '')).className = 'lox-app__row-icon';
        const txt = document.createElement('div'); txt.className = 'lox-app__row-text';
        txt.appendChild(mk('div', null, 'Mute doorbell')).className = 'lox-app__row-title';
        muteRow.appendChild(txt);
        const trailing = document.createElement('div'); trailing.className = 'lox-app__row-trailing';
        trailing.appendChild(pillToggle({ on: e.mute, brightness: 100 }, () => { e.mute = !e.mute; render(body); }));
        muteRow.appendChild(trailing);
        body.appendChild(muteRow);

        if (lockEntity) {
          const lockRow = document.createElement('div'); lockRow.className = 'lox-access-row';
          const lx = lockEntity[1];
          lockRow.appendChild(mk('span', 'color:' + (lx.locked ? '#fff' : '#E33246') + ';', lx.name + ' · Outside'));
          lockRow.appendChild(mk('span', 'color:' + (lx.locked ? '#8CFF70' : '#E33246') + ';font-weight:600;', lx.locked ? 'Verriegelt' : 'Entriegelt'));
          body.appendChild(mk('div', null, 'Linked Objects')).className = 'lox-access-list-title';
          body.appendChild(lockRow);
        }
      };
      openView({ title: e.name, modal: true, build: render });
    }

    // ---- Generic (energy) ----
    function openEnergyView(id) {
      const e = SM_LOX.state[id];
      openView({ title: e.name, build(body) {
        body.style.display = 'flex'; body.style.alignItems = 'center'; body.style.justifyContent = 'center';
        body.style.minHeight = '60%'; body.style.flexDirection = 'column'; body.style.gap = '8px';
        body.appendChild(mk('div', 'font:700 40px -apple-system,sans-serif;color:#fff;', stateText(e)));
        body.appendChild(mk('div', 'font:400 13px -apple-system,sans-serif;color:#a0a0a0;', e.tag || e.room));
      } });
    }

    const openDetail = (entityId) => {
      const e = SM_LOX.state[entityId];
      if (!e) return;
      switch (e.type) {
        case 'light': return openLightView(entityId);
        case 'cover': return openCoverView(entityId);
        case 'climate': return openClimateView(entityId);
        case 'media': return openMediaView(entityId);
        case 'lock': return openLockView(entityId);
        case 'intercom': return openIntercomView(entityId);
        default: return openEnergyView(entityId);
      }
    };

    const dispatchSignature = (id) => {
      const e = SM_LOX.state[id];
      if (!opts.actionTarget || !e || !e.signature) return;
      const clamp = (v) => Math.max(0, Math.min(1, v));
      let ex, level;
      if (e.type === 'cover') { ex = 'rollladen'; level = clamp(e.position / 100); }
      else if (e.type === 'light') { ex = 'licht'; level = clamp(e.on ? (1 - e.brightness / 100) : 1); }
      else if (e.type === 'climate') { ex = 'heizung'; level = clamp((24 - e.target) / 6); }
      else return;
      opts.actionTarget.dispatchEvent(new CustomEvent('sm-hero-action', { detail: { ex, level, prevLevel: level } }));
    };

    function refresh() {
      if (viewEl.classList.contains('open')) { homeDirty = true; return; }
      const scrollTop = container.scrollTop;
      sectionsWrap.innerHTML = '';
      const ctx = { state: SM_LOX.state, ICON, C: SM_LOX.C, openDetail, refresh, dispatchSignature, setLight, setCover, setClimate, setLock };
      sectionsWrap.appendChild(buildFavorites(ctx));
      sectionsWrap.appendChild(buildRooms(ctx));
      container.scrollTop = scrollTop;
    }

    const { header, menuBtn } = buildHeader();
    container.appendChild(header);
    container.appendChild(buildChips());
    container.appendChild(sectionsWrap);
    host.appendChild(container);
    // .lox-view mounts on host (not the internally-scrolling .lox-app) — s. Kommentar in
    // ha-app-demo.jsx: sonst ist inset:0 relativ zur gescrollten Content-Box versetzt.
    host.appendChild(viewEl);

    // Floating pill tab bar (LOXONE-APP-SPEC §1 "Bottom tab bar") — kein Dead-Button: jeder
    // Tab hat eine Funktion (Home scrollt hoch, Favorites/Rooms scrollen zur Sektion).
    const tabbar = document.createElement('div');
    tabbar.className = 'lox-tabbar';
    const tabDefs = [
      { icon: ICON.star, label: 'Favorites', fn: () => { const s = sectionsWrap.querySelector('.lox-app__fav-grid'); if (s) s.scrollIntoView({ behavior: 'smooth', block: 'start' }); } },
      { icon: ICON.home, label: 'Home', active: true, fn: () => { closeView(); container.scrollTop = 0; } },
      { icon: ICON.rooms, label: 'Rooms', fn: () => { const s = sectionsWrap.querySelector('.lox-app__room'); if (s) s.scrollIntoView({ behavior: 'smooth', block: 'start' }); } },
      { icon: ICON.hex, label: 'Devices', fn: () => { container.scrollTop = container.scrollHeight; } },
    ];
    const tabBtns = tabDefs.map((t) => {
      const b = document.createElement('button');
      b.type = 'button';
      b.className = 'lox-tabbar__btn' + (t.active ? ' active' : '');
      b.setAttribute('aria-label', t.label);
      b.appendChild(svgIcon(t.icon, 'currentColor', 20));
      b.addEventListener('click', () => {
        tabBtns0.forEach((x) => x.classList.remove('active'));
        b.classList.add('active');
        t.fn();
      });
      tabbar.appendChild(b);
      return b;
    });
    const tabBtns0 = tabBtns;
    host.appendChild(tabbar);
    menuBtn.addEventListener('click', () => { closeView(); container.scrollTop = 0; });

    refresh();

    const instance = {
      actionTarget: opts.actionTarget,
      world: opts.world,
      show() { container.style.display = ''; viewEl.style.display = ''; tabbar.style.display = ''; },
      hide() { container.style.display = 'none'; closeView(); viewEl.style.display = 'none'; tabbar.style.display = 'none'; },
      showHome() { container.scrollTop = 0; },
      destroy() {
        document.removeEventListener('keydown', onViewKeydown);
        if (closeTimer) clearTimeout(closeTimer);
        if (coverTweenRaf) cancelAnimationFrame(coverTweenRaf);
        if (container.parentNode) container.parentNode.removeChild(container);
        if (viewEl.parentNode) viewEl.parentNode.removeChild(viewEl);
        if (tabbar.parentNode) tabbar.parentNode.removeChild(tabbar);
        delete window.__loxOpen; delete window.__loxCloseView; delete window.__loxSet; delete window.__loxApp;
      },
    };

    window.__loxOpen = openDetail;
    window.__loxCloseView = closeView;
    window.__loxSet = (entityId, level) => {
      const e = SM_LOX.state[entityId];
      if (!e) return;
      if (e.type === 'cover') setCover(entityId, level);
      else if (e.type === 'light') setLight(entityId, { on: level > 0, brightness: Math.round(level * 100) });
      else if (e.type === 'climate') setClimate(entityId, 18 + level * 6);
    };
    window.__loxApp = instance;
    return instance;
  }

  return { mount };
})();
