diff --git a/docs/design/505-summary-panel/README.md b/docs/design/505-summary-panel/README.md
new file mode 100644
index 00000000..b50d1cfe
--- /dev/null
+++ b/docs/design/505-summary-panel/README.md
@@ -0,0 +1,15 @@
+# #505 designer reference
+
+Issue: https://github.com/Matysh/houseplan-card/issues/505
+Specification: [505-summary-panel-design-parity.md](../../specs/505-summary-panel-design-parity.md).
+
+Original files supplied by the owner on 2026-09-09 in `макет.zip` are preserved
+under [reference](reference/index.html). ZIP SHA-256:
+`bc754c16c9d9fc79dbbf093fcf8e14b96f8826fa01d1f0c8eccf68ab95357e15`.
+
+Open index.html and use the rightmost split control (gear, then sidebar toggle).
+The demo uses local state; its JS is design reference, not production or process
+instructions. No backend dependencies are needed. Demo dates may vary.
+Do not bundle these files into the product. The numbered spec documents the
+allowed adaptation for HA themes, accessibility and production data ownership.
+
diff --git a/docs/design/505-summary-panel/reference/DEVELOPER-HANDOFF.md b/docs/design/505-summary-panel/reference/DEVELOPER-HANDOFF.md
new file mode 100644
index 00000000..7300488a
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/DEVELOPER-HANDOFF.md
@@ -0,0 +1,143 @@
+# Dashboard 5 — передача в разработку
+
+Дата пакета: 8 сентября 2026 года.
+Основание: [houseplan-card issue #437](https://github.com/Matysh/houseplan-card/issues/437) и согласованные уточнения встречи от 8 сентября 2026 года.
+
+## Назначение
+
+Dashboard 5 — интерактивный HTML-прототип конфигурируемой информационной панели рядом с планом House Plan. Пакет предназначен как визуальная и поведенческая основа для реализации внутри существующей Lovelace-карточки.
+
+## Зафиксированное поведение
+
+- Информационная панель плавает поверх плана и не участвует в геометрии, `content bbox` или автоматическом `fit`.
+- В горизонтальной ориентации панель размещается справа.
+- В портретной ориентации панель размещается снизу, центрируется и подстраивает ширину под содержимое с ограничением шириной экрана.
+- Панель имеет собственный пользовательский заголовок.
+- В шапке House Plan используется составной контрол: слева настройка, справа показ или скрытие панели.
+- Правая кнопка использует `svg/layout-sidebar-right.svg`.
+- Отдельного состояния сворачивания всей панели и сворачивания блоков нет.
+- Значения в строках выровнены по правому краю.
+- Панель и её содержимое ограничены по высоте; при переполнении прокручиваются данные, а не план.
+- В режиме киоска панель располагается ниже плавающего блока управления.
+
+## Структура данных
+
+```text
+информационная панель
+└── блок
+ └── значение: пользовательское название — источник/состояние
+```
+
+Предлагаемая модель:
+
+```js
+{
+ title: "Сводная информация",
+ showOnMobile: true,
+ blocks: [
+ {
+ id: "stable-block-id",
+ title: "Общее",
+ visible: true,
+ scope: { type: "all" },
+ values: [
+ {
+ id: "stable-value-id",
+ label: "Количество устройств",
+ source: { type: "system", key: "device_count" }
+ }
+ ]
+ }
+ ]
+}
+```
+
+Для блока, привязанного к пространству:
+
+```js
+scope: { type: "space", spaceId: "stable-space-id" }
+```
+
+Для сущности Home Assistant:
+
+```js
+source: { type: "entity", entityId: "sensor.example" }
+```
+
+## Начальная конфигурация
+
+- Название панели: «Сводная информация».
+- Один включённый блок «Общее» для всех пространств.
+- Системные строки в порядке:
+ 1. «Количество устройств»;
+ 2. «Общая площадь комнат»;
+ 3. «Текущие дата и время».
+
+## Диалог настроек
+
+Диалог работает с единым черновиком. «Сохранить» применяет все изменения атомарно. «Отмена», крестик и Escape отбрасывают весь черновик.
+
+Поддерживаются:
+
+- название панели;
+- общий показ панели;
+- показ в нативном узком режиме Home Assistant;
+- создание, переименование, включение, выключение и удаление блоков;
+- выбор области блока: все пространства или одно определённое;
+- создание, переименование, сортировка и удаление строк;
+- сортировка блоков и строк перетаскиванием и квадратными кнопками «выше/ниже»;
+- выбор сущности по имени и `entity_id`;
+- состояния загрузки, устаревания и недоступности;
+- проверка обязательных полей и блокировка сохранения при ошибках;
+- подтверждение удаления непустого блока.
+
+Ограничения структуры: не более 10 блоков, не более 20 значений в блоке, заголовок панели или блока до 48 символов, название значения до 64 символов.
+
+## Интеграция с Home Assistant
+
+Прототип содержит демонстрационный список сущностей. В продукте необходимо:
+
+1. Получать полный доступный пользователю список из `hass.states`.
+2. Использовать стабильный `entity_id` и форматирование состояния средствами Home Assistant.
+3. Показывать системные показатели в том же селекторе источников, что и сущности.
+4. Автоматически включать в список новые Template-сущности.
+5. Сохранять ранее выбранную недоступную сущность и отмечать её как требующую внимания.
+6. Получать мобильный/узкий режим из нативного контекста Home Assistant, а не из собственного числового breakpoint.
+7. Интегрировать конфигурацию с постоянным хранилищем House Plan, совместной работой и экспортом/импортом, не добавляя данные панели в геометрию плана.
+8. Следовать действующей модели прав House Plan для доступа к структурным настройкам.
+
+## Файлы прототипа
+
+- `index.html` — экран House Plan, панель и диалоги.
+- `dashboard-5.js` — состояние, черновик настроек, фильтрация по пространству и адаптивная позиция.
+- `dashboard-5.css` — компоновка, темы, киоск и анимации.
+- `styles.css`, `components.css`, `components.html` — базовая демонстрационная оболочка.
+- `svg/` — локальные иконки интерфейса.
+
+## Проверки перед реализацией
+
+- Обычный и нативный узкий режим Home Assistant.
+- Альбомная и портретная ориентации телефона и планшета.
+- Панель справа и снизу, короткий и максимальный состав.
+- Светлая и тёмная темы, 200% zoom и safe area.
+- Клавиатурная навигация, Escape, фокус после сортировки.
+- Pan/pinch/scroll плана при открытой панели.
+- Переход между пространствами без `fit/refit` и скачка масштаба.
+- Загрузка, отсутствие данных, `unknown`, `unavailable`, восстановленная и удалённая сущность.
+- Удалённое пространство, сохранённая проблемная привязка и смена активного пространства.
+- Перезагрузка страницы, совместное редактирование, импорт и экспорт конфигурации.
+
+## Запуск прототипа
+
+Из папки `Dashboard 5`:
+
+```text
+python -m http.server 8768
+```
+
+Открыть `http://127.0.0.1:8768/`.
+
+## Граница ответственности прототипа
+
+HTML-прототип демонстрирует интерфейс и взаимодействия. Статические системные числа, демонстрационный массив сущностей и `localStorage` не являются готовой продуктовой реализацией. Источники данных, постоянное хранение, права, i18n и интеграционные тесты реализуются в кодовой базе House Plan.
+
diff --git a/docs/design/505-summary-panel/reference/IMPLEMENTATION-NOTES.md b/docs/design/505-summary-panel/reference/IMPLEMENTATION-NOTES.md
new file mode 100644
index 00000000..049bdfb8
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/IMPLEMENTATION-NOTES.md
@@ -0,0 +1,32 @@
+# Dashboard 5 — заметки по реализации
+
+Версия реализует изменения из документа «ТЗ Dashboard 4 — финальные изменения по итогам встречи 08.09.2026.md» как отдельный прототип и не изменяет Dashboard 4.
+
+## Реализовано
+
+- автоматический выбор позиции по ориентации viewport через `matchMedia("(orientation: portrait)")`;
+- `ResizeObserver` и обработчик `resize` для реакции на изменение размеров и поворот устройства;
+- плавающее размещение поверх плана;
+- компактная ширина по содержимому в правом и нижнем положении с ограничением доступным экраном;
+- направленная анимация показа и скрытия;
+- составной контрол из двух доступных кнопок;
+- единое состояние показа в шапке и диалоге;
+- собственный заголовок информационной панели;
+- отсутствие элементов и состояния сворачивания блоков;
+- внутренний скролл при переполнении;
+- безопасное расположение под управлением киоска;
+- отключение переходов при `prefers-reduced-motion`.
+
+## Прототипные ограничения
+
+- сущности заданы демонстрационным массивом;
+- сохранение основано на `localStorage`;
+- реальные контексты и форматирование Home Assistant требуют интеграции;
+- touch/pan/pinch, safe area и 200% zoom требуют проверки в реальной карточке.
+
+## Последние визуальные уточнения
+
+- кнопка показа использует `svg/layout-sidebar-right.svg`;
+- значения строк выровнены по правому краю блока;
+- в портретной ориентации панель находится снизу, центрируется и не растягивается на всю ширину без необходимости.
+
diff --git a/docs/design/505-summary-panel/reference/ISSUE-HANDOFF.md b/docs/design/505-summary-panel/reference/ISSUE-HANDOFF.md
new file mode 100644
index 00000000..b2a1fe74
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/ISSUE-HANDOFF.md
@@ -0,0 +1,22 @@
+# Текст для публикации в issue #437
+
+Подготовлен архив **Dashboard-5-Issue-437-handoff-2026-09-08.zip** с актуальной версией HTML-прототипа информационной панели.
+
+В Dashboard 5 зафиксированы последние согласованные решения:
+
+- плавающая панель поверх плана без изменения масштаба и геометрии;
+- автоматическое расположение справа в горизонтальной и снизу в портретной ориентации;
+- компактный размер по содержимому с ограничением доступным экраном;
+- составной контрол «настройки + показать/скрыть» в шапке House Plan;
+- собственный пользовательский заголовок панели;
+- единый диалог настройки и атомарные «Сохранить/Отмена»;
+- блоки всегда развёрнуты;
+- сортировка блоков и строк, включая доступные кнопки «выше/ниже»;
+- области отображения блоков по пространствам;
+- начальный блок «Общее» с тремя системными значениями;
+- состояния сущностей, темы, киоск и локальные размеры экрана.
+
+В архив включены исходники прототипа, локальные SVG, инструкция запуска и документ `DEVELOPER-HANDOFF.md` с моделью данных, интеграционными требованиями и перечнем проверок для продуктовой реализации.
+
+Прототип использует демонстрационные сущности и `localStorage`. В продукте список должен формироваться из `hass.states`, а конфигурация — сохраняться штатными средствами House Plan.
+
diff --git a/docs/design/505-summary-panel/reference/README.md b/docs/design/505-summary-panel/reference/README.md
new file mode 100644
index 00000000..f10b03c1
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/README.md
@@ -0,0 +1,46 @@
+# Dashboard 5
+
+Интерактивный HTML-прототип финальной компоновки информационной панели House Plan по итогам встречи 8 сентября 2026 года и требованиям [issue #437](https://github.com/Matysh/houseplan-card/issues/437).
+
+## Запуск
+
+Откройте папку через локальный HTTP-сервер:
+
+```text
+python -m http.server 8768
+```
+
+После запуска страница доступна по адресу `http://127.0.0.1:8768/`.
+
+## Главное отличие версии 5
+
+- панель плавает поверх плана и не изменяет его рабочую область;
+- в горизонтальной ориентации экрана располагается справа;
+- в вертикальной ориентации экрана автоматически перемещается вниз;
+- положение пересчитывается при изменении размера окна или повороте устройства;
+- габариты панели зависят от содержимого и ограничиваются доступным экраном;
+- в нижнем положении панель центрируется и сохраняет компактную ширину по содержимому;
+- собственный заголовок возвращён в панель;
+- в шапке House Plan находится компактный составной контрол без текста;
+- левая половина контрола открывает общий диалог настройки;
+- правая половина показывает и скрывает панель;
+- переключатель видимости в диалоге управляет тем же состоянием;
+- отдельное состояние сворачивания панели отсутствует;
+- блоки не сворачиваются ни в просмотре, ни в настройках;
+- появление и скрытие анимируется в направлении текущего края;
+- в режиме киоска учитываются плавающие элементы управления.
+
+## Основные файлы
+
+- `index.html` — интерфейс House Plan и разметка панели/диалогов;
+- `dashboard-5.js` — модель, адаптивная позиция, настройки и взаимодействия;
+- `dashboard-5.css` — компоновка, анимации, темы и киоск;
+- `styles.css`, `components.css` — базовое оформление плана;
+- `svg/` — локальные иконки.
+
+## Ограничение прототипа
+
+Список сущностей остаётся демонстрационным, а конфигурация сохраняется в `localStorage`. В продуктовой реализации источником сущностей должен быть `hass.states`, а хранение необходимо интегрировать со штатной конфигурацией House Plan.
+
+Полный пакет передачи разработчикам описан в `DEVELOPER-HANDOFF.md`. Текст, который можно использовать при прикреплении архива к issue, находится в `ISSUE-HANDOFF.md`.
+
diff --git a/docs/design/505-summary-panel/reference/components.css b/docs/design/505-summary-panel/reference/components.css
new file mode 100644
index 00000000..e90eccef
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/components.css
@@ -0,0 +1,425 @@
+:root {
+ --ha-blue: #03a4cf;
+ --ha-blue-dark: #0497bd;
+ --ha-text: #202124;
+ --ha-muted: #686b6e;
+ --ha-border: #dedede;
+ --ha-soft: #f1f1f1;
+ --ha-danger: #ff725d;
+}
+
+* { box-sizing: border-box; }
+
+html, body { margin: 0; min-width: 1120px; min-height: 720px; font-family: Arial, Helvetica, sans-serif; color: var(--ha-text); }
+button, input { font: inherit; }
+button { cursor: default; }
+
+.components-page { min-height: 100vh; overflow: auto; background: #dceaf0; }
+
+.ha-component-header { position: relative; z-index: 5; background: #fff; }
+.ha-view-tabs {
+ display: flex;
+ align-items: flex-end;
+ height: 82px;
+ padding-left: 21px;
+ gap: 25px;
+ border-bottom: 1px solid var(--ha-border);
+}
+.ha-view-tab {
+ position: relative;
+ display: grid;
+ place-items: center;
+ width: 56px;
+ height: 62px;
+ color: #48494a;
+ font-size: 29px;
+ font-weight: 700;
+}
+.ha-view-tab.active::after { position: absolute; right: 0; bottom: 0; left: 0; height: 3px; content: ""; background: #222; }
+
+.ha-context-row {
+ display: flex;
+ align-items: center;
+ height: 86px;
+ padding: 0 20px;
+ gap: 14px;
+ border-bottom: 1px solid var(--ha-border);
+ white-space: nowrap;
+}
+.ha-plan-label { display: flex; align-items: center; gap: 8px; font-size: 21px; }
+.ha-home-icon { color: var(--ha-blue); font-size: 28px; }
+.ha-segmented { display: flex; align-items: center; height: 57px; padding: 5px; border-radius: 17px; background: var(--ha-soft); }
+.ha-segment { display: flex; align-items: center; height: 47px; padding: 0 21px; gap: 8px; border-radius: 13px; color: #555; font-size: 18px; font-weight: 700; }
+.ha-segment small { color: #bbb; font-size: 15px; }
+.ha-segment.active { color: #fff; background: var(--ha-blue); }
+.ha-segment.active small { color: #8ed5e8; }
+.ha-segment b { margin-left: 9px; font-size: 23px; font-weight: 300; }
+.ha-add { padding: 0 13px; color: #666; font-size: 24px; }
+.ha-editor-switcher { margin-left: 0; }
+.ha-editor-switcher .ha-segment { padding: 0 18px; }
+.ha-device-count { color: #555; font-size: 17px; }
+
+.ha-tools-row {
+ display: flex;
+ align-items: center;
+ min-height: 85px;
+ padding: 14px 22px;
+ gap: 15px;
+ border-bottom: 1px solid var(--ha-border);
+ background: #fff;
+ white-space: nowrap;
+}
+.ha-tool-mode { color: #ffb52e; font-size: 34px; }
+.ha-tool {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ height: 47px;
+ padding: 0 18px;
+ gap: 11px;
+ border: 1px solid #d7d7d7;
+ border-radius: 14px;
+ color: #111;
+ background: #fff;
+ font-size: 19px;
+}
+.ha-tool.active { border-color: var(--ha-blue); color: #fff; background: var(--ha-blue); }
+.ha-tool-icon { font-size: 22px; font-weight: 700; }
+.ha-chevron { margin-left: 3px; }
+.ha-history-actions { display: flex; margin-left: auto; gap: 21px; }
+.ha-history-actions button { border: 0; color: #929292; background: transparent; font-size: 27px; }
+
+.ha-component-stage { position: relative; min-height: 1090px; overflow: hidden; background: #dbeaf0; }
+.ha-stage-grid {
+ position: absolute;
+ inset: 0;
+ opacity: .6;
+ background-image: radial-gradient(#4da2bb 1px, transparent 1.5px);
+ background-size: 10px 10px;
+}
+.ha-plan-fragment { position: absolute; inset: 100px 10% 0 15%; color: #383d40; font-weight: 700; opacity: .45; }
+.ha-plan-room { position: absolute; display: grid; place-items: center; border: 12px solid #596266; background: #a0a9ae; }
+.room-a { left: 0; top: 0; width: 70%; height: 340px; }
+.room-b { left: 28%; top: 328px; width: 31%; height: 390px; }
+.room-c { left: 58%; top: 328px; width: 28%; height: 390px; }
+.ha-modal-backdrop { position: absolute; inset: 0; background: rgba(40, 40, 40, .48); }
+
+.ha-device-modal {
+ position: absolute;
+ top: 9px;
+ left: 50%;
+ display: flex;
+ flex-direction: column;
+ width: 870px;
+ height: 1080px;
+ transform: translateX(-50%);
+ overflow: hidden;
+ border-radius: 21px;
+ background: #fff;
+ box-shadow: 0 7px 24px rgba(0,0,0,.18);
+}
+.ha-modal-header { display: flex; align-items: center; flex: 0 0 119px; padding: 0 37px; gap: 18px; }
+.ha-modal-header h1 { margin: 0; font-size: 31px; line-height: 1; }
+.ha-modal-close { display: grid; place-items: center; width: 34px; height: 34px; font-size: 40px; font-weight: 200; line-height: 1; }
+.ha-device-shapes { position: relative; display: block; width: 31px; height: 34px; }
+.ha-device-shapes i { position: absolute; display: block; background: var(--ha-blue); }
+.ha-device-shapes i:nth-child(1) { left: 0; top: 4px; width: 13px; height: 13px; }
+.ha-device-shapes i:nth-child(2) { right: 0; top: 4px; width: 13px; height: 13px; border-radius: 50%; }
+.ha-device-shapes i:nth-child(3) { left: 1px; bottom: 1px; width: 0; height: 0; border-right: 7px solid transparent; border-bottom: 0; border-left: 7px solid transparent; border-top: 13px solid var(--ha-blue); background: transparent; transform: rotate(180deg); }
+.ha-device-shapes i:nth-child(4) { right: 1px; bottom: 4px; width: 13px; height: 3px; }
+.ha-device-shapes i:nth-child(4)::after { position: absolute; left: 5px; top: -5px; width: 3px; height: 13px; content: ""; background: var(--ha-blue); }
+
+.ha-modal-body { flex: 1 1 auto; min-height: 0; padding: 8px 46px 32px 37px; overflow-y: scroll; scrollbar-color: #b7b7b7 transparent; scrollbar-width: auto; }
+.ha-modal-body::-webkit-scrollbar { width: 14px; }
+.ha-modal-body::-webkit-scrollbar-track { background: transparent; }
+.ha-modal-body::-webkit-scrollbar-thumb { border: 3px solid #fff; border-radius: 9px; background: #b7b7b7; }
+.ha-field { margin-bottom: 20px; }
+.ha-field label, .ha-field-label { display: block; margin-bottom: 12px; color: var(--ha-muted); font-size: 18px; }
+.ha-field input[type="text"], .ha-light-sources input[type="text"] {
+ width: 100%;
+ height: 45px;
+ padding: 0 14px;
+ border: 1px solid #d6d6d6;
+ border-radius: 10px;
+ color: #242424;
+ background: #fff;
+ outline: none;
+ font-size: 19px;
+}
+.ha-binding-card { padding: 20px 13px 12px; border: 1px solid #d7d7d7; border-radius: 12px; }
+.ha-choice { display: flex !important; align-items: center; margin: 0 0 21px !important; gap: 13px; color: var(--ha-muted); font-size: 18px; }
+.ha-choice input { width: 19px; height: 19px; margin: 0; accent-color: #078cff; }
+.ha-choice-row { display: flex; align-items: center; justify-content: flex-start; gap: 29px; }
+.ha-switch-label { display: flex; align-items: center; gap: 12px; color: #727272; font-size: 18px; }
+.ha-switch { position: relative; display: inline-block; width: 71px; height: 37px; border: 1px solid #a9a9a9; border-radius: 20px; background: #f7f7f7; }
+.ha-switch i { position: absolute; left: 4px; top: 3px; width: 29px; height: 29px; border-radius: 50%; background: #737373; }
+.ha-select-box { display: flex; align-items: center; width: 100%; height: 47px; padding: 0 14px; border: 1px solid #d6d6d6; border-radius: 10px; color: #222; background: #fff; font-size: 19px; }
+.strong-select { height: 54px; border-color: #555; }
+.ha-entity { margin-left: auto; color: #696969; font-weight: 400; }
+.ha-select-arrow { margin-left: 18px; font-size: 23px; font-weight: 700; }
+.action-field { margin-bottom: 30px; }
+.action-field p { margin: 9px 0 0; color: var(--ha-muted); font-size: 18px; }
+.confirmation { margin: 0 0 25px; }
+.ha-light-sources h2 { margin: 0 0 18px; color: var(--ha-muted); font-size: 18px; font-weight: 400; }
+.ha-light-sources p { margin: 0 0 8px; color: var(--ha-muted); font-size: 17px; line-height: 1.45; }
+.ha-light-sources input { margin-top: 14px; }
+.ha-light-sources input::placeholder { color: #777; opacity: 1; }
+.ha-source-type { margin: 41px 0 0; padding: 26px 18px 13px; border: 1px solid #d8d8d8; border-radius: 12px; }
+.ha-source-type legend { padding: 0 8px 0 0; font-size: 20px; font-weight: 700; }
+.ha-help { display: inline-grid; place-items: center; width: 23px; height: 23px; margin-left: 13px; border: 2px solid #707070; border-radius: 50%; color: #707070; font-size: 15px; }
+.ha-source-type .ha-choice { margin-bottom: 19px !important; }
+.ha-modal-spacer { height: 130px; }
+
+.ha-modal-footer { display: flex; align-items: center; justify-content: space-between; flex: 0 0 107px; padding: 0 47px; border-top: 1px solid #e0e0e0; background: #fff; }
+.ha-footer-left, .ha-footer-right { display: flex; align-items: center; gap: 13px; }
+.ha-action-button, .ha-text-button, .ha-save-button { display: inline-flex; align-items: center; justify-content: center; height: 48px; border-radius: 11px; background: #fff; font-size: 18px; }
+.ha-action-button { padding: 0 17px; gap: 10px; border: 1px solid #d5d5d5; }
+.ha-action-button.danger { border-color: var(--ha-danger); color: var(--ha-danger); }
+.ha-text-button { padding: 0 22px; border: 0; }
+.ha-save-button { padding: 0 22px; gap: 12px; border: 1px solid var(--ha-blue); color: #fff; background: var(--ha-blue); font-weight: 700; }
+
+@media (max-width: 1500px) {
+ .ha-context-row { gap: 8px; padding-inline: 12px; }
+ .ha-segment { padding-inline: 13px; font-size: 15px; }
+ .ha-plan-label { font-size: 18px; }
+ .ha-tools-row { gap: 8px; padding-inline: 12px; }
+ .ha-tool { padding-inline: 11px; font-size: 15px; }
+}
+
+@media (max-height: 950px) {
+ .ha-device-modal { height: calc(100vh - 18px); }
+ .ha-component-stage { min-height: calc(100vh - 253px); }
+}
+
+/* Reusable home information panel */
+.ha-home-panel {
+ position: relative;
+ z-index: 4;
+ display: flex;
+ flex: 0 0 390px;
+ width: 390px;
+ min-width: 0;
+ height: 100%;
+ flex-direction: column;
+ border-left: 1px solid #d9dcde;
+ background: #f5f7f8;
+ box-shadow: -5px 0 20px rgba(46, 58, 64, .1);
+ transition: flex-basis .22s ease, width .22s ease;
+}
+.ha-home-panel.is-collapsed { flex-basis: 48px; width: 48px; }
+.ha-home-panel__header {
+ position: relative;
+ display: flex;
+ align-items: center;
+ flex: 0 0 62px;
+ min-height: 62px;
+ padding: 0 13px 0 9px;
+ gap: 10px;
+ border-bottom: 1px solid #dfe2e4;
+ background: #fff;
+}
+.ha-icon-button {
+ display: inline-grid;
+ place-items: center;
+ width: 34px;
+ height: 34px;
+ flex: 0 0 34px;
+ padding: 0;
+ border: 0;
+ border-radius: 9px;
+ color: #555b5e;
+ background: transparent;
+ font-size: 18px;
+}
+.ha-icon-button:hover { background: #edf1f2; }
+.ha-home-panel__size-toggle { border: 1px solid #dce0e2; font-size: 27px; line-height: 1; }
+.ha-home-panel__size-toggle .when-collapsed { display: none; }
+.ha-home-panel.is-collapsed .ha-home-panel__size-toggle .when-expanded { display: none; }
+.ha-home-panel.is-collapsed .ha-home-panel__size-toggle .when-collapsed { display: inline; }
+.ha-home-panel__title-wrap { display: flex; align-items: center; min-width: 0; gap: 9px; }
+.ha-home-panel__mark { color: var(--ha-blue); font-size: 21px; }
+.ha-home-panel__title-wrap h2 { margin: 0; font-size: 16px; line-height: 1.2; }
+.ha-home-panel__title-wrap small { display: block; margin-top: 2px; color: #818588; font-size: 10px; }
+.ha-panel-settings-toggle { margin-left: auto; }
+.ha-panel-settings-toggle.is-active { color: #fff; background: var(--ha-blue); }
+.ha-home-panel__rail-title { display: none; position: absolute; top: 56px; left: 16px; color: #72777a; font-size: 11px; font-weight: 700; letter-spacing: .04em; writing-mode: vertical-rl; }
+.ha-home-panel.is-collapsed .ha-home-panel__header { flex: 1 1 auto; align-items: flex-start; padding: 10px 7px; }
+.ha-home-panel.is-collapsed .ha-home-panel__title-wrap,
+.ha-home-panel.is-collapsed .ha-panel-settings-toggle,
+.ha-home-panel.is-collapsed .ha-home-panel__content { display: none; }
+.ha-home-panel.is-collapsed .ha-home-panel__rail-title { display: block; }
+
+.ha-home-panel__content { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; padding: 10px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #b8bec1 transparent; }
+.ha-summary-card,
+.ha-info-block,
+.ha-panel-manager { margin-bottom: 9px; border: 1px solid #dde1e3; border-radius: 11px; background: #fff; box-shadow: 0 1px 2px rgba(34, 45, 50, .04); }
+.ha-summary-card { padding: 13px; }
+.ha-panel-manager { order: 0; }
+.ha-summary-card { order: 1; }
+.ha-panel-blocks { order: 2; }
+.ha-summary-card__heading,
+.ha-panel-manager__heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
+.ha-summary-actions { display: flex; align-items: center; gap: 5px; }
+.ha-eyebrow { display: block; margin-bottom: 3px; color: #878b8e; font-size: 9px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
+.ha-summary-card h3,
+.ha-panel-manager h3 { margin: 0; font-size: 15px; }
+.ha-status-badge,
+.room-state { display: inline-flex; align-items: center; min-height: 21px; padding: 0 7px; border-radius: 10px; color: #5e6467; background: #edf0f1; font-size: 9px; font-weight: 700; white-space: nowrap; }
+.ha-status-badge.good,
+.room-state.good { color: #168848; background: #e1f5e8; }
+.room-state.warning { color: #9b6500; background: #fff0ca; }
+.ha-temperature-grid { display: grid; grid-template-columns: repeat(2, 1fr); margin-top: 12px; gap: 7px; }
+.ha-temperature-metric { display: flex; align-items: center; min-width: 0; padding: 9px; gap: 9px; border-radius: 9px; background: linear-gradient(135deg, #eef8fb, #f6fafb); }
+.ha-temperature-metric.outside { background: linear-gradient(135deg, #fff6dc, #fffaf0); }
+.ha-temperature-metric .metric-icon { display: grid; place-items: center; width: 29px; height: 29px; flex: 0 0 29px; border-radius: 9px; color: #fff; background: #3995ae; font-size: 13px; }
+.ha-temperature-metric.outside .metric-icon { background: #edae2e; }
+.ha-temperature-metric span:last-child { min-width: 0; }
+.ha-temperature-metric small { display: block; margin-bottom: 2px; color: #777d80; font-size: 8px; }
+.ha-temperature-metric strong { display: block; font-size: 14px; white-space: nowrap; }
+.ha-summary-grid { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 7px; gap: 6px; }
+.ha-summary-metric { display: flex; align-items: center; min-width: 0; flex-direction: column; padding: 8px 3px; border-radius: 8px; background: #f4f6f7; text-align: center; }
+.ha-summary-metric .metric-icon { display: grid; place-items: center; width: 21px; height: 21px; margin-bottom: 4px; border-radius: 50%; color: #fff; background: #768085; font-size: 10px; }
+.ha-summary-metric strong { font-size: 13px; }
+.ha-summary-metric small { margin-top: 2px; overflow: hidden; max-width: 100%; color: #85898c; font-size: 8px; text-overflow: ellipsis; white-space: nowrap; }
+.ha-summary-note { display: flex; align-items: center; margin-top: 10px; padding-top: 9px; gap: 7px; border-top: 1px solid #eceeef; color: #6b7073; font-size: 10px; }
+.ha-warning-dot { width: 8px; height: 8px; flex: 0 0 8px; border-radius: 50%; background: #ffae00; box-shadow: 0 0 0 3px #fff1c9; }
+
+.ha-panel-blocks { min-height: 40px; }
+.ha-info-block { overflow: clip; transition: opacity .15s ease, border-color .15s ease; }
+.ha-info-block.is-dragging { border-color: var(--ha-blue); opacity: .55; }
+.ha-info-block__header { display: flex; align-items: center; min-height: 43px; padding: 0 8px; gap: 7px; }
+.ha-drag-handle,
+.ha-manager-drag { color: #a0a5a8; cursor: grab; font-size: 17px; line-height: 1; user-select: none; }
+.ha-drag-handle { visibility: hidden; width: 0; overflow: hidden; opacity: 0; transition: width .15s ease, opacity .15s ease; }
+.ha-home-panel.is-editing .ha-drag-handle { visibility: visible; width: auto; overflow: visible; opacity: 1; }
+.ha-home-panel.is-editing .ha-info-block { border-style: dashed; }
+.ha-home-panel.is-editing .ha-info-block__header { background: #fbfdfe; }
+.ha-block-settings-button { display: none; place-items: center; width: 27px; height: 27px; flex: 0 0 27px; padding: 0; border: 1px solid #cfe4ea; border-radius: 7px; color: #087f9d; background: #eef9fb; font-size: 12px; }
+.ha-home-panel.is-editing .ha-block-settings-button { display: grid; }
+.ha-block-settings-button:hover { color: #fff; background: var(--ha-blue); }
+.ha-drag-handle:active,
+.ha-manager-drag:active { cursor: grabbing; }
+.ha-block-icon { display: grid; place-items: center; width: 24px; height: 24px; flex: 0 0 24px; border-radius: 7px; color: #31798e; background: #e5f3f7; font-size: 11px; font-weight: 800; }
+.ha-info-block__toggle { display: grid; grid-template-columns: 1fr auto 16px; align-items: center; width: 100%; height: 42px; padding: 0; gap: 7px; border: 0; color: #292d2f; background: transparent; text-align: left; }
+.ha-info-block__toggle > span { font-size: 13px; font-weight: 700; }
+.ha-info-block__toggle small { color: #8a8e90; font-size: 9px; font-weight: 400; }
+.ha-info-block__toggle b { color: #74797c; font-size: 12px; text-align: center; transition: transform .18s ease; }
+.ha-info-block.is-collapsed .ha-info-block__toggle b { transform: rotate(180deg); }
+.ha-info-block.is-collapsed .ha-info-block__body { display: none; }
+.ha-info-block__body { padding: 0 11px 11px 38px; border-top: 1px solid #eef0f1; }
+.ha-status-list,
+.ha-compact-list,
+.ha-timeline { margin: 0; padding: 0; list-style: none; }
+.ha-status-list li { display: flex; align-items: center; justify-content: space-between; min-height: 42px; padding: 7px 0; gap: 8px; border-bottom: 1px solid #eef0f1; }
+.ha-status-list li:last-child { border-bottom: 0; }
+.ha-status-list li > span:first-child { min-width: 0; }
+.ha-status-list strong { display: block; overflow: hidden; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
+.ha-status-list small { display: block; margin-top: 3px; overflow: hidden; color: #858a8c; font-size: 9px; text-overflow: ellipsis; white-space: nowrap; }
+.ha-block-link { display: flex; align-items: center; justify-content: space-between; width: 100%; margin-top: 6px; padding: 7px 0 0; border: 0; color: #078eb1; background: transparent; font-size: 10px; font-weight: 700; }
+.ha-inline-stats { display: grid; grid-template-columns: repeat(3, 1fr); padding: 10px 0; gap: 5px; }
+.ha-inline-stats span { display: flex; align-items: center; flex-direction: column; padding: 7px 3px; border-radius: 7px; background: #f5f7f7; }
+.ha-inline-stats strong { font-size: 13px; }
+.ha-inline-stats small { margin-top: 2px; color: #858a8c; font-size: 8px; }
+.ha-compact-list li { display: flex; align-items: center; justify-content: space-between; min-height: 31px; gap: 8px; border-top: 1px solid #eef0f1; font-size: 10px; }
+.ha-compact-list li span { color: #73787a; font-size: 9px; white-space: nowrap; }
+.good-text { color: #168848 !important; font-weight: 700; }
+.ha-timeline { padding-top: 7px; }
+.ha-timeline li { display: flex; min-height: 43px; gap: 9px; }
+.ha-timeline strong { display: block; font-size: 10px; }
+.ha-timeline small { display: block; margin-top: 3px; color: #7d8284; font-size: 9px; line-height: 1.3; }
+.event-dot { width: 9px; height: 9px; flex: 0 0 9px; margin-top: 3px; border-radius: 50%; background: #92989b; }
+.event-dot.warning { background: #ffae00; }
+.event-dot.good { background: #38c86e; }
+
+.ha-panel-manager { padding: 12px; border-color: #b9dce5; background: #fafdfe; }
+.ha-panel-manager__heading .ha-icon-button { width: 27px; height: 27px; flex-basis: 27px; }
+.ha-panel-manager > p { margin: 8px 0 10px; color: #74797c; font-size: 9px; line-height: 1.4; }
+.ha-panel-manager__list { display: flex; flex-direction: column; gap: 5px; }
+.ha-panel-manager__item { display: grid; grid-template-columns: 20px 1fr auto; align-items: center; min-height: 36px; padding: 0 6px; border: 1px solid #dfe4e6; border-radius: 8px; background: #fff; }
+.ha-panel-manager__item.is-dragging { opacity: .5; }
+.ha-panel-manager__item.is-pinned { border-style: dashed; background: #f4f8f9; }
+.ha-panel-manager__item.is-pinned > small { color: #8a9092; font-size: 8px; }
+.ha-manager-pin { color: var(--ha-blue); font-size: 8px; text-align: center; }
+.ha-panel-manager__item label { display: flex; align-items: center; gap: 7px; font-size: 10px; font-weight: 700; }
+.ha-panel-manager__item input { width: 15px; height: 15px; margin: 0; accent-color: var(--ha-blue); }
+.ha-manager-order { display: flex; gap: 3px; }
+.ha-manager-order button { display: grid; place-items: center; width: 25px; height: 25px; padding: 0; border: 1px solid #dce0e2; border-radius: 6px; color: #5f6568; background: #fff; }
+.ha-manager-order button:disabled { opacity: .3; }
+.ha-reset-layout { width: 100%; height: 30px; margin-top: 9px; border: 1px solid #d4dade; border-radius: 7px; color: #555d60; background: #fff; font-size: 9px; font-weight: 700; }
+
+.ha-device-categories { margin-top: 3px; }
+.ha-device-category { border-top: 1px solid #e9edef; }
+.ha-device-category__toggle { display: grid; grid-template-columns: 1fr auto 17px; align-items: center; width: 100%; min-height: 37px; padding: 0; gap: 7px; border: 0; color: #303537; background: transparent; text-align: left; }
+.ha-device-category__toggle span { font-size: 10px; font-weight: 700; }
+.ha-device-category__toggle small { color: #858a8c; font-size: 8px; }
+.ha-device-category__toggle b { color: #747a7d; font-size: 12px; text-align: center; transition: transform .16s ease; }
+.ha-device-category.is-open .ha-device-category__toggle b { transform: rotate(180deg); }
+.ha-device-category__list { margin: 0; padding: 0 0 5px; list-style: none; }
+.ha-device-category__list li { display: flex; align-items: center; justify-content: space-between; min-height: 43px; padding: 6px 0 6px 8px; gap: 8px; border-top: 1px solid #eef0f1; }
+.ha-device-category__list li > span { min-width: 0; }
+.ha-device-category__list strong { display: block; overflow: hidden; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
+.ha-device-category__list small { display: block; margin-top: 3px; overflow: hidden; color: #858a8c; font-size: 8px; text-overflow: ellipsis; white-space: nowrap; }
+.ha-device-category__list li.is-device-hidden { opacity: .48; }
+.ha-device-settings-button { display: inline-flex; align-items: center; min-height: 27px; padding: 0 7px; gap: 4px; border: 1px solid #d9dfe1; border-radius: 7px; color: #4e575a; background: #fff; font-size: 9px; white-space: nowrap; }
+.ha-device-settings-button:hover { border-color: #8ecddd; color: #087f9d; background: #f1fafc; }
+
+.ha-device-editor { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; background: #fff; }
+.ha-device-editor[hidden] { display: none; }
+.ha-home-panel.is-collapsed .ha-device-editor { display: none; }
+.ha-home-panel.is-device-editor-open .ha-home-panel__content { display: none; }
+.ha-device-editor__header { display: flex; align-items: center; min-height: 63px; padding: 0 14px; gap: 10px; border-bottom: 1px solid #e2e5e7; }
+.ha-device-editor__header h3 { margin: 0; overflow: hidden; max-width: 270px; font-size: 15px; text-overflow: ellipsis; white-space: nowrap; }
+.ha-device-editor__body { flex: 1 1 auto; min-height: 0; padding: 16px; overflow-y: auto; }
+.ha-device-editor__body .ha-field { margin-bottom: 16px; }
+.ha-device-editor__body .ha-field label { margin-bottom: 7px; font-size: 11px; }
+.ha-device-editor__body input[type="text"],
+.ha-device-editor__body select { width: 100%; height: 39px; padding: 0 10px; border: 1px solid #d4dadd; border-radius: 8px; color: #2c3133; background: #fff; font-size: 12px; outline: none; }
+.ha-device-editor__body input:focus,
+.ha-device-editor__body select:focus { border-color: var(--ha-blue); box-shadow: 0 0 0 2px rgba(3,164,207,.12); }
+.ha-device-editor__body input[readonly] { color: #777e81; background: #f5f7f8; }
+.ha-device-enabled { display: flex; align-items: center; margin-top: 22px; gap: 9px; color: #52595c; font-size: 11px; }
+.ha-device-enabled input { width: 17px; height: 17px; margin: 0; accent-color: var(--ha-blue); }
+.ha-device-editor__footer { display: flex; justify-content: flex-end; min-height: 61px; padding: 12px 14px; gap: 8px; border-top: 1px solid #e2e5e7; }
+.ha-device-editor__footer button { height: 35px; padding: 0 13px; border-radius: 8px; font-size: 11px; font-weight: 700; }
+.ha-device-editor__cancel { border: 1px solid #d6dcde; color: #555d60; background: #fff; }
+.ha-device-editor__save { border: 1px solid var(--ha-blue); color: #fff; background: var(--ha-blue); }
+
+.ha-block-editor { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; background: #fff; }
+.ha-block-editor[hidden] { display: none; }
+.ha-home-panel.is-collapsed .ha-block-editor { display: none; }
+.ha-home-panel.is-block-editor-open .ha-home-panel__content { display: none; }
+.ha-block-editor__header { display: flex; align-items: center; min-height: 63px; padding: 0 14px; gap: 10px; border-bottom: 1px solid #e2e5e7; }
+.ha-block-editor__header h3 { margin: 0; overflow: hidden; max-width: 270px; font-size: 15px; text-overflow: ellipsis; white-space: nowrap; }
+.ha-block-editor__body { flex: 1 1 auto; min-height: 0; padding: 15px; overflow-y: auto; }
+.ha-block-editor__body > p { margin: 0 0 13px; color: #747a7d; font-size: 10px; line-height: 1.45; }
+.ha-block-editor__title-field { margin-bottom: 17px; padding-bottom: 15px; border-bottom: 1px solid #e6e9ea; }
+.ha-block-editor__title-field label { display: block; margin-bottom: 7px; color: #5e6568; font-size: 10px; font-weight: 700; }
+.ha-block-editor__title-field input { width: 100%; height: 38px; padding: 0 10px; border: 1px solid #d5dbdd; border-radius: 8px; color: #303638; background: #fff; font-size: 12px; outline: none; }
+.ha-block-editor__title-field input:focus { border-color: var(--ha-blue); box-shadow: 0 0 0 2px rgba(3,164,207,.12); }
+.ha-block-editor__list { display: flex; flex-direction: column; gap: 7px; }
+.ha-block-editor__item { display: grid; grid-template-columns: 1fr auto; align-items: center; min-height: 43px; padding: 0 8px 0 10px; gap: 8px; border: 1px solid #dfe4e6; border-radius: 9px; background: #fafcfc; }
+.ha-block-editor__item label { display: flex; align-items: center; min-width: 0; gap: 8px; color: #34393b; font-size: 10px; font-weight: 700; }
+.ha-block-editor__item label span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.ha-block-editor__item input { width: 16px; height: 16px; margin: 0; accent-color: var(--ha-blue); }
+.ha-block-editor__item button { height: 27px; padding: 0 8px; border: 1px solid #ffb6a9; border-radius: 7px; color: #d75643; background: #fff; font-size: 8px; font-weight: 700; }
+.ha-block-editor__empty { margin: 0; padding: 18px 10px; border: 1px dashed #ced5d8; border-radius: 9px; color: #858b8e; background: #fafcfc; font-size: 10px; text-align: center; }
+.ha-block-editor__add { margin-top: 20px; padding-top: 14px; border-top: 1px solid #e6e9ea; }
+.ha-block-editor__add > label { display: block; margin-bottom: 7px; color: #5e6568; font-size: 10px; font-weight: 700; }
+.ha-block-editor__add > div { display: flex; gap: 7px; }
+.ha-block-editor__add select { min-width: 0; height: 35px; flex: 1 1 auto; padding: 0 8px; border: 1px solid #d5dbdd; border-radius: 8px; color: #3e4447; background: #fff; font-size: 9px; }
+.ha-block-editor__add-button { height: 35px; padding: 0 10px; border: 1px solid var(--ha-blue); border-radius: 8px; color: #fff; background: var(--ha-blue); font-size: 9px; font-weight: 700; white-space: nowrap; }
+.ha-block-editor__add-button:disabled { border-color: #d6dcde; color: #969b9d; background: #eef1f2; }
+.ha-block-editor__footer { display: flex; justify-content: space-between; min-height: 61px; padding: 12px 14px; gap: 8px; border-top: 1px solid #e2e5e7; }
+.ha-block-editor__footer button { height: 35px; padding: 0 13px; border-radius: 8px; font-size: 10px; font-weight: 700; }
+.ha-block-editor__reset { border: 1px solid #d6dcde; color: #596063; background: #fff; }
+.ha-block-editor__done { border: 1px solid var(--ha-blue); color: #fff; background: var(--ha-blue); }
+
+@media (max-width: 1450px) {
+ .ha-home-panel { flex-basis: 350px; width: 350px; }
+ .ha-summary-grid { grid-template-columns: repeat(3, 1fr); }
+}
+
+@media (max-width: 1220px) {
+ .ha-home-panel { flex-basis: 320px; width: 320px; }
+}
+
diff --git a/docs/design/505-summary-panel/reference/components.html b/docs/design/505-summary-panel/reference/components.html
new file mode 100644
index 00000000..ce2427ac
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/components.html
@@ -0,0 +1,135 @@
+
+
+
+
+
+ Home Assistant — Компоненты панели
+
+
+
+
+
+
+
+
+
+
Kitchen & Living
+
Hallway
+
Guest Bedroom
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Привязка к устройству HA
+
+
+
+
+
+
+
+ Wall Lamp 3
+ entity:light.wall_lamp_3
+ ⌄
+
+
+
+
+
+
+
Ground Floor · Kitchen & Living⌄
+
+
+
+
+
Переключить состояние⌄
+
Цель: Wall Lamp 3 (light.wall_lamp_3).
+
Сейчас: выключено → включится.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/design/505-summary-panel/reference/dashboard-5.css b/docs/design/505-summary-panel/reference/dashboard-5.css
new file mode 100644
index 00000000..7688c28b
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/dashboard-5.css
@@ -0,0 +1,767 @@
+.dashboard3-panel[hidden],
+#legacy-home-panel[hidden],
+.dashboard3-settings-dialog[hidden],
+.dashboard3-entity-picker[hidden] { display: none !important; }
+
+/* Dashboard 3 header — compact Home Assistant chrome from the supplied reference. */
+.dashboard3-app-header {
+ padding: 0 14px 0 10px;
+}
+
+.dashboard3-view-tabs,
+.dashboard3-header-actions,
+.dashboard3-context-actions,
+.dashboard3-zoom-group {
+ display: flex;
+ align-items: center;
+}
+
+.dashboard3-view-tabs {
+ align-self: stretch;
+ gap: 14px;
+}
+
+.dashboard3-header-actions {
+ gap: 18px;
+}
+
+.dashboard3-header-icon {
+ position: relative;
+ display: grid;
+ width: 26px;
+ height: 100%;
+ padding: 0;
+ place-items: center;
+ border: 0;
+ color: #242729;
+ background: transparent;
+ cursor: pointer;
+}
+
+.dashboard3-header-icon svg,
+.dashboard3-context-icon svg,
+.dashboard3-zoom-group svg {
+ display: block;
+ width: 17px;
+ height: 17px;
+ fill: none;
+ stroke: currentColor;
+ stroke-width: 1.9;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+}
+
+.dashboard3-view-tab.active::after {
+ position: absolute;
+ right: -3px;
+ bottom: -1px;
+ left: -3px;
+ height: 2px;
+ content: "";
+ background: #242729;
+}
+
+.dashboard3-header-icon:hover,
+.dashboard3-context-icon:hover,
+.dashboard3-zoom-group button:hover {
+ color: #059bc1;
+ background: #f2f7f8;
+}
+
+.dashboard3-context-bar {
+ overflow: visible;
+ background: #fff;
+}
+
+.dashboard3-context-bar .ha-plan-label {
+ flex: 0 0 auto;
+ color: #17191a;
+}
+
+.ha-plan-symbol {
+ width: 17px;
+ height: 17px;
+ fill: #03a4cf;
+ stroke: #03a4cf;
+ stroke-width: 1.5;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+}
+
+.dashboard3-context-bar .ha-segmented {
+ background: #f0f0f0;
+}
+
+.dashboard3-context-bar .ha-floor-switcher {
+ flex: 0 0 auto;
+}
+
+.dashboard3-context-bar .ha-editor-switcher {
+ flex: 0 1 auto;
+ min-width: 0;
+}
+
+.dashboard3-context-bar .ha-editor-switcher .ha-segment {
+ background: transparent;
+}
+
+.dashboard3-context-actions {
+ flex: 0 0 auto;
+ margin-left: auto;
+ gap: 7px;
+}
+
+.dashboard3-zoom-group {
+ height: 31px;
+ padding: 0 3px;
+ overflow: hidden;
+ border-radius: 9px;
+ background: #f1f1f1;
+}
+
+.dashboard3-zoom-group button,
+.dashboard3-context-icon {
+ display: grid;
+ width: 30px;
+ height: 30px;
+ padding: 0;
+ place-items: center;
+ border: 0;
+ color: #242729;
+ background: transparent;
+ font: inherit;
+ font-size: 17px;
+ font-weight: 600;
+ cursor: pointer;
+}
+
+.dashboard3-context-icon {
+ border: 1px solid #e1e4e5;
+ border-radius: 8px;
+ background: #fff;
+}
+
+.dashboard3-help-icon {
+ border-radius: 9px;
+ font-size: 13px;
+}
+
+.dashboard3-help-icon::before {
+ display: grid;
+ width: 15px;
+ height: 15px;
+ place-items: center;
+ border: 1.5px solid #242729;
+ border-radius: 50%;
+ content: "";
+}
+
+.dashboard3-help-icon {
+ position: relative;
+ color: transparent;
+}
+
+.dashboard3-help-icon::after {
+ position: absolute;
+ color: #242729;
+ content: "?";
+ font-size: 10px;
+ font-weight: 800;
+}
+
+.dashboard3-space-tab {
+ border: 0;
+ background: transparent;
+ font-family: inherit;
+ cursor: pointer;
+}
+.dashboard3-space-tab.active { background: var(--ha-blue); }
+
+.dashboard3-summary-switcher {
+ flex: 0 0 auto;
+ overflow: hidden;
+}
+
+.dashboard3-summary-switcher .dashboard3-global-toggle {
+ width: auto;
+ height: 25px;
+ padding: 0 5px 0 10px;
+ border: 0;
+ border-radius: 7px 0 0 7px;
+ color: #555;
+ background: transparent;
+ font: inherit;
+ font-size: 11px;
+ font-weight: 700;
+ cursor: pointer;
+}
+.dashboard3-settings-toggle {
+ display: grid;
+ width: 25px;
+ height: 25px;
+ padding: 0 6px 0 2px;
+ place-items: center;
+ border: 0;
+ border-radius: 0 7px 7px 0;
+ color: #777;
+ background: transparent;
+ cursor: pointer;
+}
+.dashboard3-settings-toggle img { display: block; width: 13px; height: 13px; opacity: .55; }
+.dashboard3-summary-switcher .dashboard3-global-toggle[aria-pressed="true"],
+.dashboard3-summary-switcher .dashboard3-global-toggle[aria-pressed="true"] + .dashboard3-settings-toggle {
+ color: #fff;
+ background: var(--ha-blue);
+}
+.dashboard3-summary-switcher .dashboard3-global-toggle[aria-pressed="true"] + .dashboard3-settings-toggle img { filter: brightness(0) invert(1); opacity: .72; }
+.dashboard3-summary-switcher .dashboard3-global-toggle:hover,
+.dashboard3-settings-toggle:hover { background: #e5e5e5; }
+.dashboard3-summary-switcher .dashboard3-global-toggle[aria-pressed="true"]:hover,
+.dashboard3-summary-switcher .dashboard3-global-toggle[aria-pressed="true"] + .dashboard3-settings-toggle:hover { background: var(--ha-blue-dark); }
+
+@media (max-width: 1500px) {
+ .dashboard3-header-actions { gap: 14px; }
+ .dashboard3-context-actions { gap: 5px; }
+ .dashboard3-context-bar .ha-editor-switcher .ha-segment { padding-inline: 7px; }
+}
+
+@media (max-width: 1280px) {
+ .dashboard3-context-bar .ha-editor-switcher .ha-segment span { display: none; }
+ .dashboard3-context-bar .ha-editor-switcher .ha-segment { padding-inline: 6px; font-size: 9px; }
+ .dashboard3-context-icon,
+ .dashboard3-zoom-group button { width: 27px; }
+}
+
+.dashboard-body {
+ position: relative;
+}
+
+.dashboard3-panel {
+ position: absolute;
+ z-index: 6;
+ top: 12px;
+ right: 12px;
+ display: block;
+ height: fit-content;
+ min-height: 0;
+ max-height: calc(100vh - var(--global-bar-height) - 68px);
+ border-left: 0;
+ background: transparent;
+ box-shadow: none;
+}
+
+.dashboard3-panel__content {
+ display: flex;
+ width: 100%;
+ min-height: 0;
+ max-height: calc(100vh - var(--global-bar-height) - 68px);
+ flex: 0 1 auto;
+ flex-direction: column;
+ padding: 10px;
+ overflow: hidden;
+ border: 1px solid #d9dcde;
+ border-radius: 12px;
+ background: #f5f7f8;
+ box-shadow: -5px 0 20px rgba(46, 58, 64, .1);
+}
+.dashboard3-space-context {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ min-height: 29px;
+ margin-bottom: 8px;
+ padding: 0 9px;
+ border: 1px solid #dce7ea;
+ border-radius: 8px;
+ color: #7a8285;
+ background: #f8fbfc;
+ font-size: 9px;
+}
+.dashboard3-space-context strong { color: #3f494c; font-size: 9px; }
+.dashboard3-blocks { display: flex; min-height: 0; flex: 0 1 auto; flex-direction: column; gap: 9px; overflow-y: auto; padding-right: 2px; scrollbar-color: #b8bec1 transparent; scrollbar-width: thin; }
+.dashboard3-block {
+ flex: 0 0 auto;
+ overflow: hidden;
+ border: 1px solid #dde3e5;
+ border-radius: 11px;
+ background: #fff;
+ box-shadow: 0 1px 2px rgba(34, 45, 50, .04);
+}
+.dashboard3-block__header { display: flex; align-items: center; min-height: 43px; padding: 0 14px; gap: 8px; border-bottom: 1px solid #eef1f2; }
+.dashboard3-block__mark { display: grid; width: 25px; height: 25px; flex: 0 0 25px; place-items: center; border-radius: 7px; color: #157d98; background: #e7f5f8; font-size: 11px; font-weight: 800; }
+.dashboard3-block__header h3 { margin: 0; overflow: hidden; min-width: 0; flex: 1 1 auto; color: #343b3d; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
+.dashboard3-block__scope { max-width: 120px; overflow: hidden; padding: 4px 7px; border-radius: 10px; color: #677174; background: #f0f3f4; font-size: 8px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
+.dashboard3-block__values { margin: 0; padding: 4px 11px 7px; }
+.dashboard3-value {
+ display: grid;
+ min-height: 36px;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+ align-items: center;
+ gap: 12px;
+ border-bottom: 1px solid #edf0f1;
+}
+.dashboard3-value:last-child { border-bottom: 0; }
+.dashboard3-value dt,
+.dashboard3-value dd { margin: 0; min-width: 0; }
+.dashboard3-value dt { overflow: hidden; color: #697174; font-size: 9px; text-overflow: ellipsis; white-space: nowrap; }
+.dashboard3-value dd { overflow: hidden; color: #30383a; font-size: 10px; font-weight: 800; text-align: right; text-overflow: ellipsis; white-space: nowrap; }
+.dashboard3-empty {
+ display: grid;
+ min-height: 120px;
+ padding: 18px;
+ place-items: center;
+ border: 1px dashed #cfd9dc;
+ border-radius: 11px;
+ color: #899194;
+ background: #fafcfc;
+ font-size: 10px;
+ line-height: 1.45;
+ text-align: center;
+}
+
+.dashboard3-settings-dialog {
+ position: fixed;
+ z-index: 1000;
+ inset: 0;
+ display: grid;
+ padding: 16px;
+ place-items: center;
+ background: rgba(42, 49, 52, .42);
+ backdrop-filter: blur(2px);
+}
+.dashboard3-settings-dialog__window {
+ display: flex;
+ width: min(780px, calc(100vw - 32px));
+ max-height: calc(100vh - 32px);
+ flex-direction: column;
+ overflow: hidden;
+ border: 1px solid #cfdadd;
+ border-radius: 15px;
+ background: #f8fbfc;
+ box-shadow: 0 18px 60px rgba(25, 34, 38, .26);
+}
+.dashboard3-settings-dialog__header {
+ display: flex;
+ min-height: 70px;
+ align-items: center;
+ justify-content: space-between;
+ padding: 15px 17px 12px;
+ gap: 12px;
+ border-bottom: 1px solid #dfe7e9;
+ background: #fff;
+}
+.dashboard3-settings-dialog__header h2 { margin: 0; color: #303638; font-size: 22px; line-height: 1.2; }
+.dashboard3-settings-dialog__close { width: 31px; height: 31px; flex: 0 0 31px; }
+.dashboard3-settings-dialog__body { min-height: 0; padding: 14px 17px 18px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #b8bec1 transparent; }
+.dashboard3-general-settings,
+.dashboard3-block-editor { border: 1px solid #dce5e7; border-radius: 11px; background: #fff; }
+.dashboard3-general-settings { padding: 13px; }
+.dashboard3-general-settings > header h3,
+.dashboard3-block-editor__heading h3 { margin: 0; color: #343b3d; font-size: 13px; }
+.dashboard3-general-settings__grid { display: grid; margin-top: 11px; grid-template-columns: 1fr 1fr; gap: 12px; }
+.dashboard3-field > span,
+.dashboard3-editor-field > span { display: block; margin-bottom: 5px; color: #667073; font-size: 9px; font-weight: 700; }
+.dashboard3-field input,
+.dashboard3-editor-field input,
+.dashboard3-editor-field select,
+.dashboard3-value-label {
+ width: 100%;
+ height: 36px;
+ padding: 0 9px;
+ border: 1px solid #d5dddf;
+ border-radius: 7px;
+ color: #303638;
+ background: #fff;
+ font: inherit;
+ font-size: 10px;
+ outline: none;
+}
+.dashboard3-field input:focus,
+.dashboard3-editor-field input:focus,
+.dashboard3-editor-field select:focus,
+.dashboard3-value-label:focus { border-color: var(--ha-blue); box-shadow: 0 0 0 2px rgba(3, 164, 207, .12); }
+.dashboard3-field input.is-invalid,
+.dashboard3-editor-field input.is-invalid,
+.dashboard3-editor-field select.is-invalid,
+.dashboard3-value-label.is-invalid,
+.dashboard3-entity-trigger.is-invalid { border-color: #dc675a; box-shadow: 0 0 0 2px rgba(220, 103, 90, .10); }
+.dashboard3-field__error { display: block; min-height: 12px; margin-top: 3px; color: #c84f43; font-size: 8px; }
+
+.dashboard3-switch-field {
+ display: flex;
+ min-height: 54px;
+ align-items: center;
+ justify-content: space-between;
+ padding: 4px 0;
+ gap: 12px;
+ border: 0;
+ border-radius: 0;
+ cursor: pointer;
+}
+.dashboard3-switch-field > span:first-child { display: flex; min-width: 0; flex-direction: column; gap: 3px; }
+.dashboard3-switch-field strong { color: #434b4e; font-size: 9px; }
+.dashboard3-switch-field small { color: #7d8588; font-size: 8px; line-height: 1.35; }
+.dashboard3-switch-field input { position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; }
+.dashboard3-switch { position: relative; width: 36px; height: 21px; flex: 0 0 36px; border: 1px solid #b7bec1; border-radius: 12px; background: #d8dddf; transition: .16s ease; }
+.dashboard3-switch::after { position: absolute; top: 2px; left: 2px; width: 15px; height: 15px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .2); content: ""; transition: transform .16s ease; }
+.dashboard3-switch-field input:checked + .dashboard3-switch { border-color: var(--ha-blue); background: var(--ha-blue); }
+.dashboard3-switch-field input:checked + .dashboard3-switch::after { transform: translateX(15px); }
+.dashboard3-switch-field input:focus-visible + .dashboard3-switch { outline: 2px solid rgba(3, 164, 207, .28); outline-offset: 2px; }
+
+.dashboard3-block-editor { margin-top: 12px; padding: 13px; }
+.dashboard3-block-editor__heading { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 10px; gap: 12px; }
+.dashboard3-block-editor__heading p { margin: 3px 0 0; color: #7b8386; font-size: 8px; }
+.dashboard3-block-count { padding: 4px 7px; border-radius: 10px; color: #597176; background: #edf6f8; font-size: 8px; font-weight: 700; }
+.dashboard3-block-editor__list { display: flex; flex-direction: column; gap: 10px; }
+.dashboard3-editor-block { overflow: visible; border: 1px solid #dce3e5; border-radius: 10px; background: #fbfdfd; }
+.dashboard3-editor-block__header { display: grid; min-height: 50px; padding: 7px 8px; grid-template-columns: 61px minmax(120px, 1fr) 160px 31px 31px; align-items: center; gap: 7px; border-bottom: 1px solid #e5eaec; background: #fff; border-radius: 10px 10px 0 0; }
+.dashboard3-order-buttons { display: grid; grid-template-columns: repeat(2, 29px); align-items: center; gap: 3px; }
+.dashboard3-order-button { display: grid; width: 29px; height: 29px; padding: 0; place-items: center; border: 1px solid #d8dcde; border-radius: 6px; color: #586164; background: #fff; font: inherit; font-size: 13px; line-height: 1; cursor: pointer; }
+.dashboard3-order-button:hover:not(:disabled) { border-color: #83c9da; color: #087f9d; background: #f2fafc; }
+.dashboard3-order-button:disabled { opacity: .32; cursor: default; }
+.dashboard3-block-title { width: 100%; height: 34px; min-width: 0; padding: 0 8px; border: 1px solid #d5dddf; border-radius: 7px; color: #343b3d; background: #fff; font: inherit; font-size: 11px; font-weight: 800; outline: none; }
+.dashboard3-block-title:hover { border-color: #dce4e6; background: #fafcfc; }
+.dashboard3-block-title:focus { border-color: var(--ha-blue); background: #fff; box-shadow: 0 0 0 2px rgba(3, 164, 207, .12); }
+.dashboard3-block-title.is-invalid { border-color: #dc675a; background: #fff7f6; }
+.dashboard3-scope-select { width: 100%; height: 34px; min-width: 0; padding: 0 26px 0 8px; border: 1px solid #d5dddf; border-radius: 7px; color: #4e575a; background: #fff; font: inherit; font-size: 9px; outline: none; cursor: pointer; }
+.dashboard3-scope-select:focus { border-color: var(--ha-blue); box-shadow: 0 0 0 2px rgba(3, 164, 207, .12); }
+.dashboard3-scope-select.is-invalid { border-color: #dc675a; box-shadow: 0 0 0 2px rgba(220, 103, 90, .10); }
+.dashboard3-visibility { display: grid; width: 31px; height: 31px; padding: 0; place-items: center; border: 1px solid #acd9e4; border-radius: 7px; background: #f1fafc; cursor: pointer; }
+.dashboard3-visibility.is-off { border-color: #d8dcde; background: #fff; }
+.dashboard3-visibility img { width: 16px; height: 16px; opacity: .72; }
+.dashboard3-editor-block__body { padding: 10px; }
+.dashboard3-editor-values { display: flex; flex-direction: column; gap: 6px; }
+.dashboard3-editor-value { display: grid; min-height: 43px; padding: 4px; grid-template-columns: 61px minmax(135px, .9fr) minmax(190px, 1.25fr) 29px; align-items: start; gap: 6px; border: 1px solid #e2e7e9; border-radius: 8px; background: #fff; }
+.dashboard3-value-label { height: 34px; min-width: 0; }
+.dashboard3-entity-trigger { display: flex; width: 100%; min-width: 0; min-height: 34px; padding: 5px 8px; align-items: center; justify-content: space-between; gap: 8px; border: 1px solid #d5dddf; border-radius: 7px; color: #3b4345; background: #fff; font: inherit; text-align: left; cursor: pointer; }
+.dashboard3-entity-trigger__text { display: flex; min-width: 0; flex-direction: column; gap: 1px; }
+.dashboard3-entity-trigger strong,
+.dashboard3-entity-trigger small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.dashboard3-entity-trigger strong { font-size: 9px; }
+.dashboard3-entity-trigger small { color: #828a8d; font-size: 8px; font-weight: 400; }
+.dashboard3-entity-trigger > b { color: #7c8588; font-size: 10px; }
+.dashboard3-editor-value__remove { width: 29px; height: 29px; margin-top: 2px; padding: 0; border: 0; border-radius: 7px; color: #ca5548; background: #fff3f1; font-size: 15px; cursor: pointer; }
+.dashboard3-editor-value__error { display: none; grid-column: 2 / 4; margin: -2px 2px 2px; color: #c84f43; font-size: 8px; }
+.dashboard3-editor-value.has-error .dashboard3-editor-value__error { display: block; }
+.dashboard3-editor-block__footer { display: flex; justify-content: flex-end; margin-top: 10px; padding-top: 9px; border-top: 1px solid #e8edef; }
+.dashboard3-delete-block { min-height: 30px; padding: 0 11px; border: 1px solid #e2968c; border-radius: 7px; color: #c84f43; background: #fff; font: inherit; font-size: 9px; font-weight: 700; cursor: pointer; }
+.dashboard3-delete-block:hover { border-color: #d96759; background: #fff5f3; }
+.dashboard3-add-value,
+.dashboard3-add-block { display: flex; align-items: center; justify-content: center; gap: 6px; border: 1px dashed #78c4d6; color: #087f9d; background: #f2fafc; font: inherit; font-size: 9px; font-weight: 700; cursor: pointer; }
+.dashboard3-add-value { width: 100%; min-height: 32px; margin-top: 7px; border-radius: 8px; }
+.dashboard3-add-block { display: inline-flex; width: auto; min-height: 35px; margin-top: 10px; padding: 0 12px; border-radius: 9px; }
+.dashboard3-add-value img,
+.dashboard3-add-block img { width: 14px; height: 14px; opacity: .7; }
+.dashboard3-add-value:disabled,
+.dashboard3-add-block:disabled { border-color: #d9dfe1; color: #9ba1a4; background: #f7f8f8; cursor: not-allowed; }
+.dashboard3-add-value:hover:not(:disabled),
+.dashboard3-add-block:hover:not(:disabled) { color: #fff; background: var(--ha-blue); }
+.dashboard3-add-value:hover:not(:disabled) img,
+.dashboard3-add-block:hover:not(:disabled) img { filter: brightness(0) invert(1); opacity: 1; }
+
+.dashboard3-settings-dialog__footer { display: flex; min-height: 61px; align-items: center; justify-content: flex-end; padding: 11px 17px; gap: 8px; border-top: 1px solid #dfe7e9; background: #fff; }
+.dashboard3-settings-status { margin-right: auto; color: #c25246; font-size: 9px; }
+.dashboard3-settings-status.is-ready { color: #5f777c; }
+.dashboard3-button { height: 35px; padding: 0 14px; border-radius: 8px; font: inherit; font-size: 10px; font-weight: 700; cursor: pointer; }
+.dashboard3-button--secondary { border: 1px solid #d5dcde; color: #555e61; background: #fff; }
+.dashboard3-button--primary { border: 1px solid var(--ha-blue); color: #fff; background: var(--ha-blue); }
+.dashboard3-button--primary:disabled { border-color: #b9d4da; color: #eef7f8; background: #91c7d4; cursor: not-allowed; }
+
+.dashboard3-entity-picker {
+ position: fixed;
+ z-index: 1100;
+ display: flex;
+ width: min(420px, calc(100vw - 24px));
+ max-height: min(430px, calc(100vh - 24px));
+ flex-direction: column;
+ overflow: hidden;
+ border: 1px solid #cfdadd;
+ border-radius: 11px;
+ background: #fff;
+ box-shadow: 0 14px 38px rgba(25, 34, 38, .25);
+}
+.dashboard3-entity-picker__search { display: flex; min-height: 47px; align-items: center; padding: 7px 9px; gap: 7px; border-bottom: 1px solid #e3e8ea; }
+.dashboard3-entity-picker__search > span { color: #657074; font-size: 15px; }
+.dashboard3-entity-picker__search input { width: 100%; height: 33px; padding: 0 8px; border: 1px solid #d5dddf; border-radius: 7px; color: #303638; font: inherit; font-size: 10px; outline: none; }
+.dashboard3-entity-picker__search input:focus { border-color: var(--ha-blue); box-shadow: 0 0 0 2px rgba(3, 164, 207, .12); }
+.dashboard3-entity-picker__list { min-height: 80px; overflow-y: auto; padding: 5px; scrollbar-width: thin; }
+.dashboard3-entity-option { display: flex; width: 100%; min-height: 43px; padding: 6px 8px; align-items: center; justify-content: space-between; gap: 10px; border: 0; border-radius: 7px; color: #343b3d; background: #fff; font: inherit; text-align: left; cursor: pointer; }
+.dashboard3-entity-option:hover,
+.dashboard3-entity-option:focus-visible { background: #edf8fa; outline: none; }
+.dashboard3-entity-option__name { display: flex; min-width: 0; flex-direction: column; gap: 2px; }
+.dashboard3-entity-option__name strong,
+.dashboard3-entity-option__name small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.dashboard3-entity-option__name strong { font-size: 10px; }
+.dashboard3-entity-option__name small { color: #7d8588; font-size: 8px; }
+.dashboard3-entity-option__state { flex: 0 0 auto; color: #3e616a; font-size: 9px; font-weight: 700; }
+.dashboard3-entity-option.is-selected { background: #e5f5f8; }
+.dashboard3-entity-picker__empty { padding: 18px; color: #858d90; font-size: 9px; text-align: center; }
+.dashboard3-entity-picker__hint { margin: 0; padding: 8px 10px; border-top: 1px solid #e3e8ea; color: #717a7d; background: #f7fafb; font-size: 8px; line-height: 1.4; }
+
+button:focus-visible,
+.dashboard3-space-tab:focus-visible { outline: 2px solid rgba(3, 164, 207, .35); outline-offset: 2px; }
+
+@media (max-width: 1450px) {
+ .dashboard3-panel { flex-basis: 350px; width: 350px; }
+}
+
+@media (max-width: 1220px) {
+ .dashboard3-panel { flex-basis: 320px; width: 320px; }
+ .dashboard3-block__scope { display: none; }
+}
+
+@media (max-width: 760px) {
+ .dashboard3-settings-dialog { padding: 8px; }
+ .dashboard3-settings-dialog__window { width: calc(100vw - 16px); max-height: calc(100vh - 16px); }
+ .dashboard3-general-settings__grid,
+ .dashboard3-block-options { grid-template-columns: 1fr; }
+ .dashboard3-editor-block__header { grid-template-columns: 61px minmax(0, 1fr) 31px 31px; }
+ .dashboard3-scope-select { grid-column: 1 / -1; grid-row: 2; }
+ .dashboard3-editor-value { grid-template-columns: 61px minmax(0, 1fr) 29px; }
+ .dashboard3-entity-trigger { grid-column: 2 / 3; }
+ .dashboard3-editor-value__remove { grid-column: 3; grid-row: 1; }
+ .dashboard3-editor-value__error { grid-column: 2 / 4; }
+}
+
+/* Dashboard 5: sorting controls, explicit scope, data states and local view settings. */
+.dashboard4-size-dialog[hidden] { display: none !important; }
+.dashboard3-editor-block.is-dragging,
+.dashboard3-editor-value.is-dragging { opacity: .5; }
+.dashboard3-editor-block.is-drop-target,
+.dashboard3-editor-value.is-drop-target { border-color: var(--ha-blue); box-shadow: 0 0 0 2px rgba(3, 164, 207, .15); }
+.dashboard4-drag-handle { display: grid; width: 26px; height: 30px; place-items: center; border: 0; color: #7f8b8f; background: transparent; cursor: grab; font-size: 17px; user-select: none; }
+.dashboard4-drag-handle:active { cursor: grabbing; }
+.dashboard3-editor-block__header { grid-template-columns: 26px 61px minmax(120px, 1fr) 31px 31px; }
+.dashboard4-block-drag { grid-column: 1; grid-row: 1; }
+.dashboard3-editor-block__header > .dashboard3-order-buttons { grid-column: 2; grid-row: 1; }
+.dashboard3-editor-block__header > .dashboard3-block-title { grid-column: 3; grid-row: 1; }
+.dashboard3-editor-block__header > .dashboard3-visibility { grid-column: 4; grid-row: 1; }
+.dashboard4-scope-controls { display: grid; min-width: 0; grid-column: 3 / -1; grid-row: 2; grid-template-columns: minmax(150px, .9fr) minmax(160px, 1fr); gap: 5px; }
+.dashboard4-scope-controls > select[hidden] { display: none; }
+.dashboard4-scope-controls:has(.dashboard4-space-select[hidden]) { grid-template-columns: 1fr; }
+.dashboard4-scope-warning { margin: 0 0 9px; padding: 7px 9px; border: 1px solid #e4b569; border-radius: 7px; color: #805514; background: #fff9eb; font-size: 9px; }
+.dashboard3-editor-value { grid-template-columns: 27px 61px minmax(125px, .9fr) minmax(180px, 1.2fr) 29px; }
+.dashboard3-editor-value__error { grid-column: 3 / 5; }
+.dashboard4-value--loading dd { color: #738084; }
+.dashboard4-value--stale dd { color: #9a650d; }
+.dashboard4-value--unavailable dd { color: #b2443b; }
+.dashboard3-value dd small { display: block; margin-top: 1px; color: inherit; font-size: 7px; font-weight: 600; }
+.dashboard3-entity-option__state { display: flex; flex-direction: column; align-items: flex-end; }
+.dashboard3-entity-option__state small { color: #a55a21; font-size: 7px; }
+.dashboard4-entity--unavailable .dashboard3-entity-option__state { color: #b2443b; }
+.dashboard4-entity--stale .dashboard3-entity-option__state { color: #9a650d; }
+
+.dashboard4-kiosk-toggle { min-width: 44px; height: 27px; padding: 0 8px; border: 1px solid #d9dfe1; border-radius: 7px; color: #566064; background: #fff; font: inherit; font-size: 9px; font-weight: 700; cursor: pointer; }
+.dashboard4-kiosk-toggle[aria-pressed="true"] { color: #fff; border-color: var(--ha-blue); background: var(--ha-blue); }
+.dashboard4-size-dialog { position: fixed; z-index: 1200; inset: 0; display: grid; padding: 16px; place-items: center; background: rgba(42, 49, 52, .42); backdrop-filter: blur(2px); }
+.dashboard4-size-dialog__window { width: min(430px, calc(100vw - 32px)); overflow: hidden; border: 1px solid #d6dfe1; border-radius: 14px; background: #fff; box-shadow: 0 18px 55px rgba(25, 34, 38, .25); }
+.dashboard4-size-dialog__window > header { display: flex; min-height: 58px; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid #e3e8ea; }
+.dashboard4-size-dialog h2 { margin: 0; font-size: 17px; }
+.dashboard4-size-close { width: 31px; height: 31px; border: 0; border-radius: 7px; background: transparent; font-size: 21px; cursor: pointer; }
+.dashboard4-size-dialog label { display: grid; padding: 14px 16px 3px; grid-template-columns: 1fr auto; gap: 9px 12px; color: #4e585b; font-size: 12px; }
+.dashboard4-size-dialog output { font-weight: 800; }
+.dashboard4-size-dialog input[type="range"] { grid-column: 1 / -1; width: 100%; accent-color: var(--ha-blue); }
+.dashboard4-size-dialog footer { display: flex; justify-content: flex-end; padding: 14px 16px; gap: 8px; }
+.dashboard4-size-reset { min-height: 35px; padding: 0 12px; border: 1px solid #d5dcde; border-radius: 8px; background: #fff; cursor: pointer; }
+.floor-plan .device { transform: scale(var(--dashboard4-device-scale, 1)); }
+.floor-plan .room-name { font-size: calc(11px * var(--dashboard4-room-text-scale, 1)); }
+
+html[data-theme="dark"] body { color: #e8edef; background: #14191b; }
+html[data-theme="dark"] .sidebar,
+html[data-theme="dark"] .global-bar,
+html[data-theme="dark"] .dashboard3-context-bar,
+html[data-theme="dark"] .dashboard3-panel__content,
+html[data-theme="dark"] .dashboard3-block,
+html[data-theme="dark"] .dashboard3-settings-dialog__window,
+html[data-theme="dark"] .dashboard3-settings-dialog__header,
+html[data-theme="dark"] .dashboard3-settings-dialog__footer,
+html[data-theme="dark"] .dashboard3-general-settings,
+html[data-theme="dark"] .dashboard3-block-editor,
+html[data-theme="dark"] .dashboard3-editor-block,
+html[data-theme="dark"] .dashboard3-editor-block__header,
+html[data-theme="dark"] .dashboard3-editor-value,
+html[data-theme="dark"] .dashboard4-size-dialog__window { color: #e8edef; border-color: #3a4549; background: #22292c; }
+html[data-theme="dark"] input,
+html[data-theme="dark"] select,
+html[data-theme="dark"] .dashboard3-entity-trigger,
+html[data-theme="dark"] .dashboard3-order-button,
+html[data-theme="dark"] .dashboard4-kiosk-toggle,
+html[data-theme="dark"] .dashboard4-size-reset { color: #e8edef; border-color: #48555a; background: #1b2124; }
+html[data-theme="dark"] .dashboard3-block__header h3,
+html[data-theme="dark"] .dashboard3-value dd,
+html[data-theme="dark"] .dashboard3-settings-dialog h2,
+html[data-theme="dark"] .dashboard3-settings-dialog h3 { color: #f2f5f6; }
+html[data-theme="dark"] .dashboard3-value { border-color: #333e42; }
+html[data-theme="dark"] .dashboard3-value dt,
+html[data-theme="dark"] .dashboard3-block-editor__heading p { color: #aeb8bb; }
+
+body.dashboard5-kiosk .app-shell { grid-template-columns: 1fr; }
+body.dashboard5-kiosk .sidebar,
+body.dashboard5-kiosk .global-bar,
+body.dashboard5-kiosk .dashboard3-context-bar > :not(.dashboard3-context-actions) { display: none; }
+body.dashboard5-kiosk .workspace { grid-column: 1; }
+body.dashboard5-kiosk .dashboard3-context-bar { position: fixed; z-index: 30; top: 10px; right: 10px; width: auto; min-height: 42px; padding: 6px; border: 1px solid #d8dfe1; border-radius: 10px; background: rgba(255,255,255,.94); box-shadow: 0 5px 18px rgba(25,34,38,.16); }
+body.dashboard5-kiosk .dashboard3-context-actions { display: flex; }
+body.dashboard5-kiosk .dashboard-body { height: 100vh; }
+html[data-theme="dark"] body.dashboard5-kiosk .dashboard3-context-bar { border-color: #48555a; background: rgba(34, 41, 44, .96); }
+
+@media (max-width: 900px) {
+ .dashboard3-editor-block__header { grid-template-columns: 26px 61px minmax(0, 1fr) 31px 31px; }
+ .dashboard4-scope-controls { grid-column: 3 / -1; grid-row: 2; grid-template-columns: 1fr; }
+ .dashboard3-editor-value { grid-template-columns: 27px 61px minmax(0, 1fr) 29px; }
+ .dashboard3-entity-trigger { grid-column: 3 / 4; }
+ .dashboard3-editor-value__remove { grid-column: 4; grid-row: 1; }
+ .dashboard3-editor-value__error { grid-column: 3 / 5; }
+}
+
+/* Dashboard 5: one floating adaptive information panel. */
+.dashboard5-summary-control {
+ display: flex;
+ height: 29px;
+ flex: 0 0 auto;
+ overflow: hidden;
+ border: 1px solid #d8dfe1;
+ border-radius: 8px;
+ background: #fff;
+ box-shadow: 0 1px 2px rgba(30, 44, 49, .05);
+}
+.dashboard5-summary-control .dashboard3-settings-toggle,
+.dashboard5-summary-control .dashboard3-global-toggle {
+ display: grid;
+ width: 31px;
+ height: 27px;
+ padding: 0;
+ place-items: center;
+ border: 0;
+ border-radius: 0;
+ color: #586367;
+ background: transparent;
+ cursor: pointer;
+}
+.dashboard5-summary-control .dashboard3-settings-toggle {
+ border-right: 1px solid #d8dfe1;
+}
+.dashboard5-summary-control .dashboard3-settings-toggle img {
+ width: 15px;
+ height: 15px;
+ opacity: .7;
+}
+.dashboard5-summary-control .dashboard3-global-toggle img {
+ display: block;
+ width: 17px;
+ height: 17px;
+ opacity: .74;
+}
+.dashboard5-summary-control .dashboard3-settings-toggle:hover,
+.dashboard5-summary-control .dashboard3-global-toggle:hover { background: #edf2f3; }
+.dashboard5-summary-control .dashboard3-global-toggle[aria-pressed="true"] { color: #fff; background: var(--ha-blue); }
+.dashboard5-summary-control .dashboard3-global-toggle[aria-pressed="true"] img { filter: brightness(0) invert(1); opacity: .92; }
+.dashboard5-summary-control .dashboard3-global-toggle[aria-pressed="true"]:hover { background: var(--ha-blue-dark); }
+.dashboard5-summary-control button:focus-visible { position: relative; z-index: 1; outline: 2px solid rgba(3, 164, 207, .42); outline-offset: -2px; }
+
+.dashboard3-panel {
+ top: 12px;
+ right: 12px;
+ bottom: auto;
+ left: auto;
+ width: max-content;
+ min-width: 280px;
+ max-width: min(420px, calc(100% - 24px));
+ height: fit-content;
+ max-height: calc(100% - 24px);
+ flex-basis: auto;
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+ transform: translateX(18px);
+ transition: opacity .19s ease, transform .19s ease, visibility 0s linear .19s;
+}
+.dashboard3-panel.is-visible {
+ opacity: 1;
+ visibility: visible;
+ pointer-events: auto;
+ transform: translateX(0);
+ transition-delay: 0s;
+}
+.dashboard3-panel__content {
+ width: max-content;
+ min-width: 280px;
+ max-width: min(420px, calc(100vw - 24px));
+ max-height: calc(100% - 24px);
+ padding: 0;
+ overflow: hidden;
+ box-shadow: 0 8px 26px rgba(40, 54, 60, .16);
+}
+.dashboard5-panel-header {
+ display: flex;
+ min-height: 48px;
+ flex: 0 0 auto;
+ align-items: center;
+ padding: 0 14px;
+ border-bottom: 1px solid #e1e7e9;
+ background: #fff;
+}
+.dashboard5-panel-header h2 {
+ max-width: 100%;
+ margin: 0;
+ overflow: hidden;
+ color: #30383b;
+ font-size: 14px;
+ line-height: 1.25;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.dashboard3-blocks {
+ width: 100%;
+ padding: 10px;
+}
+.dashboard3-value {
+ grid-template-columns: minmax(0, 1fr) max-content;
+}
+.dashboard3-panel.dashboard5-panel-bottom {
+ top: auto;
+ right: auto;
+ bottom: 12px;
+ left: 50%;
+ width: max-content;
+ min-width: min(360px, calc(100% - 24px));
+ max-width: calc(100% - 24px);
+ max-height: min(60%, calc(100% - 24px));
+ transform: translate(-50%, 18px);
+}
+.dashboard3-panel.dashboard5-panel-bottom.is-visible { transform: translate(-50%, 0); }
+.dashboard3-panel.dashboard5-panel-bottom .dashboard3-panel__content {
+ width: max-content;
+ min-width: min(360px, calc(100vw - 24px));
+ max-width: calc(100vw - 24px);
+ max-height: min(60vh, calc(100vh - 24px));
+}
+
+/* Blocks are always expanded, including in the settings dialog. */
+.dashboard3-editor-block__header { grid-template-columns: 26px 61px minmax(120px, 1fr) 31px; }
+.dashboard3-editor-block__header > .dashboard3-visibility { grid-column: 4; grid-row: 1; }
+.dashboard4-scope-controls { grid-column: 3 / -1; }
+
+body.dashboard5-kiosk .dashboard3-panel:not(.dashboard5-panel-bottom) {
+ top: 72px;
+ max-height: calc(100vh - 84px);
+}
+body.dashboard5-kiosk .dashboard3-panel:not(.dashboard5-panel-bottom) .dashboard3-panel__content { max-height: calc(100vh - 84px); }
+body.dashboard5-kiosk .dashboard3-panel.dashboard5-panel-bottom { top: auto; bottom: 12px; }
+
+html[data-theme="dark"] .dashboard5-summary-control { border-color: #48555a; background: #1b2124; }
+html[data-theme="dark"] .dashboard5-summary-control .dashboard3-settings-toggle { border-color: #48555a; }
+html[data-theme="dark"] .dashboard5-summary-control .dashboard3-settings-toggle:hover,
+html[data-theme="dark"] .dashboard5-summary-control .dashboard3-global-toggle:hover { background: #303b3f; }
+html[data-theme="dark"] .dashboard5-panel-header { border-color: #3a4549; background: #22292c; }
+html[data-theme="dark"] .dashboard5-panel-header h2 { color: #f2f5f6; }
+
+@media (max-width: 900px) {
+ .dashboard3-editor-block__header { grid-template-columns: 26px 61px minmax(0, 1fr) 31px; }
+ .dashboard4-scope-controls { grid-column: 3 / -1; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .dashboard3-panel { transition: none; }
+}
+
diff --git a/docs/design/505-summary-panel/reference/dashboard-5.js b/docs/design/505-summary-panel/reference/dashboard-5.js
new file mode 100644
index 00000000..8d89306d
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/dashboard-5.js
@@ -0,0 +1,756 @@
+(() => {
+ const panel = document.querySelector("#dashboard3-panel");
+ const blocksContainer = document.querySelector("#dashboard3-blocks");
+ const settingsToggle = document.querySelector(".dashboard3-settings-toggle");
+ const globalToggle = document.querySelector(".dashboard3-global-toggle");
+ const panelTitleHeading = document.querySelector("#dashboard5-panel-title");
+ const dashboardBody = document.querySelector(".dashboard-body");
+ const spaceTabs = [...document.querySelectorAll(".dashboard3-space-tab")];
+ const settingsDialog = document.querySelector("#dashboard3-settings-dialog");
+ const settingsForm = settingsDialog?.querySelector(".dashboard3-settings-dialog__window");
+ const settingsClose = settingsDialog?.querySelector(".dashboard3-settings-dialog__close");
+ const settingsCancel = settingsDialog?.querySelector(".dashboard3-settings-cancel");
+ const settingsSave = settingsDialog?.querySelector(".dashboard3-settings-save");
+ const settingsStatus = document.querySelector("#dashboard3-settings-status");
+ const panelTitleInput = document.querySelector("#dashboard3-panel-title");
+ const panelTitleError = panelTitleInput?.parentElement.querySelector(".dashboard3-field__error");
+ const showMobileInput = document.querySelector("#dashboard3-show-mobile");
+ const showPanelInput = document.querySelector("#dashboard5-show-panel");
+ const editorList = document.querySelector("#dashboard3-block-editor-list");
+ const blockCount = document.querySelector("#dashboard3-block-count");
+ const addBlockButton = document.querySelector(".dashboard3-add-block");
+ const entityPicker = document.querySelector("#dashboard3-entity-picker");
+ const entitySearch = document.querySelector("#dashboard3-entity-search");
+ const entityList = document.querySelector("#dashboard3-entity-list");
+ const sizeToggle = document.querySelector(".dashboard4-size-toggle");
+ const themeToggle = document.querySelector(".dashboard4-theme-toggle");
+ const kioskToggle = document.querySelector(".dashboard4-kiosk-toggle");
+ const sizeDialog = document.querySelector("#dashboard4-size-dialog");
+ const sizeForm = sizeDialog?.querySelector(".dashboard4-size-dialog__window");
+ const sizeClose = sizeDialog?.querySelector(".dashboard4-size-close");
+ const sizeReset = sizeDialog?.querySelector(".dashboard4-size-reset");
+ const iconSizeInput = document.querySelector("#dashboard4-icon-size");
+ const textSizeInput = document.querySelector("#dashboard4-text-size");
+ const iconSizeOutput = document.querySelector("#dashboard4-icon-size-output");
+ const textSizeOutput = document.querySelector("#dashboard4-text-size-output");
+
+ if (!panel || !blocksContainer || !settingsToggle || !globalToggle || !panelTitleHeading || !dashboardBody || !spaceTabs.length || !settingsDialog || !settingsForm || !settingsClose || !settingsCancel || !settingsSave || !settingsStatus || !panelTitleInput || !panelTitleError || !showMobileInput || !showPanelInput || !editorList || !blockCount || !addBlockButton || !entityPicker || !entitySearch || !entityList || !sizeToggle || !themeToggle || !kioskToggle || !sizeDialog || !sizeForm || !sizeClose || !sizeReset || !iconSizeInput || !textSizeInput || !iconSizeOutput || !textSizeOutput) return;
+
+ const limits = { blocks: 10, values: 20 };
+ const storageKeys = {
+ config: "houseplan-dashboard5-config",
+ enabled: "houseplan-dashboard5-enabled",
+ theme: "houseplan-dashboard5-theme",
+ kiosk: "houseplan-dashboard5-kiosk",
+ sizes: "houseplan-dashboard5-local-sizes"
+ };
+ const spaces = [
+ { id: "ground-floor", name: "Ground Floor" },
+ { id: "first-floor", name: "First Floor" },
+ { id: "yard", name: "Yard" }
+ ];
+ const entities = [
+ { entityId: "sensor.kitchen_temperature", name: "Kitchen temperature", state: "20,6 °C" },
+ { entityId: "sensor.living_room_humidity", name: "Living room humidity", state: "42 %" },
+ { entityId: "light.wall_lamp_3", name: "Wall Lamp 3", state: "Выключено" },
+ { entityId: "light.terrace_lights", name: "Terrace lights", state: "Включено" },
+ { entityId: "climate.ground_floor", name: "Ground floor climate", state: "Нагрев · 21 °C" },
+ { entityId: "sensor.ground_floor_average_temperature", name: "Средняя температура первого этажа", state: "20,9 °C", template: true },
+ { entityId: "sensor.outdoor_temperature", name: "Outdoor temperature", state: "12,4 °C" },
+ { entityId: "sensor.outdoor_humidity", name: "Outdoor humidity", state: "Загрузка…", status: "loading" },
+ { entityId: "binary_sensor.front_door", name: "Front door", state: "Закрыто" },
+ { entityId: "binary_sensor.smoke_kitchen", name: "Kitchen smoke detector", state: "Норма" },
+ { entityId: "switch.sauna_heater", name: "Sauna heater", state: "Выключено" },
+ { entityId: "sensor.sauna_temperature", name: "Sauna temperature", state: "24,1 °C" },
+ { entityId: "sensor.boiler_temperature", name: "Boiler temperature", state: "54,0 °C" },
+ { entityId: "binary_sensor.boiler_water_leak", name: "Boiler water leak", state: "Норма" },
+ { entityId: "sensor.washing_machine_progress", name: "Washing machine progress", state: "66 %" },
+ { entityId: "sensor.washing_machine_state", name: "Washing machine state", state: "Rinsing" },
+ { entityId: "media_player.kitchen_display", name: "Kitchen display", state: "Ожидание" },
+ { entityId: "camera.entrance", name: "Entrance camera", state: "Недоступно", status: "unavailable" },
+ { entityId: "fan.kitchen_hood", name: "Kitchen hood", state: "Выключено" },
+ { entityId: "cover.guest_bedroom_blinds", name: "Guest bedroom blinds", state: "Открыто" },
+ { entityId: "sensor.house_energy_today", name: "House energy today", state: "8,7 kWh" },
+ { entityId: "vacuum.ground_floor", name: "Ground floor vacuum", state: "На базе" },
+ { entityId: "person.demo", name: "Demo", state: "Дома" },
+ { entityId: "weather.home", name: "Home weather", state: "Облачно · 13 °C", status: "stale" }
+ ];
+
+ let blockSequence = 1;
+ let valueSequence = 3;
+ let activeSpaceId = "ground-floor";
+ let globalEnabled = false;
+ let config = {
+ title: "Сводная информация",
+ showOnMobile: true,
+ blocks: [
+ {
+ id: "block-1",
+ title: "Общее",
+ visible: true,
+ scope: { type: "all", spaceId: null },
+ values: [
+ { id: "value-1", label: "Количество устройств", source: { type: "system", key: "device_count", name: "Количество устройств" } },
+ { id: "value-2", label: "Общая площадь комнат", source: { type: "system", key: "room_area", name: "Общая площадь комнат" } },
+ { id: "value-3", label: "Текущие дата и время", source: { type: "system", key: "date_time", name: "Текущие дата и время" } }
+ ]
+ }
+ ]
+ };
+ let draft = null;
+ let baseline = null;
+ let draftEnabled = false;
+ let baselineEnabled = false;
+ let dragContext = null;
+ let pickerContext = null;
+ let currentTheme = "light";
+ let kioskEnabled = false;
+ let localSizes = { icons: 100, text: 100 };
+
+ try {
+ const storedConfig = window.localStorage.getItem(storageKeys.config);
+ if (storedConfig) config = JSON.parse(storedConfig);
+ globalEnabled = window.localStorage.getItem(storageKeys.enabled) === "true";
+ currentTheme = window.localStorage.getItem(storageKeys.theme) || "light";
+ kioskEnabled = window.localStorage.getItem(storageKeys.kiosk) === "true";
+ const storedSizes = window.localStorage.getItem(storageKeys.sizes);
+ if (storedSizes) localSizes = { ...localSizes, ...JSON.parse(storedSizes) };
+ } catch (error) {
+ console.warn("Dashboard 5: сохранённая конфигурация недоступна", error);
+ }
+ blockSequence = Math.max(1, config.blocks.length);
+ valueSequence = Math.max(3, ...config.blocks.flatMap((block) => block.values.map((value) => Number(String(value.id).match(/\d+/)?.[0]) || 0)));
+
+ const persist = (key, value) => {
+ try {
+ window.localStorage.setItem(key, typeof value === "string" ? value : JSON.stringify(value));
+ } catch (error) {
+ console.warn("Dashboard 5: не удалось сохранить настройку", error);
+ }
+ };
+
+ const applyTheme = () => {
+ document.documentElement.dataset.theme = currentTheme;
+ themeToggle.setAttribute("aria-label", currentTheme === "dark" ? "Включить светлую тему" : "Включить тёмную тему");
+ };
+
+ const applyKiosk = () => {
+ document.body.classList.toggle("dashboard5-kiosk", kioskEnabled);
+ kioskToggle.setAttribute("aria-pressed", String(kioskEnabled));
+ kioskToggle.textContent = kioskEnabled ? "Выйти" : "Киоск";
+ };
+
+ const applyLocalSizes = () => {
+ document.documentElement.style.setProperty("--dashboard4-device-scale", String(localSizes.icons / 100));
+ document.documentElement.style.setProperty("--dashboard4-room-text-scale", String(localSizes.text / 100));
+ iconSizeInput.value = String(localSizes.icons);
+ textSizeInput.value = String(localSizes.text);
+ iconSizeOutput.textContent = `${localSizes.icons}%`;
+ textSizeOutput.textContent = `${localSizes.text}%`;
+ };
+
+ const clone = (value) => JSON.parse(JSON.stringify(value));
+ const fingerprint = (value) => JSON.stringify(value);
+ const escapeHtml = (value) => String(value).replace(/[&<>"']/g, (character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[character]);
+ const currentSpace = () => spaces.find((space) => space.id === activeSpaceId) || spaces[0];
+ const findDraftBlock = (blockId) => draft?.blocks.find((block) => block.id === blockId);
+ const findDraftValue = (blockId, valueId) => findDraftBlock(blockId)?.values.find((value) => value.id === valueId);
+
+ const isNarrowViewport = () => {
+ if (typeof window.hass?.narrow === "boolean") return window.hass.narrow;
+ return document.documentElement.dataset.haNarrow === "true";
+ };
+
+ const formatDateTime = () => new Intl.DateTimeFormat("ru-RU", {
+ day: "2-digit",
+ month: "short",
+ hour: "2-digit",
+ minute: "2-digit"
+ }).format(new Date()).replace(".", "");
+
+ const entitySnapshot = (entityId) => {
+ const hassState = window.hass?.states?.[entityId];
+ if (hassState) {
+ const rawState = hassState.state;
+ const status = rawState === "unavailable" || rawState === "unknown" ? "unavailable" : hassState.attributes?.restored ? "stale" : "ready";
+ let text;
+ try {
+ text = typeof window.hass.formatEntityState === "function" ? window.hass.formatEntityState(hassState) : rawState;
+ } catch {
+ text = rawState;
+ }
+ return { text: text || "Нет данных", status };
+ }
+ const demoEntity = entities.find((item) => item.entityId === entityId);
+ if (!demoEntity) return { text: "Недоступно", status: "unavailable" };
+ return { text: demoEntity.state || "Нет данных", status: demoEntity.status || "ready" };
+ };
+
+ const resolveValue = (value) => {
+ if (value.source.type === "system") {
+ if (value.source.key === "device_count") return { text: "50", status: "ready" };
+ if (value.source.key === "room_area") return { text: "156 м²", status: "ready" };
+ if (value.source.key === "date_time") return { text: formatDateTime(), status: "ready" };
+ }
+ return entitySnapshot(value.source.entityId);
+ };
+
+ const sourcePresentation = (source) => {
+ if (!source) return { name: "Выберите сущность", detail: "Поиск по всем сущностям HA" };
+ if (source.type === "system") return { name: source.name, detail: "Системное значение" };
+ const entity = entities.find((item) => item.entityId === source.entityId);
+ if (!entity) return { name: "Недоступная сущность", detail: `${source.entityId || "Не выбрано"} · Требует внимания` };
+ const status = entity.status === "unavailable" ? " · Недоступно" : entity.status === "stale" ? " · Данные устарели" : entity.status === "loading" ? " · Загрузка" : "";
+ return { name: entity.name, detail: `${entity.entityId}${status}` };
+ };
+
+ const blockMatchesSpace = (block) => block.scope.type === "all" || block.scope.spaceId === activeSpaceId;
+
+ const positionPanel = () => {
+ const bottom = window.matchMedia("(orientation: portrait)").matches;
+ panel.classList.toggle("dashboard5-panel-bottom", bottom);
+ panel.dataset.position = bottom ? "bottom" : "right";
+ };
+
+ const renderGlobalState = () => {
+ const hiddenByMobile = isNarrowViewport() && !config.showOnMobile;
+ const visible = globalEnabled && !hiddenByMobile;
+ panel.hidden = false;
+ panel.classList.toggle("is-visible", visible);
+ panel.setAttribute("aria-hidden", String(!visible));
+ globalToggle.setAttribute("aria-pressed", String(globalEnabled));
+ globalToggle.title = globalEnabled ? "Скрыть информационную панель" : "Показать информационную панель";
+ globalToggle.setAttribute("aria-label", globalToggle.title);
+ };
+
+ const renderPanel = () => {
+ const title = config.title.trim() || "Сводная информация";
+ panelTitleHeading.textContent = title;
+ panel.setAttribute("aria-label", title);
+ positionPanel();
+ renderGlobalState();
+
+ blocksContainer.replaceChildren();
+ const displayedBlocks = config.blocks.filter((block) => block.visible && blockMatchesSpace(block));
+ if (!displayedBlocks.length) {
+ const empty = document.createElement("div");
+ empty.className = "dashboard3-empty";
+ empty.textContent = `Для пространства «${currentSpace().name}» нет настроенных блоков.`;
+ blocksContainer.append(empty);
+ return;
+ }
+
+ displayedBlocks.forEach((block) => {
+ const valuesHtml = block.values.map((value) => {
+ const resolved = resolveValue(value);
+ const statusLabel = resolved.status === "loading" ? "Загрузка" : resolved.status === "stale" ? "Данные устарели" : resolved.status === "unavailable" ? "Источник недоступен" : "";
+ return `
+
+
${escapeHtml(value.label)}
+ ${escapeHtml(resolved.text)}${statusLabel ? `${escapeHtml(statusLabel)}` : ""}
+ `;
+ }).join("");
+ const card = document.createElement("section");
+ card.className = "dashboard3-block";
+ card.innerHTML = `
+
+ ${valuesHtml || 'В блоке пока нет значений.
'}
`;
+ blocksContainer.append(card);
+ });
+ };
+
+ const validateDraft = () => {
+ const errors = [];
+ if (!draft?.title.trim()) errors.push({ type: "panel-title" });
+ draft?.blocks.forEach((block) => {
+ if (!block.title.trim()) errors.push({ type: "block-title", blockId: block.id });
+ if (block.scope.type === "space" && !block.scope.spaceId) errors.push({ type: "space", blockId: block.id });
+ block.values.forEach((value) => {
+ const missing = [];
+ if (!value.label.trim()) missing.push("название");
+ if (!value.source) missing.push("сущность");
+ if (missing.length) errors.push({ type: "value", blockId: block.id, valueId: value.id, missing });
+ });
+ });
+ return errors;
+ };
+
+ const updateDialogState = () => {
+ if (!draft || !baseline) return;
+ const errors = validateDraft();
+ const dirty = fingerprint(draft) !== fingerprint(baseline) || draftEnabled !== baselineEnabled;
+ settingsSave.hidden = !dirty;
+ settingsSave.disabled = errors.length > 0;
+ panelTitleInput.classList.toggle("is-invalid", !draft.title.trim());
+ panelTitleError.textContent = draft.title.trim() ? "" : "Введите название панели";
+
+ editorList.querySelectorAll(".dashboard3-editor-block").forEach((element) => {
+ const blockId = element.dataset.blockId;
+ const block = findDraftBlock(blockId);
+ if (!block) return;
+ element.querySelector(".dashboard3-block-title").classList.toggle("is-invalid", !block.title.trim());
+ const scopeSelect = element.querySelector(".dashboard4-space-select");
+ const invalidScope = block.scope.type === "space" && !block.scope.spaceId;
+ if (scopeSelect) scopeSelect.classList.toggle("is-invalid", invalidScope);
+ element.querySelectorAll(".dashboard3-editor-value").forEach((rowElement) => {
+ const value = findDraftValue(blockId, rowElement.dataset.valueId);
+ if (!value) return;
+ const missing = [];
+ if (!value.label.trim()) missing.push("название");
+ if (!value.source) missing.push("сущность");
+ rowElement.classList.toggle("has-error", missing.length > 0);
+ rowElement.querySelector(".dashboard3-value-label").classList.toggle("is-invalid", !value.label.trim());
+ rowElement.querySelector(".dashboard3-entity-trigger").classList.toggle("is-invalid", !value.source);
+ rowElement.querySelector(".dashboard3-editor-value__error").textContent = missing.length ? `Заполните: ${missing.join(" и ")}` : "";
+ });
+ });
+
+ settingsStatus.textContent = errors.length
+ ? `Нельзя сохранить: незаполненных полей — ${errors.length}`
+ : dirty ? "Есть несохранённые изменения" : "";
+ settingsStatus.classList.toggle("is-ready", dirty && !errors.length);
+ };
+
+ const moveItemByOffset = (items, itemId, offset) => {
+ const sourceIndex = items.findIndex((item) => item.id === itemId);
+ const targetIndex = sourceIndex + offset;
+ if (sourceIndex < 0 || targetIndex < 0 || targetIndex >= items.length) return false;
+ [items[sourceIndex], items[targetIndex]] = [items[targetIndex], items[sourceIndex]];
+ return true;
+ };
+
+ const moveItem = (items, sourceId, targetId, placeAfter) => {
+ const sourceIndex = items.findIndex((item) => item.id === sourceId);
+ if (sourceIndex < 0) return;
+ const [moved] = items.splice(sourceIndex, 1);
+ const targetIndex = items.findIndex((item) => item.id === targetId);
+ if (targetIndex < 0) items.push(moved);
+ else items.splice(targetIndex + (placeAfter ? 1 : 0), 0, moved);
+ };
+
+ const restoreOrderFocus = (selector) => {
+ window.requestAnimationFrame(() => editorList.querySelector(selector)?.focus());
+ };
+
+ const renderEditor = () => {
+ editorList.replaceChildren();
+ blockCount.textContent = `${draft.blocks.length} из ${limits.blocks}`;
+ addBlockButton.disabled = draft.blocks.length >= limits.blocks;
+ addBlockButton.title = addBlockButton.disabled ? `Можно добавить не более ${limits.blocks} блоков` : "Добавить блок";
+
+ if (!draft.blocks.length) {
+ const empty = document.createElement("div");
+ empty.className = "dashboard3-empty";
+ empty.textContent = "Блоков пока нет. Добавьте блок, чтобы вывести данные в панели.";
+ editorList.append(empty);
+ }
+
+ draft.blocks.forEach((block, blockIndex) => {
+ const article = document.createElement("article");
+ article.className = "dashboard3-editor-block";
+ article.dataset.blockId = block.id;
+ const knownSpace = spaces.some((space) => space.id === block.scope.spaceId);
+ const spaceOptions = [
+ '',
+ block.scope.spaceId && !knownSpace ? `` : "",
+ ...spaces.map((space) => ``)
+ ].join("");
+ const valuesHtml = block.values.map((value, index) => {
+ const source = sourcePresentation(value.source);
+ return `
+
+ ⠿
+
+
+
+
+
+
+
+
+
`;
+ }).join("");
+ article.innerHTML = `
+
+
+ ${block.scope.type === "space" && block.scope.spaceId && !knownSpace ? '
Выбранное пространство недоступно. Привязка сохранена и требует внимания.
' : ""}
+
${valuesHtml || '
В блоке пока нет значений.
'}
+
+
+
`;
+
+ const blockTitle = article.querySelector(".dashboard3-block-title");
+ const blockDrag = article.querySelector(".dashboard4-block-drag");
+ const blockMoveUp = article.querySelector(".dashboard3-block-up");
+ const blockMoveDown = article.querySelector(".dashboard3-block-down");
+ const visibility = article.querySelector(".dashboard3-visibility");
+ const deleteBlock = article.querySelector(".dashboard3-delete-block");
+ const scopeSelect = article.querySelector(".dashboard4-scope-type");
+ const spaceSelect = article.querySelector(".dashboard4-space-select");
+ const addValue = article.querySelector(".dashboard3-add-value");
+
+ blockTitle.addEventListener("input", (event) => {
+ block.title = event.target.value;
+ updateDialogState();
+ });
+ blockMoveUp.addEventListener("click", () => {
+ if (!moveItemByOffset(draft.blocks, block.id, -1)) return;
+ renderEditor();
+ restoreOrderFocus(`[data-block-id="${block.id}"] .dashboard3-block-up`);
+ });
+ blockMoveDown.addEventListener("click", () => {
+ if (!moveItemByOffset(draft.blocks, block.id, 1)) return;
+ renderEditor();
+ restoreOrderFocus(`[data-block-id="${block.id}"] .dashboard3-block-down`);
+ });
+ visibility.addEventListener("click", () => {
+ block.visible = !block.visible;
+ renderEditor();
+ });
+ deleteBlock.addEventListener("click", () => {
+ if (block.values.length && !window.confirm(`Удалить блок «${block.title || "Без названия"}» и все его значения?`)) return;
+ draft.blocks = draft.blocks.filter((item) => item.id !== block.id);
+ closeEntityPicker();
+ renderEditor();
+ });
+ scopeSelect.addEventListener("change", (event) => {
+ block.scope.type = event.target.value;
+ if (block.scope.type === "all") block.scope.spaceId = null;
+ else block.scope.spaceId = null;
+ renderEditor();
+ });
+ spaceSelect.addEventListener("change", (event) => {
+ block.scope.spaceId = event.target.value || null;
+ renderEditor();
+ });
+ addValue.addEventListener("click", () => {
+ if (block.values.length >= limits.values) return;
+ block.values.push({ id: `value-${++valueSequence}`, label: "", source: null });
+ renderEditor();
+ article.ownerDocument.querySelector(`[data-block-id="${block.id}"] .dashboard3-editor-value:last-of-type .dashboard3-value-label`)?.focus();
+ });
+
+ blockDrag.addEventListener("dragstart", (event) => {
+ dragContext = { type: "block", id: block.id };
+ article.classList.add("is-dragging");
+ event.dataTransfer.effectAllowed = "move";
+ });
+ article.addEventListener("dragover", (event) => {
+ if (dragContext?.type !== "block" || dragContext.id === block.id) return;
+ event.preventDefault();
+ article.classList.add("is-drop-target");
+ });
+ article.addEventListener("dragleave", () => article.classList.remove("is-drop-target"));
+ article.addEventListener("drop", (event) => {
+ if (dragContext?.type !== "block" || dragContext.id === block.id) return;
+ event.preventDefault();
+ const rect = article.getBoundingClientRect();
+ moveItem(draft.blocks, dragContext.id, block.id, event.clientY > rect.top + rect.height / 2);
+ dragContext = null;
+ renderEditor();
+ });
+ blockDrag.addEventListener("dragend", () => {
+ dragContext = null;
+ editorList.querySelectorAll(".is-dragging, .is-drop-target").forEach((element) => element.classList.remove("is-dragging", "is-drop-target"));
+ });
+
+ article.querySelectorAll(".dashboard3-editor-value").forEach((rowElement) => {
+ const value = block.values.find((item) => item.id === rowElement.dataset.valueId);
+ if (!value) return;
+ const labelInput = rowElement.querySelector(".dashboard3-value-label");
+ const entityTrigger = rowElement.querySelector(".dashboard3-entity-trigger");
+ const removeValue = rowElement.querySelector(".dashboard3-editor-value__remove");
+ const valueMoveUp = rowElement.querySelector(".dashboard3-value-up");
+ const valueMoveDown = rowElement.querySelector(".dashboard3-value-down");
+ const valueDrag = rowElement.querySelector(".dashboard4-value-drag");
+ labelInput.addEventListener("input", (event) => {
+ value.label = event.target.value;
+ updateDialogState();
+ });
+ entityTrigger.addEventListener("click", () => openEntityPicker(block.id, value.id, entityTrigger));
+ valueMoveUp.addEventListener("click", () => {
+ if (!moveItemByOffset(block.values, value.id, -1)) return;
+ renderEditor();
+ restoreOrderFocus(`[data-block-id="${block.id}"] [data-value-id="${value.id}"] .dashboard3-value-up`);
+ });
+ valueMoveDown.addEventListener("click", () => {
+ if (!moveItemByOffset(block.values, value.id, 1)) return;
+ renderEditor();
+ restoreOrderFocus(`[data-block-id="${block.id}"] [data-value-id="${value.id}"] .dashboard3-value-down`);
+ });
+ removeValue.addEventListener("click", () => {
+ block.values = block.values.filter((item) => item.id !== value.id);
+ closeEntityPicker();
+ renderEditor();
+ });
+ valueDrag.addEventListener("dragstart", (event) => {
+ dragContext = { type: "value", id: value.id, blockId: block.id };
+ rowElement.classList.add("is-dragging");
+ event.stopPropagation();
+ event.dataTransfer.effectAllowed = "move";
+ });
+ rowElement.addEventListener("dragover", (event) => {
+ if (dragContext?.type !== "value" || dragContext.blockId !== block.id || dragContext.id === value.id) return;
+ event.preventDefault();
+ event.stopPropagation();
+ rowElement.classList.add("is-drop-target");
+ });
+ rowElement.addEventListener("drop", (event) => {
+ if (dragContext?.type !== "value" || dragContext.blockId !== block.id || dragContext.id === value.id) return;
+ event.preventDefault();
+ event.stopPropagation();
+ const rect = rowElement.getBoundingClientRect();
+ moveItem(block.values, dragContext.id, value.id, event.clientY > rect.top + rect.height / 2);
+ dragContext = null;
+ renderEditor();
+ });
+ valueDrag.addEventListener("dragend", (event) => {
+ event.stopPropagation();
+ dragContext = null;
+ editorList.querySelectorAll(".is-dragging, .is-drop-target").forEach((element) => element.classList.remove("is-dragging", "is-drop-target"));
+ });
+ });
+
+ editorList.append(article);
+ });
+ updateDialogState();
+ };
+
+ const renderEntityOptions = () => {
+ const query = entitySearch.value.trim().toLocaleLowerCase("ru-RU");
+ const filtered = entities.filter((entity) => `${entity.name} ${entity.entityId}`.toLocaleLowerCase("ru-RU").includes(query));
+ entityList.replaceChildren();
+ if (!filtered.length) {
+ const empty = document.createElement("div");
+ empty.className = "dashboard3-entity-picker__empty";
+ empty.textContent = "Сущности не найдены";
+ entityList.append(empty);
+ return;
+ }
+ const selected = pickerContext ? findDraftValue(pickerContext.blockId, pickerContext.valueId)?.source?.entityId : null;
+ filtered.forEach((entity) => {
+ const statusLabel = entity.status === "loading" ? "Загрузка" : entity.status === "stale" ? "Устарело" : entity.status === "unavailable" ? "Недоступно" : "";
+ const option = document.createElement("button");
+ option.className = `dashboard3-entity-option dashboard4-entity--${entity.status || "ready"}${selected === entity.entityId ? " is-selected" : ""}`;
+ option.type = "button";
+ option.setAttribute("role", "option");
+ option.setAttribute("aria-selected", String(selected === entity.entityId));
+ option.innerHTML = `${escapeHtml(entity.name)}${entity.template ? " · Template" : ""}${escapeHtml(entity.entityId)}${escapeHtml(entity.state)}${statusLabel ? `${escapeHtml(statusLabel)}` : ""}`;
+ option.addEventListener("click", () => {
+ if (!pickerContext) return;
+ const value = findDraftValue(pickerContext.blockId, pickerContext.valueId);
+ if (!value) return;
+ value.source = { type: "entity", entityId: entity.entityId };
+ closeEntityPicker();
+ renderEditor();
+ });
+ entityList.append(option);
+ });
+ };
+
+ const positionEntityPicker = (trigger) => {
+ const rect = trigger.getBoundingClientRect();
+ const width = Math.min(420, window.innerWidth - 24);
+ const estimatedHeight = Math.min(430, window.innerHeight - 24);
+ const left = Math.min(Math.max(12, rect.right - width), window.innerWidth - width - 12);
+ const below = rect.bottom + 5;
+ const top = below + estimatedHeight <= window.innerHeight - 12 ? below : Math.max(12, rect.top - estimatedHeight - 5);
+ entityPicker.style.left = `${left}px`;
+ entityPicker.style.top = `${top}px`;
+ };
+
+ const openEntityPicker = (blockId, valueId, trigger) => {
+ pickerContext = { blockId, valueId, trigger };
+ entitySearch.value = "";
+ entityPicker.hidden = false;
+ renderEntityOptions();
+ positionEntityPicker(trigger);
+ window.requestAnimationFrame(() => entitySearch.focus());
+ };
+
+ const closeEntityPicker = () => {
+ entityPicker.hidden = true;
+ pickerContext = null;
+ };
+
+ const openSettings = () => {
+ baseline = clone(config);
+ draft = clone(config);
+ baselineEnabled = globalEnabled;
+ draftEnabled = globalEnabled;
+ panelTitleInput.value = draft.title;
+ showPanelInput.checked = draftEnabled;
+ showMobileInput.checked = draft.showOnMobile;
+ settingsDialog.hidden = false;
+ document.body.classList.add("dashboard3-dialog-open");
+ renderEditor();
+ window.requestAnimationFrame(() => panelTitleInput.focus());
+ };
+
+ const closeSettings = (restore = true) => {
+ closeEntityPicker();
+ settingsDialog.hidden = true;
+ document.body.classList.remove("dashboard3-dialog-open");
+ if (restore) {
+ draft = null;
+ baseline = null;
+ draftEnabled = false;
+ baselineEnabled = false;
+ }
+ };
+
+ const saveSettings = () => {
+ const errors = validateDraft();
+ if (errors.length) {
+ updateDialogState();
+ editorList.querySelector(".is-invalid")?.focus();
+ return;
+ }
+ config = clone(draft);
+ globalEnabled = draftEnabled;
+ persist(storageKeys.config, config);
+ persist(storageKeys.enabled, String(globalEnabled));
+ closeSettings(false);
+ draft = null;
+ baseline = null;
+ draftEnabled = false;
+ baselineEnabled = false;
+ renderPanel();
+ };
+
+ globalToggle.addEventListener("click", () => {
+ globalEnabled = !globalEnabled;
+ persist(storageKeys.enabled, String(globalEnabled));
+ renderGlobalState();
+ });
+ themeToggle.addEventListener("click", () => {
+ currentTheme = currentTheme === "dark" ? "light" : "dark";
+ persist(storageKeys.theme, currentTheme);
+ applyTheme();
+ });
+ kioskToggle.addEventListener("click", () => {
+ kioskEnabled = !kioskEnabled;
+ persist(storageKeys.kiosk, String(kioskEnabled));
+ applyKiosk();
+ });
+ sizeToggle.addEventListener("click", () => {
+ sizeDialog.hidden = false;
+ document.body.classList.add("dashboard3-dialog-open");
+ applyLocalSizes();
+ iconSizeInput.focus();
+ });
+ sizeClose.addEventListener("click", () => {
+ sizeDialog.hidden = true;
+ document.body.classList.remove("dashboard3-dialog-open");
+ });
+ sizeForm.addEventListener("submit", (event) => {
+ event.preventDefault();
+ sizeDialog.hidden = true;
+ document.body.classList.remove("dashboard3-dialog-open");
+ });
+ sizeReset.addEventListener("click", () => {
+ localSizes = { icons: 100, text: 100 };
+ persist(storageKeys.sizes, localSizes);
+ applyLocalSizes();
+ });
+ iconSizeInput.addEventListener("input", (event) => {
+ localSizes.icons = Number(event.target.value);
+ persist(storageKeys.sizes, localSizes);
+ applyLocalSizes();
+ });
+ textSizeInput.addEventListener("input", (event) => {
+ localSizes.text = Number(event.target.value);
+ persist(storageKeys.sizes, localSizes);
+ applyLocalSizes();
+ });
+ settingsToggle.addEventListener("click", openSettings);
+ settingsClose.addEventListener("click", () => closeSettings());
+ settingsCancel.addEventListener("click", () => closeSettings());
+ settingsDialog.addEventListener("click", (event) => {
+ if (event.target === settingsDialog) closeSettings();
+ });
+ settingsForm.addEventListener("submit", (event) => {
+ event.preventDefault();
+ saveSettings();
+ });
+ panelTitleInput.addEventListener("input", (event) => {
+ draft.title = event.target.value;
+ updateDialogState();
+ });
+ showPanelInput.addEventListener("change", (event) => {
+ draftEnabled = event.target.checked;
+ updateDialogState();
+ });
+ showMobileInput.addEventListener("change", (event) => {
+ draft.showOnMobile = event.target.checked;
+ updateDialogState();
+ });
+ addBlockButton.addEventListener("click", () => {
+ if (draft.blocks.length >= limits.blocks) return;
+ const usedNumbers = draft.blocks.map((block) => Number(block.title.match(/\d+/)?.[0]) || 0);
+ const number = Math.max(0, ...usedNumbers, blockSequence) + 1;
+ blockSequence = number;
+ draft.blocks.push({
+ id: `block-${Date.now()}-${number}`,
+ title: `Блок ${number}`,
+ visible: true,
+ scope: { type: "all", spaceId: null },
+ values: [{ id: `value-${++valueSequence}`, label: "", source: null }]
+ });
+ renderEditor();
+ editorList.lastElementChild?.scrollIntoView({ behavior: "smooth", block: "nearest" });
+ editorList.lastElementChild?.querySelector(".dashboard3-block-title")?.focus();
+ });
+ entitySearch.addEventListener("input", renderEntityOptions);
+ document.addEventListener("pointerdown", (event) => {
+ if (entityPicker.hidden || entityPicker.contains(event.target) || event.target.closest(".dashboard3-entity-trigger")) return;
+ closeEntityPicker();
+ });
+ document.addEventListener("keydown", (event) => {
+ if (event.key !== "Escape") return;
+ if (!entityPicker.hidden) closeEntityPicker();
+ else if (!sizeDialog.hidden) {
+ sizeDialog.hidden = true;
+ document.body.classList.remove("dashboard3-dialog-open");
+ }
+ else if (!settingsDialog.hidden) closeSettings();
+ });
+ window.addEventListener("resize", () => {
+ if (!entityPicker.hidden && pickerContext?.trigger) positionEntityPicker(pickerContext.trigger);
+ positionPanel();
+ renderGlobalState();
+ });
+ spaceTabs.forEach((tab) => {
+ tab.addEventListener("click", () => {
+ activeSpaceId = tab.dataset.spaceId;
+ spaceTabs.forEach((item) => item.classList.toggle("active", item === tab));
+ renderPanel();
+ });
+ });
+
+ applyTheme();
+ applyKiosk();
+ applyLocalSizes();
+ renderPanel();
+ positionPanel();
+ if (typeof ResizeObserver === "function") new ResizeObserver(positionPanel).observe(dashboardBody);
+ window.addEventListener("resize", positionPanel, { passive: true });
+ window.setInterval(renderPanel, 60000);
+})();
+
diff --git a/docs/design/505-summary-panel/reference/index.html b/docs/design/505-summary-panel/reference/index.html
new file mode 100644
index 00000000..3d905ed7
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/index.html
@@ -0,0 +1,496 @@
+
+
+
+
+
+ Home Assistant — Dashboard 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ ⌗ Редактор плана
+ ⌘ Редактор устройств
+ ⌁ Редактор подложки
+
+
50 устр.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Kitchen & Living ↗
+ ♟ 20.6° ● 42% ◒ Выкл
+
+
+
+
+
+
+
+ Outdoor Storage ↗◉ Выкл
+
+
+
+
+
+ Under-Stairs Closet ↗
+
+
+
+
+
+
+
+
+ ▣●
+
+
●
+
●
+
◉
+
●
+
●
+
●
+
+
+ ▬▣⌂
+
+
+ ▣▦▦▣
+
+
✣✣
+
+
42%
+
♟
+
●
+
●
+
●
+
●
+
●
+
●
+
+
+ ↔☷↔
+
+
+ ♜▦
+
+
◉
+
66%
+
⌁
+
Rinsing
+
⌁
+
●
+
●
+
●
+
+
◉
+
43%
+
♟
+
▣▬
+
+
+
+
+
+
+
+
+
+
+
+
+
Нужно своё вычисление? Создайте Template-сущность в Home Assistant — она появится в этом списке.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/design/505-summary-panel/reference/styles.css b/docs/design/505-summary-panel/reference/styles.css
new file mode 100644
index 00000000..593fd193
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/styles.css
@@ -0,0 +1,373 @@
+:root {
+ --sidebar-width: 190px;
+ --global-bar-height: 41px;
+ --toolbar-height: 42px;
+ --blue: #00a5cf;
+ --ink: #303437;
+ --muted: #767b7e;
+ --line: #e7e7e7;
+ --room-fill: #8f979e;
+ --wall-dark: #53585b;
+}
+
+* { box-sizing: border-box; }
+
+html, body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ overflow: hidden;
+ font-family: Arial, Helvetica, sans-serif;
+ color: var(--ink);
+ background: #fff;
+}
+
+body { min-width: 1160px; min-height: 680px; }
+.components-page { min-height: 100vh; overflow: auto; }
+
+.app-shell { display: flex; width: 100vw; height: 100vh; }
+
+.sidebar {
+ position: relative;
+ z-index: 10;
+ flex: 0 0 var(--sidebar-width);
+ height: 100%;
+ border-right: 1px solid var(--line);
+ background: #fff;
+}
+
+.brand-row {
+ display: flex;
+ align-items: center;
+ height: var(--global-bar-height);
+ padding: 0 12px;
+ border-bottom: 1px solid var(--line);
+ gap: 12px;
+}
+
+.brand-name { font-size: 16px; font-weight: 500; color: #242424; }
+.nav-icon { width: 19px; text-align: center; color: #7b7f80; font-size: 17px; font-weight: 700; }
+.hamburger { font-size: 15px; transform: scaleX(1.1); }
+.plan-glyph, .active .nav-icon { color: var(--blue); }
+.lightning { font-size: 23px; }
+
+.side-nav { padding-top: 7px; }
+.nav-item {
+ display: flex;
+ align-items: center;
+ height: 34px;
+ padding: 0 12px;
+ gap: 11px;
+ color: #2e3032;
+ text-decoration: none;
+ font-size: 12px;
+ font-weight: 600;
+}
+.nav-item.active {
+ margin: 0 3px;
+ padding-left: 9px;
+ border-radius: 3px;
+ color: var(--blue);
+ background: #d9f1f7;
+}
+.sidebar-footer { position: absolute; left: 0; right: 0; bottom: 8px; }
+.sidebar-footer .nav-item { position: relative; }
+.badge {
+ position: absolute;
+ right: 11px;
+ display: grid;
+ place-items: center;
+ width: 17px;
+ height: 17px;
+ border-radius: 50%;
+ color: #fff;
+ background: #ff9900;
+ font-size: 10px;
+}
+.profile { height: 38px; }
+.avatar {
+ display: grid;
+ place-items: center;
+ width: 31px;
+ height: 31px;
+ margin-left: -5px;
+ border-radius: 50%;
+ color: #328496;
+ background: #d1f2fb;
+ font-weight: 400;
+}
+
+.workspace { position: relative; display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; height: 100%; }
+.global-bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: var(--global-bar-height);
+ padding: 0 14px 0 10px;
+ border-bottom: 1px solid var(--line);
+ background: #fff;
+ flex: 0 0 var(--global-bar-height);
+}
+.view-switcher { display: flex; align-self: stretch; gap: 14px; }
+.view-icon {
+ position: relative;
+ display: grid;
+ place-items: center;
+ width: 28px;
+ color: #555;
+ font-size: 16px;
+}
+.view-icon.selected::after {
+ position: absolute;
+ right: 0;
+ bottom: -1px;
+ left: 0;
+ height: 2px;
+ content: "";
+ background: #2d2d2d;
+}
+.global-actions { display: flex; align-items: center; gap: 24px; font-size: 20px; color: #222; }
+
+.ha-view-tabs--compact {
+ align-items: stretch;
+ align-self: stretch;
+ height: var(--global-bar-height);
+ padding-left: 0;
+ gap: 14px;
+ border-bottom: 0;
+}
+.ha-view-tabs--compact .ha-view-tab { width: 28px; height: var(--global-bar-height); font-size: 16px; }
+.ha-view-tabs--compact .ha-view-tab.active::after { height: 2px; }
+
+.ha-context-row--compact {
+ flex: 0 0 44px;
+ height: 44px;
+ min-height: 44px;
+ padding: 0 10px;
+ gap: 8px;
+ overflow: hidden;
+ font-size: 11px;
+}
+.ha-context-row--compact .ha-plan-label { gap: 5px; font-size: 12px; }
+.ha-context-row--compact .ha-home-icon { font-size: 17px; }
+.ha-context-row--compact .ha-segmented { height: 31px; padding: 3px; border-radius: 10px; }
+.ha-context-row--compact .ha-segment { height: 25px; padding: 0 11px; gap: 5px; border-radius: 7px; font-size: 11px; }
+.ha-context-row--compact .ha-segment small { font-size: 10px; }
+.ha-context-row--compact .ha-segment b { margin-left: 4px; font-size: 15px; }
+.ha-context-row--compact .ha-add { padding: 0 7px; font-size: 16px; }
+.ha-context-row--compact .ha-device-count { font-size: 11px; }
+
+.ha-tools-row--compact {
+ flex: 0 0 50px;
+ min-height: 50px;
+ height: 50px;
+ padding: 7px 10px;
+ gap: 7px;
+ overflow: hidden;
+}
+.ha-tools-row--compact .ha-tool-mode { font-size: 24px; }
+.ha-tools-row--compact .ha-tool { height: 34px; padding: 0 10px; gap: 6px; border-radius: 9px; font-size: 12px; }
+.ha-tools-row--compact .ha-tool-icon { font-size: 15px; }
+.ha-tools-row--compact .ha-history-actions { gap: 8px; }
+.ha-tools-row--compact .ha-history-actions button { font-size: 18px; }
+
+.dashboard-body { display: flex; flex: 1 1 auto; min-height: 0; overflow: hidden; }
+
+.plan-toolbar {
+ position: relative;
+ z-index: 4;
+ display: flex;
+ align-items: center;
+ height: var(--toolbar-height);
+ padding: 0 10px;
+ gap: 8px;
+ border-bottom: 1px solid #ededed;
+ background: #fff;
+ white-space: nowrap;
+ font-size: 11px;
+}
+.plan-title { display: flex; align-items: center; gap: 5px; font-size: 12px; }
+.home-mark { color: var(--blue); font-size: 17px; }
+.floor-tabs, .editor-tabs, .zoom-tools {
+ display: flex;
+ align-items: center;
+ height: 29px;
+ border-radius: 10px;
+ background: #f1f1f1;
+}
+.floor-tab { height: 29px; padding: 8px 12px; color: #606164; font-weight: 600; }
+.floor-tab small { color: #999; }
+.floor-tab.active { margin: 2px; height: 25px; padding-top: 6px; border-radius: 6px; color: #fff; background: var(--blue); }
+.add-floor { padding: 0 9px; color: #777; font-size: 16px; }
+.editor-tabs { gap: 18px; padding: 0 13px; color: #616164; font-weight: 600; }
+.device-count { color: #777; }
+.zoom-tools { margin-left: auto; border-radius: 8px; }
+.zoom-tools span { display: grid; place-items: center; width: 29px; height: 29px; font-size: 16px; }
+.settings-button { display: grid; place-items: center; width: 30px; height: 29px; border-radius: 8px; background: #f5f5f5; font-size: 15px; }
+
+.canvas {
+ position: relative;
+ flex: 1 1 auto;
+ min-width: 0;
+ height: auto;
+ overflow: hidden;
+ background: #d9ebf1;
+}
+.canvas::before {
+ position: absolute;
+ inset: 0;
+ content: "";
+ background:
+ radial-gradient(circle at 50% 45%, rgba(255,255,255,.34), transparent 37%),
+ linear-gradient(90deg, rgba(255,255,255,.08), transparent 12%, transparent 88%, rgba(255,255,255,.08));
+}
+.ambient-light { position: absolute; border-radius: 50%; filter: blur(20px); opacity: .6; }
+.light-one { right: 5%; top: 53%; width: 155px; height: 155px; background: rgba(255, 245, 145, .42); }
+.light-two { left: 39%; top: 5%; width: 70px; height: 70px; background: rgba(255, 236, 166, .22); }
+
+.floor-plan {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 700px;
+ height: 850px;
+ transform: translate(-46%, -49%) scale(var(--plan-scale, .94));
+ transform-origin: center;
+ color: #3d4143;
+}
+
+.room {
+ position: absolute;
+ border: 10px solid transparent;
+ background:
+ linear-gradient(var(--room-fill), var(--room-fill)) padding-box,
+ repeating-linear-gradient(135deg, #4f5355 0 2px, #9da2a4 2px 7px) border-box;
+}
+.room-name { position: absolute; font-size: 12px; font-weight: 700; color: #414548; white-space: nowrap; }
+.room-name sup { color: #666; font-size: 9px; }
+.room-name small { display: block; margin-top: 8px; font-size: 9px; color: #555; }
+
+.terrace {
+ left: 185px; top: 9px; width: 420px; height: 190px;
+ border: 10px solid #5d7c8a;
+ border-top-style: dashed;
+ background: rgba(175, 195, 204, .55);
+}
+.terrace h1 { margin: 42px 0 0; text-align: center; font-size: 44px; line-height: 1; }
+.kitchen { left: 12px; top: 170px; width: 590px; height: 285px; }
+.kitchen-name { right: 76px; top: 46px; font-size: 15px; text-align: left; }
+.kitchen-name small { margin-top: 8px; font-size: 11px; }
+.sauna { left: 12px; top: 445px; width: 122px; height: 150px; }
+.sauna .room-name { left: 7px; bottom: 17px; }
+.guest-wc { left: 123px; top: 445px; width: 145px; height: 150px; }
+.guest-wc .room-name { right: 9px; bottom: 17px; }
+.storage { left: 12px; top: 585px; width: 112px; height: 165px; }
+.storage-name { top: 7px; left: 6px; font-size: 9px; }
+.washroom { left: 113px; top: 585px; width: 155px; height: 165px; }
+.water-value { position: absolute; top: 57px; left: -17px; color: #4f879c; font-size: 12px; font-weight: 700; }
+.boiler { left: 155px; top: 668px; width: 113px; height: 82px; }
+.boiler .room-name { right: 4px; bottom: 17px; }
+.hallway { left: 258px; top: 445px; width: 193px; height: 305px; }
+.hallway-name { left: 0; top: 163px; }
+.stairs-closet { left: 440px; top: 365px; width: 250px; height: 165px; }
+.closet-name { right: 10px; bottom: 14px; font-size: 11px; }
+.guest-bedroom { left: 440px; top: 520px; width: 160px; height: 230px; }
+.bedroom-name { right: 7px; bottom: 17px; font-size: 11px; }
+.porch {
+ left: 12px; top: 740px; width: 440px; height: 102px;
+ border-color: #5d7c8a;
+ background: #b8aa9a;
+}
+.porch h2 { margin: 24px 0 0; text-align: center; color: #595d5f; font-size: 26px; }
+
+.door { position: absolute; width: 56px; height: 56px; border: 2px solid #555; border-left: 0; border-bottom: 0; border-radius: 0 56px 0 0; }
+.terrace-door { left: 41px; bottom: -11px; transform: rotate(-90deg); transform-origin: bottom left; }
+.kitchen-door { left: 235px; bottom: -11px; transform: rotate(90deg); transform-origin: bottom left; }
+.wc-door { right: -10px; top: 48px; transform: rotate(-90deg); }
+.wash-door { right: -10px; top: 68px; transform: rotate(-90deg); }
+.hall-door-left { left: -10px; top: 23px; transform: rotate(-90deg); }
+.hall-door-right { right: -10px; top: 58px; transform: rotate(-90deg); }
+.closet-door { left: 70px; bottom: -10px; transform: rotate(180deg); }
+.porch-door-one { left: 8px; top: -10px; transform: rotate(90deg); }
+.porch-door-two { right: 74px; top: -10px; transform: rotate(90deg); }
+.dashed-zone { position: absolute; left: 273px; top: 450px; width: 223px; height: 84px; border: 2px dashed #555; border-bottom: 0; }
+
+.device {
+ display: grid;
+ place-items: center;
+ width: 24px;
+ height: 24px;
+ border: 2px solid #9da3a7;
+ border-radius: 50%;
+ box-shadow: 0 1px 2px rgba(0,0,0,.22);
+ color: #111;
+ background: #fff;
+ font-size: 11px;
+ font-weight: 800;
+ line-height: 1;
+}
+.bulb { font-size: 10px; }
+.camera { font-size: 13px; }
+.sensor { width: 29px; font-size: 10px; }
+.thermometer { font-size: 13px; }
+.small-indicator { width: 14px; height: 14px; border: 0; color: #fff; background: #36c86c; font-size: 7px; box-shadow: 0 0 5px rgba(55,202,108,.6); }
+.device-row { position: absolute; display: flex; gap: 4px; }
+.device-column { position: absolute; display: flex; flex-direction: column; gap: 4px; }
+.top-left { left: 24px; top: 182px; }
+.d1 { left: 188px; top: 182px; }
+.d2 { left: 305px; top: 182px; }
+.d3 { left: 298px; top: 204px; }
+.d4 { left: 421px; top: 182px; }
+.d5 { left: 540px; top: 182px; }
+.online-1 { left: 271px; top: 184px; }
+.left-devices { left: 23px; top: 340px; }
+.right-devices { left: 565px; top: 202px; }
+.fan-row { position: absolute; left: 536px; top: 302px; display: flex; gap: 7px; }
+.temp-main { left: 138px; top: 411px; }
+.temp-icon { left: 183px; top: 411px; }
+.center-bulb-1 { left: 219px; top: 337px; }
+.center-bulb-2 { left: 324px; top: 446px; }
+.center-bulb-3 { left: 324px; top: 497px; }
+.center-bulb-4 { left: 324px; top: 548px; }
+.center-bulb-5 { left: 324px; top: 607px; }
+.center-bulb-6 { left: 324px; top: 660px; }
+.utility-devices { left: 23px; top: 611px; }
+.wash-devices { left: 128px; top: 622px; }
+.wash-camera { left: 158px; top: 592px; }
+.wash-percent { left: 151px; top: 633px; }
+.wash-warning { left: 187px; top: 634px; color: #fff; background: #ffae00; }
+.status-pill { position: absolute; padding: 5px 7px; border: 2px solid #fff; border-radius: 11px; color: #fff; background: #ffad00; font-size: 9px; font-weight: 700; box-shadow: 0 1px 2px rgba(0,0,0,.2); }
+.rinsing { left: 191px; top: 672px; }
+.boiler-warning { left: 123px; top: 711px; color: #fff; background: #ffae00; }
+.boiler-bulb { left: 143px; top: 692px; }
+.online-2 { left: 53px; top: 718px; }
+.online-3 { left: 325px; top: 718px; }
+.closet-camera { left: 622px; top: 386px; }
+.guest-temp { left: 514px; top: 525px; }
+.guest-thermo { left: 550px; top: 525px; }
+.guest-devices { left: 442px; top: 603px; }
+
+@media (max-height: 820px) {
+ .floor-plan { --plan-scale: .76; }
+}
+
+@media (min-height: 821px) and (max-height: 940px) {
+ .floor-plan { --plan-scale: .88; }
+}
+
+@media (min-height: 941px) {
+ .floor-plan { --plan-scale: 1; }
+}
+
+@media (max-width: 1450px) {
+ :root { --sidebar-width: 178px; }
+ .ha-context-row--compact .ha-segment { padding-inline: 7px; font-size: 10px; }
+ .ha-tools-row--compact .ha-tool { padding-inline: 7px; font-size: 11px; }
+ .floor-plan { transform: translate(-44%, -49%) scale(var(--plan-scale, .85)); }
+}
+
+@media (max-width: 1220px) {
+ .ha-context-row--compact .ha-editor-switcher .ha-segment { padding-inline: 5px; font-size: 9px; }
+ .ha-tools-row--compact .ha-tool { padding-inline: 5px; gap: 3px; font-size: 10px; }
+}
+
diff --git a/docs/design/505-summary-panel/reference/svg/chevron-down.svg b/docs/design/505-summary-panel/reference/svg/chevron-down.svg
new file mode 100644
index 00000000..37fe8e2d
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/svg/chevron-down.svg
@@ -0,0 +1 @@
+
diff --git a/docs/design/505-summary-panel/reference/svg/eye-off.svg b/docs/design/505-summary-panel/reference/svg/eye-off.svg
new file mode 100644
index 00000000..000f413e
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/svg/eye-off.svg
@@ -0,0 +1 @@
+
diff --git a/docs/design/505-summary-panel/reference/svg/eye.svg b/docs/design/505-summary-panel/reference/svg/eye.svg
new file mode 100644
index 00000000..41cf11ca
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/svg/eye.svg
@@ -0,0 +1 @@
+
diff --git a/docs/design/505-summary-panel/reference/svg/layout-sidebar-right.svg b/docs/design/505-summary-panel/reference/svg/layout-sidebar-right.svg
new file mode 100644
index 00000000..d43c300d
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/svg/layout-sidebar-right.svg
@@ -0,0 +1 @@
+
diff --git a/docs/design/505-summary-panel/reference/svg/plus.svg b/docs/design/505-summary-panel/reference/svg/plus.svg
new file mode 100644
index 00000000..d6aae34f
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/svg/plus.svg
@@ -0,0 +1 @@
+
diff --git a/docs/design/505-summary-panel/reference/svg/settings.svg b/docs/design/505-summary-panel/reference/svg/settings.svg
new file mode 100644
index 00000000..34c7acf2
--- /dev/null
+++ b/docs/design/505-summary-panel/reference/svg/settings.svg
@@ -0,0 +1 @@
+
diff --git a/docs/specs/505-summary-panel-design-parity.md b/docs/specs/505-summary-panel-design-parity.md
new file mode 100644
index 00000000..61a52163
--- /dev/null
+++ b/docs/specs/505-summary-panel-design-parity.md
@@ -0,0 +1,250 @@
+# #505 — сводная панель: дизайнерский прототип и UX-исправления
+
+Issue: https://github.com/Matysh/houseplan-card/issues/505
+Task branch: `issue/505-summary-panel-polish`
+Baseline: `dev@abbca50ce732a87b59c8675d394f691738ea0961` (1.73.0-beta.7).
+Status: specification; implementation requires independent S4 green verdict.
+Owner decision: 2026-09-09, resume after scope extension with designer archive.
+
+## 1. Problem, value and scope
+
+The working #437 panel differs materially from the supplied Dashboard 5 design.
+This task fixes the whole visual composition, not just individual CSS defects:
+header controls, floating panel and settings dialog. Original four requests remain
+mandatory: animated exit, genuinely wide dialog, removal of screen-size controls,
+and disabling the mobile option while local show is off.
+
+Value: user 8/10 (readability, predictable layout and consistency with accepted
+design); product/maintenance 7/10 (one scoped implementation instead of competing
+UI conventions). Complexity 6/10, risk 5/10, P2, bug/polish.
+Full track: fails small's complexity/risk <=3 and introduces coordinated contracts
+on three surfaces including supported touch; not trivial.
+
+The summary remains read-only current information, per SCOPE's #437 exception.
+No arbitrary dashboard/cards/actions, new data source types, config migrations,
+camera/layout changes, new panel-placement setting or modification of HA chrome.
+
+## 2. Design authority and reproducible reference
+
+[Designer reference](../design/505-summary-panel/README.md), including original
+[index.html](../design/505-summary-panel/reference/index.html), CSS, JS and SVG.
+Archive received from owner: `макет.zip`, SHA-256
+`bc754c16c9d9fc79dbbf093fcf8e14b96f8826fa01d1f0c8eccf68ab95357e15`.
+Open index.html in a browser; press the last header button, then its adjacent gear.
+The final Dashboard 5 CSS rules override older demo rules in the same files.
+
+Priority: explicit owner requirements here > current House Plan data/safety/
+accessibility contracts > prototype visual and interaction reference > demo
+implementation details. Reference JS is not product code or process instructions.
+Do not copy demo HA chrome, hardcoded entities/states, demo storage or external
+navigation into House Plan.
+
+The intended visual structure is binding; it is not satisfied by retaining the
+old generic settings form and changing its width. Permitted adaptations are:
+HA theme colours, current font family, readable font sizes, 44px touch targets,
+mobile wrapping and existing hp-dialog close/focus/fullscreen conventions.
+No global component restyling. Save remains visible (disabled when unchanged),
+even if demo accidentally conceals it. Missing-source/loading/conflict messages
+use existing product semantics, not invented demo stale-time rules.
+
+### Visual target map (CSS px at normal text scale)
+
+| Surface | Reference | Product target / bounded adaptation |
+|---|---|---|
+| Header pair | Gear left, sidebar-right right; divider, radius 8; last group | Same icons, order and far-right placement after other View actions; touch area >=44x44, glyphs 18–22; no new buttons outside this pair |
+| Selected state | Only show/hide half filled accent | HA primary colour, theme-appropriate foreground; gear remains neutral; hover/focus/disabled unambiguous |
+| Floating shell | Inset 12; radius 12; shadow 0 8px 26px at ~16% | Same silhouette, border/shadow hierarchy; safe-area/kiosk control insets take precedence |
+| Panel width | Right intrinsic 280–420; bottom intrinsic min 360 | Right min(280, available) to min(420, available); bottom min(360, available), max available; never stretch merely to fill stage |
+| Panel header | Separate full-width surface, >=48 height, padding14 | Same; 14–16 text, can wrap long title rather than hide meaning |
+| Block area | Muted container padding10, gap9; white cards radius11 | Same two-level surfaces, header divider, rows with last divider removed; adapt colours for dark HA theme |
+| Values | Label left, stronger value right | Distinct columns, right alignment, min-width:0; readable 12–14 text, wrapping instead of horizontal overflow |
+| Settings shell | 780 wide, radius15, pale body, white header/footer | Target 920, capped by viewport; increased for >=44 controls/readable 14px fields; native and HA shell both sized, not inner min-width |
+| Settings body | General-settings card, then Blocks card; padding14–18 | Same cards and hierarchy, block count, nested editor blocks; no detached legacy size section |
+| Block header | Grip, square up/down, name, eye; scope beneath | Same order and grouping; scope all/one plus space selector; narrow rows wrap |
+| Value row | Grip, square up/down, label, source, remove | Same desktop reading order, aligned row; narrow stacks label/source without hiding controls |
+| Add/delete | Dashed add row; outlined delete-block footer | Plus icon, full-row add-value; add-block below list; delete-block separated from reorder |
+| Dialog footer | Separate surface/border, right-aligned Cancel/Save | Existing hp-dialog action slots, wraps within available width, no edge clipping |
+
+Use theme-scoped semantic variables backed by HA card/primary/secondary text,
+divider and accent colours. Do not bake the prototype cyan into an orange HA theme.
+Scope all selectors under summary classes or `hp-dialog[data-kind="summary"]`.
+No changes to other editor toolbars, panels or dialogs.
+
+## 3. Controls and panel behaviour
+
+The compound control is the last item in the normal View header action group.
+Settings (left) opens the existing shared/local-only dialog; show/hide (right)
+uses the designer sidebar-right outline SVG and changes only local show intent.
+It stays available for household users, independently of admin edit controls.
+Use matching designer gear, eye/eye-off, plus and chevron; semantic arrow/remove
+icons share stroke weight. SVG uses currentColor, aria-hidden, explicit viewBox,
+width/height; every button has a localized accessible name and focus indication.
+
+In kiosk the pair stays in the existing floating-controls container; the panel
+starts below the measured controls. Neither controls nor panel are in zoomwrap.
+No refit, camera updates, stage size changes or document scroll on show/hide.
+
+The existing resolver is authoritative: stage width >= height => right, otherwise
+bottom. This is not viewport orientation. Preserve safe insets, minimum usable
+height, max60% stage height for bottom and mobile/narrow visibility policy.
+A panel that cannot fit remains temporarily hidden without changing stored intent.
+Probe/minimum-height measurements must reflect the new title/block/row styles.
+
+All blocks are expanded. Shared visibility and all/one-space filters are unchanged.
+Separate panel heading remains outside vertically scrolling blocks. Values update
+live through existing lifecycle/cache/clock logic. Scroll position is not reset by
+unrelated HA state updates. Empty configured panel/space has an explanatory empty
+state within the same shell. Long titles/values/units/IDs and 10x20 rows stay bounded.
+
+## 4. Animation lifecycle
+
+Reference motion: opacity + 18px from/to the anchored edge, 190ms ease, both right
+and bottom. Bottom retains horizontal centering throughout; no camera animation.
+Separate effective visibility/local intent from transient DOM presentation.
+
+Presentation states: hidden -> entering -> visible -> exiting -> hidden.
+Normal local off starts exit from current visual progress, immediately inert and
+aria-hidden; removal only after opacity exit completion. Local on during exit
+reverses from current rendered progress; never rebuild at full opacity first.
+Last intent wins and only one panel DOM subtree exists.
+
+A completion callback is generation-scoped and cannot remove a newer panel.
+Use filtered transition completion plus a bounded safety cleanup (<=300ms after
+normal exit start) so cancelled transition events cannot leave a ghost.
+No timers/listeners survive disconnect, identity/config-host change or route exit.
+
+Special cases:
+- Reduced motion: no movement or delayed removal; changes to media preference
+ during a transition settle to the latest intent.
+- Editor entry, route leave/disconnect, unsupported config, or identity change:
+ synchronously remove presentation and cancel pending work; no stale private data.
+- Document hidden: settle latest intent without replaying a transition on return.
+- Resize/narrow/fits makes visible panel ineligible: settle hidden immediately
+ (do not draw beyond available bounds); eligible again may enter normally.
+- Right/bottom changes mid-transition: cancel old direction/completion and settle
+ latest effective state at new anchor; no stale transform or sudden old-location flash.
+- Config/space change while remaining eligible updates content in place, no full
+ panel exit/enter and no exposure of filtered-out blocks.
+- No animation modifies or persists `local.show` by itself.
+
+## 5. Settings interactions and persistence
+
+Keep existing shared draft/revision validation and atomic save/recovery contracts
+of #437/#490/#493. Name, block CRUD, value CRUD/source picker, reorder, visibility,
+scope, confirmations and errors must remain functional after markup changes.
+General settings appear before Blocks. Full editor shows panel name plus local
+show and mobile controls as caption/helper/switch rows. Show/mobile retain their
+current shared/local ownership (mobile is shared; show is per-user/per-card).
+
+Mobile checkbox has native disabled when `!dialog.localShow` (and while saving).
+Its value is not cleared. Reenable restores exact draft value. Master off/on,
+Save, Cancel/X/Escape, reopen and reload behave consistently for all four pairs.
+Do not optimistically persist either draft toggle while editing.
+Non-admin/kiosk/unsupported-config local-only view shows the explanatory hint and
+local show only; it must not reveal or mutate shared editor fields.
+
+Remove only the summary dialog's Sizes on this screen header, sliders and reset,
+in every locale and local-only mode. Do not remove independent View/kiosk scale
+settings elsewhere, normalized storage values or scale application. Retain the
+storage-unavailable warning where relevant to local show. Opening/unchanged Save,
+title-only edit, show/mobile edit and Cancel never rewrite unrelated scale values.
+
+Block visibility is an eye/eye-off button with aria-pressed and localized action
+name. Reordering keeps both drag handles and square up/down alternatives; touch
+and keyboard need not drag. First/last buttons disabled. Stable block/value IDs
+remain authoritative for picker ownership; deleting/reordering a block cannot
+retarget an open picker accidentally. Delete nonempty block confirmation remains.
+
+Picker stays lazy, searchable and bounded to existing 100 matches; one active
+picker only. Show friendly label and secondary entity ID (or system-source
+caption) in the source button with chevron. Do not regress list keyboard/Escape,
+focus return, missing old refs, 10k source indexing, validation or broken-space
+warnings. Native and HA modal keyboard trap/close/save-busy semantics remain.
+
+## 6. Responsiveness, a11y and localization
+
+Settings shell: native fallback `wide` plus scoped --hp-dialog-wide-width:920px;
+real HA width=medium plus scoped --ha-dialog-width-md:920px (verify actual pinned
+HA frontend contract). Avoid changing generic hp-dialog widths. Real HA's native
+fullscreen policy at <=450px width or <=500px height is permitted and retained.
+
+At <=800 available dialog width stack general grid and source row fields; at
+<=480 stack block header fields/scope, keep action groups usable, preserve DOM
+reading order. Use shrinkable grid/flex children and border-box; no min-width
+forcing content wider than shell. Vertical body scroll, fixed reachable footer.
+320px and 390px, landscape short-height, 200% text, long RU/DE/FR content must work.
+All interactive targets >=44x44. Functional visual clipping is not an acceptable
+substitute for wrapping; horizontal overflow hidden must not mask inaccessible
+fields/buttons. Input labels/accessible names remain associated after regrouping.
+
+Use existing summary, btn and kiosk keys where semantics match; new en+ru+de+fr:
+`summary.general_settings`, `summary.blocks`, `summary.blocks_hint`,
+`summary.show_local_hint`, `summary.show_mobile_hint`,
+`summary.hide_block`, `summary.show_block`, `summary.system_source`.
+Reuse localized summary.count if available, otherwise add `summary.block_count`
+with count/limit formatting. Remove `summary.sizes_title` only if no remaining
+consumer. No data IDs, raw labels or untranslated new user-facing UI.
+
+## 7. Implementation boundaries and performance
+
+Expected files: src/summary-panel-style.ts, summary-panel-editor.ts,
+summary-panel-runtime-loaded.ts; small icon/presentation helper(s) if needed;
+src/houseplan-card.ts only control order; locale dictionaries; summary tests/
+runtime tests; demo/smoke_summary_panel.mjs or focused companion smoke.
+No backend/schema/type-model changes required. Config namespace stays version1;
+compatibility registry/storage shapes do not change.
+Keep summary/editor lazily loaded. No framework, remote icon request or permanent
+animation RAF loop. Animate compositor opacity/transform, never dimensions.
+No baseline increases, new large eager chunks or broad style recalculation.
+Normal gate:small, unit, typecheck, bundle parity/budget, no-new-any and docs gates.
+Full release/golden/performance capture is not required for this scoped task.
+
+## 8. Acceptance and evidence
+
+| AC | Required result | Evidence |
+|---|---|---|
+| AC1 | Compound icons/order/placement/states match §2–3 in View and kiosk | smoke DOM/order/geometry + paired reference/product visual review |
+| AC2 | Shell/title/cards/rows/right values match design; right/bottom fit and live/empty states | smoke bounds/content updates + visual review |
+| AC3 | Show/hide at190ms, retained inert exit then removal, reversals, one subtree | real-browser smoke sampling opacity/transform and DOM over time |
+| AC4 | Reduced motion, resize/anchor change, narrow/fits, editor, hidden document, route/identity/disconnect cannot leave callbacks/ghosts | runtime tests + focused lifecycle browser smoke |
+| AC5 | Desktop modal genuinely wide in native and real HA, mobile/200% text no horizontal clipping, reachable footer | native smoke geometry + authentic HA-dialog visual/bounds evidence (stub alone insufficient) |
+| AC6 | General/Blocks cards, headers, source rows, eye/reorder/add/delete follow reference | paired visual review + smoke CRUD/reorder/visibility/source/confirm |
+| AC7 | Size controls absent; old scale values preserved for admin and local-only paths | runtime + smoke persisted storage comparison |
+| AC8 | Mobile disabled under master off, preserved values, four combinations save/cancel/reopen/reload | smoke actual controls/focus and persisted config/local intent |
+| AC9 | Permissions, unavailable/future schema, conflict/lost ACK, broken refs, stable picker and 100-row cap remain | existing runtime/unit/summary smoke including 10k entities |
+| AC10 | Themes/locales/44px targets/long content/keyboard and camera independence | smoke RU/EN/DE/FR + light/dark + 320/390/desktop, review |
+| AC11 | Docs/changelogs current, scoped CSS/lazy budgets/build parity preserved | docs gate, code review, gate:small |
+
+Visual evidence is mandatory: matched 1600x1000 reference and product captures of
+control+open right panel and settings, dark desktop, bottom portrait, kiosk; plus
+narrow native/real HA settings screenshots. Use equivalent default three values
+and one custom-source row. Inspect geometry, hierarchy, glyphs, columns and
+allowed adaptations separately from irrelevant background-plan pixels.
+Record dimensions and accepted differences; no claim of pixel-identical UI.
+
+These are task diagnostic images, not updates of accepted golden/docs baselines.
+Keep portable captures/evidence instructions and reference in task docs; if raster
+artifact publishing is needed use the existing Linux CI artifact path. Never
+accept Windows screenshots as new canonical baselines. Review must be able to
+reproduce the paired UI; automation passing alone is not visual acceptance.
+
+At least one targeted negative witness for expensive smoke: demonstrate
+AC3 fails if exit DOM retention is removed and AC5 fails if shell-wide sizing is
+removed. Run mutation-gate for the relevant smoke if classed costly; do not
+weaken gate assertions or update unrelated golden files to make it pass.
+
+## 9. Risks, rollback, release artifacts
+
+Main risks: fake parity from outer-only styling; real HA vs fallback sizing;
+stale animation callback; loss of local/shared ownership; unavailable references
+or picker retargeting; touch inaccessible controls after density adaptation.
+Sections4–8 provide explicit prevention/evidence. No unresolved product question;
+any deviation beyond §2's bounded adaptations returns to owner.
+
+Rollback is revert of task commits plus bundle rebuild/sync; data remains fully
+compatible with pre-task client because no format or ownership changes.
+Release artifacts: both changelogs with #505; USER-GUIDE.ru.md and .md summary
+section update control placement, layout, mobile dependency, removed size controls;
+spec index and visual acceptance report. No release/tag/public announcement in
+this task; stop at pipeline S8 (merged, waiting for next beta).
+
diff --git a/docs/specs/README.md b/docs/specs/README.md
index 2f1e2102..47841ef5 100644
--- a/docs/specs/README.md
+++ b/docs/specs/README.md
@@ -101,6 +101,7 @@
| Issue | ТЗ |
|---|---|
+| [#505](https://github.com/Matysh/houseplan-card/issues/505) Сводная панель: дизайнерский прототип и UX-исправления | [505-summary-panel-design-parity.md](505-summary-panel-design-parity.md) |
| [#493](https://github.com/Matysh/houseplan-card/issues/493) Ограниченный picker и надёжный lifecycle сводной панели | [493-summary-panel-hardening.md](493-summary-panel-hardening.md) |
| [#437](https://github.com/Matysh/houseplan-card/issues/437) Конфигурируемая сводная панель поверх плана | [437-summary-panel.md](437-summary-panel.md) |
| [#485](https://github.com/Matysh/houseplan-card/issues/485) Радары присутствия: общий контракт и три этапа | [Общий контракт](485-radar-presence.md) · [Этап 1](485-radar-presence-stage1.md) · [Этап 2](485-radar-presence-stage2.md) · [Этап 3](485-radar-presence-stage3.md) |