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,188 @@
# VidConf — тёмные варианты оболочки
Обязательный референс для разработки.
Тёмная тема для экранов «оболочки» (auth/лобби/календарь/join/«мои
конференции»/админка), реализована **поверх** дизайн-системы, а не взамен
неё. `design/tokens.css` и `design/DESIGN_SYSTEM.md` — единственный
источник истины по значениям (см. §0 `DESIGN_SYSTEM.md`: «светлая тема —
оболочка (со светлым/тёмным вариантом), тёмная — комната конференции
всегда»). Реализация во frontend — `frontend/src/hooks/useTheme.ts` и
`frontend/src/components/ui/ThemeToggle.tsx` (см. `frontend/README.md`,
раздел «Темы оболочки»).
## Что здесь
Разметка, контент и русские данные каждого файла — 1:1 копия
соответствующего светлого макета из `design/mockups/`. Меняется только
цветовая схема: вместо индиго-на-белом (`referens.png`) — графит-мята-роза-
янтарь той же гаммы, что уже утверждена для комнаты конференции
(`collor_ico.png`, токены `[data-theme="room"]` в `tokens.css`).
| Файл | Светлый прообраз | Экран |
|---|---|---|
| `login.html` | `../auth.html` | Вход / регистрация / подтверждение email |
| `lobby.html` | `../lobby.html` | Лобби — хаб действий 2×2 |
| `calendar.html` | `../calendar.html` | Календарь-менеджер конференций (+ создание/редактирование/отмена) |
| `join.html` | `../join.html` | Подключение по ссылке/номеру → представиться (гость) → пароль |
| `my-conferences.html` | `../my-conferences.html` | Мои конференции |
| `admin.html` | `../admin.html` | Админка (конференции, пользователи, рассылка приглашений) |
`room.html` и `email-summary.html` в комплект не входят: комната уже тёмная
(эталон гаммы), а письмо-саммари по правилам проекта всегда светлое
(почтовые клиенты не гарантируют поддержку тёмной темы/CSS-переменных).
Ссылки на «Войти»/«Создать» из `lobby.html`/`my-conferences.html` ведут на
уже существующий `../room.html`; ссылка «Открыть саммари» — на
`../email-summary.html`.
Каждый файл самодостаточен: встроенный `<style>`, инлайн-SVG-иконки
(lucide-совместимые контуры), без CDN и внешних сетевых зависимостей —
открывается двойным кликом. Единственная локальная зависимость —
`<link rel="stylesheet" href="../../tokens.css">` (тот же приём, что и в
светлых макетах: используются базовые не-цветовые токены — spacing, радиусы,
типографика, тени-заготовки, шрифты).
## Переключатель темы
В шапке каждого макета — сегментированный переключатель ☀ / 🌙 (иконки
`Sun`/`Moon`, lucide-стиль), «Тёмная» — активный сегмент. Элемент
статичный (демонстрирует точку входа в реальный toggle, не переключает
тему по клику в макете) и размещён:
- `login.html` — плавающей плашкой в правом верхнем углу (у экрана нет
общего топбара, панель бренда/форма — раздельные колонки);
- `lobby.html`, `calendar.html`, `my-conferences.html` — внутри правой
группы топбара, рядом с меню пользователя;
- `join.html` — в топбаре, справа от логотипа;
- `admin.html` — в шапке сайдбара, рядом с логотипом.
## Как получена палитра
### Принцип
Тёмная схема **не изобретает новых цветов**, а переиспользует уже
утверждённые токены комнаты конференции (`tokens.css`, блок
`[data-theme='room']`, см. `DESIGN_SYSTEM.md` §1.2) в новых ролях — так
оболочка и комната читаются как одна система, когда пользователь входит
из тёмного лобби в тёмную комнату. Все переопределения делаются одним
блоком `:root { … }` в начале `<style>` каждого файла — он перекрывает
одноимённые переменные `:root` из `tokens.css` (та же специфичность,
позже в источнике — выигрывает), поэтому весь остальной CSS ниже —
дословная копия светлого макета и автоматически перекрашивается.
### Токены, переиспользованные из комнаты 1:1 (без изменения hex)
| Токен оболочки (dark) | = токен комнаты | Hex | Новая роль в оболочке |
|---|---|---|---|
| `--color-bg` | `--color-room-bg` | `#1E1E1E` | фон страницы |
| `--color-surface` | `--color-room-surface` | `#2A2A2A` | карточки, топбар, поля |
| `--color-border` | `--color-room-tile-border` | `#3F3F3F` | базовая обводка |
| `--color-ink-900` | `--color-room-text-primary` | `#E8E8E8` | основной текст |
| `--color-ink-500` | `--color-room-text-secondary` | `#B0B0B0` | вторичный текст |
| `--color-ink-300` | `--color-room-text-tertiary` | `#7A7A7A` | плейсхолдеры/disabled |
| `--color-ink-700`, `--color-accent`, `--color-border-pill`, `--color-success`, `--color-success-dot` | `--color-room-mic-on` | `#7FDDA8` | мята: бренд/заголовки/ссылки/CTA/«успех» — единый пастельно-зелёный вектор (см. ниже) |
| `--color-danger`, `--color-danger-dot` | `--color-room-mic-off` | `#EB93A1` | розово-красный: текст/иконки/обводка статуса «отменена»/ошибки |
| `--color-warning`, `--color-warning-dot` | `--color-room-speaker-ring` | `#D6A83D` | янтарь: статус «запланирована» |
| `--color-focus-ring` | `--color-room-focus-ring` | `#D6A83D` | кольцо `:focus-visible` — то же значение, что и в комнате |
| `--color-danger-solid` | `--color-room-danger-bg` | `#B85468` | заливка **сплошных** danger-кнопок (см. ниже, почему это отдельный токен) |
| `--color-danger-solid-hover` | `--color-room-danger-bg-hover` | `#B9495F` | hover сплошной danger-кнопки |
**Одна мята — три роли.** В светлой оболочке бренд-индиго (`--color-ink-700`,
заголовки/ссылки/обводка pill-кнопок) и мятный CTA (`--color-accent`) — два
разных токена с разными hex. В комнате пастельная мята уже одна на всё
«позитивное» (`--color-room-mic-on` = «микрофон включён» + активная
демонстрация экрана + счётчик участников + кнопка отправки в чате). Чтобы
не завозить в тёмную оболочку новый, ничем не подкреплённый в комнате
оттенок, `--color-ink-700` и `--color-accent` в тёмной оболочке **сведены
к одному и тому же** `#7FDDA8` — бренд, заголовки, ссылки, обводка
secondary-кнопок и заливка CTA используют один и тот же «фирменный» зелёный.
Различие ролей (бренд-текст vs интерактивная заливка) остаётся читаемым за
счёт контекста (текст vs заливка с тёмным текстом поверх), а не за счёт
второго оттенка — это соответствует прецеденту самой комнаты.
**Почему заливка danger-кнопок — отдельный токен, а не `--color-danger`.**
В светлой теме один hex (`#C7364F`) одинаково хорошо работает и как текст на
белом, и как заливка кнопки с белым текстом (тёмный, насыщенный тон). В
тёмной теме эти две роли расходятся: `--color-room-mic-off` (`#EB93A1`) —
светлый пастельный тон, читаемый как **текст на тёмном фоне** (6.30:1 на
`#2A2A2A`), но слишком светлый, чтобы на нём читался белый текст кнопки
(≈1.9:1, провал). Поэтому для сплошных danger-кнопок (`.btn-danger`
«Отменить все повторения» в календаре, «Удалить» в админке) заведена
отдельная пара `--color-danger-solid` / `--color-danger-solid-hover`,
переиспользующая уже готовые `--color-room-danger-bg(-hover)` (`#B85468` /
`#B9495F`, задокументированы в `DESIGN_SYSTEM.md` §1.2 с контрастом
4.66:1/5.02:1 для белого текста).
### Новые токены (введены только там, где у комнаты нет готового аналога)
Комната — экран без брендинга, форм и многошаговых карточек, поэтому под
нужды оболочки понадобилось несколько новых значений. Все получены
подмешиванием уже утверждённых цветов комнаты к графитовому фону/друг к
другу (не независимо придуманные тона), проверены по WCAG 2.1 (см. расчёт
ниже, тот же метод, что в `design/tools/contrast.py`):
| Токен | Hex | Как получен | Контраст | Роль |
|---|---|---|---|---|
| `--color-bg-alt` | `#242424` | между `--color-room-bg` (`#1E1E1E`) и `--color-room-surface` (`#2A2A2A`) | — | альт-фон секций, инпуты, disabled-поля |
| `--color-border-strong` | `#585858` | осветление `--color-border` | 2.02:1 на surface (нетекст, декоративный hover-акцент — дублируется тенью/курсором, не единственный носитель состояния) | обводка при hover |
| `--color-ink-400` | `#5D9A78` | мята `#7FDDA8` × фон `#1E1E1E`, смешение 35% | 5.05:1 на bg / 4.35:1 на surface | эйбрау-подписи `[ В СКОБКАХ ]`, крупные подписи (аналог ⚠︎ large-text-only из светлой темы, но с запасом) |
| `--color-accent-hover` | `#75CA9A` | мята × фон, смешение 10% | текст `#10331F` на заливке: 7.04:1 | hover CTA |
| `--color-accent-active` | `#6CB78C` | мята × фон, смешение 20% | текст `#10331F` на заливке: 5.80:1 | active/pressed CTA |
| `--color-accent-text` | `#10331F` | **не новый** — переиспользован буквальный hex, уже встречающийся инлайн в `room.html` (`.chat-input-row button`, `.tb-btn.is-positive-active`, `.badge-count`) как цвет текста на заливке `--color-room-mic-on` | 8.45:1 на мяте | текст на CTA/бренд-заливках мятой |
| `--color-success-bg` | `rgba(127,221,168,.14)` | мята с низкой непрозрачностью поверх тёмной поверхности | — | подложка бейджей «идёт сейчас»/«свободна»/«активен» |
| `--color-danger-bg` | `rgba(235,147,161,.14)` | розовый (mic-off) с низкой непрозрачностью | — | подложка бейджей «отменена» |
| `--color-warning-bg` | `rgba(214,168,61,.16)` | янтарь (speaker-ring) с низкой непрозрачностью | — | подложка бейджей «запланирована» |
| `--shadow-sm/md/lg/glass` | `rgba(0,0,0,.35.5)` | по образцу `--shadow-room-panel` комнаты (тёмные тени вместо индиго-тонированных) | — | тени карточек/модалок/glass-панелей |
Контраст всех текстовых пар проверен по формуле WCAG 2.1 — пары, использованные
в макетах (основной текст, вторичный текст, мята/роза/янтарь как текст на
`bg`/`surface`/`tile`, белый текст на `--color-danger-solid`), все дают
≥ 4.5:1 (текст) либо ≥ 3:1 (декоративные элементы, статус которых также
задублирован иконкой/текстом — см. правило §5 `DESIGN_SYSTEM.md`). Регрессия
этих пар — в `design/tools/contrast.py`.
## Точечные правки поверх скопированного CSS
Для каждого файла CSS светлого макета скопирован дословно (все `.class`
правила без изменений) — перекраска идёт только через блок `:root`. Ручных
точечных правок пять видов, во всех случаях — потому что исходное правило
использовало **литеральный hex/rgba**, а не `var()`, и не могло
перекраситься автоматически:
1. **Аватары и `.weekday-chip.is-selected`/`.badge-role-admin`** — в
светлой теме заливка `var(--color-ink-700)` (тёмный индиго) сочеталась с
жёстким `color: #fff`. Так как `--color-ink-700` теперь = мята (светлый
тон), белый текст перестаёт читаться (≈1.9:1) — заменено на
`var(--color-accent-text)` (`#10331F`, 8.45:1 на мяте).
2. **`.btn-danger`** (`calendar.html`, `admin.html`) — добавлена отдельная
заливка `var(--color-danger-solid)` вместо `var(--color-danger)` (см.
таблицу выше); белый текст кнопки не менялся.
3. **`.copy-toast`** (`my-conferences.html`) — фон был `var(--color-ink-900)`
(тёмный в светлой теме → стал бы светло-серым в тёмной, ломая тёмный
тултип); заменён на буквальный `#333333` (= `--color-room-surface-raised`),
текст остаётся `var(--color-ink-900)` (теперь светлый) — тултип снова
тёмный чип со светлым текстом.
4. **Декоративные градиенты/rgba-подложки** (`login.html`
`.brand-panel`/`.brand-panel::after`, `lobby.html` фон `body`,
`calendar.html` `.day-col.is-today`/`.cancel-alert`, `login.html`/
`join.html`/`calendar.html` box-shadow фокуса и ошибки, glass-обводка
`rgba(255,255,255,.7)` в `login.html`/`admin.html`, disabled-заливка
кнопок) — старые rgba были построены на индиго/старом danger-hex;
заменены на тональные аналоги новой палитры (мятное свечение вместо
индигового/мятного на белом, янтарный focus-glow вместо индигового,
розовый error-glow, `rgba(255,255,255,.08)` вместо `.7` для тёмного
стекла).
5. **`color-scheme: dark`** добавлен в блок `:root`, чтобы нативные
элементы форм (скроллбар, чекбоксы без кастомной стилизации) тоже
рендерились в тёмной палитре браузера.
## Доступность (проверено при подборе палитры)
- Текст ≥ 4.5:1 везде, кроме крупных/декоративных элементов (аналогично
правилу §5 `DESIGN_SYSTEM.md` для светлой темы, порог 3:1).
- Статус не передаётся только цветом: бейджи конференций — иконка + текст
(как в светлой теме), «скопировано» — смена иконки `Copy → Check` +
текстовый тултип, ошибки полей — иконка `AlertCircle` + текст под полем,
`:focus-visible` — везде видимое кольцо (`--color-focus-ring`, тот же
янтарь, что и в комнате).
- Палитра пастельная (мята/роза/янтарь той же светлоты, что уже утверждена
для комнаты), неоновых/кислотных акцентов не вводилось.

