{
	"format": "42-spec/5",
	"meta": {
		"version": "0.9.0",
		"changelog": "/spec-changelog.md",
		"schema": "/spec.schema.json",
		"examples": "/examples.json",
		"contentOrigin": "extracted (перенос канона со страниц) + ai-assisted (свипы useCases/departures/keyboard 2026-07); ревью формулировок — Сергей"
	},
	"system": {
		"name": "42",
		"title": "Спецификация дизайн-системы",
		"lead": "«42» даёт людям и ИИ-агентам один контракт. **Канон** задаёт логику и поведение; **productScope** — компонентные токены скина. Сетка `--sp-*` остаётся общей."
	},
	"canon": {
		"manifesto": "Поведение важнее украшения. Bits UI или HTML дают семантику, токены — визуал. Недоступное не показывается; на экране один accent. Канон переносим, productScope принадлежит продукту.",
		"principles": [
			{
				"title": "Поведение прежде украшения",
				"pro": "За доступные примитивы как основу",
				"contra": "против декоративных оболочек без поведения"
			},
			{
				"title": "Честные состояния",
				"pro": "За «недоступное не показываем»",
				"contra": "против фейкового `disabled` и спиннеров в контролах"
			},
			{
				"title": "Один акцент",
				"pro": "За ясную иерархию",
				"contra": "против двух действий, спорящих за главный вес"
			},
			{
				"title": "Токены, не литералы",
				"pro": "За единый источник значений",
				"contra": "против магических чисел в компонентах"
			},
			{
				"title": "Канон и productScope",
				"pro": "За переносимую логику",
				"contra": "против вкусовщины, выданной за правило"
			},
			{
				"title": "Меньше абстракций",
				"pro": "За KISS и читаемость",
				"contra": "против enterprise-узоров без выгоды"
			},
			{
				"title": "Текст помогает действовать и понимать",
				"pro": "За точную мысль без повторов",
				"contra": "против словесного шума и размытого синтаксиса"
			}
		],
		"axes": [
			{
				"axis": "variant",
				"values": "accent / default / ghost",
				"meaning": "уровень в иерархии действия, не цвет",
				"why": "Бренд меняет цвет, но сохраняет иерархию. Выбирают уровень, а не «синюю кнопку»."
			},
			{
				"axis": "tone",
				"values": "default / danger",
				"meaning": "смысл действия; ортогонален варианту",
				"why": "Danger сочетается с любым уровнем. Отдельный вариант смешал бы смысл с иерархией."
			},
			{
				"axis": "state",
				"values": "idle / hover / pressed",
				"meaning": "краткая фаза прямого взаимодействия; не значение и не валидация компонента",
				"why": "Общий словарь делает отклик предсказуемым. `focus-visible` накладывается на любую фазу. `open`, `checked`, `error` и другие значения остаются локальными."
			},
			{
				"axis": "size",
				"values": "sm / md / lg",
				"meaning": "плотность контрола; компонент публикует только реально нужное подмножество",
				"why": "Имена размеров общие; точные метрики задаёт productScope. Скин меняет геометрию без переименования `sm`, `md`, `lg` и шкалы `--sp-*`."
			}
		],
		"rules": [
			{
				"rule": "Нет `disabled`",
				"why": "Недоступное действие не показываем, а не гасим."
			},
			{
				"rule": "Нет `loading` у контролов",
				"why": "Асинхронность — на уровне формы/области."
			},
			{
				"rule": "Один accent на экран",
				"why": "Два главных действия = сломанная иерархия."
			},
			{
				"rule": "danger — это tone, не вариант",
				"why": "Разрушающее сочетается с любым уровнем."
			},
			{
				"rule": "Текст называет действие или подтверждение",
				"why": "Объект добавляется, только если без него неясен результат."
			},
			{
				"rule": "Нативная семантика и видимый `focus-visible`",
				"why": "Используем Bits UI там, где есть подходящий примитив, иначе нативный контрол; клавиатурный фокус всегда различим."
			}
		],
		"grid": {
			"module": "8px",
			"halfStep": "4px — изредка, для микроразмеров",
			"note": "Шкала `--sp-*` задаёт раскладку всех скинов. Внутреннюю геометрию компонента меняет productScope, не создавая вторую шкалу."
		},
		"motion": {
			"lead": "Движение подтверждает действие. Канон задаёт шкалу и кривую; productScope выбирает темп конкретного контрола.",
			"roles": [
				{
					"token": "--t-fast",
					"value": "120ms",
					"role": "микро-отклик: hover, нажатие, смена цвета и тени"
				},
				{
					"token": "--t-normal",
					"value": "200ms",
					"role": "смена состояния: раскрытие меню, подъём плавающей подписи"
				},
				{
					"token": "--t-slow",
					"value": "320ms",
					"role": "крупное перемещение: панели, ход бегунка"
				},
				{
					"token": "--ease",
					"value": "cubic-bezier(0.2, 0, 0, 1)",
					"role": "единая кривая: быстрый старт, мягкое торможение"
				}
			],
			"rules": [
				"Шкала `--t-*` и `--ease` — канон: скины их не переопределяют.",
				"Темп контролов (`--t-button`, `--t-control`, `--t-switch`, `--ease-button`, `--status-transition`) задаёт productScope. Скин переопределяет базовые значения Blueprint.",
				"`prefers-reduced-motion: reduce` обязателен для несущественного перемещения, масштаба и вращения; смена цвета, тени и прозрачности может остаться без геометрического движения.",
				"Движение не носит смысл: состояние читается и без анимации — цветом, формой, текстом."
			]
		},
		"departures": [
			{
				"what": "Нет `disabled` ни у одного контрола",
				"why": "Недоступное действие не показывается, а не выглядит погашенным."
			},
			{
				"what": "Нет `loading`-спиннеров в контролах",
				"why": "Асинхронность показывается на уровне формы или области."
			},
			{
				"what": "Один accent на экран — жёсткое правило, не рекомендация",
				"why": "Второй главный вес ломает иерархию."
			},
			{
				"what": "Авторское фокус-кольцо привязано к `:focus-visible`, а не к любому `:focus`",
				"why": "Псевдокласс следует эвристике браузера: клавиатурный фокус всегда видим, а поле ввода может сохранить индикатор и после указателя. Мы не скрываем такие случаи вручную."
			},
			{
				"what": "Публичная шкала отступов `--sp-*` едина для всех скинов",
				"why": "Бренд может менять внутреннюю геометрию компонента через его токены, но не объявляет собственную шкалу раскладки: иначе одинаковая композиция перестаёт быть переносимой."
			}
		]
	},
	"components": [
		{
			"kind": "component",
			"id": "button",
			"name": "Button",
			"status": "stable",
			"since": "2026-05",
			"docOrigin": [
				"authored"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Запускает действие или оформляет переход как действие.",
			"description": "Button задаёт иерархию, тон и плотность действия. `href` превращает кнопку в ссылку с тем же визуальным весом.",
			"useCases": {
				"when": [
					"Действие на экране или в форме: «Сохранить черновик», «Отменить».",
					"Переход, оформленный как действие, — через `href` (рендерится ссылка).",
					"Иконка-действие в тулбаре — `short` с обязательным `aria-label`."
				],
				"avoid": [
					"Навигация внутри текста — это обычная ссылка, не кнопка.",
					"Переключение состояния или режима — кнопка совершает действие, а не хранит выбор.",
					"Метка или фильтр — кнопка не носит значение."
				],
				"alternatives": [
					{
						"id": "switch",
						"why": "мгновенное включение/выключение настройки"
					},
					{
						"id": "segmented-control",
						"why": "выбор одного режима из короткого ряда"
					},
					{
						"id": "tag",
						"why": "кликабельная метка-фильтр (`selectable`)"
					}
				]
			},
			"props": [
				{
					"name": "variant",
					"type": "accent | default | ghost",
					"default": "default",
					"desc": "Уровень в иерархии действия (канон-ось)"
				},
				{
					"name": "tone",
					"type": "default | danger",
					"default": "default",
					"desc": "Смысл действия, ортогональный иерархии"
				},
				{
					"name": "size",
					"type": "sm | md (алиасы s | m deprecated)",
					"default": "md",
					"desc": "Плотность контрола; старые алиасы поддерживаются только для миграции"
				},
				{
					"name": "leadingIcon",
					"type": "snippet",
					"desc": "Иконка перед текстом"
				},
				{
					"name": "trailingIcon",
					"type": "snippet",
					"desc": "Иконка после текста"
				},
				{
					"name": "short",
					"type": "boolean",
					"default": "false",
					"desc": "Только иконка, без текста"
				},
				{
					"name": "indicator",
					"type": "boolean",
					"default": "false",
					"desc": "Точка-нотификатор (несовместима с ghost)"
				},
				{
					"name": "href",
					"type": "string",
					"desc": "Рендерит ссылку вместо кнопки"
				},
				{
					"name": "nativeType",
					"type": "button | submit | reset",
					"default": "button",
					"desc": "Нативный type кнопки"
				},
				{
					"name": "onclick",
					"type": "function",
					"desc": "Обработчик клика"
				},
				{
					"name": "children",
					"type": "snippet",
					"desc": "Доступный текст действия в sentence case; UPPERCASE задаёт только CSS"
				}
			],
			"axes": [
				{
					"axis": "variant",
					"title": "Варианты",
					"desc": "Уровень в иерархии действия, не цвет и не смысл. Исторические имена AdsCompass — только алиасы.",
					"rationale": "Трёх уровней достаточно: главное, обычное, третичное. Больше уровней — иерархия размывается; цвет уровнем не является (смысл несёт `tone`).",
					"values": [
						{
							"value": "accent",
							"look": "максимальный визуальный вес",
							"when": "главное действие на экране",
							"alias": "accidental (AdsCompass)"
						},
						{
							"value": "default",
							"look": "стандартный визуальный вес",
							"when": "обычное действие в интерфейсе"
						},
						{
							"value": "ghost",
							"look": "минимальный вес без постоянной заливки",
							"when": "третичное действие",
							"alias": "plain (AdsCompass)"
						}
					]
				},
				{
					"axis": "tone",
					"title": "Тон",
					"desc": "Смысл действия независимо от его места в иерархии.",
					"rationale": "Разрушающее действие может быть главным, обычным или третичным. Отдельный `tone` не заставляет кодировать опасность через `variant` и сохраняет правило одного accent.",
					"values": [
						{
							"value": "default",
							"look": "нейтральный смысл",
							"when": "обычное действие"
						},
						{
							"value": "danger",
							"look": "цвет опасности из productScope",
							"when": "необратимое или разрушающее действие"
						}
					]
				},
				{
					"axis": "size",
					"title": "Размеры",
					"rationale": "Два размера подтверждены реальными сценариями: `md` — формы, `sm` — плотные панели. Третий появится с реальным кейсом, не заранее.",
					"values": [
						{
							"value": "sm",
							"metrics": "метрики задаёт productScope",
							"when": "плотные панели и тулбары",
							"alias": "s (AdsCompass, deprecated)"
						},
						{
							"value": "md",
							"metrics": "метрики задаёт productScope",
							"when": "формы и основные действия",
							"alias": "m (AdsCompass, deprecated)"
						}
					]
				}
			],
			"composition": [
				{
					"part": "text",
					"rule": "дефолт; короткая реплика пользователя в sentence case"
				},
				{
					"part": "leading-icon",
					"rule": "иконка перед текстом"
				},
				{
					"part": "trailing-icon",
					"rule": "иконка после текста"
				},
				{
					"part": "short",
					"rule": "только иконка — обязателен aria-label"
				},
				{
					"part": "indicator",
					"rule": "точка-нотификатор; не для ошибки"
				}
			],
			"compositionNote": "UPPERCASE — только визуальный CSS-слой; доступное имя сохраняет исходный регистр. Иконка с одной или с обеих сторон.",
			"anatomy": [
				{
					"id": "control",
					"role": "part",
					"desc": "Интерактивный корень: нативный `<button>` или `<a>` в зависимости от `href`."
				},
				{
					"id": "leading-icon",
					"role": "icon",
					"required": false,
					"desc": "Ведущая иконка уточняет действие; без самостоятельного accessible name."
				},
				{
					"id": "label",
					"role": "label",
					"desc": "Доступное имя действия или подтверждения; визуально преобразуется в UPPERCASE."
				},
				{
					"id": "trailing-icon",
					"role": "icon",
					"required": false,
					"desc": "Хвостовая иконка показывает направление или результат действия."
				},
				{
					"id": "indicator",
					"role": "indicator",
					"required": false,
					"desc": "Точка-нотификатор; не передаёт ошибку и не сочетается с `ghost`."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "покой",
					"change": "базовый вид"
				},
				{
					"id": "hover",
					"when": "курсор над кнопкой",
					"change": "тень мягче (ghost — фон)"
				},
				{
					"id": "pressed",
					"when": "нажатие",
					"change": "вдавленная inset-тень и физический сдвиг на значение `--button-press-y`"
				},
				{
					"id": "focus-visible",
					"when": "клавиатурный фокус",
					"change": "сплошное контрастное внешнее кольцо; productScope-свечение может дополнять его"
				}
			],
			"statesNotes": [
				"**Нет disabled.** Недоступное действие не показываем, а не гасим.",
				"**Нет loading.** Асинхронность — на уровне формы/области, не спиннером в кнопке."
			],
			"impossible": [
				{
					"combo": "indicator + ghost",
					"why": "У текстовой кнопки нет фона, точке негде «жить» — индикатор не рендерится."
				},
				{
					"combo": "short + leadingIcon + trailingIcon",
					"why": "Короткая кнопка содержит ровно одну иконку; если переданы обе, хвостовая не рендерится."
				}
			],
			"a11y": [
				"Нативный `<button>` из Bits UI; для перехода по адресу — `<a>` (проп `href`).",
				"`focus-visible` обязателен: сплошное внешнее кольцо не ниже 3:1 относительно соседнего фона.",
				"Нативную кнопку активируют `Enter` и `Space`; ссылку с `href` — `Enter`.",
				"`short` без текста — обязателен `aria-label`; декоративные иконки и indicator скрыты через `aria-hidden`.",
				"Indicator только дополняет текстовый статус рядом или в контексте и не может быть единственным сообщением."
			],
			"keyboard": [
				{
					"keys": "Enter / Space",
					"action": "активирует нативную кнопку"
				},
				{
					"keys": "Enter",
					"action": "переходит по `href`, когда Button отрендерен ссылкой"
				}
			],
			"contrast": [
				{
					"fg": "--c-button-text",
					"bg": "--c-button-bg",
					"min": 4.5
				},
				{
					"fg": "--c-button-accent-text",
					"bg": "--c-button-accent-bg",
					"min": 4.5
				},
				{
					"fg": "--c-button-ghost-text",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-button-ghost-text",
					"bg": "--c-button-ghost-bg-hover",
					"min": 4.5
				},
				{
					"fg": "--c-button-danger",
					"bg": "--c-button-bg",
					"min": 4.5
				},
				{
					"fg": "--c-button-danger",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-button-danger-text",
					"bg": "--c-button-danger",
					"min": 4.5
				},
				{
					"fg": "--c-button-danger",
					"bg": "--c-button-ghost-bg-hover",
					"min": 4.5
				},
				{
					"fg": "--c-button-focus",
					"bg": "--c-bg",
					"min": 3
				}
			],
			"tokens": [
				"--button-focus-offset",
				"--button-gap",
				"--button-gap-sm",
				"--button-h-sm",
				"--button-indicator-offset",
				"--button-indicator-size",
				"--button-pad-x-md",
				"--button-pad-x-sm",
				"--button-pad-y-md",
				"--button-pad-y-sm",
				"--button-press-y",
				"--button-radius-md",
				"--button-radius-short",
				"--button-radius-sm",
				"--button-short-pad-md",
				"--button-short-pad-sm",
				"--c-button-accent-bg",
				"--c-button-accent-text",
				"--c-button-bg",
				"--c-button-danger",
				"--c-button-danger-text",
				"--c-button-focus",
				"--c-button-indicator",
				"--c-button-ghost-bg-hover",
				"--c-button-ghost-text",
				"--c-button-text",
				"--control-h-md",
				"--ease-button",
				"--icon-button",
				"--sh-button-accent",
				"--sh-button-accent-active",
				"--sh-button-accent-hover",
				"--sh-button-default",
				"--sh-button-default-active",
				"--sh-button-default-hover",
				"--sh-button-focus",
				"--sh-button-indicator",
				"--sh-button-ghost",
				"--t-button"
			],
			"examples": [
				{
					"id": "Types",
					"name": "Варианты и тон",
					"desc": "`variant` задаёт иерархию, `tone` — смысл; `danger` сочетается с каждым уровнем."
				},
				{
					"id": "Sizes",
					"name": "Размеры",
					"desc": "`md` — основной, `sm` — компактный; точные метрики задаёт productScope."
				},
				{
					"id": "Icons",
					"name": "Иконки",
					"desc": "Ведущая, хвостовая или обе — иконка `activity` 20px."
				},
				{
					"id": "Short",
					"name": "Короткая",
					"desc": "Только иконка, без текста — обязателен `aria-label`."
				},
				{
					"id": "Indicator",
					"name": "Индикатор",
					"desc": "Точка-нотификатор в углу. С `variant=\"ghost\"` несовместима — не рендерится."
				}
			],
			"departures": [
				{
					"what": "Ось стиля называется `variant` со значениями `accent | default | ghost`.",
					"why": "Исторические имена AdsCompass (`type`, `accidental`, `plain`) — productScope-номенклатура; канон один для всех брендов, старые имена сохранены в таблице вариантов как алиасы."
				},
				{
					"what": "Лейбл кнопки визуально преобразуется в UPPERCASE",
					"why": "Капс задаёт визуальный вес AdsCompass, но исходный текст и accessible name остаются в sentence case: регистр не кодируется в контенте."
				},
				{
					"what": "`pressed` физически сдвигает кнопку на 4px",
					"why": "Вдавливание — часть тактильной метафоры AdsCompass (bevel-тени). Обычно кнопки меняют только цвет — сдвиг здесь намеренный."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Ровно один `variant=\"accent\"` на экран."
				},
				{
					"level": "must",
					"guidance": "Передавать разрушающий смысл через `tone=\"danger\"`, не новым значением `variant`."
				},
				{
					"level": "must",
					"guidance": "Писать короткую реплику пользователя; объект добавлять, только если без него неясен результат."
				},
				{
					"level": "must-not",
					"guidance": "Добавлять `disabled` или spinner/loading — недоступное действие не рендерится вовсе."
				},
				{
					"level": "must-not",
					"guidance": "Сочетать `indicator` с `variant=\"ghost\"` — комбинация не рендерится."
				},
				{
					"level": "must",
					"guidance": "У `short`-кнопки задавать `aria-label`."
				},
				{
					"level": "must",
					"guidance": "Использовать indicator только как дополнительный визуальный сигнал; смысл дублировать текстом или состоянием контекста."
				},
				{
					"level": "must",
					"guidance": "Писать исходный лейбл в sentence case; визуальный UPPERCASE делает компонент."
				},
				{
					"level": "must-not",
					"guidance": "Использовать историческую ось `type` и значения `accidental`/`plain` — только канон `variant: accent | default | ghost`."
				}
			]
		},
		{
			"kind": "component",
			"id": "input",
			"name": "Input",
			"status": "stable",
			"since": "2026-05",
			"docOrigin": [
				"authored"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Принимает однострочное свободное значение.",
			"description": "Нативное поле с плавающей подписью, аффиксами и одним вспомогательным действием. Метрики и визуал задаёт productScope.",
			"useCases": {
				"when": [
					"Однострочный свободный ввод: имя, email, число, поисковый запрос.",
					"Значение с единицей или валютой — аффиксы `prefix`/`suffix`.",
					"Пароль — `type=\"password\"` с переключателем видимости."
				],
				"avoid": [
					"Ответ длиннее одной строки.",
					"Значение выбирается из известного набора — дай выбор, а не свободный ввод.",
					"Дата — у неё свой формат и календарь."
				],
				"alternatives": [
					{
						"id": "textarea",
						"why": "многострочный свободный текст"
					},
					{
						"id": "select",
						"why": "выбор одного значения из известного списка"
					},
					{
						"id": "input-date",
						"why": "одна дата с календарём"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "string",
					"default": "\"\"",
					"desc": "Значение поля",
					"bindable": true
				},
				{
					"name": "type",
					"type": "text | password | email | …",
					"default": "text",
					"desc": "Тип нативного input"
				},
				{
					"name": "size",
					"type": "sm | md",
					"default": "md",
					"desc": "Плотность контрола по общей шкале"
				},
				{
					"name": "label",
					"type": "string",
					"desc": "Видимая плавающая подпись и доступное имя поля"
				},
				{
					"name": "leading / trailing",
					"type": "snippet",
					"desc": "Декоративные иконки в поле"
				},
				{
					"name": "prefix / suffix",
					"type": "string",
					"desc": "Значимые аффиксы рядом со значением; входят в aria-describedby"
				},
				{
					"name": "textAlign",
					"type": "left | center | right",
					"default": "left",
					"desc": "Выравнивание значения"
				},
				{
					"name": "clearable",
					"type": "boolean",
					"default": "false",
					"desc": "Кнопка очистки при непустом редактируемом значении"
				},
				{
					"name": "characterCounter / maxlength",
					"type": "number",
					"desc": "Лимит и счётчик символов"
				},
				{
					"name": "placeholder",
					"type": "string",
					"desc": "Текст пустого состояния"
				},
				{
					"name": "revealable",
					"type": "boolean",
					"default": "type=password",
					"desc": "Переключатель видимости пароля"
				},
				{
					"name": "hint",
					"type": "string",
					"desc": "Подсказка-поповер"
				},
				{
					"name": "description",
					"type": "string",
					"desc": "Текст под полем"
				},
				{
					"name": "error",
					"type": "string",
					"desc": "Текст ошибки; задаёт aria-invalid и перекрывает description"
				},
				{
					"name": "readonly",
					"type": "boolean",
					"default": "false",
					"desc": "Только чтение"
				},
				{
					"name": "required",
					"type": "boolean",
					"default": "false",
					"desc": "Звёздочка обязательности"
				}
			],
			"axes": [
				{
					"axis": "size",
					"title": "Размеры",
					"desc": "Плотность поля, не его важность.",
					"rationale": "`md` покрывает формы, `sm` — плотные панели и подтверждён реализацией AdsCompass. `lg` не публикуется без отдельного продуктового сценария.",
					"values": [
						{
							"value": "sm",
							"when": "плотные панели и компактные фильтры",
							"metrics": "задаёт productScope"
						},
						{
							"value": "md",
							"when": "формы и основной поток ввода",
							"metrics": "задаёт productScope"
						}
					]
				}
			],
			"composition": [
				{
					"part": "label",
					"rule": "Постоянная подпись: в пустом idle занимает строку ввода, при фокусе или значении поднимается"
				},
				{
					"part": "control",
					"rule": "Нативный `<input>` с текстом значения"
				},
				{
					"part": "prefix / suffix",
					"rule": "Короткие аффиксы внутри строки ввода"
				},
				{
					"part": "leading / trailing",
					"rule": "Иконки слева и справа от текста"
				},
				{
					"part": "action",
					"rule": "Одна кнопка: очистка имеет приоритет над переключателем видимости пароля"
				},
				{
					"part": "msg",
					"rule": "Описание или ошибка под полем; справа может быть счётчик"
				}
			],
			"anatomy": [
				{
					"id": "field",
					"role": "part",
					"desc": "Корень поля связывает контрол, сообщения и вспомогательные действия."
				},
				{
					"id": "control",
					"role": "part",
					"desc": "Визуальная рамка со строкой ввода и внутренними элементами."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Постоянная доступная подпись; визуально плавает между строкой ввода и верхней позицией."
				},
				{
					"id": "affix",
					"role": "part",
					"required": false,
					"desc": "Значимый префикс или суффикс, описывающий формат значения."
				},
				{
					"id": "input",
					"role": "part",
					"desc": "Нативный однострочный `<input>` со значением и кареткой."
				},
				{
					"id": "action",
					"role": "part",
					"required": false,
					"desc": "Единственная вспомогательная кнопка: очистка или переключатель видимости пароля."
				},
				{
					"id": "message",
					"role": "label",
					"required": false,
					"desc": "Описание либо ошибка, программно связанная с полем."
				},
				{
					"id": "counter",
					"role": "indicator",
					"required": false,
					"desc": "Счётчик символов рядом с сообщением."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "поле пусто и не в фокусе",
					"change": "видимая подпись занимает строку ввода"
				},
				{
					"id": "hover",
					"when": "указатель над полем",
					"change": "productScope усиливает тень границы"
				},
				{
					"id": "focus",
					"when": "каретка в поле",
					"change": "подпись поднимается, поле получает «вдавленную» productScope-тень"
				},
				{
					"id": "focus-visible",
					"when": "фокус требует видимого индикатора",
					"change": "внешнее сплошное кольцо не ниже 3:1 дополняет тень"
				},
				{
					"id": "filled",
					"when": "есть значение",
					"change": "подпись остаётся поднятой после ухода фокуса"
				},
				{
					"id": "error",
					"when": "передан текст ошибки",
					"change": "под полем появляется текст, а input получает aria-invalid"
				},
				{
					"id": "readonly",
					"when": "значение только для чтения",
					"change": "productScope-фон отличает режим; значение остаётся доступным для фокуса и копирования"
				}
			],
			"statesNotes": [
				"**Нет disabled.** Недоступное поле не показываем (readonly — для значения «только чтение»)."
			],
			"impossible": [
				{
					"combo": "clearable + readonly",
					"why": "Readonly запрещает изменение значения, поэтому кнопка очистки не рендерится."
				},
				{
					"combo": "clear + password-toggle одновременно",
					"why": "При непустом clearable-поле показывается очистка; после очистки для password возвращается переключатель видимости."
				},
				{
					"combo": "trailing + action одновременно",
					"why": "Правая зона содержит один элемент: clear/password-toggle имеют приоритет, trailing временно не рендерится."
				}
			],
			"a11y": [
				"Нативный `<input>` получает hydration-safe id; видимый `<label for>` остаётся доступным именем при любом положении.",
				"Если видимого label нет, потребитель обязан передать `aria-label` или `aria-labelledby`; placeholder не заменяет имя.",
				"Ошибка или описание и значимые prefix/suffix объединяются в `aria-describedby`; ошибка задаёт `aria-invalid=true`.",
				"С label placeholder появляется только после подъёма подписи; без label он видим постоянно, но остаётся лишь подсказкой формата.",
				"Декоративные leading/trailing и SVG действий скрыты от accessibility tree.",
				"Hint-триггер получает имя, связанное с полем; текст поповера подключает Tooltip через aria-describedby при открытии.",
				"Интерактивные иконки имеют цель не меньше 24×24 CSS px; после очистки фокус возвращается в input."
			],
			"keyboard": [
				{
					"keys": "Tab / Shift+Tab",
					"action": "перемещает фокус между input, видимым вспомогательным действием и hint"
				},
				{
					"keys": "Enter",
					"action": "может отправить содержащую форму по нативным правилам её полей и кнопок"
				},
				{
					"keys": "Enter / Space",
					"action": "активирует кнопку очистки или показа пароля; очистка возвращает фокус в input"
				},
				{
					"keys": "Escape",
					"action": "закрывает открытый hint-поповер и сохраняет фокус на его триггере"
				}
			],
			"contrast": [
				{
					"fg": "--c-input-text",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-desc",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-affix",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-asterisk",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-action",
					"bg": "--c-input-bg",
					"min": 3
				},
				{
					"fg": "--c-control-hint",
					"bg": "--c-input-bg",
					"min": 3
				},
				{
					"fg": "--c-control-desc",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-error",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-counter",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-hint",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-input-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-hint-text",
					"bg": "--c-hint-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-text",
					"bg": "--c-input-readonly-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-input-readonly-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-affix",
					"bg": "--c-input-readonly-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-action",
					"bg": "--c-input-readonly-bg",
					"min": 3
				},
				{
					"fg": "--c-input-border",
					"bg": "--c-input-readonly-bg",
					"min": 3
				}
			],
			"tokens": [
				"--c-control-asterisk",
				"--c-control-counter",
				"--c-control-desc",
				"--c-control-error",
				"--c-control-focus",
				"--c-control-hint",
				"--c-control-label",
				"--c-hint-bg",
				"--c-hint-text",
				"--c-input-action",
				"--c-input-affix",
				"--c-input-bg",
				"--c-input-border",
				"--c-input-readonly-bg",
				"--c-input-text",
				"--control-fs-aux",
				"--control-box-radius",
				"--control-field-min",
				"--control-focus-offset",
				"--control-gap",
				"--control-msg-gap",
				"--input-action-icon",
				"--input-affix-fs-sm",
				"--input-border-w",
				"--input-content-pad-top",
				"--input-fs-sm",
				"--input-gap",
				"--input-h-md",
				"--input-h-sm",
				"--input-icon-md",
				"--input-icon-sm",
				"--input-label-fs-active",
				"--input-label-top",
				"--input-label-top-active",
				"--input-label-top-active-sm",
				"--input-label-top-sm",
				"--input-lh",
				"--input-pad-x",
				"--input-pad-x-sm",
				"--input-radius-md",
				"--input-radius-sm",
				"--sh-control-focus",
				"--sh-control-hover",
				"--sh-control-pressed",
				"--sh-input-hover-sm",
				"--sh-input-idle-sm",
				"--sh-input-pressed-sm",
				"--sh-radio",
				"--t-control"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый",
					"desc": "Подпись плавает: в покое — на месте плейсхолдера, в фокусе/со значением — наверх."
				},
				{
					"id": "Sizes",
					"name": "Размеры",
					"desc": "Канонические `sm / md`; точные метрики задаёт productScope."
				},
				{
					"id": "Icons",
					"name": "Иконки и очистка",
					"desc": "Ведущая/хвостовая иконки и кнопка очистки (clearable)."
				},
				{
					"id": "Affixes",
					"name": "Аффиксы, счётчик и выравнивание",
					"desc": "Prefix/suffix живут внутри поля; счётчик — в подвале рядом с описанием или ошибкой."
				},
				{
					"id": "Field",
					"name": "Поле",
					"desc": "Подсказка, описание, ошибка, обязательность, только чтение."
				}
			],
			"departures": [
				{
					"what": "Плавающая подпись внутри поля",
					"why": "Компактность первоисточника: подпись живёт на месте плейсхолдера и поднимается при фокусе или значении. Это не «плейсхолдер вместо label» — подпись видима всегда."
				},
				{
					"what": "Один смысловой слот сообщения: `error` перекрывает `description`",
					"why": "Во время исправления важна одна текущая инструкция. Ошибка заменяет справочное описание, а не конкурирует с ним; перенос текста на несколько строк допустим."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Давать полю доступное имя: видимый `label for`, а без него — явный `aria-label` или `aria-labelledby`; не подменять имя placeholder."
				},
				{
					"level": "must-not",
					"guidance": "Добавлять `disabled` — для «только чтение» есть `readonly`."
				},
				{
					"level": "must",
					"guidance": "Ошибку показывать текстом через `error`, не только цветом рамки."
				},
				{
					"level": "must",
					"guidance": "Использовать prefix/suffix только для краткой значимой единицы или формата, а не как украшение."
				},
				{
					"level": "must-not",
					"guidance": "Добавлять собственную кнопку справа вместе с clear/password-toggle: правая зона содержит одно действие."
				}
			]
		},
		{
			"kind": "component",
			"id": "select",
			"name": "Select",
			"status": "stable",
			"since": "2026-05",
			"docOrigin": [
				"authored"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Выбирает одно значение из короткого известного списка.",
			"description": "Select открывает плоский listbox с клавиатурной навигацией и typeahead. Метрики и визуал задаёт productScope.",
			"useCases": {
				"when": [
					"Короткий известный список нужно скрыть до запроса, чтобы экономить место.",
					"Опции самодостаточны и не требуют пояснений рядом."
				],
				"avoid": [
					"Две булевых опции «вкл/выкл».",
					"Варианты важно видеть все сразу — не прячь их в меню.",
					"Список длинный, часто меняется или требует поиска/фильтрации.",
					"Значение нельзя менять — покажи текст, а не интерактивный контрол."
				],
				"alternatives": [
					{
						"id": "switch",
						"why": "бинарное включение/выключение"
					},
					{
						"id": "radio",
						"why": "небольшой набор вариантов важно видеть и сравнивать сразу"
					},
					{
						"id": "segmented-control",
						"why": "короткие режимы переключают представление в одну строку"
					},
					{
						"id": "dualist",
						"why": "множественный выбор из длинного списка с поиском"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "string",
					"default": "\"\"",
					"desc": "Выбранное значение",
					"bindable": true
				},
				{
					"name": "options",
					"type": "Array<string | {value, label}>",
					"default": "[]",
					"desc": "Опции с уникальными строковыми value; пустой массив не рендерит контрол"
				},
				{
					"name": "size",
					"type": "sm | md",
					"default": "md",
					"desc": "Плотность контрола по общей шкале"
				},
				{
					"name": "label",
					"type": "string",
					"desc": "Видимая плавающая подпись и доступное имя триггера"
				},
				{
					"name": "placeholder",
					"type": "string",
					"desc": "Подсказка пустого состояния, не значение и не accessible name"
				},
				{
					"name": "hint / description / error",
					"type": "string",
					"desc": "Вспомогательная подсказка / сообщение / ошибка"
				},
				{
					"name": "name",
					"type": "string",
					"default": "\"\"",
					"desc": "Имя form-control; обязательно для нативной отправки и required-валидации"
				},
				{
					"name": "required",
					"type": "boolean",
					"default": "false",
					"desc": "Видимый знак и aria-required; form-валидация требует name"
				}
			],
			"axes": [
				{
					"axis": "size",
					"title": "Размеры",
					"desc": "Плотность триггера, не важность выбранного значения.",
					"rationale": "`md` покрывает формы, `sm` — плотные панели и подтверждён реализацией AdsCompass. `lg` не публикуется без отдельного продуктового сценария.",
					"values": [
						{
							"value": "sm",
							"when": "плотные панели и компактные фильтры",
							"metrics": "задаёт productScope"
						},
						{
							"value": "md",
							"when": "формы и основной поток выбора",
							"metrics": "задаёт productScope"
						}
					]
				}
			],
			"composition": [
				{
					"part": "label",
					"rule": "Плавающая подпись с id входит в aria-labelledby триггера"
				},
				{
					"part": "trigger",
					"rule": "Кнопка-поле с текущим значением и стрелкой"
				},
				{
					"part": "value",
					"rule": "Видимый label выбранной опции"
				},
				{
					"part": "menu",
					"rule": "Выпадающая панель со списком"
				},
				{
					"part": "item",
					"rule": "Опция с уникальным value; подсветка и декоративная галочка выбранного"
				},
				{
					"part": "msg",
					"rule": "Описание или ошибка под полем"
				}
			],
			"anatomy": [
				{
					"id": "field",
					"role": "part",
					"desc": "Корень связывает триггер, подпись и сообщение поля."
				},
				{
					"id": "trigger",
					"role": "part",
					"desc": "Кнопка-поле, открывающая список и удерживающая доступное имя."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Постоянная доступная подпись; поднимается при значении или открытом меню."
				},
				{
					"id": "value",
					"role": "label",
					"desc": "Текст выбранной опции либо подсказка пустого состояния."
				},
				{
					"id": "indicator",
					"role": "indicator",
					"desc": "Декоративная стрелка показывает открытое или закрытое меню."
				},
				{
					"id": "menu",
					"role": "part",
					"desc": "Выпадающая listbox-панель со всеми опциями."
				},
				{
					"id": "option",
					"role": "part",
					"desc": "Одна опция; поддерживает подсветку, выбор и декоративную галочку."
				},
				{
					"id": "message",
					"role": "label",
					"required": false,
					"desc": "Описание либо ошибка, программно связанная с триггером."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "меню закрыто",
					"change": "триггер показывает подпись и текущее или пустое значение"
				},
				{
					"id": "hover",
					"when": "указатель над триггером",
					"change": "productScope усиливает тень границы"
				},
				{
					"id": "focus-visible",
					"when": "фокус требует видимого индикатора",
					"change": "внешнее сплошное кольцо не ниже 3:1 дополняет тень"
				},
				{
					"id": "empty",
					"when": "value пуст",
					"change": "подпись занимает строку значения; placeholder появляется после открытия"
				},
				{
					"id": "open",
					"when": "меню раскрыто",
					"change": "«вдавленная» тень, стрелка вверх, label поднят"
				},
				{
					"id": "highlighted",
					"when": "опция под указателем или клавиатурной навигацией",
					"change": "контрастная подложка показывает следующий выбор"
				},
				{
					"id": "selected",
					"when": "опция выбрана",
					"change": "value и подпись остаются в триггере; в меню появляется контрастная галочка"
				},
				{
					"id": "error",
					"when": "передан текст ошибки",
					"change": "под триггером появляется текст, а кнопка получает aria-invalid"
				}
			],
			"statesNotes": [
				"**Нет disabled.** Недоступный селект не показываем."
			],
			"impossible": [
				{
					"combo": "options=[]",
					"why": "Выбирать нечего: компонент не рендерит пустой или disabled-подобный триггер."
				},
				{
					"combo": "повторяющиеся option.value",
					"why": "Value — идентичность опции и ключ списка; каждое значение обязано быть уникальным."
				},
				{
					"combo": "value отсутствует в options",
					"why": "Выбранное значение обязано быть пустым либо ссылаться на существующую опцию."
				},
				{
					"combo": "required без name в HTML-форме",
					"why": "Aria-required сохранится, но Bits UI создаёт нативный form-control только при непустом name."
				}
			],
			"a11y": [
				"Bits UI даёт trigger с listbox-поповером, активной опцией, управлением фокуса и typeahead.",
				"Видимая подпись и текущее значение объединены в `aria-labelledby`; без label потребитель обязан передать aria-label/aria-labelledby.",
				"Ошибка или описание объединяются с внешним `aria-describedby`; error задаёт `aria-invalid=true`.",
				"Required всегда задаёт `aria-required`; нативная form-валидация и отправка значения требуют непустой `name`.",
				"Декоративные стрелка и галочка скрыты от accessibility tree; hint получает имя, связанное с полем.",
				"Триггер, hint и пункты меню имеют цель не меньше 24×24 CSS px."
			],
			"keyboard": [
				{
					"keys": "Enter / Space / ArrowUp / ArrowDown",
					"action": "открывает закрытое меню; стрелка начинает с ближайшего края, если value пуст"
				},
				{
					"keys": "ArrowUp / ArrowDown / Home / End",
					"action": "перемещает подсветку; PageUp/PageDown шагают по длинному viewport"
				},
				{
					"keys": "Enter / Space",
					"action": "выбирает подсвеченную опцию и закрывает одиночный Select"
				},
				{
					"keys": "Escape",
					"action": "закрывает меню без изменения value и возвращает фокус триггеру"
				},
				{
					"keys": "Tab",
					"action": "закрывает меню и продолжает обычный порядок фокуса"
				},
				{
					"keys": "символы",
					"action": "typeahead ищет по началу label; в закрытом Select найденная опция становится value"
				}
			],
			"contrast": [
				{
					"fg": "--c-input-text",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-desc",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-asterisk",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-desc",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-error",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-hint",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-input-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-input-text",
					"bg": "--c-menu-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-text",
					"bg": "--c-menu-item-hover",
					"min": 4.5
				},
				{
					"fg": "--c-select-check",
					"bg": "--c-menu-bg",
					"min": 3
				},
				{
					"fg": "--c-select-check",
					"bg": "--c-menu-item-hover",
					"min": 3
				},
				{
					"fg": "--c-select-menu-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-select-menu-border",
					"bg": "--c-menu-bg",
					"min": 3
				},
				{
					"fg": "--c-hint-text",
					"bg": "--c-hint-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--c-control-asterisk",
				"--c-control-desc",
				"--c-control-error",
				"--c-control-focus",
				"--c-control-hint",
				"--c-control-label",
				"--c-hint-bg",
				"--c-hint-text",
				"--c-input-bg",
				"--c-input-border",
				"--c-input-text",
				"--c-menu-bg",
				"--c-menu-item-hover",
				"--c-select-check",
				"--c-select-menu-border",
				"--control-box-radius",
				"--control-field-min",
				"--control-focus-offset",
				"--control-fs-aux",
				"--control-gap",
				"--control-msg-gap",
				"--control-select-menu-max",
				"--icon-sm",
				"--input-border-w",
				"--input-chevron",
				"--input-chevron-sm",
				"--input-content-pad-top",
				"--input-fs-sm",
				"--input-gap",
				"--input-h-md",
				"--input-h-sm",
				"--input-icon-md",
				"--input-icon-sm",
				"--input-label-fs-active",
				"--input-label-top",
				"--input-label-top-active",
				"--input-label-top-active-sm",
				"--input-label-top-sm",
				"--input-lh",
				"--input-pad-x",
				"--input-pad-x-sm",
				"--input-radius",
				"--input-radius-md",
				"--input-radius-sm",
				"--menu-item-lh",
				"--menu-item-pad-x",
				"--menu-item-radius",
				"--menu-pad",
				"--select-menu-offset",
				"--sh-control-focus",
				"--sh-control-hover",
				"--sh-control-pressed",
				"--sh-input-hover-sm",
				"--sh-input-idle-sm",
				"--sh-input-pressed-sm",
				"--sh-menu",
				"--sh-radio",
				"--t-control"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый",
					"desc": "Подпись плавает; в меню — подсветка и галочка выбранного."
				},
				{
					"id": "Sizes",
					"name": "Размеры",
					"desc": "Канонические `sm / md`; точные метрики задаёт productScope."
				},
				{
					"id": "Field",
					"name": "Поле",
					"desc": "Подсказка, описание, ошибка, обязательность."
				}
			],
			"departures": [
				{
					"what": "Плавающая подпись находится внутри trigger",
					"why": "Компактность AdsCompass сохранена, но подпись больше не только визуальна: её id вместе с value явно формирует accessible name кнопки."
				},
				{
					"what": "Typeahead навигирует, но не превращает Select в поиск",
					"why": "Короткий список можно быстро открыть или выбрать по первым буквам. Фильтрация, произвольный ввод и длинные результаты требуют отдельного autocomplete/combobox-паттерна."
				},
				{
					"what": "Пустой options[] не создаёт неактивный trigger",
					"why": "Канон не показывает недоступные контролы; отсутствие вариантов решается на уровне сценария."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Добавлять `disabled` — недоступный селект не показываем."
				},
				{
					"level": "must-not",
					"guidance": "Делать Select из двух булевых опций — это Switch."
				},
				{
					"level": "must-not",
					"guidance": "Использовать placeholder как валидное значение — это пустое состояние."
				},
				{
					"level": "must",
					"guidance": "Давать каждой опции уникальный value и различимый label; value — пустой либо существует в options."
				},
				{
					"level": "must",
					"guidance": "Для required-поля внутри HTML-формы передавать непустой name."
				},
				{
					"level": "must",
					"guidance": "Без видимого label передавать явный aria-label или aria-labelledby; placeholder не заменяет имя."
				},
				{
					"level": "must",
					"guidance": "Если список перестал быстро сканироваться, использовать поиск/автодополнение, а не расширять Select."
				}
			]
		},
		{
			"kind": "component",
			"id": "textarea",
			"name": "Textarea",
			"status": "stable",
			"since": "2026-05",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Многострочное текстовое поле.",
			"description": "Textarea повторяет оболочку Input и добавляет autoresize, очистку и счётчик символов.",
			"useCases": {
				"when": [
					"Многострочный свободный текст: комментарий, описание, обращение.",
					"Длина ответа заранее неизвестна — `autoresize` для коротких, фиксированная высота для длинных."
				],
				"avoid": [
					"Однострочное значение.",
					"Структурированные данные — это набор отдельных полей, не один большой текст."
				],
				"alternatives": [
					{
						"id": "input",
						"why": "однострочный ввод"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "string",
					"default": "\"\"",
					"desc": "Текст",
					"bindable": true
				},
				{
					"name": "size",
					"type": "sm | md | lg",
					"default": "md",
					"desc": "Высота поля"
				},
				{
					"name": "label",
					"type": "string",
					"desc": "Плавающая подпись"
				},
				{
					"name": "autoresize",
					"type": "boolean",
					"default": "false",
					"desc": "Высота textarea подстраивается под текст"
				},
				{
					"name": "clearable",
					"type": "boolean",
					"default": "false",
					"desc": "Кнопка очистки при значении"
				},
				{
					"name": "characterCounter / maxlength",
					"type": "number",
					"desc": "Лимит и счётчик символов"
				},
				{
					"name": "placeholder",
					"type": "string",
					"desc": "Текст пустого состояния"
				},
				{
					"name": "hint",
					"type": "string",
					"desc": "Подсказка-поповер"
				},
				{
					"name": "description",
					"type": "string",
					"desc": "Текст под полем"
				},
				{
					"name": "error",
					"type": "string",
					"desc": "Текст ошибки (перекрывает description)"
				},
				{
					"name": "readonly",
					"type": "boolean",
					"default": "false",
					"desc": "Только чтение"
				},
				{
					"name": "required",
					"type": "boolean",
					"default": "false",
					"desc": "Звёздочка обязательности"
				}
			],
			"composition": [
				{
					"part": "label",
					"rule": "Плавающая подпись внутри поля"
				},
				{
					"part": "control",
					"rule": "Рамка с многострочным вводом"
				},
				{
					"part": "textarea",
					"rule": "Нативный `<textarea>` без собственной рамки"
				},
				{
					"part": "action",
					"rule": "Кнопка очистки и ручка изменения высоты"
				},
				{
					"part": "msg",
					"rule": "Описание или ошибка под полем; справа может быть счётчик"
				}
			],
			"anatomy": [
				{
					"id": "field",
					"role": "part",
					"desc": "Корень поля связывает контрол, сообщения и вспомогательные действия."
				},
				{
					"id": "control",
					"role": "part",
					"desc": "Визуальная рамка многострочного ввода."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Постоянная доступная подпись, плавающая над введённым текстом."
				},
				{
					"id": "textarea",
					"role": "part",
					"desc": "Нативный `<textarea>` со значением и кареткой."
				},
				{
					"id": "clear-action",
					"role": "part",
					"required": false,
					"desc": "Кнопка очистки непустого редактируемого значения."
				},
				{
					"id": "resize-action",
					"role": "part",
					"desc": "Ручка ручного изменения высоты, если autoresize не управляет ею автоматически."
				},
				{
					"id": "message",
					"role": "label",
					"required": false,
					"desc": "Описание либо ошибка, программно связанная с полем."
				},
				{
					"id": "counter",
					"role": "indicator",
					"required": false,
					"desc": "Счётчик символов рядом с сообщением."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "покой",
					"change": "подпись на месте плейсхолдера"
				},
				{
					"id": "focus",
					"when": "каретка в поле",
					"change": "подпись наверху мелким; «вдавленная» тень"
				},
				{
					"id": "error",
					"when": "есть ошибка",
					"change": "красный текст ошибки под полем"
				},
				{
					"id": "readonly",
					"when": "только чтение",
					"change": "светлый фон без редактирования"
				}
			],
			"statesNotes": [
				"**Нет disabled.** Недоступное поле не показываем (readonly — для значения «только чтение»).",
				"**focus-visible.** При фокусе с клавиатуры поле получает коралловую рамку и свечение."
			],
			"a11y": [
				"`Label` связывает подпись с полем; при `error` — `aria-invalid` и `aria-describedby`.",
				"Счётчик символов — `aria-live=\"polite\"`: озвучивается, не перебивая ввод.",
				"`focus-visible` обязателен: видимое кольцо."
			],
			"keyboard": [
				{
					"keys": "Enter",
					"action": "новая строка — форму не отправляет"
				},
				{
					"keys": "Tab",
					"action": "следующее поле (таб в текст не вставляется)"
				}
			],
			"contrast": [
				{
					"fg": "--c-input-text",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-desc",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-asterisk",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-hint",
					"bg": "--c-input-bg",
					"min": 3
				},
				{
					"fg": "--c-control-desc",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-error",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-counter",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-input-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-input-text",
					"bg": "--c-input-readonly-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-input-readonly-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--c-control-asterisk",
				"--c-control-counter",
				"--c-control-desc",
				"--c-control-error",
				"--c-control-focus",
				"--c-control-hint",
				"--c-control-label",
				"--c-control-on",
				"--c-input-bg",
				"--c-input-border",
				"--c-input-readonly-bg",
				"--c-input-text",
				"--control-fs-aux",
				"--control-gap",
				"--control-h-md",
				"--control-focus-offset",
				"--control-msg-gap",
				"--control-textarea-min",
				"--icon-sm",
				"--input-border-w",
				"--input-gap",
				"--input-label-fs-active",
				"--input-pad-x",
				"--input-radius",
				"--sh-control-focus",
				"--sh-control-hover",
				"--sh-control-pressed",
				"--sh-radio",
				"--t-control",
				"--textarea-h-l",
				"--textarea-h-s",
				"--textarea-label-top",
				"--textarea-lh",
				"--textarea-pad-top"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый"
				},
				{
					"id": "SizesClear",
					"name": "Размеры и очистка"
				},
				{
					"id": "CounterError",
					"name": "Счётчик и ошибка"
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Добавлять `disabled` — для «только чтение» есть `readonly`."
				},
				{
					"level": "must-not",
					"guidance": "Использовать Textarea для однострочного ответа — это Input."
				}
			]
		},
		{
			"kind": "component",
			"id": "color-input",
			"name": "Color Input",
			"status": "experimental",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Поле выбора цвета.",
			"description": "Color Input связывает ручной ввод hex с нативным пикером: изменение одного контрола обновляет другой.",
			"useCases": {
				"when": [
					"Один цвет как значение формы: бренд-цвет, фон, акцент.",
					"Нужны оба способа ввода: ручной hex и системный пикер."
				],
				"avoid": [
					"Палитра из нескольких цветов — это набор отдельных полей.",
					"Выбор из готовых пресетов — покажи варианты, а не пикер."
				],
				"alternatives": [
					{
						"id": "radio",
						"why": "выбор из фиксированных пресетов (`variant=\"images\"`)"
					},
					{
						"id": "input",
						"why": "текстовый код цвета без пикера"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "string",
					"default": "\"#000000\"",
					"desc": "Hex-значение цвета",
					"bindable": true
				},
				{
					"name": "size",
					"type": "sm | md",
					"default": "md",
					"desc": "Общая плотность текстового поля и свотча"
				},
				{
					"name": "label",
					"type": "string",
					"desc": "Плавающая подпись поля"
				},
				{
					"name": "…rest",
					"type": "—",
					"desc": "Прокидывается в Input — свои пропсы плюс всё, что тот принимает (hint, description, error, required, readonly)"
				}
			],
			"composition": [
				{
					"part": "label",
					"rule": "Плавающая подпись внутри поля"
				},
				{
					"part": "control",
					"rule": "Текстовый ввод hex-значения"
				},
				{
					"part": "swatch",
					"rule": "Нативный `<input type=\"color\">` — круглая кнопка пикера справа"
				}
			],
			"anatomy": [
				{
					"id": "field",
					"role": "part",
					"desc": "Общий корень текстового ввода и системного пикера."
				},
				{
					"id": "text-input",
					"role": "part",
					"desc": "Input для ручного ввода и чтения hex-значения."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Доступная плавающая подпись текстового поля."
				},
				{
					"id": "swatch",
					"role": "part",
					"desc": "Нативный `<input type=\"color\">`: показывает значение и открывает системный пикер."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "значение валидно, взаимодействия нет",
					"change": "текст и цвет свотча показывают одно значение"
				},
				{
					"id": "hover",
					"when": "указатель над текстовым полем или свотчем",
					"change": "соответствующий контрол получает productScope-отклик"
				},
				{
					"id": "focus-visible",
					"when": "один из двух контролов получает клавиатурный фокус",
					"change": "видимый индикатор охватывает именно активный контрол"
				},
				{
					"id": "valid-value",
					"when": "ручной ввод становится валидным hex",
					"change": "свотч синхронно принимает введённый цвет"
				},
				{
					"id": "picker-change",
					"when": "цвет подтверждён в системном пикере",
					"change": "его hex записывается обратно в текстовое поле"
				},
				{
					"id": "error",
					"when": "Input получает текст ошибки",
					"change": "ошибка и aria-invalid наследуются от текстового поля"
				}
			],
			"statesNotes": [
				"**Нет disabled.** Недоступное поле не показываем; режим readonly наследуется от текстового Input."
			],
			"behavior": [
				"Текст и свотч связаны: ввод валидного hex красит свотч, выбор в пикере пишет hex в поле.",
				"Подпись поднята, пока поле содержит цвет — обычно это всегда (значение по умолчанию `#000000`).",
				"Свотч — это реальный нативный контрол пикера, а не inline-иконка."
			],
			"a11y": [
				"Текстовый ввод связан с `<label for>` — hex можно ввести с клавиатуры.",
				"У свотча есть `aria-label` с подписью поля.",
				"**Нет disabled.** Недоступное поле не показываем; для «только чтение» — `readonly`."
			],
			"keyboard": [
				{
					"keys": "Tab",
					"action": "между текстовым вводом hex и свотчем"
				},
				{
					"keys": "Enter / Space",
					"action": "на свотче — открыть системный пикер"
				}
			],
			"tokens": [
				"--input-gap",
				"--input-icon-sm",
				"--input-swatch",
				"--sh-color-swatch"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый",
					"desc": "Подпись плавает наверху, пока в поле есть значение цвета. Свотч открывает нативный пикер."
				},
				{
					"id": "NoLabel",
					"name": "Без подписи",
					"desc": "Без label свотч и hex выровнены по центру строки."
				},
				{
					"id": "Field",
					"name": "Поле",
					"desc": "Наследует от Input: подсказку, описание, ошибку, обязательность, только чтение."
				}
			],
			"departures": [
				{
					"what": "Пикер — нативный `<input type=\"color\">`, не кастомная панель",
					"why": "Честный системный контрол: бесплатная доступность и знакомое поведение ОС. Кастомные пикеры — украшение, которое дороже своей пользы."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Добавлять `disabled` — недоступное поле не показываем; для «только чтение» — `readonly`."
				},
				{
					"level": "must-not",
					"guidance": "Складывать в одно поле палитру из нескольких цветов — это набор полей."
				},
				{
					"level": "must",
					"guidance": "Связывать текстовый ввод с `label for`; у свотча — `aria-label` с подписью поля."
				}
			]
		},
		{
			"kind": "component",
			"id": "input-date",
			"name": "InputDate",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Поле выбора одной даты.",
			"description": "Триггер хранит дату в формате `YYYY-MM-DD` и открывает месячный календарь Bits UI.",
			"useCases": {
				"when": [
					"Одна дата: дедлайн, дата публикации, старт кампании.",
					"Значение уходит в форму или API как ISO `YYYY-MM-DD`."
				],
				"avoid": [
					"Диапазон «от — до».",
					"Дата далеко от текущей (год рождения) — листать календарь дольше, чем ввести текстом."
				],
				"alternatives": [
					{
						"id": "input-date-range",
						"why": "диапазон дат с пресетами"
					},
					{
						"id": "input",
						"why": "текстовый ввод далёкой даты"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "string (YYYY-MM-DD)",
					"default": "\"\"",
					"desc": "Выбранная дата",
					"bindable": true
				},
				{
					"name": "size",
					"type": "md | sm",
					"default": "md",
					"desc": "Высота поля (40 / 30px)"
				},
				{
					"name": "label",
					"type": "string",
					"default": "\"Date\"",
					"desc": "Плавающая подпись поля"
				},
				{
					"name": "locale",
					"type": "string",
					"default": "\"en-US\"",
					"desc": "Локаль календаря (названия месяцев/дней)"
				},
				{
					"name": "placeholder",
					"type": "string",
					"default": "\"\"",
					"desc": "Текст в пустом поле"
				},
				{
					"name": "required",
					"type": "boolean",
					"default": "false",
					"desc": "Звёздочка обязательности у label"
				},
				{
					"name": "id",
					"type": "string",
					"default": "uuid",
					"desc": "id триггера для связи с поповером"
				}
			],
			"behavior": [
				"Поповер с календарём открывается кликом или `Enter`/`Space` на поле, закрывается кликом вне, `Esc` или повторным кликом по полю.",
				"Месяц листается стрелками `‹` / `›`; в шапке — «МЕСЯЦ ГОД» капсом.",
				"Неделя начинается с понедельника (заголовки MO–SU); сетка всегда 6 строк.",
				"Выбор дня записывает `value` (формат `YYYY-MM-DD`) и закрывает поповер.",
				"Календарь открывается на месяце текущего значения; при пустом `value` — на текущей дате.",
				"Названия месяцев и дней недели берутся из `locale`."
			],
			"anatomy": [
				{
					"id": "trigger",
					"role": "part",
					"desc": "Кнопка-поле с текущей датой, открывающая календарь."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Плавающая подпись и доступное имя триггера."
				},
				{
					"id": "value",
					"role": "label",
					"desc": "Выбранная ISO-дата либо подсказка пустого состояния."
				},
				{
					"id": "icon",
					"role": "icon",
					"desc": "Декоративная иконка календаря."
				},
				{
					"id": "popover",
					"role": "part",
					"desc": "Панель месячного календаря, связанная с триггером."
				},
				{
					"id": "navigation",
					"role": "part",
					"desc": "Заголовок месяца и кнопки перехода между месяцами."
				},
				{
					"id": "grid",
					"role": "part",
					"desc": "Календарная grid-сетка с заголовками дней недели."
				},
				{
					"id": "day",
					"role": "part",
					"desc": "Интерактивный день текущего месяца."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "покой",
					"change": "bevel-тень, label по центру"
				},
				{
					"id": "hover",
					"when": "наведение",
					"change": "усиленная тень"
				},
				{
					"id": "open",
					"when": "поповер раскрыт",
					"change": "«вдавленная» тень, label поднят, календарь снизу"
				},
				{
					"id": "день вне месяца",
					"when": "хвосты сетки",
					"change": "приглушены (opacity), недоступны"
				},
				{
					"id": "сегодня",
					"when": "текущая дата",
					"change": "жирное число (если не выбран)"
				},
				{
					"id": "наведение на день",
					"when": "курсор на дне месяца",
					"change": "светлая подложка"
				},
				{
					"id": "выбранный день",
					"when": "совпадает с value",
					"change": "тёмная подложка, белый текст"
				}
			],
			"a11y": [
				"Поведение и клавиатура календаря — bits-ui Calendar (стрелки/Enter, роли grid).",
				"Триггер — кнопка с `aria-label`; поповер привязан к ней через Popover.",
				"Стрелки навигации месяца имеют текстовые `aria-label`."
			],
			"keyboard": [
				{
					"keys": "Enter / Space",
					"action": "на поле — открыть/закрыть календарь"
				},
				{
					"keys": "← → ↑ ↓",
					"action": "перемещение по дням сетки"
				},
				{
					"keys": "Enter",
					"action": "выбрать день (закрывает поповер)"
				},
				{
					"keys": "Esc",
					"action": "закрыть без выбора"
				}
			],
			"contrast": [
				{
					"fg": "--c-input-text",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-asterisk",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-cal-nav",
					"bg": "--c-input-bg",
					"min": 3
				},
				{
					"fg": "--c-input-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-cal-day-text",
					"bg": "--c-cal-bg",
					"min": 4.5
				},
				{
					"fg": "--c-cal-day-selected-text",
					"bg": "--c-cal-day-selected-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--c-cal-bg",
				"--c-cal-day-hover",
				"--c-cal-day-selected-bg",
				"--c-cal-day-selected-text",
				"--c-cal-day-text",
				"--c-cal-heading",
				"--c-cal-nav",
				"--c-cal-range-bg",
				"--c-cal-weekday",
				"--c-control-asterisk",
				"--c-control-focus",
				"--c-control-label",
				"--c-control-on",
				"--c-input-bg",
				"--c-input-border",
				"--c-input-text",
				"--cal-day-radius",
				"--cal-day-size",
				"--cal-fs-day",
				"--cal-fs-heading",
				"--cal-fs-weekday",
				"--cal-popover-w",
				"--control-focus-offset",
				"--control-gap",
				"--icon-button",
				"--input-border-w",
				"--input-content-pad-top",
				"--input-fs-sm",
				"--input-gap",
				"--input-h",
				"--input-h-s",
				"--input-icon-md",
				"--input-icon-sm",
				"--input-label-fs-active",
				"--input-label-top",
				"--input-label-top-active",
				"--input-label-top-active-sm",
				"--input-label-top-sm",
				"--input-lh",
				"--input-pad-x",
				"--input-pad-x-sm",
				"--input-radius",
				"--input-radius-md",
				"--input-radius-sm",
				"--opacity-cal-muted",
				"--sh-cal-popover",
				"--sh-control-focus",
				"--sh-control-hover",
				"--sh-control-pressed",
				"--sh-input-hover-sm",
				"--sh-input-idle-sm",
				"--sh-input-pressed-sm",
				"--sh-radio",
				"--t-control"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый"
				},
				{
					"id": "States",
					"name": "Пустое и со значением"
				},
				{
					"id": "SizeSm",
					"name": "Размер sm"
				}
			],
			"departures": [
				{
					"what": "Неделя всегда с понедельника, сетка всегда 6 строк",
					"why": "`locale` меняет названия месяцев и дней, но не геометрию: высота поповера стабильна, раскладка предсказуема. Привычнее locale-зависимое начало недели."
				},
				{
					"what": "Значение — строка ISO `YYYY-MM-DD`, не Date-объект",
					"why": "Сериализуемо в форму и API без таймзонных сюрпризов. Привычнее отдавать Date — мы сознательно не отдаём."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Значение всегда в ISO-формате `YYYY-MM-DD`."
				},
				{
					"level": "must-not",
					"guidance": "Использовать для диапазона дат — для него есть `input-date-range`."
				}
			]
		},
		{
			"kind": "component",
			"id": "input-date-range",
			"name": "InputDateRange",
			"status": "experimental",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Поле выбора диапазона дат.",
			"description": "Триггер открывает пресеты и два независимых календаря. Первый клик задаёт начало диапазона, второй — конец.",
			"useCases": {
				"when": [
					"Период для фильтра или отчёта «от — до»; типовые интервалы — пресетами.",
					"Границы со временем — `withTime` добавляет `T HH:MM` в значение."
				],
				"avoid": [
					"Одна дата — диапазон здесь избыточен."
				],
				"alternatives": [
					{
						"id": "input-date",
						"why": "одна дата"
					}
				]
			},
			"props": [
				{
					"name": "start",
					"type": "string (YYYY-MM-DD)",
					"default": "\"\"",
					"desc": "Начало диапазона",
					"bindable": true
				},
				{
					"name": "end",
					"type": "string (YYYY-MM-DD)",
					"default": "\"\"",
					"desc": "Конец диапазона",
					"bindable": true
				},
				{
					"name": "startTime",
					"type": "string (HH:MM)",
					"default": "\"00:00\"",
					"desc": "Время начала",
					"bindable": true
				},
				{
					"name": "endTime",
					"type": "string (HH:MM)",
					"default": "\"00:00\"",
					"desc": "Время конца",
					"bindable": true
				},
				{
					"name": "withTime",
					"type": "boolean",
					"default": "true",
					"desc": "Показывать поля времени и добавлять время в значение"
				},
				{
					"name": "label",
					"type": "string",
					"default": "\"Date\"",
					"desc": "Плавающая подпись поля"
				},
				{
					"name": "required",
					"type": "boolean",
					"default": "false",
					"desc": "Звёздочка обязательности у label"
				},
				{
					"name": "locale",
					"type": "string",
					"default": "\"en-US\"",
					"desc": "Локаль календаря и пресетов недели"
				},
				{
					"name": "id",
					"type": "string",
					"default": "uuid",
					"desc": "id триггера для связи с поповером"
				}
			],
			"extra": [
				{
					"title": "Выбор диапазона (как в Airbnb)",
					"list": [
						"Клик по дню в **любом** из двух календарей. 1-й клик задаёт начало и сбрасывает конец, 2-й — конец.",
						"Если 2-й клик пришёлся **раньше** начала — он становится новым началом (конец ждёт следующего клика).",
						"Клик при уже выбранном диапазоне начинает выбор заново.",
						"При наведении (мышью) до фиксации конца показывается **предпросмотр** ленты от начала до дня под курсором.",
						"Календари листаются **независимо**. Левый открывает месяц начала, правый — месяц конца.",
						"`start` и `end` включают обе границы; формат `YYYY-MM-DD`."
					]
				},
				{
					"title": "Пресеты",
					"prose": "Колонка слева задаёт обе границы разом и переводит календари на их месяцы. Считаются от текущей даты и `locale`:",
					"list": [
						"Today, Yesterday, This week, Last week, Last 7 days, This month, Last 30 days, Last month, Last year."
					]
				},
				{
					"title": "Время",
					"list": [
						"При `withTime` под каждым календарём — поле **Time**: левое = время начала, правое = время конца.",
						"Клик по полю раскрывает панель с колонками **H** (00–23) и **min** (00–59); выбранное число — тёмное; **APPLY** закрывает панель.",
						"Значение поля при `withTime` — дата-время через `T`: `2024-01-01T22:00 — 2024-04-12T23:59`.",
						"Без `withTime` поля времени скрыты, значение — только даты."
					]
				},
				{
					"title": "Состояния дня",
					"table": {
						"head": [
							"Состояние",
							"Что меняется"
						],
						"rows": [
							[
								"край диапазона",
								"тёмная подложка, белый текст (на обоих календарях)"
							],
							[
								"внутри диапазона",
								"светлая лента-подложка, обычный текст"
							],
							[
								"предпросмотр",
								"лента до дня под курсором, пока конец не зафиксирован"
							],
							[
								"день вне месяца",
								"приглушён, недоступен"
							],
							[
								"сегодня",
								"жирное число"
							]
						]
					}
				},
				{
					"title": "Поле и футер",
					"list": [
						"Свёрнутое поле показывает «from — to» (с временем при `withTime`), иконку календаря и крестик очистки при заполненном диапазоне.",
						"Футер: «**Selected days: N**» — число дней включительно; **Cancel** откатывает к состоянию на момент открытия; **Apply** фиксирует и закрывает."
					]
				}
			],
			"anatomy": [
				{
					"id": "trigger",
					"role": "part",
					"desc": "Кнопка-поле с текущими границами, открывающая редактор диапазона."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Плавающая подпись и доступное имя триггера."
				},
				{
					"id": "value",
					"role": "label",
					"desc": "Строка start — end, дополненная временем при withTime."
				},
				{
					"id": "clear-action",
					"role": "part",
					"required": false,
					"desc": "Кнопка очистки заполненного диапазона."
				},
				{
					"id": "popover",
					"role": "part",
					"desc": "Широкая панель полного редактора диапазона."
				},
				{
					"id": "presets",
					"role": "part",
					"desc": "Колонка готовых интервалов, задающих обе границы."
				},
				{
					"id": "calendars",
					"role": "part",
					"desc": "Две независимо листаемые календарные grid-сетки."
				},
				{
					"id": "time-fields",
					"role": "part",
					"required": false,
					"desc": "Редакторы времени начала и конца при withTime."
				},
				{
					"id": "footer",
					"role": "part",
					"desc": "Счётчик выбранных дней и действия Cancel / Apply."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "поповер закрыт и диапазон заполнен",
					"change": "триггер показывает обе зафиксированные границы"
				},
				{
					"id": "empty",
					"when": "start и end пусты",
					"change": "триггер показывает пустое состояние без кнопки очистки"
				},
				{
					"id": "hover",
					"when": "указатель над триггером",
					"change": "productScope усиливает его тень"
				},
				{
					"id": "focus-visible",
					"when": "фокус требует видимого индикатора",
					"change": "триггер получает контрастное внешнее кольцо"
				},
				{
					"id": "open",
					"when": "редактор диапазона раскрыт",
					"change": "видны пресеты, два календаря и футер"
				},
				{
					"id": "selecting",
					"when": "start выбран, end ещё пуст",
					"change": "следующий допустимый день завершит диапазон либо переназначит начало"
				},
				{
					"id": "preview",
					"when": "при выборе конца указатель над допустимым днём",
					"change": "временная лента показывает будущий диапазон без фиксации"
				},
				{
					"id": "complete",
					"when": "обе границы выбраны",
					"change": "края и внутренняя лента различаются формой и цветом"
				},
				{
					"id": "time-open",
					"when": "открыт редактор времени одной границы",
					"change": "колонки часов и минут получают собственный выбор и Apply"
				}
			],
			"statesNotes": [
				"Cancel откатывает черновик к состоянию на момент открытия; Apply фиксирует его и закрывает поповер."
			],
			"a11y": [
				"Сетки, клавиатура и роли grid — две bits-ui Calendar; диапазон ведётся поверх них.",
				"Дропдауны месяца и года — нативные `<select>` с `aria-label`.",
				"Предпросмотр по наведению — визуальное усиление; выбор полностью работает кликом и с клавиатуры без мыши."
			],
			"keyboard": [
				{
					"keys": "Enter / Space",
					"action": "на поле — открыть поповер; на дне — задать начало/конец"
				},
				{
					"keys": "← → ↑ ↓",
					"action": "перемещение по дням внутри календаря"
				},
				{
					"keys": "Tab",
					"action": "по элементам поповера: пресеты, дропдауны, сетки, время, футер"
				},
				{
					"keys": "Esc",
					"action": "закрыть поповер (без Apply значение не фиксируется)"
				}
			],
			"contrast": [
				{
					"fg": "--c-input-text",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-asterisk",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-hint",
					"bg": "--c-input-bg",
					"min": 3
				},
				{
					"fg": "--c-cal-nav",
					"bg": "--c-input-bg",
					"min": 3
				},
				{
					"fg": "--c-input-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-cal-day-selected-text",
					"bg": "--c-cal-day-selected-bg",
					"min": 4.5
				},
				{
					"fg": "--c-cal-time-selected-text",
					"bg": "--c-cal-time-selected-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--c-cal-bg",
				"--c-cal-day-hover",
				"--c-cal-day-selected-bg",
				"--c-cal-day-selected-text",
				"--c-cal-day-text",
				"--c-cal-dropdown-border",
				"--c-cal-nav",
				"--c-cal-range-bg",
				"--c-cal-time-hover",
				"--c-cal-time-selected-bg",
				"--c-cal-time-selected-text",
				"--c-cal-weekday",
				"--c-control-asterisk",
				"--c-control-focus",
				"--c-control-hint",
				"--c-control-label",
				"--c-control-on",
				"--c-input-bg",
				"--c-input-border",
				"--c-input-text",
				"--cal-band-radius",
				"--cal-day-radius",
				"--cal-day-size",
				"--cal-dropdown-radius",
				"--cal-fs-day",
				"--cal-fs-dropdown",
				"--cal-fs-weekday",
				"--cal-month-w",
				"--cal-presets-max-h",
				"--cal-presets-w",
				"--cal-select-h",
				"--cal-select-pad-r",
				"--cal-time-num",
				"--cal-time-scroll-h",
				"--cal-time-w",
				"--cal-year-w",
				"--control-focus-offset",
				"--control-fs-aux",
				"--control-gap",
				"--icon-button",
				"--icon-sm",
				"--input-content-pad-top",
				"--input-fs-sm",
				"--input-gap",
				"--input-h",
				"--input-border-w",
				"--input-h-s",
				"--input-icon-md",
				"--input-icon-sm",
				"--input-label-fs-active",
				"--input-label-top",
				"--input-label-top-active",
				"--input-label-top-active-sm",
				"--input-label-top-sm",
				"--input-lh",
				"--input-pad-x",
				"--input-pad-x-sm",
				"--input-radius",
				"--input-radius-md",
				"--input-radius-sm",
				"--opacity-cal-muted",
				"--sh-cal-popover",
				"--sh-control-focus",
				"--sh-control-hover",
				"--sh-control-pressed",
				"--sh-input-hover-sm",
				"--sh-input-idle-sm",
				"--sh-input-pressed-sm",
				"--sh-menu",
				"--sh-radio",
				"--t-control"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый"
				},
				{
					"id": "Empty",
					"name": "Пустой"
				}
			],
			"departures": [
				{
					"what": "Клик раньше начала переназначает начало, а не сбрасывает выбор",
					"why": "Модель Airbnb: любой клик осмыслен, диапазон нельзя «сломать» неудачным кликом. Привычнее жёсткий порядок «сначала от, потом до»."
				},
				{
					"what": "Значение фиксируется только по Apply",
					"why": "Диапазон — составное значение: живое применение дёргало бы фильтр после первого клика. Cancel откатывает к состоянию на момент открытия."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Значения в ISO-формате `YYYY-MM-DD`, при `withTime` — дата-время через `T`; диапазон включает обе границы."
				},
				{
					"level": "must-not",
					"guidance": "Использовать для одной даты — для неё есть `input-date`."
				},
				{
					"level": "must-not",
					"guidance": "Прятать поля времени, если время значимо для фильтра."
				}
			]
		},
		{
			"kind": "component",
			"id": "checkbox",
			"name": "Checkbox",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"authored"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Выбирает один независимый бинарный признак в форме.",
			"description": "Checkbox сохраняет решение до отправки формы. `indeterminate` показывает частичный выбор агрегата, но не создаёт третье значение.",
			"useCases": {
				"when": [
					"Один независимый признак применяется при отправке формы: согласие, подписка или дополнительная опция.",
					"Родитель «Выбрать всё» отражает частичный выбор дочернего списка через indeterminate."
				],
				"avoid": [
					"Изменение должно примениться мгновенно без отдельной отправки.",
					"Пользователь выбирает ровно один вариант из взаимоисключающего набора.",
					"Несколько связанных признаков нуждаются в общей подписи и общей валидации."
				],
				"alternatives": [
					{
						"id": "switch",
						"why": "настройка применяется немедленно"
					},
					{
						"id": "radio",
						"why": "нужно выбрать ровно один взаимоисключающий вариант"
					},
					{
						"id": "checkbox-group",
						"why": "несколько связанных признаков образуют один вопрос"
					}
				]
			},
			"props": [
				{
					"name": "checked",
					"type": "boolean",
					"default": "false",
					"desc": "Выбран ли независимый признак",
					"bindable": true
				},
				{
					"name": "indeterminate",
					"type": "boolean",
					"default": "false",
					"desc": "Частичный выбор агрегата; не третье пользовательское значение",
					"bindable": true
				},
				{
					"name": "label",
					"type": "string",
					"desc": "Обязательная видимая подпись и accessible name; без неё компонент не рендерится"
				},
				{
					"name": "hint",
					"type": "string",
					"desc": "Дополнительная подсказка-поповер с именем, связанным с полем"
				},
				{
					"name": "description",
					"type": "string",
					"desc": "Нейтральное описание; скрывается при наличии error"
				},
				{
					"name": "error",
					"type": "string",
					"desc": "Ошибка, связанная через aria-describedby; перекрывает description"
				},
				{
					"name": "required",
					"type": "boolean",
					"default": "false",
					"desc": "Видимый знак и aria-required; form-валидация требует name"
				},
				{
					"name": "value",
					"type": "string",
					"default": "\"on\"",
					"desc": "Значение выбранного поля при отправке формы"
				},
				{
					"name": "name",
					"type": "string",
					"desc": "Имя form-control; включает hidden input Bits UI"
				}
			],
			"composition": [
				{
					"part": "root",
					"rule": "Интерактивная область не меньше 24×24 CSS px с ролью checkbox"
				},
				{
					"part": "box",
					"rule": "Визуальный бокс productScope внутри увеличенной области цели"
				},
				{
					"part": "mark",
					"rule": "Галочка checked или минус indeterminate; форма дополняет цвет"
				},
				{
					"part": "label",
					"rule": "Обязательная видимая подпись, связанная с root через label/for"
				},
				{
					"part": "hint",
					"rule": "Необязательная контекстная подсказка рядом с подписью"
				},
				{
					"part": "message",
					"rule": "Один слот под error либо description; error имеет приоритет"
				}
			],
			"anatomy": [
				{
					"id": "control",
					"role": "part",
					"desc": "Интерактивный root с ролью checkbox и увеличенной областью цели."
				},
				{
					"id": "box",
					"role": "part",
					"desc": "Визуальная рамка productScope внутри области цели."
				},
				{
					"id": "mark",
					"role": "indicator",
					"required": false,
					"desc": "Галочка checked либо минус indeterminate; форма дополняет цвет."
				},
				{
					"id": "label",
					"role": "label",
					"desc": "Обязательная видимая подпись и accessible name решения."
				},
				{
					"id": "message",
					"role": "label",
					"required": false,
					"desc": "Описание либо ошибка, программно связанная с checkbox."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "checked=false и нет взаимодействия",
					"change": "пустой бокс с различимой границей"
				},
				{
					"id": "hover",
					"when": "указатель над root",
					"change": "productScope усиливает тень бокса"
				},
				{
					"id": "pressed",
					"when": "root удерживается",
					"change": "бокс получает вдавленную тень до отпускания"
				},
				{
					"id": "focus-visible",
					"when": "фокус требует видимого индикатора",
					"change": "внешнее сплошное кольцо не ниже 3:1 дополняет тень"
				},
				{
					"id": "checked",
					"when": "checked=true",
					"change": "контрастная заливка и галочка подтверждают выбор"
				},
				{
					"id": "indeterminate",
					"when": "часть дочерних значений выбрана",
					"change": "та же заливка и минус показывают агрегированное состояние"
				},
				{
					"id": "error",
					"when": "передан текст ошибки",
					"change": "показывается error, description скрывается, root получает aria-invalid"
				}
			],
			"statesNotes": [
				"**Indeterminate не третье значение.** Оно вычисляется только для агрегата и при активации разрешается в checked.",
				"**Нет disabled.** Недоступную опцию не показываем, а не гасим."
			],
			"impossible": [
				{
					"combo": "label пуст",
					"why": "Независимое решение без видимого вопроса неоднозначно; компонент не рендерится."
				},
				{
					"combo": "checked=true + indeterminate=true",
					"why": "Частичный и полный выбор противоречат друг другу; indeterminate визуально победит, но потребитель обязан передавать одно состояние."
				},
				{
					"combo": "indeterminate у независимой опции",
					"why": "Частичность имеет смысл только как производное состояние агрегата «Выбрать всё»."
				},
				{
					"combo": "required без name в HTML-форме",
					"why": "Aria-required сохранится, но Bits UI создаёт нативный form-control только при непустом name."
				}
			],
			"a11y": [
				"Bits UI задаёт role=checkbox, aria-checked=true/false/mixed, aria-required и управляет фокусом.",
				"Видимая подпись обязательна и связана через label/for; клик по тексту активирует тот же root.",
				"При непустом name Bits UI создаёт нативный checkbox для отправки и constraint validation формы.",
				"Error или description объединяется с внешним aria-describedby; error задаёт aria-invalid=true.",
				"Интерактивный root и подпись имеют область не меньше 24×24 CSS px; визуальный бокс может быть компактнее.",
				"Checked и indeterminate различаются не только цветом, но и формой отметки: галочка против минуса."
			],
			"keyboard": [
				{
					"keys": "Space",
					"action": "переключает unchecked/checked; indeterminate разрешает в checked"
				},
				{
					"keys": "Enter",
					"action": "не переключает checkbox с type=button"
				},
				{
					"keys": "Tab / Shift+Tab",
					"action": "перемещает фокус в обычном порядке формы"
				}
			],
			"contrast": [
				{
					"fg": "--c-control-mark",
					"bg": "--c-control-on",
					"min": 3
				},
				{
					"fg": "--c-control-border",
					"bg": "--c-control-bg",
					"min": 3
				},
				{
					"fg": "--c-control-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-on",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-asterisk",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-desc",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-error",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-hint",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-hint-text",
					"bg": "--c-hint-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--c-control-asterisk",
				"--c-control-bg",
				"--c-control-border",
				"--c-control-desc",
				"--c-control-error",
				"--c-control-focus",
				"--c-control-hint",
				"--c-control-label",
				"--c-control-mark",
				"--c-control-on",
				"--c-hint-bg",
				"--c-hint-text",
				"--control-box",
				"--control-box-radius",
				"--control-focus-offset",
				"--control-fs-aux",
				"--control-gap",
				"--control-mark",
				"--control-msg-gap",
				"--sh-control",
				"--sh-control-focus",
				"--sh-control-hover",
				"--sh-control-on",
				"--sh-control-pressed",
				"--t-control"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый",
					"desc": "Независимое решение с обязательной видимой подписью."
				},
				{
					"id": "States",
					"name": "Состояния выбора",
					"desc": "Unchecked, checked и производное indeterminate без противоречащих флагов."
				},
				{
					"id": "Field",
					"name": "Поле",
					"desc": "Required с name, подсказка, description и приоритетный error."
				},
				{
					"id": "Indeterminate",
					"name": "Выбрать всё",
					"desc": "Родитель вычисляет checked и indeterminate из дочерних значений."
				}
			],
			"departures": [
				{
					"what": "Визуальный бокс productScope меньше интерактивной области",
					"why": "Компактная геометрия AdsCompass сохранена внутри канонической цели не меньше 24×24 CSS px."
				},
				{
					"what": "Indeterminate доступен отдельным bindable-флагом",
					"why": "Это повторяет Bits UI и упрощает агрегат «Выбрать всё», но состояние всегда вычисляется из детей и не хранится как третий ответ."
				},
				{
					"what": "Обёртка не допускает Checkbox без видимой подписи",
					"why": "Иконкоподобный checkbox скрывает смысл бинарного решения; нестандартную композицию собирают на primitive-уровне вне публичной обёртки."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Добавлять disabled — недоступную опцию не показываем, а не гасим."
				},
				{
					"level": "must-not",
					"guidance": "Использовать indeterminate как третье пользовательское значение или задавать его независимой опции."
				},
				{
					"level": "must",
					"guidance": "Передавать непустой label; без видимого вопроса Checkbox не рендерится."
				},
				{
					"level": "must",
					"guidance": "Для required-поля внутри HTML-формы передавать непустой name."
				},
				{
					"level": "must",
					"guidance": "В агрегате вычислять checked и indeterminate из дочерних значений и никогда не делать оба true."
				},
				{
					"level": "should",
					"guidance": "Формулировать label как самостоятельное утверждение, которое однозначно читается вместе с выбранным состоянием."
				}
			]
		},
		{
			"kind": "component",
			"id": "checkbox-group",
			"name": "Checkbox Group",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Множественный выбор из набора с общей подписью.",
			"description": "Checkbox Group хранит массив выбранных значений и объединяет пункты общим вопросом.",
			"useCases": {
				"when": [
					"Множественный выбор из короткого набора с общей темой: 2–7 пунктов под одной подписью."
				],
				"avoid": [
					"Один независимый признак — группа не нужна.",
					"Выбор ровно одного значения.",
					"Длинный список, где нужны поиск и обзор выбранного."
				],
				"alternatives": [
					{
						"id": "checkbox",
						"why": "один признак"
					},
					{
						"id": "radio",
						"why": "выбор одного значения"
					},
					{
						"id": "dualist",
						"why": "множественный выбор из длинного списка"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "string[]",
					"default": "[]",
					"desc": "Выбранные значения",
					"bindable": true
				},
				{
					"name": "items",
					"type": "{ value, label }[]",
					"default": "[]",
					"desc": "Набор пунктов"
				},
				{
					"name": "legend",
					"type": "string",
					"desc": "Общая подпись группы"
				},
				{
					"name": "name",
					"type": "string",
					"desc": "Общее имя поля формы"
				}
			],
			"composition": [
				{
					"part": "legend",
					"rule": "подпись группы (Checkbox.GroupLabel)"
				},
				{
					"part": "item",
					"rule": "пункт — тот же визуал, что одиночный Checkbox"
				}
			],
			"compositionNote": "**Нет disabled.** Недоступные пункты не показываем.",
			"anatomy": [
				{
					"id": "group",
					"role": "part",
					"desc": "Группа связанных независимых решений с общим form-name."
				},
				{
					"id": "legend",
					"role": "label",
					"required": false,
					"desc": "Общий видимый вопрос и доступное имя группы."
				},
				{
					"id": "item",
					"role": "part",
					"desc": "Один пункт с уникальным value."
				},
				{
					"id": "control",
					"role": "part",
					"desc": "Интерактивный checkbox конкретного пункта."
				},
				{
					"id": "mark",
					"role": "indicator",
					"required": false,
					"desc": "Галочка выбранного пункта."
				},
				{
					"id": "item-label",
					"role": "label",
					"desc": "Видимая подпись и accessible name пункта."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "группа показана без активного взаимодействия",
					"change": "каждый пункт отражает собственное checked-состояние"
				},
				{
					"id": "empty",
					"when": "value — пустой массив",
					"change": "все пункты остаются unchecked"
				},
				{
					"id": "hover",
					"when": "указатель над пунктом",
					"change": "отклик получает только его визуальный бокс"
				},
				{
					"id": "focus-visible",
					"when": "пункт получает клавиатурный фокус",
					"change": "видимое кольцо отмечает только текущий checkbox"
				},
				{
					"id": "checked",
					"when": "value содержит item.value",
					"change": "пункт получает контрастную заливку и галочку"
				}
			],
			"statesNotes": [
				"**Нет disabled.** Недоступные пункты не показываем."
			],
			"a11y": [
				"Группа и связь подписи с пунктами — из Bits UI (Checkbox.Group).",
				"Общий `name` отправляет массив значений формой."
			],
			"keyboard": [
				{
					"keys": "Tab",
					"action": "между пунктами группы"
				},
				{
					"keys": "Space",
					"action": "переключить пункт"
				}
			],
			"tokens": [
				"--c-control-bg",
				"--c-control-label",
				"--c-control-mark",
				"--c-control-on",
				"--control-box",
				"--control-box-radius",
				"--control-gap",
				"--control-group-gap",
				"--control-mark",
				"--sh-control",
				"--sh-control-focus",
				"--sh-control-hover",
				"--sh-control-on",
				"--t-control"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовая группа",
					"desc": "Общая подпись через `legend`, выбор нескольких пунктов."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Добавлять `disabled` — недоступные пункты не показываем."
				},
				{
					"level": "must-not",
					"guidance": "Использовать группу для выбора одного значения — это `radio`."
				}
			]
		},
		{
			"kind": "component",
			"id": "radio",
			"name": "Radio",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"authored"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Выбирает ровно одно значение из видимого взаимоисключающего набора.",
			"description": "Radio показывает все варианты до выбора и хранит одно существующее значение. Вопрос и валидация относятся ко всей группе.",
			"useCases": {
				"when": [
					"Небольшой набор взаимоисключающих вариантов важно видеть и сравнивать до выбора.",
					"Изображение является существенной частью сравнения — используется variant=images с подписью каждой плитки."
				],
				"avoid": [
					"Можно выбрать несколько независимых значений.",
					"Набор слишком длинный для быстрого сканирования целиком.",
					"Переключение мгновенно меняет режим текущего представления, а не значение формы."
				],
				"alternatives": [
					{
						"id": "checkbox-group",
						"why": "можно выбрать несколько значений"
					},
					{
						"id": "select",
						"why": "одно значение выбирается из более длинного списка компактно"
					},
					{
						"id": "segmented-control",
						"why": "режим текущего представления переключается мгновенно"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "string",
					"default": "\"\"",
					"desc": "Пусто до решения либо value существующего item; неизвестное сбрасывается в empty",
					"bindable": true
				},
				{
					"name": "items",
					"type": "{ value, label, description?, image? }[]",
					"default": "[]",
					"desc": "Опции с уникальными строковыми value; пустой набор не рендерит группу"
				},
				{
					"name": "label",
					"type": "string",
					"desc": "Обязательный видимый вопрос и accessible name radiogroup"
				},
				{
					"name": "variant",
					"type": "text | images",
					"default": "text",
					"desc": "Круг с подписью либо плитка с обязательным изображением"
				},
				{
					"name": "orientation",
					"type": "vertical | horizontal",
					"default": "vertical",
					"desc": "Раскладка text-варианта; images всегда переносится по строкам"
				},
				{
					"name": "hint",
					"type": "string",
					"desc": "Контекстная подсказка к вопросу группы"
				},
				{
					"name": "description",
					"type": "string",
					"desc": "Нейтральное описание группы; скрывается при наличии error"
				},
				{
					"name": "error",
					"type": "string",
					"desc": "Ошибка группы, связанная через aria-describedby"
				},
				{
					"name": "required",
					"type": "boolean",
					"default": "false",
					"desc": "Видимый знак и aria-required; form-валидация требует name"
				},
				{
					"name": "name",
					"type": "string",
					"desc": "Имя form-control; включает hidden input Bits UI"
				}
			],
			"axes": [
				{
					"axis": "variant",
					"title": "Представление",
					"desc": "Способ сравнения вариантов, не их важность.",
					"rationale": "`text` — базовый и самый компактный способ. `images` публикуется только когда визуал несёт информацию, но каждый вариант всё равно имеет текстовую подпись.",
					"values": [
						{
							"value": "text",
							"when": "варианты различаются формулировками"
						},
						{
							"value": "images",
							"when": "варианты нужно сравнивать визуально"
						}
					]
				},
				{
					"axis": "orientation",
					"title": "Раскладка text-варианта",
					"desc": "Направление визуального потока; клавиатура Bits UI принимает все стрелки в обеих раскладках.",
					"rationale": "`vertical` легче сканировать и остаётся дефолтом. `horizontal` уместен только для коротких подписей, которые помещаются в строку без потери сравнимости.",
					"values": [
						{
							"value": "vertical",
							"when": "обычный вопрос формы или подписи разной длины"
						},
						{
							"value": "horizontal",
							"when": "короткий набор коротких подписей"
						}
					]
				}
			],
			"composition": [
				{
					"part": "label",
					"rule": "Обязательный видимый вопрос с id задаёт accessible name radiogroup"
				},
				{
					"part": "group",
					"rule": "Один tab-stop, roving focus и одно выбранное value"
				},
				{
					"part": "item",
					"rule": "Уникальное значение, видимая подпись и необязательное связанное описание"
				},
				{
					"part": "control",
					"rule": "В text-варианте визуальный круг productScope расположен внутри цели 24×24"
				},
				{
					"part": "tile",
					"rule": "В images-варианте изображение декоративно для AT, а имя задаёт видимая подпись"
				},
				{
					"part": "message",
					"rule": "Один слот под error либо description группы; error имеет приоритет"
				}
			],
			"anatomy": [
				{
					"id": "group",
					"role": "part",
					"desc": "Radiogroup с одним tab-stop и одним выбранным значением."
				},
				{
					"id": "label",
					"role": "label",
					"desc": "Обязательный видимый вопрос и accessible name всей группы."
				},
				{
					"id": "hint",
					"role": "part",
					"required": false,
					"desc": "Контекстная подсказка к вопросу группы."
				},
				{
					"id": "item",
					"role": "part",
					"desc": "Один взаимоисключающий ответ с уникальным value."
				},
				{
					"id": "control",
					"role": "part",
					"desc": "Визуальный круг text-варианта либо интерактивная плитка images-варианта."
				},
				{
					"id": "item-label",
					"role": "label",
					"desc": "Видимая подпись и accessible name ответа."
				},
				{
					"id": "item-description",
					"role": "label",
					"required": false,
					"desc": "Дополнительное описание конкретного ответа."
				},
				{
					"id": "message",
					"role": "label",
					"required": false,
					"desc": "Описание либо ошибка всего взаимоисключающего вопроса."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "группа показана и value существует",
					"change": "один item checked, остальные unchecked"
				},
				{
					"id": "empty",
					"when": "value пуст",
					"change": "все items unchecked; первый остаётся единственным tab-stop без автоматического выбора"
				},
				{
					"id": "hover",
					"when": "указатель над item",
					"change": "круг или плитка получает productScope-подсветку"
				},
				{
					"id": "pressed",
					"when": "item удерживается",
					"change": "вдавленная тень подтверждает активацию до выбора"
				},
				{
					"id": "focus-visible",
					"when": "фокус требует видимого индикатора",
					"change": "внешнее сплошное кольцо не ниже 3:1 охватывает текущий item"
				},
				{
					"id": "checked",
					"when": "value совпадает с item.value",
					"change": "круг получает заливку и точку, плитка — контрастную рамку"
				},
				{
					"id": "error",
					"when": "передан текст ошибки",
					"change": "radiogroup получает aria-invalid и связанное сообщение вместо description"
				}
			],
			"statesNotes": [
				"**Empty — состояние вопроса до решения.** После выбора unchecked остаётся локальным состоянием всех пунктов, кроме единственного checked.",
				"**Нет disabled.** Недоступный вариант не показываем."
			],
			"impossible": [
				{
					"combo": "label пуст или items=[]",
					"why": "Без вопроса либо вариантов решение невозможно; компонент не рендерится."
				},
				{
					"combo": "повторяющиеся item.value",
					"why": "Value задаёт идентичность, keyed-рендер и выбранный пункт; каждое значение обязано быть уникальным."
				},
				{
					"combo": "value отсутствует в items",
					"why": "Устаревшее значение сбрасывается в empty; группа не выбирает новый ответ за пользователя."
				},
				{
					"combo": "variant=images без image у любого item",
					"why": "Неполный визуальный набор нельзя честно сравнить; группа не рендерится."
				},
				{
					"combo": "required или error на отдельном item",
					"why": "Обязательность и валидация принадлежат взаимоисключающему вопросу целиком, а не одному ответу."
				},
				{
					"combo": "required без name в HTML-форме",
					"why": "Aria-required сохранится, но нативная constraint validation требует hidden input с name."
				}
			],
			"a11y": [
				"Bits UI задаёт role=radiogroup/radio, aria-checked, roving tabindex и циклическую навигацию после выбора.",
				"В empty уже статический HTML оставляет tab-stop только первому item; гидрация не меняет доступный порядок.",
				"Видимый вопрос обязателен и формирует aria-labelledby группы; hint получает имя, связанное с этим вопросом.",
				"Подпись text-item связана через label/for; описание каждого item — через aria-describedby.",
				"Плитка получает имя из видимой подписи, а img имеет пустой alt, чтобы не дублировать тот же смысл.",
				"Error или description группы объединяется с внешним aria-describedby; error задаёт aria-invalid=true.",
				"При непустом name Bits UI создаёт один hidden input для отправки текущего value и required-валидации.",
				"Text-item имеет цель не меньше 24×24 CSS px; плитка превышает минимум своей композицией."
			],
			"keyboard": [
				{
					"keys": "Tab / Shift+Tab",
					"action": "входит в группу одним tab-stop и выходит из неё"
				},
				{
					"keys": "ArrowLeft / ArrowRight / ArrowUp / ArrowDown",
					"action": "циклически перемещает фокус; при непустом value сразу выбирает сфокусированный item"
				},
				{
					"keys": "Home / End",
					"action": "фокусирует первый / последний item; при непустом value также выбирает его"
				},
				{
					"keys": "Space",
					"action": "выбирает сфокусированный item"
				}
			],
			"contrast": [
				{
					"fg": "--c-control-mark",
					"bg": "--c-control-on",
					"min": 3
				},
				{
					"fg": "--c-control-border",
					"bg": "--c-control-bg",
					"min": 3
				},
				{
					"fg": "--c-control-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-on",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-radio-label",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-asterisk",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-desc",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-error",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-hint",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-tile-label",
					"bg": "--c-tile-bg",
					"min": 4.5
				},
				{
					"fg": "--c-tile-label",
					"bg": "--c-tile-bg-hover",
					"min": 4.5
				},
				{
					"fg": "--c-control-on",
					"bg": "--c-tile-bg",
					"min": 3
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-tile-bg",
					"min": 3
				},
				{
					"fg": "--c-hint-text",
					"bg": "--c-hint-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--c-control-asterisk",
				"--c-control-bg",
				"--c-control-border",
				"--c-control-desc",
				"--c-control-error",
				"--c-control-focus",
				"--c-control-hint",
				"--c-control-label",
				"--c-control-mark",
				"--c-control-on",
				"--c-hint-bg",
				"--c-hint-text",
				"--c-radio-label",
				"--c-tile-bg",
				"--c-tile-bg-hover",
				"--c-tile-label",
				"--control-box",
				"--control-dot",
				"--control-focus-offset",
				"--control-fs-aux",
				"--control-gap",
				"--control-group-gap",
				"--control-msg-gap",
				"--control-tile-img",
				"--control-tile-radius",
				"--radio-gap",
				"--radio-tile-letter-spacing",
				"--radio-tile-line-height",
				"--sh-control-focus",
				"--sh-control-hover",
				"--sh-control-pressed",
				"--sh-radio",
				"--sh-radio-on",
				"--sh-tile-hover",
				"--t-control"
			],
			"examples": [
				{
					"id": "Vertical",
					"name": "Вертикально",
					"desc": "Видимый вопрос, text-items и циклический выбор стрелками; рядом — hint группы."
				},
				{
					"id": "ItemField",
					"name": "Пояснения и ошибка",
					"desc": "Описание принадлежит item, required и error — группе целиком."
				},
				{
					"id": "Horizontal",
					"name": "Горизонтально",
					"desc": "Короткие подписи text-варианта переносятся по строкам."
				},
				{
					"id": "Images",
					"name": "Плитки",
					"desc": "Изображение помогает сравнить варианты, но видимая подпись остаётся доступным именем."
				}
			],
			"departures": [
				{
					"what": "Безопасный дефолт не назначается автоматически",
					"why": "Первый вариант не выбирается автоматически: иначе форма может отправить выбор, которого пользователь не делал. Явный `value` допустим только для безопасного дефолта."
				},
				{
					"what": "Видимый вопрос обязателен внутри публичной обёртки",
					"why": "Внешний заголовок страницы не гарантирует accessible name radiogroup и теряется при переносе компонента."
				},
				{
					"what": "Визуальный круг productScope меньше интерактивной области",
					"why": "Компактная геометрия AdsCompass сохранена внутри канонической цели не меньше 24×24 CSS px."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Добавлять disabled — недоступный вариант не показываем."
				},
				{
					"level": "must-not",
					"guidance": "Использовать Radio для множественного выбора — это CheckboxGroup."
				},
				{
					"level": "must-not",
					"guidance": "Помещать required или error на отдельный item; эти состояния принадлежат группе."
				},
				{
					"level": "must",
					"guidance": "Передавать непустые label и items с уникальными value; неизвестный value сбрасывается в empty."
				},
				{
					"level": "must",
					"guidance": "Для required-группы внутри HTML-формы передавать непустой name."
				},
				{
					"level": "must",
					"guidance": "В variant=images давать изображение и различимую текстовую подпись каждому item."
				},
				{
					"level": "should",
					"guidance": "Задавать начальный value только когда один вариант доказанно безопасен и не требует осознанного согласия."
				},
				{
					"level": "should",
					"guidance": "Использовать horizontal только для коротких подписей, которые остаются быстро сравнимыми после переноса."
				}
			]
		},
		{
			"kind": "component",
			"id": "switch",
			"name": "Switch",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Мгновенное переключение состояния без подтверждения.",
			"description": "Switch применяет бинарную настройку сразу, без отправки формы.",
			"useCases": {
				"when": [
					"Настройка, применяемая мгновенно, без кнопки «Сохранить»: уведомления, автопродление."
				],
				"avoid": [
					"Признак в форме, уходящий по отправке.",
					"Выбор из нескольких режимов — switch только бинарный."
				],
				"alternatives": [
					{
						"id": "checkbox",
						"why": "бинарный признак в форме с отправкой"
					},
					{
						"id": "segmented-control",
						"why": "несколько режимов в одну строку"
					}
				]
			},
			"props": [
				{
					"name": "checked",
					"type": "boolean",
					"default": "false",
					"desc": "Состояние",
					"bindable": true
				},
				{
					"name": "label",
					"type": "string",
					"desc": "Видимая подпись (слева)"
				},
				{
					"name": "hint",
					"type": "string",
					"desc": "Подсказка-поповер"
				},
				{
					"name": "description",
					"type": "string",
					"desc": "Поясняющий текст под полем"
				},
				{
					"name": "error",
					"type": "string",
					"desc": "Текст ошибки (перекрывает description)"
				},
				{
					"name": "required",
					"type": "boolean",
					"default": "false",
					"desc": "Звёздочка обязательности"
				},
				{
					"name": "value",
					"type": "string",
					"desc": "Значение для отправки формы"
				},
				{
					"name": "name",
					"type": "string",
					"desc": "Имя поля формы"
				}
			],
			"anatomy": [
				{
					"id": "field",
					"role": "part",
					"desc": "Корень связывает переключатель, подпись и сообщение."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Видимое имя мгновенно применяемой настройки."
				},
				{
					"id": "hint",
					"role": "part",
					"required": false,
					"desc": "Контекстная подсказка рядом с подписью."
				},
				{
					"id": "track",
					"role": "part",
					"desc": "Интерактивная дорожка с ролью switch."
				},
				{
					"id": "thumb",
					"role": "indicator",
					"desc": "Бегунок, чьё положение дублирует aria-checked."
				},
				{
					"id": "message",
					"role": "label",
					"required": false,
					"desc": "Описание либо ошибка настройки."
				}
			],
			"states": [
				{
					"id": "off",
					"when": "покой, выключено",
					"change": "серая дорожка, бегунок слева"
				},
				{
					"id": "on",
					"when": "включено",
					"change": "зелёная дорожка, бегунок справа"
				},
				{
					"id": "hover",
					"when": "указатель над дорожкой",
					"change": "усиливаются bevel-тени дорожки и бегунка"
				},
				{
					"id": "pressed",
					"when": "переключатель нажат",
					"change": "дорожка вдавливается, тень бегунка следует направлению"
				},
				{
					"id": "focus-visible",
					"when": "фокус с клавиатуры",
					"change": "вдавленная тень дополняется кольцом акцента"
				}
			],
			"statesNotes": [
				"**Нет disabled.** Недоступный переключатель не показываем."
			],
			"a11y": [
				"Поведение и `role=\"switch\"`, `aria-checked` — из Bits UI.",
				"Подпись связана через `<label for>`.",
				"Состояние различается положением бегунка и `aria-checked`; контур сохраняет бегунок различимым, цвет дорожки служит дополнительным сигналом."
			],
			"keyboard": [
				{
					"keys": "Space / Enter",
					"action": "переключить"
				},
				{
					"keys": "Tab",
					"action": "переход между полями"
				}
			],
			"contrast": [
				{
					"fg": "--c-switch-thumb-border",
					"bg": "--c-control-bg",
					"min": 3
				},
				{
					"fg": "--c-switch-thumb-border",
					"bg": "--c-switch-on",
					"min": 3
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-focus",
					"bg": "--c-bg",
					"min": 3
				}
			],
			"tokens": [
				"--c-control-asterisk",
				"--c-control-bg",
				"--c-control-desc",
				"--c-control-error",
				"--c-control-focus",
				"--c-control-label",
				"--c-switch-on",
				"--c-switch-thumb",
				"--c-switch-thumb-border",
				"--control-fs-aux",
				"--control-focus-offset",
				"--control-gap",
				"--control-msg-gap",
				"--sh-control-focus",
				"--sh-switch-thumb",
				"--sh-switch-thumb-hover",
				"--sh-switch-thumb-outline",
				"--sh-switch-thumb-pressed-off",
				"--sh-switch-thumb-pressed-on",
				"--sh-switch-track",
				"--sh-switch-track-hover",
				"--sh-switch-track-hover-on",
				"--sh-switch-track-on",
				"--sh-switch-track-pressed",
				"--switch-h",
				"--switch-radius",
				"--switch-thumb",
				"--switch-travel",
				"--switch-w",
				"--t-control",
				"--t-switch"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый",
					"desc": "Применяется сразу — без кнопки «Сохранить»."
				},
				{
					"id": "Field",
					"name": "Поле",
					"desc": "Подпись, звёздочка обязательности, подсказка, описание и ошибка."
				},
				{
					"id": "States",
					"name": "Состояния",
					"desc": "Выключено и включено; фокус — кольцо акцента."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Добавлять `disabled` — недоступный переключатель не показываем."
				},
				{
					"level": "must",
					"guidance": "Подпись — состояние/настройка, а не действие: «Уведомления», не «Включить»."
				},
				{
					"level": "must-not",
					"guidance": "Использовать switch там, где нужна отправка формы — это `checkbox`."
				}
			]
		},
		{
			"kind": "component",
			"id": "segmented-control",
			"name": "Segmented Control",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "primitives",
			"interaction": "always",
			"summary": "Выбор одного сегмента из ряда.",
			"description": "Segmented Control переключает короткие режимы в одной строке. Тон активного сегмента может обозначать include или exclude.",
			"useCases": {
				"when": [
					"2–5 коротких взаимоисключающих режимов в одну строку: вид списка, период, фильтр.",
					"Фильтры «включить/исключить» — тоны `include`/`exclude` (toggle-passer)."
				],
				"avoid": [
					"Множественный выбор.",
					"Длинные подписи или больше 5 опций.",
					"Навигация между страницами — это ссылки, не контрол."
				],
				"alternatives": [
					{
						"id": "radio",
						"why": "варианты с описаниями, вертикально"
					},
					{
						"id": "select",
						"why": "длинный список значений, компактно"
					},
					{
						"id": "switch",
						"why": "ровно два состояния «вкл/выкл»"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "string",
					"default": "первый item",
					"desc": "Значение активного сегмента",
					"bindable": true
				},
				{
					"name": "items",
					"type": "Array",
					"default": "[]",
					"desc": "Сегменты `{ value, label, tone? }`"
				},
				{
					"name": "icon",
					"type": "Component",
					"desc": "Иконка-обрамление каждого сегмента (phosphor)"
				},
				{
					"name": "deselectable",
					"type": "boolean",
					"default": "false",
					"desc": "Повторный клик по активному снимает выбор (бегунок гаснет)"
				}
			],
			"axes": [
				{
					"axis": "tone",
					"title": "Тоны",
					"rationale": "Тон активного фона — данные фильтра, а не украшение: `include` всегда зелёный, `exclude` всегда коралловый — пара читается без легенды.",
					"values": [
						{
							"value": "neutral",
							"look": "светлый `#f4f2f8`",
							"when": "обычный выбор (по умолчанию)"
						},
						{
							"value": "include",
							"look": "зелёный `#eff7f0`",
							"when": "включающий фильтр"
						},
						{
							"value": "exclude",
							"look": "коралл `#ffd4d4`",
							"when": "исключающий фильтр"
						}
					]
				}
			],
			"extra": [
				{
					"title": "Элемент",
					"table": {
						"head": [
							"Поле",
							"Тип",
							"Роль"
						],
						"rows": [
							[
								"value",
								"string",
								"Значение сегмента"
							],
							[
								"label",
								"string",
								"Подпись (рендерится капсом)"
							],
							[
								"tone",
								"neutral | include | exclude",
								"Цвет фона активного сегмента"
							]
						]
					}
				}
			],
			"anatomy": [
				{
					"id": "group",
					"role": "part",
					"desc": "Однострочная single-selection группа с roving focus."
				},
				{
					"id": "thumb",
					"role": "indicator",
					"required": false,
					"desc": "Единая плашка-бегунок под активным сегментом; отсутствует при снятом выборе."
				},
				{
					"id": "item",
					"role": "part",
					"desc": "Один сегмент с уникальным value и тоном активного состояния."
				},
				{
					"id": "icon",
					"role": "icon",
					"required": false,
					"desc": "Декоративная иконка-обрамление сегмента."
				},
				{
					"id": "label",
					"role": "label",
					"desc": "Короткое доступное имя режима."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "сегмент не выбран",
					"change": "прозрачный фон, приглушённый текст"
				},
				{
					"id": "active",
					"when": "сегмент выбран",
					"change": "под ним выпуклый бегунок по тону, тёмный текст"
				},
				{
					"id": "focus",
					"when": "фокус с клавиатуры",
					"change": "коралловая обводка"
				}
			],
			"statesNotes": [
				"**Бегунок.** Активный сегмент — это позиция единой плашки-бегунка; при смене она плавно едет, при снятии — гаснет.",
				"**По умолчанию один активен.** С `deselectable` повторный клик по активному снимает выбор — так toggle-passer обходится без сегмента NONE.",
				"**Нет disabled.** Недоступный сегмент в набор не кладём."
			],
			"a11y": [
				"Под капотом `ToggleGroup` (type=single) Bits UI — выбор стрелками, повторный клик снимает (в режиме deselectable).",
				"Иконки-обрамления декоративны (`aria-hidden`) — смысл несёт подпись.",
				"Тон — не единственный носитель смысла: рядом всегда есть текстовая подпись."
			],
			"keyboard": [
				{
					"keys": "← →",
					"action": "перемещение и выбор сегмента"
				},
				{
					"keys": "Enter / Space",
					"action": "при `deselectable` повторное нажатие снимает выбор"
				},
				{
					"keys": "Tab",
					"action": "вход в группу и выход из неё"
				}
			],
			"contrast": [
				{
					"fg": "--c-seg-text",
					"bg": "--c-seg-active",
					"min": 4.5
				},
				{
					"fg": "--c-seg-text",
					"bg": "--c-seg-include",
					"min": 4.5
				},
				{
					"fg": "--c-seg-text",
					"bg": "--c-seg-exclude",
					"min": 4.5
				},
				{
					"fg": "--c-seg-text-idle",
					"bg": "--c-seg-track",
					"min": 4.5
				}
			],
			"tokens": [
				"--c-control-on",
				"--c-seg-active",
				"--c-seg-exclude",
				"--c-seg-include",
				"--c-seg-text",
				"--c-seg-text-idle",
				"--c-seg-track",
				"--seg-count",
				"--seg-h",
				"--seg-icon",
				"--seg-index",
				"--seg-item-gap",
				"--seg-item-h",
				"--seg-item-min",
				"--seg-item-pad-x",
				"--seg-radius",
				"--sh-seg-active",
				"--t-control"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый",
					"desc": "Сегменты с иконкой-обрамлением; всегда один активен."
				},
				{
					"id": "TogglePasser",
					"name": "Toggle-passer",
					"desc": "Без сегмента NONE: повторный клик по активному снимает выбор — бегунок гаснет (`deselectable`)."
				},
				{
					"id": "NoIcons",
					"name": "Без иконок",
					"desc": "Иконка-обрамление опциональна — без неё остаётся только подпись."
				}
			],
			"departures": [
				{
					"what": "«Пусто» — это `deselectable`, а не сегмент NONE",
					"why": "Явный пустой сегмент занимает место и притворяется значением. Повторный клик по активному снимает выбор — так toggle-passer живёт без NONE."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Добавлять `disabled` — недоступный сегмент в набор не кладём."
				},
				{
					"level": "must-not",
					"guidance": "Использовать для множественного выбора — это набор чекбоксов."
				},
				{
					"level": "must",
					"guidance": "Без `deselectable` держать один сегмент всегда активным."
				}
			]
		},
		{
			"kind": "component",
			"id": "accordion",
			"name": "Accordion",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "always",
			"summary": "Сворачивает вторичные разделы статьи или формы.",
			"description": "Accordion использует поведение Bits UI и визуальный слой «42».",
			"useCases": {
				"when": [
					"Справка, FAQ и вторичные настройки — контент, не нужный сразу.",
					"Экономия вертикали в длинной форме или статье."
				],
				"avoid": [
					"Основной контент или главное действие — их не прячут в закрытый блок.",
					"Взаимоисключающие разделы одного уровня — это табы (в каноне их пока нет)."
				]
			},
			"props": [
				{
					"name": "items",
					"type": "Array",
					"default": "[]",
					"desc": "Секции: value, title, content, disabled"
				},
				{
					"name": "type",
					"type": "single | multiple",
					"default": "single",
					"desc": "Один или несколько открытых блоков"
				},
				{
					"name": "value",
					"type": "string | string[]",
					"default": "''",
					"desc": "Управляемое значение"
				},
				{
					"name": "disabled",
					"type": "boolean",
					"default": "false",
					"desc": "Блокирует весь Accordion"
				},
				{
					"name": "hiddenUntilFound",
					"type": "boolean",
					"default": "false",
					"desc": "Поиск браузера по закрытому контенту"
				},
				{
					"name": "emptyMessage",
					"type": "string",
					"default": "Нет разделов",
					"desc": "Текст пустого состояния"
				}
			],
			"composition": [
				{
					"part": "item",
					"rule": "Секция: заголовок и содержимое"
				},
				{
					"part": "trigger",
					"rule": "Заголовок-кнопка: title и marker (треугольник)"
				},
				{
					"part": "content",
					"rule": "Раскрываемое содержимое секции"
				},
				{
					"part": "empty",
					"rule": "Текст при пустом списке items"
				}
			],
			"anatomy": [
				{
					"id": "root",
					"role": "part",
					"desc": "Контейнер всех сворачиваемых секций и режима single / multiple."
				},
				{
					"id": "item",
					"role": "part",
					"desc": "Одна секция с собственным значением, заголовком и содержимым."
				},
				{
					"id": "heading",
					"role": "part",
					"desc": "Семантический заголовок заданного уровня."
				},
				{
					"id": "trigger",
					"role": "part",
					"desc": "Кнопка заголовка, управляющая раскрытием секции."
				},
				{
					"id": "marker",
					"role": "indicator",
					"desc": "Декоративный треугольник направления раскрытия."
				},
				{
					"id": "content",
					"role": "part",
					"desc": "Регион содержимого, связанный с trigger."
				},
				{
					"id": "empty",
					"role": "label",
					"required": false,
					"desc": "Сообщение вместо списка при пустом items."
				}
			],
			"states": [
				{
					"id": "collapsed",
					"when": "секция закрыта",
					"change": "marker вправо, контент скрыт"
				},
				{
					"id": "expanded",
					"when": "секция открыта",
					"change": "marker повёрнут вниз, контент виден"
				},
				{
					"id": "disabled",
					"when": "секция или весь блок",
					"change": "приглушено, курсор not-allowed"
				}
			],
			"a11y": [
				"Роли и ARIA — из Bits UI.",
				"Заголовок секции — на уровне `headingLevel` (по умолчанию `h3`).",
				"`hiddenUntilFound` — браузерный поиск находит текст в закрытой секции.",
				"`focus-visible` на заголовке: видимое кольцо."
			],
			"keyboard": [
				{
					"keys": "Enter / Space",
					"action": "открыть/закрыть секцию"
				},
				{
					"keys": "↑ ↓",
					"action": "между заголовками секций"
				},
				{
					"keys": "Home / End",
					"action": "к первому/последнему заголовку"
				}
			],
			"tokens": [
				"--c-accordion-hover",
				"--c-button-text",
				"--c-control-bg",
				"--c-control-desc",
				"--c-control-on",
				"--c-menu-bg",
				"--c-panel-border",
				"--control-focus-offset",
				"--icon-sm",
				"--input-pad-x",
				"--input-radius",
				"--sh-sm",
				"--t-control"
			],
			"examples": [
				{
					"id": "Single",
					"name": "Один открытый блок"
				},
				{
					"id": "Multiple",
					"name": "Несколько открытых блоков"
				},
				{
					"id": "DisabledEmpty",
					"name": "Disabled и empty"
				}
			],
			"departures": [
				{
					"what": "У Accordion есть `disabled` — вопреки сквозному правилу",
					"why": "`disabled` запрещён у контролов ввода. Accordion сохраняет временно недоступную секцию в структуре; поведение даёт Bits UI."
				},
				{
					"what": "Проп режима называется `type` (single | multiple)",
					"why": "Это API Bits UI для поведенческого режима, не ось вида — канонный запрет `type` касается осей вида (см. Основания)."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Прятать основное действие формы внутрь закрытого блока."
				},
				{
					"level": "must-not",
					"guidance": "Использовать Accordion для взаимоисключающих разделов — это табы."
				}
			]
		},
		{
			"kind": "component",
			"id": "alert",
			"name": "Alert",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "conditional",
			"summary": "Показывает короткое системное сообщение рядом с местом реакции.",
			"description": "Alert сообщает о результате, предупреждении или пояснении рядом с местом реакции. Фон, рамка и иконка дублируют тон.",
			"useCases": {
				"when": [
					"Короткое системное сообщение внутри формы или блока — рядом с местом реакции.",
					"Результат операции (`success`/`error`), предупреждение (`warning`), пояснение (`info`)."
				],
				"avoid": [
					"Ошибка конкретного поля — у поля есть `error`.",
					"Длинное объяснение — это обычный блок контента."
				],
				"alternatives": [
					{
						"id": "status",
						"why": "постоянное состояние сущности, а не сообщение о событии"
					}
				]
			},
			"props": [
				{
					"name": "tone",
					"type": "warning | success | error | info",
					"default": "warning",
					"desc": "Смысл уведомления"
				},
				{
					"name": "flat",
					"type": "boolean",
					"default": "false",
					"desc": "Убирает фон, рамку и внутренние отступы"
				},
				{
					"name": "closeable",
					"type": "boolean",
					"default": "false",
					"desc": "Показывает кнопку закрытия"
				},
				{
					"name": "children",
					"type": "snippet",
					"desc": "Текст или короткая разметка уведомления"
				}
			],
			"anatomy": [
				{
					"id": "root",
					"role": "part",
					"desc": "Инлайновый контейнер с ролью alert и визуалом выбранного тона."
				},
				{
					"id": "tone-icon",
					"role": "icon",
					"desc": "Декоративная иконка, чья форма дублирует смысл тона."
				},
				{
					"id": "content",
					"role": "label",
					"desc": "Короткое системное сообщение либо компактная разметка."
				},
				{
					"id": "close-action",
					"role": "part",
					"required": false,
					"desc": "Кнопка закрытия при closeable; единственный интерактивный элемент Alert."
				}
			],
			"states": [
				{
					"id": "warning",
					"when": "нужно внимание",
					"change": "жёлтый тинт-фон, рамка, оранжевый треугольник"
				},
				{
					"id": "success",
					"when": "операция прошла успешно",
					"change": "зелёный тинт-фон, рамка, галочка в кружке"
				},
				{
					"id": "error",
					"when": "действие не выполнено",
					"change": "красный тинт-фон, рамка, «!» в кружке"
				},
				{
					"id": "info",
					"when": "нейтральное пояснение",
					"change": "синий тинт-фон, рамка, «i» в кружке"
				},
				{
					"id": "dismissed",
					"when": "клик по close",
					"change": "уведомление исчезает локально"
				}
			],
			"a11y": [
				"Корень получает `role=\"alert\"`, чтобы сообщение было объявлено ассистивными технологиями.",
				"Кнопка закрытия имеет видимую фокус-рамку и текстовый `aria-label`.",
				"Текст уведомления должен быть коротким: длинные объяснения лучше выносить в обычный блок контента."
			],
			"keyboard": [
				{
					"keys": "Tab",
					"action": "при `closeable` — фокус кнопки закрытия"
				},
				{
					"keys": "Enter / Space",
					"action": "закрыть уведомление"
				}
			],
			"contrast": [
				{
					"fg": "--c-alert-text",
					"bg": "--c-alert-warning-bg",
					"min": 4.5
				},
				{
					"fg": "--c-alert-text",
					"bg": "--c-alert-success-bg",
					"min": 4.5
				},
				{
					"fg": "--c-alert-text",
					"bg": "--c-alert-error-bg",
					"min": 4.5
				},
				{
					"fg": "--c-alert-text",
					"bg": "--c-alert-info-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--alert-fs",
				"--alert-gap",
				"--alert-icon",
				"--alert-pad-x",
				"--alert-pad-y",
				"--alert-radius",
				"--c-alert-error-bg",
				"--c-alert-error-border",
				"--c-alert-error-icon",
				"--c-alert-info-bg",
				"--c-alert-info-border",
				"--c-alert-info-icon",
				"--c-alert-success-bg",
				"--c-alert-success-border",
				"--c-alert-success-icon",
				"--c-alert-text",
				"--c-alert-warning-bg",
				"--c-alert-warning-border",
				"--c-alert-warning-icon",
				"--c-control-on",
				"--c-tag-remove",
				"--control-focus-offset",
				"--input-lh"
			],
			"examples": [
				{
					"id": "Tones",
					"name": "Тона"
				},
				{
					"id": "Flat",
					"name": "Плоский"
				},
				{
					"id": "Closeable",
					"name": "Закрываемый"
				}
			],
			"departures": [
				{
					"what": "Дефолтный тон — `warning`, не `info`",
					"why": "Alert в первоисточнике чаще предупреждает, чем информирует: дефолт совпадает с главным сценарием. Привычнее нейтральный дефолт."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Ставить Alert вместо ошибки конкретного поля — для этого есть `error` у поля."
				},
				{
					"level": "must",
					"guidance": "Выбирать тон по смыслу, а не по желаемой заметности."
				},
				{
					"level": "must",
					"guidance": "Держать текст уведомления коротким — длинные объяснения выносить в обычный блок контента."
				}
			]
		},
		{
			"kind": "component",
			"id": "avatar",
			"name": "Avatar",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "none",
			"summary": "Стабильный визуальный идентификатор пользователя.",
			"description": "Avatar по `id` стабильно выбирает персонажа и фон. С `alt` он значим, без `alt` — декоративен.",
			"useCases": {
				"when": [
					"Визуальный якорь пользователя или аккаунта: списки, шапка, комментарии.",
					"Стабильная идентичность без загрузки фото — цвет и лицо считаются по `id`."
				],
				"avoid": [
					"Кодировать состояние сущности цветом аватара — цвет здесь идентичность, не смысл."
				],
				"alternatives": [
					{
						"id": "status",
						"why": "состояние сущности с подписью"
					}
				]
			},
			"props": [
				{
					"name": "id",
					"type": "number",
					"default": "0",
					"desc": "Сид для генерации фона и лица"
				},
				{
					"name": "size",
					"type": "xs | sm | md | lg | xl",
					"default": "sm",
					"desc": "Размер аватара"
				},
				{
					"name": "levelId",
					"type": "number | null",
					"default": "null",
					"desc": "Бейдж уровня"
				},
				{
					"name": "special",
					"type": "boolean",
					"default": "false",
					"desc": "Специальный фон персонажа"
				},
				{
					"name": "imageUrl",
					"type": "string",
					"desc": "Картинка вместо SVG-персонажа"
				},
				{
					"name": "alt",
					"type": "string",
					"desc": "Текст для картинки, если аватар не декоративный"
				}
			],
			"anatomy": [
				{
					"id": "root",
					"role": "part",
					"desc": "Круглый контейнер изображения или сгенерированного персонажа."
				},
				{
					"id": "image",
					"role": "part",
					"required": false,
					"desc": "Пользовательское изображение, загружаемое через Bits UI Avatar."
				},
				{
					"id": "fallback",
					"role": "part",
					"desc": "Локально сгенерированный SVG-персонаж при отсутствии изображения."
				},
				{
					"id": "frame",
					"role": "part",
					"desc": "ProductScope-рамка, отделяющая аватар от окружения."
				},
				{
					"id": "badge",
					"role": "indicator",
					"required": false,
					"desc": "Текстовый бейдж уровня поверх края аватара."
				}
			],
			"statesNotes": [
				"У Avatar нет пользовательских состояний: загрузка image и fallback меняют источник контента, а не режим взаимодействия."
			],
			"behavior": [
				"Если задан `imageUrl`, загрузку и fallback ведёт Bits UI Avatar.",
				"Если картинки нет, рендерится локальный SVG-персонаж на токенах AdsCompass.",
				"Для уровня используются повторяющиеся группы: bronze, silver, gold, diamond."
			],
			"a11y": [
				"С заданным `alt` корень получает `aria-label`, картинка — `alt`; без него аватар помечен `aria-hidden` (декоративный).",
				"Цвет фона и лицо — идентичность, не информация: смысл не должен опираться только на цвет.",
				"Бейдж уровня — текстовый, читается скринридером как часть подписи."
			],
			"tokens": [
				"--avatar-badge-dot",
				"--avatar-badge-dot-border",
				"--avatar-badge-fs-lg",
				"--avatar-badge-fs-md",
				"--avatar-badge-lg-bottom",
				"--avatar-badge-lg-h",
				"--avatar-badge-lg-left",
				"--avatar-badge-lg-w",
				"--avatar-badge-md-bottom",
				"--avatar-badge-md-h",
				"--avatar-badge-md-left",
				"--avatar-badge-md-w",
				"--avatar-border-lg",
				"--avatar-border-md",
				"--avatar-border-sm",
				"--avatar-border-xl",
				"--avatar-border-xs",
				"--avatar-character-radius-lg",
				"--avatar-character-radius-md",
				"--avatar-character-radius-sm",
				"--avatar-character-radius-xl",
				"--avatar-character-radius-xs",
				"--avatar-radius-lg",
				"--avatar-radius-md",
				"--avatar-radius-sm",
				"--avatar-radius-xl",
				"--avatar-radius-xs",
				"--avatar-size-lg",
				"--avatar-size-md",
				"--avatar-size-sm",
				"--avatar-size-xl",
				"--avatar-size-xs",
				"--c-avatar-badge-bronze",
				"--c-avatar-badge-diamond",
				"--c-avatar-badge-gold",
				"--c-avatar-badge-silver",
				"--c-avatar-badge-text",
				"--c-avatar-bg-0",
				"--c-avatar-bg-1",
				"--c-avatar-bg-2",
				"--c-avatar-bg-3",
				"--c-avatar-bg-4",
				"--c-avatar-bg-5",
				"--c-avatar-bg-6",
				"--c-avatar-bg-7",
				"--c-avatar-face-0",
				"--c-avatar-face-1",
				"--c-avatar-face-2",
				"--c-avatar-face-3",
				"--c-avatar-face-4",
				"--c-avatar-frame",
				"--c-avatar-ink",
				"--c-avatar-special-bg",
				"--sh-avatar-badge-lg",
				"--sh-avatar-badge-md",
				"--sh-avatar-lg",
				"--sh-avatar-md",
				"--sh-avatar-sm",
				"--sh-avatar-xl",
				"--sh-avatar-xs"
			],
			"examples": [
				{
					"id": "Sizes",
					"name": "Размеры"
				},
				{
					"id": "Badges",
					"name": "Бейдж уровня"
				}
			],
			"departures": [
				{
					"what": "Аватар по умолчанию декоративен (`aria-hidden`)",
					"why": "В списках аватар дублирует имя рядом — озвучивать его сотни раз было бы шумом. `alt` задаётся, когда аватар — единственный носитель информации."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Передавать стабильный `id`, чтобы цвет не прыгал между рендерами."
				},
				{
					"level": "must",
					"guidance": "Задавать `alt` картинке, если аватар не декоративный."
				},
				{
					"level": "must-not",
					"guidance": "Кодировать важный статус только цветом аватара — для этого есть Status."
				}
			]
		},
		{
			"kind": "component",
			"id": "divider",
			"name": "Divider",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "none",
			"summary": "Разделяет соседние блоки горизонтальной или вертикальной линией.",
			"description": "Divider использует нативный `<hr>`; productScope добавляет лёгкий гравированный край.",
			"useCases": {
				"when": [
					"Граница между смысловыми блоками внутри одной плашки — горизонтальный.",
					"Разделение инлайновых элементов в строке (тулбар, мета-строка) — вертикальный."
				],
				"avoid": [
					"Блоки уже разнесены отступом — линия лишняя.",
					"Декорация сама по себе — разделитель не несёт смысла."
				]
			},
			"props": [
				{
					"name": "orientation",
					"type": "horizontal | vertical",
					"default": "horizontal",
					"desc": "Направление линии"
				}
			],
			"axes": [
				{
					"axis": "orientation",
					"title": "Размеры",
					"values": [
						{
							"value": "horizontal",
							"desc": "на всю ширину родителя, высота 1px"
						},
						{
							"value": "vertical",
							"desc": "на высоту родителя через `align-self: stretch`, ширина 1px, минимум 24px"
						}
					]
				}
			],
			"anatomy": [
				{
					"id": "separator",
					"role": "part",
					"desc": "Нативный `<hr>` с горизонтальной либо вертикальной ориентацией."
				}
			],
			"statesNotes": [
				"У Divider нет пользовательских состояний: orientation — конфигурация геометрии, а не состояние взаимодействия."
			],
			"a11y": [
				"Под капотом нативный `<hr>` — у него уже роль `separator`.",
				"Атрибут `aria-orientation` сообщает направление для вертикального варианта.",
				"Разделитель декоративен по содержанию: смысл несут соседние блоки, не сама линия."
			],
			"tokens": [
				"--c-divider",
				"--sh-divider-h",
				"--sh-divider-v"
			],
			"examples": [
				{
					"id": "Horizontal",
					"name": "Горизонтальный"
				},
				{
					"id": "Vertical",
					"name": "Вертикальный"
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Заменять разделителем отступ — если блоки и так разнесены, линия лишняя."
				},
				{
					"level": "must-not",
					"guidance": "Нагружать линию смыслом — разделитель декоративен, смысл несут соседние блоки."
				}
			]
		},
		{
			"kind": "component",
			"id": "dualist",
			"name": "Dualist",
			"status": "experimental",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "always",
			"summary": "Двухпанельный мультиселект.",
			"description": "Dualist показывает полный список слева и выбранные элементы справа. Поиск, удаление и общий сброс помогают работать с длинным набором.",
			"useCases": {
				"when": [
					"Множественный выбор из длинного списка (десятки элементов) с поиском.",
					"Выбранное важно видеть рядом со списком: порядок, удаление, «Clear All»."
				],
				"avoid": [
					"2–3 варианта — хватит чекбоксов.",
					"Выбор одного значения."
				],
				"alternatives": [
					{
						"id": "checkbox-group",
						"why": "короткий набор с общей подписью"
					},
					{
						"id": "select",
						"why": "одно значение из списка"
					}
				]
			},
			"extra": [
				{
					"title": "Анатомия",
					"list": [
						"**Источник (слева).** Поле поиска → список с чекбоксами → счётчик списка, опционально футер «Select All».",
						"**Счётчик.** Знаменатель показывает элементы слева: найденные при поиске, иначе все. Числитель показывает выбранные и появляется только при наличии выбора.",
						"**Менеджер (справа).** Список выбранного (в порядке выбора) с удалением и опциональным редактированием → футер «Clear All».",
						"Канон визуала — узел Figma `14645-141088`: иконки edit/close серые (m200), «Clear All» — коралловый текст без иконки, чекбокс «включено» — коралловый.",
						"Высота обеих панелей фиксирована (`--dualist-h` = 300px), внутренние списки скроллятся своим тонким скроллбаром."
					]
				}
			],
			"props": [
				{
					"name": "items",
					"type": "array",
					"default": "[]",
					"desc": "Полный список `{ value, label, warning?, editable? }`"
				},
				{
					"name": "value",
					"type": "array",
					"default": "[]",
					"desc": "Выбранные `value` в порядке выбора",
					"bindable": true
				},
				{
					"name": "searchLabel",
					"type": "string",
					"default": "\"\"",
					"desc": "Плавающий label поиска. Пусто — иконка без подписи"
				},
				{
					"name": "searchRequired",
					"type": "boolean",
					"default": "false",
					"desc": "Коралловая звёздочка у label поиска"
				},
				{
					"name": "searchPlaceholder",
					"type": "string",
					"default": "\"\"",
					"desc": "Плейсхолдер поля поиска"
				},
				{
					"name": "selectAll",
					"type": "boolean",
					"default": "false",
					"desc": "Показать футер «Select All» на левой панели"
				},
				{
					"name": "selectAllLabel / clearLabel",
					"type": "string",
					"default": "Select All / Clear All",
					"desc": "Подписи футеров"
				},
				{
					"name": "onselectall",
					"type": "function",
					"desc": "Переопределяет «Select All» (по умолчанию — выбрать всё отфильтрованное)"
				},
				{
					"name": "onedititem",
					"type": "function",
					"desc": "(item) => void — клик по карандашу"
				}
			],
			"shapes": [
				{
					"name": "элемента",
					"fields": [
						{
							"name": "value",
							"desc": "Уникальный ключ элемента"
						},
						{
							"name": "label",
							"desc": "Текст в списке и в менеджере"
						},
						{
							"name": "editable",
							"desc": "Показать карандаш в менеджере (вызывает `onedititem`)"
						},
						{
							"name": "warning",
							"desc": "Треугольник-предупреждение + приглушённый текст в менеджере"
						}
					]
				}
			],
			"anatomy": [
				{
					"id": "root",
					"role": "part",
					"desc": "Двухпанельный корень выбора и управления выбранным."
				},
				{
					"id": "source-panel",
					"role": "part",
					"desc": "Левая панель полного или отфильтрованного списка."
				},
				{
					"id": "search",
					"role": "part",
					"desc": "Поле фильтрации доступных элементов."
				},
				{
					"id": "source-list",
					"role": "part",
					"desc": "Прокручиваемый список доступных элементов."
				},
				{
					"id": "checkbox",
					"role": "part",
					"desc": "Независимый выбор одного элемента источника."
				},
				{
					"id": "counter",
					"role": "indicator",
					"desc": "Live-счётчик выбранных среди текущего списка."
				},
				{
					"id": "select-all",
					"role": "part",
					"required": false,
					"desc": "Действие выбора всех отфильтрованных элементов."
				},
				{
					"id": "manager-panel",
					"role": "part",
					"desc": "Правая панель выбранных элементов в порядке выбора."
				},
				{
					"id": "selected-item",
					"role": "part",
					"desc": "Строка выбранного элемента с доступными действиями."
				},
				{
					"id": "item-actions",
					"role": "part",
					"desc": "Удаление и опциональное редактирование выбранного элемента."
				},
				{
					"id": "clear-all",
					"role": "part",
					"desc": "Действие очистки полного выбора."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "покой",
					"change": "подложка m3300 с bevel-тенью"
				},
				{
					"id": "hover",
					"when": "курсор над пунктом списка",
					"change": "лёгкая подсветка строки"
				},
				{
					"id": "checked",
					"when": "пункт выбран",
					"change": "коралловый чекбокс + строка в менеджере"
				},
				{
					"id": "focus",
					"when": "фокус пункта/кнопки с клавиатуры",
					"change": "коралловое кольцо"
				},
				{
					"id": "empty",
					"when": "ничего не выбрано",
					"change": "менеджер пуст, остаётся «Clear All»"
				}
			],
			"a11y": [
				"Пункты списка — `Checkbox.Root` из Bits UI: `role`, клавиатура (Space), `data-state`.",
				"Счётчик — `aria-live=\"polite\"` с понятной подписью «N из M выбрано».",
				"Кнопки удаления/редактирования — нативные `<button>` с `aria-label`, включающим имя элемента.",
				"`focus-visible` на пунктах и кнопках — коралловое кольцо, без сдвига раскладки."
			],
			"keyboard": [
				{
					"keys": "Tab",
					"action": "по полю поиска, пунктам списка и кнопкам"
				},
				{
					"keys": "Space",
					"action": "переключить чекбокс пункта"
				},
				{
					"keys": "Enter / Space",
					"action": "кнопки: удаление, карандаш, «Select All» / «Clear All»"
				}
			],
			"contrast": [
				{
					"fg": "--c-dualist-ink",
					"bg": "--c-menu-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--button-h-sm",
				"--c-alert-warning-icon",
				"--c-control-asterisk",
				"--c-control-bg",
				"--c-control-desc",
				"--c-control-label",
				"--c-control-mark",
				"--c-control-on",
				"--c-divider",
				"--c-dualist-ink",
				"--c-dualist-overlay",
				"--c-input-text",
				"--c-menu-bg",
				"--c-menu-item-hover",
				"--control-box",
				"--control-fs-aux",
				"--control-gap",
				"--control-mark",
				"--control-tile-radius",
				"--dualist-action-icon",
				"--dualist-cb-radius",
				"--dualist-footer-pb",
				"--dualist-h",
				"--dualist-list-pr",
				"--dualist-selected-pad",
				"--input-h",
				"--input-label-fs-active",
				"--input-label-top-active",
				"--input-lh",
				"--input-radius",
				"--menu-item-pad-x",
				"--radio-gap",
				"--sh-button-focus",
				"--sh-control",
				"--sh-control-on",
				"--sh-divider-h",
				"--sh-dualist-panel",
				"--sh-dualist-sb",
				"--t-control",
				"--tag-h-md"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый",
					"desc": "Поиск без подписи и Select All. Выбранные элементы можно редактировать и удалять; счётчик показывает выбор относительно списка."
				},
				{
					"id": "SearchSelectAll",
					"name": "Поиск с подписью и Select All",
					"desc": "Поиск с подписью и Select All. Запрос «R» переводит счётчик из режима «выбрано / всего» в «выбрано / найдено»."
				},
				{
					"id": "Empty",
					"name": "Пустой менеджер",
					"desc": "Пока ничего не выбрано — правая панель пуста, остаётся только «Clear All»."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Применять для 2–3 вариантов — там достаточно чекбоксов или обычного селекта."
				},
				{
					"level": "must-not",
					"guidance": "Прятать «Clear All» — сброс всего выбора должен быть под рукой."
				},
				{
					"level": "must-not",
					"guidance": "Подменять встроенный счётчик ручными значениями — он считает сам."
				}
			]
		},
		{
			"kind": "component",
			"id": "extra-group",
			"name": "Extra Group",
			"status": "experimental",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "none",
			"summary": "Компактная строка из числовых значений через слеш.",
			"description": "Extra Group показывает связанные числа в одной строке. Цвет и подпись поясняют значение каждого числа.",
			"useCases": {
				"when": [
					"Сводные счётчики одной сущности в строку: статусы модерации, метрики.",
					"Проценты с порогами «плохо → хорошо» — режим `range`."
				],
				"avoid": [
					"Разнородные числа без общего смысла — это просто текст.",
					"Интерактив — компонент не фокусируется, это вывод."
				],
				"alternatives": [
					{
						"id": "status",
						"why": "одно состояние с подписью и сменой значения"
					}
				]
			},
			"props": [
				{
					"name": "items",
					"type": "Array",
					"default": "[]",
					"desc": "Список значений; элемент `{ value, label, color? }`"
				},
				{
					"name": "fontSize",
					"type": "number",
					"default": "11 (токен)",
					"desc": "Размер шрифта строки в px"
				}
			],
			"axes": [
				{
					"axis": "color",
					"title": "Режимы цвета",
					"values": [
						{
							"value": "не задан",
							"desc": "приглушённый `--c-extra-muted`"
						},
						{
							"value": "\"range\"",
							"desc": "по value: <50 или >100 — красный, 50–69 — оранжевый, 70–99 — синий, =100 — зелёный"
						},
						{
							"value": "имя токена",
							"desc": "`var(<color>)` напрямую"
						}
					]
				}
			],
			"extra": [
				{
					"title": "Элемент",
					"table": {
						"head": [
							"Поле",
							"Тип",
							"Роль"
						],
						"rows": [
							[
								"value",
								"number",
								"Само число (рендерится как текст)"
							],
							[
								"label",
								"string",
								"Подпись в hover-подсказке (`title`)"
							],
							[
								"color",
								"string?",
								"Имя токена, `\"range\"` или ничего"
							]
						]
					}
				}
			],
			"anatomy": [
				{
					"id": "group",
					"role": "part",
					"desc": "Компактная строка связанных числовых значений."
				},
				{
					"id": "item",
					"role": "part",
					"desc": "Одно число с семантическим цветом и текстовой подписью в title."
				},
				{
					"id": "separator",
					"role": "part",
					"required": false,
					"desc": "Слеш перед каждым элементом, кроме первого."
				}
			],
			"statesNotes": [
				"У Extra Group нет пользовательских состояний: browser-title по наведению поясняет статичный вывод, но компонент не активируется и не получает фокус."
			],
			"a11y": [
				"Подпись значения доступна как hover-подсказка через `title`.",
				"Цвет — не единственный носитель смысла: рядом всегда есть число и подпись.",
				"Компонент не фокусируется и не имеет состояний — это вывод, а не контрол."
			],
			"tokens": [
				"--c-extra-0",
				"--c-extra-1",
				"--c-extra-2",
				"--c-extra-3",
				"--c-extra-base",
				"--c-extra-muted",
				"--extra-group-fs"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый",
					"desc": "Без явного цвета значения приглушены; слеш — префикс перед всеми, кроме первого."
				},
				{
					"id": "Tokens",
					"name": "Цвета по токенам",
					"desc": "В `color` передаётся имя CSS-переменной — значение красится напрямую."
				},
				{
					"id": "Range",
					"name": "Режим range",
					"desc": "При `color: \"range\"` цвет выбирается по числу: пороги 50 / 70 / 100."
				},
				{
					"id": "Sizes",
					"name": "Размер",
					"desc": "Размер задаётся пропом `fontSize` (px); по умолчанию — 11px."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Использовать Extra Group как контрол — компонент не фокусируется и не имеет состояний, это вывод."
				},
				{
					"level": "must-not",
					"guidance": "Класть сюда разнородные числа без общего смысла — это просто текст."
				}
			]
		},
		{
			"kind": "component",
			"id": "input-select",
			"name": "Input Select",
			"status": "experimental",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "always",
			"summary": "Составной input из сегментов-селектов.",
			"description": "Input Select объединяет связанные Select в одну строку, например правило фильтра. Всю строку можно удалить одним действием.",
			"useCases": {
				"when": [
					"Строка правила или фильтра из связанных селектов одной сущности: мессенджер · ОС · условие · версия."
				],
				"avoid": [
					"Несвязанные поля в один ряд — это обычная форма.",
					"Свободный ввод значения — сегменты только выбирают."
				],
				"alternatives": [
					{
						"id": "select",
						"why": "одиночный выбор вне составной строки"
					},
					{
						"id": "input",
						"why": "свободный ввод значения"
					}
				]
			},
			"props": [
				{
					"name": "segments",
					"type": "array",
					"default": "[]",
					"desc": "Список сегментов-селектов",
					"bindable": true
				},
				{
					"name": "deletable",
					"type": "boolean",
					"default": "false",
					"desc": "Показать кнопку удаления (trash)"
				},
				{
					"name": "ondelete",
					"type": "function",
					"desc": "Обработчик удаления"
				},
				{
					"name": "deleteLabel",
					"type": "string",
					"default": "Удалить",
					"desc": "aria-label кнопки удаления"
				},
				{
					"name": "size",
					"type": "m",
					"default": "m",
					"desc": "Высота поля (40px)"
				}
			],
			"shapes": [
				{
					"name": "сегмента",
					"desc": "Каждый элемент `segments` — объект со своим значением.",
					"fields": [
						{
							"name": "label",
							"desc": "Подпись сверху (компактный label)"
						},
						{
							"name": "value",
							"desc": "Выбранное значение"
						},
						{
							"name": "options",
							"desc": "Список опций: `string[]` или `{value, label}[]`"
						},
						{
							"name": "required",
							"desc": "Коралловая звёздочка у подписи"
						},
						{
							"name": "placeholder",
							"desc": "Текст при пустом значении"
						},
						{
							"name": "name",
							"desc": "Имя поля формы / ключ each"
						}
					]
				}
			],
			"anatomy": [
				{
					"id": "field",
					"role": "part",
					"desc": "Общая подложка связанных сегментов одной сущности."
				},
				{
					"id": "segment",
					"role": "part",
					"desc": "Один самостоятельный select внутри составной строки."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Компактная подпись и accessible name сегмента."
				},
				{
					"id": "value",
					"role": "label",
					"desc": "Текущее значение либо placeholder сегмента."
				},
				{
					"id": "indicator",
					"role": "indicator",
					"desc": "Декоративная стрелка состояния меню сегмента."
				},
				{
					"id": "divider",
					"role": "part",
					"desc": "Вертикальный разделитель соседних сегментов."
				},
				{
					"id": "menu",
					"role": "part",
					"desc": "Listbox-панель опций текущего сегмента."
				},
				{
					"id": "delete-action",
					"role": "part",
					"required": false,
					"desc": "Кнопка удаления всей составной строки."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "покой",
					"change": "подложка с bevel-тенью, делители"
				},
				{
					"id": "hover",
					"when": "курсор над сегментом",
					"change": "курсор-указатель"
				},
				{
					"id": "focus",
					"when": "фокус сегмента с клавиатуры",
					"change": "коралловое кольцо"
				},
				{
					"id": "open",
					"when": "меню сегмента раскрыто",
					"change": "подсветка сегмента, стрелка вверх"
				}
			],
			"a11y": [
				"Каждый сегмент — нативный селект из Bits UI: `role`, клавиатура, typeahead.",
				"Подпись сегмента уходит в `aria-label` триггера.",
				"`focus-visible` на сегменте и кнопке удаления — коралловое кольцо.",
				"Кнопка удаления — нативный `<button>` с `aria-label`."
			],
			"keyboard": [
				{
					"keys": "Tab",
					"action": "между сегментами и кнопкой удаления"
				},
				{
					"keys": "Enter / Space / ↓",
					"action": "открыть меню сегмента"
				},
				{
					"keys": "↑ / ↓ · Enter · Esc",
					"action": "подсветка опции · выбор · закрыть"
				},
				{
					"keys": "символы",
					"action": "typeahead по подписи опции"
				}
			],
			"contrast": [
				{
					"fg": "--c-input-text",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-control-label",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-select-chevron",
					"bg": "--c-input-bg",
					"min": 3
				},
				{
					"fg": "--c-input-select-accent",
					"bg": "--c-input-bg",
					"min": 3
				},
				{
					"fg": "--c-input-select-required",
					"bg": "--c-input-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-border",
					"bg": "--c-bg",
					"min": 3
				},
				{
					"fg": "--c-input-text",
					"bg": "--c-menu-bg",
					"min": 4.5
				},
				{
					"fg": "--c-input-text",
					"bg": "--c-menu-item-hover",
					"min": 4.5
				},
				{
					"fg": "--c-control-on",
					"bg": "--c-menu-bg",
					"min": 3
				},
				{
					"fg": "--c-control-on",
					"bg": "--c-menu-item-hover",
					"min": 3
				}
			],
			"tokens": [
				"--c-control-label",
				"--c-control-on",
				"--c-divider",
				"--c-input-bg",
				"--c-input-border",
				"--c-input-select-accent",
				"--c-input-select-chevron",
				"--c-input-select-required",
				"--c-input-text",
				"--c-menu-bg",
				"--c-menu-item-hover",
				"--control-field-min",
				"--control-gap",
				"--control-select-menu-max",
				"--icon-button",
				"--icon-sm",
				"--input-border-w",
				"--input-gap",
				"--input-h",
				"--input-label-fs-active",
				"--input-pad-x",
				"--input-radius",
				"--input-select-pad-y",
				"--input-select-req-gap",
				"--input-select-seg-gap",
				"--menu-item-lh",
				"--menu-item-pad-x",
				"--menu-item-radius",
				"--menu-pad",
				"--sh-button-focus",
				"--sh-divider-v",
				"--sh-menu",
				"--sh-radio",
				"--t-control"
			],
			"examples": [
				{
					"id": "RuleBuilder",
					"name": "Правило фильтра",
					"desc": "Четыре сегмента и удаление строки. Кнопка добавляет новое правило."
				},
				{
					"id": "Pair",
					"name": "Без удаления",
					"desc": "Число сегментов произвольно; кнопка удаления опциональна."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Использовать только для связанных полей одной сущности; несвязанные поля — обычная форма."
				},
				{
					"level": "must",
					"guidance": "Показывать кнопку удаления (trash), если строку можно удалить."
				},
				{
					"level": "must",
					"guidance": "Подпись сегмента прокидывать в `aria-label` триггера."
				}
			]
		},
		{
			"kind": "component",
			"id": "status",
			"name": "Status",
			"status": "experimental",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "conditional",
			"summary": "Компактный бейдж статуса.",
			"description": "Status показывает текущее состояние. При `editable` он открывает список значений; подпись, значок и цвет приходят из данных.",
			"useCases": {
				"when": [
					"Компактный бейдж состояния сущности в таблице или карточке.",
					"Смена статуса на месте — `editable` открывает поповер выбора."
				],
				"avoid": [
					"Сообщение о событии — статус живёт постоянно, а не появляется по случаю.",
					"Метка-категория без семантики состояния."
				],
				"alternatives": [
					{
						"id": "alert",
						"why": "сообщение о событии"
					},
					{
						"id": "tag",
						"why": "метка-категория, фильтр"
					},
					{
						"id": "extra-group",
						"why": "сводные счётчики статусов в строку"
					}
				]
			},
			"props": [
				{
					"name": "value",
					"type": "number | boolean",
					"default": "0",
					"desc": "Текущее значение",
					"bindable": true
				},
				{
					"name": "items",
					"type": "Array",
					"default": "[]",
					"desc": "Список статусов"
				},
				{
					"name": "editable",
					"type": "boolean",
					"default": "false",
					"desc": "Открывает поповер выбора"
				},
				{
					"name": "minimal",
					"type": "boolean",
					"default": "false",
					"desc": "Скрывает иконку внутри круга"
				},
				{
					"name": "icon",
					"type": "boolean",
					"default": "false",
					"desc": "Оставляет только круглый значок"
				},
				{
					"name": "onchange",
					"type": "function",
					"desc": "Колбэк после выбора статуса"
				}
			],
			"shapes": [
				{
					"name": "item",
					"fields": [
						{
							"name": "value",
							"desc": "Значение для сравнения с `value`"
						},
						{
							"name": "text",
							"desc": "Видимая подпись"
						},
						{
							"name": "key",
							"desc": "Стабильный ключ статуса"
						},
						{
							"name": "icon",
							"desc": "Имя иконки: moderated, approved или blocked"
						},
						{
							"name": "order",
							"desc": "Порядок в поповере"
						},
						{
							"name": "color",
							"desc": "Опциональный фон корня, значка и пункта поповера через CSS-переменную"
						},
						{
							"name": "selectable",
							"desc": "`false` скрывает item из поповера"
						}
					]
				}
			],
			"anatomy": [
				{
					"id": "root",
					"role": "part",
					"desc": "Статичная метка либо popover-триггер в editable-режиме."
				},
				{
					"id": "label",
					"role": "label",
					"required": false,
					"desc": "Видимая подпись текущего статуса; скрывается в icon-режиме."
				},
				{
					"id": "icon",
					"role": "icon",
					"desc": "Круглый знак статуса; форма и цвет задаются данными item."
				},
				{
					"id": "popover",
					"role": "part",
					"required": false,
					"desc": "Панель смены значения при editable."
				},
				{
					"id": "option",
					"role": "part",
					"required": false,
					"desc": "Доступный для выбора статус из selectable items."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "статус показан, popover закрыт",
					"change": "корень отображает текущую подпись и знак без дополнительного отклика"
				},
				{
					"id": "focus-visible",
					"when": "editable-триггер или option получает клавиатурный фокус",
					"change": "текущий интерактивный элемент получает видимое кольцо"
				},
				{
					"id": "open",
					"when": "editable-триггер активирован",
					"change": "popover показывает отсортированные selectable-варианты"
				},
				{
					"id": "option-hover",
					"when": "указатель над вариантом",
					"change": "его круглый знак увеличивается по productScope-motion"
				},
				{
					"id": "selected",
					"when": "выбран новый вариант",
					"change": "value и видимый статус обновляются, onchange получает item"
				}
			],
			"statesNotes": [
				"Без editable Status — статичная метка без фокуса; интерактивные состояния применимы только к режиму смены значения."
			],
			"a11y": [
				"В editable-режиме триггер и поповер управляются Bits UI Popover.",
				"Каждый вариант в поповере получает текстовый `aria-label`.",
				"Если статус не редактируется, он остаётся статичной меткой без фокуса."
			],
			"keyboard": [
				{
					"keys": "Enter / Space",
					"action": "при `editable` — открыть поповер; на варианте — выбрать"
				},
				{
					"keys": "Tab",
					"action": "между вариантами в поповере"
				},
				{
					"keys": "Esc",
					"action": "закрыть поповер без смены"
				}
			],
			"contrast": [
				{
					"fg": "--c-status-fg-on",
					"bg": "--c-status-moderated",
					"min": 4.5
				},
				{
					"fg": "--c-status-fg-on",
					"bg": "--c-status-approved",
					"min": 4.5
				},
				{
					"fg": "--c-status-fg-on",
					"bg": "--c-status-blocked",
					"min": 4.5
				}
			],
			"tokens": [
				"--c-control-on",
				"--c-status-fg-on",
				"--c-status-popup-bg",
				"--c-status-text",
				"--control-focus-offset",
				"--sh-status-icon",
				"--sh-status-popup",
				"--sh-status-popup-icon",
				"--status-bg",
				"--status-fs",
				"--status-gap",
				"--status-icon",
				"--status-icon-shift",
				"--status-lh",
				"--status-pad-bottom",
				"--status-pad-icon-bottom",
				"--status-pad-icon-top",
				"--status-pad-top",
				"--status-pad-x",
				"--status-popup-gap",
				"--status-popup-icon",
				"--status-popup-pad",
				"--status-popup-scale-hover",
				"--status-radius",
				"--status-transition",
				"--status-w"
			],
			"examples": [
				{
					"id": "Basic",
					"name": "Базовый"
				},
				{
					"id": "Editable",
					"name": "Редактируемый"
				},
				{
					"id": "Icons",
					"name": "Иконка"
				}
			],
			"departures": [
				{
					"what": "Набор статусов и цвета приходят из данных, не из вариантов компонента",
					"why": "Семантика статусов прикладная: компонент рендерит `item.color`, а не навязывает success/warning-пресеты. Привычнее фиксированный набор тонов."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Цвет передавать через данные (`item.color`), не зашивать в компонент."
				},
				{
					"level": "must-not",
					"guidance": "Делать нередактируемый статус фокусируемым — без `editable` это статичная метка."
				}
			]
		},
		{
			"kind": "component",
			"id": "tag",
			"name": "Tag",
			"status": "stable",
			"since": "2026-06",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"group": "components",
			"interaction": "conditional",
			"summary": "Компактная метка для фильтров, сегментов и выбранных значений.",
			"description": "Tag показывает категорию или выбранное значение. Он может поддерживать выбор или удаление.",
			"useCases": {
				"when": [
					"Короткие метки и категории у сущности.",
					"Выбранные значения фильтра — с удалением (`clearable`).",
					"Кликабельный тег-фильтр — `selectable`/`selected`."
				],
				"avoid": [
					"Основное действие экрана.",
					"Состояние сущности со сменой значения."
				],
				"alternatives": [
					{
						"id": "button",
						"why": "действие"
					},
					{
						"id": "status",
						"why": "состояние с поповером смены"
					}
				]
			},
			"props": [
				{
					"name": "size",
					"type": "sm | md | lg",
					"default": "md",
					"desc": "Плотность тега"
				},
				{
					"name": "color",
					"type": "string",
					"desc": "Фон через CSS-переменную"
				},
				{
					"name": "colorText",
					"type": "string",
					"default": "token",
					"desc": "Цвет текста через CSS-переменную"
				},
				{
					"name": "clearable",
					"type": "boolean",
					"default": "false",
					"desc": "Кнопка удаления"
				},
				{
					"name": "selectable",
					"type": "boolean",
					"default": "false",
					"desc": "Кликабельный тег-фильтр"
				},
				{
					"name": "selected",
					"type": "boolean",
					"default": "false",
					"desc": "Выбранное состояние"
				},
				{
					"name": "mark",
					"type": "boolean",
					"default": "false",
					"desc": "Плоская служебная метка"
				},
				{
					"name": "hideWords",
					"type": "boolean",
					"default": "true",
					"desc": "Обрезает длинный текст"
				}
			],
			"extra": [
				{
					"title": "Слоты",
					"table": {
						"head": [
							"Слот",
							"Роль"
						],
						"rows": [
							[
								"children",
								"Основной текст"
							],
							[
								"additional",
								"Префикс слева"
							],
							[
								"leading",
								"Иконка перед префиксом или текстом"
							]
						]
					}
				}
			],
			"anatomy": [
				{
					"id": "root",
					"role": "part",
					"desc": "Статичная метка либо кнопка выбора в зависимости от selectable."
				},
				{
					"id": "leading",
					"role": "icon",
					"required": false,
					"desc": "Декоративная ведущая иконка."
				},
				{
					"id": "additional",
					"role": "label",
					"required": false,
					"desc": "Компактный префикс в собственной подложке."
				},
				{
					"id": "label",
					"role": "label",
					"desc": "Основной короткий текст метки."
				},
				{
					"id": "remove-action",
					"role": "part",
					"required": false,
					"desc": "Отдельная кнопка удаления при clearable."
				}
			],
			"states": [
				{
					"id": "idle",
					"when": "метка не активирована",
					"change": "показывается базовый productScope-фон"
				},
				{
					"id": "hover",
					"when": "указатель над selectable-тегом",
					"change": "фон и тень усиливают доступность выбора"
				},
				{
					"id": "pressed",
					"when": "selectable-тег удерживается",
					"change": "вдавленная тень подтверждает активацию"
				},
				{
					"id": "focus-visible",
					"when": "selectable-тег или remove-action получает клавиатурный фокус",
					"change": "видимое кольцо отмечает конкретную кнопку"
				},
				{
					"id": "selected",
					"when": "selectable=true и selected=true",
					"change": "контрастная заливка и текст сохраняют выбранное значение"
				}
			],
			"statesNotes": [
				"Без selectable и clearable Tag — статичная метка; интерактивные состояния появляются только у соответствующей кнопки."
			],
			"a11y": [
				"Кнопка удаления — нативный `<button>` с `aria-label` «Удалить тег»; иконка — `aria-hidden`.",
				"Выбираемый тег (`selectable`) — нативный `<button>`: активация `Enter`/`Space`, видимый `focus-visible`.",
				"Невыбираемый тег — статичная метка без фокуса."
			],
			"keyboard": [
				{
					"keys": "Enter / Space",
					"action": "на выбираемом теге — переключить; на крестике — удалить"
				},
				{
					"keys": "Tab",
					"action": "фокус получают только выбираемый тег и кнопка удаления"
				}
			],
			"contrast": [
				{
					"fg": "--c-tag-text",
					"bg": "--bg-tag-default",
					"min": 4.5
				},
				{
					"fg": "--c-tag-selected-text",
					"bg": "--bg-tag-selected",
					"min": 4.5
				},
				{
					"fg": "--c-tag-additional-text",
					"bg": "--c-tag-additional-bg",
					"min": 4.5
				}
			],
			"tokens": [
				"--bg-tag-default",
				"--bg-tag-selectable",
				"--bg-tag-selectable-hover",
				"--bg-tag-selected",
				"--c-avatar-frame",
				"--c-control-on",
				"--c-panel-border",
				"--c-tag-additional-bg",
				"--c-tag-additional-border",
				"--c-tag-additional-text",
				"--c-tag-mark-bg",
				"--c-tag-remove",
				"--c-tag-selected-text",
				"--c-tag-text",
				"--c-tag-text-strong",
				"--control-focus-offset",
				"--control-gap",
				"--icon-sm",
				"--sh-tag",
				"--sh-tag-active",
				"--sh-tag-selectable",
				"--sh-tag-selected",
				"--tag-fs-lg",
				"--tag-fs-md",
				"--tag-fs-sm",
				"--tag-h-lg",
				"--tag-h-md",
				"--tag-h-sm",
				"--tag-main-max",
				"--tag-pad-x-clearable",
				"--tag-pad-x-lg",
				"--tag-pad-x-md",
				"--tag-pad-x-sm",
				"--tag-remove-color",
				"--tag-remove-nudge"
			],
			"examples": [
				{
					"id": "Sizes",
					"name": "Размеры"
				},
				{
					"id": "Prefix",
					"name": "С префиксом"
				},
				{
					"id": "Removable",
					"name": "Удаляемые"
				},
				{
					"id": "Select",
					"name": "Выбор"
				},
				{
					"id": "Mark",
					"name": "Mark"
				}
			],
			"departures": [
				{
					"what": "Цвет — свободный токен-проп, не набор вариантов",
					"why": "Метки красятся прикладными палитрами: `color`/`colorText` принимают имена CSS-переменных. Фиксированный набор тонов дал бы ложную семантику."
				}
			],
			"guidelines": [
				{
					"level": "must-not",
					"guidance": "Превращать Tag в кнопку основного действия — для этого есть Button."
				},
				{
					"level": "must",
					"guidance": "Удаление отдавать наружу через `onremove`."
				}
			]
		}
	],
	"practices": [
		{
			"kind": "practice",
			"id": "accessibility",
			"name": "Доступность",
			"status": "stable",
			"since": "2026-07",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"summary": "Фокус, клавиатура, доступные имена, контраст и движение.",
			"description": "Bits UI или нативный HTML задаёт семантику. Проект добавляет видимый фокус, доступные имена, размер цели, контраст и безопасное движение.",
			"guidelines": [
				{
					"level": "must",
					"topic": "Фокус",
					"guidance": "Показывать кольцо через `:focus-visible` у каждого интерактивного элемента."
				},
				{
					"level": "must-not",
					"topic": "Фокус",
					"guidance": "Убирать `outline` без равноценной контрастной замены.",
					"rationale": "Индикатор должен отличаться от соседних цветов не меньше чем 3:1 и оставаться видимым, пока элемент в фокусе."
				},
				{
					"level": "must",
					"topic": "Фокус",
					"guidance": "Сохранять порядок фокуса равным порядку DOM; не менять `tabindex` ради раскладки."
				},
				{
					"level": "must",
					"topic": "Фокус",
					"guidance": "Новая панель не должна полностью закрывать сфокусированный контрол. Если закрывает — перенести фокус в новый контекст."
				},
				{
					"level": "must",
					"topic": "Имена и роли",
					"guidance": "Связывать видимую подпись поля с контролом через `for` / `id`; placeholder не заменяет label."
				},
				{
					"level": "must",
					"topic": "Имена и роли",
					"guidance": "Давать `aria-label` контролу только с иконкой."
				},
				{
					"level": "must",
					"topic": "Имена и роли",
					"guidance": "Связывать ошибку с полем через `aria-invalid` и `aria-describedby`."
				},
				{
					"level": "must-not",
					"topic": "Имена и роли",
					"guidance": "Писать вручную роли и состояния, которые уже поддерживает примитив Bits UI."
				},
				{
					"level": "must",
					"topic": "Область цели",
					"guidance": "Держать указательную цель не меньше 24×24 CSS px. Видимый знак может быть меньше внутри достаточной области или вдали от соседних целей."
				},
				{
					"level": "must-not",
					"topic": "Цвет и контраст",
					"guidance": "Передавать смысл только цветом; статус дополняется иконкой или словом."
				},
				{
					"level": "must",
					"topic": "Цвет и контраст",
					"guidance": "Держать контраст обычного текста не ниже 4.5:1, а значимых иконок, границ контролов и индикаторов состояния — не ниже 3:1 относительно соседнего цвета."
				},
				{
					"level": "must",
					"topic": "Цвет и контраст",
					"guidance": "Использовать семантические токены статуса и проверять объявленные пары каждого публичного скина в среде, где компонент реально показывается."
				},
				{
					"level": "must",
					"topic": "Движение",
					"guidance": "Уважать `prefers-reduced-motion`: служебное движение отключается, смысл состояния остаётся читаемым."
				}
			],
			"references": [
				{
					"id": "keyboard",
					"title": "Клавиатурная карта",
					"lead": "Общий минимум поверх покомпонентных карт; специальное поведение задаёт примитив.",
					"columns": [
						"Клавиши",
						"Действие"
					],
					"rows": [
						[
							"Tab / Shift+Tab",
							"Переход между интерактивными элементами по порядку DOM"
						],
						[
							"Enter",
							"Активирует кнопку, ссылку или отправку формы"
						],
						[
							"Space",
							"Активирует кнопку, переключает checkbox и switch"
						],
						[
							"Esc",
							"Закрывает раскрытое меню, диалог или popover"
						],
						[
							"↑ ↓",
							"Движение по списку опций или разделам accordion"
						],
						[
							"Home / End",
							"Переход к первому или последнему пункту списка"
						]
					]
				}
			]
		},
		{
			"kind": "practice",
			"id": "forms",
			"name": "Формы",
			"status": "stable",
			"since": "2026-07",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"summary": "Композиция полей, обязательность, валидация и действия формы.",
			"description": "Правила сборки `input`, `select` и `textarea` в форму: единая анатомия поля, предсказуемая раскладка, честная обязательность и спокойная валидация.",
			"guidelines": [
				{
					"level": "should",
					"topic": "Раскладка",
					"guidance": "По умолчанию использовать один столбец и подпись над контролом."
				},
				{
					"level": "should",
					"topic": "Раскладка",
					"guidance": "Соотносить ширину поля с ожидаемым вводом и группировать близкие по смыслу поля."
				},
				{
					"level": "must",
					"topic": "Обязательность",
					"guidance": "Передавать обязательность нативным `required`, а не одной визуальной меткой."
				},
				{
					"level": "should",
					"topic": "Обязательность",
					"guidance": "Если необязательных полей меньше, помечать словом «необязательно» именно их."
				},
				{
					"level": "must-not",
					"topic": "Валидация",
					"guidance": "Показывать ошибку на каждый введённый символ."
				},
				{
					"level": "must",
					"topic": "Валидация",
					"guidance": "Показывать ошибку после blur или попытки отправки, ставить её на место подсказки и связывать через `aria-describedby`."
				},
				{
					"level": "must",
					"topic": "Валидация",
					"guidance": "Убирать ошибку сразу после исправления и объявлять новый текст через `aria-live`."
				},
				{
					"level": "must",
					"topic": "Действия",
					"guidance": "Использовать одно главное действие `accent`; отмену и возврат оформлять как `default` или `ghost`."
				},
				{
					"level": "must",
					"topic": "Действия",
					"guidance": "Передавать разрушающий смысл через `tone=\"danger\"`, а не вторым акцентом."
				},
				{
					"level": "should",
					"topic": "Скоуп",
					"guidance": "Спрашивать только данные, необходимые на текущем шаге, и показывать формат заранее в подсказке."
				}
			],
			"references": [
				{
					"id": "field-anatomy",
					"title": "Анатомия поля",
					"columns": [
						"Часть",
						"Роль"
					],
					"rows": [
						[
							"label",
							"Видимая подпись, связанная с контролом через for / id"
						],
						[
							"control",
							"Сам ввод: input, select или textarea"
						],
						[
							"hint",
							"Зачем нужно поле или в каком формате вводить"
						],
						[
							"error",
							"Ошибка вместо подсказки, когда поле невалидно"
						],
						[
							"mark",
							"Метка обязательности рядом с подписью"
						]
					]
				}
			]
		},
		{
			"kind": "practice",
			"id": "content",
			"name": "Контент и тон",
			"status": "stable",
			"since": "2026-07",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"summary": "Ясный текст для интерфейсов и документации.",
			"description": "Текст помогает действовать в интерфейсе и понимать документацию. Каждый фрагмент называет действие, состояние или решение без повторов и словесного шума.",
			"guidelines": [
				{
					"level": "must",
					"topic": "Ясность",
					"guidance": "Сначала называть факт, действие или вывод; причину и исключение добавлять только после него."
				},
				{
					"level": "must",
					"topic": "Ясность",
					"guidance": "Одно предложение выражает одну основную мысль."
				},
				{
					"level": "must",
					"topic": "Ясность",
					"guidance": "Один абзац отвечает на один вопрос."
				},
				{
					"level": "must",
					"topic": "Ясность",
					"guidance": "Строить прямую фразу: кто действует, что делает и к какому результату это приводит."
				},
				{
					"level": "must-not",
					"topic": "Ясность",
					"guidance": "Повторять одну мысль в заголовке, первом предложении и следующем абзаце."
				},
				{
					"level": "should",
					"topic": "Ясность",
					"guidance": "Удалять фразу, если без неё не меняются понимание, решение или следующий шаг."
				},
				{
					"level": "must",
					"topic": "Документация",
					"guidance": "Начинать раздел с правила или вывода; устройство, причину и пример давать следом."
				},
				{
					"level": "must",
					"topic": "Документация",
					"guidance": "Если мысль помещается в одном предложении, не растягивать её на абзац."
				},
				{
					"level": "must",
					"topic": "Документация",
					"guidance": "Один раз определить термин и дальше использовать одно название для одного понятия."
				},
				{
					"level": "must-not",
					"topic": "Документация",
					"guidance": "Смешивать правило, обоснование, исключение и пример в одном предложении."
				},
				{
					"level": "should",
					"topic": "Документация",
					"guidance": "Использовать список для последовательности, а таблицу — для сравнения одинаковых свойств; простую мысль оставлять прозой."
				},
				{
					"level": "must",
					"topic": "Тон",
					"guidance": "Писать спокойно, конкретно и на равных с читателем; важное ставить в начало."
				},
				{
					"level": "should",
					"topic": "Тон",
					"guidance": "Убирать из обычного продуктового текста шутки, хайп, восклицания и извинения без полезного действия."
				},
				{
					"level": "must-not",
					"topic": "Тон",
					"guidance": "Обвинять пользователя или обещать результат, который продукт не гарантирует."
				},
				{
					"level": "must",
					"topic": "Действия и ссылки",
					"guidance": "Писать на кнопке короткую реплику пользователя; объект добавлять, только если без него неясен результат."
				},
				{
					"level": "must-not",
					"topic": "Действия и ссылки",
					"guidance": "Писать на кнопке «Хочу», «Сохраняю» или «ОК», когда доступно точное действие или подтверждение."
				},
				{
					"level": "must",
					"topic": "Действия и ссылки",
					"guidance": "Называть в ссылке место назначения или результат, а не механику перехода «здесь» или «по ссылке»."
				},
				{
					"level": "must",
					"topic": "Действия и ссылки",
					"guidance": "Заголовком называть конкретную страницу, задачу или состояние; не использовать пустые слова «Важно» и «Информация»."
				},
				{
					"level": "must",
					"topic": "Поля и переключатели",
					"guidance": "Лейблом называть данные, а не действие ввода: «Номер телефона», не «Введите номер телефона»."
				},
				{
					"level": "must-not",
					"topic": "Поля и переключатели",
					"guidance": "Дублировать лейбл в placeholder или прятать в placeholder обязательную информацию."
				},
				{
					"level": "should",
					"topic": "Поля и переключатели",
					"guidance": "Использовать placeholder для примера или формата, если он помогает избежать ошибки."
				},
				{
					"level": "must",
					"topic": "Поля и переключатели",
					"guidance": "Формулировать подпись Switch как настройку, которая одинаково читается во включённом и выключенном состоянии."
				},
				{
					"level": "must-not",
					"topic": "Поля и переключатели",
					"guidance": "Прятать обязательное условие в hint или tooltip; там остаётся только дополнительный контекст."
				},
				{
					"level": "must",
					"topic": "Ошибки и состояния",
					"guidance": "Сообщать, что произошло и что сделать. Если известен ожидаемый результат, назвать его."
				},
				{
					"level": "must",
					"topic": "Ошибки и состояния",
					"guidance": "У поля точно называть исправление: «Введите 6 цифр», не «Некорректное значение»."
				},
				{
					"level": "must-not",
					"topic": "Ошибки и состояния",
					"guidance": "Показывать технический код или ссылаться на интернет без подтверждённой причины."
				},
				{
					"level": "must",
					"topic": "Ошибки и состояния",
					"guidance": "В пустом состоянии назвать отсутствующий контент и доступный следующий шаг. Нормальную пустоту не выдавать за ошибку."
				},
				{
					"level": "should",
					"topic": "Ошибки и состояния",
					"guidance": "Пояснять загрузку только при заметном ожидании; срок указывать только при надёжном прогнозе."
				},
				{
					"level": "must",
					"topic": "Типографика",
					"guidance": "Использовать sentence case, кавычки «ёлочки» и правильный знак для тире, диапазона и минуса."
				},
				{
					"level": "must-not",
					"topic": "Типографика",
					"guidance": "Ставить точку в конце короткого заголовка, кнопки, таба, лейбла или пункта меню."
				},
				{
					"level": "must",
					"topic": "Типографика",
					"guidance": "Не отделять процент от числа; число и валюту или единицу связывать неразрывным пробелом."
				}
			],
			"references": [
				{
					"id": "documentation",
					"title": "Сокращение документации",
					"columns": [
						"Размыто",
						"Точно",
						"Что изменилось"
					],
					"rows": [
						[
							"Компонент является средством, которое позволяет пользователю осуществить выбор значения из списка.",
							"Компонент выбирает значение из списка.",
							"Убраны отвлечённые существительные и повтор смысла."
						],
						[
							"Следует отметить, что в данном случае правило не применяется.",
							"Здесь правило не действует.",
							"Вывод стоит первым, вводная конструкция удалена."
						]
					]
				},
				{
					"id": "controls",
					"title": "Текст контролов",
					"columns": [
						"Контекст",
						"Неясно",
						"Точно"
					],
					"rows": [
						[
							"Кнопка",
							"Хочу удалить",
							"Удалить"
						],
						[
							"Подтверждение",
							"ОК",
							"Понятно"
						],
						[
							"Лейбл",
							"Введите номер телефона",
							"Номер телефона"
						],
						[
							"Ссылка",
							"Подробнее здесь",
							"Подробнее о тарифе"
						],
						[
							"Switch",
							"Включить уведомления",
							"Уведомления"
						]
					]
				},
				{
					"id": "errors",
					"title": "Ошибки",
					"columns": [
						"Неясно",
						"Точно"
					],
					"rows": [
						[
							"Что-то пошло не так",
							"Не загрузилось. Попробуйте снова"
						],
						[
							"Ошибка авторизации",
							"Войдите снова"
						],
						[
							"Некорректное значение",
							"Введите 6 цифр"
						]
					]
				},
				{
					"id": "typography",
					"title": "Короткая типографическая памятка",
					"columns": [
						"Случай",
						"Форма"
					],
					"rows": [
						[
							"Процент",
							"50%"
						],
						[
							"Сумма",
							"1 000 ₽"
						],
						[
							"Диапазон",
							"25 – 27 сентября"
						],
						[
							"Минус",
							"−5%"
						],
						[
							"Технический термин",
							"QR‑код"
						]
					]
				}
			]
		},
		{
			"kind": "practice",
			"id": "icons",
			"name": "Иконки",
			"status": "stable",
			"since": "2026-07",
			"docOrigin": [
				"extracted",
				"ai-assisted"
			],
			"summary": "Единый набор Phosphor, масштаб, цвет и доступные имена.",
			"description": "Набор — Phosphor, вес regular. Иконка усиливает текст, а не заменяет смысл; размер наследует кегль, цвет — `currentColor`.",
			"guidelines": [
				{
					"level": "must",
					"topic": "Система",
					"guidance": "Использовать один набор Phosphor и один вес regular во всём интерфейсе."
				},
				{
					"level": "should",
					"topic": "Смысл",
					"guidance": "Ставить иконку в пару с текстом, когда подпись снимает неоднозначность."
				},
				{
					"level": "must",
					"topic": "Размер",
					"guidance": "Масштабировать иконку через `1em` или токен; минимальный канон инлайн-иконки — `--icon-sm`."
				},
				{
					"level": "must-not",
					"topic": "Размер",
					"guidance": "Задавать размер иконки пиксельным литералом внутри компонента."
				},
				{
					"level": "must",
					"topic": "Цвет",
					"guidance": "Наследовать `currentColor`; для статуса использовать семантический токен."
				},
				{
					"level": "must",
					"topic": "Доступность",
					"guidance": "Помечать декоративную иконку рядом с текстом `aria-hidden=\"true\"`."
				},
				{
					"level": "must",
					"topic": "Доступность",
					"guidance": "Давать доступное имя кнопке, в которой иконка заменяет текст."
				}
			],
			"catalog": {
				"library": "phosphor-svelte",
				"weight": "regular",
				"icons": [
					"Check",
					"X",
					"Plus",
					"MagnifyingGlass",
					"CaretDown",
					"PencilSimple",
					"Trash",
					"Gear",
					"Info",
					"Warning"
				],
				"scale": [
					"--fs-sm",
					"--fs-md",
					"--fs-lg",
					"--fs-xl"
				]
			}
		}
	],
	"patterns": [
		{
			"kind": "pattern",
			"id": "form",
			"name": "Форма",
			"status": "experimental",
			"since": "2026-07",
			"docOrigin": [
				"authored",
				"ai-assisted"
			],
			"summary": "Поля, валидация, обратная связь и одно главное действие.",
			"description": "Форма соединяет цель, поля, валидацию и результат в один сценарий отправки.",
			"useCases": {
				"when": [
					"Нужно получить от пользователя несколько связанных значений.",
					"Ошибки можно исправить на месте до перехода дальше."
				],
				"avoid": [
					"Нужно изменить одно булево значение — достаточно `switch`.",
					"Данные только показываются и не редактируются."
				]
			},
			"composes": [
				{
					"id": "input",
					"role": "Короткие текстовые значения и специализированные типы ввода."
				},
				{
					"id": "select",
					"role": "Один вариант из длинного или раскрываемого списка."
				},
				{
					"id": "textarea",
					"role": "Многострочный текст."
				},
				{
					"id": "button",
					"role": "Отправка, отмена и вторичные действия формы."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Давать форме видимую цель и сохранять порядок полей равным порядку чтения."
				},
				{
					"level": "must",
					"guidance": "Показывать ошибку после blur или попытки отправки и связывать её с конкретным полем."
				},
				{
					"level": "must",
					"guidance": "Оставлять ровно одно главное действие `accent`; отмену оформлять как `default` или `ghost`."
				},
				{
					"level": "should",
					"guidance": "Делить длинную форму на именованные секции, каждая из которых отвечает за одну локальную задачу."
				},
				{
					"level": "must",
					"guidance": "Использовать heading для обычного раздела полей, а `fieldset` с `legend` — только когда секция является одним групповым вопросом."
				},
				{
					"level": "must-not",
					"guidance": "Вводить универсальный компонент FormSection, который скрывает выбор семантики heading или `fieldset`/`legend`."
				},
				{
					"level": "must-not",
					"guidance": "Блокировать всю форму из-за первой ошибки или валидировать каждый символ."
				}
			],
			"sequence": [
				{
					"step": "Цель",
					"action": "Пользователь понимает результат формы до ввода.",
					"response": "Заголовок и краткая подсказка задают контекст."
				},
				{
					"step": "Ввод",
					"action": "Пользователь заполняет поля в естественном порядке.",
					"response": "Label остаётся видимым; hint заранее объясняет формат."
				},
				{
					"step": "Проверка",
					"action": "Пользователь покидает поле или отправляет форму.",
					"response": "Ошибка появляется у поля и объявляется assistive technology."
				},
				{
					"step": "Результат",
					"action": "Пользователь исправляет данные и отправляет форму.",
					"response": "Ошибка исчезает сразу; результат показывается на уровне формы или области."
				}
			],
			"comparison": {
				"right": {
					"title": "Одна цель, именованные секции и локальная обратная связь",
					"description": "Секции отвечают за отдельные задачи, поля идут одним потоком, ошибки стоят у причины, единственная accent-кнопка называет результат.",
					"uses": [
						"input",
						"select",
						"textarea",
						"button"
					]
				},
				"wrong": {
					"title": "Безымянный поток, конкурирующие действия и ранние ошибки",
					"description": "Разные задачи смешаны без секций, несколько accent-кнопок спорят за внимание, а сообщения меняются на каждый символ и отделены от полей.",
					"uses": [
						"input",
						"button"
					]
				}
			},
			"skeleton": [
				{
					"id": "heading",
					"role": "Цель формы и краткое объяснение."
				},
				{
					"id": "sections",
					"role": "Один поток именованных задач; внутри каждой — связанные поля и подходящая HTML-семантика."
				},
				{
					"id": "feedback",
					"role": "Общая обратная связь только когда она относится ко всей форме."
				},
				{
					"id": "actions",
					"role": "Отмена и одно главное действие."
				}
			]
		},
		{
			"kind": "pattern",
			"id": "destructive-confirmation",
			"name": "Подтверждение разрушения",
			"status": "experimental",
			"since": "2026-07",
			"docOrigin": [
				"authored",
				"ai-assisted"
			],
			"summary": "Последняя проверка перед необратимым или дорогим действием.",
			"description": "Подтверждение не повторяет каждое удаление. Оно появляется только перед необратимым, дорогим или затрагивающим других действием и прямо называет последствие.",
			"useCases": {
				"when": [
					"Действие необратимо или восстановление существенно дороже подтверждения.",
					"Последствие затрагивает других людей, опубликованные данные или оплату."
				],
				"avoid": [
					"Действие легко отменить через undo.",
					"Потеря результата отсутствует или незначительна."
				]
			},
			"composes": [
				{
					"id": "alert",
					"role": "Кратко объясняет необратимое последствие, если одного текста недостаточно."
				},
				{
					"id": "button",
					"role": "Явное разрушительное действие и спокойная отмена."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Называть объект и необратимое последствие в заголовке или тексте подтверждения."
				},
				{
					"level": "must",
					"guidance": "Называть подтверждающую кнопку конкретным действием: «Удалить проект», не «Да»."
				},
				{
					"level": "must",
					"guidance": "Передавать разрушительный смысл осью `tone=danger`, не вторым уровнем акцента."
				},
				{
					"level": "must-not",
					"guidance": "Показывать подтверждение для действия, которое надёжно отменяется через undo."
				}
			],
			"sequence": [
				{
					"step": "Намерение",
					"action": "Пользователь запускает разрушительное действие.",
					"response": "Система останавливается только при реальной цене ошибки."
				},
				{
					"step": "Проверка",
					"action": "Пользователь читает объект и последствие.",
					"response": "Отмена остаётся нейтральной; разрушительная кнопка называет результат."
				},
				{
					"step": "Исход",
					"action": "Пользователь отменяет или подтверждает.",
					"response": "После подтверждения область показывает итог, а не spinner внутри кнопки."
				}
			],
			"comparison": {
				"right": {
					"title": "Конкретный объект и последствие",
					"description": "«Удалить проект “Atlas”? Участники потеряют доступ». Действия: «Отмена» и «Удалить проект».",
					"uses": [
						"alert",
						"button"
					]
				},
				"wrong": {
					"title": "Абстрактное «Вы уверены?»",
					"description": "Не названы объект и цена ошибки, а кнопки «Нет» и «Да» требуют перечитать вопрос.",
					"uses": [
						"button"
					]
				}
			},
			"skeleton": [
				{
					"id": "title",
					"role": "Необратимое действие и объект."
				},
				{
					"id": "consequence",
					"role": "Кого и какие данные затронет действие."
				},
				{
					"id": "actions",
					"role": "Нейтральная отмена и конкретное разрушительное действие."
				}
			]
		},
		{
			"kind": "pattern",
			"id": "selection-group",
			"name": "Группа выбора",
			"status": "experimental",
			"since": "2026-07",
			"docOrigin": [
				"authored",
				"ai-assisted"
			],
			"summary": "Один или несколько вариантов с явной моделью выбора.",
			"description": "Паттерн сначала фиксирует модель выбора — один, несколько или режим, — и только затем выбирает компонент. Все варианты получают общее доступное имя.",
			"useCases": {
				"when": [
					"Пользователь выбирает один или несколько вариантов из известного набора.",
					"Важно сравнить варианты до подтверждения."
				],
				"avoid": [
					"Значение вводится свободным текстом.",
					"Единственное булево значение меняется мгновенно — это `switch`."
				]
			},
			"composes": [
				{
					"id": "radio",
					"role": "Ровно один вариант из видимого набора."
				},
				{
					"id": "checkbox-group",
					"role": "Несколько независимых вариантов."
				},
				{
					"id": "segmented-control",
					"role": "Один режим из короткого ряда 2–5 вариантов."
				},
				{
					"id": "select",
					"role": "Один вариант из длинного списка, который не нужно видеть целиком."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Определять модель выбора до выбора компонента: single, multiple или mode."
				},
				{
					"level": "must",
					"guidance": "Давать группе общее видимое и доступное имя."
				},
				{
					"level": "must",
					"guidance": "Сохранять один способ выбора внутри группы и явно показывать текущее значение."
				},
				{
					"level": "must-not",
					"guidance": "Смешивать radio и checkbox в одной логической группе или использовать select для 2–3 коротких режимов."
				}
			],
			"sequence": [
				{
					"step": "Контекст",
					"action": "Пользователь читает вопрос группы.",
					"response": "Общее имя объясняет, что именно выбирается."
				},
				{
					"step": "Сравнение",
					"action": "Пользователь просматривает доступные варианты.",
					"response": "Видимость вариантов соответствует длине и цене сравнения."
				},
				{
					"step": "Выбор",
					"action": "Пользователь меняет один или несколько пунктов.",
					"response": "Состояние читается формой, текстом и ARIA, а не одним цветом."
				}
			],
			"comparison": {
				"right": {
					"title": "Компонент следует модели выбора",
					"description": "Короткие режимы видны в segmented-control, один вариант — radio, несколько — checkbox-group.",
					"uses": [
						"segmented-control",
						"radio",
						"checkbox-group"
					]
				},
				"wrong": {
					"title": "Одинаковый select для любого набора",
					"description": "Два важных варианта спрятаны, multiple выглядит как single, а у группы нет общего имени.",
					"uses": [
						"select"
					]
				}
			},
			"skeleton": [
				{
					"id": "legend",
					"role": "Общий вопрос или имя группы."
				},
				{
					"id": "options",
					"role": "Набор вариантов в одном паттерне взаимодействия."
				},
				{
					"id": "support",
					"role": "Подсказка или ошибка группы, связанная со всеми вариантами."
				}
			]
		},
		{
			"kind": "pattern",
			"id": "application-shell",
			"name": "Каркас приложения",
			"status": "experimental",
			"since": "2026-07",
			"docOrigin": [
				"authored",
				"ai-assisted"
			],
			"summary": "Глобальная навигация, основное содержимое и адаптивный доступ к ним.",
			"description": "Каркас связывает `header`, `nav`, `main` и опциональный контекстный регион. Маршруты, права и бизнес-состав остаются в продукте.",
			"useCases": {
				"when": [
					"Приложению нужна постоянная глобальная навигация между несколькими маршрутами.",
					"На узком экране навигация должна раскрываться управляемо и возвращать фокус."
				],
				"avoid": [
					"Страница является автономным документом или коротким одностраничным сценарием.",
					"Композиция повторяет бизнес-виджет, а не глобальные регионы приложения."
				]
			},
			"composes": [
				{
					"id": "button",
					"role": "Открывает навигацию на узком экране и запускает глобальные действия."
				},
				{
					"id": "avatar",
					"role": "Опционально обозначает текущего пользователя без присвоения меню самому Avatar."
				},
				{
					"id": "divider",
					"role": "Разделяет устойчивые группы навигации только при реальной смысловой границе."
				}
			],
			"guidelines": [
				{
					"level": "must",
					"guidance": "Выражать каркас семантическими регионами `header`, `nav` и `main`; давать навигации доступное имя, если на странице она не одна."
				},
				{
					"level": "must",
					"guidance": "Показывать текущий маршрут не одним цветом и размечать его через `aria-current=page`."
				},
				{
					"level": "must",
					"guidance": "На узком экране делать открытие навигации явным, поддерживать Escape и возвращать фокус на кнопку открытия после закрытия."
				},
				{
					"level": "must-not",
					"guidance": "Регистрировать Header или Sidebar как отдельные компоненты без собственного поведения и переносимого API."
				},
				{
					"level": "must-not",
					"guidance": "Закреплять в каноне названия маршрутов, продуктовые роли, права доступа и состав бизнес-действий."
				},
				{
					"level": "should",
					"guidance": "Держать глобальное действие в одном устойчивом регионе, не дублируя его одновременно в header, navigation и содержимом."
				}
			],
			"sequence": [
				{
					"step": "Ориентация",
					"action": "Пользователь определяет продукт, текущий раздел и доступные глобальные действия.",
					"response": "Header задаёт контекст, navigation явно отмечает текущий маршрут, main получает фокус документа."
				},
				{
					"step": "Переход",
					"action": "Пользователь выбирает другой глобальный раздел.",
					"response": "Маршрут меняется, активный пункт обновляет текстовый или форменный признак и `aria-current`."
				},
				{
					"step": "Узкий экран",
					"action": "Пользователь открывает, использует или закрывает навигацию.",
					"response": "Фокус управляется предсказуемо; Escape закрывает область, после закрытия фокус возвращается на trigger."
				}
			],
			"comparison": {
				"right": {
					"title": "Связанные регионы с одним источником навигации",
					"description": "Header даёт контекст, navigation хранит маршруты и текущий пункт, main содержит задачу; мобильное раскрытие сохраняет фокус.",
					"uses": [
						"button",
						"avatar",
						"divider"
					]
				},
				"wrong": {
					"title": "Независимые контейнеры без общего поведения",
					"description": "Header и Sidebar дублируют действия, активность читается только цветом, а скрытая навигация оставляет фокус вне видимой области.",
					"uses": [
						"button"
					]
				}
			},
			"skeleton": [
				{
					"id": "header",
					"role": "Контекст продукта и ограниченный набор глобальных действий."
				},
				{
					"id": "navigation",
					"role": "Именованная глобальная навигация и текущий маршрут."
				},
				{
					"id": "main",
					"role": "Единственное основное содержимое текущего маршрута."
				},
				{
					"id": "context",
					"role": "Опциональная локальная навигация или сведения, не дублирующие глобальную."
				}
			]
		}
	],
	"scopeRubric": {
		"title": "Компонент, паттерн или продуктовая деталь",
		"lead": "Самостоятельный контракт становится компонентом, координация компонентов — паттерном. Бизнес-композиция остаётся в продукте.",
		"criteria": [
			{
				"id": "independent-contract",
				"question": "Есть ли у сущности собственные состояния, семантика или повторяемый API?",
				"component": "Да: контракт можно проверить независимо от окружающего экрана.",
				"pattern": "Нет: поведение возникает только из координации частей.",
				"productLocal": "Нет: повторяется лишь конкретная бизнес-разметка."
			},
			{
				"id": "coordination",
				"question": "Нужно ли зафиксировать порядок и отношения нескольких компонентов?",
				"component": "Нет: компонент сохраняет смысл сам по себе.",
				"pattern": "Да: ценность находится в композиции и последовательности.",
				"productLocal": "Да, но отношения зависят от одного продукта."
			},
			{
				"id": "product-dependency",
				"question": "Определяются ли части навигацией, данными или ролями конкретного продукта?",
				"component": "Нет: публичный контракт переносим между продуктами.",
				"pattern": "Только содержимое; структура решения остаётся переносимой.",
				"productLocal": "Да: без продуктового контекста сущность теряет смысл."
			},
			{
				"id": "portability",
				"question": "Перенесётся ли решение без копирования маршрутов, терминов и бизнес-логики?",
				"component": "Да, как автономный примитив.",
				"pattern": "Да, как схема отношений с заменяемым содержимым.",
				"productLocal": "Нет: перенос создаст ложную универсальность."
			},
			{
				"id": "proof",
				"question": "Подтверждена ли абстракция повторением и отличимым контрактом?",
				"component": "Повторяются API, состояния и доступность.",
				"pattern": "Повторяются роли, порядок и правила композиции.",
				"productLocal": "Пока повторяется только внешний каркас одного решения."
			}
		]
	},
	"scopeDecisions": [
		{
			"id": "header",
			"name": "Header",
			"verdict": "pattern",
			"target": "application-shell",
			"relation": "region",
			"rationale": "Шапка не имеет самостоятельного API. Её смысл задают навигация, продуктовые действия и остальной каркас.",
			"evidence": [
				"В репозитории нет реализации, токен-контракта или спецификации Header как компонента.",
				"Состав шапки меняется вместе с маршрутами, ролями пользователя и возможностями продукта."
			],
			"consequence": "Описывать как регион `application-shell`; не добавлять компонент Header без новых доказательств независимого поведения."
		},
		{
			"id": "sidebar",
			"name": "Sidebar",
			"verdict": "pattern",
			"target": "application-shell",
			"relation": "region",
			"rationale": "Боковая навигация получает семантику, адаптивность и активное состояние от общего каркаса приложения, а не от автономной визуальной оболочки.",
			"evidence": [
				"В репозитории нет реализации, токен-контракта или спецификации Sidebar как компонента.",
				"Сворачивание, мобильное раскрытие и фокус зависят от отношений с header и main."
			],
			"consequence": "Описывать как навигационный регион `application-shell`; продуктовые пункты и права доступа оставлять вне канона."
		},
		{
			"id": "form-section",
			"name": "Form section",
			"verdict": "pattern",
			"target": "form",
			"relation": "region",
			"rationale": "Секция формы группирует поля, но не добавляет поведения. Её семантику задают heading или fieldset/legend.",
			"evidence": [
				"У сущности нет собственных состояний, клавиатурного поведения или обязательного компонентного API.",
				"Нужная структура зависит от смысла группы: обычный раздел и один групповой вопрос требуют разной HTML-семантики."
			],
			"consequence": "Зафиксировать секции в паттерне `form`; не вводить универсальную обёртку FormSection, скрывающую выбор семантики."
		}
	],
	"decisionScenarios": [
		{
			"id": "global-application-navigation",
			"intent": "Собрать постоянную глобальную навигацию приложения с основным содержимым.",
			"context": [
				"Навигация действует на несколько маршрутов.",
				"На узком экране нужен управляемый переход к меню."
			],
			"expected": {
				"kind": "pattern",
				"id": "application-shell"
			},
			"forbidden": [
				{
					"id": "header",
					"why": "Отдельная оболочка шапки не фиксирует отношения с navigation и main."
				},
				{
					"id": "sidebar",
					"why": "Отдельная боковая панель не задаёт адаптивность и возврат фокуса всего каркаса."
				}
			],
			"rationale": "Задача координирует регионы страницы, поэтому требует паттерна, а не нового контейнерного компонента."
		},
		{
			"id": "long-form-sections",
			"intent": "Разделить длинную форму на именованные смысловые блоки.",
			"context": [
				"Поля остаются частью одной отправки.",
				"Каждый блок отвечает за одну локальную задачу."
			],
			"expected": {
				"kind": "pattern",
				"id": "form"
			},
			"forbidden": [
				{
					"id": "form-section",
					"why": "Универсальная обёртка скроет различие heading и fieldset/legend."
				}
			],
			"rationale": "Секция — регион формы; самостоятельного поведения и переносимого API у неё нет."
		},
		{
			"id": "immediate-binary-setting",
			"intent": "Изменить одно булево значение с немедленным эффектом.",
			"context": [
				"Отдельной отправки нет.",
				"Новое состояние применяется сразу."
			],
			"expected": {
				"kind": "component",
				"id": "switch"
			},
			"forbidden": [
				{
					"id": "checkbox",
					"why": "Checkbox выражает значение, которое обычно подтверждается вместе с формой."
				},
				{
					"id": "button",
					"why": "Button запускает действие и не сообщает устойчивое булево состояние."
				}
			],
			"rationale": "Switch одновременно показывает текущее состояние и обещает немедленное переключение."
		},
		{
			"id": "submitted-binary-value",
			"intent": "Собрать булево значение и отправить его вместе с остальной формой.",
			"context": [
				"Изменение не применяется немедленно.",
				"Значение входит в общий payload формы."
			],
			"expected": {
				"kind": "component",
				"id": "checkbox"
			},
			"forbidden": [
				{
					"id": "switch",
					"why": "Switch обещал бы немедленный эффект, которого здесь нет."
				}
			],
			"rationale": "Checkbox честно выражает отмечаемое значение до отдельного подтверждения формы."
		},
		{
			"id": "visible-single-choice",
			"intent": "Выбрать ровно один вариант из небольшого набора, который нужно сравнить целиком.",
			"context": [
				"Варианты являются ответами на один вопрос.",
				"Все ответы помещаются рядом без перегрузки."
			],
			"expected": {
				"kind": "component",
				"id": "radio"
			},
			"forbidden": [
				{
					"id": "select",
					"why": "Select скроет варианты, которые важно сравнить до выбора."
				},
				{
					"id": "segmented-control",
					"why": "Segmented control предназначен для коротких режимов, а не ответов формы."
				}
			],
			"rationale": "Radio сохраняет весь набор видимым и связывает варианты общим вопросом."
		},
		{
			"id": "short-view-mode",
			"intent": "Переключать один из нескольких коротких взаимоисключающих режимов представления.",
			"context": [
				"Вариантов от двух до пяти.",
				"Выбор меняет текущий режим, а не заполняет вопрос формы."
			],
			"expected": {
				"kind": "component",
				"id": "segmented-control"
			},
			"forbidden": [
				{
					"id": "radio",
					"why": "Radio создаст семантику вопроса формы вместо переключателя режима."
				},
				{
					"id": "select",
					"why": "Select без причины спрячет короткий ряд режимов."
				}
			],
			"rationale": "Segmented control держит короткие режимы видимыми и подчёркивает единственный активный."
		},
		{
			"id": "long-single-choice",
			"intent": "Выбрать один вариант из длинного списка, который не нужно видеть целиком.",
			"context": [
				"Одновременно нужен один ответ.",
				"Полная выкладка вариантов перегрузит поток."
			],
			"expected": {
				"kind": "component",
				"id": "select"
			},
			"forbidden": [
				{
					"id": "radio",
					"why": "Radio растянет длинный список и ухудшит обзор формы."
				}
			],
			"rationale": "Select сворачивает длинный single-choice, сохраняя видимое выбранное значение."
		},
		{
			"id": "destructive-expensive-action",
			"intent": "Подтвердить необратимое или дорого восстанавливаемое действие.",
			"context": [
				"Цена ошибки выше цены дополнительного шага.",
				"Нужно назвать объект и последствие."
			],
			"expected": {
				"kind": "pattern",
				"id": "destructive-confirmation"
			},
			"forbidden": [
				{
					"id": "button",
					"why": "Одна кнопка не содержит проверки объекта и последствия."
				},
				{
					"id": "alert",
					"why": "Alert сообщает риск, но сам не организует подтверждение и отмену."
				}
			],
			"rationale": "Задача требует композиции сообщения, спокойной отмены и конкретного danger-действия."
		},
		{
			"id": "searchable-multiple-choice",
			"intent": "Выбрать несколько элементов из длинного списка с поиском и обзором результата.",
			"context": [
				"Список нельзя эффективно показать целиком.",
				"Выбранные элементы нужно проверять отдельно от доступных."
			],
			"expected": {
				"kind": "component",
				"id": "dualist"
			},
			"forbidden": [
				{
					"id": "checkbox-group",
					"why": "Плоская группа не решает поиск и обзор большого выбранного набора."
				},
				{
					"id": "select",
					"why": "Single-select не выражает набор и две рабочие области."
				}
			],
			"rationale": "Dualist делает доступный и выбранный наборы отдельными областями одного сценария."
		},
		{
			"id": "business-specific-dashboard-block",
			"intent": "Показать сводку кампании с продуктовой метрикой, статусом и локальными действиями.",
			"context": [
				"Состав зависит от доменной модели кампании.",
				"Другие продукты не разделяют те же данные и действия."
			],
			"expected": {
				"kind": "product-local",
				"id": "campaign-summary"
			},
			"forbidden": [
				{
					"id": "application-shell",
					"why": "Глобальный каркас не должен поглощать бизнес-виджеты страницы."
				},
				{
					"id": "form",
					"why": "Паттерн ввода не описывает продуктовую сводку."
				}
			],
			"rationale": "Повторяется бизнес-композиция одного продукта, а не переносимый контракт дизайн-системы."
		}
	],
	"skins": {
		"implemented": [
			{
				"id": "blueprint",
				"label": "Blueprint",
				"base": true,
				"default": true,
				"summary": "Нейтральный схематичный каркас и база наследования всех скинов.",
				"source": "src/styles/skin-defaults.css"
			},
			{
				"id": "adscompass",
				"label": "AdsCompass",
				"summary": "Точный productScope-порт AdsCompass: коралловый акцент, bevel-тени и мягкие радиусы.",
				"source": "макеты и исходные компоненты AdsCompass"
			}
		],
		"planned": [
			{
				"id": "admy",
				"label": "ADMY"
			},
			{
				"id": "goldlead",
				"label": "GoldLead"
			}
		]
	},
	"planned": []
}