'use strict';
/* ════════════════════════════════════════════════════════════════
DASHBOARD · ADMIN COMMAND CENTER
Рендерит «командный центр администратора» в /dashboard для роли admin.
Дизайн — в стиле дизайн-системы LearnSpace (/css/ls.css):
палитра violet #9B5DE5 / cyan #06D6E0, шрифты Unbounded + Manrope,
карточки-«стекло» 20px, градиентные акценты. На реальных данных
GET /api/admin/overview.
Весь CSS заскоуплен под #admin-command-center, чтобы не конфликтовать
с ls.css / dashboard.html. Деструктивные действия не выполняются
inline — кнопки ведут в соответствующие разделы /admin.
Точка входа: window.DashAdminCenter.mount(rootEl).
════════════════════════════════════════════════════════════════ */
(function () {
'use strict';
let _root = null;
let _data = null;
let _tab = 'all';
let _clockTimer = null;
/* ── subject hue cycle (совпадает с overview.js) ──────────────── */
const SUBJ_COLORS = [
'#9B5DE5', '#06D6E0', '#06D664', '#FFB347', '#F15BB5',
'#7C3AED', '#4FC3F7', '#FFD54F', '#FF8A65', '#BA68C8',
];
/* ── one-time font + CSS injection ────────────────────────────── */
function ensureAssets() {
if (!document.getElementById('acc-font')) {
const l = document.createElement('link');
l.id = 'acc-font';
l.rel = 'stylesheet';
l.href = 'https://fonts.googleapis.com/css2?family=Unbounded:wght@400;700;800&family=Manrope:wght@400;500;600;700&display=swap';
document.head.appendChild(l);
}
if (document.getElementById('acc-style')) return;
const s = document.createElement('style');
s.id = 'acc-style';
s.textContent = CSS;
document.head.appendChild(s);
}
/* ── helpers ──────────────────────────────────────────────────── */
const e = (str) => (window.LS && LS.esc ? LS.esc(str) : String(str == null ? '' : str));
function initials(name) {
if (!name) return '?';
return name.trim().split(/\s+/).slice(0, 2)
.map((w) => (w[0] ? w[0].toUpperCase() : '')).join('') || '?';
}
function hashHue(str) {
let h = 0;
for (let i = 0; i < (str || '').length; i++) h = ((h << 5) - h + str.charCodeAt(i)) | 0;
return Math.abs(h) % 360;
}
function pctClass(p) {
if (p == null) return 'mid';
return p >= 75 ? 'hi' : p >= 50 ? 'mid' : 'lo';
}
function fmtNum(n) {
if (n == null) return '0';
return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
}
function parseTs(s) {
if (!s) return null;
try { return new Date(s.replace(' ', 'T') + (s.endsWith('Z') ? '' : 'Z')); }
catch (_) { return null; }
}
function fmtAgo(s) {
const d = parseTs(s);
if (!d) return '';
const sec = Math.floor((Date.now() - d.getTime()) / 1000);
if (sec < 60) return 'только что';
const min = Math.floor(sec / 60);
if (min < 60) return min + ' мин';
const hr = Math.floor(min / 60);
if (hr < 24) return hr + ' ч';
return Math.floor(hr / 24) + ' дн';
}
function fmtSince(s) {
const d = parseTs(s);
if (!d) return '—';
let min = Math.floor((Date.now() - d.getTime()) / 60000);
if (min < 0) min = 0;
const hr = Math.floor(min / 60);
if (hr >= 48) return Math.floor(hr / 24) + 'д ' + (hr % 24) + 'ч';
return hr > 0 ? hr + 'ч ' + (min % 60) + 'м' : min + 'м';
}
function fmtBannedDate(s) {
const d = parseTs(s);
if (!d) return '';
return d.toLocaleDateString('ru', { day: 'numeric', month: 'short' });
}
function go(hash) { window.location.href = hash; }
/* ── sparkline path (array of {d, n} → 7-day path) ────────────── */
function sparkPath(raw, w, h) {
const map = {};
(raw || []).forEach((r) => { map[r.d] = r.n; });
const pts = [];
for (let i = 6; i >= 0; i--) {
const dt = new Date();
dt.setDate(dt.getDate() - i);
pts.push(map[dt.toISOString().slice(0, 10)] || 0);
}
const max = Math.max.apply(null, pts) || 1;
const pad = 2;
const xs = pts.map((_, i) => pad + (i / 6) * (w - 2 * pad));
const ys = pts.map((v) => h - pad - (v / max) * (h - 2 * pad));
return xs.map((x, i) => x.toFixed(1) + ' ' + ys[i].toFixed(1)).join(' L ');
}
/* ── greeting by hour ─────────────────────────────────────────── */
function greeting() {
const hr = new Date().getHours();
if (hr < 6) return 'Доброй ночи';
if (hr < 12) return 'Доброе утро';
if (hr < 18) return 'Добрый день';
return 'Добрый вечер';
}
function clockStr() {
const d = new Date();
return d.toLocaleTimeString('ru', { hour: '2-digit', minute: '2-digit' });
}
function dateStr() {
const d = new Date();
const s = d.toLocaleDateString('ru', { weekday: 'long', day: 'numeric', month: 'long' });
return s.charAt(0).toUpperCase() + s.slice(1);
}
/* ── KPI pulse row ────────────────────────────────────────────── */
function kpiRow(d) {
const sp = d.sparks || {};
const sessSpark = sparkPath(sp.sessions, 120, 34);
return `
Сессий запущено · 24ч
LIVE
${fmtNum(d.newSessions24h)}
${fmtNum(d.activeUsers24h)}
${fmtNum(d.classesTotal)}
`;
}
/* ── attention inbox items (unified queue) ────────────────────── */
function buildAttnItems(d) {
const items = [];
(d.bannedThisWeek || []).forEach((u) => {
items.push({
sev: 'rose', kind: 'block', kindLabel: 'Блокировка',
title: u.name || '—',
meta: `${e(u.email || '')} · ${fmtBannedDate(u.banned_at)}`,
act: 'Разблокировать', actHash: '/admin#users', solid: false,
});
});
(d.stuckSessions || []).forEach((s) => {
items.push({
sev: 'amber', kind: 'stuck', kindLabel: 'Зависла',
title: s.user_name || '—',
meta: `${e(s.subject_name || '—')} · висит ${fmtSince(s.started_at)}`,
// Глубокая ссылка на ДЕТАЛИ конкретной сессии (открывается при любом статусе):
// список /admin#sessions показывает только completed, поэтому зависшая (in_progress)
// там не находилась. На странице деталей её можно посмотреть и удалить.
act: 'Открыть', actHash: '/admin#sessions/' + s.id, solid: true,
});
});
const ab = d.abandonedSessions24h || 0;
if (ab > 0) {
items.push({
sev: 'amber', kind: 'stuck', kindLabel: 'Брошено',
title: 'Всплеск брошенных сессий',
meta: `${ab} сессий прервано за 24ч`,
act: 'Разобрать', actHash: '/admin#sessions', solid: false,
});
}
return items;
}
function attnRowHtml(it) {
const icon = it.sev === 'rose'
? ''
: '';
return `
${icon}
${e(it.kindLabel)}
${e(it.title)}
${it.meta}
`;
}
function attnCard(d) {
const items = buildAttnItems(d);
const blocks = items.filter((i) => i.kind === 'block');
const stuck = items.filter((i) => i.kind === 'stuck');
let shown = items;
if (_tab === 'block') shown = blocks;
else if (_tab === 'stuck') shown = stuck;
const body = shown.length
? `${shown.map(attnRowHtml).join('')}
`
: `
Всё в норменет событий, требующих внимания
`;
return `
Требует внимания
${items.length} ${items.length === 1 ? 'событие' : 'событий'}
все алерты
${body}
`;
}
/* ── live feed (top sessions) ─────────────────────────────────── */
function feedCard(d) {
const rows = (d.topSessions24h || []).slice(0, 8);
const subj = d.sessionsBySubject24h || [];
const total = subj.reduce((a, r) => a + r.n, 0) || 1;
const feedHtml = rows.length ? rows.map((s) => {
const name = s.user_name || '—';
const pc = s.percent;
return `
${e(initials(name))}
${e(name)}
${e(s.subject_name || '—')} · ${fmtAgo(s.finished_at)}
${pc != null ? pc : '—'}%
${(s.score != null ? s.score : 0)}/${(s.total != null ? s.total : 0)}
`;
}).join('') : 'Нет завершённых сессий за 24ч
';
let segs = '', legend = '';
subj.forEach((r, i) => {
const pct = (r.n / total * 100).toFixed(1);
const col = SUBJ_COLORS[i % SUBJ_COLORS.length];
segs += ``;
legend += `${e(r.name)} ${r.n}`;
});
const subjBlock = subj.length ? `
Сессии по предметам · 24ч${total}
${segs}
${legend}
` : '';
return `
`;
}
/* ── content health ───────────────────────────────────────────── */
function healthRow(d) {
const inv = d.inventory || {};
const card = (icon, n, lbl) => `
${fmtNum(n != null ? n : 0)}
`;
return `
Здоровье контента
${card('
', inv.questions, 'вопросов')}
${card('
', inv.tests, 'тестов')}
${card('
', inv.courses, 'курсов')}
${card('
', inv.classes, 'классов')}
`;
}
/* ── results tables (top / worst) ─────────────────────────────── */
function resTable(rows) {
if (!rows || !rows.length) return 'Нет данных за 24ч
';
const body = rows.slice(0, 5).map((s) => {
const name = s.user_name || '—';
return `
${e(initials(name))}${e(name)} |
${e(s.subject_name || '—')} |
${(s.score != null ? s.score : 0)}/${(s.total != null ? s.total : 0)} |
${s.percent != null ? s.percent : '—'}% |
`;
}).join('');
return `
| Ученик | Предмет | Счёт | % |
${body}
`;
}
function resultsRow(d) {
return `
Результаты · 24ч
Топ-5 сегодня
${resTable(d.topSessions24h)}
Худшие 5 сегодня
${resTable(d.worstSessions24h)}
`;
}
/* ── quick actions ────────────────────────────────────────────── */
function quickRow() {
const btn = (icon, title, sub, hash) => `
`;
return `
Быстрые действия
${btn('
', 'Пользователи', '#users', '/admin#users')}
${btn('
', 'Сессии', '#sessions', '/admin#sessions')}
${btn('
', 'Тесты', '#tests', '/admin#tests')}
${btn('
', 'Классы', '#classes', '/classes')}
${btn('
', 'Права', '#permissions', '/admin#permissions')}
${btn('
', 'Аудит-лог', '#sublog', '/admin#sublog')}
`;
}
/* ── header (page-head + topbar strip) ────────────────────────── */
function headHtml(d) {
const items = buildAttnItems(d);
const blocks = items.filter((i) => i.kind === 'block').length;
const stuck = (d.stuckSessions || []).length;
const ab = d.abandonedSessions24h || 0;
let sub;
if (!items.length) {
sub = 'Сегодня всё спокойно — событий, требующих внимания, нет.';
} else {
const parts = [];
if (blocks) parts.push(`${blocks} ${blocks === 1 ? 'блокировка' : 'блокировок'}`);
if (stuck) parts.push(`${stuck} ${stuck === 1 ? 'зависшая сессия' : 'зависших'}`);
if (ab) parts.push(`${ab} брошенных`);
sub = 'Требует внимания: ' + parts.join(', ') + '.';
}
return `
${greeting()}· сводка за 24 часа
Командный центр
${sub}
`;
}
/* ── full render ──────────────────────────────────────────────── */
function render() {
if (!_root || !_data) return;
const d = _data;
_root.innerHTML = `
${headHtml(d)}
${kpiRow(d)}
${attnCard(d)}
${feedCard(d)}
${healthRow(d)}
${resultsRow(d)}
${quickRow()}
`;
wire();
}
function wire() {
_root.querySelectorAll('[data-go]').forEach((el) => {
el.addEventListener('click', () => go(el.getAttribute('data-go')));
});
_root.querySelectorAll('[data-tab]').forEach((el) => {
el.addEventListener('click', () => { _tab = el.getAttribute('data-tab'); render(); });
});
const rb = _root.querySelector('[data-act="refresh"]');
if (rb) rb.addEventListener('click', () => load());
}
function renderSkeleton() {
_root.innerHTML = `
`;
}
function startClock() {
if (_clockTimer) return;
_clockTimer = setInterval(() => {
const c = document.getElementById('acc-clock');
if (c) c.textContent = clockStr();
}, 30000);
}
async function load() {
if (!_root) return;
renderSkeleton();
try {
_data = await LS.adminGetOverview();
render();
startClock();
} catch (err) {
_root.innerHTML = `
Не удалось загрузить данные
${e(err && err.message ? err.message : 'Ошибка сети')}
`;
const rb = _root.querySelector('[data-act="retry"]');
if (rb) rb.addEventListener('click', () => load());
}
}
function mount(rootEl) {
ensureAssets();
_root = rootEl;
load();
}
window.DashAdminCenter = { mount, reload: load };
/* ════════════════════ SCOPED CSS ════════════════════ */
const CSS = `
#admin-command-center{
/* совпадает с дизайн-системой /css/ls.css (стекло, border, тени, радиусы) */
--surface:rgba(255,255,255,0.82); --surface-2:#faf8ff; --surface-3:rgba(155,93,229,.06);
--border:rgba(15,23,42,.10); --border-2:rgba(15,23,42,.20);
--tx:#0F172A; --tx2:#3D4F6B; --tx3:#56687A; --tx4:#8693A6;
--acc:#9B5DE5; --acc-600:#8a4fd0; --acc-700:#7a3bc4; --acc-50:rgba(155,93,229,.10); --acc-100:rgba(155,93,229,.22);
--acc-green:#0BA85A; --acc-green-50:rgba(6,214,100,.14); --acc-amber:#D9831A; --acc-amber-50:rgba(255,179,71,.16);
--acc-rose:#E23C8E; --acc-rose-50:rgba(241,91,181,.13); --acc-cyan:#0AA6B2; --acc-cyan-50:rgba(6,214,224,.14);
--acc-violet:#9B5DE5; --acc-violet-50:rgba(155,93,229,.12);
--grad-1:linear-gradient(135deg,#06D6E0,#9B5DE5);
--acc-sh-xs:var(--shadow); --acc-sh-sm:var(--shadow); --acc-sh:var(--shadow-h);
--acc-sh-accent:0 2px 14px rgba(155,93,229,.30);
--r-sm:8px; --r:12px; --r-lg:20px;
--sans:'Manrope',system-ui,-apple-system,sans-serif;
--mono:'Manrope',system-ui,sans-serif;
--display:'Unbounded',sans-serif;
font-family:var(--sans); color:var(--tx); font-size:14px; line-height:1.5;
-webkit-font-smoothing:antialiased;
}
#admin-command-center *{ box-sizing:border-box; }
#admin-command-center .acc-shell{ max-width:1380px; margin:0 auto; }
#admin-command-center .acc-mono{ font-family:var(--mono); font-variant-numeric:tabular-nums; color:var(--tx2); }
#admin-command-center .acc-ic{ width:18px; height:18px; flex:0 0 auto; display:inline-block;
stroke:currentColor; fill:none; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
#admin-command-center .acc-ic.sm{ width:15px; height:15px; stroke-width:1.8; }
#admin-command-center .acc-ic.xs{ width:13px; height:13px; stroke-width:1.9; }
/* topbar */
#admin-command-center .acc-topbar{ display:flex; align-items:center; gap:14px; margin-bottom:18px; flex-wrap:wrap; }
#admin-command-center .acc-crumbs{ display:flex; align-items:center; gap:8px; font-size:13px; color:var(--tx3); font-weight:500; }
#admin-command-center .acc-crumbs .here{ color:var(--tx); font-weight:700; }
#admin-command-center .acc-crumbs .acc-ic{ stroke:var(--tx4); }
#admin-command-center .acc-clock{ font-family:var(--mono); font-variant-numeric:tabular-nums; font-size:12px; color:var(--tx3);
padding-left:14px; border-left:1px solid var(--border); }
#admin-command-center .acc-clock b{ color:var(--tx2); font-weight:700; }
#admin-command-center .acc-tb-right{ margin-left:auto; display:flex; align-items:center; gap:8px; }
#admin-command-center .acc-tb-icon{ width:36px; height:36px; display:grid; place-items:center;
border-radius:var(--r-sm); border:1px solid var(--border); background:var(--surface); backdrop-filter:var(--blur);
color:var(--tx2); box-shadow:var(--acc-sh-xs); transition:border-color .16s,color .16s,background .16s; }
#admin-command-center .acc-tb-icon:hover{ border-color:var(--acc-100); color:var(--acc); background:var(--acc-50); }
#admin-command-center .acc-btn{ display:inline-flex; align-items:center; gap:7px; height:36px; padding:0 16px;
border-radius:var(--r-sm); font-size:13px; font-weight:700; border:none; font-family:var(--sans);
background:var(--grad-1); color:#fff;
box-shadow:var(--acc-sh-accent); transition:filter .16s,transform .12s,box-shadow .16s; }
#admin-command-center .acc-btn:hover{ filter:brightness(1.05); box-shadow:0 10px 26px rgba(155,93,229,.42); }
#admin-command-center .acc-btn:active{ transform:translateY(1px); }
#admin-command-center .acc-btn .acc-ic{ stroke:#fff; }
/* page head */
#admin-command-center .acc-page-head{ display:flex; align-items:flex-end; justify-content:space-between; gap:24px; margin-bottom:22px; }
#admin-command-center .acc-kicker{ display:inline-flex; align-items:center; gap:8px; font-family:var(--sans);
font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--acc); margin-bottom:10px; }
#admin-command-center .acc-kicker .acc-ic{ stroke:var(--acc); }
#admin-command-center .acc-kicker .muted{ color:var(--tx3); }
#admin-command-center .acc-page-head h1{ font-family:var(--display); font-size:30px; font-weight:800; letter-spacing:-.02em; line-height:1.08; margin:0; }
#admin-command-center .acc-page-head h1 span{ background:var(--grad-1); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; color:transparent; }
#admin-command-center .acc-sub{ margin:9px 0 0; font-size:14px; color:var(--tx2); max-width:60ch; }
#admin-command-center .acc-sub b{ color:var(--tx); font-weight:700; }
/* pulse kpi */
#admin-command-center .acc-pulse{ display:grid; grid-template-columns:1.4fr 1fr 1fr 1fr; gap:16px; margin-bottom:24px; }
#admin-command-center .acc-kpi{ background:var(--surface); backdrop-filter:var(--blur); border:1.5px solid var(--border); border-radius:var(--r-lg);
padding:18px; box-shadow:var(--acc-sh-xs); position:relative; overflow:hidden; transition:transform .18s var(--ease-out),box-shadow .18s,border-color .18s; }
#admin-command-center .acc-kpi:hover{ transform:translateY(-3px); box-shadow:var(--acc-sh); border-color:var(--acc-100); }
#admin-command-center .acc-kpi.hero{ background:linear-gradient(180deg,rgba(155,93,229,.06),var(--surface)); border-color:rgba(155,93,229,.20); }
#admin-command-center .acc-kpi-top{ display:flex; align-items:center; gap:9px; margin-bottom:12px; }
#admin-command-center .acc-kpi-ic{ width:32px; height:32px; border-radius:10px; display:grid; place-items:center; flex:0 0 auto; }
#admin-command-center .acc-kpi-ic.b{ background:var(--acc-50); } #admin-command-center .acc-kpi-ic.b .acc-ic{ stroke:var(--acc); }
#admin-command-center .acc-kpi-ic.c{ background:var(--acc-cyan-50); } #admin-command-center .acc-kpi-ic.c .acc-ic{ stroke:var(--acc-cyan); }
#admin-command-center .acc-kpi-ic.g{ background:var(--acc-green-50); } #admin-command-center .acc-kpi-ic.g .acc-ic{ stroke:var(--acc-green); }
#admin-command-center .acc-kpi-ic.v{ background:var(--acc-rose-50); } #admin-command-center .acc-kpi-ic.v .acc-ic{ stroke:var(--acc-rose); }
#admin-command-center .acc-lbl{ font-size:12px; font-weight:700; color:var(--tx3); }
#admin-command-center .acc-live{ margin-left:auto; display:inline-flex; align-items:center; gap:5px;
font-family:var(--mono); font-size:9px; font-weight:700; letter-spacing:.08em; color:var(--acc-green); }
#admin-command-center .acc-dot{ width:6px; height:6px; border-radius:50%; background:var(--acc-green); position:relative; }
#admin-command-center .acc-dot::after{ content:""; position:absolute; inset:-3px; border-radius:50%;
border:1.4px solid var(--acc-green); animation:acc-ping 1.8s ease-out infinite; }
@keyframes acc-ping{ 0%{ transform:scale(.5); opacity:.85 } 100%{ transform:scale(1.8); opacity:0 } }
#admin-command-center .acc-num{ font-family:var(--display); font-size:28px; font-weight:800; letter-spacing:-.02em;
line-height:1; font-variant-numeric:tabular-nums; }
#admin-command-center .acc-kpi.hero .acc-num{ font-size:36px; }
#admin-command-center .acc-kpi-foot{ margin-top:11px; font-size:12px; color:var(--tx3); }
#admin-command-center .acc-spark{ position:absolute; right:14px; bottom:14px; width:74px; height:26px; opacity:.9; pointer-events:none; }
#admin-command-center .acc-kpi.hero .acc-spark{ width:120px; height:34px; }
#admin-command-center .acc-line{ fill:none; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }
/* main grid */
#admin-command-center .acc-grid{ display:grid; grid-template-columns:minmax(0,1.55fr) minmax(0,1fr); gap:20px; align-items:start; margin-bottom:24px; }
#admin-command-center .acc-col{ display:flex; flex-direction:column; gap:20px; }
#admin-command-center .acc-card{ background:var(--surface); backdrop-filter:var(--blur); border:1.5px solid var(--border); border-radius:var(--r-lg); box-shadow:var(--acc-sh-xs); overflow:hidden; transition:box-shadow .2s; }
#admin-command-center .acc-card:hover{ box-shadow:var(--acc-sh); }
#admin-command-center .acc-card-head{ display:flex; align-items:center; gap:10px; padding:15px 18px; border-bottom:1px solid var(--border); }
#admin-command-center .acc-ttl-ic{ width:30px; height:30px; border-radius:9px; display:grid; place-items:center; flex:0 0 auto; background:var(--acc-50); }
#admin-command-center .acc-ttl-ic .acc-ic{ stroke:var(--acc); }
#admin-command-center .acc-card-head h2{ font-family:var(--display); font-size:14px; font-weight:800; letter-spacing:-.01em; margin:0; }
#admin-command-center .acc-count{ font-family:var(--mono); font-variant-numeric:tabular-nums; font-size:11px; font-weight:700; color:var(--tx2);
background:var(--surface-3); border:1px solid var(--border); border-radius:99px; padding:3px 10px; }
#admin-command-center .acc-more{ margin-left:auto; display:inline-flex; align-items:center; gap:5px;
font-size:12.5px; font-weight:700; color:var(--tx3); cursor:pointer; transition:color .14s; }
#admin-command-center .acc-more:hover{ color:var(--acc); }
/* attention */
#admin-command-center .acc-attn{ border-color:rgba(255,179,71,.45); box-shadow:0 0 0 1px rgba(255,179,71,.06),var(--acc-sh-xs); }
#admin-command-center .acc-attn .acc-card-head{ background:linear-gradient(180deg,rgba(255,179,71,.08),var(--surface)); border-bottom-color:rgba(255,179,71,.28); }
#admin-command-center .acc-attn .acc-ttl-ic{ background:var(--acc-amber-50); }
#admin-command-center .acc-attn .acc-ttl-ic .acc-ic{ stroke:var(--acc-amber); }
#admin-command-center .acc-attn-tabs{ display:flex; gap:5px; padding:11px 12px 0; flex-wrap:wrap; }
#admin-command-center .acc-attn-tab{ display:inline-flex; align-items:center; gap:7px; height:32px; padding:0 13px; border:none;
background:none; border-radius:999px; font-size:12.5px; font-weight:700; color:var(--tx3); cursor:pointer; transition:background .14s,color .14s,box-shadow .14s; font-family:var(--sans); }
#admin-command-center .acc-attn-tab:hover{ background:var(--surface-3); color:var(--tx); }
#admin-command-center .acc-attn-tab.on{ background:var(--acc); color:#fff; box-shadow:var(--acc-sh-accent); }
#admin-command-center .acc-tag{ font-family:var(--mono); font-variant-numeric:tabular-nums; font-size:10px; font-weight:700; padding:1px 7px; border-radius:99px; background:var(--surface-3); color:var(--tx2); }
#admin-command-center .acc-attn-tab.on .acc-tag{ background:rgba(255,255,255,.22); color:#fff; }
#admin-command-center .acc-attn-list{ padding:8px; }
#admin-command-center .acc-attn-row{ display:grid; grid-template-columns:40px minmax(0,1fr) auto; gap:12px; align-items:center;
padding:11px 10px; border-radius:var(--r); transition:background .14s; }
#admin-command-center .acc-attn-row+.acc-attn-row{ margin-top:1px; }
#admin-command-center .acc-attn-row:hover{ background:var(--surface-3); }
#admin-command-center .acc-sev{ width:40px; height:40px; border-radius:12px; display:grid; place-items:center; }
#admin-command-center .acc-sev.rose{ background:var(--acc-rose-50); } #admin-command-center .acc-sev.rose .acc-ic{ stroke:var(--acc-rose); }
#admin-command-center .acc-sev.amber{ background:var(--acc-amber-50); } #admin-command-center .acc-sev.amber .acc-ic{ stroke:var(--acc-amber); }
#admin-command-center .acc-attn-main{ min-width:0; }
#admin-command-center .acc-a-row1{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
#admin-command-center .acc-attn-main h4{ font-size:13.5px; font-weight:700; letter-spacing:-.01em; margin:0; font-family:var(--sans); }
#admin-command-center .acc-kind{ font-family:var(--mono); font-size:9.5px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; padding:2px 7px; border-radius:6px; }
#admin-command-center .acc-kind.rose{ background:var(--acc-rose-50); color:var(--acc-rose); }
#admin-command-center .acc-kind.amber{ background:var(--acc-amber-50); color:var(--acc-amber); }
#admin-command-center .acc-attn-meta{ font-size:11.5px; color:var(--tx3); margin-top:3px; }
#admin-command-center .acc-attn-meta .acc-mono{ color:var(--tx2); }
#admin-command-center .acc-attn-act{ display:inline-flex; align-items:center; gap:6px; height:32px; padding:0 13px; white-space:nowrap;
border:1px solid var(--border); border-radius:var(--r-sm); background:var(--surface); font-size:12px; font-weight:700; color:var(--tx2);
box-shadow:var(--acc-sh-xs); cursor:pointer; transition:border-color .14s,color .14s,background .14s; font-family:var(--sans); }
#admin-command-center .acc-attn-act:hover{ border-color:var(--acc-100); color:var(--acc-700); background:var(--acc-50); }
#admin-command-center .acc-attn-act.solid{ background:var(--acc); color:#fff; border-color:var(--acc); box-shadow:var(--acc-sh-accent); }
#admin-command-center .acc-attn-act.solid:hover{ background:var(--acc-600); color:#fff; }
#admin-command-center .acc-attn-foot{ display:flex; align-items:center; justify-content:space-between; gap:12px;
padding:12px 18px; border-top:1px solid var(--border); background:var(--surface-2); font-size:12px; color:var(--tx3); }
#admin-command-center .acc-attn-foot b{ color:var(--tx); font-weight:700; }
#admin-command-center .acc-attn-empty{ display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px;
padding:46px 16px; text-align:center; color:var(--tx3); }
#admin-command-center .acc-attn-empty .acc-ic{ stroke:var(--acc-green); width:30px; height:30px; margin-bottom:6px; }
#admin-command-center .acc-attn-empty b{ color:var(--tx); font-size:14px; }
#admin-command-center .acc-attn-empty span{ font-size:12.5px; }
/* feed */
#admin-command-center .acc-feed{ padding:6px 8px 8px; max-height:430px; overflow-y:auto; }
#admin-command-center .acc-feed-row{ display:grid; grid-template-columns:32px minmax(0,1fr) auto; gap:11px; align-items:center; padding:9px 10px; border-radius:var(--r); transition:background .14s; }
#admin-command-center .acc-feed-row:hover{ background:var(--surface-3); }
#admin-command-center .acc-feed-av{ width:32px; height:32px; border-radius:50%; display:grid; place-items:center; color:#fff; font-size:11px; font-weight:700; font-family:var(--sans); }
#admin-command-center .acc-feed-main{ min-width:0; }
#admin-command-center .acc-feed-main b{ font-size:13px; font-weight:700; display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
#admin-command-center .acc-f-meta{ font-size:11px; color:var(--tx3); margin-top:1px; }
#admin-command-center .acc-feed-right{ text-align:right; }
#admin-command-center .acc-feed-pct{ font-family:var(--display); font-size:14px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums; }
#admin-command-center .acc-feed-pct.hi{ color:var(--acc-green); } #admin-command-center .acc-feed-pct.mid{ color:var(--acc-amber); } #admin-command-center .acc-feed-pct.lo{ color:var(--acc-rose); }
#admin-command-center .acc-feed-ago{ font-size:10px; color:var(--tx4); font-family:var(--mono); font-variant-numeric:tabular-nums; margin-top:2px; }
/* subject mini */
#admin-command-center .acc-subj-mini{ padding:14px 18px; border-top:1px solid var(--border); background:var(--surface-2); }
#admin-command-center .acc-sm-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:9px; font-size:11.5px; color:var(--tx3); font-weight:700; }
#admin-command-center .acc-sm-head b{ font-family:var(--mono); font-variant-numeric:tabular-nums; color:var(--tx2); }
#admin-command-center .acc-subj-track{ height:9px; border-radius:5px; overflow:hidden; display:flex; background:var(--surface-3); margin-bottom:9px; }
#admin-command-center .acc-seg{ height:100%; transition:width .6s cubic-bezier(.22,.72,.28,1); }
#admin-command-center .acc-subj-legend{ display:flex; flex-wrap:wrap; gap:6px 13px; font-size:11px; color:var(--tx3); }
#admin-command-center .acc-subj-legend span{ display:inline-flex; align-items:center; gap:5px; }
#admin-command-center .acc-subj-legend b{ font-family:var(--mono); font-variant-numeric:tabular-nums; color:var(--tx2); font-weight:700; }
#admin-command-center .acc-subj-dot{ width:7px; height:7px; border-radius:50%; }
/* section title */
#admin-command-center .acc-sec-title{ display:flex; align-items:center; gap:9px; margin:6px 0 14px;
font-family:var(--sans); font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--tx3); }
#admin-command-center .acc-sec-title::before{ content:''; width:3px; height:13px; border-radius:99px; background:var(--grad-1); flex:0 0 auto; }
#admin-command-center .acc-ln{ flex:1; height:1px; background:linear-gradient(90deg,var(--border),transparent); }
/* health */
#admin-command-center .acc-health{ display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-bottom:26px; }
#admin-command-center .acc-hcard{ background:var(--surface); backdrop-filter:var(--blur); border:1.5px solid var(--border); border-radius:var(--r-lg); padding:16px 17px;
box-shadow:var(--acc-sh-xs); transition:transform .18s var(--ease-out),box-shadow .18s,border-color .18s; }
#admin-command-center .acc-hcard:hover{ transform:translateY(-3px); box-shadow:var(--acc-sh); border-color:var(--acc-100); }
#admin-command-center .acc-hcard-top{ display:flex; align-items:center; gap:9px; margin-bottom:11px; }
#admin-command-center .acc-hcard-ic{ width:30px; height:30px; border-radius:9px; display:grid; place-items:center; background:var(--acc-50); color:var(--acc); }
#admin-command-center .acc-hcard-ic .acc-ic{ stroke:currentColor; }
#admin-command-center .acc-hn{ font-family:var(--display); font-size:24px; font-weight:800; letter-spacing:-.02em; line-height:1; font-variant-numeric:tabular-nums; }
/* results */
#admin-command-center .acc-results{ display:grid; grid-template-columns:1fr 1fr; gap:20px; margin-bottom:26px; }
#admin-command-center .acc-rtable{ width:100%; border-collapse:collapse; }
#admin-command-center .acc-rtable th{ text-align:left; font-family:var(--sans); font-size:10px; text-transform:uppercase; letter-spacing:.06em;
color:var(--tx3); font-weight:700; padding:10px 15px; border-bottom:1px solid var(--border); background:none; position:static; }
#admin-command-center .acc-rtable th.r,#admin-command-center .acc-rtable td.r{ text-align:right; }
#admin-command-center .acc-rtable td{ padding:11px 15px; font-size:13px; border-bottom:1px solid var(--border); }
#admin-command-center .acc-rtable tr:last-child td{ border-bottom:none; }
#admin-command-center .acc-rtable tbody tr{ transition:background .12s; }
#admin-command-center .acc-rtable tbody tr:hover{ background:var(--surface-3); }
#admin-command-center .acc-rt-user{ display:flex; align-items:center; gap:9px; font-weight:700; }
#admin-command-center .acc-rt-av{ width:26px; height:26px; border-radius:50%; display:grid; place-items:center; color:#fff; font-size:10px; font-weight:700; flex:0 0 auto; }
#admin-command-center .acc-rt-subj{ color:var(--tx2); font-size:12.5px; }
#admin-command-center .acc-rt-score{ font-family:var(--mono); font-variant-numeric:tabular-nums; color:var(--tx3); font-size:12px; }
#admin-command-center .acc-rt-pct{ font-family:var(--display); font-weight:800; font-size:13px; font-variant-numeric:tabular-nums; }
#admin-command-center .acc-rt-pct.hi{ color:var(--acc-green); } #admin-command-center .acc-rt-pct.mid{ color:var(--acc-amber); } #admin-command-center .acc-rt-pct.lo{ color:var(--acc-rose); }
/* quick */
#admin-command-center .acc-quick{ display:grid; grid-template-columns:repeat(6,1fr); gap:14px; }
#admin-command-center .acc-qbtn{ display:flex; flex-direction:column; gap:11px; padding:16px 15px; background:var(--surface); backdrop-filter:var(--blur);
border:1.5px solid var(--border); border-radius:var(--r-lg); box-shadow:var(--acc-sh-xs); text-align:left; cursor:pointer;
transition:transform .16s var(--ease-out),box-shadow .16s,border-color .16s; font-family:var(--sans); }
#admin-command-center .acc-qbtn:hover{ transform:translateY(-3px); box-shadow:var(--acc-sh); border-color:var(--acc-100); }
#admin-command-center .acc-qbtn-ic{ width:36px; height:36px; border-radius:10px; display:grid; place-items:center; background:var(--acc-50); color:var(--acc); }
#admin-command-center .acc-qbtn-ic .acc-ic{ stroke:currentColor; }
#admin-command-center .acc-qbtn b{ font-size:13px; font-weight:700; letter-spacing:-.01em; }
#admin-command-center .acc-qbtn span{ font-size:11px; color:var(--tx3); font-family:var(--mono); }
/* foot */
#admin-command-center .acc-foot{ margin-top:28px; display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:10px;
font-size:12px; color:var(--tx4); font-family:var(--mono); }
/* skeleton */
@keyframes acc-shimmer{ 0%{ background-position:-400px 0 } 100%{ background-position:400px 0 } }
#admin-command-center .acc-skel{ border-radius:var(--r-lg); border:1.5px solid var(--border);
background:linear-gradient(90deg,var(--surface-3) 25%,var(--surface) 50%,var(--surface-3) 75%); background-size:400px 100%;
animation:acc-shimmer 1.4s infinite linear; }
#admin-command-center .acc-skel-cards{ display:grid; grid-template-columns:1.4fr 1fr 1fr 1fr; gap:16px; margin-bottom:24px; }
#admin-command-center .acc-skel-cards .acc-skel{ height:118px; }
#admin-command-center .acc-skel-rows{ display:flex; flex-direction:column; gap:12px; }
#admin-command-center .acc-skel.row{ height:60px; }
/* responsive */
@media (max-width:1160px){
#admin-command-center .acc-grid{ grid-template-columns:1fr; }
#admin-command-center .acc-pulse{ grid-template-columns:1fr 1fr; }
#admin-command-center .acc-kpi.hero{ grid-column:span 2; }
#admin-command-center .acc-quick{ grid-template-columns:repeat(3,1fr); }
#admin-command-center .acc-health{ grid-template-columns:1fr 1fr; }
}
@media (max-width:760px){
#admin-command-center .acc-pulse{ grid-template-columns:1fr; }
#admin-command-center .acc-kpi.hero{ grid-column:auto; }
#admin-command-center .acc-results{ grid-template-columns:1fr; }
#admin-command-center .acc-quick{ grid-template-columns:1fr 1fr; }
#admin-command-center .acc-page-head h1{ font-size:25px; }
#admin-command-center .acc-clock{ display:none; }
}
`;
})();