Files
portal/liderra_v8_handoff/concepts/v8_login.html
T
Дмитрий 887abf444e 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>
2026-05-08 07:11:58 +03:00

525 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>&nbsp;</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>