View File

@@ -0,0 +1,303 @@
<!doctype html>
<html lang="ru" data-theme="shell-dark">
<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>
/* =====================================================================
* ТЁМНЫЙ ВАРИАНТ ОБОЛОЧКИ — ПРЕДЛОЖЕНИЕ, НЕ УТВЕРЖДЕНО.
* Разметка и контент идентичны design/mockups/admin.html 1:1, меняется
* только цветовая схема — палитра переиспользует токены комнаты
* конференции. Обоснование цветов — design/mockups/dark/README.md.
* ===================================================================== */
:root {
color-scheme: dark;
--color-bg: #1E1E1E;
--color-bg-alt: #242424;
--color-surface: #2A2A2A;
--color-surface-glass: rgba(51, 51, 51, .55);
--color-border: #3F3F3F;
--color-border-strong: #585858;
--color-border-pill: #7FDDA8;
--color-ink-900: #E8E8E8;
--color-ink-700: #7FDDA8;
--color-ink-500: #B0B0B0;
--color-ink-400: #5D9A78;
--color-ink-300: #7A7A7A;
--color-accent: #7FDDA8;
--color-accent-hover: #75CA9A;
--color-accent-active: #6CB78C;
--color-accent-text: #10331F;
--color-success: #7FDDA8;
--color-success-bg: rgba(127, 221, 168, .14);
--color-success-dot: #7FDDA8;
--color-danger: #EB93A1;
--color-danger-bg: rgba(235, 147, 161, .14);
--color-danger-dot: #EB93A1;
--color-danger-solid: #B85468;
--color-danger-solid-hover: #B9495F;
--color-warning: #D6A83D;
--color-warning-bg: rgba(214, 168, 61, .16);
--color-warning-dot: #D6A83D;
--color-focus-ring: #D6A83D;
--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 { display: inline-flex; gap: 2px; background: var(--color-bg-alt); padding: 4px; border-radius: var(--radius-full); border: 1px solid var(--color-border); }
.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; }
.app-shell { display: flex; min-height: 100vh; }
/* ---------- Сайдбар ---------- */
.sidebar { width: 260px; flex-shrink: 0; background: var(--color-surface); border-right: 1px solid var(--color-border); padding: var(--space-6) var(--space-4); display: flex; flex-direction: column; }
.sidebar-head { display: flex; align-items: center; justify-content: space-between; padding: 0 var(--space-2); margin-bottom: 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; }
.side-nav { display: flex; flex-direction: column; gap: 2px; }
.side-link {
display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: var(--radius-md);
font: var(--text-body); font-weight: 600; color: var(--color-ink-500); text-decoration: none;
}
.side-link:hover { background: var(--color-bg-alt); color: var(--color-ink-900); }
.side-link.is-active { background: var(--color-bg-alt); color: var(--color-ink-700); }
.side-link .icon { color: inherit; }
.side-footer { margin-top: auto; padding: var(--space-3) var(--space-2); border-top: 1px solid var(--color-border); }
.avatar-btn { display: flex; align-items: center; gap: 8px; padding: 8px 0; width: 100%; background: none; border: none; text-align: left; }
.avatar { width: 32px; height: 32px; border-radius: 50%; background: var(--color-ink-700); color: var(--color-accent-text); display:flex; align-items:center; justify-content:center; font: var(--text-caption); text-transform: none; letter-spacing: 0; flex-shrink: 0; }
.avatar-btn div strong { display: block; font: var(--text-body); color: var(--color-ink-900); }
.avatar-btn div span { font: 12px var(--font-body); color: var(--color-ink-500); }
/* ---------- Контент ---------- */
main { flex: 1; padding: var(--space-8); max-width: 1220px; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: var(--space-6); }
.page-head h1 { font: var(--text-display-lg); font-family: var(--font-display); color: var(--color-ink-700); margin: 0 0 6px; }
.page-head p { font: var(--text-body-lg); color: var(--color-ink-500); margin: 0; }
.btn { display: inline-flex; align-items: center; gap: 8px; border: none; cursor: pointer; font-family: var(--font-body); white-space: nowrap; transition: background var(--transition-fast), box-shadow var(--transition-fast); }
.btn-primary { background: var(--color-accent); color: var(--color-accent-text); font: var(--text-body); font-weight: 700; padding: 11px 22px; border-radius: var(--radius-full); }
.btn-primary:hover { background: var(--color-accent-hover); box-shadow: var(--shadow-md); }
.btn-secondary { background: transparent; color: var(--color-ink-700); border: 1.5px solid var(--color-border-pill); font: var(--text-body); font-weight: 600; padding: 9px 18px; border-radius: var(--radius-full); }
.btn-secondary:hover { background: var(--color-bg-alt); }
.btn-danger { background: var(--color-danger); color: #fff; font: var(--text-body); font-weight: 700; padding: 9px 18px; border-radius: var(--radius-full); }
/* Стат-виджеты (glass) */
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); margin-bottom: 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-5); background-color: var(--color-bg-alt);
}
.stat-glass strong { display: block; font: var(--text-display-xl); font-family: var(--font-display); font-size: 36px; color: var(--color-ink-700); }
.stat-glass span { font: var(--text-body); color: var(--color-ink-500); }
/* Табы разделов */
.section-tabs { display: flex; gap: var(--space-2); margin-bottom: var(--space-5); border-bottom: 1px solid var(--color-border); }
.section-tab { padding: 10px 4px; margin-right: var(--space-6); background: none; border: none; border-bottom: 2px solid transparent; font: var(--text-body-lg); font-weight: 600; color: var(--color-ink-500); }
.section-tab.is-active { color: var(--color-ink-700); border-color: var(--color-accent-active); }
.toolbar-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-5); gap: var(--space-4); }
.search-wrap { position: relative; width: 320px; }
.search-wrap .icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--color-ink-400); }
.search-wrap input { width: 100%; font: var(--text-body); padding: 9px 12px 9px 38px; border-radius: var(--radius-full); border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-ink-900); }
.search-wrap input::placeholder { color: var(--color-ink-300); }
.search-wrap input:focus { outline: none; border-color: var(--color-focus-ring); }
/* Таблица */
.table-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-2xl); overflow: hidden; margin-bottom: var(--space-8); }
table { width: 100%; border-collapse: collapse; }
thead th { text-align: left; background: var(--color-bg-alt); font: var(--text-caption); color: var(--color-ink-500); padding: 12px 20px; text-transform: uppercase; }
tbody td { padding: 14px 20px; font: var(--text-body-lg); color: var(--color-ink-900); border-top: 1px solid var(--color-border); }
tbody tr:hover { background: var(--color-bg-alt); }
.badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border-radius: var(--radius-full); font: var(--text-caption); white-space: nowrap; }
.badge-free { background: var(--color-success-bg); color: var(--color-success); }
.badge-busy { background: var(--color-danger-bg); color: var(--color-danger); }
.badge-booked { background: var(--color-warning-bg); color: var(--color-warning); }
.badge-locked { background: var(--color-bg-alt); color: var(--color-ink-700); }
.badge-role-admin { background: var(--color-ink-700); color: var(--color-accent-text); }
.badge-role-user { background: var(--color-bg-alt); color: var(--color-ink-500); }
.row-actions { display: flex; gap: 6px; }
.icon-btn { width: 32px; height: 32px; border-radius: var(--radius-md); border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-ink-500); display:flex; align-items:center; justify-content:center; }
.icon-btn:hover { border-color: var(--color-border-strong); color: var(--color-ink-900); }
.icon-btn.danger:hover { border-color: var(--color-danger); color: var(--color-danger); background: var(--color-danger-bg); }
.user-cell { display: flex; align-items: center; gap: 10px; }
.user-cell .avatar { width: 32px; height: 32px; font-size: 12px; }
.user-cell div span { display: block; font: 13px var(--font-body); color: var(--color-ink-500); }
/* Секция рассылки */
.invite-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-2xl); padding: var(--space-6); display: grid; grid-template-columns: 1fr 320px; gap: var(--space-8); }
.invite-card h2 { font: var(--text-h2); margin: 0 0 4px; color: var(--color-ink-900); }
.invite-card p.desc { font: var(--text-body); color: var(--color-ink-500); margin: 0 0 var(--space-5); }
.invite-card textarea {
width: 100%; min-height: 140px; font: var(--text-body-lg); color: var(--color-ink-900); font-family: var(--font-mono);
padding: 14px; border-radius: var(--radius-md); border: 1px solid var(--color-border); background: var(--color-surface); resize: vertical;
}
.invite-card textarea:focus { outline: none; border-color: var(--color-focus-ring); }
.invite-side { background: var(--color-bg-alt); border-radius: var(--radius-xl); padding: var(--space-5); }
.invite-side h3 { font: var(--text-h3); margin: 0 0 var(--space-3); color: var(--color-ink-900); }
.invite-side ul { margin: 0 0 var(--space-5); padding-left: 18px; font: var(--text-body); color: var(--color-ink-500); }
.invite-side li { margin-bottom: 6px; }
/* ---------- Патчи тёмной темы: заливка mint требует тёмного текста, а не белого ---------- */
.btn-danger { background: var(--color-danger-solid); }
.btn-danger:hover { filter: none; background: var(--color-danger-solid-hover); }
</style>
</head>
<body>
<svg style="display:none">
<symbol id="i-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-video" viewBox="0 0 24 24"><rect x="2" y="6" width="14" height="12" rx="2"/><path d="m22 8-6 4 6 4V8Z"/></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-users" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<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-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
<symbol id="i-plus" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol>
<symbol id="i-pencil" viewBox="0 0 24 24"><path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></symbol>
<symbol id="i-trash" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></symbol>
<symbol id="i-send" viewBox="0 0 24 24"><path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/></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-ban" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="4.9" y1="4.9" x2="19.1" y2="19.1"/></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="app-shell">
<aside class="sidebar">
<div class="sidebar-head">
<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="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>
<nav class="side-nav">
<a class="side-link is-active" href="#rooms"><svg class="icon" style="width:18px;height:18px"><use href="#i-video"/></svg>Конференции</a>
<a class="side-link" href="#closed"><svg class="icon" style="width:18px;height:18px"><use href="#i-lock"/></svg>Закрытые конференции</a>
<a class="side-link" href="#users"><svg class="icon" style="width:18px;height:18px"><use href="#i-users"/></svg>Пользователи</a>
<a class="side-link" href="#invites"><svg class="icon" style="width:18px;height:18px"><use href="#i-mail"/></svg>Рассылка приглашений</a>
</nav>
<div class="side-footer">
<button class="avatar-btn"><span class="avatar">АИ</span><div><strong>Анна Иванова</strong><span>Администратор</span></div></button>
</div>
</aside>
<main>
<div class="page-head">
<div>
<h1>Администрирование</h1>
<p>Управление конференциями, доступом и пользователями VidConf</p>
</div>
<button class="btn btn-primary"><svg class="icon" style="width:16px;height:16px"><use href="#i-plus"/></svg>Добавить конференцию</button>
</div>
<section class="stat-grid">
<div class="stat-glass"><strong>100</strong><span>Всего конференций</span></div>
<div class="stat-glass"><strong>12</strong><span>Активны сейчас</span></div>
<div class="stat-glass"><strong>34</strong><span>Забронировано сегодня</span></div>
<div class="stat-glass"><strong>248</strong><span>Пользователей</span></div>
</section>
<div class="section-tabs">
<button class="section-tab is-active">Конференции</button>
<button class="section-tab">Закрытые конференции</button>
<button class="section-tab">Пользователи</button>
<button class="section-tab">Рассылка приглашений</button>
</div>
<!-- ===================== КОНФЕРЕНЦИИ ===================== -->
<div class="toolbar-row">
<div class="search-wrap"><svg class="icon"><use href="#i-search"/></svg><input type="text" placeholder="Поиск конференции…"></div>
<span style="font:14px var(--font-body); color:var(--color-ink-500)">Показано 6 из 100</span>
</div>
<div class="table-card">
<table>
<thead><tr><th>Конференция</th><th>Вместимость</th><th>Статус</th><th>Тип</th><th></th></tr></thead>
<tbody>
<tr><td>Переговорная №01</td><td>8 мест</td><td><span class="badge badge-free"><svg class="icon" style="width:12px;height:12px"><use href="#i-check-circle"/></svg>Свободна</span></td><td>Открытая</td>
<td><div class="row-actions"><button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:16px;height:16px"><use href="#i-pencil"/></svg></button><button class="icon-btn danger" aria-label="Удалить"><svg class="icon" style="width:16px;height:16px"><use href="#i-trash"/></svg></button></div></td></tr>
<tr><td>Переговорная №02</td><td>10 мест</td><td><span class="badge badge-busy"><svg class="icon" style="width:12px;height:12px"><use href="#i-ban"/></svg>Занята</span></td><td>Открытая</td>
<td><div class="row-actions"><button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:16px;height:16px"><use href="#i-pencil"/></svg></button><button class="icon-btn danger" aria-label="Удалить"><svg class="icon" style="width:16px;height:16px"><use href="#i-trash"/></svg></button></div></td></tr>
<tr><td>Переговорная №04</td><td>12 мест</td><td><span class="badge badge-locked"><svg class="icon" style="width:12px;height:12px"><use href="#i-lock"/></svg>По паролю</span></td><td>Закрытая</td>
<td><div class="row-actions"><button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:16px;height:16px"><use href="#i-pencil"/></svg></button><button class="icon-btn danger" aria-label="Удалить"><svg class="icon" style="width:16px;height:16px"><use href="#i-trash"/></svg></button></div></td></tr>
<tr><td>Переговорная №42</td><td>16 мест</td><td><span class="badge badge-booked"><svg class="icon" style="width:12px;height:12px"><use href="#i-clock"/></svg>Забронирована</span></td><td>Открытая</td>
<td><div class="row-actions"><button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:16px;height:16px"><use href="#i-pencil"/></svg></button><button class="icon-btn danger" aria-label="Удалить"><svg class="icon" style="width:16px;height:16px"><use href="#i-trash"/></svg></button></div></td></tr>
</tbody>
</table>
</div>
<!-- ===================== ПОЛЬЗОВАТЕЛИ ===================== -->
<div class="page-head" style="margin-bottom: var(--space-4)"><h2 style="font:var(--text-h2); color:var(--color-ink-900); margin:0">Пользователи</h2></div>
<div class="table-card">
<table>
<thead><tr><th>Пользователь</th><th>Роль</th><th>Регистрация</th><th>Статус</th><th></th></tr></thead>
<tbody>
<tr>
<td><div class="user-cell"><span class="avatar">АИ</span><div>Анна Иванова<span>a.ivanova@vidconf.dev</span></div></div></td>
<td><span class="badge badge-role-admin">Администратор</span></td>
<td>03.02.2026</td>
<td><span class="badge badge-free"><svg class="icon" style="width:12px;height:12px"><use href="#i-check-circle"/></svg>Активен</span></td>
<td><div class="row-actions"><button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:16px;height:16px"><use href="#i-pencil"/></svg></button></div></td>
</tr>
<tr>
<td><div class="user-cell"><span class="avatar">МК</span><div>Мария Кузнецова<span>m.kuznetsova@company.ru</span></div></div></td>
<td><span class="badge badge-role-user">Пользователь</span></td>
<td>15.07.2026</td>
<td><span class="badge badge-booked"><svg class="icon" style="width:12px;height:12px"><use href="#i-clock"/></svg>Ждёт подтверждения</span></td>
<td><div class="row-actions"><button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:16px;height:16px"><use href="#i-pencil"/></svg></button><button class="icon-btn danger" aria-label="Удалить"><svg class="icon" style="width:16px;height:16px"><use href="#i-trash"/></svg></button></div></td>
</tr>
</tbody>
</table>
</div>
<!-- ===================== РАССЫЛКА ПРИГЛАШЕНИЙ ===================== -->
<div class="page-head" style="margin-bottom: var(--space-4)"><h2 style="font:var(--text-h2); color:var(--color-ink-900); margin:0">Рассылка приглашений</h2></div>
<div class="invite-card">
<div>
<h2>Пригласить новых пользователей</h2>
<p class="desc">Каждый адрес получит письмо со ссылкой регистрации, действительной 7 дней.</p>
<textarea placeholder="i.sokolov@company.ru&#10;o.panina@company.ru&#10;v.sedykh@company.ru">i.sokolov@company.ru
o.panina@company.ru
v.sedykh@company.ru</textarea>
<button class="btn btn-primary" style="margin-top: var(--space-4)"><svg class="icon" style="width:16px;height:16px"><use href="#i-send"/></svg>Отправить приглашения (3)</button>
</div>
<div class="invite-side">
<h3>Последняя рассылка</h3>
<ul>
<li>28.06.2026 · 12 приглашений</li>
<li>9 приняли, 3 ожидают</li>
</ul>
<button class="btn btn-secondary" style="width:100%; justify-content:center">Посмотреть журнал</button>
</div>
</div>
</main>
</div>
</body>
</html>

