first commit
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled

This commit is contained in:
2026-07-23 02:38:05 +03:00
commit 8757bec8ac
335 changed files with 61527 additions and 0 deletions

View File

@@ -0,0 +1,401 @@
<!doctype html>
<html lang="ru" data-theme="shell-dark">
<head>
<meta charset="UTF-8">
<title>VidConf — Вход / Регистрация / Подтверждение email (тёмная тема, НА СОГЛАСОВАНИИ)</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../tokens.css">
<style>
/* =====================================================================
* ТЁМНЫЙ ВАРИАНТ ОБОЛОЧКИ — ПРЕДЛОЖЕНИЕ, НЕ УТВЕРЖДЕНО.
* Разметка и контент идентичны design/mockups/auth.html 1:1, меняется
* только цветовая схема. Палитра переиспользует токены комнаты
* конференции (tokens.css, [data-theme="room"]) — единая тёмная гамма:
* графит + пастельная мята/розово-красный/жёлто-оранжевый.
* Происхождение цветов и обоснование — design/mockups/dark/README.md.
* Технически: блок :root ниже переопределяет одноимённые переменные
* оболочки из tokens.css (та же специфичность :root, но позже в
* источнике — выигрывает), поэтому весь остальной CSS ниже скопирован
* из auth.html БЕЗ ИЗМЕНЕНИЙ и автоматически перекрашивается.
* ===================================================================== */
:root {
color-scheme: dark;
--color-bg: #1E1E1E; /* = --color-room-bg */
--color-bg-alt: #242424; /* новый: между bg и surface */
--color-surface: #2A2A2A; /* = --color-room-surface */
--color-surface-glass: rgba(51, 51, 51, .55); /* на основе --color-room-surface-raised */
--color-border: #3F3F3F; /* = --color-room-tile-border */
--color-border-strong: #585858; /* новый, hover-обводка */
--color-border-pill: #7FDDA8; /* = --color-room-mic-on */
--color-ink-900: #E8E8E8; /* = --color-room-text-primary */
--color-ink-700: #7FDDA8; /* = --color-room-mic-on — бренд/заголовки/ссылки */
--color-ink-500: #B0B0B0; /* = --color-room-text-secondary */
--color-ink-400: #5D9A78; /* новый, приглушённая мята, крупный текст ⚠︎ */
--color-ink-300: #7A7A7A; /* = --color-room-text-tertiary */
--color-accent: #7FDDA8; /* = --color-room-mic-on */
--color-accent-hover: #75CA9A; /* новый */
--color-accent-active: #6CB78C; /* новый */
--color-accent-text: #10331F; /* новый, тёмный ink (прецедент room.html) */
--color-success: #7FDDA8;
--color-success-bg: rgba(127, 221, 168, .14);
--color-success-dot: #7FDDA8;
--color-danger: #EB93A1; /* = --color-room-mic-off */
--color-danger-bg: rgba(235, 147, 161, .14);
--color-danger-dot: #EB93A1;
--color-danger-solid: #B85468; /* = --color-room-danger-bg, заливка danger-кнопок */
--color-danger-solid-hover: #B9495F;
--color-warning: #D6A83D; /* = --color-room-speaker-ring */
--color-warning-bg: rgba(214, 168, 61, .16);
--color-warning-dot: #D6A83D;
--color-focus-ring: #D6A83D; /* = --color-room-focus-ring */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, .35);
--shadow-md: 0 4px 16px rgba(0, 0, 0, .4);
--shadow-lg: 0 12px 32px rgba(0, 0, 0, .5);
--shadow-glass: 0 8px 24px rgba(0, 0, 0, .45);
}
/* Переключатель темы (солнце/луна) — статичная демонстрация, не функциональна в макете */
.theme-toggle { position: fixed; top: 24px; right: 32px; z-index: 50; display: inline-flex; gap: 2px; background: var(--color-bg-alt); padding: 4px; border-radius: var(--radius-full); border: 1px solid var(--color-border); box-shadow: var(--shadow-md); }
.theme-toggle-btn { width: 32px; height: 32px; border-radius: 50%; border: none; background: transparent; color: var(--color-ink-500); display: flex; align-items: center; justify-content: center; }
.theme-toggle-btn .icon { width: 16px; height: 16px; }
.theme-toggle-btn:hover { color: var(--color-ink-900); }
.theme-toggle-btn.is-active { background: var(--color-surface); color: var(--color-ink-700); box-shadow: var(--shadow-sm); }
html { min-width: 1280px; }
body { min-height: 100vh; }
.layout { display: flex; min-height: 100vh; }
/* ---------- Брендовая панель ---------- */
.brand-panel {
width: 42%;
background: linear-gradient(160deg, #242424 0%, #1E1E1E 55%, #1A1A1A 100%);
padding: var(--space-12) var(--space-12);
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
overflow: hidden;
border-right: 1px solid var(--color-border);
}
.brand-panel::after {
content: "";
position: absolute;
right: -120px; bottom: -120px;
width: 380px; height: 380px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, rgba(127,221,168,.16), rgba(127,221,168,0) 70%);
}
.brand-mark {
display: inline-flex;
align-items: center;
gap: var(--space-2);
font: var(--text-h3);
color: var(--color-ink-700);
z-index: 1;
}
.brand-mark .logo-mark { width: 22px; height: 22px; flex-shrink: 0; }
.brand-copy { z-index: 1; }
.eyebrow {
font: var(--text-caption);
letter-spacing: .08em;
color: var(--color-ink-400);
margin: 0 0 var(--space-4);
}
.brand-headline {
font: var(--text-display-lg);
font-family: var(--font-display);
color: var(--color-ink-700);
margin: 0 0 var(--space-4);
max-width: 460px;
}
.brand-sub {
font: var(--text-body-lg);
color: var(--color-ink-500);
max-width: 420px;
margin: 0;
}
.brand-stats { display: flex; gap: var(--space-4); z-index: 1; margin-top: var(--space-8); }
.stat-glass {
background: var(--color-surface-glass);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255,255,255,.08);
box-shadow: var(--shadow-glass);
border-radius: var(--radius-xl);
padding: var(--space-4) var(--space-5);
min-width: 140px;
}
.stat-glass strong { display: block; font: var(--text-h1); font-family: var(--font-display); color: var(--color-ink-700); }
.stat-glass span { font: var(--text-caption); color: var(--color-ink-500); text-transform: none; letter-spacing: normal; }
/* ---------- Форма-панель ---------- */
.form-panel {
width: 58%;
padding: var(--space-12) var(--space-16);
display: flex;
flex-direction: column;
gap: var(--space-16);
}
.screen-block { max-width: 440px; }
.screen-label {
font: var(--text-caption);
color: var(--color-ink-400);
margin: 0 0 var(--space-3);
}
.screen-label b { color: var(--color-ink-700); }
.screen-title { font: var(--text-h1); color: var(--color-ink-900); margin: 0 0 var(--space-8); }
.divider { border: none; border-top: 1px solid var(--color-border); max-width: 720px; margin: 0; }
.tabs { display: flex; gap: var(--space-2); margin-bottom: var(--space-6); background: var(--color-bg-alt); padding: 4px; border-radius: var(--radius-full); width: fit-content; }
.tab { padding: 8px 20px; border-radius: var(--radius-full); border: none; background: transparent; font: var(--text-body); color: var(--color-ink-500); }
.tab.is-active { background: var(--color-surface); color: var(--color-ink-700); font-weight: 600; box-shadow: var(--shadow-sm); }
.field { margin-bottom: var(--space-5); }
.field label { display: block; font: var(--text-body); font-weight: 600; color: var(--color-ink-900); margin-bottom: var(--space-2); }
.input-wrap { position: relative; }
.input-wrap .icon { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--color-ink-400); }
.input-wrap input {
width: 100%;
font: var(--text-body-lg);
color: var(--color-ink-900);
padding: 11px 14px 11px 42px;
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
background: var(--color-surface);
outline: none;
}
.input-wrap input::placeholder { color: var(--color-ink-300); }
.input-wrap input:hover { border-color: var(--color-border-strong); }
.input-wrap input:focus { border-color: var(--color-focus-ring); box-shadow: 0 0 0 3px rgba(214,168,61,.18); }
.input-wrap .toggle-eye { left: auto; right: 14px; background: none; border: none; padding: 0; cursor: pointer; }
.field.has-error input { border-color: var(--color-danger); box-shadow: 0 0 0 3px rgba(235,147,161,.18); }
.error-msg { display: flex; align-items: center; gap: 6px; margin-top: var(--space-2); font: var(--text-body); color: var(--color-danger); }
.hint-msg { margin-top: var(--space-2); font: var(--text-body); color: var(--color-ink-500); }
.field-disabled input { background: var(--color-bg-alt); color: var(--color-ink-300); cursor: not-allowed; }
.row-between { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-6); }
.checkbox-row { display: flex; align-items: center; gap: 8px; font: var(--text-body); color: var(--color-ink-500); }
.link { color: var(--color-ink-700); font-weight: 600; text-decoration: none; }
.link:hover { text-decoration: underline; }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; border: none; cursor: pointer; font-family: var(--font-body); transition: background var(--transition-fast), box-shadow var(--transition-fast); }
.btn-primary {
width: 100%;
background: var(--color-accent);
color: var(--color-accent-text);
font: var(--text-body-lg);
font-weight: 700;
padding: 13px 24px;
border-radius: var(--radius-full);
}
.btn-primary:hover { background: var(--color-accent-hover); box-shadow: var(--shadow-md); }
.btn-primary:active { background: var(--color-accent-active); }
.btn-primary[disabled] { background: rgba(122,122,122,.35); color: var(--color-ink-300); cursor: not-allowed; }
.btn-secondary {
width: 100%;
background: transparent;
color: var(--color-ink-700);
border: 1.5px solid var(--color-border-pill);
font: var(--text-body-lg);
font-weight: 600;
padding: 12px 24px;
border-radius: var(--radius-full);
}
.btn-secondary:hover { background: var(--color-bg-alt); border-color: var(--color-ink-700); }
.oauth-row { display: flex; gap: var(--space-3); margin-top: var(--space-5); }
.oauth-row .btn-secondary { flex: 1; }
.separator { display: flex; align-items: center; gap: 12px; color: var(--color-ink-400); font: var(--text-caption); margin: var(--space-6) 0; text-transform: none; letter-spacing: normal; }
.separator::before, .separator::after { content: ""; flex: 1; height: 1px; background: var(--color-border); }
/* ---------- Подтверждение email ---------- */
.confirm-card {
max-width: 440px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-2xl);
padding: var(--space-8);
text-align: left;
}
.confirm-icon {
width: 56px; height: 56px; border-radius: var(--radius-full);
background: var(--color-success-bg); color: var(--color-success);
display: flex; align-items: center; justify-content: center;
margin-bottom: var(--space-5);
}
.confirm-icon .icon { width: 28px; height: 28px; }
.confirm-card h3 { font: var(--text-h2); color: var(--color-ink-900); margin: 0 0 var(--space-2); }
.confirm-card p { font: var(--text-body-lg); color: var(--color-ink-500); margin: 0 0 var(--space-6); }
.confirm-card .mail-chip {
display: inline-flex; align-items: center; gap: 8px;
background: var(--color-bg-alt); border-radius: var(--radius-full);
padding: 6px 14px; font: var(--text-mono-sm); color: var(--color-ink-700);
margin-bottom: var(--space-6);
}
/* pending confirmation state variant */
.confirm-card.pending .confirm-icon { background: var(--color-warning-bg); color: var(--color-warning); }
</style>
</head>
<body>
<svg style="display:none">
<symbol id="i-mail" viewBox="0 0 24 24"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 6 10 7 10-7"/></symbol>
<symbol id="i-lock" 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"/></symbol>
<symbol id="i-user" viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M6 21v-2a6 6 0 0 1 12 0v2"/></symbol>
<symbol id="i-eye" viewBox="0 0 24 24"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></symbol>
<symbol id="i-eye-off" viewBox="0 0 24 24"><path d="M3 3l18 18"/><path d="M10.58 10.58a2 2 0 0 0 2.83 2.83"/><path d="M9.88 5.09A9.77 9.77 0 0 1 12 5c6.5 0 10 7 10 7a13.16 13.16 0 0 1-3.06 4.06M6.1 6.1A13.16 13.16 0 0 0 2 12s3.5 7 10 7a9.77 9.77 0 0 0 4.9-1.3"/></symbol>
<symbol id="i-check-circle" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></symbol>
<symbol id="i-arrow-right" viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></symbol>
<symbol id="i-alert" 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"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></symbol>
<symbol id="i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></symbol>
<symbol id="i-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></symbol>
<symbol id="i-moon" viewBox="0 0 24 24"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z"/></symbol>
</svg>
<div class="theme-toggle" role="group" aria-label="Переключить тему оформления">
<button class="theme-toggle-btn" type="button" aria-pressed="false" aria-label="Светлая тема"><svg class="icon"><use href="#i-sun"/></svg></button>
<button class="theme-toggle-btn is-active" type="button" aria-pressed="true" aria-label="Тёмная тема"><svg class="icon"><use href="#i-moon"/></svg></button>
</div>
<div class="layout">
<!-- ===================== БРЕНД-ПАНЕЛЬ ===================== -->
<aside class="brand-panel">
<div class="brand-mark"><svg class="logo-mark" viewBox="0 0 32 32" fill="none" aria-hidden="true"><polygon points="21,16 18.5,20.3 13.5,20.3 11,16 13.5,11.7 18.5,11.7" fill="currentColor"/><circle cx="16" cy="16" r="13" stroke="currentColor" stroke-width="2.6" stroke-dasharray="12 6"/></svg> VidConf</div>
<div class="brand-copy">
<p class="eyebrow">[ SELF-HOSTED ВИДЕОКОНФЕРЕНЦИИ ]</p>
<h1 class="brand-headline">Ваши встречи.<br>Ваша инфраструктура.</h1>
<p class="brand-sub">Видео, записи и AI-саммари обрабатываются на ваших серверах - данные не уходят
за пределы вашей инфраструктуры. Гости подключаются по ссылке или номеру, без регистрации.</p>
<div class="brand-stats">
<div class="stat-glass"><strong>100%</strong><span>данных - в вашем контуре</span></div>
<div class="stat-glass"><strong>AI-саммари</strong><span>на почту после встречи</span></div>
</div>
</div>
<p class="hint-msg" style="z-index:1">© 2026 VidConf · self-hosted</p>
</aside>
<!-- ===================== ФОРМА: ВХОД / РЕГИСТРАЦИЯ ===================== -->
<main class="form-panel">
<section class="screen-block">
<p class="screen-label">Экран: <b>вход в аккаунт</b></p>
<div class="tabs" role="tablist" aria-label="Способ авторизации">
<button class="tab is-active" role="tab" aria-selected="true">Вход</button>
<button class="tab" role="tab" aria-selected="false">Регистрация</button>
</div>
<form>
<div class="field">
<label for="login-email">Рабочая почта</label>
<div class="input-wrap">
<svg class="icon"><use href="#i-mail"/></svg>
<input id="login-email" type="email" placeholder="you@company.ru" value="a.ivanova@vidconf.dev">
</div>
</div>
<div class="field has-error">
<label for="login-pass">Пароль</label>
<div class="input-wrap">
<svg class="icon"><use href="#i-lock"/></svg>
<input id="login-pass" type="password" placeholder="••••••••" value="wrongpass">
<button type="button" class="toggle-eye" aria-label="Показать пароль">
<svg class="icon"><use href="#i-eye"/></svg>
</button>
</div>
<p class="error-msg"><svg class="icon" style="width:16px;height:16px"><use href="#i-alert"/></svg> Неверный email или пароль</p>
</div>
<div class="row-between">
<label class="checkbox-row"><input type="checkbox" checked> Запомнить меня</label>
<a href="#" class="link">Забыли пароль?</a>
</div>
<button type="submit" class="btn btn-primary">Войти <svg class="icon"><use href="#i-arrow-right"/></svg></button>
</form>
</section>
<hr class="divider">
<section class="screen-block">
<p class="screen-label">Экран: <b>регистрация</b></p>
<h2 class="screen-title">Создать аккаунт</h2>
<form>
<div class="field">
<label for="reg-name">Имя и фамилия</label>
<div class="input-wrap">
<svg class="icon"><use href="#i-user"/></svg>
<input id="reg-name" type="text" placeholder="Мария Кузнецова">
</div>
</div>
<div class="field">
<label for="reg-email">Рабочая почта</label>
<div class="input-wrap">
<svg class="icon"><use href="#i-mail"/></svg>
<input id="reg-email" type="email" placeholder="you@company.ru">
</div>
</div>
<div class="field">
<label for="reg-pass">Пароль</label>
<div class="input-wrap">
<svg class="icon"><use href="#i-lock"/></svg>
<input id="reg-pass" type="password" placeholder="Минимум 8 символов">
<button type="button" class="toggle-eye" aria-label="Скрыть пароль">
<svg class="icon"><use href="#i-eye-off"/></svg>
</button>
</div>
<p class="hint-msg">Минимум 8 символов, буквы и цифры</p>
</div>
<button type="submit" class="btn btn-primary" style="margin-top:8px">Зарегистрироваться</button>
<div class="separator">или</div>
<div class="field field-disabled" style="margin-bottom:0">
<label for="reg-invite">Код приглашения (недоступно без администратора)</label>
<div class="input-wrap">
<input id="reg-invite" type="text" placeholder="INVITE-XXXX-XXXX" disabled>
</div>
</div>
</form>
</section>
<hr class="divider">
<section class="screen-block">
<p class="screen-label">Экран: <b>подтверждение email</b></p>
<div class="confirm-card" style="margin-bottom: var(--space-6);">
<div class="confirm-icon"><svg class="icon"><use href="#i-check-circle"/></svg></div>
<h3>Почта подтверждена</h3>
<p>Аккаунт <strong>a.ivanova@vidconf.dev</strong> активирован. Теперь
доступно бронирование переговорных и вход в конференции.</p>
<button class="btn btn-primary">Перейти в лобби <svg class="icon"><use href="#i-arrow-right"/></svg></button>
</div>
<div class="confirm-card pending">
<div class="confirm-icon"><svg class="icon"><use href="#i-clock"/></svg></div>
<h3>Подтвердите почту</h3>
<p>Мы отправили письмо со ссылкой на <span class="mail-chip"><svg class="icon" style="width:14px;height:14px"><use href="#i-mail"/></svg>m.kuznetsova@company.ru</span></p>
<button class="btn btn-secondary">Отправить письмо повторно</button>
</div>
</section>
</main>
</div>
</body>
</html>