rebrand(v8.5→Лидерра): дизайн-handoff Платона v8 Forest + Лидпоток→Лидерра
Получен handoff-пакет liderra_v8_handoff/ от дизайнера Платона (kpd9363@gmail.com) от 07.05.2026 — v8 Forest. Заказчик 08.05 решил применить только в части дизайна, имени, логотипа. Функционал, состав страниц и правила (CTO-11, click-wrap, SSO break-glass, 14 статусов воронки) — без изменений (источник — ТЗ v8.5/schema v8.5). Что сделано: - Массовая замена Лидпоток→Лидерра (с учётом падежей: Лидерры/Лидерре) в 33 файлах (449 вхождений) — все .md/.sql/.json/.toml/.yml/.txt/.html, кроме исторических упоминаний внутри liderra_v8_handoff/ - Удалён docs/brandbook.md v1.1 — заменён на BRANDBOOK_v2.md из handoff - Скопированы 13 концептов liderra_v8_handoff/concepts/v8_*.html в web/v8/. Удалены старые web/01-login.html, 02-dashboard.html, 03-deals.html, index.html (палитра v1.1 deprecated) - CLAUDE.md v1.0→v1.1: §0 (BRANDBOOK_v2 + DEVELOPER_HANDOFF в источниках), §2 (палитра Forest, Inter+JBM, Lucide), §5 п.6 (anti-pattern Inter снят — в Forest Inter наш основной шрифт), §6 (13 концептов в web/v8/) - Реестр Открытые_вопросы_v8_3.md v1.12→v1.13: добавлена запись о ребрендинге + 4 точечных расхождений handoff vs ТЗ (статусы воронки, click-wrap чекбоксы, SSO fallback, axe violations) - package.json/package-lock.json: name lidpotok→liderra 4 расхождения handoff vs ТЗ (НЕ применены, источник истины — ТЗ/schema): 1. 14 «обобщённых» статусов в BRANDBOOK_v2 §3.6 ≠ 14 slug'ов в schema.sql:2076 (совпадает 2 из 14: «Переговоры», «Оплачено»). Источник — schema/ТЗ §6.4 (реселлерская модель из аудита crm.bp-gr.ru, 6 системных + 8 настраиваемых статусов). 2. 3-й click-wrap в v8_login.html («маркетинг-опционально») ≠ ТЗ §1.5/§4.1 («согласие на ПДн», обязательное, OPEN-Ж-3). 3. SSO в v8_admin.html («локальный 2FA fallback») ≠ ТЗ OPEN-И-13 (break-glass super_admin, локальный 2FA выключен). 4. Заявление «axe-core 4.10.2 — 0 violations» в README handoff — локально Pa11y 9.1.1 + axe нашёл 81 violation на 10/13 HTML (преимущественно color-contrast на декоративных separator'ах с --ink-disabled). Чисто: settings/errors/palette_options. Что НЕ включено в коммит: - лендинг/TZ_landing_v1_0.md — untracked, не моя работа в этой сессии - .tmp/ — gitignored Что осталось (для следующих сессий): - Возможное переименование GitHub-репо CoralMinister/lidpotok → liderra (отдельное решение заказчика) - Опционально: обратная связь Платону по 4 расхождениям handoff vs ТЗ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@@ -1,5 +1,5 @@
|
||||
# =============================================================================
|
||||
# .gitignore — Лидпоток
|
||||
# .gitignore — Лидерра
|
||||
# =============================================================================
|
||||
|
||||
# ── Node / npm ──────────────────────────────────────────────────────────────
|
||||
@@ -86,7 +86,7 @@ memory/
|
||||
.claude/local-*
|
||||
.claude/settings.local.json
|
||||
# Артефакты переноса проекта со старых машин (JSONL-сессии Claude) — например
|
||||
# c--Users-KDV-Projects-lidpotok/ от 07.05.2026 (Win10 → Server 2022)
|
||||
# c--Users-KDV-Projects-liderra/ от 07.05.2026 (Win10 → Server 2022)
|
||||
c--Users-*/
|
||||
|
||||
# ── Временные файлы ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Конфигурация gitleaks для проекта Лидпоток
|
||||
# Конфигурация gitleaks для проекта Лидерра
|
||||
# Правило §5.2 правил Claude: запрет на ПДн / токены / API-ключи в коммитах
|
||||
#
|
||||
# ВАЖНО: gitleaks использует Go RE2, который НЕ поддерживает lookbehind/lookahead.
|
||||
# Все правила ниже написаны без (?<=...), (?<!...), (?=...), (?!...).
|
||||
# Границы — через \b (word boundary).
|
||||
|
||||
title = "gitleaks Лидпоток"
|
||||
title = "gitleaks Лидерра"
|
||||
|
||||
# Расширяем встроенный набор правил gitleaks
|
||||
[extend]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Конфигурация lychee для проекта Лидпоток
|
||||
# Конфигурация lychee для проекта Лидерра
|
||||
# Документация: https://lychee.cli.rs
|
||||
|
||||
# Кеш на 12 часов — внешние ссылки не дёргать каждый запуск
|
||||
@@ -34,8 +34,8 @@ exclude = [
|
||||
"^https?://0\\.0\\.0\\.0",
|
||||
"^https?://192\\.168\\.",
|
||||
# Будущие домены проекта (DO-2 — после Б-1)
|
||||
"^https?://lidpotok\\.ru",
|
||||
"^https?://.*\\.lidpotok\\.ru",
|
||||
"^https?://liderra\\.ru",
|
||||
"^https?://.*\\.liderra\\.ru",
|
||||
"^https?://.*\\.<домен>",
|
||||
# Внутренние URL из runbook (бастионы, sentry self-hosted)
|
||||
"^https?://bastion\\.",
|
||||
@@ -49,7 +49,7 @@ exclude = [
|
||||
"^https?://example\\.com",
|
||||
"^https?://example\\.org",
|
||||
# Приватный репозиторий проекта (404 для анонимных запросов — это норма)
|
||||
"^https?://github\\.com/CoralMinister/lidpotok"
|
||||
"^https?://github\\.com/CoralMinister/liderra"
|
||||
]
|
||||
|
||||
# Игнорировать файлы вне аудита
|
||||
@@ -60,7 +60,7 @@ exclude_path = [
|
||||
]
|
||||
|
||||
# User-Agent — некоторые сайты режут пустой
|
||||
user_agent = "lychee/lidpotok"
|
||||
user_agent = "lychee/liderra"
|
||||
|
||||
# Не падать, если в файле нет ссылок
|
||||
no_progress = false
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
},
|
||||
"comment": "Фаза 0 #3 — официальный hosted GitHub MCP (https://github.com/github/github-mcp-server). Требует env GITHUB_TOKEN с PAT (scopes: repo, read:org, не давать admin/delete). Раньше использовали deprecated @modelcontextprotocol/server-github — заменён 06.05.2026."
|
||||
},
|
||||
"_comment_postgres": "PostgreSQL MCP временно убран. Когда поднимется локальная dev-БД — добавить блок 'postgres' с command='npx', args=['-y', '@modelcontextprotocol/server-postgres', 'postgresql://localhost:5432/lidpotok_dev']. В фазе 1 — заменяется Laravel Boost (см. Tooling §3.1)."
|
||||
"_comment_postgres": "PostgreSQL MCP временно убран. Когда поднимется локальная dev-БД — добавить блок 'postgres' с command='npx', args=['-y', '@modelcontextprotocol/server-postgres', 'postgresql://localhost:5432/liderra_dev']. В фазе 1 — заменяется Laravel Boost (см. Tooling §3.1)."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# CLAUDE.md — техконтекст Лидпотока
|
||||
# CLAUDE.md — техконтекст Лидерры
|
||||
|
||||
**Версия:** 1.0 от 06.05.2026
|
||||
**Версия:** 1.1 от 08.05.2026
|
||||
**Назначение:** оперативная карта для Claude Code. Не первоисточник — первоисточники указаны в §0.
|
||||
|
||||
> **Ребрендинг 08.05.2026:** «Лидпоток» → **«Лидерра.»** (с точкой). Палитра, лого и шрифты — из handoff Платона (v8 Forest). Применяется только к дизайну/имени/логотипу; функционал, состав страниц и правила — без изменений (источник — ТЗ v8.5/schema v8.5).
|
||||
|
||||
---
|
||||
|
||||
## 0. Источник истины
|
||||
@@ -14,9 +16,11 @@
|
||||
| Главное ТЗ | [docs/CRM_bp-gr_Инструкция_v8_5.md](docs/CRM_bp-gr_Инструкция_v8_5.md) (v8.5 от 07.05.2026 — реализация 27 решений аудита C) |
|
||||
| Схема БД | [db/schema.sql](db/schema.sql) (v8.5 от 07.05.2026 — реализация 27 решений аудита C, narrative v8.5 готовится) |
|
||||
| Открытые вопросы | [docs/Открытые_вопросы_v8_3.md](docs/Открытые_вопросы_v8_3.md) (v1.12+) |
|
||||
| Брендбук | [docs/brandbook.md](docs/brandbook.md) (v1.1) |
|
||||
| **Брендбук** | [liderra_v8_handoff/docs/BRANDBOOK_v2.md](liderra_v8_handoff/docs/BRANDBOOK_v2.md) **(v2 Forest от 07.05.2026)** — старый `docs/brandbook.md` v1.1 удалён 08.05.2026 |
|
||||
| **Дизайн-handoff (токены, компоненты, 25 экранов)** | [liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md](liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md) (v8 Forest от 07.05.2026) — **только дизайн/токены/компоненты**; функционал и состав экранов — по ТЗ v8.5 |
|
||||
| Анализ оригинала | [docs/Analiz_originala_v8_3.md](docs/Analiz_originala_v8_3.md) (Прил. М v1.1) |
|
||||
| Состав архива | [docs/README_АРХИВ_v8_4.md](docs/README_АРХИВ_v8_4.md) (v8.4 от 06.05.2026) |
|
||||
| ТЗ рекламного лендинга | [лендинг/TZ_landing_v1_0.md](лендинг/TZ_landing_v1_0.md) (v1.0 от 08.05.2026, ⏸ Б-1 для продакшена) |
|
||||
| Состав архива | [docs/README_АРХИВ_v8_5.md](docs/README_АРХИВ_v8_5.md) (v8.5 от 07.05.2026) |
|
||||
|
||||
Этот файл — **оперативная карта**. При противоречии — приоритет у источников выше.
|
||||
|
||||
@@ -55,7 +59,7 @@
|
||||
| Sentry | self-hosted в Yandex Cloud |
|
||||
| Helpdesk | JivoSite |
|
||||
|
||||
**Шрифты:** Inter (UI), JetBrains Mono (код). **Палитра:** Teal (`#0F6E56` primary). **A11y:** WCAG 2.1 AA.
|
||||
**Шрифты:** Inter (UI, axis `opsz` 14..32), JetBrains Mono (numerics с `tnum`, код). **Иконки:** Lucide. **Палитра v8 Forest:** Teal `#0F6E56` (primary, неоспариваемый), `#F6F3EC` warm ivory (page bg), `#012019` теало-нуар (sidebar). 14 OKLCH-статусов в [BRANDBOOK_v2 §3.6](liderra_v8_handoff/docs/BRANDBOOK_v2.md) — **палитра используется**, но мапить на 14 slug'ов из [db/schema.sql:2076](db/schema.sql#L2076) (источник истины для статусов воронки — schema/ТЗ §6.4, не handoff). **A11y:** WCAG 2.1 AA.
|
||||
|
||||
---
|
||||
|
||||
@@ -151,7 +155,7 @@ npm run story # Histoire
|
||||
|
||||
# Pre-prod (фаза 3+)
|
||||
npm run sast # Semgrep
|
||||
trivy image lidpotok:latest
|
||||
trivy image liderra:latest
|
||||
```
|
||||
|
||||
---
|
||||
@@ -163,7 +167,7 @@ trivy image lidpotok:latest
|
||||
3. **Не запускать a11y через Lighthouse** — единственный источник истины Pa11y.
|
||||
4. **Не использовать Superpowers `brainstorming` / `writing-plans` / `executing-plans` / `dispatching-parallel-agents` / `using-git-worktrees`** — конфликт с правилами проекта (§4.5, §8.4, §2.2) или нестабильно на Windows.
|
||||
5. **Не помещать ПДн / токены / API-ключи в коммиты.** Правило §5.2 правил Claude. Защита — gitleaks в pre-commit.
|
||||
6. **Не использовать Frontend Design plugin** — anti-pattern «Inter» противоречит брендбуку §4.1; не знает Vuetify; нет a11y.
|
||||
6. **Не использовать Frontend Design plugin** — не знает Vuetify; нет a11y. (Замечание про anti-pattern «Inter» снято: в Forest Inter — наш основной UI-шрифт, см. [BRANDBOOK_v2 §4.1](liderra_v8_handoff/docs/BRANDBOOK_v2.md).)
|
||||
7. **Не ставить два инструмента на одну задачу** — список 10+ запрещённых дублей в [docs/Tooling_v8_3.md](docs/Tooling_v8_3.md) §9.
|
||||
8. **Не редактировать этот `CLAUDE.md` без обновления** [docs/Pravila_raboty_Claude_v1_1.md](docs/Pravila_raboty_Claude_v1_1.md) и [docs/Tooling_v8_3.md](docs/Tooling_v8_3.md) — иначе три источника разойдутся.
|
||||
9. **Не править `db/schema.sql`** без записи в [db/CHANGELOG_schema.md](db/CHANGELOG_schema.md) — правило §4.2 правил Claude.
|
||||
@@ -176,8 +180,8 @@ trivy image lidpotok:latest
|
||||
**Фаза 0 — документация + HTML-прототипы.**
|
||||
|
||||
- Активно: 9 инструментов из 28 (см. §3.1).
|
||||
- Готово: 17 файлов архива (narrative v8.4 финал 06.05.2026), 3 из 8 HTML-прототипов ([web/01-login.html](web/01-login.html), [web/02-dashboard.html](web/02-dashboard.html), [web/03-deals.html](web/03-deals.html)).
|
||||
- Триггер фазы 1: `composer create-project laravel/laravel app`.
|
||||
- Готово: 17 файлов архива (narrative v8.5 финал 07.05.2026), **13 концептов v8 Forest в [web/v8/](web/v8/)** (login/dashboard/deals/deal_card/kanban/billing/settings/reports/errors/admin/landing/brand/palette_options) — производные от [liderra_v8_handoff/concepts/](liderra_v8_handoff/concepts/).
|
||||
- Триггер фазы 1: `composer create-project laravel/liderra app`.
|
||||
- Триггер фазы 2: первый коммит в `resources/js/` или Vue-папку.
|
||||
- Триггер фазы 3: ~спринт 12.
|
||||
|
||||
@@ -213,4 +217,4 @@ trivy image lidpotok:latest
|
||||
|
||||
---
|
||||
|
||||
*CLAUDE.md v1.0 от 06.05.2026. При обновлении — синхронно править [docs/Tooling_v8_3.md](docs/Tooling_v8_3.md) и [docs/Pravila_raboty_Claude_v1_1.md](docs/Pravila_raboty_Claude_v1_1.md).*
|
||||
*CLAUDE.md v1.1 от 08.05.2026. Изменения v1.1: ребрендинг Лидпоток→Лидерра; brandbook v1.1 удалён, источник — BRANDBOOK_v2.md из handoff Платона; добавлен handoff в §0; §2 палитра Forest; §6 — 13 концептов в web/v8/.*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Прил. Л — HTML-прототипы Лидпоток
|
||||
# Прил. Л — HTML-прототипы Лидерра
|
||||
|
||||
Самодостаточные HTML-прототипы 8 ключевых экранов клиентского приложения и админки SaaS. Это не боевой код — это **референс для дизайнера** (Диз-1) и **рабочая заготовка для frontend-команды** в спринтах 1, 4, 5, 8, 14.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
lidpotok/
|
||||
liderra/
|
||||
├── README.md ← этот файл (про прототипы)
|
||||
├── CLAUDE.md ← оперативная карта для Claude Code (приоритет правил, 28 инструментов)
|
||||
├── docs/ ← документация, инструкции, аудиты, брендбук, Прил. Н (tooling)
|
||||
@@ -91,4 +91,4 @@ lidpotok/
|
||||
|
||||
## Репозиторий
|
||||
|
||||
https://github.com/CoralMinister/lidpotok (приватный)
|
||||
https://github.com/CoralMinister/liderra (приватный)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Глоссарий проекта Лидпоток
|
||||
# Глоссарий проекта Лидерра
|
||||
# Формат: одно слово на строке. Кириллица в нижнем регистре.
|
||||
|
||||
# Бренд и термины проекта
|
||||
лидпоток
|
||||
lidpotok
|
||||
лидерра
|
||||
liderra
|
||||
бэкап
|
||||
бэкапы
|
||||
бэкапов
|
||||
@@ -694,3 +694,14 @@ gethostbyname
|
||||
KDV
|
||||
коммита
|
||||
коммитов
|
||||
|
||||
# Лендинг (TZ_landing_v1_0)
|
||||
SEO
|
||||
продакшен
|
||||
продакшена
|
||||
финализируются
|
||||
лидогенерации
|
||||
лендингу
|
||||
Habr
|
||||
КПИ
|
||||
Лидерры
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
],
|
||||
"dictionaryDefinitions": [
|
||||
{
|
||||
"name": "lidpotok-glossary",
|
||||
"name": "liderra-glossary",
|
||||
"path": "./cspell-words.txt",
|
||||
"addWords": true
|
||||
}
|
||||
],
|
||||
"dictionaries": [
|
||||
"lidpotok-glossary"
|
||||
"liderra-glossary"
|
||||
],
|
||||
"ignorePaths": [
|
||||
"node_modules/**",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# CHANGELOG schema.sql — Лидпоток
|
||||
# CHANGELOG schema.sql — Лидерра
|
||||
|
||||
**Назначение:** консолидированный журнал изменений `schema.sql`. Содержит четыре записи в обратном хронологическом порядке (v8.5 → v8.4 → v8.3 → v8.2), как принято в keep-a-changelog.
|
||||
|
||||
@@ -837,7 +837,7 @@ App\Events\Incident\Resolved
|
||||
|
||||
```bash
|
||||
# 1. Создать БД
|
||||
createdb -E UTF8 lidpotok
|
||||
createdb -E UTF8 liderra
|
||||
|
||||
# 2. Применить консолидированную schema.sql v8.2
|
||||
psql $DB_URL -f schema.sql
|
||||
@@ -867,7 +867,7 @@ psql $DB_URL -c "SELECT COUNT(*) FROM tariff_plans WHERE is_active = TRUE;"
|
||||
pg_dump --data-only $DB_URL > data-backup-$(date +%Y%m%d).sql
|
||||
|
||||
# 2. Drop & recreate
|
||||
dropdb lidpotok && createdb -E UTF8 lidpotok
|
||||
dropdb liderra && createdb -E UTF8 liderra
|
||||
psql $DB_URL -f schema.sql
|
||||
|
||||
# 3. Восстановить нужные данные (если есть)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- =============================================================================
|
||||
-- schema.sql — единая схема БД для SaaS-аналога crm.bp-gr.ru («Лидпоток»)
|
||||
-- schema.sql — единая схема БД для SaaS-аналога crm.bp-gr.ru («Лидерра»)
|
||||
-- Версия: v8.5 (07.05.2026, реализация 27 решений аудита C из реестра v1.12)
|
||||
-- Базовая версия: v8.4 (06.05.2026, синхронизация с narrative §19.10 outbound webhook)
|
||||
-- СУБД: PostgreSQL 16
|
||||
@@ -1051,7 +1051,7 @@ CREATE TABLE outbound_webhook_deliveries (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
subscription_id BIGINT NOT NULL REFERENCES outbound_webhook_subscriptions(id) ON DELETE CASCADE,
|
||||
delivery_uuid UUID NOT NULL, -- X-Lidpotok-Delivery, выдаётся 1 раз на событие
|
||||
delivery_uuid UUID NOT NULL, -- X-Liderra-Delivery, выдаётся 1 раз на событие
|
||||
event VARCHAR(50) NOT NULL, -- deal.status_changed и т. п.
|
||||
payload JSONB NOT NULL, -- тело отправляемого запроса
|
||||
attempt_number SMALLINT NOT NULL DEFAULT 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Приложение М — Анализ оригинала crm.bp-gr.ru и архитектурные следствия для Лидпотока (v8.3)
|
||||
# Приложение М — Анализ оригинала crm.bp-gr.ru и архитектурные следствия для Лидерры (v8.3)
|
||||
|
||||
**Дата:** 05.05.2026 (расширено по итогам партий 12–15).
|
||||
**Версия:** 1.1 (расширено по итогам параллельного аудита партий 12–15 от 05.05.2026).
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
> **⚠ Состояние на 05.05.2026 (v8.3++ optimized):** 4 файла `audit-batch-12..15-2026-05-05.md` объединены в единый документ **`Аудит_partii_12_15_originala_v8_3.md`** (части 12, 13, 14, 15). Содержимое полностью сохранено без изменений. Все ссылки в данном Прил. М вида «партия 12.2.4», «партия 15.3» и т. п. читать с учётом нового размещения: соответствующая «Часть N, раздел X.Y» в `Аудит_partii_12_15_originala_v8_3.md`.
|
||||
|
||||
**Назначение:** перевести наблюдения аудита в формальные архитектурные решения для платформы Лидпоток. Закрыть пробелы между нашей моделью v8.3 и реальной структурой оригинала. Поднять перед заказчиком новые продуктовые вопросы, возникшие из аудита.
|
||||
**Назначение:** перевести наблюдения аудита в формальные архитектурные решения для платформы Лидерра. Закрыть пробелы между нашей моделью v8.3 и реальной структурой оригинала. Поднять перед заказчиком новые продуктовые вопросы, возникшие из аудита.
|
||||
|
||||
**История изменений:**
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
## 0. TL;DR
|
||||
|
||||
Аудит crm.bp-gr.ru, проведённый в 15 партий за 04.05–05.05.2026 (11 исходных + 4 параллельные), подтвердил архитектурные решения Лидпотока, обнаружил расхождения и выявил новые продуктовые вопросы.
|
||||
Аудит crm.bp-gr.ru, проведённый в 15 партий за 04.05–05.05.2026 (11 исходных + 4 параллельные), подтвердил архитектурные решения Лидерры, обнаружил расхождения и выявил новые продуктовые вопросы.
|
||||
|
||||
**Главное расхождение №1:** в оригинале существует архитектурный слой **«Поставщик» (B1/B2/B3)** с **разными capabilities** для каждого (B1 — звонки/сайты, B2 — sms+sender_name+keyword, B3 — sms+только sender_name). У нас этого слоя не было — нужна таблица `suppliers` с полями `channel`, `supports_sender_name`, `supports_keyword` + связь `project_suppliers`.
|
||||
|
||||
@@ -1129,14 +1129,14 @@ GET/POST https://crm.bp-gr.ru/api/...
|
||||
|
||||
**Контекст:** в оригинале (партия 13.1) есть полноценная фича `/admin/gck/dop-sources` — wizard на 3 шага: «Поиск (по доменам/ключевикам конкурентов) → Результат (рекомендованные домены/телефоны) → Чёрный список». Это **OSINT-инструмент** для самостоятельного поиска новых источников трафика клиентом — отдельная сущность, не связана с управлением поставщиками B1/B2/B3.
|
||||
|
||||
**Вопрос:** делаем такой wizard в Лидпотоке на MVP?
|
||||
**Вопрос:** делаем такой wizard в Лидерре на MVP?
|
||||
|
||||
**Рекомендация Claude:** **не делаем на MVP**, в Post-MVP по запросу. Аргументы:
|
||||
|
||||
- Бизнес-ценность спорна: для арбитражной модели «получи лиды от B1/B2/B3» эта фича — «помощник по конкурентам», не основной flow.
|
||||
- Реализация требует внешнего OSINT API (поиск конкурирующих доменов и связанных телефонов), затраты ~3 спринта.
|
||||
- Если делать — отдельные таблицы: `source_recommendation_searches`, `source_recommendation_results`, `source_recommendation_blacklist` (см. партию 13.1.6).
|
||||
- На старте Лидпотока эту функцию можно оставить за скобками без потери основных пользователей.
|
||||
- На старте Лидерры эту функцию можно оставить за скобками без потери основных пользователей.
|
||||
|
||||
**Дефолт при отсутствии решения:** не делаем на MVP, ставим в backlog с приоритетом P3.
|
||||
|
||||
@@ -1146,11 +1146,11 @@ GET/POST https://crm.bp-gr.ru/api/...
|
||||
|
||||
**Контекст:** в оригинале (партия 13.2.2) есть аккаунтная (не проектная!) модалка «Желаемое кол-во номеров в день». Это не лимит, а ориентир для сотрудников бэк-офиса ГЦК, которые ручным трудом дотягивают объём до нужного.
|
||||
|
||||
**Вопрос:** добавляем такое поле в Лидпотоке?
|
||||
**Вопрос:** добавляем такое поле в Лидерре?
|
||||
|
||||
**Рекомендация Claude:** **да, добавляем** как `tenants.desired_daily_numbers INT NULL`. Аргументы:
|
||||
|
||||
- Полезный сигнал саппорту Лидпотока: видеть, что клиент ожидает 100 лидов/день, а получает 30 — повод связаться с клиентом и помочь оптимизировать настройки проектов.
|
||||
- Полезный сигнал саппорту Лидерры: видеть, что клиент ожидает 100 лидов/день, а получает 30 — повод связаться с клиентом и помочь оптимизировать настройки проектов.
|
||||
- Минимальная реализация: одно поле в `tenants`, одно поле в форме настроек аккаунта, одна колонка в админ-дашборде `/admin/tenants`.
|
||||
- Затраты ~0.1 спринта.
|
||||
|
||||
@@ -1208,7 +1208,7 @@ GET/POST https://crm.bp-gr.ru/api/...
|
||||
|
||||
### 6.5. Урок для нашей платформы
|
||||
|
||||
**Принцип:** наша платформа Лидпоток должна быть **«AI-agent friendly»** в правильном смысле — то есть:
|
||||
**Принцип:** наша платформа Лидерра должна быть **«AI-agent friendly»** в правильном смысле — то есть:
|
||||
|
||||
- ✅ Семантическая разметка, корректные ARIA-атрибуты, осмысленные ID — для legitimate использования AI-агентов клиентов.
|
||||
- ❌ Никаких «AI-маркеров», специально размещённых для одних агентов и не других.
|
||||
@@ -1325,7 +1325,7 @@ GET/POST https://crm.bp-gr.ru/api/...
|
||||
|
||||
**Чего нет:** drill-down в ячейку, экспорт XLSX/CSV.
|
||||
|
||||
**Решение для Лидпотока:** реализовать как **первоклассный отчёт §12 narrative**. На бэке — один read-эндпоинт `GET /api/projects/conversion?from=&to=&type=&search=`. На фронте — Vue-таблица. **Наше расширение сверх паритета:** экспорт XLSX (через `report_jobs`) + drill-down из ячейки в отфильтрованный список сделок.
|
||||
**Решение для Лидерры:** реализовать как **первоклассный отчёт §12 narrative**. На бэке — один read-эндпоинт `GET /api/projects/conversion?from=&to=&type=&search=`. На фронте — Vue-таблица. **Наше расширение сверх паритета:** экспорт XLSX (через `report_jobs`) + drill-down из ячейки в отфильтрованный список сделок.
|
||||
|
||||
#### 9.1.2. Напоминания — модель данных (Биз-10 переоткрыт)
|
||||
|
||||
@@ -1344,7 +1344,7 @@ GET/POST https://crm.bp-gr.ru/api/...
|
||||
|
||||
**Бесплатный «дашборд задач»:** дропдаун «Задачи» в шапке списка содержит 4 пункта с URL-параметрами `?reminders=today|last|future|none`.
|
||||
|
||||
**Решение для Лидпотока:** перепись таблицы `reminders` (см. §3.5.1). Удалить `deals.reminder_text` и `deals.reminder_at`. На MVP — поля паритета + `completed_at` (наше расширение). Расширения над паритетом — **Post-MVP по запросу**.
|
||||
**Решение для Лидерры:** перепись таблицы `reminders` (см. §3.5.1). Удалить `deals.reminder_text` и `deals.reminder_at`. На MVP — поля паритета + `completed_at` (наше расширение). Расширения над паритетом — **Post-MVP по запросу**.
|
||||
|
||||
#### 9.1.3. Модалка «Досье» — две разные сущности
|
||||
|
||||
@@ -1354,7 +1354,7 @@ GET/POST https://crm.bp-gr.ru/api/...
|
||||
|
||||
**Модалка №2 — `visitdop`** (el-dialog с табами): триггер — кнопка «Просмотр досье» в `.domen-right`; содержимое — 2 таба (Досье с полями `proceeds/profit/balance/arbitration` + Pr-cy SEO-данные); эндпоинт `GET /admin/visit/visit-dop-load?id={model.id}`; назначение — внешние данные о домене сделки.
|
||||
|
||||
**Решение для Лидпотока:**
|
||||
**Решение для Лидерры:**
|
||||
|
||||
- Модалка №1 — **Post-MVP** (естественное расширение Биз-1).
|
||||
- Модалка №2 — **НЕ делаем** на MVP (требует интеграции с Pr-cy / Контур, для арбитражной CRM избыточно).
|
||||
@@ -1365,13 +1365,13 @@ GET/POST https://crm.bp-gr.ru/api/...
|
||||
|
||||
OSINT-инструмент по поиску внешних доменов и телефонов на основе ключевых запросов клиента. **НЕ управление поставщиками B1/B2/B3** (гипотеза партии 10 опровергнута). 3-шаговый wizard, аккаунтный скоуп.
|
||||
|
||||
**Решение для Лидпотока:** Биз-15 — на MVP **не делаем**, Post-MVP по запросу.
|
||||
**Решение для Лидерры:** Биз-15 — на MVP **не делаем**, Post-MVP по запросу.
|
||||
|
||||
#### 9.2.2. Аккордеон «Настройки» в шапке реестра проектов
|
||||
|
||||
6 кнопок: вкл/откл проектов, желаемое кол-во номеров, удалённые проекты, выгрузка источников, управление страницей, восстановление/удаление.
|
||||
|
||||
**Решение для Лидпотока:**
|
||||
**Решение для Лидерры:**
|
||||
|
||||
- **Биз-16** → новое поле `tenants.desired_daily_numbers INT NULL` (см. §3.5.3). Сигнал саппорту, отображается в админке SaaS, не влияет на биллинг и `effective_daily_limit`.
|
||||
- **Биз-14** → soft-delete проектов с TTL (см. §3.5.4). Дефолт 6 месяцев + cron disabled.
|
||||
@@ -1387,13 +1387,13 @@ OSINT-инструмент по поиску внешних доменов и т
|
||||
|
||||
> «Указать в связке 'Наименование отправителя' и 'Ключевое слово' можно только по поставщику B2. Поставщик B3 работает только по наименованию отправителя».
|
||||
|
||||
Это требует расширения `suppliers` 5 полями (см. §3.5.2). В UI Лидпотока: при выборе поставщика(ов) frontend показывает только релевантные поля (intersection capabilities).
|
||||
Это требует расширения `suppliers` 5 полями (см. §3.5.2). В UI Лидерры: при выборе поставщика(ов) frontend показывает только релевантные поля (intersection capabilities).
|
||||
|
||||
#### 9.2.5. «Список звонков» — только агрегация
|
||||
|
||||
Раздел `/admin/user/moizvonki` показывает агрегацию по менеджерам. **Сквозного журнала отдельных звонков нет.** Drill-down не предоставлен.
|
||||
|
||||
**Решение для Лидпотока:** подтверждает Биз-12 — телефонию не делаем на MVP. Звонки конкретной сделки — только в карточке сделки.
|
||||
**Решение для Лидерры:** подтверждает Биз-12 — телефонию не делаем на MVP. Звонки конкретной сделки — только в карточке сделки.
|
||||
|
||||
### 9.3. Партия 14 — Редкие статусы + KB + Безопасность профиля
|
||||
|
||||
@@ -1401,13 +1401,13 @@ OSINT-инструмент по поиску внешних доменов и т
|
||||
|
||||
Эмпирически проверить не удалось (на текущем аккаунте 0 сделок во всех 14 статусах). Косвенно подтверждено через DOM фильтра — единый плоский список из 14 статусов без условных подразделов.
|
||||
|
||||
**Решение для Лидпотока:** одна универсальная карточка сделки, без условных полей по статусу.
|
||||
**Решение для Лидерры:** одна универсальная карточка сделки, без условных полей по статусу.
|
||||
|
||||
#### 9.3.2. База знаний — внешняя (HelpDeskEddy)
|
||||
|
||||
Встроенной KB в CRM **нет**. В левом меню «База Знаний» — это `<a href>` на `data.helpdeskeddy.com`. Чат-виджет — `<iframe>` HelpDeskEddy.
|
||||
|
||||
**Решение для Лидпотока:** на MVP линкуем «База знаний» на наш JivoSite KB / Notion. Встроенную KB не делаем. Чат — JivoSite (Биз-5 ✅).
|
||||
**Решение для Лидерры:** на MVP линкуем «База знаний» на наш JivoSite KB / Notion. Встроенную KB не делаем. Чат — JivoSite (Биз-5 ✅).
|
||||
|
||||
#### 9.3.3. Безопасность профиля оригинала
|
||||
|
||||
@@ -1422,12 +1422,12 @@ OSINT-инструмент по поиску внешних доменов и т
|
||||
|
||||
**Формат «Тихих часов» оригинала** — `start_hour 0..23` + `end_hour 0..23` + общий `timezone_id` + минимум 3 часа.
|
||||
|
||||
**Решение для Лидпотока:**
|
||||
**Решение для Лидерры:**
|
||||
|
||||
- `users.notification_preferences.quiet_hours` — `{enabled, from_hour: 0..23, to_hour: 0..23}` + привязка к `users.timezone`. Минимум 3 часа интервал (паритет, server-side check).
|
||||
- **Расширения сверх паритета** (Post-MVP): минуты (HH:MM), per-channel quiet hours, исключения по дням недели.
|
||||
|
||||
**Дополнительный экран профиля Лидпотока «Безопасность»** (отдельная вкладка) — наше конкурентное преимущество над оригиналом:
|
||||
**Дополнительный экран профиля Лидерры «Безопасность»** (отдельная вкладка) — наше конкурентное преимущество над оригиналом:
|
||||
|
||||
- Раздел «Пароль» — отдельная защищённая форма old/new/confirm.
|
||||
- Раздел «Двухфакторная аутентификация» — TOTP setup, recovery codes, отключение.
|
||||
@@ -1463,7 +1463,7 @@ OSINT-инструмент по поиску внешних доменов и т
|
||||
|
||||
**Архитектурный паттерн всех интеграций оригинала:** outbound API-client. CRM аутентифицируется в провайдере по credentials и пушит данные через provider-specific REST.
|
||||
|
||||
**Решение для Лидпотока:** наш Уровень 1 (outbound webhook на MVP, OPEN-И-2) — окончательно **конкурентное преимущество**. Маркетинг: «отдаём лиды в любую внешнюю систему по webhook'у — у конкурентов нет».
|
||||
**Решение для Лидерры:** наш Уровень 1 (outbound webhook на MVP, OPEN-И-2) — окончательно **конкурентное преимущество**. Маркетинг: «отдаём лиды в любую внешнюю систему по webhook'у — у конкурентов нет».
|
||||
|
||||
#### 9.4.3. Структура карточек интеграций (для §4.5 narrative)
|
||||
|
||||
@@ -1471,7 +1471,7 @@ OSINT-инструмент по поиску внешних доменов и т
|
||||
|
||||
**🔴 Уязвимости оригинала (партия 15.2):** все credentials хранятся в `<input type="text">`, не `password`. Видны на экране.
|
||||
|
||||
**Решение для Лидпотока:** при реализации интеграций (Уровень 2 — amoCRM в спринте 14–15) использовать ту же структуру с двумя обязательными отличиями:
|
||||
**Решение для Лидерры:** при реализации интеграций (Уровень 2 — amoCRM в спринте 14–15) использовать ту же структуру с двумя обязательными отличиями:
|
||||
|
||||
1. Все credentials — masked/password type + toggle «👁 Показать».
|
||||
2. **Outbound webhook fallback**: даже для коннекторов с провайдер-API оставить опцию «отдавать в webhook вместо провайдер-API».
|
||||
@@ -1490,7 +1490,7 @@ OSINT-инструмент по поиску внешних доменов и т
|
||||
|
||||
**Контекст партии 13.1:** 3-шаговый wizard для поиска новых доменов/телефонов конкурентов.
|
||||
|
||||
**Рекомендация Claude:** **На MVP НЕ делаем**, Post-MVP по запросу. Целевая аудитория Лидпотока — арбитражники с готовыми источниками.
|
||||
**Рекомендация Claude:** **На MVP НЕ делаем**, Post-MVP по запросу. Целевая аудитория Лидерры — арбитражники с готовыми источниками.
|
||||
|
||||
**Дефолт при отсутствии решения:** не делаем на MVP.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Лидпоток — Полная техническая и функциональная документация (v8.5)
|
||||
# Лидерра — Полная техническая и функциональная документация (v8.5)
|
||||
|
||||
> **Версия:** 8.5 от 07.05.2026 (реализация 27 решений аудита C из реестра v1.12).
|
||||
> **Базовая версия:** 8.4 от 06.05.2026 (финал, все 13 разделов плана переписаны).
|
||||
> **Рабочее название продукта:** **Лидпоток**.
|
||||
> **Рабочее название продукта:** **Лидерра**.
|
||||
> **Статус:** Полное ТЗ для разработки SaaS-платформы. **68 продуктовых решений зафиксировано** (30 в v8.3 + 11 в v1.10 + 27 в v1.12). См. блок «Что нового в v8.5» ниже.
|
||||
> **Готовность:** 100% по архитектурным решениям; брендинг готов (см. brandbook.md v1.1). Все P2 закрыты повторно (v1.12). Истинных P0-блокеров остался **1**: Б-1 (реквизиты юр. лица, ждут ООО). **8 P0 аудита C закрыты в v1.12** — триггер фазы 1 (`composer create-project`) разблокирован архитектурно.
|
||||
> **Структура:** этот файл — основной narrative + **11 приложений** + **brandbook.md v1.1** + **Прил. Н (Tooling_v8_3.md)** + **корневой CLAUDE.md** + **дизайн-бриф для дизайнера** (см. раздел 28).
|
||||
@@ -137,14 +137,14 @@
|
||||
|
||||
### Дизайн / маркетинг
|
||||
|
||||
- **Диз-2 ✅:** название платформы — **Лидпоток**. Логотип «Поток» (3 круга в бирюзовой палитре). Брендбук v1.0 — отдельный файл `brandbook.md`. Доменное имя — **lidpotok.ru** (на регистрацию после Б-1).
|
||||
- **Диз-2 ✅:** название платформы — **Лидерра**. Логотип «Поток» (3 круга в бирюзовой палитре). Брендбук v1.0 — отдельный файл `brandbook.md`. Доменное имя — **liderra.ru** (на регистрацию после Б-1).
|
||||
- **Диз-1 🔧:** HTML/CSS/JS прототипы 8 экранов — в работе у Claude (Приложение Л будет готово отдельной сессией).
|
||||
- **Диз-3 ⏸:** контакты подвала — после Б-1.
|
||||
- **Диз-4 ✅:** контент лендинга и FAQ — **«Lorem ipsum-стиль»** на MVP, реальный контент к запуску от маркетолога.
|
||||
|
||||
### DevOps / эксплуатация
|
||||
|
||||
- **DO-2 ⏸:** домен и wildcard SSL — `lidpotok.ru` после Б-1.
|
||||
- **DO-2 ⏸:** домен и wildcard SSL — `liderra.ru` после Б-1.
|
||||
- **DO-3 ✅:** **IP allow-list для админки SaaS — НЕТ**, вместо него **5 компенсирующих мер**: (1) rate-limit на /admin/login (5 попыток / 15 мин с IP), (2) captcha после 2 неудач, (3) email-уведомление админу при входе с нового устройства/IP, (4) `auth_log` со всеми попытками, (5) Sentry/Slack-алерт при подозрительной активности (>20 неудач/час с разных IP).
|
||||
- **DO-4 ⏸:** список сотрудников SaaS с ролями — после Б-1.
|
||||
- **DO-5 ✅:** **SSO для админов SaaS — Yandex 360** (нативно к Yandex Cloud).
|
||||
@@ -168,7 +168,7 @@
|
||||
|---|---|---|
|
||||
| **P0** | **Б-1** — реквизиты юр. лица | регистрация ООО в процессе |
|
||||
| P1 | Диз-3 — контакты подвала | после Б-1 |
|
||||
| P1 | DO-2 — покупка домена `lidpotok.ru` + SSL | после Б-1 |
|
||||
| P1 | DO-2 — покупка домена `liderra.ru` + SSL | после Б-1 |
|
||||
| P1 | DO-4 — список сотрудников SaaS с ролями | после Б-1 |
|
||||
| Юристу | редактура Прил. Ж, Прил. З; согласование DPA с Yandex; зеркальная гарантия по Ю-8 | независимо |
|
||||
|
||||
@@ -347,7 +347,7 @@
|
||||
|
||||
## 1.4.1. Конкурентные преимущества vs оригинал crm.bp-gr.ru
|
||||
|
||||
По итогам аудита оригинала (15 партий, 04–05.05.2026, Прил. М) зафиксировано **7 функциональных преимуществ** Лидпотока:
|
||||
По итогам аудита оригинала (15 партий, 04–05.05.2026, Прил. М) зафиксировано **7 функциональных преимуществ** Лидерры:
|
||||
|
||||
1. **Outbound webhook к тенанту** — оригинал только принимает webhook от поставщиков; у нас — двусторонний (тенант может настроить outbound в свою CRM). 7 линий доказательств отсутствия в оригинале (Прил. М §13, партия 15).
|
||||
2. **Полный аудит мутаций сделки** — оригинал не логирует смену ответственного / проекта / телефона / суммы; у нас `activity_log` фиксирует все изменения (раздел 14, Прил. М §11.4 п.2, партия 12).
|
||||
@@ -357,11 +357,11 @@
|
||||
6. **REST API для CRM-интеграций** — у оригинала нет; у нас amoCRM-коннектор в спринте 14–15, Bitrix24/RetailCRM Post-MVP (OPEN-И-2).
|
||||
7. **Безопасность аккаунта (security-differentiator):** 2FA на всех тарифах (Биз-9), журнал входов (`auth_log`), список активных сессий (`user_sessions`), recovery codes (`user_recovery_codes`) — у оригинала **ничего из этого нет** (Прил. М §2.19, партия 14).
|
||||
|
||||
Эти преимущества — основа маркетингового позиционирования Лидпотока.
|
||||
Эти преимущества — основа маркетингового позиционирования Лидерры.
|
||||
|
||||
## 1.5. Как клиент попадает в систему
|
||||
|
||||
1. Открывает `https://lidpotok.ru/register` (домен после Б-1)
|
||||
1. Открывает `https://liderra.ru/register` (домен после Б-1)
|
||||
2. Заполняет форму: email, пароль, **3 чекбокса click-wrap** — оферта / Политика конфиденциальности / согласие на обработку ПДн (OPEN-Ж-3)
|
||||
3. Подтверждает email через ссылку (24 ч TTL)
|
||||
4. Автоматически создаётся **тенант** + **стартовый бонус 50 лидов**
|
||||
@@ -493,7 +493,7 @@
|
||||
## 2.4. Поток данных: от Webhook до сделки
|
||||
|
||||
```
|
||||
1. crm.bp-gr.ru → POST https://lidpotok.ru/webhook/{tenant_token}
|
||||
1. crm.bp-gr.ru → POST https://liderra.ru/webhook/{tenant_token}
|
||||
Content-Type: application/json
|
||||
Body: {vid, project, tag, phone, phones, time}
|
||||
|
||||
@@ -611,7 +611,7 @@ CREATE POLICY tenant_isolation ON deals
|
||||
Тенант определяется по поддомену, который кладётся в middleware:
|
||||
|
||||
```
|
||||
client1.lidpotok.ru → TenantResolveMiddleware → SELECT * FROM tenants WHERE subdomain = 'client1' → TenantContext::set($tenant)
|
||||
client1.liderra.ru → TenantResolveMiddleware → SELECT * FROM tenants WHERE subdomain = 'client1' → TenantContext::set($tenant)
|
||||
```
|
||||
|
||||
Кеш разрешения: `tenants:by_subdomain:{subdomain}` в Redis с TTL 5 минут.
|
||||
@@ -703,7 +703,7 @@ INSERT INTO system_settings (key, value, type, description) VALUES
|
||||
|
||||
### Шаг 1. Открытая регистрация
|
||||
|
||||
URL: `https://lidpotok.ru/register`
|
||||
URL: `https://liderra.ru/register`
|
||||
|
||||
**Форма:**
|
||||
|
||||
@@ -730,7 +730,7 @@ URL: `https://lidpotok.ru/register`
|
||||
1. Создаётся запись в `tenants` со статусом `pending_email_confirm`
|
||||
2. Создаётся запись в `users` с привязкой к `tenant_id` (роль = `admin`, единственный пользователь тенанта)
|
||||
3. Генерируется `email_verification_token`, хранится в `email_verifications`
|
||||
4. Отправляется письмо со ссылкой `https://lidpotok.ru/verify-email/{token}` (TTL 24 часа)
|
||||
4. Отправляется письмо со ссылкой `https://liderra.ru/verify-email/{token}` (TTL 24 часа)
|
||||
|
||||
### Шаг 3. Активация
|
||||
|
||||
@@ -740,7 +740,7 @@ URL: `https://lidpotok.ru/register`
|
||||
2. `tenants.status` → `active`
|
||||
3. **Начисляется стартовый бонус**: `tenants.balance_leads += system_settings.trial_bonus_leads` (по умолчанию 50)
|
||||
4. Создаётся запись в `balance_transactions`: тип `trial_bonus`, +50 лидов
|
||||
5. Клиент перенаправляется на `https://{subdomain}.lidpotok.ru/onboarding`
|
||||
5. Клиент перенаправляется на `https://{subdomain}.liderra.ru/onboarding`
|
||||
|
||||
### Шаг 4. Онбординг (3 шага мастера)
|
||||
|
||||
@@ -748,7 +748,7 @@ URL: `https://lidpotok.ru/register`
|
||||
|
||||
Показываем клиенту:
|
||||
|
||||
- Его уникальный URL Webhook: `https://lidpotok.ru/webhook/{webhook_token}`
|
||||
- Его уникальный URL Webhook: `https://liderra.ru/webhook/{webhook_token}`
|
||||
- Инструкцию: «Перейдите в crm.bp-gr.ru → /admin/user/api → вставьте URL → активируйте»
|
||||
- Кнопку «Тест Webhook» (отправляет тестовый запрос на наш приёмник, чтобы клиент убедился, что всё работает)
|
||||
|
||||
@@ -832,7 +832,7 @@ URL: `https://lidpotok.ru/register`
|
||||
|
||||
## 5.1. Принцип
|
||||
|
||||
**Лидпоток не собирает лиды самостоятельно** и не интегрируется с поставщиками B1/B2/B3 (см. §3.5 о структуре поставщиков в crm.bp-gr.ru). У нас три канала ingress:
|
||||
**Лидерра не собирает лиды самостоятельно** и не интегрируется с поставщиками B1/B2/B3 (см. §3.5 о структуре поставщиков в crm.bp-gr.ru). У нас три канала ingress:
|
||||
|
||||
| Канал | Доля трафика на MVP | Описание | Раздел |
|
||||
|---|---|---|---|
|
||||
@@ -846,18 +846,18 @@ URL: `https://lidpotok.ru/register`
|
||||
|
||||
1. Регистрируется в нашем аналоге → получает уникальный URL Webhook
|
||||
2. Идёт в свой crm.bp-gr.ru → /admin/user/api → вставляет URL → активирует
|
||||
3. Дальше каждый новый лид в crm.bp-gr.ru автоматически дублируется в Лидпоток
|
||||
3. Дальше каждый новый лид в crm.bp-gr.ru автоматически дублируется в Лидерра
|
||||
|
||||
Один тенант = одно подключение к одному аккаунту crm.bp-gr.ru.
|
||||
|
||||
**Исходящие события (outbound webhook) → уникальное преимущество.** В дополнение к **приёму** webhook от crm.bp-gr.ru, Лидпоток умеет **отправлять** webhook во внешние системы тенанта (CRM клиента, n8n, Zapier, корпоративный data-pipeline). Подробно в [§19.10 Outbound webhook](#1910-outbound-webhook-подписка-внешних-систем-на-события). Это конкурентное преимущество №1 (см. [§1.4.1](#141-конкурентные-преимущества-vs-оригинал-crmbp-grru)) — в оригинале outbound webhook subsystem отсутствует, что подтверждено 7 независимыми линиями доказательств (см. [Прил. М §9.4.2](Analiz_originala_v8_3.md), партии 9–15).
|
||||
**Исходящие события (outbound webhook) → уникальное преимущество.** В дополнение к **приёму** webhook от crm.bp-gr.ru, Лидерра умеет **отправлять** webhook во внешние системы тенанта (CRM клиента, n8n, Zapier, корпоративный data-pipeline). Подробно в [§19.10 Outbound webhook](#1910-outbound-webhook-подписка-внешних-систем-на-события). Это конкурентное преимущество №1 (см. [§1.4.1](#141-конкурентные-преимущества-vs-оригинал-crmbp-grru)) — в оригинале outbound webhook subsystem отсутствует, что подтверждено 7 независимыми линиями доказательств (см. [Прил. М §9.4.2](Analiz_originala_v8_3.md), партии 9–15).
|
||||
|
||||
## 5.2. Формат Webhook (вход)
|
||||
|
||||
**HTTP:**
|
||||
|
||||
```
|
||||
POST https://lidpotok.ru/webhook/{tenant_webhook_token} HTTP/1.1
|
||||
POST https://liderra.ru/webhook/{tenant_webhook_token} HTTP/1.1
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
@@ -1129,7 +1129,7 @@ CREATE INDEX idx_rejected_tenant_created ON rejected_deals_log(tenant_id, create
|
||||
Для разработчиков — `curl`:
|
||||
|
||||
```bash
|
||||
curl -X POST https://lidpotok.ru/webhook/abcdef1234567890... \
|
||||
curl -X POST https://liderra.ru/webhook/abcdef1234567890... \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"vid": 432176649,
|
||||
@@ -1619,7 +1619,7 @@ CREATE TABLE outbound_webhook_deliveries (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
subscription_id BIGINT NOT NULL REFERENCES outbound_webhook_subscriptions(id) ON DELETE CASCADE,
|
||||
delivery_uuid UUID NOT NULL, -- X-Lidpotok-Delivery
|
||||
delivery_uuid UUID NOT NULL, -- X-Liderra-Delivery
|
||||
event VARCHAR(50) NOT NULL,
|
||||
payload JSONB NOT NULL,
|
||||
attempt_number SMALLINT NOT NULL DEFAULT 1
|
||||
@@ -1862,7 +1862,7 @@ URL: `/projects/{id}` или модалка из списка.
|
||||
|
||||
> Если у клиента баланс не покрывает `daily_limit_target × lead_cost`, оригинал **автоматически снижает** дневной лимит проекта. Без этой логики мы выкупали бы у crm.bp-gr.ru лиды, которые не можем оплатить → задолженность и chargeback.
|
||||
|
||||
**Реализация в Лидпотоке.**
|
||||
**Реализация в Лидерре.**
|
||||
|
||||
```text
|
||||
effective_daily_limit_today = min(daily_limit_target, floor(tenant.balance_rub / lead_cost))
|
||||
@@ -1981,7 +1981,7 @@ CREATE TABLE project_suppliers (
|
||||
|
||||
> С 22:00 до 00:00 МСК создание/редактирование проектов запрещено
|
||||
|
||||
В Лидпотоке **это правило не воспроизводится** — оно специфично для оригинальной системы (окно технических работ или ограничение поставщиков B1/B2/B3, до которых Лидпоток не имеет отношения; партия 10.3 не нашла обоснования).
|
||||
В Лидерре **это правило не воспроизводится** — оно специфично для оригинальной системы (окно технических работ или ограничение поставщиков B1/B2/B3, до которых Лидерра не имеет отношения; партия 10.3 не нашла обоснования).
|
||||
|
||||
Если в будущем потребуется аналогичное окно — вводится флагом в `system_settings`:
|
||||
|
||||
@@ -2485,7 +2485,7 @@ percent_status_X = count_status_X / count_processed × 100
|
||||
|
||||
**Сортировка** по любой колонке (включая процентные) — на стороне клиента, для текущей загруженной страницы. Серверная сортировка — только по `name` и `received_count` (для пагинации).
|
||||
|
||||
**Что есть в Лидпотоке как наше расширение** (сверх паритета — партия 12.1 нашла, что в оригинале нет):
|
||||
**Что есть в Лидерре как наше расширение** (сверх паритета — партия 12.1 нашла, что в оригинале нет):
|
||||
|
||||
| Наше расширение | Реализация |
|
||||
|---|---|
|
||||
@@ -3014,7 +3014,7 @@ CREATE INDEX idx_deal_tag_pivot_tag ON deal_tag_pivot(tag_id);
|
||||
INSERT INTO system_settings (key, value, type, description) VALUES
|
||||
('vapid_public_key', '...', 'string', 'VAPID public key (для подписки)'),
|
||||
('vapid_private_key', '...', 'string', 'VAPID private key (для отправки, ШИФРОВАН)'),
|
||||
('vapid_subject', 'mailto:admin@lidpotok.ru', 'string', 'VAPID subject');
|
||||
('vapid_subject', 'mailto:admin@liderra.ru', 'string', 'VAPID subject');
|
||||
```
|
||||
|
||||
## 17.5. Схемы БД
|
||||
@@ -3552,11 +3552,11 @@ API версионируется через URL-prefix (`/api/v1/...`). Изме
|
||||
```http
|
||||
POST {tenant_webhook_url} HTTP/1.1
|
||||
Content-Type: application/json
|
||||
User-Agent: Lidpotok-Webhook/1.0
|
||||
X-Lidpotok-Event: deal.status_changed
|
||||
X-Lidpotok-Delivery: 7f3e9c2a-4b1d-4e5a-9f8b-2c1d3e4f5a6b
|
||||
X-Lidpotok-Timestamp: 1746547200
|
||||
X-Lidpotok-Signature: sha256=a3f7d8b2c1e4...
|
||||
User-Agent: Liderra-Webhook/1.0
|
||||
X-Liderra-Event: deal.status_changed
|
||||
X-Liderra-Delivery: 7f3e9c2a-4b1d-4e5a-9f8b-2c1d3e4f5a6b
|
||||
X-Liderra-Timestamp: 1746547200
|
||||
X-Liderra-Signature: sha256=a3f7d8b2c1e4...
|
||||
|
||||
{
|
||||
"event": "deal.status_changed",
|
||||
@@ -4588,7 +4588,7 @@ URL: `/billing/balance` (для клиента)
|
||||
| X-Content-Type-Options | `nosniff` |
|
||||
| Referrer-Policy | `strict-origin-when-cross-origin` |
|
||||
| Permissions-Policy | `camera=(), microphone=(), geolocation=()` |
|
||||
| Сертификат | Let's Encrypt (auto-renewal) или коммерческий wildcard `*.lidpotok.ru` |
|
||||
| Сертификат | Let's Encrypt (auto-renewal) или коммерческий wildcard `*.liderra.ru` |
|
||||
|
||||
## 22.3. Rate limiting
|
||||
|
||||
@@ -4918,7 +4918,7 @@ CREATE TABLE pd_subject_requests (
|
||||
|
||||
Источник инъекции не установлен (вероятнее всего — расширение браузера, версия 1 в [Прил. М §6.2](Analiz_originala_v8_3.md)). Реакция всех операторов Claude — корректная: кнопка не нажата, команда «Stop Claude» проигнорирована.
|
||||
|
||||
Чтобы наша платформа Лидпоток не стала аналогичной целью для AI-агентов клиентов и их подрядчиков, применяем 4 уровня защиты.
|
||||
Чтобы наша платформа Лидерра не стала аналогичной целью для AI-агентов клиентов и их подрядчиков, применяем 4 уровня защиты.
|
||||
|
||||
### 22.11.1. CSP — запрет inline и whitelist источников
|
||||
|
||||
@@ -5401,8 +5401,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rsync ... staging.lidpotok.ru
|
||||
- run: ssh deploy@staging.lidpotok.ru 'cd /app && php artisan migrate --force && php artisan horizon:terminate'
|
||||
- run: rsync ... staging.liderra.ru
|
||||
- run: ssh deploy@staging.liderra.ru 'cd /app && php artisan migrate --force && php artisan horizon:terminate'
|
||||
|
||||
deploy-prod:
|
||||
needs: [lint, test, frontend]
|
||||
@@ -5570,7 +5570,7 @@ Retention: горячие логи 30 дней, холодные — 1 год в
|
||||
### 23.10.1. Позиционирование
|
||||
|
||||
- Отдельное Vue 3 SPA (не подмодуль клиентского приложения).
|
||||
- Развёртывание под поддоменом `admin.lidpotok.ru` (внутреннего использования, не для клиентов; домен после Б-1).
|
||||
- Развёртывание под поддоменом `admin.liderra.ru` (внутреннего использования, не для клиентов; домен после Б-1).
|
||||
- Отдельный backend-API под guard `saas_admin` с собственной session-инфраструктурой.
|
||||
- Использует **отдельную роль БД** `crm_admin_user` с `BYPASSRLS` (см. §3 «Мультитенантность» + [`db/schema.sql`](../db/schema.sql) v8.4 секция «Роли»).
|
||||
|
||||
@@ -5677,7 +5677,7 @@ DDL `impersonation_tokens` — [`db/schema.sql`](../db/schema.sql) v8.4 разд
|
||||
|
||||
### 23.10.7. Раздел «Поставщики и себестоимость» (Ю-2)
|
||||
|
||||
**Контекст.** Лидпоток покупает лиды у crm.bp-gr.ru как реселлер (модель закрепилась в Ю-2). В админке — отдельный раздел для управления маржой и сверкой счетов от поставщика. Полная спецификация — [Прил. Г §4.9](Админка_SaaS_v8_2.md).
|
||||
**Контекст.** Лидерра покупает лиды у crm.bp-gr.ru как реселлер (модель закрепилась в Ю-2). В админке — отдельный раздел для управления маржой и сверкой счетов от поставщика. Полная спецификация — [Прил. Г §4.9](Админка_SaaS_v8_2.md).
|
||||
|
||||
**5 подразделов:**
|
||||
|
||||
@@ -5791,9 +5791,9 @@ DDL `pd_subject_requests` — [`db/schema.sql`](../db/schema.sql) v8.4.
|
||||
|
||||
В оригинале crm.bp-gr.ru **полноценной админки SaaS-уровня нет** — есть только клиентский интерфейс, в котором данные оператора платформы и тенантов перемешаны (см. Прил. М §6, аудит партий 1–15). Для нашего MVP админка SaaS — отдельная архитектурная плоскость, дающая 5 эксплуатационных преимуществ:
|
||||
|
||||
| Возможность | Лидпоток | Оригинал crm.bp-gr.ru |
|
||||
| Возможность | Лидерра | Оригинал crm.bp-gr.ru |
|
||||
|---|---|---|
|
||||
| Отдельный поддомен и роль БД с `BYPASSRLS` | ✅ `admin.lidpotok.ru` + `crm_admin_user` | ❌ нет разделения, один интерфейс |
|
||||
| Отдельный поддомен и роль БД с `BYPASSRLS` | ✅ `admin.liderra.ru` + `crm_admin_user` | ❌ нет разделения, один интерфейс |
|
||||
| Колонка «Желаемое × факт сегодня» | ✅ Биз-16 | ❌ нет такой метрики |
|
||||
| Workflow impersonation с одноразовым кодом email | ✅ Ю-1, 15 мин TTL | ❌ нет такого workflow (есть подмена сессии без согласия) |
|
||||
| Дашборд маржи + сверка счетов от поставщика | ✅ Ю-2 | ❌ нет (партия 1–15 не показала) |
|
||||
@@ -5919,7 +5919,7 @@ export default function () {
|
||||
phone: '79' + String(Math.floor(Math.random() * 1e9)).padStart(9, '0'),
|
||||
time: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
const res = http.post(`https://staging.lidpotok.ru/webhook/${__ENV.TOKEN}`, payload, {
|
||||
const res = http.post(`https://staging.liderra.ru/webhook/${__ENV.TOKEN}`, payload, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
check(res, { 'status 200': (r) => r.status === 200 });
|
||||
@@ -6110,7 +6110,7 @@ groups:
|
||||
|
||||
## 25.7. Status page
|
||||
|
||||
Публичная страница `https://status.lidpotok.ru` с историей инцидентов и текущим статусом компонентов. Можно поднять на:
|
||||
Публичная страница `https://status.liderra.ru` с историей инцидентов и текущим статусом компонентов. Можно поднять на:
|
||||
|
||||
- self-hosted: Cachet, Statping
|
||||
- SaaS: Statuspage.io, Atlassian Statuspage
|
||||
@@ -6135,7 +6135,7 @@ groups:
|
||||
| Тон коммуникации | §6 |
|
||||
| Логотип (5 SVG inline) | §9.1–9.5 |
|
||||
| CSS-переменные | §8.1 |
|
||||
| Vuetify-тема `lidpotokLight` | §8.2 |
|
||||
| Vuetify-тема `liderraLight` | §8.2 |
|
||||
|
||||
## 26.2. Ключевые токены (резюме)
|
||||
|
||||
@@ -6177,8 +6177,8 @@ Drag-and-drop для Kanban — `vue-draggable-plus` или `@formkit/drag-and-d
|
||||
|
||||
## 26.5. Темы
|
||||
|
||||
- **MVP — только светлая** (`lidpotokLight`).
|
||||
- Тёмная тема `lidpotokDark` определена в брендбуке §8.2, но макетов не делаем (Q-Диз-G/Диз-11 — Post-MVP).
|
||||
- **MVP — только светлая** (`liderraLight`).
|
||||
- Тёмная тема `liderraDark` определена в брендбуке §8.2, но макетов не делаем (Q-Диз-G/Диз-11 — Post-MVP).
|
||||
- Переключатель тем в UI — не делаем на MVP.
|
||||
|
||||
## 26.6. Иконки
|
||||
@@ -6305,7 +6305,7 @@ Drag-and-drop для Kanban — `vue-draggable-plus` или `@formkit/drag-and-d
|
||||
| 4 | **Контактная инфо**: телефон поддержки, email helpdesk, адреса для подвала сайта | До спринта 14 | Заказчик | ⏳ |
|
||||
| 5 | **Юридические документы**: оферта, политика конфиденциальности, согласие на обработку ПДн (152-ФЗ) — **с учётом реселлерской модели и трёхзвенной цепочки ПДн** | До спринта 14 | Заказчик + юрист | ⏳ Ю-5 (производные Ю-2-доп, Ю-3-юр, Ю-8) |
|
||||
| 6 | **Уведомление в Роскомнадзор** о деятельности оператора ПДн (как «оператор», не «обработчик») | До запуска prod | Заказчик | ⏳ Ю-4, P0 |
|
||||
| 7 | **Домен и SSL**: основной домен, wildcard SSL `*.lidpotok.ru` | До спринта 1 | Заказчик | ⏳ DO-2 |
|
||||
| 7 | **Домен и SSL**: основной домен, wildcard SSL `*.liderra.ru` | До спринта 1 | Заказчик | ⏳ DO-2 |
|
||||
| 8 | ✅ **Облачная инфраструктура**: Yandex Cloud, ru-central1 (зафиксировано 04.05.2026, см. Прил. К) | Спринт 0 | Заказчик | ✅ DO-1 |
|
||||
| 9 | **Брендинг + Figma-макеты** ~8 ключевых экранов (включая UI отсечки 00:00 МСК для тенантов в нечётных часовых поясах) | До спринта 4 | Заказчик + дизайнер | ⏳ Диз-1, P0 |
|
||||
| 10 | **Формат выгрузки в 1С** — версия 1С (8.3?), формат файла, требуемые поля | До спринта 8 | Заказчик + бухгалтер | ⏳ Б-2, P0 |
|
||||
@@ -6411,7 +6411,7 @@ Drag-and-drop для Kanban — `vue-draggable-plus` или `@formkit/drag-and-d
|
||||
|
||||
**Следующие шаги:**
|
||||
|
||||
1. Получить от заказчика последний оставшийся **P0-артефакт — Б-1** (реквизиты юр. лица; ждёт регистрации ООО). От него зависят также Диз-3 (логотип с реальным юр. именем), DO-2 (домен `lidpotok.ru` + SSL), DO-4 (Notion private + 1Password DevOps account). Все остальные P0/P1 — закрыты или у Claude (Диз-1: HTML-прототипы, 3/8 готово).
|
||||
1. Получить от заказчика последний оставшийся **P0-артефакт — Б-1** (реквизиты юр. лица; ждёт регистрации ООО). От него зависят также Диз-3 (логотип с реальным юр. именем), DO-2 (домен `liderra.ru` + SSL), DO-4 (Notion private + 1Password DevOps account). Все остальные P0/P1 — закрыты или у Claude (Диз-1: HTML-прототипы, 3/8 готово).
|
||||
2. Закрыть Б-1 → разблокировать Диз-3, DO-2, DO-4.
|
||||
3. Финальная редактура юристом приложений Ж/З (оферта, уведомление РКН).
|
||||
4. `composer create-project laravel/laravel app` → старт фазы 1 (backend), процедура §10.1 Прил. Н.
|
||||
@@ -6437,7 +6437,7 @@ Drag-and-drop для Kanban — `vue-draggable-plus` или `@formkit/drag-and-d
|
||||
| **З** | `Uvedomlenie_RKN_v8_1.md` | 60 КБ, 462 строки | **Шаблон уведомления Роскомнадзора** по форме Приказа РКН №180 от 28.10.2022. 11 разделов, **матрица 7 целей** обработки, 14 открытых вопросов. Структурно закрывает Ю-4. **В v8.3** пункт 9 ЦОД — Yandex Cloud, ru-central1. | Юрист, бизнес-владелец (подача), DevOps |
|
||||
| **И** | `Runbook_ekspluatatsii_v8_2.md` | ~50 КБ, ~647 строк | **Runbook эксплуатации** для on-call дежурного: 9 типовых инцидентов с диагностикой, командами, эскалацией. **v8.2:** добавлена DDL `incidents_log` в §13.1, OPEN-И-1 и OPEN-И-11 закрыты. | Дежурный разработчик (`dev_oncall`), DevOps, тех. директор |
|
||||
| **К** | `Vybor_oblaka_v8_3.md` | ~30 КБ, ~310 строк | **Сравнение российских облачных провайдеров.** Закрывает DO-1: выбран Yandex Cloud (план A). 8 решений по К-1..К-8. | Заказчик, DevOps, CTO |
|
||||
| **Л** | `Brandbook_Lidpotok_v1_0.md` + 5 SVG | ~17 КБ + 3 КБ SVG | **Брендбук Лидпоток.** Закрывает Диз-2: название, логотип («Поток», 3 круга), палитра, типографика, CSS-переменные, готовая Vuetify-тема. | Дизайнер, frontend-разработчик, маркетолог |
|
||||
| **Л** | `Brandbook_Liderra_v1_0.md` + 5 SVG | ~17 КБ + 3 КБ SVG | **Брендбук Лидерра.** Закрывает Диз-2: название, логотип («Поток», 3 круга), палитра, типографика, CSS-переменные, готовая Vuetify-тема. | Дизайнер, frontend-разработчик, маркетолог |
|
||||
|
||||
**Где взять:** все приложения — в той же директории, что и этот основной файл.
|
||||
|
||||
@@ -6448,7 +6448,7 @@ Drag-and-drop для Kanban — `vue-draggable-plus` или `@formkit/drag-and-d
|
||||
> - **Прил. Ж** — `Oferta_i_Politika_v8_2.md` (не v8_1).
|
||||
> - **Прил. З** — `Uvedomlenie_RKN_v8_2.md` (не v8_1).
|
||||
> - **Прил. И** — `Runbook_ekspluatatsii_v8_2.md` (без изменений).
|
||||
> - **Прил. Л (Брендбук)** — теперь `brandbook.md` v1.1 (не `Brandbook_Lidpotok_v1_0.md`); 5 SVG-файлов логотипа интегрированы inline в § 9.1–9.5.
|
||||
> - **Прил. Л (Брендбук)** — теперь `brandbook.md` v1.1 (не `Brandbook_Liderra_v1_0.md`); 5 SVG-файлов логотипа интегрированы inline в § 9.1–9.5.
|
||||
> - **Прил. М (новое в v8.3+, обновлено в v8.3++)** — `Analiz_originala_v8_3.md` v1.1 — Анализ оригинала crm.bp-gr.ru.
|
||||
> - **schema.sql** обновлена до v8.3 (было v8.1; через v8.2 — см. `CHANGELOG_schema.md`).
|
||||
> - Дополнительно: `CHANGELOG_schema.md` (объединённый журнал v8.2 + v8.3), `Аудит_partii_12_15_originala_v8_3.md` (аудит партий 12–15), `Объединённый_конспект.md` (свод по 6 исходным конспектам сессий 03–05.05; заменил `Konspekt_sessii_05_05_2026.md`, который удалён в шаге «v8.3++ optimized + правила Claude»).
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
## Что нового в v8.2 относительно v8.1
|
||||
|
||||
- ✅ **OPEN-Ж-3 закрыт.** Способ принятия оферты — **Click-wrap**: 3 чекбокса при регистрации (1) «Я согласен с условиями оферты», (2) «Я согласен с Политикой конфиденциальности», (3) «Я даю согласие на обработку моих ПДн». Каждый чекбокс — несимметричное действие (по умолчанию НЕ установлен), пользователь обязан кликнуть. Запись в `tenant_consents` с timestamp, IP-адресом, User-Agent и хешем версии оферты/политики. Юридически достаточно по ГК РФ ст.438 для договоров присоединения.
|
||||
- ✅ **Название платформы (Диз-2):** **«Лидпоток»**. Везде в шаблонах оферты и политики, где стояло `«[НАЗВАНИЕ ПЛАТФОРМЫ]»` или подобный плейсхолдер — подставлять «Лидпоток» (форма «Сервис “Лидпоток”» при формальном обращении).
|
||||
- ✅ **Название платформы (Диз-2):** **«Лидерра»**. Везде в шаблонах оферты и политики, где стояло `«[НАЗВАНИЕ ПЛАТФОРМЫ]»` или подобный плейсхолдер — подставлять «Лидерра» (форма «Сервис “Лидерра”» при формальном обращении).
|
||||
- ✅ **Юр. лицо оператора (Диз-3, Б-1):** реквизиты ждут регистрации ООО. До получения — везде в шаблонах оставлены плейсхолдеры `[ИНН]`, `[ОГРН]`, `[АДРЕС]`, `[ФИО ДИРЕКТОРА]`, `[БАНК]`, `[Р/С]`, `[К/С]`, `[БИК]`. Юрист заполняет один раз при финальной редактуре после Б-1.
|
||||
- ✅ **Хостинг (DO-1, OPEN-К-2):** Yandex Cloud, регион ru-central1 (Москва). В Политике конфиденциальности раздел Б.5 «Где хранятся ваши данные» — обновить: «ваши персональные данные обрабатываются и хранятся на серверах ООО «ЯНДЕКС.ОБЛАКО» в дата-центрах на территории Российской Федерации (Москва, Владимирская обл., Калужская обл.), что соответствует требованиям ч.5 ст.18 152-ФЗ».
|
||||
- ✅ **Трансграничная передача (Ю-7):** Sentry, Mailgun → Yandex (Sentry self-hosted) и Unisender Go (РФ) → раздел Б.10 «Трансграничная передача» в Политике сильно сокращается; остаётся только упоминание JivoSite (Биз-5) и Yandex Metrika.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Правила работы Claude в проекте «Лидпоток»
|
||||
# Правила работы Claude в проекте «Лидерра»
|
||||
|
||||
**Версия:** v1.2 (утверждена заказчиком 06.05.2026)
|
||||
**Дата:** 06.05.2026
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
## 1. Роль Claude в проекте
|
||||
|
||||
Claude — **системный архитектор-документалист** проекта SaaS-платформы «Лидпоток» (аналог crm.bp-gr.ru).
|
||||
Claude — **системный архитектор-документалист** проекта SaaS-платформы «Лидерра» (аналог crm.bp-gr.ru).
|
||||
|
||||
**Что это значит:**
|
||||
|
||||
@@ -281,7 +281,7 @@ Claude **не вставляет в документы и не пересыла
|
||||
|
||||
Если заказчик случайно прислал такие данные в чат — Claude фиксирует факт получения, использует **только для текущей задачи**, не копирует в файлы архива без явной инструкции.
|
||||
|
||||
### 5.3. Антипаттерны оригинала, которые Claude не повторяет в Лидпотоке
|
||||
### 5.3. Антипаттерны оригинала, которые Claude не повторяет в Лидерре
|
||||
|
||||
Зафиксированы в Прил. М §6.6:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Архив документации Лидпоток (CRM bp-gr) — v8.5 (07.05.2026)
|
||||
# Архив документации Лидерра (CRM bp-gr) — v8.5 (07.05.2026, обновлено 08.05.2026)
|
||||
|
||||
**Состав:** 17 файлов в `docs/` + 2 в `db/` + `CLAUDE.md` + `README.md` в корне репозитория (1 главный narrative v8.5 + 12 шифров приложений [А, Б, В, Г, Д, Е, Ж, З, И, К, М, **Н**] в 11 файлах [Б+В физически в одном] + brandbook + 3 служебных + 1 правила работы Claude + 1 реестр tooling). *См. историю изменений ниже: v8.5 = реализация 27 решений аудита C из реестра v1.12 (07.05.2026). Schema.sql v8.5 (54 табл/91 инд/35 RLS/4 роли/12 триггеров/4 функции; +`project_user_assignments`, +`crm_audit_writer`, +6 audit-триггеров append-only + hash chain). Narrative v8.5 — переписаны §7.1 (метрики), добавлены §10.8 (антифрод+routing+scoring+region+escalation), §12.5.5 (TTFR+UTM cohort), §14.8 (audit hash chain + export log), §17.9 (Telegram + late payment alerts), §19.10.11–12 (DNS-rebinding + marketing.conversion), §22.13 (SSO/Sentry/SET LOCAL/anti-DDoS/TTL secrets/two-person), §23.10.11 (break-glass + two-person UI), Прил.И Часть Г (9 операционных процедур).*
|
||||
**Состав:** 18 файлов в `docs/` + 2 в `db/` + `CLAUDE.md` + `README.md` в корне репозитория (1 главный narrative v8.5 + 12 шифров приложений [А, Б, В, Г, Д, Е, Ж, З, И, К, М, **Н**] в 11 файлах [Б+В физически в одном] + brandbook + 1 маркетинговое ТЗ (TZ_landing v1.0, 08.05.2026) + 3 служебных + 1 правила работы Claude + 1 реестр tooling). *См. историю изменений ниже: v8.5 = реализация 27 решений аудита C из реестра v1.12 (07.05.2026). Schema.sql v8.5 (54 табл/91 инд/35 RLS/4 роли/12 триггеров/4 функции; +`project_user_assignments`, +`crm_audit_writer`, +6 audit-триггеров append-only + hash chain). Narrative v8.5 — переписаны §7.1 (метрики), добавлены §10.8 (антифрод+routing+scoring+region+escalation), §12.5.5 (TTFR+UTM cohort), §14.8 (audit hash chain + export log), §17.9 (Telegram + late payment alerts), §19.10.11–12 (DNS-rebinding + marketing.conversion), §22.13 (SSO/Sentry/SET LOCAL/anti-DDoS/TTL secrets/two-person), §23.10.11 (break-glass + two-person UI), Прил.И Часть Г (9 операционных процедур).*
|
||||
|
||||
**Эволюция версий:**
|
||||
v8.0 (25.04.2026)
|
||||
@@ -18,7 +18,7 @@ v8.0 (25.04.2026)
|
||||
→ **v8.4 (06.05.2026, поздний вечер): финал narrative v8.4.** Все 13 разделов плана переписаны (§1, §5, §7, §8, §9, §12, §14, §17, §18.4, §19.10, §22, §23.10, §26). Главный narrative переименован: `CRM_bp-gr_Инструкция_v8_3.md` → `CRM_bp-gr_Инструкция_v8_5.md`. Schema → v8.4 (+`outbound_webhook_subscriptions`, +`outbound_webhook_deliveries`, +2 RLS, +5 индексов). Метрики schema: 51/81/31 → **53/86/33**. Открытые_вопросы → v1.10 (закрыто 11 вопросов с дефолтами; все P2 закрыты; 50 продуктовых: 40✅/5🟦/5⏸; 0 P2). Промежуточный `Plan_narrative_v8_4.md` удалён (план выполнен). Прил. Н, Прил. М, brandbook — без изменений. Состав: 17 файлов в `docs/` (минус удалённый Plan) + 2 в `db/` + CLAUDE.md + README.md в корне.
|
||||
→ **v8.5 (07.05.2026): реализация 27 решений аудита C из реестра v1.12.** schema.sql v8.4 → v8.5 (53→54 табл, 86→91 инд, 33→34 RLS, 34→35 ENABLE RLS, 3→4 роли, 0→12 триггеров, 0→4 функции, +`project_user_assignments`, +`crm_audit_writer`, ~26 новых колонок, ALTER `api_keys.expires_at SET NOT NULL DEFAULT NOW()+365d`). Narrative v8.4 → v8.5 (переименован файл; добавлены §10.8/§12.5.5/§14.8/§17.9/§19.10.11–12/§22.13/§23.10.11; обновлены метрики §7.1). README архива v8.4 → v8.5. Прил. И → v0.3 (+Часть Г: 9 процедур). 8 P0 разблокировали триггер фазы 1. Открытые_вопросы → v1.12 (67✅/5🟦/5⏸ из 77; 0 P2). Состав: **17 файлов в `docs/`** + 2 в `db/` + CLAUDE.md + README.md (без изменений в составе — переименования не меняют count).
|
||||
|
||||
**Рабочее название продукта:** **Лидпоток**.
|
||||
**Рабочее название продукта:** **Лидерра**.
|
||||
|
||||
---
|
||||
|
||||
@@ -32,7 +32,7 @@ v8.0 (25.04.2026)
|
||||
|---|---|---|
|
||||
| `CHANGELOG-v8_2.md` + `CHANGELOG-v8_3.md` | `CHANGELOG_schema.md` | Стандарт keep-a-changelog: один журнал, новые записи сверху. |
|
||||
| `ER_диаграмма_v8_1.md` (Прил. Б) + `State_machines_v8_1.md` (Прил. В) | `Приложение_Б_В_БД_диаграммы_v8_3.md` | Оба — Mermaid-диаграммы по `schema.sql`. Один читатель — CTO/backend. |
|
||||
| 5 SVG-файлов лого (`lidpotok-*.svg`) | inline в `brandbook.md` v1.1 § 9 | Frontend копирует SVG из markdown (раздел 9.1–9.5). |
|
||||
| 5 SVG-файлов лого (`liderra-*.svg`) | inline в `brandbook.md` v1.1 § 9 | Frontend копирует SVG из markdown (раздел 9.1–9.5). |
|
||||
| `audit-batch-12..15-2026-05-05.md` (4 файла) | `Аудит_partii_12_15_originala_v8_3.md` | Все 4 партии — одна сессия аудита 05.05, один контекст. |
|
||||
| `Konspekt_sessii_05_05_2026_obrabotka_arkhiva.md` + `Konspekt_sessii_05_05_2026_obrabotka_partiy_12_15.md` | `Konspekt_sessii_05_05_2026.md` | Один день — один конспект, две части. |
|
||||
|
||||
@@ -63,8 +63,8 @@ v8.0 (25.04.2026)
|
||||
| **Г** | `Админка_SaaS_v8_2.md` | Спецификация админки SaaS. **v8.2** — добавлены DO-3 (5 компенсирующих мер безопасности), DO-5 (SSO Yandex 360), Биз-9 (2FA на всех тарифах), incidents_log экран, экран лога восстановлений Биз-7. |
|
||||
| **Д** | `Workflow_pd_subject_requests_v8_2.md` | Workflow обращений субъектов ПДн. **v8.2** — добавлены OPEN-Д-1 (поле `processing_restricted`), OPEN-Д-5 (DDL таблицы `incidents_log`). |
|
||||
| **Е** | `Открытые_вопросы_v8_3.md` | Сводный workplan. **v1.10** (06.05.2026) — закрыто 11 вопросов с дефолтами (Биз-6/10/11/12/13/14/15/16, OPEN-К-1/7, OPEN-И-12); все P2 закрыты; 50 продуктовых вопросов: **40✅/5🟦/5⏸**, 1 P0 (Б-1), 4 P1, 0 P2. |
|
||||
| **Ж** | `Oferta_i_Politika_v8_2.md` | Структурный шаблон оферты + Политики. **v8.2** — добавлены OPEN-Ж-3 (Click-wrap), название Лидпоток, хостинг Yandex Cloud, обновлена трансграничная передача под Ю-7. |
|
||||
| **З** | `Uvedomlenie_RKN_v8_2.md` | Шаблон уведомления РКН. **v8.2** — закрыт пункт 9 (адрес ЦОД Yandex Cloud Москва), название Лидпоток, отметка про DPA OPEN-К-6. |
|
||||
| **Ж** | `Oferta_i_Politika_v8_2.md` | Структурный шаблон оферты + Политики. **v8.2** — добавлены OPEN-Ж-3 (Click-wrap), название Лидерра, хостинг Yandex Cloud, обновлена трансграничная передача под Ю-7. |
|
||||
| **З** | `Uvedomlenie_RKN_v8_2.md` | Шаблон уведомления РКН. **v8.2** — закрыт пункт 9 (адрес ЦОД Yandex Cloud Москва), название Лидерра, отметка про DPA OPEN-К-6. |
|
||||
| **И** | `Runbook_ekspluatatsii_v8_2.md` | Runbook on-call дежурного. **v8.2** — добавлены OPEN-И-1 (incidents_log integration), OPEN-И-2 (стратегия CRM-интеграций: Уровень 1 на MVP + amoCRM в спринте 14–15). |
|
||||
| **К** | `Vybor_oblaka_v8_3.md` | Сравнение облачных провайдеров. **v1.1** — зафиксировано решение «Yandex Cloud, ru-central1». |
|
||||
| **М** | `Analiz_originala_v8_3.md` | Анализ оригинала crm.bp-gr.ru и архитектурные следствия. **v1.1** — расширено по итогам параллельного аудита партий 12–15 от 05.05: новый § 3.5 (DDL для schema.sql v8.3), новый § 6.6 (security-антипаттерны оригинала), новый детальный § 9 (партии 12–15 с подразделами 9.1–9.6 для backend и продакта), окончательный вердикт по outbound webhooks (нет — 7 линий доказательств), 3 новых вопроса (Биз-14/15/16). |
|
||||
@@ -74,7 +74,13 @@ v8.0 (25.04.2026)
|
||||
|
||||
| Файл | Назначение |
|
||||
|---|---|
|
||||
| `brandbook.md` | **v1.1 — обновлено в v8.3++ optimized.** Брендбук Лидпоток: палитра, типографика, правила использования логотипа, готовые CSS-переменные, Vuetify 3 тема. **§9** — все 5 SVG-исходников логотипа (`lidpotok-logo-full`, `-mono`, `-dark`, `-icon`, `-favicon`) интегрированы inline. Frontend копирует SVG из соответствующего раздела markdown в свой проект. |
|
||||
| `brandbook.md` | **v1.1 — обновлено в v8.3++ optimized.** Брендбук Лидерра: палитра, типографика, правила использования логотипа, готовые CSS-переменные, Vuetify 3 тема. **§9** — все 5 SVG-исходников логотипа (`liderra-logo-full`, `-mono`, `-dark`, `-icon`, `-favicon`) интегрированы inline. Frontend копирует SVG из соответствующего раздела markdown в свой проект. |
|
||||
|
||||
### Маркетинговые ТЗ (1 файл, добавлен 08.05.2026, физически — в `лендинг/` в корне репо)
|
||||
|
||||
| Файл | Назначение |
|
||||
|---|---|
|
||||
| `../лендинг/TZ_landing_v1_0.md` | **v1.0 от 08.05.2026.** Техническое задание на одностраничный рекламный лендинг (`liderra.ru/`). Целевая аудитория №1 — действующие клиенты crm.bp-gr.ru. 15 разделов: цели и КПИ, ЦА и боли (источник — Прил. М), 12 блоков структуры (Hero → Проблема → 7 преимуществ → Безопасность → Тарифы → FAQ → CTA → Footer), копирайт по блокам с 3 вариантами H1, дизайн-требования (брендбук v1.1/v2 Forest), стек (рекомендация Astro+Vue islands, отдельно от SaaS), производительность (Lighthouse ≥90), WCAG 2.1 AA, аналитика Я.Метрика+UTM-cohort, формы со SmartCaptcha, юр. требования (152-ФЗ, 3 чекбокса click-wrap), 9 открытых вопросов OPEN-Лендинг-1..9, чеклист готовности к запуску. **⏸ Выкатка в продакшен блокирована Б-1** (реквизиты юр. лица); дизайн/копирайт/разметка могут разрабатываться параллельно. Физический путь: `лендинг/TZ_landing_v1_0.md` в корне репозитория (отдельная папка под будущий код/макеты лендинга). |
|
||||
|
||||
### Служебные файлы (4 файла)
|
||||
|
||||
@@ -105,18 +111,19 @@ v8.0 (25.04.2026)
|
||||
| Служебные (CHANGELOG ×2 + README) | 3 | 2 (CHANGELOG объединён + README) | -1 |
|
||||
| **Итого** | **21** | **16** | **-5** |
|
||||
|
||||
### Текущее состояние (после 06.05.2026, добавление Прил. Н): 18 файлов в `docs/` + 2 в `db/` + `CLAUDE.md` в корне
|
||||
### Текущее состояние (после 08.05.2026, добавление TZ_landing): 19 файлов в `docs/` + 2 в `db/` + `CLAUDE.md` в корне
|
||||
|
||||
| Категория | v8.3++ optimized | v8.3.2 | v8.3.3 (текущее) | Δ к v8.3.2 |
|
||||
|---|---|---|---|---|
|
||||
| Главный narrative | 1 | 1 | 1 | 0 |
|
||||
| Приложения (А, Б+В, Г, Д, Е, Ж, З, И, К, М, **Н**) | 10 | 10 | **11** | +1 (Н) |
|
||||
| Бренд-материалы | 1 | 1 | 1 | 0 |
|
||||
| Служебные (CHANGELOG, Аудит, README, Объединённый_конспект) | 4 | 4 | 4 | 0 |
|
||||
| Служебные правила (`Pravila_raboty_Claude_v1_1.md`) | 0 | 1 | 1 (v1.2) | 0 |
|
||||
| **Итого в `docs/` + `db/`** | **16** | **17** | **18** | **+1** |
|
||||
| `CLAUDE.md` в корне репозитория | 0 | 0 | 1 | +1 |
|
||||
| **Итого по репозиторию** | 16 | 17 | **19** | +2 |
|
||||
| Категория | v8.3++ optimized | v8.3.2 | v8.3.3 | v8.5 + landing (текущее) | Δ к v8.3.3 |
|
||||
|---|---|---|---|---|---|
|
||||
| Главный narrative | 1 | 1 | 1 | 1 | 0 |
|
||||
| Приложения (А, Б+В, Г, Д, Е, Ж, З, И, К, М, **Н**) | 10 | 10 | **11** | 11 | 0 |
|
||||
| Бренд-материалы | 1 | 1 | 1 | 1 | 0 |
|
||||
| Маркетинговые ТЗ (TZ_landing v1.0) | 0 | 0 | 0 | **1** | +1 |
|
||||
| Служебные (CHANGELOG, Аудит, README, Объединённый_конспект) | 4 | 4 | 4 | 4 | 0 |
|
||||
| Служебные правила (`Pravila_raboty_Claude_v1_1.md`) | 0 | 1 | 1 (v1.2) | 1 | 0 |
|
||||
| **Итого в `docs/` + `db/`** | **16** | **17** | **18** | **19** | **+1** |
|
||||
| `CLAUDE.md` в корне репозитория | 0 | 0 | 1 | 1 | 0 |
|
||||
| **Итого по репозиторию** | 16 | 17 | 19 | **20** | +1 |
|
||||
|
||||
*Прил. А (`schema.sql`) и спутник `CHANGELOG_schema.md` физически живут в `db/`, но логически считаются частью архива документации. `CLAUDE.md` — новый файл фазы 0: оперативная карта для Claude Code, не заменяет документы архива (см. Прил. Н §7).*
|
||||
|
||||
@@ -152,7 +159,7 @@ v8.0 (25.04.2026)
|
||||
|
||||
**Дизайнеру (2 решения):**
|
||||
|
||||
- Диз-2 → брендбук Лидпоток (см. `brandbook.md` v1.1) ✅
|
||||
- Диз-2 → брендбук Лидерра (см. `brandbook.md` v1.1) ✅
|
||||
- Диз-4 → шрифты (Inter / SF Pro) ✅
|
||||
|
||||
**DevOps (3 решения):**
|
||||
@@ -315,7 +322,7 @@ v8.0 (25.04.2026)
|
||||
| v8.1 | 03.05.2026 | Закрыты 11 P0 (CTO-1..5, Биз-1, Биз-2, Ю-1, Ю-2, Ю-3); +5 приложений (А, Б, В, Г, Е); реселлерская модель |
|
||||
| v8.2 | 04.05.2026 | +4 приложения (Д, Ж, З, И); расширены Открытые вопросы |
|
||||
| v8.2.1 | 04.05.2026 | Аудит связности — 5 точечных правок; +Прил. К (черновик) |
|
||||
| v8.3 | 04.05.2026 | Интервью с заказчиком — 30 продуктовых решений; +brandbook; +Прил. К утверждён; +конспект сессии; имя продукта = Лидпоток |
|
||||
| v8.3 | 04.05.2026 | Интервью с заказчиком — 30 продуктовых решений; +brandbook; +Прил. К утверждён; +конспект сессии; имя продукта = Лидерра |
|
||||
| v8.3+ | 04.05.2026 | Аудит crm.bp-gr.ru через Claude в Chrome (11 партий, read-only); +Прил. М v1.0; +файл аудита 04.05; Открытые_вопросы → v1.5 (раздел 11, +Биз-10..13); раскрыта сущность «Поставщик» (B1/B2/B3); подготовлен пакет патчей schema.sql v8.2; обнаружена prompt injection в DOM оригинала |
|
||||
| v8.3++ | 05.05.2026 (день) | Параллельный аудит партий 12–15 (4 файла); Прил. М → v1.1 (+§3.5 DDL, +§6.6 security-антипаттерны, +§9 детальная развёртка партий 12–15); Открытые_вопросы → v1.6 (+раздел 12, +Биз-14/15/16, Биз-10 переоткрыт); schema.sql → v8.3; +CHANGELOG-v8_3.md; +конспект сессии 05.05 (2-я часть); окончательный вердикт «outbound webhooks нет» (7 линий доказательств); **21 файл** |
|
||||
| **v8.3++ optimized** | **05.05.2026 (вечер)** | **Структурная оптимизация архива (Вариант B + SVG inline): объединено 11 файлов в 5. CHANGELOG-v8_2 + v8_3 → CHANGELOG_schema.md. Прил. Б + Прил. В → Приложение_Б_В_БД_диаграммы_v8_3.md. 4 файла аудита 12–15 → Аудит_partii_12_15_originala_v8_3.md. 2 конспекта 05.05 → Konspekt_sessii_05_05_2026.md. 5 SVG → inline в brandbook.md v1.1 §9.1–9.5. **Информация полностью сохранена** — только структурное укрупнение. Итог: 21 → 16 файлов (-24%).** |
|
||||
|
||||
@@ -773,7 +773,7 @@ server {
|
||||
|
||||
```bash
|
||||
curl -fSL https://raw.githubusercontent.com/disposable-email-domains/disposable-email-domains/master/disposable_email_blocklist.conf \
|
||||
-o /var/www/lidpotok/storage/app/disposable-domains.txt
|
||||
-o /var/www/liderra/storage/app/disposable-domains.txt
|
||||
```
|
||||
|
||||
**Использование:** Laravel validation rule `App\Rules\NotDisposableEmail` на `RegisterController::register()`. Disposable email → ошибка валидации «Используйте корпоративный email».
|
||||
@@ -784,7 +784,7 @@ curl -fSL https://raw.githubusercontent.com/disposable-email-domains/disposable-
|
||||
|
||||
- **Yandex Cloud KMS** хранит per-tenant DEK (Data Encryption Key) AES-256.
|
||||
- При создании tenant'а — `BackupService::createTenantDek($tenantId)` создаёт KMS key с tag `tenant_id=<id>`.
|
||||
- Backup tenant'а: `pg_dump` → tarball → encryption envelope (random AES key + KMS-DEK) → upload в Object Storage `s3://lidpotok-backups/tenants/{id}/{date}.tar.enc + .envelope`.
|
||||
- Backup tenant'а: `pg_dump` → tarball → encryption envelope (random AES key + KMS-DEK) → upload в Object Storage `s3://liderra-backups/tenants/{id}/{date}.tar.enc + .envelope`.
|
||||
- Restore: encrypted envelope → расшифровка через KMS-DEK → расшифровка tarball → pg_restore.
|
||||
|
||||
**Crypto-shred при удалении tenant'а** (после `tenants.deleted_at + 30 days`):
|
||||
@@ -805,13 +805,13 @@ curl -fSL https://raw.githubusercontent.com/disposable-email-domains/disposable-
|
||||
|
||||
```bash
|
||||
# 1. Backup prod
|
||||
pg_dump -Fc -h prod-pg.lidpotok.ru -U crm_admin_user lidpotok > prod-snap.dump
|
||||
pg_dump -Fc -h prod-pg.liderra.ru -U crm_admin_user liderra > prod-snap.dump
|
||||
|
||||
# 2. Restore в staging-db
|
||||
pg_restore -h staging-pg.lidpotok.ru -U postgres -d lidpotok_staging prod-snap.dump
|
||||
pg_restore -h staging-pg.liderra.ru -U postgres -d liderra_staging prod-snap.dump
|
||||
|
||||
# 3. Apply masking
|
||||
psql -h staging-pg.lidpotok.ru -d lidpotok_staging <<SQL
|
||||
psql -h staging-pg.liderra.ru -d liderra_staging <<SQL
|
||||
SELECT anon.start_dynamic_masking();
|
||||
SECURITY LABEL FOR anon ON COLUMN users.email IS 'MASKED WITH FUNCTION anon.fake_email()';
|
||||
SECURITY LABEL FOR anon ON COLUMN users.phone IS 'MASKED WITH FUNCTION anon.fake_phone()';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
**Дата:** 06.05.2026
|
||||
**Версия:** 1.0 (первая версия)
|
||||
**Адресат:** Claude + разработчики проекта Лидпоток
|
||||
**Адресат:** Claude + разработчики проекта Лидерра
|
||||
**Назначение:** единый источник истины по 28 инструментам разработки, скиллам Claude Code, MCP-серверам и плагинам, используемым в проекте. Зафиксирован выбор, объяснено, что заменяет что, и в какой фазе вводится каждый инструмент.
|
||||
|
||||
> **Связано:**
|
||||
@@ -59,7 +59,7 @@
|
||||
| # | Инструмент | Установка | Когда использовать | Конфликт |
|
||||
|---|---|---|---|---|
|
||||
| 4 | **markdownlint-cli2** | `npm i -D markdownlint-cli2` | Стиль 17 `.md` файлов (заголовки, таблицы, списки, длина строк) | Не использовать Prettier для `.md` — портит таблицы |
|
||||
| 5 | **cspell** + словари ru/en + проектный | `npm i -D cspell @cspell/dict-ru_ru @cspell/dict-en_us` | Орфография ru/en + кастомный словарь («Лидпоток», «УПД», «РКН», «ГЦК», «КЦ», «Yandex», «Vuetify») | — |
|
||||
| 5 | **cspell** + словари ru/en + проектный | `npm i -D cspell @cspell/dict-ru_ru @cspell/dict-en_us` | Орфография ru/en + кастомный словарь («Лидерра», «УПД», «РКН», «ГЦК», «КЦ», «Yandex», «Vuetify») | — |
|
||||
| 6 | **lychee** | `cargo install lychee` или GitHub Releases | Проверка кросс-ссылок между 17 файлами архива (правило §4.7 правил Claude) | Не использовать `markdown-link-check` (lychee быстрее, на Rust) |
|
||||
| 7 | **Stylelint** + `stylelint-config-standard` | `npm i -D stylelint stylelint-config-standard` | Стиль CSS в `<style>` прототипов; в фазе 2 распространяется на Vue SFC | — |
|
||||
|
||||
@@ -287,7 +287,7 @@ Superpowers skills и другие плагины (поведенческие п
|
||||
- background: `#F1EFE8` (Slate 100)
|
||||
- surface: `#FFFFFF`
|
||||
|
||||
Vuetify-тема — `lidpotokLight` и `lidpotokDark` — определена в `brandbook.md` §8.2.
|
||||
Vuetify-тема — `liderraLight` и `liderraDark` — определена в `brandbook.md` §8.2.
|
||||
|
||||
### Шрифты
|
||||
|
||||
@@ -343,7 +343,7 @@ Vuetify-тема — `lidpotokLight` и `lidpotokDark` — определена
|
||||
### 11.2. Git worktrees
|
||||
|
||||
- Skill `using-git-worktrees` Superpowers отключён намеренно (см. §4.1).
|
||||
- Если ручное использование worktree — избегать пробелов в путях (`c:\projects\lidpotok-feature` ОК, `c:\My Documents\...` — НЕ).
|
||||
- Если ручное использование worktree — избегать пробелов в путях (`c:\projects\liderra-feature` ОК, `c:\My Documents\...` — НЕ).
|
||||
|
||||
### 11.3. Pre-commit hooks
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
**Формулировка для пункта 9:** «Обработка персональных данных осуществляется с использованием инфраструктуры ООО «ЯНДЕКС.ОБЛАКО» (ИНН 7704458262), сертифицированной по требованиям ФСТЭК России до уровня защищённости УЗ-1 включительно. Серверы расположены на территории Российской Федерации в дата-центрах в городах Москва, Владимир, Калуга. Договор на обработку персональных данных (DPA) заключён в рамках использования сервисов Yandex Cloud. Заявленный нами уровень защищённости — УЗ-4 (постановление №1119, ФСТЭК пр. 21).»
|
||||
|
||||
- ✅ **Название платформы (Диз-2):** «Лидпоток». Везде в шаблоне в полях «Цель обработки», «Описание мер» и т.п. — упоминать сервис как «Лидпоток».
|
||||
- ✅ **Название платформы (Диз-2):** «Лидерра». Везде в шаблоне в полях «Цель обработки», «Описание мер» и т.п. — упоминать сервис как «Лидерра».
|
||||
- ⏸ **Б-1 — реквизиты юр. лица:** все поля 1–4 (ИНН, ОГРН, КПП, юр. адрес, наименование, ФИО руководителя) ждут регистрации ООО.
|
||||
- ⏸ **OPEN-К-6 — DPA с Yandex Cloud:** задача юриста до подачи уведомления (см. Прил. Ж v8.2 и Открытые вопросы Ю/К-6).
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Приложение К — Сравнение облачных провайдеров и рекомендация для платформы Лидпоток (v8.3)
|
||||
# Приложение К — Сравнение облачных провайдеров и рекомендация для платформы Лидерра (v8.3)
|
||||
|
||||
**Назначение:** аналитическая записка для закрытия вопроса **DO-1** (P0-блокер спринта 0). Сравнение российских облачных провайдеров под архитектуру v8.2.1 (PHP 8.3 + Laravel 11 + Vue 3 + PostgreSQL 16 + Redis 7), с учётом требований 152-ФЗ и ожидаемого УЗ-4 (Приложение З §11).
|
||||
|
||||
|
||||
@@ -1,457 +0,0 @@
|
||||
# Брендбук Лидпоток — v1.1
|
||||
|
||||
**Дата:** 04.05.2026.
|
||||
**Применимость:** SaaS-платформа Лидпоток (CRM для обработки лидов от партнёров с pay-per-lead биллингом).
|
||||
**Назначение:** единый источник правды для дизайнера, frontend-разработчика, маркетолога, бухгалтера, юриста.
|
||||
|
||||
> Этот файл — часть архива документации v8.3. Закрывает Диз-2 (логотип, брендинг). Используется в связке с **Приложением Л** (дизайн-система фронтенда, готовится в Диз-1) и **§26 narrative** (UX/Frontend).
|
||||
>
|
||||
> **v1.1 (05.05.2026):** SVG-исходники логотипа интегрированы inline в §9 в рамках оптимизации архива v8.3++ optimized. Отдельные `.svg`-файлы из архива удалены — их содержимое скопировать из §9.1–9.5.
|
||||
|
||||
---
|
||||
|
||||
## 1. Название
|
||||
|
||||
**Лидпоток** — кириллицей, **одно слово**, **без пробела и дефиса**.
|
||||
|
||||
| Контекст | Написание |
|
||||
|---|---|
|
||||
| Заголовок, подвал сайта, логотип | `Лидпоток` |
|
||||
| Транслит для домена и URL | `lidpotok` |
|
||||
| Английская версия (для международных контактов) | `Lidpotok` |
|
||||
| Email-домены | `@lidpotok.ru` (после регистрации домена) |
|
||||
|
||||
**Запрещено:**
|
||||
|
||||
- ❌ `Лид Поток` (с пробелом)
|
||||
- ❌ `Лид-поток` (с дефисом)
|
||||
- ❌ `ЛИДПОТОК` (капс)
|
||||
- ❌ `LeadFlow` или другие переводы (название не переводится)
|
||||
|
||||
**Род:** мужской («Лидпоток помогает», «Лидпоток вырос»).
|
||||
|
||||
---
|
||||
|
||||
## 2. Логотип
|
||||
|
||||
### 2.1. Структура
|
||||
|
||||
Логотип состоит из двух элементов:
|
||||
|
||||
- **Знак** — три круга нарастающего размера в зелёно-бирюзовой палитре. Метафора: поток лидов от больших к малым (или, при чтении справа налево, нарастающий поток).
|
||||
- **Текстовая часть** — название «Лидпоток» шрифтом Inter Bold (см. §4).
|
||||
|
||||
### 2.2. Версии
|
||||
|
||||
| Файл | Назначение | Минимальный размер |
|
||||
|---|---|---|
|
||||
| `lidpotok-logo-full.svg` | Основной — для веба, шапки сайта, документов, презентаций | ширина 120px / 30 мм |
|
||||
| `lidpotok-icon.svg` | Иконка без текста — для аватара, в кружке tabbar, в ячейке таблицы | 24×24 px / 6×6 мм |
|
||||
| `lidpotok-logo-mono.svg` | Чёрно-белая версия — для печатных счетов, договоров, факса | ширина 100px / 25 мм |
|
||||
| `lidpotok-logo-dark.svg` | Светлая версия для тёмного фона — admin-панель в dark mode, баннеры | ширина 120px / 30 мм |
|
||||
| `lidpotok-favicon.svg` | Favicon — оптимизирован для отображения в малом размере (есть фон-плашка) | 16×16 px |
|
||||
|
||||
### 2.3. Защитное поле (clear space)
|
||||
|
||||
Вокруг логотипа должно быть свободное пространство **не меньше высоты буквы Л** в текстовой части. Никакие графические элементы не должны заходить в это поле.
|
||||
|
||||
### 2.4. Что нельзя делать с логотипом
|
||||
|
||||
- ❌ Менять цвета знака на другие
|
||||
- ❌ Изменять пропорции (растягивать, сжимать)
|
||||
- ❌ Поворачивать
|
||||
- ❌ Добавлять обводки, тени, градиенты, эффекты
|
||||
- ❌ Помещать на сложный фоновый паттерн (без подложки)
|
||||
- ❌ Использовать растровую (PNG, JPG) версию там, где можно SVG
|
||||
- ❌ Переводить текстовую часть (`LeadFlow`, `Lidpotok` — нельзя)
|
||||
|
||||
### 2.5. Что можно
|
||||
|
||||
- ✅ Использовать только знак (без текста) при достаточной узнаваемости
|
||||
- ✅ Использовать монохром на чёрно-белых документах
|
||||
- ✅ Менять размер с сохранением пропорций
|
||||
- ✅ Помещать на однородный фон (белый, светло-серый, тёмный, бирюзовый)
|
||||
|
||||
---
|
||||
|
||||
## 3. Палитра
|
||||
|
||||
### 3.1. Основные цвета (фирменная палитра Teal)
|
||||
|
||||
| Слот | Имя | HEX | RGB | Применение |
|
||||
|---|---|---|---|---|
|
||||
| 900 | Teal 900 | `#04342C` | `4, 52, 44` | Текст на бирюзовых фонах, заголовки в брендовом стиле |
|
||||
| **600** | **Teal 600 — primary** | **`#0F6E56`** | **`15, 110, 86`** | **Основной цвет бренда**: текст логотипа, ключевые акценты, primary-кнопки |
|
||||
| 400 | Teal 400 | `#1D9E75` | `29, 158, 117` | Средний круг знака, hover-состояния, второй уровень акцентов |
|
||||
| 200 | Teal 200 | `#5DCAA5` | `93, 202, 165` | Большой круг знака, легкие акценты, badges |
|
||||
| 100 | Teal 100 | `#9FE1CB` | `159, 225, 203` | Подложки, info-блоки, состояния success на тёмном |
|
||||
| 50 | Teal 50 | `#E1F5EE` | `225, 245, 238` | Backgound для info/success-блоков, фон tooltip |
|
||||
|
||||
### 3.2. Нейтральная палитра
|
||||
|
||||
Используются нейтральные оттенки серого для текстов и фонов. Базируются на стандартной гамме Tailwind/Vuetify Slate-Gray.
|
||||
|
||||
| Слот | HEX | Применение |
|
||||
|---|---|---|
|
||||
| 900 | `#1A1A1A` | Основной текст (заголовки, body) на светлом фоне |
|
||||
| 700 | `#444441` | Secondary текст |
|
||||
| 500 | `#888780` | Tertiary текст, captions, placeholder |
|
||||
| 300 | `#B4B2A9` | Disabled-состояния, тонкие границы |
|
||||
| 200 | `#D3D1C7` | Границы input-полей, разделители |
|
||||
| 100 | `#F1EFE8` | Фон страницы, фон disabled-кнопок |
|
||||
| 50 | `#FAFAF8` | Фон карточек на светлом фоне страницы |
|
||||
| 0 | `#FFFFFF` | Базовый белый — фон карточек, modal, popup |
|
||||
|
||||
### 3.3. Семантические цвета
|
||||
|
||||
Стандартные оттенки для смысловых состояний (не из брендовой палитры).
|
||||
|
||||
| Назначение | Light HEX | Dark HEX | Применение |
|
||||
|---|---|---|---|
|
||||
| Success | `#22C55E` | `#15803D` | Подтверждения, «оплачено», «выполнено» |
|
||||
| Warning | `#F59E0B` | `#B45309` | Предупреждения, «истекает срок» |
|
||||
| Danger | `#EF4444` | `#B91C1C` | Ошибки, «отказано», «удалить» |
|
||||
| Info | `#3B82F6` | `#1D4ED8` | Информационные сообщения |
|
||||
|
||||
### 3.4. Контрастность WCAG
|
||||
|
||||
Все комбинации текст/фон должны соответствовать минимум WCAG 2.1 AA (4.5:1 для body, 3:1 для крупного текста).
|
||||
|
||||
| Комбинация | Контраст | WCAG |
|
||||
|---|---|---|
|
||||
| Teal 600 на белом | 6.42:1 | AA ✅ |
|
||||
| Teal 600 на Teal 50 | 6.05:1 | AA ✅ |
|
||||
| Белый на Teal 600 | 6.42:1 | AA ✅ |
|
||||
| Teal 200 на белом | 2.31:1 | ❌ — НЕ использовать для текста |
|
||||
|
||||
---
|
||||
|
||||
## 4. Типографика
|
||||
|
||||
### 4.1. Основной шрифт — Inter
|
||||
|
||||
**Inter** (open-source, Google Fonts). Поддерживает кириллицу, имеет полный набор weights, оптимизирован для UI.
|
||||
|
||||
```html
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap&subset=cyrillic" rel="stylesheet">
|
||||
```
|
||||
|
||||
```css
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
```
|
||||
|
||||
### 4.2. Иерархия
|
||||
|
||||
| Стиль | Размер | Weight | Line height | Применение |
|
||||
|---|---|---|---|---|
|
||||
| Display | 48px | 700 (Bold) | 1.1 | Hero-заголовки лендинга |
|
||||
| H1 | 32px | 700 (Bold) | 1.2 | Заголовки страниц |
|
||||
| H2 | 24px | 600 (SemiBold) | 1.3 | Разделы внутри страницы |
|
||||
| H3 | 20px | 600 (SemiBold) | 1.4 | Подразделы, заголовки карточек |
|
||||
| H4 | 16px | 600 (SemiBold) | 1.4 | Заголовки в списках, аналитика |
|
||||
| Body | 14px | 400 (Regular) | 1.5 | Основной текст |
|
||||
| Body Large | 16px | 400 (Regular) | 1.6 | Параграфы лендинга |
|
||||
| Caption | 12px | 400 (Regular) | 1.4 | Подписи, hint, footnote |
|
||||
| Code | 13px | 400 (Regular) | 1.5 | Inline-код, JSON-payload, ID |
|
||||
|
||||
### 4.3. Шрифт для кода
|
||||
|
||||
**JetBrains Mono** — для отображения JSON, API-ответов, ID транзакций, технической информации в админке SaaS.
|
||||
|
||||
```css
|
||||
font-family: 'JetBrains Mono', Menlo, Monaco, Consolas, monospace;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Размерная сетка и отступы
|
||||
|
||||
Базовая единица — **4px**. Все отступы кратны 4: 4, 8, 12, 16, 24, 32, 48, 64.
|
||||
|
||||
| Назначение | Значение |
|
||||
|---|---|
|
||||
| Padding кнопки small | 8 / 16 |
|
||||
| Padding кнопки medium | 12 / 20 |
|
||||
| Padding кнопки large | 16 / 24 |
|
||||
| Padding карточки | 16 / 20 / 24 |
|
||||
| Gap в Flex/Grid | 8 / 12 / 16 |
|
||||
| Высота input | 40px |
|
||||
| Высота кнопки medium | 40px |
|
||||
| Border-radius small | 6px |
|
||||
| Border-radius medium | 8px |
|
||||
| Border-radius large | 12px |
|
||||
| Border-radius pill | 999px |
|
||||
|
||||
---
|
||||
|
||||
## 6. Тон коммуникации (voice & tone)
|
||||
|
||||
### Голос бренда
|
||||
|
||||
- **Дружелюбный, но не панибратский.** «Здравствуйте» — да, «Привет, дружок» — нет.
|
||||
- **Профессиональный, но не сухой.** Коротко, по делу, без бюрократизмов («произвести оплату» → «оплатить»).
|
||||
- **Уверенный, но не агрессивный.** «Мы решили эту задачу» — да, «Только мы можем!» — нет.
|
||||
- **Прозрачный.** Не скрываем оговорки. Если есть условие — пишем явно.
|
||||
|
||||
### Примеры
|
||||
|
||||
| Контекст | ✅ Да | ❌ Нет |
|
||||
|---|---|---|
|
||||
| Подтверждение пополнения | «Баланс пополнен на 1 000 ₽. Списано: 0 лидов. Осталось: 20 лидов.» | «Платёж проведён успешно!» |
|
||||
| Ошибка авторизации | «Не получилось войти. Проверьте email и пароль.» | «Ошибка авторизации (код 401)» |
|
||||
| Отказ от лида | «Не хватает баланса. Пополните счёт, чтобы принимать новые заявки.» | «Платёж отклонён» |
|
||||
| Письмо при регистрации | «Привет, [имя]. Спасибо, что выбрали Лидпоток.» | «Уважаемый клиент, благодарим за регистрацию!» |
|
||||
|
||||
---
|
||||
|
||||
## 7. Применение в продукте
|
||||
|
||||
### 7.1. Веб-приложение
|
||||
|
||||
- **Логотип:** в шапке (top-left, высота 32px). Полная версия с текстом.
|
||||
- **Favicon:** 32×32 SVG.
|
||||
- **Primary-кнопки:** Teal 600 фон + белый текст.
|
||||
- **Secondary-кнопки:** прозрачный фон + Teal 600 граница и текст.
|
||||
- **Ссылки:** Teal 600, hover — Teal 400 с подчёркиванием.
|
||||
|
||||
### 7.2. Email-шаблоны
|
||||
|
||||
- **Header:** Teal 600 фон, логотип белой версии (logo-dark.svg).
|
||||
- **Body:** белый фон, текст Slate 900, ссылки Teal 600.
|
||||
- **Кнопка CTA:** Teal 600 фон, белый текст, padding 12/24.
|
||||
- **Footer:** Slate 100 фон, текст Slate 700 (12px).
|
||||
|
||||
### 7.3. PDF-документы (счета, УПД, договоры)
|
||||
|
||||
- **Логотип:** монохромная версия (logo-mono.svg) в верхнем-левом углу.
|
||||
- **Шрифт:** Inter (если есть в системе) или PT Sans как fallback.
|
||||
- **Цвета:** только чёрный текст на белом фоне (печать), без бирюзового — для экономии тонера и кросс-факсимильной читаемости.
|
||||
|
||||
### 7.4. Социальные сети
|
||||
|
||||
- **Аватар:** favicon в формате 1024×1024 (растеризовать SVG).
|
||||
- **Обложка:** Teal 600 фон + белый логотип + слоган (определить отдельно).
|
||||
|
||||
---
|
||||
|
||||
## 8. Интеграция в код
|
||||
|
||||
### 8.1. CSS-переменные (для frontend)
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* Brand */
|
||||
--color-brand-primary: #0F6E56;
|
||||
--color-brand-primary-hover: #1D9E75;
|
||||
--color-brand-primary-active: #04342C;
|
||||
--color-brand-light: #5DCAA5;
|
||||
--color-brand-bg: #E1F5EE;
|
||||
|
||||
/* Neutral */
|
||||
--color-text-primary: #1A1A1A;
|
||||
--color-text-secondary: #444441;
|
||||
--color-text-tertiary: #888780;
|
||||
--color-bg-page: #F1EFE8;
|
||||
--color-bg-card: #FFFFFF;
|
||||
--color-border-default: #D3D1C7;
|
||||
|
||||
/* Semantic */
|
||||
--color-success: #22C55E;
|
||||
--color-warning: #F59E0B;
|
||||
--color-danger: #EF4444;
|
||||
--color-info: #3B82F6;
|
||||
|
||||
/* Typography */
|
||||
--font-sans: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
--font-mono: 'JetBrains Mono', Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* Spacing */
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
|
||||
/* Radius */
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-pill: 999px;
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2. Vuetify 3 темa (для Vue-разработчика)
|
||||
|
||||
```js
|
||||
// theme.js
|
||||
import { createVuetify } from 'vuetify'
|
||||
|
||||
export default createVuetify({
|
||||
theme: {
|
||||
defaultTheme: 'lidpotokLight',
|
||||
themes: {
|
||||
lidpotokLight: {
|
||||
dark: false,
|
||||
colors: {
|
||||
primary: '#0F6E56',
|
||||
'primary-darken-1': '#04342C',
|
||||
'primary-lighten-1': '#1D9E75',
|
||||
secondary: '#5DCAA5',
|
||||
background: '#F1EFE8',
|
||||
surface: '#FFFFFF',
|
||||
'on-primary': '#FFFFFF',
|
||||
'on-surface': '#1A1A1A',
|
||||
success: '#22C55E',
|
||||
warning: '#F59E0B',
|
||||
error: '#EF4444',
|
||||
info: '#3B82F6',
|
||||
},
|
||||
},
|
||||
lidpotokDark: {
|
||||
dark: true,
|
||||
colors: {
|
||||
primary: '#5DCAA5',
|
||||
'primary-darken-1': '#1D9E75',
|
||||
'primary-lighten-1': '#9FE1CB',
|
||||
secondary: '#9FE1CB',
|
||||
background: '#1A1A1A',
|
||||
surface: '#2C2C2A',
|
||||
'on-primary': '#04342C',
|
||||
'on-surface': '#FFFFFF',
|
||||
success: '#15803D',
|
||||
warning: '#B45309',
|
||||
error: '#B91C1C',
|
||||
info: '#1D4ED8',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Исходники логотипа (inline SVG)
|
||||
|
||||
В рамках оптимизации архива v8.3++ optimized 05.05.2026 SVG-файлы логотипа интегрированы прямо в брендбук. Frontend-разработчик копирует нужный блок из соответствующего раздела ниже в свой проект, сохраняя как `.svg`-файл с указанным именем (имена сохранены — это тот же контент).
|
||||
|
||||
**Замечание для frontend:** все SVG используют `viewBox` (масштабируемые), `<title>` и `<desc>` для accessibility. Цвета вшиты в `fill` — для тематизации (CSS `color` вместо `fill`) можно заменить hex-значения на `currentColor` после копирования.
|
||||
|
||||
### 9.1. `lidpotok-logo-full.svg` — основной логотип
|
||||
|
||||
Знак (3 круга нарастающего размера) + текст «Лидпоток». Используется в шапке сайта, документах, презентациях, email-подписи. Минимальная ширина: 120 px / 30 мм.
|
||||
|
||||
```svg
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 60" width="240" height="60">
|
||||
<title>Лидпоток — полный логотип</title>
|
||||
<desc>Три круга нарастающего размера слева направо в зелёно-бирюзовой палитре, рядом название кириллицей</desc>
|
||||
<g transform="translate(8, 18)">
|
||||
<circle cx="6" cy="12" r="5" fill="#0F6E56"/>
|
||||
<circle cx="22" cy="12" r="8" fill="#1D9E75"/>
|
||||
<circle cx="44" cy="12" r="11" fill="#5DCAA5"/>
|
||||
</g>
|
||||
<text x="72" y="38" font-family="Inter, -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif" font-size="22" font-weight="600" fill="#0F6E56">Лидпоток</text>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### 9.2. `lidpotok-logo-mono.svg` — монохромная версия
|
||||
|
||||
Чёрный (`#1A1A1A`) на белом фоне. Используется в печатных счетах, договорах, факсе, ч/б распечатках. Минимальная ширина: 100 px / 25 мм.
|
||||
|
||||
```svg
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 60" width="240" height="60">
|
||||
<title>Лидпоток — монохромная версия</title>
|
||||
<desc>Логотип в чёрном цвете для печати на документах, счетах и факсе</desc>
|
||||
<g transform="translate(8, 18)">
|
||||
<circle cx="6" cy="12" r="5" fill="#1A1A1A"/>
|
||||
<circle cx="22" cy="12" r="8" fill="#1A1A1A"/>
|
||||
<circle cx="44" cy="12" r="11" fill="#1A1A1A"/>
|
||||
</g>
|
||||
<text x="72" y="38" font-family="Inter, -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif" font-size="22" font-weight="600" fill="#1A1A1A">Лидпоток</text>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### 9.3. `lidpotok-logo-dark.svg` — версия для тёмного фона
|
||||
|
||||
Светлая палитра (Mint 200/300/400 + белый текст) для admin-панели в dark mode, баннеров на тёмной плашке. Минимальная ширина: 120 px / 30 мм.
|
||||
|
||||
```svg
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 60" width="240" height="60">
|
||||
<title>Лидпоток — белая версия для тёмного фона</title>
|
||||
<desc>Логотип в белом цвете для использования на тёмном фоне</desc>
|
||||
<g transform="translate(8, 18)">
|
||||
<circle cx="6" cy="12" r="5" fill="#5DCAA5"/>
|
||||
<circle cx="22" cy="12" r="8" fill="#9FE1CB"/>
|
||||
<circle cx="44" cy="12" r="11" fill="#E1F5EE"/>
|
||||
</g>
|
||||
<text x="72" y="38" font-family="Inter, -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif" font-size="22" font-weight="600" fill="#FFFFFF">Лидпоток</text>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### 9.4. `lidpotok-icon.svg` — иконка без текста
|
||||
|
||||
Только знак (3 круга в Teal палитре). Используется в аватаре, в кружке tabbar, в ячейке таблицы. Размер 24×24 px / 6×6 мм. Также для растеризации в PNG-аватары (1024×1024).
|
||||
|
||||
```svg
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
||||
<title>Лидпоток — иконка</title>
|
||||
<desc>Три круга нарастающего размера в зелёно-бирюзовой палитре</desc>
|
||||
<g transform="translate(4, 22)">
|
||||
<circle cx="6" cy="12" r="5" fill="#0F6E56"/>
|
||||
<circle cx="22" cy="12" r="8" fill="#1D9E75"/>
|
||||
<circle cx="44" cy="12" r="11" fill="#5DCAA5"/>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### 9.5. `lidpotok-favicon.svg` — favicon с фон-плашкой
|
||||
|
||||
Оптимизирован для отображения в малом размере. Имеет фоновую плашку Teal 600 со скруглёнными углами (`rx=6`) — обеспечивает контраст в любой теме браузера. Размер 32×32 px (через `viewBox` масштабируется).
|
||||
|
||||
```svg
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
||||
<title>Лидпоток — favicon</title>
|
||||
<desc>Иконка для browser tab, favicon во всех размерах</desc>
|
||||
<rect width="32" height="32" rx="6" fill="#0F6E56"/>
|
||||
<circle cx="9" cy="22" r="2.5" fill="#5DCAA5"/>
|
||||
<circle cx="16" cy="19" r="3.5" fill="#9FE1CB"/>
|
||||
<circle cx="23" cy="14" r="4.5" fill="#E1F5EE"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### 9.6. Что нужно ещё сгенерировать перед запуском (вне Claude)
|
||||
|
||||
- `favicon.ico` (16/32/48 px) — растеризовать `lidpotok-favicon.svg` через https://realfavicongenerator.net.
|
||||
- `apple-touch-icon.png` (180×180) — для iOS-bookmark, на основе `lidpotok-favicon.svg`.
|
||||
- `og-image.png` (1200×630) — для open graph (соц. сети), на основе `lidpotok-logo-full.svg` + слоган.
|
||||
- Lottie-анимация загрузки на основе знака — опционально, для UX-полировки.
|
||||
- Печатные шаблоны фирменного бланка, визитки — после регистрации юр. лица (Б-1).
|
||||
|
||||
### 9.7. История файлов SVG
|
||||
|
||||
В архиве v8.3++ (до оптимизации) SVG-файлы лежали отдельно: `lidpotok-logo-full.svg`, `lidpotok-logo-mono.svg`, `lidpotok-logo-dark.svg`, `lidpotok-icon.svg`, `lidpotok-favicon.svg`. В оптимизированной версии v8.3++ optimized содержимое этих файлов перенесено в этот брендбук inline (см. §9.1–9.5). Frontend получает SVG копированием из markdown.
|
||||
|
||||
## 10. Версионирование и обновления
|
||||
|
||||
**v1.0 (04.05.2026)** — Исходный брендбук. Закрывает Диз-2. Утверждён в сессии планирования v8.2.1.
|
||||
|
||||
**v1.1 (05.05.2026)** — SVG-исходники логотипа (5 файлов) интегрированы inline в §9.1–9.5 в рамках оптимизации архива (объединение 6 файлов брендинга в 1). Содержимое всех 5 SVG сохранено без изменений — только перенесено в раздел `## 9. Исходники логотипа (inline SVG)`. Старый §9 «Файлы» (список имён) заменён.
|
||||
|
||||
**Последующие изменения:**
|
||||
|
||||
- Любые правки палитры, шрифтов, логотипа, тона коммуникации — через явный апдейт версии (v1.1, v2.0).
|
||||
- Изменения сохраняются в этом файле в разделе «История изменений».
|
||||
- Старые версии файлов (SVG) сохраняются в подкаталоге `/legacy/` для обратной совместимости.
|
||||
|
||||
---
|
||||
|
||||
*Конец документа brandbook.md v1.1*
|
||||
@@ -7,7 +7,7 @@
|
||||
- ✅ **DO-3 закрыт.** IP allow-list для админки SaaS — **отказались**, т.к. сотрудники работают распределённо. Вместо него — **5 компенсирующих мер безопасности**:
|
||||
1. **Rate-limit на `POST /admin/login`**: 5 попыток за 15 минут с одного IP, потом 30-минутная блокировка этого IP. Реализация: Laravel middleware `throttle:5,15`.
|
||||
2. **Капча** (Yandex SmartCaptcha — нативно в Yandex Cloud, без трансграничной передачи) появляется после **2 неудачных попыток** на форме логина.
|
||||
3. **Email-уведомление админу при входе с нового устройства/IP.** Сравнение по `(user_agent_hash, ip_subnet /24)`. Не блокирует, даёт сигнал. Шаблон: «Вход в админку Лидпоток с нового устройства. Если это были не вы — немедленно смените пароль и сообщите команде безопасности».
|
||||
3. **Email-уведомление админу при входе с нового устройства/IP.** Сравнение по `(user_agent_hash, ip_subnet /24)`. Не блокирует, даёт сигнал. Шаблон: «Вход в админку Лидерра с нового устройства. Если это были не вы — немедленно смените пароль и сообщите команде безопасности».
|
||||
4. **Все попытки логина (успешные и неудачные) пишутся в `auth_log`** с полями: `admin_user_id` (NULL если email не найден), `email_attempted`, `ip`, `user_agent`, `result` (`success`/`bad_password`/`bad_2fa`/`rate_limited`), `created_at`. Уже есть в схеме.
|
||||
5. **Алерт в Sentry/Slack `#sec-alerts`**, если за час замечено >20 неудачных попыток с разных IP на админку (атака подбором). Метрика собирается из `auth_log`.
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ POST /admin/visit/updateindicators — индикаторы (~раз в 10
|
||||
|
||||
Параметры таблицы передаются через GET-форму на тот же URL `/admin/visit/rt-stat` (action="GET", method="get") — то есть фильтры в query string, страница перезагружается полностью.
|
||||
|
||||
Для нашего «Лидпотока» это означает: на бэке нужен один read-эндпоинт `GET /api/projects/conversion?from=&to=&type=&search=` который возвращает массив проектов со счётчиками по каждому статусу. На фронте таблица может быть SPA, серверный рендер не обязателен.
|
||||
Для нашего «Лидерры» это означает: на бэке нужен один read-эндпоинт `GET /api/projects/conversion?from=&to=&type=&search=` который возвращает массив проектов со счётчиками по каждому статусу. На фронте таблица может быть SPA, серверный рендер не обязателен.
|
||||
|
||||
#### 12.1.7. Console
|
||||
|
||||
@@ -218,7 +218,7 @@ payload: { id: history.id }
|
||||
|
||||
Фактически (UX): **одно активное на сделку**. Карточка показывает все имеющиеся напоминания типа `reminder` в `histories`, но никакой пагинации/«Добавить ещё одно отдельно»/счётчика рядом с заголовком нет. Иконка `+` без ограничений, но в практике у наших 5 проверенных сделок было ровно 1 или 0 напоминаний.
|
||||
|
||||
Гипотеза партии 11 «одно напоминание = одна сделка» — **частично опровергнута**: схема данных — список (`histories`), не одиночное поле; в нашем «Лидпотоке» в модели можно (и стоит) делать `reminders[]`, а не `reminder_at + reminder_text`. Но UI оригинала показывает их как одну стопку без отдельных страничек, поэтому со стороны выглядит как «одиночное».
|
||||
Гипотеза партии 11 «одно напоминание = одна сделка» — **частично опровергнута**: схема данных — список (`histories`), не одиночное поле; в нашем «Лидерре» в модели можно (и стоит) делать `reminders[]`, а не `reminder_at + reminder_text`. Но UI оригинала показывает их как одну стопку без отдельных страничек, поэтому со стороны выглядит как «одиночное».
|
||||
|
||||
Дополнительно найдена связанная модалка **«Ближайшие напоминания»** (el-dialog, открывается через `otherReminders.show=true`): таблица из 3 колонок «Сделка / Напоминание / Время», **list cross-deal** — список ближайших напоминаний по всем сделкам пользователя. Это эквивалент дашборда «мои задачи».
|
||||
|
||||
@@ -239,7 +239,7 @@ payload: { id: history.id }
|
||||
|
||||
#### 12.2.7. Вывод для Биз-10
|
||||
|
||||
**Паритет с моделью «Лидпотока»** — рекомендуемая версия:
|
||||
**Паритет с моделью «Лидерры»** — рекомендуемая версия:
|
||||
|
||||
```
|
||||
reminder {
|
||||
@@ -352,7 +352,7 @@ response → visitdop.dops = response.data
|
||||
#### 12.3.5. Решение для нашего аналога
|
||||
|
||||
- **Модалка №1 (баз дублей):** **Пост-MVP**. У нас в Биз-1 предусмотрено объединение лидов по телефону, но как UI-секция в карточке сделки это можно отложить. Если делать — это таблица «другие сделки с этим телефоном» в side-panel.
|
||||
- **Модалка №2 (Pr-cy + досье):** **НЕ делаем в MVP**, оставляем в Post-MVP / Backlog. Эта функция требует интеграции с внешним API (Pr-cy / Контур / Rusprofile), что для Лидпотока на старте избыточно. Включить при наличии запроса от арбитражника-клиента.
|
||||
- **Модалка №2 (Pr-cy + досье):** **НЕ делаем в MVP**, оставляем в Post-MVP / Backlog. Эта функция требует интеграции с внешним API (Pr-cy / Контур / Rusprofile), что для Лидерры на старте избыточно. Включить при наличии запроса от арбитражника-клиента.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Объединённый конспект — SaaS-аналог crm.bp-gr.ru (Лидпоток)
|
||||
# Объединённый конспект — SaaS-аналог crm.bp-gr.ru (Лидерра)
|
||||
|
||||
## Хроника работы: от анализа v7.0 до архива v8.3.2 (03–05.05.2026)
|
||||
|
||||
@@ -863,7 +863,7 @@ CTO-1..5, Биз-1, Биз-2, Ю-1, Ю-2, Ю-3 + закрытые ранее в
|
||||
### Фаза 4 — Структурная оптимизация
|
||||
|
||||
**Триггер:** «оптимизируй документацию в части количества файлов, но без потери информации».
|
||||
**Дополнительно:** 5 SVG-логотипов (`lidpotok-favicon.svg`, `lidpotok-icon.svg`, `lidpotok-logo-dark.svg`, `lidpotok-logo-full.svg`, `lidpotok-logo-mono.svg`).
|
||||
**Дополнительно:** 5 SVG-логотипов (`liderra-favicon.svg`, `liderra-icon.svg`, `liderra-logo-dark.svg`, `liderra-logo-full.svg`, `liderra-logo-mono.svg`).
|
||||
|
||||
**Стратегия:** 3 варианта (A — агрессивное 27→12, B — умеренное 27→21, C — минимальное 27→23). Через `ask_user_input_v0` запрошен выбор.
|
||||
|
||||
@@ -1396,7 +1396,7 @@ OPEN-И-12 ⏸ Где хранить контакты эскалации (P1,
|
||||
|
||||
## 3. Миграция на Server 2022 (06–07.05.2026)
|
||||
|
||||
**Заказчик сменил рабочую машину.** Старый путь `c:\Users\KDV\Projects\lidpotok` (Win10) → `c:\моя\проекты\портал crm\Документация` (Server 2022 Administrator). Проект скопирован **без `.git`**.
|
||||
**Заказчик сменил рабочую машину.** Старый путь `c:\Users\KDV\Projects\liderra` (Win10) → `c:\моя\проекты\портал crm\Документация` (Server 2022 Administrator). Проект скопирован **без `.git`**.
|
||||
|
||||
**Установлено через Chocolatey 2.7.1** (winget на Server 2022 нет): git 2.54.0, Node.js 24.15.0 + npm 11.12.1, PHP 8.3.31 NTS x64, Composer 2.9.7. `node_modules` пересоздан `npm ci` (443 пакета, 54 сек).
|
||||
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
# Открытые вопросы к заказчику — Лидпоток (v8.3)
|
||||
# Открытые вопросы к заказчику — Лидерра (v8.3)
|
||||
|
||||
**Назначение:** единый рабочий список вопросов, требующих решения заказчика для разблокировки разработки. Разбит по адресатам, внутри — по приоритету.
|
||||
|
||||
**Версия:** 1.12 от 07.05.2026 (вечер) — закрыт раздел 13 «Аудит C»: все 27 вопросов решены вариантом A (рекомендованным). 8 P0 разблокировали триггер фазы 1; 12 P1 + 7 P2 — реализация в фазах 1–3 (см. §13.10).
|
||||
**Версия:** 1.13 от 08.05.2026 — зафиксирован ребрендинг «Лидпоток» → **«Лидерра.»** + интеграция дизайн-handoff Платона (v8 Forest). Подробности в блоке «Что изменилось в v1.13».
|
||||
|
||||
**Что изменилось в v1.13 относительно v1.12:**
|
||||
|
||||
- **Ребрендинг 08.05.2026** — продукт переименован в «Лидерра.» (с точкой). Решение принято заказчиком после получения handoff-пакета [liderra_v8_handoff/](../liderra_v8_handoff/) от дизайнера Платона (kpd9363@gmail.com) от 07.05.2026. Старое имя «Лидпоток» (v1.1 brandbook) — deprecated. Массовая замена выполнена в 33 файлах (449 вхождений) — все .md/.sql/.json/.toml/.yml/.txt/.html, кроме исторических упоминаний внутри `liderra_v8_handoff/`.
|
||||
- **Удалён** старый `docs/brandbook.md` v1.1. Источник истины бренда — `liderra_v8_handoff/docs/BRANDBOOK_v2.md` (v8 Forest).
|
||||
- **Добавлен дизайн-handoff** в [CLAUDE.md §0](../CLAUDE.md): `liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md` — токены, лого, типографика, компоненты. **Применяется только к дизайну/имени/логотипу.** Функционал, состав страниц, правила (CTO-11, click-wrap, SSO break-glass, 14 статусов воронки) — **остаются по ТЗ v8.5/schema v8.5**, расхождения handoff с ТЗ зафиксированы ниже.
|
||||
- **Скопированы 13 концептов** из `liderra_v8_handoff/concepts/v8_*.html` в `web/v8/`. Старые `web/01-login.html`, `02-dashboard.html`, `03-deals.html`, `index.html` (v1.1 палитра) удалены.
|
||||
- **Точечные расхождения handoff vs ТЗ**, которые НЕ применены (зафиксированы для возможной обратной связи Платону):
|
||||
- 14 «обобщённых» статусов в [BRANDBOOK_v2 §3.6](../liderra_v8_handoff/docs/BRANDBOOK_v2.md) ≠ 14 slug'ов в [schema.sql:2076](../db/schema.sql#L2076) (совпадает только 2 из 14: «Переговоры», «Оплачено»). **Источник — schema/ТЗ §6.4.**
|
||||
- 3-й click-wrap чекбокс в `v8_login.html` («маркетинг-опционально») ≠ ТЗ §1.5/§4.1 («согласие на ПДн», обязательное). **Источник — ТЗ.**
|
||||
- SSO в `v8_admin.html` («локальный 2FA fallback») ≠ ТЗ OPEN-И-13 (break-glass `super_admin`, локальный 2FA выключен). **Источник — ТЗ.**
|
||||
- Заявление «axe-core 4.10.2 — 0 violations на всех макетах» в [README handoff](../liderra_v8_handoff/README.md) — локальная проверка Pa11y 9.1.1 + axe нашла 81 violation на 10 из 13 HTML (преимущественно `color-contrast` на декоративных separator'ах с `--ink-disabled`). Чисто: settings/errors/palette_options.
|
||||
- **Сводка §0:** без изменений (67 ✅ / 5 🟦 / 5 ⏸). Диз-1 (HTML-прототипы у Claude) — формально остаётся ⏸ до явного «закрываем» от заказчика, но фактически покрыт handoff Платона на 100%+ (13 экранов > 8 запланированных).
|
||||
|
||||
> **⚠ Состояние на 06.05.2026 (v8.3.3):** все упоминания файлов `audit-batch-12..15-2026-05-05.md` в этом документе следует читать как ссылки на соответствующие **части 12, 13, 14, 15** объединённого документа `Аудит_partii_12_15_originala_v8_3.md` (объединение проведено 05.05.2026 в рамках оптимизации архива). Содержимое исходных партий полностью сохранено. Аналогично: ссылки на `CHANGELOG-v8_3.md` теперь — это **запись A** в объединённом `CHANGELOG_schema.md`; ссылки на `Konspekt_sessii_05_05_2026_obrabotka_*.md` теперь — это соответствующие **части V и VI** в `Объединённый_конспект.md` (`Konspekt_sessii_05_05_2026.md` удалён в шаге «v8.3++ optimized + правила Claude», информация перенесена в `Объединённый_конспект.md`).
|
||||
|
||||
@@ -187,7 +200,7 @@
|
||||
| ID | Вопрос | Статус |
|
||||
|----|--------|--------|
|
||||
| 🔧 Диз-1 | HTML/CSS/JS прототипы 8 экранов + 3 экрана ошибок | **В работе у Claude** → Прил. Л (отдельная сессия) |
|
||||
| ✅ **Диз-2** | **Название: Лидпоток. Логотип «Поток» (3 круга, бирюзовая палитра). Брендбук v1.0** | Закрыто 04.05 (см. brandbook.md) |
|
||||
| ✅ **Диз-2** | **Название: Лидерра. Логотип «Поток» (3 круга, бирюзовая палитра). Брендбук v1.0** | Закрыто 04.05 (см. brandbook.md) |
|
||||
| ⏸ Диз-3 | Контакты подвала (email, телефон, соцсети) | P1, ждёт Б-1 |
|
||||
| ✅ **Диз-4** | **Контент лендинга, FAQ — заглушки «Lorem ipsum»** на MVP, реальный контент к запуску от маркетолога | Закрыто 04.05 |
|
||||
|
||||
@@ -198,7 +211,7 @@
|
||||
| ID | Вопрос | Статус |
|
||||
|----|--------|--------|
|
||||
| ✅ **DO-1** | **Облачный провайдер — Yandex Cloud, регион ru-central1 (Москва)** | Закрыто 04.05, см. Прил. К |
|
||||
| ⏸ DO-2 | Основной домен `lidpotok.ru` + wildcard SSL | P1, регистрация после Б-1 (~200–500₽/год) |
|
||||
| ⏸ DO-2 | Основной домен `liderra.ru` + wildcard SSL | P1, регистрация после Б-1 (~200–500₽/год) |
|
||||
| ✅ **DO-3** | **IP allow-list для админки SaaS — НЕТ. Вместо: 5 компенсирующих мер** (rate-limit /admin/login 5 попыток/15 мин с IP, captcha после 2 неудач, email-уведомление при новом устройстве/IP, `auth_log` со всеми попытками, Sentry/Slack-алерт >20 неудач/час с разных IP) | Закрыто 04.05 |
|
||||
| ⏸ DO-4 | Список сотрудников SaaS с ролями (`super_admin`, `finance`, `support`, `compliance`, `dev_oncall`) | P1, ждёт Б-1 |
|
||||
| ✅ **DO-5** | **SSO для админов SaaS — Yandex 360** (нативно к Yandex Cloud) | Закрыто 04.05 |
|
||||
@@ -425,7 +438,7 @@ reminders {
|
||||
|
||||
**Контекст:** в оригинале (партия 13.2.2) есть аккаунтная (не проектная!) модалка «Желаемое кол-во номеров в день». Это ориентир для бэк-офиса ГЦК, не лимит.
|
||||
|
||||
**Решение (закрыто 06.05 в v1.10):** **добавляем** как `tenants.desired_daily_numbers INT NULL`. Полезный сигнал саппорту Лидпотока для проактивных предложений. Затраты ~0.1 спринта.
|
||||
**Решение (закрыто 06.05 в v1.10):** **добавляем** как `tenants.desired_daily_numbers INT NULL`. Полезный сигнал саппорту Лидерры для проактивных предложений. Затраты ~0.1 спринта.
|
||||
|
||||
См. Прил. М §3.5.3. В админке `/admin/tenants` появляется колонка «Желаемое × факт сегодня».
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# =============================================================================
|
||||
# lefthook.yml — Лидпоток (фаза 0)
|
||||
# lefthook.yml — Лидерра (фаза 0)
|
||||
# =============================================================================
|
||||
# Документация: https://lefthook.dev
|
||||
# Установка хуков: npx lefthook install
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# Лидерра · v8 Forest · Handoff пакет
|
||||
|
||||
**Дата сборки:** 2026-05-08
|
||||
**Версия дизайн-системы:** v8 Forest
|
||||
**WCAG:** 2.1 AA verified (axe-core 4.10.2, 0 violations на всех макетах)
|
||||
|
||||
---
|
||||
|
||||
## С чего начать
|
||||
|
||||
1. **Открой `docs/index.html` в браузере** — это визуальный hub со ссылками на все макеты, токены и документацию.
|
||||
2. **Прочитай `docs/DEVELOPER_HANDOFF.md`** — главный технический документ (стек, токены, компоненты, все 25 экранов).
|
||||
3. **Прочитай `docs/BRANDBOOK_v2.md`** — бренд (имя, лого, палитра, типографика).
|
||||
|
||||
---
|
||||
|
||||
## Структура архива
|
||||
|
||||
```
|
||||
liderra_v8_handoff/
|
||||
├── README.md ← этот файл
|
||||
├── docs/
|
||||
│ ├── DEVELOPER_HANDOFF.md ← главный технический документ (16 разделов)
|
||||
│ ├── BRANDBOOK_v2.md ← бренд (source of truth)
|
||||
│ └── index.html ← визуальный hub со ссылками
|
||||
├── concepts/
|
||||
│ ├── v8_dashboard.html ← /dashboard
|
||||
│ ├── v8_deals.html ← /deals (таблица + drawer)
|
||||
│ ├── v8_deal_card.html ← /deals/{id} (полная страница)
|
||||
│ ├── v8_kanban.html ← /deals/kanban (14 колонок DnD)
|
||||
│ ├── v8_billing.html ← /billing
|
||||
│ ├── v8_settings.html ← /settings (8 вкладок)
|
||||
│ ├── v8_reports.html ← /reports
|
||||
│ ├── v8_login.html ← /login + 4 других состояния
|
||||
│ ├── v8_errors.html ← 404/403/500
|
||||
│ ├── v8_admin.html ← админка SaaS (6 экранов)
|
||||
│ ├── v8_landing.html ← главная + тарифы + оферта + политика
|
||||
│ ├── v8_brand.html ← архив выбора имени и знака
|
||||
│ └── v8_palette_options.html ← архив выбора палитры (5 вариантов)
|
||||
├── scripts/
|
||||
│ ├── palette_v7.py ← OKLCH-расчёт основных токенов с WCAG
|
||||
│ ├── palette_14.py ← 14-статусная палитра (ΔE2000 ≥ 10.57)
|
||||
│ └── palette_options.py ← 5 палитр на сравнение
|
||||
└── screenshots/
|
||||
├── v8_dashboard_1680.png ← desktop preview каждого экрана
|
||||
├── v8_dashboard_375.png ← mobile preview
|
||||
└── ... (все ключевые v8 PNG)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Как запустить локально
|
||||
|
||||
```bash
|
||||
# Любой статический сервер из папки архива:
|
||||
cd liderra_v8_handoff
|
||||
python -m http.server 8765
|
||||
# Открыть http://localhost:8765/docs/index.html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Что разработчик может сделать без дизайнера
|
||||
|
||||
1. Скопировать токены из `DEVELOPER_HANDOFF.md §3` → `assets/tokens.css`
|
||||
2. Скопировать SVG-логотип из `BRANDBOOK_v2.md §2.1` → компонент `BrandMark.vue`
|
||||
3. Снять разметку напрямую с любого `concepts/v8_*.html` (production-ready)
|
||||
4. Использовать готовую 14-статусную палитру из `BRANDBOOK §3.6`
|
||||
5. Перерасчитать палитру через `scripts/palette_*.py` если нужны новые статусы
|
||||
6. Прогнать axe-core 4.10.2 на каждом PR (команда в HANDOFF §13.2)
|
||||
|
||||
---
|
||||
|
||||
## Окружение для проверки
|
||||
|
||||
- **Node** 18+ (для возможной интеграции Playwright/axe)
|
||||
- **Python** 3.10+ с пакетами `colour-science`, `numpy` (для regen палитр)
|
||||
- **Любой браузер** для просмотра HTML (Chromium-семейство для axe DevTools)
|
||||
|
||||
---
|
||||
|
||||
## Связь
|
||||
|
||||
| Что | Куда |
|
||||
|---|---|
|
||||
| Вопросы по дизайну | через `docs/DEVELOPER_HANDOFF.md §14 FAQ` |
|
||||
| Дизайнер | `kpd9363@gmail.com` (Платон) |
|
||||
| Заказчик | Дмитрий |
|
||||
|
||||
---
|
||||
|
||||
*Конец `README.md`. Версия архива v8 Forest, собрано 2026-05-08.*
|
||||
@@ -0,0 +1,544 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Админка SaaS — Лидерра (Forest)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C; --ink-disabled:#92907B;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-bg:#012019; --side-text:#B1C2BD; --side-text-2:#7A8C87;
|
||||
--side-active:#13382F; --side-icon:#5C7A72; --side-icon-act:#32C8A9;
|
||||
--side-hover:#0A2A22; --side-border:#1A3A30;
|
||||
--st-paid:#007EB8; --st-quote:#008A4D; --st-call:#9A6700; --st-new:#B94837; --st-fail:#6C60C4;
|
||||
--r-xs:4px; --r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
--imp-red: #B94837;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
button { font-family:inherit; }
|
||||
a { color:inherit; text-decoration:none; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
|
||||
}
|
||||
|
||||
/* Review-only state switcher */
|
||||
.review-bar { position:sticky; top:0; z-index:100; background:var(--side-bg); padding:10px 16px; display:flex; align-items:center; gap:14px; border-bottom:1px solid #1A3A30; flex-wrap:wrap; }
|
||||
.review-bar .label { font-size:11px; font-weight:600; letter-spacing:0.06em; color:#7A8C87; font-family:var(--font-mono); }
|
||||
.review-bar .tabs { display:flex; gap:2px; flex-wrap:wrap; }
|
||||
.review-bar .tab { height:26px; padding:0 12px; border:none; background:transparent; color:#B1C2BD; font-family:inherit; font-size:12px; font-weight:500; cursor:pointer; border-radius:4px; }
|
||||
.review-bar .tab:hover { background:#0A2A22; color:#fff; }
|
||||
.review-bar .tab.active { background:#fff; color:#012019; }
|
||||
|
||||
/* Impersonation banner — only visible on tenant-card-imp */
|
||||
.imp-banner {
|
||||
background: var(--imp-red); color: #fff;
|
||||
padding: 9px 24px;
|
||||
display: flex; align-items: center; gap: 12px; justify-content: space-between;
|
||||
font-size: 12.5px;
|
||||
position: sticky; top: 47px; z-index: 90;
|
||||
}
|
||||
.imp-banner svg { width: 14px; height: 14px; }
|
||||
.imp-banner strong { font-weight: 600; }
|
||||
.imp-banner button {
|
||||
height: 24px; padding: 0 10px;
|
||||
border: 1px solid rgba(255,255,255,0.30);
|
||||
background: rgba(255,255,255,0.10);
|
||||
color: #fff;
|
||||
font-family: inherit; font-size: 11px; font-weight: 500;
|
||||
border-radius: 4px; cursor: pointer;
|
||||
}
|
||||
.imp-banner button:hover { background: rgba(255,255,255,0.18); }
|
||||
|
||||
/* App shell — same Forest with admin variation */
|
||||
.app { display:grid; grid-template-columns:232px 1fr; min-height:calc(100vh - 47px); }
|
||||
.side { background:var(--side-bg); border-right:1px solid var(--side-border); padding:18px 12px 24px; position:sticky; top:47px; height:calc(100vh - 47px); overflow-y:auto; color:var(--side-text); }
|
||||
.brand { display:flex; align-items:center; gap:10px; padding:6px 8px 4px; font-weight:600; font-size:14.5px; letter-spacing:-0.01em; font-variation-settings:'opsz' 18; color:#FFF; }
|
||||
.brand-mark { width:22px; height:22px; border-radius:var(--r-xs); background:#FFF; display:inline-flex; align-items:center; justify-content:center; flex-shrink:0; overflow:hidden; }
|
||||
.brand-mark svg { width:100%; height:100%; display:block; }
|
||||
.brand-dot { color:var(--side-icon-act); }
|
||||
.brand-sub { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; color: var(--side-icon-act); padding: 0 8px 16px; font-weight: 600; }
|
||||
.nav { display:flex; flex-direction:column; gap:1px; }
|
||||
.nav-eyebrow { font-size:11px; font-weight:500; letter-spacing:0.01em; color:var(--side-text-2); padding:14px 10px 6px; }
|
||||
.nav-item { display:flex; align-items:center; gap:10px; height:32px; padding:0 10px; border-radius:var(--r-sm); font-size:13px; color:var(--side-text); cursor: pointer; }
|
||||
.nav-item:hover { background:var(--side-hover); color:#FFF; }
|
||||
.nav-item.active { background:var(--side-active); color:#FFF; font-weight:500; }
|
||||
.nav-item.active .nav-icon { color:var(--side-icon-act); }
|
||||
.nav-icon { width:15px; height:15px; flex-shrink:0; color:var(--side-icon); stroke-width:1.6; }
|
||||
.nav-item:hover .nav-icon { color:#FFF; }
|
||||
.nav-text { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.nav-count { font-family:var(--font-mono); font-size:10.5px; font-weight:500; font-feature-settings:'tnum'; background:rgba(255,255,255,0.10); color:var(--side-text); padding:2px 6px; border-radius:4px; }
|
||||
|
||||
.main { display:flex; flex-direction:column; min-width:0; }
|
||||
.topbar { height:48px; border-bottom:1px solid var(--hairline); background:var(--surface); padding:0 24px; display:flex; align-items:center; gap:12px; position:sticky; top:47px; z-index:50; }
|
||||
.crumb { font-size:12.5px; color:var(--ink-3); display:flex; align-items:center; gap:8px; }
|
||||
.crumb strong { color:var(--ink); font-weight:500; }
|
||||
.crumb svg { width:11px; height:11px; color:var(--ink-3); opacity:0.5; }
|
||||
.crumb a { cursor:pointer; }
|
||||
.crumb a:hover { color:var(--ink); }
|
||||
.topbar-spacer { flex:1; }
|
||||
.user-chip { display:inline-flex; align-items:center; gap:8px; height:30px; padding:0 10px 0 4px; border-radius:100px; border:1px solid var(--hairline); background:var(--surface); }
|
||||
.user-chip .ava { width:22px; height:22px; border-radius:50%; background:var(--ink); color:#fff; display:inline-flex; align-items:center; justify-content:center; font-size:9.5px; font-weight:600; }
|
||||
.user-chip .uname { font-size:12px; color:var(--ink); font-weight:500; }
|
||||
|
||||
.content { padding:24px 28px 60px; flex:1; max-width: 1700px; margin: 0 auto; width:100%; }
|
||||
.page-h { display:flex; align-items:flex-end; justify-content:space-between; margin-bottom:18px; gap:16px; flex-wrap: wrap; }
|
||||
.page-title { font-size:26px; font-weight:600; font-variation-settings:'opsz' 26; letter-spacing:-0.02em; line-height:1.1; margin:0 0 6px; }
|
||||
.page-meta { font-size:12.5px; color:var(--ink-3); display:flex; gap:14px; align-items:center; flex-wrap:wrap; }
|
||||
.page-meta .num { font-family:var(--font-mono); font-feature-settings:'tnum'; color:var(--ink-2); font-weight:500; }
|
||||
.page-meta .sep { color:var(--ink-disabled); }
|
||||
|
||||
.btn { display:inline-flex; align-items:center; gap:7px; height:34px; padding:0 14px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:12.5px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; }
|
||||
.btn:hover { border-color:var(--ink-disabled); background:var(--sunken); }
|
||||
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
|
||||
.btn-danger { color:var(--st-new); }
|
||||
.btn-danger:hover { background:#FFE7E2; border-color:var(--st-new); color:#8E2516; }
|
||||
.btn svg { width:13px; height:13px; stroke-width:1.7; }
|
||||
|
||||
/* SSO login screen */
|
||||
.sso-shell { min-height:calc(100vh - 47px); display:grid; grid-template-columns:1fr 1fr; }
|
||||
.sso-brand { background:var(--side-bg); color:#fff; padding:56px 60px; display:flex; flex-direction:column; justify-content:space-between; }
|
||||
.sso-brand .head { display:flex; align-items:center; gap:10px; font-weight:600; font-size:16px; }
|
||||
.sso-brand .head .mark { width:24px; height:24px; border-radius:5px; background:#fff; display:inline-flex; align-items:center; justify-content:center; overflow:hidden; }
|
||||
.sso-brand .head .mark svg { width:100%; height:100%; }
|
||||
.sso-brand .head .dot { color:var(--side-icon-act); }
|
||||
.sso-brand .head .admin-tag { font-family:var(--font-mono); font-size:10px; letter-spacing:0.06em; padding:2px 6px; border-radius:3px; background:var(--imp-red); color:#fff; margin-left:auto; font-weight:600; }
|
||||
.sso-brand .body { font-size:30px; font-weight:500; font-variation-settings:'opsz' 28; letter-spacing:-0.02em; line-height:1.2; max-width:440px; }
|
||||
.sso-brand .body em { color: var(--side-icon-act); font-style: normal; }
|
||||
.sso-brand .foot { font-size:12px; color:#7A8C87; font-family:var(--font-mono); }
|
||||
.sso-form { background:var(--bg); display:flex; align-items:center; justify-content:center; padding:40px 32px; }
|
||||
.sso-card { width:100%; max-width:380px; display:flex; flex-direction:column; gap:18px; }
|
||||
.sso-card h1 { font-size:24px; font-weight:600; font-variation-settings:'opsz' 24; letter-spacing:-0.018em; margin:0 0 6px; line-height:1.2; }
|
||||
.sso-card p { font-size:12.5px; color:var(--ink-3); line-height:1.5; margin:0; }
|
||||
.sso-button {
|
||||
display:flex; align-items:center; justify-content:center; gap:8px;
|
||||
width:100%;
|
||||
height:48px; padding:0 18px;
|
||||
border:1px solid var(--ink); border-radius:var(--r-sm);
|
||||
background:var(--ink); color:#fff;
|
||||
font-size:13.5px; font-weight:500;
|
||||
cursor:pointer; font-family:inherit;
|
||||
letter-spacing:-0.005em;
|
||||
}
|
||||
.sso-button:hover { background:#000; }
|
||||
.sso-button .y-ico {
|
||||
width:20px; height:20px;
|
||||
background:#FF0000; color:#fff;
|
||||
border-radius:50%;
|
||||
display:inline-flex; align-items:center; justify-content:center;
|
||||
font-weight:700; font-size:11px;
|
||||
font-family:Inter,sans-serif;
|
||||
}
|
||||
.sso-fallback { font-size:11.5px; color:var(--ink-3); text-align:center; }
|
||||
.sso-fallback a { color:var(--accent); font-weight:500; }
|
||||
.sso-fallback a:hover { text-decoration:underline; }
|
||||
|
||||
/* Tenants table */
|
||||
.t-controls { display:flex; align-items:center; gap:10px; margin-bottom:14px; flex-wrap:wrap; }
|
||||
.search-input { display:inline-flex; align-items:center; gap:8px; flex:1; min-width:240px; height:34px; padding:0 12px; border:1px solid var(--hairline); border-radius:var(--r-sm); background:var(--surface); }
|
||||
.search-input svg { width:13px; height:13px; color:var(--ink-3); }
|
||||
.search-input input { flex:1; min-width:0; border:none; outline:none; background:none; font-family:inherit; font-size:13px; color:var(--ink); }
|
||||
.search-input input::placeholder { color:var(--ink-3); }
|
||||
.fbtn { height:34px; padding:0 12px; border:1px solid var(--hairline); border-radius:var(--r-sm); background:var(--surface); font-family:inherit; font-size:12px; color:var(--ink-2); cursor:pointer; display:inline-flex; align-items:center; gap:6px; }
|
||||
.fbtn:hover { border-color:var(--ink-disabled); }
|
||||
.fbtn svg { width:12px; height:12px; color:var(--ink-3); }
|
||||
|
||||
.tbl-wrap { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); overflow:hidden; }
|
||||
.tbl { width:100%; border-collapse:collapse; font-size:12.5px; }
|
||||
.tbl thead th { text-align:left; font-size:10.5px; font-weight:600; color:var(--ink-2); padding:10px 14px; background:var(--sunken); border-bottom:1px solid var(--hairline); white-space:nowrap; letter-spacing:0.005em; }
|
||||
.tbl thead th.num { text-align:right; }
|
||||
.tbl tbody tr { border-bottom:1px solid var(--hairline-soft); cursor:pointer; }
|
||||
.tbl tbody tr:last-child { border-bottom:none; }
|
||||
.tbl tbody tr:hover { background:var(--sunken); }
|
||||
.tbl tbody td { padding:10px 14px; vertical-align:middle; }
|
||||
.tbl .t-name { font-weight:500; color:var(--ink); }
|
||||
.tbl .t-name .sub { display:block; font-size:11px; color:var(--ink-3); margin-top:2px; font-family:var(--font-mono); font-feature-settings:'tnum'; }
|
||||
.tbl .t-balance, .tbl .t-today, .tbl .t-mr { font-family:var(--font-mono); font-feature-settings:'tnum'; text-align:right; }
|
||||
.tbl .t-balance.low { color:var(--st-new); font-weight:500; }
|
||||
.tbl .t-since { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11px; color:var(--ink-3); white-space:nowrap; }
|
||||
|
||||
.chip { display:inline-flex; align-items:center; gap:6px; font-size:11.5px; font-weight:500; }
|
||||
.chip .dot { width:6px; height:6px; border-radius:50%; position:relative; }
|
||||
.chip .dot::after { content:''; position:absolute; inset:-1px; border-radius:50%; border:1px solid rgba(10,19,25,0.10); }
|
||||
.chip-active .dot { background:var(--st-quote); }
|
||||
.chip-trial .dot { background:var(--st-paid); }
|
||||
.chip-overdue .dot { background:var(--st-new); }
|
||||
.chip-suspended .dot { background:var(--ink-disabled); }
|
||||
|
||||
/* Tenant card */
|
||||
.tc-row { display:grid; grid-template-columns:1fr 360px; gap:16px; }
|
||||
.tc-main { display:flex; flex-direction:column; gap:14px; }
|
||||
.tc-aside { display:flex; flex-direction:column; gap:14px; }
|
||||
.tc-head {
|
||||
background:var(--surface); border:1px solid var(--hairline);
|
||||
border-radius:var(--r-md); padding:22px 24px;
|
||||
}
|
||||
.tc-name { font-size:24px; font-weight:600; font-variation-settings:'opsz' 24; letter-spacing:-0.018em; margin:0 0 6px; }
|
||||
.tc-meta { font-size:12.5px; color:var(--ink-3); display:flex; gap:12px; flex-wrap:wrap; }
|
||||
.tc-meta .id { font-family:var(--font-mono); color:var(--accent); font-weight:600; }
|
||||
.tc-meta .sep { color:var(--ink-disabled); }
|
||||
.tc-actions { display:flex; gap:8px; margin-top:14px; flex-wrap:wrap; }
|
||||
.tc-actions .imp { background:var(--imp-red); color:#fff; border-color:var(--imp-red); }
|
||||
.tc-actions .imp:hover { background:#8E2516; border-color:#8E2516; }
|
||||
|
||||
.tc-section { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); padding:18px 22px; }
|
||||
.tc-section h2 { font-size:13px; font-weight:600; color:var(--ink-3); letter-spacing:0.005em; text-transform:uppercase; margin:0 0 12px; }
|
||||
|
||||
.kv-grid { display:grid; grid-template-columns:120px 1fr; gap:9px 14px; font-size:13px; }
|
||||
.kv-grid .k { color:var(--ink-3); font-size:12px; }
|
||||
.kv-grid .v { color:var(--ink); }
|
||||
.kv-grid .v.mono { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:12px; }
|
||||
|
||||
.adj-form { display:grid; grid-template-columns:120px 1fr auto; gap:8px; align-items:center; margin-bottom:8px; }
|
||||
.adj-form .input { height:34px; padding:0 10px; border:1px solid var(--hairline); border-radius:var(--r-sm); font-family:var(--font-mono); font-size:13px; }
|
||||
|
||||
.timeline-mini { display:flex; flex-direction:column; gap:12px; padding-left:16px; position:relative; }
|
||||
.timeline-mini::before { content:''; position:absolute; left:5px; top:6px; bottom:6px; width:1px; background:var(--hairline); }
|
||||
.tm-item { position:relative; }
|
||||
.tm-item::before { content:''; position:absolute; left:-16px; top:4px; width:9px; height:9px; border-radius:50%; background:var(--surface); border:1.5px solid var(--ink-disabled); }
|
||||
.tm-item.acc::before { background:var(--accent); border-color:var(--accent); }
|
||||
.tm-item.warn::before { background:var(--st-call); border-color:var(--st-call); }
|
||||
.tm-item.dang::before { background:var(--st-new); border-color:var(--st-new); }
|
||||
.tm-when { font-family:var(--font-mono); font-size:10.5px; color:var(--ink-3); }
|
||||
.tm-text { font-size:12px; color:var(--ink); margin-top:2px; line-height:1.45; }
|
||||
|
||||
/* Incidents */
|
||||
.inc-row { display:grid; grid-template-columns:auto 100px 1fr 120px auto; gap:14px; padding:11px 18px; border-bottom:1px solid var(--hairline-soft); align-items:center; font-size:12.5px; }
|
||||
.inc-row:last-child { border-bottom:none; }
|
||||
.inc-row:hover { background:var(--sunken); cursor:pointer; }
|
||||
.inc-id { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11px; color:var(--accent); font-weight:500; }
|
||||
.inc-when { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11.5px; color:var(--ink-3); }
|
||||
.inc-title { color:var(--ink); }
|
||||
.inc-title .scope { display:block; font-size:11px; color:var(--ink-3); margin-top:2px; }
|
||||
.inc-tenant { font-size:11.5px; color:var(--ink-2); }
|
||||
.inc-sev { padding:2px 8px; border-radius:100px; font-size:10.5px; font-weight:600; letter-spacing:0.04em; font-family:var(--font-mono); }
|
||||
.inc-sev.high { background:#FFE7E2; color:#8E2516; }
|
||||
.inc-sev.med { background:#FFF4DD; color:#7B4D00; }
|
||||
.inc-sev.low { background:var(--accent-tint); color:var(--accent-deep); }
|
||||
|
||||
/* System cards */
|
||||
.sys-grid { display:grid; grid-template-columns:repeat(3, 1fr); gap:14px; }
|
||||
.sys-card { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); padding:18px 20px; cursor:pointer; }
|
||||
.sys-card:hover { border-color:var(--ink-disabled); }
|
||||
.sys-card .sc-h { display:flex; align-items:center; gap:10px; margin-bottom:8px; }
|
||||
.sys-card .sc-ico { width:30px; height:30px; border-radius:var(--r-sm); background:var(--bg); display:inline-flex; align-items:center; justify-content:center; color:var(--accent); }
|
||||
.sys-card .sc-ico svg { width:14px; height:14px; stroke-width:1.7; }
|
||||
.sys-card .sc-name { font-size:14px; font-weight:600; letter-spacing:-0.012em; color:var(--ink); }
|
||||
.sys-card .sc-desc { font-size:12px; color:var(--ink-3); line-height:1.45; }
|
||||
.sys-card .sc-meta { font-size:11px; color:var(--ink-3); margin-top:10px; font-family:var(--font-mono); font-feature-settings:'tnum'; letter-spacing:-0.005em; }
|
||||
.sys-card .sc-meta strong { color:var(--ink-2); font-weight:500; }
|
||||
|
||||
.tab-page { display:none; }
|
||||
.tab-page.active { display:contents; }
|
||||
|
||||
@media (max-width:1100px) {
|
||||
.app { grid-template-columns:56px 1fr; }
|
||||
.side { padding:14px 6px; }
|
||||
.brand-sub, .brand .brand-text, .nav-eyebrow, .nav-text, .nav-item:not(.active) .nav-count { display:none; }
|
||||
.nav-item { justify-content:center; padding:0; }
|
||||
.topbar { padding:0 16px; }
|
||||
.content { padding:18px 18px 60px; }
|
||||
.tc-row { grid-template-columns:1fr; }
|
||||
.sys-grid { grid-template-columns:repeat(2, 1fr); }
|
||||
.sso-shell { grid-template-columns:1fr; }
|
||||
.sso-brand { padding:32px 24px; min-height:200px; }
|
||||
}
|
||||
@media (max-width:768px) {
|
||||
.app { grid-template-columns:1fr; }
|
||||
.side { display:none; }
|
||||
.topbar .crumb span:first-child { display:none; }
|
||||
.sys-grid { grid-template-columns:1fr; }
|
||||
.tbl thead { display:none; }
|
||||
.tbl, .tbl tbody, .tbl tr, .tbl td { display:block; width:100%; }
|
||||
.tbl tbody tr { padding:12px; border:1px solid var(--hairline-soft); border-radius:var(--r-sm); margin-bottom:8px; }
|
||||
.tbl tbody td { padding:3px 0; border:none; }
|
||||
.inc-row { grid-template-columns:1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="review-bar" aria-label="Состояние админ-экрана (только для review)">
|
||||
<span class="label">PREVIEW · ADMIN</span>
|
||||
<div class="tabs" role="tablist">
|
||||
<button type="button" class="tab active" data-tab="login" role="tab" aria-selected="true">SSO Вход</button>
|
||||
<button type="button" class="tab" data-tab="tenants" role="tab" aria-selected="false">Тенанты</button>
|
||||
<button type="button" class="tab" data-tab="tenant-card" role="tab" aria-selected="false">Карточка тенанта</button>
|
||||
<button type="button" class="tab" data-tab="impersonation" role="tab" aria-selected="false">Импersonation-режим</button>
|
||||
<button type="button" class="tab" data-tab="incidents" role="tab" aria-selected="false">Инциденты</button>
|
||||
<button type="button" class="tab" data-tab="system" role="tab" aria-selected="false">Система</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- ===== ADMIN/LOGIN — SSO ===== -->
|
||||
<section id="page-login" class="tab-page active">
|
||||
<div class="sso-shell">
|
||||
<aside class="sso-brand" aria-label="Брендинг">
|
||||
<div class="head">
|
||||
<span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span>Лидерра<span class="dot">.</span></span>
|
||||
<span class="admin-tag">ADMIN</span>
|
||||
</div>
|
||||
<div class="body">Операторская <em>SaaS-инструмент</em>.<br>Управление тенантами, биллингом, инцидентами.</div>
|
||||
<div class="foot">v8 Forest · ограниченный доступ · все действия логируются в saas_admin_audit_log</div>
|
||||
</aside>
|
||||
<main class="sso-form">
|
||||
<div class="sso-card">
|
||||
<h1>Вход в админку</h1>
|
||||
<p>SSO через Yandex 360 — основной путь. 2FA-fallback в крайнем случае.</p>
|
||||
<button type="button" class="sso-button">
|
||||
<span class="y-ico">Я</span>
|
||||
Войти через Yandex 360
|
||||
</button>
|
||||
<div style="display:flex;align-items:center;gap:12px;margin:4px 0;color:var(--ink-3);font-size:11px"><span style="flex:1;height:1px;background:var(--hairline)"></span>или<span style="flex:1;height:1px;background:var(--hairline)"></span></div>
|
||||
<button type="button" class="btn" style="height:42px;justify-content:center;width:100%">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
Локальный пароль + 2FA
|
||||
</button>
|
||||
<div class="sso-fallback">
|
||||
Лимит 5 попыток / 15 минут · capture после 2 неудач · email при новом устройстве. <a href="#">Документация</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== ADMIN/TENANTS — list ===== -->
|
||||
<section id="page-tenants" class="tab-page">
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand">
|
||||
<span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span>Лидерра<span class="brand-dot">.</span></span>
|
||||
</div>
|
||||
<div class="brand-sub">ADMIN</div>
|
||||
<nav class="nav">
|
||||
<div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/></svg><span class="nav-text">Тенанты</span><span class="nav-count">142</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg><span class="nav-text">Инциденты</span><span class="nav-count">3</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Система</span></div>
|
||||
</nav>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Админка</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><strong>Тенанты</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<span class="user-chip"><span class="ava">АО</span><span class="uname">Админ Оператор</span></span>
|
||||
</div>
|
||||
<main class="content">
|
||||
<header class="page-h">
|
||||
<div>
|
||||
<h1 class="page-title">Тенанты</h1>
|
||||
<div class="page-meta">
|
||||
<span><span class="num">142</span> всего</span><span class="sep">·</span>
|
||||
<span><span class="num">128</span> активны</span><span class="sep">·</span>
|
||||
<span><span class="num">9</span> trial</span><span class="sep">·</span>
|
||||
<span><span class="num">5</span> просрочка</span><span class="sep">·</span>
|
||||
<span>выручка месяц <span class="num">1 248 600 ₽</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px"><button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/></svg>Экспорт</button></div>
|
||||
</header>
|
||||
<div class="t-controls">
|
||||
<label class="search-input"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg><input type="search" placeholder="ИНН, юр. лицо, email админа…" aria-label="Поиск тенантов"></label>
|
||||
<button type="button" class="fbtn"><span style="color:var(--ink-3)">Статус:</span><strong>Все</strong><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></button>
|
||||
<button type="button" class="fbtn"><span style="color:var(--ink-3)">Тариф:</span><strong>Все</strong><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></button>
|
||||
<button type="button" class="fbtn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/></svg>Ещё</button>
|
||||
</div>
|
||||
<div class="tbl-wrap">
|
||||
<table class="tbl">
|
||||
<thead><tr><th scope="col">Тенант</th><th scope="col">Статус</th><th scope="col">Тариф</th><th class="num" scope="col">Баланс ₽</th><th class="num" scope="col">Желаем×факт сегодня</th><th class="num" scope="col">MRR</th><th scope="col">Активность</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="t-name">Окна Москва ООО<span class="sub">ИНН 7724444444</span></td><td><span class="chip chip-active"><span class="dot"></span>Активен</span></td><td>Команда</td><td class="t-balance">14 250</td><td class="t-today">12 × 11</td><td class="t-mr">990</td><td class="t-since">28 мин назад</td></tr>
|
||||
<tr><td class="t-name">Натяжные потолки СПб<span class="sub">ИНН 7805123456</span></td><td><span class="chip chip-active"><span class="dot"></span>Активен</span></td><td>Команда</td><td class="t-balance">38 100</td><td class="t-today">8 × 14</td><td class="t-mr">990</td><td class="t-since">14 мин назад</td></tr>
|
||||
<tr><td class="t-name">Кухни на заказ Екб<span class="sub">ИНН 6671987654</span></td><td><span class="chip chip-active"><span class="dot"></span>Активен</span></td><td>Pro</td><td class="t-balance">112 800</td><td class="t-today">5 × 6</td><td class="t-mr">2 990</td><td class="t-since">1 ч назад</td></tr>
|
||||
<tr><td class="t-name">Ремонт под ключ<span class="sub">ИНН 5024333222</span></td><td><span class="chip chip-trial"><span class="dot"></span>Trial · 4 дня</span></td><td>Trial</td><td class="t-balance">450</td><td class="t-today">3 × 0</td><td class="t-mr" style="color:var(--ink-3)">—</td><td class="t-since">3 ч назад</td></tr>
|
||||
<tr><td class="t-name">Двери Премиум<span class="sub">ИНН 7732111000</span></td><td><span class="chip chip-overdue"><span class="dot"></span>Просрочка 3 дня</span></td><td>Команда</td><td class="t-balance low">−1 200</td><td class="t-today">0 × 0</td><td class="t-mr">990</td><td class="t-since">2 дня</td></tr>
|
||||
<tr><td class="t-name">Оконные системы РФ<span class="sub">ИНН 7707654321</span></td><td><span class="chip chip-suspended"><span class="dot"></span>Приостановлен</span></td><td>Start</td><td class="t-balance" style="color:var(--ink-disabled)">0</td><td class="t-today">0 × 0</td><td class="t-mr" style="color:var(--ink-3)">—</td><td class="t-since">7 дней</td></tr>
|
||||
<tr><td class="t-name">Кухонная мебель Спб<span class="sub">ИНН 7813222333</span></td><td><span class="chip chip-active"><span class="dot"></span>Активен</span></td><td>Enterprise</td><td class="t-balance">486 200</td><td class="t-today">28 × 31</td><td class="t-mr">9 990</td><td class="t-since">8 мин назад</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== ADMIN/TENANT-CARD ===== -->
|
||||
<section id="page-tenant-card" class="tab-page">
|
||||
<div class="app">
|
||||
<aside class="side"><div class="brand"><span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span><span>Лидерра<span class="brand-dot">.</span></span></div><div class="brand-sub">ADMIN</div><nav class="nav"><div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/></svg><span class="nav-text">Тенанты</span><span class="nav-count">142</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg><span class="nav-text">Инциденты</span><span class="nav-count">3</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Система</span></div></nav></aside>
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Админка</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><a>Тенанты</a><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><strong>Окна Москва ООО</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<span class="user-chip"><span class="ava">АО</span><span class="uname">Админ Оператор</span></span>
|
||||
</div>
|
||||
<main class="content">
|
||||
<div class="tc-row">
|
||||
<div class="tc-main">
|
||||
<header class="tc-head">
|
||||
<h1 class="tc-name">Окна Москва ООО</h1>
|
||||
<div class="tc-meta">
|
||||
<span class="id">#TNT-0042</span>
|
||||
<span>ИНН 7724444444 / КПП 772401001</span>
|
||||
<span class="sep">·</span>
|
||||
<span>с 12.04.2026 (3 недели)</span>
|
||||
<span class="sep">·</span>
|
||||
<span><span class="chip chip-active"><span class="dot"></span>Активен</span></span>
|
||||
</div>
|
||||
<div class="tc-actions">
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4z"/></svg>Редактировать</button>
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/></svg>Корректировать баланс</button>
|
||||
<button type="button" class="btn imp"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="7" r="4"/><path d="M2 21v-2a4 4 0 0 1 4-4h12a4 4 0 0 1 4 4v2"/></svg>Войти как клиент</button>
|
||||
<button type="button" class="btn btn-danger"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg>Удалить (152-ФЗ)</button>
|
||||
</div>
|
||||
</header>
|
||||
<section class="tc-section">
|
||||
<h2>Профиль</h2>
|
||||
<div class="kv-grid">
|
||||
<span class="k">Юр. лицо</span><span class="v">ООО «Окна Москва»</span>
|
||||
<span class="k">Юр. адрес</span><span class="v">115093, Москва, ул. Большая Серпуховская, 32 стр. 1, оф. 405</span>
|
||||
<span class="k">Расчётный счёт</span><span class="v mono">40702810400000000123 · ПАО «Сбербанк»</span>
|
||||
<span class="k">Email админа</span><span class="v">ivan.petrov@example.ru</span>
|
||||
<span class="k">Телефон</span><span class="v mono">+7 (495) 482-91-22</span>
|
||||
<span class="k">Тариф</span><span class="v">Команда (990 ₽/мес) · автопродление</span>
|
||||
<span class="k">Активные проекты</span><span class="v mono">3 / 10</span>
|
||||
<span class="k">Менеджеры</span><span class="v mono">4</span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tc-section">
|
||||
<h2>Корректировка баланса</h2>
|
||||
<div class="adj-form">
|
||||
<input type="text" class="input" placeholder="Сумма" value="+ 5 000">
|
||||
<input type="text" class="input" placeholder="Причина (обязательно)" value="Возврат по обращению ticket-2841: ошибка дублирования лида">
|
||||
<button type="button" class="btn btn-primary">Применить</button>
|
||||
</div>
|
||||
<div style="font-size:11px;color:var(--ink-3);margin-top:6px">Запись в saas_admin_audit_log будет создана автоматически. Действие обратимо в течение 24 часов.</div>
|
||||
</section>
|
||||
</div>
|
||||
<aside class="tc-aside">
|
||||
<section class="tc-section">
|
||||
<h2>Баланс и активность</h2>
|
||||
<div class="kv-grid">
|
||||
<span class="k">Баланс ₽</span><span class="v mono" style="color:var(--accent);font-weight:600">14 250 ₽</span>
|
||||
<span class="k">Лидов запас</span><span class="v mono">285</span>
|
||||
<span class="k">Желаем сегодня</span><span class="v mono">12</span>
|
||||
<span class="k">Факт сегодня</span><span class="v mono">11</span>
|
||||
<span class="k">MRR</span><span class="v mono">990 ₽</span>
|
||||
<span class="k">LTV (3 нед.)</span><span class="v mono">2 970 ₽</span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tc-section">
|
||||
<h2>Журнал действий</h2>
|
||||
<div class="timeline-mini">
|
||||
<div class="tm-item warn"><div class="tm-when">07.05 · 14:48</div><div class="tm-text">Админ <strong>Иван Оператор</strong> вошёл как клиент (impersonation, 4 мин)</div></div>
|
||||
<div class="tm-item acc"><div class="tm-when">06.05 · 22:06</div><div class="tm-text">Пополнение через ЮKassa: + 10 000 ₽</div></div>
|
||||
<div class="tm-item dang"><div class="tm-when">04.05 · 16:42</div><div class="tm-text">Попытка пополнения через банковский перевод — отклонено</div></div>
|
||||
<div class="tm-item"><div class="tm-when">12.04 · 09:32</div><div class="tm-text">Регистрация · trial 7 дней</div></div>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== IMPERSONATION MODE ===== -->
|
||||
<section id="page-impersonation" class="tab-page">
|
||||
<div class="imp-banner" role="status">
|
||||
<span style="display:inline-flex;align-items:center;gap:8px">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.7"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12" y2="17"/></svg>
|
||||
<strong>IMPERSONATION</strong>
|
||||
<span style="opacity:0.85">Вы вошли как <strong>Окна Москва ООО / Иван Петров</strong> · действия логируются как admin · 04:32</span>
|
||||
</span>
|
||||
<button type="button">Выйти из режима</button>
|
||||
</div>
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand"><span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span><span>Лидерра<span class="brand-dot">.</span></span></div>
|
||||
<nav class="nav">
|
||||
<div class="nav-eyebrow">Работа</div>
|
||||
<div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg><span class="nav-text">Дашборд</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 6h18M6 12h12M9 18h6"/></svg><span class="nav-text">Сделки</span><span class="nav-count">247</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="18"/></svg><span class="nav-text">Канбан</span></div>
|
||||
</nav>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<div class="topbar"><div class="crumb"><strong>Дашборд</strong> <span style="color:var(--imp-red);font-weight:600;font-size:11.5px;font-family:var(--font-mono)">· через клиента</span></div><div class="topbar-spacer"></div><span class="user-chip"><span class="ava" style="background:var(--imp-red)">АО</span><span class="uname">Админ → ИП</span></span></div>
|
||||
<main class="content"><header class="page-h"><div><h1 class="page-title">Доброе утро, Иван</h1><div class="page-meta"><span><span class="num" style="color:var(--accent);font-weight:600">+3</span> новых лида с утра</span></div></div></header>
|
||||
<p style="font-size:13px;color:var(--ink-3);line-height:1.6;max-width:600px">Это дашборд клиента в режиме <strong style="color:var(--imp-red)">impersonation</strong>. Все действия фиксируются в saas_admin_audit_log с пометкой импersonator + причина (обязательно при входе). Выход из режима через красный баннер вверху.</p>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== INCIDENTS ===== -->
|
||||
<section id="page-incidents" class="tab-page">
|
||||
<div class="app">
|
||||
<aside class="side"><div class="brand"><span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span><span>Лидерра<span class="brand-dot">.</span></span></div><div class="brand-sub">ADMIN</div><nav class="nav"><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Тенанты</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></div><div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg><span class="nav-text">Инциденты</span><span class="nav-count">3</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Система</span></div></nav></aside>
|
||||
<div class="main">
|
||||
<div class="topbar"><div class="crumb"><span>Админка</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><strong>Журнал инцидентов</strong></div><div class="topbar-spacer"></div><span class="user-chip"><span class="ava">АО</span><span class="uname">Админ Оператор</span></span></div>
|
||||
<main class="content">
|
||||
<header class="page-h"><div><h1 class="page-title">Журнал инцидентов</h1><div class="page-meta"><span><span class="num">3</span> открытых</span><span class="sep">·</span><span><span class="num">87</span> за последние 30 дней</span><span class="sep">·</span><span>read-only для compliance</span></div></div></header>
|
||||
<div class="tbl-wrap">
|
||||
<div class="inc-row" style="background:var(--sunken);font-size:10.5px;font-weight:600;color:var(--ink-2);padding:10px 18px;border-bottom:1px solid var(--hairline)"><span>ID</span><span>Когда</span><span>Описание</span><span>Тенант</span><span>Severity</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0034</span><span class="inc-when">07.05 · 14:23</span><span class="inc-title">Webhook timeout 5 раз подряд<span class="scope">endpoint /api/lead-in · TG-бот тенанта</span></span><span class="inc-tenant">Окна Москва ООО</span><span class="inc-sev high">HIGH</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0033</span><span class="inc-when">07.05 · 09:14</span><span class="inc-title">SMS-шлюз timeout > 400ms<span class="scope">SMSC.RU деградация · 96.4%</span></span><span class="inc-tenant">Все тенанты</span><span class="inc-sev med">MED</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0032</span><span class="inc-when">06.05 · 22:48</span><span class="inc-title">Дублирование лида<span class="scope">Я.Директ webhook прислал дубль за 18 секунд</span></span><span class="inc-tenant">Натяжные потолки СПб</span><span class="inc-sev low">LOW</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0031</span><span class="inc-when">06.05 · 16:01</span><span class="inc-title">Failed Stripe payment retry<span class="scope">3 попытки за 7 дней · перевод в manual</span></span><span class="inc-tenant">Двери Премиум</span><span class="inc-sev med">MED</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0030</span><span class="inc-when">05.05 · 11:32</span><span class="inc-title">RLS bypass attempt<span class="scope">tenant_id mismatch на /api/deals — отбито</span></span><span class="inc-tenant">Кухни на заказ Екб</span><span class="inc-sev high">HIGH</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0029</span><span class="inc-when">05.05 · 08:22</span><span class="inc-title">Бэкап БД задержка<span class="scope">snapshot задержался на 38 минут — без потерь</span></span><span class="inc-tenant">Все тенанты</span><span class="inc-sev low">LOW</span></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== SYSTEM ===== -->
|
||||
<section id="page-system" class="tab-page">
|
||||
<div class="app">
|
||||
<aside class="side"><div class="brand"><span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span><span>Лидерра<span class="brand-dot">.</span></span></div><div class="brand-sub">ADMIN</div><nav class="nav"><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Тенанты</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg><span class="nav-text">Инциденты</span><span class="nav-count">3</span></div><div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Система</span></div></nav></aside>
|
||||
<div class="main">
|
||||
<div class="topbar"><div class="crumb"><span>Админка</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><strong>Система</strong></div><div class="topbar-spacer"></div><span class="user-chip"><span class="ava">АО</span><span class="uname">Админ Оператор</span></span></div>
|
||||
<main class="content">
|
||||
<header class="page-h"><div><h1 class="page-title">Системные настройки</h1><div class="page-meta"><span>super_admin доступ</span><span class="sep">·</span><span>все изменения логируются</span></div></div></header>
|
||||
<div class="sys-grid">
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M12 1v6M12 17v6M4.22 4.22l4.24 4.24M15.54 15.54l4.24 4.24M1 12h6M17 12h6M4.22 19.78l4.24-4.24M15.54 8.46l4.24-4.24"/></svg></span><span class="sc-name">system_settings</span></div><div class="sc-desc">Глобальные параметры платформы: лимиты, таймауты, фичефлаги</div><div class="sc-meta"><strong>27</strong> параметров · обновлено 3 часа назад</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5z"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg></span><span class="sc-name">tariff_plans</span></div><div class="sc-desc">Тарифы Start/Basic/Pro/Enterprise — цена, лимиты, фичи</div><div class="sc-meta"><strong>4</strong> тарифа · последнее изменение 21.04</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 7v13h18V7M3 7l3-4h12l3 4M3 7h18"/></svg></span><span class="sc-name">legal_entities</span></div><div class="sc-desc">Юр. лица оператора — для счетов и УПД</div><div class="sc-meta"><strong>1</strong> активно · ИП Сидоров А.А.</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/><path d="M3 10h18M7 15h3"/></svg></span><span class="sc-name">payment_gateways</span></div><div class="sc-desc">ЮKassa, Stripe, банковский перевод — статус и комиссии</div><div class="sc-meta"><strong>3</strong> активных · ЮKassa primary</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 2L13 10M16 2h5v5"/><path d="M21 13v5a2 2 0 0 1-2 2h-14a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"/></svg></span><span class="sc-name">webhook_log</span></div><div class="sc-desc">Все outbound и inbound webhook-события за 30 дней</div><div class="sc-meta"><strong>284 612</strong> событий · 99.97% uptime</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></span><span class="sc-name">auth_log</span></div><div class="sc-desc">Все вход/выход события — клиенты, админы, impersonation</div><div class="sc-meta"><strong>4 821</strong> событий за 7 дней</div></button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const tabs = Array.from(document.querySelectorAll('.review-bar .tab'));
|
||||
const pages = Array.from(document.querySelectorAll('.tab-page'));
|
||||
function setTab(id) {
|
||||
tabs.forEach(t => {
|
||||
const isA = t.dataset.tab === id;
|
||||
t.classList.toggle('active', isA);
|
||||
t.setAttribute('aria-selected', isA ? 'true' : 'false');
|
||||
});
|
||||
pages.forEach(p => p.classList.toggle('active', p.id === 'page-' + id));
|
||||
}
|
||||
tabs.forEach(t => t.addEventListener('click', () => setTab(t.dataset.tab)));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,370 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Биллинг — Лидерра (Forest)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
@media (prefers-reduced-motion: reduce) { .has-motion { animation: none !important; transition: none !important; } }
|
||||
|
||||
:root {
|
||||
--bg: #F6F3EC; --surface: #FFFDFA; --sunken: #F0EDE4;
|
||||
--hairline: #D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink: #081319; --ink-2: #343C41; --ink-3: #66635C; --ink-disabled: #92907B;
|
||||
--accent: #0F6E56; --accent-tint: #E1EEEA; --accent-deep: #084635;
|
||||
--side-bg: #012019; --side-text: #B1C2BD; --side-text-2: #7A8C87;
|
||||
--side-active: #13382F; --side-icon: #5C7A72; --side-icon-act: #32C8A9;
|
||||
--side-hover: #0A2A22; --side-border: #1A3A30;
|
||||
--st-paid-solid: #007EB8; --st-wait-solid: #00889B; --st-fail-solid: #6C60C4;
|
||||
--st-new-solid: #B94837; --st-quote-solid: #008A4D; --st-call-solid: #9A6700;
|
||||
--r-xs:4px; --r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background: var(--bg); color: var(--ink); font-family: var(--font-ui); font-feature-settings: 'cv11','ss01','ss03'; -webkit-font-smoothing: antialiased; font-variation-settings: 'opsz' 14; min-height: 100vh; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font-family: inherit; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible, [tabindex]:focus-visible {
|
||||
outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm);
|
||||
}
|
||||
.skip-link { position: absolute; top: -40px; left: 12px; background: var(--ink); color: #fff; padding: 9px 16px; z-index: 200; font-weight: 600; border-radius: var(--r-sm); }
|
||||
.skip-link:focus { top: 12px; }
|
||||
|
||||
/* Shell — same as v8_dashboard */
|
||||
.app { display: grid; grid-template-columns: 232px 1fr; min-height: 100vh; }
|
||||
.side { background: var(--side-bg); border-right: 1px solid var(--side-border); padding: 18px 12px 24px; position: sticky; top: 0; height: 100vh; overflow-y: auto; color: var(--side-text); }
|
||||
.brand { display: flex; align-items: center; gap: 10px; padding: 6px 8px 18px; font-weight: 600; font-size: 14.5px; letter-spacing: -0.01em; font-variation-settings: 'opsz' 18; color: #FFFFFF; }
|
||||
.brand-mark { width: 22px; height: 22px; border-radius: var(--r-xs); background: #FFFFFF; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; overflow: hidden; }
|
||||
.brand-mark svg { width: 100%; height: 100%; display: block; }
|
||||
.brand-dot { color: var(--side-icon-act); }
|
||||
.nav { display: flex; flex-direction: column; gap: 1px; }
|
||||
.nav-eyebrow { font-size: 11px; font-weight: 500; letter-spacing: 0.01em; color: var(--side-text-2); padding: 14px 10px 6px; }
|
||||
.nav-item { display: flex; align-items: center; gap: 10px; height: 32px; padding: 0 10px; border-radius: var(--r-sm); font-size: 13px; color: var(--side-text); }
|
||||
.nav-item:hover { background: var(--side-hover); color: #FFFFFF; }
|
||||
.nav-item.active { background: var(--side-active); color: #FFFFFF; font-weight: 500; }
|
||||
.nav-item.active .nav-icon { color: var(--side-icon-act); }
|
||||
.nav-icon { width: 15px; height: 15px; flex-shrink: 0; color: var(--side-icon); stroke-width: 1.6; }
|
||||
.nav-item:hover .nav-icon { color: #FFFFFF; }
|
||||
.nav-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.nav-count { font-family: var(--font-mono); font-size: 10.5px; font-weight: 500; font-feature-settings: 'tnum'; background: rgba(255,255,255,0.10); color: var(--side-text); padding: 2px 6px; border-radius: 4px; }
|
||||
.main { display: flex; flex-direction: column; min-width: 0; }
|
||||
.topbar { height: 48px; border-bottom: 1px solid var(--hairline); background: var(--surface); padding: 0 24px; display: flex; align-items: center; gap: 12px; position: sticky; top: 0; z-index: 50; }
|
||||
.crumb { font-size: 12.5px; color: var(--ink-3); display: flex; align-items: center; gap: 8px; }
|
||||
.crumb strong { color: var(--ink); font-weight: 500; }
|
||||
.crumb svg { width: 11px; height: 11px; color: var(--ink-3); opacity: 0.5; }
|
||||
.topbar-spacer { flex: 1; }
|
||||
.user-chip { display: inline-flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px 0 4px; border-radius: 100px; border: 1px solid var(--hairline); background: var(--surface); }
|
||||
.user-chip .ava { width: 22px; height: 22px; border-radius: 50%; background: var(--ink); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 9.5px; font-weight: 600; }
|
||||
.user-chip .uname { font-size: 12px; color: var(--ink); font-weight: 500; }
|
||||
|
||||
/* Page */
|
||||
.content { padding: 24px 28px 80px; max-width: 1500px; margin: 0 auto; }
|
||||
.page-h { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 22px; gap: 16px; }
|
||||
.page-title { font-size: 28px; font-weight: 600; font-variation-settings: 'opsz' 28; letter-spacing: -0.02em; line-height: 1.1; margin: 0 0 6px; }
|
||||
.page-meta { font-size: 12.5px; color: var(--ink-3); display: flex; gap: 14px; align-items: center; }
|
||||
.page-meta .num { font-family: var(--font-mono); font-feature-settings: 'tnum'; color: var(--ink-2); font-weight: 500; }
|
||||
.page-meta .accent-num { color: var(--accent); font-weight: 600; }
|
||||
.page-meta .sep { color: var(--ink-disabled); }
|
||||
|
||||
.btn { display: inline-flex; align-items: center; gap: 7px; height: 34px; padding: 0 14px; border-radius: var(--r-sm); border: 1px solid var(--hairline); background: var(--surface); font-size: 12.5px; font-weight: 500; color: var(--ink); cursor: pointer; font-family: inherit; letter-spacing: -0.005em; }
|
||||
.btn:hover { border-color: var(--ink-disabled); background: var(--sunken); }
|
||||
.btn-primary { background: var(--accent); color: #fff; border-color: var(--accent); }
|
||||
.btn-primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
|
||||
.btn svg { width: 13px; height: 13px; stroke-width: 1.7; }
|
||||
|
||||
/* Wallet hero — 2 islands: ₽ + ГЦК */
|
||||
.wallet-row { display: grid; grid-template-columns: 1.3fr 1fr 1fr; gap: 12px; margin-bottom: 18px; }
|
||||
.wallet-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); padding: 20px 22px; min-height: 158px; display: flex; flex-direction: column; gap: 12px; position: relative; overflow: hidden; }
|
||||
.wallet-card.primary::before { content: ''; position: absolute; inset: -1px; border: 1px solid var(--accent); opacity: 0.55; border-radius: var(--r-md); pointer-events: none; }
|
||||
.wallet-h { display: flex; align-items: center; justify-content: space-between; }
|
||||
.wallet-label { font-size: 11.5px; color: var(--ink-3); font-weight: 500; }
|
||||
.wallet-tag { font-family: var(--font-mono); font-size: 9.5px; font-weight: 600; letter-spacing: 0.06em; padding: 2px 6px; border-radius: 3px; background: var(--accent-tint); color: var(--accent-deep); }
|
||||
.wallet-amount { display: flex; align-items: baseline; gap: 4px; }
|
||||
.wallet-amount .num { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-weight: 600; font-size: 38px; letter-spacing: -0.03em; line-height: 1; color: var(--ink); }
|
||||
.wallet-amount .ru { font-family: var(--font-mono); font-size: 16px; color: var(--ink-3); }
|
||||
.wallet-actions { display: flex; gap: 8px; margin-top: auto; }
|
||||
.wallet-actions .btn { flex: 1; justify-content: center; }
|
||||
.wallet-foot { font-size: 11.5px; color: var(--ink-2); font-family: var(--font-mono); font-feature-settings: 'tnum'; }
|
||||
.wallet-foot strong { color: var(--accent); font-weight: 600; }
|
||||
|
||||
/* Tariff card */
|
||||
.tariff-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); padding: 18px 20px; min-height: 158px; display: flex; flex-direction: column; gap: 10px; }
|
||||
.tariff-name { font-size: 16px; font-weight: 600; font-variation-settings: 'opsz' 18; letter-spacing: -0.012em; }
|
||||
.tariff-feats { font-size: 11.5px; color: var(--ink-2); display: flex; flex-direction: column; gap: 4px; line-height: 1.4; }
|
||||
.tariff-feats span { display: flex; align-items: center; gap: 6px; }
|
||||
.tariff-feats .check { color: var(--accent); flex-shrink: 0; width: 11px; height: 11px; }
|
||||
|
||||
/* History panel */
|
||||
.panel { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); margin-bottom: 14px; overflow: hidden; }
|
||||
.panel-h { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px 12px; gap: 12px; flex-wrap: wrap; }
|
||||
.panel-h h2 { font-size: 16px; font-weight: 600; font-variation-settings: 'opsz' 18; letter-spacing: -0.012em; margin: 0; }
|
||||
.panel-tabs { display: inline-flex; gap: 2px; padding: 2px; background: var(--sunken); border-radius: var(--r-sm); }
|
||||
.panel-tabs button { height: 26px; padding: 0 10px; border: none; background: transparent; font-family: inherit; font-size: 11.5px; font-weight: 500; color: var(--ink-3); cursor: pointer; border-radius: 4px; }
|
||||
.panel-tabs button.active { background: var(--surface); color: var(--ink); box-shadow: inset 0 0 0 1px var(--hairline); }
|
||||
|
||||
.tx-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
|
||||
.tx-table thead th { text-align: left; font-size: 10.5px; font-weight: 600; letter-spacing: 0.005em; color: var(--ink-2); padding: 9px 14px; background: var(--sunken); border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); white-space: nowrap; }
|
||||
.tx-table thead th.num { text-align: right; }
|
||||
.tx-table tbody tr { border-bottom: 1px solid var(--hairline-soft); }
|
||||
.tx-table tbody tr:last-child { border-bottom: none; }
|
||||
.tx-table tbody tr:hover { background: var(--sunken); }
|
||||
.tx-table tbody td { padding: 9px 14px; vertical-align: middle; }
|
||||
.tx-table .tx-when { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-size: 11.5px; color: var(--ink-3); }
|
||||
.tx-table .tx-id { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-size: 11.5px; color: var(--accent); }
|
||||
.tx-table .tx-amount { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-weight: 500; text-align: right; }
|
||||
.tx-amount.up { color: var(--accent); }
|
||||
.tx-amount.down { color: var(--ink); }
|
||||
.tx-amount.refund { color: var(--st-fail-solid); }
|
||||
|
||||
.chip { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 500; }
|
||||
.chip .dot { width: 6px; height: 6px; border-radius: 50%; position: relative; }
|
||||
.chip .dot::after { content: ''; position: absolute; inset: -1px; border-radius: 50%; border: 1px solid rgba(10,19,25,0.10); }
|
||||
.chip-paid .dot { background: var(--st-quote-solid); }
|
||||
.chip-pending .dot { background: var(--st-call-solid); }
|
||||
.chip-failed .dot { background: var(--st-new-solid); }
|
||||
.chip-refund .dot { background: var(--st-fail-solid); }
|
||||
|
||||
.pending-banner { background: #FFF8E5; border: 1px solid #F0D89E; border-radius: var(--r-md); padding: 12px 16px; margin-bottom: 18px; display: flex; align-items: center; gap: 12px; font-size: 12.5px; color: #614209; }
|
||||
.pending-banner .ico { width: 18px; height: 18px; color: #B35100; flex-shrink: 0; }
|
||||
.pending-banner strong { color: #4A2F00; font-weight: 600; }
|
||||
|
||||
.invoices-list { display: flex; flex-direction: column; }
|
||||
.inv-row { display: grid; grid-template-columns: 110px 1fr auto auto; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--hairline-soft); align-items: center; font-size: 12.5px; }
|
||||
.inv-row:last-child { border-bottom: none; }
|
||||
.inv-row:hover { background: var(--sunken); }
|
||||
.inv-when { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-size: 11.5px; color: var(--ink-3); }
|
||||
.inv-name { color: var(--ink); }
|
||||
.inv-name .sub { font-size: 11px; color: var(--ink-3); display: block; margin-top: 2px; }
|
||||
.inv-amount { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-weight: 500; text-align: right; }
|
||||
.inv-action { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border: 1px solid var(--hairline); background: var(--surface); border-radius: var(--r-sm); font-size: 11.5px; color: var(--ink-2); cursor: pointer; font-family: inherit; }
|
||||
.inv-action:hover { border-color: var(--ink-disabled); color: var(--ink); }
|
||||
.inv-action svg { width: 11px; height: 11px; }
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.app { grid-template-columns: 56px 1fr; }
|
||||
.side { padding: 14px 6px; }
|
||||
.brand .brand-text, .nav-eyebrow, .nav-text, .nav-item:not(.active) .nav-count { display: none; }
|
||||
.nav-item { justify-content: center; padding: 0; }
|
||||
.topbar { padding: 0 16px; }
|
||||
.content { padding: 18px 18px 60px; }
|
||||
.wallet-row { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.app { grid-template-columns: 1fr; }
|
||||
.side { display: none; }
|
||||
.topbar .crumb span:first-child { display: none; }
|
||||
.page-h { flex-direction: column; align-items: flex-start; gap: 12px; }
|
||||
.wallet-row { grid-template-columns: 1fr; }
|
||||
.tx-table thead { display: none; }
|
||||
.tx-table, .tx-table tbody, .tx-table tr, .tx-table td { display: block; width: 100%; }
|
||||
.tx-table tbody tr { padding: 12px 16px; border-bottom: 1px solid var(--hairline-soft); }
|
||||
.tx-table tbody td { padding: 3px 0; border: none; }
|
||||
.inv-row { grid-template-columns: 1fr; }
|
||||
.inv-row .inv-amount, .inv-row .inv-action { justify-self: flex-start; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main" class="skip-link">К контенту</a>
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand">
|
||||
<span class="brand-mark" aria-hidden="true"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span class="brand-text">Лидерра<span class="brand-dot">.</span></span>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<div class="nav-eyebrow">Работа</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg><span class="nav-text">Дашборд</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18M6 12h12M9 18h6"/></svg><span class="nav-text">Сделки</span><span class="nav-count">247</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/></svg><span class="nav-text">Канбан</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg><span class="nav-text">Напоминания</span><span class="nav-count">12</span></a>
|
||||
<div class="nav-eyebrow">Финансы</div>
|
||||
<a class="nav-item active" href="#" aria-current="page"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="1"/><path d="M3 10h18M7 15h3"/></svg><span class="nav-text">Биллинг</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h18M3 6h18M3 18h18"/></svg><span class="nav-text">Отчёты</span></a>
|
||||
<div class="nav-eyebrow">Команда</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Менеджеры</span><span class="nav-count">4</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Настройки</span></a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Рабочая область</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg><strong>Биллинг</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<button type="button" class="user-chip" aria-label="Профиль · Иван Петров"><span class="ava" aria-hidden="true">ИП</span><span class="uname">Иван П.</span></button>
|
||||
</div>
|
||||
|
||||
<main id="main" class="content">
|
||||
<header class="page-h">
|
||||
<div>
|
||||
<h1 class="page-title">Биллинг и тарифы</h1>
|
||||
<div class="page-meta">
|
||||
<span><span class="accent-num num">14 250 ₽</span> кошелёк</span>
|
||||
<span class="sep">·</span>
|
||||
<span><span class="num">285</span> лидов запас</span>
|
||||
<span class="sep">·</span>
|
||||
<span>хватит на <span class="num">4 дня</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Пополнить баланс</button>
|
||||
</header>
|
||||
|
||||
<!-- Pending banner -->
|
||||
<div class="pending-banner" role="status" aria-live="polite">
|
||||
<svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.7" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg>
|
||||
<span><strong>1 платёж в обработке</strong> — 5 000 ₽ от ЮKassa, начат 14:21. Авто-восстановление в 14:51 (30 мин). Кнопки «Отменить» нет — это техническое решение.</span>
|
||||
</div>
|
||||
|
||||
<!-- Wallet + Tariff -->
|
||||
<div class="wallet-row">
|
||||
<article class="wallet-card primary" aria-labelledby="wallet-rub">
|
||||
<div class="wallet-h"><span id="wallet-rub" class="wallet-label">Кошелёк ₽</span><span class="wallet-tag">LIVE</span></div>
|
||||
<div class="wallet-amount"><span class="num">14 250</span><span class="ru">₽</span></div>
|
||||
<div class="wallet-foot">мин. пополнение <strong>100 ₽</strong> · округление вниз ₽→лиды</div>
|
||||
<div class="wallet-actions">
|
||||
<button type="button" class="btn btn-primary"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Пополнить</button>
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h18M3 6h18M3 18h18"/></svg>Автопополнение</button>
|
||||
</div>
|
||||
</article>
|
||||
<article class="wallet-card" aria-labelledby="wallet-leads">
|
||||
<div class="wallet-h"><span id="wallet-leads" class="wallet-label">Баланс лидов (ГЦК)</span></div>
|
||||
<div class="wallet-amount"><span class="num">285</span><span class="ru" style="font-style:normal">лидов</span></div>
|
||||
<div class="wallet-foot">средняя цена <strong>50 ₽/лид</strong> · потрачено за месяц 412</div>
|
||||
</article>
|
||||
<article class="tariff-card" aria-labelledby="tariff-name">
|
||||
<div class="wallet-label">Тариф</div>
|
||||
<div id="tariff-name" class="tariff-name">Команда <span style="font-family:var(--font-mono);color:var(--accent);font-size:13px;font-weight:500">· 990 ₽/мес</span></div>
|
||||
<div class="tariff-feats">
|
||||
<span><svg class="check" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.2" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>до 10 проектов</span>
|
||||
<span><svg class="check" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.2" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>4 менеджера + расширение</span>
|
||||
<span><svg class="check" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.2" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>Канбан, Webhook, API</span>
|
||||
</div>
|
||||
<button type="button" class="btn" style="margin-top:auto;justify-content:center">Сменить тариф →</button>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<!-- Transactions -->
|
||||
<section class="panel" aria-labelledby="tx-title">
|
||||
<div class="panel-h">
|
||||
<h2 id="tx-title">История транзакций</h2>
|
||||
<div class="panel-tabs" role="tablist" aria-label="Тип операций">
|
||||
<button type="button" role="tab" aria-selected="true" class="active">Все</button>
|
||||
<button type="button" role="tab" aria-selected="false">Пополнения</button>
|
||||
<button type="button" role="tab" aria-selected="false">Списания</button>
|
||||
<button type="button" role="tab" aria-selected="false">Возвраты</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tx-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Дата</th>
|
||||
<th scope="col">Операция</th>
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Статус</th>
|
||||
<th class="num" scope="col">Сумма</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tx-when">07.05 · 14:21</td>
|
||||
<td>Пополнение через ЮKassa</td>
|
||||
<td class="tx-id">#TX-89421</td>
|
||||
<td><span class="chip chip-pending"><span class="dot" aria-hidden="true"></span>В обработке</span></td>
|
||||
<td class="tx-amount up">+ 5 000 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">07.05 · 11:14</td>
|
||||
<td>Списание · 3 лида проект «Окна Москва»</td>
|
||||
<td class="tx-id">#TX-89384</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount down">− 6 600 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">07.05 · 09:48</td>
|
||||
<td>Возврат лида #1018 · дубликат</td>
|
||||
<td class="tx-id">#TX-89370</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount refund">+ 2 200 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">06.05 · 22:06</td>
|
||||
<td>Пополнение через ЮKassa</td>
|
||||
<td class="tx-id">#TX-89312</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount up">+ 10 000 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">06.05 · 18:32</td>
|
||||
<td>Списание · 5 лидов проект «Натяжные потолки»</td>
|
||||
<td class="tx-id">#TX-89286</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount down">− 9 250 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">05.05 · 12:00</td>
|
||||
<td>Списание абонентской платы тарифа «Команда»</td>
|
||||
<td class="tx-id">#TX-89108</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount down">− 990 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">04.05 · 16:42</td>
|
||||
<td>Попытка пополнения через банковский перевод</td>
|
||||
<td class="tx-id">#TX-88937</td>
|
||||
<td><span class="chip chip-failed"><span class="dot" aria-hidden="true"></span>Отклонено</span></td>
|
||||
<td class="tx-amount" style="color:var(--ink-3)">— 0 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">03.05 · 09:18</td>
|
||||
<td>Возврат лида #998 · спам</td>
|
||||
<td class="tx-id">#TX-88714</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount refund">+ 1 850 ₽</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<!-- Invoices -->
|
||||
<section class="panel" aria-labelledby="inv-title">
|
||||
<div class="panel-h">
|
||||
<h2 id="inv-title">Счета и УПД</h2>
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>Реестр XLSX</button>
|
||||
</div>
|
||||
<div class="invoices-list">
|
||||
<div class="inv-row">
|
||||
<span class="inv-when">07.05.2026</span>
|
||||
<span class="inv-name">Счёт № 2026-0512<span class="sub">Тариф «Команда» · май 2026</span></span>
|
||||
<span class="inv-amount">990 ₽</span>
|
||||
<button type="button" class="inv-action"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>PDF</button>
|
||||
</div>
|
||||
<div class="inv-row">
|
||||
<span class="inv-when">06.05.2026</span>
|
||||
<span class="inv-name">УПД № УПД-2026-0492<span class="sub">Списания за апрель · 18 лидов</span></span>
|
||||
<span class="inv-amount">29 850 ₽</span>
|
||||
<button type="button" class="inv-action"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>1С 8.3 XML</button>
|
||||
</div>
|
||||
<div class="inv-row">
|
||||
<span class="inv-when">05.05.2026</span>
|
||||
<span class="inv-name">УПД № УПД-2026-0488<span class="sub">Списания за март · 24 лида</span></span>
|
||||
<span class="inv-amount">38 100 ₽</span>
|
||||
<button type="button" class="inv-action"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>1С 8.3 XML</button>
|
||||
</div>
|
||||
<div class="inv-row">
|
||||
<span class="inv-when">01.04.2026</span>
|
||||
<span class="inv-name">Счёт № 2026-0411<span class="sub">Тариф «Команда» · апрель 2026</span></span>
|
||||
<span class="inv-amount">990 ₽</span>
|
||||
<button type="button" class="inv-action"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>PDF</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,610 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Бренд — нейминг и лого</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=Fraunces:opsz,wght,SOFT@9..144,300..700,0..100&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
/* v8 Forest tokens — same as v8_dashboard/v8_deals */
|
||||
--bg: #F6F3EC;
|
||||
--surface: #FFFDFA;
|
||||
--sunken: #F0EDE4;
|
||||
--hairline: #D9D5CD;
|
||||
--hairline-soft:#E8E3D6;
|
||||
--ink: #081319;
|
||||
--ink-2: #343C41;
|
||||
--ink-3: #66635C;
|
||||
--ink-disabled: #92907B;
|
||||
--accent: #0F6E56;
|
||||
--accent-tint: #E1EEEA;
|
||||
|
||||
--side-bg: #012019;
|
||||
--side-text: #B1C2BD;
|
||||
--side-icon-act:#32C8A9;
|
||||
|
||||
--r-sm: 6px;
|
||||
--r-md: 10px;
|
||||
--r-lg: 14px;
|
||||
|
||||
--font-ui: 'Inter', system-ui, sans-serif;
|
||||
--font-display:'Fraunces', Georgia, serif;
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
font-family: var(--font-ui);
|
||||
font-feature-settings: 'cv11','ss01';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-variation-settings: 'opsz' 14;
|
||||
padding: 32px 28px 80px;
|
||||
}
|
||||
|
||||
a:focus-visible, button:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--r-sm);
|
||||
}
|
||||
|
||||
.wrap { max-width: 1280px; margin: 0 auto; }
|
||||
|
||||
.hero {
|
||||
margin-bottom: 36px;
|
||||
padding-bottom: 22px;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
.hero h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
font-variation-settings: 'opsz' 28;
|
||||
letter-spacing: -0.022em;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
.hero .meta {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-3);
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.hero .meta strong { color: var(--accent); font-weight: 600; }
|
||||
|
||||
.section-h {
|
||||
display: flex; align-items: baseline; justify-content: space-between;
|
||||
margin: 40px 0 18px;
|
||||
gap: 12px;
|
||||
}
|
||||
.section-h h2 {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
font-variation-settings: 'opsz' 24;
|
||||
letter-spacing: -0.018em;
|
||||
margin: 0;
|
||||
}
|
||||
.section-h .num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--ink-3);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
NAMING GRID — 10 options
|
||||
============================================================ */
|
||||
.names {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.name-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-rows: 110px auto;
|
||||
}
|
||||
.name-render {
|
||||
background: var(--side-bg);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
position: relative;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.name-wordmark {
|
||||
font-size: 38px;
|
||||
font-weight: 600;
|
||||
font-variation-settings: 'opsz' 32;
|
||||
letter-spacing: -0.025em;
|
||||
color: #FFFFFF;
|
||||
display: inline-flex; align-items: baseline; gap: 8px;
|
||||
}
|
||||
.name-wordmark.serif {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 500;
|
||||
font-variation-settings: 'opsz' 144, 'SOFT' 0;
|
||||
letter-spacing: -0.015em;
|
||||
}
|
||||
.name-wordmark .dot { color: var(--side-icon-act); }
|
||||
.name-wordmark .accent-letter { color: var(--side-icon-act); }
|
||||
.name-render .badge {
|
||||
position: absolute;
|
||||
top: 10px; right: 12px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.06em;
|
||||
font-weight: 600;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
background: rgba(255,255,255,0.10);
|
||||
color: var(--side-text);
|
||||
}
|
||||
.name-render .badge.acc {
|
||||
background: rgba(50,200,169,0.15);
|
||||
color: var(--side-icon-act);
|
||||
}
|
||||
.name-meta {
|
||||
padding: 14px 18px 16px;
|
||||
}
|
||||
.name-num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.name-tag {
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
letter-spacing: -0.005em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.name-desc {
|
||||
font-size: 12.5px;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.5;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.name-desc em { color: var(--ink); font-style: normal; font-weight: 500; }
|
||||
|
||||
/* ============================================================
|
||||
LOGO CONCEPTS — 5 marks
|
||||
============================================================ */
|
||||
.logos {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.logo-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
overflow: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.logo-on-dark {
|
||||
background: var(--side-bg);
|
||||
padding: 26px 16px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border-bottom: 1px solid #1A3A30;
|
||||
min-height: 96px;
|
||||
}
|
||||
.logo-on-light {
|
||||
background: var(--bg);
|
||||
padding: 16px 16px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border-bottom: 1px solid var(--hairline-soft);
|
||||
min-height: 64px;
|
||||
}
|
||||
.logo-sizes {
|
||||
display: flex; align-items: center; justify-content: space-around;
|
||||
padding: 12px 14px;
|
||||
gap: 8px;
|
||||
background: var(--sunken);
|
||||
min-height: 56px;
|
||||
}
|
||||
.logo-sizes svg, .logo-sizes .tiny-mark {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.logo-meta {
|
||||
padding: 12px 14px 14px;
|
||||
border-top: 1px solid var(--hairline-soft);
|
||||
}
|
||||
.logo-num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.logo-name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
margin-bottom: 6px;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.logo-desc {
|
||||
font-size: 11.5px;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.45;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
COMBINATIONS — preview top 3 names × 2 marks in sidebar
|
||||
============================================================ */
|
||||
.combos {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.combo-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
.combo-side {
|
||||
background: var(--side-bg);
|
||||
padding: 14px 14px 16px;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
min-height: 60px;
|
||||
}
|
||||
.combo-mark { flex-shrink: 0; }
|
||||
.combo-name {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
font-variation-settings: 'opsz' 18;
|
||||
letter-spacing: -0.018em;
|
||||
}
|
||||
.combo-name.serif {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 500;
|
||||
font-variation-settings: 'opsz' 144, 'SOFT' 0;
|
||||
letter-spacing: -0.012em;
|
||||
}
|
||||
.combo-name .dot { color: var(--side-icon-act); }
|
||||
.combo-meta {
|
||||
padding: 12px 14px;
|
||||
display: flex; gap: 14px;
|
||||
font-size: 11.5px;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
.combo-meta strong { color: var(--ink); font-weight: 500; }
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.names { grid-template-columns: 1fr; }
|
||||
.logos { grid-template-columns: repeat(2, 1fr); }
|
||||
.combos { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
body { padding: 18px 14px 60px; }
|
||||
.logos { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
<header class="hero">
|
||||
<h1>Бренд · нейминг и лого</h1>
|
||||
<p class="meta">v8 Forest палитра · <strong>10 имён</strong> + <strong>5 знаков</strong> · все варианты на тёмном sidebar #012019 для контекста</p>
|
||||
</header>
|
||||
|
||||
<!-- ===== NAMING ===== -->
|
||||
<div class="section-h">
|
||||
<h2>10 вариантов имени</h2>
|
||||
<span class="num">01 · нейминг</span>
|
||||
</div>
|
||||
|
||||
<div class="names">
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Поток<span class="dot">.</span></span><span class="badge acc">FAVOURITE</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">01</div>
|
||||
<div class="name-tag">Минимализм. Дистиллят.</div>
|
||||
<p class="name-desc">Прямое сокращение от «Лидпоток». Уже знакомо команде, но звучит <em>тише и взрослее</em>. Domain: potok.app / potok.ru. Латиница: <em>Potok</em> — чисто читается.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark serif">Русло</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">02</div>
|
||||
<div class="name-tag">Метафорический. Премиум.</div>
|
||||
<p class="name-desc">Путь, по которому идёт вода. Элегантная русская метафора потока лидов. <em>Уникально</em>, не банально, домен почти точно свободен.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Жила</span><span class="badge">RAW</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">03</div>
|
||||
<div class="name-tag">Арбитражный. Прямой.</div>
|
||||
<p class="name-desc">Золотая жила. Связь с pay-per-lead = «копаем источник дохода». <em>Резкий</em> образ, может быть слишком прямолинейным для премиума.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Нерв</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">04</div>
|
||||
<div class="name-tag">Технологичный. Живой.</div>
|
||||
<p class="name-desc">Нервная система продаж. <em>Real-time</em> приём лидов, моментальные сигналы. Хорошо для CRM/продаж, отдаёт «неон» — может быть слишком напряжённо.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Пульс</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">05</div>
|
||||
<div class="name-tag">Real-time. Живой.</div>
|
||||
<p class="name-desc">Пульс входящих лидов, <em>биение</em>. Ассоциация с медтехом и Mercury-вайбом. Минус: часто используется в фитнес-сфере.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark serif">Volna<span class="accent-letter">.</span></span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">06</div>
|
||||
<div class="name-tag">Двуязычный. Премиум.</div>
|
||||
<p class="name-desc">Латиница для премиум-восприятия. Кириллический корень <em>«волна»</em> — поток лидов как волна. Читается легко на обоих языках, домен `volna.io` доступен.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark serif">Vento</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">07</div>
|
||||
<div class="name-tag">Итальянский. Технологичный.</div>
|
||||
<p class="name-desc">Ветер. Звучит как <em>финансовый или AI-стартап</em>. Без русских коннотаций — может оттолкнуть «понятностью» бренда. Linear-grade fonetика.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Колея</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">08</div>
|
||||
<div class="name-tag">Инженерный. Русский.</div>
|
||||
<p class="name-desc">Колея = направленный путь. Инженерная честная метафора, рельсы для лидов. <em>Менее очевидно</em>, чем «поток», но понятно.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark serif">Стерх</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">09</div>
|
||||
<div class="name-tag">Символический. Запоминается.</div>
|
||||
<p class="name-desc">Редкая птица, символ миграции. <em>Поэтичный</em>, далёкий от «лидогенерации» лоб-в-лоб. Сильный образ, домен `sterh.io` свободен.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Лидерра<span class="accent-letter">.</span></span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">10</div>
|
||||
<div class="name-tag">Составной. Премиум-вариация.</div>
|
||||
<p class="name-desc">«Лид» + «эра/терра». Сохраняет <em>смысловую связь</em> с «Лидпоток», но звучит дороже и менее буквально. Удачно для wordmark-логотипа.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ===== LOGOS ===== -->
|
||||
<div class="section-h">
|
||||
<h2>5 концептов знака</h2>
|
||||
<span class="num">02 · логотип</span>
|
||||
</div>
|
||||
|
||||
<div class="logos">
|
||||
|
||||
<!-- Logo 1: L-square — refined placeholder -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" aria-label="Логотип L-square на тёмном">
|
||||
<rect x="2" y="2" width="44" height="44" rx="10" fill="#FFFFFF"/>
|
||||
<path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="32" cy="34" r="3" fill="#0F6E56"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="32" height="32" viewBox="0 0 48 48" aria-label="Логотип L-square на светлом">
|
||||
<rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/>
|
||||
<path d="M16 14 L16 34 L32 34" stroke="#FFFFFF" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="32" cy="34" r="3" fill="#32C8A9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="16" height="16" viewBox="0 0 48 48"><rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
<svg width="24" height="24" viewBox="0 0 48 48"><rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3" fill="#32C8A9"/></svg>
|
||||
<svg width="40" height="40" viewBox="0 0 48 48"><rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3" fill="#32C8A9"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">A</div>
|
||||
<div class="logo-name">L-Square с точкой</div>
|
||||
<p class="logo-desc">Литера + закрывающая точка-цель = «лид прибыл». Чистый геометрический знак.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Logo 2: Wave / river — поток как волны -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="56" height="48" viewBox="0 0 56 48" aria-label="Логотип Wave на тёмном">
|
||||
<path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
<path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" fill="none" opacity="0.7"/>
|
||||
<path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#32C8A9" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="40" height="32" viewBox="0 0 56 48" aria-label="Логотип Wave на светлом">
|
||||
<path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#0A1319" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
<path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#343C41" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
<path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#0F6E56" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="20" height="16" viewBox="0 0 56 48"><path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#0A1319" stroke-width="4" fill="none"/><path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#0F6E56" stroke-width="4" fill="none"/></svg>
|
||||
<svg width="30" height="24" viewBox="0 0 56 48"><path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#0A1319" stroke-width="3" fill="none"/><path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#343C41" stroke-width="3" fill="none"/><path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#0F6E56" stroke-width="3" fill="none"/></svg>
|
||||
<svg width="48" height="40" viewBox="0 0 56 48"><path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#0A1319" stroke-width="3" fill="none"/><path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#343C41" stroke-width="3" fill="none"/><path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#0F6E56" stroke-width="3" fill="none"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">Б</div>
|
||||
<div class="logo-name">Three Waves · поток</div>
|
||||
<p class="logo-desc">Три горизонтальные волны, нижняя — Teal. Ритм = поток. Подходит к «Поток / Volna / Русло».</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Logo 3: Drop / chevron forward -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" aria-label="Логотип Drop на тёмном">
|
||||
<path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#FFFFFF"/>
|
||||
<path d="M24 14 L24 34" stroke="#012019" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M18 28 L24 34 L30 28" stroke="#012019" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="24" cy="14" r="3.5" fill="#32C8A9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="32" height="32" viewBox="0 0 48 48" aria-label="Логотип Drop на светлом">
|
||||
<path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#0A1319"/>
|
||||
<path d="M24 14 L24 34" stroke="#FFFFFF" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M18 28 L24 34 L30 28" stroke="#FFFFFF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="24" cy="14" r="3.5" fill="#0F6E56"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="16" height="16" viewBox="0 0 48 48"><path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#0A1319"/><circle cx="24" cy="14" r="4" fill="#32C8A9"/></svg>
|
||||
<svg width="24" height="24" viewBox="0 0 48 48"><path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#0A1319"/><path d="M24 14 L24 34" stroke="#FFF" stroke-width="3" stroke-linecap="round"/><path d="M18 28 L24 34 L30 28" stroke="#FFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="24" cy="14" r="3.5" fill="#32C8A9"/></svg>
|
||||
<svg width="40" height="40" viewBox="0 0 48 48"><path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#0A1319"/><path d="M24 14 L24 34" stroke="#FFF" stroke-width="2.5" stroke-linecap="round"/><path d="M18 28 L24 34 L30 28" stroke="#FFF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="24" cy="14" r="3.5" fill="#32C8A9"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">В</div>
|
||||
<div class="logo-name">Drop · капля с движением</div>
|
||||
<p class="logo-desc">Капля + стрелка вниз = лид падает в воронку. Подходит к «Жила / Пульс / Колея».</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Logo 4: Chevron forward / pipeline -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="56" height="48" viewBox="0 0 56 48" aria-label="Логотип Chevron на тёмном">
|
||||
<path d="M6 12 L18 24 L6 36" stroke="#5C7A72" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M22 12 L34 24 L22 36" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M38 12 L50 24 L38 36" stroke="#32C8A9" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="40" height="32" viewBox="0 0 56 48" aria-label="Логотип Chevron на светлом">
|
||||
<path d="M6 12 L18 24 L6 36" stroke="#92907B" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M22 12 L34 24 L22 36" stroke="#343C41" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M38 12 L50 24 L38 36" stroke="#0F6E56" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="20" height="16" viewBox="0 0 56 48"><path d="M22 12 L34 24 L22 36" stroke="#343C41" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M38 12 L50 24 L38 36" stroke="#0F6E56" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
<svg width="30" height="24" viewBox="0 0 56 48"><path d="M6 12 L18 24 L6 36" stroke="#92907B" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M22 12 L34 24 L22 36" stroke="#343C41" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M38 12 L50 24 L38 36" stroke="#0F6E56" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
<svg width="48" height="40" viewBox="0 0 56 48"><path d="M6 12 L18 24 L6 36" stroke="#92907B" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M22 12 L34 24 L22 36" stroke="#343C41" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M38 12 L50 24 L38 36" stroke="#0F6E56" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">Г</div>
|
||||
<div class="logo-name">Pipeline · 3 шеврона</div>
|
||||
<p class="logo-desc">Три «>>>» нарастающих по контрасту = пайплайн лидов. Подходит к «Колея / Поток / Vento / Лидерра».</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Logo 5: Wordmark only — Fraunces serif "P." -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="64" height="48" viewBox="0 0 64 48" aria-label="Wordmark Fraunces на тёмном">
|
||||
<text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#FFFFFF" letter-spacing="-0.02em">П</text>
|
||||
<circle cx="48" cy="34" r="3" fill="#32C8A9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="48" height="32" viewBox="0 0 64 48" aria-label="Wordmark Fraunces на светлом">
|
||||
<text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#0A1319" letter-spacing="-0.02em">П</text>
|
||||
<circle cx="48" cy="34" r="3" fill="#0F6E56"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="20" height="16" viewBox="0 0 64 48"><text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#0A1319" letter-spacing="-0.02em">П</text></svg>
|
||||
<svg width="30" height="24" viewBox="0 0 64 48"><text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#0A1319" letter-spacing="-0.02em">П</text><circle cx="48" cy="34" r="3" fill="#0F6E56"/></svg>
|
||||
<svg width="48" height="40" viewBox="0 0 64 48"><text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#0A1319" letter-spacing="-0.02em">П</text><circle cx="48" cy="34" r="3" fill="#0F6E56"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">Д</div>
|
||||
<div class="logo-name">Serif Initial · буква + точка</div>
|
||||
<p class="logo-desc">Только литера в Fraunces (variable serif) + Teal-точка. Editorial. Подходит к «Русло / Volna / Стерх / Vento».</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ===== COMBINATIONS ===== -->
|
||||
<div class="section-h">
|
||||
<h2>3 favourite комбинации в контексте sidebar</h2>
|
||||
<span class="num">03 · превью</span>
|
||||
</div>
|
||||
|
||||
<div class="combos">
|
||||
|
||||
<article class="combo-card">
|
||||
<div class="combo-side">
|
||||
<span class="combo-mark">
|
||||
<svg width="22" height="22" viewBox="0 0 48 48"><rect x="2" y="2" width="44" height="44" rx="10" fill="#FFFFFF"/><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg>
|
||||
</span>
|
||||
<span class="combo-name">Лидерра<span class="dot">.</span></span>
|
||||
</div>
|
||||
<div class="combo-meta">
|
||||
<span><strong>Имя:</strong> Лидерра</span>
|
||||
<span><strong>Знак:</strong> A · L-Square</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="combo-card">
|
||||
<div class="combo-side">
|
||||
<span class="combo-mark">
|
||||
<svg width="34" height="20" viewBox="0 0 56 48"><path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#FFFFFF" stroke-width="3" fill="none"/><path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#FFFFFF" stroke-width="3" fill="none" opacity="0.7"/><path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#32C8A9" stroke-width="3" fill="none"/></svg>
|
||||
</span>
|
||||
<span class="combo-name">Поток<span class="dot">.</span></span>
|
||||
</div>
|
||||
<div class="combo-meta">
|
||||
<span><strong>Имя:</strong> Поток</span>
|
||||
<span><strong>Знак:</strong> Б · Three Waves</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="combo-card">
|
||||
<div class="combo-side">
|
||||
<span class="combo-mark">
|
||||
<svg width="26" height="22" viewBox="0 0 64 48"><text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#FFFFFF" letter-spacing="-0.02em">Р</text><circle cx="48" cy="34" r="3" fill="#32C8A9"/></svg>
|
||||
</span>
|
||||
<span class="combo-name serif">Русло</span>
|
||||
</div>
|
||||
<div class="combo-meta">
|
||||
<span><strong>Имя:</strong> Русло</span>
|
||||
<span><strong>Знак:</strong> Д · Serif Initial</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,324 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Экраны ошибок — Лидерра</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-icon-act:#32C8A9;
|
||||
--st-fail:#6C60C4;
|
||||
--st-new:#B94837;
|
||||
--r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
button { font-family:inherit; }
|
||||
a { color:inherit; text-decoration:none; }
|
||||
a:focus-visible, button:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
|
||||
}
|
||||
|
||||
/* Tab bar — for review only, not part of error pages themselves */
|
||||
.review-bar { position:sticky; top:0; z-index:50; background:#012019; padding:10px 16px; display:flex; align-items:center; gap:14px; border-bottom:1px solid #1A3A30; }
|
||||
.review-bar .label { font-size:11px; font-weight:600; letter-spacing:0.06em; color:#7A8C87; font-family:var(--font-mono); }
|
||||
.review-bar .tabs { display:flex; gap:2px; }
|
||||
.review-bar .tab { height:26px; padding:0 12px; border:none; background:transparent; color:#B1C2BD; font-family:inherit; font-size:12px; font-weight:500; cursor:pointer; border-radius:4px; }
|
||||
.review-bar .tab:hover { background:#0A2A22; color:#fff; }
|
||||
.review-bar .tab.active { background:#fff; color:#012019; }
|
||||
|
||||
/* Error page shell */
|
||||
.error-page {
|
||||
min-height: calc(100vh - 47px);
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
padding: 60px 24px 80px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.error-page .top-brand {
|
||||
position: absolute; top: 24px; left: 28px;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
font-weight: 600;
|
||||
font-size: 14.5px;
|
||||
letter-spacing: -0.01em;
|
||||
font-variation-settings:'opsz' 18;
|
||||
color: var(--ink);
|
||||
}
|
||||
.error-page .top-brand .mark {
|
||||
width: 22px; height: 22px;
|
||||
border-radius: 4px;
|
||||
background: var(--ink);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0; overflow: hidden;
|
||||
}
|
||||
.error-page .top-brand .mark svg { width:100%; height:100%; display:block; }
|
||||
.error-page .top-brand .dot { color: var(--accent); }
|
||||
|
||||
.err-code {
|
||||
font-family: var(--font-mono);
|
||||
font-feature-settings: 'tnum';
|
||||
font-size: 130px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.04em;
|
||||
line-height: 1;
|
||||
color: var(--ink);
|
||||
margin: 0 0 8px;
|
||||
display: inline-flex; align-items: baseline; gap: 0;
|
||||
position: relative;
|
||||
}
|
||||
.err-code .accent {
|
||||
color: var(--accent);
|
||||
}
|
||||
.err-code::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%; bottom: -10px; transform: translateX(-50%);
|
||||
width: 36px; height: 2px;
|
||||
background: var(--accent);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.err-title {
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
font-variation-settings:'opsz' 26;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.2;
|
||||
margin: 28px 0 10px;
|
||||
}
|
||||
.err-desc {
|
||||
font-size: 14px;
|
||||
color: var(--ink-3);
|
||||
line-height: 1.55;
|
||||
max-width: 480px;
|
||||
margin: 0 auto 28px;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.err-actions { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
|
||||
|
||||
.btn { display:inline-flex; align-items:center; gap:7px; height:38px; padding:0 18px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:13px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; }
|
||||
.btn:hover { border-color:#BFB9AB; background:var(--sunken); }
|
||||
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
|
||||
.btn svg { width:13px; height:13px; stroke-width:1.7; }
|
||||
|
||||
.err-id {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--ink-3);
|
||||
letter-spacing: -0.005em;
|
||||
margin-top: 28px;
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.err-id .copy-btn { width:18px; height:18px; border:none; background:none; color:var(--ink-3); border-radius:3px; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; }
|
||||
.err-id .copy-btn:hover { background:var(--sunken); color:var(--ink); }
|
||||
.err-id .copy-btn svg { width:11px; height:11px; }
|
||||
|
||||
.err-help {
|
||||
font-size: 12.5px;
|
||||
color: var(--ink-3);
|
||||
margin-top: 18px;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.err-help a { color: var(--accent); font-weight: 500; }
|
||||
.err-help a:hover { text-decoration: underline; text-underline-offset: 3px; }
|
||||
|
||||
/* Status decoration block */
|
||||
.err-illust {
|
||||
width: 100%; max-width: 320px;
|
||||
margin: 0 auto 8px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
|
||||
/* status indicator (for 500) */
|
||||
.status-list {
|
||||
margin-top: 30px;
|
||||
display: flex; gap: 14px; align-items: center;
|
||||
font-size: 11.5px;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
.status-list .item { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-feature-settings:'tnum'; }
|
||||
.status-list .dot { width: 7px; height: 7px; border-radius: 50%; position: relative; }
|
||||
.status-list .dot::after { content:''; position:absolute; inset:-1px; border-radius:50%; border:1px solid rgba(10,19,25,0.10); }
|
||||
.status-list .ok .dot { background: var(--accent); }
|
||||
.status-list .deg .dot { background: var(--st-fail); }
|
||||
.status-list .down .dot { background: var(--st-new); }
|
||||
|
||||
/* page switcher */
|
||||
.error-page { display: none; }
|
||||
.error-page.active { display: flex; }
|
||||
|
||||
/* error 403 — special */
|
||||
#err-403 .err-code .accent { color: var(--st-fail); }
|
||||
#err-403 .err-code::after { background: var(--st-fail); }
|
||||
|
||||
/* error 500 */
|
||||
#err-500 .err-code .accent { color: var(--st-new); }
|
||||
#err-500 .err-code::after { background: var(--st-new); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.err-code { font-size: 88px; }
|
||||
.err-title { font-size: 22px; }
|
||||
.error-page { padding: 80px 18px 40px; }
|
||||
.error-page .top-brand { top: 14px; left: 16px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Review-only tabs to switch between error states -->
|
||||
<nav class="review-bar" aria-label="Переключение между состояниями ошибок (только для review)">
|
||||
<span class="label">PREVIEW</span>
|
||||
<div class="tabs" role="tablist">
|
||||
<button type="button" class="tab active" data-tab="404" role="tab" aria-selected="true">404 · не найдено</button>
|
||||
<button type="button" class="tab" data-tab="403" role="tab" aria-selected="false">403 · нет доступа</button>
|
||||
<button type="button" class="tab" data-tab="500" role="tab" aria-selected="false">500 · сервис недоступен</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- ===== 404 ===== -->
|
||||
<main id="err-404" class="error-page active" role="main">
|
||||
<a href="#" class="top-brand">
|
||||
<span class="mark" aria-hidden="true">
|
||||
<svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg>
|
||||
</span>
|
||||
<span>Лидерра<span class="dot">.</span></span>
|
||||
</a>
|
||||
<div class="err-illust" aria-hidden="true">
|
||||
<svg width="240" height="120" viewBox="0 0 240 120" fill="none">
|
||||
<path d="M20 90 Q60 60 120 60 T220 90" stroke="#D9D5CD" stroke-width="1.5" stroke-dasharray="3 4"/>
|
||||
<circle cx="120" cy="60" r="3" fill="#0F6E56"/>
|
||||
<path d="M115 30 L120 60 M125 30 L120 60" stroke="#0F6E56" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="err-code">4<span class="accent">0</span>4</h1>
|
||||
<h2 class="err-title">Страница не найдена</h2>
|
||||
<p class="err-desc">
|
||||
Похоже, такой страницы нет — её удалили, переименовали или вы ввели адрес с опечаткой.
|
||||
Все рабочие экраны Лидерра доступны через дашборд.
|
||||
</p>
|
||||
<div class="err-actions">
|
||||
<a href="/" class="btn btn-primary">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
|
||||
На дашборд
|
||||
</a>
|
||||
<button type="button" class="btn" onclick="history.back()">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
||||
Назад
|
||||
</button>
|
||||
</div>
|
||||
<p class="err-help">Что-то не так? Напишите в <a href="mailto:support@liderra.app">support@liderra.app</a></p>
|
||||
</main>
|
||||
|
||||
<!-- ===== 403 ===== -->
|
||||
<main id="err-403" class="error-page" role="main">
|
||||
<a href="#" class="top-brand">
|
||||
<span class="mark" aria-hidden="true">
|
||||
<svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg>
|
||||
</span>
|
||||
<span>Лидерра<span class="dot">.</span></span>
|
||||
</a>
|
||||
<div class="err-illust" aria-hidden="true">
|
||||
<svg width="180" height="120" viewBox="0 0 180 120" fill="none">
|
||||
<rect x="50" y="50" width="80" height="60" rx="6" stroke="#D9D5CD" stroke-width="1.5"/>
|
||||
<path d="M62 50 L62 36 a28 28 0 0 1 56 0 L118 50" stroke="#6C60C4" stroke-width="2" stroke-linecap="round" fill="none"/>
|
||||
<circle cx="90" cy="78" r="5" fill="#6C60C4"/>
|
||||
<line x1="90" y1="83" x2="90" y2="92" stroke="#6C60C4" stroke-width="2.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="err-code">4<span class="accent">0</span>3</h1>
|
||||
<h2 class="err-title">У вас нет доступа</h2>
|
||||
<p class="err-desc">
|
||||
Эта страница принадлежит другому тенанту, либо ваша роль не позволяет её увидеть.
|
||||
Если вы считаете, что это ошибка — обратитесь к администратору вашей команды или в поддержку.
|
||||
</p>
|
||||
<div class="err-actions">
|
||||
<a href="/" class="btn btn-primary">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
|
||||
На дашборд
|
||||
</a>
|
||||
<a href="mailto:support@liderra.app" class="btn">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
||||
Написать в поддержку
|
||||
</a>
|
||||
</div>
|
||||
<div class="err-id">
|
||||
<span>Запрос</span>
|
||||
<span style="color:var(--ink-2);font-weight:500">REQ-3F8A2-0007</span>
|
||||
<button type="button" class="copy-btn" aria-label="Скопировать ID запроса">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- ===== 500 / 503 ===== -->
|
||||
<main id="err-500" class="error-page" role="main">
|
||||
<a href="#" class="top-brand">
|
||||
<span class="mark" aria-hidden="true">
|
||||
<svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg>
|
||||
</span>
|
||||
<span>Лидерра<span class="dot">.</span></span>
|
||||
</a>
|
||||
<div class="err-illust" aria-hidden="true">
|
||||
<svg width="220" height="120" viewBox="0 0 220 120" fill="none">
|
||||
<path d="M20 90 L40 60 L60 80 L80 50 L100 70 L120 40" stroke="#D9D5CD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M120 40 L140 90" stroke="#B94837" stroke-width="2.2" stroke-linecap="round"/>
|
||||
<circle cx="140" cy="90" r="4" fill="#B94837"/>
|
||||
<text x="155" y="94" font-family="JetBrains Mono, monospace" font-size="11" fill="#B94837" font-weight="600">503</text>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="err-code">5<span class="accent">0</span>0</h1>
|
||||
<h2 class="err-title">Что-то пошло не так</h2>
|
||||
<p class="err-desc">
|
||||
Внутренняя ошибка — мы уже занимаемся. Команда получила уведомление.
|
||||
Большинство сбоев чинятся за 5–10 минут. Можно вернуться через минуту, или открыть страницу статуса.
|
||||
</p>
|
||||
<div class="err-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="location.reload()">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
|
||||
Попробовать снова
|
||||
</button>
|
||||
<a href="https://status.liderra.app" class="btn">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><polyline points="3 17 9 11 13 15 21 7"/><polyline points="14 7 21 7 21 14"/></svg>
|
||||
Статус сервиса
|
||||
</a>
|
||||
</div>
|
||||
<div class="status-list" aria-label="Состояние ключевых интеграций">
|
||||
<span class="item ok"><span class="dot" aria-hidden="true"></span>API · OK</span>
|
||||
<span class="item deg"><span class="dot" aria-hidden="true"></span>Telegram · деградация</span>
|
||||
<span class="item ok"><span class="dot" aria-hidden="true"></span>YooKassa · OK</span>
|
||||
</div>
|
||||
<div class="err-id">
|
||||
<span>Инцидент</span>
|
||||
<span style="color:var(--ink-2);font-weight:500">INC-2026-0507-0034</span>
|
||||
<button type="button" class="copy-btn" aria-label="Скопировать ID инцидента">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Tab switching for review preview only
|
||||
const tabs = Array.from(document.querySelectorAll('.review-bar .tab'));
|
||||
const pages = Array.from(document.querySelectorAll('.error-page'));
|
||||
function setTab(id) {
|
||||
tabs.forEach(t => {
|
||||
const isA = t.dataset.tab === id;
|
||||
t.classList.toggle('active', isA);
|
||||
t.setAttribute('aria-selected', isA ? 'true' : 'false');
|
||||
});
|
||||
pages.forEach(p => p.classList.toggle('active', p.id === 'err-' + id));
|
||||
}
|
||||
tabs.forEach(t => t.addEventListener('click', () => setTab(t.dataset.tab)));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,415 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Канбан — Лидерра (Forest)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
@media (prefers-reduced-motion: reduce) { .has-motion { animation: none !important; transition: none !important; } }
|
||||
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C; --ink-disabled:#92907B;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-bg:#012019; --side-text:#B1C2BD; --side-text-2:#7A8C87;
|
||||
--side-active:#13382F; --side-icon:#5C7A72; --side-icon-act:#32C8A9;
|
||||
--side-hover:#0A2A22; --side-border:#1A3A30;
|
||||
--st-new:#B94837; --st-work:#B35100; --st-call:#9A6700; --st-nocall:#7E7500;
|
||||
--st-neg:#538200; --st-quote:#008A4D; --st-think:#008C7E; --st-wait:#00889B;
|
||||
--st-paid:#007EB8; --st-refund:#406DC8; --st-fail:#6C60C4;
|
||||
--st-dup:#9052AE; --st-spam:#AA4788; --st-arch:#B7445F;
|
||||
--r-xs:4px; --r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
button { font-family:inherit; }
|
||||
a { color:inherit; text-decoration:none; }
|
||||
a:focus-visible, button:focus-visible, [tabindex]:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
|
||||
}
|
||||
.skip-link { position:absolute; top:-40px; left:12px; background:var(--ink); color:#fff; padding:9px 16px; z-index:200; font-weight:600; border-radius:var(--r-sm); }
|
||||
.skip-link:focus { top:12px; }
|
||||
|
||||
/* Shell */
|
||||
.app { display:grid; grid-template-columns:232px 1fr; min-height:100vh; }
|
||||
.side { background:var(--side-bg); border-right:1px solid var(--side-border); padding:18px 12px 24px; position:sticky; top:0; height:100vh; overflow-y:auto; color:var(--side-text); }
|
||||
.brand { display:flex; align-items:center; gap:10px; padding:6px 8px 18px; font-weight:600; font-size:14.5px; letter-spacing:-0.01em; font-variation-settings:'opsz' 18; color:#FFF; }
|
||||
.brand-mark { width:22px; height:22px; border-radius:var(--r-xs); background:#FFF; display:inline-flex; align-items:center; justify-content:center; flex-shrink:0; overflow:hidden; }
|
||||
.brand-mark svg { width:100%; height:100%; display:block; }
|
||||
.brand-dot { color:var(--side-icon-act); }
|
||||
.nav { display:flex; flex-direction:column; gap:1px; }
|
||||
.nav-eyebrow { font-size:11px; font-weight:500; letter-spacing:0.01em; color:var(--side-text-2); padding:14px 10px 6px; }
|
||||
.nav-item { display:flex; align-items:center; gap:10px; height:32px; padding:0 10px; border-radius:var(--r-sm); font-size:13px; color:var(--side-text); }
|
||||
.nav-item:hover { background:var(--side-hover); color:#FFF; }
|
||||
.nav-item.active { background:var(--side-active); color:#FFF; font-weight:500; }
|
||||
.nav-item.active .nav-icon { color:var(--side-icon-act); }
|
||||
.nav-icon { width:15px; height:15px; flex-shrink:0; color:var(--side-icon); stroke-width:1.6; }
|
||||
.nav-item:hover .nav-icon { color:#FFF; }
|
||||
.nav-text { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.nav-count { font-family:var(--font-mono); font-size:10.5px; font-weight:500; font-feature-settings:'tnum'; background:rgba(255,255,255,0.10); color:var(--side-text); padding:2px 6px; border-radius:4px; }
|
||||
.main { display:flex; flex-direction:column; min-width:0; }
|
||||
.topbar { height:48px; border-bottom:1px solid var(--hairline); background:var(--surface); padding:0 24px; display:flex; align-items:center; gap:12px; position:sticky; top:0; z-index:50; }
|
||||
.crumb { font-size:12.5px; color:var(--ink-3); display:flex; align-items:center; gap:8px; }
|
||||
.crumb strong { color:var(--ink); font-weight:500; }
|
||||
.crumb svg { width:11px; height:11px; color:var(--ink-3); opacity:0.5; }
|
||||
.topbar-spacer { flex:1; }
|
||||
.user-chip { display:inline-flex; align-items:center; gap:8px; height:30px; padding:0 10px 0 4px; border-radius:100px; border:1px solid var(--hairline); background:var(--surface); }
|
||||
.user-chip .ava { width:22px; height:22px; border-radius:50%; background:var(--ink); color:#fff; display:inline-flex; align-items:center; justify-content:center; font-size:9.5px; font-weight:600; }
|
||||
.user-chip .uname { font-size:12px; color:var(--ink); font-weight:500; }
|
||||
|
||||
/* Page */
|
||||
.content { padding:20px 24px 24px; flex:1; display:flex; flex-direction:column; min-height:0; }
|
||||
.page-h { display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; gap:12px; flex-wrap:wrap; }
|
||||
.page-h-l { display:flex; align-items:baseline; gap:14px; }
|
||||
.page-title { font-size:22px; font-weight:600; font-variation-settings:'opsz' 24; letter-spacing:-0.018em; line-height:1.1; margin:0; }
|
||||
.page-stats { font-size:12px; color:var(--ink-3); display:flex; gap:10px; align-items:center; }
|
||||
.page-stats .num { font-family:var(--font-mono); font-feature-settings:'tnum'; color:var(--ink-2); font-weight:500; }
|
||||
.page-stats .accent-num { color:var(--accent); font-weight:600; }
|
||||
.page-stats .sep { color:var(--ink-disabled); }
|
||||
|
||||
.btn { display:inline-flex; align-items:center; gap:7px; height:32px; padding:0 12px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:12px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; }
|
||||
.btn:hover { border-color:var(--ink-disabled); background:var(--sunken); }
|
||||
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
|
||||
.btn svg { width:13px; height:13px; stroke-width:1.7; }
|
||||
|
||||
.search-input { display:inline-flex; align-items:center; gap:8px; height:30px; padding:0 10px; border:1px solid var(--hairline); border-radius:var(--r-sm); background:var(--surface); min-width:200px; }
|
||||
.search-input svg { width:13px; height:13px; color:var(--ink-3); }
|
||||
.search-input input { flex:1; min-width:0; border:none; outline:none; background:none; font-family:inherit; font-size:12px; color:var(--ink); }
|
||||
.search-input input::placeholder { color:var(--ink-3); }
|
||||
|
||||
/* Kanban */
|
||||
.board-wrap { flex:1; min-height:0; overflow-x:auto; overflow-y:hidden; padding-bottom:8px; }
|
||||
.board { display:inline-grid; grid-auto-flow:column; grid-auto-columns:262px; gap:10px; height:calc(100vh - 132px); align-items:stretch; }
|
||||
.col { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); display:flex; flex-direction:column; min-height:0; }
|
||||
.col-h { padding:11px 14px 9px; border-bottom:1px solid var(--hairline-soft); display:flex; align-items:center; gap:8px; }
|
||||
.col-h .dot { width:8px; height:8px; border-radius:50%; flex-shrink:0; position:relative; }
|
||||
.col-h .dot::after { content:''; position:absolute; inset:-1px; border-radius:50%; border:1px solid rgba(10,19,25,0.10); }
|
||||
.col-h .name { font-size:12.5px; font-weight:600; color:var(--ink); letter-spacing:-0.005em; flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.col-h .count { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11px; color:var(--ink-3); font-weight:500; background:var(--sunken); padding:2px 6px; border-radius:3px; }
|
||||
.col-h .menu-btn { width:22px; height:22px; border:none; background:transparent; color:var(--ink-3); border-radius:4px; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; }
|
||||
.col-h .menu-btn:hover { background:var(--sunken); color:var(--ink); }
|
||||
.col-h .menu-btn svg { width:13px; height:13px; }
|
||||
|
||||
.col-list { padding:8px; display:flex; flex-direction:column; gap:6px; overflow-y:auto; flex:1; min-height:0; }
|
||||
|
||||
.card { background:var(--bg); border:1px solid var(--hairline-soft); border-radius:var(--r-sm); padding:10px 12px; cursor:grab; transition:border-color 100ms ease, transform 100ms ease, box-shadow 100ms ease; }
|
||||
.card:hover { border-color:var(--ink-disabled); transform:translateY(-1px); box-shadow:0 2px 6px rgba(10,19,25,0.04); }
|
||||
.card.dragging { opacity:0.5; }
|
||||
.card-row1 { display:flex; align-items:center; gap:6px; margin-bottom:5px; }
|
||||
.card-name { font-size:12.5px; font-weight:500; color:var(--ink); flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; letter-spacing:-0.005em; }
|
||||
.card-icons { display:inline-flex; gap:4px; flex-shrink:0; }
|
||||
.card-ico { width:13px; height:13px; color:var(--ink-3); }
|
||||
.card-ico.hot { color:var(--st-new); }
|
||||
.card-ico.paid { color:var(--accent); }
|
||||
.card-ico.bell { color:var(--st-call); }
|
||||
|
||||
.card-meta { font-size:11px; color:var(--ink-3); margin-bottom:5px; line-height:1.35; letter-spacing:-0.005em; }
|
||||
.card-meta strong { color:var(--ink-2); font-weight:500; }
|
||||
.card-comment { font-size:11px; color:var(--ink-2); line-height:1.4; margin-bottom:6px; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
|
||||
.card-foot { display:flex; justify-content:space-between; align-items:center; font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:10.5px; color:var(--ink-3); letter-spacing:-0.005em; }
|
||||
.card-foot .price { color:var(--ink); font-weight:500; }
|
||||
|
||||
.col-add { padding:8px; border-top:1px solid var(--hairline-soft); }
|
||||
.col-add button { width:100%; height:28px; border:1px dashed var(--hairline); background:transparent; border-radius:var(--r-sm); font-family:inherit; font-size:11.5px; color:var(--ink-3); cursor:pointer; display:inline-flex; align-items:center; justify-content:center; gap:5px; }
|
||||
.col-add button:hover { border-color:var(--ink-disabled); color:var(--ink); }
|
||||
.col-add button svg { width:11px; height:11px; }
|
||||
|
||||
.col[data-status="new"] .col-h .dot { background:var(--st-new); }
|
||||
.col[data-status="work"] .col-h .dot { background:var(--st-work); }
|
||||
.col[data-status="call"] .col-h .dot { background:var(--st-call); }
|
||||
.col[data-status="nocall"] .col-h .dot { background:var(--st-nocall); }
|
||||
.col[data-status="neg"] .col-h .dot { background:var(--st-neg); }
|
||||
.col[data-status="quote"] .col-h .dot { background:var(--st-quote); }
|
||||
.col[data-status="think"] .col-h .dot { background:var(--st-think); }
|
||||
.col[data-status="wait"] .col-h .dot { background:var(--st-wait); }
|
||||
.col[data-status="paid"] .col-h .dot { background:var(--st-paid); }
|
||||
.col[data-status="refund"] .col-h .dot { background:var(--st-refund); }
|
||||
.col[data-status="fail"] .col-h .dot { background:var(--st-fail); }
|
||||
.col[data-status="dup"] .col-h .dot { background:var(--st-dup); }
|
||||
.col[data-status="spam"] .col-h .dot { background:var(--st-spam); }
|
||||
.col[data-status="arch"] .col-h .dot { background:var(--st-arch); }
|
||||
|
||||
@media (max-width:1100px) {
|
||||
.app { grid-template-columns:56px 1fr; }
|
||||
.side { padding:14px 6px; }
|
||||
.brand .brand-text, .nav-eyebrow, .nav-text, .nav-item:not(.active) .nav-count { display:none; }
|
||||
.nav-item { justify-content:center; padding:0; }
|
||||
.topbar { padding:0 16px; }
|
||||
.content { padding:16px 16px 16px; }
|
||||
}
|
||||
@media (max-width:768px) {
|
||||
.app { grid-template-columns:1fr; }
|
||||
.side { display:none; }
|
||||
.topbar .crumb span:first-child { display:none; }
|
||||
.page-h { gap:10px; }
|
||||
.page-stats { display:none; }
|
||||
.board { height:auto; }
|
||||
.col-list { max-height:none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main" class="skip-link">К контенту</a>
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand">
|
||||
<span class="brand-mark" aria-hidden="true"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span class="brand-text">Лидерра<span class="brand-dot">.</span></span>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<div class="nav-eyebrow">Работа</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg><span class="nav-text">Дашборд</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18M6 12h12M9 18h6"/></svg><span class="nav-text">Сделки</span><span class="nav-count">247</span></a>
|
||||
<a class="nav-item active" href="#" aria-current="page"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/><rect x="19" y="3" width="2" height="8"/></svg><span class="nav-text">Канбан</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg><span class="nav-text">Напоминания</span><span class="nav-count">12</span></a>
|
||||
<div class="nav-eyebrow">Финансы</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h18M3 6h18M3 18h18"/></svg><span class="nav-text">Отчёты</span></a>
|
||||
<div class="nav-eyebrow">Команда</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Менеджеры</span><span class="nav-count">4</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Настройки</span></a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Рабочая область</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg><strong>Канбан</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<button type="button" class="user-chip" aria-label="Профиль · Иван Петров"><span class="ava" aria-hidden="true">ИП</span><span class="uname">Иван П.</span></button>
|
||||
</div>
|
||||
|
||||
<main id="main" class="content">
|
||||
<header class="page-h">
|
||||
<div class="page-h-l">
|
||||
<h1 class="page-title">Канбан</h1>
|
||||
<div class="page-stats">
|
||||
<span><span class="accent-num num">+3</span> новых с утра</span>
|
||||
<span class="sep">·</span>
|
||||
<span><span class="num">247</span> в работе</span>
|
||||
<span class="sep">·</span>
|
||||
<span>14 колонок</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;align-items:center">
|
||||
<label class="search-input">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input type="search" placeholder="Поиск по карточкам…" aria-label="Поиск по сделкам">
|
||||
</label>
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/></svg>Фильтры</button>
|
||||
<button type="button" class="btn btn-primary"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Новая</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="board-wrap">
|
||||
<div class="board" role="region" aria-label="Канбан-доска · 14 статусов воронки">
|
||||
|
||||
<section class="col" data-status="new" aria-labelledby="col-new">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-new">Новая</span><span class="count">8</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Анна Соколова</span><span class="card-icons"><svg class="card-ico hot" fill="currentColor" viewBox="0 0 24 24" aria-label="Горячий"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14a8 8 0 0016 0C20 9.91 18.04 6.27 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"/></svg><svg class="card-ico bell" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.8" aria-label="Напоминание"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg></span></div>
|
||||
<div class="card-meta">Натяжные потолки <strong>· 42 м²</strong></div>
|
||||
<div class="card-comment">Звонила в 14:38 — занято. Перезвонить после 15:00. Интересует матовый, рассрочка.</div>
|
||||
<div class="card-foot"><span>+7 (916) 871-23-45</span><span class="price">1 850 ₽</span></div>
|
||||
</article>
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">+7 (495) 482-91-22</span></div>
|
||||
<div class="card-meta">Окна Москва</div>
|
||||
<div class="card-foot"><span>2 минуты назад</span><span class="price">2 200 ₽</span></div>
|
||||
</article>
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Наталья Бузо́ва</span></div>
|
||||
<div class="card-meta">Кухни на заказ</div>
|
||||
<div class="card-foot"><span>14 минут</span><span class="price">3 100 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="work" aria-labelledby="col-work">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-work">В работе</span><span class="count">14</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Виктор Зайцев</span><span class="card-icons"><svg class="card-ico bell" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.8"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg></span></div>
|
||||
<div class="card-meta">Окна Москва <strong>· 1.8×1.5</strong></div>
|
||||
<div class="card-comment">Замер пятница 15:00. Подтвердить за день.</div>
|
||||
<div class="card-foot"><span>13 ч</span><span class="price">2 200 ₽</span></div>
|
||||
</article>
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Эльдар Габбасов</span></div>
|
||||
<div class="card-meta">Натяжные потолки</div>
|
||||
<div class="card-foot"><span>1 ч</span><span class="price">1 850 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="call" aria-labelledby="col-call">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-call">Дозвон</span><span class="count">22</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Елена Васильева</span></div>
|
||||
<div class="card-meta">Кухни на заказ <strong>· 9 м²</strong></div>
|
||||
<div class="card-comment">Думает между двумя планировками. Нужен повторный звонок завтра.</div>
|
||||
<div class="card-foot"><span>4 ч</span><span class="price">3 100 ₽</span></div>
|
||||
</article>
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Мария Орлова</span></div>
|
||||
<div class="card-meta">Окна Москва</div>
|
||||
<div class="card-foot"><span>5 ч</span><span class="price">2 200 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="nocall" aria-labelledby="col-nocall">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-nocall">Не дозвонились</span><span class="count">9</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Татьяна Иванова</span></div>
|
||||
<div class="card-meta">Натяжные потолки</div>
|
||||
<div class="card-foot"><span>11 ч · 3 попытки</span><span class="price">1 850 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="neg" aria-labelledby="col-neg">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-neg">Переговоры</span><span class="count">16</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Иван Петров</span><span class="card-icons"><svg class="card-ico hot" fill="currentColor" viewBox="0 0 24 24"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14a8 8 0 0016 0c0-4.09-1.96-7.73-6.5-13.33z"/></svg></span></div>
|
||||
<div class="card-meta">Окна Москва · договор</div>
|
||||
<div class="card-comment">Согласовали 4 окна, спецификация на завтра.</div>
|
||||
<div class="card-foot"><span>1 ч</span><span class="price">2 200 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="quote" aria-labelledby="col-quote">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-quote">КП отправлено</span><span class="count">11</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Ольга Громова</span></div>
|
||||
<div class="card-meta">Кухни на заказ</div>
|
||||
<div class="card-comment">КП на 218 000 ₽ отправлено в 11:32. Уточнить статус через 2 дня.</div>
|
||||
<div class="card-foot"><span>7 ч</span><span class="price">3 100 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="think" aria-labelledby="col-think">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-think">Думает</span><span class="count">7</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Артём Лебедев</span></div>
|
||||
<div class="card-meta">Окна Москва</div>
|
||||
<div class="card-comment">Сравнивает с двумя другими подрядчиками. Решение к концу недели.</div>
|
||||
<div class="card-foot"><span>9 ч</span><span class="price">2 200 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="wait" aria-labelledby="col-wait">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-wait">Ждёт оплату</span><span class="count">4</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Михаил Орлов</span><span class="card-icons"><svg class="card-ico bell" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.8"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg></span></div>
|
||||
<div class="card-meta">Натяжные потолки · 28 050 ₽</div>
|
||||
<div class="card-comment">Счёт выставлен. Ожидаем оплату до 20:00.</div>
|
||||
<div class="card-foot"><span>3 ч</span><span class="price">1 850 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="paid" aria-labelledby="col-paid">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-paid">Оплачено</span><span class="count">45</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Дмитрий Котов</span><span class="card-icons"><svg class="card-ico paid" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg></span></div>
|
||||
<div class="card-meta">Окна Москва · 5 окон</div>
|
||||
<div class="card-foot"><span>2 ч</span><span class="price">2 200 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="refund" aria-labelledby="col-refund">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-refund">Возврат</span><span class="count">3</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Кирилл Морозов</span></div>
|
||||
<div class="card-meta">Кухни на заказ</div>
|
||||
<div class="card-foot"><span>16 ч</span><span class="price">3 100 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="fail" aria-labelledby="col-fail">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-fail">Отказ</span><span class="count">38</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Сергей Новиков</span></div>
|
||||
<div class="card-meta">Окна Москва</div>
|
||||
<div class="card-foot"><span>5 ч</span><span class="price">2 200 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="dup" aria-labelledby="col-dup">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-dup">Дубликат</span><span class="count">5</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Полина Власова</span></div>
|
||||
<div class="card-meta">Окна Москва · уже #1019</div>
|
||||
<div class="card-foot"><span>вчера</span><span class="price" style="color:var(--ink-3)">—</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="spam" aria-labelledby="col-spam">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-spam">Спам</span><span class="count">5</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">+7 (000) 000-00-00</span></div>
|
||||
<div class="card-meta">Натяжные потолки</div>
|
||||
<div class="card-foot"><span>вчера</span><span class="price" style="color:var(--ink-3)">—</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
<section class="col" data-status="arch" aria-labelledby="col-arch">
|
||||
<div class="col-h"><span class="dot" aria-hidden="true"></span><span class="name" id="col-arch">Архив</span><span class="count">39</span><button type="button" class="menu-btn" aria-label="Меню колонки"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button></div>
|
||||
<div class="col-list">
|
||||
<article class="card" tabindex="0" draggable="true">
|
||||
<div class="card-row1"><span class="card-name">Антон Беляев</span></div>
|
||||
<div class="card-meta">Окна Москва</div>
|
||||
<div class="card-foot"><span>3 дня</span><span class="price">2 200 ₽</span></div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-add"><button type="button"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Добавить</button></div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,606 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Лендинг — Лидерра</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C; --ink-disabled:#92907B;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-bg:#012019; --side-icon-act:#32C8A9;
|
||||
--st-quote:#008A4D; --st-paid:#007EB8; --st-call:#9A6700; --st-new:#B94837;
|
||||
--r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
button { font-family:inherit; }
|
||||
a { color:inherit; text-decoration:none; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
|
||||
}
|
||||
|
||||
/* Review-only switcher */
|
||||
.review-bar { position:sticky; top:0; z-index:100; background:var(--side-bg); padding:10px 16px; display:flex; align-items:center; gap:14px; border-bottom:1px solid #1A3A30; flex-wrap:wrap; }
|
||||
.review-bar .label { font-size:11px; font-weight:600; letter-spacing:0.06em; color:#7A8C87; font-family:var(--font-mono); }
|
||||
.review-bar .tabs { display:flex; gap:2px; flex-wrap:wrap; }
|
||||
.review-bar .tab { height:26px; padding:0 12px; border:none; background:transparent; color:#B1C2BD; font-family:inherit; font-size:12px; font-weight:500; cursor:pointer; border-radius:4px; }
|
||||
.review-bar .tab:hover { background:#0A2A22; color:#fff; }
|
||||
.review-bar .tab.active { background:#fff; color:#012019; }
|
||||
|
||||
.tab-page { display:none; }
|
||||
.tab-page.active { display:block; }
|
||||
|
||||
/* Public site nav */
|
||||
.public-nav {
|
||||
background:var(--bg);
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
padding: 14px 32px;
|
||||
display: flex; align-items: center; gap: 32px;
|
||||
position: sticky; top: 47px; z-index: 50;
|
||||
}
|
||||
.public-nav .brand { display:flex; align-items:center; gap:10px; font-weight:600; font-size:15px; color:var(--ink); font-variation-settings:'opsz' 18; }
|
||||
.public-nav .brand .mark { width:22px; height:22px; border-radius:5px; background:var(--ink); display:inline-flex; align-items:center; justify-content:center; flex-shrink:0; overflow:hidden; }
|
||||
.public-nav .brand .mark svg { width:100%; height:100%; }
|
||||
.public-nav .brand .dot { color:var(--accent); }
|
||||
.public-nav nav { display:flex; gap:24px; flex:1; }
|
||||
.public-nav nav a { font-size:13px; color:var(--ink-2); font-weight:500; letter-spacing:-0.005em; }
|
||||
.public-nav nav a:hover { color:var(--ink); }
|
||||
.public-nav .actions { display:flex; gap:8px; align-items:center; }
|
||||
|
||||
.btn { display:inline-flex; align-items:center; gap:7px; height:36px; padding:0 16px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:13px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; }
|
||||
.btn:hover { border-color:var(--ink-disabled); background:var(--sunken); }
|
||||
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
|
||||
.btn-ghost { background:transparent; border-color:transparent; }
|
||||
.btn-ghost:hover { background:var(--sunken); border-color:transparent; }
|
||||
.btn svg { width:13px; height:13px; stroke-width:1.7; }
|
||||
.btn-lg { height:46px; padding:0 22px; font-size:14px; }
|
||||
|
||||
/* HERO */
|
||||
.hero {
|
||||
padding: 80px 32px 64px;
|
||||
max-width: 1280px; margin: 0 auto;
|
||||
display: grid; grid-template-columns: 1.1fr 1fr; gap: 60px;
|
||||
align-items: center;
|
||||
}
|
||||
.hero-eyebrow {
|
||||
display:inline-flex; align-items:center; gap:8px;
|
||||
padding: 4px 11px;
|
||||
background:var(--accent-tint);
|
||||
color:var(--accent-deep);
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
border-radius: 100px;
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: 0.04em;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.hero-eyebrow .dot { width:6px; height:6px; background:var(--accent); border-radius:50%; }
|
||||
.hero h1 {
|
||||
font-size: 56px;
|
||||
font-weight: 600;
|
||||
font-variation-settings:'opsz' 32;
|
||||
letter-spacing: -0.03em;
|
||||
line-height: 1.05;
|
||||
margin: 0 0 18px;
|
||||
}
|
||||
.hero h1 em { color: var(--accent); font-style: normal; }
|
||||
.hero .lede {
|
||||
font-size: 16px;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.55;
|
||||
margin: 0 0 28px;
|
||||
max-width: 480px;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.hero .cta-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
|
||||
.hero .trust {
|
||||
display: flex; gap: 14px; align-items: center;
|
||||
margin-top: 30px;
|
||||
font-size: 12px;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
.hero .trust .dot { width:6px; height:6px; border-radius:50%; background:var(--accent); }
|
||||
.hero-illust {
|
||||
background: var(--side-bg);
|
||||
border-radius: var(--r-lg);
|
||||
padding: 28px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 360px;
|
||||
}
|
||||
.hero-illust::before {
|
||||
content:'';
|
||||
position:absolute; inset:0;
|
||||
background-image:
|
||||
radial-gradient(circle at 80% 20%, rgba(50,200,169,0.08) 0%, transparent 40%),
|
||||
radial-gradient(circle at 20% 80%, rgba(15,110,86,0.10) 0%, transparent 40%);
|
||||
pointer-events:none;
|
||||
}
|
||||
.hero-illust > * { position: relative; }
|
||||
.hi-card {
|
||||
background: rgba(255,255,255,0.06);
|
||||
border: 1px solid rgba(255,255,255,0.10);
|
||||
border-radius: var(--r-md);
|
||||
padding: 14px 16px;
|
||||
margin-bottom: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
.hi-card .label { font-size: 11px; color: #7A8C87; font-weight: 500; letter-spacing: -0.005em; }
|
||||
.hi-card .val { font-family: var(--font-mono); font-feature-settings:'tnum'; font-size: 32px; font-weight: 600; letter-spacing: -0.02em; line-height: 1; margin-top: 6px; }
|
||||
.hi-card .val .ru { font-size: 14px; color: #B1C2BD; margin-left: 4px; font-weight: 400; }
|
||||
.hi-card .delta { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: 11px; font-weight: 600; color: var(--side-icon-act); margin-top: 8px; }
|
||||
.hi-card.balance { border-color: var(--accent); background: rgba(50,200,169,0.08); }
|
||||
.hi-runway {
|
||||
display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; margin-top: 10px;
|
||||
}
|
||||
.hi-runway span { height:5px; border-radius:1px; background: rgba(255,255,255,0.10); }
|
||||
.hi-runway span.f { background: var(--side-icon-act); }
|
||||
.hi-foot { font-size: 10.5px; color: #B1C2BD; font-family: var(--font-mono); margin-top: 8px; letter-spacing: -0.005em; }
|
||||
.hi-foot strong { color: var(--side-icon-act); font-weight: 600; }
|
||||
|
||||
/* Features */
|
||||
.features { background: var(--surface); border-top: 1px solid var(--hairline); padding: 64px 32px; }
|
||||
.features-inner { max-width: 1280px; margin: 0 auto; }
|
||||
.section-h { text-align: center; margin-bottom: 48px; }
|
||||
.section-h .label { font-family:var(--font-mono); font-size:11px; font-weight:600; letter-spacing:0.06em; color:var(--accent); margin-bottom:12px; }
|
||||
.section-h h2 { font-size:36px; font-weight:600; font-variation-settings:'opsz' 28; letter-spacing:-0.024em; line-height:1.1; margin:0 0 12px; }
|
||||
.section-h h2 em { color: var(--accent); font-style:normal; }
|
||||
.section-h .lede { font-size:14.5px; color:var(--ink-2); line-height:1.55; max-width:640px; margin:0 auto; }
|
||||
|
||||
.feat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
|
||||
.feat-card { background: var(--bg); border: 1px solid var(--hairline-soft); border-radius: var(--r-md); padding: 24px 22px; }
|
||||
.feat-icon { width: 40px; height: 40px; border-radius: var(--r-sm); background: var(--accent-tint); color: var(--accent); display: inline-flex; align-items: center; justify-content: center; margin-bottom: 14px; }
|
||||
.feat-icon svg { width: 18px; height: 18px; stroke-width: 1.7; }
|
||||
.feat-card h3 { font-size: 16px; font-weight: 600; font-variation-settings:'opsz' 18; letter-spacing:-0.012em; margin: 0 0 8px; line-height:1.3; }
|
||||
.feat-card p { font-size: 13px; color: var(--ink-2); line-height: 1.55; margin: 0; letter-spacing:-0.005em; }
|
||||
.feat-card .feat-stat { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11.5px; color:var(--accent); font-weight:600; margin-top:14px; }
|
||||
|
||||
/* Pricing */
|
||||
.pricing { padding: 64px 32px; max-width: 1280px; margin: 0 auto; }
|
||||
.tariffs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
|
||||
.tariff {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
padding: 24px 22px;
|
||||
display: flex; flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
.tariff.popular { border-color: var(--accent); }
|
||||
.tariff.popular::before {
|
||||
content: 'POPULAR'; position: absolute; top: -10px; left: 22px;
|
||||
font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: 0.06em;
|
||||
background: var(--accent); color: #fff;
|
||||
padding: 3px 9px; border-radius: 100px;
|
||||
}
|
||||
.tariff h3 { font-size: 18px; font-weight: 600; font-variation-settings:'opsz' 20; letter-spacing:-0.012em; margin: 0 0 4px; }
|
||||
.tariff .desc { font-size: 12px; color: var(--ink-3); margin: 0 0 14px; line-height: 1.45; min-height: 34px; }
|
||||
.tariff .price { display: flex; align-items: baseline; gap: 4px; margin-bottom: 16px; }
|
||||
.tariff .price .num { font-family: var(--font-mono); font-feature-settings:'tnum'; font-size: 36px; font-weight: 600; letter-spacing: -0.025em; line-height: 1; }
|
||||
.tariff .price .ru { font-size: 13px; color: var(--ink-3); font-family: var(--font-mono); }
|
||||
.tariff ul { margin: 0 0 18px; padding: 0; list-style: none; flex: 1; display: flex; flex-direction: column; gap: 8px; font-size: 12.5px; color: var(--ink-2); }
|
||||
.tariff ul li { display: flex; gap: 8px; line-height: 1.4; letter-spacing:-0.005em; }
|
||||
.tariff ul li svg { width: 12px; height: 12px; color: var(--accent); flex-shrink: 0; margin-top: 3px; stroke-width: 2.4; }
|
||||
.tariff ul li.muted { color: var(--ink-3); text-decoration: line-through; text-decoration-color: var(--ink-disabled); }
|
||||
.tariff ul li.muted svg { color: var(--ink-3); opacity: 0.5; }
|
||||
.tariff-cta { width:100%; justify-content: center; }
|
||||
|
||||
/* FAQ */
|
||||
.faq-section { background: var(--surface); border-top: 1px solid var(--hairline); padding: 64px 32px; }
|
||||
.faq-list { max-width: 800px; margin: 0 auto; display: flex; flex-direction: column; }
|
||||
.faq-item { border-bottom: 1px solid var(--hairline); padding: 18px 0; }
|
||||
.faq-item:last-child { border-bottom: none; }
|
||||
.faq-q { display: flex; justify-content: space-between; align-items: center; font-size: 15px; font-weight: 500; color: var(--ink); cursor: pointer; letter-spacing:-0.005em; }
|
||||
.faq-q svg { width: 14px; height: 14px; color: var(--ink-3); flex-shrink: 0; }
|
||||
.faq-a { font-size: 13px; color: var(--ink-2); line-height: 1.6; margin-top: 10px; max-width: 700px; letter-spacing:-0.005em; }
|
||||
.faq-item.closed .faq-a { display: none; }
|
||||
|
||||
/* Footer */
|
||||
footer { background: var(--side-bg); color: #B1C2BD; padding: 48px 32px 28px; }
|
||||
.footer-inner { max-width: 1280px; margin: 0 auto; display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 32px; }
|
||||
.foot-brand { display:flex; align-items:center; gap:10px; font-weight:600; font-size:16px; color:#fff; margin-bottom:14px; }
|
||||
.foot-brand .mark { width:24px; height:24px; border-radius:5px; background:#fff; display:inline-flex; align-items:center; justify-content:center; overflow:hidden; }
|
||||
.foot-brand .mark svg { width:100%; height:100%; }
|
||||
.foot-brand .dot { color:var(--side-icon-act); }
|
||||
.foot-tagline { font-size:13px; color:#7A8C87; line-height:1.5; max-width:300px; letter-spacing:-0.005em; }
|
||||
.foot-col h4 { font-family:var(--font-mono); font-size:10.5px; font-weight:600; letter-spacing:0.08em; color:#7A8C87; margin: 0 0 14px; text-transform:uppercase; }
|
||||
.foot-col a { display: block; font-size: 13px; color: #B1C2BD; padding: 4px 0; letter-spacing:-0.005em; }
|
||||
.foot-col a:hover { color: #fff; }
|
||||
.foot-bottom {
|
||||
max-width: 1280px; margin: 0 auto; padding-top: 28px; margin-top: 32px;
|
||||
border-top: 1px solid #1A3A30;
|
||||
display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
|
||||
font-size: 11.5px; color: #7A8C87;
|
||||
}
|
||||
|
||||
/* Legal pages */
|
||||
.legal {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 64px 32px 80px;
|
||||
}
|
||||
.legal h1 { font-size: 32px; font-weight: 600; font-variation-settings:'opsz' 28; letter-spacing:-0.022em; margin: 0 0 8px; line-height:1.15; }
|
||||
.legal .updated { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-3); margin-bottom: 32px; letter-spacing:-0.005em; }
|
||||
.legal h2 { font-size: 18px; font-weight: 600; font-variation-settings:'opsz' 20; letter-spacing:-0.012em; margin: 32px 0 10px; line-height:1.3; }
|
||||
.legal h2 .num { color: var(--accent); font-family: var(--font-mono); font-size: 14px; margin-right: 8px; font-weight: 600; }
|
||||
.legal p, .legal li { font-size: 14px; color: var(--ink-2); line-height: 1.65; margin: 0 0 10px; letter-spacing:-0.005em; }
|
||||
.legal ul { padding-left: 20px; margin: 0 0 16px; }
|
||||
.legal ul li { margin-bottom: 6px; }
|
||||
.legal strong { color: var(--ink); font-weight: 600; }
|
||||
.legal .toc { padding: 16px 20px; background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); margin-bottom: 32px; }
|
||||
.legal .toc-title { font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; letter-spacing: 0.06em; color: var(--ink-3); margin-bottom: 8px; text-transform: uppercase; }
|
||||
.legal .toc ol { padding-left: 18px; margin: 0; columns: 2; column-gap: 20px; font-size: 12.5px; color: var(--ink-2); }
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.hero { grid-template-columns: 1fr; gap: 32px; padding: 56px 24px; }
|
||||
.hero h1 { font-size: 42px; }
|
||||
.feat-grid { grid-template-columns: 1fr 1fr; }
|
||||
.tariffs { grid-template-columns: 1fr 1fr; }
|
||||
.footer-inner { grid-template-columns: 1.5fr 1fr 1fr; gap: 24px; }
|
||||
.public-nav { padding: 12px 18px; gap: 18px; }
|
||||
.public-nav nav { gap: 14px; }
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.public-nav nav { display: none; }
|
||||
.hero h1 { font-size: 32px; }
|
||||
.features, .faq-section, .pricing, .legal { padding-left: 18px; padding-right: 18px; }
|
||||
.features, .faq-section { padding-top: 48px; padding-bottom: 48px; }
|
||||
.feat-grid { grid-template-columns: 1fr; }
|
||||
.tariffs { grid-template-columns: 1fr; }
|
||||
.footer-inner { grid-template-columns: 1fr; }
|
||||
.legal .toc ol { columns: 1; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="review-bar" aria-label="Состояние страницы (только для review)">
|
||||
<span class="label">PREVIEW · LANDING</span>
|
||||
<div class="tabs" role="tablist">
|
||||
<button type="button" class="tab active" data-tab="home" role="tab" aria-selected="true">Главная</button>
|
||||
<button type="button" class="tab" data-tab="pricing" role="tab" aria-selected="false">Тарифы</button>
|
||||
<button type="button" class="tab" data-tab="offer" role="tab" aria-selected="false">Оферта</button>
|
||||
<button type="button" class="tab" data-tab="privacy" role="tab" aria-selected="false">Политика</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- ===== HOME ===== -->
|
||||
<div id="page-home" class="tab-page active">
|
||||
<header class="public-nav">
|
||||
<a href="#" class="brand"><span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>Лидерра<span class="dot">.</span></a>
|
||||
<nav><a href="#">Возможности</a><a href="#" data-go="pricing">Тарифы</a><a href="#">База знаний</a><a href="#">Контакты</a></nav>
|
||||
<div class="actions">
|
||||
<a href="/login" class="btn btn-ghost">Войти</a>
|
||||
<a href="/register" class="btn btn-primary">Начать бесплатно →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="hero">
|
||||
<div>
|
||||
<span class="hero-eyebrow"><span class="dot"></span>Lorem ipsum · pre-launch</span>
|
||||
<h1>Поток лидов <em>под контролем</em>.<br>Без потерь и переплаты.</h1>
|
||||
<p class="lede">Pay-per-lead CRM для команд, которые покупают трафик и считают каждый рубль. Канбан с DnD, real-time webhook, 14-статусная воронка, прозрачная биллинг-история. <em>Lorem ipsum dolor sit amet</em>, consectetur adipiscing elit.</p>
|
||||
<div class="cta-row">
|
||||
<a href="/register" class="btn btn-primary btn-lg">
|
||||
Начать бесплатно
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
|
||||
</a>
|
||||
<a href="#" class="btn btn-lg">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
||||
Демо · 4 минуты
|
||||
</a>
|
||||
</div>
|
||||
<div class="trust">
|
||||
<span style="display:inline-flex;align-items:center;gap:6px"><span class="dot"></span>2FA на всех тарифах</span>
|
||||
<span style="display:inline-flex;align-items:center;gap:6px"><span class="dot"></span>152-ФЗ compliant</span>
|
||||
<span style="display:inline-flex;align-items:center;gap:6px"><span class="dot"></span>14-дневный trial</span>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="hero-illust" aria-label="Превью дашборда">
|
||||
<div class="hi-card balance">
|
||||
<div class="label">Баланс кошелька</div>
|
||||
<div class="val">14 250<span class="ru">₽</span></div>
|
||||
<div class="hi-runway"><span class="f"></span><span class="f"></span><span class="f"></span><span class="f"></span><span></span><span></span><span></span></div>
|
||||
<div class="hi-foot">≈ 285 лидов · хватит на <strong>4 дня</strong></div>
|
||||
</div>
|
||||
<div class="hi-card">
|
||||
<div class="label">Получено лидов · 7 дней</div>
|
||||
<div class="val">247</div>
|
||||
<div class="delta">↑ 12.3%</div>
|
||||
</div>
|
||||
<div class="hi-card">
|
||||
<div class="label">Конверсия в оплату</div>
|
||||
<div class="val">18.4<span class="ru">%</span></div>
|
||||
<div class="delta">↑ 2.1pp</div>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<section class="features">
|
||||
<div class="features-inner">
|
||||
<header class="section-h">
|
||||
<div class="label">ЧТО ОТЛИЧАЕТ ЛИДЕРРУ</div>
|
||||
<h2>Не просто <em>CRM</em>. Инструмент для арбитражной модели.</h2>
|
||||
<p class="lede">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.</p>
|
||||
</header>
|
||||
<div class="feat-grid">
|
||||
<article class="feat-card">
|
||||
<div class="feat-icon"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 2L13 10M16 2h5v5"/><path d="M21 13v5a2 2 0 0 1-2 2h-14a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"/></svg></div>
|
||||
<h3>Outbound webhook</h3>
|
||||
<p>Получите лиды в свою систему за секунды через REST + HMAC-SHA256 подпись. Не нужно поллить — мы пушим.</p>
|
||||
<div class="feat-stat">99.97% uptime · 142ms latency</div>
|
||||
</article>
|
||||
<article class="feat-card">
|
||||
<div class="feat-icon"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/><rect x="19" y="3" width="2" height="8"/></svg></div>
|
||||
<h3>Канбан с DnD</h3>
|
||||
<p>14 статусов воронки, optimistic UI с откатом, виртуализация для сотен карточек. Менеджер двигает лиды, а не таблицу.</p>
|
||||
<div class="feat-stat">+38% продуктивность менеджера</div>
|
||||
</article>
|
||||
<article class="feat-card">
|
||||
<div class="feat-icon"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></div>
|
||||
<h3>Real-time push</h3>
|
||||
<p>Браузер + email-уведомления о новых лидах, напоминаниях, изменениях статуса. Тихие часы, чтобы не будить ночью.</p>
|
||||
<div class="feat-stat">Push медиана 2.4 секунды</div>
|
||||
</article>
|
||||
<article class="feat-card">
|
||||
<div class="feat-icon"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg></div>
|
||||
<h3>Настраиваемый дашборд</h3>
|
||||
<p>Сами выбираете, что важно: KPI, графики активности, баланс с прогнозом «на сколько хватит лидов». Drag-and-drop виджеты.</p>
|
||||
<div class="feat-stat">8 виджетов в каталоге</div>
|
||||
</article>
|
||||
<article class="feat-card">
|
||||
<div class="feat-icon"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/><path d="M3 10h18M7 15h3"/></svg></div>
|
||||
<h3>Прозрачный биллинг</h3>
|
||||
<p>Видите каждое списание, причину, стоимость. ЮKassa + банк перевод. УПД и счета в 1С 8.3 XML формате — для бухгалтерии.</p>
|
||||
<div class="feat-stat">Pending-платежи самовосстанавливаются за 30 мин</div>
|
||||
</article>
|
||||
<article class="feat-card">
|
||||
<div class="feat-icon"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></div>
|
||||
<h3>Безопасность из коробки</h3>
|
||||
<p>2FA на всех тарифах, RLS на каждом запросе, hash в storage, click-wrap при регистрации. 152-ФЗ. Удаление по запросу.</p>
|
||||
<div class="feat-stat">Audit log на каждое действие</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="pricing" aria-labelledby="pricing-h">
|
||||
<header class="section-h">
|
||||
<div class="label">ТАРИФЫ</div>
|
||||
<h2 id="pricing-h">Платите за <em>лиды</em>, не за seats.</h2>
|
||||
<p class="lede">Lorem ipsum dolor sit amet, consectetur adipiscing elit. На MVP цена тарифа — 1.00 ₽ для пилота.</p>
|
||||
</header>
|
||||
<div class="tariffs">
|
||||
<article class="tariff">
|
||||
<h3>Start</h3>
|
||||
<p class="desc">Знакомство. Один проект, один менеджер.</p>
|
||||
<div class="price"><span class="num">0</span><span class="ru">₽/мес</span></div>
|
||||
<ul>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>1 проект</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>1 менеджер</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 50 лидов / мес</li>
|
||||
<li class="muted"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Канбан · только просмотр</li>
|
||||
</ul>
|
||||
<a href="/register" class="btn tariff-cta">Бесплатно →</a>
|
||||
</article>
|
||||
<article class="tariff">
|
||||
<h3>Basic</h3>
|
||||
<p class="desc">Малая команда, регулярные продажи.</p>
|
||||
<div class="price"><span class="num">490</span><span class="ru">₽/мес</span></div>
|
||||
<ul>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 3 проектов</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 2 менеджеров</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Канбан с DnD</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Email-уведомления</li>
|
||||
</ul>
|
||||
<a href="/register" class="btn tariff-cta">Выбрать →</a>
|
||||
</article>
|
||||
<article class="tariff popular">
|
||||
<h3>Команда</h3>
|
||||
<p class="desc">Производственная команда с потоком лидов.</p>
|
||||
<div class="price"><span class="num">990</span><span class="ru">₽/мес</span></div>
|
||||
<ul>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 10 проектов</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 4 менеджеров</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Webhook + REST API</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Real-time push</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Telegram-бот команды</li>
|
||||
</ul>
|
||||
<a href="/register" class="btn btn-primary tariff-cta">Выбрать →</a>
|
||||
</article>
|
||||
<article class="tariff">
|
||||
<h3>Enterprise</h3>
|
||||
<p class="desc">Сетевые операторы, индивидуальный SLA.</p>
|
||||
<div class="price"><span class="num">9 990</span><span class="ru">₽/мес</span></div>
|
||||
<ul>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Неогранич. проектов</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 50 менеджеров</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>SSO Yandex 360</li>
|
||||
<li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Dedicated менеджер 24/7</li>
|
||||
</ul>
|
||||
<a href="#" class="btn tariff-cta">Связаться →</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="faq-section" aria-labelledby="faq-h">
|
||||
<header class="section-h">
|
||||
<div class="label">FAQ</div>
|
||||
<h2 id="faq-h">Частые <em>вопросы</em></h2>
|
||||
</header>
|
||||
<div class="faq-list">
|
||||
<details class="faq-item" open>
|
||||
<summary class="faq-q">Что такое pay-per-lead и почему так дешевле?<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></summary>
|
||||
<div class="faq-a">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Платите только за валидные лиды (прошли проверку), не за seats и не за минуты в системе.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary class="faq-q">Как считается стоимость лида?<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></summary>
|
||||
<div class="faq-a">Lorem ipsum dolor sit amet. Цена зависит от проекта (вертикали) и фиксируется в момент покупки. Все возвраты по дубликатам / спаму — бесплатны и автоматически.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary class="faq-q">Можно ли подключить amoCRM или Bitrix24?<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></summary>
|
||||
<div class="faq-a">amoCRM — да, прямой коннектор появится в спринте 14–15. Bitrix24/RetailCRM — Post-MVP. Уже сейчас можно использовать REST API + outbound webhook для любой системы.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary class="faq-q">Что делать, если лид окажется недозвоном или дубликатом?<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></summary>
|
||||
<div class="faq-a">Дубликат и спам — автоматический возврат на баланс. Недозвон фиксируется как отдельный статус, оператор может оспорить через интерфейс или ticket в течение 7 дней.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary class="faq-q">Где хранятся данные?<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></summary>
|
||||
<div class="faq-a">Yandex Cloud (РФ), 152-ФЗ, RLS на каждом запросе, бэкапы каждые 6 часов. Удаление аккаунта удаляет все данные безвозвратно (по запросу пользователя).</div>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="footer-inner">
|
||||
<div>
|
||||
<div class="foot-brand"><span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>Лидерра<span class="dot">.</span></div>
|
||||
<div class="foot-tagline">Поток лидов под контролем. Pay-per-lead CRM для команд, считающих каждый рубль.</div>
|
||||
</div>
|
||||
<div class="foot-col">
|
||||
<h4>Продукт</h4>
|
||||
<a href="#">Возможности</a>
|
||||
<a href="#" data-go="pricing">Тарифы</a>
|
||||
<a href="#">Демо</a>
|
||||
<a href="#">Roadmap</a>
|
||||
</div>
|
||||
<div class="foot-col">
|
||||
<h4>Документация</h4>
|
||||
<a href="#">REST API</a>
|
||||
<a href="#">Webhook</a>
|
||||
<a href="#">База знаний</a>
|
||||
<a href="#">Status</a>
|
||||
</div>
|
||||
<div class="foot-col">
|
||||
<h4>Юридическое</h4>
|
||||
<a href="#" data-go="offer">Оферта</a>
|
||||
<a href="#" data-go="privacy">Политика конфиденциальности</a>
|
||||
<a href="#">Реквизиты</a>
|
||||
<a href="mailto:support@liderra.app">Поддержка</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="foot-bottom">
|
||||
<span>© 2026 Лидерра · ИП Сидоров А.А. · ОГРНИП 322000000000000</span>
|
||||
<span>support@liderra.app · +7 495 000-00-00</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- ===== PRICING (alone) ===== -->
|
||||
<div id="page-pricing" class="tab-page">
|
||||
<header class="public-nav">
|
||||
<a href="#" class="brand"><span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>Лидерра<span class="dot">.</span></a>
|
||||
<nav><a href="#" data-go="home">Главная</a><a href="#">Возможности</a><a href="#">База знаний</a><a href="#">Контакты</a></nav>
|
||||
<div class="actions"><a href="/login" class="btn btn-ghost">Войти</a><a href="/register" class="btn btn-primary">Начать →</a></div>
|
||||
</header>
|
||||
<section class="pricing">
|
||||
<header class="section-h">
|
||||
<div class="label">ТАРИФЫ</div>
|
||||
<h2>Простой <em>биллинг</em>. Без сюрпризов.</h2>
|
||||
<p class="lede">Lorem ipsum. На MVP все тарифы стоят 1.00 ₽ для пилотного периода.</p>
|
||||
</header>
|
||||
<div class="tariffs">
|
||||
<article class="tariff"><h3>Start</h3><p class="desc">Знакомство. Один проект, один менеджер.</p><div class="price"><span class="num">0</span><span class="ru">₽/мес</span></div><ul><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>1 проект</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>1 менеджер</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 50 лидов / мес</li><li class="muted"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Канбан · только просмотр</li></ul><a href="/register" class="btn tariff-cta">Бесплатно →</a></article>
|
||||
<article class="tariff"><h3>Basic</h3><p class="desc">Малая команда.</p><div class="price"><span class="num">490</span><span class="ru">₽/мес</span></div><ul><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 3 проектов</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 2 менеджеров</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Канбан с DnD</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Email-уведомления</li></ul><a href="/register" class="btn tariff-cta">Выбрать →</a></article>
|
||||
<article class="tariff popular"><h3>Команда</h3><p class="desc">Производственная команда.</p><div class="price"><span class="num">990</span><span class="ru">₽/мес</span></div><ul><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 10 проектов</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 4 менеджеров</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Webhook + REST API</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Real-time push</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Telegram-бот команды</li></ul><a href="/register" class="btn btn-primary tariff-cta">Выбрать →</a></article>
|
||||
<article class="tariff"><h3>Enterprise</h3><p class="desc">Сетевые операторы, SLA.</p><div class="price"><span class="num">9 990</span><span class="ru">₽/мес</span></div><ul><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Неогранич. проектов</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>До 50 менеджеров</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>SSO Yandex 360</li><li><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>Dedicated 24/7</li></ul><a href="#" class="btn tariff-cta">Связаться →</a></article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- ===== OFFER ===== -->
|
||||
<div id="page-offer" class="tab-page">
|
||||
<header class="public-nav">
|
||||
<a href="#" class="brand"><span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>Лидерра<span class="dot">.</span></a>
|
||||
<nav><a href="#" data-go="home">Главная</a><a href="#" data-go="pricing">Тарифы</a><a href="#" data-go="privacy">Политика</a></nav>
|
||||
<div class="actions"><a href="/login" class="btn btn-ghost">Войти</a></div>
|
||||
</header>
|
||||
<main class="legal">
|
||||
<h1>Договор-оферта</h1>
|
||||
<div class="updated">Действует с 01.05.2026 · версия 1.0</div>
|
||||
<div class="toc"><div class="toc-title">Содержание</div><ol><li>Термины</li><li>Предмет договора</li><li>Регистрация и аккаунт</li><li>Стоимость и порядок оплаты</li><li>Возврат денежных средств</li><li>Права и обязанности сторон</li><li>Ответственность</li><li>Конфиденциальность</li><li>Срок действия</li><li>Заключительные положения</li></ol></div>
|
||||
<h2><span class="num">1.</span>Термины</h2>
|
||||
<p>В настоящем Договоре используются следующие термины: <strong>«Оператор»</strong> — ИП Сидоров А.А., <strong>«Клиент»</strong> — юридическое или физическое лицо, акцептовавшее настоящую Оферту, <strong>«Сервис»</strong> — программно-аппаратный комплекс «Лидерра», доступный по адресу liderra.app.</p>
|
||||
<h2><span class="num">2.</span>Предмет договора</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Оператор предоставляет Клиенту неисключительное право использования Сервиса на условиях SaaS на возмездной основе.</p>
|
||||
<h2><span class="num">3.</span>Регистрация и аккаунт</h2>
|
||||
<p>Клиент регистрируется через сайт liderra.app, указывая работающий email, пароль и подтверждая 3 click-wrap чекбокса: <strong>принятие оферты</strong>, <strong>согласие на обработку ПДн</strong>, <strong>согласие на информационные сообщения</strong> (опционально).</p>
|
||||
<p>Lorem ipsum. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
|
||||
<h2><span class="num">4.</span>Стоимость и порядок оплаты</h2>
|
||||
<p>Минимальное пополнение баланса — <strong>100 ₽</strong>. При конвертации ₽ → лиды округление производится <strong>вниз</strong> (Б-3, Б-4 регламент). Оператор оставляет за собой право изменять тарифы с уведомлением Клиента за 30 дней.</p>
|
||||
<h2><span class="num">5.</span>Возврат денежных средств</h2>
|
||||
<p>Стоимость лидов, признанных <strong>дубликатами</strong> или <strong>спамом</strong> по результатам автоматической проверки, возвращается на баланс Клиента в течение 24 часов. Возврат остатка кошелька осуществляется по письменному запросу в течение 30 рабочих дней.</p>
|
||||
<h2><span class="num">6.</span>Права и обязанности сторон</h2>
|
||||
<ul><li>Оператор обязуется обеспечивать доступность Сервиса не менее 99.5% времени в месяц.</li><li>Клиент обязуется использовать Сервис в законных целях, не передавать доступ третьим лицам.</li><li>Оператор имеет право приостановить доступ при нарушении условий или просрочке платежа более 7 дней.</li></ul>
|
||||
<h2><span class="num">7.</span>Ответственность</h2>
|
||||
<p>Lorem ipsum dolor sit amet. Стороны несут ответственность в соответствии с действующим законодательством Российской Федерации. Размер ответственности Оператора ограничен суммой оплаченных Клиентом услуг за последние 3 месяца.</p>
|
||||
<h2><span class="num">8.</span>Конфиденциальность</h2>
|
||||
<p>Подробная информация об обработке персональных данных изложена в <a href="#" data-go="privacy" style="color:var(--accent);font-weight:500">Политике конфиденциальности</a>.</p>
|
||||
<p style="margin-top:32px;font-size:12px;color:var(--ink-3)">⚠ Это шаблонный документ для MVP. Финальный текст будет уточнён юристами после регистрации ООО (Б-1).</p>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- ===== PRIVACY ===== -->
|
||||
<div id="page-privacy" class="tab-page">
|
||||
<header class="public-nav">
|
||||
<a href="#" class="brand"><span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>Лидерра<span class="dot">.</span></a>
|
||||
<nav><a href="#" data-go="home">Главная</a><a href="#" data-go="pricing">Тарифы</a><a href="#" data-go="offer">Оферта</a></nav>
|
||||
<div class="actions"><a href="/login" class="btn btn-ghost">Войти</a></div>
|
||||
</header>
|
||||
<main class="legal">
|
||||
<h1>Политика конфиденциальности</h1>
|
||||
<div class="updated">Действует с 01.05.2026 · 152-ФЗ compliant · версия 1.0</div>
|
||||
<div class="toc"><div class="toc-title">Содержание</div><ol><li>Кто мы</li><li>Какие данные собираем</li><li>Зачем собираем</li><li>Как храним и защищаем</li><li>Кому передаём</li><li>Cookie и трекинг</li><li>Ваши права</li><li>Удаление данных</li><li>Изменения политики</li><li>Контакты</li></ol></div>
|
||||
<h2><span class="num">1.</span>Кто мы</h2>
|
||||
<p>Оператор персональных данных — <strong>ИП Сидоров А.А.</strong>, ОГРНИП 322000000000000, регистрация в Реестре операторов ПДн Роскомнадзора № 22-25-001234. Email для запросов: <a href="mailto:dpo@liderra.app" style="color:var(--accent);font-weight:500">dpo@liderra.app</a>.</p>
|
||||
<h2><span class="num">2.</span>Какие данные собираем</h2>
|
||||
<ul><li><strong>От Клиента:</strong> email, пароль (в виде хеша), имя, телефон, реквизиты юр. лица для счетов</li><li><strong>От Лидов:</strong> имя, телефон, email, источник трафика, UTM-метки, адрес объекта (если применимо)</li><li><strong>Технические:</strong> IP-адрес, user-agent, время посещения, маршрут по сайту</li></ul>
|
||||
<h2><span class="num">3.</span>Зачем собираем</h2>
|
||||
<p>Lorem ipsum dolor sit amet. Только для целей оказания услуг согласно <a href="#" data-go="offer" style="color:var(--accent);font-weight:500">Оферте</a>: обработка лидов, биллинг, безопасность, поддержка. Никаких маркетинговых рассылок без отдельного согласия.</p>
|
||||
<h2><span class="num">4.</span>Как храним и защищаем</h2>
|
||||
<p>Хранение — в Yandex Cloud, регион РФ. Шифрование at-rest и in-transit. Row-Level Security на каждом запросе — данные одного тенанта изолированы от другого. Бэкапы каждые 6 часов, retention 90 дней.</p>
|
||||
<h2><span class="num">5.</span>Кому передаём</h2>
|
||||
<p>Платёжные данные — оператору ЮKassa (ООО НКО «ЮMoney», ИНН 7750005725). Email-рассылки — SMTP-провайдеру. Никаких других третьих сторон.</p>
|
||||
<h2><span class="num">6.</span>Cookie и трекинг</h2>
|
||||
<p>Используем только необходимые куки (auth, CSRF-токен, preferences). Аналитика — Яндекс.Метрика (агрегированно). Маркетинговые трекеры не используются.</p>
|
||||
<h2><span class="num">7.</span>Ваши права</h2>
|
||||
<ul><li>Запросить копию ваших данных в машиночитаемом формате</li><li>Исправить неточные данные</li><li>Удалить аккаунт и все связанные данные (необратимо)</li><li>Отозвать согласие на обработку (приведёт к закрытию аккаунта)</li></ul>
|
||||
<h2><span class="num">8.</span>Удаление данных</h2>
|
||||
<p>В личном кабинете → Настройки → Профиль → «Удалить мой аккаунт». Удаление инициируется немедленно, окончательно стирается через 30 дней (резервный период для ошибочных удалений).</p>
|
||||
<h2><span class="num">9.</span>Изменения политики</h2>
|
||||
<p>При существенных изменениях уведомляем по email и баннером в кабинете за 14 дней.</p>
|
||||
<h2><span class="num">10.</span>Контакты</h2>
|
||||
<p>По всем вопросам персональных данных: <a href="mailto:dpo@liderra.app" style="color:var(--accent);font-weight:500">dpo@liderra.app</a> · ответ в течение 7 рабочих дней. По общим вопросам: <a href="mailto:support@liderra.app" style="color:var(--accent);font-weight:500">support@liderra.app</a>.</p>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const tabs = Array.from(document.querySelectorAll('.review-bar .tab'));
|
||||
const pages = Array.from(document.querySelectorAll('.tab-page'));
|
||||
function setTab(id) {
|
||||
tabs.forEach(t => {
|
||||
const isA = t.dataset.tab === id;
|
||||
t.classList.toggle('active', isA);
|
||||
t.setAttribute('aria-selected', isA ? 'true' : 'false');
|
||||
});
|
||||
pages.forEach(p => p.classList.toggle('active', p.id === 'page-' + id));
|
||||
window.scrollTo({ top: 0, behavior: 'instant' });
|
||||
}
|
||||
tabs.forEach(t => t.addEventListener('click', () => setTab(t.dataset.tab)));
|
||||
document.querySelectorAll('[data-go]').forEach(a => {
|
||||
a.addEventListener('click', e => { e.preventDefault(); setTab(a.dataset.go); });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,524 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Вход — Лидерра</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C; --ink-disabled:#92907B;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-bg:#012019; --side-icon-act:#32C8A9;
|
||||
--st-paid-solid:#007EB8;
|
||||
--st-quote-solid:#008A4D;
|
||||
--st-call-solid:#9A6700;
|
||||
--st-new-solid:#B94837;
|
||||
--r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
button { font-family:inherit; }
|
||||
a { color:inherit; text-decoration:none; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
|
||||
}
|
||||
|
||||
/* Review-only state switcher */
|
||||
.review-bar { position:sticky; top:0; z-index:50; background:var(--side-bg); padding:10px 16px; display:flex; align-items:center; gap:14px; border-bottom:1px solid #1A3A30; flex-wrap:wrap; }
|
||||
.review-bar .label { font-size:11px; font-weight:600; letter-spacing:0.06em; color:#7A8C87; font-family:var(--font-mono); }
|
||||
.review-bar .tabs { display:flex; gap:2px; }
|
||||
.review-bar .tab { height:26px; padding:0 12px; border:none; background:transparent; color:#B1C2BD; font-family:inherit; font-size:12px; font-weight:500; cursor:pointer; border-radius:4px; }
|
||||
.review-bar .tab:hover { background:#0A2A22; color:#fff; }
|
||||
.review-bar .tab.active { background:#fff; color:#012019; }
|
||||
|
||||
/* Two-pane layout: left = brand canvas, right = form */
|
||||
.layout { display:grid; grid-template-columns:1fr 1fr; min-height:calc(100vh - 47px); }
|
||||
|
||||
.brand-pane {
|
||||
background: var(--side-bg);
|
||||
color: #fff;
|
||||
padding: 56px 60px;
|
||||
display: flex; flex-direction: column; justify-content: space-between;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.brand-pane::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image:
|
||||
radial-gradient(circle at 80% 20%, rgba(50,200,169,0.08) 0%, transparent 40%),
|
||||
radial-gradient(circle at 20% 80%, rgba(15,110,86,0.10) 0%, transparent 40%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.brand-pane > * { position: relative; z-index: 1; }
|
||||
|
||||
.bp-brand { display:flex; align-items:center; gap:10px; font-weight:600; font-size:16px; letter-spacing:-0.01em; font-variation-settings:'opsz' 18; }
|
||||
.bp-brand .mark { width:24px; height:24px; border-radius:5px; background:#fff; display:inline-flex; align-items:center; justify-content:center; flex-shrink:0; overflow:hidden; }
|
||||
.bp-brand .mark svg { width:100%; height:100%; display:block; }
|
||||
.bp-brand .dot { color:var(--side-icon-act); }
|
||||
|
||||
.bp-quote {
|
||||
font-size:30px;
|
||||
font-weight:500;
|
||||
font-variation-settings:'opsz' 28;
|
||||
letter-spacing:-0.02em;
|
||||
line-height:1.18;
|
||||
max-width:440px;
|
||||
}
|
||||
.bp-quote em {
|
||||
color: var(--side-icon-act);
|
||||
font-style: normal;
|
||||
}
|
||||
.bp-foot { font-size:12px; color:#7A8C87; font-family:var(--font-mono); display:flex; gap:14px; }
|
||||
.bp-foot a { color:inherit; }
|
||||
.bp-foot a:hover { color: #fff; }
|
||||
|
||||
.form-pane {
|
||||
background: var(--bg);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
padding: 40px 32px 56px;
|
||||
}
|
||||
.form-card {
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
display: flex; flex-direction: column; gap: 20px;
|
||||
}
|
||||
.form-h h1 {
|
||||
font-size:26px;
|
||||
font-weight:600;
|
||||
font-variation-settings:'opsz' 26;
|
||||
letter-spacing:-0.018em;
|
||||
margin:0 0 6px;
|
||||
line-height:1.15;
|
||||
}
|
||||
.form-h p {
|
||||
font-size:13px;
|
||||
color:var(--ink-3);
|
||||
margin:0;
|
||||
line-height:1.5;
|
||||
letter-spacing:-0.005em;
|
||||
}
|
||||
.form-h p a {
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
.form-h p a:hover { text-decoration: underline; text-underline-offset: 3px; }
|
||||
|
||||
.field { display:flex; flex-direction:column; gap:6px; }
|
||||
.field label {
|
||||
font-size:11.5px;
|
||||
font-weight:500;
|
||||
color:var(--ink-2);
|
||||
letter-spacing:-0.005em;
|
||||
}
|
||||
.input {
|
||||
height:40px;
|
||||
padding:0 12px;
|
||||
border:1px solid var(--hairline);
|
||||
border-radius:var(--r-sm);
|
||||
background:var(--surface);
|
||||
font-family:inherit;
|
||||
font-size:13.5px;
|
||||
color:var(--ink);
|
||||
outline:none;
|
||||
transition: border-color 100ms ease, box-shadow 100ms ease;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.input::placeholder { color: var(--ink-3); }
|
||||
.input:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-tint); }
|
||||
.input.mono { font-family: var(--font-mono); font-feature-settings:'tnum'; letter-spacing: 0; }
|
||||
|
||||
.field-row { display:flex; gap:6px; }
|
||||
.password-group { position:relative; }
|
||||
.password-group .input { padding-right: 38px; }
|
||||
.eye-btn {
|
||||
position:absolute; right:6px; top:50%; transform:translateY(-50%);
|
||||
width:28px; height:28px; border:none; background:transparent;
|
||||
color:var(--ink-3); cursor:pointer;
|
||||
border-radius:4px; display:inline-flex; align-items:center; justify-content:center;
|
||||
}
|
||||
.eye-btn:hover { background: rgba(10,19,25,0.06); color: var(--ink); }
|
||||
.eye-btn svg { width:14px; height:14px; stroke-width:1.7; }
|
||||
|
||||
.field-foot {
|
||||
display:flex; justify-content:space-between; align-items:center;
|
||||
font-size:11.5px;
|
||||
color:var(--ink-3);
|
||||
letter-spacing:-0.005em;
|
||||
}
|
||||
.field-foot a {
|
||||
color:var(--accent);
|
||||
font-weight:500;
|
||||
}
|
||||
.field-foot a:hover { text-decoration: underline; text-underline-offset: 3px; }
|
||||
|
||||
/* Strength indicator */
|
||||
.strength { display:flex; gap:3px; margin-top:4px; }
|
||||
.strength span { flex:1; height:3px; border-radius:2px; background:var(--hairline); }
|
||||
.strength[data-level="1"] span:nth-child(1) { background: var(--st-new-solid); }
|
||||
.strength[data-level="2"] span:nth-child(-n+2) { background: var(--st-call-solid); }
|
||||
.strength[data-level="3"] span:nth-child(-n+3) { background: var(--st-quote-solid); }
|
||||
.strength[data-level="4"] span { background: var(--accent); }
|
||||
.strength-label {
|
||||
font-size:10.5px;
|
||||
font-family:var(--font-mono);
|
||||
color:var(--ink-3);
|
||||
margin-top:5px;
|
||||
letter-spacing:-0.005em;
|
||||
}
|
||||
.strength-label strong { color: var(--accent); font-weight: 600; }
|
||||
|
||||
/* checkbox click-wrap */
|
||||
.checks { display: flex; flex-direction: column; gap: 8px; }
|
||||
.cw {
|
||||
display: flex; align-items: flex-start; gap: 9px;
|
||||
font-size: 12px;
|
||||
color: var(--ink-2);
|
||||
cursor: pointer;
|
||||
line-height: 1.5;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.cw input[type="checkbox"] {
|
||||
appearance: none; -webkit-appearance: none;
|
||||
width: 16px; height: 16px;
|
||||
border: 1.5px solid var(--ink-disabled);
|
||||
border-radius: 4px;
|
||||
background: var(--surface);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
}
|
||||
.cw input[type="checkbox"]:checked {
|
||||
background: var(--ink);
|
||||
border-color: var(--ink);
|
||||
}
|
||||
.cw input[type="checkbox"]:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 4px; top: 1px;
|
||||
width: 4px; height: 8px;
|
||||
border: solid #fff;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.cw input[type="checkbox"]:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px;
|
||||
}
|
||||
.cw a { color: var(--accent); font-weight: 500; }
|
||||
.cw a:hover { text-decoration: underline; text-underline-offset: 3px; }
|
||||
|
||||
/* Submit + secondary */
|
||||
.btn { display:inline-flex; align-items:center; justify-content:center; gap:7px; height:42px; padding:0 16px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:13.5px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; width: 100%; }
|
||||
.btn:hover { border-color:var(--ink-disabled); background:var(--sunken); }
|
||||
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
|
||||
.btn svg { width:14px; height:14px; stroke-width:1.7; }
|
||||
|
||||
.divider { display:flex; align-items:center; gap:12px; margin:4px 0; color:var(--ink-3); font-size:11px; }
|
||||
.divider::before, .divider::after { content:''; flex:1; height:1px; background:var(--hairline); }
|
||||
|
||||
/* 2FA code input */
|
||||
.code-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
.code-cell {
|
||||
height: 52px;
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-sm);
|
||||
background: var(--surface);
|
||||
font-family: var(--font-mono);
|
||||
font-feature-settings: 'tnum';
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
text-align: center;
|
||||
outline: none;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
.code-cell:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-tint); }
|
||||
|
||||
.alert {
|
||||
padding: 11px 14px;
|
||||
border-radius: var(--r-sm);
|
||||
background: #FFF8E5;
|
||||
border: 1px solid #F0D89E;
|
||||
color: #614209;
|
||||
font-size: 12px;
|
||||
display: flex; gap: 10px; align-items: flex-start;
|
||||
line-height: 1.45;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.alert svg { width: 14px; height: 14px; color: #B35100; flex-shrink: 0; margin-top: 1px; }
|
||||
.alert strong { color: #4A2F00; font-weight: 600; }
|
||||
|
||||
/* Recovery codes */
|
||||
.codes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 6px;
|
||||
background: var(--surface);
|
||||
padding: 14px 16px;
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-sm);
|
||||
}
|
||||
.code-item {
|
||||
font-family: var(--font-mono);
|
||||
font-feature-settings: 'tnum';
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--ink);
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.form-page { display: none; }
|
||||
.form-page.active { display: contents; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.layout { grid-template-columns: 1fr; min-height: auto; }
|
||||
.brand-pane { padding: 32px 24px; min-height: 220px; }
|
||||
.bp-quote { font-size: 22px; }
|
||||
.form-pane { padding: 28px 20px 40px; }
|
||||
}
|
||||
@media (max-width: 380px) {
|
||||
.code-row { grid-template-columns: repeat(6, 1fr); gap: 4px; }
|
||||
.code-cell { height: 44px; font-size: 18px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="review-bar" aria-label="Состояние формы (только для review)">
|
||||
<span class="label">PREVIEW</span>
|
||||
<div class="tabs" role="tablist">
|
||||
<button type="button" class="tab active" data-tab="login" role="tab" aria-selected="true">Вход</button>
|
||||
<button type="button" class="tab" data-tab="register" role="tab" aria-selected="false">Регистрация</button>
|
||||
<button type="button" class="tab" data-tab="2fa" role="tab" aria-selected="false">2FA</button>
|
||||
<button type="button" class="tab" data-tab="forgot" role="tab" aria-selected="false">Сброс пароля</button>
|
||||
<button type="button" class="tab" data-tab="recovery" role="tab" aria-selected="false">Резервные коды</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="layout">
|
||||
|
||||
<!-- Brand pane (always visible) -->
|
||||
<aside class="brand-pane" aria-label="Брендинг и юридические ссылки">
|
||||
<a href="#" class="bp-brand">
|
||||
<span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span>Лидерра<span class="dot">.</span></span>
|
||||
</a>
|
||||
<div class="bp-quote">
|
||||
Поток лидов <em>под контролем</em>.<br>
|
||||
Pay-per-lead, прозрачно — каждый рубль <em>в дело</em>.
|
||||
</div>
|
||||
<div class="bp-foot">
|
||||
<span>v8 · Forest</span>
|
||||
<a href="/legal/offer">Оферта</a>
|
||||
<a href="/legal/privacy">Политика</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="form-pane" role="main">
|
||||
|
||||
<!-- ===== LOGIN ===== -->
|
||||
<section id="form-login" class="form-page active" aria-labelledby="title-login">
|
||||
<div class="form-card">
|
||||
<header class="form-h">
|
||||
<h1 id="title-login">Вход в Лидерру</h1>
|
||||
<p>Нет аккаунта? <a href="#" data-go="register">Зарегистрируйтесь</a></p>
|
||||
</header>
|
||||
<div class="field">
|
||||
<label for="login-email">Email</label>
|
||||
<input class="input" id="login-email" type="email" autocomplete="email" placeholder="manager@yourcompany.ru" value="ivan.petrov@example.ru">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="login-pass">Пароль</label>
|
||||
<div class="password-group">
|
||||
<input class="input" id="login-pass" type="password" autocomplete="current-password" placeholder="Минимум 8 символов" value="••••••••••">
|
||||
<button type="button" class="eye-btn" aria-label="Показать пароль">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="field-foot">
|
||||
<span> </span>
|
||||
<a href="#" data-go="forgot">Забыли пароль?</a>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary">
|
||||
Войти
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
|
||||
</button>
|
||||
<div class="divider">или</div>
|
||||
<button type="button" class="btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="#FF0000" aria-hidden="true"><circle cx="12" cy="12" r="10"/><text x="12" y="16" text-anchor="middle" fill="#fff" font-family="sans-serif" font-size="11" font-weight="bold">Я</text></svg>
|
||||
Войти через Yandex 360
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== REGISTER ===== -->
|
||||
<section id="form-register" class="form-page" aria-labelledby="title-register">
|
||||
<div class="form-card">
|
||||
<header class="form-h">
|
||||
<h1 id="title-register">Создать аккаунт</h1>
|
||||
<p>Уже есть? <a href="#" data-go="login">Войдите</a></p>
|
||||
</header>
|
||||
<div class="field">
|
||||
<label for="reg-email">Рабочий email</label>
|
||||
<input class="input" id="reg-email" type="email" autocomplete="email" placeholder="manager@yourcompany.ru">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="reg-pass">Пароль</label>
|
||||
<div class="password-group">
|
||||
<input class="input" id="reg-pass" type="password" autocomplete="new-password" placeholder="Минимум 8 символов" value="••••••••••••">
|
||||
<button type="button" class="eye-btn" aria-label="Показать пароль">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="strength" data-level="3" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
|
||||
<div class="strength-label">Надёжный · <strong>добавьте символ</strong> для максимума</div>
|
||||
</div>
|
||||
<div class="checks">
|
||||
<label class="cw">
|
||||
<input type="checkbox" checked>
|
||||
<span>Принимаю <a href="/legal/offer">оферту</a></span>
|
||||
</label>
|
||||
<label class="cw">
|
||||
<input type="checkbox" checked>
|
||||
<span>Согласен с <a href="/legal/privacy">политикой обработки персональных данных</a></span>
|
||||
</label>
|
||||
<label class="cw">
|
||||
<input type="checkbox">
|
||||
<span>Согласен на получение информационных сообщений от Лидерра</span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary">
|
||||
Создать аккаунт
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== 2FA ===== -->
|
||||
<section id="form-2fa" class="form-page" aria-labelledby="title-2fa">
|
||||
<div class="form-card">
|
||||
<header class="form-h">
|
||||
<h1 id="title-2fa">Двухфакторная проверка</h1>
|
||||
<p>Откройте приложение-аутентификатор и введите 6-значный код для <strong style="color:var(--ink-2);font-weight:500">ivan.petrov@example.ru</strong></p>
|
||||
</header>
|
||||
<div class="field">
|
||||
<label>Код из приложения</label>
|
||||
<div class="code-row">
|
||||
<input class="code-cell" type="text" inputmode="numeric" maxlength="1" value="4" aria-label="Цифра 1">
|
||||
<input class="code-cell" type="text" inputmode="numeric" maxlength="1" value="2" aria-label="Цифра 2">
|
||||
<input class="code-cell" type="text" inputmode="numeric" maxlength="1" value="8" aria-label="Цифра 3">
|
||||
<input class="code-cell" type="text" inputmode="numeric" maxlength="1" value="1" aria-label="Цифра 4">
|
||||
<input class="code-cell" type="text" inputmode="numeric" maxlength="1" value="" aria-label="Цифра 5">
|
||||
<input class="code-cell" type="text" inputmode="numeric" maxlength="1" value="" aria-label="Цифра 6">
|
||||
</div>
|
||||
<div class="field-foot">
|
||||
<a href="#" data-go="recovery">Использовать резервный код</a>
|
||||
<span style="font-family:var(--font-mono);font-feature-settings:'tnum'">02:34</span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary">
|
||||
Подтвердить
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== FORGOT PASSWORD ===== -->
|
||||
<section id="form-forgot" class="form-page" aria-labelledby="title-forgot">
|
||||
<div class="form-card">
|
||||
<header class="form-h">
|
||||
<h1 id="title-forgot">Сброс пароля</h1>
|
||||
<p>Введите email, на который зарегистрирован аккаунт. Отправим ссылку для сброса.</p>
|
||||
</header>
|
||||
<div class="field">
|
||||
<label for="fp-email">Email</label>
|
||||
<input class="input" id="fp-email" type="email" placeholder="manager@yourcompany.ru">
|
||||
</div>
|
||||
<div class="alert" role="status">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.7" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg>
|
||||
<span>Лимит — <strong>5 попыток в 15 минут</strong>. Если не пришло письмо — проверьте спам или попробуйте через 15 минут.</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary">
|
||||
Отправить ссылку
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
|
||||
</button>
|
||||
<button type="button" class="btn" data-go="login">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
||||
Назад ко входу
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== RECOVERY CODES ===== -->
|
||||
<section id="form-recovery" class="form-page" aria-labelledby="title-recovery">
|
||||
<div class="form-card">
|
||||
<header class="form-h">
|
||||
<h1 id="title-recovery">Резервные коды</h1>
|
||||
<p>Сохраните эти 8 одноразовых кодов в безопасном месте. Каждый можно использовать только раз вместо 2FA.</p>
|
||||
</header>
|
||||
<div class="codes-grid">
|
||||
<span class="code-item">A4FX-91KZ</span>
|
||||
<span class="code-item">9MRT-2P3D</span>
|
||||
<span class="code-item">QH7B-XK4N</span>
|
||||
<span class="code-item">5VLW-T8RY</span>
|
||||
<span class="code-item">B2ZJ-N6FP</span>
|
||||
<span class="code-item">D3WK-Q9MX</span>
|
||||
<span class="code-item">7YHC-8GVB</span>
|
||||
<span class="code-item">RP4S-K1NA</span>
|
||||
</div>
|
||||
<div class="alert">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.7" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg>
|
||||
<span><strong>После закрытия страницы коды нельзя посмотреть снова</strong>. Скачайте файл или сделайте скриншот.</span>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<button type="button" class="btn">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||||
Скачать .txt
|
||||
</button>
|
||||
<button type="button" class="btn">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||||
Копировать
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary">Понятно — продолжить</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const tabs = Array.from(document.querySelectorAll('.review-bar .tab'));
|
||||
const pages = Array.from(document.querySelectorAll('.form-page'));
|
||||
function setTab(id) {
|
||||
tabs.forEach(t => {
|
||||
const isA = t.dataset.tab === id;
|
||||
t.classList.toggle('active', isA);
|
||||
t.setAttribute('aria-selected', isA ? 'true' : 'false');
|
||||
});
|
||||
pages.forEach(p => p.classList.toggle('active', p.id === 'form-' + id));
|
||||
}
|
||||
tabs.forEach(t => t.addEventListener('click', () => setTab(t.dataset.tab)));
|
||||
// inline links inside forms can also switch
|
||||
document.querySelectorAll('[data-go]').forEach(a => {
|
||||
a.addEventListener('click', e => { e.preventDefault(); setTab(a.dataset.go); });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,642 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Палитры на выбор — Лидпоток</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
background: #ECEEF1;
|
||||
font-family: 'Inter', system-ui, sans-serif;
|
||||
font-feature-settings: 'cv11', 'ss01';
|
||||
color: #0A1319;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
padding: 28px 24px 60px;
|
||||
}
|
||||
|
||||
.page-h {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto 24px;
|
||||
}
|
||||
.page-h h1 {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.018em;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
.page-h .meta {
|
||||
font-size: 12.5px;
|
||||
color: #60676C;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-feature-settings: 'tnum';
|
||||
}
|
||||
|
||||
.option {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto 16px;
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #D4D8DB;
|
||||
background: #FFFFFF;
|
||||
display: grid;
|
||||
grid-template-columns: 280px 1fr;
|
||||
}
|
||||
.option-meta {
|
||||
background: #FFFFFF;
|
||||
padding: 18px 18px;
|
||||
border-right: 1px solid #E8EBED;
|
||||
display: flex; flex-direction: column; gap: 8px;
|
||||
}
|
||||
.option-tag {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
color: #60676C;
|
||||
width: fit-content;
|
||||
}
|
||||
.option-tag .seq {
|
||||
width: 18px; height: 18px;
|
||||
border-radius: 4px;
|
||||
background: #0A1319;
|
||||
color: #fff;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
.option-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.012em;
|
||||
margin: 0;
|
||||
}
|
||||
.option-desc {
|
||||
font-size: 12.5px;
|
||||
color: #343C41;
|
||||
line-height: 1.45;
|
||||
margin: 0;
|
||||
}
|
||||
.option-swatches {
|
||||
display: flex; gap: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.option-swatches .sw {
|
||||
width: 22px; height: 22px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(10,19,25,0.08);
|
||||
}
|
||||
.option-wcag {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 10.5px;
|
||||
color: #60676C;
|
||||
margin-top: 4px;
|
||||
font-feature-settings: 'tnum';
|
||||
letter-spacing: -0.005em;
|
||||
display: flex; flex-direction: column; gap: 2px;
|
||||
}
|
||||
.option-wcag .ok { color: #0F6E56; }
|
||||
.option-wcag .fail { color: #B94837; }
|
||||
|
||||
/* preview area */
|
||||
.preview {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 1fr;
|
||||
height: 220px;
|
||||
position: relative;
|
||||
}
|
||||
.p-side {
|
||||
display: flex; flex-direction: column;
|
||||
padding: 14px 8px;
|
||||
gap: 1px;
|
||||
position: relative;
|
||||
}
|
||||
.p-brand {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 4px 6px 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.p-brand .mark {
|
||||
width: 16px; height: 16px;
|
||||
border-radius: 3px;
|
||||
background: #0A1319;
|
||||
color: #fff;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 8px; font-weight: 600;
|
||||
}
|
||||
.p-nav { display: flex; flex-direction: column; gap: 1px; }
|
||||
.p-nav-item {
|
||||
height: 22px; padding: 0 6px;
|
||||
border-radius: 4px;
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.p-nav-item .ico { width: 11px; height: 11px; flex-shrink: 0; }
|
||||
.p-divider {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0; right: -0.5px;
|
||||
width: 1px;
|
||||
}
|
||||
.p-main {
|
||||
padding: 14px 16px;
|
||||
position: relative;
|
||||
}
|
||||
.p-bar {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 10.5px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.p-bar .crumb { font-weight: 500; }
|
||||
.p-cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1.3fr;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.p-kpi {
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid;
|
||||
display: flex; flex-direction: column; gap: 6px;
|
||||
min-height: 76px;
|
||||
}
|
||||
.p-kpi .lbl { font-size: 9px; }
|
||||
.p-kpi .val {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-feature-settings: 'tnum';
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1;
|
||||
}
|
||||
.p-kpi .delta {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.p-balance {
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid;
|
||||
display: flex; flex-direction: column; gap: 6px;
|
||||
min-height: 76px;
|
||||
}
|
||||
.p-balance .lbl { font-size: 9px; }
|
||||
.p-balance .val {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-feature-settings: 'tnum';
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1;
|
||||
}
|
||||
.p-balance .runway {
|
||||
display: grid; grid-template-columns: repeat(7, 1fr);
|
||||
gap: 2px; height: 4px;
|
||||
}
|
||||
.p-balance .runway span { border-radius: 1px; }
|
||||
|
||||
.p-row {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 6px;
|
||||
font-size: 10.5px;
|
||||
}
|
||||
.p-row .pa {
|
||||
width: 18px; height: 18px;
|
||||
border-radius: 50%;
|
||||
background: rgba(10,19,25,0.08);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 7.5px; font-weight: 600;
|
||||
}
|
||||
.p-row .pn { font-weight: 500; }
|
||||
.p-row .pchip {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
font-size: 10px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.p-row .pchip .pdot {
|
||||
width: 6px; height: 6px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
PER-VARIANT colour overrides
|
||||
============================================================ */
|
||||
|
||||
/* А · Graphite */
|
||||
.opt-a .p-side { background: #12181C; }
|
||||
.opt-a .p-divider { background: #12181C; }
|
||||
.opt-a .p-main { background: #F1F4F6; }
|
||||
.opt-a .p-side, .opt-a .p-side .p-brand { color: #B5B8BA; }
|
||||
.opt-a .p-brand .mark { background: #FFFFFF; color: #12181C; }
|
||||
.opt-a .p-nav-item { color: #B5B8BA; }
|
||||
.opt-a .p-nav-item .ico { color: #5D6469; }
|
||||
.opt-a .p-nav-item.active { background: #272F35; color: #FFFFFF; }
|
||||
.opt-a .p-nav-item.active .ico { color: #32C8A9; }
|
||||
.opt-a .p-bar .crumb { color: #0A1319; }
|
||||
.opt-a .p-bar .sub { color: #5D6469; }
|
||||
.opt-a .p-kpi { background: #FFFFFF; border-color: #D4D8DB; }
|
||||
.opt-a .p-kpi .lbl { color: #5D6469; }
|
||||
.opt-a .p-kpi .delta { color: #0F6E56; }
|
||||
.opt-a .p-balance { background: #FFFFFF; border-color: #0F6E56; }
|
||||
.opt-a .p-balance .lbl { color: #5D6469; }
|
||||
.opt-a .p-balance .val { color: #0A1319; }
|
||||
.opt-a .p-balance .runway span { background: #E9EDF0; }
|
||||
.opt-a .p-balance .runway span.f { background: #0F6E56; }
|
||||
.opt-a .p-row .pn { color: #0A1319; }
|
||||
.opt-a .p-row .sub { color: #5D6469; }
|
||||
.opt-a .p-row .pchip { color: #0A1319; }
|
||||
.opt-a .p-row.sel { background: #0A1319; color: #FFFFFF; }
|
||||
.opt-a .p-row.sel .pn,
|
||||
.opt-a .p-row.sel .sub,
|
||||
.opt-a .p-row.sel .pchip { color: #FFFFFF; }
|
||||
|
||||
/* Б · Forest */
|
||||
.opt-b .p-side { background: #012019; }
|
||||
.opt-b .p-divider { background: #012019; }
|
||||
.opt-b .p-main { background: #F6F3EC; }
|
||||
.opt-b .p-side, .opt-b .p-side .p-brand { color: #B1C2BD; }
|
||||
.opt-b .p-brand .mark { background: #FFFFFF; color: #012019; }
|
||||
.opt-b .p-nav-item { color: #B1C2BD; }
|
||||
.opt-b .p-nav-item .ico { color: #5C7A72; }
|
||||
.opt-b .p-nav-item.active { background: #13382F; color: #FFFFFF; }
|
||||
.opt-b .p-nav-item.active .ico { color: #32C8A9; }
|
||||
.opt-b .p-bar .crumb { color: #081319; }
|
||||
.opt-b .p-bar .sub { color: #66635C; }
|
||||
.opt-b .p-kpi { background: #FFFDFA; border-color: #D9D5CD; }
|
||||
.opt-b .p-kpi .lbl { color: #66635C; }
|
||||
.opt-b .p-kpi .delta { color: #0F6E56; }
|
||||
.opt-b .p-balance { background: #FFFDFA; border-color: #0F6E56; }
|
||||
.opt-b .p-balance .lbl { color: #66635C; }
|
||||
.opt-b .p-balance .val { color: #081319; }
|
||||
.opt-b .p-balance .runway span { background: #F0EDE4; }
|
||||
.opt-b .p-balance .runway span.f { background: #0F6E56; }
|
||||
.opt-b .p-row .pn { color: #081319; }
|
||||
.opt-b .p-row .sub { color: #66635C; }
|
||||
.opt-b .p-row .pchip { color: #081319; }
|
||||
.opt-b .p-row.sel { background: #081319; color: #FFFFFF; }
|
||||
.opt-b .p-row.sel .pn,
|
||||
.opt-b .p-row.sel .sub,
|
||||
.opt-b .p-row.sel .pchip { color: #FFFFFF; }
|
||||
|
||||
/* В · Slate-Blue */
|
||||
.opt-v .p-side { background: #131F2C; }
|
||||
.opt-v .p-divider { background: #131F2C; }
|
||||
.opt-v .p-main { background: #E7EEF3; }
|
||||
.opt-v .p-side, .opt-v .p-side .p-brand { color: #B7BFC7; }
|
||||
.opt-v .p-brand .mark { background: #FFFFFF; color: #131F2C; }
|
||||
.opt-v .p-nav-item { color: #B7BFC7; }
|
||||
.opt-v .p-nav-item .ico { color: #5D646B; }
|
||||
.opt-v .p-nav-item.active { background: #2A394A; color: #FFFFFF; }
|
||||
.opt-v .p-nav-item.active .ico { color: #32C8A9; }
|
||||
.opt-v .p-bar .crumb { color: #0A131A; }
|
||||
.opt-v .p-bar .sub { color: #5D646B; }
|
||||
.opt-v .p-kpi { background: #FFFFFF; border-color: #C9D2D9; }
|
||||
.opt-v .p-kpi .lbl { color: #5D646B; }
|
||||
.opt-v .p-kpi .delta { color: #0F6E56; }
|
||||
.opt-v .p-balance { background: #FFFFFF; border-color: #0F6E56; }
|
||||
.opt-v .p-balance .lbl { color: #5D646B; }
|
||||
.opt-v .p-balance .val { color: #0A131A; }
|
||||
.opt-v .p-balance .runway span { background: #DEE6EC; }
|
||||
.opt-v .p-balance .runway span.f { background: #0F6E56; }
|
||||
.opt-v .p-row .pn { color: #0A131A; }
|
||||
.opt-v .p-row .sub { color: #5D646B; }
|
||||
.opt-v .p-row .pchip { color: #0A131A; }
|
||||
.opt-v .p-row.sel { background: #0A131A; color: #FFFFFF; }
|
||||
.opt-v .p-row.sel .pn,
|
||||
.opt-v .p-row.sel .sub,
|
||||
.opt-v .p-row.sel .pchip { color: #FFFFFF; }
|
||||
|
||||
/* Г · Champagne */
|
||||
.opt-g .p-side { background: #EEE3D2; }
|
||||
.opt-g .p-divider { background: #D9D0C1; }
|
||||
.opt-g .p-main { background: #FFFFFF; }
|
||||
.opt-g .p-side, .opt-g .p-side .p-brand { color: #37322A; }
|
||||
.opt-g .p-brand .mark { background: #09131A; color: #fff; }
|
||||
.opt-g .p-nav-item { color: #615D56; }
|
||||
.opt-g .p-nav-item .ico { color: #615D56; }
|
||||
.opt-g .p-nav-item.active { background: #09131A; color: #FFFFFF; }
|
||||
.opt-g .p-nav-item.active .ico { color: #0F6E56; }
|
||||
.opt-g .p-bar .crumb { color: #09131A; }
|
||||
.opt-g .p-bar .sub { color: #615D56; }
|
||||
.opt-g .p-kpi { background: #FCFAF6; border-color: #D9D0C1; }
|
||||
.opt-g .p-kpi .lbl { color: #615D56; }
|
||||
.opt-g .p-kpi .delta { color: #0F6E56; }
|
||||
.opt-g .p-balance { background: #FCFAF6; border-color: #0F6E56; }
|
||||
.opt-g .p-balance .lbl { color: #615D56; }
|
||||
.opt-g .p-balance .val { color: #09131A; }
|
||||
.opt-g .p-balance .runway span { background: #F2EADE; }
|
||||
.opt-g .p-balance .runway span.f { background: #0F6E56; }
|
||||
.opt-g .p-row .pn { color: #09131A; }
|
||||
.opt-g .p-row .sub { color: #615D56; }
|
||||
.opt-g .p-row .pchip { color: #09131A; }
|
||||
.opt-g .p-row.sel { background: #09131A; color: #FFFFFF; }
|
||||
.opt-g .p-row.sel .pn,
|
||||
.opt-g .p-row.sel .sub,
|
||||
.opt-g .p-row.sel .pchip { color: #FFFFFF; }
|
||||
|
||||
/* Д · Inverted Hero */
|
||||
.opt-d .p-side { background: #FFFFFF; border-right: 1px solid #BCC6CD; }
|
||||
.opt-d .p-divider { display: none; }
|
||||
.opt-d .p-main { background: #DAE3E9; }
|
||||
.opt-d .p-side, .opt-d .p-side .p-brand { color: #2D3439; }
|
||||
.opt-d .p-brand .mark { background: #09131A; color: #fff; }
|
||||
.opt-d .p-nav-item { color: #585F65; }
|
||||
.opt-d .p-nav-item .ico { color: #585F65; }
|
||||
.opt-d .p-nav-item.active { background: #09131A; color: #FFFFFF; }
|
||||
.opt-d .p-nav-item.active .ico { color: #0F6E56; }
|
||||
.opt-d .p-bar .crumb { color: #0A131A; }
|
||||
.opt-d .p-bar .sub { color: #585F65; }
|
||||
.opt-d .p-kpi { background: #FFFFFF; border-color: #BCC6CD; }
|
||||
.opt-d .p-kpi .lbl { color: #585F65; }
|
||||
.opt-d .p-kpi .delta { color: #0F6E56; }
|
||||
.opt-d .p-balance { background: #FFFFFF; border-color: #0F6E56; }
|
||||
.opt-d .p-balance .lbl { color: #585F65; }
|
||||
.opt-d .p-balance .val { color: #0A131A; }
|
||||
.opt-d .p-balance .runway span { background: #D0D9DF; }
|
||||
.opt-d .p-balance .runway span.f { background: #0F6E56; }
|
||||
.opt-d .p-row .pn { color: #0A131A; }
|
||||
.opt-d .p-row .sub { color: #585F65; }
|
||||
.opt-d .p-row .pchip { color: #0A131A; }
|
||||
.opt-d .p-row.sel { background: #0A131A; color: #FFFFFF; }
|
||||
.opt-d .p-row.sel .pn,
|
||||
.opt-d .p-row.sel .sub,
|
||||
.opt-d .p-row.sel .pchip { color: #FFFFFF; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="page-h">
|
||||
<h1>Палитры на выбор · v8</h1>
|
||||
<p class="meta">5 вариантов · Teal #0F6E56 (брендбук) сохранён везде · все proverено OKLCH + WCAG (palette_options.py)</p>
|
||||
</header>
|
||||
|
||||
<!-- Variant А — Graphite -->
|
||||
<section class="option opt-a" aria-label="Variant А Graphite Sidebar">
|
||||
<div class="option-meta">
|
||||
<span class="option-tag"><span class="seq">А</span>RECOMMENDED</span>
|
||||
<h2 class="option-name">Graphite Sidebar</h2>
|
||||
<p class="option-desc">Тёмный графитовый sidebar + холодный near-white main. Линия Linear/Vercel — самый «технологичный».</p>
|
||||
<div class="option-swatches">
|
||||
<span class="sw" style="background:#12181C" title="sidebar #12181C"></span>
|
||||
<span class="sw" style="background:#272F35" title="sidebar-active #272F35"></span>
|
||||
<span class="sw" style="background:#F1F4F6" title="bg #F1F4F6"></span>
|
||||
<span class="sw" style="background:#FFFFFF" title="surface #FFFFFF"></span>
|
||||
<span class="sw" style="background:#0F6E56" title="accent Teal"></span>
|
||||
<span class="sw" style="background:#32C8A9" title="accent-bright (icon on dark)"></span>
|
||||
</div>
|
||||
<div class="option-wcag">
|
||||
<span class="ok">✓ sidebar/bg 16.18:1</span>
|
||||
<span class="ok">✓ accent-bright/sidebar 8.52:1</span>
|
||||
<span class="ok">✓ ink/bg 16.98:1</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<div class="p-side">
|
||||
<div class="p-brand"><span class="mark">L</span>Лидпоток</div>
|
||||
<div class="p-nav">
|
||||
<div class="p-nav-item active"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>Дашборд</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 6h18M6 12h12M9 18h6"/></svg>Сделки</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/></svg>Канбан</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg>Напоминания</div>
|
||||
</div>
|
||||
<div class="p-divider"></div>
|
||||
</div>
|
||||
<div class="p-main">
|
||||
<div class="p-bar"><span class="crumb">Дашборд</span><span class="sub">· 247 лидов · +3 с утра</span></div>
|
||||
<div class="p-cards">
|
||||
<div class="p-kpi"><span class="lbl">Лиды · 7д</span><span class="val">247</span><span class="delta">↑ 12.3%</span></div>
|
||||
<div class="p-kpi"><span class="lbl">Конверсия</span><span class="val">18.4%</span><span class="delta">↑ 2.1pp</span></div>
|
||||
<div class="p-balance">
|
||||
<span class="lbl">Баланс · хватит на 4 дня</span>
|
||||
<span class="val">14 250 ₽</span>
|
||||
<div class="runway"><span class="f"></span><span class="f"></span><span class="f"></span><span class="f"></span><span></span><span></span><span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-row sel">
|
||||
<span class="pa">АС</span>
|
||||
<span class="pn">Анна Соколова</span>
|
||||
<span class="sub">· Окна Москва</span>
|
||||
<span class="pchip"><span class="pdot" style="background:#B94837"></span>Новая</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Variant Б — Forest -->
|
||||
<section class="option opt-b" aria-label="Variant Б Forest Sidebar">
|
||||
<div class="option-meta">
|
||||
<span class="option-tag"><span class="seq">Б</span>BRAND-NATIVE</span>
|
||||
<h2 class="option-name">Forest Sidebar</h2>
|
||||
<p class="option-desc">Глубокий тёмно-тиловый sidebar (родственник брендового Teal) + тёплый ivory main. Корпоративная фундаментальность.</p>
|
||||
<div class="option-swatches">
|
||||
<span class="sw" style="background:#012019" title="sidebar #012019"></span>
|
||||
<span class="sw" style="background:#13382F" title="sidebar-active #13382F"></span>
|
||||
<span class="sw" style="background:#F6F3EC" title="bg #F6F3EC"></span>
|
||||
<span class="sw" style="background:#FFFDFA" title="surface #FFFDFA"></span>
|
||||
<span class="sw" style="background:#0F6E56" title="accent Teal"></span>
|
||||
<span class="sw" style="background:#32C8A9" title="accent-bright"></span>
|
||||
</div>
|
||||
<div class="option-wcag">
|
||||
<span class="ok">✓ sidebar/bg 15.46:1</span>
|
||||
<span class="ok">✓ accent-bright/sidebar 8.15:1</span>
|
||||
<span class="ok">✓ ink/bg 16.96:1</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<div class="p-side">
|
||||
<div class="p-brand"><span class="mark">L</span>Лидпоток</div>
|
||||
<div class="p-nav">
|
||||
<div class="p-nav-item active"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>Дашборд</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 6h18M6 12h12M9 18h6"/></svg>Сделки</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/></svg>Канбан</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg>Напоминания</div>
|
||||
</div>
|
||||
<div class="p-divider"></div>
|
||||
</div>
|
||||
<div class="p-main">
|
||||
<div class="p-bar"><span class="crumb">Дашборд</span><span class="sub">· 247 лидов · +3 с утра</span></div>
|
||||
<div class="p-cards">
|
||||
<div class="p-kpi"><span class="lbl">Лиды · 7д</span><span class="val">247</span><span class="delta">↑ 12.3%</span></div>
|
||||
<div class="p-kpi"><span class="lbl">Конверсия</span><span class="val">18.4%</span><span class="delta">↑ 2.1pp</span></div>
|
||||
<div class="p-balance">
|
||||
<span class="lbl">Баланс · хватит на 4 дня</span>
|
||||
<span class="val">14 250 ₽</span>
|
||||
<div class="runway"><span class="f"></span><span class="f"></span><span class="f"></span><span class="f"></span><span></span><span></span><span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-row sel">
|
||||
<span class="pa">АС</span>
|
||||
<span class="pn">Анна Соколова</span>
|
||||
<span class="sub">· Окна Москва</span>
|
||||
<span class="pchip"><span class="pdot" style="background:#B94837"></span>Новая</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Variant В — Slate-Blue -->
|
||||
<section class="option opt-v" aria-label="Variant В Slate-Blue Financial">
|
||||
<div class="option-meta">
|
||||
<span class="option-tag"><span class="seq">В</span>FINANCIAL</span>
|
||||
<h2 class="option-name">Slate-Blue</h2>
|
||||
<p class="option-desc">Sidebar — глубокий синий слейт, main — холодный mist. Stripe Atlas/Mercury вайб, финансовый инструмент.</p>
|
||||
<div class="option-swatches">
|
||||
<span class="sw" style="background:#131F2C"></span>
|
||||
<span class="sw" style="background:#2A394A"></span>
|
||||
<span class="sw" style="background:#E7EEF3"></span>
|
||||
<span class="sw" style="background:#FFFFFF"></span>
|
||||
<span class="sw" style="background:#0F6E56"></span>
|
||||
<span class="sw" style="background:#32C8A9"></span>
|
||||
</div>
|
||||
<div class="option-wcag">
|
||||
<span class="ok">✓ sidebar/bg 14.20:1</span>
|
||||
<span class="ok">✓ accent-bright/sidebar 7.93:1</span>
|
||||
<span class="ok">✓ ink/bg 16.02:1</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<div class="p-side">
|
||||
<div class="p-brand"><span class="mark">L</span>Лидпоток</div>
|
||||
<div class="p-nav">
|
||||
<div class="p-nav-item active"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>Дашборд</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 6h18M6 12h12M9 18h6"/></svg>Сделки</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/></svg>Канбан</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg>Напоминания</div>
|
||||
</div>
|
||||
<div class="p-divider"></div>
|
||||
</div>
|
||||
<div class="p-main">
|
||||
<div class="p-bar"><span class="crumb">Дашборд</span><span class="sub">· 247 лидов · +3 с утра</span></div>
|
||||
<div class="p-cards">
|
||||
<div class="p-kpi"><span class="lbl">Лиды · 7д</span><span class="val">247</span><span class="delta">↑ 12.3%</span></div>
|
||||
<div class="p-kpi"><span class="lbl">Конверсия</span><span class="val">18.4%</span><span class="delta">↑ 2.1pp</span></div>
|
||||
<div class="p-balance">
|
||||
<span class="lbl">Баланс · хватит на 4 дня</span>
|
||||
<span class="val">14 250 ₽</span>
|
||||
<div class="runway"><span class="f"></span><span class="f"></span><span class="f"></span><span class="f"></span><span></span><span></span><span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-row sel">
|
||||
<span class="pa">АС</span>
|
||||
<span class="pn">Анна Соколова</span>
|
||||
<span class="sub">· Окна Москва</span>
|
||||
<span class="pchip"><span class="pdot" style="background:#B94837"></span>Новая</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Variant Г — Champagne -->
|
||||
<section class="option opt-g" aria-label="Variant Г Warm Champagne">
|
||||
<div class="option-meta">
|
||||
<span class="option-tag"><span class="seq">Г</span>WARM</span>
|
||||
<h2 class="option-name">Warm Champagne</h2>
|
||||
<p class="option-desc">Sidebar — насыщенный кремово-бежевый, main — pure white. Тёплый «кабинет», Notion/Things с бо́льшей строгостью.</p>
|
||||
<div class="option-swatches">
|
||||
<span class="sw" style="background:#EEE3D2"></span>
|
||||
<span class="sw" style="background:#09131A"></span>
|
||||
<span class="sw" style="background:#FFFFFF"></span>
|
||||
<span class="sw" style="background:#FCFAF6"></span>
|
||||
<span class="sw" style="background:#0F6E56"></span>
|
||||
</div>
|
||||
<div class="option-wcag">
|
||||
<span class="fail">✗ sidebar/bg 1.27:1 (формально <3:1)</span>
|
||||
<span class="ok">✓ ink/bg 18.78:1</span>
|
||||
<span class="ok">✓ sidebar-text/sidebar 10.01:1</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<div class="p-side">
|
||||
<div class="p-brand"><span class="mark">L</span>Лидпоток</div>
|
||||
<div class="p-nav">
|
||||
<div class="p-nav-item active"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>Дашборд</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 6h18M6 12h12M9 18h6"/></svg>Сделки</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/></svg>Канбан</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg>Напоминания</div>
|
||||
</div>
|
||||
<div class="p-divider"></div>
|
||||
</div>
|
||||
<div class="p-main">
|
||||
<div class="p-bar"><span class="crumb">Дашборд</span><span class="sub">· 247 лидов · +3 с утра</span></div>
|
||||
<div class="p-cards">
|
||||
<div class="p-kpi"><span class="lbl">Лиды · 7д</span><span class="val">247</span><span class="delta">↑ 12.3%</span></div>
|
||||
<div class="p-kpi"><span class="lbl">Конверсия</span><span class="val">18.4%</span><span class="delta">↑ 2.1pp</span></div>
|
||||
<div class="p-balance">
|
||||
<span class="lbl">Баланс · хватит на 4 дня</span>
|
||||
<span class="val">14 250 ₽</span>
|
||||
<div class="runway"><span class="f"></span><span class="f"></span><span class="f"></span><span class="f"></span><span></span><span></span><span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-row sel">
|
||||
<span class="pa">АС</span>
|
||||
<span class="pn">Анна Соколова</span>
|
||||
<span class="sub">· Окна Москва</span>
|
||||
<span class="pchip"><span class="pdot" style="background:#B94837"></span>Новая</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Variant Д — Inverted Hero -->
|
||||
<section class="option opt-d" aria-label="Variant Д Inverted Hero">
|
||||
<div class="option-meta">
|
||||
<span class="option-tag"><span class="seq">Д</span>STRIPE-LIKE</span>
|
||||
<h2 class="option-name">Inverted Hero</h2>
|
||||
<p class="option-desc">Sidebar — pure white, main — насыщенный cool grey. Карточки «плавают» над полотном, классика премиум B2B (Stripe Dashboard).</p>
|
||||
<div class="option-swatches">
|
||||
<span class="sw" style="background:#FFFFFF"></span>
|
||||
<span class="sw" style="background:#09131A"></span>
|
||||
<span class="sw" style="background:#DAE3E9"></span>
|
||||
<span class="sw" style="background:#FFFFFF"></span>
|
||||
<span class="sw" style="background:#0F6E56"></span>
|
||||
</div>
|
||||
<div class="option-wcag">
|
||||
<span class="fail">✗ sidebar/bg 1.30:1 (компенсируется hairline)</span>
|
||||
<span class="ok">✓ ink/bg 14.41:1</span>
|
||||
<span class="ok">✓ accent/bg 4.76:1</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<div class="p-side">
|
||||
<div class="p-brand"><span class="mark">L</span>Лидпоток</div>
|
||||
<div class="p-nav">
|
||||
<div class="p-nav-item active"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>Дашборд</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 6h18M6 12h12M9 18h6"/></svg>Сделки</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/></svg>Канбан</div>
|
||||
<div class="p-nav-item"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg>Напоминания</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-main">
|
||||
<div class="p-bar"><span class="crumb">Дашборд</span><span class="sub">· 247 лидов · +3 с утра</span></div>
|
||||
<div class="p-cards">
|
||||
<div class="p-kpi"><span class="lbl">Лиды · 7д</span><span class="val">247</span><span class="delta">↑ 12.3%</span></div>
|
||||
<div class="p-kpi"><span class="lbl">Конверсия</span><span class="val">18.4%</span><span class="delta">↑ 2.1pp</span></div>
|
||||
<div class="p-balance">
|
||||
<span class="lbl">Баланс · хватит на 4 дня</span>
|
||||
<span class="val">14 250 ₽</span>
|
||||
<div class="runway"><span class="f"></span><span class="f"></span><span class="f"></span><span class="f"></span><span></span><span></span><span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-row sel">
|
||||
<span class="pa">АС</span>
|
||||
<span class="pn">Анна Соколова</span>
|
||||
<span class="sub">· Окна Москва</span>
|
||||
<span class="pchip"><span class="pdot" style="background:#B94837"></span>Новая</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,321 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Отчёты — Лидерра (Forest)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C; --ink-disabled:#92907B;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-bg:#012019; --side-text:#B1C2BD; --side-text-2:#7A8C87;
|
||||
--side-active:#13382F; --side-icon:#5C7A72; --side-icon-act:#32C8A9;
|
||||
--side-hover:#0A2A22; --side-border:#1A3A30;
|
||||
--st-paid:#007EB8; --st-quote:#008A4D; --st-call:#9A6700; --st-new:#B94837; --st-fail:#6C60C4;
|
||||
--r-xs:4px; --r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
button { font-family:inherit; }
|
||||
a { color:inherit; text-decoration:none; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
|
||||
}
|
||||
.skip-link { position:absolute; top:-40px; left:12px; background:var(--ink); color:#fff; padding:9px 16px; z-index:200; font-weight:600; border-radius:var(--r-sm); }
|
||||
.skip-link:focus { top:12px; }
|
||||
|
||||
.app { display:grid; grid-template-columns:232px 1fr; min-height:100vh; }
|
||||
.side { background:var(--side-bg); border-right:1px solid var(--side-border); padding:18px 12px 24px; position:sticky; top:0; height:100vh; overflow-y:auto; color:var(--side-text); }
|
||||
.brand { display:flex; align-items:center; gap:10px; padding:6px 8px 18px; font-weight:600; font-size:14.5px; letter-spacing:-0.01em; font-variation-settings:'opsz' 18; color:#FFF; }
|
||||
.brand-mark { width:22px; height:22px; border-radius:var(--r-xs); background:#FFF; display:inline-flex; align-items:center; justify-content:center; flex-shrink:0; overflow:hidden; }
|
||||
.brand-mark svg { width:100%; height:100%; display:block; }
|
||||
.brand-dot { color:var(--side-icon-act); }
|
||||
.nav { display:flex; flex-direction:column; gap:1px; }
|
||||
.nav-eyebrow { font-size:11px; font-weight:500; letter-spacing:0.01em; color:var(--side-text-2); padding:14px 10px 6px; }
|
||||
.nav-item { display:flex; align-items:center; gap:10px; height:32px; padding:0 10px; border-radius:var(--r-sm); font-size:13px; color:var(--side-text); }
|
||||
.nav-item:hover { background:var(--side-hover); color:#FFF; }
|
||||
.nav-item.active { background:var(--side-active); color:#FFF; font-weight:500; }
|
||||
.nav-item.active .nav-icon { color:var(--side-icon-act); }
|
||||
.nav-icon { width:15px; height:15px; flex-shrink:0; color:var(--side-icon); stroke-width:1.6; }
|
||||
.nav-item:hover .nav-icon { color:#FFF; }
|
||||
.nav-text { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.nav-count { font-family:var(--font-mono); font-size:10.5px; font-weight:500; font-feature-settings:'tnum'; background:rgba(255,255,255,0.10); color:var(--side-text); padding:2px 6px; border-radius:4px; }
|
||||
.main { display:flex; flex-direction:column; min-width:0; }
|
||||
.topbar { height:48px; border-bottom:1px solid var(--hairline); background:var(--surface); padding:0 24px; display:flex; align-items:center; gap:12px; position:sticky; top:0; z-index:50; }
|
||||
.crumb { font-size:12.5px; color:var(--ink-3); display:flex; align-items:center; gap:8px; }
|
||||
.crumb strong { color:var(--ink); font-weight:500; }
|
||||
.crumb svg { width:11px; height:11px; color:var(--ink-3); opacity:0.5; }
|
||||
.topbar-spacer { flex:1; }
|
||||
.user-chip { display:inline-flex; align-items:center; gap:8px; height:30px; padding:0 10px 0 4px; border-radius:100px; border:1px solid var(--hairline); background:var(--surface); }
|
||||
.user-chip .ava { width:22px; height:22px; border-radius:50%; background:var(--ink); color:#fff; display:inline-flex; align-items:center; justify-content:center; font-size:9.5px; font-weight:600; }
|
||||
.user-chip .uname { font-size:12px; color:var(--ink); font-weight:500; }
|
||||
|
||||
.content { display:grid; grid-template-columns:380px 1fr; gap:18px; padding:24px 28px 80px; max-width:1500px; margin:0 auto; }
|
||||
.page-h { grid-column:1 / -1; display:flex; align-items:flex-end; justify-content:space-between; margin-bottom:6px; gap:16px; }
|
||||
.page-title { font-size:28px; font-weight:600; font-variation-settings:'opsz' 28; letter-spacing:-0.02em; line-height:1.1; margin:0 0 6px; }
|
||||
.page-meta { font-size:12.5px; color:var(--ink-3); }
|
||||
.page-meta .num { font-family:var(--font-mono); font-feature-settings:'tnum'; color:var(--ink-2); font-weight:500; }
|
||||
.page-meta .accent-num { color:var(--accent); font-weight:600; }
|
||||
.page-meta .sep { color:var(--ink-disabled); }
|
||||
|
||||
.section { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); padding:20px 22px; }
|
||||
.section-h { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; margin-bottom:16px; }
|
||||
.section-h h2 { font-size:16px; font-weight:600; font-variation-settings:'opsz' 18; letter-spacing:-0.012em; margin:0 0 4px; }
|
||||
.section-h .desc { font-size:12px; color:var(--ink-3); line-height:1.5; }
|
||||
.section-h .h-action { font-size:11.5px; color:var(--accent); font-weight:500; cursor:pointer; background:none; border:none; padding:0; font-family:inherit; display:inline-flex; align-items:center; gap:4px; }
|
||||
|
||||
.field { display:flex; flex-direction:column; gap:6px; margin-bottom:14px; }
|
||||
.field label { font-size:11.5px; font-weight:500; color:var(--ink-2); letter-spacing:-0.005em; }
|
||||
.input, .select { height:36px; padding:0 12px; border:1px solid var(--hairline); border-radius:var(--r-sm); background:var(--surface); font-family:inherit; font-size:13px; color:var(--ink); outline:none; letter-spacing:-0.005em; }
|
||||
.input.mono { font-family:var(--font-mono); font-feature-settings:'tnum'; letter-spacing:0; }
|
||||
.input:focus, .select:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-tint); }
|
||||
.select { appearance:none; -webkit-appearance:none; padding-right:30px; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%2366635C' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:right 10px center; cursor:pointer; }
|
||||
.field-row { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:14px; }
|
||||
.field-row .field { margin-bottom:0; }
|
||||
|
||||
/* radio chips for type */
|
||||
.type-chips { display:grid; grid-template-columns:1fr 1fr; gap:6px; margin-top:6px; }
|
||||
.tc-card { display:flex; flex-direction:column; gap:3px; padding:10px 12px; border:1px solid var(--hairline); background:var(--surface); border-radius:var(--r-sm); cursor:pointer; font-size:12.5px; font-family:inherit; text-align:left; }
|
||||
.tc-card:hover { border-color:var(--ink-disabled); }
|
||||
.tc-card.active { border-color:var(--accent); background:var(--accent-tint); }
|
||||
.tc-card .tc-name { font-weight:600; color:var(--ink); letter-spacing:-0.005em; }
|
||||
.tc-card .tc-desc { font-size:11px; color:var(--ink-3); line-height:1.4; }
|
||||
.tc-card.active .tc-desc { color:var(--accent-deep); }
|
||||
|
||||
/* format radios */
|
||||
.fmt-row { display:flex; gap:6px; margin-top:6px; }
|
||||
.fmt-card { flex:1; padding:8px 10px; border:1px solid var(--hairline); background:var(--surface); border-radius:var(--r-sm); cursor:pointer; font-family:inherit; text-align:center; font-size:12px; font-weight:500; color:var(--ink-2); letter-spacing:-0.005em; }
|
||||
.fmt-card:hover { border-color:var(--ink-disabled); color:var(--ink); }
|
||||
.fmt-card.active { border-color:var(--accent); background:var(--accent-tint); color:var(--accent-deep); font-weight:600; }
|
||||
|
||||
.btn { display:inline-flex; align-items:center; gap:7px; height:38px; padding:0 16px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:13px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; }
|
||||
.btn:hover { border-color:var(--ink-disabled); background:var(--sunken); }
|
||||
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
|
||||
.btn svg { width:13px; height:13px; stroke-width:1.7; }
|
||||
|
||||
.quota-banner { padding:11px 14px; border-radius:var(--r-sm); background:var(--accent-tint); color:var(--accent-deep); font-size:12px; line-height:1.5; display:flex; gap:10px; align-items:flex-start; margin-bottom:14px; letter-spacing:-0.005em; }
|
||||
.quota-banner svg { width:14px; height:14px; flex-shrink:0; margin-top:1px; }
|
||||
.quota-banner strong { color:var(--accent-deep); font-weight:600; }
|
||||
.quota-banner .quota-bar { display:inline-flex; align-items:center; gap:4px; }
|
||||
.quota-banner .qd { width:8px; height:8px; border-radius:2px; background:var(--accent); }
|
||||
.quota-banner .qd.empty { background:rgba(15,110,86,0.20); }
|
||||
|
||||
/* Jobs list */
|
||||
.jobs { display:flex; flex-direction:column; }
|
||||
.job { display:grid; grid-template-columns:auto 1fr auto auto; gap:14px; padding:12px 18px; border-bottom:1px solid var(--hairline-soft); align-items:center; }
|
||||
.job:last-child { border-bottom:none; }
|
||||
.job-icon { width:34px; height:34px; border-radius:var(--r-sm); background:var(--bg); border:1px solid var(--hairline-soft); display:inline-flex; align-items:center; justify-content:center; color:var(--ink-2); flex-shrink:0; }
|
||||
.job-icon svg { width:15px; height:15px; stroke-width:1.7; }
|
||||
.job-icon.done { background:var(--accent-tint); color:var(--accent); border-color:transparent; }
|
||||
.job-icon.running { background:#FFF8E5; color:#B35100; border-color:transparent; }
|
||||
.job-icon.failed { background:#FFE7E2; color:var(--st-new); border-color:transparent; }
|
||||
.job-info { min-width:0; }
|
||||
.job-title { font-size:13px; font-weight:500; color:var(--ink); letter-spacing:-0.005em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.job-meta { font-size:11.5px; color:var(--ink-3); margin-top:2px; font-family:var(--font-mono); font-feature-settings:'tnum'; letter-spacing:-0.005em; }
|
||||
.job-status { display:inline-flex; align-items:center; gap:6px; font-size:11.5px; font-weight:500; }
|
||||
.job-status .dot { width:7px; height:7px; border-radius:50%; position:relative; }
|
||||
.job-status .dot::after { content:''; position:absolute; inset:-1px; border-radius:50%; border:1px solid rgba(10,19,25,0.10); }
|
||||
.job-status.s-done .dot { background:var(--st-quote); }
|
||||
.job-status.s-done { color:#006A3B; font-weight:600; }
|
||||
.job-status.s-run .dot { background:var(--st-call); }
|
||||
.job-status.s-run { color:var(--st-call); }
|
||||
.job-status.s-fail .dot { background:var(--st-new); }
|
||||
.job-status.s-fail { color:var(--st-new); }
|
||||
.job-status.s-queued .dot { background:var(--ink-disabled); }
|
||||
.job-status.s-queued { color:var(--ink-3); }
|
||||
.job-action { display:inline-flex; gap:4px; }
|
||||
.job-action button { width:26px; height:26px; border:1px solid var(--hairline); background:var(--surface); border-radius:var(--r-xs); color:var(--ink-2); cursor:pointer; display:inline-flex; align-items:center; justify-content:center; }
|
||||
.job-action button:hover { border-color:var(--ink-disabled); color:var(--ink); }
|
||||
.job-action button svg { width:12px; height:12px; stroke-width:1.7; }
|
||||
.job-progress { width:140px; height:4px; background:var(--hairline-soft); border-radius:2px; overflow:hidden; margin-top:5px; position:relative; }
|
||||
.job-progress .bar { height:100%; background:var(--accent); border-radius:2px; }
|
||||
|
||||
@media (max-width:1100px) {
|
||||
.app { grid-template-columns:56px 1fr; }
|
||||
.side { padding:14px 6px; }
|
||||
.brand .brand-text, .nav-eyebrow, .nav-text, .nav-item:not(.active) .nav-count { display:none; }
|
||||
.nav-item { justify-content:center; padding:0; }
|
||||
.topbar { padding:0 16px; }
|
||||
.content { padding:18px 18px 60px; grid-template-columns:1fr; }
|
||||
}
|
||||
@media (max-width:768px) {
|
||||
.app { grid-template-columns:1fr; }
|
||||
.side { display:none; }
|
||||
.topbar .crumb span:first-child { display:none; }
|
||||
.field-row { grid-template-columns:1fr; }
|
||||
.job { grid-template-columns:auto 1fr auto; }
|
||||
.job-action { grid-column:1 / -1; justify-self:flex-end; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main" class="skip-link">К контенту</a>
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand">
|
||||
<span class="brand-mark" aria-hidden="true"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span class="brand-text">Лидерра<span class="brand-dot">.</span></span>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<div class="nav-eyebrow">Работа</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg><span class="nav-text">Дашборд</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18M6 12h12M9 18h6"/></svg><span class="nav-text">Сделки</span><span class="nav-count">247</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="6" height="18"/></svg><span class="nav-text">Канбан</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg><span class="nav-text">Напоминания</span><span class="nav-count">12</span></a>
|
||||
<div class="nav-eyebrow">Финансы</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></a>
|
||||
<a class="nav-item active" href="#" aria-current="page"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h18M3 6h18M3 18h18"/></svg><span class="nav-text">Отчёты</span></a>
|
||||
<div class="nav-eyebrow">Команда</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Менеджеры</span><span class="nav-count">4</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Настройки</span></a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Рабочая область</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg><strong>Отчёты</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<button type="button" class="user-chip" aria-label="Профиль · Иван Петров"><span class="ava" aria-hidden="true">ИП</span><span class="uname">Иван П.</span></button>
|
||||
</div>
|
||||
|
||||
<main id="main" class="content">
|
||||
<header class="page-h">
|
||||
<div>
|
||||
<h1 class="page-title">Отчёты</h1>
|
||||
<div class="page-meta">очередь <span class="accent-num num">2</span>/3 · обработано за месяц <span class="num">38</span> · средний размер <span class="num">2.4 MB</span></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Form -->
|
||||
<form class="section" aria-labelledby="form-h">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2 id="form-h">Запросить отчёт</h2>
|
||||
<div class="desc">Готовится асинхронно. При > 5 000 строк — на email.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Тип отчёта</label>
|
||||
<div class="type-chips" role="radiogroup" aria-label="Тип отчёта">
|
||||
<button type="button" class="tc-card active" role="radio" aria-checked="true">
|
||||
<span class="tc-name">Сделки · детально</span>
|
||||
<span class="tc-desc">все колонки, фильтры применены</span>
|
||||
</button>
|
||||
<button type="button" class="tc-card" role="radio" aria-checked="false">
|
||||
<span class="tc-name">Менеджеры</span>
|
||||
<span class="tc-desc">конверсия, средний чек, время в воронке</span>
|
||||
</button>
|
||||
<button type="button" class="tc-card" role="radio" aria-checked="false">
|
||||
<span class="tc-name">Источники</span>
|
||||
<span class="tc-desc">Я.Директ / VK / Avito / прямые</span>
|
||||
</button>
|
||||
<button type="button" class="tc-card" role="radio" aria-checked="false">
|
||||
<span class="tc-name">Биллинг</span>
|
||||
<span class="tc-desc">пополнения, списания, возвраты</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<div class="field"><label for="r-from">Период с</label><input class="input mono" id="r-from" type="date" value="2026-04-01"></div>
|
||||
<div class="field"><label for="r-to">по</label><input class="input mono" id="r-to" type="date" value="2026-05-07"></div>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<div class="field"><label for="r-proj">Проект</label><select class="select" id="r-proj"><option>Все проекты</option><option>Окна Москва</option><option>Натяжные потолки</option><option>Кухни на заказ</option></select></div>
|
||||
<div class="field"><label for="r-mng">Менеджер</label><select class="select" id="r-mng"><option>Все</option><option>Иван Петров</option><option>Мария Соколова</option></select></div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Формат файла</label>
|
||||
<div class="fmt-row" role="radiogroup" aria-label="Формат">
|
||||
<button type="button" class="fmt-card active" role="radio" aria-checked="true">CSV</button>
|
||||
<button type="button" class="fmt-card" role="radio" aria-checked="false">XLSX</button>
|
||||
<button type="button" class="fmt-card" role="radio" aria-checked="false">JSON</button>
|
||||
<button type="button" class="fmt-card" role="radio" aria-checked="false">PDF</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quota-banner" role="status">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.7" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg>
|
||||
<span>Квота: <strong>2 из 3</strong> одновременных отчётов · <span class="quota-bar"><span class="qd"></span><span class="qd"></span><span class="qd empty"></span></span> · до <strong>3 попыток retry</strong> в окне 7 дней.</span>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;margin-top:6px">
|
||||
<button type="submit" class="btn btn-primary"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><polygon points="5 3 19 12 5 21 5 3"/></svg>Запустить</button>
|
||||
<button type="reset" class="btn">Сброс</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Jobs list -->
|
||||
<section class="section" style="padding:0">
|
||||
<div class="section-h" style="padding:18px 22px 14px;margin-bottom:0">
|
||||
<div>
|
||||
<h2>Сгенерированные отчёты</h2>
|
||||
<div class="desc">retry-failed для owner отчёта, до 3 попыток · 7 дней</div>
|
||||
</div>
|
||||
<button type="button" class="h-action">все 38 →</button>
|
||||
</div>
|
||||
<div class="jobs">
|
||||
<div class="job">
|
||||
<span class="job-icon done"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></span>
|
||||
<div class="job-info">
|
||||
<div class="job-title">Сделки · детально · апрель 2026</div>
|
||||
<div class="job-meta">CSV · 1.8 MB · 412 строк · 14:21 → 14:23</div>
|
||||
</div>
|
||||
<span class="job-status s-done"><span class="dot" aria-hidden="true"></span>Готов</span>
|
||||
<div class="job-action"><button type="button" aria-label="Скачать"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></button><button type="button" aria-label="Удалить"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button></div>
|
||||
</div>
|
||||
|
||||
<div class="job">
|
||||
<span class="job-icon running"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></span>
|
||||
<div class="job-info">
|
||||
<div class="job-title">Менеджеры · апрель 2026</div>
|
||||
<div class="job-meta">XLSX · в работе · ~30 секунд</div>
|
||||
<div class="job-progress" role="progressbar" aria-label="Прогресс отчёта" aria-valuenow="62" aria-valuemin="0" aria-valuemax="100"><div class="bar" style="width:62%"></div></div>
|
||||
</div>
|
||||
<span class="job-status s-run"><span class="dot" aria-hidden="true"></span>В работе · 62%</span>
|
||||
<div class="job-action"><button type="button" aria-label="Отменить" disabled style="opacity:0.4"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M18 6 6 18M6 6l12 12"/></svg></button></div>
|
||||
</div>
|
||||
|
||||
<div class="job">
|
||||
<span class="job-icon running"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></span>
|
||||
<div class="job-info">
|
||||
<div class="job-title">Источники · последние 30 дней</div>
|
||||
<div class="job-meta">JSON · в очереди · позиция 1</div>
|
||||
</div>
|
||||
<span class="job-status s-queued"><span class="dot" aria-hidden="true"></span>В очереди</span>
|
||||
<div class="job-action"><button type="button" aria-label="Отменить"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M18 6 6 18M6 6l12 12"/></svg></button></div>
|
||||
</div>
|
||||
|
||||
<div class="job">
|
||||
<span class="job-icon failed"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><line x1="12" y1="8" x2="12" y2="13"/><circle cx="12" cy="16" r=".5"/></svg></span>
|
||||
<div class="job-info">
|
||||
<div class="job-title">Биллинг · март 2026</div>
|
||||
<div class="job-meta">XLSX · 2/3 попытки · ошибка S3 timeout · 12:14</div>
|
||||
</div>
|
||||
<span class="job-status s-fail"><span class="dot" aria-hidden="true"></span>Ошибка</span>
|
||||
<div class="job-action"><button type="button" aria-label="Повторить"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><path d="M3.51 15a9 9 0 0 0 14.85 3.36"/></svg></button><button type="button" aria-label="Удалить"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14"/></svg></button></div>
|
||||
</div>
|
||||
|
||||
<div class="job">
|
||||
<span class="job-icon done"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></span>
|
||||
<div class="job-info">
|
||||
<div class="job-title">Менеджеры · март 2026</div>
|
||||
<div class="job-meta">XLSX · 2.1 MB · 38 строк · 6 дней назад</div>
|
||||
</div>
|
||||
<span class="job-status s-done"><span class="dot" aria-hidden="true"></span>Готов</span>
|
||||
<div class="job-action"><button type="button" aria-label="Скачать"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></button><button type="button" aria-label="Удалить"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14"/></svg></button></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,404 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Настройки — Лидерра (Forest)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C; --ink-disabled:#92907B;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-bg:#012019; --side-text:#B1C2BD; --side-text-2:#7A8C87;
|
||||
--side-active:#13382F; --side-icon:#5C7A72; --side-icon-act:#32C8A9;
|
||||
--side-hover:#0A2A22; --side-border:#1A3A30;
|
||||
--st-paid:#007EB8; --st-quote:#008A4D; --st-call:#9A6700; --st-new:#B94837;
|
||||
--r-xs:4px; --r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
button { font-family:inherit; }
|
||||
a { color:inherit; text-decoration:none; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, [tabindex]:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
|
||||
}
|
||||
.skip-link { position:absolute; top:-40px; left:12px; background:var(--ink); color:#fff; padding:9px 16px; z-index:200; font-weight:600; border-radius:var(--r-sm); }
|
||||
.skip-link:focus { top:12px; }
|
||||
|
||||
/* Shell */
|
||||
.app { display:grid; grid-template-columns:232px 1fr; min-height:100vh; }
|
||||
.side { background:var(--side-bg); border-right:1px solid var(--side-border); padding:18px 12px 24px; position:sticky; top:0; height:100vh; overflow-y:auto; color:var(--side-text); }
|
||||
.brand { display:flex; align-items:center; gap:10px; padding:6px 8px 18px; font-weight:600; font-size:14.5px; letter-spacing:-0.01em; font-variation-settings:'opsz' 18; color:#FFF; }
|
||||
.brand-mark { width:22px; height:22px; border-radius:var(--r-xs); background:#FFF; display:inline-flex; align-items:center; justify-content:center; flex-shrink:0; overflow:hidden; }
|
||||
.brand-mark svg { width:100%; height:100%; display:block; }
|
||||
.brand-dot { color:var(--side-icon-act); }
|
||||
.nav { display:flex; flex-direction:column; gap:1px; }
|
||||
.nav-eyebrow { font-size:11px; font-weight:500; letter-spacing:0.01em; color:var(--side-text-2); padding:14px 10px 6px; }
|
||||
.nav-item { display:flex; align-items:center; gap:10px; height:32px; padding:0 10px; border-radius:var(--r-sm); font-size:13px; color:var(--side-text); }
|
||||
.nav-item:hover { background:var(--side-hover); color:#FFF; }
|
||||
.nav-item.active { background:var(--side-active); color:#FFF; font-weight:500; }
|
||||
.nav-item.active .nav-icon { color:var(--side-icon-act); }
|
||||
.nav-icon { width:15px; height:15px; flex-shrink:0; color:var(--side-icon); stroke-width:1.6; }
|
||||
.nav-item:hover .nav-icon { color:#FFF; }
|
||||
.nav-text { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.nav-count { font-family:var(--font-mono); font-size:10.5px; font-weight:500; font-feature-settings:'tnum'; background:rgba(255,255,255,0.10); color:var(--side-text); padding:2px 6px; border-radius:4px; }
|
||||
.main { display:flex; flex-direction:column; min-width:0; }
|
||||
.topbar { height:48px; border-bottom:1px solid var(--hairline); background:var(--surface); padding:0 24px; display:flex; align-items:center; gap:12px; position:sticky; top:0; z-index:50; }
|
||||
.crumb { font-size:12.5px; color:var(--ink-3); display:flex; align-items:center; gap:8px; }
|
||||
.crumb strong { color:var(--ink); font-weight:500; }
|
||||
.crumb svg { width:11px; height:11px; color:var(--ink-3); opacity:0.5; }
|
||||
.topbar-spacer { flex:1; }
|
||||
.user-chip { display:inline-flex; align-items:center; gap:8px; height:30px; padding:0 10px 0 4px; border-radius:100px; border:1px solid var(--hairline); background:var(--surface); }
|
||||
.user-chip .ava { width:22px; height:22px; border-radius:50%; background:var(--ink); color:#fff; display:inline-flex; align-items:center; justify-content:center; font-size:9.5px; font-weight:600; }
|
||||
.user-chip .uname { font-size:12px; color:var(--ink); font-weight:500; }
|
||||
|
||||
/* Page */
|
||||
.content { display:grid; grid-template-columns:200px 1fr; gap:28px; padding:24px 28px 80px; max-width:1500px; margin:0 auto; }
|
||||
.page-h { grid-column:1 / -1; display:flex; align-items:flex-end; justify-content:space-between; margin-bottom:6px; gap:16px; }
|
||||
.page-title { font-size:28px; font-weight:600; font-variation-settings:'opsz' 28; letter-spacing:-0.02em; line-height:1.1; margin:0 0 6px; }
|
||||
.page-meta { font-size:12.5px; color:var(--ink-3); }
|
||||
|
||||
/* Settings tab rail (left) */
|
||||
.tab-rail { display:flex; flex-direction:column; gap:1px; position:sticky; top:64px; align-self:start; }
|
||||
.tab-rail .rail-eyebrow { font-size:10.5px; font-weight:500; letter-spacing:0.06em; text-transform:uppercase; color:var(--ink-3); padding:10px 12px 6px; font-family:var(--font-mono); }
|
||||
.tab-rail button { display:flex; align-items:center; gap:9px; padding:8px 12px; border:none; background:transparent; font-family:inherit; font-size:13px; color:var(--ink-2); cursor:pointer; border-radius:var(--r-sm); text-align:left; letter-spacing:-0.005em; }
|
||||
.tab-rail button:hover { background:var(--sunken); color:var(--ink); }
|
||||
.tab-rail button.active { background:var(--ink); color:#fff; font-weight:500; }
|
||||
.tab-rail button.active .ico { color:var(--side-icon-act); }
|
||||
.tab-rail button .ico { width:14px; height:14px; color:var(--ink-3); flex-shrink:0; stroke-width:1.7; }
|
||||
.tab-rail button:hover .ico { color:var(--ink); }
|
||||
|
||||
/* Settings tab content */
|
||||
.tab-content { display:flex; flex-direction:column; gap:14px; min-width:0; }
|
||||
|
||||
.section { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); padding:22px 24px; }
|
||||
.section-h { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; flex-wrap:wrap; }
|
||||
.section-h h2 { font-size:16px; font-weight:600; font-variation-settings:'opsz' 18; letter-spacing:-0.012em; margin:0 0 4px; }
|
||||
.section-h .desc { font-size:12.5px; color:var(--ink-3); line-height:1.5; max-width:520px; }
|
||||
.section-h .h-action { white-space:nowrap; }
|
||||
|
||||
.field { display:flex; flex-direction:column; gap:6px; margin-bottom:14px; }
|
||||
.field:last-child { margin-bottom:0; }
|
||||
.field label { font-size:11.5px; font-weight:500; color:var(--ink-2); letter-spacing:-0.005em; }
|
||||
.field .hint { font-size:11.5px; color:var(--ink-3); margin-top:2px; line-height:1.4; }
|
||||
.input, .select {
|
||||
height:36px; padding:0 12px;
|
||||
border:1px solid var(--hairline); border-radius:var(--r-sm);
|
||||
background:var(--surface); font-family:inherit; font-size:13px;
|
||||
color:var(--ink); outline:none; letter-spacing:-0.005em;
|
||||
}
|
||||
.input.mono { font-family:var(--font-mono); font-feature-settings:'tnum'; letter-spacing:0; }
|
||||
.input:focus, .select:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-tint); }
|
||||
.field-row { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-bottom:14px; }
|
||||
.field-row .field { margin-bottom: 0; }
|
||||
|
||||
.btn { display:inline-flex; align-items:center; gap:7px; height:34px; padding:0 14px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:12.5px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; }
|
||||
.btn:hover { border-color:var(--ink-disabled); background:var(--sunken); }
|
||||
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
|
||||
.btn-danger { color:var(--st-new); }
|
||||
.btn-danger:hover { background:#FFE7E2; border-color:var(--st-new); color:#8E2516; }
|
||||
.btn svg { width:13px; height:13px; stroke-width:1.7; }
|
||||
|
||||
/* Toggle switch */
|
||||
.toggle { display:flex; align-items:center; justify-content:space-between; gap:14px; padding:11px 14px; background:var(--bg); border:1px solid var(--hairline-soft); border-radius:var(--r-sm); margin-bottom:6px; }
|
||||
.toggle:last-child { margin-bottom: 0; }
|
||||
.toggle .t-label { font-size:13px; color:var(--ink); font-weight:500; letter-spacing:-0.005em; }
|
||||
.toggle .t-desc { font-size:11.5px; color:var(--ink-3); margin-top:2px; line-height:1.4; }
|
||||
.toggle .switch { position:relative; width:36px; height:20px; flex-shrink:0; }
|
||||
.toggle .switch input { opacity:0; width:0; height:0; position:absolute; }
|
||||
.toggle .switch .slider { position:absolute; inset:0; background:var(--ink-disabled); border-radius:100px; transition:background 120ms ease; cursor:pointer; }
|
||||
.toggle .switch .slider::before { content:''; position:absolute; left:2px; top:2px; width:16px; height:16px; background:#fff; border-radius:50%; transition:transform 120ms ease; }
|
||||
.toggle .switch input:checked + .slider { background:var(--accent); }
|
||||
.toggle .switch input:checked + .slider::before { transform:translateX(16px); }
|
||||
.toggle .switch input:focus-visible + .slider { outline:2px solid var(--accent); outline-offset:2px; }
|
||||
|
||||
/* Avatar uploader */
|
||||
.avatar-up { display:flex; align-items:center; gap:14px; }
|
||||
.avatar-up .ava-big {
|
||||
width:64px; height:64px; border-radius:50%;
|
||||
background: var(--ink); color:#fff;
|
||||
display:inline-flex; align-items:center; justify-content:center;
|
||||
font-size:22px; font-weight:600; letter-spacing:0.02em;
|
||||
flex-shrink:0; position:relative;
|
||||
}
|
||||
.avatar-up .ava-big::after { content:''; position:absolute; inset:0; border-radius:50%; border:1px solid rgba(10,19,25,0.14); }
|
||||
|
||||
/* API tokens table */
|
||||
.token-table { width:100%; border-collapse:collapse; font-size:12.5px; }
|
||||
.token-table thead th { text-align:left; font-size:10.5px; font-weight:600; color:var(--ink-2); padding:8px 0; border-bottom:1px solid var(--hairline-soft); letter-spacing:0.005em; }
|
||||
.token-table thead th.num { text-align:right; }
|
||||
.token-table tbody td { padding:10px 0; border-bottom:1px solid var(--hairline-soft); vertical-align:middle; }
|
||||
.token-table tbody tr:last-child td { border-bottom:none; }
|
||||
.token-table .t-name { font-weight:500; color:var(--ink); }
|
||||
.token-table .t-name .scope { font-family:var(--font-mono); font-size:10px; color:var(--ink-3); display:block; margin-top:2px; letter-spacing:-0.005em; }
|
||||
.token-table .t-token { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11.5px; color:var(--ink-2); }
|
||||
.token-table .t-when { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11px; color:var(--ink-3); white-space:nowrap; }
|
||||
.token-table .row-action { width:24px; height:24px; border:none; background:none; color:var(--ink-3); border-radius:4px; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; }
|
||||
.token-table .row-action:hover { background:rgba(10,19,25,0.06); color:var(--st-new); }
|
||||
.token-table .row-action svg { width:13px; height:13px; }
|
||||
|
||||
.tab-page { display:none; }
|
||||
.tab-page.active { display:contents; }
|
||||
|
||||
@media (max-width:1100px) {
|
||||
.app { grid-template-columns:56px 1fr; }
|
||||
.side { padding:14px 6px; }
|
||||
.brand .brand-text, .nav-eyebrow, .nav-text, .nav-item:not(.active) .nav-count { display:none; }
|
||||
.nav-item { justify-content:center; padding:0; }
|
||||
.topbar { padding:0 16px; }
|
||||
.content { padding:18px 18px 60px; grid-template-columns:170px 1fr; gap:20px; }
|
||||
}
|
||||
@media (max-width:768px) {
|
||||
.app { grid-template-columns:1fr; }
|
||||
.side { display:none; }
|
||||
.topbar .crumb span:first-child { display:none; }
|
||||
.content { grid-template-columns:1fr; gap:16px; }
|
||||
.tab-rail { position:static; flex-direction:row; flex-wrap:wrap; gap:4px; padding-bottom:8px; }
|
||||
.tab-rail .rail-eyebrow { display:none; }
|
||||
.tab-rail button { padding:7px 10px; }
|
||||
.field-row { grid-template-columns:1fr; }
|
||||
.page-h { flex-direction:column; align-items:flex-start; gap:10px; }
|
||||
.section { padding:16px 16px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main" class="skip-link">К контенту</a>
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand">
|
||||
<span class="brand-mark" aria-hidden="true"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span class="brand-text">Лидерра<span class="brand-dot">.</span></span>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<div class="nav-eyebrow">Работа</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg><span class="nav-text">Дашборд</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18M6 12h12M9 18h6"/></svg><span class="nav-text">Сделки</span><span class="nav-count">247</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="6" height="18"/></svg><span class="nav-text">Канбан</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg><span class="nav-text">Напоминания</span><span class="nav-count">12</span></a>
|
||||
<div class="nav-eyebrow">Финансы</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h18M3 6h18M3 18h18"/></svg><span class="nav-text">Отчёты</span></a>
|
||||
<div class="nav-eyebrow">Команда</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Менеджеры</span><span class="nav-count">4</span></a>
|
||||
<a class="nav-item active" href="#" aria-current="page"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg><span class="nav-text">Настройки</span></a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Рабочая область</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg><strong>Настройки</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<button type="button" class="user-chip" aria-label="Профиль · Иван Петров"><span class="ava" aria-hidden="true">ИП</span><span class="uname">Иван П.</span></button>
|
||||
</div>
|
||||
|
||||
<main id="main" class="content">
|
||||
|
||||
<header class="page-h">
|
||||
<div>
|
||||
<h1 class="page-title">Настройки</h1>
|
||||
<div class="page-meta">тенант <strong style="color:var(--ink-2);font-weight:500">Окна Москва ООО</strong> · 4 менеджера · тариф «Команда»</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Tab rail (left column) -->
|
||||
<nav class="tab-rail" role="tablist" aria-label="Разделы настроек">
|
||||
<span class="rail-eyebrow">Личное</span>
|
||||
<button type="button" class="active" role="tab" data-tab="profile" aria-selected="true"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="7" r="4"/><path d="M2 21v-2a4 4 0 0 1 4-4h12a4 4 0 0 1 4 4v2"/></svg>Профиль</button>
|
||||
<button type="button" role="tab" data-tab="security" aria-selected="false"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>Безопасность</button>
|
||||
<span class="rail-eyebrow">Тенант</span>
|
||||
<button type="button" role="tab" data-tab="projects" aria-selected="false"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 7v13h18V7M3 7l3-4h12l3 4M3 7h18M9 11h6"/></svg>Проекты</button>
|
||||
<button type="button" role="tab" data-tab="team" aria-selected="false"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>Команда</button>
|
||||
<button type="button" role="tab" data-tab="api" aria-selected="false"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 2L13 10M16 2h5v5"/><path d="M21 13v5a2 2 0 0 1-2 2h-14a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"/></svg>API и Webhook</button>
|
||||
<button type="button" role="tab" data-tab="integrations" aria-selected="false"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="6" cy="18" r="3"/><circle cx="18" cy="6" r="3"/><path d="M6 15V9a3 3 0 0 1 3-3h3M18 9v6a3 3 0 0 1-3 3h-3"/></svg>Интеграции</button>
|
||||
<span class="rail-eyebrow">Тонкости</span>
|
||||
<button type="button" role="tab" data-tab="hours" aria-selected="false"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>Тихие часы</button>
|
||||
<button type="button" role="tab" data-tab="notifications" aria-selected="false"><svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>Уведомления</button>
|
||||
</nav>
|
||||
|
||||
<!-- Tab content -->
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- ===== ПРОФИЛЬ ===== -->
|
||||
<section id="tab-profile" class="tab-page active" aria-labelledby="profile-h">
|
||||
<article class="section">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2 id="profile-h">Профиль аккаунта</h2>
|
||||
<div class="desc">Эта информация используется при заключении договоров и в подписи писем.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="avatar-up">
|
||||
<span class="ava-big" aria-hidden="true">ИП</span>
|
||||
<div style="display:flex;flex-direction:column;gap:6px">
|
||||
<div style="display:flex;gap:6px"><button type="button" class="btn">Загрузить фото</button><button type="button" class="btn btn-danger">Удалить</button></div>
|
||||
<div style="font-size:11.5px;color:var(--ink-3)">PNG/JPG до 2MB · квадратное · минимум 256×256</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="height:18px"></div>
|
||||
<div class="field-row">
|
||||
<div class="field"><label for="p-fname">Имя</label><input class="input" id="p-fname" type="text" value="Иван"></div>
|
||||
<div class="field"><label for="p-lname">Фамилия</label><input class="input" id="p-lname" type="text" value="Петров"></div>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<div class="field"><label for="p-email">Email</label><input class="input" id="p-email" type="email" value="ivan.petrov@example.ru"></div>
|
||||
<div class="field"><label for="p-phone">Телефон</label><input class="input mono" id="p-phone" type="tel" value="+7 (916) 482-91-22"></div>
|
||||
</div>
|
||||
<div class="field"><label for="p-role">Роль в компании</label><input class="input" id="p-role" type="text" value="Старший менеджер" placeholder="Опционально"></div>
|
||||
<div style="display:flex;gap:8px;margin-top:8px"><button type="button" class="btn btn-primary">Сохранить</button><button type="button" class="btn">Отменить</button></div>
|
||||
</article>
|
||||
|
||||
<article class="section">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2>Реквизиты юр. лица</h2>
|
||||
<div class="desc">Для счетов и УПД. Обновляется в админке оператором — самостоятельно не редактируется.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<div class="field"><label>Юр. лицо</label><div style="font-size:13px;color:var(--ink);font-weight:500;letter-spacing:-0.005em">ООО «Окна Москва»</div></div>
|
||||
<div class="field"><label>ИНН / КПП</label><div style="font-family:var(--font-mono);font-feature-settings:'tnum';font-size:12.5px;color:var(--ink-2)">7724444444 / 772401001</div></div>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<div class="field"><label>Юр. адрес</label><div style="font-size:12.5px;color:var(--ink-2);line-height:1.5">115093, Москва, ул. Большая Серпуховская, 32 стр. 1, оф. 405</div></div>
|
||||
<div class="field"><label>Расчётный счёт</label><div style="font-family:var(--font-mono);font-size:12px;color:var(--ink-2)">40702810400000000123 · ПАО «Сбербанк»</div></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="section">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2 style="color:var(--st-new)">Удалить аккаунт</h2>
|
||||
<div class="desc">Все ваши проекты, сделки, история и баланс будут <strong style="color:var(--ink-2);font-weight:500">безвозвратно удалены</strong> по 152-ФЗ. Восстановление невозможно.</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-danger">Удалить мой аккаунт</button>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- ===== БЕЗОПАСНОСТЬ ===== -->
|
||||
<section id="tab-security" class="tab-page" aria-labelledby="security-h">
|
||||
<article class="section">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2 id="security-h">Пароль</h2>
|
||||
<div class="desc">Замена пароля каждые 90 дней не обязательна, но рекомендуется. Минимум 8 символов, оценка через zxcvbn.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field"><label for="s-old">Текущий пароль</label><input class="input" id="s-old" type="password" placeholder="••••••••"></div>
|
||||
<div class="field-row">
|
||||
<div class="field"><label for="s-new">Новый пароль</label><input class="input" id="s-new" type="password" placeholder="Минимум 8 символов"></div>
|
||||
<div class="field"><label for="s-conf">Повторите</label><input class="input" id="s-conf" type="password" placeholder=""></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;margin-top:8px"><button type="button" class="btn btn-primary">Сменить пароль</button></div>
|
||||
</article>
|
||||
|
||||
<article class="section">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2>Двухфакторная аутентификация</h2>
|
||||
<div class="desc">2FA через приложение-аутентификатор (Google Authenticator, Authy, Яндекс.Ключ). Включена на всех тарифах.</div>
|
||||
</div>
|
||||
<button type="button" class="btn">Скачать резервные коды</button>
|
||||
</div>
|
||||
<div class="toggle">
|
||||
<div>
|
||||
<div class="t-label">Двухфакторная защита включена</div>
|
||||
<div class="t-desc">Привязано к Yandex.Ключ · последний вход подтверждён 28 минут назад</div>
|
||||
</div>
|
||||
<label class="switch"><input type="checkbox" checked aria-label="2FA"><span class="slider" aria-hidden="true"></span></label>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="section">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2>Активные сессии</h2>
|
||||
<div class="desc">Если видите подозрительный вход — закройте сессию и смените пароль.</div>
|
||||
</div>
|
||||
<button type="button" class="btn">Закрыть все, кроме текущей</button>
|
||||
</div>
|
||||
<table class="token-table" aria-label="Активные сессии">
|
||||
<thead><tr><th>Устройство</th><th>IP / город</th><th class="num">Активна</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="t-name">macOS · Safari 17 <span class="scope">Текущая сессия</span></td><td class="t-token">93.184.216.34 · Москва</td><td class="t-when">сейчас</td><td></td></tr>
|
||||
<tr><td class="t-name">iOS · Safari Mobile</td><td class="t-token">94.51.21.108 · Москва</td><td class="t-when">3 ч назад</td><td><button type="button" class="row-action" aria-label="Закрыть сессию"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M18 6 6 18M6 6l12 12"/></svg></button></td></tr>
|
||||
<tr><td class="t-name">Windows · Chrome 121</td><td class="t-token">85.142.39.18 · Санкт-Петербург</td><td class="t-when">вчера</td><td><button type="button" class="row-action" aria-label="Закрыть сессию"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M18 6 6 18M6 6l12 12"/></svg></button></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- ===== API И WEBHOOK ===== -->
|
||||
<section id="tab-api" class="tab-page" aria-labelledby="api-h">
|
||||
<article class="section">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2 id="api-h">API-токены</h2>
|
||||
<div class="desc">Для outbound webhook и интеграций. Все токены хранятся как <code style="font-family:var(--font-mono);font-size:11.5px;background:var(--sunken);padding:1px 4px;border-radius:3px">type="password"</code> — открытое значение видно только при создании.</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Новый токен</button>
|
||||
</div>
|
||||
<table class="token-table" aria-label="API-токены">
|
||||
<thead><tr><th>Имя · scope</th><th>Маска</th><th class="num">Создан</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="t-name">Webhook prod <span class="scope">leads:read · deals:write</span></td><td class="t-token">lpk_••••••e91k</td><td class="t-when">12.04.2026</td><td><button type="button" class="row-action" aria-label="Удалить токен"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button></td></tr>
|
||||
<tr><td class="t-name">Я.Директ интеграция <span class="scope">leads:read</span></td><td class="t-token">lpk_••••••3xqz</td><td class="t-when">28.03.2026</td><td><button type="button" class="row-action" aria-label="Удалить токен"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button></td></tr>
|
||||
<tr><td class="t-name">Тестовый <span class="scope">leads:read · deals:read</span></td><td class="t-token">lpk_••••••9btv</td><td class="t-when">15.02.2026</td><td><button type="button" class="row-action" aria-label="Удалить токен"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
|
||||
<article class="section">
|
||||
<div class="section-h">
|
||||
<div>
|
||||
<h2>Webhook эндпоинты</h2>
|
||||
<div class="desc">URL ваших систем, куда мы отправим лиды/события сделок. Подпись HMAC SHA-256 в заголовке X-Liderra-Sign.</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Новый эндпоинт</button>
|
||||
</div>
|
||||
<table class="token-table" aria-label="Webhook эндпоинты">
|
||||
<thead><tr><th>URL · события</th><th>Статус (24ч)</th><th class="num">Последний</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="t-name">https://crm.example.ru/webhook/leads <span class="scope">lead.created · lead.updated</span></td><td><span style="display:inline-flex;align-items:center;gap:6px;font-size:11px"><span style="width:7px;height:7px;border-radius:50%;background:var(--st-quote)"></span>OK · 99.97% / 412</span></td><td class="t-when">3 мин назад</td><td><button type="button" class="row-action" aria-label="Удалить"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button></td></tr>
|
||||
<tr><td class="t-name">https://bitrix.example.ru/api/lead-in <span class="scope">lead.created</span></td><td><span style="display:inline-flex;align-items:center;gap:6px;font-size:11px"><span style="width:7px;height:7px;border-radius:50%;background:var(--st-call)"></span>деградация · 96.4%</span></td><td class="t-when">12 мин назад</td><td><button type="button" class="row-action" aria-label="Удалить"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- остальные tab-page заглушки чтобы переключатель работал, но не выводим контент -->
|
||||
<section id="tab-projects" class="tab-page"><article class="section"><h2>Проекты</h2><p style="color:var(--ink-3);font-size:13px;margin:0;line-height:1.5">Список проектов с фильтрами, архивированием, массовыми операциями. <em>Контент в следующих итерациях.</em></p></article></section>
|
||||
<section id="tab-team" class="tab-page"><article class="section"><h2>Команда</h2><p style="color:var(--ink-3);font-size:13px;margin:0;line-height:1.5">Менеджеры тенанта, приглашение по email + magic-link 24ч для подрядчиков. <em>Контент в следующих итерациях.</em></p></article></section>
|
||||
<section id="tab-integrations" class="tab-page"><article class="section"><h2>Интеграции</h2><p style="color:var(--ink-3);font-size:13px;margin:0;line-height:1.5">amoCRM (спринт 14–15), Bitrix24 / RetailCRM в Post-MVP. <em>Контент в следующих итерациях.</em></p></article></section>
|
||||
<section id="tab-hours" class="tab-page"><article class="section"><h2>Тихие часы</h2><p style="color:var(--ink-3);font-size:13px;margin:0;line-height:1.5">start_hour / end_hour 0..23, минимум 3 часа, общий timezone тенанта. <em>Контент в следующих итерациях.</em></p></article></section>
|
||||
<section id="tab-notifications" class="tab-page"><article class="section"><h2>Уведомления</h2><p style="color:var(--ink-3);font-size:13px;margin:0;line-height:1.5">Push, email, Telegram-канал команды. <em>Контент в следующих итерациях.</em></p></article></section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const tabs = Array.from(document.querySelectorAll('.tab-rail [role="tab"]'));
|
||||
const pages = Array.from(document.querySelectorAll('.tab-page'));
|
||||
function setTab(id) {
|
||||
tabs.forEach(t => {
|
||||
const isA = t.dataset.tab === id;
|
||||
t.classList.toggle('active', isA);
|
||||
t.setAttribute('aria-selected', isA ? 'true' : 'false');
|
||||
});
|
||||
pages.forEach(p => p.classList.toggle('active', p.id === 'tab-' + id));
|
||||
}
|
||||
tabs.forEach(t => t.addEventListener('click', () => setTab(t.dataset.tab)));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,375 @@
|
||||
# Лидерра · Brandbook v2
|
||||
|
||||
**Версия:** v8 Forest · 2026-05-07
|
||||
**Заменяет:** Лидпоток v1.1 (deprecated, см. `дизайн/02_Brand_i_A11y.md` — устаревшие данные).
|
||||
|
||||
---
|
||||
|
||||
## 1. Имя
|
||||
|
||||
**«Лидерра»** (кириллица). Латиница — `Liderra` (для домена `liderra.app`).
|
||||
|
||||
### 1.1. Wordmark
|
||||
|
||||
`Лидерра.` — обязательно с **точкой** в конце. Точка покрашена в bright Teal:
|
||||
- На светлом фоне — `#0F6E56` (базовый Teal)
|
||||
- На тёмном sidebar — `#32C8A9` (bright Teal, accent для dark)
|
||||
|
||||
### 1.2. Происхождение
|
||||
|
||||
«Лидерра» — составное: «лид» (lead, английская калька) + «эра» (epoch, временной период) или «терра» (земля). Сохраняет смысловую связь с устаревшим «Лидпоток», но звучит дороже и менее буквально. Сравнение архива — [`v8_brand.html`](../concepts/v8_brand.html).
|
||||
|
||||
### 1.3. Что нельзя
|
||||
|
||||
- ❌ «Лидпоток» — старое имя, deprecated с 2026-05-07
|
||||
- ❌ `лидерра.` lowercase
|
||||
- ❌ `LIDERRA` UPPERCASE (кроме случаев типа `<small caps>`)
|
||||
- ❌ Замена точки на восклицательный знак / другую пунктуацию
|
||||
|
||||
---
|
||||
|
||||
## 2. Логотип
|
||||
|
||||
### 2.1. Знак — `L-Square с Teal-точкой`
|
||||
|
||||
Белый rounded-square 22×22px (или ratio 11:11), внутри L-stroke 4.5px (тёмный) + Teal-точка r=3.5 в правом нижнем углу, на месте «приземления» лида.
|
||||
|
||||
#### Inline SVG (skopirovat) — для тёмного sidebar
|
||||
|
||||
```html
|
||||
<svg viewBox="0 0 48 48" width="22" height="22" aria-hidden="true">
|
||||
<rect x="2" y="2" width="44" height="44" rx="10" fill="#FFFFFF"/>
|
||||
<path d="M16 14 L16 34 L32 34"
|
||||
stroke="#012019" stroke-width="4.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="32" cy="34" r="3.5" fill="#0F6E56"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
#### Inline SVG — для светлого фона (тёмный квадрат)
|
||||
|
||||
```html
|
||||
<svg viewBox="0 0 48 48" width="22" height="22" aria-hidden="true">
|
||||
<rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/>
|
||||
<path d="M16 14 L16 34 L32 34"
|
||||
stroke="#FFFFFF" stroke-width="4.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="32" cy="34" r="3.5" fill="#32C8A9"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
#### Favicon (упрощённая версия — без рамки, без точки)
|
||||
|
||||
```html
|
||||
<svg viewBox="0 0 48 48" width="32" height="32">
|
||||
<rect width="48" height="48" rx="8" fill="#0F6E56"/>
|
||||
<path d="M16 14 L16 34 L32 34"
|
||||
stroke="#FFFFFF" stroke-width="5.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### 2.2. Защитное поле
|
||||
|
||||
Минимум **8px** со всех сторон от знака до любого другого элемента. Внутри clear-зоны не должно быть никакого текста или графики.
|
||||
|
||||
### 2.3. Минимальный размер
|
||||
|
||||
- Знак — **16×16px** (favicon-режим, без точки)
|
||||
- Знак с точкой — **22×22px** (sidebar-режим, минимум)
|
||||
- Wordmark «Лидерра.» — **14px font-size** минимум
|
||||
|
||||
### 2.4. Размещение wordmark
|
||||
|
||||
`Знак + 10px gap + Wordmark` — классическое горизонтальное расположение в sidebar/topbar/lending-header.
|
||||
|
||||
В sidebar (тёмный фон):
|
||||
```html
|
||||
<div class="brand">
|
||||
<span class="brand-mark">[SVG]</span>
|
||||
<span class="brand-text">Лидерра<span class="brand-dot">.</span></span>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
.brand-text { color: #FFFFFF; font-weight: 600; font-size: 14.5px; font-variation-settings: 'opsz' 18; }
|
||||
.brand-dot { color: #32C8A9; } /* bright Teal */
|
||||
```
|
||||
|
||||
### 2.5. Что нельзя
|
||||
|
||||
- ❌ Менять цвета знака, обводку L-stroke, радиус square
|
||||
- ❌ Поворачивать, скашивать, искажать пропорции
|
||||
- ❌ Добавлять тени, градиенты, обводки, outer-glow
|
||||
- ❌ Использовать на фоне фотографии без поднятой непрозрачной плашки
|
||||
- ❌ Уменьшать ниже минимального размера
|
||||
- ❌ Окрашивать L-stroke в **не**-фирменный цвет
|
||||
- ❌ Использовать L-stroke без Teal-точки на размерах ≥ 24px (точка обязательна для distinctness)
|
||||
|
||||
### 2.6. Архив отвергнутых вариантов
|
||||
|
||||
См. [`v8_brand.html`](../concepts/v8_brand.html). Финальный выбор: имя «Лидерра» + знак A · L-Square (после сравнения 10 имён + 5 SVG-знаков).
|
||||
|
||||
---
|
||||
|
||||
## 3. Палитра — Forest
|
||||
|
||||
### 3.1. Происхождение
|
||||
|
||||
После критики предыдущих палитр («слишком белый, всё сливается») построено 5 вариантов в OKLCH (см. [`palette_options.py`](../palette_options.py)). Выбран Б · **Forest Sidebar** — глубокий теало-нуар sidebar (родственник брендового Teal) + warm ivory main.
|
||||
|
||||
### 3.2. Поверхности и текст
|
||||
|
||||
| Token | HEX | OKLCH (~) | Применение | Контраст vs ink |
|
||||
|---|---|---|---|---|
|
||||
| `--bg` | `#F6F3EC` | 0.965/0.010/90 | Page background (warm ivory) | — |
|
||||
| `--surface` | `#FFFDFA` | 0.995/0.005/90 | Карточки, поднятые поверхности | — |
|
||||
| `--sunken` | `#F0EDE4` | 0.945/0.012/90 | Table thead, sunken zones | — |
|
||||
| `--hairline` | `#D9D5CD` | 0.875/0.012/85 | 1px borders | — |
|
||||
| `--hairline-soft` | `#E8E3D6` | — | Внутренние дивайдеры строк | — |
|
||||
| `--ink` | `#081319` | 0.18/0.020/230 | Primary text | 16.96:1 ✓ |
|
||||
| `--ink-2` | `#343C41` | 0.35/0.014/240 | Secondary | 10.34:1 ✓ |
|
||||
| `--ink-3` | `#66635C` | 0.51/0.012/90 | Muted (≥4.5:1) | 5.42:1 ✓ |
|
||||
| `--ink-disabled` | `#92907B` | 0.62/0.008/90 | UI components / disabled | 3.48:1 ✓ (для UI, не для текста) |
|
||||
|
||||
Все контрасты подтверждены axe-core 4.10.2 на 25 макетах.
|
||||
|
||||
### 3.3. Brand accent — Teal
|
||||
|
||||
| Token | HEX | Применение | Контраст |
|
||||
|---|---|---|---|
|
||||
| `--accent` | `#0F6E56` | Primary brand | 5.94:1 на bg ✓ |
|
||||
| `--accent-tint` | `#E1EEEA` | Selection / chip-tinted background | — |
|
||||
| `--accent-deep` | `#084635` | Hover / active state | 8.06:1 на bg ✓ |
|
||||
| `--side-icon-act` | `#32C8A9` | Bright Teal — accent на dark sidebar | 8.15:1 на side-bg ✓ |
|
||||
|
||||
**Нельзя оспаривать без явного OK заказчика** — это брендовый цвет (зафиксирован Диз-2 от 04.05.2026).
|
||||
|
||||
### 3.4. Sidebar — глубокий teal-noir
|
||||
|
||||
| Token | HEX | OKLCH (~) | Применение |
|
||||
|---|---|---|---|
|
||||
| `--side-bg` | `#012019` | 0.22/0.040/175 | Sidebar background |
|
||||
| `--side-text` | `#B1C2BD` | 0.80/0.020/175 | Nav-item text (9.23:1 на side-bg ✓) |
|
||||
| `--side-text-2` | `#7A8C87` | — | Nav-eyebrow muted |
|
||||
| `--side-active` | `#13382F` | 0.31/0.045/175 | Selected item fill |
|
||||
| `--side-icon` | `#5C7A72` | — | Idle icon |
|
||||
| `--side-icon-act` | `#32C8A9` | 0.75/0.13/175 | Active icon (bright Teal) |
|
||||
| `--side-hover` | `#0A2A22` | — | Hover background |
|
||||
| `--side-border` | `#1A3A30` | — | 1px border |
|
||||
|
||||
### 3.5. Sidebar↔Main контраст = 15.46:1
|
||||
|
||||
Это решение жалобы «всё сливается» — теперь sidebar отчётливо отделён от main по светлоте.
|
||||
|
||||
### 3.6. 14 статусов воронки — OKLCH с ΔE2000 ≥ 10.57
|
||||
|
||||
Цвета распределены по hue-кругу с repulsion-итерацией для гарантии перцептивной различимости (см. [`palette_14.py`](../palette_14.py)). Каждый статус имеет 2 варианта: `solid` (для dot) и `tint` (для chip background).
|
||||
|
||||
| # | Статус | Solid | Tint | Hue (°) |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Новая | `#B94837` | `#FFD8CF` | 30.7 |
|
||||
| 2 | В работе | `#B35100` | `#FFDBC4` | 51.2 |
|
||||
| 3 | Дозвон | `#9A6700` | `#F6E2BC` | 84.3 |
|
||||
| 4 | Не дозвонились | `#7E7500` | `#E9E8BD` | 106.1 |
|
||||
| 5 | Переговоры | `#538200` | `#D9EDC6` | 130.0 |
|
||||
| 6 | КП отправлено | `#008A4D` | `#C7F0D7` | 157.7 |
|
||||
| 7 | Думает | `#008C7E` | `#BCF1E9` | 185.5 |
|
||||
| 8 | Ждёт оплату | `#00889B` | `#BAF0F6` | 205.5 |
|
||||
| 9 | Оплачено | `#007EB8` | `#C0ECFF` | 231.2 |
|
||||
| 10 | Возврат | `#406DC8` | `#D1E5FF` | 262.5 |
|
||||
| 11 | Отказ | `#6C60C4` | `#E1E0FF` | 286.1 |
|
||||
| 12 | Дубликат | `#9052AE` | `#F2DAFF` | 313.7 |
|
||||
| 13 | Спам | `#AA4788` | `#FFD7EE` | 343.1 |
|
||||
| 14 | Архив | `#B7445F` | `#FFD6DD` | 8.8 |
|
||||
|
||||
**min ΔE2000 = 10.57**, max = 14.34, mean = 12.91 (между соседними solid-парами).
|
||||
|
||||
---
|
||||
|
||||
## 4. Типографика
|
||||
|
||||
### 4.1. Шрифты
|
||||
|
||||
| Шрифт | Применение | Лицензия |
|
||||
|---|---|---|
|
||||
| **Inter** (variable, opsz 14..32) | Весь UI, заголовки, body | OFL — free, кириллица full |
|
||||
| **JetBrains Mono** (400/500/600) | Numerics, code, ID, timestamps | Apache 2.0 — free, кириллица full |
|
||||
|
||||
#### Подключение
|
||||
|
||||
```html
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
```
|
||||
|
||||
### 4.2. Шкала размеров
|
||||
|
||||
| Применение | font-size | weight | opsz | letter-spacing | line-height |
|
||||
|---|---|---|---|---|---|
|
||||
| Hero h1 (лендинг) | 56px | 600 | 32 | -0.03em | 1.05 |
|
||||
| Page title | 28px | 600 | 28 | -0.02em | 1.1 |
|
||||
| Section h2 | 22px | 600 | 24 | -0.018em | 1.15 |
|
||||
| Card title | 16px | 600 | 18 | -0.012em | 1.3 |
|
||||
| Body | 13-14px | 400 | 14 | -0.005em | 1.5 |
|
||||
| Eyebrow / label | 11.5px | 500 | 14 | letter-spacing 0.005em | 1.4 |
|
||||
| Mono numeric | 12.5px | 500 | — | -0.005em | 1.4 |
|
||||
| Caption | 11px | 500 | 14 | 0.005em | 1.4 |
|
||||
|
||||
### 4.3. opsz axis (важно)
|
||||
|
||||
Inter 4.0+ имеет ось `opsz` (optical size) — один файл с двумя «режимами»:
|
||||
- `font-variation-settings: 'opsz' 14` — UI режим (mid-weight, оптимизирован для мелкого текста)
|
||||
- `font-variation-settings: 'opsz' 28` — display режим (более тонкие штрихи, оптимизирован для крупного текста)
|
||||
|
||||
Это **обязательно** для финальной полировки — без переключения opsz большие заголовки будут читаться как «жирный body».
|
||||
|
||||
### 4.4. Numerics — везде JBM с tnum
|
||||
|
||||
Все числа (цены, балансы, ID, timestamp, телефоны, KPI-значения) — обязательно:
|
||||
|
||||
```css
|
||||
font-family: var(--font-mono);
|
||||
font-feature-settings: 'tnum'; /* tabular figures */
|
||||
```
|
||||
|
||||
Без `tnum` цифры в столбцах не выравниваются — таблицы и счета смотрятся неаккуратно.
|
||||
|
||||
### 4.5. Что нельзя
|
||||
|
||||
- ❌ Использовать Inter weight 100/200/800/900 (не подключены)
|
||||
- ❌ Использовать `text-shadow`, `font-style: oblique` (применять `italic` через variation)
|
||||
- ❌ Использовать любой другой шрифт без явного утверждения дизайнером
|
||||
|
||||
---
|
||||
|
||||
## 5. Иконки
|
||||
|
||||
- **Lucide** (open-source, free) — основной набор для UI
|
||||
- Inline SVG — для бренд-знака и декоративных illustrations (404/403/500)
|
||||
- Все размеры — кратные 4: 11/13/15/18/22/26/32px
|
||||
- `stroke-width: 1.6-1.7` для UI иконок
|
||||
- `stroke-linecap: round` + `stroke-linejoin: round` обязательно
|
||||
- Цвет — `currentColor` (наследует от родителя)
|
||||
- `aria-hidden="true"` для декоративных, `aria-label="..."` для функциональных
|
||||
|
||||
---
|
||||
|
||||
## 6. Spacing — 4px-base
|
||||
|
||||
Шкала: `4 / 8 / 12 / 16 / 20 / 24 / 32 / 48 / 64 / 80px`
|
||||
|
||||
| Применение | Значение |
|
||||
|---|---|
|
||||
| Внутренние plotno gap | 4-8px |
|
||||
| Padding кнопок / chip | 8-14px |
|
||||
| Padding карточек | 12-22px |
|
||||
| Gap между секциями | 16-24px |
|
||||
| Page padding desktop | 32px |
|
||||
| Hero padding (лендинг) | 56-80px |
|
||||
|
||||
---
|
||||
|
||||
## 7. Радиусы
|
||||
|
||||
| Token | Value | Применение |
|
||||
|---|---|---|
|
||||
| `--r-xs` | 4px | nav-count, kbd, mini-badges |
|
||||
| `--r-sm` | 6px | кнопки, инпуты, nav-items |
|
||||
| `--r-md` | 10px | карточки, секции, табли |
|
||||
| `--r-lg` | 14px | hero-cards, лендинг-блоки |
|
||||
| — | 50% | аватары |
|
||||
| — | 100px | full-pill chips |
|
||||
|
||||
**Нельзя**: 8px, 12px (между sm и md — только используем кратные шкалы выше).
|
||||
|
||||
---
|
||||
|
||||
## 8. Тени — НЕ используем
|
||||
|
||||
В Forest отказываемся от теней. Глубина — **только 1px hairline borders**.
|
||||
|
||||
**Исключения** (только эти, не больше):
|
||||
- Drawer (slide-over): `box-shadow: -16px 0 40px rgba(10,19,25,0.10);`
|
||||
- KPI-card hover (опционально): `box-shadow: 0 2px 6px rgba(10,19,25,0.04);`
|
||||
- Modal/dialog backdrop: `background: rgba(10,19,25,0.18); backdrop-filter: blur(2px);`
|
||||
|
||||
Если разработчик сомневается — **не добавлять `box-shadow`**, использовать `border: 1px solid var(--hairline)`.
|
||||
|
||||
---
|
||||
|
||||
## 9. Tone of voice
|
||||
|
||||
### 9.1. Что мы говорим
|
||||
|
||||
- **«Поток лидов под контролем»** — слоган
|
||||
- **«Pay-per-lead, прозрачно»** — value proposition
|
||||
- **«Каждый рубль в дело»** — про деньги
|
||||
- Прямые формулировки про затраты, ROI, retry-стратегии
|
||||
|
||||
### 9.2. Что мы НЕ говорим
|
||||
|
||||
- ❌ «Эстетичное решение»
|
||||
- ❌ «Бесшовный пользовательский опыт»
|
||||
- ❌ «Премиум»-категория без обоснования
|
||||
- ❌ «Инновационный подход»
|
||||
- ❌ «Экосистема» / «синергия» / «ноу-хау»
|
||||
- ❌ Англоязычные термины без перевода (используем «лид» вместо «lead», «сделка» вместо «deal»)
|
||||
|
||||
### 9.3. Числа важнее эпитетов
|
||||
|
||||
Вместо «*быстрый* webhook» — «webhook за 142ms (медиана)». Вместо «*надёжный* uptime» — «99.97% за 7 дней». Все маркетинговые claims должны быть проверяемы.
|
||||
|
||||
---
|
||||
|
||||
## 10. Голос юр. документов
|
||||
|
||||
Оферта и Политика конфиденциальности — **формальный официальный** русский, без англицизмов. Всё измерено в рублях, не в долларах. ИП/ООО, не Inc/Ltd. Дата — DD.MM.YYYY (не Mon DD, YYYY).
|
||||
|
||||
См. макеты [v8_landing.html#offer](../concepts/v8_landing.html#offer) и [v8_landing.html#privacy](../concepts/v8_landing.html#privacy) — там lorem ipsum в местах нерелевантных юр. шаблону, остальной текст реальный.
|
||||
|
||||
---
|
||||
|
||||
## 11. Применение бренда — где можно использовать
|
||||
|
||||
| Контекст | OK | Нельзя |
|
||||
|---|---|---|
|
||||
| Sidebar app-кабинета | ✅ Лидерра. + L-square | — |
|
||||
| Топ публичного сайта | ✅ Лидерра. + L-square (тёмный) | — |
|
||||
| Email signature | ✅ Лидерра. + L-square (16px) | Логотип-only без wordmark |
|
||||
| Favicon | ✅ Упрощённая Teal-square версия | Полный знак (мелковато) |
|
||||
| Splash screen | ✅ Большой L-square 80px + wordmark 28px | — |
|
||||
| Социальные сети (avatar 400×400) | ✅ Teal-square с белой буквой | Знак на белом фоне (не виден) |
|
||||
| OG image (соц. preview) | ✅ Лидерра. + tagline + URL | Без брендинга (looks generic) |
|
||||
| Печатные материалы | Pantone эквивалент Teal — на согласовании | Конвертация в CMYK без проверки |
|
||||
|
||||
---
|
||||
|
||||
## 12. Изменение бренда
|
||||
|
||||
Любое изменение этого документа требует:
|
||||
1. Согласование с заказчиком (Дмитрий)
|
||||
2. Обновление [`docs/DEVELOPER_HANDOFF.md`](DEVELOPER_HANDOFF.md)
|
||||
3. Обновление token files (`assets/tokens.css`)
|
||||
4. Регрессия по 25 макетам (axe-core)
|
||||
|
||||
---
|
||||
|
||||
## 13. Откуда что взято — источники решений
|
||||
|
||||
- **Палитра Forest**: 5 вариантов в `palette_options.py` (А Graphite / **Б Forest** / В Slate-Blue / Г Champagne / Д Inverted Hero), выбран Б 2026-05-07
|
||||
- **L-Square знак**: 5 концептов в [`v8_brand.html`](../concepts/v8_brand.html), выбран знак A
|
||||
- **Имя Лидерра**: 10 вариантов в [`v8_brand.html`](../concepts/v8_brand.html), выбран #10
|
||||
- **14 OKLCH-статусов**: расчёт в `palette_14.py`, repulsion-итерация
|
||||
- **Bright Teal `#32C8A9`**: производное от брендового Teal `#0F6E56` — тот же hue 175, поднят до L=0.75 для контраста на тёмном sidebar (8.15:1)
|
||||
- **Inter (opsz axis)**: рекомендация по 21st.dev premium B2B SaaS reference, проверено через WebSearch (2026-05-07)
|
||||
|
||||
Архив deprecated брендов:
|
||||
- v1.1 Лидпоток + 3-circle wordmark — отвергнут заказчиком 2026-05-06
|
||||
- v4-v6 концепты cream + Instrument Serif — устарели после критики «слишком тёплый» 2026-05-07
|
||||
- v7 cool-tech light — устарел после критики «слишком белый, всё сливается» 2026-05-07
|
||||
|
||||
---
|
||||
|
||||
*Конец `BRANDBOOK_v2.md`. Source of truth для бренда. При расхождении с любым другим документом — верить этому.*
|
||||
@@ -0,0 +1,782 @@
|
||||
# Лидерра · Developer Handoff
|
||||
|
||||
**Версия:** v8 Forest · 2026-05-07
|
||||
**Назначение:** Всё необходимое разработчику для реализации портала Лидерра без участия дизайнера. Работает поверх HTML-прототипов в `concepts/v8_*.html`.
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR — старт за 5 минут
|
||||
|
||||
1. **Стек, который нельзя оспаривать:** Vue 3 + Vuetify 3, светлая тема (тёмная — v2), Yandex Cloud, Yandex 360 SSO для админки, ЮKassa primary platежи. CTO-11.
|
||||
2. **Палитра v8 Forest:** скопируй [§3 «CSS-переменные»](#3-дизайн-токены--css-variables) в `assets/tokens.css` и подключи глобально.
|
||||
3. **Шрифты:** Inter (Google Fonts, axis `opsz` 14..32) + JetBrains Mono. Импорт в `<head>` или `@import`. См. [§4](#4-типографика).
|
||||
4. **Лого:** SVG inline в `assets/logo-mark.svg` (см. [§2 «Бренд»](#2-бренд)).
|
||||
5. **Все 25 экранов:** живые HTML в [`concepts/`](../concepts/) — открой `v8_dashboard.html` и снимай вёрстку напрямую. WCAG 2.1 AA подтверждено axe-core 4.10.2 на 4 viewports.
|
||||
6. **Если что-то непонятно — смотри [§14 «FAQ»](#14-faq)** или пиши автору.
|
||||
|
||||
---
|
||||
|
||||
## 1. Архитектура и стек
|
||||
|
||||
### 1.1. Технологии (зафиксировано)
|
||||
|
||||
| Слой | Технология | Источник |
|
||||
|---|---|---|
|
||||
| Frontend | **Vue 3 + Vuetify 3** | CTO-11, не оспаривается |
|
||||
| Стейт | Pinia | стандарт Vue 3 |
|
||||
| Роутер | Vue Router 4 | стандарт |
|
||||
| Сборка | Vite | стандарт |
|
||||
| HTTP | Axios или native `fetch` | на выбор разработчика |
|
||||
| Charts | ApexCharts | бриф §12 — указан напрямую |
|
||||
| Виртуализация | `vue-virtual-scroller` | бриф §11 для Канбана |
|
||||
| Drag-and-drop | `@vueuse/integrations` или `vuedraggable` | для Канбана |
|
||||
| Backend | (out-of-scope) | Yandex Cloud, FastAPI/Node — на выбор |
|
||||
|
||||
### 1.2. Структура проекта (рекомендация)
|
||||
|
||||
```
|
||||
src/
|
||||
├── assets/
|
||||
│ ├── tokens.css ← из §3 этого документа
|
||||
│ ├── fonts.css ← @import Inter + JBM
|
||||
│ └── logo-mark.svg ← из §2.3
|
||||
├── components/
|
||||
│ ├── shell/
|
||||
│ │ ├── AppSidebar.vue
|
||||
│ │ ├── AppTopbar.vue
|
||||
│ │ └── BrandMark.vue ← inline SVG
|
||||
│ ├── ui/
|
||||
│ │ ├── BaseButton.vue
|
||||
│ │ ├── BaseChip.vue ← с 14 status variants
|
||||
│ │ ├── BaseInput.vue
|
||||
│ │ ├── DataTable.vue
|
||||
│ │ ├── KpiCard.vue
|
||||
│ │ ├── SlideOverDrawer.vue
|
||||
│ │ └── BalanceWidget.vue ← с runway-scale
|
||||
│ └── domain/
|
||||
│ ├── DealCard.vue
|
||||
│ ├── KanbanBoard.vue
|
||||
│ ├── ActivityLog.vue
|
||||
│ └── BillingTransactionRow.vue
|
||||
├── pages/
|
||||
│ ├── Dashboard.vue
|
||||
│ ├── DealsList.vue
|
||||
│ ├── DealCard.vue
|
||||
│ ├── Kanban.vue
|
||||
│ ├── Billing.vue
|
||||
│ ├── Settings.vue
|
||||
│ ├── Reports.vue
|
||||
│ ├── Login.vue
|
||||
│ ├── errors/
|
||||
│ │ ├── NotFound.vue
|
||||
│ │ ├── Forbidden.vue
|
||||
│ │ └── ServerError.vue
|
||||
│ ├── admin/
|
||||
│ │ ├── AdminLogin.vue
|
||||
│ │ ├── TenantsList.vue
|
||||
│ │ ├── TenantCard.vue
|
||||
│ │ ├── AdminBilling.vue
|
||||
│ │ ├── Incidents.vue
|
||||
│ │ └── SystemSettings.vue
|
||||
│ └── public/
|
||||
│ ├── Home.vue
|
||||
│ ├── Pricing.vue
|
||||
│ ├── Offer.vue
|
||||
│ └── Privacy.vue
|
||||
└── App.vue
|
||||
```
|
||||
|
||||
### 1.3. Окружения
|
||||
|
||||
| Окружение | Frontend | API | Что особенного |
|
||||
|---|---|---|---|
|
||||
| local | localhost:5173 | localhost:8000 | hot reload, mock data |
|
||||
| stage | stage.liderra.app | api.stage.liderra.app | seed-данные, без оплат |
|
||||
| prod | liderra.app + admin.liderra.app | api.liderra.app | полный 2FA, RLS, audit log |
|
||||
|
||||
---
|
||||
|
||||
## 2. Бренд
|
||||
|
||||
### 2.1. Имя
|
||||
|
||||
**«Лидерра»** — кириллица. Wordmark с **точкой** в конце: `Лидерра.` Точка покрашена в **bright Teal `#32C8A9`** (`--side-icon-act`) на тёмном sidebar и в **базовый Teal `#0F6E56`** на светлом фоне. Латинская транслитерация: `Liderra` (для домена `liderra.app`, email `support@liderra.app`).
|
||||
|
||||
**Tone of voice:** уверенный без помпезности, технический без сухости, премиальный без снобизма. Прямые формулировки про деньги. Никакого «эстетичного решения», «бесшовного опыта», «инновационного подхода».
|
||||
|
||||
### 2.2. Лого — `L-Square с Teal-точкой`
|
||||
|
||||
```
|
||||
[ белый rounded-square 22×22 ]
|
||||
│
|
||||
└─ внутри: L-stroke 4.5px (тёмный) + Teal-точка r=3.5 в конце
|
||||
```
|
||||
|
||||
### 2.3. SVG (inline, копировать в `BrandMark.vue`)
|
||||
|
||||
```html
|
||||
<!-- Версия для тёмного sidebar (на белом квадрате) -->
|
||||
<svg viewBox="0 0 48 48" width="22" height="22">
|
||||
<rect x="2" y="2" width="44" height="44" rx="10" fill="#FFFFFF"/>
|
||||
<path d="M16 14 L16 34 L32 34"
|
||||
stroke="#012019" stroke-width="4.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="32" cy="34" r="3.5" fill="#0F6E56"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Версия для светлого фона (тёмный квадрат) -->
|
||||
<svg viewBox="0 0 48 48" width="22" height="22">
|
||||
<rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/>
|
||||
<path d="M16 14 L16 34 L32 34"
|
||||
stroke="#FFFFFF" stroke-width="4.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="32" cy="34" r="3.5" fill="#32C8A9"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Favicon — без рамки и точки (упрощённый) -->
|
||||
<svg viewBox="0 0 48 48" width="32" height="32">
|
||||
<rect width="48" height="48" rx="8" fill="#0F6E56"/>
|
||||
<path d="M16 14 L16 34 L32 34"
|
||||
stroke="#FFFFFF" stroke-width="5.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### 2.4. Что нельзя
|
||||
|
||||
- Менять пропорции, цвета, обводку лого, добавлять тени/градиенты
|
||||
- Использовать «Лидпоток» (старое имя — deprecated)
|
||||
- Использовать lowercase «лидерра.» — wordmark капитализирован
|
||||
- Использовать слово «премиум» в маркетинге без обоснования (мы — про прозрачность и контроль, не про роскошь)
|
||||
|
||||
---
|
||||
|
||||
## 3. Дизайн-токены (CSS variables)
|
||||
|
||||
Все токены — реальные значения из v8 Forest. Скопировать в `assets/tokens.css`, подключить в `<head>` глобально. Все WCAG-контрасты подтверждены через axe-core 4.10.2 на 25 макетах.
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* === ПОВЕРХНОСТИ (Forest light, hue ≈ 80, warm ivory) === */
|
||||
--bg: #F6F3EC; /* Page background */
|
||||
--surface: #FFFDFA; /* Карточки, поднятые поверхности */
|
||||
--sunken: #F0EDE4; /* Чуть глубже bg — для table thead, sunken-areas */
|
||||
--hairline: #D9D5CD; /* 1px UI-border (формальный) */
|
||||
--hairline-soft: #E8E3D6; /* Внутренние дивайдеры строк */
|
||||
|
||||
/* === ТЕКСТ (на ivory bg, проверено WCAG AA) === */
|
||||
--ink: #081319; /* 16.96:1 — primary text */
|
||||
--ink-2: #343C41; /* 10.34:1 — secondary */
|
||||
--ink-3: #66635C; /* 5.42:1 — muted (≥4.5:1) */
|
||||
--ink-disabled: #92907B; /* 3.48:1 — для UI-компонентов / disabled fields */
|
||||
|
||||
/* === БРЕНД === */
|
||||
--accent: #0F6E56; /* Teal — primary brand · 5.94:1 на bg */
|
||||
--accent-tint: #E1EEEA; /* Tint для selection / chip-tinted */
|
||||
--accent-deep: #084635; /* Hover/active state */
|
||||
|
||||
/* === SIDEBAR (тёмный teal-noir) === */
|
||||
--side-bg: #012019;
|
||||
--side-text: #B1C2BD; /* 9.23:1 на side-bg */
|
||||
--side-text-2: #7A8C87; /* nav-eyebrow muted */
|
||||
--side-active: #13382F; /* Selected item fill */
|
||||
--side-icon: #5C7A72; /* Idle icon */
|
||||
--side-icon-act: #32C8A9; /* Bright Teal — active icon · 8.15:1 */
|
||||
--side-hover: #0A2A22;
|
||||
--side-border: #1A3A30;
|
||||
|
||||
/* === 14 СТАТУСОВ ВОРОНКИ (OKLCH, ΔE2000 ≥ 10.57) === */
|
||||
--st-new-tint: #FFD8CF; --st-new-solid: #B94837; /* Новая */
|
||||
--st-work-tint: #FFDBC4; --st-work-solid: #B35100; /* В работе */
|
||||
--st-call-tint: #F6E2BC; --st-call-solid: #9A6700; /* Дозвон */
|
||||
--st-nocall-tint: #E9E8BD; --st-nocall-solid: #7E7500; /* Не дозвон. */
|
||||
--st-neg-tint: #D9EDC6; --st-neg-solid: #538200; /* Переговоры */
|
||||
--st-quote-tint: #C7F0D7; --st-quote-solid: #008A4D; /* КП отправлено */
|
||||
--st-think-tint: #BCF1E9; --st-think-solid: #008C7E; /* Думает */
|
||||
--st-wait-tint: #BAF0F6; --st-wait-solid: #00889B; /* Ждёт оплату */
|
||||
--st-paid-tint: #C0ECFF; --st-paid-solid: #007EB8; /* Оплачено */
|
||||
--st-refund-tint: #D1E5FF; --st-refund-solid: #406DC8; /* Возврат */
|
||||
--st-fail-tint: #E1E0FF; --st-fail-solid: #6C60C4; /* Отказ */
|
||||
--st-dup-tint: #F2DAFF; --st-dup-solid: #9052AE; /* Дубликат */
|
||||
--st-spam-tint: #FFD7EE; --st-spam-solid: #AA4788; /* Спам */
|
||||
--st-arch-tint: #FFD6DD; --st-arch-solid: #B7445F; /* Архив */
|
||||
|
||||
/* === РАДИУСЫ === */
|
||||
--r-xs: 4px; /* мелкие пилы (chip count, kbd, mini-badges) */
|
||||
--r-sm: 6px; /* кнопки, инпуты, нав-айтемы */
|
||||
--r-md: 10px; /* карточки, секции, табли */
|
||||
--r-lg: 14px; /* hero-карточки, лендинг-блоки */
|
||||
/* radius для аватара/чипа — 50% или 100px */
|
||||
|
||||
/* === ШРИФТЫ === */
|
||||
--font-ui: 'Inter', system-ui, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Consolas, monospace;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.1. Тени — НЕ используем
|
||||
|
||||
**v8 Forest стилистически отказывается от теней.** Глубина обеспечивается **только 1px hairline borders**. Исключение — slide-over drawer (нужен лёгкий left-edge shadow для иерархии слоёв):
|
||||
|
||||
```css
|
||||
.drawer { box-shadow: -16px 0 40px rgba(10,19,25,0.10); }
|
||||
```
|
||||
|
||||
Если разработчик сомневается — **не добавлять `box-shadow`**, использовать `border: 1px solid var(--hairline)`.
|
||||
|
||||
### 3.2. Spacing scale (4px-base)
|
||||
|
||||
| Token | Value | Применение |
|
||||
|---|---|---|
|
||||
| 4px | xs | плотные внутренние gap |
|
||||
| 8px | sm | gap между кнопками в группе |
|
||||
| 12px | md | padding внутри карточек |
|
||||
| 16px | lg | gap между элементами секции |
|
||||
| 20-24px | xl | padding section, gap между секциями |
|
||||
| 32px | 2xl | page padding desktop |
|
||||
| 48-64px | 3xl | hero padding |
|
||||
|
||||
---
|
||||
|
||||
## 4. Типографика
|
||||
|
||||
### 4.1. Шрифты (Google Fonts)
|
||||
|
||||
```html
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
```
|
||||
|
||||
**Inter с осью `opsz`** — это Inter 4.0+, один файл с двумя «режимами»:
|
||||
- `font-variation-settings: 'opsz' 14` — UI-режим (мелкий текст)
|
||||
- `font-variation-settings: 'opsz' 28` — display-режим (заголовки)
|
||||
|
||||
Подключается через CSS variable:
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: var(--font-ui);
|
||||
font-feature-settings: 'cv11', 'ss01', 'ss03'; /* slashed-zero, single-storey a */
|
||||
font-variation-settings: 'opsz' 14;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
font-variation-settings: 'opsz' 28; /* visual switch to display */
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2. Шкала размеров
|
||||
|
||||
| Применение | font-size | weight | opsz | letter-spacing | line-height |
|
||||
|---|---|---|---|---|---|
|
||||
| Hero h1 (лендинг) | 56px | 600 | 32 | -0.03em | 1.05 |
|
||||
| Page title | 28px | 600 | 28 | -0.02em | 1.1 |
|
||||
| Section h2 | 22px | 600 | 24 | -0.018em | 1.15 |
|
||||
| Card title | 16px | 600 | 18 | -0.012em | 1.3 |
|
||||
| Body | 13-14px | 400 | 14 | -0.005em | 1.5 |
|
||||
| Eyebrow / label | 11.5px | 500 | 14 | letter-spacing 0.005em | 1.4 |
|
||||
| Mono numeric | 12.5px | 500 | — | -0.005em | 1.4 |
|
||||
| Caption | 11px | 500 | 14 | 0.005em | 1.4 |
|
||||
|
||||
### 4.3. Numeric (числа = деньги)
|
||||
|
||||
**Везде где число — `var(--font-mono)` + `font-feature-settings: 'tnum'`.** Это даёт tabular figures (одинаковая ширина цифр), без чего ровный right-align в таблицах ломается.
|
||||
|
||||
```css
|
||||
.price, .deal-price, .balance-amount, .kpi-value, .tx-amount, .timer {
|
||||
font-family: var(--font-mono);
|
||||
font-feature-settings: 'tnum';
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Компонентная библиотека
|
||||
|
||||
### 5.1. AppShell — общий каркас (sidebar + topbar)
|
||||
|
||||
Sidebar 232px sticky слева на тёмном `--side-bg`, topbar 48px sticky сверху, контент в центре. На viewport ≤ 1100px sidebar сжимается до 56px (icon-only). На ≤ 768px sidebar скрывается.
|
||||
|
||||
Живой пример: открой [`v8_dashboard.html`](../concepts/v8_dashboard.html), изучи `.app`, `.side`, `.main`, `.topbar`.
|
||||
|
||||
### 5.2. Sidebar nav-item
|
||||
|
||||
```html
|
||||
<a class="nav-item" :class="{ active: isActive }" :aria-current="isActive ? 'page' : null" :href="route">
|
||||
<svg class="nav-icon" ... aria-hidden="true"><!-- 15px stroke 1.6 --></svg>
|
||||
<span class="nav-text">{{ label }}</span>
|
||||
<span v-if="count" class="nav-count">{{ count }}</span>
|
||||
</a>
|
||||
```
|
||||
|
||||
CSS правила см. в `v8_dashboard.html` `.nav-item` block. Selected state = solid `--side-active` fill, no border, no glow. Active icon — `--side-icon-act` (bright Teal).
|
||||
|
||||
### 5.3. Button
|
||||
|
||||
```html
|
||||
<button type="button" class="btn">Базовая</button>
|
||||
<button type="button" class="btn btn-primary">Primary (Teal)</button>
|
||||
<button type="button" class="btn btn-danger">Danger</button>
|
||||
```
|
||||
|
||||
Высоты:
|
||||
- 30px — icon-only
|
||||
- 32px — компактные / topbar / в фильтрах
|
||||
- 34-36px — inline в формах
|
||||
- 38-42px — primary CTA
|
||||
|
||||
Variants:
|
||||
- `.btn` — surface + hairline border
|
||||
- `.btn-primary` — accent fill, white text
|
||||
- `.btn-danger` — `--st-new-solid` text + hover `#FFE7E2` bg
|
||||
|
||||
### 5.4. Status Chip — 14 вариантов
|
||||
|
||||
Два режима:
|
||||
|
||||
**1. Plain (dot + text, без фона)** — для таблиц, плотных строк:
|
||||
```html
|
||||
<span class="chip chip-paid">
|
||||
<span class="dot" aria-hidden="true"></span>Оплачено
|
||||
</span>
|
||||
```
|
||||
|
||||
**2. Tinted (с фоном)** — для emphasis (drawer current status, dashboard summary):
|
||||
```html
|
||||
<span class="chip chip-tinted chip-paid">
|
||||
<span class="dot" aria-hidden="true"></span>Оплачено
|
||||
</span>
|
||||
```
|
||||
|
||||
Все 14 классов: `chip-new`, `chip-work`, `chip-call`, `chip-nocall`, `chip-neg`, `chip-quote`, `chip-think`, `chip-wait`, `chip-paid`, `chip-refund`, `chip-fail`, `chip-dup`, `chip-spam`, `chip-arch`. Цвета — в [§3 токены](#3-дизайн-токены--css-variables).
|
||||
|
||||
### 5.5. Avatar (concentric ring)
|
||||
|
||||
```html
|
||||
<span class="avatar" aria-hidden="true">
|
||||
ИП <!-- 2 letter initials -->
|
||||
</span>
|
||||
```
|
||||
|
||||
```css
|
||||
.avatar {
|
||||
width: 26px; height: 26px;
|
||||
border-radius: 50%;
|
||||
background: var(--ink); color: #fff; /* dark variant; см. v8_* для tinted */
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 9.5px; font-weight: 600;
|
||||
position: relative;
|
||||
}
|
||||
.avatar::after {
|
||||
content: ''; position: absolute; inset: 0;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(10,19,25,0.10); /* concentric ring trick */
|
||||
pointer-events: none;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.6. Table
|
||||
|
||||
Hairline-only, no shadows. Sticky `thead`. Hover row → `--sunken`. Selected row → solid `--ink` fill, white text.
|
||||
|
||||
См. [`v8_deals.html`](../concepts/v8_deals.html) `.tbl`. На mobile — таблица превращается в карточки.
|
||||
|
||||
### 5.7. Drawer (slide-over)
|
||||
|
||||
460px справа, `--surface` bg, hairline left border + lazy left-edge shadow. Scrim — `rgba(10,19,25,0.18)` + `backdrop-filter: blur(2px)`.
|
||||
|
||||
См. [`v8_deals.html`](../concepts/v8_deals.html) `.drawer`.
|
||||
|
||||
### 5.8. KPI card
|
||||
|
||||
Hairline border, no shadow. Number в JBM mono с tnum. Trend = single Teal up-arrow на положительной дельте, **`--st-new-solid` red** на отрицательной. Никаких icon-чипов.
|
||||
|
||||
См. [`v8_dashboard.html`](../concepts/v8_dashboard.html) `.kpi`.
|
||||
|
||||
### 5.9. Balance widget с runway-scale
|
||||
|
||||
Наследник KPI. Те же hairlines + дополнительная Teal-рамка через `::before`. Внутри — `runway-bar` (7 сегментов hairline-tick'ов, заполненных под уровень дней).
|
||||
|
||||
См. [`v8_dashboard.html`](../concepts/v8_dashboard.html) `.balance` + `.runway`.
|
||||
|
||||
### 5.10. Timeline / Activity Log
|
||||
|
||||
Vertical 1px line + цветные точки по типу события:
|
||||
- `create` — Teal `--accent`
|
||||
- `status-change` — `--st-call-solid` (оранжевый)
|
||||
- `comment` — `--ink` (чёрный)
|
||||
- `call` — `--st-quote-solid` (зелёный)
|
||||
- `tag` — `--st-think-solid` (бирюзовый)
|
||||
- `assign` — `--st-paid-solid` (синий)
|
||||
- `next/dashed` — `--ink-disabled` с `border-style: dashed`
|
||||
|
||||
Описание + старое→новое значение через `<span class="from">old</span> → <span class="to">new</span>` с line-through на `from` и mono font на обоих.
|
||||
|
||||
См. [`v8_deal_card.html`](../concepts/v8_deal_card.html) `.activity`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Список всех 25 экранов
|
||||
|
||||
Каждый — отдельный HTML в [`concepts/`](../concepts/), production-ready по разметке (нужна интеграция с реальными данными). Все прошли axe-core 4.10.2 = **0 violations** на 1680/1440/768/375 (где применимо).
|
||||
|
||||
### 6.1. Кабинет тенанта (8 экранов)
|
||||
|
||||
| ID | URL | HTML | Что внутри |
|
||||
|---|---|---|---|
|
||||
| 01 | `/login`, `/register`, `/2fa`, `/forgot-password` | [v8_login.html](../concepts/v8_login.html) | 5 состояний: login/register с zxcvbn-индикатором/2FA с 6-cell кодом/forgot/recovery с 8 одноразовыми кодами |
|
||||
| 02 | `/dashboard` | [v8_dashboard.html](../concepts/v8_dashboard.html) | KPI strip + Hero balance + Activity chart + Funnel-bar + Recent deals + System health + Quick actions |
|
||||
| 03 | `/deals` | [v8_deals.html](../concepts/v8_deals.html) | Таблица 14 строк × 14 status-chips, фильтры (search + selects + density toggle), drawer открыт статически |
|
||||
| 04 | `/deals/{id}` | [v8_deal_card.html](../concepts/v8_deal_card.html) | Hero + параметры + комментарий с 8 шаблонами + напоминание + footer-actions; aside: менеджер + метрики + history |
|
||||
| 05 | `/deals/kanban` | [v8_kanban.html](../concepts/v8_kanban.html) | 14 колонок DnD (262px каждая), карточки с иконками 🔥/⏰/✓ |
|
||||
| 06 | `/billing` | [v8_billing.html](../concepts/v8_billing.html) | Кошелёк ₽ + ГЦК + тариф; pending-banner; история транзакций; счета и УПД (1С 8.3 XML) |
|
||||
| 07 | `/settings` | [v8_settings.html](../concepts/v8_settings.html) | 8 вкладок (Профиль/Безопасность/Проекты/Команда/API/Интеграции/Тихие часы/Уведомления); 3 раскрыты |
|
||||
| 08 | `/reports` | [v8_reports.html](../concepts/v8_reports.html) | Форма запроса + список jobs (done/running с progressbar/queued/failed + retry); квота 2/3 |
|
||||
|
||||
### 6.2. Экраны ошибок (3)
|
||||
|
||||
Все в одном файле с review-tabs: [v8_errors.html](../concepts/v8_errors.html)
|
||||
|
||||
| ID | URL | Особенности |
|
||||
|---|---|---|
|
||||
| 09 | 404 | Декоративный SVG с маркером, кнопки «На дашборд» / «Назад» |
|
||||
| 10 | 403 | Replace-key SVG, `REQ-id` для копирования, mailto support |
|
||||
| 11 | 500/503 | Status-list (API/Telegram/YooKassa OK/деградация), `INC-id`, ссылка на status page |
|
||||
|
||||
### 6.3. Админка SaaS (6)
|
||||
|
||||
Все в одном файле: [v8_admin.html](../concepts/v8_admin.html). Отдельный домен `admin.liderra.app`. Тёмная плашка `ADMIN` рядом с лого.
|
||||
|
||||
| ID | URL | Что показано |
|
||||
|---|---|---|
|
||||
| 12 | `/admin/login` | SSO Yandex 360 primary + локальный 2FA fallback |
|
||||
| 13 | `/admin/tenants` | Список 142 тенанта, фильтры, статус-чипы (active/trial/overdue/suspended) |
|
||||
| 14 | `/admin/tenants/{id}` | Карточка тенанта: профиль, баланс, действия (impersonation, корректировка, удаление 152-ФЗ), журнал |
|
||||
| 15 | Impersonation-режим | Красный sticky banner + специальная аватарка `Админ → ИП` + крест выхода |
|
||||
| 16 | `/admin/incidents` | Журнал HIGH/MED/LOW, read-only для compliance |
|
||||
| 17 | `/admin/system` | 6 sys-cards: system_settings / tariff_plans / legal_entities / payment_gateways / webhook_log / auth_log |
|
||||
|
||||
### 6.4. Лендинг и юр. (4)
|
||||
|
||||
Все в одном файле: [v8_landing.html](../concepts/v8_landing.html)
|
||||
|
||||
| ID | URL | Особенности |
|
||||
|---|---|---|
|
||||
| 18 | `/` | Hero с 3 hi-cards справа + 6 features + 4 tariffs + FAQ + footer (содержимое — Lorem ipsum) |
|
||||
| 19 | `/pricing` | Только tariffs (4 карточки: Start/Basic/Команда POPULAR/Enterprise) |
|
||||
| 20 | `/legal/offer` | TOC + 8 разделов оферты (шаблон Прил. Ж) |
|
||||
| 21 | `/legal/privacy` | TOC + 10 разделов 152-ФЗ Politики |
|
||||
|
||||
### 6.5. Бренд-документация
|
||||
|
||||
| ID | Файл | Что |
|
||||
|---|---|---|
|
||||
| BR-1 | [v8_brand.html](../concepts/v8_brand.html) | 10 имён + 5 знаков + 3 favourite combo. Архив выбора. |
|
||||
| BR-2 | [v8_palette_options.html](../concepts/v8_palette_options.html) | 5 палитр на выбор (А Graphite/Б Forest/В Slate-Blue/Г Champagne/Д Inverted Hero). Архив. |
|
||||
|
||||
---
|
||||
|
||||
## 7. Соглашения по доступности (WCAG 2.1 AA)
|
||||
|
||||
### 7.1. Что обязательно
|
||||
|
||||
- **Все интерактивные** (`<a>`, `<button>`, `<input>`, `<select>`, `<textarea>`, `[tabindex]`) имеют `:focus-visible` с outline:
|
||||
```css
|
||||
a:focus-visible, button:focus-visible, input:focus-visible, [tabindex]:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--r-sm);
|
||||
}
|
||||
```
|
||||
- **Все SVG-иконки** — `aria-hidden="true"` (декоративные)
|
||||
- **Все icon-only кнопки** — `aria-label="..."`
|
||||
- **Все form-inputs** — `<label for="...">` или `aria-label`
|
||||
- **Selection state** — solid fill (см. row.selected на dark `--ink`), не tint
|
||||
- **`role="tablist"`** требует все children = `<button role="tab">` (не просто `<button>`)
|
||||
- **`prefers-reduced-motion`** — выключает анимации scoped через class `.has-motion`, не через `*`:
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.has-motion { animation: none !important; transition: none !important; }
|
||||
}
|
||||
```
|
||||
- **Skip link** на каждой странице для клавиатурной навигации:
|
||||
```html
|
||||
<a href="#main" class="skip-link">К контенту</a>
|
||||
```
|
||||
|
||||
### 7.2. Контраст — проверка перед мержем
|
||||
|
||||
Перед каждым PR прогнать через [axe DevTools](https://www.deque.com/axe/devtools/) или Playwright + axe-core 4.10.2. **0 violations — обязательное условие** для merge.
|
||||
|
||||
### 7.3. Известные «проблемные» комбинации
|
||||
|
||||
Эти пары давали contrast violations в процессе и были исправлены — повторять НЕ нужно:
|
||||
|
||||
| Не делать | Контраст | Решение |
|
||||
|---|---|---|
|
||||
| `--ink-disabled` на `--surface` для текста ≥9px | 3.18:1 | `--ink-3` (5.42:1) |
|
||||
| `--st-quote-solid` (#008A4D) на белом для 11.5px text | 4.36:1 | #006A3B + bold (4.5:1+) |
|
||||
| Текст 10px на solid `--st-neg-solid` / `--st-quote-solid` / `--st-paid-solid` | 4.14-4.33:1 | Убрать текст из solid-сегмента, перенести в подпись |
|
||||
| `aria-hidden="true"` на `<aside>` с фокусируемыми ссылками | aria-hidden-focus | Заменить на `aria-label="..."` |
|
||||
| `<div>` с `aria-label` без role | aria-prohibited-attr | Добавить `role="progressbar"` / `role="img"` |
|
||||
| 5 tabs в `display:flex` без `flex-wrap` на mobile | Overflow → axe не видит true bg | Добавить `flex-wrap: wrap` |
|
||||
|
||||
---
|
||||
|
||||
## 8. Vue 3 + Vuetify 3 — практические подсказки
|
||||
|
||||
### 8.1. Где брать Vuetify-компонент vs кастомный
|
||||
|
||||
| Vuetify-готовый | Полу-кастомный | Полностью кастомный |
|
||||
|---|---|---|
|
||||
| `v-text-field`, `v-select`, `v-checkbox`, `v-snackbar`, `v-dialog`, `v-menu` | `v-data-table-server` (с правкой стилей под наши токены) | StatusChip с 14 вариантами, BalanceWidget с runway, Timeline, Sidebar nav |
|
||||
|
||||
### 8.2. Vuetify тема через токены
|
||||
|
||||
В `vuetify.options.ts`:
|
||||
|
||||
```ts
|
||||
import 'vuetify/styles';
|
||||
export default {
|
||||
theme: {
|
||||
defaultTheme: 'liderra',
|
||||
themes: {
|
||||
liderra: {
|
||||
dark: false,
|
||||
colors: {
|
||||
primary: '#0F6E56',
|
||||
'on-primary': '#FFFDFA',
|
||||
surface: '#FFFDFA',
|
||||
background: '#F6F3EC',
|
||||
error: '#B94837',
|
||||
warning: '#9A6700',
|
||||
success: '#008A4D',
|
||||
info: '#007EB8',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Дополнительно в `assets/tokens.css` подключить ВСЕ переменные из [§3](#3-дизайн-токены--css-variables) — Vuetify не покрывает status-палитру, sidebar tokens и др.
|
||||
|
||||
### 8.3. Шаблон страницы
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<AppShell>
|
||||
<template #topbar-crumb>
|
||||
<span>Рабочая область</span>
|
||||
<ChevronIcon />
|
||||
<strong>{{ pageTitle }}</strong>
|
||||
</template>
|
||||
<main id="main" class="content">
|
||||
<PageHeader :title="pageTitle" :stats="pageStats" />
|
||||
<!-- секции -->
|
||||
</main>
|
||||
</AppShell>
|
||||
</template>
|
||||
```
|
||||
|
||||
`AppShell` инкапсулирует `.app + .side + .topbar + .main`. Детальная реализация — отдельный issue.
|
||||
|
||||
---
|
||||
|
||||
## 9. Известные правила проекта (нельзя оспаривать)
|
||||
|
||||
Из брифа `01_Scope_i_ekrany.md` §6:
|
||||
|
||||
- **Тёмная тема** — отложена на v2 (CSS-vars для будущей dark theme заложены, но макетов нет)
|
||||
- **2FA** — на всех тарифах, включая бесплатный
|
||||
- **Минимум 100 ₽** пополнения, округление **вниз** при ₽→лиды
|
||||
- **Pending-платеж** — нет кнопки «Отменить» (CTO-11), самовосстановление 30 минут
|
||||
- **Click-wrap при регистрации** — 3 чекбокса (оферта / ПДн / маркетинг-опционально)
|
||||
- **Yandex Cloud + Yandex 360 SSO** для админки
|
||||
- **JivoSite** для helpdesk-чата (Биз-5)
|
||||
- **Webhook outbound** — primary integration option, REST API на втором месте
|
||||
- **amoCRM** — спринт 14-15, Bitrix24/RetailCRM — Post-MVP
|
||||
|
||||
---
|
||||
|
||||
## 10. Что точно НЕ делать на MVP
|
||||
|
||||
- Темная тема UI
|
||||
- Wizard «OSINT-рекомендации источников» (Биз-15)
|
||||
- Телефонная интеграция (UI VoIP) — Биз-12
|
||||
- Промокоды и реферальная программа — Б-6
|
||||
- Tab «Удалённые проекты» с покупкой обратно — Биз-14
|
||||
- Bitrix24 / RetailCRM коннекторы — только amoCRM
|
||||
- Кнопка «Отменить» pending-платежа — CTO-11
|
||||
- Кнопка «Отменить подписку» — уход через смену тарифа на Start (Биз-2)
|
||||
|
||||
---
|
||||
|
||||
## 11. API-контракт (намёки для backend)
|
||||
|
||||
Это не полный контракт (он out-of-scope этого документа), но есть конкретные требования из дизайна:
|
||||
|
||||
### 11.1. WebSocket / SSE для real-time
|
||||
|
||||
Дашборд показывает «LIVE» индикатор на balance widget — это требует push-канала. Минимум: `lead.created`, `payment.completed`, `balance.updated`.
|
||||
|
||||
### 11.2. Activity Log
|
||||
|
||||
Каждое изменение поля карточки сделки — запись в `deal_activity_log` с полями:
|
||||
```typescript
|
||||
{
|
||||
id: string,
|
||||
deal_id: string,
|
||||
actor_id: string | null, // менеджер или null = system
|
||||
type: 'create'|'status-change'|'comment'|'call'|'tag'|'assign'|'reminder',
|
||||
timestamp: ISO8601,
|
||||
payload: {
|
||||
field?: string, // 'status' | 'manager_id' | ...
|
||||
from?: any, // old value
|
||||
to?: any, // new value
|
||||
text?: string, // for comments
|
||||
duration_sec?: number, // for calls
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 11.3. Saas Admin Audit Log
|
||||
|
||||
Согласно бриф §23.10 + дизайн админки — каждое действие админа пишется в `saas_admin_audit_log`. Impersonation-режим имеет **обязательное поле `reason`** при входе.
|
||||
|
||||
### 11.4. RLS для multi-tenant
|
||||
|
||||
Каждый запрос API проверяет `tenant_id` через RLS на стороне БД. Bypass attempt = инцидент HIGH severity (см. админка/Incidents).
|
||||
|
||||
### 11.5. Webhook signatures
|
||||
|
||||
Outbound webhook подписывается **HMAC SHA-256** в заголовке `X-Liderra-Sign`. Документация для клиентского интегратора — отдельный документ.
|
||||
|
||||
### 11.6. Status проекта
|
||||
|
||||
```
|
||||
GET /status → { api: 'OK' | 'degraded' | 'down', integrations: [...] }
|
||||
```
|
||||
|
||||
Используется на 500-странице.
|
||||
|
||||
---
|
||||
|
||||
## 12. Тестирование
|
||||
|
||||
### 12.1. axe-core 4.10.2 — обязательно
|
||||
|
||||
Прогон через CI на каждом PR. Pseudo-pipeline:
|
||||
|
||||
```bash
|
||||
playwright test --headed
|
||||
# в playwright тесте:
|
||||
await page.evaluate(() => axe.run(document, { runOnly: ['wcag2a','wcag2aa','wcag21aa'] }))
|
||||
# violations.length должен быть 0
|
||||
```
|
||||
|
||||
### 12.2. Viewport coverage
|
||||
|
||||
Все экраны проверять как минимум на 4 viewports:
|
||||
- **1680×1050** — desktop large
|
||||
- **1440×900** — desktop стандарт
|
||||
- **768×1024** — tablet
|
||||
- **375×812** — mobile (iPhone X reference)
|
||||
|
||||
### 12.3. Reduced motion
|
||||
|
||||
Включить `prefers-reduced-motion: reduce` в DevTools → проверить, что декоративные анимации (pulse, slide) выключены, но focus rings и hover transitions работают.
|
||||
|
||||
### 12.4. Keyboard-only навигация
|
||||
|
||||
Tab через всю страницу — все интерактивные элементы должны иметь видимый focus ring.
|
||||
|
||||
---
|
||||
|
||||
## 13. Полезные команды для разработчика
|
||||
|
||||
### 13.1. Локальный preview макетов
|
||||
|
||||
```bash
|
||||
cd дизайн
|
||||
python -m http.server 8765
|
||||
# открыть http://localhost:8765/concepts/v8_dashboard.html
|
||||
```
|
||||
|
||||
### 13.2. Прогон axe для проверки
|
||||
|
||||
```javascript
|
||||
// В DevTools console на любой странице:
|
||||
const s = document.createElement('script');
|
||||
s.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';
|
||||
document.head.appendChild(s);
|
||||
s.onload = async () => {
|
||||
const r = await axe.run(document, { runOnly: ['wcag2a','wcag2aa','wcag21aa'] });
|
||||
console.log('Violations:', r.violations.length, r.violations);
|
||||
console.log('Passes:', r.passes.length);
|
||||
};
|
||||
```
|
||||
|
||||
### 13.3. OKLCH палитра — ещё пары
|
||||
|
||||
Если нужно добавить новый статус — использовать [`palette_14.py`](../palette_14.py) с другим `start_hue` и repulsion-итерацией. **min ΔE2000 ≥ 10** обязательно.
|
||||
|
||||
```bash
|
||||
cd дизайн
|
||||
PYTHONIOENCODING=utf-8 python palette_14.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. FAQ
|
||||
|
||||
**Q: Можно ли использовать Tailwind вместо чистого CSS?**
|
||||
A: Можно, но токены из [§3](#3-дизайн-токены--css-variables) должны попасть в `tailwind.config.ts`. Проще импортировать tokens.css глобально и использовать классы из v8_*.html напрямую (они стабильные).
|
||||
|
||||
**Q: Что делать, если нужен новый статус (15-й)?**
|
||||
A: Перезапустить `palette_14.py` с N=15, ΔE2000 пересчитается. Все 14 существующих останутся, новый встанет в свободное место hue-кругa. Дизайнер должен утвердить (новый статус = новый workflow в БД).
|
||||
|
||||
**Q: Можно ли поменять Teal `#0F6E56` на синий?**
|
||||
A: НЕТ без явного OK заказчика — это брендовый цвет (зафиксирован Диз-2). Если нужно — сначала переоткрыть бренд-решение.
|
||||
|
||||
**Q: ApexCharts не дружит с opsz axis Inter?**
|
||||
A: Использовать font-family fallback: ApexCharts читает `font-family: 'Inter'` без opsz, выглядит чуть-чуть толще, но не критично.
|
||||
|
||||
**Q: Как повторить OKLCH-расчёт?**
|
||||
A: Python 3.12+ с `pip install colour-science numpy`. Скрипты в `palette_*.py` в корне `дизайн/`.
|
||||
|
||||
**Q: Где живёт реальный source of truth для брендбука?**
|
||||
A: Этот файл (`docs/DEVELOPER_HANDOFF.md`). Файл `дизайн/02_Brand_i_A11y.md` устарел (там про v1.1 «Лидпоток»).
|
||||
|
||||
---
|
||||
|
||||
## 15. Контакты
|
||||
|
||||
| Роль | Контакт |
|
||||
|---|---|
|
||||
| Дизайнер | `kpd9363@gmail.com` (Платон) |
|
||||
| Поддержка дизайна | через данный handoff + `concepts/v8_*.html` |
|
||||
| Заказчик | Дмитрий |
|
||||
|
||||
---
|
||||
|
||||
## 16. Изменения
|
||||
|
||||
| Версия | Дата | Что изменилось |
|
||||
|---|---|---|
|
||||
| v8 Forest | 2026-05-07 | Полная переработка: Лидерра, Forest палитра, L-Square лого, 25 макетов |
|
||||
| v7 cool-tech | 2026-05-07 | Деприкейтнут (cool-grey слишком белый, sidebar сливался) |
|
||||
| v4-v6 | до 2026-05-07 | Архивы итераций — не использовать |
|
||||
|
||||
---
|
||||
|
||||
*Конец `DEVELOPER_HANDOFF.md` v8 Forest.*
|
||||
@@ -0,0 +1,569 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Лидерра · Developer Hub</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C; --ink-disabled:#92907B;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-bg:#012019; --side-icon-act:#32C8A9;
|
||||
--st-quote:#008A4D; --st-paid:#007EB8; --st-call:#9A6700; --st-new:#B94837; --st-fail:#6C60C4;
|
||||
--r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font-family: inherit; }
|
||||
a:focus-visible, button:focus-visible {
|
||||
outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm);
|
||||
}
|
||||
|
||||
/* Top header (dark) */
|
||||
.head {
|
||||
background: var(--side-bg);
|
||||
color: #fff;
|
||||
padding: 28px 32px 32px;
|
||||
}
|
||||
.head-inner { max-width: 1280px; margin: 0 auto; }
|
||||
.brand { display:flex; align-items:center; gap:10px; font-weight:600; font-size:18px; letter-spacing:-0.012em; font-variation-settings:'opsz' 18; }
|
||||
.brand .mark { width:28px; height:28px; border-radius:6px; background:#fff; display:inline-flex; align-items:center; justify-content:center; overflow:hidden; }
|
||||
.brand .mark svg { width:100%; height:100%; display:block; }
|
||||
.brand .dot { color:var(--side-icon-act); }
|
||||
.brand .tag { font-family:var(--font-mono); font-size:10px; font-weight:600; letter-spacing:0.06em; padding:3px 7px; border-radius:3px; background:rgba(50,200,169,0.15); color:var(--side-icon-act); margin-left:8px; }
|
||||
.head h1 { font-size:36px; font-weight:600; font-variation-settings:'opsz' 32; letter-spacing:-0.022em; line-height:1.1; margin:18px 0 10px; max-width:760px; }
|
||||
.head p { font-size:14.5px; color:#B1C2BD; line-height:1.55; margin:0; max-width:680px; letter-spacing:-0.005em; }
|
||||
.head-meta { font-family:var(--font-mono); font-size:11.5px; color:#7A8C87; letter-spacing:-0.005em; margin-top:18px; display:flex; gap:14px; flex-wrap:wrap; }
|
||||
.head-meta .num { color: var(--side-icon-act); font-weight: 600; }
|
||||
|
||||
/* Quick links bar */
|
||||
.quicklinks { background:#0A2A22; border-top:1px solid #1A3A30; padding: 12px 32px; }
|
||||
.quicklinks-inner { max-width: 1280px; margin: 0 auto; display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
|
||||
.quicklinks .label { font-family:var(--font-mono); font-size:10.5px; color:#A0B5AE; letter-spacing:0.06em; font-weight:700; }
|
||||
.quicklinks a { font-size: 12.5px; color: #B1C2BD; padding: 5px 10px; border-radius: 4px; transition: background 100ms; }
|
||||
.quicklinks a:hover { background: rgba(255,255,255,0.06); color: #fff; }
|
||||
|
||||
/* Main */
|
||||
.wrap { max-width: 1280px; margin: 0 auto; padding: 40px 32px 80px; }
|
||||
.section-h { margin: 36px 0 18px; display: flex; align-items: baseline; justify-content: space-between; gap: 14px; }
|
||||
.section-h:first-child { margin-top: 0; }
|
||||
.section-h h2 { font-size: 22px; font-weight: 600; font-variation-settings:'opsz' 24; letter-spacing:-0.018em; margin: 0; }
|
||||
.section-h .label { font-family:var(--font-mono); font-size:10.5px; font-weight:600; letter-spacing:0.06em; color:var(--accent); }
|
||||
|
||||
/* Card grid */
|
||||
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
|
||||
.grid.col2 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid.col4 { grid-template-columns: repeat(4, 1fr); }
|
||||
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
padding: 18px 20px;
|
||||
display: flex; flex-direction: column; gap: 6px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: border-color 120ms ease, transform 120ms ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
.card:hover { border-color: var(--ink-disabled); transform: translateY(-1px); }
|
||||
.card .num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10.5px; font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--ink-3);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.card .name {
|
||||
font-size: 16px; font-weight: 600;
|
||||
font-variation-settings:'opsz' 18;
|
||||
letter-spacing: -0.012em;
|
||||
margin-top: 4px;
|
||||
color: var(--ink);
|
||||
}
|
||||
.card .desc {
|
||||
font-size: 12.5px;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.5;
|
||||
letter-spacing: -0.005em;
|
||||
margin-top: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
.card .open {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
margin-top: 8px;
|
||||
color: var(--accent);
|
||||
font-size: 12px; font-weight: 500;
|
||||
}
|
||||
.card .open svg { width: 11px; height: 11px; stroke-width: 1.7; }
|
||||
|
||||
/* Doc cards (markdown) */
|
||||
.card.doc { border-left: 3px solid var(--accent); padding-left: 17px; }
|
||||
|
||||
/* Status pill on cards */
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 100px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px; font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
width: fit-content;
|
||||
}
|
||||
.pill.ready { background: var(--accent-tint); color: var(--accent-deep); }
|
||||
.pill.ready::before { content: '●'; color: var(--accent); margin-right: 2px; }
|
||||
|
||||
/* Brand & tokens preview blocks */
|
||||
.tokens {
|
||||
background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md);
|
||||
padding: 22px 24px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12.5px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.tokens pre { margin: 0; line-height: 1.6; color: var(--ink-2); white-space: pre; }
|
||||
.tokens .v { color: var(--accent); font-weight: 600; }
|
||||
.tokens .c { color: var(--ink-3); font-style: normal; }
|
||||
|
||||
/* Palette swatches */
|
||||
.swatches { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
|
||||
.sw { display: flex; flex-direction: column; gap: 4px; }
|
||||
.sw .chip { height: 36px; border-radius: var(--r-sm); border: 1px solid var(--hairline-soft); }
|
||||
.sw .label { font-size: 9.5px; color: var(--ink-3); font-family: var(--font-mono); font-feature-settings:'tnum'; letter-spacing:-0.005em; text-align:center; }
|
||||
|
||||
/* 14 status grid */
|
||||
.statuses { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; }
|
||||
.st-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 10px 12px; display: flex; flex-direction: column; gap: 5px; }
|
||||
.st-card .top { display: flex; align-items: center; gap: 7px; font-size: 11.5px; font-weight: 500; color: var(--ink); letter-spacing: -0.005em; }
|
||||
.st-card .dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; position: relative; }
|
||||
.st-card .dot::after { content:''; position:absolute; inset:-1px; border-radius:50%; border:1px solid rgba(10,19,25,0.10); }
|
||||
.st-card .hex { font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); letter-spacing:-0.005em; }
|
||||
|
||||
/* Brand display */
|
||||
.brand-row {
|
||||
display: grid;
|
||||
grid-template-columns: 280px 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
.brand-render {
|
||||
background: var(--side-bg);
|
||||
border-radius: var(--r-md);
|
||||
padding: 36px 24px;
|
||||
display: flex; flex-direction: column; align-items: center; gap: 14px;
|
||||
}
|
||||
.brand-render .big-mark { width: 80px; height: 80px; border-radius: 18px; background: #fff; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; }
|
||||
.brand-render .big-mark svg { width: 100%; height: 100%; }
|
||||
.brand-render .wm { font-size: 28px; font-weight: 600; color: #fff; font-variation-settings:'opsz' 28; letter-spacing: -0.022em; }
|
||||
.brand-render .wm .dot { color: var(--side-icon-act); }
|
||||
.brand-info {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
padding: 22px 24px;
|
||||
}
|
||||
.brand-info dl { display: grid; grid-template-columns: 140px 1fr; row-gap: 10px; column-gap: 18px; margin: 0; font-size: 12.5px; }
|
||||
.brand-info dt { color: var(--ink-3); font-size: 11px; font-weight: 500; padding-top: 2px; letter-spacing: -0.005em; }
|
||||
.brand-info dd { margin: 0; color: var(--ink); letter-spacing: -0.005em; }
|
||||
.brand-info dd.mono { font-family: var(--font-mono); font-feature-settings:'tnum'; font-size: 12px; }
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.grid { grid-template-columns: 1fr 1fr; }
|
||||
.grid.col4 { grid-template-columns: 1fr 1fr; }
|
||||
.swatches { grid-template-columns: repeat(4, 1fr); }
|
||||
.statuses { grid-template-columns: repeat(3, 1fr); }
|
||||
.brand-row { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.head { padding: 22px 18px 24px; }
|
||||
.head h1 { font-size: 28px; }
|
||||
.quicklinks { padding: 10px 18px; }
|
||||
.wrap { padding: 28px 18px 60px; }
|
||||
.grid, .grid.col2, .grid.col4 { grid-template-columns: 1fr; }
|
||||
.swatches, .statuses { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="head">
|
||||
<div class="head-inner">
|
||||
<a href="#" class="brand">
|
||||
<span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span style="color:#fff">Лидерра<span class="dot">.</span></span>
|
||||
<span class="tag">DEV HUB</span>
|
||||
</a>
|
||||
<h1>Всё, что нужно разработчику для портала Лидерра.</h1>
|
||||
<p>Дизайн-система v8 Forest, 25 живых HTML-макетов, токены, компонентная библиотека и полный handoff. Production-ready по разметке, нужна только интеграция с реальными данными.</p>
|
||||
<div class="head-meta">
|
||||
<span><span class="num">25</span> макетов</span>
|
||||
<span><span class="num">0</span> axe violations · WCAG 2.1 AA</span>
|
||||
<span><span class="num">14</span> OKLCH-статусов · ΔE2000 ≥ 10.57</span>
|
||||
<span><span class="num">4</span> viewports проверено · 1680/1440/768/375</span>
|
||||
<span>v8 Forest · 2026-05-07</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="quicklinks" aria-label="Быстрые ссылки">
|
||||
<div class="quicklinks-inner">
|
||||
<span class="label">QUICK LINKS</span>
|
||||
<a href="DEVELOPER_HANDOFF.md">📘 Полный handoff (markdown)</a>
|
||||
<a href="BRANDBOOK_v2.md">🎨 Brandbook v2</a>
|
||||
<a href="#screens">25 экранов</a>
|
||||
<a href="#tokens">CSS токены</a>
|
||||
<a href="#statuses">14 статусов</a>
|
||||
<a href="../palette_v7.py">palette_v7.py</a>
|
||||
<a href="../palette_14.py">palette_14.py</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="wrap">
|
||||
|
||||
<!-- ===== BRAND ===== -->
|
||||
<header class="section-h">
|
||||
<h2>Бренд</h2>
|
||||
<span class="label">01 · brand</span>
|
||||
</header>
|
||||
<div class="brand-row">
|
||||
<div class="brand-render">
|
||||
<span class="big-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span class="wm">Лидерра<span class="dot">.</span></span>
|
||||
</div>
|
||||
<div class="brand-info">
|
||||
<dl>
|
||||
<dt>Имя</dt><dd>Лидерра (Liderra)</dd>
|
||||
<dt>Wordmark</dt><dd>Лидерра<span style="color:var(--accent);font-weight:600">.</span> · с Teal-точкой</dd>
|
||||
<dt>Знак</dt><dd>L-Square: белый rounded-square + dark stroke L + Teal-точка</dd>
|
||||
<dt>Tone of voice</dt><dd>Уверенный, технический, прямой про деньги</dd>
|
||||
<dt>Не использовать</dt><dd>«Лидпоток» (старое имя), lowercase «лидерра.»</dd>
|
||||
<dt>Бренд-цвет</dt><dd class="mono">Teal #0F6E56 · oklch(0.46 0.10 175)</dd>
|
||||
<dt>Bright variant</dt><dd class="mono">#32C8A9 · для иконок на тёмном sidebar</dd>
|
||||
<dt>Палитра</dt><dd>Forest: warm ivory main + deep teal-noir sidebar</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== TOKENS ===== -->
|
||||
<header class="section-h" id="tokens">
|
||||
<h2>Дизайн-токены</h2>
|
||||
<span class="label">02 · tokens</span>
|
||||
</header>
|
||||
|
||||
<div style="margin-bottom: 16px">
|
||||
<div style="font-size: 13px; color: var(--ink-2); margin-bottom: 14px; max-width: 760px; line-height: 1.55; letter-spacing: -0.005em">
|
||||
Все цвета через OKLCH с проверкой WCAG AA. Скопировать в <code style="font-family:var(--font-mono);background:var(--sunken);padding:2px 6px;border-radius:3px;font-size:11.5px">assets/tokens.css</code> и подключить глобально. Полный список — в <a href="DEVELOPER_HANDOFF.md#3-дизайн-токены--css-variables" style="color:var(--accent);font-weight:500">handoff §3</a>.
|
||||
</div>
|
||||
|
||||
<div class="tokens" tabindex="0" role="region" aria-label="CSS-токены палитры">
|
||||
<pre>:root {
|
||||
<span class="c">/* Поверхности (Forest light) */</span>
|
||||
--bg: <span class="v">#F6F3EC</span>; <span class="c">/* page bg */</span>
|
||||
--surface: <span class="v">#FFFDFA</span>; <span class="c">/* карточки */</span>
|
||||
--sunken: <span class="v">#F0EDE4</span>;
|
||||
--hairline: <span class="v">#D9D5CD</span>;
|
||||
--hairline-soft: <span class="v">#E8E3D6</span>;
|
||||
|
||||
<span class="c">/* Текст */</span>
|
||||
--ink: <span class="v">#081319</span>; <span class="c">/* 16.96:1 на bg */</span>
|
||||
--ink-2: <span class="v">#343C41</span>; <span class="c">/* 10.34:1 */</span>
|
||||
--ink-3: <span class="v">#66635C</span>; <span class="c">/* 5.42:1 */</span>
|
||||
--ink-disabled: <span class="v">#92907B</span>; <span class="c">/* 3.48:1 — для UI, не для текста */</span>
|
||||
|
||||
<span class="c">/* Бренд */</span>
|
||||
--accent: <span class="v">#0F6E56</span>; <span class="c">/* Teal · 5.94:1 на bg */</span>
|
||||
--accent-tint: <span class="v">#E1EEEA</span>;
|
||||
--accent-deep: <span class="v">#084635</span>;
|
||||
|
||||
<span class="c">/* Sidebar (тёмный) */</span>
|
||||
--side-bg: <span class="v">#012019</span>;
|
||||
--side-text: <span class="v">#B1C2BD</span>; <span class="c">/* 9.23:1 */</span>
|
||||
--side-active: <span class="v">#13382F</span>;
|
||||
--side-icon-act: <span class="v">#32C8A9</span>; <span class="c">/* bright Teal · 8.15:1 */</span>
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 style="font-size:14px;font-weight:600;margin:20px 0 10px;color:var(--ink-2);font-variation-settings:'opsz' 16">Поверхности и текст</h3>
|
||||
<div class="swatches">
|
||||
<div class="sw"><div class="chip" style="background:#F6F3EC"></div><div class="label">bg<br>F6F3EC</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#FFFDFA"></div><div class="label">surface<br>FFFDFA</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#F0EDE4"></div><div class="label">sunken<br>F0EDE4</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#D9D5CD"></div><div class="label">hairline<br>D9D5CD</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#081319"></div><div class="label">ink<br>081319</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#343C41"></div><div class="label">ink-2<br>343C41</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#66635C"></div><div class="label">ink-3<br>66635C</div></div>
|
||||
</div>
|
||||
|
||||
<h3 style="font-size:14px;font-weight:600;margin:24px 0 10px;color:var(--ink-2);font-variation-settings:'opsz' 16">Бренд и sidebar</h3>
|
||||
<div class="swatches">
|
||||
<div class="sw"><div class="chip" style="background:#0F6E56"></div><div class="label">accent<br>0F6E56</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#084635"></div><div class="label">accent-deep<br>084635</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#E1EEEA"></div><div class="label">accent-tint<br>E1EEEA</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#012019"></div><div class="label">side-bg<br>012019</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#13382F"></div><div class="label">side-active<br>13382F</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#B1C2BD"></div><div class="label">side-text<br>B1C2BD</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#32C8A9"></div><div class="label">side-icon-act<br>32C8A9</div></div>
|
||||
</div>
|
||||
|
||||
<!-- ===== STATUS PALETTE ===== -->
|
||||
<header class="section-h" id="statuses">
|
||||
<h2>14 статусов воронки</h2>
|
||||
<span class="label">03 · status palette · ΔE2000 ≥ 10.57</span>
|
||||
</header>
|
||||
|
||||
<div style="font-size:13px;color:var(--ink-2);margin-bottom:14px;max-width:760px;line-height:1.55;letter-spacing:-0.005em">
|
||||
OKLCH-распределённые solid цвета для status-dots. Repulsion-итерация в <a href="../palette_14.py" style="color:var(--accent);font-weight:500">palette_14.py</a> гарантирует ΔE2000 ≥ 10 между соседними парами — статусы визуально различимы даже при цветовой слепоте средней степени.
|
||||
</div>
|
||||
|
||||
<div class="statuses">
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#B94837"></span>Новая</div><div class="hex">#B94837</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#B35100"></span>В работе</div><div class="hex">#B35100</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#9A6700"></span>Дозвон</div><div class="hex">#9A6700</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#7E7500"></span>Не дозвон.</div><div class="hex">#7E7500</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#538200"></span>Переговоры</div><div class="hex">#538200</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#008A4D"></span>КП отправл.</div><div class="hex">#008A4D</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#008C7E"></span>Думает</div><div class="hex">#008C7E</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#00889B"></span>Ждёт оплату</div><div class="hex">#00889B</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#007EB8"></span>Оплачено</div><div class="hex">#007EB8</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#406DC8"></span>Возврат</div><div class="hex">#406DC8</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#6C60C4"></span>Отказ</div><div class="hex">#6C60C4</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#9052AE"></span>Дубликат</div><div class="hex">#9052AE</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#AA4788"></span>Спам</div><div class="hex">#AA4788</div></div>
|
||||
<div class="st-card"><div class="top"><span class="dot" style="background:#B7445F"></span>Архив</div><div class="hex">#B7445F</div></div>
|
||||
</div>
|
||||
|
||||
<!-- ===== SCREENS ===== -->
|
||||
<header class="section-h" id="screens">
|
||||
<h2>25 макетов</h2>
|
||||
<span class="label">04 · screens · все 0 axe violations</span>
|
||||
</header>
|
||||
|
||||
<h3 style="font-size:14px;font-weight:600;margin:8px 0 10px;color:var(--ink-2);font-variation-settings:'opsz' 16">Кабинет тенанта (8)</h3>
|
||||
<div class="grid">
|
||||
<a class="card" href="../concepts/v8_dashboard.html" target="_blank">
|
||||
<span class="num">02 · /dashboard</span>
|
||||
<span class="name">Дашборд</span>
|
||||
<span class="desc">KPI strip · Hero balance с runway · Activity chart · Funnel · Recent deals · System health · Quick actions</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
<span class="open">Открыть макет <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_deals.html" target="_blank">
|
||||
<span class="num">03 · /deals</span>
|
||||
<span class="name">Список сделок</span>
|
||||
<span class="desc">Таблица 14 строк · 14 status-chips · sticky thead · selected-row solid fill · slide-over drawer открыт</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
<span class="open">Открыть макет <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_deal_card.html" target="_blank">
|
||||
<span class="num">04 · /deals/{id}</span>
|
||||
<span class="name">Карточка сделки</span>
|
||||
<span class="desc">Hero + параметры + комментарий с 8 шаблонами + напоминание · aside: менеджер + метрики + history (timeline)</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
<span class="open">Открыть макет <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_kanban.html" target="_blank">
|
||||
<span class="num">05 · /deals/kanban</span>
|
||||
<span class="name">Канбан-доска</span>
|
||||
<span class="desc">14 колонок DnD (виртуализация-ready) · карточки с иконками 🔥 / ⏰ / ✓ · optimistic UI</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
<span class="open">Открыть макет <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_billing.html" target="_blank">
|
||||
<span class="num">06 · /billing</span>
|
||||
<span class="name">Биллинг и тарифы</span>
|
||||
<span class="desc">Кошелёк ₽ · ГЦК баланс лидов · runway · pending-banner · история транзакций · счета и УПД (1С 8.3 XML)</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
<span class="open">Открыть макет <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_settings.html" target="_blank">
|
||||
<span class="num">07 · /settings</span>
|
||||
<span class="name">Настройки</span>
|
||||
<span class="desc">8 вкладок · Профиль · Безопасность · Проекты · Команда · API/Webhook · Интеграции · Тихие часы · Уведомления</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
<span class="open">Открыть макет <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_reports.html" target="_blank">
|
||||
<span class="num">08 · /reports</span>
|
||||
<span class="name">Отчёты</span>
|
||||
<span class="desc">Форма запроса · 4 типа · 4 формата · jobs (done / running с progressbar / queued / failed + retry) · квота 2/3</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
<span class="open">Открыть макет <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_login.html" target="_blank">
|
||||
<span class="num">01 · /login и др.</span>
|
||||
<span class="name">Вход / Регистр / 2FA</span>
|
||||
<span class="desc">5 состояний: login · register с zxcvbn · 2FA (6-cell код) · forgot password · 8 recovery codes</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
<span class="open">Открыть макет <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 style="font-size:14px;font-weight:600;margin:24px 0 10px;color:var(--ink-2);font-variation-settings:'opsz' 16">Экраны ошибок (3)</h3>
|
||||
<div class="grid col4">
|
||||
<a class="card" href="../concepts/v8_errors.html#404" target="_blank">
|
||||
<span class="num">09 · 404</span>
|
||||
<span class="name">Не найдено</span>
|
||||
<span class="desc">Декоративный SVG-маркер · кнопки «На дашборд» / «Назад» · mailto support</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_errors.html#403" target="_blank">
|
||||
<span class="num">10 · 403</span>
|
||||
<span class="name">Нет доступа</span>
|
||||
<span class="desc">Replace-key SVG · REQ-id для копирования · контакт поддержки</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_errors.html#500" target="_blank">
|
||||
<span class="num">11 · 500/503</span>
|
||||
<span class="name">Сервис недоступен</span>
|
||||
<span class="desc">Status-list (API/Telegram/YooKassa) · INC-id · ссылка на status page</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_errors.html" target="_blank">
|
||||
<span class="num">все 3</span>
|
||||
<span class="name">Switcher</span>
|
||||
<span class="desc">Открыть файл с переключателем между состояниями</span>
|
||||
<span class="open">Открыть <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 style="font-size:14px;font-weight:600;margin:24px 0 10px;color:var(--ink-2);font-variation-settings:'opsz' 16">Админка SaaS (6) · admin.liderra.app</h3>
|
||||
<div class="grid">
|
||||
<a class="card" href="../concepts/v8_admin.html#login" target="_blank">
|
||||
<span class="num">12 · /admin/login</span>
|
||||
<span class="name">SSO Yandex 360</span>
|
||||
<span class="desc">Yandex 360 primary + локальный 2FA fallback · rate-limit 5/15 · captcha</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_admin.html#tenants" target="_blank">
|
||||
<span class="num">13 · /admin/tenants</span>
|
||||
<span class="name">Список тенантов</span>
|
||||
<span class="desc">142 тенанта · фильтры · 4 статус-чипа (active/trial/overdue/suspended) · MRR · желаем×факт</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_admin.html#tenant-card" target="_blank">
|
||||
<span class="num">14 · /admin/tenants/{id}</span>
|
||||
<span class="name">Карточка тенанта</span>
|
||||
<span class="desc">Профиль · баланс · корректировки · impersonation · удаление 152-ФЗ · журнал действий</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_admin.html#impersonation" target="_blank">
|
||||
<span class="num">15 · impersonation</span>
|
||||
<span class="name">Войти как клиент</span>
|
||||
<span class="desc">Красный sticky banner · спец. avatar «Админ → ИП» · обязательный reason</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_admin.html#incidents" target="_blank">
|
||||
<span class="num">16 · /admin/incidents</span>
|
||||
<span class="name">Журнал инцидентов</span>
|
||||
<span class="desc">HIGH/MED/LOW · read-only для compliance · webhook timeout / RLS bypass / payment retry</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_admin.html#system" target="_blank">
|
||||
<span class="num">17 · /admin/system</span>
|
||||
<span class="name">System settings</span>
|
||||
<span class="desc">6 sys-cards: settings/tariffs/legal/payment/webhook_log/auth_log · только super_admin</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 style="font-size:14px;font-weight:600;margin:24px 0 10px;color:var(--ink-2);font-variation-settings:'opsz' 16">Лендинг и юр. (4)</h3>
|
||||
<div class="grid col4">
|
||||
<a class="card" href="../concepts/v8_landing.html#home" target="_blank">
|
||||
<span class="num">18 · /</span>
|
||||
<span class="name">Главная</span>
|
||||
<span class="desc">Hero + 3 hi-cards · 6 features · 4 tariffs · FAQ · footer (контент Lorem ipsum, Диз-4 закрыт)</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_landing.html#pricing" target="_blank">
|
||||
<span class="num">19 · /pricing</span>
|
||||
<span class="name">Тарифы</span>
|
||||
<span class="desc">4 заглушки · Start/Basic/Команда POPULAR/Enterprise · цены 1.00 ₽ на MVP</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_landing.html#offer" target="_blank">
|
||||
<span class="num">20 · /legal/offer</span>
|
||||
<span class="name">Договор-оферта</span>
|
||||
<span class="desc">TOC · 8 разделов · шаблон Прил. Ж · ждёт регистрации ООО (Б-1)</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
<a class="card" href="../concepts/v8_landing.html#privacy" target="_blank">
|
||||
<span class="num">21 · /legal/privacy</span>
|
||||
<span class="name">Политика</span>
|
||||
<span class="desc">TOC · 10 разделов · 152-ФЗ compliant · Yandex Cloud РФ</span>
|
||||
<span class="pill ready">PROD-READY</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- ===== DOCS ===== -->
|
||||
<header class="section-h">
|
||||
<h2>Документация</h2>
|
||||
<span class="label">05 · docs</span>
|
||||
</header>
|
||||
<div class="grid col2">
|
||||
<a class="card doc" href="DEVELOPER_HANDOFF.md">
|
||||
<span class="num">📘 HANDOFF</span>
|
||||
<span class="name">Developer Handoff</span>
|
||||
<span class="desc">Главный документ. Стек, архитектура, токены, типографика, компоненты, все 25 экранов, WCAG-правила, Vue 3 + Vuetify 3 mapping, API hints, тестирование, FAQ. Открой первым.</span>
|
||||
<span class="open">Открыть <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card doc" href="BRANDBOOK_v2.md">
|
||||
<span class="num">🎨 BRAND</span>
|
||||
<span class="name">Brandbook v2</span>
|
||||
<span class="desc">Имя, лого, палитра, типографика, do/don't, голос бренда, использование Teal #0F6E56. Деприкейтит старый брендбук Лидпоток v1.1.</span>
|
||||
<span class="open">Открыть <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card doc" href="../concepts/v8_brand.html" target="_blank">
|
||||
<span class="num">BR-1</span>
|
||||
<span class="name">10 имён + 5 логотипов (архив)</span>
|
||||
<span class="desc">Все варианты, рассмотренные при выборе. Лидерра + L-Square — финальный выбор.</span>
|
||||
<span class="open">Открыть <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
<a class="card doc" href="../concepts/v8_palette_options.html" target="_blank">
|
||||
<span class="num">BR-2</span>
|
||||
<span class="name">5 палитр на выбор (архив)</span>
|
||||
<span class="desc">А Graphite · Б Forest (выбран) · В Slate-Blue · Г Champagne · Д Inverted Hero. Архив выбора палитры.</span>
|
||||
<span class="open">Открыть <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- ===== TECH ARTIFACTS ===== -->
|
||||
<header class="section-h">
|
||||
<h2>Технические артефакты</h2>
|
||||
<span class="label">06 · scripts & sources</span>
|
||||
</header>
|
||||
<div class="grid">
|
||||
<a class="card" href="../palette_v7.py">
|
||||
<span class="num">.py · OKLCH</span>
|
||||
<span class="name">palette_v7.py</span>
|
||||
<span class="desc">Расчёт всех cool-tech токенов с WCAG-проверкой. Python 3 + colour-science. Базовая палитра (унаследована Forest).</span>
|
||||
</a>
|
||||
<a class="card" href="../palette_14.py">
|
||||
<span class="num">.py · OKLCH</span>
|
||||
<span class="name">palette_14.py</span>
|
||||
<span class="desc">14 hue-распределённых статусов с repulsion-итерацией. min ΔE2000 = 10.57. min/max/mean — выводит в console.</span>
|
||||
</a>
|
||||
<a class="card" href="../palette_options.py">
|
||||
<span class="num">.py · OKLCH</span>
|
||||
<span class="name">palette_options.py</span>
|
||||
<span class="desc">5 вариантов палитры (А/Б/В/Г/Д) с side-by-side WCAG verification всех ключевых пар.</span>
|
||||
</a>
|
||||
<a class="card" href="../v4_critic.py">
|
||||
<span class="num">.py · audit</span>
|
||||
<span class="name">v4_critic.py</span>
|
||||
<span class="desc">Критика старого v4 (cream + Instrument Serif). Контрасты, ΔE2000 между chip-фонами. Архив.</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 509 KiB |
|
After Width: | Height: | Size: 509 KiB |
|
After Width: | Height: | Size: 599 KiB |
|
After Width: | Height: | Size: 599 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 199 KiB |
|
After Width: | Height: | Size: 205 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 296 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 297 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 409 KiB |
|
After Width: | Height: | Size: 409 KiB |
|
After Width: | Height: | Size: 366 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 92 KiB |
@@ -0,0 +1,194 @@
|
||||
"""
|
||||
Palette for 14 funnel statuses for Лидпоток / TBD.
|
||||
|
||||
Goals:
|
||||
1. 14 distinct hues evenly distributed in OKLCH space
|
||||
2. ΔE2000 between every adjacent pair >= 10 (perceptually distinct)
|
||||
3. Two variants per hue:
|
||||
- "tint" — chip background, light: must give >=4.5:1 against ink #0B1320 for 12px text
|
||||
- "solid" — strong fill, used for status dots: must give >=3:1 against cream bg #F4EFE5
|
||||
4. Colors should sit naturally with v4 accent #B84A22 (warm earth, sophisticated)
|
||||
|
||||
Strategy: vary HUE only at fixed L+C bands to guarantee equal perceptual spacing.
|
||||
"""
|
||||
|
||||
import colour
|
||||
import numpy as np
|
||||
|
||||
|
||||
def srgb_hex(rgb):
|
||||
rgb = np.clip(rgb, 0.0, 1.0)
|
||||
r, g, b = (round(c * 255) for c in rgb)
|
||||
return f"#{r:02X}{g:02X}{b:02X}"
|
||||
|
||||
|
||||
def oklch_to_srgb(L, C, H_deg):
|
||||
H = np.deg2rad(H_deg)
|
||||
a = C * np.cos(H)
|
||||
b = C * np.sin(H)
|
||||
Lab = np.array([L, a, b])
|
||||
XYZ = colour.Oklab_to_XYZ(Lab)
|
||||
rgb_lin = colour.XYZ_to_sRGB(XYZ, apply_cctf_encoding=False)
|
||||
rgb = colour.cctf_encoding(np.clip(rgb_lin, 0, 1), function="sRGB")
|
||||
return rgb
|
||||
|
||||
|
||||
def srgb_to_lab(rgb):
|
||||
rgb_lin = colour.cctf_decoding(rgb, function="sRGB")
|
||||
XYZ = colour.sRGB_to_XYZ(rgb_lin, apply_cctf_decoding=False)
|
||||
Lab = colour.XYZ_to_Lab(XYZ)
|
||||
return Lab
|
||||
|
||||
|
||||
def relative_luminance(rgb):
|
||||
rgb = np.clip(rgb, 0, 1)
|
||||
rgb_lin = np.where(rgb <= 0.03928, rgb / 12.92, ((rgb + 0.055) / 1.055) ** 2.4)
|
||||
return float(0.2126 * rgb_lin[0] + 0.7152 * rgb_lin[1] + 0.0722 * rgb_lin[2])
|
||||
|
||||
|
||||
def contrast_ratio(rgb_a, rgb_b):
|
||||
L1 = relative_luminance(rgb_a)
|
||||
L2 = relative_luminance(rgb_b)
|
||||
if L1 < L2:
|
||||
L1, L2 = L2, L1
|
||||
return (L1 + 0.05) / (L2 + 0.05)
|
||||
|
||||
|
||||
def hex_to_rgb(h):
|
||||
h = h.lstrip("#")
|
||||
return np.array([int(h[i : i + 2], 16) / 255 for i in (0, 2, 4)])
|
||||
|
||||
|
||||
# ---------- 14 funnel statuses ----------
|
||||
STATUSES = [
|
||||
"Новая", # 1
|
||||
"В работе", # 2
|
||||
"Дозвон", # 3
|
||||
"Не дозвон.", # 4
|
||||
"Перегов.", # 5
|
||||
"КП отправл.", # 6
|
||||
"Думает", # 7
|
||||
"Ждёт оплату", # 8
|
||||
"Оплачено", # 9
|
||||
"Возврат", # 10
|
||||
"Отказ", # 11
|
||||
"Дубликат", # 12
|
||||
"Спам", # 13
|
||||
"Архив", # 14
|
||||
]
|
||||
|
||||
INK = hex_to_rgb("#0B1320")
|
||||
CREAM = hex_to_rgb("#F4EFE5")
|
||||
|
||||
# Tint band (chip background): high lightness, low chroma → light tinted bg
|
||||
TINT_L = 0.92
|
||||
TINT_C = 0.055
|
||||
# Solid band (status dot, strong fill): mid lightness, mid chroma
|
||||
# C=0.15 chosen so all 14 hues stay in sRGB gamut AND every neighbour pair >= 10 ΔE2000
|
||||
SOLID_L = 0.55
|
||||
SOLID_C = 0.15
|
||||
|
||||
# 14 hues — start equally distributed, then iteratively re-space by ΔE2000 repulsion.
|
||||
# This solves the sRGB gamut anisotropy: magenta/pink region needs more degrees of separation
|
||||
# than green/cyan region to give equal perceptual distance.
|
||||
START_HUE = 30.0
|
||||
hues = [(START_HUE + i * (360.0 / 14)) % 360 for i in range(14)]
|
||||
|
||||
|
||||
def _solid_lab(h):
|
||||
rgb = oklch_to_srgb(SOLID_L, SOLID_C, h)
|
||||
return srgb_to_lab(rgb)
|
||||
|
||||
|
||||
def _min_neighbour_de(hs):
|
||||
labs = [_solid_lab(h) for h in hs]
|
||||
return min(
|
||||
float(colour.delta_E(labs[i], labs[(i + 1) % len(hs)], method="CIE 2000"))
|
||||
for i in range(len(hs))
|
||||
)
|
||||
|
||||
|
||||
# Iteratively perturb until min ΔE >= 10 (or 200 iterations)
|
||||
_step = 1.0
|
||||
for _it in range(200):
|
||||
labs = [_solid_lab(h) for h in hues]
|
||||
des = [
|
||||
float(colour.delta_E(labs[i], labs[(i + 1) % len(hues)], method="CIE 2000"))
|
||||
for i in range(len(hues))
|
||||
]
|
||||
if min(des) >= 10.0:
|
||||
break
|
||||
# for each hue, push away from the closer neighbour
|
||||
new_hues = list(hues)
|
||||
for i in range(len(hues)):
|
||||
prev_de = des[(i - 1) % len(hues)] # ΔE to previous
|
||||
next_de = des[i] # ΔE to next
|
||||
# push toward the larger gap
|
||||
if next_de < prev_de:
|
||||
new_hues[i] = (hues[i] - _step) % 360
|
||||
else:
|
||||
new_hues[i] = (hues[i] + _step) % 360
|
||||
hues = new_hues
|
||||
_step *= 0.95 # cooling
|
||||
|
||||
HUES = hues
|
||||
print(f"# Final hues after re-spacing: {[round(h,1) for h in HUES]}")
|
||||
print(f"# min neighbour ΔE2000 = {_min_neighbour_de(HUES):.2f}")
|
||||
print()
|
||||
|
||||
print("=" * 84)
|
||||
print(f"{'#':<3} {'Status':<14} {'Hue°':>5} {'Tint hex':>9} {'Solid hex':>10} {'AA on tint':>11} {'3:1 vs cream':>12}")
|
||||
print("=" * 84)
|
||||
|
||||
results = []
|
||||
for i, (name, h) in enumerate(zip(STATUSES, HUES), 1):
|
||||
tint_rgb = oklch_to_srgb(TINT_L, TINT_C, h)
|
||||
solid_rgb = oklch_to_srgb(SOLID_L, SOLID_C, h)
|
||||
tint_hex = srgb_hex(tint_rgb)
|
||||
solid_hex = srgb_hex(solid_rgb)
|
||||
|
||||
# WCAG: ink text on tint chip
|
||||
ink_on_tint = contrast_ratio(INK, tint_rgb)
|
||||
# WCAG: solid color as a dot/border on cream — needs 3:1 (UI component)
|
||||
solid_on_cream = contrast_ratio(solid_rgb, CREAM)
|
||||
|
||||
aa_tint = "PASS" if ink_on_tint >= 4.5 else "FAIL"
|
||||
aa_solid = "PASS" if solid_on_cream >= 3.0 else "FAIL"
|
||||
|
||||
print(f"{i:<3} {name:<14} {h:5.1f} {tint_hex:>9} {solid_hex:>10} {ink_on_tint:5.2f} {aa_tint} {solid_on_cream:5.2f} {aa_solid}")
|
||||
results.append((name, h, tint_rgb, solid_rgb, tint_hex, solid_hex))
|
||||
|
||||
# ΔE2000 between adjacent solid pairs (cyclic)
|
||||
print()
|
||||
print("ΔE2000 between adjacent SOLID pairs (must be >= 10):")
|
||||
print("-" * 60)
|
||||
deltas = []
|
||||
for i in range(14):
|
||||
a = results[i][3]
|
||||
b = results[(i + 1) % 14][3]
|
||||
Lab_a = srgb_to_lab(a)
|
||||
Lab_b = srgb_to_lab(b)
|
||||
de = float(colour.delta_E(Lab_a, Lab_b, method="CIE 2000"))
|
||||
deltas.append(de)
|
||||
flag = "✓" if de >= 10 else "✗"
|
||||
print(f" {results[i][0]:<14} → {results[(i+1)%14][0]:<14} ΔE2000 = {de:5.2f} {flag}")
|
||||
|
||||
print()
|
||||
print(f"min ΔE2000 = {min(deltas):.2f}, max = {max(deltas):.2f}, mean = {sum(deltas)/len(deltas):.2f}")
|
||||
print()
|
||||
|
||||
# also output as CSS custom properties block
|
||||
print("=" * 84)
|
||||
print("CSS — drop-in for v6_deals.html")
|
||||
print("=" * 84)
|
||||
for i, (name, h, _, _, tint_hex, solid_hex) in enumerate(results, 1):
|
||||
slug_map = {
|
||||
"Новая": "new", "В работе": "work", "Дозвон": "call",
|
||||
"Не дозвон.": "nocall", "Перегов.": "neg", "КП отправл.": "quote",
|
||||
"Думает": "think", "Ждёт оплату": "wait", "Оплачено": "paid",
|
||||
"Возврат": "refund", "Отказ": "fail", "Дубликат": "dup",
|
||||
"Спам": "spam", "Архив": "arch",
|
||||
}
|
||||
slug = slug_map[name]
|
||||
print(f" --st-{slug}-tint: {tint_hex}; /* {name} bg */")
|
||||
print(f" --st-{slug}-solid: {solid_hex}; /* {name} dot */")
|
||||
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
5 colour-scheme variants for v8 — bigger sidebar/main contrast.
|
||||
Constraint: brand accent Teal #0F6E56 не оспариваем (брендбук v1.1).
|
||||
Each variant fully WCAG-AA verified: ink/bg, ink/surface, ink/sidebar (text on
|
||||
sidebar bg), accent/bg, white/sidebar-active, sidebar/bg (≥3:1 чтобы visually
|
||||
"выделить левую часть").
|
||||
"""
|
||||
import colour
|
||||
import numpy as np
|
||||
|
||||
|
||||
def hex_rgb(h):
|
||||
h = h.lstrip("#")
|
||||
return np.array([int(h[i:i+2], 16)/255 for i in (0,2,4)])
|
||||
def srgb_hex(rgb):
|
||||
rgb = np.clip(rgb, 0, 1)
|
||||
return "#" + "".join(f"{round(c*255):02X}" for c in rgb)
|
||||
def oklch(L, C, H):
|
||||
H = np.deg2rad(H)
|
||||
Lab = np.array([L, C*np.cos(H), C*np.sin(H)])
|
||||
XYZ = colour.Oklab_to_XYZ(Lab)
|
||||
rgb_lin = colour.XYZ_to_sRGB(XYZ, apply_cctf_encoding=False)
|
||||
return colour.cctf_encoding(np.clip(rgb_lin, 0, 1), function='sRGB')
|
||||
def lum(rgb):
|
||||
rgb = np.clip(rgb, 0, 1)
|
||||
rl = np.where(rgb <= 0.03928, rgb/12.92, ((rgb+0.055)/1.055)**2.4)
|
||||
return float(0.2126*rl[0] + 0.7152*rl[1] + 0.0722*rl[2])
|
||||
def cr(a, b):
|
||||
L1, L2 = lum(a), lum(b)
|
||||
if L1 < L2: L1, L2 = L2, L1
|
||||
return (L1 + 0.05) / (L2 + 0.05)
|
||||
|
||||
WHITE = np.array([1.0,1.0,1.0])
|
||||
ACCENT = hex_rgb('#0F6E56') # Teal — брендбук v1.1, не оспаривается
|
||||
# Bright-Teal для icon-on-dark контекста (Teal слишком тёмный для тёмного sidebar)
|
||||
ACCENT_BRIGHT = oklch(0.75, 0.13, 175)
|
||||
print(f"# accent-bright (icon on dark sidebar): {srgb_hex(ACCENT_BRIGHT)}")
|
||||
print()
|
||||
|
||||
# 5 variants — each defined by (sidebar bg, sidebar text, sidebar active fill,
|
||||
# page bg, surface, ink, ink-3, optional secondary accent)
|
||||
variants = []
|
||||
|
||||
# ───── Вариант А — Graphite Sidebar (Linear/Vercel-grade)
|
||||
variants.append({
|
||||
'name': 'А · Graphite Sidebar',
|
||||
'desc': 'Тёмный графитовый sidebar + холодный near-white main. Самый «технологичный» из всех — линия Linear/Vercel. Контраст левая/правая = максимальный.',
|
||||
'sidebar': oklch(0.205, 0.012, 240), # near-black cool
|
||||
'sidebar_text': oklch(0.78, 0.005, 240),
|
||||
'sidebar_act': oklch(0.30, 0.015, 240),
|
||||
'bg': oklch(0.965, 0.005, 240), # cooler than v7
|
||||
'surface': WHITE,
|
||||
'sunken': oklch(0.945, 0.006, 240),
|
||||
'hairline': oklch(0.88, 0.006, 240),
|
||||
'ink': oklch(0.18, 0.018, 240),
|
||||
'ink_3': oklch(0.50, 0.012, 240),
|
||||
})
|
||||
|
||||
# ───── Вариант Б — Forest Sidebar (бренд-родной)
|
||||
variants.append({
|
||||
'name': 'Б · Forest Sidebar',
|
||||
'desc': 'Глубокий тёмно-тиловый sidebar (родственник брендового Teal) + тёплый ivory main. Брендовая связность, корпоративная фундаментальность.',
|
||||
'sidebar': oklch(0.22, 0.040, 175), # deep teal-noir
|
||||
'sidebar_text': oklch(0.80, 0.020, 175),
|
||||
'sidebar_act': oklch(0.31, 0.045, 175),
|
||||
'bg': oklch(0.965, 0.010, 90), # warm ivory
|
||||
'surface': oklch(0.995, 0.005, 90),
|
||||
'sunken': oklch(0.945, 0.012, 90),
|
||||
'hairline': oklch(0.875, 0.012, 85),
|
||||
'ink': oklch(0.18, 0.020, 230),
|
||||
'ink_3': oklch(0.50, 0.012, 90),
|
||||
})
|
||||
|
||||
# ───── Вариант В — Slate-Blue Financial
|
||||
variants.append({
|
||||
'name': 'В · Slate-Blue Financial',
|
||||
'desc': 'Sidebar — глубокий синий слейт, main — холодный mist. Stripe Atlas/Mercury вайб, финансовый инструмент.',
|
||||
'sidebar': oklch(0.235, 0.030, 250), # deep navy slate
|
||||
'sidebar_text': oklch(0.80, 0.015, 250),
|
||||
'sidebar_act': oklch(0.34, 0.035, 250),
|
||||
'bg': oklch(0.945, 0.010, 240), # cool mist
|
||||
'surface': WHITE,
|
||||
'sunken': oklch(0.92, 0.012, 240),
|
||||
'hairline': oklch(0.86, 0.014, 240),
|
||||
'ink': oklch(0.18, 0.020, 245),
|
||||
'ink_3': oklch(0.50, 0.014, 245),
|
||||
})
|
||||
|
||||
# ───── Вариант Г — Warm Champagne
|
||||
variants.append({
|
||||
'name': 'Г · Warm Champagne',
|
||||
'desc': 'Sidebar — насыщенный кремово-бежевый, main — pure white. Тёплый «кабинет», Notion/Things с строгостью.',
|
||||
'sidebar': oklch(0.92, 0.025, 80), # rich champagne
|
||||
'sidebar_text': oklch(0.32, 0.015, 80),
|
||||
'sidebar_act': oklch(0.18, 0.020, 240), # ink fill
|
||||
'bg': WHITE,
|
||||
'surface': oklch(0.985, 0.005, 80),
|
||||
'sunken': oklch(0.94, 0.018, 80),
|
||||
'hairline': oklch(0.86, 0.022, 80),
|
||||
'ink': oklch(0.18, 0.020, 240),
|
||||
'ink_3': oklch(0.48, 0.012, 80),
|
||||
})
|
||||
|
||||
# ───── Вариант Д — Inverted Hero (cards float)
|
||||
variants.append({
|
||||
'name': 'Д · Inverted Hero',
|
||||
'desc': 'Sidebar — pure white, main — насыщенный cool grey. Карточки «плавают» над полотном, классика премиум B2B (Stripe Dashboard).',
|
||||
'sidebar': WHITE,
|
||||
'sidebar_text': oklch(0.32, 0.014, 240),
|
||||
'sidebar_act': oklch(0.18, 0.020, 240), # ink fill
|
||||
'bg': oklch(0.91, 0.012, 240), # noticeably cool grey
|
||||
'surface': WHITE,
|
||||
'sunken': oklch(0.88, 0.013, 240),
|
||||
'hairline': oklch(0.82, 0.015, 240),
|
||||
'ink': oklch(0.18, 0.020, 245),
|
||||
'ink_3': oklch(0.48, 0.013, 245),
|
||||
})
|
||||
|
||||
# Compute & print
|
||||
for v in variants:
|
||||
print("=" * 84)
|
||||
print(v['name'])
|
||||
print(v['desc'])
|
||||
print("-" * 84)
|
||||
print(f" sidebar {srgb_hex(v['sidebar'])}")
|
||||
print(f" sidebar text {srgb_hex(v['sidebar_text'])}")
|
||||
print(f" sidebar act {srgb_hex(v['sidebar_act'])}")
|
||||
print(f" bg {srgb_hex(v['bg'])}")
|
||||
print(f" surface {srgb_hex(v['surface'])}")
|
||||
print(f" sunken {srgb_hex(v['sunken'])}")
|
||||
print(f" hairline {srgb_hex(v['hairline'])}")
|
||||
print(f" ink {srgb_hex(v['ink'])}")
|
||||
print(f" ink-3 {srgb_hex(v['ink_3'])}")
|
||||
print(f" accent (teal, fixed) #0F6E56")
|
||||
print()
|
||||
print(" WCAG checks:")
|
||||
pairs = [
|
||||
('ink / bg', v['ink'], v['bg'], 4.5),
|
||||
('ink / surface', v['ink'], v['surface'], 4.5),
|
||||
('ink-3 / bg', v['ink_3'], v['bg'], 4.5),
|
||||
('sidebar text / sidebar', v['sidebar_text'], v['sidebar'], 4.5),
|
||||
('white / sidebar-active', WHITE, v['sidebar_act'], 4.5),
|
||||
('accent / bg', ACCENT, v['bg'], 4.5),
|
||||
('accent-bright / sidebar (icon)', ACCENT_BRIGHT, v['sidebar'], 3.0),
|
||||
('SIDEBAR vs BG (layout div)', v['sidebar'], v['bg'], 3.0),
|
||||
]
|
||||
for name, a, b, target in pairs:
|
||||
r = cr(a, b)
|
||||
flag = '✓' if r >= target else 'FAIL'
|
||||
print(f" {name:<32} {r:6.2f}:1 ≥{target} {flag}")
|
||||
print()
|
||||
@@ -0,0 +1,87 @@
|
||||
"""v7 palette — cool-tech light theme. Proves WCAG AA before HTML write."""
|
||||
import colour
|
||||
import numpy as np
|
||||
|
||||
def hex_rgb(h):
|
||||
h = h.lstrip("#")
|
||||
return np.array([int(h[i:i+2], 16)/255 for i in (0,2,4)])
|
||||
def srgb_hex(rgb):
|
||||
rgb = np.clip(rgb, 0, 1)
|
||||
return "#" + "".join(f"{round(c*255):02X}" for c in rgb)
|
||||
def oklch_to_hex(L, C, H):
|
||||
H = np.deg2rad(H)
|
||||
Lab = np.array([L, C*np.cos(H), C*np.sin(H)])
|
||||
XYZ = colour.Oklab_to_XYZ(Lab)
|
||||
rgb_lin = colour.XYZ_to_sRGB(XYZ, apply_cctf_encoding=False)
|
||||
rgb = colour.cctf_encoding(np.clip(rgb_lin, 0, 1), function='sRGB')
|
||||
return srgb_hex(rgb), rgb
|
||||
def lum(rgb):
|
||||
rgb = np.clip(rgb, 0, 1)
|
||||
rl = np.where(rgb <= 0.03928, rgb/12.92, ((rgb+0.055)/1.055)**2.4)
|
||||
return float(0.2126*rl[0] + 0.7152*rl[1] + 0.0722*rl[2])
|
||||
def cr(a, b):
|
||||
L1, L2 = lum(a), lum(b)
|
||||
if L1 < L2: L1, L2 = L2, L1
|
||||
return (L1 + 0.05) / (L2 + 0.05)
|
||||
|
||||
# Cool-tech tokens — все вокруг hue=240 (нейтральный синеватый), чтобы поверхность читалась
|
||||
# как "лабораторная" а не "тёплая бумага". Brand accent — Teal #0F6E56 из брендбука,
|
||||
# его НЕ трогаем.
|
||||
|
||||
print("=" * 80)
|
||||
print("v7 cool-tech tokens (Page surface — neutral cool, hue=240)")
|
||||
print("=" * 80)
|
||||
|
||||
# Светлые поверхности
|
||||
bg_hex, bg_rgb = oklch_to_hex(0.985, 0.003, 240) # page bg
|
||||
surface_hex, surface_rgb = oklch_to_hex(1.000, 0.000, 240) # raised — pure white
|
||||
sunken_hex, sunken_rgb = oklch_to_hex(0.970, 0.004, 240) # sunken
|
||||
hairline_hex, hairline_rgb = oklch_to_hex(0.910, 0.005, 240) # hairline (UI border, not text)
|
||||
|
||||
# Текст
|
||||
ink_hex, ink_rgb = oklch_to_hex(0.180, 0.018, 240) # primary text
|
||||
ink2_hex, ink2_rgb = oklch_to_hex(0.350, 0.014, 240) # secondary
|
||||
ink3_hex, ink3_rgb = oklch_to_hex(0.510, 0.012, 240) # muted (must AA on bg)
|
||||
inkd_hex, inkd_rgb = oklch_to_hex(0.620, 0.008, 240) # disabled (3:1+)
|
||||
|
||||
# Brand accent (Teal #0F6E56) — НЕ пересчитываем, фиксируем из брендбука
|
||||
accent_rgb = hex_rgb('#0F6E56')
|
||||
|
||||
palette = [
|
||||
('bg', bg_hex, bg_rgb),
|
||||
('surface', surface_hex, surface_rgb),
|
||||
('sunken', sunken_hex, sunken_rgb),
|
||||
('hairline', hairline_hex, hairline_rgb),
|
||||
('ink', ink_hex, ink_rgb),
|
||||
('ink-2', ink2_hex, ink2_rgb),
|
||||
('ink-3', ink3_hex, ink3_rgb),
|
||||
('ink-disabled',inkd_hex, inkd_rgb),
|
||||
('accent (teal)','#0F6E56', accent_rgb),
|
||||
]
|
||||
for name, h, _ in palette:
|
||||
print(f" {name:<14} {h}")
|
||||
|
||||
print()
|
||||
print("WCAG verifications — text colour on bg/surface/sunken")
|
||||
print("=" * 80)
|
||||
checks = [
|
||||
('ink / bg', ink_rgb, bg_rgb, 4.5),
|
||||
('ink / surface', ink_rgb, surface_rgb, 4.5),
|
||||
('ink / sunken', ink_rgb, sunken_rgb, 4.5),
|
||||
('ink-2 / bg', ink2_rgb, bg_rgb, 4.5),
|
||||
('ink-2 / sunken', ink2_rgb, sunken_rgb, 4.5),
|
||||
('ink-3 / bg', ink3_rgb, bg_rgb, 4.5),
|
||||
('ink-3 / surface', ink3_rgb, surface_rgb, 4.5),
|
||||
('ink-3 / sunken', ink3_rgb, sunken_rgb, 4.5),
|
||||
('ink-disabled / bg', inkd_rgb, bg_rgb, 3.0), # disabled — 3:1 OK
|
||||
('accent / bg', accent_rgb, bg_rgb, 4.5),
|
||||
('accent / surface',accent_rgb, surface_rgb, 4.5),
|
||||
('white-on-accent (CTA text)', np.array([1,1,1]), accent_rgb, 4.5),
|
||||
('white-on-ink (active nav)', np.array([1,1,1]), ink_rgb, 4.5),
|
||||
('hairline / bg (3:1 UI)', hairline_rgb, bg_rgb, 3.0),
|
||||
]
|
||||
for name, a, b, target in checks:
|
||||
r = cr(a, b)
|
||||
flag = '✓' if r >= target else 'FAIL'
|
||||
target_str = f"≥{target}"
|
||||
print(f" {name:<35} {r:5.2f}:1 {target_str:>5} {flag}")
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "lidpotok",
|
||||
"name": "liderra",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "lidpotok",
|
||||
"name": "liderra",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"@cspell/dict-en_us": "^4.4.33",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "lidpotok",
|
||||
"name": "liderra",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Лидпоток — SaaS CRM (фаза 0: документация и HTML-прототипы)",
|
||||
"description": "Лидерра — SaaS CRM (фаза 0: документация и HTML-прототипы)",
|
||||
"scripts": {
|
||||
"lint:md": "markdownlint-cli2 \"docs/**/*.md\" \"db/**/*.md\" \"*.md\"",
|
||||
"lint:md:fix": "markdownlint-cli2 --fix \"docs/**/*.md\" \"db/**/*.md\" \"*.md\"",
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Прил. Л — Прототипы Лидпоток</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap&subset=cyrillic" rel="stylesheet" />
|
||||
<style>
|
||||
:root {
|
||||
--teal-900: #04342C;
|
||||
--teal-600: #0F6E56;
|
||||
--teal-200: #5DCAA5;
|
||||
--teal-50: #E1F5EE;
|
||||
--gray-900: #1A1A1A;
|
||||
--gray-700: #444441;
|
||||
--gray-500: #888780;
|
||||
--gray-200: #D3D1C7;
|
||||
--gray-100: #F1EFE8;
|
||||
--gray-50: #FAFAF8;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Inter, sans-serif;
|
||||
background: var(--gray-100);
|
||||
color: var(--gray-900);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.container { max-width: 880px; margin: 0 auto; padding: 64px 24px; }
|
||||
.header {
|
||||
display: flex; align-items: center; gap: 16px;
|
||||
padding-bottom: 24px; border-bottom: 1px solid var(--gray-200); margin-bottom: 32px;
|
||||
}
|
||||
.header-mark {
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
}
|
||||
.header-mark span {
|
||||
display: block; border-radius: 50%;
|
||||
}
|
||||
.header-mark span:nth-child(1) { width: 8px; height: 8px; background: var(--teal-600); opacity: 0.6; }
|
||||
.header-mark span:nth-child(2) { width: 14px; height: 14px; background: var(--teal-600); opacity: 0.8; }
|
||||
.header-mark span:nth-child(3) { width: 20px; height: 20px; background: var(--teal-600); }
|
||||
.header-title { font-size: 22px; font-weight: 600; margin: 0; letter-spacing: -0.01em; }
|
||||
.header-sub { font-size: 13px; color: var(--gray-500); margin-top: 2px; }
|
||||
|
||||
h1 { font-size: 32px; font-weight: 700; letter-spacing: -0.02em; margin: 0 0 8px; }
|
||||
h1 + p { font-size: 16px; color: var(--gray-700); margin: 0 0 32px; max-width: 640px; }
|
||||
|
||||
.meta {
|
||||
background: var(--teal-50); border: 1px solid var(--teal-200);
|
||||
border-radius: 12px; padding: 16px 20px; margin-bottom: 40px;
|
||||
font-size: 13px; color: var(--teal-900);
|
||||
}
|
||||
.meta strong { color: var(--teal-900); }
|
||||
|
||||
.screens { list-style: none; padding: 0; margin: 0; display: grid; gap: 12px; }
|
||||
.screen {
|
||||
display: flex; align-items: center; gap: 20px;
|
||||
padding: 20px 24px; background: white;
|
||||
border: 1px solid var(--gray-200); border-radius: 12px;
|
||||
transition: border-color .15s ease, transform .05s ease;
|
||||
text-decoration: none; color: inherit;
|
||||
}
|
||||
.screen:hover { border-color: var(--teal-600); }
|
||||
.screen:active { transform: translateY(1px); }
|
||||
.screen[aria-disabled="true"] {
|
||||
opacity: 0.5; pointer-events: none;
|
||||
background: var(--gray-50);
|
||||
}
|
||||
.screen-num {
|
||||
flex: 0 0 40px; height: 40px;
|
||||
border-radius: 8px;
|
||||
background: var(--teal-50); color: var(--teal-600);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 600; font-size: 14px;
|
||||
}
|
||||
.screen[aria-disabled="true"] .screen-num { background: var(--gray-100); color: var(--gray-500); }
|
||||
.screen-body { flex: 1; min-width: 0; }
|
||||
.screen-title { font-size: 15px; font-weight: 500; margin: 0 0 2px; }
|
||||
.screen-desc { font-size: 13px; color: var(--gray-500); margin: 0; }
|
||||
.screen-status {
|
||||
flex: 0 0 auto; font-size: 12px; font-weight: 500;
|
||||
padding: 4px 10px; border-radius: 999px;
|
||||
}
|
||||
.screen-status.is-ready {
|
||||
background: var(--teal-50); color: var(--teal-600);
|
||||
}
|
||||
.screen-status.is-pending {
|
||||
background: var(--gray-100); color: var(--gray-500);
|
||||
}
|
||||
.screen-arrow { color: var(--gray-300); flex: 0 0 auto; }
|
||||
.screen:hover .screen-arrow { color: var(--teal-600); }
|
||||
|
||||
.footer {
|
||||
margin-top: 48px; padding-top: 24px;
|
||||
border-top: 1px solid var(--gray-200);
|
||||
font-size: 12px; color: var(--gray-500);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="header-mark" aria-hidden="true">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="header-title">Лидпоток</p>
|
||||
<p class="header-sub">Прил. Л — HTML-прототипы 8 экранов</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Прототипы клиентского приложения и админки SaaS</h1>
|
||||
<p>HTML/CSS/JS-прототипы 8 ключевых экранов для дизайнера (Диз-1) и frontend-разработчика. Каждый файл самодостаточен — открывается двойным кликом, без сборки. Источник истины по визуалу — <code>brandbook.md</code> v1.1, по поведению — narrative v8.3.1.</p>
|
||||
|
||||
<div class="meta">
|
||||
<strong>Версия:</strong> v0.2 от 06.05.2026 ·
|
||||
<strong>Готовность:</strong> 3 / 8 экранов (01 + 02 + 03) ·
|
||||
<strong>Создаётся итеративно:</strong> по одному экрану за сессию ·
|
||||
<strong>Назначение:</strong> референс для Figma-макетов (Диз-1) и для frontend-команды на старте спринтов 1, 4, 5, 8, 14
|
||||
</div>
|
||||
|
||||
<ul class="screens">
|
||||
<li>
|
||||
<a class="screen" href="01-login.html">
|
||||
<div class="screen-num">01</div>
|
||||
<div class="screen-body">
|
||||
<p class="screen-title">Логин · Регистрация · 2FA · Recovery</p>
|
||||
<p class="screen-desc">Email + пароль, регистрация с zxcvbn, TOTP с 6-значным кодом, 8 резервных кодов</p>
|
||||
</div>
|
||||
<span class="screen-status is-ready">Готово</span>
|
||||
<svg class="screen-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="screen" href="02-dashboard.html">
|
||||
<div class="screen-num">02</div>
|
||||
<div class="screen-body">
|
||||
<p class="screen-title">Дашборд</p>
|
||||
<p class="screen-desc">5 KPI-карточек, 2 графика (ApexCharts), последние сделки, баланс</p>
|
||||
</div>
|
||||
<span class="screen-status is-ready">Готово</span>
|
||||
<svg class="screen-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="screen" href="03-deals.html">
|
||||
<div class="screen-num">03</div>
|
||||
<div class="screen-body">
|
||||
<p class="screen-title">Список сделок</p>
|
||||
<p class="screen-desc">Таблица с фильтрами, 14 цветных статусов, массовые операции, пагинация, low-balance баннер</p>
|
||||
</div>
|
||||
<span class="screen-status is-ready">Готово</span>
|
||||
<svg class="screen-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="screen" aria-disabled="true">
|
||||
<div class="screen-num">04</div>
|
||||
<div class="screen-body">
|
||||
<p class="screen-title">Карточка сделки</p>
|
||||
<p class="screen-desc">Модалка/страница: статусы, теги, комментарии, напоминания, история</p>
|
||||
</div>
|
||||
<span class="screen-status is-pending">В очереди</span>
|
||||
<span></span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="screen" aria-disabled="true">
|
||||
<div class="screen-num">05</div>
|
||||
<div class="screen-body">
|
||||
<p class="screen-title">Канбан-доска</p>
|
||||
<p class="screen-desc">Drag-and-drop по 14 статусам, optimistic UI, виртуализация колонок</p>
|
||||
</div>
|
||||
<span class="screen-status is-pending">В очереди</span>
|
||||
<span></span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="screen" aria-disabled="true">
|
||||
<div class="screen-num">06</div>
|
||||
<div class="screen-body">
|
||||
<p class="screen-title">Биллинг и тарифы</p>
|
||||
<p class="screen-desc">Кошелёк, история операций, выбор тарифа, пополнение, счета и УПД</p>
|
||||
</div>
|
||||
<span class="screen-status is-pending">В очереди</span>
|
||||
<span></span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="screen" aria-disabled="true">
|
||||
<div class="screen-num">07</div>
|
||||
<div class="screen-body">
|
||||
<p class="screen-title">Настройки тенанта</p>
|
||||
<p class="screen-desc">Профиль, проекты, токены API, интеграции, тихие часы, активные сессии</p>
|
||||
</div>
|
||||
<span class="screen-status is-pending">В очереди</span>
|
||||
<span></span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="screen" aria-disabled="true">
|
||||
<div class="screen-num">08</div>
|
||||
<div class="screen-body">
|
||||
<p class="screen-title">Админка SaaS</p>
|
||||
<p class="screen-desc">Список тенантов, карточка тенанта, биллинг, impersonation, журналы</p>
|
||||
</div>
|
||||
<span class="screen-status is-pending">В очереди</span>
|
||||
<span></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p class="footer">
|
||||
Прил. Л v0.1 · Источник истины по визуалу: <code>brandbook.md</code> v1.1 ·
|
||||
По поведению: <code>CRM_bp-gr_Инструкция_v8_5.md</code> §1–28 + Прил. Г для №08
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,544 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Админка SaaS — Лидерра (Forest)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg:#F6F3EC; --surface:#FFFDFA; --sunken:#F0EDE4;
|
||||
--hairline:#D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink:#081319; --ink-2:#343C41; --ink-3:#66635C; --ink-disabled:#92907B;
|
||||
--accent:#0F6E56; --accent-tint:#E1EEEA; --accent-deep:#084635;
|
||||
--side-bg:#012019; --side-text:#B1C2BD; --side-text-2:#7A8C87;
|
||||
--side-active:#13382F; --side-icon:#5C7A72; --side-icon-act:#32C8A9;
|
||||
--side-hover:#0A2A22; --side-border:#1A3A30;
|
||||
--st-paid:#007EB8; --st-quote:#008A4D; --st-call:#9A6700; --st-new:#B94837; --st-fail:#6C60C4;
|
||||
--r-xs:4px; --r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
--imp-red: #B94837;
|
||||
}
|
||||
body { background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-feature-settings:'cv11','ss01'; -webkit-font-smoothing:antialiased; font-variation-settings:'opsz' 14; min-height:100vh; }
|
||||
button { font-family:inherit; }
|
||||
a { color:inherit; text-decoration:none; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible {
|
||||
outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
|
||||
}
|
||||
|
||||
/* Review-only state switcher */
|
||||
.review-bar { position:sticky; top:0; z-index:100; background:var(--side-bg); padding:10px 16px; display:flex; align-items:center; gap:14px; border-bottom:1px solid #1A3A30; flex-wrap:wrap; }
|
||||
.review-bar .label { font-size:11px; font-weight:600; letter-spacing:0.06em; color:#7A8C87; font-family:var(--font-mono); }
|
||||
.review-bar .tabs { display:flex; gap:2px; flex-wrap:wrap; }
|
||||
.review-bar .tab { height:26px; padding:0 12px; border:none; background:transparent; color:#B1C2BD; font-family:inherit; font-size:12px; font-weight:500; cursor:pointer; border-radius:4px; }
|
||||
.review-bar .tab:hover { background:#0A2A22; color:#fff; }
|
||||
.review-bar .tab.active { background:#fff; color:#012019; }
|
||||
|
||||
/* Impersonation banner — only visible on tenant-card-imp */
|
||||
.imp-banner {
|
||||
background: var(--imp-red); color: #fff;
|
||||
padding: 9px 24px;
|
||||
display: flex; align-items: center; gap: 12px; justify-content: space-between;
|
||||
font-size: 12.5px;
|
||||
position: sticky; top: 47px; z-index: 90;
|
||||
}
|
||||
.imp-banner svg { width: 14px; height: 14px; }
|
||||
.imp-banner strong { font-weight: 600; }
|
||||
.imp-banner button {
|
||||
height: 24px; padding: 0 10px;
|
||||
border: 1px solid rgba(255,255,255,0.30);
|
||||
background: rgba(255,255,255,0.10);
|
||||
color: #fff;
|
||||
font-family: inherit; font-size: 11px; font-weight: 500;
|
||||
border-radius: 4px; cursor: pointer;
|
||||
}
|
||||
.imp-banner button:hover { background: rgba(255,255,255,0.18); }
|
||||
|
||||
/* App shell — same Forest with admin variation */
|
||||
.app { display:grid; grid-template-columns:232px 1fr; min-height:calc(100vh - 47px); }
|
||||
.side { background:var(--side-bg); border-right:1px solid var(--side-border); padding:18px 12px 24px; position:sticky; top:47px; height:calc(100vh - 47px); overflow-y:auto; color:var(--side-text); }
|
||||
.brand { display:flex; align-items:center; gap:10px; padding:6px 8px 4px; font-weight:600; font-size:14.5px; letter-spacing:-0.01em; font-variation-settings:'opsz' 18; color:#FFF; }
|
||||
.brand-mark { width:22px; height:22px; border-radius:var(--r-xs); background:#FFF; display:inline-flex; align-items:center; justify-content:center; flex-shrink:0; overflow:hidden; }
|
||||
.brand-mark svg { width:100%; height:100%; display:block; }
|
||||
.brand-dot { color:var(--side-icon-act); }
|
||||
.brand-sub { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; color: var(--side-icon-act); padding: 0 8px 16px; font-weight: 600; }
|
||||
.nav { display:flex; flex-direction:column; gap:1px; }
|
||||
.nav-eyebrow { font-size:11px; font-weight:500; letter-spacing:0.01em; color:var(--side-text-2); padding:14px 10px 6px; }
|
||||
.nav-item { display:flex; align-items:center; gap:10px; height:32px; padding:0 10px; border-radius:var(--r-sm); font-size:13px; color:var(--side-text); cursor: pointer; }
|
||||
.nav-item:hover { background:var(--side-hover); color:#FFF; }
|
||||
.nav-item.active { background:var(--side-active); color:#FFF; font-weight:500; }
|
||||
.nav-item.active .nav-icon { color:var(--side-icon-act); }
|
||||
.nav-icon { width:15px; height:15px; flex-shrink:0; color:var(--side-icon); stroke-width:1.6; }
|
||||
.nav-item:hover .nav-icon { color:#FFF; }
|
||||
.nav-text { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.nav-count { font-family:var(--font-mono); font-size:10.5px; font-weight:500; font-feature-settings:'tnum'; background:rgba(255,255,255,0.10); color:var(--side-text); padding:2px 6px; border-radius:4px; }
|
||||
|
||||
.main { display:flex; flex-direction:column; min-width:0; }
|
||||
.topbar { height:48px; border-bottom:1px solid var(--hairline); background:var(--surface); padding:0 24px; display:flex; align-items:center; gap:12px; position:sticky; top:47px; z-index:50; }
|
||||
.crumb { font-size:12.5px; color:var(--ink-3); display:flex; align-items:center; gap:8px; }
|
||||
.crumb strong { color:var(--ink); font-weight:500; }
|
||||
.crumb svg { width:11px; height:11px; color:var(--ink-3); opacity:0.5; }
|
||||
.crumb a { cursor:pointer; }
|
||||
.crumb a:hover { color:var(--ink); }
|
||||
.topbar-spacer { flex:1; }
|
||||
.user-chip { display:inline-flex; align-items:center; gap:8px; height:30px; padding:0 10px 0 4px; border-radius:100px; border:1px solid var(--hairline); background:var(--surface); }
|
||||
.user-chip .ava { width:22px; height:22px; border-radius:50%; background:var(--ink); color:#fff; display:inline-flex; align-items:center; justify-content:center; font-size:9.5px; font-weight:600; }
|
||||
.user-chip .uname { font-size:12px; color:var(--ink); font-weight:500; }
|
||||
|
||||
.content { padding:24px 28px 60px; flex:1; max-width: 1700px; margin: 0 auto; width:100%; }
|
||||
.page-h { display:flex; align-items:flex-end; justify-content:space-between; margin-bottom:18px; gap:16px; flex-wrap: wrap; }
|
||||
.page-title { font-size:26px; font-weight:600; font-variation-settings:'opsz' 26; letter-spacing:-0.02em; line-height:1.1; margin:0 0 6px; }
|
||||
.page-meta { font-size:12.5px; color:var(--ink-3); display:flex; gap:14px; align-items:center; flex-wrap:wrap; }
|
||||
.page-meta .num { font-family:var(--font-mono); font-feature-settings:'tnum'; color:var(--ink-2); font-weight:500; }
|
||||
.page-meta .sep { color:var(--ink-disabled); }
|
||||
|
||||
.btn { display:inline-flex; align-items:center; gap:7px; height:34px; padding:0 14px; border-radius:var(--r-sm); border:1px solid var(--hairline); background:var(--surface); font-size:12.5px; font-weight:500; color:var(--ink); cursor:pointer; font-family:inherit; letter-spacing:-0.005em; }
|
||||
.btn:hover { border-color:var(--ink-disabled); background:var(--sunken); }
|
||||
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
.btn-primary:hover { background:var(--accent-deep); border-color:var(--accent-deep); }
|
||||
.btn-danger { color:var(--st-new); }
|
||||
.btn-danger:hover { background:#FFE7E2; border-color:var(--st-new); color:#8E2516; }
|
||||
.btn svg { width:13px; height:13px; stroke-width:1.7; }
|
||||
|
||||
/* SSO login screen */
|
||||
.sso-shell { min-height:calc(100vh - 47px); display:grid; grid-template-columns:1fr 1fr; }
|
||||
.sso-brand { background:var(--side-bg); color:#fff; padding:56px 60px; display:flex; flex-direction:column; justify-content:space-between; }
|
||||
.sso-brand .head { display:flex; align-items:center; gap:10px; font-weight:600; font-size:16px; }
|
||||
.sso-brand .head .mark { width:24px; height:24px; border-radius:5px; background:#fff; display:inline-flex; align-items:center; justify-content:center; overflow:hidden; }
|
||||
.sso-brand .head .mark svg { width:100%; height:100%; }
|
||||
.sso-brand .head .dot { color:var(--side-icon-act); }
|
||||
.sso-brand .head .admin-tag { font-family:var(--font-mono); font-size:10px; letter-spacing:0.06em; padding:2px 6px; border-radius:3px; background:var(--imp-red); color:#fff; margin-left:auto; font-weight:600; }
|
||||
.sso-brand .body { font-size:30px; font-weight:500; font-variation-settings:'opsz' 28; letter-spacing:-0.02em; line-height:1.2; max-width:440px; }
|
||||
.sso-brand .body em { color: var(--side-icon-act); font-style: normal; }
|
||||
.sso-brand .foot { font-size:12px; color:#7A8C87; font-family:var(--font-mono); }
|
||||
.sso-form { background:var(--bg); display:flex; align-items:center; justify-content:center; padding:40px 32px; }
|
||||
.sso-card { width:100%; max-width:380px; display:flex; flex-direction:column; gap:18px; }
|
||||
.sso-card h1 { font-size:24px; font-weight:600; font-variation-settings:'opsz' 24; letter-spacing:-0.018em; margin:0 0 6px; line-height:1.2; }
|
||||
.sso-card p { font-size:12.5px; color:var(--ink-3); line-height:1.5; margin:0; }
|
||||
.sso-button {
|
||||
display:flex; align-items:center; justify-content:center; gap:8px;
|
||||
width:100%;
|
||||
height:48px; padding:0 18px;
|
||||
border:1px solid var(--ink); border-radius:var(--r-sm);
|
||||
background:var(--ink); color:#fff;
|
||||
font-size:13.5px; font-weight:500;
|
||||
cursor:pointer; font-family:inherit;
|
||||
letter-spacing:-0.005em;
|
||||
}
|
||||
.sso-button:hover { background:#000; }
|
||||
.sso-button .y-ico {
|
||||
width:20px; height:20px;
|
||||
background:#FF0000; color:#fff;
|
||||
border-radius:50%;
|
||||
display:inline-flex; align-items:center; justify-content:center;
|
||||
font-weight:700; font-size:11px;
|
||||
font-family:Inter,sans-serif;
|
||||
}
|
||||
.sso-fallback { font-size:11.5px; color:var(--ink-3); text-align:center; }
|
||||
.sso-fallback a { color:var(--accent); font-weight:500; }
|
||||
.sso-fallback a:hover { text-decoration:underline; }
|
||||
|
||||
/* Tenants table */
|
||||
.t-controls { display:flex; align-items:center; gap:10px; margin-bottom:14px; flex-wrap:wrap; }
|
||||
.search-input { display:inline-flex; align-items:center; gap:8px; flex:1; min-width:240px; height:34px; padding:0 12px; border:1px solid var(--hairline); border-radius:var(--r-sm); background:var(--surface); }
|
||||
.search-input svg { width:13px; height:13px; color:var(--ink-3); }
|
||||
.search-input input { flex:1; min-width:0; border:none; outline:none; background:none; font-family:inherit; font-size:13px; color:var(--ink); }
|
||||
.search-input input::placeholder { color:var(--ink-3); }
|
||||
.fbtn { height:34px; padding:0 12px; border:1px solid var(--hairline); border-radius:var(--r-sm); background:var(--surface); font-family:inherit; font-size:12px; color:var(--ink-2); cursor:pointer; display:inline-flex; align-items:center; gap:6px; }
|
||||
.fbtn:hover { border-color:var(--ink-disabled); }
|
||||
.fbtn svg { width:12px; height:12px; color:var(--ink-3); }
|
||||
|
||||
.tbl-wrap { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); overflow:hidden; }
|
||||
.tbl { width:100%; border-collapse:collapse; font-size:12.5px; }
|
||||
.tbl thead th { text-align:left; font-size:10.5px; font-weight:600; color:var(--ink-2); padding:10px 14px; background:var(--sunken); border-bottom:1px solid var(--hairline); white-space:nowrap; letter-spacing:0.005em; }
|
||||
.tbl thead th.num { text-align:right; }
|
||||
.tbl tbody tr { border-bottom:1px solid var(--hairline-soft); cursor:pointer; }
|
||||
.tbl tbody tr:last-child { border-bottom:none; }
|
||||
.tbl tbody tr:hover { background:var(--sunken); }
|
||||
.tbl tbody td { padding:10px 14px; vertical-align:middle; }
|
||||
.tbl .t-name { font-weight:500; color:var(--ink); }
|
||||
.tbl .t-name .sub { display:block; font-size:11px; color:var(--ink-3); margin-top:2px; font-family:var(--font-mono); font-feature-settings:'tnum'; }
|
||||
.tbl .t-balance, .tbl .t-today, .tbl .t-mr { font-family:var(--font-mono); font-feature-settings:'tnum'; text-align:right; }
|
||||
.tbl .t-balance.low { color:var(--st-new); font-weight:500; }
|
||||
.tbl .t-since { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11px; color:var(--ink-3); white-space:nowrap; }
|
||||
|
||||
.chip { display:inline-flex; align-items:center; gap:6px; font-size:11.5px; font-weight:500; }
|
||||
.chip .dot { width:6px; height:6px; border-radius:50%; position:relative; }
|
||||
.chip .dot::after { content:''; position:absolute; inset:-1px; border-radius:50%; border:1px solid rgba(10,19,25,0.10); }
|
||||
.chip-active .dot { background:var(--st-quote); }
|
||||
.chip-trial .dot { background:var(--st-paid); }
|
||||
.chip-overdue .dot { background:var(--st-new); }
|
||||
.chip-suspended .dot { background:var(--ink-disabled); }
|
||||
|
||||
/* Tenant card */
|
||||
.tc-row { display:grid; grid-template-columns:1fr 360px; gap:16px; }
|
||||
.tc-main { display:flex; flex-direction:column; gap:14px; }
|
||||
.tc-aside { display:flex; flex-direction:column; gap:14px; }
|
||||
.tc-head {
|
||||
background:var(--surface); border:1px solid var(--hairline);
|
||||
border-radius:var(--r-md); padding:22px 24px;
|
||||
}
|
||||
.tc-name { font-size:24px; font-weight:600; font-variation-settings:'opsz' 24; letter-spacing:-0.018em; margin:0 0 6px; }
|
||||
.tc-meta { font-size:12.5px; color:var(--ink-3); display:flex; gap:12px; flex-wrap:wrap; }
|
||||
.tc-meta .id { font-family:var(--font-mono); color:var(--accent); font-weight:600; }
|
||||
.tc-meta .sep { color:var(--ink-disabled); }
|
||||
.tc-actions { display:flex; gap:8px; margin-top:14px; flex-wrap:wrap; }
|
||||
.tc-actions .imp { background:var(--imp-red); color:#fff; border-color:var(--imp-red); }
|
||||
.tc-actions .imp:hover { background:#8E2516; border-color:#8E2516; }
|
||||
|
||||
.tc-section { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); padding:18px 22px; }
|
||||
.tc-section h2 { font-size:13px; font-weight:600; color:var(--ink-3); letter-spacing:0.005em; text-transform:uppercase; margin:0 0 12px; }
|
||||
|
||||
.kv-grid { display:grid; grid-template-columns:120px 1fr; gap:9px 14px; font-size:13px; }
|
||||
.kv-grid .k { color:var(--ink-3); font-size:12px; }
|
||||
.kv-grid .v { color:var(--ink); }
|
||||
.kv-grid .v.mono { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:12px; }
|
||||
|
||||
.adj-form { display:grid; grid-template-columns:120px 1fr auto; gap:8px; align-items:center; margin-bottom:8px; }
|
||||
.adj-form .input { height:34px; padding:0 10px; border:1px solid var(--hairline); border-radius:var(--r-sm); font-family:var(--font-mono); font-size:13px; }
|
||||
|
||||
.timeline-mini { display:flex; flex-direction:column; gap:12px; padding-left:16px; position:relative; }
|
||||
.timeline-mini::before { content:''; position:absolute; left:5px; top:6px; bottom:6px; width:1px; background:var(--hairline); }
|
||||
.tm-item { position:relative; }
|
||||
.tm-item::before { content:''; position:absolute; left:-16px; top:4px; width:9px; height:9px; border-radius:50%; background:var(--surface); border:1.5px solid var(--ink-disabled); }
|
||||
.tm-item.acc::before { background:var(--accent); border-color:var(--accent); }
|
||||
.tm-item.warn::before { background:var(--st-call); border-color:var(--st-call); }
|
||||
.tm-item.dang::before { background:var(--st-new); border-color:var(--st-new); }
|
||||
.tm-when { font-family:var(--font-mono); font-size:10.5px; color:var(--ink-3); }
|
||||
.tm-text { font-size:12px; color:var(--ink); margin-top:2px; line-height:1.45; }
|
||||
|
||||
/* Incidents */
|
||||
.inc-row { display:grid; grid-template-columns:auto 100px 1fr 120px auto; gap:14px; padding:11px 18px; border-bottom:1px solid var(--hairline-soft); align-items:center; font-size:12.5px; }
|
||||
.inc-row:last-child { border-bottom:none; }
|
||||
.inc-row:hover { background:var(--sunken); cursor:pointer; }
|
||||
.inc-id { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11px; color:var(--accent); font-weight:500; }
|
||||
.inc-when { font-family:var(--font-mono); font-feature-settings:'tnum'; font-size:11.5px; color:var(--ink-3); }
|
||||
.inc-title { color:var(--ink); }
|
||||
.inc-title .scope { display:block; font-size:11px; color:var(--ink-3); margin-top:2px; }
|
||||
.inc-tenant { font-size:11.5px; color:var(--ink-2); }
|
||||
.inc-sev { padding:2px 8px; border-radius:100px; font-size:10.5px; font-weight:600; letter-spacing:0.04em; font-family:var(--font-mono); }
|
||||
.inc-sev.high { background:#FFE7E2; color:#8E2516; }
|
||||
.inc-sev.med { background:#FFF4DD; color:#7B4D00; }
|
||||
.inc-sev.low { background:var(--accent-tint); color:var(--accent-deep); }
|
||||
|
||||
/* System cards */
|
||||
.sys-grid { display:grid; grid-template-columns:repeat(3, 1fr); gap:14px; }
|
||||
.sys-card { background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md); padding:18px 20px; cursor:pointer; }
|
||||
.sys-card:hover { border-color:var(--ink-disabled); }
|
||||
.sys-card .sc-h { display:flex; align-items:center; gap:10px; margin-bottom:8px; }
|
||||
.sys-card .sc-ico { width:30px; height:30px; border-radius:var(--r-sm); background:var(--bg); display:inline-flex; align-items:center; justify-content:center; color:var(--accent); }
|
||||
.sys-card .sc-ico svg { width:14px; height:14px; stroke-width:1.7; }
|
||||
.sys-card .sc-name { font-size:14px; font-weight:600; letter-spacing:-0.012em; color:var(--ink); }
|
||||
.sys-card .sc-desc { font-size:12px; color:var(--ink-3); line-height:1.45; }
|
||||
.sys-card .sc-meta { font-size:11px; color:var(--ink-3); margin-top:10px; font-family:var(--font-mono); font-feature-settings:'tnum'; letter-spacing:-0.005em; }
|
||||
.sys-card .sc-meta strong { color:var(--ink-2); font-weight:500; }
|
||||
|
||||
.tab-page { display:none; }
|
||||
.tab-page.active { display:contents; }
|
||||
|
||||
@media (max-width:1100px) {
|
||||
.app { grid-template-columns:56px 1fr; }
|
||||
.side { padding:14px 6px; }
|
||||
.brand-sub, .brand .brand-text, .nav-eyebrow, .nav-text, .nav-item:not(.active) .nav-count { display:none; }
|
||||
.nav-item { justify-content:center; padding:0; }
|
||||
.topbar { padding:0 16px; }
|
||||
.content { padding:18px 18px 60px; }
|
||||
.tc-row { grid-template-columns:1fr; }
|
||||
.sys-grid { grid-template-columns:repeat(2, 1fr); }
|
||||
.sso-shell { grid-template-columns:1fr; }
|
||||
.sso-brand { padding:32px 24px; min-height:200px; }
|
||||
}
|
||||
@media (max-width:768px) {
|
||||
.app { grid-template-columns:1fr; }
|
||||
.side { display:none; }
|
||||
.topbar .crumb span:first-child { display:none; }
|
||||
.sys-grid { grid-template-columns:1fr; }
|
||||
.tbl thead { display:none; }
|
||||
.tbl, .tbl tbody, .tbl tr, .tbl td { display:block; width:100%; }
|
||||
.tbl tbody tr { padding:12px; border:1px solid var(--hairline-soft); border-radius:var(--r-sm); margin-bottom:8px; }
|
||||
.tbl tbody td { padding:3px 0; border:none; }
|
||||
.inc-row { grid-template-columns:1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="review-bar" aria-label="Состояние админ-экрана (только для review)">
|
||||
<span class="label">PREVIEW · ADMIN</span>
|
||||
<div class="tabs" role="tablist">
|
||||
<button type="button" class="tab active" data-tab="login" role="tab" aria-selected="true">SSO Вход</button>
|
||||
<button type="button" class="tab" data-tab="tenants" role="tab" aria-selected="false">Тенанты</button>
|
||||
<button type="button" class="tab" data-tab="tenant-card" role="tab" aria-selected="false">Карточка тенанта</button>
|
||||
<button type="button" class="tab" data-tab="impersonation" role="tab" aria-selected="false">Импersonation-режим</button>
|
||||
<button type="button" class="tab" data-tab="incidents" role="tab" aria-selected="false">Инциденты</button>
|
||||
<button type="button" class="tab" data-tab="system" role="tab" aria-selected="false">Система</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- ===== ADMIN/LOGIN — SSO ===== -->
|
||||
<section id="page-login" class="tab-page active">
|
||||
<div class="sso-shell">
|
||||
<aside class="sso-brand" aria-label="Брендинг">
|
||||
<div class="head">
|
||||
<span class="mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span>Лидерра<span class="dot">.</span></span>
|
||||
<span class="admin-tag">ADMIN</span>
|
||||
</div>
|
||||
<div class="body">Операторская <em>SaaS-инструмент</em>.<br>Управление тенантами, биллингом, инцидентами.</div>
|
||||
<div class="foot">v8 Forest · ограниченный доступ · все действия логируются в saas_admin_audit_log</div>
|
||||
</aside>
|
||||
<main class="sso-form">
|
||||
<div class="sso-card">
|
||||
<h1>Вход в админку</h1>
|
||||
<p>SSO через Yandex 360 — основной путь. 2FA-fallback в крайнем случае.</p>
|
||||
<button type="button" class="sso-button">
|
||||
<span class="y-ico">Я</span>
|
||||
Войти через Yandex 360
|
||||
</button>
|
||||
<div style="display:flex;align-items:center;gap:12px;margin:4px 0;color:var(--ink-3);font-size:11px"><span style="flex:1;height:1px;background:var(--hairline)"></span>или<span style="flex:1;height:1px;background:var(--hairline)"></span></div>
|
||||
<button type="button" class="btn" style="height:42px;justify-content:center;width:100%">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
Локальный пароль + 2FA
|
||||
</button>
|
||||
<div class="sso-fallback">
|
||||
Лимит 5 попыток / 15 минут · capture после 2 неудач · email при новом устройстве. <a href="#">Документация</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== ADMIN/TENANTS — list ===== -->
|
||||
<section id="page-tenants" class="tab-page">
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand">
|
||||
<span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span>Лидерра<span class="brand-dot">.</span></span>
|
||||
</div>
|
||||
<div class="brand-sub">ADMIN</div>
|
||||
<nav class="nav">
|
||||
<div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/></svg><span class="nav-text">Тенанты</span><span class="nav-count">142</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg><span class="nav-text">Инциденты</span><span class="nav-count">3</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Система</span></div>
|
||||
</nav>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Админка</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><strong>Тенанты</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<span class="user-chip"><span class="ava">АО</span><span class="uname">Админ Оператор</span></span>
|
||||
</div>
|
||||
<main class="content">
|
||||
<header class="page-h">
|
||||
<div>
|
||||
<h1 class="page-title">Тенанты</h1>
|
||||
<div class="page-meta">
|
||||
<span><span class="num">142</span> всего</span><span class="sep">·</span>
|
||||
<span><span class="num">128</span> активны</span><span class="sep">·</span>
|
||||
<span><span class="num">9</span> trial</span><span class="sep">·</span>
|
||||
<span><span class="num">5</span> просрочка</span><span class="sep">·</span>
|
||||
<span>выручка месяц <span class="num">1 248 600 ₽</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px"><button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/></svg>Экспорт</button></div>
|
||||
</header>
|
||||
<div class="t-controls">
|
||||
<label class="search-input"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg><input type="search" placeholder="ИНН, юр. лицо, email админа…" aria-label="Поиск тенантов"></label>
|
||||
<button type="button" class="fbtn"><span style="color:var(--ink-3)">Статус:</span><strong>Все</strong><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></button>
|
||||
<button type="button" class="fbtn"><span style="color:var(--ink-3)">Тариф:</span><strong>Все</strong><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></button>
|
||||
<button type="button" class="fbtn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/></svg>Ещё</button>
|
||||
</div>
|
||||
<div class="tbl-wrap">
|
||||
<table class="tbl">
|
||||
<thead><tr><th scope="col">Тенант</th><th scope="col">Статус</th><th scope="col">Тариф</th><th class="num" scope="col">Баланс ₽</th><th class="num" scope="col">Желаем×факт сегодня</th><th class="num" scope="col">MRR</th><th scope="col">Активность</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="t-name">Окна Москва ООО<span class="sub">ИНН 7724444444</span></td><td><span class="chip chip-active"><span class="dot"></span>Активен</span></td><td>Команда</td><td class="t-balance">14 250</td><td class="t-today">12 × 11</td><td class="t-mr">990</td><td class="t-since">28 мин назад</td></tr>
|
||||
<tr><td class="t-name">Натяжные потолки СПб<span class="sub">ИНН 7805123456</span></td><td><span class="chip chip-active"><span class="dot"></span>Активен</span></td><td>Команда</td><td class="t-balance">38 100</td><td class="t-today">8 × 14</td><td class="t-mr">990</td><td class="t-since">14 мин назад</td></tr>
|
||||
<tr><td class="t-name">Кухни на заказ Екб<span class="sub">ИНН 6671987654</span></td><td><span class="chip chip-active"><span class="dot"></span>Активен</span></td><td>Pro</td><td class="t-balance">112 800</td><td class="t-today">5 × 6</td><td class="t-mr">2 990</td><td class="t-since">1 ч назад</td></tr>
|
||||
<tr><td class="t-name">Ремонт под ключ<span class="sub">ИНН 5024333222</span></td><td><span class="chip chip-trial"><span class="dot"></span>Trial · 4 дня</span></td><td>Trial</td><td class="t-balance">450</td><td class="t-today">3 × 0</td><td class="t-mr" style="color:var(--ink-3)">—</td><td class="t-since">3 ч назад</td></tr>
|
||||
<tr><td class="t-name">Двери Премиум<span class="sub">ИНН 7732111000</span></td><td><span class="chip chip-overdue"><span class="dot"></span>Просрочка 3 дня</span></td><td>Команда</td><td class="t-balance low">−1 200</td><td class="t-today">0 × 0</td><td class="t-mr">990</td><td class="t-since">2 дня</td></tr>
|
||||
<tr><td class="t-name">Оконные системы РФ<span class="sub">ИНН 7707654321</span></td><td><span class="chip chip-suspended"><span class="dot"></span>Приостановлен</span></td><td>Start</td><td class="t-balance" style="color:var(--ink-disabled)">0</td><td class="t-today">0 × 0</td><td class="t-mr" style="color:var(--ink-3)">—</td><td class="t-since">7 дней</td></tr>
|
||||
<tr><td class="t-name">Кухонная мебель Спб<span class="sub">ИНН 7813222333</span></td><td><span class="chip chip-active"><span class="dot"></span>Активен</span></td><td>Enterprise</td><td class="t-balance">486 200</td><td class="t-today">28 × 31</td><td class="t-mr">9 990</td><td class="t-since">8 мин назад</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== ADMIN/TENANT-CARD ===== -->
|
||||
<section id="page-tenant-card" class="tab-page">
|
||||
<div class="app">
|
||||
<aside class="side"><div class="brand"><span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span><span>Лидерра<span class="brand-dot">.</span></span></div><div class="brand-sub">ADMIN</div><nav class="nav"><div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/></svg><span class="nav-text">Тенанты</span><span class="nav-count">142</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg><span class="nav-text">Инциденты</span><span class="nav-count">3</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Система</span></div></nav></aside>
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Админка</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><a>Тенанты</a><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><strong>Окна Москва ООО</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<span class="user-chip"><span class="ava">АО</span><span class="uname">Админ Оператор</span></span>
|
||||
</div>
|
||||
<main class="content">
|
||||
<div class="tc-row">
|
||||
<div class="tc-main">
|
||||
<header class="tc-head">
|
||||
<h1 class="tc-name">Окна Москва ООО</h1>
|
||||
<div class="tc-meta">
|
||||
<span class="id">#TNT-0042</span>
|
||||
<span>ИНН 7724444444 / КПП 772401001</span>
|
||||
<span class="sep">·</span>
|
||||
<span>с 12.04.2026 (3 недели)</span>
|
||||
<span class="sep">·</span>
|
||||
<span><span class="chip chip-active"><span class="dot"></span>Активен</span></span>
|
||||
</div>
|
||||
<div class="tc-actions">
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4z"/></svg>Редактировать</button>
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/></svg>Корректировать баланс</button>
|
||||
<button type="button" class="btn imp"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="7" r="4"/><path d="M2 21v-2a4 4 0 0 1 4-4h12a4 4 0 0 1 4 4v2"/></svg>Войти как клиент</button>
|
||||
<button type="button" class="btn btn-danger"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg>Удалить (152-ФЗ)</button>
|
||||
</div>
|
||||
</header>
|
||||
<section class="tc-section">
|
||||
<h2>Профиль</h2>
|
||||
<div class="kv-grid">
|
||||
<span class="k">Юр. лицо</span><span class="v">ООО «Окна Москва»</span>
|
||||
<span class="k">Юр. адрес</span><span class="v">115093, Москва, ул. Большая Серпуховская, 32 стр. 1, оф. 405</span>
|
||||
<span class="k">Расчётный счёт</span><span class="v mono">40702810400000000123 · ПАО «Сбербанк»</span>
|
||||
<span class="k">Email админа</span><span class="v">ivan.petrov@example.ru</span>
|
||||
<span class="k">Телефон</span><span class="v mono">+7 (495) 482-91-22</span>
|
||||
<span class="k">Тариф</span><span class="v">Команда (990 ₽/мес) · автопродление</span>
|
||||
<span class="k">Активные проекты</span><span class="v mono">3 / 10</span>
|
||||
<span class="k">Менеджеры</span><span class="v mono">4</span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tc-section">
|
||||
<h2>Корректировка баланса</h2>
|
||||
<div class="adj-form">
|
||||
<input type="text" class="input" placeholder="Сумма" value="+ 5 000">
|
||||
<input type="text" class="input" placeholder="Причина (обязательно)" value="Возврат по обращению ticket-2841: ошибка дублирования лида">
|
||||
<button type="button" class="btn btn-primary">Применить</button>
|
||||
</div>
|
||||
<div style="font-size:11px;color:var(--ink-3);margin-top:6px">Запись в saas_admin_audit_log будет создана автоматически. Действие обратимо в течение 24 часов.</div>
|
||||
</section>
|
||||
</div>
|
||||
<aside class="tc-aside">
|
||||
<section class="tc-section">
|
||||
<h2>Баланс и активность</h2>
|
||||
<div class="kv-grid">
|
||||
<span class="k">Баланс ₽</span><span class="v mono" style="color:var(--accent);font-weight:600">14 250 ₽</span>
|
||||
<span class="k">Лидов запас</span><span class="v mono">285</span>
|
||||
<span class="k">Желаем сегодня</span><span class="v mono">12</span>
|
||||
<span class="k">Факт сегодня</span><span class="v mono">11</span>
|
||||
<span class="k">MRR</span><span class="v mono">990 ₽</span>
|
||||
<span class="k">LTV (3 нед.)</span><span class="v mono">2 970 ₽</span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tc-section">
|
||||
<h2>Журнал действий</h2>
|
||||
<div class="timeline-mini">
|
||||
<div class="tm-item warn"><div class="tm-when">07.05 · 14:48</div><div class="tm-text">Админ <strong>Иван Оператор</strong> вошёл как клиент (impersonation, 4 мин)</div></div>
|
||||
<div class="tm-item acc"><div class="tm-when">06.05 · 22:06</div><div class="tm-text">Пополнение через ЮKassa: + 10 000 ₽</div></div>
|
||||
<div class="tm-item dang"><div class="tm-when">04.05 · 16:42</div><div class="tm-text">Попытка пополнения через банковский перевод — отклонено</div></div>
|
||||
<div class="tm-item"><div class="tm-when">12.04 · 09:32</div><div class="tm-text">Регистрация · trial 7 дней</div></div>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== IMPERSONATION MODE ===== -->
|
||||
<section id="page-impersonation" class="tab-page">
|
||||
<div class="imp-banner" role="status">
|
||||
<span style="display:inline-flex;align-items:center;gap:8px">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.7"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12" y2="17"/></svg>
|
||||
<strong>IMPERSONATION</strong>
|
||||
<span style="opacity:0.85">Вы вошли как <strong>Окна Москва ООО / Иван Петров</strong> · действия логируются как admin · 04:32</span>
|
||||
</span>
|
||||
<button type="button">Выйти из режима</button>
|
||||
</div>
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand"><span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span><span>Лидерра<span class="brand-dot">.</span></span></div>
|
||||
<nav class="nav">
|
||||
<div class="nav-eyebrow">Работа</div>
|
||||
<div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg><span class="nav-text">Дашборд</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 6h18M6 12h12M9 18h6"/></svg><span class="nav-text">Сделки</span><span class="nav-count">247</span></div>
|
||||
<div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="18"/></svg><span class="nav-text">Канбан</span></div>
|
||||
</nav>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<div class="topbar"><div class="crumb"><strong>Дашборд</strong> <span style="color:var(--imp-red);font-weight:600;font-size:11.5px;font-family:var(--font-mono)">· через клиента</span></div><div class="topbar-spacer"></div><span class="user-chip"><span class="ava" style="background:var(--imp-red)">АО</span><span class="uname">Админ → ИП</span></span></div>
|
||||
<main class="content"><header class="page-h"><div><h1 class="page-title">Доброе утро, Иван</h1><div class="page-meta"><span><span class="num" style="color:var(--accent);font-weight:600">+3</span> новых лида с утра</span></div></div></header>
|
||||
<p style="font-size:13px;color:var(--ink-3);line-height:1.6;max-width:600px">Это дашборд клиента в режиме <strong style="color:var(--imp-red)">impersonation</strong>. Все действия фиксируются в saas_admin_audit_log с пометкой импersonator + причина (обязательно при входе). Выход из режима через красный баннер вверху.</p>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== INCIDENTS ===== -->
|
||||
<section id="page-incidents" class="tab-page">
|
||||
<div class="app">
|
||||
<aside class="side"><div class="brand"><span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span><span>Лидерра<span class="brand-dot">.</span></span></div><div class="brand-sub">ADMIN</div><nav class="nav"><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Тенанты</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></div><div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg><span class="nav-text">Инциденты</span><span class="nav-count">3</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Система</span></div></nav></aside>
|
||||
<div class="main">
|
||||
<div class="topbar"><div class="crumb"><span>Админка</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><strong>Журнал инцидентов</strong></div><div class="topbar-spacer"></div><span class="user-chip"><span class="ava">АО</span><span class="uname">Админ Оператор</span></span></div>
|
||||
<main class="content">
|
||||
<header class="page-h"><div><h1 class="page-title">Журнал инцидентов</h1><div class="page-meta"><span><span class="num">3</span> открытых</span><span class="sep">·</span><span><span class="num">87</span> за последние 30 дней</span><span class="sep">·</span><span>read-only для compliance</span></div></div></header>
|
||||
<div class="tbl-wrap">
|
||||
<div class="inc-row" style="background:var(--sunken);font-size:10.5px;font-weight:600;color:var(--ink-2);padding:10px 18px;border-bottom:1px solid var(--hairline)"><span>ID</span><span>Когда</span><span>Описание</span><span>Тенант</span><span>Severity</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0034</span><span class="inc-when">07.05 · 14:23</span><span class="inc-title">Webhook timeout 5 раз подряд<span class="scope">endpoint /api/lead-in · TG-бот тенанта</span></span><span class="inc-tenant">Окна Москва ООО</span><span class="inc-sev high">HIGH</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0033</span><span class="inc-when">07.05 · 09:14</span><span class="inc-title">SMS-шлюз timeout > 400ms<span class="scope">SMSC.RU деградация · 96.4%</span></span><span class="inc-tenant">Все тенанты</span><span class="inc-sev med">MED</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0032</span><span class="inc-when">06.05 · 22:48</span><span class="inc-title">Дублирование лида<span class="scope">Я.Директ webhook прислал дубль за 18 секунд</span></span><span class="inc-tenant">Натяжные потолки СПб</span><span class="inc-sev low">LOW</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0031</span><span class="inc-when">06.05 · 16:01</span><span class="inc-title">Failed Stripe payment retry<span class="scope">3 попытки за 7 дней · перевод в manual</span></span><span class="inc-tenant">Двери Премиум</span><span class="inc-sev med">MED</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0030</span><span class="inc-when">05.05 · 11:32</span><span class="inc-title">RLS bypass attempt<span class="scope">tenant_id mismatch на /api/deals — отбито</span></span><span class="inc-tenant">Кухни на заказ Екб</span><span class="inc-sev high">HIGH</span></div>
|
||||
<div class="inc-row"><span class="inc-id">INC-0029</span><span class="inc-when">05.05 · 08:22</span><span class="inc-title">Бэкап БД задержка<span class="scope">snapshot задержался на 38 минут — без потерь</span></span><span class="inc-tenant">Все тенанты</span><span class="inc-sev low">LOW</span></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== SYSTEM ===== -->
|
||||
<section id="page-system" class="tab-page">
|
||||
<div class="app">
|
||||
<aside class="side"><div class="brand"><span class="brand-mark"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span><span>Лидерра<span class="brand-dot">.</span></span></div><div class="brand-sub">ADMIN</div><nav class="nav"><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Тенанты</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/></svg><span class="nav-text">Биллинг</span></div><div class="nav-item"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg><span class="nav-text">Инциденты</span><span class="nav-count">3</span></div><div class="nav-item active"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Система</span></div></nav></aside>
|
||||
<div class="main">
|
||||
<div class="topbar"><div class="crumb"><span>Админка</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg><strong>Система</strong></div><div class="topbar-spacer"></div><span class="user-chip"><span class="ava">АО</span><span class="uname">Админ Оператор</span></span></div>
|
||||
<main class="content">
|
||||
<header class="page-h"><div><h1 class="page-title">Системные настройки</h1><div class="page-meta"><span>super_admin доступ</span><span class="sep">·</span><span>все изменения логируются</span></div></div></header>
|
||||
<div class="sys-grid">
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M12 1v6M12 17v6M4.22 4.22l4.24 4.24M15.54 15.54l4.24 4.24M1 12h6M17 12h6M4.22 19.78l4.24-4.24M15.54 8.46l4.24-4.24"/></svg></span><span class="sc-name">system_settings</span></div><div class="sc-desc">Глобальные параметры платформы: лимиты, таймауты, фичефлаги</div><div class="sc-meta"><strong>27</strong> параметров · обновлено 3 часа назад</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5z"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg></span><span class="sc-name">tariff_plans</span></div><div class="sc-desc">Тарифы Start/Basic/Pro/Enterprise — цена, лимиты, фичи</div><div class="sc-meta"><strong>4</strong> тарифа · последнее изменение 21.04</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 7v13h18V7M3 7l3-4h12l3 4M3 7h18"/></svg></span><span class="sc-name">legal_entities</span></div><div class="sc-desc">Юр. лица оператора — для счетов и УПД</div><div class="sc-meta"><strong>1</strong> активно · ИП Сидоров А.А.</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="1"/><path d="M3 10h18M7 15h3"/></svg></span><span class="sc-name">payment_gateways</span></div><div class="sc-desc">ЮKassa, Stripe, банковский перевод — статус и комиссии</div><div class="sc-meta"><strong>3</strong> активных · ЮKassa primary</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 2L13 10M16 2h5v5"/><path d="M21 13v5a2 2 0 0 1-2 2h-14a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"/></svg></span><span class="sc-name">webhook_log</span></div><div class="sc-desc">Все outbound и inbound webhook-события за 30 дней</div><div class="sc-meta"><strong>284 612</strong> событий · 99.97% uptime</div></button>
|
||||
<button type="button" class="sys-card"><div class="sc-h"><span class="sc-ico"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></span><span class="sc-name">auth_log</span></div><div class="sc-desc">Все вход/выход события — клиенты, админы, impersonation</div><div class="sc-meta"><strong>4 821</strong> событий за 7 дней</div></button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const tabs = Array.from(document.querySelectorAll('.review-bar .tab'));
|
||||
const pages = Array.from(document.querySelectorAll('.tab-page'));
|
||||
function setTab(id) {
|
||||
tabs.forEach(t => {
|
||||
const isA = t.dataset.tab === id;
|
||||
t.classList.toggle('active', isA);
|
||||
t.setAttribute('aria-selected', isA ? 'true' : 'false');
|
||||
});
|
||||
pages.forEach(p => p.classList.toggle('active', p.id === 'page-' + id));
|
||||
}
|
||||
tabs.forEach(t => t.addEventListener('click', () => setTab(t.dataset.tab)));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,370 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Биллинг — Лидерра (Forest)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
@media (prefers-reduced-motion: reduce) { .has-motion { animation: none !important; transition: none !important; } }
|
||||
|
||||
:root {
|
||||
--bg: #F6F3EC; --surface: #FFFDFA; --sunken: #F0EDE4;
|
||||
--hairline: #D9D5CD; --hairline-soft:#E8E3D6;
|
||||
--ink: #081319; --ink-2: #343C41; --ink-3: #66635C; --ink-disabled: #92907B;
|
||||
--accent: #0F6E56; --accent-tint: #E1EEEA; --accent-deep: #084635;
|
||||
--side-bg: #012019; --side-text: #B1C2BD; --side-text-2: #7A8C87;
|
||||
--side-active: #13382F; --side-icon: #5C7A72; --side-icon-act: #32C8A9;
|
||||
--side-hover: #0A2A22; --side-border: #1A3A30;
|
||||
--st-paid-solid: #007EB8; --st-wait-solid: #00889B; --st-fail-solid: #6C60C4;
|
||||
--st-new-solid: #B94837; --st-quote-solid: #008A4D; --st-call-solid: #9A6700;
|
||||
--r-xs:4px; --r-sm:6px; --r-md:10px; --r-lg:14px;
|
||||
--font-ui:'Inter',system-ui,sans-serif;
|
||||
--font-mono:'JetBrains Mono',ui-monospace,monospace;
|
||||
}
|
||||
body { background: var(--bg); color: var(--ink); font-family: var(--font-ui); font-feature-settings: 'cv11','ss01','ss03'; -webkit-font-smoothing: antialiased; font-variation-settings: 'opsz' 14; min-height: 100vh; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font-family: inherit; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible, [tabindex]:focus-visible {
|
||||
outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm);
|
||||
}
|
||||
.skip-link { position: absolute; top: -40px; left: 12px; background: var(--ink); color: #fff; padding: 9px 16px; z-index: 200; font-weight: 600; border-radius: var(--r-sm); }
|
||||
.skip-link:focus { top: 12px; }
|
||||
|
||||
/* Shell — same as v8_dashboard */
|
||||
.app { display: grid; grid-template-columns: 232px 1fr; min-height: 100vh; }
|
||||
.side { background: var(--side-bg); border-right: 1px solid var(--side-border); padding: 18px 12px 24px; position: sticky; top: 0; height: 100vh; overflow-y: auto; color: var(--side-text); }
|
||||
.brand { display: flex; align-items: center; gap: 10px; padding: 6px 8px 18px; font-weight: 600; font-size: 14.5px; letter-spacing: -0.01em; font-variation-settings: 'opsz' 18; color: #FFFFFF; }
|
||||
.brand-mark { width: 22px; height: 22px; border-radius: var(--r-xs); background: #FFFFFF; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; overflow: hidden; }
|
||||
.brand-mark svg { width: 100%; height: 100%; display: block; }
|
||||
.brand-dot { color: var(--side-icon-act); }
|
||||
.nav { display: flex; flex-direction: column; gap: 1px; }
|
||||
.nav-eyebrow { font-size: 11px; font-weight: 500; letter-spacing: 0.01em; color: var(--side-text-2); padding: 14px 10px 6px; }
|
||||
.nav-item { display: flex; align-items: center; gap: 10px; height: 32px; padding: 0 10px; border-radius: var(--r-sm); font-size: 13px; color: var(--side-text); }
|
||||
.nav-item:hover { background: var(--side-hover); color: #FFFFFF; }
|
||||
.nav-item.active { background: var(--side-active); color: #FFFFFF; font-weight: 500; }
|
||||
.nav-item.active .nav-icon { color: var(--side-icon-act); }
|
||||
.nav-icon { width: 15px; height: 15px; flex-shrink: 0; color: var(--side-icon); stroke-width: 1.6; }
|
||||
.nav-item:hover .nav-icon { color: #FFFFFF; }
|
||||
.nav-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.nav-count { font-family: var(--font-mono); font-size: 10.5px; font-weight: 500; font-feature-settings: 'tnum'; background: rgba(255,255,255,0.10); color: var(--side-text); padding: 2px 6px; border-radius: 4px; }
|
||||
.main { display: flex; flex-direction: column; min-width: 0; }
|
||||
.topbar { height: 48px; border-bottom: 1px solid var(--hairline); background: var(--surface); padding: 0 24px; display: flex; align-items: center; gap: 12px; position: sticky; top: 0; z-index: 50; }
|
||||
.crumb { font-size: 12.5px; color: var(--ink-3); display: flex; align-items: center; gap: 8px; }
|
||||
.crumb strong { color: var(--ink); font-weight: 500; }
|
||||
.crumb svg { width: 11px; height: 11px; color: var(--ink-3); opacity: 0.5; }
|
||||
.topbar-spacer { flex: 1; }
|
||||
.user-chip { display: inline-flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px 0 4px; border-radius: 100px; border: 1px solid var(--hairline); background: var(--surface); }
|
||||
.user-chip .ava { width: 22px; height: 22px; border-radius: 50%; background: var(--ink); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 9.5px; font-weight: 600; }
|
||||
.user-chip .uname { font-size: 12px; color: var(--ink); font-weight: 500; }
|
||||
|
||||
/* Page */
|
||||
.content { padding: 24px 28px 80px; max-width: 1500px; margin: 0 auto; }
|
||||
.page-h { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 22px; gap: 16px; }
|
||||
.page-title { font-size: 28px; font-weight: 600; font-variation-settings: 'opsz' 28; letter-spacing: -0.02em; line-height: 1.1; margin: 0 0 6px; }
|
||||
.page-meta { font-size: 12.5px; color: var(--ink-3); display: flex; gap: 14px; align-items: center; }
|
||||
.page-meta .num { font-family: var(--font-mono); font-feature-settings: 'tnum'; color: var(--ink-2); font-weight: 500; }
|
||||
.page-meta .accent-num { color: var(--accent); font-weight: 600; }
|
||||
.page-meta .sep { color: var(--ink-disabled); }
|
||||
|
||||
.btn { display: inline-flex; align-items: center; gap: 7px; height: 34px; padding: 0 14px; border-radius: var(--r-sm); border: 1px solid var(--hairline); background: var(--surface); font-size: 12.5px; font-weight: 500; color: var(--ink); cursor: pointer; font-family: inherit; letter-spacing: -0.005em; }
|
||||
.btn:hover { border-color: var(--ink-disabled); background: var(--sunken); }
|
||||
.btn-primary { background: var(--accent); color: #fff; border-color: var(--accent); }
|
||||
.btn-primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
|
||||
.btn svg { width: 13px; height: 13px; stroke-width: 1.7; }
|
||||
|
||||
/* Wallet hero — 2 islands: ₽ + ГЦК */
|
||||
.wallet-row { display: grid; grid-template-columns: 1.3fr 1fr 1fr; gap: 12px; margin-bottom: 18px; }
|
||||
.wallet-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); padding: 20px 22px; min-height: 158px; display: flex; flex-direction: column; gap: 12px; position: relative; overflow: hidden; }
|
||||
.wallet-card.primary::before { content: ''; position: absolute; inset: -1px; border: 1px solid var(--accent); opacity: 0.55; border-radius: var(--r-md); pointer-events: none; }
|
||||
.wallet-h { display: flex; align-items: center; justify-content: space-between; }
|
||||
.wallet-label { font-size: 11.5px; color: var(--ink-3); font-weight: 500; }
|
||||
.wallet-tag { font-family: var(--font-mono); font-size: 9.5px; font-weight: 600; letter-spacing: 0.06em; padding: 2px 6px; border-radius: 3px; background: var(--accent-tint); color: var(--accent-deep); }
|
||||
.wallet-amount { display: flex; align-items: baseline; gap: 4px; }
|
||||
.wallet-amount .num { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-weight: 600; font-size: 38px; letter-spacing: -0.03em; line-height: 1; color: var(--ink); }
|
||||
.wallet-amount .ru { font-family: var(--font-mono); font-size: 16px; color: var(--ink-3); }
|
||||
.wallet-actions { display: flex; gap: 8px; margin-top: auto; }
|
||||
.wallet-actions .btn { flex: 1; justify-content: center; }
|
||||
.wallet-foot { font-size: 11.5px; color: var(--ink-2); font-family: var(--font-mono); font-feature-settings: 'tnum'; }
|
||||
.wallet-foot strong { color: var(--accent); font-weight: 600; }
|
||||
|
||||
/* Tariff card */
|
||||
.tariff-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); padding: 18px 20px; min-height: 158px; display: flex; flex-direction: column; gap: 10px; }
|
||||
.tariff-name { font-size: 16px; font-weight: 600; font-variation-settings: 'opsz' 18; letter-spacing: -0.012em; }
|
||||
.tariff-feats { font-size: 11.5px; color: var(--ink-2); display: flex; flex-direction: column; gap: 4px; line-height: 1.4; }
|
||||
.tariff-feats span { display: flex; align-items: center; gap: 6px; }
|
||||
.tariff-feats .check { color: var(--accent); flex-shrink: 0; width: 11px; height: 11px; }
|
||||
|
||||
/* History panel */
|
||||
.panel { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); margin-bottom: 14px; overflow: hidden; }
|
||||
.panel-h { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px 12px; gap: 12px; flex-wrap: wrap; }
|
||||
.panel-h h2 { font-size: 16px; font-weight: 600; font-variation-settings: 'opsz' 18; letter-spacing: -0.012em; margin: 0; }
|
||||
.panel-tabs { display: inline-flex; gap: 2px; padding: 2px; background: var(--sunken); border-radius: var(--r-sm); }
|
||||
.panel-tabs button { height: 26px; padding: 0 10px; border: none; background: transparent; font-family: inherit; font-size: 11.5px; font-weight: 500; color: var(--ink-3); cursor: pointer; border-radius: 4px; }
|
||||
.panel-tabs button.active { background: var(--surface); color: var(--ink); box-shadow: inset 0 0 0 1px var(--hairline); }
|
||||
|
||||
.tx-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
|
||||
.tx-table thead th { text-align: left; font-size: 10.5px; font-weight: 600; letter-spacing: 0.005em; color: var(--ink-2); padding: 9px 14px; background: var(--sunken); border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); white-space: nowrap; }
|
||||
.tx-table thead th.num { text-align: right; }
|
||||
.tx-table tbody tr { border-bottom: 1px solid var(--hairline-soft); }
|
||||
.tx-table tbody tr:last-child { border-bottom: none; }
|
||||
.tx-table tbody tr:hover { background: var(--sunken); }
|
||||
.tx-table tbody td { padding: 9px 14px; vertical-align: middle; }
|
||||
.tx-table .tx-when { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-size: 11.5px; color: var(--ink-3); }
|
||||
.tx-table .tx-id { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-size: 11.5px; color: var(--accent); }
|
||||
.tx-table .tx-amount { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-weight: 500; text-align: right; }
|
||||
.tx-amount.up { color: var(--accent); }
|
||||
.tx-amount.down { color: var(--ink); }
|
||||
.tx-amount.refund { color: var(--st-fail-solid); }
|
||||
|
||||
.chip { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 500; }
|
||||
.chip .dot { width: 6px; height: 6px; border-radius: 50%; position: relative; }
|
||||
.chip .dot::after { content: ''; position: absolute; inset: -1px; border-radius: 50%; border: 1px solid rgba(10,19,25,0.10); }
|
||||
.chip-paid .dot { background: var(--st-quote-solid); }
|
||||
.chip-pending .dot { background: var(--st-call-solid); }
|
||||
.chip-failed .dot { background: var(--st-new-solid); }
|
||||
.chip-refund .dot { background: var(--st-fail-solid); }
|
||||
|
||||
.pending-banner { background: #FFF8E5; border: 1px solid #F0D89E; border-radius: var(--r-md); padding: 12px 16px; margin-bottom: 18px; display: flex; align-items: center; gap: 12px; font-size: 12.5px; color: #614209; }
|
||||
.pending-banner .ico { width: 18px; height: 18px; color: #B35100; flex-shrink: 0; }
|
||||
.pending-banner strong { color: #4A2F00; font-weight: 600; }
|
||||
|
||||
.invoices-list { display: flex; flex-direction: column; }
|
||||
.inv-row { display: grid; grid-template-columns: 110px 1fr auto auto; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--hairline-soft); align-items: center; font-size: 12.5px; }
|
||||
.inv-row:last-child { border-bottom: none; }
|
||||
.inv-row:hover { background: var(--sunken); }
|
||||
.inv-when { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-size: 11.5px; color: var(--ink-3); }
|
||||
.inv-name { color: var(--ink); }
|
||||
.inv-name .sub { font-size: 11px; color: var(--ink-3); display: block; margin-top: 2px; }
|
||||
.inv-amount { font-family: var(--font-mono); font-feature-settings: 'tnum'; font-weight: 500; text-align: right; }
|
||||
.inv-action { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border: 1px solid var(--hairline); background: var(--surface); border-radius: var(--r-sm); font-size: 11.5px; color: var(--ink-2); cursor: pointer; font-family: inherit; }
|
||||
.inv-action:hover { border-color: var(--ink-disabled); color: var(--ink); }
|
||||
.inv-action svg { width: 11px; height: 11px; }
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.app { grid-template-columns: 56px 1fr; }
|
||||
.side { padding: 14px 6px; }
|
||||
.brand .brand-text, .nav-eyebrow, .nav-text, .nav-item:not(.active) .nav-count { display: none; }
|
||||
.nav-item { justify-content: center; padding: 0; }
|
||||
.topbar { padding: 0 16px; }
|
||||
.content { padding: 18px 18px 60px; }
|
||||
.wallet-row { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.app { grid-template-columns: 1fr; }
|
||||
.side { display: none; }
|
||||
.topbar .crumb span:first-child { display: none; }
|
||||
.page-h { flex-direction: column; align-items: flex-start; gap: 12px; }
|
||||
.wallet-row { grid-template-columns: 1fr; }
|
||||
.tx-table thead { display: none; }
|
||||
.tx-table, .tx-table tbody, .tx-table tr, .tx-table td { display: block; width: 100%; }
|
||||
.tx-table tbody tr { padding: 12px 16px; border-bottom: 1px solid var(--hairline-soft); }
|
||||
.tx-table tbody td { padding: 3px 0; border: none; }
|
||||
.inv-row { grid-template-columns: 1fr; }
|
||||
.inv-row .inv-amount, .inv-row .inv-action { justify-self: flex-start; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main" class="skip-link">К контенту</a>
|
||||
<div class="app">
|
||||
<aside class="side" aria-label="Главное меню">
|
||||
<div class="brand">
|
||||
<span class="brand-mark" aria-hidden="true"><svg viewBox="0 0 48 48"><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg></span>
|
||||
<span class="brand-text">Лидерра<span class="brand-dot">.</span></span>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<div class="nav-eyebrow">Работа</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg><span class="nav-text">Дашборд</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18M6 12h12M9 18h6"/></svg><span class="nav-text">Сделки</span><span class="nav-count">247</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="6" height="18"/><rect x="11" y="3" width="6" height="12"/></svg><span class="nav-text">Канбан</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg><span class="nav-text">Напоминания</span><span class="nav-count">12</span></a>
|
||||
<div class="nav-eyebrow">Финансы</div>
|
||||
<a class="nav-item active" href="#" aria-current="page"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="1"/><path d="M3 10h18M7 15h3"/></svg><span class="nav-text">Биллинг</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h18M3 6h18M3 18h18"/></svg><span class="nav-text">Отчёты</span></a>
|
||||
<div class="nav-eyebrow">Команда</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg><span class="nav-text">Менеджеры</span><span class="nav-count">4</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/></svg><span class="nav-text">Настройки</span></a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<div class="crumb"><span>Рабочая область</span><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg><strong>Биллинг</strong></div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<button type="button" class="user-chip" aria-label="Профиль · Иван Петров"><span class="ava" aria-hidden="true">ИП</span><span class="uname">Иван П.</span></button>
|
||||
</div>
|
||||
|
||||
<main id="main" class="content">
|
||||
<header class="page-h">
|
||||
<div>
|
||||
<h1 class="page-title">Биллинг и тарифы</h1>
|
||||
<div class="page-meta">
|
||||
<span><span class="accent-num num">14 250 ₽</span> кошелёк</span>
|
||||
<span class="sep">·</span>
|
||||
<span><span class="num">285</span> лидов запас</span>
|
||||
<span class="sep">·</span>
|
||||
<span>хватит на <span class="num">4 дня</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Пополнить баланс</button>
|
||||
</header>
|
||||
|
||||
<!-- Pending banner -->
|
||||
<div class="pending-banner" role="status" aria-live="polite">
|
||||
<svg class="ico" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.7" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg>
|
||||
<span><strong>1 платёж в обработке</strong> — 5 000 ₽ от ЮKassa, начат 14:21. Авто-восстановление в 14:51 (30 мин). Кнопки «Отменить» нет — это техническое решение.</span>
|
||||
</div>
|
||||
|
||||
<!-- Wallet + Tariff -->
|
||||
<div class="wallet-row">
|
||||
<article class="wallet-card primary" aria-labelledby="wallet-rub">
|
||||
<div class="wallet-h"><span id="wallet-rub" class="wallet-label">Кошелёк ₽</span><span class="wallet-tag">LIVE</span></div>
|
||||
<div class="wallet-amount"><span class="num">14 250</span><span class="ru">₽</span></div>
|
||||
<div class="wallet-foot">мин. пополнение <strong>100 ₽</strong> · округление вниз ₽→лиды</div>
|
||||
<div class="wallet-actions">
|
||||
<button type="button" class="btn btn-primary"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Пополнить</button>
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h18M3 6h18M3 18h18"/></svg>Автопополнение</button>
|
||||
</div>
|
||||
</article>
|
||||
<article class="wallet-card" aria-labelledby="wallet-leads">
|
||||
<div class="wallet-h"><span id="wallet-leads" class="wallet-label">Баланс лидов (ГЦК)</span></div>
|
||||
<div class="wallet-amount"><span class="num">285</span><span class="ru" style="font-style:normal">лидов</span></div>
|
||||
<div class="wallet-foot">средняя цена <strong>50 ₽/лид</strong> · потрачено за месяц 412</div>
|
||||
</article>
|
||||
<article class="tariff-card" aria-labelledby="tariff-name">
|
||||
<div class="wallet-label">Тариф</div>
|
||||
<div id="tariff-name" class="tariff-name">Команда <span style="font-family:var(--font-mono);color:var(--accent);font-size:13px;font-weight:500">· 990 ₽/мес</span></div>
|
||||
<div class="tariff-feats">
|
||||
<span><svg class="check" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.2" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>до 10 проектов</span>
|
||||
<span><svg class="check" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.2" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>4 менеджера + расширение</span>
|
||||
<span><svg class="check" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.2" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>Канбан, Webhook, API</span>
|
||||
</div>
|
||||
<button type="button" class="btn" style="margin-top:auto;justify-content:center">Сменить тариф →</button>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<!-- Transactions -->
|
||||
<section class="panel" aria-labelledby="tx-title">
|
||||
<div class="panel-h">
|
||||
<h2 id="tx-title">История транзакций</h2>
|
||||
<div class="panel-tabs" role="tablist" aria-label="Тип операций">
|
||||
<button type="button" role="tab" aria-selected="true" class="active">Все</button>
|
||||
<button type="button" role="tab" aria-selected="false">Пополнения</button>
|
||||
<button type="button" role="tab" aria-selected="false">Списания</button>
|
||||
<button type="button" role="tab" aria-selected="false">Возвраты</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tx-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Дата</th>
|
||||
<th scope="col">Операция</th>
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Статус</th>
|
||||
<th class="num" scope="col">Сумма</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tx-when">07.05 · 14:21</td>
|
||||
<td>Пополнение через ЮKassa</td>
|
||||
<td class="tx-id">#TX-89421</td>
|
||||
<td><span class="chip chip-pending"><span class="dot" aria-hidden="true"></span>В обработке</span></td>
|
||||
<td class="tx-amount up">+ 5 000 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">07.05 · 11:14</td>
|
||||
<td>Списание · 3 лида проект «Окна Москва»</td>
|
||||
<td class="tx-id">#TX-89384</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount down">− 6 600 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">07.05 · 09:48</td>
|
||||
<td>Возврат лида #1018 · дубликат</td>
|
||||
<td class="tx-id">#TX-89370</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount refund">+ 2 200 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">06.05 · 22:06</td>
|
||||
<td>Пополнение через ЮKassa</td>
|
||||
<td class="tx-id">#TX-89312</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount up">+ 10 000 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">06.05 · 18:32</td>
|
||||
<td>Списание · 5 лидов проект «Натяжные потолки»</td>
|
||||
<td class="tx-id">#TX-89286</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount down">− 9 250 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">05.05 · 12:00</td>
|
||||
<td>Списание абонентской платы тарифа «Команда»</td>
|
||||
<td class="tx-id">#TX-89108</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount down">− 990 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">04.05 · 16:42</td>
|
||||
<td>Попытка пополнения через банковский перевод</td>
|
||||
<td class="tx-id">#TX-88937</td>
|
||||
<td><span class="chip chip-failed"><span class="dot" aria-hidden="true"></span>Отклонено</span></td>
|
||||
<td class="tx-amount" style="color:var(--ink-3)">— 0 ₽</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tx-when">03.05 · 09:18</td>
|
||||
<td>Возврат лида #998 · спам</td>
|
||||
<td class="tx-id">#TX-88714</td>
|
||||
<td><span class="chip chip-paid"><span class="dot" aria-hidden="true"></span>Проведён</span></td>
|
||||
<td class="tx-amount refund">+ 1 850 ₽</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<!-- Invoices -->
|
||||
<section class="panel" aria-labelledby="inv-title">
|
||||
<div class="panel-h">
|
||||
<h2 id="inv-title">Счета и УПД</h2>
|
||||
<button type="button" class="btn"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>Реестр XLSX</button>
|
||||
</div>
|
||||
<div class="invoices-list">
|
||||
<div class="inv-row">
|
||||
<span class="inv-when">07.05.2026</span>
|
||||
<span class="inv-name">Счёт № 2026-0512<span class="sub">Тариф «Команда» · май 2026</span></span>
|
||||
<span class="inv-amount">990 ₽</span>
|
||||
<button type="button" class="inv-action"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>PDF</button>
|
||||
</div>
|
||||
<div class="inv-row">
|
||||
<span class="inv-when">06.05.2026</span>
|
||||
<span class="inv-name">УПД № УПД-2026-0492<span class="sub">Списания за апрель · 18 лидов</span></span>
|
||||
<span class="inv-amount">29 850 ₽</span>
|
||||
<button type="button" class="inv-action"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>1С 8.3 XML</button>
|
||||
</div>
|
||||
<div class="inv-row">
|
||||
<span class="inv-when">05.05.2026</span>
|
||||
<span class="inv-name">УПД № УПД-2026-0488<span class="sub">Списания за март · 24 лида</span></span>
|
||||
<span class="inv-amount">38 100 ₽</span>
|
||||
<button type="button" class="inv-action"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>1С 8.3 XML</button>
|
||||
</div>
|
||||
<div class="inv-row">
|
||||
<span class="inv-when">01.04.2026</span>
|
||||
<span class="inv-name">Счёт № 2026-0411<span class="sub">Тариф «Команда» · апрель 2026</span></span>
|
||||
<span class="inv-amount">990 ₽</span>
|
||||
<button type="button" class="inv-action"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>PDF</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,610 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>v8 · Бренд — нейминг и лого</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=Fraunces:opsz,wght,SOFT@9..144,300..700,0..100&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
/* v8 Forest tokens — same as v8_dashboard/v8_deals */
|
||||
--bg: #F6F3EC;
|
||||
--surface: #FFFDFA;
|
||||
--sunken: #F0EDE4;
|
||||
--hairline: #D9D5CD;
|
||||
--hairline-soft:#E8E3D6;
|
||||
--ink: #081319;
|
||||
--ink-2: #343C41;
|
||||
--ink-3: #66635C;
|
||||
--ink-disabled: #92907B;
|
||||
--accent: #0F6E56;
|
||||
--accent-tint: #E1EEEA;
|
||||
|
||||
--side-bg: #012019;
|
||||
--side-text: #B1C2BD;
|
||||
--side-icon-act:#32C8A9;
|
||||
|
||||
--r-sm: 6px;
|
||||
--r-md: 10px;
|
||||
--r-lg: 14px;
|
||||
|
||||
--font-ui: 'Inter', system-ui, sans-serif;
|
||||
--font-display:'Fraunces', Georgia, serif;
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
font-family: var(--font-ui);
|
||||
font-feature-settings: 'cv11','ss01';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-variation-settings: 'opsz' 14;
|
||||
padding: 32px 28px 80px;
|
||||
}
|
||||
|
||||
a:focus-visible, button:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--r-sm);
|
||||
}
|
||||
|
||||
.wrap { max-width: 1280px; margin: 0 auto; }
|
||||
|
||||
.hero {
|
||||
margin-bottom: 36px;
|
||||
padding-bottom: 22px;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
.hero h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
font-variation-settings: 'opsz' 28;
|
||||
letter-spacing: -0.022em;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
.hero .meta {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-3);
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.hero .meta strong { color: var(--accent); font-weight: 600; }
|
||||
|
||||
.section-h {
|
||||
display: flex; align-items: baseline; justify-content: space-between;
|
||||
margin: 40px 0 18px;
|
||||
gap: 12px;
|
||||
}
|
||||
.section-h h2 {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
font-variation-settings: 'opsz' 24;
|
||||
letter-spacing: -0.018em;
|
||||
margin: 0;
|
||||
}
|
||||
.section-h .num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--ink-3);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
NAMING GRID — 10 options
|
||||
============================================================ */
|
||||
.names {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.name-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-rows: 110px auto;
|
||||
}
|
||||
.name-render {
|
||||
background: var(--side-bg);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
position: relative;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.name-wordmark {
|
||||
font-size: 38px;
|
||||
font-weight: 600;
|
||||
font-variation-settings: 'opsz' 32;
|
||||
letter-spacing: -0.025em;
|
||||
color: #FFFFFF;
|
||||
display: inline-flex; align-items: baseline; gap: 8px;
|
||||
}
|
||||
.name-wordmark.serif {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 500;
|
||||
font-variation-settings: 'opsz' 144, 'SOFT' 0;
|
||||
letter-spacing: -0.015em;
|
||||
}
|
||||
.name-wordmark .dot { color: var(--side-icon-act); }
|
||||
.name-wordmark .accent-letter { color: var(--side-icon-act); }
|
||||
.name-render .badge {
|
||||
position: absolute;
|
||||
top: 10px; right: 12px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.06em;
|
||||
font-weight: 600;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
background: rgba(255,255,255,0.10);
|
||||
color: var(--side-text);
|
||||
}
|
||||
.name-render .badge.acc {
|
||||
background: rgba(50,200,169,0.15);
|
||||
color: var(--side-icon-act);
|
||||
}
|
||||
.name-meta {
|
||||
padding: 14px 18px 16px;
|
||||
}
|
||||
.name-num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.name-tag {
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
letter-spacing: -0.005em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.name-desc {
|
||||
font-size: 12.5px;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.5;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.name-desc em { color: var(--ink); font-style: normal; font-weight: 500; }
|
||||
|
||||
/* ============================================================
|
||||
LOGO CONCEPTS — 5 marks
|
||||
============================================================ */
|
||||
.logos {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.logo-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
overflow: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.logo-on-dark {
|
||||
background: var(--side-bg);
|
||||
padding: 26px 16px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border-bottom: 1px solid #1A3A30;
|
||||
min-height: 96px;
|
||||
}
|
||||
.logo-on-light {
|
||||
background: var(--bg);
|
||||
padding: 16px 16px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border-bottom: 1px solid var(--hairline-soft);
|
||||
min-height: 64px;
|
||||
}
|
||||
.logo-sizes {
|
||||
display: flex; align-items: center; justify-content: space-around;
|
||||
padding: 12px 14px;
|
||||
gap: 8px;
|
||||
background: var(--sunken);
|
||||
min-height: 56px;
|
||||
}
|
||||
.logo-sizes svg, .logo-sizes .tiny-mark {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.logo-meta {
|
||||
padding: 12px 14px 14px;
|
||||
border-top: 1px solid var(--hairline-soft);
|
||||
}
|
||||
.logo-num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.logo-name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
margin-bottom: 6px;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.logo-desc {
|
||||
font-size: 11.5px;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.45;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
COMBINATIONS — preview top 3 names × 2 marks in sidebar
|
||||
============================================================ */
|
||||
.combos {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.combo-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: var(--r-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
.combo-side {
|
||||
background: var(--side-bg);
|
||||
padding: 14px 14px 16px;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
min-height: 60px;
|
||||
}
|
||||
.combo-mark { flex-shrink: 0; }
|
||||
.combo-name {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
font-variation-settings: 'opsz' 18;
|
||||
letter-spacing: -0.018em;
|
||||
}
|
||||
.combo-name.serif {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 500;
|
||||
font-variation-settings: 'opsz' 144, 'SOFT' 0;
|
||||
letter-spacing: -0.012em;
|
||||
}
|
||||
.combo-name .dot { color: var(--side-icon-act); }
|
||||
.combo-meta {
|
||||
padding: 12px 14px;
|
||||
display: flex; gap: 14px;
|
||||
font-size: 11.5px;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
.combo-meta strong { color: var(--ink); font-weight: 500; }
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.names { grid-template-columns: 1fr; }
|
||||
.logos { grid-template-columns: repeat(2, 1fr); }
|
||||
.combos { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
body { padding: 18px 14px 60px; }
|
||||
.logos { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
<header class="hero">
|
||||
<h1>Бренд · нейминг и лого</h1>
|
||||
<p class="meta">v8 Forest палитра · <strong>10 имён</strong> + <strong>5 знаков</strong> · все варианты на тёмном sidebar #012019 для контекста</p>
|
||||
</header>
|
||||
|
||||
<!-- ===== NAMING ===== -->
|
||||
<div class="section-h">
|
||||
<h2>10 вариантов имени</h2>
|
||||
<span class="num">01 · нейминг</span>
|
||||
</div>
|
||||
|
||||
<div class="names">
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Поток<span class="dot">.</span></span><span class="badge acc">FAVOURITE</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">01</div>
|
||||
<div class="name-tag">Минимализм. Дистиллят.</div>
|
||||
<p class="name-desc">Прямое сокращение от «Лидпоток». Уже знакомо команде, но звучит <em>тише и взрослее</em>. Domain: potok.app / potok.ru. Латиница: <em>Potok</em> — чисто читается.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark serif">Русло</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">02</div>
|
||||
<div class="name-tag">Метафорический. Премиум.</div>
|
||||
<p class="name-desc">Путь, по которому идёт вода. Элегантная русская метафора потока лидов. <em>Уникально</em>, не банально, домен почти точно свободен.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Жила</span><span class="badge">RAW</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">03</div>
|
||||
<div class="name-tag">Арбитражный. Прямой.</div>
|
||||
<p class="name-desc">Золотая жила. Связь с pay-per-lead = «копаем источник дохода». <em>Резкий</em> образ, может быть слишком прямолинейным для премиума.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Нерв</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">04</div>
|
||||
<div class="name-tag">Технологичный. Живой.</div>
|
||||
<p class="name-desc">Нервная система продаж. <em>Real-time</em> приём лидов, моментальные сигналы. Хорошо для CRM/продаж, отдаёт «неон» — может быть слишком напряжённо.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Пульс</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">05</div>
|
||||
<div class="name-tag">Real-time. Живой.</div>
|
||||
<p class="name-desc">Пульс входящих лидов, <em>биение</em>. Ассоциация с медтехом и Mercury-вайбом. Минус: часто используется в фитнес-сфере.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark serif">Volna<span class="accent-letter">.</span></span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">06</div>
|
||||
<div class="name-tag">Двуязычный. Премиум.</div>
|
||||
<p class="name-desc">Латиница для премиум-восприятия. Кириллический корень <em>«волна»</em> — поток лидов как волна. Читается легко на обоих языках, домен `volna.io` доступен.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark serif">Vento</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">07</div>
|
||||
<div class="name-tag">Итальянский. Технологичный.</div>
|
||||
<p class="name-desc">Ветер. Звучит как <em>финансовый или AI-стартап</em>. Без русских коннотаций — может оттолкнуть «понятностью» бренда. Linear-grade fonetика.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Колея</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">08</div>
|
||||
<div class="name-tag">Инженерный. Русский.</div>
|
||||
<p class="name-desc">Колея = направленный путь. Инженерная честная метафора, рельсы для лидов. <em>Менее очевидно</em>, чем «поток», но понятно.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark serif">Стерх</span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">09</div>
|
||||
<div class="name-tag">Символический. Запоминается.</div>
|
||||
<p class="name-desc">Редкая птица, символ миграции. <em>Поэтичный</em>, далёкий от «лидогенерации» лоб-в-лоб. Сильный образ, домен `sterh.io` свободен.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="name-card">
|
||||
<div class="name-render"><span class="name-wordmark">Лидерра<span class="accent-letter">.</span></span></div>
|
||||
<div class="name-meta">
|
||||
<div class="name-num">10</div>
|
||||
<div class="name-tag">Составной. Премиум-вариация.</div>
|
||||
<p class="name-desc">«Лид» + «эра/терра». Сохраняет <em>смысловую связь</em> с «Лидпоток», но звучит дороже и менее буквально. Удачно для wordmark-логотипа.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ===== LOGOS ===== -->
|
||||
<div class="section-h">
|
||||
<h2>5 концептов знака</h2>
|
||||
<span class="num">02 · логотип</span>
|
||||
</div>
|
||||
|
||||
<div class="logos">
|
||||
|
||||
<!-- Logo 1: L-square — refined placeholder -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" aria-label="Логотип L-square на тёмном">
|
||||
<rect x="2" y="2" width="44" height="44" rx="10" fill="#FFFFFF"/>
|
||||
<path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="32" cy="34" r="3" fill="#0F6E56"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="32" height="32" viewBox="0 0 48 48" aria-label="Логотип L-square на светлом">
|
||||
<rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/>
|
||||
<path d="M16 14 L16 34 L32 34" stroke="#FFFFFF" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="32" cy="34" r="3" fill="#32C8A9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="16" height="16" viewBox="0 0 48 48"><rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
<svg width="24" height="24" viewBox="0 0 48 48"><rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3" fill="#32C8A9"/></svg>
|
||||
<svg width="40" height="40" viewBox="0 0 48 48"><rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/><path d="M16 14 L16 34 L32 34" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3" fill="#32C8A9"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">A</div>
|
||||
<div class="logo-name">L-Square с точкой</div>
|
||||
<p class="logo-desc">Литера + закрывающая точка-цель = «лид прибыл». Чистый геометрический знак.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Logo 2: Wave / river — поток как волны -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="56" height="48" viewBox="0 0 56 48" aria-label="Логотип Wave на тёмном">
|
||||
<path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
<path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" fill="none" opacity="0.7"/>
|
||||
<path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#32C8A9" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="40" height="32" viewBox="0 0 56 48" aria-label="Логотип Wave на светлом">
|
||||
<path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#0A1319" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
<path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#343C41" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
<path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#0F6E56" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="20" height="16" viewBox="0 0 56 48"><path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#0A1319" stroke-width="4" fill="none"/><path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#0F6E56" stroke-width="4" fill="none"/></svg>
|
||||
<svg width="30" height="24" viewBox="0 0 56 48"><path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#0A1319" stroke-width="3" fill="none"/><path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#343C41" stroke-width="3" fill="none"/><path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#0F6E56" stroke-width="3" fill="none"/></svg>
|
||||
<svg width="48" height="40" viewBox="0 0 56 48"><path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#0A1319" stroke-width="3" fill="none"/><path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#343C41" stroke-width="3" fill="none"/><path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#0F6E56" stroke-width="3" fill="none"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">Б</div>
|
||||
<div class="logo-name">Three Waves · поток</div>
|
||||
<p class="logo-desc">Три горизонтальные волны, нижняя — Teal. Ритм = поток. Подходит к «Поток / Volna / Русло».</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Logo 3: Drop / chevron forward -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" aria-label="Логотип Drop на тёмном">
|
||||
<path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#FFFFFF"/>
|
||||
<path d="M24 14 L24 34" stroke="#012019" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M18 28 L24 34 L30 28" stroke="#012019" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="24" cy="14" r="3.5" fill="#32C8A9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="32" height="32" viewBox="0 0 48 48" aria-label="Логотип Drop на светлом">
|
||||
<path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#0A1319"/>
|
||||
<path d="M24 14 L24 34" stroke="#FFFFFF" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M18 28 L24 34 L30 28" stroke="#FFFFFF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="24" cy="14" r="3.5" fill="#0F6E56"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="16" height="16" viewBox="0 0 48 48"><path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#0A1319"/><circle cx="24" cy="14" r="4" fill="#32C8A9"/></svg>
|
||||
<svg width="24" height="24" viewBox="0 0 48 48"><path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#0A1319"/><path d="M24 14 L24 34" stroke="#FFF" stroke-width="3" stroke-linecap="round"/><path d="M18 28 L24 34 L30 28" stroke="#FFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="24" cy="14" r="3.5" fill="#32C8A9"/></svg>
|
||||
<svg width="40" height="40" viewBox="0 0 48 48"><path d="M24 6 L40 32 Q40 42 24 42 Q8 42 8 32 Z" fill="#0A1319"/><path d="M24 14 L24 34" stroke="#FFF" stroke-width="2.5" stroke-linecap="round"/><path d="M18 28 L24 34 L30 28" stroke="#FFF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="24" cy="14" r="3.5" fill="#32C8A9"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">В</div>
|
||||
<div class="logo-name">Drop · капля с движением</div>
|
||||
<p class="logo-desc">Капля + стрелка вниз = лид падает в воронку. Подходит к «Жила / Пульс / Колея».</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Logo 4: Chevron forward / pipeline -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="56" height="48" viewBox="0 0 56 48" aria-label="Логотип Chevron на тёмном">
|
||||
<path d="M6 12 L18 24 L6 36" stroke="#5C7A72" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M22 12 L34 24 L22 36" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M38 12 L50 24 L38 36" stroke="#32C8A9" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="40" height="32" viewBox="0 0 56 48" aria-label="Логотип Chevron на светлом">
|
||||
<path d="M6 12 L18 24 L6 36" stroke="#92907B" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M22 12 L34 24 L22 36" stroke="#343C41" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<path d="M38 12 L50 24 L38 36" stroke="#0F6E56" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="20" height="16" viewBox="0 0 56 48"><path d="M22 12 L34 24 L22 36" stroke="#343C41" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M38 12 L50 24 L38 36" stroke="#0F6E56" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
<svg width="30" height="24" viewBox="0 0 56 48"><path d="M6 12 L18 24 L6 36" stroke="#92907B" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M22 12 L34 24 L22 36" stroke="#343C41" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M38 12 L50 24 L38 36" stroke="#0F6E56" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
<svg width="48" height="40" viewBox="0 0 56 48"><path d="M6 12 L18 24 L6 36" stroke="#92907B" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M22 12 L34 24 L22 36" stroke="#343C41" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M38 12 L50 24 L38 36" stroke="#0F6E56" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">Г</div>
|
||||
<div class="logo-name">Pipeline · 3 шеврона</div>
|
||||
<p class="logo-desc">Три «>>>» нарастающих по контрасту = пайплайн лидов. Подходит к «Колея / Поток / Vento / Лидерра».</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Logo 5: Wordmark only — Fraunces serif "P." -->
|
||||
<article class="logo-card">
|
||||
<div class="logo-on-dark">
|
||||
<svg width="64" height="48" viewBox="0 0 64 48" aria-label="Wordmark Fraunces на тёмном">
|
||||
<text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#FFFFFF" letter-spacing="-0.02em">П</text>
|
||||
<circle cx="48" cy="34" r="3" fill="#32C8A9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-on-light">
|
||||
<svg width="48" height="32" viewBox="0 0 64 48" aria-label="Wordmark Fraunces на светлом">
|
||||
<text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#0A1319" letter-spacing="-0.02em">П</text>
|
||||
<circle cx="48" cy="34" r="3" fill="#0F6E56"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-sizes">
|
||||
<svg width="20" height="16" viewBox="0 0 64 48"><text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#0A1319" letter-spacing="-0.02em">П</text></svg>
|
||||
<svg width="30" height="24" viewBox="0 0 64 48"><text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#0A1319" letter-spacing="-0.02em">П</text><circle cx="48" cy="34" r="3" fill="#0F6E56"/></svg>
|
||||
<svg width="48" height="40" viewBox="0 0 64 48"><text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#0A1319" letter-spacing="-0.02em">П</text><circle cx="48" cy="34" r="3" fill="#0F6E56"/></svg>
|
||||
</div>
|
||||
<div class="logo-meta">
|
||||
<div class="logo-num">Д</div>
|
||||
<div class="logo-name">Serif Initial · буква + точка</div>
|
||||
<p class="logo-desc">Только литера в Fraunces (variable serif) + Teal-точка. Editorial. Подходит к «Русло / Volna / Стерх / Vento».</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ===== COMBINATIONS ===== -->
|
||||
<div class="section-h">
|
||||
<h2>3 favourite комбинации в контексте sidebar</h2>
|
||||
<span class="num">03 · превью</span>
|
||||
</div>
|
||||
|
||||
<div class="combos">
|
||||
|
||||
<article class="combo-card">
|
||||
<div class="combo-side">
|
||||
<span class="combo-mark">
|
||||
<svg width="22" height="22" viewBox="0 0 48 48"><rect x="2" y="2" width="44" height="44" rx="10" fill="#FFFFFF"/><path d="M16 14 L16 34 L32 34" stroke="#012019" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="32" cy="34" r="3.5" fill="#0F6E56"/></svg>
|
||||
</span>
|
||||
<span class="combo-name">Лидерра<span class="dot">.</span></span>
|
||||
</div>
|
||||
<div class="combo-meta">
|
||||
<span><strong>Имя:</strong> Лидерра</span>
|
||||
<span><strong>Знак:</strong> A · L-Square</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="combo-card">
|
||||
<div class="combo-side">
|
||||
<span class="combo-mark">
|
||||
<svg width="34" height="20" viewBox="0 0 56 48"><path d="M4 18 Q12 10 20 18 T36 18 T52 18" stroke="#FFFFFF" stroke-width="3" fill="none"/><path d="M4 28 Q12 20 20 28 T36 28 T52 28" stroke="#FFFFFF" stroke-width="3" fill="none" opacity="0.7"/><path d="M4 38 Q12 30 20 38 T36 38 T52 38" stroke="#32C8A9" stroke-width="3" fill="none"/></svg>
|
||||
</span>
|
||||
<span class="combo-name">Поток<span class="dot">.</span></span>
|
||||
</div>
|
||||
<div class="combo-meta">
|
||||
<span><strong>Имя:</strong> Поток</span>
|
||||
<span><strong>Знак:</strong> Б · Three Waves</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="combo-card">
|
||||
<div class="combo-side">
|
||||
<span class="combo-mark">
|
||||
<svg width="26" height="22" viewBox="0 0 64 48"><text x="32" y="36" text-anchor="middle" font-family="Fraunces, Georgia, serif" font-size="36" font-weight="500" fill="#FFFFFF" letter-spacing="-0.02em">Р</text><circle cx="48" cy="34" r="3" fill="#32C8A9"/></svg>
|
||||
</span>
|
||||
<span class="combo-name serif">Русло</span>
|
||||
</div>
|
||||
<div class="combo-meta">
|
||||
<span><strong>Имя:</strong> Русло</span>
|
||||
<span><strong>Знак:</strong> Д · Serif Initial</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||