View File

@@ -0,0 +1,558 @@
<!doctype html>
<html lang="ru" data-theme="shell-dark">
<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>
/* =====================================================================
* ТЁМНЫЙ ВАРИАНТ ОБОЛОЧКИ — ПРЕДЛОЖЕНИЕ, НЕ УТВЕРЖДЕНО.
* Разметка и контент идентичны design/mockups/calendar.html 1:1, меняется
* только цветовая схема — палитра переиспользует токены комнаты
* конференции. Обоснование цветов — design/mockups/dark/README.md.
* ===================================================================== */
:root {
color-scheme: dark;
--color-bg: #1E1E1E;
--color-bg-alt: #242424;
--color-surface: #2A2A2A;
--color-surface-glass: rgba(51, 51, 51, .55);
--color-border: #3F3F3F;
--color-border-strong: #585858;
--color-border-pill: #7FDDA8;
--color-ink-900: #E8E8E8;
--color-ink-700: #7FDDA8;
--color-ink-500: #B0B0B0;
--color-ink-400: #5D9A78;
--color-ink-300: #7A7A7A;
--color-accent: #7FDDA8;
--color-accent-hover: #75CA9A;
--color-accent-active: #6CB78C;
--color-accent-text: #10331F;
--color-success: #7FDDA8;
--color-success-bg: rgba(127, 221, 168, .14);
--color-success-dot: #7FDDA8;
--color-danger: #EB93A1;
--color-danger-bg: rgba(235, 147, 161, .14);
--color-danger-dot: #EB93A1;
--color-danger-solid: #B85468;
--color-danger-solid-hover: #B9495F;
--color-warning: #D6A83D;
--color-warning-bg: rgba(214, 168, 61, .16);
--color-warning-dot: #D6A83D;
--color-focus-ring: #D6A83D;
--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 { display: inline-flex; gap: 2px; background: var(--color-bg-alt); padding: 4px; border-radius: var(--radius-full); border: 1px solid var(--color-border); }
.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; }
.topbar {
position: sticky; top: 0; z-index: 20;
display: flex; align-items: center; justify-content: space-between;
padding: var(--space-4) var(--space-8);
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
}
.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-5); }
.nav-link { font: var(--text-body); font-weight: 600; color: var(--color-ink-500); text-decoration: none; }
.nav-link.is-active { color: var(--color-ink-700); }
.avatar-btn { display: flex; align-items: center; gap: 8px; background: var(--color-bg-alt); border: 1px solid var(--color-border); border-radius: var(--radius-full); padding: 6px 14px 6px 6px; }
.avatar { width: 28px; height: 28px; border-radius: 50%; background: var(--color-ink-700); color: var(--color-accent-text); display:flex; align-items:center; justify-content:center; font: var(--text-caption); text-transform: none; letter-spacing: 0; }
.avatar-btn .user-name { font: var(--text-body); font-weight: 600; color: var(--color-ink-900); }
main { padding: var(--space-8); max-width: 1440px; margin: 0 auto; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: var(--space-6); }
.page-head h1 { font: var(--text-display-lg); font-family: var(--font-display); color: var(--color-ink-700); margin: 0 0 6px; }
.page-head p { font: var(--text-body-lg); color: var(--color-ink-500); margin: 0; max-width: 560px; }
.btn { display: inline-flex; align-items: center; gap: 8px; border: none; cursor: pointer; font-family: var(--font-body); white-space: nowrap; transition: background var(--transition-fast), box-shadow var(--transition-fast); }
.btn-primary { background: var(--color-accent); color: var(--color-accent-text); font: var(--text-body); font-weight: 700; padding: 11px 22px; 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,.3); color: var(--color-ink-300); cursor: not-allowed; }
.btn-secondary { background: transparent; color: var(--color-ink-700); border: 1.5px solid var(--color-border-pill); font: var(--text-body); font-weight: 600; padding: 9px 18px; border-radius: var(--radius-full); }
.btn-secondary:hover { background: var(--color-bg-alt); }
.btn-danger { background: var(--color-danger); color: #fff; font: var(--text-body); font-weight: 700; padding: 9px 18px; border-radius: var(--radius-full); }
.btn-danger:hover { filter: brightness(0.92); }
.btn-text { background: none; border: none; color: var(--color-danger); font: var(--text-body); font-weight: 600; padding: 4px; }
.btn-text:hover { text-decoration: underline; }
.btn.full { width: 100%; justify-content: center; }
:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; }
/* ---------- Основной грид: неделя + карточка создания ---------- */
.layout-grid { display: grid; grid-template-columns: 1fr 400px; gap: var(--space-6); align-items: start; }
.week-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-2xl); padding: var(--space-6); }
.week-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-5); }
.date-nav { display: flex; align-items: center; gap: var(--space-3); }
.date-nav button { width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-ink-500); display:flex; align-items:center; justify-content:center; }
.date-nav button:hover { border-color: var(--color-border-strong); color: var(--color-ink-900); }
.date-nav span { font: var(--text-h3); color: var(--color-ink-900); min-width: 200px; text-align: center; }
.view-toggle { display: flex; gap: 4px; background: var(--color-bg-alt); padding: 4px; border-radius: var(--radius-full); }
.view-toggle button { padding: 7px 16px; border-radius: var(--radius-full); border: none; background: transparent; font: var(--text-body); font-weight: 600; color: var(--color-ink-500); }
.view-toggle button.is-active { background: var(--color-surface); color: var(--color-ink-700); box-shadow: var(--shadow-sm); }
.week-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--space-4); }
.day-col { background: var(--color-bg-alt); border-radius: var(--radius-lg); padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); min-height: 460px; }
.day-col.is-today { background: rgba(127,221,168,.10); }
.day-col-head { text-align: center; padding-bottom: var(--space-2); margin-bottom: var(--space-1); border-bottom: 1px solid var(--color-border); }
.day-col-head .dow { display: block; font: var(--text-caption); color: var(--color-ink-500); }
.day-col-head .dom { display: block; font: var(--text-h3); color: var(--color-ink-900); margin-top: 2px; }
.day-col.is-today .day-col-head .dom { color: var(--color-ink-700); }
.conf-chip { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-3); display: flex; flex-direction: column; gap: 4px; cursor: pointer; transition: box-shadow var(--transition-fast), border-color var(--transition-fast); text-align: left; }
.conf-chip:hover { box-shadow: var(--shadow-sm); border-color: var(--color-border-strong); }
.chip-time { display: flex; align-items: center; gap: 4px; font: var(--text-caption); color: var(--color-ink-500); text-transform: none; letter-spacing: normal; }
.chip-title { display: flex; align-items: center; gap: 6px; font: var(--text-body); font-weight: 700; color: var(--color-ink-900); }
.chip-meta { display: flex; align-items: center; gap: 10px; font: 12px var(--font-body); color: var(--color-ink-500); }
.chip-meta .meta-item { display: flex; align-items: center; gap: 4px; }
.conf-badge { display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px; border-radius: var(--radius-full); font: var(--text-caption); font-size: 11px; width: fit-content; }
.conf-badge.scheduled { background: var(--color-warning-bg); color: var(--color-warning); }
.conf-badge.pinned { background: var(--color-bg-alt); color: var(--color-ink-700); border: 1px solid var(--color-border-strong); }
.conf-badge.live { background: var(--color-success-bg); color: var(--color-success); }
.conf-badge.cancelled { background: var(--color-danger-bg); color: var(--color-danger); }
.conf-chip.is-cancelled { opacity: .6; }
.conf-chip.is-cancelled .chip-title { text-decoration: line-through; color: var(--color-ink-500); }
.day-empty { flex: 1; display: flex; align-items: center; justify-content: center; text-align: center; font: var(--text-body); color: var(--color-ink-300); padding: var(--space-4) 0; }
/* ---------- Карточка создания / редактирования ---------- */
.create-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-2xl); padding: var(--space-6); position: sticky; top: 96px; }
.create-card h2 { font: var(--text-h2); color: var(--color-ink-900); margin: 0 0 var(--space-5); display: flex; align-items: center; gap: 8px; }
.field { margin-bottom: var(--space-5); }
.field label { display: flex; align-items: center; gap: 6px; font: var(--text-body); font-weight: 600; color: var(--color-ink-900); margin-bottom: var(--space-2); }
.field input, .field textarea, .field select {
width: 100%; font: var(--text-body-lg); color: var(--color-ink-900);
padding: 10px 14px; border-radius: var(--radius-md); border: 1px solid var(--color-border);
background: var(--color-surface);
}
.field input:focus, .field textarea:focus, .field select:focus { outline: none; border-color: var(--color-focus-ring); box-shadow: 0 0 0 3px rgba(214,168,61,.18); }
.field-row { display: flex; gap: var(--space-3); }
.field-row .field { flex: 1; }
.field-hint { font: 12px var(--font-body); color: var(--color-ink-500); margin: 6px 2px 0; }
/* Тумблер (используется и для «закрытая по паролю», и для «закрепить») */
.toggle-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-3) 0; border-top: 1px solid var(--color-border); }
.toggle-copy strong { display: block; font: var(--text-body); color: var(--color-ink-900); }
.toggle-copy span { font: 12px var(--font-body); color: var(--color-ink-500); }
.switch { position: relative; width: 44px; height: 24px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.slider { position: absolute; inset: 0; background: var(--color-border-strong); border-radius: var(--radius-full); transition: background var(--transition-fast); cursor: pointer; }
.slider::before { content:""; position:absolute; width:18px; height:18px; left:3px; top:3px; background:#fff; border-radius:50%; transition: transform var(--transition-fast); }
.switch input:checked + .slider { background: var(--color-accent-active); }
.switch input:checked + .slider::before { transform: translateX(20px); }
.switch input:focus-visible + .slider { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; }
.password-block { display: none; margin-top: var(--space-4); }
.toggle-block:has(#pw-toggle:checked) .password-block { display: block; }
/* ---------- Блок повторений (recurrence) ---------- */
.pin-block .recurrence-panel { display: none; }
.pin-block:has(input[type="checkbox"]:checked) .recurrence-panel {
display: flex; flex-direction: column; gap: var(--space-2);
margin-top: var(--space-4); padding-top: var(--space-4);
border-top: 1px dashed var(--color-border);
}
.recurrence-option-row {
display: flex; align-items: center; gap: 10px;
padding: 10px 12px; border: 1px solid var(--color-border); border-radius: var(--radius-md);
cursor: pointer; font: var(--text-body); font-weight: 600; color: var(--color-ink-900);
transition: border-color var(--transition-fast), background var(--transition-fast);
}
.recurrence-option-row input[type="radio"] { width: 16px; height: 16px; accent-color: var(--color-ink-700); flex-shrink: 0; }
.recurrence-option:has(input:checked) .recurrence-option-row { border-color: var(--color-ink-700); background: var(--color-bg-alt); }
.recurrence-sub { display: none; padding: var(--space-3) 12px 4px 38px; }
.recurrence-option:has(input:checked) .recurrence-sub { display: block; }
.weekday-toggles { display: flex; gap: 6px; flex-wrap: wrap; }
.weekday-chip {
width: 34px; height: 34px; border-radius: 50%;
border: 1.5px solid var(--color-border); background: var(--color-surface);
font: var(--text-caption); color: var(--color-ink-500);
display: flex; align-items: center; justify-content: center;
}
.weekday-chip.is-selected { background: var(--color-ink-700); border-color: var(--color-ink-700); color: var(--color-accent-text); }
.recurrence-hint { font: 12px var(--font-body); color: var(--color-ink-500); margin-top: 8px; }
/* ---------- Доп. состояния (ниже основного грида) ---------- */
.states-section { margin-top: var(--space-10); }
.states-head { margin-bottom: var(--space-5); }
.states-head h2 { font: var(--text-h1); color: var(--color-ink-900); margin: 0 0 4px; }
.states-head p { font: var(--text-body-lg); color: var(--color-ink-500); margin: 0; }
.states-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); align-items: start; }
.state-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-2xl); padding: var(--space-6); }
.screen-label { font: var(--text-caption); color: var(--color-ink-400); margin: 0 0 var(--space-4); }
.screen-label b { color: var(--color-ink-700); }
.next-occurrence { display: flex; align-items: center; gap: 8px; background: var(--color-bg-alt); border-radius: var(--radius-md); padding: 10px 14px; font: var(--text-body); color: var(--color-ink-700); font-weight: 600; margin-bottom: var(--space-5); }
.btn-row { display: flex; gap: var(--space-3); margin-top: var(--space-5); }
.btn-row .btn { flex: 1; justify-content: center; }
.danger-link-row { text-align: center; margin-top: var(--space-4); }
.cancel-alert { display: flex; gap: 10px; background: var(--color-warning-bg); border: 1px solid rgba(214,168,61,.35); color: var(--color-warning); border-radius: var(--radius-md); padding: var(--space-4); font: var(--text-body); margin-bottom: var(--space-5); }
.cancel-alert strong { display: block; margin-bottom: 2px; color: var(--color-ink-900); }
.cancel-options { display: flex; flex-direction: column; gap: var(--space-3); }
.cancel-options .btn { width: 100%; justify-content: center; }
/* ---------- Патч: заливка danger-кнопок отдельным «глубоким» тоном (не совпадает
с pastel-текстовым --color-danger, чтобы белый текст держал контраст) ---------- */
.btn-danger { background: var(--color-danger-solid); }
.btn-danger:hover { background: var(--color-danger-solid-hover); filter: none; }
</style>
</head>
<body>
<svg style="display:none">
<symbol id="i-chevron-left" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></symbol>
<symbol id="i-chevron-right" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></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-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-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-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-plus" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol>
<symbol id="i-repeat" viewBox="0 0 24 24"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/></symbol>
<symbol id="i-play" viewBox="0 0 24 24"><polygon points="6 3 20 12 6 21 6 3"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<symbol id="i-x" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></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>
<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>
<nav class="topbar-right">
<a href="lobby.html" class="nav-link">Лобби</a>
<a href="calendar.html" class="nav-link is-active">Календарь</a>
<a href="my-conferences.html" class="nav-link">Мои конференции</a>
<a href="admin.html" class="nav-link">Админка</a>
<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>
<button class="avatar-btn"><span class="avatar">АИ</span><span class="user-name">Анна Иванова</span></button>
</nav>
</header>
<main>
<div class="page-head">
<div>
<h1>Календарь конференций</h1>
<p>Запланируйте разовую встречу или закрепите постоянную конференцию с повторением — рабочая неделя 1418 июля</p>
</div>
<button class="btn btn-primary"><svg class="icon" style="width:16px;height:16px"><use href="#i-plus"/></svg>Новая конференция</button>
</div>
<div class="layout-grid">
<!-- ===================== СЕТКА КОНФЕРЕНЦИЙ ПО ДНЯМ ===================== -->
<section class="week-card">
<div class="week-toolbar">
<div class="date-nav">
<button aria-label="Предыдущая неделя"><svg class="icon" style="width:16px;height:16px"><use href="#i-chevron-left"/></svg></button>
<span>14 — 18 июля 2026</span>
<button aria-label="Следующая неделя"><svg class="icon" style="width:16px;height:16px"><use href="#i-chevron-right"/></svg></button>
</div>
<div class="view-toggle">
<button class="is-active">Неделя</button>
<button>Месяц</button>
</div>
</div>
<div class="week-grid">
<div class="day-col">
<div class="day-col-head"><span class="dow">ПН</span><span class="dom">14</span></div>
<button class="conf-chip">
<span class="conf-badge scheduled"><svg class="icon" style="width:11px;height:11px"><use href="#i-clock"/></svg>Запланирована</span>
<span class="chip-time">09:0009:15</span>
<span class="chip-title">Дейли команды</span>
<span class="chip-meta"><span class="meta-item"><svg class="icon" style="width:12px;height:12px"><use href="#i-users"/></svg>6</span></span>
</button>
<div class="day-empty">Свободный день</div>
</div>
<div class="day-col is-today">
<div class="day-col-head"><span class="dow">ВТ</span><span class="dom">15</span></div>
<button class="conf-chip">
<span class="conf-badge pinned"><svg class="icon" style="width:11px;height:11px"><use href="#i-repeat"/></svg>Закреплена</span>
<span class="chip-time">11:0011:45</span>
<span class="chip-title">Ревью спринта</span>
<span class="chip-meta"><span class="meta-item"><svg class="icon" style="width:12px;height:12px"><use href="#i-users"/></svg>8</span></span>
</button>
<button class="conf-chip">
<span class="conf-badge live"><svg class="icon" style="width:11px;height:11px"><use href="#i-play"/></svg>Идёт сейчас</span>
<span class="chip-time">14:00</span>
<span class="chip-title"><svg class="icon" style="width:13px;height:13px;flex-shrink:0"><use href="#i-lock"/></svg>Синк по релизу 2.4</span>
<span class="chip-meta"><span class="meta-item"><svg class="icon" style="width:12px;height:12px"><use href="#i-users"/></svg>4</span></span>
</button>
</div>
<div class="day-col">
<div class="day-col-head"><span class="dow">СР</span><span class="dom">16</span></div>
<button class="conf-chip is-cancelled">
<span class="conf-badge cancelled"><svg class="icon" style="width:11px;height:11px"><use href="#i-x"/></svg>Отменена</span>
<span class="chip-time">10:0010:30</span>
<span class="chip-title">Собес: Frontend</span>
</button>
<div class="day-empty">Свободный день</div>
</div>
<div class="day-col">
<div class="day-col-head"><span class="dow">ЧТ</span><span class="dom">17</span></div>
<button class="conf-chip">
<span class="conf-badge pinned"><svg class="icon" style="width:11px;height:11px"><use href="#i-repeat"/></svg>Закреплена</span>
<span class="chip-time">11:0011:45</span>
<span class="chip-title">Ревью спринта</span>
<span class="chip-meta"><span class="meta-item"><svg class="icon" style="width:12px;height:12px"><use href="#i-users"/></svg>8</span></span>
</button>
<button class="conf-chip">
<span class="conf-badge scheduled"><svg class="icon" style="width:11px;height:11px"><use href="#i-clock"/></svg>Запланирована</span>
<span class="chip-time">15:0015:30</span>
<span class="chip-title">1:1 с руководителем</span>
<span class="chip-meta"><span class="meta-item"><svg class="icon" style="width:12px;height:12px"><use href="#i-users"/></svg>2</span></span>
</button>
</div>
<div class="day-col">
<div class="day-col-head"><span class="dow">ПТ</span><span class="dom">18</span></div>
<button class="conf-chip">
<span class="conf-badge scheduled"><svg class="icon" style="width:11px;height:11px"><use href="#i-clock"/></svg>Запланирована</span>
<span class="chip-time">12:0013:00</span>
<span class="chip-title"><svg class="icon" style="width:13px;height:13px;flex-shrink:0"><use href="#i-lock"/></svg>Итоги квартала</span>
<span class="chip-meta"><span class="meta-item"><svg class="icon" style="width:12px;height:12px"><use href="#i-users"/></svg>14</span></span>
</button>
<div class="day-empty">Ещё нет конференций</div>
</div>
</div>
</section>
<!-- ===================== КАРТОЧКА СОЗДАНИЯ (состояние: создание, пусто) ===================== -->
<aside class="create-card" id="create-card">
<h2><svg class="icon"><use href="#i-calendar"/></svg>Новая конференция</h2>
<form>
<div class="field">
<label>Название</label>
<input type="text" placeholder="Например, Синк по релизу 2.4">
</div>
<div class="field-row">
<div class="field"><label>Дата</label><input type="text" value="21.07.2026"></div>
<div class="field"><label>Время</label><input type="text" value="10:0010:30"></div>
</div>
<div class="field">
<label>Участники (email через запятую)</label>
<textarea rows="2" placeholder="i.sokolov@company.ru, m.kuznetsova@company.ru"></textarea>
</div>
<div class="toggle-block">
<div class="toggle-row" style="border-top:none; padding-top:0">
<div class="toggle-copy"><strong>Закрытая по паролю</strong><span>Вход только по паролю, включая гостей</span></div>
<label class="switch"><input type="checkbox" id="pw-toggle"><span class="slider"></span></label>
</div>
<div class="password-block field">
<label><svg class="icon" style="width:14px;height:14px"><use href="#i-lock"/></svg>Пароль конференции</label>
<input type="password" placeholder="Минимум 6 символов">
</div>
</div>
<div class="pin-block">
<div class="toggle-row">
<div class="toggle-copy"><strong>Закрепить постоянную конференцию</strong><span>Сохранится и попадёт в «Мои конференции», не исчезнет после встречи</span></div>
<label class="switch"><input type="checkbox" id="pin-toggle"><span class="slider"></span></label>
</div>
<!-- Раскрывается по клику на тумблер выше (CSS :has(), без JS) -->
<div class="recurrence-panel">
<div class="recurrence-option">
<label class="recurrence-option-row">
<input type="radio" name="recurrence" checked>
По дням недели, еженедельно
</label>
<div class="recurrence-sub">
<div class="weekday-toggles">
<span class="weekday-chip">ПН</span>
<span class="weekday-chip is-selected">ВТ</span>
<span class="weekday-chip">СР</span>
<span class="weekday-chip is-selected">ЧТ</span>
<span class="weekday-chip">ПТ</span>
<span class="weekday-chip">СБ</span>
<span class="weekday-chip">ВС</span>
</div>
<p class="recurrence-hint">Ближайшие: вторник 21 июля, четверг 23 июля</p>
</div>
</div>
<div class="recurrence-option">
<label class="recurrence-option-row">
<input type="radio" name="recurrence">
Раз в 2 недели
</label>
<div class="recurrence-sub">
<div class="field-row">
<div class="field" style="margin-bottom:0"><label>Начиная с</label><input type="text" value="21.07.2026"></div>
<div class="field" style="margin-bottom:0"><label>День недели</label><select><option>Вторник</option><option>Среда</option></select></div>
</div>
<p class="recurrence-hint">Следующие: 4 августа, 18 августа</p>
</div>
</div>
<div class="recurrence-option">
<label class="recurrence-option-row">
<input type="radio" name="recurrence">
Раз в месяц
</label>
<div class="recurrence-sub">
<div class="field"><label>Число месяца</label><input type="text" value="31" inputmode="numeric"></div>
<p class="recurrence-hint">Если в месяце нет такого числа — переносится на последний день месяца (напр. 28/29 февраля)</p>
</div>
</div>
<div class="recurrence-option">
<label class="recurrence-option-row">
<input type="radio" name="recurrence">
Каждые N дней
</label>
<div class="recurrence-sub">
<div class="field-row">
<div class="field" style="margin-bottom:0"><label>N (дней)</label><input type="text" value="10" inputmode="numeric"></div>
<div class="field" style="margin-bottom:0"><label>Начиная с</label><input type="text" value="21.07.2026"></div>
</div>
<p class="recurrence-hint">Ближайшие: через 10, 20, 30 дней</p>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary full" style="margin-top: var(--space-5)">Создать конференцию</button>
</form>
</aside>
</div>
<!-- ===================== ДОП. СОСТОЯНИЯ: РЕДАКТИРОВАНИЕ И ОТМЕНА ===================== -->
<section class="states-section">
<div class="states-head">
<h2>Другие состояния карточки</h2>
<p>Раскрытая карточка с закреплением, редактирование существующей закреплённой конференции и отмена вхождения/всех повторений.</p>
</div>
<div class="states-grid">
<!-- Состояние: редактирование существующей закреплённой конференции -->
<div class="state-card">
<p class="screen-label">Состояние: <b>редактирование закреплённой конференции</b></p>
<div class="next-occurrence">
<svg class="icon" style="width:16px;height:16px"><use href="#i-clock"/></svg>
Следующее вхождение: вторник, 22 июля, 11:00
</div>
<form>
<div class="field"><label>Название</label><input type="text" value="Ревью спринта"></div>
<div class="field-row">
<div class="field"><label>Время</label><input type="text" value="11:0011:45"></div>
<div class="field"><label>Участники</label><input type="text" value="8 человек"></div>
</div>
<div class="pin-block">
<div class="toggle-row" style="border-top:none; padding-top:0">
<div class="toggle-copy"><strong>Закреплена</strong><span>Повторяется по расписанию</span></div>
<label class="switch"><input type="checkbox" id="pin-toggle-edit" checked><span class="slider"></span></label>
</div>
<div class="recurrence-panel">
<div class="recurrence-option">
<label class="recurrence-option-row">
<input type="radio" name="recurrence-edit" checked>
По дням недели, еженедельно
</label>
<div class="recurrence-sub">
<div class="weekday-toggles">
<span class="weekday-chip">ПН</span>
<span class="weekday-chip is-selected">ВТ</span>
<span class="weekday-chip">СР</span>
<span class="weekday-chip is-selected">ЧТ</span>
<span class="weekday-chip">ПТ</span>
<span class="weekday-chip">СБ</span>
<span class="weekday-chip">ВС</span>
</div>
</div>
</div>
<div class="recurrence-option">
<label class="recurrence-option-row"><input type="radio" name="recurrence-edit">Раз в 2 недели</label>
</div>
<div class="recurrence-option">
<label class="recurrence-option-row"><input type="radio" name="recurrence-edit">Раз в месяц</label>
</div>
<div class="recurrence-option">
<label class="recurrence-option-row"><input type="radio" name="recurrence-edit">Каждые N дней</label>
</div>
</div>
</div>
<div class="btn-row">
<button type="submit" class="btn btn-primary">Сохранить изменения</button>
<button type="button" class="btn btn-secondary">Открепить</button>
</div>
<div class="danger-link-row">
<button type="button" class="btn-text">Удалить конференцию навсегда</button>
</div>
</form>
</div>
<!-- Состояние: отмена одной / всех повторяющихся -->
<div class="state-card">
<p class="screen-label">Состояние: <b>отмена вхождения повторяющейся конференции</b></p>
<div class="cancel-alert">
<svg class="icon" style="width:18px;height:18px;flex-shrink:0;margin-top:1px"><use href="#i-alert"/></svg>
<div><strong>«Ревью спринта» — вторник, 22 июля, 11:00</strong>Это повторяющаяся конференция (по вторникам и четвергам). Что отменить?</div>
</div>
<div class="cancel-options">
<button class="btn btn-secondary"><svg class="icon" style="width:16px;height:16px"><use href="#i-x"/></svg>Отменить только это вхождение</button>
<button class="btn btn-danger"><svg class="icon" style="width:16px;height:16px"><use href="#i-x"/></svg>Отменить все повторения</button>
</div>
<div class="danger-link-row">
<button type="button" class="btn-text" style="color: var(--color-ink-500)">Оставить как есть</button>
</div>
</div>
</div>
</section>
</main>
</body>
</html>

