fix: polish device marker geometry and input

Issue: #213
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-08-20 11:00:22 +03:00
parent c749d68a4d
commit 0e5ee030fe
37 changed files with 1760 additions and 951 deletions
+16 -9
View File
@@ -220,8 +220,11 @@ mutable and warning-deduplicated.
## Sizes
`icon_size` in the config = **% of the visible plan area width** (default 2.5). Implementation:
`.stage { container-type: inline-size }` + sizes in `cqw`. Legacy px values (>8) are ignored.
`icon_size` in the config = **% of the visible plan area width** (default 2.5).
The surface boundary resolves this legacy public unit to the current effective
device base (`2.25` for the default) before the shared face sees it; the face
does not apply a late visual factor. Implementation: `.stage {
container-type: inline-size }` + sizes in `cqw`. Legacy px values (>8) are ignored.
## Sticky header
@@ -238,8 +241,11 @@ projection (sources, value, icon, classes, metrics and explanation),
`src/device-face.ts` renders one package-derived shell/core DOM on the full plan,
device preview and static space card. The saved coordinate remains the icon-core
centre; Text is shell-centred, while a Double shell extends around the anchored
core. The 101.5/80 shell/core ratio, Light/Dark context, full-text fitting and
44×44 interactive hit area are renderer facts rather than surface-specific DOM.
core. The 101.5/80 shell/core ratio, shared shell/core centre, Light/Dark
context, full-text fitting and 44×44 core-centred interaction floor are
renderer facts rather than surface-specific DOM. A positioned shell frame owns
the complete visible capsule hit area; its event bubbles to the marker's one
action path.
`badge` shows the icon/morph and semantic core; `icon_ripple` additionally shows three finite
event waves or one continuous wave for presence, mechanical transition and actual work;
`value` replaces the icon
@@ -256,9 +262,9 @@ suppresses legacy output. Bottom badges stack above system LQI, and a derived
LQI badge de-duplicates that system row. `hp-device-preview` fits and centres
the complete face bounding box rather than allowing satellites to clip.
`normalizeDeviceDisplay()` is the mandatory compatibility gate for every consumer and maps
legacy `ripple` to `icon_ripple`. `markerLqiBand()` / `markerLqiColor()` are
deliberately marker-only (`<=40` red, `41…179` amber, `>=180` green); room fill
keeps the continuous `logic.ts::lqiColor()` gradient. The marker dialog builds
legacy `ripple` to `icon_ripple`. `markerLqiBand()` remains marker-only semantic
metadata for accessibility, while `markerLqiColor()` delegates to the shared
continuous `logic.ts::lqiColor()` red-to-green scale. The marker dialog builds
its unsaved draft through `buildDevices`,
then `hp-device-preview` shows the actual projection, integration provenance from
registry/config-entry metadata and isolated short/continuous activity demonstrations.
@@ -548,8 +554,9 @@ are two casement leaves. A gate has the same data/light/contact/lock semantics a
uses two half-width leaves opening only 10° toward the exterior face and no large swing arc.
Its default width in the editor is 300 cm. `openingAmount` (pure) maps the contact state to
0..1: no sensor → door/gate drawn open / window closed (static-plan convention);
`unavailable`/`unknown` freeze that default. The lock renders as an HTML padlock badge
(`.oplock`) in the device layer; a lock is
`unavailable`/`unknown` freeze that default. The lock renders as a compact
package-derived shell/core HTML padlock badge (`.oplock`) in the device layer,
with theme-aware locked/unlocked/unknown states; a lock is
**never** toggled from the plan (`resolveToggleIntent` returns a secure no-op). View-mode UX: hover outline,
drag along walls (continuous re-snap, saved on release), click → status card (250 ms timer),
double click → properties dialog. In markup mode the "Opening" tool handles clicks instead.
+8
View File
@@ -2,6 +2,14 @@
## Unreleased
- Device marker cores now stay concentric with their outer shell at fractional
sizes and common DPRs. Their current footprint is preserved without a late
`0.9` visual factor, while the MDI glyph is 10% larger. A value capsule owns
hover and the configured action across its complete visible area; opening
lock badges follow the compact light/dark Lock/Unlock artwork; and marker
LQI colours again move continuously from red to green
([#213](https://github.com/Matysh/houseplan-card/issues/213)).
## v1.65.0-beta.8 — 2026-08-20
- Device markers are now 10% more compact without moving their saved centres or
+8
View File
@@ -8,6 +8,14 @@
## Не выпущено
- Core маркера устройства теперь остаётся соосным внешнему shell при дробных
размерах и распространённых DPR. Текущий размер сохранён без позднего
коэффициента `0.9`, а MDI-глиф увеличен на 10%. Вся видимая капсула со
значением получает единые hover и выбранное действие; компактные значки
замков проёмов соответствуют светлым/тёмным Lock/Unlock макетам; цвет LQI
маркера снова плавно меняется от красного к зелёному
([#213](https://github.com/Matysh/houseplan-card/issues/213)).
## v1.65.0-beta.8 — 2026-08-20
- Маркеры устройств стали на 10% компактнее без сдвига сохранённого центра и
+18 -1
View File
@@ -375,6 +375,22 @@ owner's amber Dark Unlock override are recorded in that directory's README.
covered by `smoke_device_preview_parity`, `smoke_static_icon` and
`smoke_disabled_device` after a fresh production build.
## Device marker geometry and input polish (#213)
- [ ] `node demo/smoke_device_icon_pixel_alignment.mjs` covers core bases
24…112 CSS px in quarter-pixel steps at DPR 1/1.25/1.5/2. DOM centres,
isolated painted centroids/support and a deliberate 1 CSS px mutant must
distinguish browser raster parity from a persistent offset.
- [ ] `node demo/smoke_device_icon_design.mjs` keeps the effective 32/56/96
geometry, uses the direct 0.55 MDI/core ratio and proves hover plus the
configured action from the far value-capsule end at right/bottom/left/top.
- [ ] Opening binding/registry-less/lock-action smokes preserve the secure
no-toggle-on-plan invariant while checking compact Light/Dark
locked/unlocked/unknown shell/core presentation.
- [ ] Unit presentation coverage compares marker LQI colour with the shared
continuous `lqiColor()` across former 40/41 and 179/180 boundaries; bands
remain semantic metadata only.
## Touch support and release gates
The product contract is defined in `docs/TOUCH-SUPPORT.md`:
@@ -1593,7 +1609,8 @@ require hands on real hardware — they remain for the human pass.
- [ ] Bind a contact sensor: open → leaf swings and the arc draws on in the accent colour;
closed → leaf lies along the wall, arc hidden; invert flips this
- [ ] Sensor unavailable → the opening freezes at its static default (door open / window closed)
- [ ] A door with a lock shows the padlock badge: green locked / orange unlocked / grey unknown
- [ ] A door with a lock shows the package padlock badge: black/dark locked,
amber unlocked and neutral unknown in both themes
- [ ] A gate defaults to 300 cm, has two equal leaves, no swing arc and opens
10° outwards; contact, inversion, lock, drag and resize anchoring match a door
- [ ] Clicking an opening (or the padlock) in view mode opens the info card with both states;
+8 -4
View File
@@ -310,8 +310,9 @@ near-end openings remain visible and are not moved until their geometry is
edited.
An opening may bind a contact; doors and gates may also bind a lock. View paints
the moving leaf and state. A lock badge is green when locked and orange when
unlocked. A plan tap never toggles a lock. The opening card provides a labelled
the moving leaf and state. A lock badge uses the package's black/dark locked
state and amber unlocked state. A plan tap never toggles a lock. The opening
card provides a labelled
lock/unlock control, with confirmation before unlocking.
Openings may slide along joined wall corners. A double click in Plan opens
@@ -435,14 +436,17 @@ state; and always-static icon. A separate value badge can show an entity state,
useful attribute, average LQI or linked light state on any side of the marker.
Text and adjacent values are sections of the same shell. They shrink to a
readable floor and then expand the shell; they are never ellipsized.
The complete visible value capsule is one hover and action target: clicking or
tapping its value section runs exactly the same configured action and safety
checks as the icon core.
Virtual devices use the ordinary neutral/hover background with a dashed outer
circle. An HA-less virtual device does not invent unavailable or activity;
a linked virtual light may still follow its real controller. Unavailable keeps the ordinary
presentation with the standard icon opacity reduction, no visual hover and no
motion; its existing click/tap still opens information or settings. Marker LQI
is red at 0–40, amber at 41–179 and green at 180+, without changing the room
fill gradient.
uses the same continuous red-to-green scale as before the package update; the
room fill gradient and the displayed number are unchanged.
Interactive View/kiosk and Device-editor markers have at least a 44×44 CSS px
target. Enter and Space reuse the exact current click and confirmation path;
+12 -8
View File
@@ -523,8 +523,8 @@ YAML-сущность без `unique_id` и строки в Entity Registry: п
### Замок
- `locked` показывается как закрытый замок;
- `unlocked`/`open` — как открытый, с оранжевым состоянием;
- `locked` показывается как закрытый чёрный/тёмный замок из дизайн-пакета;
- `unlocked`/`open` — как открытый, с жёлтой подложкой дизайн-пакета;
- неизвестное состояние получает нейтральное/неизвестное оформление;
- клик по значку открывает карточку проёма;
- закрытие выполняется без дополнительного подтверждения;
@@ -558,9 +558,9 @@ House Plan читает реестры устройств, сущностей и
После первичного заполнения новые устройства отмечаются красной точкой. Точка исчезает после первого открытия маркера в редакторе устройств.
Визуальная часть маркера рисуется на 10% компактнее выбранного базового и
индивидуального размера; сохранённая точка остаётся его центром, а область
нажатия не уменьшается. У режима **Значение** внутренняя подложка числа имеет
Текущий компактный размер маркера сохраняется напрямую в его базовой геометрии,
без дополнительного уменьшения уже рассчитанного результата; сохранённая точка
остаётся центром, а область нажатия не уменьшается. У режима **Значение** внутренняя подложка числа имеет
ровные полукруглые торцы и одинаковый отступ внутри внешнего shell.
### Виды привязки
@@ -833,6 +833,10 @@ Hover включается только после события от наст
другим молча — отображается `—`, а редактор позволяет сохранить связь или
выбрать новую.
Вся видимая внешняя капсула является одной областью наведения и действия:
клик или тап по секции значения выполняет то же выбранное действие с теми же
подтверждениями и ограничениями безопасности, что и нажатие по core иконки.
Для старых маркеров без этой настройки прежние автоматические °/% сохраняются
как дополнительная секция того же shell.
Явно выключенный бейдж подавляет эту legacy-эвристику. Явный бейдж не зависит
@@ -850,9 +854,9 @@ Glow и заливка «Свет» продолжают работать по
настройкам устройства. Для привязок, способных сообщать о тревоге, редактор
показывает неблокирующее предупреждение перед сохранением этого режима.
Число Zigbee LQI под маркером имеет три фиксированных диапазона: **0–40 —
красный, 41–179 — жёлтый, 180 и выше — зелёный**. Это не меняет плавный
градиент LQI-заливки комнаты.
Цвет числа Zigbee LQI под маркером плавно меняется от красного при слабом
сигнале к зелёному при сильном. Числовое значение и градиент LQI-заливки
комнаты не меняются.
В Просмотре/киоске и редакторе устройств интерактивный маркер получает
клавиатурный фокус и область действия не меньше 44×44 CSS px. Enter и Space
Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

+17 -17
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"fixture": "synthetic-only",
"sourceFingerprint": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"sourceFingerprint": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"captureScriptSha256": "34f2219790d46efd8250e7a1bd829cb8fc0b0547e1260635fefa52407551b41b",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -13,8 +13,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"imageSha256": "8780c6f05a9f2af55b97512dfa8d277bb77ab9ae0dafb50640cc3237028055dc"
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "2885f96e348b15ab7c696e56e99bddcd9bb2ee94218a883e5a2155f45f5042aa"
},
"view-touch": {
"file": "02-view-touch.png",
@@ -24,8 +24,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"imageSha256": "5e41c81ded9eb67deea49840c260eaaa404cab61abf014a1a275586dafe66e53"
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "f62d8af3617c00a5e99511bd765980d2e27badf25047a1be34046b64195abe6c"
},
"space-create": {
"file": "03-space-create.png",
@@ -35,7 +35,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "c53db2e5c642a5549c13f3c93a5b359fed69bdb2621bf71a243a877ffcb95e6b"
},
"room-contour-close": {
@@ -46,7 +46,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "2f13ea645af306eee9cc7c52699c928377b72f412b08c1a3db6beb848ace1b51"
},
"plan-context-tray": {
@@ -57,7 +57,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "a6c526fede11bc3503fd2384bd4a3f6afa008f481c5c47578f06cae6c81c6f9b"
},
"device-editor": {
@@ -68,8 +68,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"imageSha256": "bfba4647c04bdf4b46dc9cea3b42e349b3ea67007bb38e16880983dbee14653e"
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "9585b59add4d35b5a6f028ce5b720ef1b77a3f8b19436d192cd1a0e6fc637264"
},
"device-display-preview": {
"file": "06-device-display-preview.png",
@@ -79,8 +79,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"imageSha256": "8cdfe00ead5558d013265617104997fdd138337322aa82f6a3e1d15eeb36cf5e"
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "cfc317da4628d079a116ff71311fbf06b1eb3b181928a5ea7ba888ab936a5e8b"
},
"background-editor": {
"file": "07-background-editor.png",
@@ -90,7 +90,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "d7cfe70551d9260169df8efd832e32ed7df99c7f60b55d1fd4a8322bd4c47175"
},
"room-card": {
@@ -101,8 +101,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"imageSha256": "ab29f6fdb31d3117e935a93a237105d42a5103687ac892ef426a4887ae7960c4"
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
},
"device-info": {
"file": "09-device-info.png",
@@ -112,8 +112,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "d5eb5e11c38b54fee7310db684182bb1f6280b493ee68588c33143a5c9c511a2",
"imageSha256": "30bf3f2a1311fc2bffeac6359773925fea22150df2845015c630e009016f095f"
"sourceSha256": "9da382c4687982ba911ec847e146c6730971fe79a77c6edc044512a91144306a",
"imageSha256": "a06cbf83f09e2f67b3566d7c0b10e973db060c3d20786f26f74ada6a21937c3e"
}
}
}
+34 -22
View File
@@ -26,7 +26,7 @@
капсулы не включает hover. После изменения все концентрические слои остаются
визуально по центру, фактический размер маркера не меняется, глиф становится на
10% крупнее, LQI снова использует плавную шкалу, дверной замок соответствует
#179, а вся капсула является mouse-hover областью.
#179, а вся капсула является общей областью hover и действия.
## 3. Проблема и подтверждённые причины
@@ -83,7 +83,7 @@ No-Blur fallback; owner override Dark Unlock остаётся amber.
3. Увеличить базовый MDI viewport на 10% без отдельного glyph multiplier.
4. Перевести opening Lock/Unlock на принятую визуальную систему #179 без
изменения security/action contract.
5. Сделать всю видимую value/legacy capsule mouse-hover областью.
5. Сделать всю видимую value/legacy capsule общей областью hover и действия.
6. Вернуть marker LQI прежнюю плавную цветовую шкалу, меняя только цвет.
## 6. Scope
@@ -112,7 +112,6 @@ No-Blur fallback; owner override Dark Unlock остаётся amber.
значения LQI;
- изменение low/mid/high категорий в aria-label/data attributes;
- изменение непрерывной комнатной LQI-заливки;
- расширение либо сокращение click/tap/action области value capsule;
- новый hover на touch/pen, sticky hover или новая touch-editor parity;
- изменение lock service calls, подтверждения Unlock либо secure invariant;
- backdrop-filter на marker/opening lock;
@@ -192,8 +191,12 @@ layout без per-size special cases. Запрещены hardcoded `translateX/Y
- DOM-centres core и его shell-end отличаются не более чем на один browser
layout quantum;
- alpha/color-weighted painted centroids расходятся менее чем на 0.5 физического
пикселя по каждой оси;
- на изолированных solid diagnostic слоях alpha/color-weighted centroid
расходится не более чем на половину CSS-пикселя плюс один anti-alias quantum
(`DPR / 2 + 0.1` физического пикселя), а центр дискретного painted support —
не более чем на один физический raster pixel; это откалиброванные пределы
смены чётности диаметров, а не допуск устойчивого смещения на один
CSS-пиксель;
- противоположные видимые inset не дают устойчивой однопиксельной ступени;
- проверка умеет покраснеть на v1.65.0-beta.8 либо на документированном mutant,
восстанавливающем прежний layout.
@@ -229,7 +232,7 @@ Unknown/unavailable сохраняет нейтральный `lock-question` с
маркеров устройств не получают нового lock action. Скрытый/disabled/missing
exact opening reference ведёт себя по прежним #117 правилам.
## 11. Hover всей внешней capsule
## 11. Hover и действие всей внешней capsule
Для `.device-shell.with-values` видимая border-box capsule целиком является
mouse-hover областью, включая:
@@ -241,15 +244,17 @@ mouse-hover областью, включая:
- все четыре направления right/bottom/left/top.
Над любой точкой capsule активируются те же visual hover, z-index и tooltip,
что над core. Hover остаётся instance-local и разрешён только canonical
что над core. Click/tap, long press и действие marker также работают по всей
видимой capsule, включая value/legacy sections и промежутки; используется тот
же action, confirmation и security contract, что у core. Hover остаётся
instance-local и разрешён только canonical
`data-pointer-hover` после настоящей fine mouse. Touch/pen и compatibility mouse
не включают и не оставляют hover; unavailable по-прежнему не получает visual
hover.
Расширение касается hover/tooltip. Click/tap/action hit contract и 44×44 core
target не расширяются скрыто на value sections. Реализация обязана отделить
hover ownership от action ownership, если browser bubbling иначе превратил бы
всю capsule в новую click target.
Минимальный core-centred target 44×44 сохраняется, но больше не является
единственной action-областью для вытянутого marker. Keyboard path остаётся на
самом marker и выполняет то же действие независимо от наличия sections.
Preview воспроизводит визуальный hover при переданном mouse gate, но остаётся
неинтерактивным. Static plan marker остаётся read-only; его footer button не
@@ -322,7 +327,10 @@ opening exact lock state
real mouse modality
→ full capsule hover owner
→ visual hover + tooltip; action owner remains unchanged
→ visual hover + tooltip
full capsule pointer target
→ existing marker action / confirmation / long press
```
Ожидаемые зоны изменений:
@@ -388,9 +396,10 @@ real mouse modality
capsule включает один mouse visual hover/z-index/tooltip; unavailable,
touch/pen и compatibility mouse не включают его. **Доказательство:** real
PointerEvent desktop/hybrid browser smoke + computed styles.
8. **AC8 — action boundary.** Value/legacy section не становится новым click/
tap action target; core/44×44 action semantics, confirmation и gesture safety
прежние. **Доказательство:** pointer target/action-count smoke.
8. **AC8 — action всей capsule.** Click/tap по value/legacy section и
промежутку выполняет то же marker action, что core; confirmation, secure
invariant и gesture safety сохраняются. Core-centred target остаётся не
меньше 44×44. **Доказательство:** four-side pointer target/action-count smoke.
9. **AC9 — continuous marker LQI.** Marker color совпадает с `lqiColor()` на
representative и boundary-adjacent values; 40/41 и 179/180 не используют
дискретные palette jumps. **Доказательство:** pure boundary/dense-range tests
@@ -476,7 +485,7 @@ Linux CI artifact. Перед beta выполняются full smoke, golden и
| Pixel тест окажется привязан к платформе | DOM invariant + alpha centroid; golden canon только Linux |
| Больший glyph клиппится у сложного MDI path | representative custom/state-swapped matrix на min size |
| Opening lock станет слишком крупным | сохраняется текущий compact footprint, меняются внутренние ratios |
| Hover capsule расширит click target | разные hover/action owners и action-count smoke |
| Capsule даст двойной action через bubbling | единый handler на marker и action-count smoke |
| Continuous marker LQI затронет room fill | reuse `lqiColor`, отдельный marker projection и room regression |
| Refactor добавит layout/compositor cost | CSS-only geometry, no observers/blur, pre-beta performance |
@@ -511,7 +520,7 @@ beta; stable — только после promotion rule.
2. Перенести current effective sizes на единую base-resolution boundary и
удалить late factor из face/puck.
3. Перестроить concentric shell/core tracks и увеличить direct glyph base.
4. Подключить full-capsule hover с отдельным action owner.
4. Подключить full-capsule hover и существующий marker action к единому target.
5. Вернуть continuous marker LQI color при неизменном band/a11y.
6. Перевести opening lock на #179 tokens/geometry, сохранив compact footprint и
security path.
@@ -528,15 +537,18 @@ beta; stable — только после promotion rule.
его внутренние proportions/tokens, а не новый абсолютный размер.
2. Unknown opening lock использует package shell/default theme и текущий
`lock-question`, потому что архив не содержит отдельного Unknown asset.
3. Запрос владельца расширяет именно hover/tooltip value capsule; click/tap
target остаётся прежним, так как отдельного решения расширить действие нет.
3. По уточнению владельца во время реализации вся видимая capsule является и
hover-, и action-target; отдельного hover-only слоя нет.
4. Low/mid/high band остаётся только semantic/a11y facet; цвет независимо
разрешается общей continuous `lqiColor()`.
5. Base compatibility может нормализоваться один раз на границе surface size,
но внутри `.dev`, puck и face запрещён любой поздний 0.9-equivalent factor.
6. Pixel-centroid smoke использует controlled solid background и color/alpha
segmentation; точные thresholds калибруются failing-before-fix/mutant и не
становятся browser-vendor whitelist.
6. Pixel-centroid smoke использует controlled solid background, изолированные
solid shell/core слои и color/alpha segmentation. Совместный тонкий annulus
не используется для centroid: вычитание большой core-площади усиливает
обычный edge-AA. Предел weighted-centroid `DPR / 2 + 0.1` и предел painted
support в один физический raster pixel получены на матрице DPR и дополняются
нулевым DOM-center delta и mutant на 1 CSS px; это не browser whitelist.
7. Общий concentric layout предпочтительнее размера, округлённого через CSS
`round()`: последний зависит от поддержки WebView и не решает shared-center
invariant сам по себе.