Аудит паритета · 8 сентября 2026

Паритет «По любви»

Полная сверка нативного Android-приложения ru.polyubvi.app с макетами Figma «По Любви Дизайн_Client» и с Flutter-приложением, экран за экраном: что совпадает, что реализовано иначе и чего нет вовсе.

Figma: По Любви Дизайн_Client (Copy).fig · md5 74083b20…2a4c · 938 МБ Flutter: farmgameowner/date → po-lyubvi-flutter · v1.4.2+128 · снапшот 29.06.2026 Android: /root/plibvi/android · v1.0.0 (versionCode 1) · ветка feature/figma-parity

Итог

Из 71 логического экрана и флоу ни один Android-экран не совпадает с макетом пиксель-в-пиксель. Android-приложение покрывает основной сценарий (вход → анкета → лента → мэтч → чат → профиль), но почти везде это собственный дизайн на Material-компонентах с другими текстами, а не воспроизведение Figma или Flutter.

0
пиксель-в-пиксель
2
близко к паритету: тексты и структура совпадают
47
есть, но расходится с Figma и/или Flutter
16
отсутствует в Android
6
есть только в Android (dating-tz)

Счёт ведётся по логическим экранам и bottom-sheet’ам из Figma и Flutter (не по фреймам Figma: одних только состояний ленты в Figma больше 40). Полный список — в разделе «Экраны».

Главное, что нужно знать

  1. UI kit не перенесён. Android использует Material 3: OutlinedTextField с подписью снаружи, Material Checkbox/Switch/Slider/RangeSlider, AlertDialog вместо bottom-sheet, обычные скругления вместо «smooth» (squircle) из Figma. Совпадают только палитра, шрифты, радиусы и набор иконок/картинок.
  2. Тексты не из arb. В Android все строки захардкожены в Kotlin (strings.xml содержит только имя приложения). Из ~600 строк Flutter-локализации дословно повторены только экраны входа; на остальных экранах заголовки, подписи и кнопки другие («Частые вопросы» вместо «Часто спрашивают», «Удалить аккаунт» вместо «Удалить профиль», «Это взаимно!» вместо «Это по любви!»).
  3. Чат — самое слабое место. Своё сообщение чёрное (Figma и Flutter — терракотовое #C66F53), время сообщения не показывается вовсе, нет редактирования, удаления, копирования, предупреждения о ссылках, «печатает…», онлайн-статуса собеседника, заголовка с верификацией/подпиской, пагинации истории, локального кэша. Подзаголовок «в сети / не в сети» отражает состояние WebSocket, а не собеседника.
  4. Онбординг переписан. Порядок шагов Flutter сохранён, но каждый вопрос анкеты стал отдельным экраном со списком вариантов: слайдеры с эмодзи, горизонтальные радио-кнопки, зависимые «[Необязательно]» под-вопросы, разводная «Не упусти свою любовь», шаг геолокации, счётчики «N из 10» / «🚩 0 из 7 💚 0 из 7» и трёхсегментный прогресс-бар — не реализованы. Должность и «О себе» стали обязательными (в Figma/Flutter — пропускаемые), лимит «О себе» 300 вместо 2000.
  5. Медиа и верификация. В онбординге нет сетки 3×2 и видео-кружка 208 — одно фото в круге и системный выбор видео; записи видео в приложении нет. Верификация — один экран со статусом вместо четырёх, селфи можно загрузить из галереи (в Figma и Flutter — только фронтальная камера с рамкой-подсказкой), тексты не из макета.
  6. Лента без экономики реакций. Нет счётчика лайков в шапке, лимитов и тултипа «Подожди 1 ч 36 м», кнопки возврата анкеты (undo), видео-кружка на карточке, обучающих видео, плашки «нецензурная лексика», BS «Не пропусти симпатию». Описание анкеты убрано в шторку «Узнать больше» вместо скролла под карточкой. Категории совпадения (matching_categories) не показываются.
  7. Подписка, реферал, сообщества — упрощённые белые экраны. Нет чёрного дизайна пейволла с офферами 2×2, скидками и юридическими кнопками; нет WebView с success/fail-редиректами, BS «Оформляем покупку»/«Оплата не прошла», экрана TYP, управления продлением; реферал делится user_id вместо OneLink-ссылки и не вызывает POST /referral/approve; в сообществах нет видимости значка, приглашений по ссылке, поиска, BS вступления и результата заявки.
  8. Отсутствуют системные экраны: «Твой аккаунт заблокирован» (код 101), hard/soft update и «Технические работы» (GET /update-works, коды 121/122), превью проверенного селфи, превью «Моя анкета», превью профиля пары из чата.
  9. Риск данных на Android. PATCH /users/profile собирается «полностью» с дефолтами («Пользователь», «—», male, 1995-01-01, 170 см, «Пока без описания», «Не указано»); анкета отправляется с show=true для всех шагов (Flutter скрывает шаг «Стабильность»); дату рождения и пол можно менять в редактировании (Figma: «Исправить дату рождения будет нельзя»); пропущенные анкеты не передаются в not_shown_user_ids и возвращаются после перезагрузки.
  10. docs/FLUTTER_PARITY.md ставит ✅ почти по всем пунктам. Это верно только в смысле «функция существует в каком-то виде»: из 44 отмеченных пунктов паритет с Figma/Flutter по дизайну, текстам и поведению выдерживают 2.

Источники и метод

Сверка сделана по исходникам, без доверия к описаниям и чеклистам: прочитаны все страницы Figma-файла, весь Flutter-код фич и весь Kotlin-код Android.

Figma
Файл /root/figma/polyubvi-2026-09-08.fig (копия с ноутбука, 938 МБ). Разобран локально через fig2sketch в текстовые дампы дерева нод по страницам: Онбординг 644, Главная 2265, Профиль 2426, Симпатии 7761, Чаты 7047, Доработки 8831, V3 10129, V4 12894, V5 16970, UI Kit 644, Сторы 10454 + черновые Wireframing / Internal Only Canvas / DSGN concept / Прототипы / Presentations. Читались размеры, отступы, заливки, типографика и тексты каждого фрейма. Рендеры не смотрелись (экспорт из .fig без Figma API невозможен), поэтому оценки «пиксель-в-пиксель» строятся на геометрии из нод.
Flutter
Снапшот GitHub farmgameowner/date (коммит 359d99c, 29.06.2026) → po-lyubvi-flutter, 1.4.2+128. Прочитаны lib/api, lib/uikit, lib/core, все 22 фичи в lib/features, lib/persistence, arb-локализация (999 строк). Вендорная копия в /root/plibvi/backend/po-lyubvi-flutter отличается от GitHub только добавкой trust-бейджа (UserCardTrustDto, UserTrustEntity, чип на карточке) и папкой features/dating_tz с README и тремя entity без экранов.
Android
/root/plibvi/android, все 84 Kotlin-файла (core, ui/auth, onboarding, main, feed, sympathy, chats, profile, subscription, verification, location, geo, scoring, voice, events, components, theme), AndroidManifest.xml, build.gradle.kts, ресурсы (assets/icons 139 SVG, assets/images 10 PNG, шрифты, strings.xml, themes.xml), docs/FLUTTER_PARITY.md.
Что считается совпадением
«Совпадает» — те же тексты, порядок элементов, размеры/отступы/цвета из макета и то же поведение. «Есть, но расходится» — экран или функция существует, но отличается хотя бы в одном из: тексты, композиция, компоненты, состояния, API. «Отсутствует» — экрана/функции в Android нет. Оценка ведётся по паре Figma→Android; там, где Flutter сам отходит от Figma, это отмечено отдельно.
Обозначения
✓ совпадает ◐ есть, но расходится ✕ отсутствует ⊕ только Android

Сводка по флоу

Сколько экранов каждого раздела Figma доехало до Android и в каком состоянии.

РазделЭкранов✓◐✕Коротко
Вход и системные6132Приветствие, телефон и OTP повторяют тексты Flutter; нет экрана блокировки и обновлений.
Онбординг «Данные о себе»6051Все шаги есть, но другие компоненты и подписи; нет шага геолокации.
Анкетирование и разное9072Вопрос = экран со списком; нет слайдеров, разводной, записи видео.
Верификация4031Один экран вместо четырёх; селфи из галереи; нет превью проверенного фото.
Лента14095Свайп и мэтч есть; нет undo, лимитов, видео, обучающих роликов, BS уведомлений.
Подписка3021Список тарифов и оплата в браузере; нет TYP, pending/error, продления.
Симпатии3030Списки вместо карточек-форм; таймер сырой строкой; псевдонимов нет.
Чаты6033Базовая переписка; нет контекстного меню, превью пары, BS уведомлений.
Профиль120111Полностью свой «redesigned home»; нет превью «Моя анкета».
Общие компоненты2110Таббар совпадает; снеки/ошибки — нет.
Только Android6⊕События «По душе», компания, GEO, скоринг, голос, trust-чип.

Дизайн-система

Токены совпадают, компоненты — нет. Figma везде использует «smooth»-скругления (squircle), Flutter повторяет их через SmoothRectangleBorder, Android рисует обычные RoundedCornerShape.

ЭлементFigma (UI Kit 644)Flutter (lib/uikit)Android (ui/theme, ui/components)Вердикт
ПалитраTerracotta #C66F53, Pale Beige #E7C9B2, Powdery #F2E7E3, Innocent White, Black #000; Beigeish Black #514A47, Beigeish Gray #B3ACA9, Powdery-50a (#F2E7E3 @50%), Dark Beige #9E8075, Dark Powdery #E1D6D1, Error Red #FA5F3DAppColorScheme 1:1; Powdery-50a вынесен непрозрачным #F9F3F2; тёмная тема = светлаяPlColors 1:1 (#F9F3F2 как powdery50a); только светлая Material-схема✓
ТипографикаTT Runs: headingHeavy 800 30/32, headingLight 500 30/32, titleHeavy 700 22/24, titleLight 500 22/24, captionBig 800 18/22, captionHeavy 700 18/22; Manrope: Subtitle 700 20/24, lineBold 700 16/20, lineLight 540 16/20, baseBold 700 14/18, baseLight 540 14/18, captionSmall 500 12/16AppTextScheme 14 стилей, lineLight/baseLight weight 500 (в Figma 540); шрифты TT-Runs + ManropePlType те же 14 стилей, те же размеры и letterSpacing; шрифты manrope_medium/bold, tt_runs_medium/bold/extrabold✓
Радиусы и отступыr8/12/16/20/28 smooth; отступы 4…48AppDimens crSmall…crBlock, SmoothRectangleBorder вездеPlDimens те же числа, но RoundedCornerShape (без squircle)◐ форма углов
Кнопка main (Big 56)#000 r12 pad 0 12, текст 16 белый; pressed #514A47; disabled #F2E7E3@50% + текст #9E8075 op50%; load — loader Medium 32; иконка 24 heavy слеваPLPrimaryButton: h56 r12 smooth, pressed #514A47, disabled #F9F3F2 + #9E8075@50%, лоадер 28×25 #9E8075; small 44; inverted; invertedTerracottaPrimaryButton: Material Button h56 r12, disabled #F9F3F2 + #9E8075@50%, лоадер 22 #9E8075; ripple вместо смены цвета; нет small/inverted-вариантов (в Auth используется тот же 56)◐
Кнопка secondarystroke #E1D6D1 1.33, текст Manrope Medium 16 #C66F53 (на экранах чаще #000), pressed op65%PLSecondaryButton: белая, stroke #E1D6D1 1.33, текст терракота (pressed 65%); secondarySmall 44 чёрный текст; secondaryInvertedSecondaryButton: OutlinedButton stroke чёрная 1.5, текст чёрный lineBold — другой стиль◐
Text-кнопка44, Manrope Medium 16PLTextButton h40 lineBold чёрный; textSecondary красныйMaterial TextButton, стили по месту (baseBold чёрный/терракота/красный)◐
Круглые кнопки Like/Dislike80×80 r10000, stroke белый 3 outside; Like #C66F53 + heart filled 40; Dislike #000 + reject 40; Disabled #B3ACA9 + иконка #514A47UserCardActionsButtonsWidget: 80 круги, cross_filled/heart_filled 40, disabled #B3ACA9/#514A47, обводка белая 3FeedFab: 68 (записка 54) с тенью 6dp, обводка 2.5, иконки 28; в симпатиях 64/72, в GEO 64/72◐
Поле ввода input--line366×56 r12 stroke #E1D6D1/#C66F53(focus) pad 18 20; подпись плавает внутрь белой плашкой 74×24 @12,−12 Manrope 12 #9E8075; prefix «+7»; caption под полем с иконкой 16; .44-button ErasePlTextInput: r12 smooth, stroke #E1D6D1, focus терракота 1.33, floating label #9E8075, hint #E1D6D1, suffix erase_filled, курсор терракота 1.5×20PlTextField: Material OutlinedTextField, подпись снаружи над полем (baseLight #514A47), фон #F9F3F2, hint #B3ACA9, без suffix-очистки (комментарий в коде: floating label «пробивает» обводку)◐
Textarea + счётчикinput--textarea, счётчик «0 / 120» на белой плашке справа сверхуPlTextArea: label + счётчик caption #9E8075 на плашкеНет компонента; счётчики «N/120» рисуются текстом под полем там, где есть◐
OTP-ячейкиinput--code-group: 6 ячеек, состояния default/filled/focus/blocked/errorPinput 6 × 56×56 r12, stroke #E1D6D1 1.33 / фокус терракота / disabled #F9F3F2 / error красный текстOtpPinField: 6 × 48×56 r12, фон #F9F3F2, граница 1/1.5, «·» placeholder, ошибки — текстом под полем◐
Ячейки даты рожденияinput--bday-group: 8 ячеек Д Д М М Г Г Г Г с точкамиPinput 8 × 38.75×56, маска «ДДММГГГГ», точки-разделителиНет: одно поле с визуальной маской 15.05.1995✕
Чекбоксcheckbox-cell 378×80 (V4): квадрат с галкойPlCheckBox 20×20 r8, обводка терракота 2, галка checkbox_icon_14, текст lineLight #514A47Material Checkbox (terracotta checked / #9E8075 unchecked)◐
Свитчер.swicher в switcher-cell 366×60PlSwitch 56×32, бегунок 26×24 r12, терракота/пудра, disabled #E7C9B2/#F9F3F2, 300 мсMaterial Switch (терракотовый трек)◐
Пикеры picker-single / picker-multiplesingle 120×44 r12 (checked #C66F53 белый текст, default stroke #E1D6D1); multiple h40 r28 pad 0 20 (Checked #C66F53, Deletable #000 + ×, dark-non-interactive #514A47, light stroke #F2E7E3, disabled #F2E7E3@50%)PLButtonStyles.selected/unselected h44 (терракота / stroke #F2E7E3 1.33), чипы интересов r28 pad 20/13, флаги чёрные r28 pad 11/20 с close_lightВарианты анкеты: строки r12 на всю ширину с чёрной заливкой выбранного; чипы флагов accent@12–16% r12/r20 с «×»; чипы интересов отсутствуют (список)◐
Слайдеры анкеты и фильтровslider-cell 358×94 (сегменты, эмодзи по краям, тег-подсказка); .manipulator 40×40 #C66F53 stroke белый 2; slider радиуса 334×3 с точкамиPLSlider (8 делений, сегменты терракота/пудра/чёрный, thumb r16 с обводкой), PLSliderWithAdditionalInfo (подписи + плашка #F9F3F2), PlLineSlider (линия 2px #514A47, thumb r16)Material Slider/RangeSlider (терракота/#E1D6D1); в анкете слайдер-вопросы показаны списком вариантов✕ в анкете ◐ в фильтрах
Барабанные пикеры (cupertino-picker)248×228 fill #F2E7E3@50% r16 (рост), cupertino-picker-pair 296×228 (возраст «от/до»), value-type cm / num / empty («Добавить»)PlScrollPicker (ListWheel itemExtent 40, фон #F9F3F2 r16, выделение 52px белое r8, вибрация)Нет: рост — числовое поле; возраст — RangeSlider✕
Navbarnavbar--iOS 44: title Manrope Bold 16 #000 по центру, back 24 @14, close @337, text-кнопки справа/слева Manrope Medium 16; progress-cell; chat-навбар 88 с фото 56 + имя + статусPlAppBar h44 centerTitle lineBold, back svg 18×11.5; ChatAppBar 56↔88PlTopBar h56, title titleHeavy 22 слева, subtitle caption, back 22, EnvBadge (не prod), action красный baseBold◐
Bottom-sheetбелый r28/28/0/0 smooth pad 24 12 8 12, handle-cell 36×4, overlay #000@60%PlBottomSheet: ручка 36×4 над листом, r28 smooth, pad 12/12/24/8, maxHeightRatio 9/16Material ModalBottomSheet только для «Узнать больше» и «Добавить фото» (r28, ручка 40×4); остальные BS Figma заменены полноэкранными экранами или AlertDialog◐
Диалогdialog--iOS (2 actions) / dialog--android (Material 3 r28)PlDialog: Cupertino на iOS, AlertDialog.adaptive на Android (TODO по стилю кнопок)Material AlertDialog (titleHeavy + lineLight + TextButton) — соответствует dialog--android✓
Snack390×56 сверху, negative #FA5F3D / positive #000, текст Manrope Medium 16 белый по центруSnackQueueFlow: overlay сверху на всю ширину, #FA5F3D/#000, lineLight белый, 4 с, свайп вверхНет единого снека: тосты терракотовой пилюлей сверху ленты, чёрная пилюля снизу профиля, красная лента «Нет сети», inline-тексты ошибок красным✕
LoaderLarge 48 (кольцо stroke 3 #000), Medium 32, Small 20PlCircularProgressIndicator large/medium/small, PLFullScreenLoader (чёрный 60% + белый)Material CircularProgressIndicator (терракота) + shimmer-скелетоны (#F9F3F2→#E7C9B2) + BrandedLoading («пульсирующий» круг 72 + «Загружаем…»)◐
Tooltip147×32 r12 pad 8 12, Light #F2E7E3@50% текст #C66F53 / Dark #C66F53 текст #F2E7E3, треугольник 17×8PLTooltip (терракота r12, caption #F2E7E3), прогресс-подсказки онбординга (#F9F3F2 плашка)Нет✕
Таббар.tab 48×40, Selected — чёрная «vector-shape» 50×44 подложка + белая иконка; 4 вкладки; заметка про бейдж «7»SafeArea + pad 8/24, элемент 48×40, иконки 24 heavy / selected svg 40, вибрация lightImpact; бейджей нетRow pad 6/24, TabSvg 48×40, selected svg 40 без тинта, idle 24 чёрная; верхняя линия 1px #E1D6D1 (в Flutter нет); анимация скрытия 240 мс; бейджей нет✓
Ошибка загрузки (placeholder)иллюстрация + «Не загрузилось :(» ExtraBold 18 #C66F53 + «Попробуй обновить страницу» + Small «Обновить»ErrorPlaceholder: error_placeholder.png h240, captionBig терракота, lineLight #514A47, primarySmall «Обновить» с refresh_16_heavy; вариант «Нет интернета :(»ErrorScreen: error_placeholder.png 160 + «Упс» titleHeavy + текст + PrimaryButton «Повторить» на всю ширину; сетевые ошибки распознаются по подстрокам◐
Иконки и картинки139 SVG (16/24/32/40 light/heavy/filled), pic-* иллюстрации, shape_1…5, 10 PNGassets/icons 139 svg + assets/images 10 pngassets/icons 139 svg + assets/images 10 png скопированы; PlIconAssets использует ~45 из них; иллюстрации pic-checkmark, shape_1…5, location_pic, pic_directional_sign, progress_circle и др. в UI не задействованы✓ ассеты
Нажатия«Карточки скейлим до 0.95×; ссылки и иконки — альфа 65%»PlOpacityGestureDetector (0.65), PLScaledGestureDetector (0.95)Material ripple / clickable без эффекта◐
Локализация—app_ru.arb, 999 строк; plural/gender-формыstrings.xml = только app_name; все тексты в Kotlin-константах, склонения через ручные функции (participantsLabel, friendsWord)◐
Тёмная темаНе задана (все экраны светлые; чёрные — только пейволл-успех, реферал, сообщества, блокировка)ThemeModeBloc, но darkTheme == lightThemeТолько lightColorScheme✓ (нигде нет)

Экраны

Для каждого экрана: что нарисовано в Figma (с размерами из нод), как это сделано во Flutter и что сделано в Android, затем список расхождений. Тексты в кавычках приведены дословно из макета, arb-файла и Kotlin-кода.

✓ совпадает◐ есть, но расходится✕ отсутствует⊕ только Android

Вход и системные экраны

6 экранов · ✓ 1 · ◐ 3 · ✕ 2
S01

Сплэш

◐ есть, но расходится

Figma

Auth 1: фон #C66F53, логотип-леттеринг из 7 векторов #F2E7E3 320×86 по центру. Отдельный фрейм «Android»: сплэш-контейнер 288×288 (Android 12 splash) с логотипом 192×52.

Flutter

flutter_native_splash (конфиг flutter_native_splash.yaml) + первый кадр приложения задерживается до прекеша couple.png для неавторизованных.

Android

Тема Theme.PoLyubvi = Material.Light.NoActionBar без атрибутов splash (windowSplashScreenBackground/AnimatedIcon не заданы). На Android 12+ система покажет иконку лаунчера на светлом фоне по умолчанию; фирменный терракотовый фон и леттеринг отсутствуют.

Расхождения

  • Нет терракотового фона #C66F53 и логотипа 192×52 в контейнере 288 из макета «Android».
  • Ассет логотипа-леттеринга в assets отсутствует (в Figma «по_любви_logo_sign_style»).
S02

Приветствие «Первый шаг к семейному счастью» (Auth 2)

✓ близко к паритету

Figma

Заголовок «Первый шаг к семейному счастью» Inter Bold 27/32 #C66F53 (несогласованность макета: остальные заголовки TT Runs), подзаголовок «Найдём вторую половинку, которая разделяет твои ценности» Manrope 16 #514A47, коллаж pic-welcome 296×232 (две фото в брендовых формах shape 3/2, повёрнуты −6.86°/6.29°, 7 эмодзи-бейджей 38×38 👼🏻 🎓 🧳 👰🏻‍♀️ 🎨 💍 🥂), ниже «Укажи телефон» TT Runs Bold 22 + «Создадим тебе новый аккаунт или найдём существующий», кнопка main «Войти по смс».

Flutter

AuthCta: Spacer · enterPhoneSplashScreenTitle headingHeavy терракота по центру · 12 · subtitle lineLight #514A47 · 24 · couple.png (pad 31) · 48 · enterPhoneScreenTitle titleHeavy · 8 · enterPhoneSplashScreenBodyText lineLight · Spacer · primary «Войти по смс» (pad 12/8). Коллаж заменён растровым couple.png.

Android

AuthCtaStep: та же последовательность и те же тексты (AuthCopy дословно из arb), couple.png высотой 220 с pad 31, отступы 12/24/48/8, PrimaryButton «Войти по смс» + 16 снизу. Шрифты TT Runs ExtraBold 30 / Manrope.

Расхождения

  • Кнопка — Material Button (ripple, обычные углы) вместо smooth r12 с pressed #514A47.
  • Высота картинки зафиксирована 220 (Flutter — по ширине); при узких экранах пропорции могут отличаться.
  • И Flutter, и Android показывают PNG вместо векторного коллажа с эмодзи-бейджами из макета (заложено во Flutter — не дефект Android).
S03

Ввод телефона (Auth 3 / V4 3.2)

◐ есть, но расходится

Figma

«Укажи телефон» Bold 21/24 + «Пришлём СМС с кодом доступа»; input--line 366×56 r12 smooth stroke 1.33 с плавающей подписью «Номер телефона», hint «987 654-32-10», prefix «+7»; icon-caption с замком «Никто не узнает твой номер»; checkbox-cell 378×80 «Я старше 18 лет и не состою в отношениях или браке» (V4); текст согласия Manrope 12/16 #514A47 с выделенной «политики конфиденциальности»; button-stack «Далее» @y738; snack #FA5F3D «Не отправилось, попробуй ещё раз».

Flutter

EnterPhone: pad 12; 16 + title titleHeavy + 8 + subtitle + 24 + PlTextInput (prefix «+7 » #9E8075, маска ### ###-##-##, paste-очистка 8/7/+7, autofocus) + 24 + lock_light терракота + «Никто не узнает твой номер» + Expanded + PlCheckBox + 20 + Text.rich caption (часть 2 терракотовая, тап → policyUrl) + 16 + primary «Далее» (enabled: телефон валиден И чекбокс). Ошибки — снек через ErrorDisplayer. Без AppBar.

Android

AuthPhoneStep: TextButton «Назад» (back 20 + baseBold) · 16 · «Укажи телефон» titleHeavy · 8 · subtitle · 24 · PlPhoneField (подпись снаружи, +7 prefix, маска) · 24 · lock 16 + «Никто не узнает твой номер» · 32 · inline-ошибка красным · Material Checkbox + тот же текст · 20 · лицензия caption (вся строка кликабельна) · 16 · PrimaryButton «Далее» (enabled: 10 цифр И чекбокс). Ошибки: «Введите номер полностью (10 цифр после +7)», «Подтверди, что тебе есть 18 и ты не в браке», текст сервера.

Расхождения

  • Поле: подпись снаружи над полем и фон #F9F3F2 вместо плавающей подписи внутри обводки #E1D6D1 (input--line).
  • Чекбокс Material вместо 20×20 r8 с терракотовой обводкой; текст чекбокса чёрный baseLight (Flutter — lineLight #514A47).
  • Кнопка «Назад» текстом в верхнем левом углу — нет в Figma/Flutter.
  • Ошибка запроса выводится строкой под полем вместо снека #FA5F3D сверху.
  • Кликабельна вся строка согласия, а не только «политики конфиденциальности».
  • Кнопка «Далее» не прижата к низу в button-stack, а идёт в потоке после текста.
S04

Ввод OTP (Auth 4 / 4.1–4.4)

◐ есть, но расходится

Figma

«Введи код»; «Отправили 6 цифр в СМС на +7(910)436-30-78» (номер #C66F53); text-кнопка «Изменить номер» с иконкой 16 heavy; input--code-group 6 ячеек; «Новый код можно будет получить через 59 с.»; состояния: 4.1 ввод (43 с.), 4.2 проверка (ячейки blocked + кнопка «Запросить новый код» load), 4.3 ошибка (ячейки Error), 4.4 «Запросить новый код» активна. Кнопки «Войти» нет — код проверяется автоматически.

Flutter

EnterOtp: pad 16; title titleHeavy; Text.rich (subtitle + телефон терракотой «+7(###)###-##-##»); «Изменить номер» (edit_16_heavy + baseBold); 24; Pinput 6×56 r12 stroke #E1D6D1, focus терракота, disabled #F9F3F2, ошибка красная + вибрация + очистка; SMS User Consent; ошибка «Неверный код, попробуй другой» baseLight #FA5F3D; таймер «Новый код можно будет получить через\nN сек» из code_lifetime сервера → кнопка refresh_16_heavy + «Запросить новый код». Автопроверка по 6 цифрам, кнопки входа нет.

Android

AuthOtpStep: TextButton «Изменить номер» (edit 16) · «Введи код» titleHeavy · «Отправили 6 цифр в СМС на +7 987 654-32-10» lineLight (номер не выделен) · OtpPinField 6×48×56 · подсказка «Тестовый код: …» (debug local/dusha) · ошибка caption red · TextButton «Новый код можно будет получить через N с.» / «Запросить новый код» · Spacer · PrimaryButton «Войти» (enabled при 6 цифрах; автосабмит тоже есть). Таймер 45 с задан в клиенте (RESEND_COOLDOWN_SEC), code_lifetime сервера игнорируется.

Расхождения

  • Ячейки 48 шириной (Figma/Flutter 56), фон #F9F3F2 вместо белого с обводкой.
  • Номер телефона не выделен терракотой; формат «+7 987 654-32-10» вместо «+7(987)654-32-10».
  • Добавлена кнопка «Войти», которой нет ни в макете, ни во Flutter.
  • Таймер повторной отправки — 45 с с клиента, а не code_lifetime (Figma 59 с) с сервера.
  • Нет состояния «blocked» ячеек во время проверки, нет вибрации/очистки при неверном коде, нет иконки refresh у «Запросить новый код».
  • Ошибка — текст сервера (например, «Неверный запрос.» подменяется техническим текстом), а не «Неверный код, попробуй другой».
S05

«Твой аккаунт заблокирован» (banned user)

✕ отсутствует

Figma

Экран fill #000: pic-attention 56 + «Твой аккаунт заблокирован» TT Runs ExtraBold 30 белый + «Твоя анкета или поведение нарушили правила сообщества. Если произошла ошибка, пиши в поддержку»; кнопки «Отменить подписку» (белая, если есть подписка) + «Написать в поддержку» (secondary белая обводка, иконка); подпись «Прочитай ещё раз правила сообщества, прежде чем обращаться в службу поддержки».

Flutter

BlockedUserInfo (/blocked-user): триггер — 500 с code 101 в любом ответе или при refresh; чёрный фон, AppBar с «Выйти» терракотой; pic_attention, blockedUserInfoTitle, description, ссылка на правила, PLSecondaryButton.inverted с support_24 «Написать в поддержку» → ContactUs BS. Кнопки «Отменить подписку» нет (дефект Flutter относительно макета).

Android

Нет. HttpClient не различает код 101: любой 401 после неудачного refresh → logout на экран входа; 500 с code 101 покажется как текст ошибки на экране.

Расхождения

  • Экран и обработка блокировки (serverCode 101, UserBlocked) отсутствуют полностью.
S06

Обновление приложения и технические работы (V4)

✕ отсутствует

Figma

hard update: «Обнови приложение» ExtraBold 30, иллюстрация 300×220, «Мы сделали поиск партнёра ещё точнее и комфортнее.» ExtraBold 18 #C66F53 + «Скорей скачивай новую версию…», кнопка «Обновить». maintenance: «Технические работы» + иллюстрация + «Технические работы будут завершены через: 01 : 23 : 48». soft update BS: update 56 + «Обнови приложение» + текст + «Обновить»/«Напомнить позже». maintenance BS/alert-карточка #514A47 на профиле.

Flutter

GET /update-works + заголовок X-App-Version + интерцептор кодов 121/122; HardUpdateWidget (оверлей: hardUpdateTitle headingHeavy, hard_update_image.png, title/description с сервера, secondary «Обновить» → стор). Soft update и тех. работы — TODO (не реализованы).

Android

Нет. Эндпоинт /update-works не вызывается, заголовок версии не отправляется, hard_update_image.png лежит в assets без использования.

Расхождения

  • Нет принудительного обновления, мягкого обновления и заглушки тех. работ.

Онбординг «Данные о себе»

6 экранов · ◐ 5 · ✕ 1
S07

Сплэш онбординга «Привет, расскажи о себе!» (Auth 5)

◐ есть, но расходится

Figma

«Привет, расскажи о себе!» ExtraBold 30/32 #C66F53; «Мы составили опросник для поиска серьёзных отношений. Больше ответов — больше найдётся партнёров»; кольцо прогресса 222×200 (три эллипса stroke 12: #E1D6D1, #C66F53, #E7C9B2) с «60%» ExtraBold 30 + «доступно» Bold 21; 13 сердечек #C66F53; кнопка «Заполнить профиль».

Flutter

Шаг 0: pad 16/8, title терракота + 12 + subtitle #514A47 + 48 + svg progress_circle (pad 19); primary «Заполнить профиль». AppBar скрыт.

Android

SplashStep внутри OnboardingChrome: caption «Расскажи о себе» + LinearProgressIndicator 4dp; Spacer; «Расскажи о себе» headingHeavy чёрный; 12; «Несколько коротких шагов — и анкета появится в ленте» lineLight; Spacer; PrimaryButton «Начать»; 24.

Расхождения

  • Все три текста другие («Расскажи о себе» / «Несколько коротких шагов…» / «Начать» вместо «Привет, расскажи о себе!» / «Мы составили опросник…» / «Заполнить профиль»).
  • Заголовок чёрный вместо терракотового; нет кольца прогресса с «60% доступно» и сердечек (progress_circle.svg есть в assets, не используется).
  • Сверху уже показан прогресс-бар и подпись, хотя в макете на сплэше их нет.
S08

Имя и фамилия (Auth 6–8)

◐ есть, но расходится

Figma

«Как тебя зовут?»; input «Имя» hint «Саша»; input «Фамилия» с caption «Увидят только твои пары» и иконкой hidden-light; icon-caption «Можно отредактировать в профиле» (edit light); navbar с прогрессом (quarter); кнопка «Далее» активна при обоих полях.

Flutter

Шаг 1: pad 12/8, 24 + enterNameScreenTitle headingHeavy + 24 + PlTextInput «Имя» (hint «Саша», формат [\sа-яА-Я-], maxLength 100) + 12 + «Фамилия» (hint «Иванов») + 4 + hidden_16_light #9E8075 + «Увидят только твои пары» baseLight; низ: edit_16_light терракота + «Можно отредактировать в профиле» + 16 + primary «Далее» (enabled: оба непустых). AppBar: back (неактивен на первом шаге) + 3-сегментный прогресс.

Android

NameStep: OnboardingChrome (caption «Имя» + прогресс 0.10) → «Как тебя зовут?» titleHeavy · 24 · PlTextField «Имя» (hint «Как вас зовут») · 12 · «Фамилия» (hint «Фамилия») · ошибка «Введите имя»/«Введите фамилию» · 24 · PrimaryButton «Далее» · TextButton «Назад». Без ограничений на символы и длину.

Расхождения

  • Заголовок titleHeavy 22 вместо headingHeavy 30.
  • Хинты «Как вас зовут» / «Фамилия» вместо «Саша» / «Иванов»; обращение на «вы» противоречит тону приложения.
  • Нет подписи «Увидят только твои пары» с иконкой и «Можно отредактировать в профиле».
  • Прогресс: линейный индикатор + подпись «Имя» вместо трёхсегментного бара 6px с подсказками «Доступно 25% анкет».
  • Кнопка «Назад» текстом внизу вместо back-иконки в navbar.
S09

Дата рождения (Auth 9–9.3)

◐ есть, но расходится

Figma

«Когда у тебя день рождения?»; input--bday-group 8 ячеек Д Д М М Г Г Г Г; caption «Люди увидят только возраст»; 9.1 — «Тебе 38 лет» (Inter Regular 40) + «Убедись, что всё верно. Исправить дату рождения будет нельзя»; 9.2 — «Тебе нет 18» (TT Runs Medium 30) + «Приложение доступно только для совершеннолетних пользователей», кнопка disabled; состояния ячеек error.

Flutter

Шаг 2: Pinput 8 ячеек 38.75×56 r12 с маской «ДДММГГГГ» и точками после 2 и 4 ячейки; ошибка «Введи реальную дату» #FA5F3D; при валидной дате — возраст headingLight терракота («Тебе 38 лет» / «Тебе нет 18») + caption; низ: lock_light + «Люди увидят только возраст» + primary «Далее» (enabled только при ≥18). Валидация: год ≥1900, не будущее, реальная дата.

Android

BirthdayStep: «Дата рождения» titleHeavy · PlTextField «Дата рождения» hint 15.05.1995 с DateVisualTransformation · ошибка «Дата в формате ДД.ММ.ГГГГ» · «Далее» · «Назад». Возраст не показывается; 18+ проверяется только при финальном submit («Регистрация с 18 лет»), после чего пользователь возвращается на этот шаг.

Расхождения

  • Одно поле с маской вместо 8 ячеек.
  • Заголовок «Дата рождения» вместо «Когда у тебя день рождения?».
  • Нет превью возраста «Тебе N лет» и предупреждения «Исправить дату рождения будет нельзя», нет подписи «Люди увидят только возраст».
  • Проверка 18+ отложена до конца онбординга (в макете кнопка «Далее» блокируется сразу).
  • В редактировании профиля Android дату рождения можно менять (макет: нельзя).
S10

Пол и рост (Auth 13–14)

◐ есть, но расходится

Figma

«Твой пол и рост?»; «Я» Medium 30 #B3ACA9; picker-single «Мужчина» / «Женщина» 120×44 (checked #C66F53); «Мой рост» + cupertino-picker 248×228 fill #F2E7E3@50% r16 (см); caption «Увидят все пользователи» (revealed-light); прогресс third.

Flutter

Шаг 3: title headingHeavy; 48; «Я» headingLight #B3ACA9; кнопки «Мужчина»/«Женщина» selected/unselected (gap 8); 48; «Мой рост» headingLight; PlScrollPicker 248×228, 50…250 «N см», default 180; низ: revealed_16_light + «Увидят все пользователи» + primary «Далее». Из профиля редактируется только рост.

Android

GenderHeightStep: «Пол и рост» titleHeavy · «Пол» baseBold · чипы «Мужской» / «Женский» (r12, чёрная заливка выбранного) · PlTextField «Рост, см» hint 170 (числовое, default 170, валидация 50–250, ошибка «Рост: от 50 до 250 см») · «Далее» · «Назад».

Расхождения

  • Тексты: «Пол и рост» / «Мужской» / «Женский» вместо «Твой пол и рост?» / «Мужчина» / «Женщина»; нет «Я» и «Мой рост».
  • Числовое поле вместо барабана 248×228; значение по умолчанию 170 вместо 180.
  • Выбранный вариант — чёрный чип r12 вместо терракотового picker-single 120×44 r12.
  • Нет подписи «Увидят все пользователи».
S11

Геолокация «Где ты находишься?» (Auth 15–17, 24; Главная 0.1)

✕ отсутствует

Figma

«Где ты находишься?» Bold 22 + «Хотим найти людей поближе к тебе»; иллюстрация location-pic 200×240 (пин #C66F53 + тень #E7C9B2); кнопки «Поделиться геопозицией» (main, иконка 24 heavy) + «Пропустить» (secondary); системный алерт iOS; вариант отказа с «Настройки» / «Выбрать город»; прогресс 3/4.

Flutter

LocationDeterminationView: title headingHeavy + subtitle «Разреши доступ к геолокации, чтобы мы смогли найти людей поближе к тебе»; состояния firstRequest (svg location_pic + primary «Далее» — без «Пропустить»), openSettingsOrChooseCity (location_pic_sad + «Не получили локацию» + «Разреши приложению доступ…» + «Настройки» / «Выбрать город»), citySelected («Твой город» + имя терракотой + «Выбрать другой»), loading. Отправляет POST /users/profile/position {position:{lat,long}} или {city_id}. Тот же экран встраивается в ленту, если гео не определена.

Android

Шага нет. Фаза CityPick сразу открывает SelectCityScreen; разрешение ACCESS_FINE_LOCATION запрашивается только на экране «Пересечения» (GEO). В фильтрах кнопка геолокации отсутствует. Координаты пользователя в /users/profile/position никогда не отправляются — только city_id.

Расхождения

  • Экран запроса геопозиции и автоопределение города отсутствуют; матчинг по координатам недоступен.
  • Тексты «Не получили локацию», «Настройки геолокации», «Сохранить и продолжить», «Твой город» нигде не используются.
S12

Выбор города (Auth 18–26)

◐ есть, но расходится

Figma

input--search «Поиск города» 366×56 r12 в search-cell 390×72; список city-cell 390×60 (город + область, picked с галочкой, last-child); skeleton short/long; ошибка «Не загрузилось :(» + «Обновить»; список главных городов по умолчанию (Москва, Санкт-Петербург, Абакан…).

Flutter

SelectCityView: PlAppBar без back, справа close; PlSearchField (autofocus, hint «Поиск города»); GET /cities?term (пусто → главные города; поиск от 3 символов, debounce 500 мс); строка pad 20/16: name lineLight + region caption #B3ACA9 + checkmark 16 терракота у выбранного (он поднимается первым); Divider #F9F3F2; шиммеры; пусто — «Не нашли город» + «Поищи ошибки или укажи ближайший населённый пункт»; error — ErrorPlaceholder.

Android

SelectCityScreen: PlTopBar «Город» с back; PlTextField «Поиск» hint «Москва» (pad 16); debounce 300 мс, поиск с первого символа; список карточек r12 #F9F3F2 (name lineBold + region caption); пусто — «Введи название города» / «Ничего не найдено»; ошибка — ErrorScreen; тап → POST position {city_id} → onSelected. Выбранный город не отмечается галочкой.

Расхождения

  • Строки-карточки с фоном вместо плоских city-cell с разделителем и галочкой выбранного.
  • Подпись «Поиск» + hint «Москва» вместо hint «Поиск города»; заголовок «Город» и back вместо close справа.
  • Пустые состояния: «Введи название города» / «Ничего не найдено» вместо «Не нашли город» + подсказки.
  • Скелетонов нет — Material-лоадер; поиск от 1 символа и 300 мс (Flutter 3 символа / 500 мс) — больше запросов к /cities.

Анкетирование, интересы, флаги, открывашки, медиа

9 экранов · ◐ 7 · ✕ 2
S13

Шаги анкетирования (1–8: дети, карьера, стабильность, новый опыт, духовность, курение, алкоголь, прошлые отношения)

◐ есть, но расходится

Figma

Экран шага: progress-bar, navbar, категория TT Runs Bold 22 #C66F53 («Отношение к детям»…), question-cell: вопрос Manrope Bold 20 #000 + subtitle Manrope 16 #514A47; ответ — slider-cell 358×94 с тегом («Не против детей» / «Хочу детей» / «Чайлдфри») и эмодзи по краям (🛀/💼, 🖇️/🕊️) или radio-group picker-single (горизонтально 2–3 по 175/120, вертикально 358×44, wrap для религий); «[Необязательно]» Manrope 16 #B3ACA9 над вторым вопросом, который появляется в зависимости от ответа (дети → «Сколько всего ты хочешь детей?»); внизу icon-caption «Увидят все пользователи» / «Не будет показано»; tooltip «Доступно 25% анкет»; loader 48 для шага.

Flutter

Survey: шаг = экран со всеми вопросами шага (pad 16/24: title headingHeavy терракота + description + вопросы gap 32 + зависимые под-вопросы при выборе ответа); single — Wrap кнопок selected/unselected h44 minWidth 120 (повторный тап снимает выбор); multiple — чипы; slider — PLSlider с подписями и плашкой-хинтом #F9F3F2; «[Необязательно]» lineLight #B3ACA9; низ: статус видимости (revealed/hidden_16_light + «Будет показано в анкете» / «Не будет показано в анкете»; шаг «Стабильность» скрыт) + primary «Далее» (enabled при ответах на обязательные). Аналитика action_survey_next_step/skip_step.

Android

SurveyQuestionStep: каждый вопрос — отдельный экран (flattenSurvey: required-шаги первыми, затем optional); stepTitle baseBold терракота · вопрос titleHeavy · description · «Можно выбрать несколько · N выбрано» · варианты вертикальным списком r12 (чёрная заливка выбранного, stroke #E1D6D1) · «Далее» / «Пропустить» (для необязательного) · «Назад». Слайдер-вопросы разбираются в тот же список опций (hint как подпись). Вложенные под-вопросы внутри ответов (answer.questions) парсером не читаются и никогда не показываются. Статуса видимости нет; ответы всегда отправляются с show=true. Шаги с hidden=true не фильтруются (комментарий в коде).

Расхождения

  • Нет слайдеров с эмодзи и тегами — все вопросы выглядят одинаково списком.
  • Нет зависимых «[Необязательно]» под-вопросов («Сколько всего ты хочешь детей?», «У партнёра тоже сильные карьерные амбиции», «У твоей пары должна быть квартира?» и т. д.) — эти ответы на Android в принципе не собираются.
  • Нет горизонтальных picker-single 120×44 и wrap-раскладки; выбранный вариант чёрный, а не #C66F53.
  • Категория — мелкий baseBold вместо Bold 22 терракотой; нет подсказок-тултипов и статуса «Не будет показано в анкете».
  • Флаг видимости шага теряется: Android всегда шлёт show=true (в макете и Flutter шаг «Стабильность» скрыт от других пользователей).
  • Порядок: все обязательные вопросы всех шагов подряд, потом необязательные — макет чередует их внутри шага.
S14

Разводная «Не упусти свою любовь» (шаг 9)

✕ отсутствует

Figma

Иллюстрация (конверт/указатель) + «Не упусти свою любовь» + «Ответь на необязательные вопросы, и мы подберём максимально подходящего тебе человека» (Manrope Regular 16) + «Заполнить до конца» (main) + «Дополнить позже» (secondary) + tooltip «Шанс найти пару в 2 раза выше».

Flutter

OptionalPartView (шаг 13): pic_directional_sign + 32 + optionalSegmentScreenTitle headingHeavy + 8 + subtitle; primary «Заполнить до конца» / secondary «Дополнить позже» (→ skipToMedia).

Android

Нет. Необязательные вопросы идут подряд с кнопкой «Пропустить»; возможности «Дополнить позже» и перейти сразу к медиа нет (pic_directional_sign.svg в assets не используется).

Расхождения

  • Экран и переход «Дополнить позже → медиа» отсутствуют.
S15

Интересы (10/15–17)

◐ есть, но расходится

Figma

«Интересы» + «Выбери до 10 любимых занятий и хобби. Подберём для тебя людей со схожими увлечениями»; navbar «0 из 10» → «10 из 10» (#C66F53 при 10); категории Manrope Bold 16 #9E8075 с эмодзи (🚴🏻‍♂️ Спорт и фитнес, 🎨 Творчество, ⛺️ Путешествия, ✍🏻 Саморазвитие, 🎲 Развлечения, 🍏 Диета/☕️ Еда); чипы picker-multiple r28 wrap по центру; при 10 остальные disabled; «Пропустить» пока 0, «Далее» при ≥1; экран 1942 высотой со скроллом.

Flutter

InterestsFlow (шаг 14): title/description с сервера, категории lineBold #9E8075 + Wrap чипов r28 (selected терракота/белый pad 20/13; unselected stroke #E1D6D1; inactive #F9F3F2 при 10); AppBar «N из 10» (терракота при 10); низ primary «Далее» / secondary «Пропустить» при 0. Ответы уходят в survey шаг 9.

Android

Отдельного экрана нет: multiple_choice-вопросы шага интересов проходят как обычные SurveyQuestionStep — по одному экрану на категорию, варианты вертикальным списком, подпись «Можно выбрать несколько · N выбрано», лимит SURVEY_MULTI_MAX 10 на вопрос (а не суммарно на все категории).

Расхождения

  • Категории разбиты на отдельные экраны; нет единого экрана с чипами и счётчиком «N из 10» в navbar.
  • Лимит 10 применяется к каждой категории, а не ко всем интересам вместе (в макете — суммарно 10).
  • Нет эмодзи в названиях категорий и состояния disabled при достижении лимита.
S16

Должность (11)

◐ есть, но расходится

Figma

«Кем ты работаешь?» / «Укажи название своей должности»; input «Моя должность» hint «Менеджер», caption «Увидят только твои пары» (hidden-light); «Пропустить» → при вводе «Далее»; tooltip «Доступно 85% анкет».

Flutter

Шаг 15: title + subtitle + PlTextInput (maxLength 50, формат буквы/цифры/.,!?:;, autofocus, label «Моя должность», hint «Менеджер»); низ primary «Далее» / secondary «Пропустить» (AnimatedCrossFade пока пусто).

Android

OccupationStep: «Чем занимаешься?» titleHeavy · PlTextField «Работа / занятие» hint «Дизайнер, врач, студент…» (в UI до 80 символов, при отправке обрезается до 50) · ошибка «Укажите работу или род занятий» — поле обязательное · «Далее» · «Назад».

Расхождения

  • Тексты другие; нет подзаголовка и подписи «Увидят только твои пары».
  • Шаг нельзя пропустить (в макете и Flutter — можно).
  • Лимит в поле 80, реальный лимит бэка 50 — ввод молча обрезается при отправке.
S17

Флаги (12–12.4)

◐ есть, но расходится

Figma

«Твои «Флаги»» / «Что моментально отталкивает или привлекает тебя в партнёре?»; блок 🚩 «Недопустимо» + «Например: вечно в телефоне» (Manrope 14 #B3ACA9) + чип «+»; блок 💚 «Обожаю» + «Например: сюрпризы» + «+»; navbar «🚩 0 из 7 💚 0 из 7»; bottom-sheet с input «Красный флаг» hint «вечно в телефоне» + «Сохранить ответ»; добавленные — чёрные чипы Deletable с ×.

Flutter

RedFlagsView (шаг 16): ListView pad 16: title + subtitle + 🚩 + «Недопустимо» (subtitle) + «Например: вечно в телефоне» baseBold #B3ACA9 + WrappedTextList (чёрные чипы r28 pad 11/20 белый текст + close_light; кнопка «+» белая stroke #E1D6D1, скрыта при 7) + 💚 «Обожаю» + «Например: сюрпризы» + список; AppBar «🚩 N из 7 💚 N из 7» (терракота при 7); BS AddFlagBottomSheet (PlTextInput maxLength 30 + «Сохранить ответ»); низ «Далее» / «Пропустить».

Android

FlagsStep: «Флаги» titleHeavy · «Что цените и с чем не готовы мириться. Можно пропустить.» · «Зелёные флаги» lineBold терракота · чипы accent@12% r12 с «×» · строка «Добавить» + поле (hint «честность») + чёрный круг 48 «+» · «Красные флаги» lineBold #FA5F3D · чипы + поле (hint «грубость») · «Далее» · «Пропустить» · «Назад». Лимит 7 / 30 символов соблюдён.

Расхождения

  • Порядок блоков обратный: сначала зелёные, потом красные (макет: 🚩 сверху).
  • Нет эмодзи 🚩/💚, названий «Недопустимо»/«Обожаю» и примеров «Например: …»; заголовок «Флаги» вместо «Твои «Флаги»».
  • Добавление inline-полем вместо bottom-sheet «Красный флаг» / «Зелёный флаг» + «Сохранить ответ».
  • Чипы полупрозрачные accent@12% r12 вместо чёрных r28 с иконкой close_light.
  • Нет счётчика «🚩 N из 7 💚 N из 7» в navbar.
S18

Открывашки (13–13.4, 18)

◐ есть, но расходится

Figma

««Открывашки»» / «Выбери до трёх карточек и ответь на вопросы. Другие пользователи увидят ответы в твоей анкете»; navbar «0 из 3»; чипы категорий (Личность / Семья / Мечты / Отношения) sticky @y98; секции с карточками ⟨prompt⟩ 366×60/80 #F2E7E3@50% r16; BS ответа: вопрос + textarea «Твой ответ» hint «Напиши, что думаешь» счётчик «0 / 120» + «Сохранить ответ»; выбранные карточки терракотовые с ответом поднимаются наверх; skeleton; ошибка «Не загрузилось :(».

Flutter

PromptsView (шаг 17): VerticalScrollableTabView с pinned категориями (чипы r28), SliverAnimatedList ответов (терракотовые карточки r16 с trash/edit), секции вопросов (#F9F3F2 r16 pad 20 lineLight #9E8075); BS PromptsBottomSheet (PlTextArea maxLength 120 + «Сохранить ответ»); AppBar «N из 3»; шиммеры; низ «Далее»/«Пропустить». Макс 3.

Android

PromptsStep: «Открывашки» · «До 3 ответов — помогают начать разговор. Можно пропустить.» · выбранные карточки #F9F3F2 r12 (title baseBold терракота + value + «×») · категории caption #B3ACA9 + вопросы lineBold r12 (чёрный выбранный) · поле «Ответ» + SecondaryButton «Добавить открывашку» · «Далее» · «Пропустить» · «Назад». Лимит 120 символов без счётчика; категории не sticky.

Расхождения

  • Подзаголовок другой; заголовок без кавычек «Открывашки».
  • Нет счётчика «N из 3» в navbar и sticky-чипов категорий.
  • Ответ вводится inline-полем, а не в bottom-sheet со счётчиком «0 / 120».
  • Карточки выбранных ответов — светлые #F9F3F2 вместо терракотовых с иконками trash/edit.
S19

«Что мы забыли спросить?» (14)

◐ есть, но расходится

Figma

«Что мы забыли спросить?» (ExtraBold 30 / Bold 22) / «Расскажи о себе в свободной форме или переходи к просмотру анкет»; textarea «Обо мне» hint «Люблю шоколадное мороженое» счётчик «0 / 2000»; «Пропустить» → «Далее».

Flutter

AboutMeView (шаг 18): title + subtitle + PlTextArea (maxLength 2000, 1–5 строк, label «Обо мне», hint «Люблю шоколадное мороженое»); «Далее» / «Пропустить».

Android

AboutMeStep: «Что мы забыли спросить?» ✓ · «Пара предложений о себе» · PlTextField «О себе» hint «Пара предложений» (multi-line, максимум 300 символов) · поле обязательное (ошибка «Напишите пару слов о себе») · «Далее» · «Назад».

Расхождения

  • Шаг нельзя пропустить (макет: «Пропустить»).
  • Лимит 300 вместо 2000, счётчика нет.
  • Подзаголовок, подпись и hint другие.
S20

Медиа: фото и видео (Медиа 1–6)

◐ есть, но расходится

Figma

«Добавь видео и фото» ExtraBold 30 / «Запиши видео-кружочек. Пользователи с видео находят пару в два раза быстрее.»; video-add-circle 208×208 stroke #E1D6D1 (No-Permission с «Настройки»); подпись «1 фото — минимум» → «Зажми фото, чтобы перетащить»; photo-grid 366×264 3×2 ячейки 116.67×128 r16 stroke #E1D6D1 (photo-add-cell, tooltip «+ 3%»); системный picker «Выбери до 6 фото»; кнопка «Сохранить»; модерация «Проверяем…»; BS «Нельзя удалить фото» (1 фото — минимум).

Flutter

MediaContentView (шаг 19): title/description, блок 256 с VideoContentComponent (круг 208: empty video_add 40 терракота / player loop mute / удаление с подтверждением «Удалить?» 3 с), подпись, GalleryContentFlow (ReorderableGridView 3 колонки 116×128 r16, drag, модерация, удаление 44 blur, ошибки, «+ 3%»); PhotoPickBottomSheet «Сделать фото» / «Выбрать из галереи»; лимиты 6 фото / 10 МБ, видео 10 с / 50 МБ; низ primary «Сохранить» (enabled при ≥1 фото).

Android

Два экрана. PhotoPickStep: «Добавьте фото» · «С фото анкета заметнее и попадает в ленту. Можно пропустить и добавить позже.» · photo_helper.png 160 · круг 200 (#F9F3F2, stroke #E7C9B2, photo_placeholder 48 + «Выбрать») · «Выбрать фото»/«Далее» · «Выбрать другое» · «Пропустить фото» · «Назад» — одно фото, можно пропустить. VideoPickStep: «Видео о себе» · «Короткое видео повышает доверие…» · плашка 160 r16 «Выбрать видео» (системный picker) · «Сохранить и начать» / «Пропустить и начать». Сетка 3×2 реализована только в редактировании профиля.

Расхождения

  • В онбординге нет сетки 3×2 и видео-кружка 208 — одно фото в круге + отдельный шаг видео.
  • Обращение на «вы» («Добавьте фото», «Можно пропустить»); тексты не из макета.
  • Фото можно пропустить (макет и Flutter: «1 фото — минимум», кнопка «Сохранить» недоступна без фото; Flutter затем принудительно ведёт на редактирование профиля через ProfileReaction).
  • Нет обработки permanentlyDenied для камеры/галереи с кнопкой «Настройки».
  • Порядок: фото → видео (макет: один экран с кружком над сеткой).
S21

Запись видео-кружка (Медиа 1.1–1.8, 5–5.2)

✕ отсутствует

Figma

Чёрный экран: video-circle Big 366×366, navbar type=media с таймером «0:6,13», record-indicator, кнопки .56-button (закрыть) / button-camera 72 (video / Recording / done / photo / disabled) / camera-switch; состояния done «0:10,00», loading, error snack, Paused, Cam Error («Камера не работает»); «Проверь фото» превью 338×560.

Flutter

VideoRecordView: AppBar чёрный («Отмена», точка записи + таймер «m:ss,ms», макс 10 с), CameraPreview в круглом клипе с прогресс-кольцом, тулбар trash_40 / кнопка записи 72 (58→24 терракота) / camera_switch 40, «Камера не работает», лимит 50 МБ, ошибки cameraException*.

Android

Нет. Видео выбирается из галереи системным PickVisualMedia (без ограничения 10 с / 50 МБ на клиенте) и загружается POST /users/profile/video; предпросмотра кружка нет ни в онбординге, ни в профиле.

Расхождения

  • Экран записи, таймер, лимит 10 с, круглый предпросмотр и удаление с подтверждением отсутствуют.

Верификация

4 экрана · ◐ 3 · ✕ 1
S22

Разводная верификации «Сделай селфи и получи галочку» (1)

◐ есть, но расходится

Figma

pic-checkmark 56 + «Сделай селфи и получи галочку» Bold 22; pro-cell ×4 (иконка 24 + текст 16 #514A47): «10 лайков в день вместо 8», «Твою анкету увидят на 60% больше людей», «Пользователи с галочкой получают больше симпатий», «Это бесплатно и займёт меньше минуты»; кнопки «Получить галочку» (main) + «В другой раз» (secondary).

Flutter

StartVerification: verification_illustration + startVerificationTitle headingHeavy + 4 преимущества (иконки match_24_light / revealed_24_light / checkmark_24_light / time_light + lineLight #514A47) + primary «Получить галочку» / secondary «В другой раз». Открывается автоматически после онбординга (replaceAll [MatchingAgeRange, VerificationRouter]) и из профиля/симпатий/чата.

Android

VerificationScreen (Intro): PlTopBar «Верификация»; smiling_woman.png 180; карточка «Статус» #F9F3F2 («Не пройдена»); «Подтверди, что анкета настоящая. Сделай селфи — так другим спокойнее лайкать.» + «• Лицо хорошо видно • Без масок и чужих фото • Нейтральный фон»; PrimaryButton «Далее» + SecondaryButton «Позже». Показывается один раз после экрана возраста (postVerifyDone) и из профиля.

Расхождения

  • Тексты преимуществ («10 лайков в день вместо 8»…) и заголовок не используются; вместо них собственный текст и буллеты.
  • Иллюстрация — smiling_woman.png (в макете это картинка инструкции), а не pic-checkmark/verification_illustration.
  • Карточка «Статус» и заголовок navbar «Верификация» — нет в макете.
  • Кнопки «Далее»/«Позже» вместо «Получить галочку»/«В другой раз».
S23

Инструкция и камера-селфи (2–3.3, 19–20)

◐ есть, но расходится

Figma

Инструкция: photo-frame 366×366 r28 с плейсхолдером-инструкцией + «Открой камеру, повтори жест и сделай селфи»; navbar «Пропустить»; icon-caption «Селфи увидят только твои пары» + «Открыть камеру»; 19 — отказ камеры (gear 40 + «Не можем открыть камеру» + «Настройки»). Селфи (чёрный): photo-frame Helper (силуэт) + «✌️» 64 + «Повтори жест» Bold 20 белый + «Твоя будущая пара увидит это фото, улыбнись :)» #B3ACA9; кнопки close / shutter / camera-switch; «Проверь фото»; loading; error snack; «Камера не работает».

Flutter

Instruction (PlAppBar back + «Пропустить»; картинка smiling_woman.png или прошлое селфи r28; verificationInstructionTitle; низ hidden_16_light + «Селфи увидят только твои пары» + «Открыть камеру» → camera permission; denied → gear_40 + тексты + «Настройки»). Selfie: чёрный экран, CameraPreview (фронтальная, veryHigh) + photo_helper.png рамка, «✌️» 64 + «Повтори жест» + подпись; trash / button_camera_take / camera_switch; crop в квадрат, зеркалирование, POST /users/profile/verify/photo; ошибки 107/108/115.

Android

Тот же VerificationScreen (Capture): иллюстрация verification_illustration 160 или превью снимка 200 r16; «Сделай селфи или выбери фото из галереи»; PrimaryButton «Сделать селфи» (системная камера TakePicturePreview, любая камера, без рамки-подсказки и жеста) + SecondaryButton «Из галереи» (PickVisualMedia); отправка сразу после выбора без «Проверь фото».

Расхождения

  • Загрузка селфи из галереи — прямое нарушение сценария верификации (макет и Flutter допускают только живое селфи с фронтальной камеры и жестом ✌️).
  • Нет собственного экрана камеры: силуэта-подсказки, «Повтори жест», проверки фото перед отправкой, переключения камеры.
  • Нет подписи «Селфи увидят только твои пары», нет обработки «Не можем открыть камеру» с кнопкой «Настройки» (только текст ошибки «Нужен доступ к камере для селфи»).
  • Нет обработки ошибок 107/108/115 текстами из arb — показывается текст сервера.
S24

Ожидание проверки (4–4.1)

◐ есть, но расходится

Figma

Иллюстрация (лупа над фото) + «Отправили фото на проверку» Bold 22 + «Обработка обычно занимает от 10 минут. Сообщим о результате в пуш-уведомлении»; кнопки «Включить уведомления» (secondary) + «Завершить» (main); системный алерт push.

Flutter

Waiting (назад нельзя): verification_waiting_illustration + title/subtitle из arb; secondary «Включить уведомления» (permission → openAppSettings при permanentlyDenied) + primary «Завершить» (fetch статуса → закрыть).

Android

Тот же экран (Waiting): verification_waiting 180 + карточка «Статус: На проверке» + «Проверяем фото. Обычно это занимает немного времени — можно закрыть экран.» + SecondaryButton «Обновить статус»; polling 20×3 с; в Done — «Готово! Значок верификации появится в профиле и в ленте.» + «Готово». Rejected — «Проверка не прошла. Сделай новое селфи при хорошем свете, лицо полностью в кадре.»

Расхождения

  • Тексты другие; нет кнопки «Включить уведомления» и запроса push-разрешения (макет обещает пуш о результате).
  • Нет кнопки «Завершить» — вместо неё «Обновить статус» и polling.
  • Состояния Done/Rejected показываются на этом же экране (в макете отдельных экранов результата нет — результат приходит пушем и отражается в профиле).
S25

Превью проверенного селфи «Фото проверено» (Чаты)

✕ отсутствует

Figma

Экран «Селфи»: фото 366×366 r28 + pic-checkmark + «Фото проверено» ExtraBold 30 + «Константин — реальный человек, мы проверили его селфи»; navbar «Селфи» + «Закрыть». Открывается по тапу на «Проверен» в шапке чата.

Flutter

VerificationPhotoPreviewFlow: PlAppBar «Селфи» + «Закрыть»; фото r28 (placeholder figure_female/male по гендеру), pic_checkmark, headingHeavy «Фото проверено», subtitle с именем.

Android

Нет. В чате нет блока верификации собеседника; verified_photo_url из ChatUserDto не используется.

Расхождения

  • Экран отсутствует; пользователь не может увидеть проверенное селфи пары.

Лента анкет

14 экранов · ◐ 9 · ✕ 5
S26

«Последний шаг и в бой!» — возраст партнёра (Главная 0)

◐ есть, но расходится

Figma

Title Inter Bold 27 #C66F53 «Последний шаг и в бой!» + «Давай определимся с возрастом желаемого партнёра» Inter 14 #514A47; «от» / «до» TT Runs Medium 30 #B3ACA9 + cupertino-picker-pair 296×228; button-caption: icon-caption «Ищем пару от 25 до 45 лет» (search-light 16) + «Смотреть анкеты».

Flutter

MatchingAgeRange: ListView pad 20: 44 + title headingHeavy терракота + 12 + subtitle + 85 + два PlScrollPicker 116×228 (18…99, default 25/45, минимум 3 года, bounce) с линией 32×1; низ: search 16 терракота + «Ищем пару от 25 до 45 лет» + primary «Смотреть анкеты» → сохранить ageRange → MatchingRoute.

Android

AgeRangeScreen: «Последний шаг и в бой!» titleHeavy чёрный + subtitle ✓ + 32 + строка «Возраст» subtitle / «25–45» lineBold терракота + «Ищем пару от 25 до 45» baseLight #B3ACA9 + Material RangeSlider 18–99 (span ≥3) + PrimaryButton «Смотреть анкеты» (PATCH recalculate с текущими фильтрами) + TextButton «Пропустить»; системный Back = пропустить.

Расхождения

  • Заголовок чёрный titleHeavy 22 вместо терракотового 27/30.
  • RangeSlider вместо двух барабанов «от»/«до».
  • Подпись без слова «лет» и без иконки поиска; лишняя кнопка «Пропустить» (в макете экран обязательный).
  • Возраст не сохраняется локально как во Flutter (ageRange в MatchingStorage) — только на бэке через recalculate.
S27

Карточка анкеты и верхняя панель (Главная 1, 1.1 full, 1.2/1.3 scroll)

◐ есть, но расходится

Figma

navbar-custom 390×68: слева profile-return 56 (превью предыдущей анкеты + arrow-return = вернуть), по центру counter-box (два сердца + барабан цифр «12»), справа filters. profile-face 366×627 r28: фото, pager 80×8 @24,24 (точки), gradient-bottom 160, summary @20,505: distance (location-filled 16 + «Москва»/«9 км»), name-box («Всеволод» TT Runs Bold 22 + «31» Medium 22 + check-white 24 + premium-icon), match-data (match-filled 32 + «78» Medium 22 + «%» 16 + tags-common чипы #252525@70% blur r12 «Не планирую детей», «Йога»), video-circle 96 @top-right. Ниже (gap 32): «Отправить записку» secondary 240 с counter 3; должность Bold 16 + био 14 по центру; рост «171 см» Medium 22 #9E8075; values-теги #F2E7E3@50% r12; «Мои увлечения» чипы; открывашки; 🚩/💚 чипы; «Пожаловаться» красная. Плавающие Dislike 80 @8,669 / Like 80 @302,669 (уменьшаются до 38–40 при скролле). Tab Bar.

Flutter

MatchingView: ReactionsBar 68 (undo 56 с фото + arrow_return, сердце 42×34 с числом лайков и анимацией, filters_24_heavy), карточка UserCardView (карусель фото с тапами, pager svg, градиент 160 #1B1A1C, location/ActivityStatus («Онлайн» / «Был 3 часа назад»), имя titleHeavy + возраст titleLight + premium 24 + checkmark_white 24, match_filled 32 + процент + логотип общего сообщества + теги matchingCategories, видео-аватар 96 с фулскрином), UserDescriptionWidget под карточкой (obscene-плашка, «Отправить записку» с бейджем, job/bio, «N см», values, «Состою в сообществах», «Мои увлечения», открывашки, 🚩 Недопустимо в партнёре / 💚 Обожаю в партнёре, «Пожаловаться»), FAB 80/80 при скролле. Свайп с оверлеями like/dislike.

Android

FeedScreen: PlTopBar «Лента» + filters. Карточка r28 (width = height×0.6) с чёрной подложкой и запечённым scrim-градиентом; индикаторы фото — полоски 3dp сверху (Instagram-стиль), тап-зоны 48; «···» 36 чёрный@42% → жалоба; низ pad 18/16: «Имя, возраст» titleHeavy белый + verified 18 #E7C9B2 + «★» + TrustChip; locationText lineLight; «Совпадение N%» baseBold #E7C9B2 + «В сети» caption; job baseBold; bio 2 строки; кнопка «Узнать больше» → UserCardDetailsSheet (шторка с секциями: открывашки, о себе, увлечения/ценности/сообщества, зелёные/красные флаги). Док кнопок 118: pass 68 чёрная / записка 54 белая / like 68 терракота. Следующая карточка размыта под текущей. Нет undo, счётчика лайков, видео-кружка, категорий совпадения, скролла описания под карточкой.

Расхождения

  • Верхняя панель: «Лента» + фильтры вместо undo-кнопки с фото + счётчика лайков-барабана + фильтров.
  • Пропорции карточки фиксированы 0.6 (макет 366×627 ≈ 0.58, на всю ширину); пейджер — полоски вместо точек 8px @24,24.
  • Статус активности «В сети» только для online; «Был 3 часа назад» / «Был(а) только что» не выводится.
  • Совпадение — «Совпадение N%» текстом вместо иконки match-filled 32 + «78» Medium 22 + «%»; категорий совпадения (чипы «Не планирую детей», «Йога») нет.
  • Премиум — символ «★» вместо premium-icon 24 #E7C9B2; галочка 18 #E7C9B2 вместо check-white 24.
  • Описание анкеты не под карточкой, а в модальной шторке «Узнать больше»; заголовки в шторке другие («Образ жизни», «В отношениях», «🟢 Зелёные флаги» / «🔴 Красные флаги» вместо «🚩 Недопустимо в партнёре» / «💚 Обожаю в партнёре»); рост «171 см» Medium 22 #9E8075 заменён на мета-строку «171 см · Москва · в сети».
  • Кнопка записки — круглая 54 с самолётиком вместо широкой secondary «Отправить записку» с бейджем оставшихся записок.
  • Кнопки Like/Dislike 68 с тенью и всегда видимы (макет: 80, уменьшаются до 38–40 при скролле).
  • Нет видео-кружка 96 и полноэкранного видео (S37), нет плашки «Анкета содержит нецензурную лексику» (S38).
  • Обращение «Пожаловаться на анкету» и меню «···» — макет: текстовая кнопка «Пожаловаться» с иконкой alert внизу описания.
S28

Лайк, дизлайк, возврат анкеты (5–5.3, profile-return)

◐ есть, но расходится

Figma

Свайп: карточка уменьшается/поворачивается (3°→10.42°), like-overlay растёт 137→843 px rot 22–30°, dislike-overlay влево; счётчик уменьшается; следующая карточка снизу. profile-return State=Default показывает фото предыдущей анкеты — тап возвращает её (undo); «from rewind» пейволл при исчерпании возвратов.

Flutter

CardActionsComponent: порог 30%, угол ±π/24, action-анимация 1050 мс, оверлеи ×16; лайк → POST /matching/like/{id} (3 попытки, 110 → снек «Закончились доступные реакции»), −1 лайк оптимистично; дизлайк локально; undo → POST /matching/bring-back (без подписки −1 возврат, при 0 → пейволл endedUndos), повторный дизлайк после лайка → POST /matching/undo-like/{id}; instant match → MatchRoute; первый лайк → BS уведомлений.

Android

Свайп: порог 20% ширины или скорость 750 px/s, поворот до 14°, rubber-band; like_overlay/dislike_overlay 120 с масштабом .75→1.1; лайк → POST /matching/like (optimistic advance), pass → локальный dismiss; кнопки дока делают то же. Возврата анкеты нет; повторного показа пропущенных после перезагрузки не избежать (dismissedPersonIds только в памяти, не передаются в recalculate).

Расхождения

  • Undo/«вернуть анкету» (POST /matching/bring-back, /matching/undo-like) отсутствует — обещанная подпиской «Бесконечные возвраты» функция не работает.
  • Ошибка лимита реакций (code 110) не распознаётся — общий текст ошибки вместо «Закончились доступные реакции».
  • Оверлеи like/dislike меньше и без поворота 22–30°, счётчик сердец не анимируется (его нет).
S29

Лимит лайков: счётчик, тултип «Подожди 1 ч 36 м», «zero likes» (6–6.2)

✕ отсутствует

Figma

counter-box «Heart zero» (белое сердце stroke #E7C9B2), кнопка Like Disabled #B3ACA9, Tooltip Dark #C66F53 «Подожди 1 ч 36 м» над баром; пейволл «from likes» («Открой больше сердечек», «+4 сердечка каждый день»).

Flutter

MatchingReactionsBloc: GET /users/profile/reactions (likes/superlikes/returns); heart_dotted при 0; тап по сердцу/лайк без лайков: не верифицирован → VerificationRouter, нет подписки → SubscriptionOffers(endedLikes), иначе тултип «Подожди {N ч M мин}» до конца дня.

Android

Нет. Эндпоинт /users/profile/reactions не вызывается, счётчика и тултипов нет; лимит проявится только серверной ошибкой на POST like.

Расхождения

  • Экономика реакций (лимиты лайков/записок/возвратов, ведущие в пейволл) отсутствует полностью.
S30

Мэтч «Это по любви!» (11)

◐ есть, но расходится

Figma

Экран fill #C66F53; три размытых эллипса (#E7C9B2 466, #F2E7E3 266, #FFD3B2 335) + огромное сердце stroke #F2E7E3@50% rot 10; два shape-brand 160×182 (form 4 rot −10° своё фото, form 3 rot 10° фото пары) с белой обводкой 4; «Это по любви!» TT Runs ExtraBold 30 белый + «Вы и Всеволод нравитесь друг другу» + кнопка «Открыть чат»; закрыть.

Flutter

MatchView: терракота, AppBar с close 16×12 белым, три белых круга alpha .6 с анимацией, heart_zero 552×439, карточки 160×182 (своя — SvgClip shape_3 + outline, +10°; пары — r40/200 обводка 4, −10°) с въездом по Cubic(0.47,0,0.23,1.38); matchTitle headingHeavy + matchSubtitle(name) + primary «Открыть чат» → ChatsList + Chat.

Android

MatchOverlay: терракота, крестик 40 белый@22%, heart_zero 160 белая@12%, две карточки 148×196 r16 stroke 3 белая (пары и своя) ±10°; «Это взаимно!» headingHeavy белый + «Ты и {имя} понравились\nдруг другу» lineLight; белая кнопка «Написать {имя}» baseBold терракота + «Продолжить свайпы»; запрашивает POST_NOTIFICATIONS.

Расхождения

  • Тексты: «Это взаимно!» / «Ты и … понравились друг другу» / «Написать …» / «Продолжить свайпы» вместо «Это по любви!» / «Вы и … нравитесь друг другу» / «Открыть чат».
  • Фото в прямоугольниках 148×196 r16 вместо брендовых форм shape 3/4 160×182 (SVG shape_1…5 лежат в assets).
  • Кнопка — белая с терракотовым текстом (Flutter: PLPrimaryButton, чёрная? — во Flutter primaryInvertedTerracottaForeground белая с терракотой: совпадает с Android), «Продолжить свайпы» — лишняя.
  • Нет размытых эллипсов-фона (#E7C9B2/#F2E7E3/#FFD3B2) и анимации въезда карточек с ±30°.
S31

Записка (супер-лайк) (8)

◐ есть, но расходится

Figma

Фикс-панель снизу r28 pad 24 12 8 12: «Отправь записку» Manrope Bold 20 + «Всеволод увидит твоё сообщение до взаимной симпатии» + textarea 366×56→96 stroke #C66F53 label «Текст записки» hint «Сделай комплимент» counter «0 / 200» + кнопка «Отправить»; snack «Отправили твою записку».

Flutter

SuperLikeView (BS): superLikeTitle subtitle + superlikeSubtitle(name) baseLight + PlTextArea (label/hint, maxLength 120 — макет 200) + primary «Отправить» → POST /matching/super-like → снек «Отправили твою записку» + свайп вправо. Без записок и подписки → SubscriptionOffers(endedSuperLikes).

Android

Material AlertDialog r16: «Написать записку» titleHeavy терракота · «Для {имя}» lineBold · «Увидит до взаимной симпатии · одна тёплая фраза» caption · PlTextField «Текст» hint «Что тебя зацепило?» · счётчик «N/120» · PrimaryButton «Отправить записку» · «Отмена». Успех — карточка уходит, без снека.

Расхождения

  • Диалог по центру вместо нижней панели; все тексты другие.
  • Лимит 120 (как во Flutter; макет — 200).
  • Нет снека «Отправили твою записку», нет проверки остатка записок и перехода в пейволл.
S32

Жалоба на анкету (10–10.4)

◐ есть, но расходится

Figma

bottom-sheet 390×320: «На что хочешь пожаловаться?» Manrope Bold 20 + secondary-кнопки «Нецензурная лексика», «Откровенные фотографии», «Неприемлемое поведение», «Не тот, за кого себя выдаёт» (+ скрытая «Другое»); loader на выбранной кнопке; success — карточка улетает + чёрная snack «Получили твоё обращение»; error — snack #FA5F3D.

Flutter

Report BS: title subtitle + список PLSecondaryButton (gap 8), лоадер на выбранной, остальные disabled; POST /users/{id}/report {report_place: user_card, report_reason}; снек «Получили твоё обращение»; карточка удаляется из ленты.

Android

ReportUserDialog (AlertDialog): «На что хочешь пожаловаться?» titleHeavy · 4 строки lineBold (те же причины; «Не тот, за кого себя выдаёт» с «ё») · «Отмена». Карточка скрывается оптимистично, тост терракотовой пилюлей «Пожаловали. Анкета больше не появится в ленте»; при ошибке «Анкета скрыта. Жалобу не удалось отправить — попробуй позже».

Расхождения

  • AlertDialog со строками вместо bottom-sheet с secondary-кнопками 56.
  • Тексты результата другие («Пожаловали…» вместо «Получили твоё обращение»), нет состояния loader на кнопке.
S33

BS «Не пропусти симпатию» после первого лайка (5.4–5.5)

✕ отсутствует

Figma

bottom-sheet 390×304: pic-match 56 + «Не пропусти симпатию» Manrope Bold 20 + «Когда Всеволод поставит тебе лайк, узнай об этом сразу» + «Включить уведомления» (main) + «Пропустить» (secondary) → системный алерт push.

Flutter

SympathyNotificationPermission BS после первого лайка (FirstLikeStorage), не показывается при granted; кнопка «Открыть настройки» при permanentlyDenied.

Android

Нет. Системный запрос POST_NOTIFICATIONS вызывается напрямую при показе MatchOverlay и при входе в чат, без объясняющего листа.

Расхождения

  • Кастомный pre-permission bottom-sheet отсутствует (и для симпатий, и для чата — см. S51).
S34

Обучение свайпу и скроллу (2.a–2.d, 3–3.2, Доработки)

◐ есть, но расходится

Figma

Overlay 60% на карточке + close; две изогнутые белые стрелки + сердце по центру + кружок-указатель; карточка качается ±3°; текст «Смахни вправо, чтобы лайкнуть или влево, чтобы отклонить партнёра» Manrope 14 белый + «Понятно» Bold 16 #C66F53. Scroll education: pointer 72 + «Смахни вверх, чтобы посмотреть анкету подробнее» + «Понятно». Тайминги анимации в заметках.

Flutter

SwipeTraining: затемнение #000@80% r28, поворот ±π/64 циклом 4.4 с, training_pointer + training_arrow_left/right, текст matchingTrainingSwipeDescription + «Понятно»; ScrollTraining с автоскроллом и текстом «Смахни вверх…». Флаг first_open_cards.

Android

SwipeTrainingOverlay: затемнение #000@82%, бейджи cross «Пропуск» / heart «Нравится» по бокам (для события «Не интересно»/«Пойду»), демо-карточка 140×186 со штампами, палец «👆» в белом круге, текст «Свайпни вправо, если нравится. Влево — пропустить.» + кнопка «Понятно» (белая r12) + «или нажми в любом месте». Флаг swipe_training_done. Обучения скроллу нет (описание в шторке).

Расхождения

  • Собственная композиция: эмодзи-палец и штампы вместо SVG training_arrow_left/right + training_pointer (лежат в assets); текст другой.
  • Нет второго шага «Смахни вверх, чтобы посмотреть анкету подробнее».
S35

Пусто / ошибка / нет сети в ленте (0.6–0.8)

◐ есть, но расходится

Figma

0.6: иллюстрация сломанного пина + «Не загрузилось :(» ExtraBold 18 #C66F53 + «Попробуй обновить страницу» + Small «Обновить» 135×44. 0.7: «Нет интернета» + «Проверь подключение к сети» (без кнопки). 0.8: pic-no-people 200×240 + «Анкеты закончились» + «Измени параметры поиска или приходи завтра» (без кнопки).

Flutter

ErrorPlaceholder («Не загрузилось :(» / «Нет интернета :(» + «Попробуй подключиться к сети и обновить страницу» + «Обновить»); empty — no_users.png + matchingNoUsers/Description без кнопки.

Android

Empty: no_users.png 200×240 + «Анкеты закончились» captionBig терракота + «Измени параметры поиска или приходи завтра» + PrimaryButton «Обновить» (70% ширины). Сеть: «Нет связи с сервером» + «Проверь интернет и попробуй снова» + «Обновить». Ошибка: ErrorScreen «Упс» + текст + «Повторить».

Расхождения

  • Пустое состояние совпадает по текстам и картинке, но добавлена кнопка «Обновить», которой нет в макете.
  • Ошибка и «нет сети» — другие тексты и большая primary-кнопка вместо Small 44.
S36

Обучающие видео в ленте (9 videos / 9.1 / 9.2)

✕ отсутствует

Figma

Видео-карточка 366×627 r28 среди анкет: .sound-switcher 44, плашки-субтитры «У ВСЕХ» «БЫЛО» «ТАК», прогресс-сегменты 326×3 #C66F53; paused — overlay 60% + play 112. V3: videoplayer fullscreen с лайком/шэром/закладкой, списки «Советы по здоровым отношениям» (в работе).

Flutter

GET /advertising (show_frequency, videos) при profile.showTrainingVideo; FullScreenVideoPlayer-карточка каждые N анкет, свайп закрывает, громкость запоминается, аналитика action_view_healthy_video. Списки/закладки V3 — нет.

Android

Нет; вместо этого в ленте появляются карточки событий «По душе» из /feed (dating-tz).

Расхождения

  • Функция и настройка «Обучающие видео» (свитчер в настройках есть, но ни на что не влияет) отсутствуют.
S37

Видео-кружок анкеты и полноэкранное видео (4–4.4)

✕ отсутствует

Figma

video-circle 96 на карточке (loading stroke #B3ACA9, failed placeholder); fullscreen: overlay #252525@55% blur, video-circle Big 326 с плеером/прогрессом/паузой, close @322,20, mute 44.

Flutter

Видео-аватар 96×96 (белая обводка 2, loop, mute), тап → полноэкранный CircularVideoPlayer с прогресс-кольцом и звуком; кэш MatchingVideosCacheManager на 5 видео.

Android

Нет. video_url из UserMediaDto не отображается ни на карточке, ни в шторке, ни в симпатиях/чате; в профиле видно только «Видео загружено».

Расхождения

  • Видео пользователей не воспроизводится нигде.
S38

Плашка «Анкета содержит нецензурную лексику» (Доработки)

✕ отсутствует

Figma

Блок 366×144 fill #000 r16 pad 12 12 20 12 между карточкой и «Отправить записку»: oopps 56 + «Анкета содержит нецензурную лексику» Manrope Bold 20 белый.

Flutter

UserDescriptionWidget при isObscene: чёрная карточка r16 pad 12/12/12/20 с oopps 56 + matchingObsceneLanguageWarningText subtitle белый. В профиле — ObsceneLanguageWidget (S52).

Android

Нет. Поле is_obscene читается в UserProfileInfoDto, но нигде не показывается; в UserCardDto его нет вовсе.

Расхождения

  • Предупреждение о нецензурной лексике отсутствует и в ленте, и в профиле.
S39

Фильтры «Настройки поиска» (filters 1–5, V3/V4)

◐ есть, но расходится

Figma

navbar «Настройки поиска» + right «Готово» (#9E8075 / #E1D6D1 disabled); Filter-cell 390 stroke-bottom #F2E7E3@50% pad 24 16: «Город» Bold 20 + «Автоматически»/«Петропавловск-Камчатский» #B3ACA9 + иконки geolocation heavy и edit heavy; «Радиус поиска» + «50 км»/«Любой» + slider 358×44 (.manipulator 40 #514A47) + switcher «Можно немного расширить»; «Возраст» + «25–45 лет» + cupertino-picker-pair 366×140 + switcher; «Рост» + «Не важен»/«От 160 см»/«160–180 см» + picker-pair («Добавить»); V4: «Показывать только участников сообщества» + switcher-cell с логотипами 32; loader/error/snack «Не сохранилось, попробуй ещё раз».

Flutter

FiltersView: PlAppBar back + «Настройки поиска» + «Готово» (терракота при изменениях); секции с Divider: Город (cityName/«Автоматически» + geolocation_24_heavy (запрос GPS) + edit_24_heavy → SelectCity), Радиус (PlLineSlider 1…100, «N км»/«Любой», PlSwitch «Можно немного расширить»), Возраст (два PlScrollPicker + PlSwitch), Сообщества (строки с логотипом 32 + PlSwitch), Рост (кнопка «+ Добавить» ↔ PlScrollPicker 70…250, «Не важен»/«до N см»/«от N см»/«N-M см»); PATCH /matching/recalculate; full-screen loader; снек при ошибке.

Android

FiltersScreen: PlTopBar «Фильтры» / «Кого показывать в ленте» + back; карточки r16 stroke: «Локация» (плашка geolocation + город + «Город поиска» + «Изменить» → SelectCityScreen; GPS нет), «Радиус» (Material Slider 1–100, «макс.» при 100, подписи «1 км»/«100 км», Switch «Расширять, если мало людей»), «Возраст» (RangeSlider 18–99 + Switch «Расширять возраст»), «Рост» (RangeSlider 70–250, min span 10, без «Не важен»), «Сообщества» (чипы; только вступившие переключаемы; счётчик N / M и пояснения); низ PrimaryButton «Применить» (enabled при dirty) + «Измени параметры, чтобы применить».

Расхождения

  • Заголовок «Фильтры» / «Кого показывать в ленте» вместо «Настройки поиска»; сохранение кнопкой «Применить» внизу вместо «Готово» в navbar.
  • Нет кнопки геолокации (автоматическое определение города недоступно), «Автоматически» заменено на «Авто (геолокация)»/«Не указано».
  • Возраст и рост — RangeSlider вместо барабанов; рост нельзя оставить «Не важен» (всегда отправляется диапазон 150–200 по умолчанию, фильтруя анкеты без роста).
  • Порядок секций: Локация → Радиус → Возраст → Рост → Сообщества (макет V4: Город → Радиус → Возраст → Сообщества → Рост).
  • Тексты свитчеров: «Расширять, если мало людей» / «Расширять возраст» вместо «Можно немного расширить»; «макс.» вместо «Любой».
  • Сообщества — чипы с галочкой вместо строк с логотипом 32 и свитчером; заголовок «Сообщества» вместо «Показывать только участников сообщества».
  • Ошибка сохранения — текст в форме, не снек «Не сохранилось, попробуй ещё раз».

Подписка «Любовь без границ»

3 экрана · ◐ 2 · ✕ 1
S40

Пейволл «Планы подписки» (premium 1/2, from likes/rewind/lovenotes)

◐ есть, но расходится

Figma

navbar «Подписка» + close; заголовок TT Runs ExtraBold 30 #C66F53, зависящий от точки входа: «Узнай, кому понравилась твоя анкета» / «Открой больше сердечек» / «Не поздно передумать» / «В три раза больше записок» + подзаголовок «Оформи подписку «Любовь без границ» — …»; offers 366×140: две карточки 179×140 r16 (stroke #E1D6D1, выбранная stroke #C66F53 2 + заголовок терракотой): «1 месяц» ExtraBold 16 + «299 ₽» + «299 ₽ / мес.» #B3ACA9; «3 месяца» + «699 ₽» + зачёркнутое «899 ₽» + «233 ₽ / мес.» + бейдж «-20%» r8; «Что в подписке» + чипы #F2E7E3@50% r28 (#9E8075): «+4 лайка в день», «Кому понравилась анкета», «Бесконечные возвраты», «+2 записки в день»; terms (iOS/Android тексты Manrope 12) + Small secondary «Условия» (rules-heavy) / «Приватность»; фикс-низ: icon-caption «Списываем деньги раз в 3 месяца» + main #1B1A1C «Купить за 699 ₽» + text «Восстановить покупки»; overlay-лоадер; payment native (App Store sheet) / webview «Оплата» с таббаром СБП/банк.

Flutter

SubscriptionOffers(entryPoint sympathy|endedLikes|endedUndos|endedSuperLikes|other): PlAppBar title + close; ListView: title headingHeavy терракота по entryPoint + description + сетка тарифов 2 колонки (TariffWidget r20 stroke #E1D6D1 / выбран терракота 2; период captionBig, цена titleLight + зачёркнутая базовая с анимированной линией, «oneTimePayment.price / period» caption, бейдж «-N%»); «Что в подписке» + FeatureWidget по logo/text с сервера; legal caption iOS/Android + две small-кнопки «Условия подписки»/«Политика»; низ primary «Купить за {цена}» + iOS «Восстановить покупки». Android: POST payment/android → ExternalPayment (InAppWebView, перехват success/fail redirect, банковские схемы) → POST payment/android/status → TYP / Pending BS / Failed BS; snack noSubscriptionAfterSuccessPayment.

Android

SubscriptionScreen (единый экран для «есть/нет подписки»): PlTopBar «Подписка» / «Premium активен» | «Больше возможностей»; карточка терракота@10%: «Premium «По любви»» subtitle + «Подписка активна до …» или «Больше лайков, супер-лайки, кто лайкнул вас, расширенные фильтры.»; список фич «• text» (fallback 4 пункта); «Тарифы» вертикальными строками r12 (чёрная выбранная): name lineBold + периодЛейбл (Неделя/Месяц/3 месяца/Год) + «N ₽» (discountPrice или basePrice); подпись про оплату через Google Play / браузер; PrimaryButton «Оформить» → PlayBilling (если PREFER_PLAY_BILLING и есть play_store_product_id) либо POST /payments/android → Chrome Custom Tabs; при возврате POST /payments/android/status + GET /subscriptions/me; кнопка «Проверить оплату»; «Готово» при активной подписке.

Расхождения

  • Нет заголовков по точке входа — экран открывается только из профиля (нет ни одного триггера из ленты/симпатий/чата, потому что нет лимитов и блюра-CTA).
  • Тарифы — список строк вместо двух карточек 179×140 с зачёркнутой ценой, «N ₽ / мес.» и бейджем «-20%»; oneTimePayment/discount не показываются.
  • Название «Premium «По любви»» вместо «Любовь без границ»; чипы фич заменены маркированным списком.
  • Нет ссылок «Условия»/«Приватность», нет юридического текста про списание с банковского счёта, нет «Восстановить покупки» и icon-caption «Списываем деньги раз в 3 месяца».
  • Оплата открывается во внешних Custom Tabs, а не в WebView с навбаром «Оплата»: успех/ошибка не перехватываются по redirect — статус проверяется вручную кнопкой.
  • Цены форматируются как «N ₽» без копеек/запятой (Flutter PriceFormatter).
S41

Результат оплаты: TYP «Любовь без границ на 3 месяца», ошибка, ожидание (payment-success/error/pending)

◐ есть, но расходится

Figma

payment-success: экран fill #000 с blur-эллипсами #C66F53: premium-icon 56 + «Любовь без границ на 3 месяца» ExtraBold 30 белый + «Отменить можно в «Профиле»»; «Доступно» Bold 20 + чипы #000@20% blur; низ «Активна» Bold 20 #C66F53 + «699 ₽ спишутся 9 Сентября» (СБП: «Подключите списание в СБП…»). payment-error BS: pic-attention + «Оплата не прошла» + «Деньги не списались. Попробуй оплатить подписку ещё раз» + «Оплатить заново 699 ₽» + «Отмена». payment-pending BS: pic-time + «Оформляем покупку» + «Подписка активируется в течение часа, либо мы вернём деньги» + «Готово» (+ вариант СБП «Не забудь при оплате по СБП»).

Flutter

PurchaseTyp (чёрный, два размытых круга терракота@50%, premiumIcon 56 #E7C9B2, «{название} на {период}» headingHeavy, subscriptionTYPCancelInfoText, «Доступно» + FeatureWidget с фоном #000@20%, низ «Активна» терракота + «N ₽ спишутся d MMMM» или текст СБП); PendingPayment BS (picTime + тексты + «Готово»); FailedPayment BS (picAttention + «Оплата не прошла» + «Оплатить заново {цена}» + «Отмена»).

Android

Нет отдельных экранов: после возврата из Custom Tabs SubscriptionScreen показывает статус текстом («Подписка активна до …» / «Платёж в обработке» / текст ошибки) на том же белом экране; успех не празднуется, повторная оплата — снова «Оформить».

Расхождения

  • Нет чёрного TYP-экрана, BS ошибки с кнопкой «Оплатить заново N ₽» и BS ожидания «Оформляем покупку».
  • Нет текста «Отменить можно в «Профиле»» и даты следующего списания.
S42

Управление продлением и блок подписки в профиле (subscription ON / not-prolonged, cancelling sheet)

✕ отсутствует

Figma

Профиль: чёрный блок 366×428 r20 «Подписка на 3 месяца» + чипы stroke #514A47 («Кому понравилась анкета», «+4 лайка», «3 записки», «Бесконечные возвраты») + «Активна» #C66F53 + «299 ₽ спишутся 9 Сентября» / not-prolonged «Закончится 9 Сентября» + Small белая «Включить продление» (Android). Настройки: секция «Подписка» — «Активна»/«Оформить»/«До 6 Сентября» + строка «Продлевать автоматически» со свитчером; cancelling sheet: pic-attention + «Отключить продление подписки?» + «Платные функции перестанут работать 9 Сентября. Деньги списываться не будут» + «Оставить продление» / «Выключить продление»; snacks «Включили продление подписки», «Подписка оформлена с другого девайса».

Flutter

SubscriptionWidget в профиле (чёрный r28, premiumIcon, заголовок по типу Ordinary/Gift/GiftReferral, пилюли фич, «Активна», следующий платёж / «Закончится …», кнопка smallInverted «Включить продление» для external notProlonged); в настройках свитчер «Продлевать автоматически» → CancelProlongation BS / ProlongSubscription (PATCH subscription/me status); ProlongSubscriptionListener → снек «Включили продление подписки»; SubscriptionUpdateListener refetch по пушу/сети/foreground.

Android

Нет. PATCH /subscriptions/me не вызывается; в настройках нет секции «Подписка»; в профиле — только PriorityCard «Premium «По любви»» (пока нет подписки) и текст «Подписка активна до …» на экране подписки. Типы gift/giftReferral не различаются.

Расхождения

  • Отмена/возобновление автопродления и чёрный блок подписки в профиле отсутствуют полностью.

Симпатии

3 экрана · ◐ 3
S43

Таб «Симпатии»: записки и «Поставили лайк»

◐ есть, но расходится

Figma

«Симпатии» ExtraBold 30 #C66F53 по центру + «Узнай, кому понравилась твоя анкета»; секция «Записки» Bold 22 + счётчик «27» Medium 22; notes-block 390×276 с двумя повёрнутыми подложками (#F2E7E3@50% rot 8°, #F2E7E3 rot −5°) + карточка lovenote 366×228 r28 stroke #E1D6D1 (««» ExtraBold 30 #E7C9B2 + текст + фото 40 + имя ExtraBold 16 #C66F53 + tag «Новая» + counter); «Поставили лайк» Bold 22 → колонка like-sender 366×416 (брендовые формы 1…5 по кругу, поворот 10°/5°/−10° при скролле, name-label терракотовая плашка попеременно слева/справа rot ±5°, тег «Новый» чёрный rot 15°); «no subscription»: заблюренные фото + псевдонимы «Незнакомец 32», «Красавчик», «Ухажёр 28», «Суженый 31» + таймер «13 ч. 48 мин» + фикс-кнопка «Узнай, кто поставил лайк» #1B1A1C; empty: сердечки #E7C9B2 + «Симпатий нет» + «Здесь появятся все, кому понравилась твоя анкета»; «Записки» пусто: «Когда кто-нибудь напишет тебе записку, мы пришлём уведомление» + pic-no-lovenotes 200; при скролле navbar «Поставили лайк» + up-chevron; skeleton; error «Не загрузилось :(».

Flutter

SympathyView 1:1 с макетом: заголовок + subtitle, блок записок (подложки ±6°, SuperLikeCardFrame, бейдж непрочитанных), SympathyLikeCardWidget с SvgClip shape_1…5, blur 30, плашка имени ±5°, «Новый» rot 15°, поворот при скролле leadingEdge×10°, псевдонимы sympathyMale1..10/Female1..10, TimerWidget time_filled + «N ч M мин», пагинация за 3 карточки, FAB «Узнай, кто поставил лайк» → SubscriptionOffers(sympathy), AppBar-оверлей «Поставили лайк» + chevron_up при уходе блока записок, пустые состояния user_likes_empty с градиентной маской, шиммеры shape_1.

Android

SympathyScreen: PlTopBar «Симпатии»; Material SingleChoiceSegmentedButtonRow «Лайки N» / «Записки N»; строка «Таймер бесплатного просмотра: {timer_expires_at как пришёл с сервера}» caption терракота; список строк LikeRow (аватар 56 + «Имя, возраст» + бейдж NEW + «Совпадение N%» / «Открой, чтобы увидеть» + «🔒» при blur + «›»); SuperLikeCard (аватар 56 + имя + NEW + «Совпадение N% · супер-лайк» + текст записки в рамке терракота@35%). Пусто: user_likes_empty «Пока нет лайков» / «Когда кто-то лайкнет вас — появится здесь»; pic_no_lovenotes «Нет записок» / «Супер-лайки с текстом появятся здесь» + «Обновить». Без пагинации (первая страница), без псевдонимов, без FAB подписки.

Расхождения

  • Композиция полностью другая: сегмент-контрол и плоские строки вместо заголовка терракотой, стека записок с повёрнутыми подложками и колонки карточек в брендовых формах 1…5 с поворотом при скролле.
  • Заблюренные лайки показывают настоящее имя и возраст с замком вместо псевдонимов «Незнакомец 32»/«Красавчик»/«Ухажёр 28»/«Суженый 31» — раскрывает данные, которые макет специально скрывает.
  • Таймер выводится сырой строкой timer_expires_at, а не обратным отсчётом «13 ч. 48 мин» с иконкой time_filled и авто-снятием блюра по истечении.
  • Нет фикс-кнопки «Узнай, кто поставил лайк» → пейволл; нет плавающего navbar «Поставили лайк» с up-chevron.
  • Тексты пустых состояний другие («Пока нет лайков» / «Когда кто-то лайкнет вас…» вместо «Симпатий нет» / «Здесь появятся все, кому понравилась твоя анкета»; обращение на «вы»).
  • Нет пагинации по курсору и обновления по пушу likes/super-likes/foreground.
S44

Список записок «lovenotes screen» (Записки)

◐ есть, но расходится

Figma

navbar «Записки» + «Закрыть»; overlay #FFFFFF@90%; стек lovenote 366×228 gap 8 (фото, имя, текст, «57% подходит те…» / «Сегодня в 12:11» / «8 Августа»); тап → profile-preview; deeplink loading (skeleton), pagination skeleton, error snack «Ранние записки не загрузились».

Flutter

SuperLikesView: PlAppBar «Записки» + back; SuperLikeCardFrame + SuperLikeCardWidget («« » headingHeavy #E7C9B2 + message + аватар 40 + имя captionBig терракота; «Новая» плашка #F9F3F2 r16); пагинация за 5; 5 скелетов; пусто → назад на Sympathy (TODO).

Android

Отдельного экрана нет — записки живут во вкладке «Записки» сегмент-контрола (SuperLikeCard строки). Тап → SympathyPreviewScreen с текстом записки в плашке.

Расхождения

  • Нет экрана-стека карточек-цитат с ««» и плашкой «Новая»; нет пагинации и снека «Ранние записки не загрузились».
S45

Просмотр анкеты из симпатий «profile-preview»

◐ есть, но расходится

Figma

navbar «Иван Забелин» + menu; profile-face 366×627 r28 (pager, gradient, distance, name-box с check-white, match-rate 78% + tags-common) + полное описание (job/bio, «171 см», values, «Мои увлечения», открывашки, 🚩/💚, «Пожаловаться»); плавающие Dislike 80 @8,722 / Like 80 @302,722; скрытая «Отправить записку» с counter.

Flutter

SympathyProfilePreview: PlAppBar fullName + TooltipDisplayer; карточка CardActionsComponent(UserCardComponent) 84% высоты + UserDescriptionWidget («Отправить записку», «Пожаловаться»); FAB UserCardActionsButtonsWidget; логика: нет лайков → верификация / пейволл endedLikes / тултип; записка без остатка → пейволл endedSuperLikes; POST /users/likes/{id} или /users/superlikes/{id} (прочтение); анимация свайпа 1500/1050 мс + pop.

Android

SympathyPreviewScreen: PlTopBar «Симпатия»/«Записка» + subtitle имя; карточка r28 (blur 28 при blured + замок 44 + «Скрыто без Premium» + «Один профиль в день виден бесплатно»); индикаторы фото, тап-зоны; низ: чип «Супер-лайк», «Имя, возраст» белый, «Совпадение N%», локация, job, bio 2 строки, текст записки в плашке белая@18%, «Узнать больше» → шторка. Blured: PrimaryButton «Открыть Premium» + «Пропустить»; иначе FAB cross 64 / heart 72 + подпись «Лайкни в ответ или пропусти». GET /users/{id}, POST likes/{id} mark read; лайк → POST like → MatchOverlay.

Расхождения

  • Заголовок «Симпатия»/«Записка» вместо полного имени; нет меню.
  • Описание анкеты в шторке, а не под карточкой; нет кнопки «Отправить записку» из превью.
  • Кнопки 64/72 без белой обводки 3 вместо 80/80; добавлена подпись «Лайкни в ответ или пропусти».
  • Для заблюренной карточки показан экран с «Открыть Premium» — в макете превью заблюренной анкеты недоступно (тап ведёт сразу в пейволл).
  • Дизлайк из превью не отправляется на сервер (POST dislike нет) — лайк-запись остаётся на бэке.

Чаты

6 экранов · ◐ 3 · ✕ 3
S46

Таб «Чаты»: «Твои пары» + «Сообщения»

◐ есть, но расходится

Figma

«Твои пары» Bold 22/ExtraBold 30 #C66F53 по центру + «Сделай первый шаг — напиши «Привет»»; карусель match-new (крайние 80 + имя 14, центральный 128 + имя 16 + .status 16 + .sound-switcher go-to-chat 44); chats-frame: «Сообщения» Bold 22 + rules-кнопка; chat-preview 390×131.77 (фото 80 + status, «Константин, 33», «7 минут назад»/«Вчера в 19:01», last-message, counter «99+», V4: значок сообщества, checkmark, premium, галочки прочтения, «Печатает»); при скролле navbar «Сообщения» (rules справа); пусто: «Взаимные симпатии появятся тут» + пунктирное сердце; «Переписок нет» + «Найди пару и начинай общение» + Small «Смотреть анкеты»; баннер community-rules «Твои границы в твоих руках. Видишь нарушение — пиши нам» + «Правила сообщества»; «network searching»: loader Small + «Загрузка»; skeleton; pagination error snack «Ранние чаты не загрузились»; BS chat actions.

Flutter

ChatsListView: SliverPersistentHeader 44↔328 («Твои пары» headingHeavy терракота + subtitle + CarouselSlider viewportFraction 0.4 с zoom (80/128)² + online-точка + go-to-chat 44 + «Сообщения» titleHeavy + rules; collapsed — «Сообщения» lineBold + LostConnection + rules); ChatWidget pad 24/12/24/16 (аватар 80 + online 16 + «Имя, возраст» subtitle + typing/last message + время по правилам (сегодня/вчера/день недели/дата) + menu 24 + счётчик 24 r12 «99+»); баннер правил (закрывается, CommunityRulesTrainingPassed); пустые состояния; шиммеры; пагинация за 5; WS-обновления (new-pair, text, last-read, online, typing, edit/delete); drift-кэш + /user-chats/updates; ошибки tooManyConnections/wsConnection/noNetwork.

Android

ChatsScreen: PlTopBar «Чаты»; секция «Новые пары» lineBold + LazyRow аватаров 64 + имя caption (ширина 72); строки чатов (аватар 56 + имя lineBold + последнее сообщение baseBold/baseLight или «Начните общение» + время HH:mm caption + счётчик unread терракота); разделитель indent 84. Пусто: pic_chatting «Пока нет чатов» + текст + «Обновить». GET /user-chats + /user-chats/new-pairs при открытии; без WS-обновления списка, без пагинации, без typing/online, без меню ⋮, без баннера правил, без карусели с zoom.

Расхождения

  • Нет sliver-хедера «Твои пары» терракотой с подзаголовком и карусели с увеличением центрального аватара 128 + кнопкой go-to-chat.
  • Строки чатов: аватар 56 без online-статуса, нет «Имя, возраст» (только имя), время всегда HH:mm (макет: «7 минут назад» / «Вчера в 19:01»), нет меню ⋮ и «Печатает».
  • Список не обновляется по WebSocket (новое сообщение/пара/прочтение видны только после повторного открытия таба); нет пагинации и кэша.
  • Нет баннера «Твои границы в твоих руках…» и кнопки «Правила сообщества» в шапке.
  • Тексты пустых состояний другие («Пока нет чатов» вместо «Переписок нет» + «Найди пару и начинай общение» + «Смотреть анкеты»); «Взаимные симпатии появятся тут» отсутствует.
  • Нет BS «Действия с перепиской» из списка (S48).
S47

Переписка (default state, message bubbles, input-bar)

◐ есть, но расходится

Figma

navbar type=chat 88 (фото 56 + имя + статус) с menu/rules; top-row 164: pic-checkmark 56 + «Проверен»/«Не проверен» | фото 128 | premium-icon 56 + «Подписка»/«Нет подписки»; activity-status (.status 16 + «В сети» / «Не в сети» / «Поиск сети...» / V3 «Был в сети 3 часа назад») + «Константин Сидоров» Bold 22 + «29 лет» Medium 22; пусто «Сообщений нет» Medium 30 #B3ACA9 + «Напиши «Привет» или сделай комплимент»; пузыри: своё #C66F53 r12 smooth pad 12 16 белый текст + время «13:02» Manrope 12 #E7C9B2 + галочки read (#E7C9B2/#21C004) внутри; чужое #F2E7E3 + время #9E8075; «Изменено»; хвост у последнего; группировка gap 4; date-separator «Сегодня»/«9 Сентября»; From3 «Печатает» с точками; input-bar 390×60: input--chat 314×44 r12 (растёт до 104/184) + .44-button send (#F2E7E3@50% → чёрная); pagination loaders; «searching network» overlay 35% на фото.

Flutter

ChatView: ChatAppBar 56↔88 (аватар 56 с online-точкой и затемнением при обрыве WS, имя / «Печатает…» терракота / «Нет соединения»), rules + menu; ChatUserInfoWidget (verified_checkmark/unverified 56 + «Проверен»/«Не проверен» → VerificationPhotoPreview; аватар 128 → NewPairProfilePreview; premium_icon/no_premium 56 + «Подписка»/«Нет подписки» → пейволл); ActivityStatus; «Сообщений нет» + subtitle; DateDivider, UnreadMessagesDivider «Непрочитанные»; MessageWidget (80% ширины, r12 smooth, #C66F53/#F2E7E3, время внутри + «изменено» + message_sent/read 17px, хвост 20×25, reply-цитата с линией 2×32, pending time 24); свайп влево → ответ; long press → контекстное меню; MessageInputWidget (блок edit/reply + PlTextInput 1–5 строк maxLength 1000 + кнопка 44 круг); пагинация prev/next по 30 вокруг lastRead; typing 4 с; set-last-read throttle 4 с.

Android

ChatThreadScreen: PlTopBar имя + subtitle «в сети / подключение… / не в сети» — по состоянию сокета, а не собеседника + меню ⋮ («Пожаловаться» → ReportUserDialog place chat → предложить «Удалить пару»; «Удалить пару» → AlertDialog → POST delete-pair). LazyColumn pad 12, gap 4, разделитель дня caption #B3ACA9 по центру («Сегодня/Вчера/13 июл»); MessageBubble max 280dp r16: своё чёрное, чужое #F9F3F2, текст lineLight; pending « …»; хвост SVG 12; статус message_sent/read 14 под пузырём; время сообщения не показывается; «Не отправлено — нажми, чтобы повторить»; long press → ответ (reply), цитата «Имя: текст» caption над пузырём. Пусто: pic_chatting «Начните общение» + «Напишите первое сообщение — {имя} увидит его в чате». Композер: OutlinedTextField r28 #F9F3F2 «Сообщение» maxLines 4 + кнопка 48 круг чёрная/#F9F3F2 send 22. История: одна страница /messages/previous по last_message без догрузки; set-last-read через WS.

Расхождения

  • Своё сообщение чёрное вместо терракотового #C66F53; чужое #F9F3F2 вместо #F2E7E3; r16 вместо r12 smooth; ширина 280dp вместо 80%.
  • Время сообщения («13:02») не выводится нигде; статус доставки под пузырём, а не внутри; «Изменено» не поддерживается.
  • Нет блока информации о собеседнике («Проверен»/«Не проверен», фото 128, «Подписка»/«Нет подписки», «29 лет») в начале переписки — вместо него общий placeholder «Начните общение».
  • Статус в шапке отражает соединение приложения, а не онлайн собеседника; «Печатает» и «Был в сети 3 часа назад» отсутствуют (user-is-typing/online/offline из WS игнорируются).
  • Нет разделителя «Непрочитанные», нет пагинации вглубь истории (только последние 30), нет лоадеров пагинации.
  • Композер: поле r28 с подписью «Сообщение» и кнопка 48 вместо input--chat r12 + .44-button; лимит 1000 символов не задан.
  • Ответ (reply) реализован long-press'ом без контекстного меню; свайп влево для ответа отсутствует.
  • Обращение «Напишите первое сообщение» (на «вы») вместо «Напиши «Привет» или сделай комплимент».
S48

Меню чата и разрыв пары (chat actions BS, «Почему хочешь закрыть взаимную симпатию?»)

◐ есть, но расходится

Figma

BS: «Анатолий, 31» Bold 20 + «Действия с перепиской» + secondary «Поделиться профилем», «Пожаловаться и заблокировать», «Завершить общение»/«Удалить пару»; второй BS «Почему хочешь закрыть взаимную симпатию?» — «Мы встретились лично», «Неприемлемое поведение», «Не тот, за кого себя выдаёт», «Больше неинтересно» (+ «Другое» в arb); жалоба из чата — BS причин (obscene_language, behavior, catfisher, other).

Flutter

ChatMenu BS (share → Share.share(chatMenuShareTextTemplate), report → Report BS place=chat, delete → DeletePair BS с 5 причинами, loading на кнопке); БАГ: DeletePairConverter отправляет metOffline для любой причины.

Android

Material DropdownMenu ⋮ в шапке треда: «Пожаловаться» (ReportUserDialog с 4 причинами chat, после успеха диалог «Удалить пару?»), «Удалить пару» (AlertDialog «Удалить пару?» → POST /users/{id}/delete-pair без причины). «Поделиться профилем» нет.

Расхождения

  • Dropdown вместо bottom-sheet «Действия с перепиской»; нет «Поделиться профилем».
  • Нет выбора причины разрыва пары («Мы встретились лично» и др.) — reason не отправляется.
  • Меню доступно только из треда, не из списка чатов (в макете ⋮ есть в chat-preview).
S49

Превью профиля пары из чата (profile-preview с go-to-chat)

✕ отсутствует

Figma

Тот же profile-preview, но правая кнопка-круг = go-to-chat-filled (чёрная), без dislike; BS-меню «Поделиться профилем» / «Пожаловаться и заблокировать» / «Удалить пару». V3 «Твои пары» список: shape-brand 68×80 + «Фамилия Имя, 36» + «Дата создания пары 22.11.2024» + share.

Flutter

NewPairProfilePreview: PlAppBar fullName + menu; UserCardWithDescription + плавающая кнопка (белый круг 86 + терракота 80 + goToChatFilled 40) → ChatRoute; открывается по тапу на аватар в карусели/шапке чата. Списка «Твои пары» V3 нет.

Android

Нет. Аватар в шапке треда и в «Новых парах» ведёт сразу в чат; полную анкету пары (фото, открывашки, флаги) из чата открыть нельзя.

Расхождения

  • Экран анкеты пары из чата отсутствует.
S50

Контекстное меню сообщения: ответить, копировать, изменить, удалить, ссылки (V5)

✕ отсутствует

Figma

Overlay #000@60% blur 16 + кнопки 153×44 #F2E7E3 stroke #E7C9B2: свои — «Ответить», «Скопировать», «Изменить», «Удалить» (→ «Удалить у меня и у Константин» / «Удалить у меня»); чужие — «Ответить», «Скопировать»; snack «Сообщение было скопировано» (#C66F53); input-bar «Редактирование» / «Ответ на сообщение: Константин» + цитата + close; пузырь you reply с цитатой (линия 2×32, имя #C66F53/#E7C9B2); BS для ссылок: pic-attention + «Ты уверен, что хочешь перейти в сторонний сервис?» + «Ссылки могут содержать опасные ресурсы и мошеннические ловушки» + «Перейти» / «Отмена».

Flutter

MessageContextMenu (blur 16 + #000@80%, меню 180 #F2E7E3 r12: Изменить (<4 ч), Ответить, Копировать (snack), Удалить (<2 ч, подменю «только у меня» / «у всех»)); WS edit-text/delete-message с откатом при ошибке; поиск источника ответа по страницам с подсветкой BlinkWidget; ExternalResourceWarningSheet перед открытием ссылки.

Android

Только reply по long-press. Редактирование, удаление, копирование, подсветка/переход к цитируемому сообщению, предупреждение о внешних ссылках — отсутствуют; ссылки в тексте не кликабельны. ChatWebSocket отправляет только send-text и set-last-read.

Расхождения

  • Контекстное меню сообщения и все его действия, кроме ответа, отсутствуют; входящие edit-text/delete-message от собеседника не применяются к экрану.
S51

BS «Не пропусти сообщение» после первого сообщения

✕ отсутствует

Figma

pic-chatting 56 + «Не пропусти сообщение» Bold 20 + «Когда Константин напишет, узнай об этом сразу» + «Включить уведомления» (main) + «Пропустить» (secondary) → системный алерт; заметка «либо в настройки кидать, если запрет был ранее».

Flutter

ChatRequestNotificationPermission BS после 2-го своего сообщения в первом чате; permanentlyDenied → «Открыть настройки».

Android

Нет. Системный запрос POST_NOTIFICATIONS при входе в тред без объясняющего листа; pic_chatting используется только для пустых состояний.

Расхождения

  • Pre-permission лист отсутствует.

Профиль

12 экранов · ◐ 11 · ✕ 1
S52

Таб «Профиль» (non-verified / verified / subscription ON / not-prolonged)

◐ есть, но расходится

Figma

top-row 164: слева круг 68×66 stroke #E1D6D1 с gear 40, центр video-circle 128 (фото/видео), справа круг с profile-edit 40 + бейдж «63%» Manrope 12 #9E8075; «Александра Иванова» ExtraBold 18 (+ checkmark-filled #C66F53 у verified) + «32 года» Medium 16 #514A47; switcher-text-icon 208×52 «Анкету видно всем» (#C66F53) / «Видят только пары» (#F2E7E3) с большим бегунком и loader; blocks gap 8: verify 366×328 stroke r20 (pic-checkmark 56 + «Получи галочку →» Bold 20 + «Подтверди личность — сделай селфи» #B3ACA9 + чипы «Бесплатно и быстро», «+2 лайка», «+60% охват», «Больше симпатий»); premium-offer 366×300 #000 r20 с blur-эллипсами (premium-icon 56 + «Любовь без границ» ExtraBold 30 + «Узнай, кто поставл лайк твоей анкете» + белая «Выбрать тариф») или subscription-active; «Ваша анкета содержит нецензурную лексику» блок (Доработки); mail «Добавь почту» Bold 20 + подпись + ячейка 366×56/68 с email 32 #C66F53 (или «Электронная почта» + edit + значение); links-cells 60: faq «Часто спрашивают», support «Связаться с нами», rules «Правила сообщества», lock «Политика конфиденциальности», trash «Удалить профиль» (#FA5F3D); V4: «Советы по здоровым отношениям», «Пригласи друга», «Сообщества»; «Выйти» secondary; maintenance alert-карточка #514A47.

Flutter

ProfileView 1:1: _ButtonsHeader (круги 66×68 gear40/profileEdit + бейдж «N%», аватар 128 с зацикленным видео или photoAdd 80), fullName captionBig + checkmarkFilled, «N лет/года», _VisibilityButton 210×52 r100 (терракота/#F2E7E3, бегунок 46×44 с revealed/hidden или лоадером, PATCH isVisible), ObsceneLanguageWidget, _VerificationCTAWidget (рамка r28 + 4 чипа), NoSubscriptionWidget/SubscriptionWidget (чёрные r28 с размытыми кругами), _AddEmailWidget (DottedBorder r20 с email32 → BS), _MenuWidget (referral, communities, faq, support, rules→URL, lock→URL, trash), PLSecondaryButton «Выйти».

Android

ProfileHome («redesigned home»): PlTopBar «Профиль» / «Видна в ленте»/«Скрыта из ленты» + gear40 24 → Settings; hero-карточка #F9F3F2 r28 pad 20 (аватар 104 stroke 3 #E7C9B2 + бейдж verified 28; «Имя Фамилия, возраст» titleHeavy; job; TrustChipLabel; ««bio»» 3 строки; CompletenessBar «Анкета сильная»/«Заполни анкету» + «N%» + полоса 6; PrimaryButton «Редактировать»; VisibilityToggleRow «Анкета видна»/«Анкета скрыта» + «Скрыть»/«Показать» терракота, тост); «Усиль анкету»: PriorityCard «Верификация»/«Значок доверия…»/«Пройти», «Доверие и проверки», «Premium «По любви»»/«Больше лайков, супер-лайки, фильтры»/«Тарифы»; группы меню (белые r16 stroke): «Анкета и аккаунт» (Редактировать анкету, Настройки, Верификация, Доверие, О себе голосом, Подписка Premium, Электронная почта), «Знакомства и события» (События «По душе», Пересечения, Сообщества, Пригласи друзей), «Помощь» (Частые вопросы, Связаться с нами, Правила сообщества, Политика конфиденциальности), «Опасная зона» (Удалить аккаунт); футер: телефон, email / «Почта не указана · нажми, чтобы добавить», «ID …»; SecondaryButton «Выйти».

Расхождения

  • Полностью другая композиция: hero-карточка + группы меню вместо top-row с круглыми кнопками 68×66, аватара 128 с видео, бейджа «63%» и большого свитчера 208×52 «Анкету видно всем»/«Видят только пары».
  • Нет verify-карточки «Получи галочку →» с 4 чипами и чёрного premium-offer «Любовь без границ»/«Выбрать тариф» с размытыми эллипсами — вместо них светлые PriorityCard с другими текстами.
  • Нет блока «Добавь почту» с иконкой email 32 в пунктирной рамке — почта в футере и в меню.
  • Нет предупреждения «Ваша анкета содержит нецензурную лексику» (isObscene не используется).
  • Пункты меню другие/добавлены («Настройки», «Верификация», «Подписка Premium», «Электронная почта», «Доверие», «О себе голосом», «События», «Пересечения»); нет иконок 24 у пунктов; «Удалить аккаунт» вместо «Удалить профиль».
  • Видео-аватар не воспроизводится; аватар 104 вместо 128.
  • Обращение в футере на «ты» («нажми»), но в остальных экранах смешано с «вы».
S53

Удаление профиля: причины, диалог, таймер 10 секунд (deleting 1–3)

◐ есть, но расходится

Figma

BS 390×344 «Почему хочешь удалить профиль?» + secondary «Я нашел пару», «Не нравится приложение», «Уже неактуально», «Другое»; диалог «Удалить профиль?» / «Твои данные пропадут навсегда и мы не сможем их восстановить» / «Отмена» + «Удалить»; экран-таймер fill #C66F53: «10» Medium 30 + «секуд до удаления аккаунта» + белая кнопка «Отменить удаление».

Flutter

DeletionReason BS (4 причины pairFound/dislike/notRelevant/other) → PlDialog → ProfileDeleteTimer (терракота, «N» headingLight + profileDeleteTimerSubtitle(N), tertiary «Отменить удаление», 10 с → POST deactivate {reason} → logout).

Android

AlertDialog «Удалить аккаунт?» + «Анкета будет деактивирована. Войти снова можно будет только с новым аккаунтом.» + «Удалить»/«Отмена» → POST deactivate reason «other» → logout сразу.

Расхождения

  • Нет BS с причинами — на сервер всегда уходит reason «other» (аналитика причин удаления теряется).
  • Нет терракотового экрана обратного отсчёта с «Отменить удаление» — аккаунт деактивируется мгновенно.
  • Тексты диалога другие.
S54

Настройки (уведомления, контент, подписка, push-denied)

◐ есть, но расходится

Figma

navbar «Настройки» + «Готово»; «Уведомления» Bold 20 + switcher-cell 366×60: «Новые пары», «Новые сообщения», «Рекламные предложения», «Напоминания об активности», «Приглашение друзей»; «Контент»: «Обучающие видео»; «Подписка»: статус + «Продлевать автоматически»; push-denied: «Уведомления … Выключены» + «Включи уведомления в Настройках телефона и узнавай о новых симпатиях» + Small «Настройки».

Flutter

ProfileSettings: PlAppBar + PLTextButton «Сохранить»; запрос push-разрешения при старте; свитчеры PlSwitch (disabled без разрешения) + _OpenSettingsWidget; «Контент» + «Показывать обучающее видео»; _SubscriptionWidget со свитчером продления; PATCH settings → fetchProfile.

Android

ProfileSettingsScreen: PlTopBar «Настройки» + action «Сохранить» (красный, при dirty); «Уведомления» subtitle + техническое пояснение про Firebase caption + TextButton «Системные настройки уведомлений» + Material Switch: «Новые пары», «Новые сообщения», «Напоминание об активности», «Реферальная программа», «Маркетинг», «Обучающие подсказки»; «Безопасность» subtitle + текст про жалобы; PATCH settings + PATCH profile (marketing).

Расхождения

  • Нет секции «Подписка» с автопродлением; нет секции «Контент».
  • Названия и порядок свитчеров другие («Маркетинг» вместо «Рекламные предложения», «Реферальная программа» вместо «Приглашение друзей», «Обучающие подсказки» вместо «Обучающие видео»); свитчеры не блокируются при выключенных push и нет блока «Выключены» + Small «Настройки».
  • Кнопка «Сохранить» красная baseBold в PlTopBar вместо «Готово»/«Сохранить» Manrope 16.
  • Пояснение про Firebase — технический текст, не предусмотренный макетом; добавлена секция «Безопасность».
S55

Добавление почты (BS «Добавь почту»)

◐ есть, но расходится

Figma

BS: input--line «Электронная почта» + checkbox-cell «Хочу получать персональные предложения и новости» + «Сохранить» (disabled до валидного ввода).

Flutter

EnterEmail BS: PlTextInput autofocus maxLength 256 + PlCheckBox + PLPrimaryButton (enabled при валидном/пустом email и изменениях) → PATCH profile.

Android

EnterEmailScreen (полноэкранный): PlTopBar «Электронная почта»; текст «Так мы сможем связаться, если что-то пойдёт не так — и при желании присылать новости.»; PlTextField hint name@example.com; Material Checkbox «Можно присылать новости и предложения»; ошибка «Введите корректный email»; «Сохранили»; PrimaryButton «Сохранить».

Расхождения

  • Экран вместо bottom-sheet; тексты чекбокса и пояснения другие.
S56

Связаться с нами (BS «Текст обращения» + «Почта для связи»)

◐ есть, но расходится

Figma

BS: input--textarea «Текст обращения» hint «Проблема или предложение» счётчик «0 / 1000» + input--line «Почта для связи» hint «yourem@il.com» + «Отправить»; чёрная snack «Получили твоё обращение».

Flutter

ContactUs BS: PlTextArea 1000 + PlTextInput email 256 + PLPrimaryButton → POST feedback → snack.

Android

SupportScreen (экран): PlTopBar «Поддержка»; «Опиши проблему — мы ответим на почту.»; поля «Текст обращения» (hint «Проблема или предложение», 1000) + «Почта для связи» (hint yourem@il.com, 256); валидация «Напишите чуть подробнее (от 5 символов)» / «Укажи почту для ответа»; inline «Получили твоё обращение»; PrimaryButton «Отправить» → POST /feedback.

Расхождения

  • Экран вместо bottom-sheet; нет счётчика «0 / 1000»; результат — inline-текст, а не чёрный snack.
  • Минимум 5 символов — собственное правило Android.
S57

Часто спрашивают (FAQ)

◐ есть, но расходится

Figma

navbar «Часто спрашивают»; блоки gap 24: вопрос Manrope Bold 20 + ответ Manrope 14 #514A47 (все ответы раскрыты).

Flutter

FAQ: PlAppBar faqTitle; GET faq; title subtitle + info baseLight, разделитель 24; ErrorPlaceholder.

Android

FaqScreen: PlTopBar «Частые вопросы»; аккордеон: карточки #F9F3F2 r12 pad 16, вопрос lineBold, ответ по тапу; «Пока нет вопросов».

Расхождения

  • Аккордеон с карточками вместо плоского списка с раскрытыми ответами; заголовок «Частые вопросы» вместо «Часто спрашивают».
S58

Правила сообщества и Политика конфиденциальности

◐ есть, но расходится

Figma

Экран «Rules»: navbar «Правила сообщества»; «Зачем эти правила?» + текст; разделы «Уважение», «Честность», «Безопасность», «Конфиденциальность», «Контент» (Bold 20 + Manrope 14 #514A47). Экрана политики нет — только ссылка.

Flutter

Оба пункта открывают внешние URL (communityRulesUrl / policyUrl) через launchUrl — внутреннего экрана правил нет (дефект Flutter относительно макета).

Android

LegalLinkScreen: PlTopBar title + захардкоженный текст (RULES — 5 пунктов; PRIVACY — 3 абзаца) + PrimaryButton «Открыть полную версию» → https://plbvi.ru/help/rules / Google Docs.

Расхождения

  • Текст правил в Android — собственный краткий (5 пунктов), не совпадает с разделами макета «Уважение/Честность/Безопасность/Конфиденциальность/Контент».
  • Ссылка на политику — Google Docs, а не policyUrl из конфигурации Flutter.
S59

Редактирование анкеты (editing-profile, photo-moderation, informer, name editing, editing-question)

◐ есть, но расходится

Figma

navbar «80% заполнено» (#B3ACA9) + «Показать»; video-circle 208; «Зажми фото, чтобы перетащить»; photo-grid 366×264 (tooltips «+ 4%», «Проверяем…», «Новые фото проходят проверку»); V5 карточка «Измени селфи» 366×160 с превью 116×128 и refresh; секции 390×102 stroke #F2E7E3@50%: «Имя и фамилия», «Должность», «О себе», «Рост» («169 см»), «Интересы» (plus + «Добавь ответ + 5%»), «Флаги» (🚩 тёмные чипы #514A47 / 💚 stroke #F2E7E3), «Открывашки» (prompt on-profile + tooltip «Добавь открывашку + 2%»); answer-блоки по 8 категориям (tag + optional-question «→ «Не важно»» / «+ Дополнить»); фикс-кнопка secondary «Показать анкету»; BS «Нельзя удалить фото» + «1 фото — минимум…» + «Понятно»; экраны редактирования шага: «Как тебя зовут?» + «Сохранить ответ» (#1B1A1C); «Курение» Bold 22 #C66F53 + вопросы с picker-single + switcher-cell (скрыть ответ) + «Сохранить ответ».

Flutter

ProfileEdit: AppBar «N%» терракота + прогресс 174×6; VideoContentComponent 208 + подпись + GalleryContentFlow + VerificationPreviewWidget («Измени селфи»); секции с Divider (имя, должность, био, рост, интересы-чипы, флаги (красные #514A47 залитые / зелёные stroke #F2E7E3), открывашки-карточки #F2E7E3@50% r20 / DottedBorder «+2%», 8 шагов анкеты с «→ «ответ»» / «Дополнить»); каждая секция → OnboardingRoute(step, isFromOnboarding false) с кнопкой «Сохранить ответ»; PLSecondaryButton «Показать превью» → ProfileCardPreview; canPop=false без фото.

Android

ProfileEditScreen — одна длинная форма: PlTopBar «Редактирование» / «Анкета N%»; «Фото» + ProfilePhotoGallery (6 слотов 3×116/128 r16, «Главное», «×», «Проверяем…», «+»/«+3%», drag, BS «Добавить фото» Сделать фото/Выбрать из галереи, лимит 6/10 МБ, нельзя удалить последнее); «Видео» + «Видео загружено» + «Удалить видео»/«Добавить видео» (галерея); LinearProgress + «Заполненность N% — фото и ответы повышают видимость»; поля Имя, Фамилия, Пол (чипы), Дата рождения (маска), Рост, Работа / занятие, О себе (300); FlagsSection 🟢/🔴 с «Быстрый выбор» (+ Честность, Юмор, Эмпатия, Амбиции, Верность, Любопытство, Забота / Грубость, Ложь, Ревность, Пассивность, Нарциссизм, Контроль, Неуважение); PromptsSection N/3 (FilledPromptSlot с номером, EmptyPromptSlot) → PromptPickStep («Шаг 1 · выбери вопрос» / «Шаг 2 · твой ответ», hint «Коротко, до 120 символов — это icebreaker»); SecondaryButton «Изменить ответы анкеты · N» → SurveyEditStep (только required-шаги, «i / N»); PrimaryButton «Сохранить» → PATCH полный.

Расхождения

  • Одна форма с кнопкой «Сохранить» вместо секций «title + edit → отдельный шаг с «Сохранить ответ»».
  • Пол и дата рождения редактируемы (макет: «Исправить дату рождения будет нельзя», пол не редактируется); ProfilePatchBuilder при сохранении подставляет дефолты в незаполненные поля.
  • Нет видео-кружка 208 с плеером и «Зажми фото, чтобы перетащить» над сеткой (подпись есть, кружка нет); видео — только текст «Видео загружено».
  • Нет карточки «Измени селфи» (повторная верификация из редактирования).
  • Ответы анкеты не показываются в форме (нет тегов «Хочу детей»/«→ «Не важно»»); нет необязательных под-вопросов и «+ Дополнить»; редактируются только обязательные шаги.
  • Флаги — «Быстрый выбор» с предустановками (Честность, Юмор… / Грубость, Ложь…) — этого нет ни в макете, ни во Flutter; стили чипов другие; порядок 🟢→🔴 обратный.
  • Открывашки — слоты с номерами, hint «это icebreaker» (англицизм) вместо карточек prompt on-profile и tooltip «Добавь открывашку + 2%».
  • Нет кнопки «Показать анкету» / превью (S60); нет BS «Нельзя удалить фото» (кнопка просто не даёт удалить).
  • Интересы не редактируются отдельной секцией (внутри анкеты, если шаг required — иначе недоступны).
S60

Превью своей анкеты «Моя анкета» (profile-preview)

✕ отсутствует

Figma

navbar «80% заполнено» → «Моя анкета» + «Закрыть»; profile-face 366×627 с pager, «2 км», name-box, 78% + tags-common, video-circle 96, snack «Проверяем эту фотографию» на фото; полное описание как у карточки ленты; «Пожаловаться» присутствует (не скрыт в макете).

Flutter

ProfileCardPreview: PlAppBar «Моя анкета» + «Закрыть»; UserCardWithDescription(UserEntity.my, distance «2 км», cachedVideo, isObscene); перезагрузка при CommunitiesUpdate.

Android

Нет. Пользователь не может увидеть свою анкету глазами других.

Расхождения

  • Экран отсутствует.
S61

Сообщества (com_main, com_no com, com_list, push-custom sheets)

◐ есть, но расходится

Figma

Экран fill #000 с blur-эллипсами #C66F53; navbar «Сообщества» белый + Community list/exit + «Готово»; карусель логотипов 120 (соседние 80) + «Т-БАНК» ExtraBold 18 + «210 участников» + «Дата вступления 23.04.2025»; switcher-text-icon 220×52 «Значок видно всем» / «Значок скрыт» + подпись «Ты видишь участников, а они тебя» / «Ты не видишь участников, а они тебя»; «Покинуть сообщество» #FA5F3D; белая нижняя панель r28 «Пригласить в сообщество» + текст про приватность + btn_copy «...po.lubvi/community/TBANK» + «Отправить приглашение»; нет сообществ: иллюстрация + «Нет сообществ» + текст + теги «Дополнительные фильтры», «Значок сообщества» + список switcher-cell с логотипами и числами + «Смотреть сообщества»/«Не вступать»; V5 список: navbar «Список доступных сообществ» + input--search «Поиск сообщества» + community-cell 358×74 (логотип 40 + имя + «126 участников» + join / «Вы участник») + «Ничего не найдено» + email info@plbvi.ru; push-custom sheets #000: «Тебя приглашают в сообщество …» + теги + «Вступить в сообщество»/«Позже»; «Ты подал заявку…»; «Тебя исключили…»; «Тебя не приняли…»; «Тебя приняли…»; «Ты вступил в сообщество Тенёк» + «Ты получаешь:»; iOS alert «Покинуть сообщество Т-БАНК?»; snacks.

Flutter

Communities (чёрный, CarouselSlider 120 с zoom, CommunityLogoWidget/буква, детали + CommunityVisibilityButton 210×58 + «Покинуть», белый низ с PLCopyWidget + «Отправить приглашение»); CommunityEmptyView; CommunitiesList (белый, PlSearchField с ранжированием/опечатками, join 24); CommunitySuccessJoinBs, CommunityAccession BS (deeplink AppsFlyer community_invite, сохранённые приглашения, postponed 1440 мин), CommunityRequestResult BS (accepted/declined/excluded по пушу); PlDialog выхода с разными текстами для закрытых; CommunitiesErrorDisplayer.

Android

CommunitiesScreen (белый): PlTopBar «Сообщества» / «Круги интересов · фильтры в ленте»; карточка «Зачем это» + StatChip «В каталоге N» / «Ты в N»; «Твои сообщества» (CommunityCard терракота@8% r16: иконка 52 + ✓, name, «N участников · закрытое · твоё», кнопка «Выйти» красная рамка); «Выбери круги»/«Ещё в каталоге» (карточки #F9F3F2 + «Вступить»); «Ты уже во всех доступных сообществах»; пусто pic_match «Пока нет сообществ». API: GET all + mine, POST join/leave.

Расхождения

  • Белый каталог-список вместо чёрного экрана с каруселью логотипов 120 и деталями текущего сообщества.
  • Нет видимости значка (POST visible/invisible), даты вступления, ссылки-приглашения (inviteLink, copy/share, «Отправить приглашение»), поиска по списку.
  • Нет приглашений по deeplink, BS «Тебя приглашают…», результатов заявок (принят/отклонён/исключён), BS успешного вступления «Ты получаешь:», диалога выхода с текстом для приватных, email info@plbvi.ru для создания сообщества.
  • Вступление в закрытое сообщество кнопкой «Вступить» напрямую — макет предполагает только по приглашению с модерацией.
S62

Реферальная программа «Пригласи друга» (ref_main, ref_share, ref-info, push-custom)

◐ есть, но расходится

Figma

Экран fill #000; «Пригласи друга» ExtraBold 30 #C66F53 + «если он подтвердит свою личность при регистрации, то ты получишь 5 дней премиум подписки, а друг 3 дня в подарок» #B3ACA9; иллюстрация invite/pic 390×292; две карточки 175×140 #514A47@50% r16 («6» + «друзей приглашено», «30» + «дней подписки получено»); карточка прогресса со шкалой «0 5 10 15 20» + .progress + «Пригласи ещё 4 друга и получи дополнительно 7 дней подписки»; btn_copy dark «...po.lubvi/referal/olenionok123» + copy; белая кнопка «Отправить ссылку» (текст #C66F53); подпись «Ты пригласил больше, чем 85% приглашающих. Подробнее о программе»; «История приглашений» (бейдж «+5»/«+7» + premium-icon + «Друг зарегистрировался 23.05.2025 в 19:01» / «Поздравляем! Ты пригласил 5 друзей!»); пусто «Ты ещё никого не пригласил…»; snacks «Приглашение отправлено», «Ссылка скопирована», «Эта реферальная ссылка уже была использована», «Ты уже участвуешь…», «Нельзя пригласить себя»; ref-info карточка «Премиум в подарок» + «Подписка активна до 20.05.2025» + «Понятно»; push-custom BS «Ты зарегистрировался по приглашению от друга» + «Подтверди свою личность сделав селфи, чтобы получить 3 дня…» + теги + «Подтвердить свою личность»/«Позже».

Flutter

ReferralProgram (чёрный, sliver-хедер 44↔152, freindsImage/referralBox, PLCopyWidget.onDark(OneLink) + «Отправить ссылку», _FriendsDaysWidget, _ProgressBarWidget с метками motivationProgram, тексты по премиуму/процентилю, «История приглашений» с «+N» + premiumIcon); ReferralConfirmation BS (POST referral/approve → VerificationRouter); ReferralCongrats диалог по пушу; AppsFlyer deeplink referral-invite; snacks.

Android

ReferralScreen (белый): PlTopBar «Пригласи друзей» / «Premium-дни за приглашения»; referral_box.png 140; «Друг входит — оба в плюсе» titleHeavy; текст; BenefitCard «Другу +N дн. за вход и анкету» / «Тебе +N дн. за каждое приглашение»; «Твой прогресс» (StatBlock приглашено / дней Premium / топ N%) + «Ещё N дн. начислятся позже»; «Бонусы за объём» (уровни ✓/○); «Твой код» = userId + «Скопировать код»/«Скопировано»; PrimaryButton «Поделиться приглашением» (ACTION_SEND с текстом «Присоединяйся к «По любви» — знакомства по-человечески. Тебе +N дн. … Мой код: {userId}»); подпись про начисление.

Расхождения

  • Белый экран вместо чёрного с иллюстрацией invite/pic и карточками #514A47@50%.
  • Делится «кодом» = userId вместо реферальной ссылки po.lubvi/referal/… (AppsFlyer OneLink): у приглашённого нет пути ввести код — POST referral/approve/{referrerId} в Android никогда не вызывается, программа фактически не замыкается.
  • Нет «Истории приглашений», прогресс-бара со шкалой 0–20, BS подтверждения участия, диалога «Премиум в подарок», deeplink-обработки, «Подробнее о программе».
  • Тексты и заголовок («Пригласи друзей», «Друг входит — оба в плюсе») другие.
S63

Обучающие видео в профиле и список «Советы по здоровым отношениям» (V3, в работе)

◐ частично

Figma

Пункт «Советы по здоровым отношениям» (video light) первым в links-cells; экраны videos/videoplayer с лайком, закладками, «Сохранённые видео» — секция «В работе».

Flutter

Не реализовано (только рекламные видео в ленте через /advertising).

Android

Не реализовано.

Расхождения

  • Функция не доделана ни в одном клиенте; макет помечен «В работе» — не считаю дефектом Android, фиксирую для полноты.

Общие компоненты

2 экрана · ✓ 1 · ◐ 1
S64

Таббар (4 вкладки)

✓ близко к паритету

Figma

Tab Bar iPhone 390×83 белый r28/28, 4 таба (cards, hearth, chat, profile) gap 83 pad 0 24; .tab 48×40 Selected: чёрная «vector-shape» 50×44 + белая filled-иконка; unselected heavy; заметка: бейдж «7» TT Runs Bold 16 белый rot 15°.

Flutter

SafeArea + pad 8/24, элемент 48×40, иконки 24 heavy / selected svg 40, lightImpact; бейджей нет; порядок Лента/Симпатии/Чаты/Профиль.

Android

Row pad 6/24, TabSvg 48×40, selected svg 40 без тинта, idle 24; верхняя линия 1px #E1D6D1; анимация скрытия 240 мс; бейджей нет; порядок тот же.

Расхождения

  • Лишняя верхняя линия 1px #E1D6D1 (в макете таббар без обводки, с r28 сверху).
  • Нет вибрации при переключении; бейджа непрочитанных нет ни у кого (макет — заметка).
S65

Снеки, лоадеры, ошибки, пуш-уведомления, диплинки

◐ есть, но расходится

Figma

Snack 390×56 negative #FA5F3D / positive #000 сверху; Loader Large 48 / Medium 32 / Small 20; overlay 60% + loader; Lockscreen push шаблон + Push Icon Android 96 #C66F53 монохром; permission-алерты.

Flutter

SnackQueueFlow (очередь, 4 с, свайп вверх); PLFullScreenLoader; FCM + APNs, NotificationDeeplink (chat, likes, super-likes, verify, subscription, profile, profileEdit, communityCheck/exclusion, referral); AppsFlyer OneLink; foreground-пуши обновляют блоки.

Android

Нет единого снека: терракотовые/чёрные пилюли-тосты, красная лента офлайна, inline-тексты; Material/Branded лоадеры; FCM (если google-services.json) с одним каналом и тапом → таб по типу пуша; диплинков AppsFlyer/OneLink нет; foreground-пуши не обновляют экраны.

Расхождения

  • Нет системного снека сверху #FA5F3D/#000 с очередью; ошибки показываются разными способами на разных экранах.
  • Нет AppsFlyer OneLink и обработки deeplink community_invite / referral-invite; тап по пушу не открывает конкретный чат/анкету.
  • Иконка пуша — не проверено (ресурс монохромной иконки 96 в res не найден в прочитанном дереве).

Только в Android (расширения plibvi, нет в Figma и Flutter)

6 элементов · ⊕ 6
S66

События «По душе»: каталог, карточка события, детали

⊕ только Android

Figma

Нет.

Flutter

Нет (в бэкенд-копии есть только README и DTO dating_tz без UI).

Android

EventsScreen (PlTopBar «События «По душе»» / «Компания, не свидание»; сегменты «Каталог» / «Приглашения · N»; карточки #F9F3F2 r16 с cover 148 + чип даты + «Пойду»/«Скрыто» + title + локация + атрибуция + «Компания от N» + «Подробнее →»; пусто «Пока тихо»); EventDetailScreen (hero 220, «Фото: автор», баннер «Тебя зовут в компанию», пилюли, «О событии», «Темы», «Источник ↗», sticky «Пойду» / «Позвать в компанию» / «Не интересно»); EventCard в ленте с действиями «Не интересно» / «Пойду» и чипом «Выход компанией · не свидание». API GET /feed, /events, POST reaction.

Расхождения

  • Собственная функция; дизайн не описан в Figma, стилистика — карточки #F9F3F2 в духе остальных Android-экранов.
S67

Приглашение в компанию (CompanyInviteScreen, inbox)

⊕ только Android

Figma

Нет.

Flutter

Нет.

Android

«Позвать в компанию»: кандидаты из чатов + лайков, прогресс «Выбрано N из M», сообщение, POST /events/company-invite; inbox «Входящие»/«Исходящие» InviteCard (cover 64, бейджи Принято/Отклонено/Ответить/Есть ответы/Отправлено); respond через company-invites/{id}/respond; фича-флаг company_invite.

Расхождения

  • Собственная функция.
S68

Пересечения GEO (EncountersScreen)

⊕ только Android

Figma

Нет.

Flutter

Нет.

Android

PlTopBar «Пересечения» / «Кто рядом → анкета → лайк»; «Случайные встречи» + «Проверить, кто рядом» (ACCESS_FINE_LOCATION → POST /geo/ping → GET /geo/encounters + GET /users/{id}); EncounterPersonCard («~N м · N раз · когда» + «Открыть анкету ›»); EncounterPersonPreview с чипом «Пересечение» и FAB pass/like 64/72 → MatchOverlay; аналитика geo_*.

Расхождения

  • Собственная функция; единственное место, где Android запрашивает геолокацию (см. S11).
S69

Скоринг «Доверие» (ScoringScreen)

⊕ только Android

Figma

Нет.

Flutter

Нет.

Android

PlTopBar «Доверие» / «Проверки для значка в ленте»; «Твой уровень» (Базовый/Повышенный/Высокий/Пока без проверок + «N из 100» + бейджи Личность/ФССП ок/Без судимостей); «Результаты» (CheckStatusCard); выбор проверок (Госуслуги (ЕСИА), ФССП · алименты, Судимости, Сведения о детях, Социальные сигналы) + ConsentRow (152-ФЗ) + «Запустить проверки»; TrustChip на карточках ленты/симпатий/профиля.

Расхождения

  • Собственная функция; TrustChip добавляет на карточку ленты элемент, которого нет в макете (S27).
S70

«О себе голосом» (VoiceOnboardingScreen)

⊕ только Android

Figma

Нет.

Flutter

Нет.

Android

Фазы Intro («Расскажи о себе вслух» + 3 шага + «Начать»/«Не сейчас»), Chat (пузыри ассистент #F9F3F2 / пользователь терракота, поле + 🎤 системный RecognizerIntent ru-RU + «Завершить и собрать черновик»), Done («Что попадёт в bio» + «Записать в анкету» → PATCH profile bio); POST /voice/sessions|turns|apply с локальным fallback на 4 вопроса.

Расхождения

  • Собственная функция; единственное место, где пузырь пользователя терракотовый (в чате — чёрный, см. S47).
S71

Trust chip и mixed feed (флаги mixed_feed / company_invite / scoring)

⊕ только Android

Figma

Нет.

Flutter

В бэкенд-копии Flutter есть diff с trust chip на карточке (не в GitHub-снапшоте), UI-фичи dating_tz нет.

Android

GET /feature-flags → mixed_feed (GET /feed вместо /matching/users), company_invite, scoring; TrustChip caption 11sp «Trust · high/+/…» на карточке, в шторке, в профиле; аналитика POST /analytics.

Расхождения

  • Собственная функция; при mixed_feed лента получает карточки событий между анкетами (аналог места рекламных видео S36).

API и интеграции

Какие эндпоинты бэкенда «По любви» вызывает каждый клиент. Отсутствующий вызов означает, что соответствующая функция физически не может работать в Android, что бы ни было нарисовано на экране.

ЭндпоинтFlutterAndroidКомментарий
Авторизация и общие
POST /auth/otp✓✓Android игнорирует code_lifetime из ответа (таймер 45 с в клиенте).
POST /auth/otp/verify✓✓Flutter: коды 102/105 → красное поле + вибрация + очистка; Android — текст сервера.
POST /auth/token/refresh✓✓Android: один повтор под мьютексом; 500 code 101 (блокировка) не различается.
POST /push-notifications/add-fcm-token✓✓Flutter шлёт на chats base url; Android — при аутентификации, если FCM_ENABLED.
POST /push-notifications/delete-fcm-token✓✓Оба — при логауте.
POST /users/deactivate✓✓Android всегда reason=other; Flutter 4 причины + таймер 10 с.
GET /update-works✓✕Нет hard update и тех. работ; заголовок X-App-Version не отправляется.
GET /advertising✓✕Обучающие видео в ленте.
GET (ip-сервис)?format=json✓✕Внешний IP для аналитики/гео.
GET /cities?term✓✓Android: поиск с 1 символа, debounce 300 мс (Flutter 3 / 500).
Лента и реакции
GET /matching/users✓✓Android — только при выключенном mixed_feed; иначе GET /feed.
PATCH /matching/recalculate✓✓Android не передаёт not_shown_user_ids — пропущенные анкеты возвращаются после перезагрузки.
GET /matching/filters✓✓
POST /matching/like/{id}✓✓Flutter: 3 попытки, код 110 → «Закончились доступные реакции»; Android — общий текст.
POST /matching/super-like/{id}✓✓Записка; лимит 120 символов в обоих.
POST /matching/bring-back✓✕Возврат анкеты (undo).
POST /matching/undo-like/{id}✓✕Отмена лайка после undo.
POST /matching/users/activity✓✕Актуализация онлайн-статусов кэшированных карточек.
GET /users/{id}✓✓Полная карточка для превью.
GET /users/profile/reactions✓✕Остатки лайков/записок/возвратов — основа счётчика и пейволлов.
GET /users/likes · POST /users/likes/{id}✓✓Android без пагинации по курсору user_id; DELETE объявлен, но дизлайк из превью не вызывает его.
GET /users/superlikes · POST /users/superlikes/{id}✓✓То же.
POST /users/{id}/report✓✓Оба: user_card / chat, message не отправляется.
POST /users/{id}/delete-pair✓✓Android без reason; Flutter всегда metOffline (баг конвертера).
Профиль
GET / PATCH /users/profile✓✓Android PATCH всегда полный с дефолтами (ProfilePatchBuilder); Flutter — только изменённые поля.
POST / PATCH / DELETE /users/profile/photos✓✓Загрузка, порядок, удаление; коды 106/109/113 в Android не маппятся на тексты.
POST / DELETE /users/profile/video✓✓Android без ограничения 10 с / 50 МБ на клиенте.
POST /users/profile/position✓◐Android отправляет только {city_id}; {position:{lat,long}} никогда.
GET / PATCH /users/profile/settings✓✓
GET /users/profile/verify/status✓✓Android — polling 20×3 с; Flutter — по пушу verify/сети/foreground.
POST /users/profile/verify/photo✓◐Android допускает фото из галереи; коды 107/108/115 не маппятся.
GET /faq · POST /feedback✓✓
GET /survey · GET /prompts✓✓Android не читает вложенные answer.questions и hidden-шаги.
Подписка и оплата
GET /subscriptions/tariffs✓✓Android читает play_store_product_id; discount/oneTimePayment не показывает.
GET /subscriptions/me✓✓Android-модель: только status/is_active/expires_at (нет paymentSystem, tariff, features, deferredDays).
PATCH /subscriptions/me✓✕Продление / отмена продления.
POST /payments/android · /status✓✓Android: Custom Tabs + ручная проверка; Flutter: WebView + redirect-перехват.
POST /payments/ios/status · /restore✓—Только iOS.
Google Play Billing—⊕PlayBillingCoordinator при PREFER_PLAY_BILLING; во Flutter Android-оплата только через эквайринг.
Сообщества и реферал
GET /communities/all · GET /communities · GET /communities/{id}✓✓GET /{id} в Android объявлен, но не используется (нет deeplink).
POST /communities/{id}/join · /leave✓✓Android вступает в закрытые напрямую; коды 118/119/120 не маппятся.
POST /communities/{id}/visible · /invisible✓✕Видимость значка сообщества.
GET /referral/details✓✓Android-модель без activitiesHistory.
POST /referral/approve?user_id✓✕Подтверждение участия приглашённого — реферальная цепочка на Android не замыкается.
Чаты (REST, chats base url)
GET /user-chats?chat_id✓◐Android — без курсора (первая страница).
GET /user-chats/{id}✓✓
GET …/messages/previous✓◐Android — одна страница 30 от last_message, без догрузки.
GET …/messages/next✓✕Загрузка вокруг lastRead.
GET /user-chats/new-pairs✓✓
GET / DELETE /user-chats/new-pairs/{user_id}✓✕Пара остаётся в «Новых парах» после первого сообщения.
GET /user-chats/updates?last_sync✓✕Актуализация локального кэша (в Android кэша нет).
Чаты (WebSocket wss://…/api-chats/v1/chat)
→ send-text (с reply_to)✓✓Android: outbox с таймаутом 30 с, повтор по тапу.
→ set-last-read✓✓Flutter throttle 4 с + отправка при закрытии.
→ user-is-typing✓✕
→ edit-text · delete-message✓✕
← send-text✓◐Android применяет только в открытом треде; список чатов не обновляется.
← new-pair · user-is-online/offline · user-is-typing · edit-text · delete-message · set-last-read✓✕Прокидываются как JSON, но не меняют UI.
Закрытие 1008 «Invalid token» / «Max connections reached»✓✕Flutter: refresh + reconnect / состояние tooManyConnections.
Только Android (DatingTzApi, расширения plibvi)
GET /feed · GET /events · GET /public/events · POST /events/{id}/reaction · POST /events/company-invite · company-invites/{id}/respond · /users/profile/event-interests—⊕События «По душе» и компания.
POST /geo/ping · GET /geo/encounters—⊕Пересечения.
GET /feature-flags · POST /analytics/events—⊕Флаги mixed_feed, public_events, geo_encounters, company_invite, scoring, voice_onboarding, chat_moderation; своя очередь аналитики (батчи 5/50, кап 500).
GET /scoring/me · /available · POST /scoring/checks—⊕Скоринг «Доверие».
POST /voice/sessions · /turns · /apply—⊕«О себе голосом».

Сторонние интеграции и инфраструктура

ИнтеграцияFlutterAndroidКомментарий
AppMetrica + Firebase Analytics (40 событий)✓✕Android шлёт свои события в собственный /analytics/events (feed_loaded, like_sent, onboarding_step, geo_*, …); набор событий не совпадает — сквозная продуктовая аналитика между платформами невозможна.
Firebase Crashlytics✓✕В Android-манифесте/градле Crashlytics не найден.
Firebase RemoteConfig (is_ios_subscription_enabled)✓✕Android использует серверные /feature-flags.
AppsFlyer OneLink (referral-invite, community_invite)✓✕Deeplink-приглашения и атрибуция установок отсутствуют.
FCM-пуши✓◐Android: работает только при наличии google-services.json (FCM_ENABLED); маршрутизация — таб по типу, без открытия конкретного чата/анкеты; foreground-пуш не обновляет экраны.
SMS User Consent (автозаполнение OTP)✓✕
Secure storage токенов✓✓flutter_secure_storage AES-GCM / EncryptedTokenStore AES-256 (с самовосстановлением при порче keyset).
Локальная БД чатов (drift, 30 дней)✓✕Android каждый раз грузит чаты с сервера.
Кэш карточек ленты и видео (MatchingStorage, MatchingVideosCacheManager)✓✕Android держит колоду только в памяти.
Кэш изображений✓✓ImagesCacheManager 30 дней/400 / Coil 25 % памяти, 120 МБ диск.
Certificate pinning✕⊕Android: CERT_PINS из BuildConfig.
Debug-экран (сервер, прокси, тема, БД)✓✕Android: только EnvBadge и подсказка тестового кода OTP.
Флейворы / окруженияdev / qa / prod (stage.plbvi.ru, production.plbvi.ru)dusha / dushalove / stage / prod (dusha.uxbrain.ru, dushalove.uxbrain.ru, stage.plbvi.ru, production.plbvi.ru)applicationId com.polyubvi.app (+.dev у stage); versionName 1.0.0 против 1.4.2+128.
Локализацияapp_ru.arb (999 строк)strings.xml: только app_nameВсе Android-тексты в Kotlin.
Ориентация / масштаб текстаportraitUp; withNoTextScaling (системный масштаб шрифта отключён)Не зафиксировано в манифесте; sp-масштаб системныйЗдесь Android доступнее, чем Flutter.

Что заявляет docs/FLUTTER_PARITY.md и что есть на самом деле

Файл в репозитории Android ставит ✅ по 44 пунктам. Ниже каждый пункт сопоставлен с фактом из кода; ссылки ведут на карточки экранов.

Пункт чеклистаЗаявленоФактЭкраны
Auth
CTA → phone → OTP✅◐ Флоу и тексты есть; поля Material с подписью снаружи, чекбокс Material, ячейки 48×56, лишняя кнопка «Войти», таймер 45 с с клиента, номер не выделен терракотой.S02 S03 S04
Phone mask +7 / 10 digits✅✓S03
Local OTP / debug hint✅⊕ Во Flutter такого нет; в Android подсказка «Тестовый код» в local/dusha.S04
Token refresh✅◐ Работает; блокировка (code 101) не обрабатывается — нет экрана «Твой аккаунт заблокирован».S05
Onboarding
Name, gender, birthday, height✅◐ Другие заголовки/хинты/варианты; одно поле даты вместо 8 ячеек; нет превью возраста и подписей приватности; 18+ проверяется в конце.S08 S09 S10
About (job/bio)✅◐ Оба шага сделаны обязательными (во Flutter — «Пропустить»); лимиты 80→50 и 300 против 2000.S16 S19
City✅◐ Есть выбор города; нет шага геолокации «Где ты находишься?» и автоопределения.S11 S12
Survey + multi-select interests✅◐ Каждый вопрос — экран со списком; нет слайдеров, горизонтальных пикеров, зависимых под-вопросов; show=true всегда; лимит 10 на категорию, а не на все интересы.S13 S15
Green/red flags✅◐ Обратный порядок, свои тексты, inline-ввод вместо BS, другие чипы.S17
Prompts (openers)✅◐ Без sticky-категорий, счётчика «N из 3», BS ответа.S18
Photo✅◐ Одно фото в круге, можно пропустить (Flutter: сетка 3×2, минимум 1).S20
Video ✅ (edit + onboarding optional)✅◐ Только выбор из галереи; записи 10 с и кружка-превью нет.S21
Exact Flutter step order✅◐ Пропущены шаг геолокации (4) и разводная «Не упусти свою любовь» (13); медиа разбито на два шага; интересы не отдельный экран.S11 S14 S20
Post age-range✅◐ RangeSlider вместо барабанов, лишняя «Пропустить», чёрный заголовок.S26
Post verification prompt (soft once + «Позже»)✅◐ Есть; тексты не из макета, нет 4 преимуществ.S22
Main tabs
4 tabs + selected SVG pills✅✓ Лишняя верхняя линия 1px.S64
Deep link → tab (+ onelink host)✅◐ Только выбор таба; OneLink/AppsFlyer-приглашения не обрабатываются; конкретный чат/анкета не открываются.S65
Feed
Matching deck + swipe✅◐ Нет undo, счётчика лайков, видео-кружка, описания под карточкой.S27 S28
Like / pass / superlike✅◐ Есть; нет лимитов реакций и переходов в пейволл; записка — диалог.S29 S31
Match overlay → chat✅◐ Другие тексты и формы фото, нет анимации въезда.S30
Filters (age/height/radius/city/community)✅◐ RangeSlider вместо барабанов; нет геолокации; рост не может быть «Не важен»; другой порядок и тексты.S39
Photo pager + scroll sections✅◐ Полоски вместо точек; секций под карточкой нет — модальная шторка «Узнать больше» с другими заголовками.S27
Report with reasons✅◐ AlertDialog вместо BS с кнопками; тексты результата другие.S32
Mixed feed events✅⊕ Расширение plibvi.S66
Swipe training✅◐ Своя композиция; нет обучения скроллу.S34
Match / swipe animations✅◐ Свайп есть; нет анимации счётчика сердец и оверлеев ×16 с поворотом.S28
Sympathy
Likes / superlikes list✅◐ Плоские списки вместо карточек в брендовых формах и стека записок.S43 S44
Preview like-back / skip✅◐ Есть; «пропустить» не удаляет лайк на сервере.S45
Blur lock✅◐ Блюр есть, но имя/возраст раскрыты, таймер сырой строкой, нет CTA «Узнай, кто поставил лайк».S43
Chats
List + new pairs✅◐ Без live-обновления, пагинации, меню, баннера правил, sliver-хедера.S46
Thread + WS send✅◐ Цвета пузырей другие, нет времени сообщений, статус по сокету, одна страница истории.S47
Delete pair / report menu✅◐ Dropdown вместо BS; без причины разрыва; без «Поделиться профилем».S48
Date dividers✅✓ Стиль другой (caption по центру без линий).S47
Reply (long-press) + read/sent icons✅◐ Ответ есть; иконки под пузырём; нет контекстного меню, копирования, редактирования, удаления.S47 S50
Message tails✅✓S47
Profile
Home header + visibility✅◐ Полностью другая композиция («redesigned home»).S52
Edit photos/video/fields/flags/prompts/survey✅◐ Одна форма; пол и дата редактируемы; ответы анкеты не видны; «Быстрый выбор» флагов — своя выдумка.S59
Settings push API✅◐ Нет секций «Контент» и «Подписка»; названия свитчеров другие.S54
Verification multi-step✅◐ Один экран со статусами; селфи из галереи; нет камеры с жестом.S22 S23 S24
Subscription + payment URL✅◐ Список тарифов + Custom Tabs; нет пейволла по точке входа, TYP, pending/failed, продления.S40 S41 S42
FAQ API✅◐ Аккордеон вместо списка; заголовок другой.S57
Support feedback form✅◐ Экран вместо BS; без счётчика; результат inline.S56
Rules/privacy + Flutter URLs✅◐ Экран с собственным кратким текстом правил + ссылка; во Flutter — только внешние URL, в Figma — экран с другими разделами.S58
Communities✅◐ Белый каталог; нет видимости, приглашений, поиска, результатов заявок.S61
Referral✅◐ Делится userId вместо ссылки; approve не вызывается; нет истории.S62
Delete account✅◐ Без причин и таймера 10 с.S53
Email enter + marketing✅◐ Экран вместо BS; тексты другие.S55
Assets / chrome
Launcher icons from Flutter✅◐ Не сверял попиксельно (см. «Что не проверено»); сплэш-тема не настроена.S01
In-app SVG/PNG set✅✓ 139 SVG + 10 PNG скопированы; в UI задействовано около 45 иконок.—
Text field no white label cutout✅◐ Заявлено как достижение, но это осознанный уход от input--line из макета: подпись вынесена наружу, фон #F9F3F2.S03
Dating-TZ (расширения)
Mixed feed, Events, Company invite, GEO, Analytics queue, Feature flags, Scoring/voice, Trust chip✅⊕ Код существует и соответствует описанию; в Figma и Flutter аналогов нет, поэтому паритет к ним неприменим.S66–S71

Итог по чеклисту: из 44 отмеченных ✅ пунктов паритет с Figma/Flutter по дизайну, текстам и поведению выдерживают 4 (маска телефона, таббар, разделители дат, хвосты пузырей), ещё 10 — расширения plibvi, к которым паритет неприменим; остальные 30 — «функция есть в каком-то виде».

Особенности и дефекты самого Flutter (наследуются как «эталон»)

Flutter — не идеальная реализация Figma. Эти пункты важно знать, чтобы не переносить их в Android как норму и не считать расхождением с Flutter то, что на самом деле ближе к макету.

  1. DeletePairConverter маппит все причины разрыва пары в metOffline — сервер никогда не получает «Неприемлемое поведение» и другие.
  2. Жалоба без текста. POST /users/{id}/report отправляется без message (TODO в коде).
  3. Soft update и «Технические работы» не реализованы (UpdatesBloc emit noUpdates) — есть только hard update.
  4. Тёмной темы нет: darkTheme == lightTheme, ThemeModeBloc переключает одно и то же.
  5. Бейджей на таббаре нет, хотя в Figma есть заметка про бейдж «7».
  6. EmptyPhotoWidget задаёт height 116 / width 128 — перепутаны местами (размер спасает GridView).
  7. Шаг геолокации в состоянии firstRequest не показывает «Пропустить»/«Выбрать город» (кнопка одна — «Далее», хотя в arb ключ называется ShareLocationButton, а в Figma — «Поделиться геопозицией» + «Пропустить»).
  8. Опечатка в arb: repeatVerificationSubtitle — «Это фото могут видеть только только твои пары».
  9. Пустое состояние «Записок» не отрисовано — при 0 записок экран просто закрывается (TODO).
  10. Правила сообщества и политика открываются внешними URL, тогда как в Figma есть экран «Правила сообщества» с разделами.
  11. Экран блокировки без кнопки «Отменить подписку» (в Figma есть).
  12. Лимит записки 120 символов против «0 / 200» в макете.
  13. MediaQuery.withNoTextScaling — системное масштабирование шрифта отключено для всего приложения (доступность).
  14. PlDialog на Android — AlertDialog.adaptive с TODO «обновить стиль кнопок, когда будет дизайн».
  15. Приветствие использует растровый couple.png вместо векторного коллажа с эмодзи из макета.
  16. lineLight/baseLight weight 500 против 540 в Figma (Variable-шрифт не используется).
  17. Дизлайк не отправляется на сервер (локальное скрытие; not_shown_user_ids уходят при recalculate) — Android повторяет эту логику, но без not_shown_user_ids.
  18. После онбординга сразу открывается верификация поверх экрана возраста (replaceAll [MatchingAgeRange, VerificationRouter]); при каждом логине с surveyPercentage > 0 — экран возраста, если isFirstMatchingLaunch не сброшен.
  19. Опечатки в макете Figma, которые не должны попасть в код: «поставл лайк», «секуд до удаления», «вовзращай», «важайте частную жизнь», «Пригласиеще 4 друга», «только только» повторено из arb.

Есть в Figma, но нет ни во Flutter, ни в Android

Риски Android по данным и поведению

Не визуальные расхождения, а места, где Android может испортить данные пользователя или сломать серверную логику. Каждое подтверждено чтением кода.

  1. ProfilePatchBuilder всегда шлёт полный PATCH /users/profile с дефолтами для незаполненных полей: «Пользователь», «—», male, 1995-01-01, 170, «Пока без описания», «Не указано». Любое сохранение с экрана, где не загружено одно из полей, перезаписывает реальные данные на бэке.
  2. Анкета уходит с show=true для всех шагов и hidden-шаги не фильтруются: ответы шага «Стабильность» (жильё, кредиты), которые макет и Flutter скрывают от других, становятся видимыми.
  3. Вложенные под-вопросы (answer.questions) не парсятся — часть данных анкеты, влияющих на матчинг («Сколько всего ты хочешь детей?», «У партнёра тоже сильные карьерные амбиции», «Должна быть квартира?», «Важны ли религиозные взгляды партнёра?»), с Android не собирается никогда.
  4. Дата рождения и пол редактируются из профиля (макет: «Исправить дату рождения будет нельзя», пол не меняется) — обход возрастной проверки после регистрации.
  5. 18+ проверяется только на финальном submit онбординга; несовершеннолетний проходит все шаги и получает ошибку в конце.
  6. Верификация из галереи: селфи можно заменить любой картинкой с устройства — обесценивает галочку для всех пользователей, включая iOS/Flutter.
  7. dismissedPersonIds только в памяти и не передаются как not_shown_user_ids в recalculate — после перезапуска пропущенные анкеты возвращаются.
  8. Ошибки лимитов не распознаются (код 110 и др.): пользователь видит технический текст вместо «Закончились доступные реакции» и не попадает в пейволл.
  9. Причина деактивации всегда «other»; причина разрыва пары не отправляется.
  10. POST /referral/approve не вызывается, делится userId вместо ссылки — реферальные дни никому не начисляются с Android.
  11. Блокировка (500 code 101) не обрабатывается — заблокированный пользователь получает бесконечные ошибки/логаут без объяснения.
  12. Фильтр роста всегда отправляется (150–200 по умолчанию, «Не важен» невозможен) — анкеты без указанного роста отсеиваются.
  13. Заблюренные лайки раскрывают имя и возраст — платная информация («Незнакомец 32» в макете) отдаётся бесплатно.
  14. Статус «в сети» в чате — это состояние сокета, а не собеседника; пользователь делает выводы об активности пары по неверному сигналу.
  15. Список чатов не обновляется по WebSocket, история — только последние 30 сообщений; edit/delete от собеседника не применяются — экран может показывать удалённые или устаревшие сообщения.
  16. Ограничения медиа не проверяются на клиенте (видео 10 с / 50 МБ) — большие файлы уходят на сервер и падают с ошибкой 113 без понятного текста.
  17. Занятость и «О себе» обязательны, лимит «О себе» 300 — пользователь Flutter с био на 2000 символов при сохранении с Android потеряет текст (обрезка/валидация).
  18. Нет hard update: несовместимую версию нельзя принудительно обновить с сервера.
  19. Лимит интересов 10 на категорию вместо 10 суммарно — на бэк могут уйти до 60 интересов.
  20. Обращение смешано («Введите имя», «Добавьте фото», «Когда кто-то лайкнет вас» против «ты» в остальных местах и в макете) — не риск данных, но заметно пользователю на каждом экране.

Что не проверено

Границы этого аудита, чтобы не выдавать предположение за факт.

Приложение: полные конспекты источников

Все рабочие заметки, по которым построен отчёт, без сокращений: Figma по страницам, Flutter по фичам, Android по пакетам. Открывайте нужный блок — это первичные данные с размерами нод, текстами и логикой.

Figma — страница «Онбординг» (644) (notes_figma_onboarding.md, 31657 байт)
# Figma «По Любви Дизайн_Client» — страница «Онбординг» (guid 644:5049), прочитано полностью 08.09.2026

## Общая система (из нод)
- Кадр 390×844 (iPhone), статус-бар 54, navbar--iOS 44 @y54, home-indicator 34 @y810.
- Цвета: бренд #C66F53 (терракота), фон #FFFFFF, текст #000000, вторичный текст #514A47, третичный #B3ACA9, «коричневый» #9E8075, беж #F2E7E3 (и 50%), бордер #E1D6D1, кнопка main #000000/#1B1A1C, ошибка/снек #FA5F3D, disabled кнопка #F2E7E3@50%, чёрный чип Deletable #000000.
- Шрифты: TT Runs Trial Variable Bold 21/22 lh24 (заголовки), ExtraBold 30 lh32 (крупные заголовки), Medium 30 lh32 (крупные подписи «Я», «Мой рост», «Тебе нет 18»); Manrope Medium 16 lh20 ls0.2 (подзаголовки), Manrope Bold 20 lh24 (вопросы), Manrope Bold 16 lh20 (категории интересов, #9E8075), Manrope Medium 12/14 (капшены), Inter (иногда: Auth 2 заголовок Inter Bold 27/32; Auth 9.1 «Тебе 38 лет» Inter Regular 40/32 + Inter Regular 12/16 — вероятно недоработка). Akzidenz-Grotesk — только заголовки секций макета.
- Кнопка ⟨button⟩: Big 366×56 r12 smooth, pad 0 12; main: fill #000000 (текст белый), disabled: #F2E7E3@50%, load: то же + лоадер; secondary: stroke #E1D6D1 1.33; Small 44 высота pad 0 20; text-кнопка (44). button-stack/button-cell: 390×72, pad 8 12 8 12, gap 8/16, прижат к низу (y738).
- Инпут ⟨input--line⟩ 366×56 r12 smooth stroke 1.33 (#C66F53 active / #E1D6D1 inactive / fill #F2E7E3@50% blocked), pad 18 20; варианты Start / Active-filled / Inactive / Inactive-filled / blocked; label + hint + caption + иконка (hidden-light «Увидят только твои пары»).
- ⟨input--code-group⟩ 6 ячеек OTP; ⟨input--bday-group⟩ 8 ячеек ДДММГГГГ (state default/filled/focus/filled-focused/blocked/Error Inactive/error-focused).
- ⟨picker-single⟩ 44 высота r12 smooth (default: белая, stroke #E1D6D1; checked: fill #C66F53). ⟨picker-multiple⟩ 40 высота r28 smooth (Default stroke, Checked #C66F53, disabled #F2E7E3@50%, Deletable #000000 с иконкой ×, skeleton).
- ⟨slider-cell⟩ 358×94 (State Middle/Positive/Negative/Minimum) с тегом (например «Не против детей», «Хочу детей», «Чайлдфри», «Не хочу детей», «Не перерабатываю», «Много работаю», «Люблю отдохнуть», «Уважаю границы», «Люблю контроль») и эмодзи по краям (🛀/💼, 🖇️/🕊️).
- ⟨progress-bar⟩ 390×20 @y98 pad 4 12 8 12: filling quarter/third/half/3/4/full; несколько сегментов (2–3 полосы).
- ⟨icon-caption⟩ 320×18: иконка 16 + текст («Увидят все пользователи» revealed-light; «Люди увидят только возраст»; «Можно отредактировать в профиле»; «Селфи увидят только твои пары» hidden-light).
- ⟨Tooltip⟩ 147×32 fill #F2E7E3@50% r12 у navbar: «Доступно 25% анкет», «Доступно 20% анкет», «Тебя увидят 45% людей», «Доступно 60% анкет», «Доступно 85% анкет», «Шанс найти пару в 2 раза выше» (position right).
- ⟨snack / type=negative⟩ 390×56 fill #FA5F3D @y54 «Не отправилось, попробуй ещё раз».
- bottom-sheet: fill #FFFFFF r28/28/0/0 smooth pad 24 12 8 12 + .handle-cell; overlay #000000@60%.
- ⟨permission-ios⟩ системный алерт (Cam/Mic/Gallery/push); ⟨keyboard⟩ iOS.
- ⟨loader / state=Large⟩ 48×48.

## Секция «Авторизация» (Согласовано)
- Auth 1: сплэш, фон #C66F53, логотип-леттеринг из 7 векторов #F2E7E3 320×86 по центру. «Android»: сплэш-контейнер 288×288 с логотипом 192×52 (Android 12 splash).
- Auth 2 (welcome): title «Первый шаг к семейному счастью» Inter Bold 27/32 #C66F53; subtitle «Найдём вторую половинку, которая разделяет твои ценности» Manrope 16/20 #514A47; коллаж 296×232: две фото в масках shape-brand (form=3, form=2, повёрнуты −6.86°/6.29°) + 7 эмодзи-бейджей 38×38 (белый круг, stroke #F2E7E3 3 outside): 👼🏻 🎓 🧳 👰🏻‍♀️ 🎨 💍 🥂; ниже «Укажи телефон» TT Runs Bold 22/24 + «Создадим тебе новый аккаунт или найдём существующий»; кнопка «Войти по СМС» (fill #1B1A1C) в button-stack.
- Auth 3 (телефон): «Укажи телефон» 21/24 + «Пришлём СМС с кодом доступа»; input--line label «Номер телефона» hint «987 654-32-10»; icon-caption (пустая/замок «Никто не узнает твой номер» по arb); текст согласия Manrope 12/16 #514A47 «Нажимая «Далее», я соглашаюсь с условиями политики конфиденциальности, а также даю своё согласие на обработку моих персональных данных» (ссылочная часть #C66F53); кнопка «Далее» disabled; клавиатура Numeric. Состояния: 3.1 ввод «910 43», 3.2 «910 436-30-79» + кнопка активна, 3.3 inactive-filled «910 436» (клавиатура скрыта, кнопка внизу y682), 3.4 blocked + кнопка load, 3.5 ошибка: snack negative «Не отправилось, попробуй ещё раз». В макете НЕТ чекбокса «Я старше 18 лет…» (в arb Flutter есть enterPhoneScreenTermsCheckboxDescription).
- Auth 4 (OTP): «Введи код»; «Отправили 6 цифр в СМС на +7(910)436-30-78» (номер #C66F53); text-кнопка «Изменить номер» (с иконкой 16 heavy); input--code-group 6 ячеек + «Новый код можно будет получить через 59 с.»; 4.1 ввод (43 с.), 4.2 Validation Process — ячейки blocked + кнопка «Запросить новый код» state=load (15 с.), 4.3 ошибка — ячейки Error Inactive/error-focused, 4.4 — кнопка «Запросить новый код» активна (heavy иконка). Клавиатура Numeric+Suggestion.
- Auth 5 («Привет, расскажи о себе!») ExtraBold 30/32 #C66F53; «Мы составили опросник для поиска серьёзных отношений. Больше ответов — больше найдётся партнёров»; кольцо прогресса 222×200 (три эллипса stroke 12: фон #E1D6D1, фронт #C66F53, скрытый #E7C9B2) с «60%» ExtraBold 30 + «доступно» Bold 21; 13 «сердечек» (favorite) #C66F53 разных размеров/прозрачности; скрытые бейджи профиля; кнопка «Заполнить профиль».

## Секция «Данные о себе» (Согласовано)
- Auth 6/7/8 (имя): «Как тебя зовут?»; input «Имя» (hint Саша) + «Фамилия» (caption «Увидят только твои пары», иконка hidden-light); icon-caption «Можно отредактировать в профиле» (light); navbar с прогрессом; progress quarter; клавиатура текстовая. 7: «Никита» active; 8: имя filled, фамилия «Ко» — кнопка «Далее» активна.
- Auth 9/9.0/9.1/9.2/9.3: «Когда у тебя день рождения?»; input--bday-group (Д Д М М Г Г Г Г); caption «Люди увидят только возраст»; 9.1 — «Тебе 38 лет» (Inter Regular 40) + «Убедись, что всё верно. Исправить дату рождения будет нельзя» (Inter 12/16), кнопка активна; 9.2 — «Тебе нет 18» (TT Runs Medium 30) + «Приложение доступно только для совершеннолетних пользователей», кнопка disabled; 9.3 (backlog) — «Дата неверная» + «Укажи настоящую дату рождения» (title ExtraBold 30).
- Auth 13/14: «Твой пол и рост?»; «Я» Medium 30 #B3ACA9; picker-single «Мужчина»/«Женщина» 120×44; «Мой рост» + cupertino-picker 248×228 fill #F2E7E3@50% r16 (value-type=cm); caption «Увидят все пользователи» (revealed-light); progress third; 14: «Мужчина» checked, кнопка активна.
- Auth 15/16/17/24 (гео): «Где ты находишься?» Bold 22 + «Хотим найти людей поближе к тебе» (в arb Flutter: «Разреши доступ к геолокации, чтобы мы смогли найти людей поближе к тебе»); иллюстрация location-pic 200×240 (пин #C66F53 + тень #E7C9B2); кнопки: «Поделиться геопозицией» (main, с иконкой 24 heavy) + «Пропустить» (secondary); progress 3/4. 16: поверх — системный алерт iOS «Разрешить приложению “По Любви” использовать вашу геопозицию?» / «Точная геопозиция поможет нам найти пару поближе к тебе» / Однократно / При использовании / Запретить (+ карта, Precise: On). 17: ошибка «Не получили локацию» (#C66F53, Bold 21) + «Разреши приложению доступ к геолокации в настройках телефона»; Small кнопки «Настройки» (settings-heavy иконка) + «Выбрать город»; внизу «Пропустить». 24: город определён: «Твой город» (#9E8075 Manrope 16) + «Петровавловск-Камчатский» TT Runs Medium 30 #C66F53 + Small secondary «Выбрать другой»; внизу «Настройки геолокации» (secondary с иконкой) + «Сохранить и продолжить» (main).
- Auth 18/19/20/21/22/23/25/26 (выбор города): input--search «Поиск города» 366×56 r12 в search-cell 390×72 @y98; список city-cell 390×60 (type Default / Region: город + область baseline, gap 12 / picked / last-child; skeleton-short/long 56); 18 — скелетон; 23 — «Не загрузилось :(» ExtraBold 18 #C66F53 + «Попробуй обновить страницу» + Small «Обновить» + иллюстрация; 22 — список (Москва, Санкт-Петербург, Абакан, Абинск, Агата, Агинское/Забайкальский край, Адлер, Адыгейск, Азов/Ростовская область, Алагир, Алапаевск, Александров, Александровск); 19 — прокрутка (search уехал вверх); 20 — Focus; 21 — Typing «Череп» → Череповец, Черепаново/Владимирская обл., Черепаново/Свердловская обл., Черепица (last-child); 25 — picked «Череповец» первой строкой; 26 — «Не нашли город» ExtraBold 18 #C66F53 + «Хотим найти людей поближе к тебе» (в arb: «Поищи ошибки или укажи ближайший населённый пункт»).

## Секция «Анкетирование» (Готово) — «Ценности и приоритеты*» / «Жизненные обстоятельства*» (*обязательно)
Общая структура экрана вопроса: progress-bar full/(частичный), navbar, title категории TT Runs Bold 22 #C66F53 (варианты ExtraBold 30), question-cell: вопрос Manrope Bold 20 #000 + subtitle Manrope 16 #514A47, ответ (slider-cell или radio-group из picker-single: горизонтально 2–3 равных 175/120 или вертикально 358×44, wrap для длинных), «[Необязательно]» Manrope 16 #B3ACA9 над вторым вопросом; button-caption: icon-caption «Увидят все пользователи» + «Далее» (disabled пока не отвечен обязательный); tooltip «Доступно N% анкет».
- 1/1.0 loader/1.1–1.5 «Отношение к детям»: «Я хочу завести ребенка» / «Насколько это про тебя?» slider (Middle «Не против детей», Positive «Хочу детей», Negative «Не хочу детей», Minimum «Чайлдфри»); [Необязательно] «Сколько всего ты хочешь детей?» — Одного / Два или больше / Ещё не знаю (показывается при Positive). 1.0 — loader 48. Tooltip «Доступно 25% анкет», «Доступно 20% анкет».
- 2/2.1/2.2 «Карьера»: «Личная жизнь или работа?» / «Расставь приоритеты» slider (🛀 … 💼: «Не перерабатываю», «Много работаю», «Люблю отдохнуть»); [Необязательно] «У партнёра тоже сильные карьерные амбиции» (Нет / Не важно / Да, wrap) — вариант «Поддержишь ли ты сильные карьерные амбиции партнёра?»; скрытый «Пожертвуешь карьерой ради партнёра?» (Никогда / По ситуации) и «Пожертвовуешь личной жизнью ради карьеры партнера?».
- 3/3.1 «Стабильность»: «Есть ли у тебя жильё?» / «Готовое или строящееся — не важно» (Нет / Да); [Необязательно] «У твоей пары должна быть квартира?» (Нет / Не важно / Да); скрытый «Переедешь в другой город или страну в случае чего?» (Нет / Да).
- 4/4.1/4.2 «Новый опыт»: «Насколько свободными ты видишь идеальные отношения?» slider (🖇️ … 🕊️: «Уважаю границы», «Люблю контроль»); [Необязательно] «Должен ли партнёр разделять твоё видение?» (Нет / Да; скрытый «Не важно»).
- 5/5.1/5.2 «Духовность»: «Твои религиозные взгляды» / «Выбери один вариант» — wrap picker-single: Православный, Католик, Протестант, Мусульманин, Иудей, Буддист, Агностик, Атеист, Другое; [Необязательно] «Важны ли для тебя религиозные взгляды партнёра?» — Не важны / Обращаю внимание / Должны совпадать с моими.
- 6/6.1 «Курение» / «Отметь свои привычки и отношение к курению потенциального партнёра»: «Ты куришь?» — Нет / Бывает / Курю; «Как ты отнесёшься к курению партнёра?» — Это недопустимо / Иногда — окей / Спокойно. Tooltip «Тебя увидят 45% людей», «Доступно 60% анкет».
- 7/7.1 «Алкоголь» / «Укажи отношение к алкоголю у себя и у партнёра»: «Твоё отношение к алкоголю» — Я не пью / Выпиваю по праздникам / Пью раз в месяц или чаще; «Отношения партнёра с алкоголем» — Не важно / Не выпивает / Иногда — окей / Составлю компанию. (7.1 — вертикальный список, кадр 874 высотой/скролл.)
- 8/8.1 «Прошлые отношения» / «Если у тебя или будущего партнёра есть дети, вам стоит узнать это сразу»: «Есть ли у тебя дети?» — Нет / Да; «Как ты отнесёшься к наличию детей у партнёра?» — Мне не подходит / Положительно / Не хочу отвечать.
- 9 «Не упусти свою любовь» (разводная): иллюстрация (открытка/конверт), «Ответь на необязательные вопросы, и мы подберём максимально подходящего тебе человека» (Manrope Regular 16); кнопки «Заполнить до конца» (main) + «Дополнить позже» (secondary); tooltip справа «Шанс найти пару в 2 раза выше».

## Секция «Интересы, хобби, разное 👀»
- 10/15/16/17 «Интересы» / «Выбери до 10 любимых занятий и хобби. Подберём для тебя людей со схожими увлечениями» (variant: «…Мы подберём для тебя людей со схожими увлечениями.»); navbar title «0 из 10» → «1 из 10» → «3 из 10» → «10 из 10» (#C66F53 при 10); категории Manrope Bold 16 #9E8075 с эмодзи; чипы picker-multiple r28 wrap gap 8, по центру:
  - 🚴🏻‍♂️ Спорт и фитнес: Бег, Плавание, Велоспорт, Единоборства, Спортзал, Теннис, Сноуборд, Лыжи, Танцы, Скалолазание, Футбол, Волейбол, Баскетбол, Йога, Сёрфинг, Скейтбординг.
  - 🎨 Творчество: Фотография, Дизайн, Видео, Рисование, Музыка, Рукоделие, Писательство, Цифровой арт, Блоггинг (+ Кино, Театр в варианте 15).
  - ⛺️ Путешествия и приключения: Культурный туризм, Пляжный отдых, Экстремальный туризм, Походы, Гастротуры, Автопутешествия (+ Хайкинг, Кемпинг в 15).
  - ✍🏻 Саморазвитие и образование: Психология, Религия, Медитации, Онлайн-курсы, Волонтёрство, Наука, Нетворкинг, Коучинг, Иностранные языки.
  - 🎲 Развлечения и досуг: Кино, Настолки, Видеоигры, Театр, Концерты, Выставки, Бары и клубы, Стендап, Кафе и рестораны.
  - 🍏 Диета (10) / ☕️ Еда и напитки (15): Здоровое питание, Домашняя еда, Стритфуд, Кето, Веганство, Вегетарианство / Кофе, Вино, Здоровое питание, Вегетарианство, Веганство, Кето.
  - 17: при 10 выбранных остальные чипы disabled (#F2E7E3@50%). Кнопка «Пропустить» (secondary) пока 0, «Далее» (main) при ≥1. Кадр 10 высотой 1942 (скролл).
- 11/11.1 «Кем ты работаешь?» / «Укажи название своей должности»; input «Моя должность» hint «Менеджер» (caption «Увидят только твои пары» hidden-light); «Пропустить» → при вводе «Диз» «Далее»; tooltip «Доступно 85% анкет».
- 12/12.1–12.4 «Твои «Флаги»» / «Что моментально отталкивает или привлекает тебя в партнёре?»; блок 🚩 «Недопустимо» / «Например: вечно в телефоне» (Manrope 14 #B3ACA9) + чип «+»; блок 💚 «Обожаю» / «Например: сюрпризы» + «+»; navbar title «🚩 0 из 7     💚 0 из 7» (→ 1 из 7, 3 из 7 / 7 из 7); 12.1/12.2 — bottom-sheet с input «Красный флаг» (hint «вечно в телефоне») + «Сохранить ответ» (disabled→active «ходит в Симач»); 12.2/12.3 — «Зелёный флаг» («кудри <3», hint «хороший парфюм»); чипы Deletable чёрные: «ходит в Симач», «рэпер», «постоянные жалобы» / «хороший парфюм», «кудри <3», «за меня платят», «ответственность», «низкой голос», «доброта», «есть собака шиба ину»; «Пропустить» → «Далее». Flow 2 стартует здесь.
- 13/13.01 loading/13.1–13.4/18 ««Открывашки»» / «Выбери до трёх карточек и ответь на вопросы. Другие пользователи увидят ответы в твоей анкете»; navbar «0 из 3» → «1 из 3» → «2 из 3»; navigation-чипы категорий (Личность / Семья / Мечты / Отношения) под заголовком (sticky @y98 при скролле); секции prompt-section (title Manrope Bold 20 по центру) со списком ⟨prompt⟩ 366×60/80 fill #F2E7E3@50% r16 pad 20:
  - Личность: Твой самый нелепый талант?; Если бы твоя жизнь была фильмом, то каким?; За что тебя чаще всего критикуют?; Что ты коллекционируешь и почему?; Твоя самая странная привычка?; От какого блюда невозможно отказаться?; Какой поступок не простишь никому?; Представь — тебе 100 лет. Что посоветуешь себе сегодняшнему?; Какую правду о жизни, тебе хотелось бы узнать раньше? (вар. «Гениальная идея, которую хотелось бы услышать раньше.»); Если твоя жизнь — сериал, то какая тема этого сезона?
  - Семья: За что ты чувствуешь благодарность маме?; Главный секрет счастливого брака?; Самый ценный урок от твоих родителей?; О чём ты никогда не споришь с родственниками?; Как выглядит идеальный семейный отдых?; Какую твою тайну родственники до сих пор не знают?
  - Мечты: Какие твои достижения раньше казались невозможными?; Какая мелочь изменила бы твою жизнь к лучшему?; Чем будешь заниматься, если не придётся работать?; Куда переедешь нераздумывая?; Какая твоя мечта кажется сейчас нереальной?
  - Отношения: Самый быстрый способ испортить свидание?; Как ты осознаёшь, что влюбляешься?; Чего не стоит дарить тебе ни на какие праздники?; Как ты представляешь себе идеальную свадьбу?; Как ты понимаешь «честность в отношениях»?; Лучший способ помириться?; Какую ошибку в отношениях никогда не повторишь?; Как заслужить твоё доверие?
  - 13.2/13.3: bottom-sheet ответа: вопрос (Manrope 16→Bold 20), input--textarea «Твой ответ» hint «Напиши, что думаешь» счётчик «0 / 120» → «56 / 120» → «104 / 120», «Сохранить ответ»; 13.4: выбранные карточки ⟨prompt / State=editing⟩ fill #C66F53 (вопрос + ответ, напр. «Поднимать вещи с пола ногой») подняты над списком; 13.01 skeleton (shimmer); 18 — ошибка «Не загрузилось :(» + «Обновить».
- 14/14.1/14.2 «Что мы забыли спросить?» (ExtraBold 30 / Bold 22) / «Расскажи о себе в свободной форме или переходи к просмотру анкет»; input--textarea «Обо мне» hint «Люблю шоколадное мороженое» счётчик «0 / 2000» → «400 / 2000»; «Пропустить» → «Далее».

## Секция «Медиа 👀» (Готово)
- 1/1.7/1.11/2/2.1/2.3/3/3.1/3.2/3.3/3.4/3.5/6/6.1–6.4 «Добавь видео и фото» ExtraBold 30 / «Запиши видео-кружочек.⏎Пользователи с видео находят пару в два раза быстрее.»; ⟨video-add-circle⟩ 208×208 stroke #E1D6D1 (State Default / No-Permission с ссылкой «Настройки»); «1 фото — минимум» (#B3ACA9) → «Зажми фото, чтобы перетащить»; photo-grid 366×264 (3×2 ячейки 116.67×128 r16 smooth stroke #E1D6D1 inside; photo-add-cell Default/Disabled; photo-editing: added-default, loading, error (stroke #FA5F3D), drop-none, placeholder #F2E7E3, drop-target (stroke #C66F53 3), drag (140×153.6 увеличен)); кнопка «Сохранить» disabled → active; 3 — bottom-sheet «Сделать фото» / «Выбрать из галереи»; 1.11 — системные алерты Mic/Cam («Мы не можем открыть камеру без твоего разрешения»); 3.1 — Gallery alert; 3.3 No Camera Permission — bottom-sheet «Разреши приложению доступ к Камере в настройках телефона, чтобы сделать фото» + Small «Настройки» + «Выбрать из галереи»; 3.4 No Gallery — «…к Галерее…, чтобы выбрать фото» + «Сделать фото»; 3.5 No any — иконка gear 40 + «Не получили доступ к фото» ExtraBold 18 #C66F53 + «Без фото мы не можем активировать твою анкету. Разреши доступ к Галерее и Камере в настройках телефона» + «Настройки»; 2.1 — video-circle с .delete-transformer Alert (подтверждение удаления видео); 2.3 — video-circle loading; 6 — video-circle Default Size=Medium (видео записано) + 1 фото; 6.1 — 5 фото loading; 6.2 — error на всех; 6.3 — 1 ok + error; 6.2 Drag/6.3 'n'/6.4 Drop — drag-and-drop.
- 4 photo-picker iOS (системный: «Выбери до 6 фото», Отмена/Добавить, Фото/Альбомы, «Показать выбранные (1)»); 4.1 photo-picker Android (Material Media Picker).
- Запись видео (чёрные экраны): 1.1 — video-circle Big 366×366 + navbar type=media (таймер), кнопки: .56-button (закрыть) / button-camera 72 (state video/Recording/done/photo/disabled) / .56-button camera-switch; 1.2 — Recording, таймер «0:6,13» + record-indicator; 1.3 — done «0:10,00» Progress; 1.4 — loading (overlay + loader); 1.5 — error snack; 1.6 — Paused; 1.8 Cam Error — круг stroke #514A47 + иконка camera-error 40 + «Камера не работает» (Manrope Bold 14 белый).
- 5 camera-view (системная камера iOS), 5.1/5.2 «Проверь фото» — превью 338×560 r33.78 с кнопками, loading, error snack.
- Заметка дизайнера: «1 тап — меняем состояние, повторный тап — удаляем видео с эффектом fade».

## Секция «Верификация» (Готово)
- 1: pic-checkmark 56 + «Сделай селфи и получи галочку» Bold 22; pro-cell ×4 (иконка 24 + текст 16 #514A47): «10 лайков в день вместо 8» (match-light), «Твою анкету увидят на 60% больше людей» (revealed-light), «Пользователи с галочкой получают больше симпатий» (checkmark), «Это бесплатно и займёт меньше минуты» (time light); кнопки «Получить галочку» (main) + «В другой раз» (secondary).
- 2/2.1: ⟨photo-frame / Type=Default⟩ 366×366 r28 (плейсхолдер-инструкция) + «Открой камеру, повтори жест и сделай селфи» (Manrope Medium/Bold 16); navbar right «Пропустить»; button-caption: icon-caption «Селфи увидят только твои пары» (hidden-light) + «Открыть камеру»; 2.1 — системный алерт камеры.
- 19: gear 40 + «Не можем открыть камеру» ExtraBold 18 #C66F53 + «Для верификации нужно селфи.⏎Разреши доступ к камере в настройках телефона» + Small «Настройки»; «Открыть камеру» disabled.
- 3 (селфи, чёрный): photo-frame Type=Helper (силуэт-подсказка) + «✌️» 64 + «Повтори жест» Manrope Bold 20 белый + «Твоя будущая пара увидит это фото, улыбнись :)» 14 #B3ACA9; кнопки close / shutter(photo) / camera-switch; 3.1 — «Проверь фото» (фото), кнопка done; 3.2 — loading; 3.3 Error — snack; 20 — «Камера не работает» внутри рамки.
- 4/4.1: иллюстрация (лупа над фото) + «Отправили фото на проверку» Bold 22 + «Обработка обычно занимает от 10 минут. Сообщим о результате в пуш-уведомлении»; кнопки «Включить уведомления» (secondary) + «Завершить» (main); 4.1 — системный алерт push.
- 3.6 Settings — скрин iOS-настроек приложения («ALLOW ПО ЛЮБВИ TO ACCESS», «ПО ЛЮБВИ SETTINGS»).
Figma — страница «Главная» (2265): лента, фильтры, подписка (notes_figma_main.md, 26016 байт)
# Figma — страница «Главная» (2265), прочитана полностью (7602 строк; 1375 уникальных)

Секции: «Анкеты» — Готово, «Фильтры» — Готово, «Подписка» — Готово, «Прототипы» (анимации). Ссылки-заметки: «JSON Анимации», «Видео-пример обучения», «Видео-пример анимации лайка / лайка в скролле / дизлайка / открытия кружочка / обучения», «Видео анимации мэтча», «Эффекты можно запускать параллельно». Тайминги карточки обучения: «Вправо 0–0.4 s, Ожидание 0.4–1.4, В центр 1.4–1.8, Ожидание 1.8–2.2, Влево 2.2–2.6, Ожидание 2.6–3.6, В центр 3.6–4.0, Ожидание 4.0–4.4 s».

## Экран «Анкеты» (таб «Главная», frame '1', '12', '1.1 full', '1.2/1.3 scroll')
- 390×844 белый; Status Bar 54; **navbar-custom** 390×68 @y54 (fixed): слева ⟨profile-return⟩ 56×56 r28 @20,4 (State=None — пусто/белый; State=Default — превью предыдущей анкеты (фото) с overlay 35% и иконкой ⟨24 / arrow-return heavy⟩ = кнопка «вернуть пропущенную анкету»); по центру counter-box 80×64 @155,0: vector-image 62×40 — два сердца (#E7C9B2 «Subtract» + #C66F53 «Vector»), поверх counter с маской — ⟨counter-reel⟩ 32×112 rot 15° (барабан цифр; число лайков «12», «7», «1», «11»; Position=1/2/3/changing-position = анимация прокрутки); текст «7» TT Runs Bold 16 белый rot 15° = число оставшихся лайков внутри сердца; State=Loading (counter-box loader), State=Default с Subtract #C66F53@50%; zero likes — «Heart zero» 42×34 белое сердце stroke #E7C9B2 w2 (без цифры); справа ⟨.44-button⟩ 44×44 @320,12 с иконкой (override «24 / Erase-filled → type / weight=heavy») — кнопка фильтров/настроек поиска.
- **profile-info** 366×1777(2009 full) @12,126 (V gap 32): **profile-face** 366×627 r28 smooth clip: фото IMG (STRETCH/FILL), (HIDDEN) gradient up 80px, ⟨gradient-bottom⟩ 390×160 @−12,467, ⟨pager⟩ 80×8 @24,24 (индикаторы фото gap 4; 2–5 точек), summary 326×98 @20,505 (V gap 12): distance (⟨16 / location-filled⟩ белый + «Москва»/«9 км»/«Красноярск»/«Красногорск»/«2 км» Manrope Medium 14 lh18 белый), name-box (H gap 8): имя TT Runs Bold 22 lh24 белый («Всеволод», «Фиалка-Лили Мария-Герда» maxLines 1 trunc, «Владислав», «Анатолий», «Ангелина», «Карина») + возраст TT Runs Medium 22 («37», «28», «31», «24», «30», «33») + Frame (H gap 4): check-white 24 (белый круг 12 + ⟨checkmark-filled⟩ #C66F53 — верифицирован; HIDDEN когда нет) + premium-icon 24 (логотип-вектор #E7C9B2 20×20 — есть подписка); match-data (H gap 16 центр): match-rate (⟨32 / match-filled⟩ #F2E7E3 + «56»/«72»/«78» TT Runs Medium 22 + «%» Medium 16 baseline) + tags-common (H gap 4, fill-primary): tag fill #252525@70% (+#9C9C9C) r12 pad 0 16 1 16 bgblur r50/100, текст Manrope Medium 12 белый: «Не планирую детей», «Йога» / «Не курю», «Футбол», «Кето» / «Хочу детей»; ⟨video-circle / Size=profile⟩ 96×96 @250,16 (справа вверху, stroke белый 3, shadow r32 30%; State=loading — stroke #B3ACA9 без фото; State=placeholder — stroke #B3ACA9 (видео не загрузилось)); ⟨loader Large⟩ 48 по центру фото при загрузке (0.4 Content Loading — фото чёрное); плейсхолдеры без фото: чёрный фон + ⟨figure-male⟩/⟨figure-female⟩ 108×192 @129,217 (0.51/0.52).
- Далее (gap 32): (в '1.1 full'/'1.3') ⟨button Big secondary⟩ 240×56 @63 «Отправить записку» с иконкой send-note-heavy и .counter=3 (осталось записок; state=disabled + counter 0 при «out of love notes | subscribe on»; default + counter 0 при «subscribe off» → ведёт на пейвол); job+description (pad 0 4, gap 16): должность Manrope Bold 16 lh20 CENTER #000 (в '1.1 full' — Bold 20 lh24; пример длинной должности «Заведующий сектором конкурсов  на поставки товаров» 2 строки) + био Manrope Medium 14 lh18 CENTER #514A47 (пример длинного «выполнение работ и оказание услуг для государственных нужд Управления экономического развития, финансов и конкурсов Департамента потребительского рынка и услуг города Москвы»); height «171 см» TT Runs Medium 22 lh24 CENTER #9E8075 (в '1.1 full' — Manrope Medium 14 lh18 #9E8075); values (wrap gap 8 центр) ⟨tag⟩ #F2E7E3@50% r12 pad 12 16: «Не планирую детей», «Много работаю», «Есть жильё», «Уважаю границы», «Православная», «Не курю», «Могу выпить»; interests «Мои увлечения» Manrope Bold 16 + ⟨picker-multiple Default⟩ r28 h40 pad 0 20 stroke #E1D6D1 текст #514A47: «Скейтбординг», «Йога», «Рисование», «Культурный туризм», «Походы», «Видеоигры», «Вегетарианство»; prompts (V gap 8) ⟨prompt on-profile⟩ 366×140 #F2E7E3@50% r16 pad 12 12 20 12: «Главный секрет счастливого брака?» / «не говорить со мной до того, как я выпью утренний кофе», «Как ты осознаёшь, что влюбляешься?» / «Начинаю копировать твои фирменные фразочки и слова ;)»; red-flags «🚩 Недопустимо в партнёре» (в '1' — ⟨picker-multiple Checked⟩ fill #514A47/#000; в '1.1 full'/scroll — dark-non-interactive #514A47): «Смотрит аниме», «пьет чай с сахаром», «не занимается спортом»; green-flags «💚 Обожаю в партнёре» (Default stroke #E1D6D1 / light-non-interactive stroke #F2E7E3): «инициативность», «умеет слушать», «карие глаза», «не курит»; ⟨button Big text⟩ «Пожаловаться» (#FA5F3D + иконка alert) внизу ('1.1 full').
- Плавающие кнопки (fixed, при скролле '1.3'/'1.1 full'): ⟨button-circle / Action=Dislike⟩ 80×80 @8,669 fill #000 stroke белый 3 outside (иконка outlined); ⟨button-circle / Action=Like⟩ 80×80 @302,669 fill #C66F53 (override State fill #000?). Disabled Like: fill #B3ACA9 ('6.2 zero likes control'). При скролле кнопки уменьшаются до 38–40 px ('5.2.1/5.3.1 … buttons': Like 38×38 @323,690, Dislike 40×40 @28,689) — в прототипе «like-scrolled-page» кнопка Like вырастает до 96 и схлопывается в точку, появляется like-overlay.
- Tab Bar iPhone 390×83 @0,761 (fixed; вкладка «Главная» выбрана, сердце filled).
- **0 (первый запуск, «Последний шаг и в бой!»)**: title Inter Bold 27 lh32 #C66F53 «Последний шаг и в бой!» + «Давай определимся с возрастом желаемого партнёра» Inter 14 #514A47; content-main 296×320 @47,299: «от» / «до» TT Runs Medium 30 #B3ACA9 + ⟨cupertino-picker-pair⟩ 296×228 (два барабана возраста); button-caption 390×106 @y704: ⟨icon-caption⟩ «Ищем пару от 25 до 45 лет» (иконка 16 search-light) + ⟨button Big main⟩ «Смотреть анкеты».
- **0.1 Geo**: «Где ты находишься?» TT Runs ExtraBold 30 lh32 CENTER + «Хотим найти людей поближе к тебе» Manrope 16 #514A47; location-pic 200×240 @95,377 (пин #C66F53 + #E7C9B2/#F2E7E3 тень); overlay 60% + системный iOS-алерт геолокации «Разрешить приложению “По Любви” использовать вашу геопозицию?» / «Точная геопозиция поможет нам найти пару поближе к тебе» / карта Precise: On / «Однократно» / «При использовании» / «Запретить». Второй '0.1 Geo' (отказ): иллюстрация (пин с «спящими» глазами) + «Не получили локацию» TT Runs ExtraBold 18 #C66F53 + «Разреши приложению доступ к геолокации в настройках телефона» + кнопки Small: «Настройки» (main, иконка settings-heavy) + «Выбрать город» (secondary).
- **0.2/0.3 Loading**: ⟨loader Large⟩ 48 по центру @171,415; counter-box State=Loading / Default(50%).
- **0.6 Error**: illustration (сломанный пин #F2E7E3/#E1D6D1) + «Не загрузилось :(» ExtraBold 18 #C66F53 + «Попробуй обновить страницу» + ⟨button Small main⟩ «Обновить» (135×44).
- **0.7 No connection**: та же иллюстрация + «Нет интернета» + «Проверь подключение к сети» (кнопка «Обновить» скрыта).
- **0.8 Loading (пусто)**: ⟨pic-no-people⟩ 200×240 + «Анкеты закончились» + «Измени параметры поиска или приходи завтра» (кнопка «Обновить» скрыта).
- **4 video exists / 4.1.1 loader / 4.1.2 fullscreen / 4.2 stopped / 4.3 loading / 4.4 failed** (видео-кружок): в карточке video-circle 96 (loading — stroke #B3ACA9; failed — placeholder); fullscreen: overlay #252525@55% bgblur r100 на карточке, ⟨video-circle / Size=Big⟩ 326×326 @20,150 (State=Progress с .player-circle-big Started / loading (чёрный) / Paused), ⟨24 / close⟩ @322,20 (белый крестик справа сверху), в прототипе ss-proto 44×44 (mute-heavy) появляется с dissolve.
- **9 videos / 9.1 paused / 9.2 sound on** («Обучающие видео» — контент из настроек): video 366×627 r28 с картинкой, ⟨.sound-switcher⟩ 44×44 белый r20 @314,575 (off/on), плашки текста «У ВСЕХ» «БЫЛО» «ТАК» Manrope Bold 20 ls1 #9E8075 на #F2E7E3 r12 @y507 (субтитры-стиль), ⟨.progress⟩ 326×3 @20,24 (сегменты; filling=third, filler #C66F53); paused: overlay 60% + gradient up + ⟨.button-play⟩ 112×112 по центру, progress 6px.
- **5 like / 5.1 like / 5.2 like (scrolled) / 5.2.1** (анимация лайка): карточка уменьшается (339×581 op80% → 356×610 op90%), поворачивается rot 3°/10.42°, следующая карточка снизу; ⟨like-overlay⟩ 137→511→843 px rot ~22–30° op90% (сердце #C66F53 с blur-эллипсами); counter-reel Position=changing-position (счётчик уменьшается).
- **5.2 dislike / 5.3 dislike / 5.3.1**: карточка улетает влево rot −4.75° → −27.45°, ⟨dislike-overlay⟩ 403→968 px op90%; profile-return уменьшается 33.6 op50%; counter-reel Position=1.
- **5.3 next profile**: новая карточка («Анатолий 31, Красноярск», без галочки, pager 2 точки), profile-return State=Default с фото предыдущего, counter «11».
- **5.4 push-custom** (после первого лайка): overlay 60% + bottom-sheet 390×304: ⟨pic-match⟩ 56 + «Не пропусти симпатию» Manrope Bold 20 + «Когда Всеволод поставит тебе лайк, узнай об этом сразу» (Bold 20 #514A47) + «Включить уведомления» (main) + «Пропустить» (secondary). **5.5 push-system**: ⟨permission-ios / type=push⟩ системный алерт.
- **6 zero likes / 6.1 tip / 6.2 control**: counter-box «Heart zero» (белое сердце stroke #E7C9B2), Like-кнопка Disabled #B3ACA9, tooltip ⟨Tooltip / top-middle Dark⟩ 124×32 @133.5,118 fill #C66F53 «Подожди 1 ч 36 м» (таймер до следующих лайков).
- **7 checking photos**: карточка с фото на модерации — profile-face с двумя изображениями (image rot180 + photo) и summary «Владислав 28, 9 км, 72%, теги Не курю/Футбол/Кето», без галочки, premium-icon; profile-return с фото.
- **11 match** (мэтч): экран fill #C66F53, background-vector: три размытых эллипса (#E7C9B2 466, #F2E7E3 266, #FFD3B2 335, blur r243) + огромное сердце «Heart zero» stroke #F2E7E3@50% w3 rot 10; pic-frames 322×207 @34,272: два ⟨shape-brand⟩ 160×182 (form=4 rot −10° с фото пользователя, form=3 rot 10° с фото пары; в анимации влетают с ±30° с белой обводкой 4); bottom-content 366×140 @12,662 (V gap 24): «Это по любви!» TT Runs ExtraBold 30 lh32 белый + «Вы и Всеволод нравитесь друг другу» (ExtraBold 30 в одном варианте / Manrope Medium 16 в прототипе) + ⟨button Big main⟩ «Открыть чат» (fill #000, с иконкой heavy); navbar с кнопкой закрыть (prop:bool=True второй) — статус-бар Dark.
- **8 Sending a note** (записка): экран с карточкой, снизу фикс «button-сell» 390×260/300 r28/28 pad 24 12 8 12: «Отправь записку» Manrope Bold 20 + «Всеволод увидит твоё сообщение до взаимной симпатии» (Bold 20 / Medium 14) + ⟨input--textarea⟩ 366×56→96 stroke #C66F53 label «Текст записки» hint «Сделай комплимент» counter «0 / 200» → «128 / 200» (пример текста «Я лучше проведу одно мгновение, обнимая тебя, чем всю жизнь, зная, что никогда не смогу этого сделать») + ⟨button Big main⟩ «Отправить» (disabled #F2E7E3@50% → default #000 → state=load); клавиатура iOS; после отправки — карточка улетает как лайк + snack «Отправили твою записку» (чёрная). Вариант с тремя круглыми кнопками в ряд (H gap 24 @y363): Like outlined, ⟨40 / lovenote⟩, Like — (старый вариант панели действий: дизлайк / записка / лайк).
- **10 report / 10.1 sheet / 10.2 loader / 10.3 success / 10.4 error**: bottom-sheet 390×320: «На что хочешь пожаловаться?» Manrope Bold 20 + кнопки secondary: «Нецензурная лексика», «Откровенные фотографии», «Неприемлемое поведение», «Не тот, за кого себя выдаёт», (HIDDEN) «Другое»; loader: первая кнопка state=load, остальные disabled (stroke #F2E7E3); success: карточка уменьшена op60% (улетает) + чёрная snack «Получили твоё обращение» (статус-бар #514A47); error: snack #FA5F3D «Не отправилось, попробуй ещё раз».
- **2.a–2.d / 2.4–2.5 anim education / 3 scroll education / 3.1 / 3.2 tooltip verification / 3.a–3.c** (обучение): overlay 60% на карточке; «guide»: две изогнутые стрелки (белые векторы) влево/вправо + сердце по центру + ⟨40 / heart outlined⟩ 64×64 @298,325; карточка качается rot ±3° (анимация «свайпни вправо — лайк, влево — дизлайк»; Frame 1321315618: кружок-указатель 64×60 stroke белый + сердце #E7C9B2/#C66F53@46% + крест stroke #F2E7E3 w14 rot −15); scroll education: pointer 72×72 (кружок 64×60 stroke белый 3 + точка 4) по центру карточки — «проскролль анкету вниз» (карточка сдвигается вверх на 88 px в 3.c); 3.2: tooltip ⟨bottom-middle Dark⟩ 128×32 @162.5,613 #C66F53 «Надёжная анкета» под галочкой верификации.
- **banned user** ×2 + **banned user reporting**: экран fill #000: ⟨pic-attention⟩ 56 + «Твой аккаунт заблокирован» TT Runs ExtraBold 30 белый + «Твоя анкета или поведение нарушили правила сообщества. Если произошла ошибка, пиши в поддержку» Manrope 16 белый; buttons @12,682: «Отменить подписку» (белая main; скрыта если нет подписки) + «Написать в поддержку» (secondary stroke белый, текст белый, иконка heavy); подпись «Прочитай ещё раз правила сообщества, прежде чем обращаться в службу поддержки» Manrope 12 #B3ACA9 (со 2-й части белым — ссылка «правила сообщества»); navbar right text «Выйти» #C66F53; reporting — bottom-sheet: ⟨input--textarea⟩ «Текст обращения» hint «Опиши ситуацию» «0 / 1000» + ⟨input--line⟩ «Почта для связи» hint «yourem@il.com» + «Отправить» disabled.

## Фильтры («filters 1…5», navbar title «Настройки поиска», right «Готово» #9E8075 / #E1D6D1 disabled)
- Список Filter-cell 390 stroke-bottom #F2E7E3@50% pad 24 16:
  1. «Город» Manrope Bold 20 + значение «Автоматически» (Bold 20 #B3ACA9 / Medium 16 #B3ACA9) или «Петропавловск-Камчатский» (Medium 16 #B3ACA9 maxLines 1 trunc) + иконки справа ⟨24 / geolocation heavy⟩ и ⟨24 / edit heavy⟩ (gap 16).
  2. «Радиус поиска» + значение «50 км» / «81 км» / «Любой» Manrope Medium 16 #514A47; slider 358×44: линия 334×3, сегменты (.slider-segment #F2E7E3 фон + #514A47 активная часть), ⟨.manipulator⟩ 40×40 #514A47, точки increment 3×3 по краям; ниже ⟨switcher-cell no-border⟩ «Можно немного расширить» (Active / Disabled при «Любой»).
  3. «Возраст» + «25–45 лет» / «18–99 лет» + ⟨cupertino-picker-pair⟩ 366×140 (два барабана) + switcher «Можно немного расширить» (Disabled при 18–99).
  4. «Рост» + «Не важен» / «От 160 см» / «160–180 см» + cupertino-picker-pair (value-type=empty «Добавить» / cm «160 см»).
- Состояния: 4.1 loader (⟨loader Large⟩ по центру), 4.2 error (иллюстрация «Не загрузилось :(» + «Обновить»), 3 | loader (сохранение: loader поверх), 3.1 | error — snack #FA5F3D «Не сохранилось, попробуй ещё раз»; 2/2.1 — проскроллено.

## Подписка / пейвол («premium 1/2/1.1/1.2/1.3», «from likes/rewind/lovenotes», «payment native/webview/error/pending/success», «restore purchases (iOS)»)
- navbar title «Подписка», right «Готово» (disabled #E1D6D1), left закрыть.
- Заголовок @16,122 (V gap 12): «Узнай, кому понравилась твоя анкета» (Inter Bold 27 lh32 #C66F53 в premium 1 / TT Runs ExtraBold 30 lh32 #C66F53 в premium 2/1.1) + «Оформи подписку «Любовь без границ» — увидишь, кто поставил тебе лайк» (Inter 14 / Manrope Medium 16 #514A47). Варианты входа: «from likes» — «Открой больше сердечек» / «Оформи подписку «Любовь без границ» и получай +4 сердечка каждый день»; «from rewind» — «Не поздно передумать» / «Оформи подписку «Любовь без границ» и вовзращай (опечатка) пропущенные анкеты без ограничений»; «from lovenotes» — «В три раза больше записок» / «Оформи подписку «Любовь без границ» и отправляй 6 записок в день вместо 3».
- offers 366×140 (H gap 8): offer 1 179×140 stroke #E1D6D1 r16 pad 20: «1 месяц» TT Runs ExtraBold 16 + «299 ₽» (Inter 26 / TT Runs Medium 22) + «299 ₽ / мес.» Manrope 12 #B3ACA9; offer 2 (выбран: stroke #C66F53 w2, title #C66F53): «3 месяца» + «699 ₽» + зачёркнутое «899 ₽» (Manrope 14 #514A47 + линия #C66F53) + «233 ₽ / мес.» + бейдж «-20%» 47×24 #C66F53 (или #000) r8 absolute справа сверху (@120,−12).
- «Что в подписке» (Manrope Medium 16 / Bold 20) + tags wrap (fill #F2E7E3@50% r28, текст #9E8075): ⟨match-light⟩ «+4 лайка в день», ⟨hearth light⟩ «Кому понравилась анкета», ⟨arrow-return light⟩ «Бесконечные возвраты», (HIDDEN) ⟨filters⟩ «Премиум-фильтры», ⟨lovenote-light⟩ «+2 записки в день» (в других вариантах «+4 лайка», «+3 записки»).
- terms @16,742: текст iOS «При оформлении подписки платеж будет списан с Вашего ITunes аккаунта … доступны в Настройках ITunes аккаунта.» (Manrope 16 / 12 #514A47) или Android/СБП «При оформлении подписки платеж будет списан с вашего банковского счета … Управление подпиской, включая ее отмену, можно выполнить в настройках аккаунта "По любви".» (Manrope 12); кнопки Small secondary: «Условия» (иконка rules-heavy) + «Приватность» (иконка light).
- bottom fixed 390×170 stroke-top #F2E7E3@50% pad 8 12: ⟨icon-caption⟩ «Списываем деньги раз в 3 месяца» / «раз в месяц» (иконка Light) + ⟨button Big main⟩ #1B1A1C «Купить за 699 ₽» / «Купить за 299 ₽» + ⟨button text⟩ «Восстановить покупки».
- 1.2 loading: overlay 60% + loader; 1.3 error: (аналог). restore purchases (iOS): overlay + loader поверх пейвола.
- payment native: iOS App Store sheet (overlay 80%, «Recurring Payment Overview» #EBEBEB: App Store, «Dating App» / «По любви», «RUR 699 per 3 month», «Cancel at any time in Settings > Apple ID…», Confirm with Side Button, Double Click to Pay). payment webview: ⟨webview Navbar⟩ «Оплата» + картинка формы оплаты + ⟨webview Tabbar⟩ (СБП/банк).
- payment-error: bottom-sheet ⟨pic-attention⟩ + «Оплата не прошла» + «Деньги не списались. Попробуй оплатить подписку ещё раз» + «Оплатить заново 699 ₽» (main) + «Отмена» (secondary).
- payment-pending: sheet ⟨pic-time⟩ + «Оформляем покупку» + «Подписка активируется в течение часа, либо мы вернём деньги» + «Готово»; второй вариант (СБП): «Не забудь при оплате по СБП» + «Привязать к СБП счёт в приложении банка для автоматического списания средств».
- payment-success: экран fill #000 с blur-эллипсами #C66F53 (r300/r280); ⟨premium-icon⟩ 56 + «Любовь без границ на 3 месяца» TT Runs ExtraBold 30 белый + «Отменить можно в «Профиле»» Manrope 14/16 белый; «Доступно» Manrope Bold 20 белый + tags fill #000@20% bgblur r50 (белый текст): «+4 лайка в день», «Кому понравилась анкета», «Бесконечные возвраты», «+2 записки в день»; внизу bottom-text: «Активна» Manrope Bold 20 #C66F53 + «699 ₽ спишутся 9 Сентября» Manrope 14 белый (вариант СБП: «Подключите списание в СБП для автоматического продления подписки»); (HIDDEN) белая кнопка «Смотреть анкеты»; navbar title «Подписка» (#000?) статус-бар Dark.
Figma — страница «Профиль» (2426) (notes_figma_profile.md, 24114 байт)
# Figma — страница «Профиль» (2426), прочитана полностью (4038 строк дампа)

Секции-заголовки (Akzidenz-Grotesk 56 + виджет статуса): «Основной» — Готово, «Анкета» — Готово, «Модерация фото» — Готово, «Настройки» — Готово. Пометка «iOS only» (rot 90) у скриншота iOS Subscription settings.

## Таб «Профиль» — главный экран (frame «non-verified», «verified», «subscription ON», «subscription not-prolonged iOS/Android» ×4, «deleting 1–3», «switcher loader | to off/on», «email-exists», «report», «snackbar», «email»)
- 390×844, scroll vertical, фон #FFFFFF, Status Bar iPhone 54, Tab Bar iPhone 390×83 внизу (fixed), вкладка «Профиль» выбрана.
- **top-row** 390×164 @y54 (H, pad 16 0 20 0, align MIN/CENTER): 
  - слева wrapper 131×66 → круг «settings» 68×66 stroke #E1D6D1 w1.33 inside, r10000, иконка ⟨40 / gear⟩ по центру;
  - центр ⟨video-circle / State=Default, Size=Small⟩ 128×128 (фото пользователя в круге, белый фон; иконка видео-кружка);
  - справа wrapper 131×66 → круг «profile» 68×66 stroke #E1D6D1 с ⟨40 / profile-edit⟩ + бейдж «field-name» 40×16 r8 белый absolute снизу по центру (@14,54) с текстом «63%» Manrope Medium 12 lh16 ls0.2 #9E8075 (maxLines 1, trunc) — процент заполненности анкеты.
- **name+age** 358×52 @16,218 (V gap 8): name-box (H gap 8) «Александра Иванова» TT Runs Trial Variable ExtraBold 18 lh22 CENTER #000000 (+ в verified-варианте ⟨24 / checkmark-filled⟩ #C66F53 справа от имени, name-box 282×24) ; «32 года» Manrope Medium 16 lh20 ls0.2 CENTER #514A47.
- **switcher-text-icon** 208×52 @91,290 (по центру, stroke белый 3 outside, r10000): State=On fill #C66F53 pad 0 4 0 20 текст «Анкету видно всем» / State=Off fill #F2E7E3 pad 0 20 0 4 текст «Видят только пары»; вариант loader (.hande-big-switcher swap state=loader) во фреймах «switcher loader | to off» (On + loader) и «| to on» (Off + loader). Переключатель видимости анкеты.
- **blocks** 366×636/620 @12,366 (V gap 8):
  - **verify** 366×328 stroke #E1D6D1 w1.33 inside r20 smooth pad 24 20 32 20 gap 8 (только non-verified): ⟨pic-checkmark⟩ 56 (в одном варианте — Star #C66F53 r4 + галка stroke #F2E7E3 w4), «Получи галочку →» Manrope Bold 20 lh24 CENTER #000, «Подтверди личность — сделай селфи» Manrope Medium 14 lh18 ls0.1 CENTER #B3ACA9, чипы wrap gap 8 pad 8 0 8 0 по центру: tag fill #F2E7E3@50% r28 pad 0 20 0 16 gap 8 (иконка 24 + текст Manrope Medium 16 #9E8075): ⟨24 / time light⟩ «Бесплатно и быстро», ⟨24 / match-light⟩ «+2 лайка», ⟨24 / revealed-light⟩ «+60% охват», ⟨24 / checkmark⟩ «Больше симпатий».
  - **premium-offer** 366×300 fill #000000 r20 smooth pad 24 8 8 8 gap 20 (нет подписки): фон background-vectors — три размытых (blur r120) эллипса #C66F53 (121, 99, 209 px) ; text (pad 0 12): ⟨premium-icon⟩ 56, «Любовь без границ» TT Runs ExtraBold 30 lh32 CENTER белый, «Узнай, кто поставл лайк твоей анкете» (опечатка «поставл» в макете) Manrope Bold 20 lh24 CENTER белый; кнопка ⟨button Big main⟩ 350×56 белая с чёрным текстом «Выбрать тариф». Вариант «Frame 1321315576» 366×284 — фон-картинка (IMG) + логотип-вектор 40 белый вместо premium-icon.
  - **subscription-active** 366×428 (иOS/ON) или 366×496 (Android not-prolonged: + Small-кнопка) fill #000000 r20 pad 24 8 32 8 gap 24: ⟨premium-icon⟩ 56; «Подписка на 3 месяца» TT Runs ExtraBold 30 lh32 белый; tags wrap gap 8 pad 4 0 4 0 по центру: tag stroke #514A47 w1.33 r28 pad 0 20 0 16 (иконка 24 + Manrope Medium 16 белый): ⟨24 / hearth light⟩ «Кому понравилась анкета», ⟨24 / match-light⟩ «+4 лайка», ⟨24 / lovenote-light⟩ «3 записки», ⟨24 / arrow-return light⟩ «Бесконечные возвраты», (HIDDEN) ⟨24 / filters light⟩ «Премиум-фильтры»; bottom-text (V gap 8): «Активна» Manrope Bold 14 lh18 #C66F53 (в not-prolonged — Bold 20 lh24 #C66F53) + «299 ₽ спишутся 9 Сентября» Manrope Medium 16 белый (subscription ON) / «Закончится 9 Сентября» Manrope Medium 14 lh18 белый (not-prolonged); в Android-варианте not-prolonged — ⟨button Small main⟩ 191×44 белая «Включить продление» @y420. Снэки поверх: «Подписка оформлена с другого девайса» (snack #FA5F3D, статус-бар #FA5F3D) и «Включили продление подписки» (чёрная snack, статус-бар #514A47/#000).
- **content** 366×560/572 @12,698 (V gap 32):
  - **mail** (V gap 16): «Добавь почту» Manrope Bold 20 lh24 CENTER #000 + подзаголовок Manrope Medium 14 lh18 ls0.1 CENTER #514A47: «Так мы сможем связаться с тобой, если возникнут проблемы с приложением или аккаунтом» (non-verified/switcher-loader) или «Не придётся указывать имейл каждый раз, чтобы отправить жалобу» (остальные); ячейка 366×56 «photo-add-cell» stroke #E1D6D1 r12 или «Tooltip» 366×68 stroke #E1D6D1 r16 (в iOS-варианте r12) pad 8 12 8 12 — внутри ⟨32 / email⟩ #C66F53 по центру.
  - **email-exists** вариант: блок «Электронная почта» Manrope Bold 20 #514A47 + иконка edit + значение «ivanova@surf.dev».
  - **links-cells** 366×300 (V): link-cell 366×60 stroke #F2E7E3@50% w1 inside (кроме последней) pad 0 4 0 4 gap 16: иконка 24 light + текст Manrope Medium 16 lh20 ls0.2 LEFT #000 (fill-primary): ⟨faq⟩ «Часто спрашивают», ⟨support⟩ «Связаться с нами», ⟨rules⟩ «Правила сообщества», ⟨lock⟩ «Политика конфиденциальности», ⟨trash⟩ «Удалить профиль» (текст #FA5F3D, без нижней обводки).
  - **button secondary** 366×56 stroke #E1D6D1 w1.33 r12 «Выйти».
- Порядок сверху вниз на не-верифицированном без подписки: top-row → имя/возраст → switcher → verify-card → premium-offer → mail → links → Выйти. На verified без подписки: verify-card отсутствует. На подписке: вместо premium-offer — subscription-active.

## Bottom-sheets на табе «Профиль»
- **report / contact-us («Связаться с нами»)**: bottom-sheet r28 pad 24 12 8 12: ⟨input--textarea⟩ «Текст обращения» hint «Проблема или предложение» + счётчик «0 / 1000»; ⟨input--line⟩ «Почта для связи» hint «yourem@il.com»; кнопка «Отправить» (disabled → active). Ответ: чёрная snack «Получили твоё обращение».
- **email («Добавь почту»)**: sheet: ⟨input--line⟩ «Электронная почта» + checkbox-cell «Хочу получать персональные предложения и новости» + кнопка «Сохранить» (disabled до валидного ввода).
- **deleting** (после «Удалить профиль»): overlay #000@60%, bottom-sheet 390×344 r28/28/0/0 pad 24 12 8 12 gap 8, handle сверху: «Почему хочешь удалить профиль?» Manrope Bold 20 lh24 CENTER; кнопки secondary 366×56: «Я нашел пару», «Не нравится приложение», «Уже неактуально», «Другое».
- **deleting 2**: iOS-диалог ⟨dialog--iOS / 2 Actions Horizontal⟩ 270×136.5 #F2F2F2@80% blur 22: «Удалить профиль?» / «Твои данные пропадут навсегда и мы не сможем их восстановить» / «Отмена» + «Удалить» (#007AFF). Switcher в состоянии Off «Видят только пары» (фон).
- **deleting 3** (таймер удаления): overlay fill #C66F53 (overlay master #000) на весь экран, статус-бар Dark, по центру timer 219×80: «10» TT Runs Trial Variable Medium 30 lh32 CENTER белый + «секуд до удаления аккаунта» (опечатка «секуд» в макете) Manrope Medium 16 lh20 CENTER белый; внизу @12,746 кнопка ⟨button Big main⟩ белая с чёрным текстом «Отменить удаление».

## Настройки (navbar «Настройки» + right «Готово»)
- Секция «Уведомления» Manrope Bold 20 + switcher-cell 366×60 (иконка? нет; текст + ⟨.swicher⟩ справа): «Новые пары», «Новые сообщения», «Рекламные предложения», «Напоминания об активности», «Приглашение друзей».
- Секция «Контент»: «Обучающие видео» (switcher).
- Секция «Подписка»: статус «Активна» / «Оформить» (#C66F53) / «До 6 Сентября»; строка «Продлевать автоматически» (switcher Active/Disabled).
- push-denied: «Уведомления … Выключены» + инструкция «Включи уведомления в Настройках телефона и узнавай о новых симпатиях» + Small-кнопка «Настройки».
- cancelling sheet: ⟨pic-attention⟩ + «Отключить продление подписки?» + «Платные функции перестанут работать 9 Сентября. Деньги списываться не будут» + «Оставить продление» (main) + «Выключить продление» (secondary).
- re-prolonged: snack «Включили продление подписки»; loader overlay; скриншот «iOS Subscription settings» (iOS only).

## Редактирование анкеты («editing-profile» 390×3856; «photo-moderation»; «informer»; «name editing»; «editing-question»)
- navbar title «80% заполнено» / «76% заполнено» (#B3ACA9), right «Показать» ; фикс внизу кнопка secondary «Показать анкету» (с иконкой).
- Верх: ⟨video-circle / Size=Medium⟩ 208; подпись «Зажми фото, чтобы перетащить» #B3ACA9; photo-grid 366×264 (5 фото + ячейка «добавить»; tooltips «+ 4%»).
- Секции 390×102 stroke #F2E7E3@50% pad 24 16 24 16: title Manrope Bold 20 + ⟨edit heavy 24⟩ + значение Manrope 14/16 #514A47: «Имя и фамилия», «Должность», «О себе» (длинное био), «Рост» («169 см»), «Интересы» (иконка plus; «Добавь ответ + 5%» #9E8075 или список чипов), «Флаги» («Добавь ответ + 5%» / «🚩 Недопустимо» тёмные чипы #514A47, «💚 Обожаю» светлые чипы stroke #F2E7E3), «Открывашки» (⟨prompt / on-profile⟩ карточки #F2E7E3@50% r16 + tooltip «Добавь открывашку + 2%»; preview-вариант — ⟨prompt / in-profile⟩ fill #C66F53; empty — plus + tooltip; loading — 3 ⟨Skeleton⟩ prompt; error — tooltip stroke #FA5F3D + иконка refresh + «Попробовать снова»).
- Далее блоки «answer» по категориям анкетирования: title Manrope Bold 20 + edit; tag #F2E7E3@50% r12 pad 12 16: «Хочу детей», «Много работаю», «Нет жилья», «Люблю контроль», «Православный», «Не курю», «Не пью», «Нет детей»; optional-question-cell: ⟨hidden-light 24⟩ + вопрос Manrope Bold 16 #514A47 + ответ «→ «Не важно»» / «→ «Нет»» / «→ «Обращаю внимание»» / «→ «Это недопустимо»» / «→ «Не хочу отвечать»» или «+ Дополнить» (#C66F53).
- **photo-moderation**: подпись «Новые фото проходят проверку», фото State8 «Проверяем…».
- **informer** (попытка удалить единственное фото): bottom-sheet ⟨pic-attention⟩ + «Нельзя удалить фото» + «1 фото — минимум. Загрузи новую фотографию, чтобы удалить старую» + кнопка «Понятно».
- **name editing** 390×844: content @12,114 (V gap 24): «Как тебя зовут?» TT Runs ExtraBold 30 lh32 CENTER #000; inputs (V gap 12): ⟨input--line / Inactive-filled⟩ 366×56 stroke #E1D6D1 r12 pad 18 20 18 20 label «Имя» value «Александра»; ⟨input--line⟩ label «Фамилия» value «Иванова» + caption «Увидят только твои пары» + иконка ⟨16 / hidden-light⟩; navbar (back-chevron, без прогресса); button-stack 390×72 @y738 stroke-top #F2E7E3@50% pad 8 12 8 12: ⟨button Big main⟩ fill #1B1A1C «Сохранить ответ» (скрытый switcher-cell над кнопкой).
- **editing-question** («Курение»): content @16,114 (V gap 32): «Курение» TT Runs Bold 22 lh24 CENTER #C66F53 + «Отметь свои привычки и отношение к курению потенциального партнёра» Manrope Medium 16 #514A47; question-cell «Ты куришь?» Manrope Bold 20 + radio-group H gap 8: ⟨picker-single⟩ 120×44 «Нет» (checked #C66F53), «Бывает», «Курю»; question-cell «Как ты отнесёшься к курению партнёра?» + radio-group V gap 8 ⟨picker-single⟩ 358×44: «Это недопустимо» (checked), «Иногда — окей», «Спокойно»; (HIDDEN) «Сколько всего ты хочешь детей?» Manrope Bold 16 #C66F53: «Одного», «Два или больше», «Ещё не знаю»; navbar без прогресса; скрытый tooltip «Доступно 60% анкет»; button-stack 390×140 @y670: ⟨switcher-cell / no-border⟩ 366×60 (swicher Active; смысл — «скрыть ответ»/показывать?) + ⟨button Big main⟩ #1B1A1C «Сохранить ответ».

## Правила сообщества («Rules», navbar title «Правила сообщества»)
- Content @16,122 (V gap 24), блоки (V gap 12): «Зачем эти правила?» Manrope Bold 16 lh20 #000 + текст Manrope Bold 16 lh20 #514A47: «Добро пожаловать в приложение «По Любви»!⏎Наша цель — создать безопасное и уважительное пространство, где люди могут найти настоящую любовь и построить прочные отношения. Чтобы обеспечить позитивный опыт для всех пользователей, мы разработали следующие правила сообщества.⏎Соблюдение этих правил поможет нам вместе создать атмосферу доверия, уважения и искренности.»; далее заголовки Manrope Bold 20 lh24 + текст Manrope Medium 14 lh18 #514A47: «Уважение» — «Относитесь ко всем участникам с уважением, независимо от их пола, возраста, национальности или убеждений.»; «Честность» — «Предоставляйте правдивую информацию о себе в профиле и общении.»; «Безопасность» — «Не делитесь личной контактной информацией в открытом доступе. Используйте встроенные функции приложения для общения.»; «Конфиденциальность» — «важайте частную жизнь других пользователей. Не распространяйте личную информацию или сообщения без разрешения.» (в макете начинается с «важайте» — обрезано «У»); «Контент» — тот же текст-заглушка (#000).

## FAQ («FAQ», navbar title «Часто спрашивают»)
- Content @16,122 (V gap 24), блоки: вопрос Manrope Bold 20 lh24 #000 + ответ Manrope Medium 14 lh18 #514A47:
  1. «Как работает система подбора партнеров?» — «Алгоритм учитывает ваши ценности, предпочтения, интересы и цели, указанные в анкете. Сначала показываем вам пользователей с максимальным уровнем совместимости. Расстояние до потенциального партнёра также влияет на результаты выдачи.»
  2. «Могу ли я скрыть свой профиль от определенных пользователей?» — «Ты можешь полностью скрыть анкету из поиска, но мэтчи смогут просматривать твой профиль и писать сообщения. Удали пару, чтобы ограничить пользователю доступ к своей анкете.»
  3. «Как я могу пожаловаться на неподобающее поведение?» — «В профиле каждого пользователя есть кнопка "Пожаловаться". Мы рассматриваем все жалобы и принимаем соответствующие меры.»
  4. «Можно ли удалить свой аккаунт?» — «Да, в настройках профиля есть опция полного удаления аккаунта. Все ваши данные будут безвозвратно удалены из нашей системы.»
  5. «Как часто обновляются предложения потенциальных партнеров?» — «Список обновляется ежедневно, учитывая новых пользователей и изменения в профилях существующих.»
  6. «Контент» — заглушка (копия из правил).

## Превью своей анкеты («profile-preview» ×4: обычный, проскроллен, «Проверяем эту фотографию» snack, с пейджером)
- navbar: title «80% заполнено» (#B3ACA9) → при скролле «Моя анкета»; right text-button «Закрыть» (#E1D6D1 — видимо, «Закрыть» неактивная/светлая).
- profile-info 366×1921 @12,114 (V gap 32): profile-face 366×627 r28 smooth clip: фото (IMG FILL), (HIDDEN) gradient up, ⟨gradient-bottom⟩ 390×160 @−12,467, ⟨pager⟩ 66×8 @24,24 (индикаторы фото #B3ACA9 / active), summary @20,505 (V gap 12): distance (⟨16 / location-filled⟩ белый + «2 км» Manrope Medium 14 белый), name-box (H gap 8): «Александра» TT Runs Bold 22 lh24 белый + «27» TT Runs Medium 22 белый + check-white 24 (ellipse белый 12 + checkmark-filled #C66F53); match-data (H gap 16): match-rate (⟨32 / match-filled⟩ #F2E7E3 + «78» TT Runs Medium 22 + «%» Medium 16, baseline), tags-common (H gap 4): tag fill #252525@70% bgblur r50 r12 pad 0 16 1 16: «Не планирую детей», «Йога» (Manrope Medium 12 белый); ⟨video-circle / Size=profile⟩ 96×96 @250,16 (справа сверху, stroke белый 3, shadow r32 #1B1A1C@30%); snack на фото 366×56 @0,286 fill #000@35% bgblur 50: ⟨24 / time⟩ + «Проверяем эту фотографию» Manrope Medium 16 белый; (HIDDEN) «Проверяем фото… » Manrope 12 белый @20,40.
- (HIDDEN) кнопка «Отправить записку» 240×56 secondary с иконкой send-note-heavy и counter 3 — на своём превью не показывается.
- job+description (pad 0 4, V gap 16): «Дизайнер интерьеров» Manrope Bold 16 lh20 CENTER #000; био Manrope Medium 14 lh18 CENTER #514A47: «Я творческий и увлечённый человек, работаю дизайнером интерьеров. Обожаю своё дело — создавать уютные и стильные пространства, наполненные жизнью и красотой.»
- height: «171 см» TT Runs Medium 22 lh24 CENTER #9E8075.
- values (wrap gap 8 центр): ⟨tag⟩ #F2E7E3@50% r12 pad 12 16: «Не планирую детей», «Много работаю», «Есть жильё», «Уважаю границы», «Православная», «Не курю», «Могу выпить».
- interests: «Мои увлечения» Manrope Bold 16 CENTER + ⟨picker-multiple / Default⟩ (stroke #E1D6D1 r28 h40 pad 0 20, текст #514A47): «Скейтбординг», «Йога», «Рисование», «Культурный туризм», «Походы», «Видеоигры», «Вегетарианство».
- prompts (V gap 8): ⟨prompt / on-profile⟩ 366×140 #F2E7E3@50% r16 pad 12 12 20 12 gap 12: «Главный секрет счастливого брака?» / «не говорить со мной до того, как я выпью утренний кофе»; «Как ты осознаёшь, что влюбляешься?» / «Начинаю копировать твои фирменные фразочки и слова ;)».
- red-flags: «🚩 Недопустимо в партнёре» Manrope Bold 16 + ⟨picker-multiple / dark-non-interactive⟩ fill #514A47 r28: «Смотрит аниме», «пьет чай с сахаром», «не занимается спортом».
- green-flags: «💚 Обожаю в партнёре» + ⟨picker-multiple / light-non-interactive⟩ stroke #F2E7E3: «инициативность», «умеет слушать», «карие глаза», «не курит».
- Внизу ⟨button Big text⟩ «Пожаловаться» (#FA5F3D, иконка alert) — на своём превью присутствует как в общем компоненте карточки (в макете не скрыт!).
- Порядок секций карточки анкеты: фото+summary → должность+био → рост → values-теги → увлечения → открывашки → 🚩 → 💚 → Пожаловаться.
Figma — страницы «Симпатии» (7761) и «Чаты» (7047) (notes_figma_sympathy_chats.md, 13450 байт)
# Figma — страницы «Симпатии» (7761:36382) и «Чаты» (7047:32641), прочитано полностью

## Симпатии (секции «Кому понравилась анкета» — Готово, «Скролл лайков» — Готово)
- Экран-таб «Симпатии»: title «Симпатии» TT Runs ExtraBold 30/32 #C66F53 по центру @y78 + «Узнай, кому понравилась твоя анкета» Manrope 16 #514A47 (maxLines 1). Секция «Записки» (TT Runs Bold 22, по центру) + счётчик «27» (TT Runs Medium 22, #000 / #B3ACA9); notes-block 390×276: две повёрнутые подложки (#F2E7E3@50% rot 8°, #F2E7E3 rot −5°, r28) + карточка ⟨lovenote⟩ 366×228 r28 smooth stroke #E1D6D1 pad 20 16 24 16 с .counter (=2) — клик → «lovenotes screen». Секция «Поставили лайк» (TT Runs Bold 22 по центру @y502) → колонка like-sender 366×416.59 (brand shapes 1…5 по кругу; повёрнуты 10°/5°/−10° при скролле; name-label слева/справа попеременно: «Константин 32», «Миша», «Иван 28», «Дима 31»; shape6_twolines для длинных имён «Фиалка-Лили Мария-Герда»); gap 48. Tab Bar iPhone 83 внизу.
- Плейсхолдеры: серые #C4C4C4 формы + photo-placeholder-big; lovenote без фото — photo-placeholder.
- Skeleton: lovenote Skeleton (stroke #F2E7E3), like-sender серый, счётчики скрыты.
- Empty (нет лайков): сердечки #E7C9B2 + «Симпатий нет» ExtraBold 18 #C66F53 + «Здесь появятся все, кому понравилась твоя анкета» (+ скрытая Small-кнопка «Смотреть анкеты» с иконкой match-light); блок записок пустой: «Записки» + «Когда кто-нибудь напишет тебе записку, мы пришлём уведомление» + ⟨pic-no-lovenotes⟩ 200×200. Вариант empty 2: subtitle «Здесь появятся все, кому понравилась твоя анкета» под заголовком.
- Error: иллюстрация + «Не загрузилось :(» + «Попробуй обновить страницу» + Small «Обновить».
- Скролл «Поставили лайк»: заголовок секции уезжает под navbar; navbar title «Поставили лайк», иконка up-chevron (наверх); loader-состояние (серый like-sender).
- «lovenotes screen» (список записок): navbar title «Записки» + right «Закрыть»; overlay #FFFFFF@90%; стек lovenote 366×228 gap 8: (фото, «Иван»), («Александр», «Твоя улыбка сделала мой день, спасибо)))», «57% подходит те…» / «Сегодня в 12:11»), («Всеволод», «Кажется, мы знакомы. Может быть мы учились вместе? Помнишь Марию Степановну??», «8 Августа»); клик → profile-preview. Состояния: deeplink loading (skeleton карточек), pagination (skeleton в конце), pagination error — snack «Ранние записки не загрузились» (чёрная snack #000000, type=positive).
- «profile-preview» (просмотр анкеты из симпатий): navbar title «Иван Забелин», иконка menu; profile-face 366×627 r28: фото, pager (66×8 @24,24), gradient-bottom, summary @y505: distance (location-filled 16 + «Москва» Manrope 14 белый), name-box («Иван» TT Runs Bold 22 + «29» Medium 22 белые + check-white 24 верифицирован), match-data: match-rate (32 match-filled #F2E7E3 + «78» Medium 22 + «%» 16) + tags-common (чипы #252525@70% blur r12: «Не планирую детей», «Йога»); ниже job+description («Дизайнер интерьеров» Manrope Bold 16 + bio Manrope 14 #514A47 по центру), height «171 см» TT Runs Medium 22 #9E8075, values-теги ⟨tag⟩ #F2E7E3@50% r12 pad 12 16 («Не планирую детей», «Много работаю», «Есть жильё», «Уважаю границы», «Православная», «Не курю», «Могу выпить»), «Мои увлечения» (picker-multiple Default, текст #514A47), prompts ⟨prompt / on-profile⟩ (#F2E7E3@50% r16: «Главный секрет счастливого брака?» / «не говорить со мной до того, как я выпью утренний кофе»; «Как ты осознаёшь, что влюбляешься?» / «Начинаю копировать твои фирменные фразочки и слова ;)»), «🚩 Недопустимо в партнёре» (dark-non-interactive #514A47: «Смотрит аниме», «пьет чай с сахаром», «не занимается спортом»), «💚 Обожаю в партнёре» (light-non-interactive stroke #F2E7E3: «инициативность», «умеет слушать», «карие глаза», «не курит»), text-кнопка «Пожаловаться» (#FA5F3D + иконка alert); скрытая кнопка «Отправить записку» (secondary с иконкой send-note-heavy и .counter 3); плавающие button-circle 80×80 (stroke белый 3): Dislike чёрная слева @8,722, Like #C66F53 справа @302,722.
- «no subscription»: like-sender с заблюренными фото и «Незнакомец 32», «Красавчик», «Ухажёр 28», «Суженый 31» + таймер (timer+shape); внизу фикс button-stack: «Узнай, кто поставил лайк» (#1B1A1C) (+ скрытый switcher-cell).
- Заметка дизайнера: «Используем брендовые формы с 1 по 5 по очереди. Лейбл с именем отображаем попеременно слева или справа. Крутим фотки при скролле. Текущую на от 0 до +10°; следующую от -10° до 0°.» Ссылки «Формы карточек», «Видео анимации».

## Чаты (секции «Все чаты и симпатии» — Готово, «Переписка» — Готово)
- Экран-таб «Чаты» («all»): «Твои пары» TT Runs Bold 22 (вариант ExtraBold 30) #C66F53 по центру @y78 + «Сделай первый шаг — напиши «Привет»» Manrope 14/16 #514A47; carousel «match-new» (центрированный, gap 20): крайние 80×80 круги + имя Manrope 14, центральный 128×128 (r1333) + имя Manrope 16 + .status 16 (online/offline, absolute слева) + .sound-switcher 44×44 (белый r20; иконка go-to-chat #C66F53); ниже chats-frame @y342: title-box «Сообщения» TT Runs Bold 22 + .44-button (иконка Erase-filled heavy — очистить?/справа); список ⟨chat-preview⟩ 390×131.77 stroke #F2E7E3@50% inside pad 24 16 24 12 gap 12: фото 80 (Ellipse), .status, имя+возраст («Константин, 33», «Константин, 27», «Всеволод, 36», «Анатолий, 31», «Дима, 26», «Василий, 25»), время («7 минут назад», «Вчера в 19:01»), last-message («Извини, не могу в этот раз с тобой», «Пицца с ананасами — топ!»), .counter #C66F53 r12 («99+»). Tab Bar с выбранной вкладкой (heart filled).
- network searching: «Сообщения» + loader Small + «Загрузка» (#B3ACA9).
- skeleton: chat-preview Skeleton ×5, carousel skeleton (круги #F2E7E3 + плашки).
- Пусто (нет пар): «Твои пары» + «Взаимные симпатии появятся тут» + пунктирная иллюстрация (сердце stroke #E1D6D1 3) ; пусто (нет чатов): «Переписок нет» ExtraBold 18 #C66F53 + «Найди пару и начинай общение» + Small «Смотреть анкеты» (match-light) поверх плейсхолдеров с градиентом.
- scroll: navbar «Сообщения» (иконка rules справа) появляется при скролле; «Твои пары» уезжает; вариант карусели с кольцами (stroke #E1D6D1/#C66F53 3 = непрочитанное/новое?) 96×122 / 132×158.
- pagination: skeleton в конце; error: snack «Ранние чаты не загрузились» (чёрная).
- even amount of matches: баннер community-rules 366×132 r16 stroke #E1D6D1: «Твои границы в твоих руках. Видишь нарушение — пиши нам» (Manrope 16 #9E8075) + иконка закрыть (Erase-filled #E1D6D1) + text-кнопка «Правила сообщества» (#C66F53, иконка rules); центральный match «Иван-Максимкин».
- chat actions (bottom-sheet из списка): «Анатолий, 31» Manrope Bold 20 + «Действия с перепиской» + кнопки secondary: «Поделиться профилем», «Пожаловаться и заблокировать», «Завершить общение» (в другом варианте «Удалить пару»); второй sheet «Почему хочешь закрыть взаимную симпатию?» — «Мы встретились лично», «Неприемлемое поведение», «Не тот, за кого себя выдаёт», «Больше неинтересно» (в arb ещё «Другое»).
- profile-preview из чата: тот же профиль, но правая кнопка-круг = go-to-chat-filled (чёрная), без dislike; bottom-sheet меню: «Поделиться профилем», «Пожаловаться и заблокировать», «Удалить пару». Экран «Селфи»: фото 366×366 r28 + pic-checkmark + «Фото проверено» ExtraBold 30 + «Константин — реальный человек, мы проверили его селфи»; navbar «Селфи» + «Закрыть».
- Переписка («default state»): navbar (menu справа) ; top-row @y98 164 высотой: слева pic-checkmark 56 + «Проверен» (Manrope 12/14 #514A47) / «Не проверен» (серая звезда #E1D6D1 с «?» + #9E8075); центр photo-box 128×128 круг; справа premium-icon 56 (#C66F53) + «Подписка» / «Нет подписки» (#B3ACA9 иконка, #9E8075 текст); name+age @y262: activity-status (.status 16 + «В сети» / «Не в сети» / «Поиск сети...» #B3ACA9) + «Константин Сидоров» TT Runs Bold 22 (вар. ExtraBold 18) + «29 лет» Medium 22; пусто: «Сообщений нет» TT Runs Medium 30/22 #B3ACA9 + «Напиши «Привет» или сделай комплимент»; input-bar 390×60 @y750 stroke #F2E7E3@50%: ⟨input--chat⟩ 314×44 r12 (States empty / start / active-filled / unfocused-filled; растёт до 104 при длинном тексте) + .44-button send (фон #F2E7E3@50%, иконка #B3ACA9 → при тексте чёрная кнопка). Вариант input--textarea r22.
- «searching network»: фото с overlay 35% + loader Medium, статус «Поиск сети...». no-photo: photo-placeholder 128.
- message: ⟨_Message Bubble / From=You⟩ (справа, gap 8) и From=Your Friend / From3 (слева); входящее сообщение: fill #F2E7E3 r12 smooth pad 12 16, текст Manrope 16 #000, время «13:02» Manrope 12 #9E8075 внутри пузыря справа-снизу; хвост (Tail) скрыт; сообщения группируются gap 4; время у последнего в группе («12:51», «13:01»); прочитано/статус — bool-пропсы.
- chat navbar type=chat 390×88 (name «Константин Сидоров», «Загрузка...», «Поиск сети...» с loader/overlay), stroke bottom #F2E7E3@50%.
- pagination-bottom / pagination-top: loader Medium 32 по центру; date-separator 390×46 («Сегодня», «9 Сентября») pad 24 16 4 16.
- Notification permission bottom-sheet после отправки первого сообщения: ⟨pic-chatting⟩ 56 + «Не пропусти сообщение» Manrope Bold 20 + «Когда Константин напишет, узнай об этом сразу» + «Включить уведомления» (main) + «Пропустить» (secondary); затем системный алерт push; заметка «либо в настройки кидать, если запрет был ранее».
- Заметка: бейдж «7» на таббаре (TT Runs Bold 16 белый rot 15°).
Figma — «Доработки», V3, V4, V5, «Развитие», UI Kit, сторы, черновики (notes_figma_v3_v5.md, 65096 байт)
# Figma — страницы «Доработки» (8831), «V3» (10129), «V4» (12894), «V5» (16970), «📈 Развитие» (4546), UI Kit (644) — прочитаны полностью

## Доработки (секции «Доработка обучение» — Готово, «Доработка подписка» — Готово)
- Профиль (non-verified) с блоком «mail» 366×226 fill #F2E7E3@50% r16 pad 20 12 24 12: ⟨oopps⟩ 56 (красный #FA5F3D «Ой») + «Ваша анкета содержит нецензурную лексику» Manrope Bold 20 + «Это может плохо отразиться на выборе партнёра и повлиять на первое впечатление» Manrope 14 + ссылка «Правила сообщества» Manrope 14 #FA5F3D; блок стоит ПЕРЕД verify-card.
- Карточка анкеты в ленте (1.1 full): между профилем и кнопкой «Отправить записку» — блок «mail» 366×144 fill #000 r16 pad 12 12 20 12: ⟨oopps⟩ + «Анкета содержит  нецензурную лексику» Manrope Bold 20 белый. Вариант pager с #FFFFFF@50% и tags-common fill #000@30%.
- Обучение (2.a): guide overlay 60% на карточке + ⟨24 / close⟩ @326,16 + стрелки + сердце + текст @y422: «Смахни вправо, чтобы лайкнуть или влево, чтобы отклонить партнёра» Manrope 14 белый + «Понятно» Manrope Bold 16 #C66F53. (3.b): pointer + «Смахни вверх, чтобы посмотреть  анкету подробнее» + «Понятно».
- Подписка «from likes»: теги «Что в подписке» fill #C66F53@60% + белый текст (вместо #F2E7E3@50%/#9E8075); payment-success теги fill #FFFFFF@8% bgblur; «Отменить можно в «Профиле»» Manrope 16.

## V3 (секции «Медиа 👀» — Готово, «Фильтры» — Готово, «Видео + раздел избранного» — В работе, «Статус активности» — Готово, «Твои пары» — Готово)
- **Чат: редактирование сообщения** («message»): navbar type=default с ⟨24 / menu⟩; input-bar 390×50 @y700 над полем: ⟨24 / edit light⟩ + «Редактирование» Manrope 12 #C66F53 + текст сообщения (обрезан «…») + ⟨24 / close⟩; ⟨_Message Bubble / From=you edit⟩; активность «Был 3 часа назад» (⟨status V3 / off⟩).
- **Медиа-онбординг («1»)**: navbar title «0 из 10» (счётчик фото?), progress-bar 3/4 + full + full; «Добавь видео и фото» TT Runs ExtraBold 30 + «Запиши видео-кружочек.⏎Пользователи с видео находят пару в два раза быстрее.» Manrope 16 #514A47; ⟨video-add-circle⟩ 208 @91,302 (stroke #E1D6D1, иконка 40 video-add; State=No-Permission: stroke #F2E7E3 + gear + «Не можем открыть камеру» + «Настройки»); «1 фото — минимум. После загрузки фото будут отмодерированы автоматически» Manrope 16 #B3ACA9; photo-grid 366×264 (3×2 ⟨photo-add-cell⟩ 116.67×128 stroke #E1D6D1 r16, иконка 40 photo-add); кнопка «Сохранить» disabled.
- **photo-moderation (V3)**: «Ваше фото не прошло модерацию» Manrope 16 #FA5F3D + «1 фото — минимум…»; сетка 4 фото + 2 add-cell; tooltips «+ 4%».
- **Редактирование анкеты — полный список секций** (390×102 stroke #F2E7E3@50% pad 24 16 24 16, title Manrope Bold 20 + ⟨24 / edit heavy⟩ @334, значение Manrope 14 #514A47): «Имя и фамилия» / «Александра Иванова»; «Должность» / «Дизайнер интерьеров»; «О себе» (236 высотой) / длинный текст «Я творческий и увлечённый человек… В свободное время люблю прогулки на природе, йогу, чтение…»; «Рост» / «169 см»; «Интересы» (⟨24 / plus⟩) / «Добавь ответ + 5%» #9E8075; «Флаги»; «Открывашки» (468 высотой): prompts-box V gap 8: ⟨prompt on-profile⟩ «Какую правду о жизни, тебе хотелось бы узнать раньше?» / «Мало кто задумывается, но слова «cherry» и «черешня» — однокоренные.», «Твой самый нелепый талант?» / «Поднимать вещи с пола ногой», Tooltip 366×80 stroke #E1D6D1 r16 «Добавь открывашку + 2%»; затем «answer» блоки (390 stroke #F2E7E3@50% pad 24 12): «Отношение к детям» → tag «Хочу детей» + optional «Сколько всего ты хочешь детей?» (⟨24 / hidden-light⟩ + Manrope Bold 16 #514A47) → «+ Дополнить» (#C66F53); «Карьера» → «Много работаю» + «У твоего партнёра тоже сильные карьерные амбиции» → «→ «Не важно»» + «Пожертвуешь карьерой ради партнера?» → «+ Дополнить»; «Стабильность» → «Нет жилья» + «У твоей пары должна быть квартира?» → «Не важно» + «Переедешь в другой город или страну в случае чего?» → ««Нет»»; «Новый опыт» → «Люблю контроль» + «Должен ли партнёр иметь похожее видение отношений?»; «Духовность» → «Православный» + «Важны ли для тебя религиозные взгляды партнёра?» → ««Обращаю внимание»»; «Курение» → «Не курю» + «Как ты отнесёшься к курению партнёра?» → «→ «Это недопустимо»»; «Алкоголь» → «Не пью» + «Отношения партнёра с алкоголем» → «→ «Не важно»»; «Прошлые отношения» → «Нет детей» + «Как ты отнесёшься к наличию детей у партнёра?» → «→ «Не хочу отвечать»». navbar «80% заполнено» + «Показать»; кнопка «Показать анкету» (secondary, иконка heavy).
- **Фильтры V3 (premium-фильтры)**: navbar «Настройки поиска» + «Готово» (#9E8075 активен / #E1D6D1 disabled). Бесплатные: Город (Автоматически; иконки geolocation heavy + edit heavy), Радиус поиска (50/81 км, слider, switcher «Можно немного расширить»), Возраст (25–45 лет, cupertino-picker-pair, switcher). Премиум (с ⟨premium-icon⟩ 24 у заголовка; при отсутствии подписки — ⟨24 / lock heavy⟩ #E7C9B2, элементы disabled #F2E7E3@50%, и premium-offer карточка «Премиум фильтры» / «Оформи подписку чтобы активировать» / «Выбрать тариф» после базовых фильтров): «Рост» (Не важен / От 160 см / 160–180 см; cupertino cm/«Добавить»); «Верификация» + значение «Есть» + switcher «Показывать только проверенных пользователей»; «Аккаунт с подпиской» + «Нет» + switcher «Показывать только пользователей с купленной премиум подпиской»; «Минимальное количество  фото в профиле» + «от 2»/«от 1» + ⟨picker-numbers⟩ 48×44 «1…6» (pick #C66F53); «Видео» + switcher «Наличие видео-кружочка  в анкете»; «Совместимость» + «от 70%»/«от 0%» + ⟨slider-cell / Middle⟩ («0%» #B3ACA9 … «100%» #C66F53, tag «От 70%»); «Религиозные взгляды» + picker-single wrap: «Православный», «Католик», «Протестант», «Мусульманин», «Иудей», «Буддист», «Агностик», «Атеист», «Не важно»; «Отношение к алкоголю»: «Негативно», «Нейтрально» (checked), «Положительно»; «Отношение к курению» (те же три). Внизу button-caption: «Применить» (main) / «Сбросить фильтры» (secondary); 4.1 loader; 4.2 error; 15 — snack «Не сохранилось, попробуй ещё раз»; 16 — loader при сохранении.
- **premium 2 (V3)**: «Сделай поиск более точным  с премиум-фильтрами» / «Оформи подписку «Любовь без границ» и получи дополнительные настройки поиска»; «Что в подписке» с тегом «Дополнительные фильтры» (⟨24 / filters⟩); subscription ON — тег «Дополнительные фильтры» (280×42) в подписке; **popup** «Расширили подписку» (subscription-active 350×382 по центру с close: «Добавили премиум фильтры для более точного поиска» + тег + «Выбрать тариф» / «Понятно»; комментарий «Показывать 1 раз после обновления.»).
- **Видео («9 videos» V3)**: ⟨video - card⟩ 366×627 r28 в ленте среди карточек (комментарий «Просмотр среди карточек»): Property Playing/Default/sub/ends/empty: фон-видео («psychologist»), gradient dwn @459 168px, текст «Как устроить дома романтический вечер» Manrope 14 белый + «Опубликовано 21.07.2024» #F2E7E3@50%, справа колонка ⟨.sound-switcher white on⟩ + ⟨.like empty white⟩ (heart heavy → filled при лайке; State fill white), ⟨.progress⟩ 330×3 #FFFFFF@15%; sub — плашки «У всех» «было» «так» (#F2E7E3 r12 Manrope Bold 20 ls1 #9E8075); ends — overlay 60% + ⟨.button-play⟩ 112 + ⟨button Small secondary⟩ «Все видео» (иконка 16 video, текст #F2E7E3) + скрытый «Смотреть снова» (24 reload 40); empty — stroke #F2E7E3 + ⟨pic-no-videos⟩ 200; «Обучение»: bg #000@56% + стрелки вверх/вниз + «Листай вверх или вниз, чтобы смотреть ещё больше видео» + «Понятно» #C66F53; snack «Видео добавлено в избранное» / «Видео убрано из избранного».
- **videoplayer-*** (fullscreen, fill #000): видео 518×921; navbar title «Здоровые отношения» белый + «Закрыть»; ⟨.like⟩ 44 @330,54 (white / #F2E7E3@50%); paused: ⟨.button-play⟩ 112 по центру / ⟨40 / pause⟩; controls @y575 колонка справа: ⟨24 / mute-heavy⟩, ⟨24 / share heavy⟩, ⟨24 / bookmark add/filled⟩, (HIDDEN) ⟨24 / compact⟩; время «14:55 / 20:11» Manrope 12 белый; ⟨.progress⟩ 326×3 #FFFFFF@15% filler белый; «Смотреть снова» (reload 40 + Manrope 12) / ⟨button Small secondary⟩ «Другие видео» / «Все видео»; add favorites: bookmark filled + snack.
- **videos (список)**: navbar «Советы по здоровым отношениям» / «Сохранённые видео» + «Закрыть»; empty: ⟨pic-no-videos⟩ + «Нет видео» + «Видео скоро будут добавлены»; saved empty: «Ничего не добавили» + «Здесь будут храниться видео, которые вам понравились» + Small «Смотреть видео»; ⟨videoframe⟩ 140×198 (превью 140×162 r16 + play 40 + название «Свадьба через месяц после знакомства» Manrope 14; error — stroke #F2E7E3).
- **Профиль V3**: links-cells с новой строкой ⟨24 / video light⟩ «Советы по здоровым отношениям» первой; (в «profile» V3 «Твои пары» ⟨24 / match-light⟩ Manrope Bold 16 — ссылка на список пар).
- **Статус активности (Online/Offline на карточке)**: distance-строка H gap 12: (location + «Москва») + online (⟨status V3 / on⟩ 16 + «Онлайн» Manrope 14 lh16 белый) / (⟨status V3 / off⟩ + «Был 3 часа назад» Manrope Bold 14 белый); tags-common fill #000@30%. status V3: on = сердечко #BBE31F/#C66F53 8×6; off = полумесяц #E7C9B2.
- **Твои пары (Matching)**: navbar «Твои пары» + «Закрыть»; пусто: «Взаимные симпатии появятся тут» Manrope 16 #514A47 + пунктирное сердце stroke #E1D6D1 3; список (SYMBOL Frame 1321315673 390×120 stroke #F2E7E3 pad 20 12 gap 17): ⟨shape-brand form=3…6⟩ 68×80 с фото + «Преображенский Александр, 36» Manrope Bold 20 (+ ⟨checkmark-filled⟩ #C66F53 absolute справа) + «Дата создания пары 22.11.2024» Manrope 12 #B3ACA9 + (HIDDEN) «ID 234834 » Manrope 14 #514A47 + ⟨24 / copy light⟩ + (HIDDEN) «Пользователь в чёрном списке» / «Пользователь заблокирован» Manrope 12 #FA5F3D + ⟨24 / share heavy⟩ @257; примеры: «Киреев  Иван, 28» ID 534556, «Железняк Вячеслав, 40», «Александр, 36», «Раенко Ильдар, 31», «Иванов Александр, 36»; Shimmer: skeleton-плашки #F2E7E3 r1000. «1.1 full» из списка пар: navbar «Карточка партнёра» + «Закрыть», без кнопок like/dislike, Tab Bar внизу.
- popup (V3 «Расширили подписку») — см. выше. '1.0 loader' — не найдено здесь (см. V4).

## V4 (секции «Обновление и технические работы» — Готово, «Онбординг» — В работе, «Реферальная программа» — Готово, «Сообщества» — Готово)
- **hard update**: «Обнови приложение» TT Runs ExtraBold 30 @y94; иллюстрация 300×220 (круги #E1D6D1/#F2E7E3, спящие глазки, ⟨24 / refresh heavy⟩ 65, сердечки #C66F53, тень #B3ACA9@10%); «Мы сделали поиск партнёра ещё точнее  и комфортнее.» TT Runs ExtraBold 18 #C66F53 + « Скорей скачивай новую версию, чтобы найти свою вторую половинку!» Manrope 16 #514A47; кнопка secondary «Обновить» внизу.
- **maintenance**: «Технические работы» ExtraBold 30; иллюстрация (розетка/вилка #F2E7E3/#E1D6D1); «Технические работы будут завершены через:» Manrope 16 #514A47 + «01 : 23 : 48» TT Runs ExtraBold 18 #514A47.
- **soft update** (bottom-sheet поверх ленты): ⟨update⟩ 56 + «Обнови приложение» Manrope Bold 20 + «Мы улучшили поиск партнёра.  Обнови приложение, чтобы найти свою вторую половинку!» + «Обновить» (main) + «Напомнить позже» (secondary).
- **maintenance bs**: ⟨main⟩ 56 (gear) + «Технические работы» + «Приложение будет недоступно  24 Июня с 12:00 до 14:00» + «Ок». **maintenance alert** (на профиле): prompt-карточка 366×108 fill #514A47 r16: «Технические работы» Manrope Bold 20 белый + ⟨24 / close⟩ + «Приложение будет недоступно  24 Июня с 12:00 до 14:00» Manrope 14 #F2E7E3 — ставится первым блоком.
- **Профиль V4 links**: ⟨24 / video light⟩ «Советы по здоровым отношениям», ⟨24 / refferal Light⟩ «Пригласи друга» (Manrope Bold 16 / Medium 16) или «Реферальная программа», ⟨24 (people) Light⟩ «Сообщества», далее faq/support/rules/lock/trash.
- **Реферальная программа (ref_main ×7, ref_share, 1.0 loader)**: экран fill #000; navbar fill #E7C9B2 (!) container white; title «Пригласи друга» TT Runs ExtraBold 30 #C66F53 + «если он подтвердит свою личность  при регистрации, то ты получишь 5 дней премиум подписки, а друг 3 дня в подарок» Manrope 16 #B3ACA9 (вариант: « помоги ему найти свою любовь.  Ведь, чем больше друзей вокруг, тем больше возможностей для счастливых знакомств!»); ⟨invite/pic⟩ 390×292 / ⟨invite/pic2⟩ 390×290 (иллюстрации: конверт/подарок, premium-icon, бейджи «+5» «+3» «+7»); статистика cupertino-picker-pair 366×140: две карточки 175×140 fill #514A47@50% r16 (blur-эллипс #C66F53): «6» TT Runs Bold 22 белый + «друзей приглашено» Manrope Bold/Medium 16 #B3ACA9; «30» + «дней подписки  получено»; карточка прогресса 366×141 fill #514A47@50% r16: шкала «0 5 10 15 20» (Manrope 16 #F2E7E3/#B3ACA9) + ⟨.progress quarter⟩ 326×6 filler #C66F53 + «Пригласи ещё 4 друга и получи дополнительно 7 дней подписки» (варианты: «Пригласиеще 4 друга…+7 дней», «Пригласи 5 друзей и получи  дополнительно +7 дней подписки», «Продолжай приглашать друзей  и получай 7 дней подписки»); ⟨btn_copy dark⟩ 366×56 stroke #F2E7E3@50% «...po.lubvi/referal/olenionok123» + ⟨24 / copy heavy⟩; ⟨button Big main⟩ белая с текстом #C66F53 «Отправить ссылку»; подпись «Ты пригласил больше, чем 85% приглашающих.⏎Подробнее о программе» (ссылка #C66F53) / «Ты входишь в 85% приглашающих.» / «Продолжай приглашать друзей чтобы получить больше дней премиум подписки. Подробнее о программе» / «Начни приглашать друзей чтобы получить больше дней премиум подписки. Подробнее о программе»; «История приглашений» Manrope Bold 20 белый: строки 366×44 (бейдж 72×44 r12: «+5»/«+7» TT Runs ExtraBold 18 белый + premium-icon 24; текст «Друг зарегистрировался  23.05.2025 в 19:01» / «Поздравляем!  Ты пригласил 5 друзей! » / «Подруга зарегистрировалась 23.05.2025 в 19:01» Manrope 12 #B3ACA9; разделители #514A47); пусто: «Ты ещё никого не пригласил.  Как только друг подтвердит свою личность, ты получишь подарок »; статистика «1»/«7», «20»/«100»; snacks: «Приглашение отправлено», «Ссылка скопирована», «Эта реферальная ссылка уже была использована», «Ты уже участвуешь в реферальной программе», negative «Нельзя пригласить себя»; ref_share — iOS share sheet («Первый шаг к семейному счастью» / «Скачивай приложение для поиска серьёзных отношений po.lubvi/referal/olenionok123»), btn_copy fill #514A47@50% с полной ссылкой «po.lubvi/referal/olenionok123», «Подробнее о программе» Manrope Bold 20 #C66F53 раскрывает статистику; 1.0 loader — тёмный loader / тёмная ошибка «Не загрузилось :(» (#F2E7E3@50%) + белая кнопка «Обновить» (#C66F53?).
- **5.4 ref-info** (карточка по центру 350×358 fill #000 r20): ⟨premium-icon⟩ + «Премиум  в подарок » ExtraBold 30 белый + «Друг зарегистрировался  по твоей ссылке, 5 дней премиум подписки уже активны.» / «Поздравляем, ты зарегистрировался по реферальной ссылке, 3 дня премиум подписки уже активны» #B3ACA9 + «Подписка активна до 20.05.2025» #F2E7E3 + «Понятно» #C66F53. **Lockscreen push**: «Ты пригласил друга» / «Подписка на 5 дней уже активна. Попробуйте все преимущества премиум подписки уже сейчас!».
- **subscription ON (реферал)**: «5 дней подписки в подарок» / «Месяц подписки в подарок» / «Подписка  на месяц  в подарок» ExtraBold 30; теги; «Активна до 20.05.2025» Manrope 14 #F2E7E3 + «Продлить подписку» Manrope Bold 20 #C66F53.
- **5.4 push-custom (реферал)**: bottom-sheet fill #000 390×660: ⟨premium-icon⟩ + «Ты зарегистрировался по приглашению от друга» Manrope Bold 20 белый + «Подтверди свою личность сделав селфи, чтобы получить 3 дня премиум подписки» #B3ACA9 + теги (stroke #514A47: «+4 лайка в день», «Кому понравилась анкета», «Дополнительные фильтры», «Бесконечные возвраты», «+2 записки в день») + белая кнопка «Подтвердить свою личность» (текст #C66F53) + «Позже» (secondary).
- **Onboarding V4 (Auth 3 / 3.2)**: «Укажи телефон» TT Runs Bold 22 + «Пришлём СМС с кодом доступа»; ⟨input--line Start⟩ «Номер телефона» placeholder «987 654-32-10» (+7 prefix; Active-filled «910 436-30-79»); ⟨checkbox-cell⟩ 378×80 «Я старше 18 лет и не состою  в отношениях или браке» (checkbox on/checked); текст согласия «Нажимая «Далее», я соглашаюсь с условиями политики конфиденциальности, а также даю своё согласие на обработку моих персональных данных» Manrope 12; «Далее» disabled → default; numeric keyboard.
- **Сообщества (com_main ×6, com_no com ×3, com_main_ext alert ×2, com_list…, Tag ×3, Tags, filters 1/2, scroll)**: экран fill #000 с blur-эллипсами #C66F53; navbar «Сообщества» белый, left ⟨24 / exit⟩ или ⟨24 / Community list⟩, right «Готово»; текущее сообщество: логотип 120×120 (#FFDD2D Т-Банк; #64ADF2 Храм; #043868 Еврохим; ⟨community/empty⟩ #F2E7E3@50% с буквой; #000 Surf) + «Т-БАНК» TT Runs ExtraBold 18/16 белый + «210 участников» + «Дата вступления 23.04.2025» Manrope 14 #B3ACA9; соседние сообщества по бокам 80×80 (карусель, gap): «Храм », «Еврохим» + подпись; ⟨switcher-text-icon⟩ 220×52 «Значок видно всем» (On) / «Значок скрыт» (Off, fill #F2E7E3@50%) + «Ты видишь участников, а они тебя» / «Ты не видишь участников, а они тебя» Manrope 12 #B3ACA9; «Покинуть сообщество» Manrope 14 #FA5F3D; нижняя белая панель 390×248 r28: «Пригласить в сообщество» Manrope Bold 20 + «Сообщество приватно и участники утверждаются модератором» / «Все сообщества приватны…» / «Это открытое сообщество и вступить может любой пользователь по приглашению» + ⟨btn_copy light⟩ «...po.lubvi/community/TBANK» + «Отправить приглашение» (secondary); com_main «Ты можешь вступить  еще в 3 сообщества» + «Чем больше сообществ, тем больше возможностей найти партнёра по интересам»; com_no com: иллюстрация 120 (#F2E7E3 спящий) + «Нет сообществ» + «Ты не состоишь в сообществе,  попроси участника пригласить тебя  и получи » / «стань участником и получи » + теги «Дополнительные фильтры», «Значок сообщества»; панель «Список сообществ, куда можно вступить по приглашению» + «это люди, объединённые общими целями  или интересами. Чтобы создать своё сообщество, напиши на  info@plbvi.ru» (ссылка) + список switcher-cell 358×56: логотип 32 + имя белый + «2 537»/«1 894»/«350»/«110»/«97» + ⟨24 / profile filled⟩ #B3ACA9 (Еврохим, Т-БАНК, Храм Рождества Пресвятой Богородицы в Крылатском..., Surf, Евробанк); кнопка «Смотреть сообщества» / «Не вступать»; alert iOS «Покинуть сообщество  Т-БАНК?»; snacks «Ты покинул сообщество », «Приглашение в сообщество отправлено»; push-custom sheets (fill #000): «Тебя приглашают в сообщество  Т-БАНК» + «вступив, ты сможешь знакомиться  с участниками и получить:» + теги (Дополнительные фильтры; ⟨community badge⟩ «Значок сообщества») + «Вступить в сообщество» + «Позже»; «Ты подал заявку на вступление  в приватное сообщество» + «После утверждения модератором,  ты сможешь знакомиться с участниками  и получить:» + «Ок»; «Тебя исключили из сообщества Храм Рождества Пресвятой Богородицы в Крылатском» + «Ты сможешь вернуться, только если тебя пригласит участник» + «Ок»; «Тебя не приняли в сообщество Т-Банк» + «Модератор не подтвердил, что ты являешься членом сообщества»; «Тебя приняли в сообщество …» + «Ты получил значок сообщества и можешь видеть его участников»; «Ты вступил в сообщество Тенёк» + «Ты получаешь:» + теги + «ОК»; комментарии: «Иконка не пришла с бэка или не прогрузилась» (community/empty), «Когда на главном экране нажали “позже” и зашли в сообщества. Первым показывается всегда сообщество в которое пригласили», «2/3 сообщества и перелистываем на второе», «нет сообществ», «когда сотрудник увольняется или посторонний человек и модератор его исключает».
- **Tag на карточке ленты**: tags-common: значок сообщества 32×32 (#FFDD2D / #64ADF2 / community/empty) перед тегами; tags fill #000@30%. **Tags (карточка полная)**: секция «Состою в сообществах» Manrope Bold 16 + чипы 366×… fill #F2E7E3@50% r12 pad 12 16 gap 12: логотип 24 + название («Храм Рождества Пресвятой Богородицы в Крылатском» 2 строки, «Т-БАНК», «Еврохим») — между «values» и «Мои увлечения».
- **filters V4**: новый блок после Возраста: «Показывать только участников сообщества » Manrope Bold 20 (premium-icon скрыт) + switcher-cell ×N: логотип 32 + «Т-БАНК»/«Еврохим»/«Храм…» Manrope 16 #514A47 + ⟨.swicher⟩ (Active yes/no); остальное как V3.
- **Чаты V4 (scroll)**: navbar «Сообщения» + ⟨24-flutter / rules⟩; chat-preview State=Default V3: ⟨status V3⟩ on/off слева от фото (@−8,32), значок сообщества 24 stroke белый 2 на фото (@56,55), имя + ⟨checkmark-filled⟩ + ⟨premium-icon⟩ 24, ⟨24 / menu⟩, last-message + typing-state (HIDDEN) + ⟨read / read⟩ (галочки #C66F53) + время; примеры «Всеволод, 36» «Вчера в 19:01» «Да, было бы здорово, давай в пятницу»; «Анатолий, 31»; «Дима, 26»; «Василий, 25» «Пицца с ананасами — топ!».

## V5 (секции «Изменить селфи» — Готово, «Сообщества» — В работе, «Чаты» — Готово)
- **Изменить селфи («1»)**: в редактировании анкеты после photo-grid — карточка verify 366×160 stroke #E1D6D1 r20 pad 16 (H gap 12): «Измени селфи» Manrope Bold 20 + «Это фото могут видеть только твои пары. Если фото тебя не устраивает, можешь обновить» Manrope 14 #B3ACA9 + превью селфи 116.67×128 r16 с ⟨pic-checkmark⟩ 24 и кнопкой ⟨24 / refresh heavy⟩ 44×44 (#7F7F7F@20% blur). «2»: ⟨photo-frame Default⟩ 366×366 r28 + «Это твое прошлое фото, точно хочешь его заменить?» Manrope 16 #514A47; navbar «0 из 10» + «Пропустить»; icon-caption «Селфи увидят только твои пары» (hidden-light) + «Заменить фото» (main; disabled в '19'); «3»: камера (navbar media dark «Отмена», ⟨photo-frame Helper⟩ 366 с силуэтом-подсказкой, «✌️» 64 + «Повтори жест» Manrope Bold 20 белый + «Твоя будущая пара увидит это фото, улыбнись :)» Manrope 14 #B3ACA9, кнопки ⟨.56-button⟩ trash / ⟨button-camera photo⟩ 72 / ⟨.56-button⟩ camera-switch); «4»: иллюстрация ⟨pic-verification⟩ + «Отправили фото на проверку» ExtraBold 30 + «Обработка обычно занимает от 10 минут. Сообщим о результате в пуш-уведомлении» + «Включить уведомления» (secondary) + «Завершить» (main); «19»: ⟨40 / gear⟩ + «Не можем открыть камеру» ExtraBold 18 #C66F53 + «Для верификации нужно селфи.⏎Разреши доступ к камере в настройках телефона» + Small «Настройки» (settings-heavy); «20»: overlay + ⟨permission-ios Cam⟩ «Приложение «По Любви» запрашивает доступ к Камере»; «21»: overlay 60% на фото + loader.
- **Чаты V5 — ссылки, ответы, редактирование, контекстное меню**: «Ссылки в сообщениях»: ссылка внутри текста #C66F53 underline (входящее) / белый в исходящем (Message fill #FFFFFF override для длинной URL?); bottom-sheet при тапе: ⟨pic-attention⟩ + «Ты уверен, что хочешь перейти  в сторонний сервис?» Manrope Bold 20 + «Ссылки могут содержать опасные ресурсы и мошеннические ловушки» + «Перейти» (main) + «Отмена» (secondary); ⟨date-separator⟩ 390×46 «9 Сентября»/«Сегодня» (Manrope 14 #9E8075, линии #F2E7E3@50%); message-group gap 4; «Ответ на сообщение»: input-bar 390×50 @y700: ⟨24 / reply⟩ + «Ответ на сообщение: Константин» Manrope 12 #C66F53 + цитата Manrope 12 #000 + ⟨24 / close⟩; пузырь ⟨_Message Bubble / you reply⟩ (цитата с курсором-полоской 2×32 #C66F53/#FFFFFF, имя «Константин»/«Анна» Manrope 12 #C66F53/#E7C9B2, текст цитаты Manrope 12, основной текст Manrope 16), «подскролл к цитируемому сообшению» — подсветка #F2E7E3@50% строки; исходящее пузырь fill #C66F53 pad 12 16 r12 хвост, текст Manrope 16 белый, time-box «Изменено» + «12:48» Manrope 12 #E7C9B2 + ⟨read⟩ галочки (#E7C9B2 / #21C004?); входящее fill #F2E7E3, «Изменено» + время #9E8075; контекстное меню (overlay #000@60% bgblur 16): кнопки 153×44 fill #F2E7E3 stroke #E7C9B2 (свои: «Ответить» (16 reply) / «Скопировать» (24 copy) / «Изменить » (16 edit) / «Удалить» (16 delete); чужие: «Ответить» / «Скопировать»); удаление: «Удалить у меня и у Константин» (170×52) / «Удалить у меня»; snack «Сообщение было скопировано» (fill #C66F53!); статус «Был в сети 3 часа назад» / «В сети»; редактирование: input-bar «Редактирование» + текст; длинный текст: input--textarea растёт до 184 (text-cropper 160), клавиатура; ⟨.44-button⟩ send fill #000 (активна) / #F2E7E3@50% (нет текста).
- **Сообщества V5 (com_list…)**: navbar «Список доступных сообществ» (#514A47) + ⟨24 / exit⟩ + «Готово»; ⟨input--search⟩ 366×56 «Поиск сообщества» (Typing «Храм»); список ⟨community-cell⟩ 358×74/94 (Join: логотип 40 + «Еврохим» Manrope 16 #514A47 + «126 участников» Manrope 14 #B3ACA9 + ⟨24 / join⟩; Exit: «Вы участник»; no logo: ⟨community/empty⟩; Long name: 2 строки «Храм Рождества Пресвятой Богородицы в Крылатском...» + «2 533 участника»; примеры «Еврохим  Казань», «ПАТРИАРШИЙ СОБОР ВО ИМЯ ВОСКРЕСЕНИЯ ХРИСТОВА», «Храм Христа Спасителя»); search/none: «Ничего не найдено» ExtraBold 18 #C66F53 + «Проверь верно ли введено название.  Чтобы создать своё сообщество, напиши на  info@plbvi.ru»; join sheet «Ты вступил в сообщество Тенёк» + «Ты получаешь:» + теги + «ОК».

## 📈 Развитие (страница идей, bg #3A3A3A)
- Галерея добавления фото ('4'–'4.9'): экран fill #000, navbar dark «0 из 6»…«6 из 6»; photo-container 279.44×464.2 r28 (stroke #514A47) с ⟨photo-placeholder⟩ 96; «Выбери первую фотографию» Manrope 12 #B3ACA9; gallery — шторка (⟨.handle-cell⟩ белая) с рядами ⟨photo-gallery⟩ 127.33×127.33 gap 4 (select → ⟨.counter Selected⟩ 24 белый с номером; unselected — Empty stroke #F2E7E3@50%); выбранные фото листаются горизонтально (H gap 8 pad 0 12) с ⟨.counter non-interactive⟩ #514A47 и ⟨24 / trash heavy⟩ @16,424; «Изменить порядок можно на следующем шаге»; заметки дизайнера: «Соотн. сторон фотки = соотношению на Главном. Соотн. на Главном считается индивидуально по девайсу (чтобы занимала весь экран) Высота фотки на экране добавления = 0.55 от высоты девайса (844*0.55=464) Ширина фотки на экране добавления…», «Галерея фоток работает по принципу шторки — магнитится к навбару, но тормозит в 48pt от него, чтобы было видно, что происходит за ней (то, что за ней становится 35% альфа) перекрывая большие фотки, дальше скролл идет только вн…».
- Auth 3 (старая): «Укажи телефон» Bold 22 + «Пришлём СМС с кодом доступа» + input с caption «Никто не узнает твой номер» (lock light #9E8075) + consent + «Далее». Auth 13: «Твой пол и рост?» ExtraBold 30; «Я» TT Runs Medium 30 #B3ACA9 + picker-single «Мужчина»/«Женщина» 120×44; «Мой рост» + ios--picker 288×220 (177…183 см, выбран «180 см»); icon-caption «Увидят все пользователи» (revealed-light) + «Далее»; progress-bar third.
- Чаты (old 'all'): «Твои пары» ExtraBold 30 #C66F53 + «Сделай первый шаг — напиши «Привет»» + carousel (92/140 круги stroke #E1D6D1/#C66F53 3 + .sound-switcher go-to-chat) + «Сообщения» TT Runs Bold 22 + ⟨24-flutter / rules⟩ + chat-preview list; 'even amount of matches' — баннер правил.
- Симпатии (old 'all'): «Симпатии» + «Узнай, кому понравилась твоя анкета»; «Записки» 27; notes-stack (подложки rot 5°/175°) + powdery-card (цитата «В тебе есть что-то волшебное, что заставляет сердце биться быстрее. Ты особенная, и я рад, что наши пути пересеклись.» + «Александр» ExtraBold 16 #C66F53 + «Сегодня в 15:22») + .counter «2»; «Поставили лайк» 113; список записок (overlay белый 90%, navbar «Записки»); photo-moderation (photo-editing error «Замени фото» #FA5F3D, State8 «Проверяем…»; «Новые фото проходят проверку»; «76% заполнено»); errors (separate): ⟨lovenote Error⟩ («Не загрузилось :(» + «Попробуй обновить список» + «Обновить»).

## UI Kit (644) — компоненты
- **Colors**: Brand — Terracotta #C66F53, Pale Beige #E7C9B2, Powdery #F2E7E3, Innocent White #FFFFFF, Black #000000; Additional — Beigeish Black #514A47, Beigeish Gray #B3ACA9, Powdery-50a #F2E7E3 a:50%, Dark Beige #9E8075, Dark Powdery #E1D6D1, Error Red #FA5F3D.
- **Typography**: TT Runs — headingHeavy 800 30/32, headingLight 500 30/32, titleHeavy 700 22/24, titleLight 500 22/24 (в макете 21), captionBig 800 18/22, captionHeavy 700 18/22, captionMedium 800 16/20; Manrope — Subtitle 700 20/24 ls0.2, lineBold 700 16/20 ls0.2, lineLight 540(!) 16/20 ls0.2, baseBold 700 14/18 ls0.1, baseLight 540 14/18 ls0.1, captionSmall 500 12/16 ls0.2.
- **Button** (COMPONENT-SET size Big 56 / Small 44 × type main/secondary/text × state default/pressed/disabled/load): Big main #000 r12 pad 0 12 текст Inter Semi Bold 16 (в макетах Manrope) белый, иконка 24 heavy слева gap 8 + компенсатор 4; pressed #514A47; disabled #F2E7E3@50% текст #9E8075 op50%; load — loader Medium 32; secondary stroke #E1D6D1 1.33 текст Manrope Medium 16 #C66F53 (в экранах чаще #000), pressed op65%, disabled stroke #F2E7E3 текст #E1D6D1; text — Manrope Bold 16 #000, pressed #514A47, disabled #E1D6D1; Small main #000 r12 pad 0 20 h44 текст Inter Semi Bold 14 / Manrope Bold 13–14 + иконка 16 refresh heavy gap 4; Small secondary stroke #E1D6D1 текст Manrope Bold 14 #000; Small text Manrope Medium 16 / Bold 14; .counter на secondary — 24 круг stroke #E1D6D1 absolute справа сверху.
- **button-circle** 80×80 r10000 stroke белый 3 outside: Like #C66F53 + ⟨40 / heart filled⟩ белый; Dislike #000 + ⟨40 / reject filled⟩ белый; Disabled #B3ACA9 + иконка #514A47.
- **Inputs**: input--line 366×56 stroke #E1D6D1/#C66F53(focus) r12 pad 18 20: fieldname Manrope 16 #9E8075 (плавает вверх в field-name 74×24 белая плашка @12,−12 Manrope 12 #9E8075 при Start/Active/Inactive-filled), prefix «+7», placeholder #E1D6D1, text #000, cursor #C66F53 1.5×20, caption под полем (иконка 16 + Manrope 14 #9E8075), .44-button (Erase-filled #F2E7E3@50%) справа при Active-filled; blocked fill #F2E7E3@50% текст #9E8075; disabled-empty. input--code 56×56 (default/placeholder #E1D6D1/focus stroke #C66F53/filled/error-focused текст #FA5F3D/Error Inactive/blocked #F2E7E3@50% #9E8075); input--code-group 6×56 gap 4 + timer-caption «Новый код можно будет получить через 59 с.» (Inter 12, число #9E8075) + error-caption «Неверный код, попробуй другой» Manrope 13 #FA5F3D + Small text «Запросить новый код»; input--bday-group 8 ячеек 38.75×56 с точками; input--search 366×56 (⟨24 / search⟩ + «Search» #9E8075; Focus stroke #C66F53; Typing/Filled с .44-button clear); input--textarea 366×56+ (States disabled/empty/start/active-filled/unfocused-filled/blocked; counter «0 / 200» плашка 57×24 белая @298,−12 Manrope 14 #9E8075; text-cropper pad 18 0); input--chat 366×44 r12 (те же состояния, pad 12 0); btn_copy dark/light (366×56 r12 stroke, текст Manrope Bold 16 белый/#514A47 + ⟨24 / copy heavy⟩).
- **Atoms**: .cursor 1.5×20 #C66F53; .progress (сегмент 119.33×6 #F2E7E3 r1000, filler #C66F53; filling empty/quarter/third/half/3/4/full); .44-button 44×44 (иконка 24 по центру + скрытый .status); .slider-segment 35×2 Default #F2E7E3 / Negative #514A47 / Positive #C66F53; .manipulator 40×40 (#C66F53 stroke белый 2 + точка 4 белая; Pressed 44); .handle-cell 390×20 (индикатор 36×4 белый r100); .scroll 2×48 #E1D6D1; .record-indicator 7 #C66F53/#514A47; .56-button (иконка 40); .sound-switcher 44 r20 (off mute-heavy / on sound-on; white варианты без фона); .button-play 112 (белый треугольник 94×92); .player-circle-big 358; .delete-transformer (Default: белая кнопка 44 trash → Alert: #FA5F3D r20 «Удалить?» Manrope 16 белый); .counter 24 r12 (Selected белый текст Manrope Bold 14 #000 / non-interactive #514A47 текст #B3ACA9 / Empty stroke #F2E7E3@50% 2); .swicher 56×32 r16 pad 4 (Active no #F2E7E3 / yes #C66F53; Disabled no #F2E7E3@50% / yes #E7C9B2; circle 26×24 белый r12); .indicator-pager 10×8 (active белый «O» / inactive белый 50%); .num-box 32×28 TT Runs ExtraBold 16 белый; checkbox 24 (on default stroke #C66F53 2 r4 / checked fill #C66F53 + галка белая; off #E1D6D1 40%); .status 16 (online #C66F53 / offline #E1D6D1, stroke белый 3); .hande-big-switcher 46×44 белый r22 (hidden / visible / loader Small); .last-message-box (текст + typing-state «Печатает» + три точки #C66F53 + .counter); name-label (side left/right/2 line: name-box fill #C66F53 r12 pad 20 32 rot ±5°: имя TT Runs Bold 18 белый + возраст Medium 22); .like 44 r20 (off/empty white/on/fill white; hearth heavy/filled); 40 / pause; read (read/unread галочки #E7C9B2/#21C004); status V3 (on/off).
- **Selectors**: picker-single 120×44 r12 (default stroke #E1D6D1 текст Inter Semi Bold 14 #000 / disable #F2E7E3@50% #9E8075 / checked #C66F53 белый); picker-multiple h40 r28 pad 0 20 (Default stroke #E1D6D1 Manrope Medium 14 #000 / light-non-interactive stroke #F2E7E3 #514A47 / Checked #C66F53 белый / dark-non-interactive #514A47 / disabled #F2E7E3@50% / skeleton / Deletable #000 + ⟨16 / close-light⟩); slider-cell 358×94 (States Middle/Positive/Maximum/Negative/Minimum: подписи-эмодзи «😶» слева #514A47 / справа #C66F53, tag по центру, линия 334×3 с increment-точками 3×3 и 8 сегментами); button-camera 72 (video #C66F53 круг 58 + stroke белый; Recording квадрат 24 #C66F53 r4; done белый круг + ⟨40 / checkmark⟩; photo белый круг 58; disabled #514A47; done-disabled #B3ACA9); switcher-text-icon 208×52 r10000 stroke белый 3 (On #C66F53 текст Manrope Medium 14 белый + .hande-big-switcher visible; Off #F2E7E3 hidden + текст #9E8075; примечание «Тоггл одной ширины на одном экране, текстовый блок одной ширины, вне зав. от текста»); picker-numbers 48×44 r12 (Default/pick #C66F53/disable).
- **Snack** 335/390×56 pad 16 20: negative #FA5F3D / positive #000, текст Manrope Medium 16 белый по центру.
- **Graphic**: pic-welcome 296×232 (две shape-brand с фото + эмодзи-кружки 👼🏻 🎓 🧳 👰🏻‍♀️ 🎨 💍 🥂), pic-progress-circle 320×298 (кольцо #E1D6D1/#C66F53 w12 + «60%» ExtraBold 30 + «доступно» Medium 22 + сердечки), pic-location-main, pic-directional-sign, pic-checkmark 56 (звезда #C66F53 + галка #F2E7E3 w4), pic-verification 200×240, pic-location-fail, pic-network, pic-no-people, pic-match 56, pic-chatting 56, pic-attention 56 (#C66F53 + «!»), update 56 (стрелка), main 56 (gear), pic-time 56, premium-icon 56 (#E7C9B2 логотип), pic-no-lovenotes 200, pic-no-videos 200, oopps 56 (#FA5F3D «Ой» TT Runs Bold 24 белый), invite/pic, invite/pic2.
- **Navbar**: navbar--iOS 375×44: default light (container белый; progress-cell скрыт: «Заполнено 80%» Inter Semi Bold 16 #C66F53 + .progress 3/4; title Manrope Bold 16 #000 по центру; ⟨24 / back⟩ @14, ⟨24 / close⟩ @337; button-text-right «Сохранить» / button-text-left «Отмена» Manrope Medium 16); chat light 375×88 (back, ⟨24 / menu⟩, ⟨24 / rules heavy⟩ @297, person: photo 56 + «Name Surname» Manrope Bold 16 + .status); default dark (#000, белый текст); media dark (#000: «Отмена» Manrope Bold 16 белый + timer-cell «0:00,00» Manrope 16 #B3ACA9 с .record-indicator; примечание «цифры в таймере — monospace»); navbar-custom 390×68 (counter-box + .44-button + profile-return); Navigation Bar iPhone Default Modal.
- **Loader**: Large 48 (кольцо 40×36 stroke 3), Medium 32, Small 20 — stroke #000.
- **Other**: icon-caption 320×18 (иконка 16 + текст Manrope 13 #514A47 maxLines 2); progress-bar 390×20 pad 4 12 8 12 (3 сегмента .progress gap 4); Tooltip 147×32 r12 pad 8 12 (Light #F2E7E3@50% текст Manrope 12 #C66F53 / Dark #C66F53 текст #F2E7E3; треугольник 17×8; позиции bottom-middle/top-middle/right); photo-editing 122×128 r16 (added-default: фото + кнопка 44 #7F7F7F@20% blur с иконкой (trash) @4,80; State8 «Проверяем…» чёрная плашка 28 + overlay 35%; drop-none op65%; error: плашка #FA5F3D «Ошибка» + trash + refresh; loading loader Medium; drop-target stroke #C66F53 3; drag 1.2× (146.4×153.6); placeholder #F2E7E3); photo-gallery 128 (select True/False + .counter); tag 89×42 #F2E7E3@50% r12 pad 12 16 Manrope Medium 12 #9E8075; prompt (empty 366×80 #F2E7E3@50% r16 текст #9E8075; Skeleton #F2E7E3; editing 366×140 #C66F53 (две .44-button по бокам вопроса, вопрос Manrope 16 #F2E7E3, ответ белый); on-profile #F2E7E3@50% r16 pad 12 12 20 12: вопрос Manrope Medium 14 #9E8075, ответ Manrope Bold 20 #514A47); photo-frame 366×366 r28 (Default / Helper с силуэтом op35%); pager 80×8 (6 индикаторов gap 4); counter-reel 32×112 (digits .num-box 28 высотой; Position 1/2/3/changing); gradient-bottom 390×160 (#1B1A1C 90%→0); _Message Bubble (Your Friend: #F2E7E3 r12 pad 12 16 текст SF Pro 17 (в экранах Manrope 16) + time Manrope 12 #9E8075 + Tail; you reply; Partner edit («Изменено»); From3 «Печатает» + три точки; You: #C66F53 pad 12 16 текст белый + time #E7C9B2 + Read; you edit; reply); date-separator; like-sender 366×416.59 (shape 1…6 + name-label + tag «Новый» #000 rot 15 + timer «13 ч. 48 мин» для заблюренных); photo-placeholder 128 / small 64 / big 366×396 (#F2E7E3 + силуэт #9E8075 35%); video - card; community/empty 24/32/56 (#F2E7E3@50% буква TT Runs ExtraBold #514A47); overlay 390×844 #000 60%; bottom-sheet 390 r28/28 pad 24 12 8 12 gap 8 (+ .handle-cell над листом); profile-return 56 (Default фото + overlay 35% + ⟨arrow-return heavy⟩ / None белый); counter-box 80×64 (Empty «Heart zero» stroke #E7C9B2 / Loading #F2E7E3 / Default); chat-preview 390×131.77 (Default / Skeleton / Default V3); lovenote 366×228 (Default: ««» ExtraBold 30 #E7C9B2 + text Manrope 16 #514A47 + sender фото 40 + «Константин» ExtraBold 16 #C66F53 + tag «Новая» #F2E7E3@50% r16 + .counter «3»; Skeleton; Error).
- **Cells**: city-cell 390×60 (Default «Череповец» Manrope 16; picked + ⟨16 / checkmark-heavy⟩; skeleton short/long; last-child; Region «Череповец» + «Вологодская область» #B3ACA9); video-circle (Default Big 366 / Medium 208 (+ .delete-transformer @136,8) / loading Medium / Small 128 / profile 96 stroke белый 3 / loading profile (stroke #B3ACA9 + overlay blur + loader) / placeholder profile (⟨40 / video-play⟩) / Progress Big (.player-circle-big + .sound-switcher) / loading Big (#000 + loader Large) / Paused Big (overlay 35% + .button-play)); video-add-circle 208 (Default / No-Permission); photo-add-cell 116.67×128 (Default / Disabled stroke #F2E7E3); switcher-cell 366×60 (no-border / border-bottom; текст Manrope 16 #514A47 + .swicher); checkbox-cell 366×60 (.44-button checkbox + текст); videoframe 140×198; community-cell 358×74/94 (Join / Exit / no logo / Long name).
- **Icons FOR DESIGN** (масштабы 16 light 1.33/heavy 1.5; 24 light 1.5/heavy 2/filled; 32; 40 light 2/heavy 4): 16: location-filled, empty-heavy, settings-heavy, hidden-light, revealed-light, edit (light/heavy), refresh (Light/Heavy), search-light, close-light, checkmark-heavy, match-light, rules-heavy, lock (light/heavy), video, delete (light/heavy), reply (Heavy/Light), profile; 24: revealed-light, hidden-light, lock, empty, Erase-filled, checkmark-filled, alert, back, reply, close, search, geolocation, edit, trash, mute-heavy, sound-on, checkmark, match-light, hearth (heavy/light/filled), cards (heavy/light/filled), chat (heavy/filled), profile (heavy/filled), lovenote-light, Community list, refresh, arrow-return, filters, plus, faq, support, send-note-heavy, menu, rules, go-to-chat, exit, join, up-chevron, time, copy, video, reload, fullscreen, compact, share, bookmark (heavy/add/filled), refferal, «24» (people); 32: match-filled, email; 40: video-add, camera-error, video-play, trash, gear, camera-switch, photo-add, checkmark, profile-edit, lovenote, reject (outlined/filled), heart (outlined/filled), pause. **Icons FOR FLUTTER TYPE** — те же в виде flattened vectors (+ 24-flutter/go-to-chat, 24-flutter/rules, 40-flutter/go-to-chat-filled, 24/filtering-options-heavy, back-heavy, close-heavy, search-heavy, sound-on-heavy, time-light, lock-light, edit-light/heavy, refresh-light/heavy).
- **Development**: App Icon 600 (логотип #C66F53 на белом; iOS 120/60 r26/r13; Android без скругления); Push Icon Android 96 (#C66F53 монохром); Lockscreen push шаблон; «Pressed States: Для карточек скейлим до 0.95x; Для ссылок, иконок снижаем альфу до 65%»; «Если экспортируете картинку в PNG, то делайте 3x размер»; логотип «по_любви_logo_sign_style_elements» 1920×1080; фигуры figure-male/female.
- **System Elements**: action-sheet, share-sheet iOS, cupertino-picker (value-type cm / num-low / num-high / empty+ («Добавить» #C66F53 stroke #E1D6D1 r16)), cupertino-picker-pair 296×228 (два барабана + линия 32 между), dialog--iOS (1/2H/2V/3 actions), dialog--android (Material 3 r28, Roboto), Status Bar iPhone (Light/Dark × Background), status-bar Android, home-indicator iOS, Tabbar (.tab 48×40 Selected=True: чёрная «vector-shape» 50×44 подложка + белая иконка filled; Selected=False: иконка heavy; Tab Bar iPhone 390×83 r28/28 белый, 4 таба gap 83 pad 0 24), Keyboard (Alphabetic/Numeric iPhone Big/Small/Android; Numeric+Suggestion), toolbar «Готово», permission-ios (Gallery «Приложение «По Любви» запрашивает доступ к Фото» / «Это нужно, чтобы прикрепить фото из Галереи к анкете» + «Выбрать фото...» / «Дать доступ ко всем фото» / «Не разрешать»; Mic «…доступ к Микрофону» / «Это нужно, чтобы записать кружочек и сделать фото» + «Запретить»/«Разрешить»; Cam; push «…собирается присылать вам пуш-уведомления» + «Упоминания могут содержать напоминания, звуки и наклейки значков…» + «OK»).

## Сторы (10454) — маркетинговые скриншоты (не экраны приложения)
- iOS 5.5" (1242×2208) ×7, iOS 6.5" (1242×2688) ×7, android (1080×1920) ×7, icon 512 (белый / #C66F53), Feature graphic 1024×500. Фон #C66F53 + blur-эллипсы + контурные сердца #F2E7E3@50%. Тексты слайдов (TT Runs ExtraBold 72/64 + Bold/Medium 40): «Приложение для поиска серьёзных отношений» + логотип-леттеринг + «Первый шаг к семейному счастью»; «Умный подбор партнёра» / «Найдём вторую половинку, которая разделяет твои ценности»; «Записки и лайки» / «Получай или отправляй лайки и записки чтобы привлечь внимание»; «Проверенные пользователи» / «Пользователи проходят проверку по фото»; «Видеосообщение в анкете» / «Чтобы рассмотреть потенциального партнёра получше»; «Не упусти свою любовь» / «Ответь на необязательные вопросы, и мы подберём максимально подходящего тебе человека» (+ экран мэтча «Это по любви!»); «Удобный мессенджер для общения» / «Если вы понравились друг другу, приступайте к комфортному общению». Внутри — копии экранов (карточка ленты «Хочу детей»/«Путешествия», like-анимация, lovenote, чаты, симпатии с like-sender, «Открой камеру, повтори жест и сделай селфи» + «Открыть камеру», экран '9' «Не упусти свою любовь» + «Заполнить до конца» / «Дополнить позже» + tooltip «Шанс найти пару в 2 раза выше»).

## Черновые страницы (инвентаризация, не финальный дизайн)
- **Wireframing** (7962 строк): вайрфреймы Auth/Personal/Likes/Personal 4.2/verify 1–10/media 1–5.2/main 2–21/paywall 1–1.2 с описанием пользовательских сценариев («Ввод OTP-кода Пользователь может: ввести OTP-код и перейти в авторизованную зону…», «Обязательная информация… 9 экранов…», «Необязательная информация…», «Фото и видео контент…», «Красные и зеленые флаги…», «"Подсказки" (prompts)…», «Доступна авторизация только по номеру телефона…»); тексты-кандидаты: «Пользователи с 4+ фото находят пару на 30% быстрее», «Верифицированные пользователи быстрее находят вторую половинку», «Никому не показываем дату рождения. Другие пользователи увидят только твой возраст», «Убедись, что всё верно. Исправить дату рождения будет нельзя», «Не показываем твой номер другим пользователям и не передадём третьим лицам», «Отправили код в СМС на +7(910)436-30-78 или в пуш-уведомлении», «Новый код можно будет получить через 40 с.», «Имейл потребуется для восстановления аккаунта…», «Расширить допустимый возраст, когда закончатся анкеты от 25 до 60 лет», «Увеличить радиус поиска, если закончатся анкеты людей поблизости», «Показывать обучающие видео в разделе просмотра анкет», «Премиум фильтры Открыть ->», «Средства списываются раз в месяц Условия и Политика конфиденциальности», «Заполним профиль до конца или перейдём к просмотру анкет?», «Мы проверили твой профиль, всё круто. Теперь у тебя будет галочка», «Алгоритм распознает жест в течение... Подожди ещё немного», «Парочки могут просмотреть селфи друг друга только после взаимной симпатии», «Случайные пользователи не увидят это фото», «Когда Владислав поставит лайк твоей анкете, узнай об этом сразу.», «Константин получит твою симпатию и короткое сообщение», «К кредитам относятся любые займы, рассрочки и ипотеки.», «Опиши ситуацию, и мы обязательно разберёмся», список городов (Москва, Санкт-Петербург, Казань, Самара, Нижний Новгород, Краснодар, Арзамас, Армавир, Архангельск, Астрахань…; Берёзовский Свердловская/Кемеровская область…), TODO-заметки («Определиться с премиум-подпиской: что входит…», «Определиться — добавляем ли триал-период…», «Рассмотреть триал-период. Мб как часть более дорогого тарифа»).
- **Internal Only Canvas** (7912): iOS/M3 системные компоненты, переменные цветов, шаблоны permission-алертов (Diary), типографика TTRuns-800-30-32 / Manrope-400-16-20.
- **DSGN — concept** (10237): ранние концепты (main 2.2 Video on ×7, v2/v3, «правки по коментам», «Иконки NEW», скриншоты 2024-06-11), тексты «Я выбираю тебя…», «Привет! Я Константин, 36 лет. Дизайнер…».
- **Прототипы** (8626): интерактивные прототипы онбординга (Auth 8–172), 5.1 ×10, all ×4, badge, switcher-visibility, «сделяль», paywall 1, комментарии («Ещё кнопка с глазом вызывает сомнения…», «При клике на плашки с бейджем PRO открываем предложение премиума», «Подберем пару быстрее, если ответите на вопросы:», «Увеличить диапазон поиска, если закончатся анкеты людей указанного возраста», «Часто ли вы пробуете новые практики в личной жизни?», «Если у тебя или будущего партнёра есть дети, вам стоит узнать это сразу», «Например: сидит в телефоне во время разговоров», «Это моменты, на которые ты обращаешь внимание, чтобы понять, насколько человек подходит тебе для долгосрочны…»).
- **Presentations for client** (428) и Presentation #2/#3: слайды 1920×1080 «Концепция приложения По Любви» (surf.ru), «Главный экран» («Изменения: Основной цвет фона, Иконки элементов интерфейса, Счётчик сердец, Отображение фото при возврате к анкете, Верстка информации в карточке, Пагинатор для фотографи…»), «Симпатия», «Дальше...», «Ноль сердец»; тексты «Приложение доступно только для совершеннолетних пользователей», «Отправили 6 цифр в пуш-уведомлении или в СМС на +7(910)436-30-78», «Наши психологи составили опросник для поиска серьёзных отношений. Больше ответов — больше найдётся партн…».
- **Cover**: «По любви!» / «Design_Client».
Flutter — API-слой, UI Kit, ассеты, ядро, аналитика, роутер (notes_flutter_core.md, 18343 байт)
# Flutter (po-lyubvi, main, 1.4.2+128) — API-слой, UI Kit, ассеты — прочитано полностью

## API (Dio+Retrofit, lib/api/service) — полный список эндпоинтов
- Advertising: GET /advertising (видео для карточек ленты).
- Auth: POST /auth/otp (AuthByCodeRequest → AuthByCodeResponse), POST /auth/otp/verify (→ AuthResponse).
- Chat (отдельный chats base url): GET /user-chats/{chat_id}/messages/next?batch_id&number, GET …/messages/previous, GET /user-chats/new-pairs, DELETE /user-chats/new-pairs/{user_id}, GET /user-chats/new-pairs/{user_id}, GET /user-chats?chat_id (курсор), GET /user-chats/{chat_id}, GET /user-chats/updates?last_sync.
- Cities: GET /cities?term (пусто → 20 главных городов).
- Common: POST /push-notifications/add-fcm-token, POST /users/deactivate (DeactivationReasonRequest; после — разлогин), POST /push-notifications/delete-fcm-token, POST /auth/token/refresh, GET /update-works (тех. работы/обновление; должен работать при ошибках 121/122).
- Community: GET /communities/all, GET /communities, GET /communities/{id}, POST /communities/{id}/invisible, /join, /leave, /visible.
- Ip: GET ''?format=json (внешний сервис IP).
- Likes: GET /users/likes?user_id, DELETE /users/likes/{user_id} (→ UserLikeWithTimer), POST /users/likes/{user_id} (прочтение), GET /users/superlikes?user_id, DELETE /users/superlikes/{user_id}, POST /users/superlikes/{user_id} (прочтение записок).
- Matching: POST /matching/bring-back (возврат анкеты → available_reactions), GET /matching/filters, POST /matching/like/{user_id}, PATCH /matching/recalculate (RecalculateRequest; ≥1 фильтр; координаты/город фиксируются в профиле), POST /matching/super-like/{user_id} (SuperlikeRequest = записка), POST /matching/undo-like/{user_id}, GET /matching/users, POST /matching/users/activity (статусы активности).
- Payment: POST /payments/android (PaymentAndroidRequest → PaymentAndroidResponse), POST /payments/android/status, POST /payments/ios/status, POST /payments/ios/status/restore.
- Profile: GET /faq, POST /feedback (FeedbackDto), GET/PATCH /users/profile (PersonalDetailsRequest → UserProfile), PATCH /users/profile/photos (reorder), DELETE /users/profile/photos/{id}, POST /users/profile/position, GET /users/profile/reactions, GET/PATCH /users/profile/settings, GET /users/profile/verify/status, DELETE /users/profile/video; (multipart вне Retrofit) POST /users/profile/photos (одно фото), POST /users/profile/verify/photo, POST /users/profile/video.
- Referral: POST /referral/approve?user_id, GET /referral/details.
- Subscription: GET/PATCH /subscriptions/me (ChangeSubscriptionRequest), GET /subscriptions/tariffs.
- Survey: GET /prompts (категоризованные открывашки), GET /survey.
- Users: POST /users/{id}/delete-pair (DeletePairRequest), POST /users/{id}/report (ReportRequest), GET /users/{id} (UserCard).

## UI Kit (lib/uikit) — токены и компоненты
- Цвета (AppColorScheme, light==dark палитра одинаковая — тёмной темы фактически нет): brandTerracotta #C66F53, brandPaleBeige #E7C9B2, brandPowdery #F2E7E3, brandInnocentWhite #FFFFFF, brandBlack #000000, additionalBeigeishBlack #514A47, additionalBeigeishGray #B3ACA9, additionalPowdery50a #F9F3F2 (непрозрачный эквивалент #F2E7E3@50%), additionalDarkBeige #9E8075, additionalDarkPowdery #E1D6D1, additionalErrorRed #FA5F3D.
- Типографика (AppTextScheme): cameraEmoji TT Runs 800 64/64; headingHeavy TT 800 30/32; headingLight TT 500 30/32; titleHeavy TT 700 22/24; titleLight TT 500 22/24; captionBig TT 800 18/22; captionMedium TT 700 18/22; subtitle Manrope 700 20/24 ls0.2; lineHeavy TT 800 16/20; lineBold Manrope 700 16/20 ls0.2; lineLight Manrope 500 16/20 ls0.2 (в Figma 540); baseBold Manrope 700 14/18 ls0.1; baseLight Manrope 500 14/18 ls0.1; caption Manrope 500 12/16 ls0.2. Шрифт TT-Runs + Manrope (default fontFamily Manrope).
- AppDimens: радиусы 8/12/16/20/28 (crSmall/crMedium/crBig/crLarge/crBlock); паддинги 4/8/12/16/18/20/24/32/48/64. Везде SmoothRectangleBorder (squircle, как Figma «smooth»).
- Кнопки (PLButton, TextButton без сплэша, animationDuration 1мс, минимальная ширина 64): primary — h56, r12, чёрный фон (#000), pressed #514A47, disabled фон #F9F3F2 + текст #9E8075@50%, текст lineBold, лоадер medium (28×25, stroke 2, цвет #9E8075); primarySmall h44 baseBold pad 20; primaryInverted (белый фон, чёрный текст, лоадер терракота); primaryInvertedTerracottaForeground (белая кнопка с терракотовым текстом — для мэтча/пейволла); secondary — h56 r12 stroke #E1D6D1 1.33, текст терракота (pressed 65%), белый фон; secondaryTransparent; secondarySmall (h44, текст чёрный); secondaryInverted (чёрный фон, белая обводка, терракотовый текст); tertiary (белый фон чёрный текст h56); text (h40, чёрный текст lineBold, transparent); textSecondary (красный #FA5F3D, pressed терракота); textSmall (baseBold pad 8); unselected (h44, stroke #F2E7E3 1.33, текст baseLight чёрный, pad 12/20) и selected (h44, фон терракота, текст белый) — чипы-пикеры.
- Нажатия: PlOpacityGestureDetector (opacity 0.65 за 100мс, min 44×44), PLScaledGestureDetector (scale 0.95 для карточек), PlTabGestureDetector (InkWell r20 для таббара).
- PlCheckBox: 20×20, r8, обводка терракота 2, галочка checkbox_icon_14 белая, текст lineLight #514A47, отступ 10.
- PlSwitch: 56×32, pad 4, фон терракота/пудра (disabled #E7C9B2/#F9F3F2), бегунок 26×24 белый r12, анимация 300мс.
- PlAppBar: высота 44, centerTitle, title lineBold, back = svg back 18×11.5, фон белый, elevation 0/1.
- PlBottomSheet: ручка 36×4 белая над листом (gap 8), лист белый r28 сверху (smooth), pad 12/12/24/8, viewInsets учтены; showPlBottomSheet (modal, transparent, maxHeightRatio 9/16).
- AppAlert/PlDialog: Cupertino на iOS, AlertDialog.adaptive/TextButton на Android (PlDialogAction: iOS системно-синий текст; Android — PLTextButton baseLight чёрный; TODO «обновить стиль кнопок для android, когда будет дизайн»).
- PLCopyWidget (реферал/сообщества): рамка r12 stroke #E1D6D1 1.5 (onLight) / #F9F3F2 (onDark), pad 16, текст lineBold, обрезка начала строки «...» бинарным поиском, иконка copy_24_heavy терракота.
- ErrorPlaceholder: картинка error_placeholder.png h240, заголовок captionBig терракота, подзаголовок lineLight #514A47, кнопка primarySmall «Обновить» с иконкой refresh_16_heavy; TimeoutException → тексты «Нет интернета»; иначе «Не загрузилось :(».
- Поля: PlTextInput (TextField, r12 smooth, stroke #E1D6D1, focused терракота 1.33, disabled фон #F9F3F2, contentPadding 20/18, hint #E1D6D1, label #9E8075 (floating caption), suffix erase_filled 18×16 при тексте, курсор терракота 1.5×20); PlTextArea (label + счётчик «N / 200» справа сверху caption #9E8075 на белой плашке; maxLength 200); PlSearchField (иконка search #E1D6D1 слева, hint #9E8075).
- Изображения: PLImage (OctoImage, fade 0), shimmer #F2E7E3/#F9F3F2, circle avatar с photo_placeholder при ошибке; PLSvgPicture (iOS fix scale 0.9999).
- Лоадеры: PlCircularProgressIndicator large 40×36/4, medium 28×25/2, small 16×14/1.5 (цвет по умолчанию чёрный); PLFullScreenLoader (чёрный 60% + белый large).
- Прочее: CircleGradientWidget (размытые круги фона), FeatureWidget (тег подписки: pad 9/16/9/20, r28, фон терракота@60%, иконка 24 по url/asset + lineLight белый), InactiveWrapper (opacity 0.5 + IgnorePointer), PlLineSlider (линия 2px #514A47, thumb r16 с точкой), PlScrollPicker (ListWheel itemExtent 40, фон #F9F3F2 r16, выделение 52px белое r8, вибрация), PLSlider (8 делений, −4..4, сегменты: терракота/пудра/чёрный, thumb r16 с обводкой), PLSliderWithAdditionalInfo (подписи слева/центр/справа, центр на плашке #F9F3F2 r12), PlScaffold (AnnotatedRegion системных баров по яркости фона), PLTooltip (терракота r12 pad 12/8, текст caption #F2E7E3, manual trigger).
- Тема: lightTheme/darkTheme с одинаковой палитрой; NoSplash; fontFamily Manrope.

## Ассеты
- Картинки: couple.png (сплэш/авторизация), dislike_overlay.png, like_overlay.png, error_placeholder.png, freinds_image.png (реферал), hard_update_image.png, no_users.png, photo_helper.png, referral_box.png, smiling_woman.png.
- SVG (139): alert, arrow_return_heavy, back, button_camera_aproove/take, camera_error/switch, cards_24_heavy/cards_selected, chat_24_heavy/chat_selected, checkbox_icon_14, checkmark(+24_light/filled/white), chevron_up_24, close/close_light, communities_24_heavy, community_empty_logo/list_24/logo_24_heavy, copy_24_heavy/ligh, cross_filled, edit_16/24 heavy/light, email_32, erase_filled, faq_24, female/male_photo_placeholder, figure_female/male, filtering_options_heavy, filters_24(+heavy), gear/gear_40, geolocation_24 heavy/light, go_to_chat(+filled), heart(+24_heavy/dotted/filled/selected/zero), hidden_16/24_light, join, location_filled/pic/pic_sad, lock(+24 heavy/light, light), match_16/24_light, match_filled, menu, message_read/reply/sent/tail, mute_heavy, my_message_tail, new_pairs_hearts, no_premium_icon, offline/online_heavy, oopps, pager(+selected), photo_add, photo_placeholder(+big), pic_attention/chatting/checkmark/directional_sign/match/no_lovenotes/time, play, plus_24(+heavy), premium/premium_icon, profile_24_heavy/profile_edit/profile_selected, progress_circle, referral_24/box/box_bg/box_fg, refresh_16 heavy/light, revealed_16/24_light, rules_24_light/rules_heavy, search, send_note_heavy, settings_heavy, shape_1..5 + shape_3_outline, sound_on, status_offline/online, support_24, sympathy_user_placeholder, time(+filled/light), training_arrow_left/right/pointer, trash(+24 heavy/light, 40), unverified_checkmark, user_likes_empty, verification_illustration(+waiting), verified_checkmark, video_add.
- runner: portraitUp only, Firebase (Crashlytics), первый кадр после precache couple.png для неавторизованных; AppScope через AppScopeRegister; flavors dev/qa/prod (main_*.dart).

## Архитектура/ядро (core, features/app, navigation, main)
- Конфиг: prod https://production.plbvi.ru/api/v1, stage https://stage.plbvi.ru/api/v1; chats …/api-chats/v1; WS wss://…/api-chats/v1/chat; policyUrl (Google Doc), communityRulesUrl https://plbvi.ru/help/rules, subscriptionConditionsUrl (Google Doc), referralIfoUrl (Google Doc); AppMetrica key; AppsFlyer devKey/iosAppId 6744737369/inviteLinkId 6FiA (OneLink для реферала и сообществ). Firebase project nikeadating; bundle iOS com.envisagedigitallimited.polyubvi.
- Аналитика (AppMetrica + Firebase; 40 событий): action_open_tab(sympathy/card/chat/profile), event_server_error, action_permission_push(confirm/reject), action_survey_next_step/skip_step(step_N), event_survey_complete(name, phone, year, gender!), view_verify_first_screen(source survey/profile/like/userCard/chat/deepLink/referral), action_verify_open_screen(instruction/selfie/finalScreen), action_like_user(available_likes, source heartsCounter/swipeRight/likeButton, is_premium, is_verified, survey_percentage, photos_count, is_video, is_scrolled, view_seconds), action_dislike_user(swipeLeft/dislikeButton…), action_super_like_user, action_open_note, event_change_profile, event_change_visible, event_open_chat, action_view_healthy_video(view_seconds, video_url), view_tariff_plans(source sympathy_button/sympathy_blured_card/user_cards_last_like/last_undo/last_super_like/user_cards_indicator/profile/profile_settings), action_tap_pay, event_payment_started, event_ios_payment_failure, view_android_webview_payment, view_android_webview_complete(success/fail), event_android_payment_failure, event_payment_completed(pending/success/fail; iap/acquiring; tbank_card/tbank_sbp), action_tap_restore, action_tap_not_prolonged, event_referral_details_screen, action_copy/send_referral_link(invited_users), event_referral_invite_bottom_sheet(referral_link/delayed_transition), event_referral_accept, action_referral_postpone, event_communities_details_screen, event_community_invite_bottom_sheet(community_link/delayed_transition), action_community_postpone/decline, event_community_join/leave, action_copy/send_community_link.
- ApiException special codes: 101 UserBlocked, 102 OTPCodeInvalid, 104 SendingCodeFailed, 105 OTPCodeExpired, 106 TooManyPhotos, 107 AnotherPhotoPendingVerify, 108 ProfileAlreadyVerified, 109 TryDeleteLastModeratedPhoto, 110 ReachedLimitOfReactions, 111 ServiceUnavailable, 112 PaymentPendingAcquiring, 113 FileTooBig, 114 TooManyRegistrations, 115 VerificationAttemptsLimit, 116 SBPNotLinked, 117 GiftSubscriptionStatusError, 118 UserAlreadyInCommunity, 119 JoinAlreadyRequested, 120 RejectedJoinRequest, 121 RequiredHardUpdate (+122 тех. работы); HTTP 400/404/429. Timeout → TimeoutException.
- ErrorDisplayer (снек сверху): user blocked → serverMsg или apiExceptionUserBlocked; sending code failed; OTP expired; too many registrations; 400 → apiExceptionBadRequest; 404 → apiExceptionNotFound; 118/119 community; 429 → apiExceptionTooManyRequests; любой specialCode (500) → apiExceptionBadRequest; Timeout → apiExceptionNoConnection; default → generalError.
- Снеки (SnackQueueFlow/DefaultSnackController): overlay сверху на всю ширину, фон #FA5F3D (error) / #000 (success), pad top=statusBar+16, 16 по бокам, текст lineLight белый по центру, 4 с, свайп вверх закрывает, анимация fade+expansion.
- Dio: interceptors AppVersion (header версии), UpdateError (121/122 → hard update/тех. работы), BlockedUser (101), Fresh (Bearer, refresh при 401 через /auth/token/refresh). Токены в flutter_secure_storage (AES-GCM); user data в SharedPreferences; чаты в локальной БД (ChatsDatabase/drift?); кеши картинок/видео (ImagesCacheManager, ProfileVideoCacheManager, MatchingVideosCacheManager).
- Стартовые роуты (AppStartValuesRegister): unauthorized → AuthRouter; авторизован и surveyPercentage>0 → (isFirstMatchingLaunch ? MatchingAgeRangeRoute : MainRouter); иначе OnboardingRoute(step).
- Роутер (auto_route, Cupertino-переходы): /auth (sign-in-by-sms=AuthCta, enter-phone, enter-otp); / AuthorizedRouter → main (TabsRoot: sympathy, matching(initial), chats, profile; chats/:chatId; faq; matching-first-launch; filters; profile-edit; sympathy-super-likes; sympathy-profile-preview/:userId; profile_settings; profile_card_preview; new-pair-profile-preview; match (fadeIn 300мс); verification-photo-preview; profile_delete_timer; subscription-offers; external-payment; subscription-typ; referral-route; communities-route; communities-list-route), onboarding, video-record, select-city, verification (start, instruction, waiting, selfie); /blocked-user; /debug.
- MainFlow listeners: Notifications, ProfileUpdate, ProfileReaction, VerificationStatusUpdate, SubscriptionUpdate, ProlongSubscription, MatchingActions, ReferralConfirmationInit, CommunityAccessionInit, CommunityRequestResult. App-уровень: UpdatesListener, NotificationPermissionListener, AuthReaction, Vibration, SnackQueue, MediaQuery.withNoTextScaling (масштаб текста системный отключён!), Overlay + FlutterEasyDialogs.
- Таббар: порядок Симпатии(heart) → Анкеты(cards) → Чаты(chat) → Профиль(profile); SafeArea + pad 8/24/8/24, Row spaceBetween, элемент 48×40, иконки 24 heavy/selected, InkWell r20; вибрация lightImpact на тап; аналитика первого открытия таба. Нет бейджей на табах в коде таббара (в Figma заметка про «7»).
- Тема: ThemeModeBloc (light по умолчанию; darkTheme = lightTheme — тёмной темы нет). Вибрация: error=vibrate, scroll=selectionClick, tap=lightImpact, declined=mediumImpact.
- Permission: notification/photos (Android ≤32 → storage), PermissionDenied/DeniedForever; аналитика push confirm/reject.
- RemoteConfig (Firebase): единственный ключ is_ios_subscription_enabled (default false); fetch interval 30 мин.
- Report: причины для карточек — obsceneLanguage, forbiddenMedia, behavior, catfisher; для чата — obsceneLanguage, behavior, catfisher, other; BS PlBottomSheet: заголовок subtitle по центру + список secondary-кнопок (gap 8), при загрузке — лоадер на выбранной кнопке и остальные disabled (#E1D6D1 текст); успех → снек reportBSSuccessText + pop; ошибка → ErrorDisplayer. POST /users/{id}/report {report_place: user_card|chat, report_reason}; message не отправляется (TODO).
- UrlLaunch: внешний браузер; mailto.
Flutter — auth, blocked user, updates (notes_flutter_auth_etc.md, 5563 байт)
# Flutter — auth, blocked_user, updates (прочитано полностью)

## Auth
- AuthCta (сплэш «Укажи телефон»): PlScaffold, SafeArea, Spacer; заголовок enterPhoneSplashScreenTitle headingHeavy терракота по центру + 12 + подзаголовок lineLight #514A47 + 24 + картинка couple.png (pad 31 по бокам) + 48 + enterPhoneScreenTitle titleHeavy чёрный + 8 + enterPhoneSplashScreenBodyText lineLight; Spacer; primary кнопка enterPhoneSplashScreenButtonTitle (pad 12/8). Нажатие → replace EnterPhoneRoute. (QaWrapperComponent — дебаг-обёртка на картинке.)
- EnterPhone: pad 12; 16 + title titleHeavy + 8 + subtitle lineLight + 24 + PlTextInput (prefixText «+7 » #9E8075, label, hint, autofocus, keyboard phone, маска «### ###-##-##», paste-форматтер обрезает 8/7/+7), 24 + Row по центру: lock_light терракота + 4 + securityNote baseLight #514A47; Expanded; PlCheckBox «Я старше 18 лет…» (isTermsAccepted, default false); 20; Text.rich caption: license1 (#514A47) + license2 (терракота, тап → policyUrl) + license3; 16; primary «Далее» enabled только при валидном телефоне (7+10 цифр) И чекбоксе; loading при запросе; 8. Успех → EnterOtpRoute(phone, otpToken, codeLifetime). Ошибки → ErrorDisplayer.
- EnterOtp: pad 16; 16 + title titleHeavy + 8 + Text.rich (subtitle lineLight #514A47 + телефон «+7(###)###-##-##» терракота, maxLines 2, центр) + 8 + «Изменить номер» (edit_16_heavy + baseBold чёрный; navigate EnterPhoneRoute) + 24 + Pinput 6 ячеек 56×56 r12 stroke #E1D6D1 1.33 (gap 4; focused терракота; disabled фон #F9F3F2 текст #9E8075; ошибка — красный текст), курсор терракота 1.5×20, автофокус; SMS User Consent на Android (регэксп 6 цифр); под полем AnimatedSize ошибка «apiExceptionOTPCodeInvalid» baseLight #FA5F3D (только для 102; для 102/105 поле красное + вибрация + очистка); ниже: при загрузке small-лоадер #9E8075 (minH 44), иначе таймер: «enterOTPScreenNewCodeTimerTitle» (#B3ACA9) + «N сек» / «M мин N сек» чёрный (baseLight) → по окончании — кнопка refresh_16_heavy + «Запросить новый код» baseBold. Таймер = codeLifetime с сервера. Ошибка verify показывается 4 с, затем сброс в notVerified. Resend → signIn заново, новый otpToken/lifetime, перезапуск таймера. Успех → AuthBloc login → AuthReaction: surveyPercentage>0 → replaceAll [MatchingAgeRangeRoute] (!) иначе Onboarding(step).
- Логаут (LogoutRequested): отвязка FCM токена (delete-fcm-token) → logout → очистка всех сторэджей (кроме реферального при логауте; при первом запуске — всё). Разлогин → AuthRouter или BlockedUserInfoRoute если блокировка.
- Refresh token: 3 попытки при сетевых ошибках, при 500 code 101 → blocked; RevokeTokenException → logout.

## BlockedUser (экран «Твой аккаунт заблокирован», /blocked-user)
- Триггер: любой ответ 500 с code 101 (интерцептор) или при refresh → авто-logout → экран. Чёрный фон, PlAppBar чёрный без back, справа текст «Выйти» (lineBold терракота, pad right 16) → replaceAll AuthRouter. Контент pad 16: 24 + pic_attention 56 + 16 + title headingHeavy белый центр + 8 + description lineLight белый центр; внизу: Text.rich caption (#B3ACA9 + «правила сообщества» белым с тапом → communityRulesUrl https://plbvi.ru/help/rules + #B3ACA9) + 16 + PLSecondaryButton.inverted (чёрный фон, белая обводка, терракотовый текст) с иконкой support_24 + «Написать в поддержку» → showContactUsBS (BS обращения). Кнопки «Отменить подписку» из Figma НЕТ.

## Updates (hard update / soft / тех. работы)
- GET /update-works → hard_update / soft_update / service_works (title, description, version, app_store_link, google_play_link; service: start_time, end_time). Заголовок X-App-Version на всех запросах. Интерцептор: 500 code 121 → событие hardUpdateRequired → повторная проверка. Hard update показывается только если версия приложения < required.
- HardUpdateWidget (оверлей поверх всего приложения, PlScaffold): pad 40/12 (bottom 8): «hardUpdateTitle» headingHeavy чёрный центр; Spacer; (pad 33/24) картинка hard_update_image.png + 24 + server title captionBig терракота (max 3 строки) + 8 + description lineLight #514A47 (max 6); Spacer; PLSecondaryButton «hardUpdateUpdateButton» → googlePlayLink/appStoreLink.
- Soft update и тех. работы — TODO (не реализованы: emit noUpdates). Ошибка запроса → полноэкранный ErrorPlaceholder с «Обновить».
Flutter — onboarding, notification permission, FCM (notes_flutter_onboarding.md, 6526 байт)
# Flutter — onboarding, notification_permission, notifications (прочитано полностью)

## Онбординг (OnboardingFlow, PageView без свайпа, шаги-константы)
- Шаги: 0 tellAboutYourself (сплэш «Расскажи о себе»), 1 nameAndSurname (firstStep), 2 birthday, 3 genderAndHeight, 4 city (LocationDeterminationFlow), 5…12 survey (10 шагов анкетирования с сервера GET /survey; внутри — свой PageView: N обязательных шагов + OptionalPartFlow (13) + InterestsFlow (14)), 15 occupation, 16 redAndGreens (флаги), 17 prompts (открывашки), 18 aboutYourself («Что мы забыли о тебе спросить?»), 19 media (фото/видео). Шаг хранится локально (OnboardingStorage), при повторном входе — продолжение с шага. Переходы анимированы 300 мс (кэш переходов).
- AppBar онбординга (UserAboutAppbar): скрыт (Visibility maintainSize) на шаге 0; back (svg back 18×11.5, неактивен на шаге 1 — InactiveWrapper opacity 0.5); titleWidget: PromptsAppbarTitle «N + promptsAppbarTitle» (терракота при 3), FlagsAppbarTitle (flagsTitlePart1 #B3ACA9 + red число (терракота при 7) + part2 + green + part3), InterestsAppbarTitle («N + interestsTitle», терракота когда все 10); внизу — индикатор прогресса по step (bottomIndicatorHeight; см. user_about).
- Survey: экран шага — pad 16/24: title headingHeavy терракота центр + 8 + description lineLight #514A47 + 32 + вопросы (gap 32) + доп. вопросы (появляются при выборе ответа с вложенными questions; при смене ответа старые доп. ответы удаляются). Вопрос: если не обязателен — «surveryOptionalQuestion» lineLight #B3ACA9 + 8; title subtitle (Manrope 700 20) чёрный центр; description lineLight #B3ACA9; 20; ответы: single — Wrap 8/8 центр, кнопки minWidth 120, PLButtonStyles.selected (терракота, белый текст baseLight) / unselected (stroke #F2E7E3 1.33, чёрный текст), h44, повторный тап снимает выбор; multiple — Wrap чипов, toggle; slider — Row: leftLabel … центр плашка #F9F3F2 r12 pad 12/16 с hint (baseLight #9E8075) … rightLabel, затем PLSlider (min 0, max N-1, divisions N-1; терракота/чёрный сегменты).
- Нижняя панель survey (скрыта на optional/interests): pad 12/12; если step.canHideAnswers — Row revealed_16_light/hidden_16_light терракота + surveyAnswerVisibleStatus / surveyAnswerInvisibleStatus (baseLight #514A47) (видимость определяется: step != stabilityStepInRequest — то есть шаг «Стабильность» скрыт от других по умолчанию, свитчера в UI нет — только статус); кнопка primary h56 «surveyNextButton» (из онбординга) / «surveySaveButton» (из редактирования; enabled только если ответы изменены), enabled когда есть ответы на все обязательные вопросы шага. Loading/Error (ErrorPlaceholder + reload).
- Submit (шаг media): fetchProfile → собрать personalInfo/additionalInfo из локальных сторэджей + isVisible=true + survey (SurveyItemEntity show/step/questions) → PATCH /users/profile; успех → аналитика event_survey_complete (name, phone, year, gender) → OnboardingNavigationBloc: если есть referrerId (реферал) → replaceAll [MatchingAgeRangeRoute], иначе replaceAll [MatchingAgeRangeRoute, VerificationRouter] (т.е. после онбординга сразу открывается верификация поверх экрана возраста!). Ошибка → снек.
- Аналитика: action_survey_next_step / skip_step по каждому шагу.

## Notification permission (BS)
- SympathyNotificationPermission (после лайка): pic_match 56 + 8 + notificationPermissionRequestTitle subtitle центр + 8 + notificationPermissionRequestSubtitle(name) lineLight + 16 + primary «notificationPermissionRequestForwardBtn» (или «chatNotificationPermissionSettingsButton» → openAppSettings если permanentlyDenied) + 8 + secondary «notificationPermissionRequestSkipBtn». Не показывается, если permission granted/initial. После возврата из настроек — повторная проверка; granted → pop.
- ChatNotificationPermission (после первого сообщения): pic_chatting + 8 + chatNotificationPermissionRequestTitle + 8 + chatNotificationPermissionRequestReason(userName) + кнопки chatNotificationPermissionRequestButton / Settings + chatNotificationPermissionRequestSkipButton.

## Notifications (FCM)
- Init только при granted permission; токен → POST add-fcm-token (на chats base url!) при auth; onTokenRefresh; iOS foreground presentation; Android foreground → local notification (канал po_lyubvi_default_notification_channel «Уведомления», importance high, icon @drawable/ic_notification, цвет #C66F53 prod/qa). При возврате из фона — перепроверка permission.
- Диплинки пушей (data.deeplink, host): user-cards → Matching; likes → Sympathy; super-likes → Sympathy + SuperLikes; chats → ChatsList; chat-details?chat_id → ChatsList + Chat; profile; profile-edit → Profile + ProfileEdit; verify?is_successful (true → Profile; false → Profile + VerificationRouter); subscription?is_successful (iOS без remote-флага → Profile; true → Profile; false → Profile + SubscriptionOffers(other)); referral → Profile + ReferralProgram(deeplink); referral-accrual → ReferralCongrats(isLinkOwner true); referral-verify → ReferralCongrats(false); community / community-check?community_id&is_accepted / community-exclusion?community_id → CommunitiesRoute(deeplink). Если первый запуск матчинга ещё не пройден — любой диплинк → MatchingAgeRangeRoute.
Flutter — экраны шагов онбординга / редактирования (notes_flutter_user_about.md, 8328 байт)
# Flutter — user_about (экраны онбординга/редактирования) — прочитано полностью

- Общее: все экраны шагов = SafeArea + PlScaffold, заголовок headingHeavy (TT Runs 800 30/32; терракотовый только на сплэше и survey), подзаголовок lineLight; кнопки внизу в pad 12/8; из редактирования профиля (isFromOnboarding=false) кнопка = «Сохранить» и enabled только при изменении; из онбординга — «Далее», плюс secondary «Пропустить» (AnimatedCrossFade: пока поле пустое показывается «Пропустить»).
- Прогресс-бар онбординга (UserAboutAppbar bottom 20px): 3 сегмента (pad 12, gap 4, высота 6, фон #F2E7E3, заливка терракота, r5 smooth): 1) step/5 (шаги 0–5), 2) (step−4)/(14−5) (survey→interests), 3) (step−13)/(19−13) (optional→media); при заполнении сегмента показывается оверлей-подсказка (плашка #F9F3F2 r12 pad 12/8, caption терракота + треугольник 17×8) с текстами aboutUserAppBarFirstMsg / aboutUserAppBarSecondMsg (у 3-го нет).
- Сплэш «Расскажи о себе» (шаг 0): pad 16/8, title терракота + 12 + subtitle #514A47 + 48 + svg progress_circle (pad 19); primary aboutUserSplashScreenButtonLabel.
- Имя (шаг 1): pad 12/8, 24 + enterNameScreenTitle (headingHeavy, слева) + 24 + PlTextInput Имя (label/hint, formatter [\sа-яА-Я-], maxLength 100, autofocus) + 12 + Фамилия + 4 + Row (pad left 20): hidden_16_light #9E8075 + enterNameScreenPrivacyNoteText baseLight #9E8075. Низ: (онбординг) edit_16_light терракота + enterNameScreenPossibleToEditNote baseLight #514A47, 16, primary «Далее»/«Сохранить» (enabled: оба непустых (trim с «-»); при редактировании — только если изменилось).
- Дата рождения (шаг 2): pad 12; 24 + birthDateTitle центр + 24 + Pinput 8 ячеек 38.75×56 r12 (stroke #E1D6D1 1.33, focused терракота, текст lineLight; маска-подсказка birthDateFieldObscuringTextMask посимвольно (ДДММГГГГ) #E1D6D1, разделители «.» после 2 и 4 ячейки, курсор мигает 450мс, контекстное меню отключено); ниже: invalid → birthDateValidationError baseLight #FA5F3D (pad top 12); valid → 42 + возраст headingLight терракота (birthDateAgeValid(N) или birthDateAgeInvalid если <18) + 12 + caption (birthDateAgeValidCaption / birthDateAgeInvalidCaption) lineLight центр. Низ: lock_light терракота + 6 + birthDatePrivacyHint baseLight; 16; primary birthDateNextBtnLabel enabled только при valid и ≥18. Валидация: год ≥1900 и не в будущем, несуществующая дата → invalid.
- Пол и рост (шаг 3): 24 + title (userGenderHeightScreenTitle / только рост: userGenderHeightScreenOnlyHeightTitle); (онбординг) 48 + userGenderHeightScreenSubtitle headingLight #B3ACA9 + 8 + Row центр: AnswerButtonWidget «Мужчина»/«Женщина» (selected терракота / unselected stroke; gap 8); 48 + userGenderHeightScreenHeightSubtitle headingLight #B3ACA9 + 12 + PlScrollPicker 248×228 (значения 50…250 «N см», default 180, выбранный чёрный, остальные #B3ACA9) + 86. Низ: revealed_16_light терракота + userGenderHeightScreenCaption baseLight #514A47; 16; primary userGenderHeightScreenButtonLabel / SaveButtonLabel (enabled: пол выбран (в онбординге) и рост; при редактировании — рост изменён). Из профиля редактируется только рост (пол не меняется).
- Город (шаг 4): LocationDeterminationFlow (см. notes location).
- Survey (5–12): см. notes_flutter_onboarding.
- Разводная необязательной части (13, OptionalPartView): центр: pic_directional_sign + 32 + optionalSegmentScreenTitle headingHeavy + 8 + optionalSegmentScreenSubtitle lineLight; низ primary optionalSegmentScreenNextBtnLabel (→ next) + 8 + secondary optionalSegmentScreenSkipBtnLabel (→ skipToMedia, шаг 19).
- Интересы (14): pad 16/24; title = questions.title (шаг survey с requiredToAnswer=false) headingHeavy + 8 + description lineLight #514A47 + 32; по категориям: question.title lineBold #9E8075 + 16 + Wrap 8/8 чипов r28: selected — терракота фон, белый baseLight, pad 20/13; unselected — белый stroke #E1D6D1 1.33; inactive (уже 10 выбрано) — фон #F9F3F2 текст #9E8075 без тапа. Макс 10. Аппбар: «N + interestsTitle» (терракота при 10). Низ: primary interestsForwardBtn/interestsSavedBtn (loading пока грузится) / secondary interestsSkipBtn (когда 0 выбрано). Ответы сохраняются в survey шаг 9 (InterestsConstants.onboardingStepNum = 9).
- Должность (15): pad 12; 24 + userOccupationTitle + 8 + userOccupationSubtitle + 32 + PlTextInput (maxLength 50, formatter буквы/цифры/.,!?:;, autofocus, label/hint). Низ primary userOccupationNextBtnText / SaveBtnText или secondary Skip.
- Флаги (16, RedFlagsView): ListView pad 16: 24 + redFlagsScreenTitle + 8 + redFlagsScreenSubtitle + 24 + redFlagEmoji (subtitle) + 4 + redFlagsMeaning (subtitle) + 8 + «redGreenFlagsExampleLabel redFlagsExample» baseBold #B3ACA9 + 16 + WrappedTextList (Wrap 8/8 центр: чипы чёрные r28 pad 11/20 текст белый baseLight maxLines1 + иконка close_light 10×8 (44 зона) для удаления; кнопка «redFlagsAddBtnLabel» белая stroke #E1D6D1 1.33 r28 — скрыта при 7 флагах) + 24 + greenFlagEmoji + 4 + greenFlagsMeaning + 8 + пример + 16 + список. AppBar: FlagsAppbarTitle (терракота при 7). Добавление — BS (AddFlagBottomSheet: PlTextInput maxLength 30 autofocus label/hint + 8 + primary redFlagsScreenBSBtnText). Низ: primary redFlagsScreenNextBtnText / profileEditSaveBtnLabel или secondary redFlagsScreenSkipBtnText.
- Открывашки (17, PromptsView): pad 12; VerticalScrollableTabView: заголовок (pad 16: 24 + promptsTitle + 8 + promptsSubtitle + 16), SliverAnimatedList ответов (PromptAnswerWidget: терракотовая карточка r16 pad 12/12/12/20: Row trash_24_heavy белый | вопрос baseLight белый центр | edit_24_heavy; 12; ответ subtitle белый центр; gap 8), pinned SliverAppBar (56) с горизонтальным списком категорий (чипы r28 pad 20/11: selected терракота+белый / белый stroke #E1D6D1; ScrollablePositionedList, gap 8), 16, затем секции категорий: name subtitle центр + 12 + список вопросов (PromptQuestionWidget: #F9F3F2 r16 pad 20, lineLight #9E8075 центр; gap 8) + 20. Макс 3 ответа (тап на вопрос игнорируется при 3). BS ответа (PromptsBottomSheet): вопрос subtitle центр + 20 + PlTextArea (label promptsAddLabel, hint promptsAddHint, maxLength 120, 1 строка, autofocus) + 8 + primary promptsAddSaveBtn. Загрузка: шиммеры (5 чипов 76×12, плашка 120×20, карточки 60px r16); ошибка — ErrorPlaceholder. AppBar: «N + promptsAppbarTitle» (терракота при 3). Низ: primary promptsForwardBtn / promptsSaveBtn или secondary promptsSkipBtn.
- О себе (18, AboutMeView): pad 12; 24 + aboutMeTitle + 8 + aboutMeSubtitle + 32 + PlTextArea (maxLength 2000, 1–5 строк, label aboutMeLabel, hint aboutMeHint, autofocus). Низ: primary aboutMeSubmitButton / aboutMeSaveButton или secondary aboutMeSkipButton.
- Медиа (19): см. notes media.
Flutter — геолокация, город, медиа, запись видео (notes_flutter_location_media.md, 9446 байт)
# Flutter — location_determination, media (прочитано полностью)

## Геолокация (шаг 4 онбординга и фильтры)
- LocationDeterminationView: PlScaffold, SafeArea, скролл; pad 16/24: locationDeterminationTitle headingHeavy чёрный центр + 8 + locationDeterminationSubtitle baseLight чёрный центр; состояния: loading — large-лоадер #E1D6D1 по центру; firstRequest/done — Expanded svg location_pic + primary locationDeterminationShareLocationButton (pad 12, снизу 8) (НЕТ кнопки «Пропустить»/«Выбрать город» в этом состоянии, хотя интерфейс имеет onSkipPressed → skipToSurvey); openSettingsOrChooseCity — центр: location_pic_sad + 24 + locationDeterminationErrorTitle captionBig терракота + 8 + locationDeterminationErrorSubtitle lineLight #514A47 + 24 + Row: primarySmall (settings_heavy белый + locationDeterminationSettingsButton) + 8 + secondarySmall locationDeterminationSelectCityButton; citySelected — Spacer, locationDeterminationSelectedCityTitle baseLight #9E8075 + 8 + cityName headingLight терракота + 24 + secondarySmall «Выбрать другой город» + 48; Spacer; внизу pad 12/8: secondary «locationDeterminationLocationSettingsButton» + 8 + primary «locationDeterminationSaveAndContinueButton».
- Логика: на входе (и при возврате из настроек) navigateByLocation: saved auto+granted → lastKnown → done (в онбординге done автоматически → nextPage) + актуальная позиция в фоне (POST /users/profile/position {position:{lat,long}}); notSaved+granted → определить и отправить; denied → firstRequest; permanentlyDenied → openSettingsOrChooseCity; manual → citySelected. Кнопка «Поделиться» → requestPermission → determine+send (успех: saveAuto; ошибка сети → снек + firstRequest; нет геопозиции → openSettingsOrChooseCity). Выбор города → SelectCityRoute → POST position {city_id} → saveCity → citySelected. Настройки → Geolocator.openLocationSettings.
- SelectCityView: PlAppBar без back, справа close 24; PlSearchField (pad 12/8, autofocus, hint selectCitySearchBarHintText); GET /cities?term (пусто → главные города; поиск при ≥3 символах, debounce 500мс, иначе кэш); список: строка pad 20/16: name lineLight чёрный + 12 + region caption #B3ACA9 (ellipsis) + checkmark 16 терракота если выбран (выбранный город поднимается первым); Divider #F9F3F2 1px; PLScaledGestureDetector; loading — шиммеры 104/144×20 (pad 20/18) с divider; empty — selectCityEmptyStateTitle captionBig терракота + 8 + selectCityEmptyStateSubtitle; error — ErrorPlaceholder. Выбор → pop(city).

## Медиа (шаг 19 онбординга; виджеты переиспользуются в редактировании профиля)
- MediaContentView: CustomScrollView: 24 + mediaContentTitle headingHeavy чёрный центр (pad 16) + 8 + mediaContentDescription lineLight #514A47; если галерея И камера permanentlyDenied → _PermissionsDeniedWidget (gear_40 чёрный + 24 + mediaContentPhotoPermissionsNotGranted captionBig терракота + 8 + …Description lineLight + 24 + SettingsButton primarySmall settings_heavy + mediaContentSettingsButton); иначе: блок 256px высотой с VideoContentComponent по центру, подпись (mediaContentDragAndDrop если >1 фото / mediaContentOnePhotoMinimum lineLight #B3ACA9 центр), 16, GalleryContentFlow (pad 12). Низ (белый, pad 12/8): primary mediaContentSaveButton (enabled если есть ≥1 загруженное фото; loading при submit) → OnboardingComponent.submit.
- VideoContent (кружок 208): permissions denied (camera/mic) → круг stroke #F2E7E3 1.33: 36 + mediaContentCantOpenCamera caption #9E8075 (pad 45) + 16 + gear_40 + 4 + mediaContentSettingsButton baseBold → openAppSettings; loading — круг stroke #E1D6D1 + large loader; empty — круг stroke #E1D6D1, video_add 40 терракота, (не онбординг) внизу 16 «mediaContentPercentForVideo» baseLight #9E8075; recorded/uploaded — EternalVideoPlayer (кругл. клип, loop, mute, тап → запись заново) + кнопка удаления (44 белая r20 trash_24_heavy; первый тап → расширяется до 100 красная #FA5F3D с текстом mediaContentDeleteQuery lineBold белый, 3 с; второй тап → удалить); loading — круг #F9F3F2 + medium loader #9E8075. addVideo → запрос camera+microphone → VideoRecordRoute.
- VideoRecordView (полноэкранный, чёрный): AppBar h44 чёрный: слева PLTextButton videoRecordCancel (lineLight белый; #514A47 во время записи), title: точка 8 (терракота при записи / #B3ACA9) + 8 + таймер «m:ss,ms» lineLight #B3ACA9 (макс 10 с); тело: квадрат (ширина−24) по центру — CameraPreview в круглом клипе (CircularPlayerClipper) + прогресс-кольцо (PlaybackProgressPainter width 3 pad 4 цвет #F9F3F2@50%); loading — large белый; camera error — круг 208 stroke #514A47 1.3 + camera_error 40 белая + 8 + videoRecordCameraError baseLight белый; recorded — CircularVideoPlayer; тулбар (pad 16/24, h72): trash_40 (белая если recorded, иначе #514A47) | кнопка записи 72 (обводка белая 3; внутри 58→24 терракота круг→квадрат r4 при записи; после записи — белый круг 66 с checkmark 40 терракота = upload; disabled при uploaded/error, opacity 0.5) | camera_switch 40 (белая только в camera-состоянии и не при записи). Оверлей загрузки: чёрный 60% + лоадер 48/4 белый. Ограничения: 10 с, 50 МБ (VideoTooBigException → videoRecordingFileLimitation); ошибки: videoRecordingFailed → cameraExceptionVideoRecordingFailed; IOError/cannotCreateFile → cameraExceptionNoSpace; 113 → videoRecordFileTooBig; timeout → videoRecordingTimeoutError. После upload (POST /users/profile/video multipart 'video' video/mp4) → кэш по ETag → pop. Удаление — DELETE /users/profile/video. Пауза приложения останавливает запись.
- Галерея (GalleryContentView): ReorderableGridView 3 колонки, gap 8, aspect 116/128; ячейка 116×128 r16 smooth фон #F2E7E3 cover; загруженное фото: (не онбординг и !isModerated) → затемнение #000@35% + шапка 28px чёрная r16 сверху с profileEditPhotoModeratingTitle caption белый; кнопка удаления 44×44 r12 слева-снизу (4,4) blur 5 фон #000@40% trash_24_heavy белый (скрыта при drag/reorder; при reorder opacity .65 + лоадер 32 белый); drag placeholder: пунктир терракота 3 [12,12] r16 / #F2E7E3 плашка; loading: opacity .65 + лоадер; error: затемнение + шапка красная #FA5F3D 28px с mediaContentError caption + кнопки refresh_16_heavy (справа-снизу 8/12) и trash_24_heavy (слева-снизу); пустая: 116×128 (в коде height 116 width 128 — перепутано, но grid задаёт размер) белая stroke #E1D6D1 1.33 r16, photo_add 40 терракота, (не онбординг) внизу 8 «mediaContentPercentForPhoto» baseLight #9E8075; loading галереи — 6 пустых ячеек opacity .5. Порядок: загруженные → loading → error → пустые. Макс 6 фото, 10 МБ (PhotoTooBigException → mediaContentExceedsLimit), сжатие в jpeg (FlutterImageCompress), POST /users/profile/photos multipart 'photo'; reorder → PATCH /users/profile/photos {photos:[{id,order}]}; удаление DELETE /users/profile/photos/{id}; удаление последнего фото → BS DeleteLastPhoto (pic_attention 56 + 8 + mediaContentDeleteLastPhotoBSTitle subtitle + 8 + …Subtitle lineLight + 16 + secondary mediaContentDeleteLastPhotoBSButtonText); ошибки: 400 → mediaContentSendFail, 109 → mediaContentCannotDeleteLastPhoto, 113 → mediaContentFileTooBig, 106 → mediaContentTooManyFiles, timeout → mediaContentTimeoutError. Вибрация selectionClick при drag.
- PhotoPickBottomSheet (pad 12, top 24, bottom 8): secondary mediaContentMakePhoto + 8 + secondary mediaContentPickFromGallery; если камера permanentlyDenied → текст mediaContentCameraDisabled + 24 + SettingsButton + 28; если галерея denied → (порядок реверсирован) SettingsButton + текст mediaContentGalleryDisabled. Выбор из галереи — множественный (до свободных слотов), камера — image_picker.
Flutter — верификация, симпатии, общие плееры (notes_flutter_verification_sympathy.md, 11130 байт)
# Flutter — verification, sympathy, common (прочитано полностью)

## Верификация (VerificationRouter: start → instruction → selfie → waiting)
- StartVerification («Разводная»): Spacer; pad 16: svg verification_illustration + 16 + startVerificationTitle headingHeavy чёрный центр + 16 + 4 «преимущества» (pad 12 вертикально: иконка терракота match_24_light / revealed_24_light / checkmark_24_light / time_light + 4 + текст lineLight #514A47 центр: startVerificationAdvantageFirst…Fourth); Spacer; pad 12/8: primary startVerificationGetCheckmark → Instruction (analytics instruction); secondary startVerificationSkipButton → закрыть флоу (removeRoute VerificationRouter).
- Instruction: PlAppBar с back; справа текст «verificationInstructionSkipButton» lineBold чёрный (только если ещё нет verifiedPhotoUrl); тело: картинка (pad 12, квадрат r28 smooth) — smiling_woman.png или своё прошлое селфи (cachedNetwork) + 16 + текст (pad 16) verificationInstructionTitle / verificationInstructionRepeatTitle lineLight #514A47 центр; низ (pad 8/12): hidden_16_light терракота + verificationInstructionOpenCameraSubtitle baseLight #514A47; 16; primary verificationInstructionOpenCameraButton / …RepeatOpenCameraButton (disabled пока грузится профиль). Нажатие → запрос camera permission → granted → SelfieRoute (analytics selfie); denied → экран: gear_40 + 24 + verificationInstructionPermissionDeniedTitle captionBig терракота + 8 + …SubtitleFirst lineLight + 4 + …SubtitleSecond + 24 + primarySmall settings_heavy + verificationInstructionSettingsButton (openAppSettings; при возврате — перепроверка). Ошибка загрузки — ErrorPlaceholder.
- Selfie (чёрный экран): PlAppBar чёрный, слева selfieCancelButton lineBold белый (pad 16, leadingWidth 96) → pop; тело pad top16/bottom24: квадрат (pad 12, r28) CameraPreview (front, veryHigh, без аудио) + поверх photo_helper.png (рамка-подсказка) / после снимка — превью фото (flipX если фронталка); центр (Expanded): selfieHintEmojiTitle (cameraEmoji TT Runs 800 64) + 8 + selfieHintTitleTakePhoto / …CheckPhoto titleHeavy белый + 8 + selfieHintSubtitleTakePhoto / …CheckPhoto baseLight #B3ACA9; низ Row spaceBetween (pad 16): trash (белая после снимка / #514A47) | button_camera_take / button_camera_aproove (svg) | camera_switch (белая до снимка). Отправка: crop до квадрата по центру, зеркалирование, PNG, сжатие → POST /users/profile/verify/photo multipart 'photo' photo.png; loading overlay чёрный 60% + large белый; успех → replace VerificationWaitingRoute (analytics finalScreen); ошибки: 107 → selfieAnotherPhotoPendingVerifyErrorText, 108 → selfieProfileAlreadyVerifiedErrorText, 115 → apiExceptionVerificationAttemptsLimit.
- Waiting (WillPopScope false — назад нельзя): pad top16/bottom8; центр (pad 16): svg verification_waiting_illustration + 32 + verificationWaitingTitle headingHeavy чёрный + 8 + verificationWaitingSubtitle lineLight #514A47; низ pad 12: secondary verificationWaitingEnableNotificationButton (запрос push permission; permanentlyDenied → openAppSettings; после — fetch статуса и закрыть) + 8 + primary verificationWaitingCloseButton (fetch статуса → закрыть флоу). Full-screen loader при запросе.
- VerificationStatusBloc: GET /users/profile/verify/status → accepted/pending/rejected/null; обновляется при foreground-пуше verify, при появлении сети, при возврате из фона.

## Симпатии (таб 1, SympathyView)
- Экран: ScrollablePositionedList: [0] заголовок (pad 12/24/12/32): sympathyTitle headingHeavy терракота центр + 8 + sympathySubtitle lineLight центр; [1] блок записок: loading — «sympathyNotesTitle» titleHeavy чёрный + 8 + карточка-скелет + 24 + «sympathyLikesTitle» titleHeavy; пусто — sympathyNotesTitle + 8 + sympathyNotesEmptySubtitle lineLight центр (pad 16) + 16 + svg pic_no_lovenotes + 44 + sympathyLikesTitle; есть — RichText «sympathyNotesTitle» titleHeavy + « N» titleLight (totalCount) + 8 + SympathyScreenNotesCardWidget (pad 12; вертикальный pad 24; две подложки повернуты +6° (#F9F3F2) и −6° (#F2E7E3) r28 + основная карточка SuperLikeCardFrame (белая, stroke #F2E7E3 1.33 r28, minH 184, pad 20/16/24/16) с первой запиской (без маркера «новая»); бейдж непрочитанных 24 круг терракота с числом baseBold белый справа-сверху) → тап → SuperLikesRoute; + 24 + sympathyLikesTitle; [2..] карточки лайков: SympathyLikeCardWidget (pad 12, AspectRatio 366/416, SvgClip формой shape_1..5 по кругу (likeIndex % 5), фото cover (blur 30 если isBlurred), placeholder #F2E7E3 + лоадер, ошибка — sympathy_user_placeholder; плашка имени: терракота r12 pad 32/20 maxW 270: name captionMedium белый + « age» titleLight, повёрнута ±5°, слева (чётные, left 20) / справа (нечётные, right 20) внизу; «sympathyNewLikeHint» (непрочитанный) — чёрная плашка r12 pad 16/12 baseBold белый rot 15° справа (right 15) сверху); карточки вращаются при скролле (angle = leadingEdge*10°, первая — 0), отступ снизу 48; тап → SympathyProfilePreviewRoute(like) если не blurred; если blurred → SubscriptionOffers(sympathy) (iOS без флага — вибрация declined). Заблюренные показываются под псевдонимами (sympathyMale1..10 для женщин / sympathyFemale1..10 для мужчин, по index%10). Таймер на первой заблюренной карточке: TimerWidget — time_filled 48×44 + 14 + «N ч M мин» captionBig белый (тик каждые 10 с; по истечении — снять blur с первой). Пагинация: за 3 карточки до конца (VisibilityDetector) — loadMore (курсор userId, totalCount); ошибка → снек. Пусто (0 лайков): SympathyLikesEmptyWidget — sympathyLikesEmptyTitle captionBig терракота + 8 + sympathyLikesEmptySubtitle lineLight (pad 16) + svg user_likes_empty с градиентной маской (#F2E7E3@0 → #E7C9B2@50). Loading: LikeCardLoadingWidget (шиммер shape_1). Ошибка: заголовок + ErrorPlaceholder.
- FAB (centerDocked): если нет подписки и есть заблюренные лайки → primary «sympathySubscriptionBtn» (pad 12/8) → SubscriptionOffers(sympathy); под список добавляется 72px. AppBar-оверлей (AnimatedOpacity 150мс, появляется когда блок записок [1] ушёл из видимости): белый 44px: sympathyLikesTitle lineBold по центру + chevron_up_24 справа (right 14) → scrollTo(0).
- Данные: GET /users/likes?user_id (likes[{is_blured,is_read,user_info{birthday,first_name,match_level,photo_preview_url,user_id}}], timer_expires_at, pagination.total) + GET /users/superlikes (love_notes[{is_read,message,user_info}], unread_count, pagination.total). Обновление: возврат из фона, foreground-пуш likes/super-likes, действия над юзером (лайк/дизлайк/записка/мэтч/жалоба удаляют карточку и пересчитывают totalCount), подписка куплена → снять blur, подписка истекла → перезапрос.
- SuperLikesView («Записки», SuperLikesRoute): PlAppBar title sympathyNotesAppbarTitle + back; список карточек (pad 12, gap 8, top 16): SuperLikeCardFrame + SuperLikeCardWidget (minH 185: «« » headingHeavy #E7C9B2 + 4 + message lineLight #514A47; 28; Row: аватар 40 круг + 12 + firstName captionBig терракота; непрочитанная — плашка «sympathyNotesNewNoteHint» #F9F3F2 r16 pad 8/16 caption терракота справа-сверху (−4,−8)); тап → SympathyProfilePreviewRoute(superLike) (analytics action_open_note); пагинация за 5 до конца; loading — 5 скелетов; пусто → navigate обратно на Sympathy (empty state не отрисован — TODO).
- SympathyProfilePreview: PlAppBar title = fullName + TooltipDisplayer (тултип «закончились лайки»); тело: 16 + карточка (pad 12, высота 84% экрана) CardActionsComponent(UserCardComponent) + UserDescriptionWidget (pad 12; кнопка «Отправить записку», «Пожаловаться» → ReportBS(cards)); FAB centerFloat: UserCardActionsButtonsWidget (dislike/like). Логика лайка: нет лайков и не верифицирован/pending → VerificationRouter (analytics like); нет лайков + iOS без флага → тултип; нет лайков и нет подписки → SubscriptionOffers(endedLikes); нет лайков → тултип; иначе MatchingActions.likeRequested + удалить из списка + анимация свайпа (swipe 1500мс, action 1050мс) + pop. Дизлайк — dislikeRequested + удалить + свайп + pop. Записка: нет суперлайков + нет подписки → SubscriptionOffers(endedSuperLikes); иначе showSuperLikeBottomSheet. Премиум-иконка → SubscriptionOffers(sympathy) (если нет подписки); галочка → VerificationRouter (если не verified/pending). При просмотре — POST /users/likes/{id} или /users/superlikes/{id} (прочтение). Успешная жалоба/мэтч/записка → pop.

## Common
- TooltipDisplayer (PLTooltip, авто-скрытие 3 с), CircularVideoPlayer (кружок: тап пауза/плей с оверлеем #000@35% и play 112, прогресс-кольцо (width 6→3, pad 8→4, #F9F3F2), кнопка звука 44 белая r20 mute_heavy/sound_on в позиции (54, dim−54), loop через cycle), FullScreenVideoPlayer (чёрный фон, cover, тап пауза, звук справа-снизу 8/8, прогресс-полоса 6px #F2E7E3/терракота pad 24/20), MediaPlayerLayoutDelegate/CircularPlayerClipper, AnalyticsComponentMixin.
Flutter — лента, мэтч, записка, обучение, фильтры (notes_flutter_matching.md, 15319 байт)
# Flutter — matching (лента анкет, фильтры, мэтч, записка, обучение) — прочитано полностью

## Загрузка ленты (MatchingFlow, таб 2 «Анкеты»)
- Порядок: LocationDeterminationBloc (navigateByLocation) → при done/citySelected → MatchingReactionsBloc.started (GET /users/profile/reactions, 3 попытки) → MatchingBloc.started(locationFilter, shouldRecalculate). Если гео не определена (permanentlyDenied/нет города) — в теле ленты показывается LocationDeterminationComponent(isFromOnboarding:false) (экран «Где ты находишься?» с кнопкой «Поделиться» / настройки+выбрать город). Нет сети → ErrorPlaceholder «Нет интернета».
- MatchingBloc: если есть ageRange первого запуска или гео поменялась → PATCH /matching/recalculate {filters:{age/location}, not_shown_user_ids: сохранённые} и сохранить карточки; иначе — сохранённые карточки из локального кэша (+ POST /matching/users/activity для актуализации онлайн-статусов) или GET /matching/users. Пагинация: при 3 или 0 оставшихся карточках → loadNextPage (до 3 попыток; hasNextPage=false при пустом ответе). После свайпа карточка удаляется из списка и кэша (shownCards++). Действие над юзером (лайк/дизлайк/жалоба и т.д. из других экранов) удаляет его из ленты.
- Обучение (первый вход, флаг shouldShowTraining): SwipeTraining → ScrollTraining → trainingEnd. Реклама/«здоровые видео»: если profile.showTrainingVideo → GET /advertising (show_frequency, videos[{order,video_url}]); каждые showFrequency карточек показывается полноэкранная видео-карточка (FullScreenVideoPlayer, свайп влево/вправо закрывает; громкость запоминается; аналитика action_view_healthy_video), контроллер предзагружается за 3 карточки.
- Видео-аватары: видео карточек (preview url) кэшируются (MatchingVideosCacheManager, 5 вперёд), для текущей и следующей карточки — контроллеры (loop, mute); картинки предзагружаются (5 карточек, кэш до 50).

## MatchingView
- AppBar h68 = ReactionsBarComponent: слева (pad 20) кнопка Undo 56×56 (круг с фото последнего юзера + затемнение 35% + arrow_return_heavy белая; без юзера — иконка #B3ACA9; масштаб/прозрачность реагируют на свайп); центр — сердце heart 42×34 (терракота с числом лайков внутри TT Runs 600 16 белый, счётчик анимируется при свайпе (цифры «едут» вверх); 0 лайков — heart_dotted; загрузка — heart #F2E7E3; поворот на 22.5° (0.0625 turns) если лайки >0) + _HeartAnimationWidget (сердце «вылетает» при свайпе вправо до 30%); справа (pad 26) filters_24_heavy (чёрная; #B3ACA9 пока не загружено). TooltipDisplayer над баром (тултип «закончились лайки», текст matchingEndedLikesWaitTooltipText(«N ч M мин» до конца дня)).
- Тело: CustomScrollView (скролл заблокирован при обучении/рекламе): 4 + карточка (pad 12, высота = экран−12) UserCardsStackWidget + 8 + UserDescriptionWidget (pad 12). FAB centerFloat: UserCardActionsButtonsWidget появляется при скролле > высоты/4 (pad 8/12: круг 80 чёрный cross_filled 40 белая слева | круг 80 терракота heart_filled 40 справа; disabled → #B3ACA9 фон, иконка #514A47; обводка белая 3; scale-анимация нажатия). Загрузка — large лоадер #E1D6D1; ошибка — ErrorPlaceholder.
- UserCardsStackWidget: под карточками — empty state (no_users.png 200×240 + 24 + matchingNoUsers captionBig терракота + 8 + matchingNoUsersDescription lineLight #514A47) или лоадер пагинации (medium #E1D6D1) или ErrorPlaceholder; следующая карточка (AbsorbPointer) под текущей; текущая — CardActionsComponent (свайп: угол ±π/24 + смещение 100px, порог 30%; завершение — action-анимация 1050мс: поворот π/12 и вылет за экран; оверлеи like_overlay.png/dislike_overlay.png 137×137 с масштабом до ×16 (лайк — слева-сверху с поворотом 3π/16; дизлайк — слева по центру)); возвращённая анкета (undo) выезжает слева (300мс) поверх.
- SwipeTraining: затемнение #000@80% на карточке (r28), поворот карточки ±π/64 в цикле 4.4 с; training_pointer h37 двигается влево/вправо с масштабом; стрелки training_arrow_left/right h46 белые (масштаб 1.3 в нужную сторону); текст (left/right 24, bottom 140): matchingTrainingSwipeDescription baseLight белый + 8 + matchingTrainingSwipeDoneButton lineBold терракота; тап в любом месте завершает. ScrollTraining: затемнение 80%, автоскролл 0→100→0 с паузами 1с/2с, круглый «пойнтер» (48+24·focus, обводка белая 3, точка 8−4·focus), текст matchingTrainingScrollDescription + matchingTrainingScrollDoneButton.
- UserCardView (r28): чёрная подложка; CarouselSlider фото (cover, тап слева/справа = prev/next, свайп фото отключён; placeholder чёрный + large белый лоадер; ошибка — male/female_photo_placeholder 96 #514A47); внизу градиент 160px (#1B1A1C 0→25→50→90%) pad 24: Row [location_filled 16 белая + 4 + city|distance baseLight белый] + 12 + ActivityStatusWidget (status_online/offline 16 + 2 + текст baseLight: activityStatusOnline / «Был(а) только что / N минут назад / N часов назад / вчера / дата»; для своей анкеты всегда «В сети»); 12; Row gap 8: firstName titleHeavy белый (ellipsis) + age titleLight + premium 24 (#E7C9B2, тап → пейволл) + checkmark_white 24 (тап → верификация); 12; Row: match_filled 32 + 4 + matchPercentage titleLight белый + 4 + «%» (TT Runs 500 16) + (commonCommunity → лого 32) + справа теги matchingCategories (#252525@70% r12 h32 pad 8/16 caption белый, отсортированы по длине убыв., gap 4, 1 строка). Пагинатор фото сверху-слева (24,24): svg pager/pager_selected белые (0.5/1), gap 4 — только если фото >1. Видео-аватар 96×96 (top 16 right 16): круг с белой обводкой 2, VideoPlayer cover; при загрузке чёрный круг + small лоадер; тап → полноэкранное видео: затемнение 80% + квадрат (pad 20) CircularVideoPlayer + close_light 24 (#F9F3F2) top/right 20; загрузка — чёрный круг + large лоадер + кнопка mute 44 (52,52). Для своей анкеты — плашка на фото «profileCardPreviewModeratingTitle» (полоса 56 #000@45% + time 24 белая + lineLight) для немодерированных.
- UserDescriptionWidget (под карточкой): 32; (isObscene) чёрная карточка r16 pad 12/12/12/20: oopps 56 + 8 + matchingObsceneLanguageWarningText subtitle белый + 32; кнопка «Отправить записку» (PLSecondaryButton с send_note_heavy + matchingSendNote lineBold; бейдж-кружок 24 белый stroke #E1D6D1 1.3 с числом записок baseBold терракота справа-сверху (−8,−8)) + 32; job lineBold чёрный центр; bio (16) baseLight #514A47 центр; height (32) «matchingHeight(N)» titleLight #9E8075; values (32) Wrap 8/8: чипы #F9F3F2 r12 pad 12/16 baseLight #9E8075; communities (32) «matchingCommunities» lineBold + 16 + чипы #F9F3F2 r12 pad 12/16 с лого 24 + name baseLight чёрный; interests (32) «matchingMyHobbies» lineBold + 16 + чипы белые stroke #E1D6D1 1.3 r28 pad 12/20 baseLight #514A47; prompts (32) карточки #F9F3F2 r16 pad 12 (bottom 20): 12 + question baseLight #9E8075 центр + 24 + answer subtitle #514A47 центр (gap 8); redFlags (32) «matchingIncompatable» lineBold + 16 + чипы #514A47 r28 pad 12/20 baseLight белый; greenFlags (32) «matchingCompatible» + чипы белые stroke #F2E7E3 1.3 r28; «Пожаловаться» (top 48): alert 24 #FA5F3D + 8 + matchingReport lineBold #FA5F3D; 48.
- Лайк (свайп/кнопка): нет лайков и не верифицирован → VerificationRouter; нет лайков и нет подписки → SubscriptionOffers(endedLikes) (analytics user_cards_last_like); нет лайков → тултип; иначе анимация → POST /matching/like/{id} (3 попытки; 110 → reachedLimit → снек matchingReachedReactionsLimit + обновить реакции); оптимистичный −1 лайк; первый лайк → BS запроса push (если permission не granted); undo запоминает последнего. Дизлайк — локально (без запроса!). Instant match (user_match_info в ответе) → MatchRoute (fadeIn) с моим первым фото (из ProfileStatusBloc). Тап на сердце в баре: есть лайки → вибрация; не верифицирован → VerificationRouter; нет подписки → SubscriptionOffers(endedLikes); иначе тултип.
- Undo (кнопка возврата): только одна анкета назад; нет возвратов и нет подписки → SubscriptionOffers(endedUndos) (analytics user_cards_last_undo); иначе POST /matching/bring-back, returns −1 (если нет подписки), карточка выезжает обратно; повторный свайп: лайк → like; дизлайк после лайка → POST /matching/undo-like/{id} (+1 лайк).
- Записка (super like): нет суперлайков и нет подписки → SubscriptionOffers(endedSuperLikes); BS SuperLikeView: superLikeTitle subtitle + 8 + superlikeSubtitle(name) baseLight + 28 + PlTextArea (label superLikeLabel, hint superLikeHint, maxLength 120, autofocus) + 8 + primary superLikeBtnLabel (enabled при тексте, loading) → POST /matching/super-like/{id} {message} → снек superLikeSuccessSnackBarText + pop + свайп вправо карточки.
- MatchView («Это по любви!»): фон терракота, AppBar прозрачный с close 16×12 белым справа; три размытых белых круга (265/335/465, alpha .6, анимация появления 20–40% из 3 с); heart_zero svg 552×439 по центру (fade 70–90%); две карточки 160×182: своя — SvgClip shape_3 + shape_3_outline, поворот +10°, выезжает слева; пары — r40 сверху/200 снизу, белая обводка 4, поворот −10°, справа (40–70%, curve Cubic(0.47,0,0.23,1.38)); низ (pad 12/8): matchTitle headingHeavy белый + 8 + matchSubtitle(name) lineLight белый + 24 + primary matchOpenChatBtnLabel → ChatsList + Chat(chatId) (analytics event_open_chat).
- MatchingAgeRange («Последний шаг и в бой!», первый запуск): ListView pad 20: 44 + matchingFirstLaunchTitle headingHeavy терракота + 12 + matchingFirstLaunchSubtitle lineLight + 85 + Row: пикер «matchingFirstLaunchPickerLabelFrom» (headingLight #B3ACA9 + 12 + PlScrollPicker 116×228, 18…99, default 25) + 16 + линия 32×1 #E1D6D1 + 16 + пикер «…To» (default 45); мин. дистанция 3 года (анимация bounce); низ pad 12/8: Row search 16 терракота + 1 + matchingFirstLaunchHintText(max,min); 16; primary matchingFirstLaunchForwardBtnLabel → сохранить (isFirstMatchingLaunch=false, ageRange) → replace MatchingRoute.

## Фильтры (FiltersView, «Настройки поиска»)
- PlAppBar: back 18×11 чёрный, title filtersAppBarTitle, справа filtersAppBarActionButtonTitle lineBold (терракота если изменены, иначе #E1D6D1 disabled) → saveFilters → MatchingBloc.applyFilters (PATCH /matching/recalculate со всеми фильтрами; full-screen loader; успех → pop; ошибка → снек). GET /matching/filters на входе. Loading — large #E1D6D1; error — ErrorPlaceholder.
- Секции (ListView, разделитель Divider 1px #E1D6D1): Город (pad 12/24): filtersLocationTitle subtitle + 16 + cityName / filtersLocationAuto lineLight #B3ACA9 (ellipsis) + иконки geolocation_24_heavy (терракота; запрос permission → getPosition; permanentlyDenied → openLocationSettings) + 8 + edit_24_heavy (→ SelectCityRoute); Радиус (pad 24/16): filtersRadiusTitle subtitle + значение filtersRadiusValue(N) / filtersRadiusMaxValue lineLight #514A47; 20; PlLineSlider 1…100 (default 50); 12; Row filtersExpandSwitcherTitle lineLight (#514A47 / #B3ACA9 disabled при 100) + PlSwitch; Возраст (pad 16/24): filtersAgeTitle + filtersAgeRangeHint(max,min); 20; два PlScrollPicker (aspect 151/140, 18…99, мин. дистанция 3, bounceOut) с линией 32×1; 12; Row filtersExpandSwitcherTitle + PlSwitch (disabled при 18–99); Сообщества (pad 12/24, только если есть опции): filtersCommunitiesTitle subtitle + 12 + строки (pad 12): CommunityLogoWidget 32 + 12 + name lineLight (2 строки) + PlSwitch; Рост (pad 16/24): filtersHeightTitle + текст (filtersAgeRangeMinMaxNull / MinNull(max) / MaxNull(min) / filtersAgeRange(max,min)); 20; два виджета (ширина/2.5, высота/1.07): кнопка «+ filtersHeightAddBtnLabel» (stroke терракота 1.33 r16, plus_24_heavy + lineBold терракота) ↔ PlScrollPicker 70…250 «N см» (мин. дистанция 10, elasticOut); линия 32×1. Обновление при изменении сообществ (CommunitiesUpdateBloc).
Flutter — чаты (WS, REST, drift, экраны) (notes_flutter_chats.md, 17780 байт)
# Flutter — фича «Чаты» (features_chats.txt, 13133 строк, прочитано полностью)

## Транспорт
- WebSocket `wss://…/api-chats/v1/chat` (заголовок Authorization Bearer), ретраи 0.5–10 с. WsManagementBloc: подключается при старте, отключается/переподключается при смене сети.
- Входящие типы: new-pair, send-text (front_id, batch_id, number, created_at, reply_to), set-last-read (older_than_receivers_last_read), edit-text / edit-text-error, delete-message / delete-message-error, user-is-offline / user-is-online / user-is-typing. Ошибки валидации detail 'Chat not found' / 'Invalid message'. Закрытие 1008 'Invalid token' → refresh токена и reconnect; 'Max connections reached' → состояние tooManyConnections.
- Исходящие: send-text {type, chat_id, front_id, text, reply_to{batch_id, front_id, number}}, delete-message {…is_sender_of_message, delete_for_receiver_too, initiator_id}, set-last-read, user-is-typing {chat_id}, edit-text {…is_last_message}.
- REST (база чатов): GET /user-chats?chat_id (курсор; hasNextPage = chats.length>1), GET /user-chats/{id} (404 → ChatNotFound), GET …/messages/next|previous?batch_id&number (страница 30), GET /user-chats/new-pairs, GET/DELETE /user-chats/new-pairs/{user_id}, GET /user-chats/updates?last_sync (актуализация кэша; удаляет чаты, которых нет; применяет правки/удаления сообщений). POST /users/{id}/delete-pair (разрыв пары).
- Локальная БД drift: чаты, пользователи, сообщения, новые пары; сообщения чатов старше 30 дней удаляются. ChatCacheBloc обновляет кэш по WS edit/delete/new-pair (текст — debounce 5 с).
- Константы: typingTimerDuration 4 с, setLastReadTimerDuration 4 с, ChatMessageNumber max 299, maxAgeOfEditableMessage 4 ч, maxAgeOfDeletableMessage 2 ч.

## ChatBloc (экран переписки)
- Начальная загрузка: connect WS → чат + userId/userName → tryLoadFromCache (грузит prev-страницы пока нет lastMessage и lastRead) → иначе по lastRead (нет → последняя страница; lastRead == last → последняя; иначе prev+next вокруг lastRead).
- Pending-сообщения по frontId (uuid); своё сообщение из WS заменяет pending; если не все next-страницы загружены — сначала грузит последнюю.
- Edit: только своё, edit-text (is_last_message); ошибка → откат + snack messageEditMessageError. Delete: delete-message (deleteForReceiverToo только если своё && forAll), флаг isDeleting; ошибка → откат + snack messageDeleteMessageError.
- Reply: payload reply_to. Поиск источника ответа: грузит prev-страницы пока не найдёт (Found / NeedToFindFurther / NotFound), подсветка BlinkWidget (#F2E7E3@50%, 600 мс), скролл к сообщению 800 мс easeInOut alignment .5; пока ищет — оверлей #000@20% + blur 2 + большой лоадер terracotta.
- После 2-го своего отправленного сообщения при первом общении → BS ChatRequestNotificationPermission. Первое сообщение в чате → DELETE new-pairs/{userId}.
- Reconnect сети → перезагрузка. ChatNotFound → snack chatNotFoundError + переход в ChatsList; userDeleted/userReported → ChatsList. Эфемерные ошибки: messagesPaginationError, invalidMessageError.
- Ссылки: BS-предупреждение, потом запуск (добавляет https://).
- ChatUserBloc: online/offline (lastVisit=now), typing-таймер 4 с, перезагрузка при сети. MeTypingBloc: user-is-typing каждые 4 с пока печатает (текст непустой + фокус). LastReadBloc: throttle set-last-read 4 с, отправка при закрытии, учёт удалённых сообщений.

## ChatView (экран переписки)
- loading → большой лоадер; error → ErrorPlaceholder.
- ChatAppBarAnimatedSize: collapsed 56 ↔ expanded 88. В title аватар 56 круг (при отсутствии WS-соединения затемнение #000@35% + маленький лоадер; индикатор online 16 белый круг слева −8). Низ 28: caption fullName (чёрный) / UserTypingWidget (terracotta) / chatNoConnectionTitle (#B3ACA9). Actions: rules_24_light + menu. Нижний разделитель 1px #F9F3F2. Тап по аватару → NewPairProfilePreviewRoute.
- Тело: MessagesListWidget (ScrollablePositionedList reverse) + MessageInputWidget.
- Пустой чат: ChatUserInfoWidget + 32 + messagesEmptyTitle titleLight #B3ACA9 + 8 + messagesEmptySubtitle lineLight #514A47 + 24.
- ChatUserInfoWidget: 16 + Row spaceEvenly [verified_checkmark/unverified_checkmark 56 + 2 + «Проверен»/«Не проверен» baseLight (#514A47/#9E8075); аватар 128 круг (тап → превью профиля); premium_icon/no_premium_icon 56 + «Подписка»/«Нет подписки»] + 20 + typing/ActivityStatusWidget (#514A47) + 16 + fullName titleHeavy по центру (pad 16) + 8 + chatUserAge(age) titleLight. Тап «Проверен» → VerificationPhotoPreviewRoute(chatUser, myGender). Тап premium → SubscriptionOffers(other) если нет подписки.
- DateDividerWidget: pad 16, линии 1px #F9F3F2 + дата baseLight #9E8075, gap 12 (Сегодня / d MMMM / d MMMM yyyy); в списке отступ top 36 / bottom 18.
- UnreadMessagesDividerWidget: линии 1px #F2E7E3@50% (margin bottom 4) + messagesUnreadTitle baseLight #9E8075, gap 12; отступ 16/16 (после date-divider 8).
- Разделители между сообщениями: 4 (тот же автор), 8 (другой автор). Последний элемент — отступ снизу 12. Лоадеры пагинации: medium #E1D6D1 (снизу pad 4/16, сверху pad 16). Если есть предыдущая страница, ChatUserInfoWidget не показывается.
- Пагинация: триггер 10 элементов от края (paginationTriggerItem). lastSeen → onLastSeenMessageChanged. Скролл вниз при shouldScrollToBottom 240 мс.
- Статус доставки: pending → нет иконки; lastRead до сообщения → sent; после/равно → read.
- MessageWidget: max width 80% экрана, боковой margin 12, padding 16/12, r12 smooth; фон своё #C66F53 / чужое #F2E7E3; текст своё белый / чужое чёрный; ссылки подчёркнуты (свои белые, чужие terracotta); время caption (#E7C9B2 своё / #9E8075 чужое) + «изменено» (messageEdited) + иконка статуса message_sent/message_read 17px #E7C9B2 (только своё); время встраивается в последнюю строку если помещается, иначе строкой ниже (pad 16/16/10). Хвост (myMessageTail/messageTail 20×25) у последнего в группе, смещение −5. Pending/deleting: иконка time 24 #B3ACA9 рядом с пузырём. Ответ (reply source): вертикальная линия 2×32 (своё #E7C9B2 / чужое terracotta) + имя автора caption + текст caption 1 строка, maxHeight 35, тап → переход к источнику.
- Свайп влево по сообщению до −80 (haptic при −40) → ответ. Long press (не pending/deleting) → haptic + контекстное меню.
- MessageContextMenu: blur 16 + чёрный 80% фон, сообщение перемещается (safe top 80), меню 180 wide #F2E7E3 r12 smooth, пункты pad 20/14 baseBold с иконкой 16 (edit_16_heavy / message_reply / copy_24_heavy / trash_24_heavy), разделители #E7C9B2, справа для своих/слева для чужих, отступ 16. Пункты: Изменить (своё и возраст < 4 ч), Ответить, Копировать (snack messageContextMenuCopyResultMessage), Удалить (возраст < 2 ч; своё → подменю «только у меня» / «у всех (имя)»; чужое → у меня).
- MessageInputWidget: белый контейнер pad 12/8, верхняя граница #F9F3F2; блок выбранного сообщения (иконка 20 #B3ACA9 edit_16_heavy/message_reply + линия 1×32 terracotta + подпись caption terracotta (messagesEdit / messagesReplyTo(name)) + текст caption чёрный 1 строка + close 24) + 8 + divider + 8; Row: PlTextInput (hint messageInputPlaceholder #9E8075, без suffix, maxLength 1000, 1–5 строк) + 8 + кнопка отправки 44 круг (чёрная + send_note_heavy белая когда активна; #F9F3F2 + #9E8075 когда неактивна; неактивна если WS закрыт/перезагрузка или пустой текст).
- ExternalResourceWarningSheet: pic_attention 56 + 8 + messageLinkPressedTitle subtitle + 8 + messageLinkPressedDescription lineLight + 16 + primary messageLinkPressedSubmit + 8 + secondary messageLinkPressedCancel.

## ChatsListBloc (список чатов)
- initialChats из кэша БД иначе GET; newPairs из кэша иначе GET; актуализация кэша через updates. Состояния: initial/loading/loaded/loadingMore/reloadingAfterNetworkReconnect/ephemeralError(loadingMoreError, cacheLoadingError)/tooManyConnectionsError/wsConnectionError/error/noNetworkError.
- WS: new-pair (fetch пары с 3 ретраями, prepend), text (чат наверх; unread+1; новая пара → становится чатом, deletePair; неизвестный чат → fetch), last-read (свой; пересчёт unread), online/offline для чатов и пар, typing (TypingUsersSet с таймерами), edit последнего сообщения, delete последнего → refetch чата; ChatsWereOpened (onNavigateIn + resumed) → обновление кэша + перезагрузка; deletedPairs/reportedUsers удаляют чат и пару.
- Эфемерные ошибки: snack chatsPaginationError, chatsCacheLoadingError (error type).
- Нажатие на чат/пару игнорируется, когда WS закрыт или идёт reloadingAfterNetworkReconnect. Аналитика EventOpenChat.

## ChatsListView
- PlScaffold, SafeArea(bottom:false), CustomScrollView: SliverPersistentHeader pinned (ChatsHeaderDelegate min 44 / max 328; collapsed: «Сообщения»(chatsTitle) lineBold чёрный по центру + _LostConnectionWidget + справа @16 кнопка rules; expanded: 24 + newPairsAppBarTitle («Твои пары») headingHeavy terracotta + 8 + подзаголовок lineLight #514A47 (newPairsAppBarSubtitle / newPairsEmptyAppBarSubtitle; шиммер 20 высотой pad 51 при загрузке) + 24 + карусель (156) + 32 + футер Row [16, «Сообщения» titleHeavy чёрный, LostConnection, spacer, rules-кнопка, 16] + 8). Collapsed-фон белый при t==1, opacity-анимация 150 мс.
- _LostConnectionWidget (WS закрыт / reloading / cacheActualizationInProgress): 18 + маленький лоадер #B3ACA9 + 6 + chatsLostNetwork lineLight #B3ACA9.
- Карусель новых пар: CarouselSlider viewportFraction 0.4, enlargeCenterPage zoom, enlargeFactor (80/128)^2, бесконечный скролл при >2 пар; карточка: аватар 128 круг (тап → NewPairProfilePreview, только у выбранной), индикатор online 16 белый круг слева −8 (точка 8 terracotta/#E1D6D1), кнопка go-to-chat 44 белый круг @bottom −8/right −8 (только у выбранной; opacity-анимация 150 мс), + 8 + имя lineLight #514A47. Пусто: SVG newPairsHearts 280×156. Шиммер: 2 малых (80, текст 56×14) + большой (128, текст 88×16) + 2 малых, gap 20, 10 между фото и текстом.
- Список чатов: ChatsWidget (MultiSliver: баннер правил + SliverList.separated разделитель 1px #F2E7E3@50% + лоадер medium #E1D6D1 pad 16 при loadingMore); пагинация при видимости 5-го элемента с конца.
- Пустой список: SliverFillRemaining по центру: chatsEmptyTitle captionBig terracotta + 8 + chatsEmptySubtitle lineLight #514A47 + 24 + PLPrimaryButton.small chatsOpenUserCardsButtonTitle → MatchingRoute.
- Шиммер чатов: 5 элементов (круг 80, плашки 104×20 / 200×14 / 88×12).
- ChatWidget: PLScaledGestureDetector, pad top/bottom 24, left 12, right 16; аватар 80 круг + индикатор online (16 белый круг слева −8) + 12 + колонка (3 + SizedBox 74: «Имя, возраст» subtitle чёрный 1 строка + 8 + AnimatedSwitcher 240 мс: UserTypingWidget либо [текст последнего сообщения baseLight #514A47 1 строка + 5 + время caption #B3ACA9]) + 12 + колонка [кнопка menu 24, 4, счётчик непрочитанных]. Счётчик: terracotta r12 min 24×24 pad 3/8 baseBold белый, «99+» при >99.
- Формат времени последнего сообщения: сегодня → messageTimeToday (время), вчера → messageTimeYesterday, <7 дней → messageDay (день недели), тот же год → messageDayMonth, иначе messageDayMonthYear.
- Кнопка «Правила сообщества» (rules_24_light 24) видна только когда есть чаты и обучение пройдено (CommunityRulesTrainingPassed). Баннер CommunityRulesBannerWidget (pad 12/8; белый r16 smooth stroke 1.33 #E1D6D1, pad 20/52/16/8: communityTrainingRulesBannerTitle lineLight #9E8075 + 8 + строка [rules_24_light terracotta + 8 + communityTrainingRulesBannerButtonTitle lineBold terracotta] pad vertical 16; крестик erase_filled 24 #E1D6D1 @top 6 right 6) — виден только когда чаты есть и обучение не пройдено; AnimatedCrossFade 240 мс.
- Ошибки: tooManyConnections/wsConnection → ErrorPlaceholder(reload); error → ErrorPlaceholder(error); noNetwork → ErrorPlaceholder.customTexts(errorPlaceholderNoInternetTitle/Subtitle).

## Меню чата (ChatMenu BS)
- PlBottomSheet: «Имя, возраст» subtitle + 8 + chatMenuSubtitle lineLight + 16 + три PLSecondaryButton (lineBold terracotta): chatMenuShareProfileBtnLabel («Поделиться профилем» → Share.share(chatMenuShareTextTemplate(age, firstName, userId))), chatMenuReportAndBlockBtnLabel (→ pop + Report BS place=chat), chatMenuDeletePairBtnLabel (→ pop + DeletePair BS). Gap 8.

## Разрыв пары (DeletePair BS)
- deletePairTitle subtitle по центру + 16 + 5 PLSecondaryButton (gap 8): deletePairMetOffline, deletePairBehavior, deletePairCatfisher, deletePairNotInterested, deletePairOther; loading на нажатой кнопке, остальные disabled; успех → pop; ошибка → ErrorDisplayer. БАГ: DeletePairConverter маппит все причины в DeletePairReasonDto.metOffline.

## Превью профиля новой пары (NewPairProfilePreview)
- PlAppBar title fullName + action menu 24 (→ ChatMenu BS). loading → большой лоадер; loaded → UserCardWithDescription(user, onVerifiedTap, onPremiumTap) + плавающая кнопка @right 8 bottom 8: белый круг 86 + terracotta круг 80 + goToChatFilled 40 белая (→ ChatRoute + EventOpenChat); error → ErrorPlaceholder; userDeleted/userReported → navigate ChatsListRoute.
- onPremiumTap: есть подписка → vibrateSingleTapDeclined; iOS без подписок в RC → declined; иначе SubscriptionOffers(other). onVerifiedTap: verified/pending → declined; иначе VerificationRouter + ViewVerifyFirstScreen(userCard).

## Превью фото верификации (VerificationPhotoPreview)
- PlAppBar title verificationPhotoPreviewAppBarTitle («Селфи»), action PLTextButton verificationPhotoPreviewAppBarCloseButton («Закрыть») lineBold terracotta, без back. Body pad 12/16: SmoothClipRRect r28 AspectRatio 1: фото verifiedPhotoUrl (placeholder: чёрный + figure_female/figure_male по гендеру владельца — противоположный) + 32 + pic_checkmark + 16 + verificationPhotoPreviewTitle headingHeavy чёрный + 8 + verificationPhotoPreviewSubtitle(firstName) lineLight #514A47 по центру.

## Мелкие виджеты
- OnlineIndicatorWidget: точка 8 (terracotta online / #E1D6D1 offline), AnimatedSwitcher 150 мс.
- UserTypingWidget: userTyping baseLight terracotta + 4 + LoadingIndicator ballPulse 13×13 (terracotta 100/70/50%).
Flutter — профиль, настройки, редактирование, превью (notes_flutter_profile.md, 14773 байт)
# Flutter — фича «Профиль» (features_profile.txt, 6995 строк, прочитано полностью)

## Данные / API
- ProfileRepository: GET /profile (кэш AsyncCache ephemeral, BehaviorSubject profileChanges), PATCH /profile (PersonalDetailsRequestDto: additionalInfo{bio, greenFlags, redFlags, job, promptAnswers[{id,value}]}, email, isVisible, marketingIsOn, survey[{step, show, data[{answerId, questionId}]}], personalInfo{firstName,lastName,height,gender,birthday yyyy-MM-dd}) → EventChangeProfile + сохраняет UserStorageDto(id, surveyPercentage); resetProfile при логауте; GET survey/prompts (категории), GET survey (шаги).
- ProfileEntity: userId, personalInfo{gender,birthday,firstName,lastName,height}, profileInfo{phone, surveyPercentage, isVisible, isObscene, showTrainingVideo, verifiedPhotoUrl?, isMarketingOn?, email?}, additionalInfo?, survey[SurveyItemEntity{questions[{id, answer{options[{id,text}], type single/multiple/slider}}], show, step}], media?{photos[{id, order, isModerated, photoUrl}], video{videoPreviewUrl, videoUrl}}.
- UserSettings: GET/PATCH profile/settings {activityReminder, marketing, newMatch, newMessages, showTrainingVideo, referralActivity}.
- FAQ: GET faq → questions[{title, info}]. Feedback: POST feedback {email?, message}. Deactivate: POST deactivate {reason: dislike|notRelevant|other|pairFound}.
- ProfileEditConstants: шаги анкеты в запросе 1 дети, 2 работа, 3 стабильность, 4 новый опыт, 5 религия, 6 курение, 7 алкоголь, 8 отношения, 9 интересы; в онбординге +4.
- ProfileStatusBloc (общий): загружает профиль при старте, подписан на profileChanges; ProfileUpdateListener обновляет профиль при foreground-пуше с deeplink profile/profileEdit, при восстановлении сети, при возврате из фона.
- ProfileReactionBloc: профиль без фото → snack profileReactionNoPhotoSnack (error) + navigate ProfileEditRoute; на экране редактирования canPop=false пока нет фото (vibrateError + snack + скролл вверх).

## Экран «Профиль» (таб)
- PlScaffold + SafeArea; loading → большой лоадер #E1D6D1; error → ErrorPlaceholder(reload).
- ListView pad vertical 16: _ButtonsHeader Row spaceEvenly [кнопка настроек: круг 66×68 stroke 1.33 #E1D6D1, внутри gear40 terracotta (pad 14); аватар 128 круг (фото / видео зациклено без звука / пусто → круг stroke #E1D6D1 + photoAdd 80 terracotta → редактирование; шиммер #F2E7E3@50%); кнопка редактирования: тот же круг + profileEdit terracotta + снизу −4 бейдж на белом pad 8 «N%» caption #9E8075 (profilePercent)] + 20 + fullName captionBig чёрный по центру (+ 8 + checkmarkFilled terracotta если верифицирован) + 8 + profileAge(age) lineLight #514A47 + 20 + _VisibilityButton + 24 + [ObsceneLanguageWidget если isObscene] + _VerificationCTAWidget + _SubscriptionWidget + 32 + _AddEmailWidget + 32 + _MenuWidget + 32 + PLSecondaryButton profileLogoutButton (pad 12).
- _VisibilityButton: AnimatedContainer 210×52 r100 (visible: terracotta / invisible: #F2E7E3), текст baseLight (visible: белый @left 20 profileVisibilityEnableStatus / invisible: #9E8075 @left 58 profileVisibilityDisableStatus), белый бегунок 46×44 r100 (@left 160 / 4) с иконкой revealed24Light / hidden24Light #9E8075 или маленьким лоадером; анимация 200 мс; тап → PATCH isVisible + EventChangeVisible; ошибка → ErrorDisplayer.
- ObsceneLanguageWidget (pad 12/12/bottom 8): контейнер #F2E7E3@50% r20 pad 12/12/20/12: oopps 56 + 8 + profileObsceneLanguageWarningText subtitle чёрный + 8 + profileObsceneLanguageWarningExplanationText baseLight чёрный + ссылка profileMenuRules baseLight #FA5F3D → communityRulesUrl.
- _VerificationCTAWidget (скрыт при accepted/pending/loading): pad 12/12/bottom 8, PLScaledGestureDetector → VerificationRouter + ViewVerifyFirstScreen(profile); рамка r28 stroke #E1D6D1: 24 + picCheckmark + 8 + profileVerificationTitle subtitle + 8 + profileVerificationSubtitle baseLight #B3ACA9 + 16 + Wrap (pad 20, gap 8) 4 чипа (#F2E7E3@50% r28 pad 8/20, иконка 24 #9E8075 + 8 + текст lineLight #9E8075): time / match24Light / revealed24Light / checkmark24Light с текстами profileVerificationFirstChip…FourthChip + 32.
- _SubscriptionWidget (pad 12): скрыт при initial/loading/error и на iOS без RC; HasSubscription → SubscriptionWidget; NoSubscription → NoSubscriptionWidget.
  - NoSubscriptionWidget: чёрный r28 с тремя размытыми terracotta кругами (209 @left −80 bottom −60; 121 @right −60 top 32; 99 @right 65 top 79 alpha .7); 24 + premiumIcon 56 #E7C9B2 + 16 + subscriptionName headingHeavy белый + 8 + profileSubscriptionCTA subtitle белый + 20 + PLPrimaryButton.inverted profileSubscriptionChooseTariff (pad 8) → SubscriptionOffers(other) + ViewTariffPlans(profile).
  - SubscriptionWidget: чёрный r28: 24 + premiumIcon 56 #E7C9B2 + 16 + заголовок headingHeavy белый (Ordinary: durationFormatter(subscriptionText, period); Gift: subscriptionGiftDuration; GiftReferral: subscriptionGiftDeferredDaysDuration(дней до expireDate)) + 20 + фичи (чёрные пилюли r28 stroke 1.33 #514A47 pad 9/16/9/20: logoUrl 24 + 8 + description lineLight белый, gap 8) + 24 + subscriptionStatusActive subtitle terracotta + 8 + строка baseLight белая (active: subscriptionNextPayment(цена, дата); notProlonged/Gift/GiftReferral: profileSubscriptionWillEnd(subscriptionUntilDate)) + кнопка продления PLPrimaryButton.smallInverted profileSubscriptionProlongButtonText (только Ordinary external notProlonged) + bottom 32.
- _AddEmailWidget: нет email → pad 12: profileAddEmailTitle subtitle + 8 + profileAddEmailSubtitle baseLight #514A47 + 16 + DottedBorder r20 #E1D6D1 dash 3.5/6.5 высота 68 с email32 terracotta → BS EnterEmail; есть email → Row [profileEmailTitle subtitle #514A47, edit24Heavy terracotta] + email baseLight #514A47 + 32 + Divider #F2E7E3@50%.
- _MenuWidget (pad 12): пункты pad 4/8, разделитель снизу #F2E7E3@50%, иконка 24 + 12 + текст baseLight: referral24 profileMenuReferral (→ ReferralProgram source profile), communities24Heavy profileMenuCommunities (→ Communities source profile), faq24 profileMenuFaq (→ FaqRoute), support24 profileMenuSupport (→ ContactUs BS), rules24Light profileMenuRules (→ communityRulesUrl), lock24Light profileMenuPrivacy (→ policyUrl), trash24Light profileMenuDeleteProfile (#FA5F3D, без разделителя) → DeletionReason BS.
- Логаут: authBloc logoutRequested.
- Удаление аккаунта: BS DeletionReason (maxHeightRatio 12/16): deletionReasonTitle subtitle чёрный по центру + 16 + 4 PLSecondaryButton gap 8: deletionReasonPairFound, deletionReasonDislike, deletionReasonNotRelevant, deletionReasonOther → pop → PlDialog (profileDeleteAccountDialogTitle / Subtitle; действия: profileDeleteAccountDialogDeleteButton destructive, profileDeleteAccountDialogCancelButton default) → push ProfileDeleteTimerRoute.
- ProfileDeleteTimer: фон terracotta; по центру pad 32: «N» headingLight белый + 8 + profileDeleteTimerSubtitle(N) lineLight белый; внизу PLButton tertiary profileDeleteTimerCancelButton (pad 12/8); таймер 10 с → POST deactivate → logout; ошибка → ErrorDisplayer + pop.
- Видео-аватар: play при resume/onNavigateIn, pause при paused/onNavigateOut.

## BS «Ввод email» (EnterEmail)
- PlBottomSheet pad 12/16 (bottom 8): PlTextInput autofocus, maxLength 256, label enterEmailLabelText, hint enterEmailHintText, email keyboard + 8 + PlCheckBox enterEmailCheckboxDescription (marketingIsOn) + 16 + PLPrimaryButton enterEmailSaveButton (enabled если email валиден или пуст И что-то изменилось; loading). Сохранение → PATCH profile (email null если пусто) → pop; ошибка → ErrorDisplayer.

## BS «Связаться с нами» (ContactUs), maxHeightRatio 12/16
- PlBottomSheet pad 12/8: PlTextArea label contactUsMessageLabel hint contactUsMessageHint maxLength 1000 1–4 строки + 8 + PlTextInput email (contactUsEmailLabel/Hint, 256) + 8 + PLPrimaryButton contactUsSendButton (enabled: сообщение непустое И email валиден и непустой). Успех → snack contactUsSuccessSnack (success) + pop; ошибка → ErrorDisplayer.

## Экран «Частые вопросы» (FAQ)
- PlAppBar faqTitle, navBar прозрачный; loading → большой лоадер #E1D6D1; error → ErrorPlaceholder; ListView pad 16/24: title subtitle чёрный + 12 + info baseLight #514A47; разделитель 24.

## Экран «Настройки профиля»
- PlAppBar profileSettingsTitle + PLTextButton profileSettingsSaveButton (enabled при изменениях). При старте запрашивает разрешение на уведомления и проверяет статус.
- ListView pad 24/12: заголовок profileSettingsNotificationSubtitle subtitle (pad 4/12) + справа profileSettingsNotificationDisabled lineLight #514A47 если нет разрешения; свитчеры (pad 14/4; текст lineLight #514A47 / #B3ACA9 disabled; PlSwitch): profileSettingsNewMatchSwitcher, profileSettingsNewMessagesSwitcher, profileSettingsMarketing, profileSettingsActivityReminder, profileSettingsReferralActivity (все disabled без разрешения); без разрешения → _OpenSettingsWidget (pad 4/4/12/top 32: profileSettingsOpenSettingsDescription lineLight #514A47 центр + 24 + PLPrimaryButton.small [settingsHeavy белый + 4 + profileSettingsOpenSettingsButton] → openAppSettings); + 20 + profileSettingsContentSubtitle subtitle + свитчер profileSettingsTrainingVideo + 32 + _SubscriptionWidget (не на iOS без RC): Row [profileSettingsSubscriptionTitle subtitle, справа: active → profileSettingsSubscriptionActive; notProlonged/Gift → profileSettingsSubscriptionUntil(date); нет → profileSettingsSubscriptionPurchaseCTAButton lineBold terracotta → SubscriptionOffers(other) + ViewTariffPlans(profileSettings)] + 12 + свитчер profileSettingsSubscriptionProlongText (active: on; notProlonged external: off enabled; иначе off disabled). Выкл продления → CancelProlongation BS; вкл → ProlongSubscription.
- Сохранение → PATCH settings, затем fetchProfile; PLFullScreenLoader при isUpdate/prolong processing; ошибка → ErrorDisplayer.

## Экран «Редактирование профиля» (ProfileEdit)
- AppBar: back (18×11.5) + по центру: profileEditAppbarPercent(N) lineBold terracotta (анимация счётчика 500 мс) + 4 + прогресс-бар 174×6 r3 (#F2E7E3@50% / terracotta, easeInOutCubic 500 мс) + 7. WillPopScope canPop.
- Body ListView pad vertical 16: pad 12: 8 + VideoContentComponent(isFormOnboarding false) + 24 + _GallerySuperscriptWidget (lineLight #B3ACA9 по центру: >1 фото и есть немодерированные → profileEditGalleryModerationSuperscript; >1 фото → mediaContentDragAndDrop; иначе mediaContentOnePhotoMinimum) + 16 + GalleryContentFlow + 24 + VerificationPreviewWidget; затем секции с Divider 1px #F2E7E3@50%.
- Секции (_ProfileEditingSection pad 16/24: Row [title subtitle чёрный, иконка справа plus24 (пусто) / edit24Heavy terracotta] + content): profileEditNameSegmentTitle → fullName (→ Onboarding nameAndSurnameStep); profileEditNameJobTitle → job или profileEditEmptyAnswer5Percent (→ occupationStep); profileEditNameBioTitle → bio (→ aboutYourselfStep); profileEditNameHeightTitle → profileEditHeightCentimetre(h) (→ genderAndHeightStep); интересы (чипы stroke #E1D6D1 r28 pad 20/11 baseLight #514A47, Wrap gap 8; → interestsStep); флаги (profileEditRedFlagTitle baseBold + чипы #514A47 залитые белый текст; 20; profileEditGreenFlagTitle + чипы stroke #F2E7E3; → redAndGreensStep); открывашки (loading: 3 шиммера 80 r20; error: DottedBorder #FA5F3D 80 с refresh16Light + profileEditReloadPromptsButton; content: карточки #F2E7E3@50% r20 pad 12 (bottom 20): question baseLight #9E8075 центр + 12 + answer subtitle #514A47 центр; если <3 → DottedBorder #E1D6D1 80 с profileEditEmptyPrompt2Percent; → promptsStep); 8 обязательных шагов анкеты (дети, работа, стабильность, новый опыт, религия, курение, алкоголь, отношения): первый ответ — плашка #F2E7E3@50% r12 pad 16/12 baseLight #9E8075; далее вопросы: [hidden24Light #B3ACA9 если шаг скрыт] + вопрос lineBold #514A47 + 12 + «→ «ответ»» baseLight или кнопка profileEditAddAnswersButton baseBold terracotta; gap 16; → OnboardingRoute(step+4, isFromOnboarding false).
- Низ: PLSecondaryButton [cards24Heavy + 4 + profileEditShowPreview] → ProfileCardPreviewRoute.
- VerificationPreviewWidget (только accepted и есть verifiedPhotoUrl): белый r20 stroke 1.33 #E1D6D1 pad 16: [repeatVerificationTitle subtitle + 8 + repeatVerificationSubtitle baseLight #B3ACA9] + 12 + фото 116×128 r20 (#F2E7E3) с кнопкой 44 r12 blur 5 #000@40% refresh16Heavy белая @bottom 4 left 4 (→ VerificationRouter/VerificationInstruction) и verificationIllustration 24 @top 4 right 4.

## Экран «Предпросмотр анкеты» (ProfileCardPreview)
- PlAppBar profileCardPreviewTitle + PLTextButton profileCardPreviewCloseButton, без back, navBar прозрачный; loading → большой лоадер; content → UserCardWithDescription(UserEntity.my: location distance '2 км', cachedVideo, isObscene из профиля); error → ErrorPlaceholder. Перезагрузка при CommunitiesUpdate ShouldUpdate. onPremiumTap/onVerifiedTap как в чатах.
Flutter — подписка и оплата (notes_flutter_subscription.md, 9081 байт)
# Flutter — фича «Подписка» (features_subscription.txt, 4230 строк, прочитано полностью)

## Данные / API
- Конфигурация: iOS → IAP (in_app_purchase, только физическое устройство), Android → external (эквайринг T-Bank: карта / СБП). SubscriptionConfigEntity iap|external.
- API: GET subscription/tariffs → {tariffs[{id, name, billingPeriod week|month|twoMonths|threeMonths|sixMonths|year, cost{basePrice, currency RUB, period, discountPrice?, discount?, oneTimePayment{price, period}}, product{appStoreProductId}}], features[{logo, text}]}; GET subscription/me (204 → нет подписки; ретрай 3×1 с) → {expiresAt, status active|notProlonged, paymentSystem acquiring|applePay|gift|giftReferral, paymentSystemSubtype tbankCard|tbankSbp, tariff, features, deferredDays}; PATCH subscription/me {status active|notProlonged} (продление / отмена продления, ретрай 3); POST payment/android {tariffId} → {paymentUrl, successUrl, failUrl} (ретрай 3, кроме serviceUnavailable/paymentPendingAcquiring); POST payment/android/status → {paymentStatus success|pending|fail, failMessage, paymentSystemSubtype}; POST payment/ios/status {receiptData}, POST payment/ios/status/restore.
- SubscriptionEntity: Ordinary{expireDate, status, isExternal, tariffId, period, cost, paymentSubsystem, features} | Gift{expireDate, tariffId, features, deferredDays} | GiftReferral{expireDate, tariffId, features}.
- SubscriptionBloc (общий, lazy false): initial/loading/hasSubscription/noSubscription/error; таймер истечения (|expireDate − now| + 3 мин → refetch). SubscriptionUpdateListener: refetch при foreground-пуше deeplink subscription, при восстановлении сети, при возврате из фона.
- ProlongSubscriptionBloc: prolongRequested → PATCH active → refetch → prolonged / error FailedToProlong; cancelProlongationRequested → PATCH notProlonged → canceled. Ошибки подарочных подписок бросают Error.
- SubscriptionPurchaseBloc: purchaseRequested → purchasing → success(TYP) | paymentUrl (Android) | error; paymentStatusCheckRequested(didCompletePayment) → checkingPaymentStatus → success → refetch subscription → success | noSubscriptionAfterSuccessPayment; pending (если завершил оплату) → paymentStatusPending; pending (не завершил) → initial; fail → paymentStatusFailed(message) + EventExternalPaymentFailure; restoreRequested (iOS) → restoring.
- Аналитика: EventPaymentStarted(platform), EventPaymentCompleted(status, paymentSystem, subsystem), EventIAPPaymentFailure, EventExternalPaymentFailure, ActionTapPay(tariffId, basePrice, discountPrice, platform, period), ActionTapRestore, ActionTapNotProlonged, ViewWebViewPayment(isSuccessful), ViewWebViewComplete(status), ViewTariffPlans(source profile|profileSettings|…).
- PriceFormatter: убирает нулевые копейки, точка → запятая, « ₽». DurationFormatter: subscriptionDurationWeek/Month/TwoMonths/ThreeMonths/SixMonths/Year и формы «X на …» (subscriptionDurationForWeek(text) …).
- SubscriptionErrorDisplayer: serviceUnavailable → subscriptionPurchaseServiceUnavailableErrorText; paymentPendingAcquiring → subscriptionPurchasePaymentPendingAcquiringErrorText; SBPNotLinked → apiExceptionSBPNotLinked; giftSubscriptionStatusError → apiExceptionGiftSubscriptionStatusError; FailedToProlong → subscriptionFailedToProlongErrorText; FailedToCancel → subscriptionFailedToCancelProlongationErrorText; IAP: tariffNotFound / noAvailableTariffs / purchaseError; notAvailable/cancelled — молча.

## Экран «Планы подписки» (SubscriptionOffers), entryPoint sympathy|endedLikes|endedUndos|endedSuperLikes|other
- PlAppBar без leading, title subscriptionOffersHeader, action close 24 чёрный. loading → большой лоадер #E1D6D1; error → ErrorPlaceholder(reload); PLFullScreenLoader при checkingPaymentStatus.
- Контент ListView: 24 + title headingHeavy terracotta по центру (subscriptionOffersFromSympathyTitle / FromEndedLikesTitle / FromEndedUndosTitle / FromEndedSuperLikesTitle / DefaultTitle) + 12 + description lineLight #514A47 (…Description) + 32 + тарифы (LayoutGrid 2 колонки, gap 8, pad 12) + 32 + фичи (subscriptionFeaturesTitle subtitle + 20 + FeatureWidget.fromIconUrl(logo, text) gap 8) + 32 + legal (pad 16: info caption #514A47 (subscriptionInfoIosIAP / subscriptionInfoAndroidExternal) + 24 + Row из двух PLSecondaryButton.small [rulesHeavy 16 + 4 + subscriptionConditionsButtonTitle baseBold] / [lock 16 + 4 + subscriptionPolicyButtonTitle], gap 8) + 32.
- TariffWidget: белый r20 pad 20, stroke 1.33 #E1D6D1 / выбран 2 terracotta (анимация 150 мс easeInOutCubic): период captionBig (terracotta если выбран / чёрный) + 38 + Row [цена titleLight чёрный, зачёркнутая базовая цена baseLight #514A47 с анимированной terracotta линией 1.33 (800 мс, Interval .6–1)] + 2 + «oneTimePayment.price / period» caption #B3ACA9; бейдж скидки «-N%» caption белый (pad 8/4, r8, terracotta если выбран / чёрный) @right 12 top −12. Тап недоступен во время покупки.
- Низ: контейнер pad 12/8, верхняя граница #F2E7E3@50%: PLPrimaryButton subscriptionPurchaseTitle(цена) (loading при purchasing, disabled при restoring); на iOS IAP + 8 + PLTextButton subscriptionRestoreTitle.
- Слушатели: paymentUrl → EventPaymentStarted(android) + push ExternalPaymentRoute → paymentStatusCheckRequested(didCompletePayment); paymentStatusPending → PendingPayment BS; paymentStatusFailed → FailedPayment BS(цена, retry → onPurchasePressed); success → replace PurchaseTypRoute; error → SubscriptionErrorDisplayer; noSubscriptionAfterSuccessPayment → snack noSubscriptionAfterSuccessPaymentErrorText + pop.

## Экран «Внешняя оплата» (ExternalPayment, WebView)
- PlScaffold + InAppWebView(url); shouldOverrideUrlLoading: failRedirectUrl → status error + ViewWebViewComplete(fail); successRedirectUrl → success + ViewWebViewComplete(success); не-http → launchUrlString external (банковское приложение); onLoadStop(url==paymentUrl) → ViewWebViewPayment(true); onReceivedError → ViewWebViewPayment(false). pop(true) только когда приложение активно (resumed) и статус success/error.

## BS «Ожидание оплаты» (PendingPayment)
- picTime 56 + 8 + subscriptionPendingPaymentTitle subtitle + 8 + subscriptionPendingPaymentDescription lineLight #514A47 + 16 + PLPrimaryButton subscriptionPendingPaymentButtonTitle → pop + pop экрана offers.

## BS «Ошибка оплаты» (FailedPayment)
- picAttention 56 + 8 + subscriptionFailedPaymentTitle subtitle + 8 + subscriptionFailedPaymentDescription lineLight + 16 + PLPrimaryButton subscriptionFailedPaymentRetryButtonTitle(price) + 8 + PLSecondaryButton subscriptionFailedPaymentCancelButtonTitle.

## Экран TYP (PurchaseTyp)
- Чёрный фон + два terracotta@50% размытых круга (527 @right −320 bottom −130; 286 @left −120 top 120); PlAppBar прозрачный: subscriptionTYPHeader lineBold белый + close 24 белый. Контент pad 16: 24 + premiumIcon 56 #E7C9B2 + 16 + «subscriptionName на период» headingHeavy белый + 8 + subscriptionTYPCancelInfoText lineLight белый + 48 + subscriptionTYPFeaturesTitle subtitle белый + 20 + FeatureWidget.fromIconUrl (фон #000@20%) gap 8; внизу: subscriptionStatusActive subtitle terracotta + 8 + (notProlonged && external && СБП → subscriptionNotProlongedSBP lineLight; иначе subscriptionNextPayment(цена, дата) baseLight белый) + 8.

## BS «Отмена продления» (CancelProlongation)
- picAttention 56 + 8 + subscriptionCancelProlongationTitle subtitle + 8 + subscriptionCancelProlongationDescription(subscriptionUntilDate) lineLight #514A47 + 16 + PLPrimaryButton subscriptionCancelProlongationPositiveButtonTitle (оставить → pop) + 8 + PLSecondaryButton subscriptionCancelProlongationNegativeButtonTitle (loading). Отмена: external → cancelProlongationRequested → pop при canceled; IAP → открыть настройки подписок стора + проверка при resume; несоответствие → snack subscriptionManagementIAPConfigurationErrorText / subscriptionManagementExternalConfigurationErrorText. Аналитика ActionTapNotProlonged.
- ProlongSubscriptionListener (глобально): prolonged → snack subscriptionProlongSucceedText (success); error → SubscriptionErrorDisplayer.
Flutter — сообщества и реферальная программа (notes_flutter_communities_referral.md, 14965 байт)
# Flutter — фичи «Сообщества» (features_communities.txt, 5142) и «Реферальная программа» (features_referral_program.txt, 2742), прочитано полностью

## Сообщества — данные / API
- CommunityApi: GET communities (мои: {communities[{communityId, name, participantsCount, inviteLink, isClosed, isVisible, iconUrl, registratedAt}]}), GET communities/all ({allCommunities[{communityId, name, participantsCount, iconUrl, isClosed}]}), GET communities/{id} (CommunityShort {communityId, name, isClosed, iconUrl}), POST communities/{id}/join (EventCommunityJoin), POST communities/{id}/leave (EventCommunityLeave), POST communities/{id}/visible, POST communities/{id}/invisible.
- CommunitiesRepository: BehaviorSubject кэш моих/всех сообществ (latestOrFetch), инвалидация; leave/visible/invisible обновляют кэш локально; join инвалидирует кэш моих.
- Приглашения (AppsFlyer deeplink `community_invite` + deep_link_sub1=communityId): CommunitiesDeeplinkBloc слушает install-conversion (non-organic, пока не обработан isInstallInviteProcessed) и onDeeplink; сохраняет CommunityInviteEntity.raw в ICommunityStorage (JSON invites). CommunityAccessionInitBloc: updateSavedInvites (синхронизирует accepted с моими сообществами), inviteReceived(communityId) → alreadyJoined / confirmationBS(isFromSavedInvite false), processSavedInvite(isFromCommunities) — при входе в АЗ и на экране Сообщества: берёт первое raw или postponed старше 1440 мин → confirmationBS(isFromSavedInvite true). Статусы приглашения accepted/postponed/rejected.
- CommunityAccessionBloc: started → сохраняет postponed + EventCommunityInviteBottomSheet(source, id); joinCommunity → POST join → accepted → success(shouldUpdateCommunities = !isClosed); joinLater → postponed; cancel → rejected. Аналитика ActionCommunityDecline / ActionCommunityPostpone.
- CommunityRequestResultBloc: по пушу NotificationDeeplinkCommunityCheck(isAccepted) / CommunityExclusion → GET community → BS результата accepted/declined/excluded (declined/excluded удаляют приглашение; accepted/excluded → CommunitiesUpdate shouldUpdated).
- CommunitiesUpdateBloc: shouldUpdate → перезагрузка экрана Сообщества и ProfileCardPreview.
- CommunitiesErrorDisplayer: UserAlreadyInCommunity / RejectedJoinRequest / JoinAlreadyRequested → specialMessage или apiExceptionNotFound.
- Аналитика: EventCommunitiesDetailsScreen(source profile|…), EventSendCommunityLink, EventCopyCommunityLink.

## Экран «Сообщества» (Communities, source из профиля)
- Чёрный фон; PlAppBar чёрный: back белый (18×11.5), title communitiesAppBarTitle lineBold белый, action communityList24 белый → CommunitiesList.
- loading → PLFullScreenLoader; loadFailure → ErrorPlaceholder; updating (выход) → PLFullScreenLoader поверх.
- Нет сообществ (CommunityEmptyView, pad 16): CircleGradient terracotta 150 @top 70; 24 + communityEmptyLogo 120 + 24 + communitiesEmptyStateTitle lineHeavy белый + 24 + communitiesEmptyStateSubtitle lineLight #B3ACA9 + 24 + FeatureWidget.fromAsset(filters24Heavy, communityFeatureAdditionalFilters; прозрачный, stroke 1.33 #514A47 r28, белый текст) + 8 + FeatureWidget(communityLogo24Heavy, communityFeatureCommunityLogo) + Spacer + communityAvailableCommunitiesTitle subtitle белый + 8 + communityAvailableCommunitiesSubtitle1 lineLight #B3ACA9 + Subtitle2 terracotta подчёркнутый (→ email communitySupportEmail) + 16 + PLSecondaryButton.inverted communityCommunitiesListButton + 24.
- Есть сообщества (_CommunityDataView): фон два CircleGradient terracotta 150 (@top 80, @bottom −120); 24 + карусель (CarouselSlider height 120, viewportFraction 0.55, zoom (80/120)^2, без бесконечного скролла; элемент pad 8: CommunityLogoWidget 120 (круглая иконка либо круг #F2E7E3 с первой буквой captionBig fontSize size*0.625 #514A47) или placeholder communityEmptyLogo; vibrateScrollImpact при смене) + 24; при выбранном placeholder → CommunityPlaceholderStateWidget (pad 16: communityPlaceholderJoinCountTitle(N) lineHeavy белый + 8 + communityPlaceholderOpportunitiesSubtitle lineLight #B3ACA9 + Spacer + communityPlaceholderAvailableListTitle subtitle белый + 8 + communityPlaceholderDescription + communityPlaceholderSupportEmail terracotta (→ email) + 24 + PLSecondaryButton.inverted communityPlaceholderViewCommunitiesButton + 24); иначе CommunityDetailsWidget: name lineHeavy белый + 8 + communityParticipantsCount(n) baseLight #B3ACA9 + [8 + communityEntryDate(date)] + 32 + CommunityVisibilityButton + 24 + PLTextButton.secondary communityLeaveButton + 24; затем белый низ r24 (pad 16/24 + bottom safe): communityInviteTitle subtitle чёрный + 8 + communityInviteSubtitleClosed/Opened lineLight #514A47 + 16 + PLCopyWidget.onLight(inviteLink) + 8 + PLSecondaryButton communitySendInviteButton (Share communityLinkSharingText(name, link) → snack communityLinkSharedSnack) + 8.
- CommunityVisibilityButton: 210×58 r100 stroke 3 белый (фон invisible #F2E7E3@25%), текст baseLight (visible: белый @left 20 communityVisibilityEnabledStatus / invisible: #514A47 @left 76 communityVisibilityDisabledStatus), бегунок 46×44 белый @left 157 / 7 с revealed24Light #9E8075 / hidden24Light terracotta / маленьким лоадером; + 8 + описание caption #B3ACA9 (communityVisibilityEnabledDescription / DisabledDescription).
- Выход: PlDialog communityDeleteAccountDialogTitle(name) + communityPrivateLeaveDialogBody / communityLeaveDialogBody; действия communityLeaveDialogLeaveButton (destructive) / communityLeaveDialogCancelButton. Успех → snack communityLeftSnack (success). Копирование ссылки → snack communityLinkCopiedSnack.
- Placeholder в карусели добавляется, когда есть мои сообщества и есть доступные (all − mine > 0).

## Экран «Список сообществ» (CommunitiesList)
- PlAppBar communitiesListAppBarTitle (белый экран); loading → большой лоадер #E1D6D1; error → ErrorPlaceholder; data: PlSearchField (pad 8/12, hint communitiesListSearchHint, debounce 500 мс; ранжирование: точное 1000 / startsWith 800 / contains 600 / слово с 1 опечаткой 400 / всё название с 1 опечаткой 200) + список (только не мои): элемент pad 8/16: CommunityLogoWidget 40 + 12 + [name lineLight #514A47 (2 строки) + 4 + communityParticipantsCount baseLight #B3ACA9] + иконка join 24 (→ POST join); разделитель 1px #E1D6D1 indent 68/16. Пусто: communitiesListEmptyTitle captionBig terracotta + 8 + communitiesListEmptySubtitle1 + Subtitle2 terracotta подчёркнутый (email). Updating → PLFullScreenLoader.
- После вступления → CommunitySuccessJoinBs (чёрный BS 0.8, три CircleGradient terracotta 223/114/114; pad 12/24/8: CommunityLogoWidget 56 + 8 + communitySuccessJoinCloseTitle(name)/OpenTitle subtitle белый + 8 + communitySuccessJoinCloseSubtitle/OpenSubtitle lineLight #B3ACA9 + 32 + фичи (stroke 1.33 #514A47 r28 pad 9/16/9/20: filters24 белая + communityAccessionBSFeature1; логотип 24 + communityAccessionBSFeature2) gap 8 + 40 + PLSecondaryButton communityRequestResultOkButton + 8) + CommunitiesUpdate shouldUpdated.

## BS «Вступление в сообщество» (CommunityAccession, по ссылке / сохранённому приглашению)
- Тот же чёрный BS: CommunityLogoWidget 56 + 8 + communityAccessionBSTitle(name) subtitle белый + 8 + communityAccessionBSSubtitle lineLight #B3ACA9 + 32 + 2 фичи + 32 + PLButton primaryInvertedColorsTerracotraForeground communityAccessionBSJoinButton (loading) + 8 + PLButton secondaryTransparent (isFromCommunities ? communityAccessionBSCancelButton : communityAccessionBSLaterButton) + 8. Успех → pop (+ обновление сообществ если открытое); ошибка → CommunitiesErrorDisplayer + pop. alreadyJoined → snack communityAccessionErrorAlreadyJoined.

## BS «Результат заявки» (CommunityRequestResult)
- Чёрный BS: логотип 56 + 8 + communityRequestResultAcceptedTitle(name) / DeniedTitle / ExcludedTitle subtitle белый + 8 + …Subtitle lineLight #B3ACA9 + 16 + PLSecondaryButton communityRequestResultOkButton + 8.

## Реферальная программа — данные / API
- ReferralApi: GET referral/details → {isInvited, deferredDays, referralSummary{totalDays, invitedUsers, invitersPercent}?, inviteDays (бонус рефереру), friendDays (бонус другу), motivationProgram[{days, invitationsCount}], activitiesHistory[{days, invitationsCount?, registratedAt?, gender?}]?}; POST referral/approve/{referrerId} (EventReferralAcceptEvent).
- Deeplink AppsFlyer `referral-invite` + deep_link_sub1=referrerId (install conversion non-organic или onDeeplink, isDeferred); ReferralDeeplinkBloc сохраняет referrerId/isDeferred в IReferralProgramStorage (только если ещё не сохранён). generateReferralLink(userId) через AppsFlyer OneLink.
- ReferralConfirmationInitBloc.initialLoad: details.isInvited → alreadyInvited; нет referrerId → noReferrer; didAccept → didAcceptReferralProgramParticipation; postponed < 1440 мин → postponed; tariffsAndFeatures + profile → referrerId == my userId → clearStorage + selfInvited (snack referralConfirmationSelfInviteErrorTitle); иначе confirmationBS(referrerId, referralBonusDays, features, gender, isDeferred) + EventReferralInviteBottomSheetEvent(source delayedTransition|referralLink).
- ReferralConfirmationBloc: approved → POST approve → saveDidAccept(true) → success → ViewVerifyFirstScreen(referral) + pop(true) + push VerificationRouter; postponed (закрытие BS без результата true) → saveDidAccept(false) + savePostponedAt(now) + ActionReferralPostponeEvent.
- ReferralCongratsBloc: pushReceived(isLinkOwner) + subscriptionUpdated(expiredAt) → success → navigate ProfileRoute + диалог поздравления.
- Аналитика: EventReferralDetailsScreenEvent(source profile|…), ActionSendReferralLinkEvent(invitedUsers), ActionCopyReferralLinkEvent(invitedUsers).

## BS «Подтверждение участия» (ReferralConfirmation)
- Чёрный BS 0.8 с тремя CircleGradient: premiumIcon 56 #E7C9B2 + 8 + referralConfirmationTitle(gender) subtitle белый + 8 + referralConfirmationSubtitle(days) lineLight #B3ACA9 + 32 + список фич (высота 30% экрана, FeatureWidget.fromIconUrl прозрачный stroke 1.33 #514A47 r28, gap 8) + 32 + PLButton primaryInvertedColorsTerracotraForeground referralConfirmationConfirmButtonLabel (loading) + 8 + PLButton secondaryTransparent referralConfirmationCancelButtonLabel + 8.

## Диалог «Поздравление» (ReferralCongrats)
- showDialog по центру pad 20: чёрный r28 SmoothClip с двумя CircleGradient (114 @bottom −15 left −74; 223 @bottom 12 right −120); pad 24/8/20/20: premiumIcon 56 #E7C9B2 + 16 + referralCongratsTitle headingHeavy белый + 16 + referralCongratsLinkOwnerSubtitle(days) / LinkReceiverSubtitle(days) lineLight #B3ACA9 + 24 + referralCongratsSubscriptionExpireDate(date) baseLight #F2E7E3 + 24 + текст-кнопка referralCongratsConfirmButtonLabel subtitle terracotta.

## Экран «Реферальная программа» (ReferralProgram, source profile)
- Чёрный фон. Loading: чёрный AppBar с белым back + PLFullScreenLoader (#514A47). Error: чёрный AppBar + ErrorPlaceholder (subtitle #B3ACA9, кнопка secondarySmall).
- Data: SliverPersistentHeader pinned (min 44 / max 152): expanded — back белый + referralProgramTitle headingHeavy terracotta + 8 + подзаголовок lineLight #B3ACA9 (referralProgramSubtitlePremium / referralProgramSubtitleNoPremium(referrerDays, referralDays)); collapsed — referralProgramTitle lineBold белый (150 мс).
- Тело: если totalDays==0 || invitedUsers==0 → картинка (Images.freindsImage при премиуме / Images.referralBox) иначе 32; StatisticsBlockWidget (pad 12): без участия → PLCopyWidget.onDark(link) + 16 + PLSecondaryButton referralProgramShareLinkButton + 32 + (premium: referralProgramInfoNoFriendsPremium(referrerDays, referralDays) lineLight #B3ACA9; иначе referralProgramInviteForMoreDays1 + «\n» + InviteForMoreDays2 terracotta → referralIfoUrl) + 32; _FriendsDaysWidget (две карточки 140 высотой gap 16, _InfoBlockDecorator r20 с CircleGradient terracotta@50% 100 + заливка #514A47@50%: 34 + число titleHeavy белый + 8 + referralProgramFriendsInvited(n) / referralProgramDaysReceived(n) lineLight #B3ACA9) + 16 + _ProgressBarWidget (pad 20/24: полоса 6 r3 #B3ACA9 + заполнение белое по invited/maxLabel, метки lineLight (#B3ACA9 / белый достигнуто) уровней мотивации + 0; + 16 + описание lineLight #B3ACA9: referralProgramDaysMaxMotivation(bonus) / DaysNewMemberMotivation(next, bonus) / DaysMemberMotivation(remaining, bonus)); с участием → 32 + PLCopyWidget.onDark + 16 + PLSecondaryButton share + 32 + (percentile>0: referralProgramStatisticsInfo1(p) + «\n» + Info2 terracotta; иначе StatisticsInfoZeroPercent1/2); премиум без участия → 32 + InviteForMoreDays1/2; + 24. Далее referralProgramActivityTitle subtitle белый + 8 + (пусто → referralProgramActivityEmptyContent lineLight #B3ACA9 pad 16) + карточки активности (pad 12: «+N» captionBig белый + 4 + premiumIcon 20 #E7C9B2 в блоке 68 + 16 + [16 + текст caption #B3ACA9 (referralProgramActivityFriendTitle(gender, date) / ActivityProgressTitle(count)) + 15 + линия 1px #514A47]) + 48.
- Share → Share.share(referralProgramLinkSharingText(url)) → snack referralProgramSuccessLinkSharing (success) + ActionSendReferralLink; копирование → snack referralProgramSuccessLinkCopying + ActionCopyReferralLink; «Подробнее» → referralIfoUrl.
Flutter — persistence и debug-экран (notes_flutter_persistence_debug.md, 2555 байт)
# Flutter — persistence (1666 строк) и debug (791 строк), прочитано

## Кэши
- ImagesCacheManager: stalePeriod 30 дней, 400 объектов. MatchingVideosCacheManager: 7 дней, 5 видео. ProfileVideoCacheManager: 365 дней, 1 видео.

## SharedPreferences-хранилища (ключи)
- ChatStorage: is_first_chatting (для BS уведомлений после 2-го сообщения).
- CommunityStorage: community_invites_info (JSON), install_invite (bool).
- CommunityRulesTrainingStorage: is_trained_community_rules.
- ConfigStorage (debug): proxy_url, url_type, chats_url_type, ws_url_type, is_debug_analytics_tracking_enabled.
- FirstRunStorage: first_run.
- LocationDeterminationStorage: city_id, city_name, get_detect_type (manual|auto|notSaved).
- MatchingStorage: min_age, max_age, first_open_age, first_open_cards (обучение свайпам пройдено), user_cards (JSON-список карточек), shown_cards, shown_videos (последнее показанное рекламное видео), advertisin_volume (громкость рекламы, default 0); + кэш видео матчинга.
- MediaStorage: кэш изображений и видео профиля (putVideoFileInCache/putPhotoInCache с eTag).
- FirstLikeStorage: first_like (BS уведомлений после первого лайка).
- OnboardingStorage: onboarding_step, survey_answers (JSON-список ответов по шагам).
- ReferralProgramStorage (SecureStorage, НЕ очищается при логауте): referrer_id, is_organic (isDeferred), referral_accept, accepted_at (UTC ISO).
- SympathyStorage: time_expires_at (таймер открытия лайка).
- ThemeModeStorage: theme mode.
- TokenStorage (SecureStorage): app_auth_token {access_token, refresh_token}.
- OnboardingUserInfoStorage: first_name, last_name, prompt_answers (JSON), job, birth_date, gender (male/female), height, red_flags, green_flags, bio.
- UserStorage: user_id, survey_percentage.

## Debug-экран (только не-prod сборки; открывается долгим нажатием на QaWrapper)
- Карточки: переключение сервера (radio Url stage/prod + кнопка подключить), прокси (поле + подключить), свитч debug-трекинга аналитики, тема (light/dark/system), значения RemoteConfig; кнопки: просмотр БД (drift viewer), очистить БД, выйти.
Android — каркас, тема, сеть, API, таббар, auth (notes_android_core.md, 10315 байт)
# Android (ru.polyubvi.app) — core, root, theme, main, auth (прочитано: root.txt, ui_theme.txt, core.txt, ui_main.txt, ui_auth.txt)

## Каркас
- AppContainer (ручной DI): NetworkMonitor, HttpClient (API_BASE_URL) + chatsHttpClient (CHATS_BASE_URL), EncryptedTokenStore, OnboardingPrefs, API: Auth, Matching, Likes, Chat, Profile, Survey, Common, DatingTz, Subscription, Payment, Community, Referral, Faq, Cities; AnalyticsQueue (своя очередь → POST /analytics/events, батчи 5/50, кап 500), FeatureFlagsStore (GET /feature-flags, TTL 5 мин; ключи mixed_feed, public_events, geo_encounters, company_invite, scoring, voice_onboarding, chat_moderation), ChatWebSocket, PushService, SessionManager. Токен подставляется в оба клиента; refresh через POST /auth/token/refresh под мьютексом.
- MainActivity: edge-to-edge, deepLinkState (uri+seq), запрос POST_NOTIFICATIONS контекстно (не на холодном старте); RootContent: Bootstrapping → LoadingScreen; Offline → OfflineRetryScreen; Unauthenticated → AuthScreen; NeedsOnboarding → OnboardingScreen; Authenticated → (ageRangeDone? нет → AgeRangeScreen (Back = пропустить)) → (postVerifyDone? нет → VerificationScreen soft, autoDismissIfVerified) → MainTabsScreen. При аутентификации: featureFlags.refresh, pushService.syncToken, analytics auth_ok.
- PoLyubviApp: StrictMode в debug, NotificationHelper.ensureChannels, networkMonitor.start, onLogoutExtra = clear OnboardingPrefs + FCM unregister; Coil ImageLoader (memory 25%, disk 120 МБ, respectCacheHeaders false).
- SessionManager: bootstrap → GET /users/profile; сетевая ошибка → Offline (не разлогинивает); 401 → refresh → повтор; отказ refresh → logout. needsOnboarding = нет имени или даты рождения или surveyPercentage < 7. Состояния Bootstrapping/Unauthenticated/NeedsOnboarding/Authenticated/Offline.
- OnboardingPrefs (pl_onboarding): age_range_done, post_verify_done, swipe_training_done. EncryptedTokenStore (polyubvi_tokens, AES256; при порче keyset — очистка и пересоздание).
- Нет: RemoteConfig (Firebase), AppMetrica, AppsFlyer, drift/локальной БД чатов, тёмной темы (только light), локализации (строки захардкожены), обновлений (hard update).

## Тема
- PlColors = палитра Flutter 1:1 (terracotta #C66F53, paleBeige #E7C9B2, powdery #F2E7E3, white, black #000, beigeishBlack #514A47, beigeishGray #B3ACA9, powdery50a #F9F3F2, darkBeige #9E8075, darkPowdery #E1D6D1, errorRed #FA5F3D). PlDimens = радиусы 8/12/16/20/28, отступы 4/8/12/16/18/20/24/32/48, кнопки 56/44.
- PlType: TT Runs (medium/bold/extrabold) headingHeavy 30/32 EB, headingLight 30/32 M, titleHeavy 22/24 B, titleLight 22/24 M, captionBig 18/22 EB, captionMedium 18/22 B, lineHeavy 16/20 EB; Manrope (medium/bold): subtitle 20/24 B ls .2, lineBold 16/20 B, lineLight 16/20 M, baseBold 14/18 B ls .1, baseLight 14/18 M, caption 12/16 M. (Во Flutter AppTextScheme те же 14 стилей — сверить letterSpacing/lineHeight.)
- Material3 lightColorScheme только; тёмной темы нет (во Flutter тёмная = светлая, паритет фактически есть).

## Сеть
- HttpClient: OkHttp, таймауты 30 с, логирование только в debug, cert pinning из BuildConfig.CERT_PINS; 401 → refresh 1 раз; ошибки → ApiException(message из detail/message/error, status, serverCode, isNetwork). Особая подмена «Неверный запрос.» → «Данные анкеты неполные или не приняты сервером…». Нет маппинга кодов ApiException 101–122 как во Flutter (specialMessage/сообщения по коду).
- ChatWebSocket: OkHttp WS, ping 30 с, header Bearer, reconnect backoff n² с (≤30 с, 5 попыток-множитель), outbox с таймаутом 30 с → onSendFailed(front_id). Исходящие: только send-text (с reply_to) и set-last-read. НЕТ: user-is-typing, edit-text, delete-message. Входящие события просто прокидываются как JSONObject.
- Apis: Auth (POST /auth/otp, /auth/otp/verify, /auth/token/refresh); Matching (GET /matching/users, GET /users/{id}, POST /matching/like/{id}, POST /matching/super-like/{id} {message}, GET /matching/filters, PATCH /matching/recalculate); Likes (GET /users/likes, /users/superlikes, POST/DELETE /users/likes/{id}, /users/superlikes/{id}); Faq (GET /faq); Chat (GET /user-chats, /user-chats/new-pairs, /user-chats/{id}, /user-chats/{id}/messages/previous?batch_id&number); Profile (GET/PATCH /users/profile, POST/DELETE /users/profile/photos, PATCH reorder, POST/DELETE /users/profile/video, GET /users/profile/verify/status, POST /users/profile/verify/photo, POST /users/profile/position, POST /users/deactivate {reason}, POST /users/{id}/report {report_place, report_reason}, POST /users/{id}/delete-pair, POST /feedback, GET/PATCH /users/profile/settings); Community (GET /communities/all, GET /communities, POST join/leave — НЕТ visible/invisible, GET /communities/{id}); Referral (GET /referral/details — НЕТ approve); Payment (POST /payments/android, POST /payments/android/status); Cities (GET /cities?term=); Subscription (GET /subscriptions/tariffs, GET /subscriptions/me — НЕТ PATCH me продление/отмена); Survey (GET /survey, GET /prompts); Common (POST push-notifications/add-fcm-token, delete-fcm-token). НЕТ: DELETE new-pairs/{userId}, messages/next, updates, dislike (нет POST /matching/dislike?!), undo, advertising, ip, chat report place.
- DatingTzApi (расширение plibvi, отсутствует во Flutter): /feed, /events, /public/events, /events/{id}/reaction, company-invite(s), /users/profile/event-interests, /geo/ping, /geo/encounters, /analytics/events, /feature-flags, /scoring/me|available|checks, /voice/sessions|turns|apply.
- Модели: UserCardDto (+ trust {trust_tier, overall_score, badges} — trust chip; communities; shared_event_interests), UserDataDto (likes), ChatDto/ChatMessageDto (front_id, reply_to, is_edited), NewPairDto, TariffDto (product play_store_product_id), UserSubscriptionDto {status, is_active, expires_at} (без paymentSystem/tariff/features/deferredDays), FaqItemDto, UserSettingsDto (6 флагов), ReferralDetails (без activitiesHistory), Survey модели (single/multiple/slider с вложенными options), ProfilePatchBuilder (PATCH всегда полный: дефолты «Пользователь»/«—»/male/1995-01-01/170/«Пока без описания»/«Не указано» — риск затирания данных).
- Пуши: каналы default/messages/matches/events; deepLinkFromData: company_invite/event → polubvi://events…, message/chat → polubvi://chat?chat_id; FCM service показывает уведомление сам. DeepLinkParser: хосты chat-details/chat/chats → таб 2; likes/super-likes → 0; user-cards/matching/feed → 1; profile/… /community/scoring/voice → 3; events/company-invite → таб 3 + eventId/invite.

## MainTabsScreen
- 4 таба (0 симпатии, 1 лента (стартовая), 2 чаты, 3 профиль); нижняя панель: белая, верхняя линия 1px #E1D6D1, pad 6/24, TabSvg 48×40 (выбранная — многоцветная SVG 40, невыбранная — 24 чёрная); анимация скрытия при immersive (240 мс). KeepAliveTab (табы остаются в композиции). Back на не-ленте → лента. Баннер «Нет сети — проверь интернет» (caption белый на #FA5F3D@95%) сверху. MatchOverlay поверх всего (zIndex 20). Deeplink → выбор таба / eventId в профиль. НЕТ бейджа непрочитанных (во Flutter тоже нет).

## Auth
- AuthCopy: строки из arb (splash, phone, otp) захардкожены; policyUrl Google Docs.
- CTA: белый фон, pad 16: Spacer + splashTitle headingHeavy terracotta + 12 + splashSubtitle lineLight #514A47 + 24 + couple.png (pad 31, высота 220) + 48 + phoneTitle («Укажи телефон») titleHeavy + 8 + splashBody lineLight + Spacer + PrimaryButton «Войти по смс» + 16. (Flutter AuthCta: см. notes_flutter_auth_etc — сверить порядок/тексты.)
- Phone: кнопка «Назад» (иконка back 20 + baseBold), phoneTitle titleHeavy + 8 + phoneSubtitle lineLight + 24 + PlPhoneField (label «Номер телефона», +7, маска) + 24 + строка lock 16 terracotta + «Никто не узнает твой номер» baseLight + 32 + ошибка (baseLight #FA5F3D) + Checkbox Material (terracotta) + termsCheckbox baseLight + 20 + лицензия caption (part2 terracotta, вся строка кликабельна → policyUrl) + 16 + PrimaryButton «Далее» (enabled: 10 цифр + чекбокс). Ошибки: «Введите номер полностью (10 цифр после +7)», «Подтверди, что тебе есть 18 и ты не в браке».
- OTP: кнопка «Изменить номер» (edit 16 + baseBold), «Введи код» titleHeavy + 8 + «Отправили 6 цифр в СМС на +7 …» lineLight + 24 + OtpPinField (6, autofocus, автосабмит при 6 цифрах) + подсказка «Тестовый код: …» (debug local/dusha) + ошибка caption red + 12 + TextButton «Новый код можно будет получить через N с.» / «Запросить новый код» (baseBold terracotta/#B3ACA9; cooldown 45 с клиентский, не code_lifetime с сервера) + Spacer + PrimaryButton «Войти» (enabled 6 цифр) + 16. Ошибка верификации — текст сервера или «Неверный код».
Android — компоненты, онбординг, лента, симпатии, чаты (notes_android_ui1.md, 22461 байт)
# Android — компоненты, онбординг, лента, симпатии, чаты (ui_components, ui_onboarding, ui_feed, ui_sympathy, ui_chats — прочитано полностью)

## Компоненты (CommonUi, PlAssets, PlTopBar и др.)
- PrimaryButton: чёрная, белый текст lineBold, h56, r12, disabled #F9F3F2 + #9E8075@50%, лоадер 22 #9E8075. SecondaryButton: OutlinedButton stroke 1.5 чёрная, текст lineBold чёрный, h56 r12 (во Flutter secondary = заливка #F2E7E3 без обводки; здесь другой стиль!).
- PlTextField: подпись СНАРУЖИ поля (baseLight #514A47), OutlinedTextField фон #F9F3F2, r12, граница #E1D6D1 / фокус terracotta, текст lineLight, placeholder #B3ACA9. (Flutter PlTextInput: label внутри контейнера #F9F3F2 без обводки — расхождение.)
- PlPhoneField: +7 prefix, маска 900 123-45-67. OtpPinField: 6 ячеек 48×56 r12 фон #F9F3F2, граница #E1D6D1/terracotta, символ titleHeavy, «·» placeholder (Flutter Pinput 6×56 — ячейка 56 шириной).
- ErrorScreen: error_placeholder.png 160 + «Упс» titleHeavy + текст lineLight + PrimaryButton «Повторить» (Flutter ErrorPlaceholder: «Не загрузилось :(» + «Попробуй обновить страницу» + small «Обновить»).
- EmptyScreen: PNG 200×240 / SVG 160 + title captionBig terracotta + subtitle lineLight + PrimaryButton 70% ширины.
- RemoteImage (Coil, без blur-слоя), AvatarImage circle, prefetchImages. Пустое фото: #E1D6D1 + иконка Person Material.
- PlImages (PNG те же имена, что во Flutter: couple, dislike_overlay, error_placeholder, freinds_image, hard_update_image, like_overlay, no_users, photo_helper, referral_box, smiling_woman). PlIconAssets — подмножество SVG (heart/cards/chat/profile + *_selected, heart_filled, heart_zero, close, cross_filled, lock_light, send_note_heavy, plus_24_heavy, training_*, photo_placeholder, verified_checkmark, filters_24_heavy, back, edit_16/24_heavy, geolocation_24_heavy, user_likes_empty, pic_no_lovenotes, pic_chatting, gear, gear_40, profile_edit, premium_icon, checkmark, chevron_up_24, verification_illustration, verification_waiting_illustration, oopps, pic_match, pic_attention, new_pairs_hearts, refresh_16_heavy, message_sent/read/reply, message_tail, my_message_tail).
- PlTopBar: белый, statusBars inset, h56, back 22 + title titleHeavy (22 TT Runs) + subtitle caption #B3ACA9; EnvBadge (не prod) #F2E7E3 r8 caption terracotta; action TextButton baseBold #FA5F3D; trailing. (Flutter PlAppBar 44 высотой, title lineBold 16 по центру.)
- ReportUserDialog: Material AlertDialog «На что хочешь пожаловаться?» titleHeavy + пункты lineBold + «Отмена». Причины user_card: obscene_language «Нецензурная лексика», forbidden_media «Откровенные фотографии», behavior «Неприемлемое поведение», catfisher «Не тот, за кого себя выдаёт»; chat: obscene_language, behavior, catfisher, other «Другое». (Flutter Report BS с primary-кнопками, тексты из arb — сверить.)
- Скелетоны shimmer (#F9F3F2 → #E7C9B2@85%): Feed/List/Sympathy/Chats/Settings/Profile/ChatThread; SmoothContent fade 340 мс; BrandedLoading (пульс 72 круг + «Загружаем…»).
- OfflineRetryScreen: «Нет связи с сервером» + «Проверь интернет и попробуй ещё раз. Твоя сессия сохранена.» + кнопка terracotta r16 h52 «Повторить».
- TrustChip (dating-tz): caption 11sp, фон #E7C9B2@22% / terracotta@12%, r20, «Trust · high/+/…». UserCardDetailsSheet (ModalBottomSheet r28, ручка 40×4): аватар 72 + имя/возраст titleHeavy + verified 18 + job + meta (рост · локация · в сети) + чипы «Совпадение N%» / trust / «Premium»; баннер «Вам по душе одно и то же» (shared events); «Открывашки» (карточки #F9F3F2 r16 stroke terracotta@18%: вопрос caption terracotta + ответ lineBold); «О себе» (««bio»» в #F9F3F2 r16); «Образ жизни» (Увлечения / Ценности / Сообщества чипы); «В отношениях» (🟢 Зелёные флаги / 🔴 Красные флаги, зелёные/красные Material-цвета E8F5E9/2E7D32, FFEBEE); «Закрыть» baseBold terracotta.
- PlDateTime: formatChatDay «Сегодня/Вчера/13 июл», formatShortTime HH:mm, formatRelativeWhen.

## Онбординг (OnboardingScreen/ViewModel) — собственный дизайн, не Figma
- Порядок: Splash → Name → Birthday → GenderHeight → CityPick (SelectCityScreen) → SurveyQuestion (все вопросы анкеты подряд, required первыми) → Occupation → Flags → Prompts → AboutMe → PhotoPick → VideoPick → Submitting → Done. Chrome: progressLabel caption #B3ACA9 + LinearProgressIndicator 4dp terracotta/#E1D6D1 (монотонный 0.04…0.96) + pad 16. Везде кнопки «Далее» PrimaryButton + TextButton «Назад» (+ «Пропустить» где опционально).
- Splash: «Расскажи о себе» headingHeavy ЧЁРНЫЙ + «Несколько коротких шагов — и анкета появится в ленте» + «Начать». (Flutter: сплэш онбординга с картинкой/заголовком terracotta — сверить по notes_flutter_user_about.)
- Name: «Как тебя зовут?» titleHeavy + поля «Имя» (hint «Как вас зовут») и «Фамилия»; ошибки «Введите имя»/«Введите фамилию». Birthday: «Дата рождения» + поле с маской ДД.ММ.ГГГГ (hint 15.05.1995); ошибка «Дата в формате ДД.ММ.ГГГГ»; 18+ проверяется только при submit («Регистрация с 18 лет»). (Flutter: Pinput 8 ячеек, отдельный экран.)
- GenderHeight: «Пол и рост» + чипы «Мужской»/«Женский» (чёрная заливка выбранного) + поле «Рост, см» (50–250, default 170). (Flutter: picker роста 248×228, 50–250 default 180.)
- CityPick: SelectCityScreen (см. ui_location). Survey: stepTitle baseBold terracotta + вопрос titleHeavy + описание + «Можно выбрать несколько · N выбрано» + список опций (r12, чёрная заливка выбранного, stroke #E1D6D1) + «Далее»/«Пропустить». Нет слайдера (slider-компонент рендерится как список опций!). Максимум 10 для multi.
- Occupation: «Чем занимаешься?» + поле «Работа / занятие» (обязательно; ошибка «Укажите работу или род занятий»; max 80 в UI, 50 при отправке). (Flutter: occupation — опционально.)
- Flags: «Флаги» + «Что цените и с чем не готовы мириться. Можно пропустить.»; Зелёные (terracotta) / Красные (#FA5F3D) чипы accent@12% r12 с «×»; строка добавления (поле + чёрный круг 48 «+»); max 7, длина 30. (Flutter: отдельные BS ввода, чипы стиля #514A47/stroke.)
- Prompts: «Открывашки» + «До 3 ответов — помогают начать разговор. Можно пропустить.»; список выбранных (карточки #F9F3F2 r12: title baseBold terracotta + value + «×»); категории caption + вопросы lineBold (r12, чёрный выбранный) + поле «Ответ» + SecondaryButton «Добавить открывашку»; max 3, 120 символов.
- AboutMe: «Что мы забыли спросить?» + «Пара предложений о себе» + поле «О себе» (обязательно! ошибка «Напишите пару слов о себе»; max 300 в UI). (Flutter: about me опционально, 2000.)
- PhotoPick: «Добавьте фото» + текст + photo_helper.png 160 + круг 200 (#F9F3F2, stroke 2 #E7C9B2, photo_placeholder 48 + «Выбрать») + PrimaryButton «Выбрать фото»/«Далее» + SecondaryButton «Выбрать другое» + «Пропустить фото» + «Назад». Одно фото (Flutter: галерея 3×2 до 6 фото + видео-кружок 208, обязателен минимум 1 фото).
- VideoPick: «Видео о себе» + текст + плашка 160 r16 «Выбрать видео»/«Видео выбрано ✓» + PrimaryButton «Сохранить и начать» + «Пропустить и начать» (terracotta) + «Назад». (Flutter: запись видео в приложении 10 с, экран VideoRecord.)
- Submit: PATCH /users/profile полный (isVisible true, marketing true, дефолты «Пользователь»/«—»), затем upload photo, upload video; ошибки маппятся на шаг. Аналитика onboarding_step по фазам.
- Нет: экрана «Уведомления»/пермишенов на онбординге, прогресс-бара 3 сегментов как во Flutter, шага «Определение локации» (гео-запрос) — вместо него выбор города.

## AgeRangeScreen (после онбординга)
- «Последний шаг и в бой!» titleHeavy + «Давай определимся с возрастом желаемого партнёра» + строка «Возраст» subtitle / «25–45» lineBold terracotta + «Ищем пару от 25 до 45» baseLight #B3ACA9 + Material RangeSlider 18–99 (min span 3, default 25–45) + PrimaryButton «Смотреть анкеты» (PATCH recalculate с текущими фильтрами) + TextButton «Пропустить». Back = пропустить. (Flutter MatchingAgeRange — сверить тексты/слайдер PlRangeSlider.)

## Лента (FeedScreen) — dating-tz mixed feed
- PlTopBar «Лента» + кнопка filters_24_heavy. Источник: GET /feed (persons + events, если флаг mixed_feed) иначе GET /matching/users. Колода: карточка r28 чёрная подложка, width = height×0.6, тень нет; свайп draggable (порог 20% ширины или скорость 750 px/s), поворот до 14°, rubber-band; like_overlay.png 120 (@top-start 48/24, rotate 12) / dislike_overlay.png 120 (@center-start) при перетаскивании; следующая карточка размытая (blur 20→0, alpha .22→.8). Док кнопок (высота 118): PersonActions — pass 68 чёрная cross_filled 28 / note 54 белая stroke terracotta send 22 / like 68 terracotta heart_filled 28; EventActions — «Не интересно» / «Пойду» + чип «Выход компанией · не свидание».
- PersonCard: фото full-bleed + scrim градиент; индикаторы фото сверху (полоски 3dp), тап-зоны 48 слева/справа; кнопка «···» (36 чёрный@42%) → ReportUserDialog; низ pad 18/16: «Имя, возраст» titleHeavy белый + verified 18 #E7C9B2 + «★» premium + TrustChip; locationText lineLight; «Совпадение N%» baseBold #E7C9B2 + «В сети» caption; job baseBold; bio baseLight 2 строки; кнопка «Узнать больше» (белая r20 terracotta) → UserCardDetailsSheet. (Flutter UserCardView: gradient 160, location/activity, name/age/premium/verified, match% + категории-чипы, pager, видео-аватар 96, полноэкранное видео, UserDescriptionWidget секции — здесь нет видео-аватара, нет категорий совпадения, нет пейджера-свайпа, описание в шторке.)
- EventCard (dating-tz): cover/градиент, чипы «По душе» terracotta + «от N чел.», title titleHeavy, description, дата formatEventWhen #E7C9B2, локация, интересы, строка атрибуции источника.
- Пусто: no_users.png + «Анкеты закончились» + «Измени параметры поиска или приходи завтра» + «Обновить». Сеть: «Нет связи с сервером» + «Проверь интернет и попробуй снова». Тосты terracotta (event/report).
- Супер-лайк: Material AlertDialog «Написать записку» titleHeavy terracotta + «Для {имя}» + «Увидит до взаимной симпатии · одна тёплая фраза» + поле «Текст» (hint «Что тебя зацепило?», счётчик N/120) + PrimaryButton «Отправить записку» + «Отмена». (Flutter: BS SuperLike с полем и лимитом — сверить.)
- Жалоба из ленты: optimistic hide + тост «Пожаловали. Анкета больше не появится в ленте». Нет: dislike через API (pass только локальный dismiss — POST dislike не вызывается!), undo/возврат анкеты, счётчик лайков/лимиты, рекламные видео, обучение скроллу, кэш карточек, offline-очередь.
- Аналитика: feed_loaded, feed_item_shown, like_sent, pass_sent, superlike_sent, match_shown, event_reaction, report_user, matching_recalculate, swipe_training_done.

## SwipeTrainingOverlay (первый запуск ленты)
- Затемнение #000@82% в границах карточки; бейджи слева cross «Пропуск» / справа heart «Нравится» (для события «Не интересно»/«Пойду»); демо-карточка 140×186 с градиентом и штампами; палец «👆» в белом круге 48; текст «Свайпни вправо, если нравится. Влево — пропустить.» + кнопка «Понятно» (белая r12 terracotta) + «или нажми в любом месте». (Flutter SwipeTraining: training_arrow_left/right + training_pointer SVG, тексты из arb — сверить.)

## MatchOverlay
- Полноэкранная terracotta; крестик 40 белый@22%; heart_zero 160 белая@12% фон; две карточки фото 148×196 r16 stroke 3 белая с поворотом ±10°; «Это взаимно!» headingHeavy белый + «Ты и {имя} понравились\nдруг другу» lineLight; кнопка белая r12 h56 «Написать {имя}» baseBold terracotta + «Продолжить свайпы» baseBold белый. Запрашивает POST_NOTIFICATIONS. (Flutter MatchView — сверить тексты/анимацию.)

## Фильтры (FiltersScreen)
- PlTopBar «Фильтры» / «Кого показывать в ленте»; карточки r16 stroke #E1D6D1@85% фон #F9F3F2@35% pad 16: «Локация» (плашка geolocation + город + «Город поиска» + «Изменить» → SelectCityScreen); «Радиус» (Slider 1–100 км, «макс.» при 100, подписи 1 км/100 км, свитч «Расширять, если мало людей»); «Возраст» (RangeSlider 18–99 + свитч «Расширять возраст»); «Рост» (RangeSlider 70–250, min span 10); «Сообщества» (чипы, только вступившие toggle, счётчик N/M, подсказки). Низ: PrimaryButton «Применить» (enabled при dirty) + «Измени параметры, чтобы применить». PATCH /matching/recalculate. (Flutter Filters: город/радиус/возраст/сообщества/рост — сверить порядок и стили PlSlider.)

## Симпатии (SympathyScreen)
- PlTopBar «Симпатии»; Material SingleChoiceSegmentedButtonRow «Лайки N» / «Записки N» (чёрный активный, #F9F3F2 неактивный); строка «Таймер бесплатного просмотра: {timer_expires_at raw}» caption terracotta; списки: LikeRow (аватар 56 + «Имя, возраст» lineBold + бейдж NEW terracotta + subtitle «Совпадение N%» / «Открой, чтобы увидеть» + «🔒» при blur + «›»), разделитель indent 84; SuperLikeCard (аватар 56 + имя + NEW + «Совпадение N% · супер-лайк» + текст записки в рамке terracotta@35%). Пусто: user_likes_empty «Пока нет лайков» / «Когда кто-то лайкнет вас — появится здесь»; pic_no_lovenotes «Нет записок» / «Супер-лайки с текстом появятся здесь» + «Обновить».
- (Flutter/Figma: сетка карточек с брендовыми формами 1–5, повороты при скролле, блок «Записки» с повёрнутыми подложками, пагинация, blur + псевдонимы «Незнакомец 32», таймер, FAB подписки, «Узнай, кто поставил лайк» — ничего этого нет; реализация списком.)
- SympathyPreviewScreen: PlTopBar «Симпатия»/«Записка» + subtitle имя; карточка r28 (#E1D6D1) с фото (blur 28 при blured + замок 44 + «Скрыто без Premium» + «Один профиль в день виден бесплатно»), индикаторы фото, тап-зоны; низ: чип «Супер-лайк», «Имя, возраст» titleHeavy белый, «Совпадение N%», локация, job, bio 2 строки, текст записки в плашке белая@18%, «Узнать больше» → UserCardDetailsSheet. Blured: PrimaryButton «Открыть Premium» + «Пропустить»; иначе FAB cross 64 чёрная / heart 72 terracotta + подпись «Лайкни в ответ или пропусти». Лайк → POST like → MatchOverlay. GET /users/{id} для полной карточки, POST likes/{id} mark read. (Flutter SympathyProfilePreview: UserCardWithDescription + like/dislike круги 80 — сверить.)

## Чаты (ChatsScreen / ChatThreadScreen)
- Список: PlTopBar «Чаты»; секция «Новые пары» lineBold + LazyRow аватаров 64 + имя caption (ширина 72); чаты: аватар 56 + имя lineBold + последнее сообщение (baseBold чёрный при unread / baseLight #514A47; «Начните общение» если нет) + время HH:mm caption (terracotta при unread) + счётчик unread (terracotta круг caption); разделитель indent 84. Пусто: pic_chatting «Пока нет чатов» + длинный текст + «Обновить». GET /user-chats + /user-chats/new-pairs при открытии; без WS-обновления списка, без пагинации, без typing/online в списке, без меню чата (⋮ в списке), без баннера правил сообщества, без sliver-хедера «Твои пары», без свайп-карусели с zoom.
- Тред: PlTopBar имя + subtitle «в сети/подключение…/не в сети» (по состоянию СОКЕТА, не пользователя!) + меню ⋮ («Пожаловаться» → ReportUserDialog place chat → после жалобы предлагает «Удалить пару»; «Удалить пару» → AlertDialog «Удалить пару?» → POST delete-pair). Список сообщений LazyColumn pad 12, gap 4, разделитель дня caption #B3ACA9 по центру («Сегодня/Вчера/13 июл»); MessageBubble: max 280dp, r16, СВОЁ — ЧЁРНОЕ (Flutter/Figma: terracotta #C66F53!), чужое #F9F3F2 (Flutter: #F2E7E3), текст lineLight белый/чёрный, pending « …», хвост SVG 12, статус message_sent/read 14 (#B3ACA9/terracotta) под пузырём (Flutter: время + статус внутри пузыря; здесь ВРЕМЯ СООБЩЕНИЯ НЕ ПОКАЗЫВАЕТСЯ), «Не отправлено — нажми, чтобы повторить». Long press → ответ (reply). Цитата ответа: «Имя: текст» caption над пузырём. Пусто: pic_chatting «Начните общение» + «Напишите первое сообщение — {имя} увидит его в чате» (Flutter: ChatUserInfoWidget с верификацией/подпиской + «Сообщений нет»). Композер: OutlinedTextField r28 #F9F3F2 «Сообщение» maxLines 4 + кнопка 48 круг чёрная/#F9F3F2 send 22. История: только messages/previous по курсору last_message (одна страница 30, без догрузки), set-last-read через WS. Нет: редактирования, удаления, копирования, контекстного меню, typing, online-статуса собеседника, non-first-page пагинации, «Непрочитанные» разделителя, предупреждения о ссылках, BS уведомлений после 2-го сообщения, DELETE new-pairs, кэша.
Android — профиль, подписка, верификация, город, GEO, скоринг (notes_android_ui2.md, 20321 байт)
# Android — профиль, подписка, верификация, город, GEO, скоринг (ui_profile, ui_subscription, ui_verification, ui_location, ui_geo, ui_scoring — прочитано полностью)

## Профиль (ProfileScreen / ProfileHome) — «redesigned home (feature/profile-redesign)», НЕ дизайн Figma/Flutter
- Роуты внутри таба: Home, Edit, Settings, Verification, Subscription, Faq, Support, Rules, Privacy, Communities, Referral, Email, Events, Encounters, CompanyInvite, Scoring, Voice (всё без NavHost, через state).
- Home: PlTopBar «Профиль» / subtitle «Видна в ленте»/«Скрыта из ленты» + EnvBadge + gear40 (24) → Settings. Hero-карточка #F9F3F2 r28 pad 20: аватар 104 круг stroke 3 #E7C9B2 + бейдж verified 28 (белый круг, verified 20 terracotta); «Имя Фамилия, возраст» titleHeavy; job baseLight; TrustChipLabel (скоринг, по флагу); ««bio»» lineLight 3 строки; CompletenessBar (белая r12: «Анкета сильная»/«Заполни анкету» baseBold + «N%» terracotta + полоса 6 + подпись); PrimaryButton «Редактировать»; VisibilityToggleRow (белая r12: «Анкета видна»/«Анкета скрыта» + подпись + «Скрыть»/«Показать» terracotta; PATCH isVisible; тост «Анкета видна в ленте»/«Анкета скрыта»).
- «Усиль анкету»: PriorityCard (terracotta@8% r16 stroke): «Верификация» / «Значок доверия в ленте и симпатиях» / «Пройти» (если не verified); «Доверие и проверки» (скоринг, флаг); «Premium «По любви»» / «Больше лайков, супер-лайки, фильтры» / «Тарифы» (если не premium).
- Группы меню (белые r16 stroke #E1D6D1, строки lineLight + «›»): «Анкета и аккаунт»: Редактировать анкету, Настройки, Верификация, [Доверие (скоринг)], [О себе голосом], Подписка Premium, Электронная почта; «Знакомства и события»: [События «По душе»], [Пересечения (GEO)], Сообщества, Пригласи друзей; «Помощь»: Частые вопросы, Связаться с нами, Правила сообщества, Политика конфиденциальности; «Опасная зона»: Удалить аккаунт (красный). Футер: телефон caption, email / «Почта не указана · нажми, чтобы добавить» (terracotta), «ID xxxxxxxx…»; SecondaryButton «Выйти».
- Удаление: AlertDialog «Удалить аккаунт?» + «Анкета будет деактивирована. Войти снова можно будет только с новым аккаунтом.» + «Удалить»/«Отмена» → POST deactivate reason «other» → logout. (Flutter: BS причин (4) + диалог + экран таймера 10 с terracotta.)
- Нет во Flutter-стиле: кнопка настроек/редактирования в кругах 66×68, аватар 128 с видео, «N%» бейдж, _VisibilityButton 210×52 переключатель, ObsceneLanguageWidget (isObscene не используется), _VerificationCTAWidget с 4 чипами, чёрный блок подписки NoSubscriptionWidget/SubscriptionWidget с размытыми кругами и фичами, _AddEmailWidget с пунктирной рамкой, меню с иконками (referral24, communities, faq, support, rules, lock, trash).

## Редактирование профиля (ProfileEditScreen) — одна длинная форма, не по Figma
- PlTopBar «Редактирование» / «Анкета N%». «Фото» lineBold + ProfilePhotoGallery (6 слотов 3 колонки 116/128, gap 8, r16; фото + «Главное» бейдж + «×» 36 чёрный@55% + «Проверяем…» для немодерированных; пустой слот белый stroke 1.5 #E1D6D1 «+» titleHeavy terracotta + «+3%» caption; long-press drag reorder; подсказки «1 фото — минимум» / «Новые фото проходят проверку» / «Зажми фото, чтобы перетащить»; BS «Добавить фото»: SecondaryButton «Сделать фото» / «Выбрать из галереи»; лимит 6, 10 МБ; нельзя удалить последнее). «Видео» lineBold + «Видео загружено» + SecondaryButton «Удалить видео» / «Добавить видео» (выбор из галереи, без записи; нет превью-кружка 208). LinearProgress 6 + «Заполненность N% — фото и ответы повышают видимость». Поля: Имя, Фамилия, Пол (чипы), Дата рождения (маска), Рост, см, Работа / занятие, О себе (multi-line, max 300). FlagsSection «🟢 Зелёные флаги» / «🔴 Красные флаги» (карточка #F9F3F2 r16 stroke accent@20%: счётчик N/7, подсказки «Быстрый выбор» (+ Честность, Юмор, Эмпатия, Амбиции, Верность, Любопытство, Забота / + Грубость, Ложь, Ревность, Пассивность, Нарциссизм, Контроль, Неуважение), поле «Своё слово или фраза» + кнопка «Добавить»; чипы accent@16% r20 с «×»). PromptsSection «Открывашки» N/3 + «Три коротких ответа на вопросы — как icebreaker в чате» + 3 слота (FilledPromptSlot с номером-кружком terracotta, title, value, «Изменить», «×»; EmptyPromptSlot «Добавить открывашку»/«Сначала заполни предыдущую» + «+»). SecondaryButton «Изменить ответы анкеты · N» → SurveyEditStep (PlTopBar «Анкета» «i / N»; только required-шаги; варианты r12 чёрный выбранный; «Далее»/«Сохранить анкету»; «К форме профиля»). PrimaryButton «Сохранить» → PATCH полный.
- PromptPickStep: PlTopBar «Новая открывашка»/«Изменить открывашку» + «Шаг 1 · выбери вопрос»/«Шаг 2 · твой ответ»; список категорий/вопросов (r12 #F9F3F2, занятые серые), поле «Твой ответ» (hint «Коротко, до 120 символов — это icebreaker», счётчик N / 120), PrimaryButton «Сохранить открывашку», «Сменить вопрос», «Отмена».
- (Flutter ProfileEdit: AppBar с процентом и прогресс-баром 174×6, VideoContentComponent + галерея + VerificationPreviewWidget, секции с иконками edit/plus, переходы на шаги онбординга (OnboardingRoute step), интересы-чипы stroke, флаги (красные залитые #514A47 / зелёные stroke #F2E7E3), открывашки-карточки #F2E7E3@50% r20, 8 обязательных шагов анкеты с ответами «→ «…»», кнопка «Показать превью» (cards24Heavy) → ProfileCardPreview. В Android: нет ProfileCardPreview, нет VerificationPreviewWidget (повторная верификация), нет отображения ответов анкеты в форме, нет интересов отдельной секцией (внутри анкеты).)

## Настройки (ProfileSettingsScreen)
- PlTopBar «Настройки» + action «Сохранить» (красный baseBold, только при dirty). «Уведомления» subtitle + пояснение про Firebase (caption, техно-текст!) + TextButton «Системные настройки уведомлений» + Material Switch: «Новые пары», «Новые сообщения», «Напоминание об активности», «Реферальная программа», «Маркетинг», «Обучающие подсказки»; «Безопасность» subtitle + текст про жалобы. PATCH settings + PATCH profile (marketing). (Flutter: заголовки profileSettingsNotificationSubtitle / «Выключены» / свитчеры PlSwitch 56×32 / _OpenSettingsWidget / profileSettingsContentSubtitle + «Показывать обучающее видео» / блок подписки со свитчем автопродления; нет блока подписки/продления в Android.)

## Email (EnterEmailScreen) — экран, не BS
- PlTopBar «Электронная почта»; текст «Так мы сможем связаться, если что-то пойдёт не так — и при желании присылать новости.»; поле «Электронная почта» (hint name@example.com, 256); Checkbox «Можно присылать новости и предложения»; ошибка «Введите корректный email»; «Сохранили»; PrimaryButton «Сохранить» → PATCH settings(marketing) + PATCH profile(email). (Flutter: BS с полем enterEmailLabelText/HintText + PlCheckBox enterEmailCheckboxDescription + enterEmailSaveButton.)

## FAQ (FaqScreen)
- PlTopBar «Частые вопросы»; аккордеон: карточки #F9F3F2 r12 pad 16, вопрос lineBold, ответ раскрывается по тапу (lineLight #514A47); «Пока нет вопросов». (Flutter: список без аккордеона, title subtitle + info baseLight, gap 24.)

## Правила / Политика (LegalLinkScreen)
- PlTopBar title + захардкоженный текст (ProfileInfoCopy.RULES / PRIVACY — 5 пунктов правил; политика 3 абзаца) + PrimaryButton «Открыть полную версию» → https://plbvi.ru/help/rules / Google Docs policy. (Flutter: сразу launchUrl communityRulesUrl / policyUrl без экрана.)

## Поддержка (SupportScreen) — экран, не BS
- PlTopBar «Поддержка»; «Опиши проблему — мы ответим на почту.»; поле «Текст обращения» (hint «Проблема или предложение», 1000) + «Почта для связи» (hint yourem@il.com, 256); валидация «Напишите чуть подробнее (от 5 символов)» / «Укажи почту для ответа»; «Получили твоё обращение»; PrimaryButton «Отправить» → POST /feedback. (Flutter ContactUs BS: contactUsMessageLabel/Hint, contactUsEmailLabel/Hint, contactUsSendButton, snack contactUsSuccessSnack.)

## Сообщества (CommunitiesScreen) — белый экран, не чёрный дизайн Flutter
- PlTopBar «Сообщества» / «Круги интересов · фильтры в ленте»; карточка «Зачем это» + текст + StatChip «В каталоге N» / «Ты в N»; тост; «Твои сообщества» (CommunityCard terracotta@8% r16: иконка 52 + ✓, name lineBold, «N участников · закрытое · твоё», кнопка «Выйти» (красная рамка)); «Выбери круги»/«Ещё в каталоге» (карточки #F9F3F2, кнопка «Вступить» terracotta); «Ты уже во всех доступных сообществах»; подпись про закрытые. Пусто: pic_match «Пока нет сообществ». API: GET all + GET mine, POST join/leave. НЕТ: карусель логотипов 120 с zoom, видимость сообщества (visible/invisible), приглашение по ссылке (invite link, share, copy), дата вступления, BS вступления/результата заявки, deeplink-приглашения, список с поиском (Левенштейн), BS успешного вступления, диалог выхода с текстом для закрытых, email поддержки communitySupportEmail.

## Реферальная программа (ReferralScreen) — белый экран, не чёрный дизайн Flutter
- PlTopBar «Пригласи друзей» / «Premium-дни за приглашения»; referral_box.png 140; «Друг входит — оба в плюсе» titleHeavy; текст; BenefitCard «Другу +N дн. за вход и анкету» / «Тебе +N дн. за каждое приглашение»; «Твой прогресс» (StatBlock приглашено / дней Premium / топ N%) + «Ещё N дн. начислятся позже»; «Бонусы за объём» (уровни ✓/○ «N друзей» «+N дн.»); «Твой код» = userId + «Скопировать код»/«Скопировано»; PrimaryButton «Поделиться приглашением» (Intent ACTION_SEND с текстом «Присоединяйся к «По любви» — знакомства по-человечески. Тебе +N дн. … Мой код: {userId}»); подпись про начисление. НЕТ: AppsFlyer OneLink ссылка (generateReferralLink), история активности (activitiesHistory), sliver-хедер, PLCopyWidget, прогресс-бар с метками, BS подтверждения участия реферала (approve), диалог поздравления, deeplink-обработка referral-invite, «Подробнее» (referralIfoUrl), premium/no-premium варианты текстов.

## Подписка (SubscriptionScreen) — не Figma/Flutter дизайн
- PlTopBar «Подписка» / «Premium активен» | «Больше возможностей»; карточка terracotta@10%: «Premium «По любви»» subtitle + «Подписка активна до …» / «Больше лайков, супер-лайки, кто лайкнул вас, расширенные фильтры.»; список фич «•  text» (fallback 4 пункта); «Тарифы»: строки r12 (чёрная выбранная): name lineBold + periodLabel (Неделя/Месяц/3 месяца/Год) + цена «N ₽» (discountPrice ?: basePrice; без зачёркнутой базовой, без бейджа скидки, без oneTimePayment, без сетки 2×2); подпись про оплату (Google Play / браузер); PrimaryButton «Оформить» → PlayBilling (если PREFER_PLAY_BILLING и play_store_product_id) иначе POST /payments/android → Custom Tabs; при возврате — POST /payments/android/status + GET /subscriptions/me; «Проверить оплату»; «Готово» если есть подписка. НЕТ: entryPoint-заголовки (sympathy/endedLikes/endedUndos/endedSuperLikes), WebView с success/fail redirect, BS pending/failed, TYP экран, продление/отмена продления, «Условия подписки»/«Политика» кнопки, restore, чёрный дизайн с кругами, форматирование цены (запятая, копейки), gift/giftReferral.

## Верификация (VerificationScreen) — 1 экран со статусами, не 4 экрана Flutter
- PlTopBar «Верификация»; иллюстрация (smiling_woman.png 180 / verification_illustration / verification_waiting / превью селфи 200 r16); карточка «Статус» #F9F3F2 r16 (Верифицирован / На проверке / Отклонён — загрузите другое фото / Не пройдена); тексты: Intro «Подтверди, что анкета настоящая. Сделай селфи — так другим спокойнее лайкать.» + «• Лицо хорошо видно • Без масок и чужих фото • Нейтральный фон»; Rejected «Проверка не прошла. Сделай новое селфи…»; Capture «Сделай селфи или выбери фото из галереи»; Waiting «Проверяем фото. Обычно это занимает немного времени — можно закрыть экран.»; Done «Готово! Значок верификации появится в профиле и в ленте.». Кнопки: Intro «Далее» + SecondaryButton «Позже»; Capture «Сделать селфи» (TakePicturePreview системная камера) + «Из галереи» (!); Waiting/Done «Обновить статус» + «Готово». Polling 20×3 с. (Flutter: VerificationStart (преимущества, чипы), VerificationInstruction, VerificationSelfie (своя камера, фронтальная, без галереи), VerificationWaiting; статусы через VerificationStatusBloc.)

## Выбор города (SelectCityScreen)
- PlTopBar «Город»; PlTextField «Поиск» (hint «Москва»), debounce 300 мс, GET /cities?term=; список карточек #F9F3F2 r12 (name lineBold + region caption); «Введи название города» / «Ничего не найдено»; тап → POST /users/profile/position {city_id} → onSelected. (Flutter SelectCity: PlSearchField, поиск от 3 символов, debounce 500, популярные города; LocationDetermination экран с гео-запросом отсутствует в Android — нет автоопределения по GPS для position, нет экрана «Определение локации».)

## GEO «Пересечения» (EncountersScreen) — dating-tz, нет во Flutter/Figma
- PlTopBar «Пересечения» / «Кто рядом → анкета → лайк»; карточка «Случайные встречи» + PrimaryButton «Проверить, кто рядом» (ACCESS_FINE_LOCATION → POST /geo/ping → GET /geo/encounters + GET /users/{id} для карточек); список EncounterPersonCard (аватар 56 + «Имя, возраст» + «~N м · N раз · когда» + «Открыть анкету ›»); EncounterPersonPreview (карточка r28 + чип «Пересечение» + FAB pass/like 64/72 → MatchOverlay). Аналитика geo_open/geo_ping/geo_encounter_open/geo_like/geo_pass/geo_match_chat.

## Скоринг «Доверие» (ScoringScreen) — dating-tz, нет во Flutter/Figma
- PlTopBar «Доверие» / «Проверки для значка в ленте»; карточки «Больше доверия — больше отклика», «Твой уровень» (Базовый/Повышенный/Высокий/Пока без проверок + «N из 100» + прогресс + бейджи Личность/ФССП ок/Без судимостей), «Результаты» (CheckStatusCard с точкой статуса: в очереди/проверяем/готово/не вышло/отменено, +N к оценке), выбор проверок (Госуслуги (ЕСИА), ФССП · алименты, Судимости, Сведения о детях, Социальные сигналы) + ConsentRow (152-ФЗ) + PrimaryButton «Запустить проверки»/«Добавить проверки» + «Обновить статус» + подпись про local-окружение.
Android — голос, события, ресурсы, FLUTTER_PARITY.md (notes_android_ui3.md, 4219 байт)
# Android — голос, события «По душе», ресурсы, паритет-чеклист (ui_voice, ui_events, res/assets, docs/FLUTTER_PARITY.md — прочитано)

## «О себе голосом» (VoiceOnboardingScreen) — dating-tz, нет во Flutter/Figma
- Фазы Intro («О себе голосом» / «Быстрее, чем форма»: карточка «Расскажи о себе вслух» + 3 шага + «Как это работает» + «Начать» / «Не сейчас»), Chat («Разговор»: пузыри ассистент #F9F3F2 / пользователь terracotta, поле + 🎤 (системный RecognizerIntent ru-RU) + «Отправить» + «Завершить и собрать черновик»), Done («Черновик»/«Сохранено»: «Что попадёт в bio» + «Записать в анкету» (PATCH profile bio) / «Закрыть без записи» / «К профилю»). Сервер POST /voice/sessions|turns|apply, локальный fallback с 4 канонными вопросами.

## События «По душе» (EventsScreen, CompanyInviteScreen) — dating-tz, нет во Flutter/Figma
- PlTopBar «События «По душе»» / «Компания, не свидание»; сегменты «Каталог» / «Приглашения · N» (если флаг company_invite); каталог GET /events (30) карточки #F9F3F2 r16: cover 148 + чип даты + чип «Пойду»/«Скрыто», title lineBold, локация, атрибуция источника, «Компания от N» + «Подробнее →»; пусто pic_match «Пока тихо». Inbox: «Входящие»/«Исходящие» InviteCard (cover 64, badge Принято/Отклонено/Ответить/Есть ответы/Отправлено). EventDetailScreen: hero 220 + title + дата + «Фото: автор»; баннер «Тебя зовут в компанию»; пилюли локация / «от N человек» / «Ты идёшь»; «О событии», «Темы», «Источник ↗»; sticky «Пойду» / «Позвать в компанию» / «Не интересно» (POST reaction или company-invites/{id}/respond). CompanyInviteScreen: «Позвать в компанию» — кандидаты из чатов+лайков, прогресс «Выбрано N из M», сообщение, POST /events/company-invite.

## Ресурсы Android
- assets/icons: 139 SVG (полный набор Flutter SvgIcons скопирован), assets/images: 10 PNG (те же имена, что Flutter Images: couple, dislike_overlay, error_placeholder, freinds_image, hard_update_image, like_overlay, no_users, photo_helper, referral_box, smiling_woman), launcher_icon. res/font: manrope_bold/medium, tt_runs_bold/extrabold/medium (нет Manrope Regular/SemiBold, TT Runs Regular — если Flutter использует только Medium/Bold/ExtraBold, ок). strings.xml: только app_name «По любви» — все строки захардкожены в Kotlin (нет l10n). Нет тёмной темы.
- build.gradle.kts: applicationId com.polyubvi.app, minSdk 28, targetSdk 35, versionCode 1, versionName 1.0.0; флейворы dusha (dusha.uxbrain.ru), dushalove (dushalove.uxbrain.ru), stage (stage.plbvi.ru, suffix .dev), prod (production.plbvi.ru); FCM_ENABLED по наличию google-services.json; CERT_PINS; PREFER_PLAY_BILLING. (Flutter: flavors dev/qa/prod, version 1.4.2+128, URLs plbvi.ru stage/prod.)
- docs/FLUTTER_PARITY.md: заявляет ✅ почти по всем пунктам (Auth, Onboarding, Main tabs, Feed «Photo pager + scroll sections ✅», Sympathy, Chats «Reply + read/sent icons ✅», Profile «Verification multi-step ✅», «Subscription + payment URL ✅», Communities ✅, Referral ✅, Assets ✅). По факту (см. сверку): большинство ✅ означает «функция есть в каком-то виде», а не паритет с Figma/Flutter по дизайну/тексту/поведению.
Flutter — все строки локализации app_ru.arb (arb_strings.txt, 999 строк)
"description": "Общие сроки"
"searchBarHintText": "Поиск",
"positiveResponse": "Да",
"next": "Далее",
"errorPlaceholderTitle": "Не загрузилось :(",
"errorPlaceholderBody": "Попробуй обновить страницу",
"errorPlaceholderNoInternetTitle": "Нет интернета :(",
"errorPlaceholderNoInternetSubtitle": "Попробуй подключиться к сети и обновить страницу",
"buttonUpdateLabel": "Обновить",
"description": "Дебаг экран"
"debugScreenServerSubtitle": "Сервер",
"debugScreenServerConnectButton": "Переключить сервер",
"debugScreenProxySubtitle": "Прокси",
"debugScreenProxyInfo": "Активирует передачу через прокси сервер.",
"debugScreenProxyEditTextLabel": "Введите адрес прокси сервера",
"debugScreenProxyConnectButton": "Подключить",
"debugScreenDebugAnalyticsTrackingSubtitle": "Аналитика",
"debugScreenDebugAnalyticsTrackingInfo": "Трекать аналитику",
"debugScreenThemeSubtitle": "Тема приложения",
"debugScreenThemeLight": "Светлая тема",
"debugScreenThemeDark": "Темная темя",
"debugScreenThemeSystem": "Системная тема",
"debugScreenRemoteConfig": "Удаленная конфигурация",
"debugScreenDBViewerTitle": "DB Viewer",
"debugScreenClearDBTitle": "Почистить БД",
"debugScreenLogoutTitle": "Выйти",
"debugScreenReloadAppMessage": "Перезагрузите приложение, чтобы увидеть примененные изменения",
"description": "Общие текста"
"generalError": "Не отправилось, попробуй ещё раз",
"description": "Общие текста ошибок с API"
"apiExceptionUserBlocked": "Ваш профиль заблокирован",
"apiExceptionSendingCodeFailed": "Не отправилось, попробуй ещё раз",
"apiExceptionOTPCodeInvalid": "Неверный код, попробуй другой",
"apiExceptionOTPCodeExpired": "Код больше не действителен, запросите новый код",
"apiExceptionTooManyRegistrations": "Превышено количество попыток регистрации по указанному номеру телефона",
"apiExceptionNoConnection": "Нет интернета, проверь сеть",
"apiExceptionBadRequest": "Не отправилось, попробуй еще раз",
"apiExceptionNotFound": "Что-то пошло не так",
"apiExceptionJoinAlreadyRequestedError": "Вы уже подали заявку на вступление в сообщество",
"apiExceptionUserAlreadyInCommunityError": "Вы уже состоите в этом сообществе",
"apiExceptionTooManyRequests": "Слишком частые запросы, подождите немного",
"apiExceptionSBPNotLinked": "Для операции к СБП должен быть привязан счёт",
"apiExceptionGiftSubscriptionStatusError": "Нельзя продлить подарочную подписку",
"apiExceptionVerificationAttemptsLimit": "Превышено количество попыток верификации, обратись в поддержку",
"description": "Экран ввода телефона"
"enterPhoneSplashScreenTitle": "Первый шаг к семейному счастью",
"enterPhoneSplashScreenSubtitle": "Найдём вторую половинку, которая разделяет твои ценности",
"enterPhoneSplashScreenBodyText": "Создадим тебе новый аккаунт или найдём существующий",
"enterPhoneSplashScreenButtonTitle": "Войти по смс",
"enterPhoneScreenTitle": "Укажи телефон",
"enterPhoneScreenSubtitle": "Пришлём СМС с кодом доступа",
"enterPhoneScreenPhoneInputLabel": "Номер телефона",
"enterPhoneScreenPhoneInputHint": "987 654-32-10",
"enterPhoneScreenPhoneInputHintPrefix": "+7 ",
"enterPhoneScreenSecurityNoteText": "Никто не узнает твой номер",
"enterPhoneScreenTermsCheckboxDescription": "Я старше 18 лет и не состою в отношениях или браке",
"enterPhoneScreenLicenseText1": "Нажимая «Далее», я соглашаюсь с условиями",
"enterPhoneScreenLicenseText2": " политики конфиденциальности",
"enterPhoneScreenLicenseText3": ", а также даю своё согласие на обработку моих персональных данных",
"enterPhoneScreenSnackErrorText": "Не отправилось, попробуй ещё раз",
"description": "Экран ввода OTP-кода"
"enterOTPScreenTitle": "Введи код",
"enterOTPScreenSubtitle": "Отправили 6 цифр в СМС на ",
"enterOTPScreenChangeNumber": "Изменить номер",
"enterOTPScreenNewCodeTimerTitle": "Новый код можно будет получить через\n",
"enterOTPScreenResendCodeButtonTitle": "Запросить новый код",
"enterOTPScreenSeconds": "с.",
"enterOTPScreenMinutes": "м.",
"description": "Экран шага анкетирования"
"surveyAnswerVisibleStatus": "Будет показано в анкете",
"surveyAnswerInvisibleStatus": "Не будет показано в анкете",
"surveryOptionalQuestion": "[Необязательно]",
"surveyNextButton": "Далее",
"surveySaveButton": "Сохранить",
"description": "Экран ввода имени и фамилии"
"enterNameScreenTitle": "Как тебя зовут?",
"enterNameScreenFirstNameInputLabel": "Имя",
"enterNameScreenLastNameInputLabel": "Фамилия",
"enterNameScreenFirstNameInputHint": "Саша",
"enterNameScreenLastNameInputHint": "Иванов",
"enterNameScreenPrivacyNoteText": "Увидят только твои пары",
"enterNameScreenPossibleToEditNote": "Можно отредактировать в профиле",
"enterNameScreenNextButtonTitle": "Далее",
"enterNameScreenSaveButtonTitle": "Сохранить",
"description": "Экран данные о себе"
"aboutUserAppBarFirstMsg": "Доступно 25% анкет",
"aboutUserAppBarSecondMsg": "Шанс найти пару в 2 раза выше",
"aboutUserSplashScreenTitle": "Привет, расскажи о себе!",
"aboutUserSplashScreenSubtitle": "Мы составили опросник для поиска серьёзных отношений. Больше ответов — больше найдётся партнёров",
"aboutUserSplashScreenButtonLabel": "Заполнить профиль",
"description": "Экран О себе"
"aboutMeTitle": "Что мы забыли спросить?",
"aboutMeSubtitle": "Расскажи о себе в свободной форме или переходи к просмотру анкет",
"aboutMeLabel": "Обо мне",
"aboutMeHint": "Люблю шоколадное мороженое",
"aboutMeSkipButton": "Пропустить",
"aboutMeSubmitButton": "Далее",
"aboutMeSaveButton": "Сохранить",
"description": "Экран Должность"
"userOccupationTitle": "Кем ты работаешь?",
"userOccupationSubtitle": "Укажи название своей должности",
"userOccupationLabelText": "Моя должность",
"userOccupationHintText": "Менеджер",
"userOccupationSkipBtnText": "Пропустить",
"userOccupationNextBtnText": "Далее",
"userOccupationSaveBtnText": "Сохранить",
"description": "Экран Разводная необязательной части"
"optionalSegmentScreenTitle": "Не упусти свою любовь",
"optionalSegmentScreenSubtitle": "Ответь на необязательные вопросы, и мы подберём максимально подходящего тебе человека",
"optionalSegmentScreenNextBtnLabel": "Заполнить до конца",
"optionalSegmentScreenSkipBtnLabel": "Дополнить позже",
"description": "Экран Дата рождения"
"birthDateTitle": "Когда у тебя день рождения?",
"birthDateAgeValid": "Тебе {age, plural, zero{лет} one{{age} год} few{{age} года} many{{age} лет} other{{age} лет}}",
"birthDateAgeInvalid": "Тебе нет 18",
"birthDateAgeValidCaption": "Убедись, что всё верно. Исправить дату рождения будет нельзя",
"birthDateAgeInvalidCaption": "Приложение доступно только для совершеннолетних пользователей",
"birthDatePrivacyHint": "Люди увидят только возраст",
"birthDateValidationError": "Введи реальную дату",
"birthDateNextBtnLabel": "Далее",
"birthDateFieldObscuringTextMask": "ДДММГГГГ",
"description": "Экран Пол и рост"
"userGenderHeightScreenTitle": "Твой пол и рост?",
"userGenderHeightScreenOnlyHeightTitle": "Твой рост?",
"userGenderHeightScreenSubtitle": "Я",
"userGenderHeightScreenHeightSubtitle": "Мой рост",
"userGenderHeightScreenCaption": "Увидят все пользователи",
"userGenderHeightScreenButtonLabel": "Далее",
"userGenderHeightScreenSaveButtonLabel": "Сохранить",
"userGenderHeightScreenMale": "Мужчина",
"userGenderHeightScreenFemale": "Женщина",
"userGenderHeightScreenCentimetre": "см",
"description": "Экран Интересы"
"interestsSkipBtn": "Пропустить",
"interestsForwardBtn": "Далее",
"interestsSavedBtn": "Сохранить",
"interestsTitle": " из 10",
"description": "Экран Красные и зеленые флаги"
"redFlagsScreenTitle": "Твои «Флаги»",
"redFlagsScreenSubtitle": "Что моментально отталкивает или привлекает тебя в партнёре?",
"redFlagsMeaning": "Недопустимо",
"greenFlagsMeaning": "Обожаю",
"redGreenFlagsExampleLabel": "Например:",
"redFlagsExample": "вечно в телефоне",
"greenFlagsExample": "сюрпризы",
"redFlagEmoji": "🚩",
"redFlagText": "Красный флаг",
"greenFlagEmoji": "💚",
"greenFlagText": "Зелёный флаг",
"redFlagsScreenSkipBtnText": "Пропустить",
"redFlagsScreenNextBtnText": "Далее",
"redFlagsScreenBSBtnText": "Сохранить ответ",
"flagsTitlePart1": "🚩 ",
"flagsTitlePart2": " из 7     💚 ",
"flagsTitlePart3": " из 7",
"redFlagsAddBtnLabel": "+",
"description": "Экран определения местоположения"
"locationDeterminationTitle": "Где ты находишься?",
"locationDeterminationSubtitle": "Разреши доступ к геолокации, чтобы мы смогли найти людей поближе к тебе",
"locationDeterminationSelectedCityTitle": "Твой город",
"locationDeterminationShareLocationButton": "Далее",
"locationDeterminationSettingsButton": "Настройки",
"locationDeterminationSelectCityButton": "Выбрать город",
"locationDeterminationErrorTitle": "Не получили локацию",
"locationDeterminationErrorSubtitle": "Разреши приложению доступ к геолокации в настройках телефона",
"locationDeterminationLocationSettingsButton": "Настройки геолокации",
"locationDeterminationSaveAndContinueButton": "Сохранить и продолжить",
"locationDeterminationChooseAnotherCityButton": "Выбрать другой",
"description": "Экран выбора города"
"selectCitySearchBarHintText": "Поиск города",
"selectCityEmptyStateTitle": "Не нашли город",
"selectCityEmptyStateSubtitle": "Поищи ошибки или укажи ближайший населённый пункт",
"description": "Экран разводная на верификацию"
"startVerificationTitle": "Сделай селфи и получи галочку",
"startVerificationAdvantageFirst": "10 лайков в день вместо 8",
"startVerificationAdvantageSecond": "Твою анкету увидят на 60% больше людей",
"startVerificationAdvantageThird": "Пользователи с галочкой получают больше симпатий",
"startVerificationAdvantageFourth": "Это бесплатно и займёт меньше минуты",
"startVerificationSkipButton": "В другой раз",
"startVerificationGetCheckmark": "Получить галочку",
"description": "Экран загрузки медиа контента пользователя"
"mediaContentTitle": "Добавь видео и фото",
"mediaContentDescription": "Запиши видео-кружочек.\nПользователи с видео находят пару в два раза быстрее.",
"mediaContentDragAndDrop": "Зажми фото, чтобы перетащить",
"mediaContentOnePhotoMinimum": "1 фото — минимум",
"mediaContentSaveButton": "Сохранить",
"mediaContentDeleteQuery": "Удалить?",
"mediaContentPhotoPermissionsNotGranted": "Не получили доступ к фото",
"mediaContentPhotoPermissionsNotGrantedDescription": "Без фото мы не можем активировать твою анкету. Разреши доступ к Галерее и Камере в настройках телефона",
"mediaContentSettingsButton": "Настройки",
"mediaContentCantOpenCamera": "Не можем открыть камеру",
"mediaContentMakePhoto": "Сделать фото",
"mediaContentCameraDisabled": "Разреши приложению доступ к Камере в настройках телефона, чтобы сделать фото",
"mediaContentPickFromGallery": "Выбрать из галереи",
"mediaContentGalleryDisabled": "Разреши приложению доступ к Галерее в настройках телефона, чтобы выбрать фото",
"mediaContentError": "Ошибка",
"mediaContentTooManyFiles": "Загружено больше 6 фотографий",
"mediaContentTimeoutError": "Нет интернета, проверь сеть",
"mediaContentSendFail": "Не отправилось, попробуй ещё раз",
"mediaContentCannotDeleteLastPhoto": "Нельзя удалить последнее фото",
"mediaContentFileTooBig": "Файл слишком много весит",
"mediaContentExceedsLimit": "Фото должно быть меньше 10 МБ",
"mediaContentPercentForPhoto": "+ 3%",
"mediaContentPercentForVideo": "+ 6%",
"mediaContentDeleteLastPhotoBSTitle": "Нельзя удалить фото",
"mediaContentDeleteLastPhotoBSSubtitle": "1 фото — минимум. Загрузи новую фотографию, чтобы удалить старую",
"mediaContentDeleteLastPhotoBSButtonText": "Понятно",
"description": "Экран записи видео"
"videoRecordCancel": "Отмена",
"cameraExceptionVideoRecordingFailed": "Не получилось записать видео",
"cameraExceptionNoSpace": "Не хватает памяти на телефоне",
"videoRecordCameraError": "Камера не работает",
"videoRecordFileTooBig": "Файл слишком много весит",
"videoRecordingTimeoutError": "Нет интернета, проверь сеть",
"videoRecordingFileLimitation": "Видео должно быть меньше 50 МБ",
"description": "Инструкция верификации"
"verificationInstructionSkipButton": "Пропустить",
"verificationInstructionPermissionDeniedTitle": "Не можем открыть камеру",
"verificationInstructionPermissionDeniedSubtitleFirst": "Для верификации нужно селфи.",
"verificationInstructionPermissionDeniedSubtitleSecond": "Разреши доступ к камере в настройках телефона.",
"verificationInstructionSettingsButton": "Настройки",
"verificationInstructionTitle": "Открой камеру, повтори жест и сделай селфи",
"verificationInstructionRepeatTitle": "Это твое прошлое фото, точно хочешь его заменить?",
"verificationInstructionOpenCameraButton": "Открыть камеру",
"verificationInstructionRepeatOpenCameraButton": "Заменить фото",
"verificationInstructionOpenCameraSubtitle": "Селфи увидят только твои пары",
"description": "Селфи"
"selfieCancelButton": "Отмена",
"selfieHintTitleCheckPhoto": "Проверь фото",
"selfieHintTitleTakePhoto": "Повтори жест",
"selfieHintSubtitleCheckPhoto": "Твоя будущая пара увидит это фото, улыбнись :)",
"selfieHintSubtitleTakePhoto": "Твоя будущая пара увидит это фото, улыбнись :)",
"selfieHintEmojiTitle": "✌️",
"selfieAnotherPhotoPendingVerifyErrorText": "Другое фото ожидает верификации. Попробуйте позже",
"selfieProfileAlreadyVerifiedErrorText": "Ваш профиль уже прошёл верификацию",
"description": "Ожидание верификации"
"verificationWaitingTitle": "Отправили фото на проверку",
"verificationWaitingSubtitle": "Обработка обычно занимает от 10 минут. Сообщим о результате в пуш-уведомлении",
"verificationWaitingEnableNotificationButton": "Включить уведомления",
"verificationWaitingCloseButton": "Завершить",
"description": "Превью фото верификации"
"verificationPhotoPreviewAppBarTitle": "Селфи",
"verificationPhotoPreviewAppBarCloseButton": "Закрыть",
"verificationPhotoPreviewTitle": "Фото проверено",
"verificationPhotoPreviewSubtitle": "{name} — реальный человек, мы проверили его селфи",
"placeholders": {
"name": {
"type": "String"
"description": "Матчинг"
"matchingTrainingSwipeDescription": "Смахни вправо, чтобы лайкнуть, или влево, чтобы отклонить партнёра",
"matchingTrainingSwipeDoneButton": "Понятно",
"matchingTrainingScrollDescription": "Смахни вверх, чтобы посмотреть анкету подробнее",
"matchingTrainingScrollDoneButton": "Понятно",
"matchingReachedReactionsLimit": "Закончились доступные реакции",
"matchingTimeoutError": "Нет интернета, проверь сеть",
"matchingPercent": "%",
"matchingSendNote": "Отправить записку",
"matchingMyHobbies": "Мои увлечения",
"matchingCommunities": "Состою в сообществах",
"matchingIncompatable": "🚩 Недопустимо в партнёре",
"matchingCompatible": "💚 Обожаю в партнёре",
"matchingReport": "Пожаловаться",
"matchingHeight": "{height} см",
"placeholders": {
"height": {
"type": "int"
"matchingNoUsers": "Анкеты закончились",
"matchingNoUsersDescription": "Измени параметры поиска или приходи завтра",
"matchingEndedLikesWaitTooltipText": "Подожди {time}",
"placeholders": {
"time": {
"type": "String"
"matchingEndedLikesWaitDurationInMinutes": "{minutes} м",
"placeholders": {
"minutes": {
"type": "int"
"matchingEndedLikesWaitDurationInHoursAndMinutes": "{hours} ч {minutes} м",
"placeholders": {
"hours": {
"type": "int"
"minutes": {
"type": "int"
"matchingEndedLikesWaitDurationInHours": "{hours} ч",
"placeholders": {
"hours": {
"type": "int"
"matchingObsceneLanguageWarningText": "Анкета содержит 
нецензурную лексику",
"description": "Экран подсказки"
"promptsTitle": "«Открывашки»",
"promptsSubtitle": "Выбери до трёх карточек и ответь на вопросы. Другие пользователи увидят ответы в твоей анкете",
"promptsAddLabel": "Твой ответ",
"promptsAddHint": "Напиши, что думаешь",
"promptsAddSaveBtn": "Сохранить ответ",
"promptsForwardBtn": "Далее",
"promptsSaveBtn": "Сохранить",
"promptsSkipBtn": "Пропустить",
"promptsAppbarTitle": " из 3",
"description": "Рут Главной"
"mainRootMainTab": "Главная",
"mainRootChatTab": "Чаты",
"mainRootProfileTab": "Профиль",
"mainRootFavouriteTab": "Избранное",
"description": "Реакции на изменения профиля"
"profileReactionNoPhotoSnack": "Добавь хотя бы одно фото в профиль",
"description": "Профиль"
"profileVisibilityEnableStatus": "Анкету видно всем",
"profileVisibilityDisableStatus": "Видят только пары",
"profileEmailTitle": "Электронная почта",
"profileAddEmailTitle": "Добавь почту",
"repeatVerificationTitle": "Измени селфи",
"repeatVerificationSubtitle": "Это фото могут видеть только только твои пары. Если фото тебя не устраивает, можешь обновить",
"profileAddEmailSubtitle": "Так мы сможем связаться с тобой, если возникнут проблемы с приложением или аккаунтом",
"profileMenuReferral": "Пригласи друга",
"profileMenuCommunities": "Сообщества",
"profileMenuFaq": "Часто спрашивают",
"profileMenuSupport": "Связаться с нами",
"profileMenuRules": "Правила сообщества",
"profileMenuPrivacy": "Политика конфиденциальности",
"profileMenuDeleteProfile": "Удалить профиль",
"profileLogoutButton": "Выйти",
"profileAge": "{age, plural, zero{лет} one{{age} год} few{{age} года} many{{age} лет} other{{age} лет}}",
"profilePercent": "{percent}%",
"profileDeleteAccountDialogTitle": "Удалить профиль?",
"profileDeleteAccountDialogSubtitle": "Твои данные пропадут навсегда и мы не сможем их восстановить.",
"profileDeleteAccountDialogDeleteButton": "Удалить",
"profileDeleteAccountDialogCancelButton": "Отмена",
"profileSubscriptionCTA": "Узнай, кто поставил лайк твоей анкете",
"profileSubscriptionChooseTariff": "Выбрать тариф",
"profileSubscriptionProlongButtonText": "Включить продление",
"profileSubscriptionWillEnd": "Закончится {subscriptionUntilDate}",
"placeholders": {
"subscriptionUntilDate": {
"type": "String"
"profileVerificationTitle": "Получи галочку →",
"profileVerificationSubtitle": "Подтверди личность — сделай селфи",
"profileVerificationFirstChip": "Бесплатно и быстро",
"profileVerificationSecondChip": "+2 лайка",
"profileVerificationThirdChip": "+60% охват",
"profileVerificationFourthChip": "Больше симпатий",
"profileObsceneLanguageWarningText": "Ваша анкета содержит 
нецензурную лексику",
"profileObsceneLanguageWarningExplanationText": "Это может плохо отразиться на выборе партнёра и повлиять на первое впечатление",
"description": "Редактирование профиля"
"profileEditMediaInstruction": "Зажми фото, чтобы перетащить",
"profileEditNameSegmentTitle": "Имя и фамилия",
"profileEditNameJobTitle": "Должность",
"profileEditNameBioTitle": "О себе",
"profileEditNameHeightTitle": "Рост",
"profileEditHeightCentimetre": "{height} см",
"placeholders": {
"height": {
"type": "int"
"profileEditNameInterestsTitle": "Интересы",
"profileEditNameFlagsTitle": "Флаги",
"profileEditNamePromptsTitle": "Открывашки",
"profileEditEmptyAnswer5Percent": "Добавь ответ + 5%",
"profileEditEmptyPrompt2Percent": "Добавь 3 открывашки +5%",
"profileEditRedFlagTitle": "🚩 Недопустимо",
"profileEditGreenFlagTitle": "💚 Обожаю",
"profileEditSaveBtnLabel": "Сохранить",
"profileEditReloadPromptsButton": "Попробовать снова",
"profileEditAppbarPercent": "{percent}% заполнено",
"profileEditAddAnswersButton": "+ Добавить",
"profileEditShowPreview": "Показать анкету",
"profileEditGalleryModerationSuperscript": "Новые фото проходят проверку",
"profileEditPhotoModeratingTitle": "Проверяем...",
"description": "Настройки профиля"
"profileSettingsTitle": "Настройки",
"profileSettingsSaveButton": "Готово",
"profileSettingsNotificationSubtitle": "Уведомления",
"profileSettingsNotificationDisabled": "Выключены",
"profileSettingsNewMatchSwitcher": "Новые пары",
"profileSettingsNewMessagesSwitcher": "Новые сообщения",
"profileSettingsMarketing": "Рекламные предложения",
"profileSettingsActivityReminder": "Напоминания об активности",
"profileSettingsReferralActivity": "Приглашение друзей",
"profileSettingsContentSubtitle": "Контент",
"profileSettingsTrainingVideo": "Обучающие видео",
"profileSettingsOpenSettingsDescription": "Включи уведомления в Настройках телефона и узнавай о новых симпатиях",
"profileSettingsOpenSettingsButton": "Настройки",
"profileSettingsSubscriptionTitle": "Подписка",
"profileSettingsSubscriptionUntil": "До {subscriptionUntilDate} включительно",
"placeholders": {
"subscriptionUntilDate": {
"type": "String"
"profileSettingsSubscriptionProlongText": "Продлевать автоматически",
"profileSettingsSubscriptionPurchaseCTAButton": "Оформить",
"profileSettingsSubscriptionActive": "Активна",
"description": "Причина удаления аккаунта"
"deletionReasonTitle": "Почему хочешь удалить профиль?",
"deletionReasonPairFound": "Я нашел пару",
"deletionReasonDislike": "Не нравится приложение",
"deletionReasonNotRelevant": "Уже неактуально",
"deletionReasonOther": "Другое",
"description": "Таймер удаления аккаунта"
"profileDeleteTimerCancelButton": "Отменить удаление",
"profileDeleteTimerSubtitle": "{count, plural, one {секунда} few {секунды} many {секунд} other {секунды}} до удаления",
"description": "Превью анкеты в профиле"
"profileCardPreviewTitle": "Моя анкета",
"profileCardPreviewCloseButton": "Закрыть",
"profileCardPreviewModeratingTitle": "Проверяем эту фотографию",
"description": "Часто спрашивают"
"faqTitle": "Часто спрашивают",
"description": "Боттомщит ввода email"
"enterEmailLabelText": "Электронная почта",
"enterEmailHintText": "yourem@il.com",
"enterEmailCheckboxDescription": "Хочу получать персональные предложения и новости",
"enterEmailSaveButton": "Сохранить",
"description": "Боттомщит Связаться с нами"
"contactUsMessageLabel": "Текст обращения",
"contactUsMessageHint": "Проблема или предложение",
"contactUsEmailLabel": "Почта для связи",
"contactUsEmailHint": "yourem@il.com",
"contactUsSendButton": "Отправить",
"contactUsSuccessSnack": "Получили твоё обращение",
"description": "Чаты"
"newPairsAppBarTitle": "Твои пары",
"newPairsAppBarSubtitle": "Сделай первый шаг — напиши «Привет»",
"newPairsEmptyAppBarSubtitle": "Взаимные симпатии появятся тут",
"chatsTitle": "Сообщения",
"chatsLostNetwork": "Загрузка",
"chatsEmptyTitle": "Переписок нет",
"chatsEmptySubtitle": "Найди пару и начинай общение",
"chatsOpenUserCardsButtonTitle": "Смотреть анкеты",
"chatsPaginationError": "Ранние чаты не загрузились",
"chatsCacheLoadingError": "Не удалось загрузить переписки, попробуйте позже",
"messagesPaginationError": "Сообщения не загрузились",
"invalidMessageError": "Не отправилось, попробуй ещё раз",
"chatNotFoundError": "Проблемы с чатом, попробуй ещё раз",
"userTyping": "Печатает",
"messageInputPlaceholder": "Сообщение",
"messagesEmptyTitle": "Сообщений нет",
"messagesEmptySubtitle": "Напиши «Привет» или сделай комплимент",
"messagesEdit": "Редактирование",
"messageEdited": "Изменено",
"messagesReplyTo": "Ответ на сообщение: {name}",
"placeholders": {
"name": {
"type": "String"
"messagesUnreadTitle": "Непрочитанные сообщения",
"messageContextMenuCopy": "Скопировать",
"messageContextMenuReply": "Ответить",
"messageContextMenuEdit": "Изменить",
"messageContextMenuDelete": "Удалить",
"messageContextMenuDeleteOnlyForMe": "Удалить у меня",
"messageContextMenuDeleteForAll": "Удалить у меня и у {name}",
"placeholders": {
"name": {
"type": "String"
"messageContextMenuCopyResultMessage": "Сообщение было скопировано",
"messageDeleteMessageError": "Проблемы с чатом, попробуй ещё раз",
"messageEditMessageError": "Не удалось изменить сообщение, попробуй ещё раз",
"messageLinkPressedTitle": "Ты уверен, что хочешь перейти 
в сторонний сервис?",
"messageLinkPressedDescription": "Ссылки могут содержать опасные ресурсы и мошеннические ловушки",
"messageLinkPressedSubmit": "Перейти",
"messageLinkPressedCancel": "Отмена",
"chatNoConnectionTitle": "Загрузка...",
"chatUserVerified": "Проверен",
"chatUserNotVerified": "Не проверен",
"chatUserHasSubscription": "Подписка",
"chatUserNoSubscription": "Нет подписки",
"chatUserOnline": "В сети",
"chatUserOffline": "Не в сети",
"communityTrainingRulesBannerTitle": "Твои границы в твоих руках. Видишь нарушение — пиши нам",
"communityTrainingRulesBannerButtonTitle": "Правила сообщества",
"chatUserAge": "{age, plural, zero{лет} one{{age} год} few{{age} года} many{{age} лет} other{{age} лет}}",
"placeholders": {
"age": {
"type": "int"
"messageTime": "{time}",
"placeholders": {
"time": {
"type": "DateTime",
"format": "HH:mm",
"isCustomDateFormat": "true"
"messageTimeToday": "Сегодня в {time}",
"placeholders": {
"time": {
"type": "DateTime",
"format": "HH:mm",
"isCustomDateFormat": "true"
"messageTimeYesterday": "Вчера в {time}",
"placeholders": {
"time": {
"type": "DateTime",
"format": "HH:mm",
"isCustomDateFormat": "true"
"messageDay": "{day}",
"placeholders": {
"day": {
"type": "DateTime",
"format": "EEEE"
"messageDayMonth": "{date}",
"placeholders": {
"date": {
"type": "DateTime",
"format": "d MMMM",
"isCustomDateFormat": "true"
"messageDayMonthYear": "{date}",
"placeholders": {
"date": {
"type": "DateTime",
"format": "dd.MM.yyyy",
"isCustomDateFormat": "true"
"messagesDateDividerToday": "Сегодня",
"messagesDateDividerDayMonth": "{date}",
"placeholders": {
"date": {
"type": "DateTime",
"format": "d MMMM",
"isCustomDateFormat": "true"
"messageDateDividerDayMonthYear": "{date}",
"placeholders": {
"date": {
"type": "DateTime",
"format": "d MMMM, yyyy",
"isCustomDateFormat": "true"
"description": "Запрос разрешения на отправку увдомлений в чате."
"chatNotificationPermissionRequestTitle": "Не пропусти сообщение",
"chatNotificationPermissionRequestReason": "Когда {name} напишет, узнай об этом сразу",
"placeholders": {
"name": {
"type": "String"
"chatNotificationPermissionSettingsButton": "Открыть настройки",
"chatNotificationPermissionRequestButton": "Включить уведомления",
"chatNotificationPermissionRequestSkipButton": "Пропустить",
"description": "Первый запуск мэтчинга"
"matchingFirstLaunchTitle": "Последний шаг и в бой!",
"matchingFirstLaunchSubtitle": "Давай определимся с возрастом желаемого партнёра",
"matchingFirstLaunchPickerLabelFrom": "от",
"matchingFirstLaunchPickerLabelTo": "до",
"matchingFirstLaunchHintText": "Ищем пару от {from} до {age, plural, zero{лет} one{{age} года} few{{age} лет} many{{age} лет} other{{age} лет}}",
"matchingFirstLaunchForwardBtnLabel": "Смотреть анкеты",
"description": "Экран фильтры"
"filtersAppBarTitle": "Настройки поиска",
"filtersAppBarActionButtonTitle": "Готово",
"filtersLocationTitle": "Город",
"filtersCommunitiesTitle": "Показывать только участников сообщества ",
"filtersLocationAuto": "Автоматически",
"filtersRadiusTitle": "Радиус поиска",
"filtersRadiusValue": "{value} км",
"filtersRadiusMaxValue": "Любой",
"filtersExpandSwitcherTitle": "Можно немного расширить",
"filtersAgeTitle": "Возраст",
"filtersAgeMaxValue": "Не важен",
"filtersAgeRangeHint": "{from}-{age, plural, zero{лет} one{{age} год} few{{age} года} many{{age} лет} other{{age} лет}}",
"filtersHeightTitle": "Рост",
"filtersHeightAddBtnLabel": "Добавить",
"filtersAgeRange": "{min}-{max} см",
"filtersAgeRangeMinNull": "до {max} см",
"filtersAgeRangeMaxNull": "от {min} см",
"filtersAgeRangeMinMaxNull": "Не важен",
"description": "BS выбора причины жалобы"
"reportBSTitle": "На что хочешь пожаловаться?",
"reportBSSuccessText": "Получили твоё обращение",
"reportObsceneLanguage": "Нецензурная лексика",
"reportNudity": "Откровенные фотографии",
"reportBehavior": "Неприемлемое поведение",
"reportCatfisher": "Не тот, за кого себя выдает",
"reportOther": "Другое",
"description": "Экран запроса разрешения на уведомления"
"notificationPermissionRequestTitle": "Не пропусти симпатию",
"notificationPermissionRequestSubtitle": "Когда {name} поставит тебе лайк, узнай об этом сразу",
"notificationPermissionRequestForwardBtn": "Включить уведомления",
"notificationPermissionRequestSkipBtn": "Пропустить",
"description": "Экран Симпатии"
"sympathyTitle": "Симпатии",
"sympathySubtitle": "Узнай, кому понравилась твоя анкета",
"sympathySubscriptionBtn": "Узнай, кто поставил лайк",
"sympathyNotesTitle": "Записки",
"sympathyNotesEmptySubtitle": "Когда кто-нибудь напишет тебе записку, мы пришлём уведомление",
"sympathyLikesTitle": "Поставили лайк",
"sympathyLikesEmptyTitle": "Симпатий нет",
"sympathyLikesEmptySubtitle": "Здесь появятся все, кому понравилась твоя анкета",
"sympathyNotesAppbarTitle": "Записки",
"sympathyNotesErrorTItle": "Ранние записки не загрузились",
"sympathyNewLikeHint": "Новый",
"sympathyNotesNewNoteHint": "Новая",
"sympathyMale1": "Незнакомец",
"sympathyMale2": "Красавчик",
"sympathyMale3": "Ухажёр",
"sympathyMale4": "Суженый",
"sympathyMale5": "Поклонник",
"sympathyMale6": "Кавалер",
"sympathyMale7": "Воздыхатель",
"sympathyMale8": "Избранник",
"sympathyMale9": "Интеллектуал",
"sympathyMale10": "Романтик",
"sympathyFemale1": "Незнакомка",
"sympathyFemale2": "Красавица",
"sympathyFemale3": "Суженая",
"sympathyFemale4": "Шутница",
"sympathyFemale5": "Поклонница",
"sympathyFemale6": "Избранница",
"sympathyFemale7": "Муза",
"sympathyFemale8": "Совершенство",
"sympathyFemale9": "Модница",
"sympathyFemale10": "Интеллектуалка",
"sympathyHoursRemains": "{hours} ч.",
"sympathyMinutesRemains": "{minutes} мин",
"description": "Экран Взаимная симпатия"
"matchTitle": "Это по любви!",
"matchSubtitle": "Вы и {matchUserName} нравитесь друг другу",
"matchOpenChatBtnLabel": "Открыть чат",
"description": "BS контекстного меню чата"
"chatMenuSubtitle": "Действия с перепиской",
"chatMenuShareProfileBtnLabel": "Поделиться профилем",
"chatMenuReportAndBlockBtnLabel": "Пожаловаться и заблокировать",
"chatMenuDeletePairBtnLabel": "Удалить пару",
"chatMenuShareTextTemplate": "* Информация о партнёре из приложения \"По любви\":\n* Имя: {first_name}\n* Возраст: {age}\n* ID аккаунта: {user_id}",
"description": "BS разрыва пары"
"deletePairTitle": "Почему хочешь закрыть взаимную симпатию?",
"deletePairMetOffline": "Мы встретились лично",
"deletePairBehavior": "Недопустимое поведение",
"deletePairCatfisher": "Не тот за кого себя выдаёт",
"deletePairNotInterested": "Больше неинтересно",
"deletePairOther": "Другое",
"description": "Подписка"
"subscriptionOffersHeader": "Подписка",
"subscriptionOffersFromSympathyTitle": "Узнай, кому понравилась твоя анкета",
"subscriptionOffersFromSympathyDescription": "Оформи подписку «Любовь без границ» — увидишь, кто поставил тебе лайк",
"subscriptionOffersFromEndedLikesTitle": "Открой больше сердечек",
"subscriptionOffersFromEndedLikesDescription": "Оформи подписку «Любовь без границ» и получай дополнительные сердечки каждый день",
"subscriptionOffersFromEndedUndosTitle": "Не поздно передумать",
"subscriptionOffersFromEndedUndosDescription": "Оформи подписку «Любовь без границ» и вовзращай пропущенные анкеты без ограничений",
"subscriptionOffersFromEndedSuperLikesTitle": "Открой больше записок",
"subscriptionOffersFromEndedSuperLikesDescription": "Оформи подписку «Любовь без границ» и отправляй больше записок каждый день",
"subscriptionOffersDefaultTitle": "Узнай, кому понравилась твоя анкета",
"subscriptionOffersDefaultDescription": "Оформи подписку «Любовь без границ» — увидишь, кто поставил тебе лайк",
"subscriptionFeaturesTitle": "Что в подписке",
"subscriptionInfoIosIAP": "При оформлении подписки платеж будет списан с Вашего ITunes аккаунта с указанием стоимости продления. Автоматическое обновление подписки произойдет, если она не будет отменена как минимум за 24 часа до окончания оплаченного периода. После приобретения подписки управление и отмена автоматического обновления подписки доступны в Настройках ITunes аккаунта.",
"subscriptionInfoAndroidExternal": "При оформлении подписки платеж будет списан с вашего банковского счета с указанием стоимости продления. Подписка будет автоматически продлена, если она не будет отменена как минимум за 24 часа до окончания текущего периода.\nУправление подпиской, включая ее отмену, можно выполнить в настройках аккаунта \"По любви\".",
"subscriptionConditionsButtonTitle": "Условия",
"subscriptionPolicyButtonTitle": "Приватность",
"subscriptionPurchaseTitle": "Купить за {price}",
"placeholders": {
"price": {
"type": "String"
"subscriptionRestoreTitle": "Восстановить покупки",
"subscriptionPendingPaymentTitle": "Оформляем покупку",
"subscriptionPendingPaymentDescription": "Подписка активируется в течение часа, либо мы вернём деньги",
"subscriptionPendingPaymentButtonTitle": "Готово",
"subscriptionFailedPaymentTitle": "Оплата не прошла",
"subscriptionFailedPaymentDescription": "Деньги не списались. Попробуй оплатить подписку ещё раз",
"subscriptionFailedPaymentRetryButtonTitle": "Оплатить заново {price}",
"placeholders": {
"price": {
"type": "String"
"subscriptionFailedPaymentCancelButtonTitle": "Отмена",
"subscriptionCancelProlongationTitle": "Отключить продление подписки?",
"subscriptionCancelProlongationDescription": "Платные функции перестанут работать {subscriptionUntilDate}. Деньги списываться не будут",
"placeholders": {
"subscriptionUntilDate": {
"type": "String"
"subscriptionCancelProlongationPositiveButtonTitle": "Оставить продление",
"subscriptionCancelProlongationNegativeButtonTitle": "Выключить продление",
"subscriptionDurationWeek": "1 неделя",
"subscriptionDurationForWeek": "{text} на 1 неделю",
"placeholders": {
"text": {
"type": "String"
"subscriptionDurationMonth": "1 месяц",
"subscriptionDurationForMonth": "{text} на 1 месяц",
"placeholders": {
"text": {
"type": "String"
"subscriptionDurationTwoMonths": "2 месяца",
"subscriptionDurationForTwoMonths": "{text} на 2 месяца",
"placeholders": {
"text": {
"type": "String"
"subscriptionDurationThreeMonths": "3 месяца",
"subscriptionDurationForThreeMonths": "{text} на 3 месяца",
"placeholders": {
"text": {
"type": "String"
"subscriptionDurationSixMonths": "6 месяцев",
"subscriptionDurationForSixMonths": "{text} на 6 месяцев",
"placeholders": {
"text": {
"type": "String"
"subscriptionDurationYear": "1 год",
"subscriptionDurationForYear": "{text} на 1 год",
"placeholders": {
"text": {
"type": "String"
"subscriptionGiftDuration": "Месяц подписки в подарок",
"subscriptionGiftDeferredDaysDuration": "{days, plural, one{{days} день} few{{days} дня} many{{days} дней} other{{days} дней}} в подарок",
"placeholders": {
"days": {
"type": "int"
"subscriptionPurchaseServiceUnavailableErrorText": "Сервис недоступен, попробуй позже",
"subscriptionPurchasePaymentPendingAcquiringErrorText": "Платеж в обработке у эквайринга",
"noSubscriptionAfterSuccessPaymentErrorText": "Во время оплаты произошла ошибка",
"iapTariffNotFoundErrorText": "Этот тарифный план недоступен. Попробуй другой",
"iapNoAvailableTariffsErrorText": "На данный момент тарифы недоступны. Попробуй позже",
"iapPurchaseErrorText": "Во время оплаты произошла ошибка",
"subscriptionTYPHeader": "Подписка",
"subscriptionName": "Любовь без границ",
"subscriptionText": "Подписка",
"subscriptionTYPFeaturesTitle": "Доступно",
"subscriptionTYPCancelInfoText": "Отменить можно в «Профиле»",
"subscriptionStatusActive": "Активна",
"subscriptionNextPayment": "{price} {priceForPluralization, plural, one{спишется} few{спишутся} many{спишутся} other{спишутся}} {subscriptionPaymentDate}",
"placeholders": {
"price": {
"type": "String"
"priceForPluralization": {
"type": "int"
"subscriptionPaymentDate": {
"type": "String"
"subscriptionNotProlongedSBP": "Подключите списание в СБП для автоматического продления подписки",
"subscriptionPaymentDate": "{date}",
"placeholders": {
"date": {
"type": "DateTime",
"format": "d MMMM",
"isCustomDateFormat": "true"
"subscriptionUntilDate": "{date}",
"placeholders": {
"date": {
"type": "DateTime",
"format": "d MMMM",
"isCustomDateFormat": "true"
"subscriptionProlongSucceedText": "Включили продление подписки",
"subscriptionFailedToProlongErrorText": "Не удалось включить продление подписки. Попробуй ещё раз",
"subscriptionFailedToCancelProlongationErrorText": "Не удалось отключить продление подписки. Попробуй ещё раз",
"subscriptionManagementIAPConfigurationErrorText": "Подписка была оформлена с Android устройства",
"subscriptionManagementExternalConfigurationErrorText": "Подписка была оформлена с iOS устройства",
"description": "Супер лайк"
"superLikeTitle": "Отправь записку",
"superlikeSubtitle": "{name} увидит твоё сообщение до взаимной симпатии",
"superLikeLabel": "Текст записки",
"superLikeHint": "Сделай комплимент",
"superLikeBtnLabel": "Отправить",
"superLikeSuccessSnackBarText": "Отправили твою записку",
"description": "Экран заблокированнного пользователя"
"blockedUserInfoLogoutText": "Выйти",
"blockedUserInfoTitle": "Твой аккаунт заблокирован",
"blockedUserInfoDescription": "Твоя анкета или поведение нарушили правила сообщества. Если произошла ошибка, пиши в поддержку",
"blockedUserInfoCommunityRules1": "Прочитай ещё раз ",
"blockedUserInfoCommunityRules2": "правила сообщества",
"blockedUserInfoCommunityRules3": ", прежде чем обращаться в службу поддержки",
"blockedUserInfoCancelSubscriptionButtonText": "Отменить подписку",
"blockedUserInfoBlockedSupportButtonText": "Написать в поддержку",
"description": "Экран реферальной программы"
"referralProgramTitle": "Пригласи друга",
"referralProgramSubtitlePremium": " помоги ему найти свою любовь. Ведь чем больше друзей вокруг, тем больше возможностей для счастливых знакомств!",
"referralProgramSubtitleNoPremium": "если он подтвердит свою личность при регистрации, то ты получишь {userDays} {userDays, plural, one{день} few{дня} many{дней} other{дня}} премиум подписки, а друг {friendDays} {friendDays, plural, one{день} few{дня} many{дней} other{дня}} в подарок",
"description": "Текст в AppBar с динамическими днями подписки",
"placeholders": {
"userDays": {
"type": "int"
"friendDays": {
"type": "int"
"referralProgramShareLinkButton": "Отправить ссылку",
"referralProgramInviteForMoreDays1": "Начни приглашать друзей чтобы получить больше дней премиум подписки.",
"referralProgramInviteForMoreDays2": "Подробнее о программе",
"referralProgramInfoNoFriendsPremium": "Если друг подтвердит личность при регистрации, ты получишь {inviterDays, plural, one{{inviterDays} день} few{{inviterDays} дня} many{{inviterDays} дней} other{{inviterDays} дней}} премиума, а друг получит {inviteeDays, plural, one{{inviteeDays} день} few{{inviteeDays} дня} many{{inviteeDays} дней} other{{inviteeDays} дней}} в подарок",
"description": "Сообщение о бонусе за приглашение",
"placeholders": {
"inviterDays": {
"type": "int"
"inviteeDays": {
"type": "int"
"referralProgramFriendsInvited": "{friends, plural, one{друг \nприглашен} few{друга \nприглашено} many{друзей \nприглашено} other{друзей \nприглашено}}",
"placeholders": {
"friends": {
"type": "int"
"referralProgramDaysReceived": "{days, plural, one{день подписки получен} few{дня подписки получено} many{дней подписки получено} other{дней подписки получено}}",
"placeholders": {
"days": {
"type": "int"
"referralProgramDaysMotivation1": "Пригласи ",
"referralProgramDaysMotivation2": "{friends, plural, one{{friends} друга} few{{friends} друзей} many{{friends} друзей} other{{friends} друзей}}",
"placeholders": {
"friends": {
"type": "int"
"referralProgramDaysMotivation3": " и получи дополнительно +7 дней подписки",
"referralProgramStatisticsInfo1": "Ты пригласил больше, чем {percent}% приглашающих.",
"placeholders": {
"percent": {
"type": "int"
"referralProgramStatisticsInfoZeroPercent1": "Продолжай приглашать друзей чтобы получить больше дней премиум подписки.",
"referralProgramStatisticsInfoZeroPercent2": "Подробнее о программе",
"referralProgramDaysNewMemberMotivation": "Пригласи {friends, plural, one{{friends} друга} few{{friends} друзей} many{{friends} друзей} other{{friends} друзей}} и получи дополнительно {days, plural, one{{days} день} few{{days} дня} many{{days} дней} other{{days} дней}} подписки",
"placeholders": {
"friends": {
"type": "int"
"days": {
"type": "int"
"referralProgramDaysMemberMotivation": "Пригласи ещё {friends, plural, one{{friends} друга} few{{friends} друзей} many{{friends} друзей} other{{friends} друзей}} и получи дополнительно {days, plural, one{{days} день} few{{days} дня} many{{days} дней} other{{days} дней}} подписки",
"placeholders": {
"friends": {
"type": "int"
"days": {
"type": "int"
"referralProgramDaysMaxMotivation": "Продолжай приглашать друзей и получай дополнительно +{days, plural, one{{days} день} few{{days} дня} many{{days} дней} other{{days} дней}} подписки",
"placeholders": {
"days": {
"type": "int"
"referralProgramStatisticsInfo2": "Подробнее о программе",
"referralProgramActivityProgressTitle": "Поздравляем! \nТы пригласил {count, plural, one{{count} друга} few{{count} друзей} many{{count} друзей} other{{count} друзей}}!",
"placeholders": {
"count": {
"type": "int"
"referralProgramActivityFriendTitle": "{gender, select, male{Друг зарегистрировался!} female{Подруга зарегистрировалась!} other{Друг зарегистрировался!}} \n{date}",
"description": "Сообщение о регистрации друга/подруги с указанием даты и времени",
"placeholders": {
"gender": {
"type": "String",
"example": "male/female"
"date": {
"type": "DateTime",
"format": "dd.MM.y в HH:mm",
"isCustomDateFormat": "true"
"referralProgramActivityTitle": "История приглашений",
"referralProgramActivityEmptyContent": "Ты ещё никого не пригласил. Как только друг подтвердит свою личность, ты получишь подарок",
"referralProgramLinkSharingText": "Первый шаг к семейному счастью 💕\n\nСкачивай приложение \"По любви\" для поиска серьёзных отношений {link}",
"placeholders": {
"link": {
"type": "String"
"referralProgramSuccessLinkCopying": "Ссылка скопирована",
"referralProgramSuccessLinkSharing": "Приглашение отправлено",
"description": "Диалог поздравления реферала"
"referralCongratsTitle": "Премиум в подарок",
"referralCongratsLinkOwnerSubtitle": "Друг зарегистрировался по твоей ссылке, {days} {days, plural, one{день} few{дня} many{дней} other{дней}} премиум подписки уже активны.",
"description": "Количество дней подписки за рефералку",
"placeholders": {
"days": {
"type": "int"
"referralCongratsLinkReceiverSubtitle": "Поздравляем, ты зарегистрировался по реферальной ссылке, {days} {days, plural, one{день} few{дня} many{дней} other{дней}} премиум подписки уже активны.",
"description": "Количество дней подписки за рефералку",
"placeholders": {
"days": {
"type": "int"
"referralCongratsSubscriptionExpireDate": "Подписка активна до {date}",
"placeholders": {
"date": {
"type": "DateTime",
"format": "dd.MM.yyyy",
"isCustomDateFormat": "true"
"referralCongratsConfirmButtonLabel": "Понятно",
"description": "BS подтверждения реферала"
"referralConfirmationSelfInviteErrorTitle": "Нельзя пригласить себя",
"referralConfirmationTitle": "Ты {gender, select, male{зарегистрировался} female{зарегистрировалась} other{зарегистрировался}} по приглашению от друга",
"description": "Заголовок BS подтверждения рефералки",
"placeholders": {
"gender": {
"type": "String",
"example": "male/female"
"referralConfirmationSubtitle": "Подтверди свою личность сделав селфи, чтобы получить {friend_days} {friend_days, plural, one{день} few{дня} many{дней} other{дней}} премиум подписки",
"description": "Количество дней подписки за рефералку",
"placeholders": {
"friend_days": {
"type": "int"
"referralConfirmationConfirmButtonLabel": "Подтвердить свою личность",
"referralConfirmationCancelButtonLabel": "Позже",
"description": "Экран сообществ"
"communitiesAppBarTitle": "Сообщества",
"communitiesEmptyStateTitle": "Нет сообществ",
"communitiesEmptyStateSubtitle": "Ты не состоишь в сообществе, попроси участника пригласить тебя и получи",
"communityFeatureAdditionalFilters": "Дополнительные фильтры",
"communityFeatureCommunityLogo": "Значок сообщества",
"communityAvailableCommunitiesTitle": "Список сообществ, куда можно вступить",
"communityAvailableCommunitiesSubtitle1": "это люди, объединённые общими целями или интересами. Чтобы создать своё сообщество, напиши на ",
"communityAvailableCommunitiesSubtitle2": "info@plbvi.ru",
"communityCommunitiesListButton": "Смотреть сообщества",
"communitySupportEmail": "info@plbvi.ru",
"communityParticipantsCount": "{count} {count, plural, one{участник} few{участника} many{участников} other{участников}}",
"placeholders": {
"count": {
"type": "int"
"communityEntryDate": "Дата вступления {date}",
"placeholders": {
"date": {
"type": "DateTime",
"format": "dd.MM.yyyy",
"isCustomDateFormat": "true"
"communityVisibilityEnabledStatus": "Значок видно всем",
"communityVisibilityEnabledDescription": "Ты видишь участников, а они тебя",
"communityVisibilityDisabledStatus": "Значок скрыт",
"communityVisibilityDisabledDescription": "Ты не видишь участников, а они тебя",
"communityLeaveButton": "Покинуть сообщество",
"communityInviteTitle": "Пригласить в сообщество",
"communityInviteSubtitleClosed": "Сообщество приватно и участники утверждаются модератором",
"communityInviteSubtitleOpened": "Это открытое сообщество и вступить может любой пользователь по приглашению",
"communitySendInviteButton": "Отправить приглашение",
"communityLeftSnack": "Ты покинул сообщество",
"communityLinkSharedSnack": "Приглашение в сообщество отправлено",
"communityLinkCopiedSnack": "Ссылка скопирована",
"communityDeleteAccountDialogTitle": "Покинуть сообщество {community}?",
"placeholders": {
"community": {
"type": "String"
"communityPrivateLeaveDialogBody": "Это приватное сообщество. Ты сможешь вернуться по приглашению, только если утвердит модератор",
"communityLeaveDialogBody": "Ты сможешь вернуться в любой момент по приглашению от участника",
"communityLeaveDialogLeaveButton": "Покинуть",
"communityLeaveDialogCancelButton": "Отмена",
"communityLinkSharingText": "{community} приглашает тебя в сообщество для поиска семейного счастья \n\nСкачивай приложение \"По любви\" для поиска серьёзных отношений {link}",
"placeholders": {
"community": {
"type": "String"
"link": {
"type": "String"
"description": "Вступление в сообщество"
"communityAccessionErrorAlreadyJoined": "Ты уже вступил в это сообщество",
"communityAccessionBSTitle": "Тебя приглашают в сообщество {community}",
"placeholders": {
"community": {
"type": "String"
"communityAccessionBSSubtitle": "вступив, ты сможешь знакомиться с участниками и получить:",
"communityAccessionBSJoinButton": "Вступить",
"communityAccessionBSCancelButton": "Не вступать",
"communityAccessionBSLaterButton": "Позже",
"communityAccessionBSFeature1": "Дополнительные фильтры",
"communityAccessionBSFeature2": "Значок сообщества",
"description": "Результат запроса вступления в сообщество"
"communityRequestResultAcceptedTitle": "Тебя приняли в сообщество {community}",
"placeholders": {
"community": {
"type": "String"
"communityRequestResultAcceptedSubtitle": "Ты получил значок сообщества и можешь видеть его участников",
"communityRequestResultDeniedTitle": "Тебя не приняли в сообщество {community}",
"placeholders": {
"community": {
"type": "String"
"communityRequestResultDeniedSubtitle": "Модератор не подтвердил, что ты являешься членом сообщества",
"communityRequestResultExcludedTitle": "Тебя исключили из сообщества {community}",
"placeholders": {
"community": {
"type": "String"
"communityRequestResultExcludedSubtitle": "Ты сможешь вернуться, только если тебя пригласит участник",
"communityRequestResultOkButton": "ОK",
"description": "Экран списка сообществ"
"communitiesListAppBarTitle": "Список доступных сообществ",
"communitiesListSearchHint": "Поиск сообщества",
"communitiesListEmptyTitle": "Ничего не найдено",
"communitiesListEmptySubtitle1": "Проверь верно ли введено название. \u2028Чтобы создать своё сообщество, напиши на  ",
"communitiesListEmptySubtitle2": "info@plbvi.ru",
"description": "Экран жесткого обновления"
"hardUpdateTitle": "Обнови приложение",
"hardUpdateUpdateButton": "Обновить",
"description": "Экран успешного всупления в сообщество"
"communitySuccessJoinOpenTitle": "Ты вступил в сообщество {community}",
"placeholders": {
"community": {
"type": "String"
"communitySuccessJoinCloseTitle": "Ты подал заявку на вступление в приватное сообщество {community}",
"placeholders": {
"community": {
"type": "String"
"communitySuccessJoinOpenSubtitle": "Ты получаешь:",
"communitySuccessJoinCloseSubtitle": "После утверждения модератором, ты сможешь знакомиться с участниками и получить:",
"communityPlaceholderJoinCountTitle": "Ты можешь вступить \nещё в {count} {count, plural, zero{сообществ} one{сообщество} few{сообщества} many{сообществ} other{сообществ}}",
"placeholders": {
"count": {
"type": "int"
"communityPlaceholderOpportunitiesSubtitle": "Чем больше сообществ, тем больше возможностей найти партнера по интересам",
"communityPlaceholderViewCommunitiesButton": "Смотреть сообщества",
"communityPlaceholderAvailableListTitle": "Список сообществ, куда можно вступить",
"communityPlaceholderDescription": "это люди, объединенные общими целями или интересами. Чтобы создать свое сообщество, напиши на ",
"communityPlaceholderSupportEmail": "info@plbvi.ru",
"description": "Статус активности пользователя"
"activityStatusOnline": "Онлайн",
"activityStatusJustNow": "только что",
"activityStatusWasMale": "Был",
"activityStatusWasFemale": "Была",
"activityStatusYesterday": "вчера",
"activityStatusMinutesAgo": "{minutes} {minutes, plural, one{минуту} few{минуты} many{минут} other{минут}} назад",
"description": "Статус активности - минуты назад",
"placeholders": {
"minutes": {
"type": "int"
"activityStatusHoursAgo": "{hours} {hours, plural, one{час} few{часа} many{часов} other{часов}} назад",
"description": "Статус активности - часы назад",
"placeholders": {
"hours": {
"type": "int"
"activityStatusDate": "{date}",
"description": "Статус активности - дата без года",
"placeholders": {
"date": {
"type": "DateTime",
"format": "d.MM",
"isCustomDateFormat": "true"
"activityStatusDateWithYear": "{date}",
"description": "Статус активности - дата с годом",
"placeholders": {
"date": {
"type": "DateTime",
"format": "dd.MM.yyyy",
"isCustomDateFormat": "true"
"activityStatusGenderTimeText": "{genderPrefix} {timeText}",
"description": "Комбинация гендерного префикса и времени для статуса активности",
"placeholders": {
"genderPrefix": {
"type": "String"
"timeText": {
"type": "String"