// Interaktiver HA-App-Nachbau fuers Phone-Mockup (Task A3+B). Vanilla imperative DOM
// (kein JSX/React noetig) — nur ueber <script type="text/babel" src> geladen, damit
// derselbe Babel-Loader-Mechanismus wie fuer die anderen Hero-Skripte greift.
// Konsumiert window.SM_HA (ha-app-data.js). Registriert eine Factory auf window.
window.HaAppDemo = (function () {
  const SVG_NS = 'http://www.w3.org/2000/svg';
  const ROOMS = ['Wohnzimmer', 'Küche', 'Klima', 'Energie', 'Zutritt'];
  // Sun/brightness glyph for the light more-info's mode-row (HA-APP-SPEC §4.5) — not part of
  // SM_HA.ICON since it's purely a local decorative glyph, no other view needs it.
  const ICON_BRIGHTNESS = 'M12 3v2M12 19v2M5 12H3M21 12h-2M6.3 6.3 4.9 4.9M19.1 19.1l-1.4-1.4' +
    'M6.3 17.7l-1.4 1.4M19.1 4.9l-1.4 1.4M12 7a5 5 0 1 0 0 10a5 5 0 1 0 0-10Z';

  function injectCss() {
    if (document.getElementById('ha-app-css')) return;
    const style = document.createElement('style');
    style.id = 'ha-app-css';
    style.textContent = `
.ha-app{ position:absolute; inset:0; z-index:1; overflow-y:auto; -webkit-overflow-scrolling:touch;
  background:#fafafa; color:#141414; font-family:Inter,system-ui,sans-serif; }
.ha-app *{ box-sizing:border-box; }
.ha-app__header{ height:56px; background:#fff; padding:0 12px; display:flex; align-items:center; gap:12px; }
.ha-app__icon-btn{ appearance:none; border:none; background:none; padding:8px; margin:-8px; display:flex;
  align-items:center; justify-content:center; color:#141414; cursor:pointer; line-height:1; }
.ha-app__hamburger-bars{ display:flex; flex-direction:column; gap:4px; width:18px; }
.ha-app__hamburger-bar{ display:block; height:2px; width:100%; background:#141414; border-radius:1px; }
.ha-app__title{ font:400 20px/32px Inter,system-ui,sans-serif; color:#141414; flex:1; }
.ha-app__kebab{ font-size:20px; }
.ha-app__chips{ display:flex; flex-wrap:wrap; gap:8px; padding:12px; }
.ha-app__chip{ display:inline-flex; align-items:center; gap:6px; background:#fff;
  border:1px solid rgba(0,0,0,.12); border-radius:100px; padding:6px 12px; }
.ha-app__chip-value{ font:500 12px/1 Inter,system-ui,sans-serif; color:#141414; }
.ha-app__section{ padding:4px 12px 16px; }
.ha-app__section-title{ font:400 16px/25.6px Inter,system-ui,sans-serif; color:#141414;
  letter-spacing:.1px; padding:8px 0; }
.ha-app__grid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.ha-app__tile{ background:#fff; border:1px solid rgba(0,0,0,.12); border-radius:12px; box-shadow:none;
  padding:12px; box-sizing:border-box; min-height:56px; display:flex; flex-direction:column; gap:12px;
  cursor:pointer; }
.ha-app__tile.wide{ grid-column:1 / -1; }
.ha-app__tile-row{ display:flex; align-items:center; gap:10px; }
.ha-app__tile-icon{ width:36px; height:36px; flex:0 0 36px; display:flex; align-items:center; justify-content:center; }
.ha-app__tile-thumb{ width:56px; height:32px; flex:0 0 56px; border-radius:6px; background-size:cover;
  background-position:center; background-color:#e5e5e5; }
.ha-app__tile-text{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.ha-app__tile-name{ font:500 14px/22.4px Inter,system-ui,sans-serif; color:#141414; }
.ha-app__tile-state{ font:400 12px/14.4px Inter,system-ui,sans-serif; color:#5e5e5e; }
.ha-app__slider{ position:relative; height:42px; border-radius:21px; touch-action:none; user-select:none; }
.ha-app__slider-fill{ position:absolute; top:0; left:0; bottom:0; border-radius:21px; }
.ha-app__slider-thumb{ position:absolute; top:50%; width:4px; height:18px; border-radius:2px; background:#fff;
  transform:translate(-50%,-50%); }
.ha-app__stepper{ display:flex; align-items:center; justify-content:space-between; background:#f2f2f2;
  border-radius:100px; padding:4px; }
.ha-app__stepper-btn{ appearance:none; width:32px; height:32px; flex:0 0 32px; border-radius:50%; border:none;
  background:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer;
  font:500 16px/1 Inter,system-ui,sans-serif; color:#141414; }
.ha-app__stepper-value{ font:500 14px/1 Inter,system-ui,sans-serif; color:#141414; }
.ha-view{ position:absolute; inset:0; z-index:6; background:#fff; overflow-y:auto;
  transform:translateX(100%); transition:transform .28s ease; display:flex; flex-direction:column; }
.ha-view.open{ transform:translateX(0); }
.ha-view__bar{ position:sticky; top:0; height:56px; display:flex; align-items:center; gap:8px;
  padding:0 8px; background:#fff; }
.ha-view__back{ width:48px; height:48px; border:0; background:transparent; cursor:pointer;
  display:flex; align-items:center; justify-content:center; }
.ha-view__title{ font:500 20px/32px Inter,system-ui,sans-serif; color:#141414; }
.ha-view__body{ flex:1; padding:8px 16px 24px; }
@media (prefers-reduced-motion: reduce){ .ha-view{ transition:none; } }
.ha-generic-state{ font:400 36px/43.2px Inter,system-ui,sans-serif; color:#141414; text-align:center; }
.ha-cover-readout{ font:400 36px/43.2px Inter,system-ui,sans-serif; color:#141414; text-align:center; margin-top:8px; }
.ha-cover-caption{ font:400 13px/1 Inter,system-ui,sans-serif; color:#5e5e5e; text-align:center; margin-top:6px; }
.ha-blindwin{ position:relative; width:100%; aspect-ratio:4/3; border-radius:12px; overflow:hidden;
  background:linear-gradient(#bfe0f5,#eaf6fd); border:1px solid rgba(0,0,0,.1); margin:16px 0; }
.ha-blind{ position:absolute; left:0; top:0; width:100%;
  background:repeating-linear-gradient(#c9cfd6 0 6px, #b6bdc6 6px 8px); box-shadow:0 2px 6px rgba(0,0,0,.25);
  transition:height .12s linear; }
.ha-cover-slider{ width:56px; height:220px; border-radius:28px; background:#e9edf0; margin:0 auto; position:relative; }
.ha-cover-slider-fill{ position:absolute; left:0; right:0; bottom:0; border-radius:28px; background:#926bc7; }
.ha-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,.3); transform:translate(-50%,50%); }
.ha-cover-chips{ display:flex; justify-content:space-between; gap:8px; margin-top:20px; }
.ha-cover-chip{ flex:1; padding:8px 0; border-radius:100px; border:none; background:#f2f2f2;
  font:500 13px/1 Inter,system-ui,sans-serif; color:#141414; cursor:pointer; }
.ha-cover-chip.active{ background:#926bc7; color:#fff; }
.ha-light-readout{ font:400 36px/43.2px Inter,system-ui,sans-serif; color:#141414; text-align:center; margin-top:8px; }
.ha-light-caption{ font:400 13px/1 Inter,system-ui,sans-serif; color:#5e5e5e; text-align:center; margin-top:6px; }
.ha-lampglow{ width:120px; height:120px; margin:24px auto 8px; border-radius:50%; display:flex;
  align-items:center; justify-content:center; transition:opacity .2s; }
.ha-light-slider{ width:56px; height:220px; border-radius:28px; margin:16px auto 0; position:relative; }
.ha-light-slider-fill{ position:absolute; left:0; right:0; bottom:0; border-radius:28px; }
.ha-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,.3); transform:translate(-50%,50%); }
.ha-light-modes{ display:flex; align-items:center; justify-content:center; gap:10px; background:#f3f4f4;
  border-radius:100px; padding:6px 14px; margin-top:20px; }
.ha-light-mode-btn{ appearance:none; width:40px; height:40px; border-radius:50%; border:none; background:transparent;
  display:flex; align-items:center; justify-content:center; cursor:pointer; padding:0; }
.ha-light-mode-btn.selected{ background:#141414; }
.ha-light-mode-divider{ width:1px; height:24px; background:rgba(0,0,0,.12); }
.ha-light-mode-swatch{ width:24px; height:24px; border-radius:50%; }
.ha-light-mode-swatch--color{ background:conic-gradient(red,orange,yellow,lime,cyan,blue,magenta,red); }
.ha-light-mode-swatch--temp{ background:linear-gradient(90deg,#ffb46b,#cfe4ff); }
.ha-climate-readout{ font:400 36px/43.2px Inter,system-ui,sans-serif; color:#141414; text-align:center; margin-top:8px; }
.ha-climate-caption{ font:400 13px/1 Inter,system-ui,sans-serif; color:#5e5e5e; text-align:center; margin-top:6px; }
.ha-thermo-dial{ width:180px; height:180px; margin:20px auto; border-radius:50%; display:flex;
  align-items:center; justify-content:center; }
.ha-thermo-dial-inner{ width:144px; height:144px; border-radius:50%; background:#fff;
  box-shadow:0 1px 4px rgba(0,0,0,.08); display:flex; align-items:center; justify-content:center; }
.ha-thermo-dial-value{ font:500 28px/1 Inter,system-ui,sans-serif; color:#141414; }
.ha-climate-stepper{ display:flex; align-items:center; justify-content:space-between; background:#f2f2f2;
  border-radius:100px; padding:8px; margin-top:20px; }
.ha-climate-stepper-btn{ appearance:none; width:48px; height:48px; flex:0 0 48px; border-radius:50%; border:none;
  background:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer;
  font:500 22px/1 Inter,system-ui,sans-serif; color:#141414; }
.ha-climate-stepper-value{ font:500 20px/1 Inter,system-ui,sans-serif; color:#141414; }
.ha-climate-chips{ display:flex; justify-content:space-between; gap:8px; margin-top:16px; }
.ha-climate-chip{ flex:1; padding:8px 0; border-radius:100px; border:none; background:#f2f2f2;
  font:500 13px/1 Inter,system-ui,sans-serif; color:#141414; cursor:pointer; }
.ha-climate-chip.active{ background:#ff9800; color:#fff; }
`;
    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 || '#141414');
    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;
  }

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

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

    const title = document.createElement('div');
    title.className = 'ha-app__title';
    title.textContent = 'Demo';

    const kebabBtn = document.createElement('button');
    kebabBtn.type = 'button';
    kebabBtn.className = 'ha-app__icon-btn ha-app__kebab';
    kebabBtn.setAttribute('aria-label', 'Mehr');
    kebabBtn.textContent = '⋮';

    header.appendChild(menuBtn);
    header.appendChild(title);
    header.appendChild(kebabBtn);
    return header;
  }

  function buildChips() {
    const row = document.createElement('div');
    row.className = 'ha-app__chips';
    const chips = (window.SM_HA && window.SM_HA.chips) || [];
    const ICON = (window.SM_HA && window.SM_HA.ICON) || {};
    for (const chip of chips) {
      const pill = document.createElement('div');
      pill.className = 'ha-app__chip';
      const d = ICON[chip.icon];
      if (d) pill.appendChild(svgIcon(d, chip.color, 16));
      const val = document.createElement('span');
      val.className = 'ha-app__chip-value';
      val.textContent = chip.v;
      pill.appendChild(val);
      row.appendChild(pill);
    }
    return row;
  }

  // Sensor/energy_tile glyph choice by device tag — shape isn't spec'd exactly,
  // only the color (gray, or accent for device:'pv'); pick a sensible existing glyph.
  function sensorGlyph(e, ICON) {
    if (e.device === 'temperature') return ICON.thermo;
    if (e.device === 'car') return ICON.car;
    return ICON.energy;
  }

  // Wide (2-col) light slider — horizontal fat pill (HA-APP-SPEC §3.A). Drag sets
  // brightness 0..100 with live fill during the drag, full refresh() on release.
  function attachWideSlider(sliderEl, fillEl, thumbEl, e, refresh, onSettle) {
    function pctFromClientX(clientX) {
      const rect = sliderEl.getBoundingClientRect();
      const x = Math.max(rect.left, Math.min(rect.right, clientX));
      return Math.round(((x - rect.left) / rect.width) * 100);
    }
    function paint(val) {
      fillEl.style.width = val + '%';
      thumbEl.style.left = val + '%';
    }
    let dragging = false;
    sliderEl.addEventListener('pointerdown', (ev) => {
      dragging = true;
      sliderEl.setPointerCapture(ev.pointerId);
      e.brightness = pctFromClientX(ev.clientX);
      paint(e.brightness);
      ev.stopPropagation();
    });
    sliderEl.addEventListener('pointermove', (ev) => {
      if (!dragging) return;
      e.brightness = pctFromClientX(ev.clientX);
      paint(e.brightness);
    });
    function release() {
      if (!dragging) return;
      dragging = false;
      refresh();
      if (onSettle) onSettle();
    }
    sliderEl.addEventListener('pointerup', release);
    sliderEl.addEventListener('pointercancel', release);
  }

  // HA-exact tile factory (HA-APP-SPEC §2/§6.2) — one card per entity, switching on
  // state[id].type. Every returned card carries data-entity for testability + later
  // deep-view wiring. Icon-tap toggles (light/lock); name/state-area tap opens detail;
  // tiles with no toggle domain open detail from anywhere on the row.
  function tile(id, ctx) {
    const { state, ICON, C, openDetail, refresh, dispatchSignature } = ctx;
    const e = state[id];
    // Home-Tile-Schnellbedienung soll — wie die More-Info — die linke Buehne animieren,
    // wenn die Entity eine Signature-Szene ist (Bar lamp/Spotlights/Heizung).
    const sig = () => { if (dispatchSignature && e.signature) dispatchSignature(id); };

    const card = document.createElement('div');
    card.className = 'ha-app__tile' + (e.wide ? ' wide' : '');
    card.dataset.entity = id;

    const row = document.createElement('div');
    row.className = 'ha-app__tile-row';

    const textStack = document.createElement('div');
    textStack.className = 'ha-app__tile-text';
    const nameEl = document.createElement('div');
    nameEl.className = 'ha-app__tile-name';
    nameEl.textContent = e.name;
    const stateEl = document.createElement('div');
    stateEl.className = 'ha-app__tile-state';
    textStack.appendChild(nameEl);
    textStack.appendChild(stateEl);

    const iconBox = document.createElement('div');
    iconBox.className = 'ha-app__tile-icon';
    let toggleFn = null;

    switch (e.type) {
      case 'light': {
        const bg = e.warm ? C.lightWarmBg : C.lightStdBg;
        const fg = e.warm ? C.lightWarmFg : C.lightStdFg;
        if (e.on) {
          iconBox.style.background = bg;
          iconBox.style.borderRadius = '50%';
          iconBox.appendChild(svgIcon(ICON.bulb, fg, 24));
          stateEl.textContent = e.brightness + ' %';
        } else {
          iconBox.appendChild(svgIcon(ICON.bulb, C.off, 24));
          stateEl.textContent = 'Aus';
          stateEl.style.color = C.off;
        }
        toggleFn = () => {
          e.on = !e.on;
          if (e.on && e.brightness === 0) e.brightness = 100;
          refresh();
          sig();
        };
        break;
      }
      case 'cover': {
        const open = e.position > 0;
        iconBox.appendChild(svgIcon(ICON.blind, open ? C.coverOn : C.off, 24));
        stateEl.textContent = open ? 'Geöffnet · ' + e.position + ' %' : 'Geschlossen';
        break;
      }
      case 'media': {
        const playing = e.state === 'playing';
        iconBox.appendChild(svgIcon(ICON.audio, playing ? C.mediaOn : C.off, 24));
        stateEl.textContent = playing ? 'Wiedergabe' : 'Aus';
        break;
      }
      case 'climate': {
        const heating = e.mode === 'heat';
        iconBox.appendChild(svgIcon(ICON.heat, heating ? C.accent : C.off, 24));
        stateEl.textContent = e.current.toFixed(1) + ' °C';
        break;
      }
      case 'heatpump': {
        iconBox.appendChild(svgIcon(ICON.heat, C.accent, 24));
        stateEl.textContent = 'WP · ' + e.current + ' °C · COP ' + e.cop;
        break;
      }
      case 'sensor':
      case 'energy_tile': {
        const color = e.device === 'pv' ? C.accent : C.off;
        iconBox.appendChild(svgIcon(sensorGlyph(e, ICON), color, 24));
        stateEl.textContent = e.value + (e.unit ? ' ' + e.unit : '');
        break;
      }
      case 'lock': {
        iconBox.appendChild(svgIcon(ICON.lock, e.locked ? C.ink : C.accent, 24));
        stateEl.textContent = e.locked ? 'Verriegelt' : 'Entriegelt';
        toggleFn = () => { e.locked = !e.locked; refresh(); };
        break;
      }
      case 'door': {
        iconBox.appendChild(svgIcon(ICON.shield, e.open ? C.accent : C.off, 24));
        stateEl.textContent = e.open ? 'Offen' : 'Geschlossen';
        break;
      }
      case 'camera': {
        iconBox.className = 'ha-app__tile-thumb';
        iconBox.style.backgroundImage = 'url(' + e.poster + ')';
        stateEl.textContent = 'Live';
        stateEl.style.color = C.mediaOn;
        break;
      }
      default: {
        iconBox.appendChild(svgIcon(ICON.bulb, C.off, 24));
      }
    }

    row.appendChild(iconBox);
    row.appendChild(textStack);
    card.appendChild(row);

    // Icon-tap vs tile-tap distinction (HA-APP-SPEC §4): icon toggles (when a toggle
    // domain), name/state area always opens detail. Non-toggle domains open detail
    // from anywhere in the row (icon included, via bubbling).
    if (toggleFn) {
      iconBox.addEventListener('click', (ev) => { ev.stopPropagation(); toggleFn(); });
    }
    row.addEventListener('click', () => openDetail(id));

    // Wide light slider (Spotlights) — full-width fat pill below the name/state row.
    if (e.type === 'light' && e.wide) {
      const bg = e.warm ? C.lightWarmBg : C.lightStdBg;
      const fg = e.warm ? C.lightWarmFg : C.lightStdFg;
      const slider = document.createElement('div');
      slider.className = 'ha-app__slider';
      slider.style.background = bg;
      const fill = document.createElement('div');
      fill.className = 'ha-app__slider-fill';
      fill.style.background = fg;
      fill.style.width = e.brightness + '%';
      const thumb = document.createElement('div');
      thumb.className = 'ha-app__slider-thumb';
      thumb.style.left = e.brightness + '%';
      slider.appendChild(fill);
      slider.appendChild(thumb);
      card.appendChild(slider);
      attachWideSlider(slider, fill, thumb, e, refresh, sig);
    }

    // Climate stepper — full-width pill with circular ±0.5 buttons.
    if (e.type === 'climate') {
      const stepper = document.createElement('div');
      stepper.className = 'ha-app__stepper';
      const minus = document.createElement('button');
      minus.type = 'button';
      minus.className = 'ha-app__stepper-btn';
      minus.textContent = '−';
      minus.addEventListener('click', (ev) => {
        ev.stopPropagation();
        e.target = Math.max(18, Math.min(24, Math.round((e.target - 0.5) * 10) / 10));
        refresh();
        sig();
      });
      const value = document.createElement('div');
      value.className = 'ha-app__stepper-value';
      value.textContent = e.target.toFixed(1) + ' °C';
      const plus = document.createElement('button');
      plus.type = 'button';
      plus.className = 'ha-app__stepper-btn';
      plus.textContent = '+';
      plus.addEventListener('click', (ev) => {
        ev.stopPropagation();
        e.target = Math.max(18, Math.min(24, Math.round((e.target + 0.5) * 10) / 10));
        refresh();
        sig();
      });
      stepper.appendChild(minus);
      stepper.appendChild(value);
      stepper.appendChild(plus);
      card.appendChild(stepper);
    }

    return card;
  }

  function buildSection(room, ctx) {
    const section = document.createElement('div');
    section.className = 'ha-app__section';

    const heading = document.createElement('div');
    heading.className = 'ha-app__section-title';
    heading.textContent = room;

    const grid = document.createElement('div');
    grid.className = 'ha-app__grid';
    const entries = Object.entries(ctx.state).filter(([, e]) => e.room === room);
    for (const [id] of entries) grid.appendChild(tile(id, ctx));

    section.appendChild(heading);
    section.appendChild(grid);
    return section;
  }

  function buildSections(ctx) {
    const frag = document.createDocumentFragment();
    for (const room of ROOMS) frag.appendChild(buildSection(room, ctx));
    return frag;
  }

  // A3 rendered only the Wohnzimmer section, which often didn't fill the tall phone
  // screen (400x880) on its own. A dynamic bottom buffer ensured the container stayed
  // internally scrollable in every case (contract: internal scroll, not page scroll).
  // Task B adds all five sections, whose combined content now overflows on its own
  // (need <= 0) — so this helper self-eliminates and appends no spacer. Left as-is
  // per contract. Note: scrollHeight is capped at clientHeight (browser semantics) —
  // actual content height must come from summing child rects, not scrollHeight itself.
  function ensureScrollable(container) {
    const contentH = Array.from(container.children).reduce((sum, el) => sum + el.getBoundingClientRect().height, 0);
    const need = container.clientHeight - contentH;
    if (need > 0) {
      const spacer = document.createElement('div');
      spacer.className = 'ha-app__spacer';
      spacer.style.height = (need + 32) + 'px';
      container.appendChild(spacer);
    }
  }

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

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

    const SM_HA = window.SM_HA || { state: {}, ICON: {}, C: {} };

    const sectionsWrap = document.createElement('div');
    sectionsWrap.className = 'ha-app__sections';

    // --- C1: Deep-View-Router — one reusable slide-in view over the home screen,
    // reused across opens (helpers openView/closeView below); back-button + Escape
    // (only while open) close it. Closing never dispatches sm-hero-reset — the left
    // stage keeps showing its last level.
    const viewEl = document.createElement('div');
    viewEl.className = 'ha-view';
    const viewBar = document.createElement('div');
    viewBar.className = 'ha-view__bar';
    const backBtn = document.createElement('button');
    backBtn.type = 'button';
    backBtn.className = 'ha-view__back';
    backBtn.setAttribute('aria-label', 'Zurück');
    backBtn.appendChild(svgIcon(SM_HA.ICON.back, SM_HA.C.ink, 24));
    const viewTitle = document.createElement('div');
    viewTitle.className = 'ha-view__title';
    viewBar.appendChild(backBtn);
    viewBar.appendChild(viewTitle);
    const viewBody = document.createElement('div');
    viewBody.className = 'ha-view__body';
    viewEl.appendChild(viewBar);
    viewEl.appendChild(viewBody);

    let closeTimer = null;

    function openView(view) {
      if (closeTimer) { clearTimeout(closeTimer); closeTimer = null; }
      viewTitle.textContent = view.title || '';
      viewBody.innerHTML = '';
      viewBody.removeAttribute('style');
      view.build(viewBody);
      viewEl.scrollTop = 0;
      requestAnimationFrame(() => { viewEl.classList.add('open'); });
    }

    let homeDirty = false;
    function closeView() {
      viewEl.classList.remove('open');
      if (homeDirty) { homeDirty = false; refresh(); }
      coverRefs = null;
      lightRefs = 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);

    // --- C2: Rollladen more-info — the signature interaction. coverRefs holds the
    // currently-open cover view's live DOM refs so setCover() can repaint it without
    // caring whether a view is open at all; window.__haSet (and later tasks) call
    // setCover() directly regardless of UI state.
    let coverRefs = null; // { id, readout, caption, blindEl, sliderFill, sliderThumb, chipEls, presets }

    function paintCoverView() {
      if (!coverRefs) return;
      const pos = SM_HA.state[coverRefs.id].position;
      coverRefs.readout.textContent = pos + ' %';
      coverRefs.caption.textContent = pos > 0 ? 'Geöffnet' : 'Geschlossen';
      coverRefs.blindEl.style.height = (100 - pos) + '%';
      coverRefs.sliderFill.style.height = pos + '%';
      coverRefs.sliderThumb.style.bottom = pos + '%';
      coverRefs.chipEls.forEach((chip, i) => chip.classList.toggle('active', coverRefs.presets[i] === pos));
    }

    // Level convention (must match the hero-welten left stage): level = position/100,
    // level 1 = offen. Single choke point for drag + chip taps + the test hook —
    // updates state, repaints the view (if open), fires sm-hero-action, refreshes
    // the home tile.
    function setCover(entityId, level) {
      const e = SM_HA.state[entityId];
      if (!e) return;
      level = Math.max(0, Math.min(1, level));
      const prevLevel = e.position / 100;
      e.position = Math.round(level * 100);
      if (coverRefs && coverRefs.id === entityId) paintCoverView();
      if (opts.actionTarget) {
        opts.actionTarget.dispatchEvent(new CustomEvent('sm-hero-action', { detail: { ex: 'rollladen', level, prevLevel } }));
      }
      refresh();
    }

    // Preset-Chips sollen den Rollladen ueber ~1,8s fahren (nicht instant springen) — der
    // "Mit"-Scrub folgt Frame fuer Frame; die Person triggert per Debounce einmal aufs Ziel.
    let coverTweenRaf = null;
    function tweenCover(entityId, targetLevel, duration) {
      if (coverTweenRaf) cancelAnimationFrame(coverTweenRaf);
      const startLevel = SM_HA.state[entityId].position / 100;
      const start = performance.now();
      const step = (now) => {
        const t = Math.min(1, (now - start) / (duration || 1800));
        const eased = t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
        setCover(entityId, startLevel + (targetLevel - startLevel) * eased);
        coverTweenRaf = t < 1 ? requestAnimationFrame(step) : null;
      };
      coverTweenRaf = requestAnimationFrame(step);
    }

    function openCoverView(id) {
      const e = SM_HA.state[id];
      openView({
        title: e.name,
        build(body) {
          const readout = document.createElement('div');
          readout.className = 'ha-cover-readout';
          const caption = document.createElement('div');
          caption.className = 'ha-cover-caption';

          const winEl = document.createElement('div');
          winEl.className = 'ha-blindwin';
          const blindEl = document.createElement('div');
          blindEl.className = 'ha-blind';
          winEl.appendChild(blindEl);

          const sliderTrack = document.createElement('div');
          sliderTrack.className = 'ha-cover-slider';
          const sliderFill = document.createElement('div');
          sliderFill.className = 'ha-cover-slider-fill';
          const sliderThumb = document.createElement('div');
          sliderThumb.className = 'ha-cover-slider-thumb';
          sliderTrack.appendChild(sliderFill);
          sliderTrack.appendChild(sliderThumb);

          const presets = [0, 25, 50, 75, 100];
          const chipsRow = document.createElement('div');
          chipsRow.className = 'ha-cover-chips';
          const chipEls = presets.map((p) => {
            const chip = document.createElement('button');
            chip.type = 'button';
            chip.className = 'ha-cover-chip';
            chip.textContent = p + ' %';
            chip.addEventListener('click', () => tweenCover(id, p / 100));
            chipsRow.appendChild(chip);
            return chip;
          });

          body.appendChild(readout);
          body.appendChild(caption);
          body.appendChild(winEl);
          body.appendChild(sliderTrack);
          body.appendChild(chipsRow);

          coverRefs = { id, readout, caption, blindEl, sliderFill, sliderThumb, chipEls, presets };
          paintCoverView();

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

    // --- C3: Licht more-info. Bar lamp/Spotlights carry signature:'licht' and drive the left
    // stage; Floor lamp/Worktop are ordinary lights (update locally, never dispatch — no person).
    let lightRefs = null; // { id, readout, glowEl, sliderFill, sliderThumb }

    function lightLevel(e) {
      return e.on ? (1 - e.brightness / 100) : 1;
    }

    function paintLightView() {
      if (!lightRefs) return;
      const e = SM_HA.state[lightRefs.id];
      lightRefs.readout.textContent = e.on ? e.brightness + ' %' : 'Aus';
      lightRefs.glowEl.style.opacity = e.on ? e.brightness / 100 : 0;
      lightRefs.sliderFill.style.height = e.brightness + '%';
      lightRefs.sliderThumb.style.bottom = e.brightness + '%';
    }

    // Level convention (must match the hero-welten left stage): level = on ? (1-brightness/100)
    // : 1 — level 1 = "aus" (before/passive), level 0 = full brightness "an" (after/active).
    // Single choke point for the power button, the drag slider, and the test hook.
    function setLight(id, patch) {
      const e = SM_HA.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 openLightView(id) {
      const e = SM_HA.state[id];
      const ICON = SM_HA.ICON;
      const C = SM_HA.C;
      const glowColor = e.warm ? C.lightWarmFg : C.lightStdFg;
      const trackColor = e.warm ? C.lightWarmBg : C.lightStdBg;
      openView({
        title: e.name,
        build(body) {
          const readout = document.createElement('div');
          readout.className = 'ha-light-readout';
          const caption = document.createElement('div');
          caption.className = 'ha-light-caption';
          caption.textContent = 'Vor wenigen Minuten';

          const glow = document.createElement('div');
          glow.className = 'ha-lampglow';
          glow.style.background = 'radial-gradient(circle, ' + glowColor + ' 0%, transparent 70%)';
          glow.appendChild(svgIcon(ICON.bulb, C.ink, 40));

          const sliderTrack = document.createElement('div');
          sliderTrack.className = 'ha-light-slider';
          sliderTrack.style.background = trackColor;
          const sliderFill = document.createElement('div');
          sliderFill.className = 'ha-light-slider-fill';
          sliderFill.style.background = glowColor;
          const sliderThumb = document.createElement('div');
          sliderThumb.className = 'ha-light-slider-thumb';
          sliderTrack.appendChild(sliderFill);
          sliderTrack.appendChild(sliderThumb);

          const modeRow = document.createElement('div');
          modeRow.className = 'ha-light-modes';
          const powerBtn = document.createElement('button');
          powerBtn.type = 'button';
          powerBtn.className = 'ha-light-mode-btn';
          powerBtn.setAttribute('aria-label', 'Ein/Aus');
          powerBtn.appendChild(svgIcon(ICON.power, C.ink, 20));
          const divider = document.createElement('div');
          divider.className = 'ha-light-mode-divider';
          const brightBtn = document.createElement('button');
          brightBtn.type = 'button';
          brightBtn.className = 'ha-light-mode-btn';
          brightBtn.setAttribute('aria-label', 'Helligkeit');
          const brightIcon = svgIcon(ICON_BRIGHTNESS, '#fff', 20);
          brightBtn.appendChild(brightIcon);
          const colorBtn = document.createElement('button');
          colorBtn.type = 'button';
          colorBtn.className = 'ha-light-mode-btn';
          colorBtn.setAttribute('aria-label', 'Farbe');
          const colorSwatch = document.createElement('span');
          colorSwatch.className = 'ha-light-mode-swatch ha-light-mode-swatch--color';
          colorBtn.appendChild(colorSwatch);
          const tempBtn = document.createElement('button');
          tempBtn.type = 'button';
          tempBtn.className = 'ha-light-mode-btn';
          tempBtn.setAttribute('aria-label', 'Farbtemperatur');
          const tempSwatch = document.createElement('span');
          tempSwatch.className = 'ha-light-mode-swatch ha-light-mode-swatch--temp';
          tempBtn.appendChild(tempSwatch);
          modeRow.appendChild(powerBtn);
          modeRow.appendChild(divider);
          modeRow.appendChild(brightBtn);
          modeRow.appendChild(colorBtn);
          modeRow.appendChild(tempBtn);

          // Only Power is functional; Brightness/Color/Color-Temp just move the "selected"
          // black-circle indicator (HA-APP-SPEC §4.5) — no real effect on the entity.
          let selectedMode = 'brightness';
          function paintModes() {
            brightBtn.classList.toggle('selected', selectedMode === 'brightness');
            colorBtn.classList.toggle('selected', selectedMode === 'color');
            tempBtn.classList.toggle('selected', selectedMode === 'temp');
            brightIcon.setAttribute('stroke', selectedMode === 'brightness' ? '#fff' : C.ink);
          }
          paintModes();
          brightBtn.addEventListener('click', () => { selectedMode = 'brightness'; paintModes(); });
          colorBtn.addEventListener('click', () => { selectedMode = 'color'; paintModes(); });
          tempBtn.addEventListener('click', () => { selectedMode = 'temp'; paintModes(); });
          powerBtn.addEventListener('click', () => {
            const en = SM_HA.state[id];
            const willOn = !en.on;
            const patch = { on: willOn };
            if (willOn && en.brightness === 0) patch.brightness = 100;
            setLight(id, patch);
          });

          body.appendChild(readout);
          body.appendChild(caption);
          body.appendChild(glow);
          body.appendChild(sliderTrack);
          body.appendChild(modeRow);

          lightRefs = { id, readout, glowEl: glow, sliderFill, sliderThumb };
          paintLightView();

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

    // --- C4: Heizung more-info (climate.wohnzimmer). Single signature climate entity —
    // always dispatches (unlike lights there's no ordinary-vs-signature split here).
    let climateRefs = null; // { id, readout, caption, dial, dialValue, stepperValue, chipEls, presets }

    function climateLevel(e) {
      return (24 - e.target) / 6;
    }

    function paintClimateView() {
      if (!climateRefs) return;
      const e = SM_HA.state[climateRefs.id];
      climateRefs.readout.textContent = e.current.toFixed(1) + ' °C';
      climateRefs.caption.textContent = 'Soll ' + e.target.toFixed(1) + ' °C';
      const pct = Math.max(0, Math.min(100, ((e.target - 16) / (28 - 16)) * 100));
      climateRefs.dial.style.background = 'conic-gradient(' + SM_HA.C.accent + ' ' + pct + '%, #e5e5e5 0)';
      climateRefs.dialValue.textContent = e.target.toFixed(1) + ' °C';
      climateRefs.stepperValue.textContent = e.target.toFixed(1) + ' °C';
      climateRefs.chipEls.forEach((chip, i) => {
        chip.classList.toggle('active', Math.abs(climateRefs.presets[i] - e.target) < 0.05);
      });
    }

    // Level convention (must match the hero-welten left stage): level = (24-target)/6 — target
    // 24° (warm) → level 0 (active/after shown), target 18° (cold) → level 1 (before). Clamped
    // to [18,24]. Single choke point for the stepper, presets, and the test hook.
    function setClimate(id, target) {
      const e = SM_HA.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();
      const level = climateLevel(e);
      if (opts.actionTarget) {
        opts.actionTarget.dispatchEvent(new CustomEvent('sm-hero-action', { detail: { ex: 'heizung', level, prevLevel } }));
      }
      refresh();
    }

    function openClimateView(id) {
      const e = SM_HA.state[id];
      openView({
        title: e.name,
        build(body) {
          const readout = document.createElement('div');
          readout.className = 'ha-climate-readout';
          const caption = document.createElement('div');
          caption.className = 'ha-climate-caption';

          const dial = document.createElement('div');
          dial.className = 'ha-thermo-dial';
          const dialInner = document.createElement('div');
          dialInner.className = 'ha-thermo-dial-inner';
          const dialValue = document.createElement('div');
          dialValue.className = 'ha-thermo-dial-value';
          dialInner.appendChild(dialValue);
          dial.appendChild(dialInner);

          const stepper = document.createElement('div');
          stepper.className = 'ha-climate-stepper';
          const minus = document.createElement('button');
          minus.type = 'button';
          minus.className = 'ha-climate-stepper-btn';
          minus.setAttribute('aria-label', 'Kälter');
          minus.textContent = '−';
          minus.addEventListener('click', () => setClimate(id, SM_HA.state[id].target - 0.5));
          const stepperValue = document.createElement('div');
          stepperValue.className = 'ha-climate-stepper-value';
          const plus = document.createElement('button');
          plus.type = 'button';
          plus.className = 'ha-climate-stepper-btn';
          plus.setAttribute('aria-label', 'Wärmer');
          plus.textContent = '+';
          plus.addEventListener('click', () => setClimate(id, SM_HA.state[id].target + 0.5));
          stepper.appendChild(minus);
          stepper.appendChild(stepperValue);
          stepper.appendChild(plus);

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

          body.appendChild(readout);
          body.appendChild(caption);
          body.appendChild(dial);
          body.appendChild(stepper);
          body.appendChild(chipsRow);

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

    // Placeholder state text for the generic more-info stub — task D replaces this with
    // the real per-domain views (see TODO at the routing switch below). light/climate got
    // their real views in C3/C4 and no longer reach this path.
    function genericStateText(e) {
      if (!e) return '';
      switch (e.type) {
        case 'heatpump': return 'WP · ' + e.current + ' °C · COP ' + e.cop;
        case 'media': return e.state === 'playing' ? 'Wiedergabe' : 'Aus';
        case 'sensor':
        case 'energy_tile': return e.value + (e.unit ? ' ' + e.unit : '');
        case 'lock': return e.locked ? 'Verriegelt' : 'Entriegelt';
        case 'door': return e.open ? 'Offen' : 'Geschlossen';
        case 'camera': return 'Live';
        default: return '';
      }
    }

    function openGenericView(id) {
      const e = SM_HA.state[id];
      openView({
        title: (e && e.name) || id,
        build(body) {
          body.style.display = 'flex';
          body.style.alignItems = 'center';
          body.style.justifyContent = 'center';
          body.style.minHeight = '60%';
          const readout = document.createElement('div');
          readout.className = 'ha-generic-state';
          readout.textContent = genericStateText(e);
          body.appendChild(readout);
        },
      });
    }

    // ================= Task D: Deep-Views (Statistik, Energie/Fronius, Media, Zutritt, Wärmepumpe, Kamera) =================
    const mk = (tag, css, text) => { const n = document.createElement(tag); if (css) n.style.cssText = css; if (text != null) n.textContent = text; return n; };
    const de = (n) => String(n).replace('.', ',');
    const chartColor = (e) => e.device === 'pv' ? '#ff9800' : e.device === 'temperature' ? '#03a9f4'
      : e.device === 'car' ? '#4db6ac' : e.device === 'power' ? '#4db6ac' : SM_HA.C.teal;
    function bigReadout(body, value, caption) {
      body.appendChild(mk('div', 'font:400 36px/43.2px Inter,system-ui,sans-serif;color:#141414;text-align:center;margin-top:8px;', value));
      if (caption) body.appendChild(mk('div', 'font:400 13px Inter,system-ui,sans-serif;color:#5e5e5e;text-align:center;margin-bottom:8px;', caption));
    }

    // D1 — generic sensor history graph ("Statistik funktioniert überall")
    function openSensorView(id) {
      const e = SM_HA.state[id];
      openView({ title: e.name, build(body) {
        bigReadout(body, de(e.value) + (e.unit ? ' ' + e.unit : ''), 'Verlauf heute');
        const chart = mk('div', 'margin:8px 0;'); body.appendChild(chart);
        SM_HA_CHART.draw(chart, SM_HA.history(id), { type: 'area', color: chartColor(e), height: 150 });
      } });
    }

    // D2 — Fronius energy dashboard: flow diagram (animated) + live values + stats
    function energyFlowSVG(en) {
      const kw = (w) => de((Math.abs(w) / 1000).toFixed(1)) + ' kW';
      const node = (x, y, col, label, val) =>
        `<circle cx="${x}" cy="${y}" r="26" fill="#fff" stroke="${col}" stroke-width="3"/>` +
        `<text x="${x}" y="${y - 3}" text-anchor="middle" font-size="10" font-weight="600" fill="#141414">${label}</text>` +
        `<text x="${x}" y="${y + 12}" text-anchor="middle" font-size="8" fill="#5e5e5e">${val}</text>`;
      const dot = (ax, ay, bx, by, col) =>
        `<circle r="3.5" fill="${col}"><animate attributeName="cx" from="${ax}" to="${bx}" dur="1.7s" repeatCount="indefinite"/><animate attributeName="cy" from="${ay}" to="${by}" dur="1.7s" repeatCount="indefinite"/></circle>`;
      const HX = 150, HY = 150;
      const gridExport = en.grid < 0; // negativ = Einspeisung
      const battCharge = en.battery.charge > 0;
      return `<svg viewBox="0 0 300 210" width="100%" style="max-height:210px">
        <line x1="150" y1="50" x2="${HX}" y2="${HY}" stroke="#edf0f2" stroke-width="2"/>
        <line x1="250" y1="118" x2="${HX}" y2="${HY}" stroke="#edf0f2" stroke-width="2"/>
        <line x1="50" y1="118" x2="${HX}" y2="${HY}" stroke="#edf0f2" stroke-width="2"/>
        ${dot(150, 50, HX, HY, '#ff9800')}
        ${gridExport ? dot(HX, HY, 250, 118, '#488fc2') : dot(250, 118, HX, HY, '#488fc2')}
        ${battCharge ? dot(HX, HY, 50, 118, '#4db6ac') : dot(50, 118, HX, HY, '#4db6ac')}
        ${node(150, 50, '#ff9800', 'PV', kw(en.pv))}
        ${node(250, 118, '#488fc2', 'Netz', kw(en.grid))}
        ${node(50, 118, '#4db6ac', 'Speicher', en.battery.soc + ' %')}
        ${node(HX, HY, '#607d8b', 'Haus', kw(en.house + en.heatpump + en.wallbox))}
        <text x="150" y="196" text-anchor="middle" font-size="8" fill="#9e9e9e">inkl. Wärmepumpe &amp; Wallbox</text>
      </svg>`;
    }
    function openEnergyView() {
      const en = SM_HA.energy;
      openView({ title: 'Energie', build(body) {
        const kw = (w) => de((Math.abs(w) / 1000).toFixed(2)) + ' kW';
        const grid = mk('div', 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:4px 0 12px;');
        [['Erzeugung', kw(en.pv), '#ff9800'], ['Verbrauch', kw(en.house + en.heatpump + en.wallbox), '#607d8b'],
         ['Netz', (en.grid < 0 ? 'Einspeisung ' : 'Bezug ') + kw(en.grid), '#488fc2'], ['Speicher', en.battery.soc + ' %', '#4db6ac']]
          .forEach(([lab, val, col]) => { const c = mk('div', 'background:#f6f8f9;border-radius:10px;padding:10px;');
            c.appendChild(mk('div', 'font:600 11px Inter;letter-spacing:.04em;text-transform:uppercase;color:#5e5e5e;', lab));
            c.appendChild(mk('div', 'font:600 17px Inter;color:' + col + ';margin-top:2px;', val)); grid.appendChild(c); });
        body.appendChild(grid);
        const flow = mk('div', 'margin:2px 0 14px;'); flow.innerHTML = energyFlowSVG(en); body.appendChild(flow);
        body.appendChild(mk('div', 'font:600 13px Inter;color:#141414;margin:4px 0;', 'Heute · Erzeugung vs. Verbrauch'));
        const chart = mk('div', ''); body.appendChild(chart);
        const prod = SM_HA.history('sensor.pv');
        const cons = prod.map((p, i) => ({ t: i, v: 0.42 + 0.22 * Math.sin(i / 4 + 1.2) }));
        SM_HA_CHART.draw(chart, [prod, cons], { type: 'area', color: '#ff9800', color2: '#488fc2', height: 140 });
      } });
    }

    // D3 — Wallbox "Last charge" → Ladesession-Graph
    function openWallboxView(id) {
      const e = SM_HA.state[id];
      openView({ title: 'Wallbox', build(body) {
        bigReadout(body, de(e.value) + ' kWh', 'Letzte Ladung');
        const badge = mk('div', 'text-align:center;margin-bottom:10px;');
        badge.appendChild(mk('span', 'display:inline-block;background:#fff3e0;color:#e65100;font:600 12px Inter;padding:5px 12px;border-radius:100px;', '⚡ PV-Überschuss-Laden'));
        body.appendChild(badge);
        const chart = mk('div', ''); body.appendChild(chart);
        const h = SM_HA.history(id).map((p) => ({ t: p.t, v: p.v * 11 }));
        SM_HA_CHART.draw(chart, h, { type: 'area', color: '#4db6ac', height: 150 });
        body.appendChild(mk('div', 'font:400 12px Inter;color:#5e5e5e;text-align:center;margin-top:6px;', 'Ladeleistung (kW) über die Session'));
      } });
    }

    // D4 — Media-Player (Nest)
    function openMediaView(id) {
      const e = SM_HA.state[id];
      const render = (body) => {
        body.innerHTML = '';
        body.appendChild(mk('div', 'width:150px;height:150px;margin:12px auto;border-radius:16px;background:linear-gradient(135deg,#03a9f4,#7e57c2);box-shadow:0 8px 24px rgba(0,0,0,.2);'));
        body.appendChild(mk('div', 'font:600 18px Inter;color:#141414;text-align:center;', e.track.title));
        body.appendChild(mk('div', 'font:400 14px Inter;color:#5e5e5e;text-align:center;margin-bottom:12px;', e.track.artist));
        const prog = mk('div', 'height:4px;background:#e5e5e5;border-radius:2px;margin:0 8px 16px;overflow:hidden;');
        prog.appendChild(mk('div', 'height:100%;width:38%;background:#03a9f4;')); body.appendChild(prog);
        const ctr = mk('div', 'display:flex;align-items:center;justify-content:center;gap:26px;margin-bottom:18px;');
        const btn = (label, big, fn) => { const b = mk('button', 'border:0;background:transparent;cursor:pointer;font-size:' + (big ? '30px' : '22px') + ';color:#141414;', label); b.addEventListener('click', fn); return b; };
        ctr.appendChild(btn('⏮', false, () => {}));
        ctr.appendChild(btn(e.state === 'playing' ? '⏸' : '▶', true, () => { e.state = e.state === 'playing' ? 'paused' : 'playing'; render(body); refresh(); }));
        ctr.appendChild(btn('⏭', false, () => {}));
        body.appendChild(ctr);
        const vol = mk('div', 'display:flex;align-items:center;gap:8px;padding:0 12px;');
        vol.appendChild(mk('span', 'font-size:15px;', '🔉'));
        const track = mk('div', 'flex:1;height:6px;background:#e5e5e5;border-radius:3px;position:relative;cursor:pointer;');
        const fill = mk('div', 'height:100%;background:#03a9f4;border-radius:3px;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); vol.appendChild(track); body.appendChild(vol);
      };
      openView({ title: e.name, build: render });
    }

    // D5 — Zutritt: Türschloss + Tür/Fenster-Status + Ereignisse
    function openLockView(id) {
      const e = SM_HA.state[id];
      const render = (body) => {
        body.innerHTML = '';
        const big = mk('button', 'display:block;width:120px;height:120px;margin:16px auto 8px;border-radius:50%;border:0;cursor:pointer;font-size:44px;' + (e.locked ? 'background:#e8f5e9;color:#2e7d32;' : 'background:#fff3e0;color:#e65100;'), e.locked ? '🔒' : '🔓');
        big.addEventListener('click', () => { e.locked = !e.locked; render(body); refresh(); });
        body.appendChild(big);
        body.appendChild(mk('div', 'font:600 18px Inter;color:#141414;text-align:center;margin-bottom:16px;', e.locked ? 'Verriegelt' : 'Entriegelt'));
        body.appendChild(mk('div', 'font:600 13px Inter;color:#141414;margin:8px 4px;', 'Tür & Fenster'));
        Object.values(SM_HA.state).filter((x) => x.type === 'door').forEach((x) => {
          const r = mk('div', 'display:flex;justify-content:space-between;padding:10px 4px;border-bottom:1px solid #eee;');
          r.appendChild(mk('span', 'font:400 14px Inter;color:#141414;', x.name));
          r.appendChild(mk('span', 'font:500 13px Inter;color:' + (x.open ? '#e65100' : '#2e7d32') + ';', x.open ? 'Offen' : 'Geschlossen')); body.appendChild(r);
        });
        body.appendChild(mk('div', 'font:600 13px Inter;color:#141414;margin:14px 4px 6px;', 'Ereignisse'));
        ['08:12  Haustür entriegelt', '07:40  Terrassentür geschlossen', 'Gestern 22:30  „Gute Nacht" — alles verriegelt'].forEach((t) => body.appendChild(mk('div', 'font:400 13px Inter;color:#5e5e5e;padding:4px;', t)));
      };
      openView({ title: e.name, build: render });
    }

    // D6 — Wärmepumpe + Heizstab (volle Geräte-Karte, AT-relevant)
    function openHeatpumpView(id) {
      const e = SM_HA.state[id];
      const render = (body) => {
        body.innerHTML = '';
        bigReadout(body, e.current + ' °C', 'Vorlauf · Soll ' + e.target + ' °C');
        const grid = mk('div', 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:8px 0;');
        [['Modus', e.mode === 'heat' ? 'Heizen' : 'Warmwasser'], ['Arbeitszahl', 'COP ' + de(e.cop)],
         ['Quelle', e.source + ' °C'], ['Puffer', '48 °C']].forEach(([lab, val]) => {
          const c = mk('div', 'background:#f6f8f9;border-radius:10px;padding:10px;');
          c.appendChild(mk('div', 'font:600 11px Inter;letter-spacing:.04em;text-transform:uppercase;color:#5e5e5e;', lab));
          c.appendChild(mk('div', 'font:600 17px Inter;color:#141414;margin-top:2px;', val)); grid.appendChild(c);
        });
        body.appendChild(grid);
        const hs = mk('div', 'display:flex;align-items:center;justify-content:space-between;border-radius:12px;padding:12px;margin:8px 0;background:' + (e.heizstab ? '#fff3e0' : '#f6f8f9') + ';');
        const left = mk('div', '');
        left.appendChild(mk('div', 'font:600 14px Inter;color:#141414;', 'Heizstab-Backup · Technische Alternative'));
        left.appendChild(mk('div', 'font:400 12px Inter;color:#5e5e5e;', e.heizstab ? 'Aktiv · ' + e.heizstabKw + ' kW' : 'Aus'));
        hs.appendChild(left);
        const sw = mk('button', 'width:48px;height:28px;border-radius:14px;border:0;cursor:pointer;position:relative;background:' + (e.heizstab ? '#ff9800' : '#c2c2c2') + ';');
        sw.appendChild(mk('span', 'position:absolute;top:3px;left:' + (e.heizstab ? '23px' : '3px') + ';width:22px;height:22px;border-radius:50%;background:#fff;'));
        sw.addEventListener('click', () => { e.heizstab = !e.heizstab; e.heizstabKw = e.heizstab ? 3 : 0; SM_HA.energy.heatpump = 900 + (e.heizstab ? 3000 : 0); render(body); refresh(); });
        hs.appendChild(sw); body.appendChild(hs);
        body.appendChild(mk('div', 'font:600 13px Inter;color:#141414;margin:8px 4px 4px;', 'Arbeitszahl heute'));
        const chart = mk('div', ''); body.appendChild(chart);
        SM_HA_CHART.draw(chart, SM_HA.history('climate.waermepumpe'), { type: 'area', color: '#ff9800', height: 130 });
      };
      openView({ title: e.name, build: render });
    }

    // D7 — Kamera Live-Feed (Glasfront-Villa aus dem Erklärungsvideo = dasselbe Haus)
    function openCameraView(id) {
      const e = SM_HA.state[id];
      openView({ title: e.name, build(body) {
        body.style.padding = '8px';
        const frame = mk('div', 'position:relative;width:100%;border-radius:12px;overflow:hidden;background:#000;');
        const vid = document.createElement('video');
        vid.src = e.feed; if (e.poster) vid.poster = e.poster;
        vid.muted = true; vid.loop = true; vid.autoplay = true; vid.setAttribute('playsinline', '');
        vid.style.cssText = 'width:100%;display:block;';
        frame.appendChild(vid);
        const live = mk('div', 'position:absolute;top:10px;left:10px;display:flex;align-items:center;gap:6px;background:rgba(0,0,0,.5);color:#fff;font:600 11px Inter;padding:4px 10px;border-radius:100px;');
        live.appendChild(mk('span', 'width:8px;height:8px;border-radius:50%;background:#f44336;'));
        live.appendChild(mk('span', null, 'LIVE'));
        frame.appendChild(live);
        frame.appendChild(mk('div', 'position:absolute;bottom:10px;right:10px;background:rgba(0,0,0,.5);color:#fff;font:500 11px Inter;padding:3px 8px;border-radius:6px;', e.name));
        body.appendChild(frame);
        vid.play().catch(() => {});
      } });
    }

    const openDetail = (entityId) => {
      const e = SM_HA.state[entityId];
      if (!e) return;
      if (e.deep === 'energy') return openEnergyView();
      if (e.deep === 'wallbox') return openWallboxView(entityId);
      if (e.deep === 'camera' || e.type === 'camera') return openCameraView(entityId);
      if (e.deep === 'heatpump' || e.type === 'heatpump') return openHeatpumpView(entityId);
      if (e.deep === 'media' || e.type === 'media') return openMediaView(entityId);
      switch (e.type) {
        case 'cover': return openCoverView(entityId);
        case 'light': return openLightView(entityId);
        case 'climate': return openClimateView(entityId);
        case 'lock': return openLockView(entityId);
        case 'sensor': case 'energy_tile': return openSensorView(entityId);
        default: return openGenericView(entityId);
      }
    };

    // Signature-Dispatch fuer Home-Tile-Schnellbedienung (Bar lamp/Spotlights/Heizung) —
    // gleiche sm-hero-action wie die More-Info-Views, damit auch Interaktionen direkt auf
    // der Kachel die linke "Mit/Ohne"-Buehne animieren. level-Konvention wie in C2-C4.
    const dispatchSignature = (id) => {
      const e = SM_HA.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() {
      // Home nicht neu aufbauen, solange eine More-Info offen ist (sie verdeckt die Kacheln) —
      // sonst reisst ein Slider-Drag (setCover ~60x/s) das ganze Home-DOM ab. Dirty markieren,
      // beim Schliessen der View einmal rebuilden.
      if (viewEl.classList.contains('open')) { homeDirty = true; return; }
      const scrollTop = container.scrollTop;
      const oldSpacer = container.querySelector('.ha-app__spacer');
      if (oldSpacer) oldSpacer.parentNode.removeChild(oldSpacer);
      sectionsWrap.innerHTML = '';
      sectionsWrap.appendChild(buildSections({ state: SM_HA.state, ICON: SM_HA.ICON, C: SM_HA.C, openDetail, refresh, dispatchSignature }));
      ensureScrollable(container);
      container.scrollTop = scrollTop;
    }

    container.appendChild(buildHeader());
    container.appendChild(buildChips());
    container.appendChild(sectionsWrap);
    host.appendChild(container);
    // .ha-view mounts on `host` (the phone-screen), not inside the internally-
    // scrolling `.ha-app` — otherwise its inset:0 box is positioned in .ha-app's
    // unscrolled content coordinates and ends up offset once the home list is
    // scrolled. host is position:relative/overflow:hidden and never scrolls, so
    // .ha-view's inset:0 always pins to the visible phone screen.
    host.appendChild(viewEl);

    // ---- F1: Hamburger-Sidebar (Demo / Karte=Geofencing / Energie) ----
    function openMapView() {
      openView({ title: 'Karte', build(body) {
        body.style.padding = '10px';
        const map = mk('div', "position:relative;width:100%;aspect-ratio:4/3;border-radius:12px;overflow:hidden;border:1px solid rgba(0,0,0,.08);background:#e9ecef center/cover no-repeat url('assets/map-stadt.png');");
        // Geofence um Zuhause
        map.appendChild(mk('div', 'position:absolute;left:40%;top:56%;width:38%;padding-bottom:38%;transform:translate(-50%,-50%);border-radius:50%;border:2px dashed #03a9f4;background:rgba(3,169,244,.12);'));
        // Zuhause-Marker (in der Geofence)
        const home = mk('div', 'position:absolute;left:40%;top:56%;transform:translate(-50%,-100%);text-align:center;');
        home.innerHTML = "<div style='width:26px;height:26px;margin:0 auto;border-radius:50% 50% 50% 0;transform:rotate(-45deg);background:#4caf50;border:2px solid #fff;box-shadow:0 3px 8px rgba(0,0,0,.35);'></div>";
        home.appendChild(mk('div', 'margin-top:3px;display:inline-block;background:#fff;border-radius:6px;padding:2px 8px;font:600 11px Inter;color:#141414;box-shadow:0 2px 6px rgba(0,0,0,.25);white-space:nowrap;', 'Zuhause'));
        map.appendChild(home);
        // Anna-Marker (unterwegs, ausserhalb der Geofence)
        const anna = mk('div', 'position:absolute;left:74%;top:24%;transform:translate(-50%,-100%);text-align:center;');
        anna.innerHTML = "<div style='width:22px;height:22px;margin:0 auto;border-radius:50% 50% 50% 0;transform:rotate(-45deg);background:#ff9800;border:2px solid #fff;box-shadow:0 3px 8px rgba(0,0,0,.35);'></div>";
        anna.appendChild(mk('div', 'margin-top:3px;display:inline-block;background:#fff;border-radius:6px;padding:2px 8px;font:600 11px Inter;color:#141414;box-shadow:0 2px 6px rgba(0,0,0,.25);white-space:nowrap;', 'Anna'));
        map.appendChild(anna);
        body.appendChild(map);
        body.appendChild(mk('div', 'font:600 13px Inter;color:#141414;margin:12px 4px 6px;', 'Anwesenheit'));
        [['Thomas Basting', 'Zuhause', '#2e7d32'], ['Anna', 'Unterwegs · 2,4 km', '#e65100'], ['Auto', 'Abwesend', '#9e9e9e']].forEach((r) => {
          const row = mk('div', 'display:flex;justify-content:space-between;align-items:center;padding:10px 4px;border-bottom:1px solid #eee;');
          row.appendChild(mk('span', 'font:400 14px Inter;color:#141414;', r[0]));
          row.appendChild(mk('span', 'font:500 13px Inter;color:' + r[2] + ';', r[1]));
          body.appendChild(row);
        });
      } });
    }
    const scrim = mk('div', 'position:absolute;inset:0;background:rgba(0,0,0,.35);opacity:0;transition:opacity .25s;pointer-events:none;z-index:7;');
    const sidebar = mk('div', 'position:absolute;top:0;left:0;bottom:0;width:80%;max-width:230px;background:#fff;transform:translateX(-100%);transition:transform .28s ease;z-index:8;box-shadow:2px 0 16px rgba(0,0,0,.15);padding:10px 0;overflow-y:auto;');
    const openSidebar = () => { scrim.style.opacity = '1'; scrim.style.pointerEvents = 'auto'; sidebar.style.transform = 'translateX(0)'; };
    const closeSidebar = () => { scrim.style.opacity = '0'; scrim.style.pointerEvents = 'none'; sidebar.style.transform = 'translateX(-100%)'; };
    scrim.addEventListener('click', closeSidebar);
    sidebar.appendChild(mk('div', 'font:600 17px Inter;color:#141414;padding:10px 16px 16px;', 'Home Assistant'));
    const HOME_ICON = 'M3 11l9-7 9 7 M5 10v9h14v-9 M10 19v-5h4v5';
    const MAP_ICON = 'M9 4 4 6v15l5-2 6 2 5-2V4l-5 2z M9 4v15 M15 6v15';
    [
      { label: 'Demo', icon: HOME_ICON, active: true, fn: () => { closeSidebar(); closeView(); container.scrollTop = 0; } },
      { label: 'Karte', icon: MAP_ICON, fn: () => { closeSidebar(); openMapView(); } },
      { label: 'Energie', icon: SM_HA.ICON.energy, fn: () => { closeSidebar(); openEnergyView(); } },
    ].forEach((it) => {
      const row = document.createElement('button');
      row.type = 'button';
      row.style.cssText = 'display:flex;align-items:center;gap:12px;width:calc(100% - 8px);margin:2px 4px;padding:0 12px;height:40px;border:0;border-radius:8px;cursor:pointer;font:' + (it.active ? '600' : '400') + ' 14px Inter,system-ui,sans-serif;text-align:left;background:' + (it.active ? '#e0f3f8' : 'transparent') + ';color:' + (it.active ? '#009ac7' : '#141414') + ';';
      row.appendChild(svgIcon(it.icon, it.active ? '#009ac7' : '#7a7a7a', 20));
      row.appendChild(mk('span', null, it.label));
      row.addEventListener('click', it.fn);
      sidebar.appendChild(row);
    });
    host.appendChild(scrim);
    host.appendChild(sidebar);
    const hamburgerBtn = container.querySelector('.ha-app__hamburger');
    if (hamburgerBtn) hamburgerBtn.addEventListener('click', openSidebar);

    refresh();

    const instance = {
      actionTarget: opts.actionTarget,
      world: opts.world,
      show() { container.style.display = ''; viewEl.style.display = ''; },
      hide() { container.style.display = 'none'; closeView(); viewEl.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);
        delete window.__haOpen; delete window.__haCloseView; delete window.__haSet; delete window.__haApp;
      },
    };

    window.__haOpen = openDetail;
    window.__haCloseView = closeView;
    window.__haSet = (entityId, level) => {
      const e = SM_HA.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);
      // TODO(task D): weitere Domain-Set-Hooks ergänzen, sobald deren More-Infos existieren.
    };
    window.__haApp = instance;
    return instance;
  }

  return { mount };
})();
