Решили: панель интерфейса собирается из двух источников — материал приходит запечённой процедурной текстурой (9-slice), форма и оправа рисуются вершинами (Painter2D). Фактуры печёт редакторное окно Alebardium → UI → Bake Panel Textures, оправу рисует контрол PanelFrame.

Почему: плоская заливка читалась схемой, а не предметом («очень скучная форма», «нет рамки вокруг какой-то»). Разведение по природе: фактура — это шум на площади, её дешевле посчитать один раз и положить в атлас; кайма и накладки — это геометрия, которая обязана держать РАЗМЕР при любой ширине панели, а растянутая картинка тянет её вместе с серединой. Отвергнуто: (1) рисовать всё картинкой — 9-slice плывёт на накладках; (2) рисовать всё вершинами — Painter2D не умеет текстурную заливку, фактуры не будет; (3) filter: custom шейдером поверх — самый гибкий и самый дорогой путь, не нужен, пока хватает двух первых.

Сказано: «Т.е. да, создавать для UI что-то процедурно - это правильный путь, который часто юзать будем, думаю» и «Да, давай как постоянный тул» (02.08.2026, сессия d2239d53). Выбор материала — «Самое нормальное то 2» (пергамент) и оправы — «Берем 3» (двойная кайма без накладок), 03.08.2026.

Грабли:

  • Тема .tss держит СВОИ копии импортированных ярусов. После правки tokens.*.uss обязателен переимпорт theme.uss И GuildmasterRuntimeTheme.tss, иначе часть переменных молча не резолвится: у меня отвалились разом стекло и сериф, при этом старые переменные работали — выглядело как «сломался шрифт», а не как устаревший кэш. Полчаса на диагностику.
  • Переимпорт UXML на живом play сносит построенное дерево. UIDocument пересобирает rootVisualElement из ассета, а слои строит бутстрап в Start, который уже отработал — экран становится пустым. Ловится дважды за сессию; лечится перезапуском play, а не поиском бага.
  • CustomStyleProperty<float> читает <number>, а не <length>: --gm-frame-width: 2px молча не резолвится, нужно 2 (та же конвенция, что у --gm-slant-width).
  • Оправа — оверлей, а не контейнер. Контейнер потребовал бы переписать структуру всех экранов; оверлей вешается одной строкой разметки и не трогает фон. Цена — он рисуется ПОВЕРХ содержимого, поэтому годится только для периметра.
  • Текстуры импортируются кодом (ApplyImportSettings): мип-уровни мылят кромку 9-slice, повтор рвёт край, сжатие съедает тонкое зерно. Забытый вручную пункт виден на панели сразу.

Владелец правды: Scripts/EditorTools/UI/PanelTextureBakerWindow.cs, Scripts/UI/Components/PanelFrame.cs, UI/Theme/components.uss (секции --sheet и __frame), дизайн-метафора — docs/wiki/gdd/10-vision/ui-visual-language.md.