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>
525 lines
22 KiB
HTML
525 lines
22 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; --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>
|