157893b516
Layout review found archive-main relied on the default flex-direction while the base `main` rule forces column, so the sidebar/content split was fragile — now set explicitly to row. The sidebar app-version pill is shrunk and centred so it fits the 72px icon rail instead of being clipped. HTML tag balance, CSS brace balance and all table column counts (cameras 9, storages 9, users 4) checked out clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
584 lines
32 KiB
CSS
584 lines
32 KiB
CSS
/* NVR UI — чистый тёмный интерфейс. Прототип компоновки iVMS-4200,
|
|
но без перегруза: только то, что нужно для live и архива. */
|
|
|
|
:root {
|
|
--bg: #0f1115;
|
|
--panel: #161a22;
|
|
--panel-2: #1d2230;
|
|
--border: #272d3a;
|
|
--text: #e6e9ef;
|
|
--text-dim: #8b93a7;
|
|
--accent: #3f0303; /* акцент: тёмно-красный */
|
|
--accent-2: #2a0202; /* затемнённый вариант */
|
|
--ok: #22c55e;
|
|
--warn: #f59e0b;
|
|
--err: #ef4444;
|
|
--header-h: 50px;
|
|
--footer-h: 22px;
|
|
--sidebar-w: 72px;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
html, body {
|
|
margin: 0; height: 100%;
|
|
background: var(--bg); color: var(--text);
|
|
font: 14px/1.4 -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ── Шапка (50px, бренд по центру) ── */
|
|
header.app-header {
|
|
height: var(--header-h); flex: none; display: flex; align-items: center; justify-content: center;
|
|
padding: 0 14px; background: var(--panel); border-bottom: 1px solid var(--border);
|
|
}
|
|
header.app-header .brand { font-size: 18px; }
|
|
.brand { font-weight: 700; letter-spacing: .5px; color: #fff; }
|
|
.brand span { color: #c0392b; }
|
|
nav { display: flex; gap: 4px; }
|
|
nav a {
|
|
color: var(--text-dim); text-decoration: none; padding: 7px 14px;
|
|
border-radius: 7px; font-weight: 500;
|
|
}
|
|
nav a:hover { background: var(--panel-2); color: var(--text); }
|
|
nav a.active { background: var(--accent); color: #fff; }
|
|
.spacer { flex: 1; }
|
|
.stats { display: flex; gap: 16px; color: var(--text-dim); font-size: 12px; }
|
|
.stats b { color: var(--text); font-weight: 600; }
|
|
.clock { font-variant-numeric: tabular-nums; color: var(--text); }
|
|
|
|
/* ── Раскладка (между шапкой и футером) ── */
|
|
.layout { display: flex; height: calc(100% - var(--header-h) - var(--footer-h)); }
|
|
|
|
/* ── Футер (бренд + CPU/RAM/диск + часы + версия) ── */
|
|
footer.app-footer {
|
|
height: var(--footer-h); display: flex; align-items: center; gap: 14px;
|
|
padding: 0 14px; background: var(--panel); border-top: 1px solid var(--border);
|
|
font-size: 11px; color: var(--text-dim);
|
|
}
|
|
footer.app-footer .brand { font-size: 12px; }
|
|
footer.app-footer .stats { display: flex; gap: 14px; font-size: 11px; }
|
|
footer.app-footer .clock { font-size: 11px; color: var(--text); }
|
|
footer.app-footer #footer-version { font-variant-numeric: tabular-nums; }
|
|
aside {
|
|
width: var(--sidebar-w); background: var(--panel); border-right: 1px solid var(--border);
|
|
display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px 0; overflow: hidden;
|
|
}
|
|
.aside-title {
|
|
padding: 12px 14px 8px; font-size: 11px; text-transform: uppercase;
|
|
letter-spacing: .8px; color: var(--text-dim);
|
|
}
|
|
/* ── Навигация в сайдбаре ── */
|
|
.nav-item {
|
|
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
|
|
width: 64px; height: 64px; padding: 0; border-radius: 12px;
|
|
color: var(--text-dim); text-decoration: none; font-weight: 500; font-size: 9px; line-height: 1.15;
|
|
border: 0; background: none; cursor: pointer; text-align: center; font-family: inherit;
|
|
}
|
|
.nav-item:hover { background: var(--panel-2); color: var(--text); }
|
|
.nav-item.active { background: var(--panel-2); color: #fff; box-shadow: inset 0 0 0 1px #c0392b; }
|
|
.nav-item .ico { font-size: 26px; line-height: 1; width: auto; text-align: center; }
|
|
.aside-spacer { flex: 1; }
|
|
|
|
/* Иконка настройки раскладки рядом с Live view */
|
|
.navline { display: flex; align-items: center; }
|
|
.nav-gear { padding: 12px 14px; color: var(--text-dim); text-decoration: none; font-size: 16px; }
|
|
.nav-gear:hover { color: #fff; background: var(--panel-2); }
|
|
.nav-gear.active { color: #fff; }
|
|
|
|
/* Подменю раскладок под "Live view" */
|
|
.subnav { display: flex; flex-direction: column; padding: 2px 0 6px; }
|
|
.subitem {
|
|
padding: 6px 16px 6px 50px; color: var(--text-dim); text-decoration: none;
|
|
font-size: 13px; border-left: 3px solid transparent;
|
|
}
|
|
.subitem:hover { background: var(--panel-2); color: var(--text); }
|
|
.subitem.active { color: #fff; border-left-color: #c0392b; background: var(--panel-2); }
|
|
.subitem.subfs { padding-left: 16px; margin-top: 4px; border-top: 1px solid var(--border); padding-top: 8px; }
|
|
.subitem.subplay { padding-left: 16px; font-weight: 600; color: var(--text); margin-bottom: 4px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
|
|
.app-version { padding: 6px 2px; font-size: 10px; color: var(--text-dim); text-align: center; width: 100%; }
|
|
.nav-item.logout { color: var(--text-dim); border-top: 1px solid var(--border); }
|
|
.nav-item.logout:hover { color: #e57373; }
|
|
|
|
/* ── Чипы камер (Live toolbar) ── */
|
|
.cam-chips { display: flex; gap: 6px; flex-wrap: wrap; }
|
|
.chip {
|
|
display: inline-flex; align-items: center; gap: 7px; padding: 5px 11px;
|
|
border: 1px solid var(--border); border-radius: 15px; background: var(--panel-2);
|
|
cursor: pointer; font-size: 13px; color: var(--text);
|
|
}
|
|
.chip:hover { border-color: #c0392b; }
|
|
.chip.active { background: var(--accent); border-color: #c0392b; color: #fff; }
|
|
|
|
#cam-list { flex: 1; overflow-y: auto; } /* legacy, на случай переиспользования */
|
|
main { flex: 1; min-height: 0; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
|
|
/* ── Список камер ── */
|
|
.cam-item {
|
|
display: flex; align-items: center; gap: 10px; padding: 9px 14px;
|
|
cursor: pointer; border-left: 3px solid transparent;
|
|
}
|
|
.cam-item:hover { background: var(--panel-2); }
|
|
.cam-item.active { background: var(--panel-2); border-left-color: var(--accent); }
|
|
.cam-item .name { flex: 1; font-weight: 500; }
|
|
.cam-item .ip { font-size: 11px; color: var(--text-dim); }
|
|
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--text-dim); flex: none; }
|
|
.dot.online { background: var(--ok); box-shadow: 0 0 6px var(--ok); }
|
|
.dot.retrying { background: var(--warn); animation: pulse 1.2s infinite; }
|
|
.dot.offline, .dot.starting { background: var(--text-dim); }
|
|
@keyframes pulse { 50% { opacity: .35; } }
|
|
|
|
/* ── Тулбар ── */
|
|
.toolbar {
|
|
display: flex; align-items: center; gap: 10px; padding: 8px 14px;
|
|
background: var(--panel); border-bottom: 1px solid var(--border);
|
|
}
|
|
.toolbar .group { display: flex; gap: 4px; }
|
|
button, .btn {
|
|
background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
|
|
padding: 6px 11px; border-radius: 6px; cursor: pointer; font-size: 13px;
|
|
}
|
|
button:hover, .btn:hover { border-color: var(--accent); }
|
|
button.active { background: var(--accent); border-color: var(--accent); color: #fff; }
|
|
.toggle { display: flex; align-items: center; gap: 10px; color: var(--text-dim); cursor: pointer; }
|
|
|
|
/* Переключатели в виде тумблеров (off — серый, on — зелёный) */
|
|
input[type="checkbox"] {
|
|
appearance: none; -webkit-appearance: none; margin: 0;
|
|
width: 40px; height: 22px; flex: none; border-radius: 11px;
|
|
background: var(--panel-2); border: 1px solid var(--border);
|
|
position: relative; cursor: pointer; transition: background .15s, border-color .15s;
|
|
}
|
|
input[type="checkbox"]::before {
|
|
content: ""; position: absolute; top: 2px; left: 2px;
|
|
width: 16px; height: 16px; border-radius: 50%;
|
|
background: var(--text-dim); transition: transform .15s, background .15s;
|
|
}
|
|
input[type="checkbox"]:checked {
|
|
background: rgba(34, 197, 94, .25); border-color: var(--ok);
|
|
}
|
|
input[type="checkbox"]:checked::before { transform: translateX(18px); background: var(--ok); }
|
|
input[type="checkbox"]:focus-visible { outline: 2px solid var(--ok); outline-offset: 2px; }
|
|
input[type="date"], input[type="number"], select {
|
|
background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
|
|
padding: 6px 8px; border-radius: 6px;
|
|
}
|
|
|
|
/* ── Сетка видео ── */
|
|
.grid {
|
|
flex: 1; min-height: 0; min-width: 0; display: grid; gap: 4px; padding: 4px;
|
|
background: #000; overflow: hidden;
|
|
grid-auto-rows: 1fr; grid-auto-columns: 1fr; /* все дорожки строго равны */
|
|
}
|
|
/* «Соло»: одна камера на весь экран (двойной клик по ячейке в полноэкранном режиме) */
|
|
.grid.solo { grid-template-columns: 1fr !important; grid-template-rows: 1fr !important; }
|
|
.grid.solo > .tile:not(.solo) { display: none; }
|
|
|
|
/* ── Панель управления под сеткой (футер фрейма live): листание + play/stop + фулскрин ── */
|
|
footer.grid-bar {
|
|
height: 32px; flex: none; display: flex; align-items: center; gap: 8px;
|
|
padding: 0 8px; background: var(--panel); border-top: 1px solid var(--border);
|
|
}
|
|
.grid-bar .gb-left { flex: 1; display: flex; justify-content: flex-start; }
|
|
.grid-bar .gb-center { flex: 0 0 auto; display: flex; justify-content: center; gap: 8px; }
|
|
.grid-bar .gb-right { flex: 1; display: flex; justify-content: flex-end; gap: 8px; }
|
|
.grid-bar .gb-btn {
|
|
height: 24px; min-width: 30px; padding: 0 8px; border: 1px solid var(--border);
|
|
background: var(--panel-2); color: var(--text); border-radius: 6px; cursor: pointer;
|
|
font-size: 14px; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
|
|
}
|
|
.grid-bar .gb-btn:hover:not(:disabled) { border-color: #c0392b; color: #fff; }
|
|
.grid-bar .gb-btn:disabled { opacity: .3; cursor: default; }
|
|
.grid-bar .gb-play { min-width: 42px; font-size: 15px; }
|
|
/* кнопка «Раскладка» (window split) + всплывающее меню (раскрывается вверх) */
|
|
.grid-bar .gb-split { position: relative; display: inline-flex; }
|
|
.grid-bar .gb-split-menu {
|
|
display: none; position: absolute; bottom: calc(100% + 6px); right: 0; z-index: 50;
|
|
flex-direction: column; gap: 2px; padding: 4px; min-width: 60px;
|
|
background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
|
|
box-shadow: 0 6px 20px rgba(0, 0, 0, .5);
|
|
}
|
|
.grid-bar .gb-split-menu.open { display: flex; }
|
|
.grid-bar .gb-split-menu button {
|
|
height: 24px; padding: 0 10px; border: 1px solid transparent; border-radius: 5px;
|
|
background: none; color: var(--text); cursor: pointer; font-size: 13px;
|
|
font-variant-numeric: tabular-nums; text-align: center;
|
|
}
|
|
.grid-bar .gb-split-menu button:hover { background: var(--panel-2); }
|
|
.grid-bar .gb-split-menu button.active { background: var(--accent); border-color: #c0392b; color: #fff; }
|
|
.tile {
|
|
position: relative; background: #05070a; border: 1px solid var(--border);
|
|
display: flex; align-items: center; justify-content: center; overflow: hidden;
|
|
min-height: 0; min-width: 0; /* не растягиваться под видео */
|
|
}
|
|
.tile .hevc-player { position: absolute; inset: 0; width: 100%; height: 100%; background: #000; }
|
|
.tile .hevc-player canvas { width: 100%; height: 100%; display: block; }
|
|
/* canvas WASM/WebCodecs-плеера: вписываем кадр в ячейку с сохранением пропорций */
|
|
.wc-canvas { width: 100%; height: 100%; display: block; object-fit: contain; background: #000; }
|
|
.tile video, .tile video-stream {
|
|
display: block; width: 100%; height: 100%; max-width: 100%; max-height: 100%;
|
|
object-fit: contain; background: #000;
|
|
}
|
|
.tile .label {
|
|
position: absolute; left: 0; top: 0; padding: 3px 9px; font-size: 12px;
|
|
background: rgba(0,0,0,.55); border-bottom-right-radius: 6px; pointer-events: none;
|
|
}
|
|
.tile .label .dot { display: inline-block; vertical-align: middle; margin-right: 6px; }
|
|
.tile .empty { color: var(--text-dim); font-size: 13px; }
|
|
.tile .no-link {
|
|
font-family: Georgia, "Times New Roman", serif; font-weight: 700;
|
|
font-size: clamp(16px, 3.2vw, 40px); letter-spacing: 3px; color: #9a9a9a;
|
|
text-shadow: 0 1px 0 #000, 0 0 6px rgba(0,0,0,.6); user-select: none;
|
|
}
|
|
.tile-btn {
|
|
position: absolute; top: 4px; right: 4px; z-index: 2; padding: 2px 8px; font-size: 11px;
|
|
background: rgba(0,0,0,.55); border: 1px solid var(--border); color: var(--text);
|
|
border-radius: 5px; cursor: pointer; opacity: .7;
|
|
}
|
|
.tile-btn:hover { opacity: 1; border-color: #c0392b; }
|
|
.tile-btn.active { background: var(--accent); border-color: #c0392b; color: #fff; opacity: 1; }
|
|
.tile:fullscreen { border: none; background: #000; }
|
|
.tile:fullscreen video, .tile:fullscreen video-stream { object-fit: contain; }
|
|
#grid:fullscreen { width: 100vw; height: 100vh; background: var(--bg); padding: 0; }
|
|
/* в полноэкранном режиме панель-футер скрыта (вход/выход — клавиша F, выход также Esc) */
|
|
#live-main:fullscreen .grid-bar,
|
|
#arch-content:fullscreen .grid-bar { display: none; }
|
|
.hevc-hint {
|
|
position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
|
|
background: rgba(63, 3, 3, .88); color: #fff; font-size: 11px;
|
|
padding: 5px 8px; text-align: center; line-height: 1.3;
|
|
}
|
|
|
|
/* ── Архив ── */
|
|
/* в ряд: сайдбар 220px слева + контент справа (явно, т.к. базовое main = column) */
|
|
.archive-main { flex: 1; display: flex; flex-direction: row; overflow: hidden; }
|
|
/* левый сайдбар архива: 220px, поделён по высоте пополам */
|
|
.arch-side {
|
|
width: 220px; flex: none; display: flex; flex-direction: column;
|
|
background: var(--panel); border-right: 1px solid var(--border); overflow: hidden;
|
|
}
|
|
/* верхний фрейм — список камер: заполняет всё над карточкой даты, без полосы прокрутки */
|
|
.arch-cams { flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: none; }
|
|
.arch-cams::-webkit-scrollbar { width: 0; height: 0; display: none; }
|
|
.arch-cam {
|
|
padding: 6px 12px; font-size: 13px; color: var(--text); cursor: pointer;
|
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
}
|
|
.arch-cam:hover { background: var(--panel-2); }
|
|
.arch-cam.active { background: #c0392b; color: #fff; font-weight: 600; }
|
|
/* нижний фрейм даты — карточка (дата сверху + календарь), прижата вниз */
|
|
.arch-date {
|
|
flex: none; margin: 10px; padding: 10px;
|
|
background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px;
|
|
display: flex; flex-direction: column; gap: 8px;
|
|
}
|
|
.arch-dateinput {
|
|
width: 100%; background: var(--panel); color: var(--text); border: 1px solid var(--border);
|
|
border-radius: 6px; padding: 5px 8px; font-size: 13px;
|
|
}
|
|
.arch-cal { user-select: none; }
|
|
.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
|
|
.cal-title { font-size: 13px; font-weight: 600; color: var(--text); }
|
|
.cal-nav {
|
|
background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
|
|
width: 24px; height: 24px; border-radius: 6px; cursor: pointer; padding: 0; line-height: 1; font-size: 14px;
|
|
}
|
|
.cal-nav:hover { border-color: #c0392b; color: #fff; }
|
|
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
|
|
.cal-wd { font-size: 10px; color: var(--text-dim); text-align: center; padding: 2px 0; }
|
|
.cal-day {
|
|
text-align: center; font-size: 12px; padding: 4px 0; border-radius: 5px; cursor: pointer;
|
|
color: var(--text); font-variant-numeric: tabular-nums;
|
|
}
|
|
.cal-day.empty { visibility: hidden; cursor: default; }
|
|
.cal-day:hover:not(.empty) { background: rgba(255, 255, 255, .07); }
|
|
.cal-day.today { box-shadow: inset 0 0 0 1px var(--text-dim); }
|
|
.cal-day.sel { background: #c0392b; color: #fff; font-weight: 600; }
|
|
/* контент архива (плеер + таймлайн) */
|
|
.arch-content { flex: 1; display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
|
|
.player-wrap { flex: 1; background: #000; display: flex; align-items: center; justify-content: center; min-height: 0; }
|
|
.player-wrap video { max-width: 100%; max-height: 100%; }
|
|
.player-wrap .hint { color: var(--text-dim); }
|
|
|
|
/* ── плеер архива в стиле Playerjs ── */
|
|
.player-wrap .pjs { position: relative; width: 100%; height: 100%; background: #000; outline: none; overflow: hidden; }
|
|
.pjs-video { width: 100%; height: 100%; display: block; object-fit: contain; background: #000; cursor: pointer; }
|
|
/* центр: большая кнопка play (на паузе) + спиннер (при буферизации) */
|
|
.pjs-center { position: absolute; inset: 0; pointer-events: none; }
|
|
.pjs-bigplay {
|
|
position: absolute; top: 50%; left: 50%; width: 74px; height: 74px; border-radius: 50%;
|
|
border: none; cursor: pointer; pointer-events: auto; background: rgba(10,12,18,.55); color: #fff;
|
|
font-size: 26px; padding-left: 5px; opacity: 0; transition: opacity .15s, transform .15s;
|
|
transform: translate(-50%, -50%) scale(.9);
|
|
}
|
|
.pjs.paused:not(.buffering) .pjs-bigplay { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
|
.pjs-bigplay:hover { background: rgba(10,12,18,.82); }
|
|
.pjs-spinner {
|
|
position: absolute; top: 50%; left: 50%; width: 46px; height: 46px; margin: -23px 0 0 -23px;
|
|
border: 3px solid rgba(255,255,255,.22); border-top-color: #fff; border-radius: 50%; opacity: 0;
|
|
}
|
|
.pjs.buffering .pjs-spinner { opacity: 1; animation: pjs-spin .8s linear infinite; }
|
|
@keyframes pjs-spin { to { transform: rotate(360deg); } }
|
|
/* нижняя панель управления */
|
|
.pjs-bar {
|
|
position: absolute; left: 0; right: 0; bottom: 0; z-index: 5;
|
|
display: flex; align-items: center; gap: 10px; padding: 26px 14px 11px;
|
|
background: linear-gradient(transparent, rgba(0,0,0,.8));
|
|
opacity: 0; transform: translateY(8px); transition: opacity .2s, transform .2s;
|
|
}
|
|
.pjs.show .pjs-bar, .pjs:hover .pjs-bar, .pjs.paused .pjs-bar { opacity: 1; transform: none; }
|
|
.pjs-btn {
|
|
background: none; border: none; color: #fff; cursor: pointer; font-size: 16px; line-height: 1;
|
|
padding: 5px 7px; border-radius: 5px; opacity: .9; transition: background .12s, opacity .12s;
|
|
}
|
|
.pjs-btn:hover { opacity: 1; background: rgba(255,255,255,.14); }
|
|
.pjs-time, .pjs-dur { color: #fff; font-size: 12px; font-variant-numeric: tabular-nums; min-width: 40px; text-align: center; opacity: .92; }
|
|
/* шкала перемотки */
|
|
.pjs-seek { position: relative; flex: 1; height: 5px; border-radius: 3px; background: rgba(255,255,255,.25); cursor: pointer; touch-action: none; }
|
|
.pjs-seek:hover { height: 7px; }
|
|
.pjs-buf { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: rgba(255,255,255,.35); border-radius: 3px; }
|
|
.pjs-prog { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: #e23b3b; border-radius: 3px; }
|
|
.pjs-knob {
|
|
position: absolute; top: 50%; width: 13px; height: 13px; margin-left: -6.5px; border-radius: 50%;
|
|
background: #e23b3b; box-shadow: 0 0 4px rgba(0,0,0,.5); transform: translateY(-50%) scale(0); transition: transform .12s;
|
|
}
|
|
.pjs-seek:hover .pjs-knob, .pjs.show .pjs-knob { transform: translateY(-50%) scale(1); }
|
|
.pjs.fs:not(.show) { cursor: none; }
|
|
|
|
/* DVR-таймлайн архива: лента тащится мышью/колесом, красный указатель зафиксирован по центру */
|
|
.timeline {
|
|
height: 96px; background: var(--panel); border-top: 1px solid var(--border);
|
|
overflow: hidden; position: relative; cursor: grab; user-select: none; touch-action: none;
|
|
}
|
|
.timeline.grabbing { cursor: grabbing; }
|
|
.tl-track { position: absolute; top: 0; bottom: 0; left: 0; will-change: transform; }
|
|
/* деления времени */
|
|
.tl-tick { position: absolute; top: 0; bottom: 0; width: 0; border-left: 1px solid var(--border); }
|
|
.tl-tick.hour { border-left-color: #3a4254; }
|
|
.tl-tick > span {
|
|
position: absolute; top: 7px; left: 5px; font-size: 11px; color: var(--text-dim);
|
|
font-variant-numeric: tabular-nums; white-space: nowrap;
|
|
}
|
|
.tl-tick.hour > span { color: var(--text); }
|
|
/* полоса записей (жёлтая) */
|
|
.tl-seg {
|
|
position: absolute; top: 36px; height: 40px; background: #e9c46a; border-radius: 2px;
|
|
}
|
|
.tl-seg.active { background: #f4a261; }
|
|
/* маркер «эфир» (текущее время; движется вместе с лентой) */
|
|
.tl-now { position: absolute; top: 0; bottom: 0; width: 0; border-left: 2px dashed var(--ok); z-index: 3; }
|
|
.tl-now > span {
|
|
position: absolute; top: 7px; left: 4px; font-size: 10px; font-weight: 700; color: var(--ok); white-space: nowrap;
|
|
}
|
|
/* фиксированный красный указатель по центру (playhead) */
|
|
.tl-center { position: absolute; left: 50%; top: 0; bottom: 0; width: 0; border-left: 2px solid #e03131; z-index: 6; pointer-events: none; }
|
|
.tl-center::after {
|
|
content: ""; position: absolute; top: 0; left: 0; transform: translateX(-50%);
|
|
border: 6px solid transparent; border-top-color: #e03131; border-bottom: 0;
|
|
}
|
|
.tl-center > span {
|
|
position: absolute; top: 6px; left: 0; transform: translateX(-50%);
|
|
font-size: 11px; font-weight: 700; color: #fff; background: #e03131;
|
|
padding: 1px 6px; border-radius: 3px; white-space: nowrap; font-variant-numeric: tabular-nums;
|
|
}
|
|
.tl-empty {
|
|
position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%);
|
|
text-align: center; color: var(--text-dim); pointer-events: none;
|
|
}
|
|
|
|
.muted { color: var(--text-dim); }
|
|
.empty-state { padding: 40px; text-align: center; color: var(--text-dim); }
|
|
|
|
/* ── Редактор раскладки ── */
|
|
.layout-grid { display: grid; gap: 8px; }
|
|
.lcell { background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; padding: 10px; }
|
|
.lcell-no { font-size: 11px; color: var(--text-dim); margin-bottom: 6px; }
|
|
.lcell select { width: 100%; }
|
|
|
|
/* ── Страница входа ── */
|
|
.login-body {
|
|
display: flex; align-items: center; justify-content: center; height: 100%;
|
|
background: radial-gradient(circle at 50% 30%, #1a1014, var(--bg));
|
|
}
|
|
.login-card {
|
|
width: 320px; background: var(--panel); border: 1px solid var(--border);
|
|
border-radius: 12px; padding: 28px 26px; display: flex; flex-direction: column; gap: 12px;
|
|
box-shadow: 0 12px 40px rgba(0,0,0,.5);
|
|
}
|
|
.login-brand { font-size: 26px; text-align: center; }
|
|
.login-sub { text-align: center; color: var(--text-dim); font-size: 13px; margin-bottom: 8px; }
|
|
.login-card input {
|
|
background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
|
|
padding: 11px 13px; border-radius: 7px; font-size: 14px;
|
|
}
|
|
.login-card input:focus { outline: none; border-color: #c0392b; }
|
|
.login-card button {
|
|
background: var(--accent); border: 1px solid #c0392b; color: #fff; font-weight: 600;
|
|
padding: 11px; border-radius: 7px; cursor: pointer; margin-top: 4px;
|
|
}
|
|
.login-card button:hover { background: var(--accent-2); }
|
|
.login-err { color: #e57373; font-size: 13px; text-align: center; min-height: 18px; }
|
|
|
|
/* ── Настройки ── */
|
|
.settings-main { flex: 1; overflow-y: auto; padding: 20px; }
|
|
.settings-main .wrap { max-width: 920px; margin: 0 auto; }
|
|
|
|
/* Вкладки (как в браузере) */
|
|
.tabs { display: flex; flex-wrap: wrap; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 18px; }
|
|
.tab {
|
|
background: none; border: none; border-bottom: 2px solid transparent; border-radius: 0;
|
|
color: var(--text-dim); padding: 10px 18px; cursor: pointer; font-size: 14px; font-weight: 500;
|
|
}
|
|
.tab:hover { color: var(--text); }
|
|
.tab.active { color: #fff; border-bottom-color: #c0392b; }
|
|
.tab-panel[hidden] { display: none; }
|
|
/* ряд настроек профиля: слева — ровная сетка параметров, справа — «Готовый профиль» */
|
|
.tr-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; padding-left: 28px; align-items: flex-start; }
|
|
.tr-row[hidden] { display: none; }
|
|
/* 3 группы параметров перекодирования: Кодек / Битрейт / Готовый профиль */
|
|
.tr-group { border: 1px solid var(--border); border-radius: 10px; padding: 8px 12px 12px; background: var(--panel-2); }
|
|
.tr-gtitle { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .4px; margin-bottom: 8px; }
|
|
.tr-gbody { display: flex; flex-wrap: wrap; gap: 12px 14px; align-items: flex-end; }
|
|
.tr-row label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--text-dim); }
|
|
.tr-gbody select, .tr-gbody input[type="number"] { display: block; }
|
|
.tr-gbody input[type="number"] { width: 80px; }
|
|
.tr-group-preset select { min-width: 120px; }
|
|
.tr-row select:disabled, .tr-row input:disabled { opacity: .4; cursor: not-allowed; }
|
|
/* блок профиля: режим (свой плеер / перекодировать) + ряд параметров */
|
|
.tr-block { padding: 12px 0; border-top: 1px solid var(--border); }
|
|
.tr-block:first-of-type { border-top: none; padding-top: 0; }
|
|
.tr-block h3 { margin: 0 0 8px; font-size: 13px; font-weight: 600; color: var(--text); }
|
|
/* 3-позиционный сегментированный переключатель режима */
|
|
.seg-toggle {
|
|
display: inline-flex; border: 1px solid var(--border); border-radius: 8px;
|
|
overflow: hidden; background: var(--panel-2);
|
|
}
|
|
.seg-toggle label {
|
|
display: flex; align-items: center; cursor: pointer; border-right: 1px solid var(--border);
|
|
}
|
|
.seg-toggle label:last-child { border-right: none; }
|
|
.seg-toggle input[type="radio"] { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; pointer-events: none; }
|
|
.seg-toggle span {
|
|
padding: 7px 14px; font-size: 13px; color: var(--text-dim);
|
|
white-space: nowrap; transition: background .12s, color .12s;
|
|
}
|
|
.seg-toggle label:hover span { color: var(--text); }
|
|
.seg-toggle input:checked + span { background: var(--accent); color: #fff; }
|
|
.tab[hidden], .card[hidden] { display: none; }
|
|
.card {
|
|
background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
|
|
padding: 16px 18px; margin-bottom: 18px;
|
|
}
|
|
.card h2 { margin: 0 0 14px; font-size: 15px; font-weight: 600; }
|
|
.card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
|
|
.card-head h2 { margin: 0; }
|
|
|
|
/* ── Модальное окно ── */
|
|
.modal-overlay {
|
|
position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 50;
|
|
display: flex; align-items: center; justify-content: center;
|
|
}
|
|
.modal {
|
|
width: 440px; max-width: calc(100vw - 32px); background: var(--panel);
|
|
border: 1px solid var(--border); border-radius: 12px; padding: 22px 24px;
|
|
box-shadow: 0 16px 50px rgba(0,0,0,.55);
|
|
}
|
|
.modal h3 { margin: 0 0 18px; font-size: 17px; }
|
|
.modal .field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
|
|
.modal .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
|
.modal label.fl { color: var(--text-dim); font-size: 12px; }
|
|
.modal input[type="text"], .modal input[type="password"] {
|
|
background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
|
|
padding: 9px 11px; border-radius: 7px; font-size: 14px; width: 100%;
|
|
}
|
|
.modal input[type="text"]:focus, .modal input[type="password"]:focus { outline: none; border-color: #c0392b; }
|
|
.modal input:disabled { opacity: .5; }
|
|
.modal .switch-field { flex-direction: row; align-items: center; gap: 10px; }
|
|
.modal .err { color: #e57373; font-size: 13px; min-height: 18px; margin-bottom: 4px; }
|
|
.modal .actions { display: flex; justify-content: flex-end; gap: 8px; }
|
|
.modal #modal-save { background: var(--accent); border-color: #c0392b; color: #fff; font-weight: 600; }
|
|
.modal #modal-save:hover { background: var(--accent-2); }
|
|
.kv { display: grid; grid-template-columns: 200px 1fr; gap: 8px 16px; }
|
|
.kv .k { color: var(--text-dim); }
|
|
.kv .v { font-variant-numeric: tabular-nums; }
|
|
table.cams { width: 100%; border-collapse: collapse; }
|
|
table.cams th, table.cams td {
|
|
text-align: left; padding: 2px 10px; border-bottom: 1px solid var(--border);
|
|
font-size: 12px; line-height: 1.35;
|
|
}
|
|
table.cams th { color: var(--text-dim); font-weight: 500; font-size: 11px; padding: 4px 10px; }
|
|
table.cams td.actions { white-space: nowrap; text-align: right; }
|
|
table.cams tr.slot-empty td { opacity: .5; }
|
|
/* камеры: жёстко фиксированная высота строки 22px независимо от контролов.
|
|
line-height:1 + контролы по центру и ниже 22px → строки не распирает. */
|
|
table.cams.cams-fixed td, table.cams.cams-fixed th {
|
|
height: 22px; padding-top: 0; padding-bottom: 0; line-height: 1;
|
|
}
|
|
table.cams.cams-fixed td > * { vertical-align: middle; }
|
|
/* тумблеры «Вкл»/«Запись» в строке — компактные (30×16) */
|
|
table.cams.cams-fixed td input[type="checkbox"] {
|
|
width: 30px; height: 16px; border-radius: 8px;
|
|
}
|
|
table.cams.cams-fixed td input[type="checkbox"]::before { width: 12px; height: 12px; top: 1px; left: 1px; }
|
|
table.cams.cams-fixed td input[type="checkbox"]:checked::before { transform: translateX(14px); }
|
|
/* селекты и инпуты в строке — на 2px ниже строки, по центру */
|
|
table.cams.cams-fixed td select.stor-sel,
|
|
table.cams.cams-fixed td select.cell-sel,
|
|
table.cams.cams-fixed td input.cell-in { height: 20px; }
|
|
/* кнопки действий ⟳/✕ (режим редактирования) — компактные */
|
|
table.cams.cams-fixed td.actions button {
|
|
height: 20px; padding: 0 6px; font-size: 13px; line-height: 1;
|
|
}
|
|
|
|
/* ── Инлайн-редактор строки (вместо всплывающего окна) ── */
|
|
.cam-edit { background: var(--panel-2); padding: 12px 12px 14px; }
|
|
.cam-edit-grid { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: flex-end; }
|
|
.cam-edit-grid label { display: flex; flex-direction: column; gap: 4px; font-size: 11px; color: var(--text-dim); }
|
|
.cam-edit-grid input[type="text"], .cam-edit-grid input[type="password"] {
|
|
background: var(--panel); border: 1px solid var(--border); color: var(--text);
|
|
padding: 7px 9px; border-radius: 6px; font-size: 13px; width: 150px;
|
|
}
|
|
.cam-edit-grid #e-id, .cam-edit-grid #e-ip { width: 130px; }
|
|
.cam-edit-grid #e-main, .cam-edit-grid #e-sub { width: 178px; }
|
|
.cam-edit-grid input:focus { outline: none; border-color: #c0392b; }
|
|
.cam-edit-grid input:disabled { opacity: .5; }
|
|
.cam-edit-grid label.sw { flex-direction: row; align-items: center; gap: 8px; color: var(--text); }
|
|
.cam-edit-foot { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
|
|
.cam-edit-foot .err { color: #e57373; font-size: 12px; flex: 1; }
|
|
.cam-edit-foot [data-act="save-edit"] { background: var(--accent); border-color: #c0392b; color: #fff; font-weight: 600; }
|
|
.cam-bulk-head { font-size: 11px; color: var(--text-dim); margin-bottom: 8px; letter-spacing: .3px; }
|
|
button.primary { background: var(--accent); border-color: #c0392b; color: #fff; font-weight: 600; }
|
|
button.danger { border-color: #c0392b; color: #e74c3c; }
|
|
button.danger:hover { background: #c0392b; border-color: #c0392b; color: #fff; }
|
|
table.cams td.actions button { padding: 2px 8px; font-size: 12px; }
|
|
/* инлайн-селекты в строке (хранилище, канал) — компактные, не раздувают высоту строки */
|
|
table.cams td select.stor-sel, table.cams td select.cell-sel {
|
|
height: 22px; padding: 1px 4px; font-size: 12px; line-height: 1;
|
|
border-radius: 5px; width: 100%; max-width: 150px; box-sizing: border-box;
|
|
vertical-align: middle;
|
|
}
|
|
/* массовое редактирование: инпуты прямо в ячейках, компактные (высота строки не растёт) */
|
|
table.cams td input.cell-in {
|
|
height: 22px; padding: 1px 6px; font-size: 12px; line-height: 1;
|
|
border-radius: 5px; width: 100%; box-sizing: border-box; vertical-align: middle;
|
|
background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
|
|
}
|
|
table.cams td input.cell-in:focus { outline: none; border-color: #c0392b; }
|
|
table.cams td.cell-dual { white-space: nowrap; }
|
|
table.cams td.cell-dual input.cell-in { display: inline-block; width: calc(50% - 3px); }
|
|
table.cams td.cell-dual input.cell-in + input.cell-in { margin-left: 4px; }
|
|
table.cams tr.bulk-row td { background: rgba(192, 57, 43, .05); }
|
|
.badge {
|
|
display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px;
|
|
border-radius: 11px; font-size: 12px; background: var(--panel-2);
|
|
}
|
|
.badge-active { background: rgba(46, 204, 113, .18); color: var(--ok); font-weight: 600; }
|
|
.q-cell { display: inline-flex; align-items: center; gap: 6px; }
|
|
.q-cell .q-input { width: 78px; }
|
|
.q-cell button { padding: 2px 8px; font-size: 12px; }
|
|
.logbox {
|
|
margin: 4px 10px 12px; padding: 10px; background: #05070a; border: 1px solid var(--border);
|
|
border-radius: 6px; font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
|
|
font-size: 12px; color: var(--text-dim); white-space: pre-wrap; max-height: 220px; overflow: auto;
|
|
}
|