View File

@@ -0,0 +1,253 @@
<!doctype html>
<html lang="ru" data-theme="shell-dark">
<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>
/* =====================================================================
* ТЁМНЫЙ ВАРИАНТ ОБОЛОЧКИ — ПРЕДЛОЖЕНИЕ, НЕ УТВЕРЖДЕНО.
* Разметка и контент идентичны design/mockups/join.html 1:1, меняется
* только цветовая схема — палитра переиспользует токены комнаты
* конференции. Обоснование цветов — design/mockups/dark/README.md.
* ===================================================================== */
:root {
color-scheme: dark;
--color-bg: #1E1E1E;
--color-bg-alt: #242424;
--color-surface: #2A2A2A;
--color-surface-glass: rgba(51, 51, 51, .55);
--color-border: #3F3F3F;
--color-border-strong: #585858;
--color-border-pill: #7FDDA8;
--color-ink-900: #E8E8E8;
--color-ink-700: #7FDDA8;
--color-ink-500: #B0B0B0;
--color-ink-400: #5D9A78;
--color-ink-300: #7A7A7A;
--color-accent: #7FDDA8;
--color-accent-hover: #75CA9A;
--color-accent-active: #6CB78C;
--color-accent-text: #10331F;
--color-success: #7FDDA8;
--color-success-bg: rgba(127, 221, 168, .14);
--color-success-dot: #7FDDA8;
--color-danger: #EB93A1;
--color-danger-bg: rgba(235, 147, 161, .14);
--color-danger-dot: #EB93A1;
--color-danger-solid: #B85468;
--color-danger-solid-hover: #B9495F;
--color-warning: #D6A83D;
--color-warning-bg: rgba(214, 168, 61, .16);
--color-warning-dot: #D6A83D;
--color-focus-ring: #D6A83D;
--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 { display: inline-flex; gap: 2px; background: var(--color-bg-alt); padding: 4px; border-radius: var(--radius-full); border: 1px solid var(--color-border); }
.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); }
/*
* Подключение к конференции (фаза R1, пивот) — три состояния одного flow:
* (а) ввод ссылки/номера, (б) «представиться» для гостя (имя обязательно,
* email факультативно — для саммари), (в) пароль закрытой конференции.
*/
html { min-width: 1280px; }
body { min-height: 100vh; display: flex; flex-direction: column; background: 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); text-decoration: none; }
.brand-mark .logo-mark { width: 22px; height: 22px; flex-shrink: 0; }
main { flex: 1; display: flex; flex-direction: column; align-items: center; padding: var(--space-6) var(--space-8) var(--space-16); gap: var(--space-16); }
.screen-block { width: 100%; max-width: 440px; }
.screen-label { font: var(--text-caption); color: var(--color-ink-400); margin: 0 0 var(--space-3); text-align: center; }
.screen-label b { color: var(--color-ink-700); }
.flow-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-2xl); padding: var(--space-8); }
.flow-icon { width: 52px; height: 52px; border-radius: var(--radius-full); background: var(--color-bg-alt); color: var(--color-ink-700); display: flex; align-items: center; justify-content: center; margin: 0 auto var(--space-5); }
.flow-icon .icon { width: 24px; height: 24px; }
.flow-card h1 { font: var(--text-h1); color: var(--color-ink-900); margin: 0 0 var(--space-2); text-align: center; }
.flow-card .flow-sub { font: var(--text-body-lg); color: var(--color-ink-500); margin: 0 0 var(--space-6); text-align: center; }
.field { margin-bottom: var(--space-5); }
.field label { display: flex; align-items: center; gap: 6px; font: var(--text-body); font-weight: 600; color: var(--color-ink-900); margin-bottom: var(--space-2); }
.field-optional { font-weight: 400; color: var(--color-ink-400); }
.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-hint { margin-top: var(--space-2); font: var(--text-body); color: var(--color-ink-500); }
.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); }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; width: 100%; border: none; cursor: pointer; font-family: var(--font-body); transition: background var(--transition-fast), box-shadow var(--transition-fast); }
.btn-primary { 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 { 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); }
:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; }
.link-back { display: block; text-align: center; margin-top: var(--space-5); font: var(--text-body); font-weight: 600; color: var(--color-ink-500); text-decoration: none; }
.link-back:hover { color: var(--color-ink-700); }
/* Плашка целевой конференции (состояния б/в) */
.target-conf { display: flex; align-items: center; gap: 12px; background: var(--color-bg-alt); border-radius: var(--radius-lg); padding: var(--space-4); margin-bottom: var(--space-6); }
.target-conf .icon-wrap { width: 40px; height: 40px; border-radius: 50%; background: var(--color-surface); border: 1px solid var(--color-border); color: var(--color-ink-700); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.target-conf strong { display: block; font: var(--text-body); color: var(--color-ink-900); }
.target-conf span { font: var(--text-mono-sm); color: var(--color-ink-500); }
.divider { border: none; border-top: 1px solid var(--color-border); width: 100%; max-width: 720px; margin: 0; }
</style>
</head>
<body>
<svg style="display:none">
<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-hash" viewBox="0 0 24 24"><line x1="4" y1="9" x2="20" y2="9"/><line x1="4" y1="15" x2="20" y2="15"/><line x1="10" y1="3" x2="8" y2="21"/><line x1="16" y1="3" x2="14" y2="21"/></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-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-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-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-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-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>
<header class="topbar">
<a class="brand-mark" href="lobby.html"><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</a>
<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>
</header>
<main>
<!-- ===================== (а) ВВОД ССЫЛКИ ИЛИ НОМЕРА ===================== -->
<section class="screen-block">
<p class="screen-label">Экран: <b>ввод ссылки или номера</b></p>
<div class="flow-card">
<div class="flow-icon" aria-hidden="true"><svg class="icon"><use href="#i-link2"/></svg></div>
<h1>Подключиться к конференции</h1>
<p class="flow-sub">Вставьте ссылку-приглашение или введите номер конференции</p>
<form>
<div class="field">
<label for="join-input">Ссылка или номер</label>
<div class="input-wrap">
<svg class="icon"><use href="#i-hash"/></svg>
<input id="join-input" type="text" placeholder="vidconf.ru/j/... или 8842 104" value="8842 104">
</div>
<p class="field-hint">Найдите номер или ссылку в приглашении организатора</p>
</div>
<button type="submit" class="btn btn-primary">Продолжить <svg class="icon"><use href="#i-arrow-right"/></svg></button>
</form>
<a href="lobby.html" class="link-back">Назад в лобби</a>
</div>
</section>
<hr class="divider">
<!-- ===================== (б) ПРЕДСТАВИТЬСЯ (ГОСТЬ) ===================== -->
<section class="screen-block">
<p class="screen-label">Экран: <b>представиться (гость)</b></p>
<div class="flow-card">
<div class="target-conf">
<span class="icon-wrap" aria-hidden="true"><svg class="icon" style="width:18px;height:18px"><use href="#i-video"/></svg></span>
<div><strong>Синк по релизу 2.4</strong><span>№ 8842 104</span></div>
</div>
<div class="flow-icon" aria-hidden="true"><svg class="icon"><use href="#i-user"/></svg></div>
<h1>Как вас зовут?</h1>
<p class="flow-sub">У вас нет аккаунта VidConf — представьтесь, чтобы войти как гость</p>
<form>
<div class="field">
<label for="guest-name">Имя<span class="field-optional">· обязательно</span></label>
<div class="input-wrap">
<svg class="icon"><use href="#i-user"/></svg>
<input id="guest-name" type="text" placeholder="Как к вам обращаться" value="Пётр Волков">
</div>
</div>
<div class="field">
<label for="guest-email">Email<span class="field-optional">· необязательно</span></label>
<div class="input-wrap">
<svg class="icon"><use href="#i-mail"/></svg>
<input id="guest-email" type="email" placeholder="you@mail.ru">
</div>
<p class="field-hint">Укажите, если хотите получить саммари конференции на почту</p>
</div>
<button type="submit" class="btn btn-primary">Войти в конференцию <svg class="icon"><use href="#i-arrow-right"/></svg></button>
</form>
<a href="join.html" class="link-back">Назад</a>
</div>
</section>
<hr class="divider">
<!-- ===================== (в) ПАРОЛЬ ЗАКРЫТОЙ КОНФЕРЕНЦИИ ===================== -->
<section class="screen-block">
<p class="screen-label">Экран: <b>пароль закрытой конференции</b> (с состоянием ошибки)</p>
<div class="flow-card">
<div class="target-conf">
<span class="icon-wrap" aria-hidden="true"><svg class="icon" style="width:18px;height:18px"><use href="#i-lock"/></svg></span>
<div><strong>Итоги квартала</strong><span>№ 4471 208 · закрытая</span></div>
</div>
<div class="flow-icon" aria-hidden="true"><svg class="icon"><use href="#i-lock"/></svg></div>
<h1>Введите пароль</h1>
<p class="flow-sub">Эта конференция закрыта — доступ только по паролю от организатора</p>
<form>
<div class="field has-error">
<label for="join-pass">Пароль конференции</label>
<div class="input-wrap">
<svg class="icon"><use href="#i-lock"/></svg>
<input id="join-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> Неверный пароль, попробуйте ещё раз</p>
</div>
<button type="submit" class="btn btn-primary">Войти <svg class="icon"><use href="#i-arrow-right"/></svg></button>
</form>
<a href="join.html" class="link-back">Назад</a>
</div>
</section>
</main>
</body>
</html>

View File

@@ -0,0 +1,403 @@
<!doctype html>
<html lang="ru" data-theme="shell-dark">
<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>
/* =====================================================================
* ТЁМНЫЙ ВАРИАНТ ОБОЛОЧКИ — ПРЕДЛОЖЕНИЕ, НЕ УТВЕРЖДЕНО.
* Разметка и контент идентичны design/mockups/lobby.html 1:1, меняется
* только цветовая схема — палитра переиспользует токены комнаты
* конференции. Обоснование цветов — design/mockups/dark/README.md.
* ===================================================================== */
:root {
color-scheme: dark;
--color-bg: #1E1E1E;
--color-bg-alt: #242424;
--color-surface: #2A2A2A;
--color-surface-glass: rgba(51, 51, 51, .55);
--color-border: #3F3F3F;
--color-border-strong: #585858;
--color-border-pill: #7FDDA8;
--color-ink-900: #E8E8E8;
--color-ink-700: #7FDDA8;
--color-ink-500: #B0B0B0;
--color-ink-400: #5D9A78;
--color-ink-300: #7A7A7A;
--color-accent: #7FDDA8;
--color-accent-hover: #75CA9A;
--color-accent-active: #6CB78C;
--color-accent-text: #10331F;
--color-success: #7FDDA8;
--color-success-bg: rgba(127, 221, 168, .14);
--color-success-dot: #7FDDA8;
--color-danger: #EB93A1;
--color-danger-bg: rgba(235, 147, 161, .14);
--color-danger-dot: #EB93A1;
--color-danger-solid: #B85468;
--color-danger-solid-hover: #B9495F;
--color-warning: #D6A83D;
--color-warning-bg: rgba(214, 168, 61, .16);
--color-warning-dot: #D6A83D;
--color-focus-ring: #D6A83D;
--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 { display: inline-flex; gap: 2px; background: var(--color-bg-alt); padding: 4px; border-radius: var(--radius-full); border: 1px solid var(--color-border); }
.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 { height: 100%; min-width: 1280px; }
body {
min-height: 100vh;
display: flex;
flex-direction: column;
background:
radial-gradient(1100px 520px at 10% -10%, rgba(214, 168, 61, 0.05), transparent 60%),
radial-gradient(900px 480px at 105% 110%, rgba(127, 221, 168, 0.14), 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: var(--color-accent-text);
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%; }
.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%);
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 340px);
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;
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; }
.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); }
.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 в центре мозаики ---------- */
.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>
<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>
<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">
<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>
<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="login.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>

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>

