Решили: вершинный цвет в UI Toolkit отдаётся КАК ЕСТЬ — ручных переводов в линейное пространство в UI-коде быть не должно; цвет перед фиксацией сверяется замером пикселя, а не взглядом на скрин; значения утверждённого Максом кадра снимаются пипеткой и кладутся в токен буквально.

Почему: шейдер UITK конвертирует вершинный tint сам, как Canvas у uGUI. Наш VertexColor делал это второй раз, и заливка приходила на экран втрое темнее заданного: rgb(44,140,146)rgb(6,62,69). Проверено пробой — предкомпенсированный c.gamma дал 41,133,139, то есть ровно заданное. Отсюда вывод: конверсий должно быть ноль.

Штатной альтернативы ручному мешу нет: градиентов в USS Unity не даёт и не планирует, а градиент на кнопке нужен. Значит вторая дорога цвета (USS → custom property → поле C# → вершина) остаётся, и чистота — не в её удалении, а в том, что по ней нельзя молча изменить значение: одна функция на весь UI, тождественная; у компонента нет своего цвета (дефолт Color.clear); инвариант держит тест.

Сказано: «Загугли лучше как это все делать КОРРЕКТНО. А то уже который раз! И сама не замечаешь этого!!!», «И, плиз, коммить КАЖДОЕ свое изменение при работе над UI, чтобы можно было откатиться к предыдущим вариантам», «верни кнопке именно этот цвет!» (05.08.2026).

Грабли:

  1. Симптом лечился трижды. Разговор начался с «кнопки слишком тёмные», и два раунда ушли на подбор оттенков — при том что ломался конвейер, а не палитра. Корень один: доверие скрину вместо замера. На тёмной гамме расхождение «задано / нарисовано» выглядит дизайнерским решением.
  2. Докстринг сутки предписывал ровно то, что запрещал. Комментарий от 04.08 объяснял, почему конверсия нужна, и был причиной дефекта. Комментарий не падает — поэтому инвариант такого рода обязан жить тестом.
  3. Живая правка полей рефлексией не переживает пересчёт стиля. Пересчёт случается не в момент вызова ve.style.*, а на следующем кадре, перечитывает USS и возвращает всё назад. Дважды выглядело как «агент вернул старый цвет». Лечится подпиской на CustomStyleResolvedEvent.
  4. Кайма и заливка идут РАЗНЫМИ дорогами. Painter2D конвертирует сам, ручной меш — нет; в сеансе с ещё не перекомпилированным кодом это значит, что компенсацию надо давать только заливке. Одинаковая поправка обеим красит кайму мимо.

Владелец правды: Assets/_Project/Scripts/UI/Components/PlateButton.cs (VertexColor — тождество), Assets/_Project/UI/Theme/tokens.primitives.uss (ступени patina-900/950/outline, снятые пипеткой), скилл xgaida-x-nixi-uitk (разделы «Цвет: одна дорога на экран» и «Коммитить каждое изменение UI»).