Files
vidconf/design/mockups/lobby.html

415 lines
22 KiB
HTML
Raw Permalink 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>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>