View File

@@ -0,0 +1,403 @@
<!doctype html>
<html lang="ru" data-theme="shell-dark">
<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>
/* =====================================================================
* ТЁМНЫЙ ВАРИАНТ ОБОЛОЧКИ — ПРЕДЛОЖЕНИЕ, НЕ УТВЕРЖДЕНО.
* Разметка и контент идентичны design/mockups/my-conferences.html 1:1,
* меняется только цветовая схема — палитра переиспользует токены
* комнаты конференции. Обоснование цветов — design/mockups/dark/README.md.
* ===================================================================== */
:root {
color-scheme: dark;
--color-bg: #1E1E1E;
--color-bg-alt: #242424;
--color-surface: #2A2A2A;
--color-surface-glass: rgba(51, 51, 51, .55);
--color-border: #3F3F3F;
--color-border-strong: #585858;
--color-border-pill: #7FDDA8;
--color-ink-900: #E8E8E8;
--color-ink-700: #7FDDA8;
--color-ink-500: #B0B0B0;
--color-ink-400: #5D9A78;
--color-ink-300: #7A7A7A;
--color-accent: #7FDDA8;
--color-accent-hover: #75CA9A;
--color-accent-active: #6CB78C;
--color-accent-text: #10331F;
--color-success: #7FDDA8;
--color-success-bg: rgba(127, 221, 168, .14);
--color-success-dot: #7FDDA8;
--color-danger: #EB93A1;
--color-danger-bg: rgba(235, 147, 161, .14);
--color-danger-dot: #EB93A1;
--color-danger-solid: #B85468;
--color-danger-solid-hover: #B9495F;
--color-warning: #D6A83D;
--color-warning-bg: rgba(214, 168, 61, .16);
--color-warning-dot: #D6A83D;
--color-focus-ring: #D6A83D;
--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 { display: inline-flex; gap: 2px; background: var(--color-bg-alt); padding: 4px; border-radius: var(--radius-full); border: 1px solid var(--color-border); }
.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; }
.topbar {
position: sticky; top: 0; z-index: 20;
display: flex; align-items: center; justify-content: space-between;
padding: var(--space-4) var(--space-8);
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
}
.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-5); }
.nav-link { font: var(--text-body); font-weight: 600; color: var(--color-ink-500); text-decoration: none; }
.nav-link.is-active { color: var(--color-ink-700); }
.avatar-btn { display: flex; align-items: center; gap: 8px; background: var(--color-bg-alt); border: 1px solid var(--color-border); border-radius: var(--radius-full); padding: 6px 14px 6px 6px; }
.avatar { width: 28px; height: 28px; border-radius: 50%; background: var(--color-ink-700); color: var(--color-accent-text); display:flex; align-items:center; justify-content:center; font: var(--text-caption); text-transform: none; letter-spacing: 0; }
.avatar-btn .user-name { font: var(--text-body); font-weight: 600; color: var(--color-ink-900); }
main { padding: var(--space-8); max-width: 1280px; margin: 0 auto; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: var(--space-6); }
.page-head h1 { font: var(--text-display-lg); font-family: var(--font-display); color: var(--color-ink-700); margin: 0 0 6px; }
.page-head p { font: var(--text-body-lg); color: var(--color-ink-500); margin: 0; }
.btn { display: inline-flex; align-items: center; gap: 8px; border: none; cursor: pointer; font-family: var(--font-body); white-space: nowrap; transition: background var(--transition-fast), box-shadow var(--transition-fast); }
.btn-primary { background: var(--color-accent); color: var(--color-accent-text); font: var(--text-body); font-weight: 700; padding: 11px 22px; 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-secondary { background: transparent; color: var(--color-ink-700); border: 1.5px solid var(--color-border-pill); font: var(--text-body); font-weight: 600; padding: 9px 18px; border-radius: var(--radius-full); }
.btn-secondary:hover { background: var(--color-bg-alt); }
:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; }
.filters { display: flex; gap: 8px; margin-bottom: var(--space-6); }
.chip { display: inline-flex; align-items: center; gap: 6px; padding: 9px 16px; border-radius: var(--radius-full); border: 1.5px solid var(--color-border); background: var(--color-surface); font: var(--text-body); font-weight: 600; color: var(--color-ink-500); }
.chip.is-active { border-color: var(--color-ink-700); color: var(--color-ink-700); background: var(--color-bg-alt); }
.conf-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); }
.conf-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-2xl); padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); transition: box-shadow var(--transition-base), border-color var(--transition-base); }
.conf-card:hover { box-shadow: var(--shadow-md); border-color: var(--color-border-strong); }
.conf-card.is-ended { opacity: .85; }
.conf-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
.conf-title-wrap h3 { margin: 0 0 4px; font: var(--text-h3); color: var(--color-ink-900); display: flex; align-items: center; gap: 6px; }
.conf-title-wrap p { margin: 0; font: var(--text-body); color: var(--color-ink-500); }
.conf-badge { display: inline-flex; align-items: center; gap: 5px; padding: 4px 12px; border-radius: var(--radius-full); font: var(--text-caption); white-space: nowrap; }
.conf-badge.pinned { background: var(--color-bg-alt); color: var(--color-ink-700); border: 1px solid var(--color-border-strong); }
.conf-badge.scheduled { background: var(--color-warning-bg); color: var(--color-warning); }
.conf-badge.ended { background: var(--color-bg-alt); color: var(--color-ink-500); }
.conf-meta-row { display: flex; align-items: center; gap: var(--space-4); font: var(--text-body); color: var(--color-ink-500); }
.conf-meta-row .meta-item { display: flex; align-items: center; gap: 6px; }
.pills-row { display: flex; gap: var(--space-3); }
.pill-group { flex: 1; min-width: 0; }
.pill-label { display: block; font: var(--text-caption); color: var(--color-ink-400); margin-bottom: 4px; }
.code-pill { display: flex; align-items: center; gap: 6px; background: var(--color-bg-alt); border: 1px solid var(--color-border); border-radius: var(--radius-full); padding: 6px 6px 6px 14px; }
.pill-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: var(--text-mono-sm); color: var(--color-ink-700); }
.copy-btn { position: relative; width: 28px; height: 28px; flex-shrink: 0; border-radius: 50%; border: none; background: var(--color-surface); color: var(--color-ink-500); display: flex; align-items: center; justify-content: center; transition: color var(--transition-fast), box-shadow var(--transition-fast); }
.copy-btn:hover { color: var(--color-ink-700); box-shadow: var(--shadow-sm); }
.copy-btn .icon-check { display: none; color: var(--color-success); }
.copy-btn.is-copied .icon-copy { display: none; }
.copy-btn.is-copied .icon-check { display: block; }
.copy-toast {
position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%);
background: #333333; color: var(--color-ink-900); font: var(--text-caption); text-transform: none; letter-spacing: normal;
padding: 4px 10px; border-radius: var(--radius-sm); white-space: nowrap;
opacity: 0; pointer-events: none; transition: opacity var(--transition-fast);
}
.copy-btn.is-copied .copy-toast { opacity: 1; }
.conf-card-footer { display: flex; gap: var(--space-3); margin-top: auto; }
.conf-card-footer .btn { flex: 1; justify-content: center; }
.icon-btn { width: 40px; height: 40px; flex-shrink: 0; border-radius: 50%; border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-ink-500); display: flex; align-items: center; justify-content: center; }
.icon-btn:hover { border-color: var(--color-border-strong); color: var(--color-ink-900); }
</style>
</head>
<body>
<svg style="display:none">
<symbol id="i-repeat" viewBox="0 0 24 24"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/></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-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-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-users" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<symbol id="i-copy" viewBox="0 0 24 24"><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"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></symbol>
<symbol id="i-play" viewBox="0 0 24 24"><polygon points="6 3 20 12 6 21 6 3"/></symbol>
<symbol id="i-pencil" viewBox="0 0 24 24"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></symbol>
<symbol id="i-file-text" viewBox="0 0 24 24"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="16" y2="17"/></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>
<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>
<nav class="topbar-right">
<a href="lobby.html" class="nav-link">Лобби</a>
<a href="calendar.html" class="nav-link">Календарь</a>
<a href="my-conferences.html" class="nav-link is-active">Мои конференции</a>
<a href="admin.html" class="nav-link">Админка</a>
<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>
<button class="avatar-btn"><span class="avatar">АИ</span><span class="user-name">Анна Иванова</span></button>
</nav>
</header>
<main>
<div class="page-head">
<div>
<h1>Мои конференции</h1>
<p>5 конференций · 3 закреплённые с повторением</p>
</div>
<a href="calendar.html" class="btn btn-primary">Запланировать новую</a>
</div>
<div class="filters">
<button class="chip is-active">Все</button>
<button class="chip"><svg class="icon" style="width:14px;height:14px"><use href="#i-repeat"/></svg>Закреплённые</button>
<button class="chip"><svg class="icon" style="width:14px;height:14px"><use href="#i-clock"/></svg>Предстоящие</button>
<button class="chip">Завершённые</button>
</div>
<section class="conf-grid">
<!-- Закреплена, еженедельно -->
<article class="conf-card">
<div class="conf-card-head">
<div class="conf-title-wrap">
<h3>Ревью спринта</h3>
<p>По вторникам и четвергам, 11:0011:45</p>
</div>
<span class="conf-badge pinned"><svg class="icon" style="width:12px;height:12px"><use href="#i-repeat"/></svg>Закреплена</span>
</div>
<div class="conf-meta-row">
<span class="meta-item"><svg class="icon" style="width:15px;height:15px"><use href="#i-clock"/></svg>Ближайшее: вт, 22 июля, 11:00</span>
<span class="meta-item"><svg class="icon" style="width:15px;height:15px"><use href="#i-users"/></svg>8 участников</span>
</div>
<div class="pills-row">
<div class="pill-group">
<span class="pill-label">Ссылка</span>
<div class="code-pill">
<span class="pill-value">vidconf.ru/j/sprint-review</span>
<button class="copy-btn" data-copy="https://vidconf.ru/j/sprint-review" aria-label="Скопировать ссылку">
<svg class="icon icon-copy" style="width:14px;height:14px"><use href="#i-copy"/></svg>
<svg class="icon icon-check" style="width:14px;height:14px"><use href="#i-check"/></svg>
<span class="copy-toast" aria-hidden="true">Скопировано</span>
</button>
</div>
</div>
<div class="pill-group">
<span class="pill-label">Номер</span>
<div class="code-pill">
<span class="pill-value">8842 104</span>
<button class="copy-btn" data-copy="8842 104" aria-label="Скопировать номер">
<svg class="icon icon-copy" style="width:14px;height:14px"><use href="#i-copy"/></svg>
<svg class="icon icon-check" style="width:14px;height:14px"><use href="#i-check"/></svg>
<span class="copy-toast" aria-hidden="true">Скопировано</span>
</button>
</div>
</div>
</div>
<div class="conf-card-footer">
<a href="../room.html" class="btn btn-primary"><svg class="icon" style="width:16px;height:16px"><use href="#i-play"/></svg>Войти</a>
<button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:17px;height:17px"><use href="#i-pencil"/></svg></button>
</div>
</article>
<!-- Закреплена, ежемесячно, закрытая паролем -->
<article class="conf-card">
<div class="conf-card-head">
<div class="conf-title-wrap">
<h3><svg class="icon" style="width:15px;height:15px;flex-shrink:0"><use href="#i-lock"/></svg>Синк по релизу 2.4</h3>
<p>Раз в месяц, 15-го числа, 14:0015:00</p>
</div>
<span class="conf-badge pinned"><svg class="icon" style="width:12px;height:12px"><use href="#i-repeat"/></svg>Закреплена</span>
</div>
<div class="conf-meta-row">
<span class="meta-item"><svg class="icon" style="width:15px;height:15px"><use href="#i-clock"/></svg>Ближайшее: 15 августа, 14:00</span>
<span class="meta-item"><svg class="icon" style="width:15px;height:15px"><use href="#i-users"/></svg>4 участника</span>
</div>
<div class="pills-row">
<div class="pill-group">
<span class="pill-label">Ссылка</span>
<div class="code-pill">
<span class="pill-value">vidconf.ru/j/release-sync</span>
<button class="copy-btn" data-copy="https://vidconf.ru/j/release-sync" aria-label="Скопировать ссылку">
<svg class="icon icon-copy" style="width:14px;height:14px"><use href="#i-copy"/></svg>
<svg class="icon icon-check" style="width:14px;height:14px"><use href="#i-check"/></svg>
<span class="copy-toast" aria-hidden="true">Скопировано</span>
</button>
</div>
</div>
<div class="pill-group">
<span class="pill-label">Номер</span>
<div class="code-pill">
<span class="pill-value">5217 933</span>
<button class="copy-btn" data-copy="5217 933" aria-label="Скопировать номер">
<svg class="icon icon-copy" style="width:14px;height:14px"><use href="#i-copy"/></svg>
<svg class="icon icon-check" style="width:14px;height:14px"><use href="#i-check"/></svg>
<span class="copy-toast" aria-hidden="true">Скопировано</span>
</button>
</div>
</div>
</div>
<div class="conf-card-footer">
<a href="join.html" class="btn btn-primary"><svg class="icon" style="width:16px;height:16px"><use href="#i-lock"/></svg>Войти по паролю</a>
<button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:17px;height:17px"><use href="#i-pencil"/></svg></button>
</div>
</article>
<!-- Предстоящая, разовая -->
<article class="conf-card">
<div class="conf-card-head">
<div class="conf-title-wrap">
<h3>1:1 с руководителем</h3>
<p>Разовая · 21 июля, 15:0015:30</p>
</div>
<span class="conf-badge scheduled"><svg class="icon" style="width:12px;height:12px"><use href="#i-clock"/></svg>Предстоящая</span>
</div>
<div class="conf-meta-row">
<span class="meta-item"><svg class="icon" style="width:15px;height:15px"><use href="#i-users"/></svg>2 участника</span>
</div>
<div class="pills-row">
<div class="pill-group">
<span class="pill-label">Ссылка</span>
<div class="code-pill">
<span class="pill-value">vidconf.ru/j/one-on-one</span>
<button class="copy-btn" data-copy="https://vidconf.ru/j/one-on-one" aria-label="Скопировать ссылку">
<svg class="icon icon-copy" style="width:14px;height:14px"><use href="#i-copy"/></svg>
<svg class="icon icon-check" style="width:14px;height:14px"><use href="#i-check"/></svg>
<span class="copy-toast" aria-hidden="true">Скопировано</span>
</button>
</div>
</div>
<div class="pill-group">
<span class="pill-label">Номер</span>
<div class="code-pill">
<span class="pill-value">2094 471</span>
<button class="copy-btn" data-copy="2094 471" aria-label="Скопировать номер">
<svg class="icon icon-copy" style="width:14px;height:14px"><use href="#i-copy"/></svg>
<svg class="icon icon-check" style="width:14px;height:14px"><use href="#i-check"/></svg>
<span class="copy-toast" aria-hidden="true">Скопировано</span>
</button>
</div>
</div>
</div>
<div class="conf-card-footer">
<button class="btn btn-secondary" style="flex:1" disabled><svg class="icon" style="width:16px;height:16px"><use href="#i-clock"/></svg>Начнётся через 3 дня</button>
<button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:17px;height:17px"><use href="#i-pencil"/></svg></button>
</div>
</article>
<!-- Завершена (незакреплённая — повторный вход недоступен, только саммари) -->
<article class="conf-card is-ended">
<div class="conf-card-head">
<div class="conf-title-wrap">
<h3>Итоги квартала</h3>
<p>Разовая · 29 июня, 12:0013:00</p>
</div>
<span class="conf-badge ended"><svg class="icon" style="width:12px;height:12px"><use href="#i-check-circle"/></svg>Завершена</span>
</div>
<div class="conf-meta-row">
<span class="meta-item"><svg class="icon" style="width:15px;height:15px"><use href="#i-users"/></svg>14 участников</span>
</div>
<div class="conf-card-footer">
<a href="../email-summary.html" class="btn btn-secondary" style="flex:1"><svg class="icon" style="width:16px;height:16px"><use href="#i-file-text"/></svg>Открыть саммари</a>
</div>
</article>
<!-- Закреплена, каждые N дней -->
<article class="conf-card">
<div class="conf-card-head">
<div class="conf-title-wrap">
<h3>Ежеквартальный отчёт</h3>
<p>Каждые 90 дней, 10:0011:00</p>
</div>
<span class="conf-badge pinned"><svg class="icon" style="width:12px;height:12px"><use href="#i-repeat"/></svg>Закреплена</span>
</div>
<div class="conf-meta-row">
<span class="meta-item"><svg class="icon" style="width:15px;height:15px"><use href="#i-clock"/></svg>Ближайшее: 3 октября, 10:00</span>
<span class="meta-item"><svg class="icon" style="width:15px;height:15px"><use href="#i-users"/></svg>22 участника</span>
</div>
<div class="pills-row">
<div class="pill-group">
<span class="pill-label">Ссылка</span>
<div class="code-pill">
<span class="pill-value">vidconf.ru/j/q-report</span>
<button class="copy-btn" data-copy="https://vidconf.ru/j/q-report" aria-label="Скопировать ссылку">
<svg class="icon icon-copy" style="width:14px;height:14px"><use href="#i-copy"/></svg>
<svg class="icon icon-check" style="width:14px;height:14px"><use href="#i-check"/></svg>
<span class="copy-toast" aria-hidden="true">Скопировано</span>
</button>
</div>
</div>
<div class="pill-group">
<span class="pill-label">Номер</span>
<div class="code-pill">
<span class="pill-value">6630 812</span>
<button class="copy-btn" data-copy="6630 812" aria-label="Скопировать номер">
<svg class="icon icon-copy" style="width:14px;height:14px"><use href="#i-copy"/></svg>
<svg class="icon icon-check" style="width:14px;height:14px"><use href="#i-check"/></svg>
<span class="copy-toast" aria-hidden="true">Скопировано</span>
</button>
</div>
</div>
</div>
<div class="conf-card-footer">
<button class="btn btn-secondary" style="flex:1" disabled><svg class="icon" style="width:16px;height:16px"><use href="#i-clock"/></svg>Начнётся 3 октября</button>
<button class="icon-btn" aria-label="Редактировать"><svg class="icon" style="width:17px;height:17px"><use href="#i-pencil"/></svg></button>
</div>
</article>
</section>
</main>
<script>
// Копирование ссылки/номера в буфер обмена + временная подсказка «Скопировано».
document.querySelectorAll('.copy-btn').forEach((btn) => {
const originalLabel = btn.getAttribute('aria-label');
btn.addEventListener('click', () => {
const text = btn.dataset.copy || '';
if (navigator.clipboard) { navigator.clipboard.writeText(text).catch(() => {}); }
btn.classList.add('is-copied');
btn.setAttribute('aria-label', 'Скопировано');
setTimeout(() => {
btn.classList.remove('is-copied');
btn.setAttribute('aria-label', originalLabel);
}, 1500);
});
});
</script>
</body>
</html>