Волна 5 эпика #674, перенос ТЗ (класс C). Из 240 файлов `docs/specs/` в `legacy/specs/` уехали 219: на них не ссылается ни один живой файл (код, тесты, скрипты, workflow, документы вне архива и ревью). Остались 21 ТЗ — на которые ссылаются код, ADR, ISOMETRIC, SUN, RADAR, LIGHT (`docs/specs/067`), DECOR-EDITOR, support-relay, и те, на которые ссылаются они сами; README каталога объясняет, где искать остальное. Открытых issue с файлом ТЗ среди перенесённых нет. Относительные ссылки перенесённых файлов переписаны (`../X` → `../../docs/X`, соседние оставшиеся ТЗ → `../../docs/specs/…`) — все 26 резолвятся. Попутно: битая ссылка в `089-isometric-view-stage1.md:8` на удалённый `089-isometric-view.md` — теперь команда `git show` по истории. Строка в `legacy/README.md`. Issue: #682 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
34 KiB
Issue #213 — геометрия, размеры, LQI и hover маркеров устройств
- Issue: https://github.com/Matysh/houseplan-card/issues/213
- Связанные завершённые задачи: #179, #211, #212
- Нормативный дизайн-пакет: архив #179, версия 1.1.1
- Тип: bug / polish, обычный трек
- Пользовательское изменение: да
- Touch editor: best effort / intentionally degraded
1. Сценарий и персона
Персона: домочадец или гость, который читает состояние дома в View/kiosk, и администратор, который проверяет те же маркеры в редакторе устройств.
Сценарий: пользователь меняет масштаб плана либо открывает House Plan на экране с другим DPR и ожидает, что круг, внешний shell и MDI-глиф останутся визуально соосными. Маркер со значением должен реагировать на мышь по всей видимой капсуле, LQI — снова менять цвет плавно, а замок рядом с дверью — выглядеть как Lock/Unlock из принятого пакета #179.
2. Что человек увидит до и после
До изменения на отдельных масштабах внутренний элемент выглядит сдвинутым на один пиксель относительно кольца, MDI-глиф маловат, дверной замок живёт в старой визуальной системе, LQI перескакивает между тремя цветами, а часть видимой капсулы не включает hover. После изменения все концентрические слои остаются визуально по центру, фактический размер маркера не меняется, глиф становится на 10% крупнее, LQI снова использует плавную шкалу, дверной замок соответствует #179, а вся капсула является общей областью hover и действия.
3. Проблема и подтверждённые причины
Текущий renderer после #212 вычисляет core так:
resolved card size × per-marker scale × --device-visual-factor(0.9)
Затем shell, border и padding рассчитываются отдельными дробными выражениями.
Flex layout позиционирует core через независимо округлённые shell/padding/border.
На проверенной матрице браузерных размеров геометрические центры отличаются на
layout quantum; на отдельных scale/DPR антиалиасинг превращает его в заметную
асимметрию на один физический пиксель. Постоянный translate(1px) исправил бы
только один случай и сломал соседние размеры.
Дополнительно подтверждено:
- общий
--device-visual-factor: 0.9остаётся отдельным поздним множителем; - MDI viewport равен
0.5 × core; - opening lock использует старый компактный круг, зелёный locked и не использует shell/core Lock/Unlock пакета #179;
.device-shellне владеет pointer hit testing, а круглаяdev::beforeобласть не покрывает вытянутую частьwith-values;markerLqiColor()после #179 возвращает три фиксированных цвета, хотя общийlqiColor()уже содержит прежний непрерывный red→green HSL-gradient.
Текущий smoke проверяет отношения размеров на 32/56/96 px, но не прогоняет дробную матрицу, несколько DPR и pixel-centroid симметрию.
4. Нормативные источники и приоритет
При расхождении применяются:
- решения владельца в теле #213 и последующих комментариях;
- это ТЗ после зелёного SPEC-REVIEW;
- дизайн-пакет #179 версии 1.1.1 и принятые уточнения #211;
- контракты #212 для фактического размера, 44×44 hit area и pointer modality;
docs/SCOPE.md,docs/TOUCH-SUPPORT.md,docs/UX-MODES.mdи пользовательское руководство;- текущая реализация как compatibility baseline там, где источники выше ничего не меняют.
SVG Lock/Unlock из #179 нормативны по форме, отношению shell/core, глифу, цветам, stroke и shadows. Как и в #179/#211, production использует динамический DOM/MDI, а не включает reference SVG в runtime bundle. Для Dark применяется принятый No-Blur fallback; owner override Dark Unlock остаётся amber.
5. Цели
- Убрать scale-зависимое визуальное расхождение центров shell/core/glyph.
- Удалить отдельный поздний коэффициент
0.9, сохранив текущие фактические размеры всех существующих настроек и поверхностей. - Увеличить базовый MDI viewport на 10% без отдельного glyph multiplier.
- Перевести opening Lock/Unlock на принятую визуальную систему #179 без изменения security/action contract.
- Сделать всю видимую value/legacy capsule общей областью hover и действия.
- Вернуть marker LQI прежнюю плавную цветовую шкалу, меняя только цвет.
6. Scope
В задачу входят:
- общий device face в full View/kiosk, Device editor/preview и
houseplan-space-card; - base-size resolution и fallback/default bases, включая vacuum puck;
- круглый Icon, основной Text и Double/value/legacy sections;
- MDI glyph viewport для динамических и пользовательских MDI-иконок;
- opening lock badge в View и его light/dark/locked/unlocked/unknown projection;
- marker-only LQI color;
- instance-local mouse hover для полной внешней капсулы;
- unit/source-contract, browser smoke, visual/golden и performance evidence;
- пользовательская, архитектурная и testing-документация.
7. Не входит в задачу
- изменение сохранённых координат,
icon_size, per-markersize, angle, display/value/action либо backend schema; - изменение фактического внешнего размера marker относительно v1.65.0-beta.8;
- изменение размера core/shell из-за увеличения MDI-глифа;
- новые пользовательские scale, LQI или hover настройки;
- изменение источника, агрегации, форматирования, доступности либо числового значения LQI;
- изменение low/mid/high категорий в aria-label/data attributes;
- изменение непрерывной комнатной LQI-заливки;
- новый hover на touch/pen, sticky hover или новая touch-editor parity;
- изменение lock service calls, подтверждения Unlock либо secure invariant;
- backdrop-filter на marker/opening lock;
- принятие golden baseline на Windows или выпуск без команды владельца.
8. Контракт размеров
8.1. Удаление позднего 0.9
После изменения .dev и vacuum puck не содержат
--device-visual-factor, эквивалентного post-resolution multiplier либо цепочки
resolved × 0.9 внутри face geometry.
Размер, переданный общему face как base, уже является текущим effective base. Для v1.65.0-beta.8 reference:
new effective core = current effective core
new effective shell = current effective shell
new effective puck = current effective puck
Это выполняется для default card, явного icon_size, kiosk multiplier,
per-marker size, preview и static card. Числовые пользовательские значения и
их относительные отношения не меняются. Нормализация legacy public size units,
если она нужна для совместимости, принадлежит единой границе base resolution;
внутри face нет второго коэффициента.
Base constants меняются на текущие effective values: fallback full/static,
preview и vacuum используют уже уменьшенные значения. Запрещено сохранять
старые bases и маскировать их новым переименованным 0.9 в CSS.
8.2. MDI-глиф
Базовый viewport MDI меняется с 0.50 × core на 0.55 × core. Это изменение
самого base ratio, без --glyph-factor, вложенного scale(1.1) либо второй
трансформации.
Правило одинаково для default, custom MDI и state-swapped glyph; marker angle по-прежнему вращает глиф вокруг центра. Core, shell, anchor, pulse и hit area от увеличения glyph не меняются. Reference/runtime matrix должна подтверждать, что глиф не клиппится на минимальном размере.
8.3. Совместимость
- storage/card config не переписывается;
- Open → Save не материализует новые поля или bases;
- default и явно настроенные marker sizes сохраняют текущий pixel result с допуском layout quantum;
- relative ratio между двумя разными per-marker size остаётся прежним;
- координата остаётся центром icon core;
- минимальная интерактивная область остаётся не меньше 44×44 CSS px.
9. Контракт соосности и растеризации
9.1. Геометрия
Shell и core круглого marker строятся от одной координаты центра. Позиция core не выводится из независимо округлённой суммы left/top padding и border.
Для Text/Double/value/legacy shell:
- сохранённая точка остаётся центром icon core;
- прямые стороны и полукруглый торец сохраняют inset пакета;
- core и соответствующий ему круглый конец внешней capsule имеют общий центр;
- расширение секций идёт только в выбранную сторону;
- смена стороны не меняет anchor.
Допустимы CSS grid, явные tracks, общий concentric pseudo-layer либо другой
layout без per-size special cases. Запрещены hardcoded translateX/Y(1px),
таблица исключений по viewport и UA/DPR sniffing.
9.2. Проверяемая симметрия
На матрице base core от 24 до 112 CSS px, включая дробные значения с шагом не
крупнее 0.25 px, и DPR 1, 1.25, 1.5, 2:
- DOM-centres core и его shell-end отличаются не более чем на один browser layout quantum;
- на изолированных 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.
Golden остаётся визуальным доказательством на выбранных failing-before-fix
размерах; один DOM getBoundingClientRect() без pixel check недостаточен.
10. Opening Lock/Unlock
10.1. Известные состояния
Значок замка у двери/ворот использует визуальный контракт Lock/Unlock #179:
- package shell/core ratio и concentric geometry;
- Locked: black/
#252525theme projection и белый locked glyph; - Unlocked: amber
#F0A00C, с theme-specific glyph из принятого reference; - Light/Dark stroke, inner shadow и external shadows из #179/#211;
- Dark No-Blur production fallback;
- корректный closed/open lock MDI path, а не перекраска одного glyph.
Opening badge сохраняет нынешний компактный общий footprint, позицию возле створки и способ вычисления offset. Package ratios применяются внутри этого footprint; он не превращается в полноразмерный device marker и не участвует в device layout/declump.
10.2. Unknown и действия
Unknown/unavailable сохраняет нейтральный lock-question смысл, но использует
тот же новый shell/core каркас без ложного locked/unlocked цвета.
Клик по opening badge только открывает карточку проёма. Lock выполняется лишь явной кнопкой карточки, Unlock — после подтверждения. Pointer/keyboard paths маркеров устройств не получают нового lock action. Скрытый/disabled/missing exact opening reference ведёт себя по прежним #117 правилам.
11. Hover и действие всей внешней capsule
Для .device-shell.with-values видимая border-box capsule целиком является
mouse-hover областью, включая:
- core;
- промежуток между core и value section;
- value/legacy sections;
- внутренний padding до внешнего stroke;
- все четыре направления right/bottom/left/top.
Над любой точкой capsule активируются те же visual hover, z-index и tooltip,
что над 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.
Минимальный core-centred target 44×44 сохраняется, но больше не является единственной action-областью для вытянутого marker. Keyboard path остаётся на самом marker и выполняет то же действие независимо от наличия sections.
Preview воспроизводит визуальный hover при переданном mouse gate, но остаётся неинтерактивным. Static plan marker остаётся read-only; его footer button не имеет отношения к этой capsule.
12. Плавный marker LQI
Цвет числа LQI снова использует прежнюю непрерывную функцию:
<= 40 → hue 0 (red endpoint)
40..180 → linear hue 0..120
>= 180 → hue 120 (green endpoint)
Нормативный output совпадает с общим lqiColor():
hsl(round(hue), 85%, 55%). Внутри диапазона соседние значения меняют hue
плавно; на бывших границах 40/41 и 179/180 нет перехода между отдельными
palette colors.
Меняется только presentation.lqiColor marker. Сохраняются:
- resolved average/source и число;
show_signal, отсутствие missing/unavailable подписи и LQI=0;- low/mid/high
lqiBandдля понятного aria-label и data attributes; - позиция строки под shell и Double-bottom offset;
- room fill gradient и room tooltip color.
Три package цвета #179 больше не являются нормативом для marker LQI, но остаются semantic colors других состояний и pulse.
13. UX, accessibility и touch
- View и kiosk на desktop/touch остаются release-blocking;
- essential state/action не зависит от hover;
- focus-visible не зависит от mouse gate;
- aria-label продолжает содержать число LQI и категорию low/mid/high;
- увеличение glyph не меняет accessible name/role/tab order;
- opening lock сохраняет существующий понятный glyph и click-to-info;
- Device editor остаётся desktop-first, а его touch safety floor не меняется;
- pan/pinch/long press/pointercancel не создают hover или action.
14. Данные, migration и i18n
Новых config/layout/localStorage/backend полей нет. Schema и сериализация не меняются, data migration не нужна. Existing card/marker size values должны читаться с тем же фактическим результатом.
Новых пользовательских строк не ожидается. Существующие RU/EN LQI a11y bands сохраняются; изменить нужно только описание цветовой шкалы в руководствах. Если реализация потребует новый текст, обязательна RU/EN parity без расширения пользовательских настроек.
15. Архитектурный контракт
card config / marker scale / surface base
→ one resolved effective base size
→ resolveDevicePresentation
→ renderDeviceFace
→ one concentric face geometry on full / preview / static
resolved marker LQI
→ continuous marker color + unchanged semantic band
→ shared full / preview / static face
opening exact lock state
→ shared #179 lock visual tokens/geometry
→ opening badge → click opens info only
real mouse modality
→ full capsule hover owner
→ visual hover + tooltip
full capsule pointer target
→ existing marker action / confirmation / long press
Ожидаемые зоны изменений:
src/styles.ts— base values, concentric layout, glyph base ratio, capsule hover и opening-lock tokens;src/device-presentation.ts— continuous marker LQI color при сохранённом band;src/device-face.ts— только если нужен явный hover/action layer;src/houseplan-card.ts— resolved full base, opening lock DOM и hover owner;src/space-render.ts,src/hp-device-preview.tsи vacuum styles — только для base/parity wiring, второй renderer запрещён;- unit/source tests, targeted smokes, reference capture, docs и changelogs.
Нельзя вводить второй device face renderer, отдельную LQI-функцию с другой формулой, global pointer latch, per-marker ResizeObserver или per-frame JS.
16. Edge cases
- default и explicit global
icon_size, kiosk scale и per-marker 0.5…3; - base 24…112 px, дробные quarter-pixel значения, DPR 1/1.25/1.5/2;
- Light/Dark hot switch и цветной план;
- Icon, Text, Double right/left/top/bottom и legacy third section;
- длинное значение, CJK/кириллица,
0,falseи unit; - selected/focus/active/alarm/virtual/unavailable вместе с новой geometry;
- custom MDI, rotated glyph и state-swapped lock/cover/light icons;
- opening lock locked/unlocked/locking/unlocking/unknown/unavailable;
- opening у двери и ворот, hidden openings и registry-less exact lock;
- LQI
0,1,39,40,41,42,178,179,180,181,255, missing и value badge, который сам показывает LQI; - mouse впервые входит с capsule section, touch→mouse hybrid, pointerleave, space/mode/hidden/disconnect cleanup;
- two cards on one page and non-interactive preview/static surfaces;
- 200 markers without added layout loop/compositor layer.
17. Acceptance criteria
- AC1 — size contract. Отдельный
--device-visual-factor/эквивалентный late multiplier удалён; default, explicit global/per-marker, kiosk, preview, static и vacuum сохраняют effective v1.65.0-beta.8 size с допуском одного layout quantum. Доказательство: source/unit facts + computed-style browser matrix + reference capture. - AC2 — glyph. MDI viewport использует прямой base
0.55 × core, без дополнительного multiplier/transform; core/shell/anchor не меняются и glyph не клиппится. Доказательство: source assertion + icon/custom/rotation browser smoke + 32/56/96 reference. - AC3 — concentric geometry. Icon/Text/Double shell-end и core выполняют §9 на дробной size/DPR matrix; нет hardcoded pixel compensation. Доказательство: DOM-centre assertions + pixel-centroid smoke, красный до фикса или на documented mutant + reviewed golden.
- AC4 — anchor/parity. Saved coordinate, side anchor, relative scale, Text expansion и 44×44 minimum action area не меняются на full, preview и static surfaces. Доказательство: unit layout facts + preview/static/icon scale smokes.
- AC5 — opening locks. Door/gate locked и unlocked совпадают с #179 по package layers/tokens в Light/Dark при сохранённом compact footprint; unknown нейтрален. Доказательство: reference/runtime visual matrix + opening browser smoke/golden.
- AC6 — lock security. Opening badge только открывает info; lock/unlock service и confirmation invariant не меняются, disabled/missing references не рисуют ложный action. Доказательство: existing opening binding, registry-less, lock-action и lock-invariant smokes.
- AC7 — capsule hover. На всех четырёх позициях любая точка внешней capsule включает один mouse visual hover/z-index/tooltip; unavailable, touch/pen и compatibility mouse не включают его. Доказательство: real PointerEvent desktop/hybrid browser smoke + computed styles.
- 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.
- AC9 — continuous marker LQI. Marker color совпадает с
lqiColor()на representative и boundary-adjacent values; 40/41 и 179/180 не используют дискретные palette jumps. Доказательство: pure boundary/dense-range tests- browser computed color.
- AC10 — only color changes. LQI text/source/format/visibility/position, low-mid-high aria/data band и room fill/tooltip gradient не меняются. Доказательство: presentation/a11y unit + existing room fill regression.
- AC11 — data/i18n compatibility. Schema, saved settings и RU/EN key set не меняются; Open → Save не материализует defaults. Доказательство: serialization/i18n unit + code review.
- AC12 — performance. Нет per-frame JS, per-marker observer/media listener,
backdrop-filter, UA/DPR sniffing либо роста постоянных animation layers;
200-marker budget не ухудшен материально. Доказательство: source review
- pre-beta performance smoke.
- AC13 — release artifacts. Оба changelog, RU/EN guide, architecture,
testing, targeted smoke/reference и visual fingerprints актуальны.
Доказательство: docs diff +
check-docs+ release review.
18. План автотестов
18.1. Цикл реализации
npm run typecheck
npm test
npm run build
node scripts/check-docs.mjs --external
Unit/source coverage:
- отсутствие late 0.9 factor и наличие canonical effective bases;
- direct glyph base 0.55;
- shared concentric layout contract без fixed pixel nudge;
- dense continuous LQI color и неизменный band/a11y;
- opening-lock state/token projection;
- hover/action ownership inventory;
- config/i18n parity.
18.2. Targeted browser smoke перед S7
Обязательны:
- расширенный
demo/smoke_device_icon_design.mjs; - новый либо отдельный pixel-alignment smoke с fractional size/DPR matrix;
demo/smoke_device_preview_parity.mjs;demo/smoke_static_icon.mjs;demo/smoke_icon_scale.mjs;demo/smoke_state_value.mjs;- capsule hover/action smoke для четырёх badge positions;
demo/smoke_touch_tips.mjsи hybrid pointer regression;demo/smoke_opening_binding.mjs;demo/smoke_registryless_opening.mjs;demo/smoke_lock_action.mjsиdemo/smoke_lock_invariant.mjs.
До исправления требуется зафиксировать красное доказательство минимум для alignment, late 0.9 source contract, glyph 0.50, opening-lock visual, capsule hover и categorical marker LQI. Critical guards получают mutation proof либо эквивалентное documented failing-before-fix сравнение.
18.3. Golden и pre-beta
Light/Dark golden/reference matrix включает:
- 32/56/96 и выбранные дробные failing sizes;
- Icon, Text, Double four-side/legacy;
- default/hover/focus/active/alarm/virtual/unavailable;
- custom/rotated glyph;
- opening Lock/Unlock/Unknown рядом с SVG #179;
- LQI low-end, несколько промежуточных оттенков и high-end;
- desktop hover и post-touch no-hover.
Windows capture диагностический. Baseline принимается только из reviewed полного Linux CI artifact. Перед beta выполняются full smoke, golden и performance gates по release runbook; backend не затронут.
19. Риски и меры
| Риск | Мера |
|---|---|
| Удаление 0.9 увеличит custom sizes | effective-size matrix для default/explicit/kiosk/per-marker до и после |
| Новый layout сдвинет anchor или side capsule | shared anchor facts и four-side smoke |
| Pixel тест окажется привязан к платформе | DOM invariant + alpha centroid; golden canon только Linux |
| Больший glyph клиппится у сложного MDI path | representative custom/state-swapped matrix на min size |
| Opening lock станет слишком крупным | сохраняется текущий compact footprint, меняются внутренние ratios |
| 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 |
20. Откат
Откат одним implementation revert возвращает прежний layout, factor, glyph, opening badge, hover и marker LQI colors. Миграции данных нет, поэтому storage rollback не нужен. Golden baseline возвращается только через нормативный reviewed Linux artifact, не ручным принятием Windows capture.
21. Release-артефакты
В user-visible implementation commit одновременно обновляются:
docs/CHANGELOG.mdиdocs/CHANGELOG.ru.mdсо ссылкой на #213;docs/USER-GUIDE.mdиdocs/USER-GUIDE.ru.md: effective size без отдельного factor, glyph, smooth marker LQI, opening lock и capsule hover;docs/ARCHITECTURE.md: base-size boundary, shared concentric face и marker-LQI band/color separation;docs/TESTING.md: fractional/DPR pixel alignment, lock reference и hover/action/LQI regressions;- reference/golden/source fingerprints и screenshots, если их источник изменён.
Release note: исправлено центрирование новых маркеров на разных масштабах, увеличены внутренние глифы, дверные замки приведены к новому дизайну, hover охватывает всю капсулу, а LQI снова использует плавную цветовую шкалу. Сначала beta; stable — только после promotion rule.
22. План реализации
- Добавить failing source/unit/browser guards для шести подтверждённых проблем.
- Перенести current effective sizes на единую base-resolution boundary и удалить late factor из face/puck.
- Перестроить concentric shell/core tracks и увеличить direct glyph base.
- Подключить full-capsule hover и существующий marker action к единому target.
- Вернуть continuous marker LQI color при неизменном band/a11y.
- Перевести opening lock на #179 tokens/geometry, сохранив compact footprint и security path.
- Обновить parity surfaces, docs/changelogs/reference fixtures.
- Выполнить fast gates и все named smokes §18.2, затем передать в S7.
- Перед beta выполнить полный Linux golden/smoke/performance gate.
23. Принятые технические предположения
Следующее принято предположительно и может быть изменено ревьюером без нового продуктового решения, если все AC сохраняются:
- Opening lock сохраняет нынешний компактный footprint и offset; #179 задаёт его внутренние proportions/tokens, а не новый абсолютный размер.
- Unknown opening lock использует package shell/default theme и текущий
lock-question, потому что архив не содержит отдельного Unknown asset. - По уточнению владельца во время реализации вся видимая capsule является и hover-, и action-target; отдельного hover-only слоя нет.
- Low/mid/high band остаётся только semantic/a11y facet; цвет независимо
разрешается общей continuous
lqiColor(). - Base compatibility может нормализоваться один раз на границе surface size,
но внутри
.dev, puck и face запрещён любой поздний 0.9-equivalent factor. - 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. - Общий concentric layout предпочтительнее размера, округлённого через CSS
round(): последний зависит от поддержки WebView и не решает shared-center invariant сам по себе.