Files
AIRegulation-DocAnalysis/Prototype/dashboard-sidebar.html

710 lines
32 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>System Status — T-Systems Regulation Hub</title>
<style>
/* ─── Design tokens ─────────────────────────────────────────────── */
:root {
/* Sidebar (light rail) */
--rail-bg: #ffffff;
--rail-surface: #f7f8fa;
--rail-fg: #111827;
--rail-muted: #8b929e;
--rail-border: #e8eaed;
--rail-hover: rgba(0,0,0,.04);
--rail-active: rgba(226,0,116,.07);
/* Canvas (content area) */
--bg: #f2f4f7;
--surface: #ffffff;
--fg: #111827;
--muted: #6b7280;
--border: #e5e7eb;
/* Brand */
--accent: #e20074;
--accent-dim: rgba(226,0,116,.10);
--accent-hover: #c8006a;
--success: #16a34a;
--warn: #d97706;
--danger: #dc2626;
/* Type */
--font-display: "TeleNeoWeb-Bold","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
--font-body: "TeleNeoWeb-Regular","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
--font-mono: ui-monospace,"JetBrains Mono",Menlo,monospace;
--radius-sm: 6px;
--radius-md: 10px;
--radius-pill: 9999px;
--sidebar-w: 232px;
--shadow-card: 0 1px 4px rgba(0,0,0,.06), 0 0 0 1px rgba(0,0,0,.04);
--shadow-sm: 0 1px 2px rgba(0,0,0,.05);
}
/* ─── Reset ────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; }
html, body { height: 100%; }
body {
background: var(--bg);
color: var(--fg);
font-family: var(--font-body);
font-size: 14px;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
h1,h2,h3,h4 { font-family: var(--font-display); line-height: 1.2; letter-spacing: -0.015em; text-wrap: balance; }
a { color: inherit; text-decoration: none; }
button, input, select { font: inherit; cursor: pointer; }
/* ─── App shell ─────────────────────────────────────────────────── */
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
/* ─── Sidebar ───────────────────────────────────────────────────── */
.sidebar {
position: sticky; top: 0; height: 100vh;
overflow-y: auto; overflow-x: hidden;
display: flex; flex-direction: column;
background: var(--rail-bg);
border-right: 1px solid var(--rail-border);
z-index: 20;
}
.sidebar-brand {
display: flex; align-items: center; gap: 10px;
height: 54px; padding: 0 16px;
border-bottom: 1px solid var(--rail-border);
flex-shrink: 0;
}
.brand-logo {
width: 28px; height: 28px;
background: var(--accent);
border-radius: 7px;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
}
.brand-logo svg { color: #fff; }
.brand-name { font-family: var(--font-display); font-size: 13px; color: var(--rail-fg); line-height: 1.2; font-weight: 700; }
.brand-sub { font-size: 10px; color: var(--rail-muted); font-family: var(--font-mono); letter-spacing: .04em; margin-top: 2px; }
.sidebar-nav { flex: 1; padding: 10px 0; }
.nav-group { padding: 0 8px 4px; }
.nav-group + .nav-group { margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--rail-border); }
.nav-group-label {
display: block;
font-family: var(--font-mono); font-size: 10px;
text-transform: uppercase; letter-spacing: .12em;
color: var(--rail-muted);
padding: 0 8px 6px;
}
.nav-item {
display: flex; align-items: center; gap: 9px;
height: 34px; padding: 0 8px;
border-radius: var(--radius-sm);
color: #4b5563;
font-size: 13px;
transition: background 120ms, color 120ms;
position: relative;
}
.nav-item:hover { background: var(--rail-hover); color: var(--rail-fg); text-decoration: none; }
.nav-item.active {
background: var(--rail-active);
color: var(--accent);
font-weight: 600;
}
.nav-item.active::before {
content: "";
position: absolute; left: 0; top: 6px; bottom: 6px;
width: 3px; border-radius: 0 3px 3px 0;
background: var(--accent);
}
.nav-icon { width: 15px; height: 15px; flex-shrink: 0; opacity: .55; }
.nav-item:hover .nav-icon,
.nav-item.active .nav-icon { opacity: 1; }
.nav-badge {
margin-left: auto;
min-width: 18px; height: 17px; padding: 0 5px;
border-radius: var(--radius-pill);
background: var(--accent-dim); color: var(--accent);
font-size: 10px; font-family: var(--font-mono); font-weight: 700;
display: flex; align-items: center; justify-content: center;
}
.sidebar-footer {
border-top: 1px solid var(--rail-border);
padding: 10px 8px;
flex-shrink: 0;
}
.sidebar-user {
display: flex; align-items: center; gap: 9px;
padding: 8px; border-radius: var(--radius-sm);
cursor: pointer;
transition: background 120ms;
}
.sidebar-user:hover { background: var(--rail-hover); }
.avatar {
width: 28px; height: 28px;
border-radius: 50%;
background: var(--accent); color: #fff;
font-size: 11px; font-weight: 700;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
}
.user-name { font-size: 12px; font-weight: 600; color: var(--rail-fg); }
.user-role { font-size: 10px; color: var(--rail-muted); font-family: var(--font-mono); margin-top: 1px; }
.sidebar-action {
display: flex; align-items: center; gap: 9px;
height: 32px; padding: 0 8px;
border-radius: var(--radius-sm);
color: var(--rail-muted); font-size: 12px;
border: none; background: transparent; width: 100%;
text-align: left;
transition: background 120ms, color 120ms;
}
.sidebar-action:hover { background: var(--rail-hover); color: var(--rail-fg); }
/* ─── Content area ──────────────────────────────────────────────── */
.content-area { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
.topbar {
position: sticky; top: 0; z-index: 10;
display: flex; align-items: center; gap: 10px;
height: 54px; padding: 0 24px;
border-bottom: 1px solid var(--border);
background: rgba(242,244,247,.9);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
flex-shrink: 0;
}
.topbar-title { font-weight: 600; font-size: 14px; flex: 1; color: var(--fg); }
.search {
display: flex; align-items: center; gap: 8px;
border: 1px solid var(--border); border-radius: var(--radius-sm);
background: var(--surface);
padding: 0 10px; height: 32px; width: 240px;
transition: border-color 140ms;
}
.search:focus-within { border-color: color-mix(in oklab, var(--accent), transparent 60%); }
.search input { border: 0; outline: none; background: transparent; width: 100%; color: var(--fg); font-size: 13px; }
.search input::placeholder { color: var(--muted); }
.btn {
height: 32px; border-radius: var(--radius-sm);
border: 1px solid var(--border);
padding: 0 12px;
background: var(--surface); color: var(--fg);
font-size: 13px; font-weight: 500;
cursor: pointer;
transition: background 120ms, border-color 120ms;
white-space: nowrap;
display: inline-flex; align-items: center; gap: 6px;
}
.btn:hover { background: var(--bg); border-color: #b0b7c0; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* ─── Page ──────────────────────────────────────────────────────── */
.page { padding: 20px; display: grid; gap: 18px; flex: 1; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; }
.eyebrow {
font-family: var(--font-mono); font-size: 10px;
text-transform: uppercase; letter-spacing: .1em;
color: var(--accent); margin-bottom: 6px;
}
.page-head h1 { font-size: clamp(20px, 2.4vw, 28px); }
.page-head-desc { margin-top: 4px; font-size: 13px; color: var(--muted); max-width: 520px; line-height: 1.55; }
/* ─── Cards ─────────────────────────────────────────────────────── */
.card {
background: var(--surface);
border-radius: var(--radius-md);
box-shadow: var(--shadow-card);
padding: 16px 18px;
}
/* ─── Stats row ─────────────────────────────────────────────────── */
.stats-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 12px; }
.stat-card {
border-top: 2px solid var(--border);
transition: border-color 200ms;
}
.stat-card:hover { border-top-color: var(--accent); }
.stat-card .s-label {
font-family: var(--font-mono); font-size: 10px;
text-transform: uppercase; letter-spacing: .1em; color: var(--muted);
}
.stat-card .s-value {
margin-top: 10px;
font-size: 32px; line-height: 1;
font-family: var(--font-display);
font-variant-numeric: tabular-nums;
letter-spacing: -0.02em;
color: var(--fg);
}
.stat-card .s-sub { margin-top: 8px; font-size: 11px; color: var(--muted); line-height: 1.5; }
/* ─── Two-column panel grid ─────────────────────────────────────── */
.panel-grid { display: grid; grid-template-columns: 1.4fr 0.9fr; gap: 18px; }
.stack { display: grid; gap: 18px; }
/* ─── Section heads ─────────────────────────────────────────────── */
.section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.section-head h2 { font-size: 16px; }
.ghost-link {
color: var(--muted); font-size: 12px;
padding: 2px 0; border-radius: 4px;
transition: color 120ms;
}
.ghost-link:hover { color: var(--fg); text-decoration: none; }
/* ─── Task rows ─────────────────────────────────────────────────── */
.task-list, .program-list, .event-list { display: grid; gap: 8px; }
.task-row, .program-row, .event-row {
display: grid; gap: 10px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 10px 12px;
background: var(--surface);
transition: border-color 120ms, box-shadow 120ms;
}
.task-row:hover, .program-row:hover {
border-color: #c8cdd8;
box-shadow: var(--shadow-sm);
}
.task-row { grid-template-columns: 1.6fr 0.8fr 0.8fr 0.6fr; align-items: center; }
.program-row{ grid-template-columns: 1fr auto; align-items: start; }
.event-row { grid-template-columns: 76px 1fr; align-items: start; }
/* ─── KPI strip ─────────────────────────────────────────────────── */
.kpi-strip { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; margin-top: 12px; }
.kpi {
border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 10px 12px; background: #f8f9fb;
}
.kpi strong { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 17px; color: var(--fg); }
.kpi-label { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.meter { height: 3px; border-radius: 999px; background: #e5e7eb; overflow: hidden; margin-top: 8px; }
.meter > span { display: block; height: 100%; background: var(--accent); border-radius: inherit; }
/* ─── Dark mode ────────────────────────────────────────────────── */
[data-theme="dark"] {
--rail-bg: #1a1c22;
--rail-surface: #22242c;
--rail-fg: #f0f2f5;
--rail-muted: #7a8390;
--rail-border: #2d3038;
--rail-hover: rgba(255,255,255,.05);
--rail-active: rgba(226,0,116,.12);
--bg: #111318;
--surface: #1a1c22;
--fg: #f0f2f5;
--muted: #7a8390;
--border: #2d3038;
}
[data-theme="dark"] body { color-scheme: dark; }
[data-theme="dark"] .topbar { background: rgba(17,19,24,.9); }
[data-theme="dark"] .kpi { background: #1e2028; }
[data-theme="dark"] .task-row, [data-theme="dark"] .program-row, [data-theme="dark"] .event-row { background: #1e2028; }
/* ─── Status pills ──────────────────────────────────────────────── */
.status {
display: inline-flex; align-items: center; gap: 5px;
padding: 2px 8px; border-radius: var(--radius-pill);
font-size: 11px; font-family: var(--font-mono); font-weight: 600;
width: fit-content; white-space: nowrap;
}
.status::before {
content: ""; width: 5px; height: 5px;
border-radius: 50%; background: currentColor; flex-shrink: 0;
}
.status.ok { color: var(--success); background: color-mix(in oklab,var(--success),transparent 90%); }
.status.warn { color: var(--warn); background: color-mix(in oklab,var(--warn),transparent 90%); }
.status.risk { color: var(--danger); background: color-mix(in oklab,var(--danger),transparent 90%); }
/* ─── Pill (version tag) ────────────────────────────────────────── */
.pill {
display: inline-flex; align-items: center;
height: 20px; padding: 0 8px;
border-radius: var(--radius-pill);
border: 1px solid var(--border);
color: var(--muted); font-size: 10px;
font-family: var(--font-mono); font-weight: 500;
}
/* ─── Task CTA buttons ──────────────────────────────────────────── */
.task-cta {
display: inline-flex; align-items: center; gap: 4px;
height: 26px; padding: 0 10px;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
background: transparent; color: var(--muted);
font-size: 11px; font-family: var(--font-body);
white-space: nowrap; cursor: pointer;
transition: border-color 120ms, color 120ms, background 120ms;
}
.task-cta:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); text-decoration: none; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 12px; }
.note { font-size: 12px; color: var(--muted); line-height: 1.5; }
/* ─── Footer ────────────────────────────────────────────────────── */
.footer {
display: flex; align-items: center; justify-content: space-between; gap: 16px;
min-height: 34px; padding: 0 20px;
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 10px; font-family: var(--font-mono);
letter-spacing: .1em; text-transform: uppercase;
flex-shrink: 0;
}
.footer-live { display: inline-flex; align-items: center; gap: 7px; }
.footer-dot {
width: 6px; height: 6px; border-radius: 50%;
background: #22c55e;
box-shadow: 0 0 0 3px rgba(34,197,94,.2);
}
/* ─── Responsive ────────────────────────────────────────────────── */
@media (max-width: 1200px) {
.stats-grid { grid-template-columns: repeat(2,1fr); }
.panel-grid { grid-template-columns: 1fr; }
.kpi-strip { grid-template-columns: 1fr 1fr; }
.task-row { grid-template-columns: 1fr auto; }
}
@media (max-width: 700px) {
.app-shell { grid-template-columns: 1fr; }
.sidebar { display: none; }
.stats-grid, .kpi-strip { grid-template-columns: 1fr 1fr; }
}
</style>
</head>
<body>
<div class="app-shell">
<!-- ─── Sidebar ─────────────────────────────────────────────────────── -->
<aside class="sidebar" aria-label="Primary navigation">
<div class="sidebar-brand">
<div class="brand-logo">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<path d="M2 3.5h12v1.5H2zm5.25 1.5h1.5v8h-1.5zm-3 2h2.25v1.5H4.25zm5.25 0h2.25v1.5H9.5zm-3 3h2.5v1.5H6.5z" fill="currentColor"/>
</svg>
</div>
<div>
<div class="brand-name">T-Systems</div>
<div class="brand-sub">Regulation Hub</div>
</div>
</div>
<nav class="sidebar-nav" aria-label="Primary">
<div class="nav-group">
<span class="nav-group-label">Main</span>
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/index.html">
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
<path d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z" fill="currentColor" opacity=".6"/>
</svg>
Overview
</a>
<a class="nav-item" href="perception.html">
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
<circle cx="8" cy="8" r="2.5" fill="currentColor"/>
<path d="M8 2.5C4.91 2.5 2.5 5.42 2.5 8S4.91 13.5 8 13.5 13.5 10.58 13.5 8 11.09 2.5 8 2.5zm0 9.5C5.52 12 3.5 10.24 3.5 8S5.52 4 8 4s4.5 1.76 4.5 4-2.02 4-4.5 4z" fill="currentColor" opacity=".45"/>
</svg>
Regulatory Signals
<span class="nav-badge">6</span>
</a>
<a class="nav-item active" href="dashboard-sidebar.html">
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
<path d="M1.5 2.5h13v1H1.5zm0 3h13v1H1.5zm0 3h8v1h-8zm0 3h6v1h-6z" fill="currentColor"/>
</svg>
System Status
<span class="nav-badge">3</span>
</a>
</div>
<div class="nav-group">
<span class="nav-group-label">Workbench</span>
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-management.html">
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
<path d="M3 1h7l3 3v11H3V1zm1 1v12h8V5h-3V2H4zm5 .5V4h1.5L9 1.5zM6 7h4v1H6zm0 2h4v1H6zm0 2h3v1H6z" fill="currentColor"/>
</svg>
Documents
</a>
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/compliance-analysis.html">
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
<path d="M8 1l7 3-1 6a7 7 0 01-6 5A7 7 0 011 10L0 4l8-3zm0 1.2L1.3 4.8l.8 5.1A6 6 0 008 14.8a6 6 0 005.9-4.9l.8-5.1L8 2.2zM7.5 5h1v4.5h-1V5zm0 5.5h1v1h-1v-1z" fill="currentColor"/>
</svg>
Compliance Analysis
</a>
</div>
<div class="nav-group">
<span class="nav-group-label">Chat</span>
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/regulation-chat.html">
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
<path d="M2 2h12a1 1 0 011 1v8a1 1 0 01-1 1H5l-3 2.5V3a1 1 0 011-1zm0 1v9.5L4.5 11H14V3H2zm2 2h8v1H4zm0 2h6v1H4z" fill="currentColor"/>
</svg>
Regulation Q&amp;A
</a>
</div>
</nav>
<div class="sidebar-footer">
<div class="sidebar-user">
<div class="avatar">TS</div>
<div>
<div class="user-name">T-Systems User</div>
<div class="user-role">Compliance Analyst</div>
</div>
</div>
<button class="sidebar-action" type="button" onclick="toggleTheme()">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none">
<path d="M8 3a5 5 0 100 10A5 5 0 008 3zM2 8a6 6 0 1112 0A6 6 0 012 8z" fill="currentColor"/>
</svg>
<span>Dark mode</span>
</button>
</div>
</aside>
<!-- ─── Content ──────────────────────────────────────────────────────── -->
<div class="content-area">
<header class="topbar">
<span class="topbar-title">System Status</span>
<div class="search">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
<path d="M6.5 1a5.5 5.5 0 014.23 9.02l3.62 3.62-.7.71-3.63-3.63A5.5 5.5 0 116.5 1zm0 1a4.5 4.5 0 100 9 4.5 4.5 0 000-9z" fill="currentColor" opacity=".4"/>
</svg>
<input type="search" placeholder="Search regulations, documents…" aria-label="Search" />
</div>
<button class="btn">Export status</button>
<a class="btn btn-primary" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/upload-modal.html">New upload</a>
</header>
<main class="page">
<!-- Page head -->
<section class="page-head">
<div>
<div class="eyebrow">System Status</div>
<h1>System Status</h1>
<p class="page-head-desc">Ingestion pipeline, active compliance programs, and regulatory watch — all in one place.</p>
</div>
<span class="pill">v1.0.0</span>
</section>
<!-- Stats -->
<section class="stats-grid">
<article class="card stat-card">
<div class="s-label">Documents total</div>
<div class="s-value mono" id="ds-docs"></div>
<div class="s-sub">Ingested into the knowledge base</div>
</article>
<article class="card stat-card">
<div class="s-label">Vector chunks</div>
<div class="s-value mono" id="ds-chunks"></div>
<div class="s-sub">regulations_dense_1024_v2 serving retrieval</div>
</article>
<article class="card stat-card">
<div class="s-label">High-impact signals</div>
<div class="s-value mono" id="ds-high"></div>
<div class="s-sub">Regulatory signals requiring immediate review</div>
</article>
<article class="card stat-card">
<div class="s-label">Last 90 days</div>
<div class="s-value mono" id="ds-90d"></div>
<div class="s-sub">Recent regulatory publications</div>
</article>
</section>
<!-- Two-column panels -->
<section class="panel-grid">
<!-- Left column -->
<div class="stack">
<article class="card">
<div class="section-head">
<h2>Workflow queue</h2>
<a class="ghost-link" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-management.html">Open documents &rarr;</a>
</div>
<div class="task-list">
<div class="task-row">
<div>
<strong>GB/T 31484-2015 battery density revision</strong>
<div class="note">Uploaded by EV Safety Team · version 2026-04 addendum</div>
</div>
<span class="status warn">Embedding</span>
<span class="mono">chunk build active</span>
<a class="task-cta" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-detail.html">Inspect &rarr;</a>
</div>
<div class="task-row">
<div>
<strong>UNECE R155 annex interpretation note</strong>
<div class="note">Parser artifacts ready · waiting for analyst assignment</div>
</div>
<span class="status ok">Ready</span>
<span class="mono">19 clauses linked</span>
<a class="task-cta" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/compliance-analysis.html">Analyze &rarr;</a>
</div>
<div class="task-row">
<div>
<strong>GB 26112-2010 roof strength scan</strong>
<div class="note">OCR confidence dropped below threshold on 6 pages</div>
</div>
<span class="status risk">Failed</span>
<span class="mono">Retry #2</span>
<a class="task-cta" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-management.html">Resolve &rarr;</a>
</div>
</div>
</article>
<article class="card">
<div class="section-head">
<h2>Active compliance programs</h2>
<a class="ghost-link" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/compliance-analysis.html">Review findings &rarr;</a>
</div>
<div class="program-list">
<div class="program-row">
<div>
<strong>Intelligent cockpit homologation</strong>
<p class="note">42 related standards across driver monitoring, EMC, and child safety. Four findings still open for MY27 platform.</p>
</div>
<span class="status risk">High risk</span>
</div>
<div class="program-row">
<div>
<strong>Battery swap certification dossier</strong>
<p class="note">Clause mapping complete. Thermal event test evidence package awaiting supplier document refresh.</p>
</div>
<span class="status warn">Pending</span>
</div>
<div class="program-row">
<div>
<strong>Connected fleet cybersecurity</strong>
<p class="note">RAG checks aligned with UNECE R155. Chat follow-up requested on remote key rotation obligations.</p>
</div>
<span class="status ok">On track</span>
</div>
</div>
<div class="kpi-strip">
<div class="kpi">
<div class="kpi-label">Retrieval hit rate</div>
<strong>87%</strong>
<div class="meter"><span style="width:87%"></span></div>
</div>
<div class="kpi">
<div class="kpi-label">Evidence coverage</div>
<strong>72%</strong>
<div class="meter"><span style="width:72%"></span></div>
</div>
<div class="kpi">
<div class="kpi-label">Reviewer SLA</div>
<strong>18h</strong>
<div class="meter"><span style="width:64%"></span></div>
</div>
</div>
</article>
</div>
<!-- Right column -->
<div class="stack">
<article class="card">
<div class="section-head"><h2>System health</h2><a class="ghost-link" href="#">Refresh</a></div>
<div class="task-list">
<div class="task-row" style="grid-template-columns:1fr auto">
<div>
<strong>Aliyun parser backend</strong>
<div class="note">Poll interval 5 s · timeout 900 s</div>
</div>
<span class="status warn">Queue depth 7</span>
</div>
<div class="task-row" style="grid-template-columns:1fr auto">
<div>
<strong>Embedding model</strong>
<div class="note">text-embedding-v3 · dimension 1024</div>
</div>
<span class="status ok">Healthy</span>
</div>
<div class="task-row" style="grid-template-columns:1fr auto">
<div>
<strong>Vector store</strong>
<div class="note">Milvus regulations_dense_1024_v2</div>
</div>
<span class="status ok">Serving</span>
</div>
</div>
</article>
<article class="card">
<div class="section-head">
<h2>Regulatory watch</h2>
<a class="ghost-link" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/regulation-chat.html">Ask chat &rarr;</a>
</div>
<div class="event-list">
<div class="event-row">
<span class="mono note">2d ago</span>
<div>
<strong>GB 38031 thermal propagation draft updated</strong>
<p class="note" style="-webkit-line-clamp:2;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden">Potential impact on current battery enclosure narrative. Evidence gap flagged in two supplier submissions.</p>
</div>
</div>
<div class="event-row">
<span class="mono note">5d ago</span>
<div>
<strong>UNECE R155 Q&amp;A added note on incident response logs</strong>
<p class="note" style="-webkit-line-clamp:2;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden">Connected fleet program must confirm retention windows and ownership controls.</p>
</div>
</div>
<div class="event-row">
<span class="mono note">12d ago</span>
<div>
<strong>GB/T 18487 charging interface interpretation circulated</strong>
<p class="note" style="-webkit-line-clamp:2;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden">No blocker yet, but three documents should be re-run against the new clause wording.</p>
</div>
</div>
</div>
</article>
</div>
</section>
</main>
<footer class="footer">
<span>T-Systems Regulation Hub</span>
<div class="footer-live">
<span class="footer-dot" aria-hidden="true"></span>
<span>Online</span>
</div>
</footer>
</div>
</div>
<script>
// ─── Theme toggle (P0 fix: reads data-theme attribute) ───────────────
function toggleTheme() {
const html = document.documentElement;
const next = html.dataset.theme === 'dark' ? 'light' : 'dark';
html.dataset.theme = next;
localStorage.setItem('theme', next);
const btn = document.querySelector('.sidebar-action');
if (btn) btn.querySelector('span') && (btn.querySelector('span').textContent = next === 'dark' ? 'Light mode' : 'Dark mode');
}
// Restore saved theme
(function() {
const saved = localStorage.getItem('theme');
if (saved) document.documentElement.dataset.theme = saved;
})();
// ─── Live stats from perception API ──────────────────────────────────
async function loadDashboardStats() {
try {
const r = await fetch('http://6.86.80.9:5173/api/v1/perception/stats');
if (!r.ok) return;
const s = await r.json();
const set = (id, val) => { const el = document.getElementById(id); if (el && val != null) el.textContent = val; };
set('ds-high', s.high_impact);
set('ds-90d', s.recent_90d);
set('ds-docs', s.total);
} catch(e) { /* silent — fallback to — */ }
}
loadDashboardStats();
</script>
</body>
</html>