# UI Pattern Формы используют `react-hook-form`, `zodResolver` и `FormProvider`. Form state создаётся в контейнере или VM, чистое представление получает `onSubmit` и `disabled`, а поля подключает через `useFormContext`. Во время submit все действия формы блокируются. Ошибки Zod отображаются у поля; ошибки сети сохраняют введённые значения и показываются отдельным `Alert`. Запросные экраны без успешных данных выбирают состояние в порядке loading, error, empty, loaded. Если query уже содержит данные, они остаются видимыми при фоновом refetch и его ошибке: рядом показываются неблокирующий progress и локальное предупреждение с retry. Такое состояние называется Client Cached Snapshot; оно не заменяет доменное server-side stale/fallback состояние. Initial loading без доступного результата использует page-owned structural skeleton, повторяющий semantic regions экрана (heading, cards, filters, summaries или rows), а не `StateView` или отдельный spinner. Partial loading принадлежит section view и занимает только ещё не загруженный блок. Skeleton region имеет доступное busy-обозначение, но его декоративные shapes не содержат данных, не интерактивны и не получают фокус. Route fallback сохраняет доступный application shell и skeleton-изирует content area; до auth `ready` public и protected guards показывают neutral shell skeleton. Button `loading` для mutation и compact progress при Client Cached Snapshot остаются контекстными indicators и не заменяются skeleton. Для finance-экранов используется граница `container -> ViewModel -> view`. `*.tsx` без суффикса — container: он вызывает единственный page VM hook и выбирает top-level Loading/Error/Empty/Loaded view. `*.vm.ts` владеет data fetching, auth/route inputs, state selection, retry callbacks, domain grouping и derived presentation data. `*.view.tsx` и соседние presentation components получают props и рендерят JSX. View modules не вызывают data/auth/router/navigation/VM hooks: никаких `useBrokerage*`, `usePortfolio`, `useAuth`, `useParams`, `useNavigate` или page VM hooks внутри `*.view.tsx` и `components/*.view.tsx`. View modules также не группируют domain collections и не считают derived collection values вроде долей Portfolio Position. Эти значения готовит VM. Допустимые hooks во view: `useTranslation()` для labels, form-context hooks в form views и локальный UI-only state для rendering fallback, например failed image state. Простое форматирование одного переданного значения через существующий formatter остаётся presentation concern; вычисления из всей коллекции остаются во VM. View files дробятся по смысловой нагрузке, а не только по количеству строк. Каждый top-level state и каждый именуемый section страницы — отдельный `*.view.tsx` в `components/`, а не накопление внутри одного большого `*.view.tsx`. Контейнер импортирует state views из `./components` и выбирает нужный по `viewModel.state`. Держать loading/error/empty/loaded components в одном файле view нельзя. Known API errors, retry и stale snapshot остаются local views, а непойманные route errors обрабатывает общий route-keyed ErrorBoundary.