Первоначальная версия VidConf

This commit is contained in:
2026-07-23 01:04:01 +03:00
commit 896455381a
335 changed files with 61527 additions and 0 deletions

414
design/mockups/lobby.html Normal file
View File

@@ -0,0 +1,414 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>VidConf — Лобби (хаб действий)</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../tokens.css">
<style>
/*
* Лобби — «хаб действий» (фаза R1, пивот). Заменяет список комнат:
* предустановленных комнат больше нет, конференции создаются динамически.
* По мотивам alternative_design/lobby.html, с правками фазы R1:
* - 4 плитки СТРОГО одинакового размера (фиксированная высота, не min-height,
* чтобы сетка была симметричной вне зависимости от длины текста внутри);
* - симметричная сетка 2×2 без grid-template-areas (порядок в DOM = порядок
* чтения = порядок задачи: Создать → Календарь → Подключиться → Мои);
* - плитки крупнее референсного эскиза (высота 340px против ~220px в alt);
* - медальон VidConf на пересечении рядов/колонок (без магических % как
* в alt-версии; точную формулу положения см. в правке 2-й волны ниже —
* после неё центр уже не буквально 50%/50%, а зеркально-симметричная
* точка равного перекрытия всех 4 карточек).
* Токены — только новые имена (--color-accent*), см. tokens.css §1.1/1.3.
*
* Итерация правок (2026-07-16, гейт утверждения, 1-я волна): плитки
* расширены в 1.5 раза (тайл 544px → 816px, контейнер 1120px → 1664px,
* gap не менялся — ширина реально выросла, а не растянулась через gap);
* типографика внутри плиток укрупнена пропорционально (см.
* .action-icon/.action-title/.action-object/.action-note/.hub-logo ниже).
* Высота ряда (340px) сохранена без изменений — просили шире, не крупнее
* по высоте.
*
* Итерация правок (2026-07-16, гейт утверждения, 2-я волна): медальон
* заменён на знак-объектив (design/logo.svg, см. DESIGN_SYSTEM.md §1.4) и
* теперь ЛЕЖИТ ПОВЕРХ равных частей видимых углов всех 4 карточек (было —
* просто сидел в промежутке сетки на пересечении 50%/50%). Точная
* геометрия и обоснование смещения центра медальона — в комментарии перед
* `.hub-logo` и в DESIGN_SYSTEM.md §4.12.
*
* Итерация правок (2026-07-16, гейт утверждения, 3-я волна): плитки сужены
* на 15% (тайл 816px → 694px, контейнер 1664px → 1420px); высота (340px)
* и gap (32px) не менялись, типографика не менялась. Перекрытие медальона
* зависит только от gap/margin-top (не от ширины тайла) — геометрия и
* величина перекрытия (29.2px) остаются теми же, см. `.hub-logo` ниже;
* пересчёт подтверждён headless-рендером на 1280/1440/1728px.
*/
html { height: 100%; min-width: 1280px; }
body {
min-height: 100vh;
display: flex;
flex-direction: column;
background:
radial-gradient(1100px 520px at 10% -10%, rgba(59, 77, 149, 0.06), transparent 60%),
radial-gradient(900px 480px at 105% 110%, rgba(212, 242, 227, 0.28), transparent 55%),
var(--color-bg);
}
/* ---------- Верхняя панель ---------- */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-5) var(--space-8);
}
.brand-mark { display: inline-flex; align-items: center; gap: 8px; font: var(--text-h3); color: var(--color-ink-700); }
.brand-mark .logo-mark { width: 22px; height: 22px; flex-shrink: 0; }
.topbar-right { display: flex; align-items: center; gap: var(--space-4); }
.burger {
width: 44px; height: 44px;
display: inline-flex; align-items: center; justify-content: center;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
color: var(--color-ink-700);
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.burger:hover { border-color: var(--color-border-strong); box-shadow: var(--shadow-sm); }
.burger[aria-expanded="true"] { border-color: var(--color-ink-700); }
.menu-wrap { position: relative; }
.menu {
position: absolute;
top: calc(100% + 10px);
right: 0;
min-width: 236px;
padding: var(--space-2);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
display: none;
z-index: 30;
}
.menu.is-open { display: block; }
.menu a {
display: flex; align-items: center; gap: 10px;
padding: 10px 14px;
border-radius: var(--radius-sm);
font: var(--text-body-lg); font-weight: 600;
color: var(--color-ink-900);
text-decoration: none;
}
.menu a:hover { background: var(--color-bg-alt); color: var(--color-ink-700); }
.menu a .icon { color: var(--color-ink-400); }
.menu-divider { height: 1px; background: var(--color-border); margin: var(--space-2) 6px; }
.menu a.danger { color: var(--color-danger); }
.menu a.danger .icon { color: var(--color-danger); }
.user-pill {
display: inline-flex; align-items: center; gap: 10px;
padding: 6px 16px 6px 6px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-full);
font: var(--text-body); font-weight: 600; color: var(--color-ink-900);
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.user-pill:hover { border-color: var(--color-border-strong); box-shadow: var(--shadow-sm); }
.user-pill .avatar {
width: 30px; height: 30px; border-radius: 50%;
background: var(--color-ink-700); color: #fff;
display: flex; align-items: center; justify-content: center;
font: var(--text-caption);
}
/* ---------- Мозаика действий ---------- */
main {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-6) var(--space-8) var(--space-12);
}
.hub-wrap { display: flex; flex-direction: column; align-items: center; gap: var(--space-8); width: 100%; }
/* width:100% — обязателен: без явной ширины .hub-wrap как flex-item без
flex-grow сжимается по контенту (max-content), и width: min(…, 100%)
у .hub не может резолвиться от неопределённого контейнера (циклическая
зависимость) — контейнер сетки фактически игнорировал заданный кап.
С width:100% контейнер .hub-wrap получает определённую ширину от
<main>, и .hub реально расширяется до заданного max-width. */
.hub-intro { text-align: center; max-width: 560px; }
.hub-intro .eyebrow { font: var(--text-caption); color: var(--color-ink-400); margin: 0 0 var(--space-2); }
.hub-intro h1 { font: var(--text-display-lg); font-family: var(--font-display); color: var(--color-ink-700); margin: 0; }
.hub {
position: relative;
width: min(1420px, 100%); /* тайл 694px × 2 + gap 32px. Правка гейта
(2026-07-16, 3-я итерация): тайл ×0.85 от предыдущих 816px (816×0.85=
693.6≈694px, контейнер 1664px→1420px); высота (340px) и gap (32px) не
менялись — сужение только по горизонтали. */
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 340px); /* фиксированная высота — гарантирует
строгую симметрию сетки 2×2 независимо от длины текста в плитках */
gap: var(--space-8);
}
/* Карточка-действие. Медальон с иконкой «сидит» на верхней кромке. */
.action {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
gap: var(--space-3);
padding: var(--space-10) var(--space-12) var(--space-8);
margin-top: 34px; /* место под выступающий медальон (68px / 2) */
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-3xl);
text-decoration: none;
color: var(--color-ink-900);
box-shadow: var(--shadow-sm);
transition:
transform var(--transition-base),
box-shadow var(--transition-base),
border-color var(--transition-base);
}
.action:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
border-color: var(--color-border-strong);
}
.action:active { transform: translateY(-1px); }
.action-icon {
position: absolute;
top: -34px;
left: 50%;
transform: translateX(-50%);
width: 68px; height: 68px;
border-radius: 50%;
background: var(--color-surface);
border: var(--border-thick) solid var(--color-ink-700);
color: var(--color-ink-700);
display: flex; align-items: center; justify-content: center;
box-shadow: var(--shadow-md);
}
.action-icon .icon { width: 30px; height: 30px; }
/* Типографика плиток укрупнена пропорционально расширению тайла (итерация
правок гейта, 2026-07-16): заголовок 34px → 40px, подпись-объект
18px → 21px, примечание 16px → 17px. Токены шкалы (--text-display-lg
и т.д.) не подходят напрямую — берём кастомные размеры того же
начертания/семейства для гармоничного шага. */
.action-title {
margin: 0;
font: 600 40px/1.2 var(--font-display);
letter-spacing: 0.01em;
color: var(--color-ink-700);
text-transform: uppercase;
}
.action-object { margin: 0; font: 600 21px/1.35 var(--font-body); color: var(--color-ink-900); }
.action-note { margin: 0; font: 400 17px/1.5 var(--font-body); color: var(--color-ink-500); }
/* Главное действие — CTA дизайн-системы (пастельная мята) */
.action--create {
background: var(--color-accent);
border-color: transparent;
}
.action--create:hover { background: var(--color-accent-hover); border-color: transparent; }
.action--create:active { background: var(--color-accent-active); }
.action--create .action-title { font: 700 54px/1.1 var(--font-display); }
.action--create .action-title,
.action--create .action-object { color: var(--color-accent-text); }
.action--create .action-note { color: var(--color-ink-700); }
.action--create .action-icon {
background: var(--color-accent);
border-color: var(--color-accent-text);
color: var(--color-accent-text);
}
/* ---------- Медальон VidConf в центре мозаики ---------- */
/* Правка гейта (2026-07-16, вторая итерация): медальон должен ЛЕЖАТЬ ПОВЕРХ
видимых углов всех 4 карточек на одинаковую величину, а не просто сидеть
в промежутке сетки. Геометрия (числа фиксированные, не проценты — высота
.hub всегда 2×340px + gap 32px = 712px, независимо от ширины вьюпорта):
• по вертикали видимый низ карточек ряда 1 — на y=340px от верха .hub
(340px трек), видимый верх карточек ряда 2 — на y=406px (грид-гап 32px
+ margin-top 34px карточки под собственный action-icon). Равноудалённая
от обеих точка — y=373px (33px до каждой), а не геометрический центр
сетки 356px (712/2) — из-за несимметричного резерва под action-icon.
• по горизонтали — строго центр колонок (gap 32px, по 16px до каждой
карточки), там асимметрии нет.
Медальон центрирован в (50%, 373px). Такое позиционирование даёт ТОЧНО
одинаковое перекрытие всех 4 углов по построению (зеркальная симметрия
по обеим осям), проверено headless-рендером: offset между углами — 0px
(см. design/tools/ — рендер-проверка выполнялась вручную скриптом, не
хранится в репозитории).
Диаметр 156px (было 132px) при радиусе скругления карточек 32px
(--radius-3xl) даёт перекрытие видимой (закруглённой) части каждой
карточки ≈29px по диагонали — в середине требуемого диапазона 2035px. */
.hub-logo {
position: absolute;
top: 373px; left: 50%;
transform: translate(-50%, -50%);
width: 156px; height: 156px;
border-radius: 50%;
background: var(--color-surface);
border: var(--border-thick) solid var(--color-ink-700);
box-shadow: var(--shadow-lg);
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 6px;
z-index: 20;
pointer-events: none;
}
.hub-logo .logo-mark {
width: 56px; height: 56px;
color: var(--color-ink-700);
}
.hub-logo .logo-word {
font: 600 16px/1 var(--font-display);
color: var(--color-ink-700);
}
/* ---------- Появление ---------- */
@media (prefers-reduced-motion: no-preference) {
.action, .hub-logo {
animation: rise 480ms cubic-bezier(0.22, 0.9, 0.3, 1) backwards;
}
.action--create { animation-delay: 60ms; }
.action--calendar { animation-delay: 140ms; }
.action--join { animation-delay: 220ms; }
.action--mine { animation-delay: 300ms; }
.hub-logo { animation-delay: 420ms; }
@keyframes rise {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
.hub-logo { animation-name: rise-logo; }
@keyframes rise-logo {
from { opacity: 0; transform: translate(-50%, calc(-50% + 14px)); }
to { opacity: 1; transform: translate(-50%, -50%); }
}
}
</style>
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-video" viewBox="0 0 24 24"><path d="M15 6H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2v-2.5l5 3.5V7l-5 3.5V8a2 2 0 0 0-2-2z"/></symbol>
<symbol id="i-calendar" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></symbol>
<symbol id="i-link2" viewBox="0 0 24 24"><path d="M9 17H7A5 5 0 0 1 7 7h2"/><path d="M15 7h2a5 5 0 1 1 0 10h-2"/><line x1="8" y1="12" x2="16" y2="12"/></symbol>
<symbol id="i-user" viewBox="0 0 24 24"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
<symbol id="i-download" 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"/></symbol>
<symbol id="i-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></symbol>
<symbol id="i-layout-dashboard" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="9" rx="1"/><rect x="14" y="3" width="7" height="5" rx="1"/><rect x="14" y="12" width="7" height="9" rx="1"/><rect x="3" y="16" width="7" height="5" rx="1"/></symbol>
<symbol id="i-logout" viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></symbol>
</svg>
<header class="topbar">
<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="topbar-right">
<button class="user-pill">
<span class="avatar" aria-hidden="true">ИИ</span>
Иванов Иван
</button>
<div class="menu-wrap">
<button class="burger" id="burger" aria-expanded="false" aria-controls="main-menu" aria-label="Открыть меню">
<svg class="icon" viewBox="0 0 24 24"><line x1="4" y1="7" x2="20" y2="7"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="17" x2="20" y2="17"/></svg>
</button>
<nav class="menu" id="main-menu" aria-label="Основное меню">
<a href="#"><svg class="icon"><use href="#i-user"/></svg> Профиль</a>
<a href="admin.html"><svg class="icon"><use href="#i-layout-dashboard"/></svg> Админка</a>
<a href="#"><svg class="icon"><use href="#i-download"/></svg> Скачать приложение</a>
<a href="#"><svg class="icon"><use href="#i-info"/></svg> О сервисе</a>
<div class="menu-divider"></div>
<a href="auth.html" class="danger"><svg class="icon"><use href="#i-logout"/></svg> Выйти</a>
</nav>
</div>
</div>
</header>
<main>
<div class="hub-wrap">
<div class="hub-intro">
<p class="eyebrow">[ ДОБРО ПОЖАЛОВАТЬ В VIDCONF ]</p>
<h1>Что делаем сегодня?</h1>
</div>
<div class="hub">
<a class="action action--create" href="room.html">
<span class="action-icon" aria-hidden="true"><svg class="icon"><use href="#i-video"/></svg></span>
<h2 class="action-title">Создать</h2>
<p class="action-object">конференцию</p>
<p class="action-note">начнётся сразу — вы первый участник</p>
</a>
<a class="action action--calendar" href="calendar.html">
<span class="action-icon" aria-hidden="true"><svg class="icon"><use href="#i-calendar"/></svg></span>
<h2 class="action-title">Календарь</h2>
<p class="action-object">конференций</p>
<p class="action-note">запланировать · закрепить · повторять</p>
</a>
<a class="action action--join" href="join.html">
<span class="action-icon" aria-hidden="true"><svg class="icon"><use href="#i-link2"/></svg></span>
<h2 class="action-title">Подключиться</h2>
<p class="action-object">к конференции</p>
<p class="action-note">по ссылке или номеру</p>
</a>
<a class="action action--mine" href="my-conferences.html">
<span class="action-icon" aria-hidden="true"><svg class="icon"><use href="#i-user"/></svg></span>
<h2 class="action-title">Мои</h2>
<p class="action-object">конференции</p>
<p class="action-note">закреплённые и предстоящие</p>
</a>
<div class="hub-logo" aria-hidden="true">
<svg class="logo-mark" viewBox="0 0 64 64" fill="none" aria-hidden="true">
<polygon points="41,32 36.5,39.8 27.5,39.8 23,32 27.5,24.2 36.5,24.2" fill="currentColor"/>
<circle cx="32" cy="32" r="15" stroke="currentColor" stroke-width="3" stroke-dasharray="16 7 28 9 12 16" stroke-dashoffset="5"/>
<circle cx="32" cy="32" r="20" stroke="currentColor" stroke-width="2" stroke-dasharray="22 10 8 14 34 12 6 10" stroke-dashoffset="40"/>
<circle cx="32" cy="32" r="25" stroke="currentColor" stroke-width="3.5" stroke-dasharray="40 14 12 10 26 16 20 8" stroke-dashoffset="80"/>
<circle cx="32" cy="32" r="29" stroke="currentColor" stroke-width="1.6" stroke-dasharray="10 6 60 20 30 10 24 8" stroke-dashoffset="120"/>
</svg>
<span class="logo-word">VidConf</span>
</div>
</div>
</div>
</main>
<script>
// Бургер-меню: открытие/закрытие, клик мимо и Escape закрывают.
const burger = document.getElementById('burger');
const menu = document.getElementById('main-menu');
function setMenu(open) {
menu.classList.toggle('is-open', open);
burger.setAttribute('aria-expanded', String(open));
burger.setAttribute('aria-label', open ? 'Закрыть меню' : 'Открыть меню');
}
burger.addEventListener('click', () => setMenu(!menu.classList.contains('is-open')));
document.addEventListener('click', (e) => {
if (!e.target.closest('.menu-wrap')) setMenu(false);
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') { setMenu(false); burger.focus(); }
});
</script>
</body>
</html>