887abf444e
Получен 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>
325 lines
15 KiB
HTML
325 lines
15 KiB
HTML
<!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>
|