Files
cameleer-server/docs/ui-mocks/mock-v3-metrics-dashboard.html

2178 lines
91 KiB
HTML
Raw Normal View History

<!--
============================================================================
CAMELEER3 v3 — Metrics & KPI Dashboard (Light Theme)
============================================================================
DESIGN NOTES
============
PURPOSE:
A dedicated KPI/Metrics page focused on system-wide and per-route performance
metrics. Separate from the transaction list (v2), this page provides at-a-glance
system health, per-route performance comparison, and time-series trend analysis.
LAYOUT:
- Same 220px warm charcoal sidebar as v2 (with "Metrics" active)
- Same 48px topbar with breadcrumb: Dashboard > Metrics
- Scrollable main content with:
1. Time range selector bar + auto-refresh indicator
2. System Health KPI cards (5 large cards)
3. Per-Route Performance Table with sparklines
4. 2x2 Time-Series Chart grid (SVG, no libraries)
5. Live Event Feed panel (right rail, optional)
DESIGN SYSTEM (identical to v2-light):
- Surface: warm parchment #F5F2ED, cards #FFFFFF
- Sidebar: warm charcoal #2C2520
- Brand accent: amber-gold #C6820E
- Typography: DM Sans body + JetBrains Mono data
- Status: olive green #3D7C47, burnt orange #C27516, terracotta #C0392B, teal #1A7F8E
- Shadows, radii, border styles all from v2
DATA STORY:
- order-processing: high throughput, low latency, healthy
- payment-flow: moderate throughput, HIGHER latency (payment gateway), occasional errors
- shipment-notify: steady, fast
- inventory-check: fast, low volume
- customer-validation: moderate, stable
- email-notification: low volume, very fast
- retry-handler: LOW throughput but HIGH error rate (by design, retries failed messages)
- dead-letter-processor: very low volume, 100% "errors" (receives poison pills)
CHARTS:
All SVG inline, no external libraries. Sparklines in KPI cards and table.
Four large charts: throughput area, latency lines, error bars, volume heatmap.
============================================================================
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1920">
<title>Cameleer3 — Metrics Dashboard</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
/* ==========================================================================
RESET & FOUNDATIONS (identical to v2)
========================================================================== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
/* Surface palette (warm parchment) */
--bg-body: #F5F2ED;
--bg-surface: #FFFFFF;
--bg-raised: #FAF8F5;
--bg-inset: #F0EDE8;
--bg-hover: #F5F0EA;
/* Sidebar (warm charcoal) */
--sidebar-bg: #2C2520;
--sidebar-hover: #3A322C;
--sidebar-active: #4A3F38;
--sidebar-text: #BFB5A8;
--sidebar-muted: #7A6F63;
/* Text */
--text-primary: #1A1612;
--text-secondary: #5C5347;
--text-muted: #9C9184;
--text-faint: #C4BAB0;
/* Borders */
--border: #E4DFD8;
--border-subtle: #EDE9E3;
/* Brand accent (amber-gold) */
--amber: #C6820E;
--amber-light: #F0D9A8;
--amber-bg: #FDF6E9;
--amber-deep: #8B5A06;
/* Status colors (warm) */
--success: #3D7C47;
--success-bg: #EFF7F0;
--success-border: #C2DFC6;
--warning: #C27516;
--warning-bg: #FEF5E7;
--warning-border: #F0D9A8;
--error: #C0392B;
--error-bg: #FDF0EE;
--error-border: #F0C4BE;
--running: #1A7F8E;
--running-bg: #E8F5F7;
--running-border: #B0DDE4;
/* Typography */
--font-body: 'DM Sans', system-ui, -apple-system, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
/* Spacing & Radii */
--radius-sm: 5px;
--radius-md: 8px;
--radius-lg: 12px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(44, 37, 32, 0.06);
--shadow-md: 0 2px 8px rgba(44, 37, 32, 0.08);
--shadow-lg: 0 4px 16px rgba(44, 37, 32, 0.10);
--shadow-card: 0 1px 3px rgba(44, 37, 32, 0.04), 0 0 0 1px rgba(44, 37, 32, 0.04);
/* Chart palette (warm spectrum) */
--chart-1: #C6820E; /* amber */
--chart-2: #3D7C47; /* olive green */
--chart-3: #1A7F8E; /* teal */
--chart-4: #C27516; /* burnt orange */
--chart-5: #8B5A06; /* deep amber */
--chart-6: #6B8E4E; /* sage */
--chart-7: #C0392B; /* terracotta */
--chart-8: #9C7A3C; /* gold */
}
html { font-size: 14px; }
body {
font-family: var(--font-body);
background: var(--bg-body);
color: var(--text-primary);
line-height: 1.5;
min-height: 100vh;
-webkit-font-smoothing: antialiased;
}
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
/* ==========================================================================
ANIMATIONS
========================================================================== */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(61, 124, 71, 0.35); }
50% { box-shadow: 0 0 0 5px rgba(61, 124, 71, 0); }
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.animate-in { animation: fadeIn 0.3s ease-out both; }
.delay-1 { animation-delay: 0.04s; }
.delay-2 { animation-delay: 0.08s; }
.delay-3 { animation-delay: 0.12s; }
.delay-4 { animation-delay: 0.16s; }
.delay-5 { animation-delay: 0.20s; }
/* ==========================================================================
LAYOUT
========================================================================== */
.app {
display: flex;
height: 100vh;
overflow: hidden;
}
/* ==========================================================================
SIDEBAR (220px, warm charcoal — identical structure to v2)
========================================================================== */
.sidebar {
width: 220px;
flex-shrink: 0;
background: var(--sidebar-bg);
display: flex;
flex-direction: column;
overflow: hidden;
}
.sidebar-logo {
padding: 16px 18px;
display: flex;
align-items: center;
gap: 10px;
border-bottom: 1px solid rgba(255,255,255,0.06);
}
.sidebar-logo .brand {
font-family: var(--font-mono);
font-weight: 600;
font-size: 15px;
color: var(--amber-light);
letter-spacing: -0.3px;
}
.sidebar-logo .version {
font-family: var(--font-mono);
font-size: 10px;
color: var(--sidebar-muted);
margin-left: 2px;
}
.sidebar-search { padding: 10px 12px; }
.sidebar-search input {
width: 100%;
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.08);
border-radius: var(--radius-sm);
padding: 6px 10px 6px 28px;
color: var(--sidebar-text);
font-family: var(--font-body);
font-size: 12px;
outline: none;
transition: border-color 0.15s;
}
.sidebar-search input::placeholder { color: var(--sidebar-muted); }
.sidebar-search input:focus { border-color: rgba(198, 130, 14, 0.4); }
.sidebar-search-wrap { position: relative; }
.sidebar-search-wrap .search-icon {
position: absolute;
left: 9px;
top: 50%;
transform: translateY(-50%);
color: var(--sidebar-muted);
font-size: 12px;
}
.sidebar-section {
padding: 14px 12px 5px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1.2px;
color: var(--sidebar-muted);
}
.sidebar-items {
flex: 1;
overflow-y: auto;
padding: 0 6px;
}
.sidebar-item {
display: flex;
align-items: center;
gap: 10px;
padding: 7px 12px;
border-radius: var(--radius-sm);
color: var(--sidebar-text);
font-size: 13px;
cursor: pointer;
transition: all 0.12s;
text-decoration: none;
border-left: 3px solid transparent;
margin-bottom: 1px;
}
.sidebar-item:hover { background: var(--sidebar-hover); color: #E8DFD4; }
.sidebar-item.active { background: var(--sidebar-active); color: var(--amber-light); border-left-color: var(--amber); }
.sidebar-item .health {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
.health-live { background: #5DB866; box-shadow: 0 0 6px rgba(93, 184, 102, 0.4); }
.health-stale { background: var(--warning); }
.health-dead { background: var(--sidebar-muted); }
.sidebar-item .item-info { flex: 1; min-width: 0; }
.sidebar-item .item-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar-item .item-meta { font-size: 11px; color: var(--sidebar-muted); font-family: var(--font-mono); }
.sidebar-item .item-count {
font-family: var(--font-mono);
font-size: 11px;
color: var(--sidebar-muted);
background: rgba(255,255,255,0.06);
padding: 1px 6px;
border-radius: 10px;
}
.sidebar-item.active .item-count { background: rgba(198, 130, 14, 0.2); color: var(--amber-light); }
.sidebar-divider {
height: 1px;
background: rgba(255,255,255,0.06);
margin: 6px 12px;
}
/* Agent health in sidebar */
.sidebar-agents-header {
padding: 14px 12px 6px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1.2px;
color: var(--sidebar-muted);
display: flex;
align-items: center;
justify-content: space-between;
}
.sidebar-agent-badge {
font-family: var(--font-mono);
font-size: 10px;
padding: 1px 6px;
border-radius: 10px;
background: rgba(93, 184, 102, 0.15);
color: #5DB866;
}
.agent-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
margin: 0 6px 2px;
border-radius: var(--radius-sm);
font-size: 11px;
color: var(--sidebar-text);
transition: background 0.1s;
}
.agent-item:hover { background: var(--sidebar-hover); }
.agent-dot {
width: 6px;
height: 6px;
border-radius: 50%;
flex-shrink: 0;
}
.agent-info { flex: 1; min-width: 0; }
.agent-name { font-family: var(--font-mono); font-size: 11px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.agent-detail { font-size: 10px; color: var(--sidebar-muted); }
.agent-stats-col {
text-align: right;
font-family: var(--font-mono);
font-size: 10px;
color: var(--sidebar-muted);
}
.agent-tps { color: var(--sidebar-text); }
.sidebar-bottom {
border-top: 1px solid rgba(255,255,255,0.06);
padding: 6px;
}
.sidebar-bottom .sidebar-item {
font-size: 12px;
color: var(--sidebar-muted);
border-left: 3px solid transparent;
}
.sidebar-bottom .sidebar-item:hover { color: var(--sidebar-text); }
/* ==========================================================================
MAIN CONTENT AREA
========================================================================== */
.main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
/* Top bar */
.topbar {
display: flex;
align-items: center;
gap: 12px;
padding: 0 24px;
height: 48px;
flex-shrink: 0;
background: var(--bg-surface);
border-bottom: 1px solid var(--border);
}
.topbar-breadcrumb {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--text-muted);
}
.topbar-breadcrumb .crumb-active { color: var(--text-primary); font-weight: 600; }
.topbar-breadcrumb .crumb-sep { color: var(--text-faint); font-size: 11px; }
.topbar-right {
margin-left: auto;
display: flex;
align-items: center;
gap: 12px;
}
.topbar-env {
font-family: var(--font-mono);
font-size: 10px;
font-weight: 600;
padding: 3px 10px;
border-radius: 10px;
background: var(--success-bg);
color: var(--success);
border: 1px solid var(--success-border);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.topbar-shift {
font-family: var(--font-mono);
font-size: 10px;
padding: 3px 10px;
border-radius: 10px;
background: var(--running-bg);
color: var(--running);
border: 1px solid var(--running-border);
}
.topbar-user {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--text-secondary);
}
.topbar-avatar {
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--amber-bg);
color: var(--amber);
font-weight: 600;
font-size: 11px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--amber-light);
}
/* Content scroll container */
.content {
flex: 1;
overflow-y: auto;
padding: 20px 24px 40px;
min-width: 0;
}
/* ==========================================================================
TIME RANGE SELECTOR BAR
========================================================================== */
.time-range-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.time-range-pills {
display: flex;
gap: 4px;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 3px;
box-shadow: var(--shadow-card);
}
.time-pill {
padding: 5px 14px;
border-radius: var(--radius-sm);
font-size: 12px;
font-weight: 500;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.12s;
background: none;
border: none;
font-family: var(--font-body);
}
.time-pill:hover { background: var(--bg-hover); }
.time-pill.active {
background: var(--amber);
color: white;
font-weight: 600;
}
.refresh-group {
display: flex;
align-items: center;
gap: 10px;
}
.auto-refresh {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--text-muted);
font-family: var(--font-mono);
}
.auto-refresh-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--success);
animation: pulse 2s ease-in-out infinite;
}
.refresh-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 5px 12px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-surface);
color: var(--text-secondary);
font-size: 11px;
font-family: var(--font-body);
cursor: pointer;
transition: all 0.15s;
box-shadow: var(--shadow-sm);
}
.refresh-btn:hover { border-color: var(--text-faint); background: var(--bg-raised); }
.refresh-icon {
display: inline-block;
font-size: 12px;
}
.last-updated {
font-size: 10px;
color: var(--text-faint);
font-family: var(--font-mono);
}
/* ==========================================================================
KPI CARDS (System Health Overview)
========================================================================== */
.kpi-strip {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;
margin-bottom: 20px;
}
.kpi-card {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
padding: 16px 18px 12px;
box-shadow: var(--shadow-card);
position: relative;
overflow: hidden;
transition: box-shadow 0.15s;
cursor: pointer;
}
.kpi-card:hover { box-shadow: var(--shadow-md); }
.kpi-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
}
.kpi-card.card-amber::before { background: linear-gradient(90deg, var(--amber), transparent); }
.kpi-card.card-green::before { background: linear-gradient(90deg, var(--success), transparent); }
.kpi-card.card-error::before { background: linear-gradient(90deg, var(--error), transparent); }
.kpi-card.card-teal::before { background: linear-gradient(90deg, var(--running), transparent); }
.kpi-card.card-warn::before { background: linear-gradient(90deg, var(--warning), transparent); }
.kpi-label {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.6px;
color: var(--text-muted);
margin-bottom: 6px;
}
.kpi-value-row {
display: flex;
align-items: baseline;
gap: 6px;
margin-bottom: 4px;
}
.kpi-value {
font-family: var(--font-mono);
font-size: 26px;
font-weight: 600;
line-height: 1.2;
}
.kpi-value.val-amber { color: var(--amber); }
.kpi-value.val-green { color: var(--success); }
.kpi-value.val-error { color: var(--error); }
.kpi-value.val-teal { color: var(--running); }
.kpi-value.val-warn { color: var(--warning); }
.kpi-unit {
font-size: 12px;
color: var(--text-muted);
}
.kpi-trend {
font-family: var(--font-mono);
font-size: 11px;
display: inline-flex;
align-items: center;
gap: 2px;
margin-left: auto;
}
.trend-up-good { color: var(--success); }
.trend-up-bad { color: var(--error); }
.trend-down-good { color: var(--success); }
.trend-down-bad { color: var(--error); }
.trend-flat { color: var(--text-muted); }
.kpi-detail {
font-size: 11px;
color: var(--text-muted);
margin-top: 2px;
}
.kpi-detail strong { color: var(--text-secondary); }
.kpi-sparkline {
margin-top: 8px;
height: 32px;
}
.kpi-sparkline svg { width: 100%; height: 100%; }
/* Latency sub-values */
.latency-values {
display: flex;
gap: 12px;
margin-top: 4px;
}
.latency-item {
display: flex;
flex-direction: column;
align-items: center;
}
.latency-label {
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
color: var(--text-faint);
letter-spacing: 0.5px;
}
.latency-val {
font-family: var(--font-mono);
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
}
.latency-val.lat-green { color: var(--success); }
.latency-val.lat-amber { color: var(--warning); }
.latency-val.lat-red { color: var(--error); }
.latency-trend {
font-family: var(--font-mono);
font-size: 9px;
}
/* Mini donut */
.mini-donut-wrap {
display: flex;
align-items: center;
gap: 12px;
margin-top: 6px;
}
.mini-donut { position: relative; width: 40px; height: 40px; }
.donut-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: var(--font-mono);
font-size: 9px;
font-weight: 600;
color: var(--text-secondary);
}
.donut-legend {
display: flex;
flex-direction: column;
gap: 2px;
font-size: 10px;
color: var(--text-muted);
font-family: var(--font-mono);
}
.donut-legend-active { color: var(--success); }
/* ==========================================================================
PER-ROUTE PERFORMANCE TABLE
========================================================================== */
.table-section {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
overflow: hidden;
margin-bottom: 20px;
}
.table-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
border-bottom: 1px solid var(--border-subtle);
}
.table-title { font-size: 13px; font-weight: 600; color: var(--text-primary); }
.table-right {
display: flex;
align-items: center;
gap: 12px;
}
.table-meta { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); }
.table-scroll { overflow-x: auto; }
table {
width: 100%;
border-collapse: collapse;
}
thead {
background: var(--bg-raised);
border-bottom: 1px solid var(--border);
}
th {
padding: 9px 14px;
text-align: left;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
color: var(--text-muted);
white-space: nowrap;
user-select: none;
cursor: pointer;
transition: color 0.12s;
}
th:hover { color: var(--text-secondary); }
th.sorted { color: var(--amber); }
.sort-arrow { font-size: 8px; margin-left: 3px; opacity: 0.4; }
th.sorted .sort-arrow { opacity: 1; }
tbody tr {
border-bottom: 1px solid var(--border-subtle);
transition: background 0.08s;
cursor: pointer;
}
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: var(--bg-hover); }
td {
padding: 9px 14px;
font-size: 13px;
vertical-align: middle;
white-space: nowrap;
}
.route-name-cell {
font-weight: 500;
color: var(--text-primary);
}
.route-name-cell:hover { color: var(--amber); }
.route-group-cell {
font-size: 11px;
color: var(--text-muted);
}
.mono { font-family: var(--font-mono); }
.cell-sparkline { display: inline-block; vertical-align: middle; }
/* Latency cell color coding */
.lat-cell { font-family: var(--font-mono); font-size: 12px; font-weight: 500; }
.lat-fast { color: var(--success); }
.lat-normal { color: var(--text-primary); }
.lat-slow { color: var(--warning); }
.lat-breach { color: var(--error); font-weight: 600; }
/* Error rate cell */
.err-rate { font-family: var(--font-mono); font-size: 12px; }
.err-low { color: var(--success); }
.err-mid { color: var(--warning); }
.err-high { color: var(--error); font-weight: 600; }
/* Status badge */
.status-badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 2px 10px;
border-radius: 12px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.2px;
}
.status-success { background: var(--success-bg); color: var(--success); border: 1px solid var(--success-border); }
.status-warning { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
.status-error { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border); }
.status-dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: currentColor;
}
.drill-arrow {
color: var(--text-faint);
font-size: 11px;
transition: color 0.12s;
}
tbody tr:hover .drill-arrow { color: var(--amber); }
/* ==========================================================================
CHART GRID (2x2)
========================================================================== */
.chart-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
margin-bottom: 20px;
}
.chart-card {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
overflow: hidden;
}
.chart-card-header {
padding: 12px 16px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid var(--border-subtle);
}
.chart-card-title {
font-size: 12px;
font-weight: 600;
color: var(--text-primary);
}
.chart-card-subtitle {
font-size: 10px;
color: var(--text-muted);
font-family: var(--font-mono);
}
.chart-body {
padding: 16px;
height: 220px;
position: relative;
}
.chart-body svg { width: 100%; height: 100%; }
/* Chart elements */
.chart-grid-line { stroke: var(--border-subtle); stroke-width: 1; stroke-dasharray: 3 3; }
.chart-axis-line { stroke: var(--border); stroke-width: 1; }
.chart-label { font-family: var(--font-mono); font-size: 9px; fill: var(--text-faint); }
.chart-value-label { font-family: var(--font-mono); font-size: 8px; fill: var(--text-muted); }
.sla-line { stroke: var(--error); stroke-width: 1; stroke-dasharray: 4 3; opacity: 0.5; }
.sla-label-text { fill: var(--error); font-size: 8px; font-family: var(--font-mono); opacity: 0.6; }
.sla-zone { fill: var(--error); opacity: 0.04; }
/* Chart legend */
.chart-legend {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 8px 16px 12px;
border-top: 1px solid var(--border-subtle);
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
font-size: 10px;
color: var(--text-muted);
font-family: var(--font-mono);
}
.legend-dot {
width: 8px;
height: 3px;
border-radius: 1px;
}
/* Heatmap */
.heatmap-grid {
display: flex;
flex-direction: column;
gap: 2px;
height: 100%;
}
.heatmap-row {
display: flex;
align-items: center;
gap: 2px;
flex: 1;
}
.heatmap-label {
width: 90px;
font-family: var(--font-mono);
font-size: 9px;
color: var(--text-muted);
text-align: right;
padding-right: 6px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.heatmap-cells {
display: flex;
gap: 2px;
flex: 1;
}
.heatmap-cell {
flex: 1;
border-radius: 2px;
position: relative;
cursor: pointer;
transition: transform 0.1s;
min-height: 16px;
}
.heatmap-cell:hover { transform: scale(1.15); z-index: 1; }
.heatmap-hour-labels {
display: flex;
gap: 2px;
margin-left: 92px;
margin-top: 2px;
}
.heatmap-hour-label {
flex: 1;
text-align: center;
font-family: var(--font-mono);
font-size: 8px;
color: var(--text-faint);
}
/* Heatmap intensity scale */
.heat-0 { background: var(--bg-inset); }
.heat-1 { background: #FDF6E9; }
.heat-2 { background: #F8E6C1; }
.heat-3 { background: #F0D9A8; }
.heat-4 { background: #E5C07A; }
.heat-5 { background: #D4A24E; }
.heat-6 { background: #C6820E; }
.heat-7 { background: #A66A0A; }
.heat-8 { background: #8B5A06; }
.heatmap-scale {
display: flex;
align-items: center;
gap: 4px;
padding: 6px 16px 10px;
justify-content: flex-end;
}
.heatmap-scale-label {
font-size: 9px;
color: var(--text-faint);
font-family: var(--font-mono);
}
.heatmap-scale-cells {
display: flex;
gap: 2px;
}
.heatmap-scale-cell {
width: 12px;
height: 10px;
border-radius: 1px;
}
/* ==========================================================================
LIVE EVENT FEED
========================================================================== */
.live-feed-section {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
overflow: hidden;
}
.live-feed-header {
padding: 10px 16px;
border-bottom: 1px solid var(--border-subtle);
display: flex;
align-items: center;
justify-content: space-between;
}
.live-feed-title {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 8px;
}
.live-feed-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--success);
animation: pulse 2s ease-in-out infinite;
}
.live-feed-list {
max-height: 240px;
overflow-y: auto;
}
.feed-item {
display: flex;
gap: 10px;
padding: 8px 16px;
border-bottom: 1px solid var(--border-subtle);
transition: background 0.08s;
cursor: pointer;
}
.feed-item:hover { background: var(--bg-hover); }
.feed-item:last-child { border-bottom: none; }
.feed-icon {
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
flex-shrink: 0;
margin-top: 1px;
}
.feed-icon.feed-error { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border); }
.feed-icon.feed-warn { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
.feed-icon.feed-info { background: var(--running-bg); color: var(--running); border: 1px solid var(--running-border); }
.feed-icon.feed-ok { background: var(--success-bg); color: var(--success); border: 1px solid var(--success-border); }
.feed-content { flex: 1; min-width: 0; }
.feed-text {
font-size: 12px;
color: var(--text-primary);
line-height: 1.4;
}
.feed-text strong { font-weight: 600; }
.feed-text .feed-route { color: var(--amber); font-family: var(--font-mono); font-size: 11px; }
.feed-meta {
font-size: 10px;
color: var(--text-faint);
font-family: var(--font-mono);
margin-top: 1px;
}
/* Utility */
.mono { font-family: var(--font-mono); }
</style>
</head>
<body>
<div class="app">
<!-- ====================================================================
SIDEBAR (warm charcoal — identical to v2, "Metrics" highlighted)
==================================================================== -->
<aside class="sidebar">
<div class="sidebar-logo">
<svg width="24" height="26" viewBox="940 300 900 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#C6820E" d="M 1385.2768,1205.0273 c -1.7727,-1.0809 -3.4419,-2.9917 -3.7093,-4.2463 -0.2674,-1.2545 -0.6851,-23.881 -0.9282,-50.281 -0.4544,-49.3389 -1.4003,-65.7355 -5.6645,-98.1921 -10.4552,-79.57719 -28.3441,-125.96796 -62.5438,-162.19311 -9.2529,-9.80096 -19.9748,-18.38696 -33.931,-27.17162 -12.6119,-7.93853 -24.8581,-13.73088 -49.5,-23.41316 -41.7081,-16.38789 -67.8677,-32.29827 -89.0588,-54.16599 -34.7586,-35.86827 -54.3653,-83.30392 -59.0504,-142.86402 -0.8357,-10.6245 -0.92,-22.5842 -0.2891,-41 0.4899,-14.3 0.8164,-35.225 0.7255,-46.5 -0.1983,-24.59199 -2.0251,-34.76973 -8.4617,-47.14159 -6.2021,-11.92135 -10.364,-15.10789 -36.2345,-27.74305 -25.4032,-12.40694 -27.7701,-13.30168 -35.4717,-13.40961 -4.63013,-0.0649 -7.47783,0.72754 -15.6593,4.35747 -9.86218,4.37563 -10.15161,4.43672 -21,4.43267 -10.86566,-0.004 -11.08917,-0.0517 -18.30169,-3.90103 -17.43524,-9.3052 -21.50075,-23.5772 -13.55784,-47.59486 2.73631,-8.27402 10.00732,-22.43593 14.69263,-28.61719 5.4984,-7.25394 10.89014,-11.83885 20.83968,-17.72117 15.06768,-8.90827 29.60254,-11.98884 63.90372,-13.54403 l 16.5765,-0.75156 14.9235,-7.05735 c 8.2079,-3.88154 17.1735,-7.76831 19.9235,-8.63727 7.7052,-2.43474 21.059,-4.67186 27.8605,-4.66741 8.0518,0.005 22.643,2.41202 30.0139,4.95066 l 5.8744,2.02321 4.8856,-4.09041 c 10.341,-8.65797 18.6496,-12.95738 28.96,-14.98583 6.9966,-1.37649 26.3532,-0.64631 32.1116,1.21134 4.5531,1.46885 5.4951,3.7902 6.3689,15.69592 1.5167,20.66426 -5.0112,40.44987 -18.1772,55.09363 -4.3065,4.78983 -4.5016,5.25488 -3.4977,8.33516 4.5184,13.86447 5.3154,38.07517 2.1537,65.42564 -5.169,44.71349 -5.0411,70.7797 0.5404,110.15637 6.8135,48.06863 22.3335,73.51874 48.2051,79.04779 24.1748,5.16643 45.2921,-5.78181 66.8353,-34.65077 4.809,-6.4442 11.1363,-14.93006 14.0608,-18.85747 7.3865,-9.91943 25.6102,-27.11708 35.952,-33.92766 12.5235,-8.24739 26.8808,-14.74833 42.8527,-19.40354 15.4108,-4.49168 26.7091,-9.74984 36.2432,-16.86731 3.4338,-2.56341 13.3338,-12.03104 22,-21.03916 26.2356,-27.27067 44.5755,-40.32368 66.9928,-47.68062 19.1052,-6.26998 35.4927,-7.73718 50.8681,-4.55427 37.0856,7.67726 63.5507,26.77589 100.2888,72.37362 16.6011,20.60463 29.9711,32.07977 51.6071,44.29313 39.3131,22.19195 50.1228,30.50985 68.8076,52.94655 5.4963,6.6 12.9187,14.91604 16.4941,18.4801 33.6796,33.57259 54.1965,72.51092 61.6587,117.0199 2.7315,16.29242 3.3374,26.62538 3.1861,54.33713 l -0.1465,26.83714 -2.7975,2.40572 c -3.9132,3.36522 -7.2806,3.99163 -11.2591,2.09442 -6.4731,-3.08682 -6.5715,-3.57833 -6.878,-34.36929 -0.2908,-29.2175 -2.0265,-46.13705 -6.6479,-64.80512 -3.2997,-13.32891 -12.2529,-34.61943 -17.0313,-40.5 l -2.0314,-2.5 0.5698,3.5 c 5.8574,35.97875 4.2855,72.40287 -5.3946,125 -8.4016,45.65054 -4.3665,69.39588 20.7318,122 11.4175,23.93009 12.5452,27.25907 12.4212,36.6684 -0.1018,7.7227 -2.5346,19.5162 -13.1674,63.8316 -1.0558,4.4 -3.5336,14.975 -5.5063,23.5 -1.9727,8.525 -4.2259,18.2 -5.0072,21.5 -0.7812,3.3 -3.4495,15.225 -5.9296,26.5 -9.1636,41.6596 -13.4372,59.8787 -14.5204,61.9027 -2.4994,4.6702 -5.2312,5.0973 -32.6024,5.0973 H 1765.8 l -3.4,-3.4 c -2.3518,-2.3518 -3.4,-4.3226 -3.4,-6.3925 0,-1.6458 2.2347,-12.1533 4.966,-23.35 8.6906,-35.6259 11.6969,-54.778 12.6921,-80.8575 1.3475,-35.3073 -4.6406,-62.7687 -18.7825,-86.137 -7.6672,-12.66954 -11.9163,-17.84148 -29.8756,-36.36415 -22.9362,-23.65574 -34.6222,-39.72583 -47.9268,-65.90697 -5.7294,-11.27448 -13.6061,-31.99995 -15.5051,-40.79778 -0.3212,-1.48824 -1.0176,-3.84619 -1.5475,-5.2399 l -0.9634,-2.534 -11.7786,7.15145 c -38.634,23.45687 -74.5513,34.71091 -124.1895,38.91257 -14.5402,1.23075 -58.2359,0.66344 -71.9142,-0.93369 -5.4419,-0.63542 -6.4542,-0.49723 -7.25,0.98972 -0.5545,1.03621 -0.9249,60.63835 -0.9249,148.84365 0,161.6262 0.4025,151.2052 -5.9673,154.4992 -2.3865,1.2341 -7.4633,1.5162 -27.233,1.5132 -22.3926,-0 -24.5527,-0.158 -27.5229,-1.969 z M 1424,1045.6452 c 0,-87.21053 0.3878,-144.17994 1.0365,-152.24998 1.8115,-22.53829 7.2373,-44.8067 16.1142,-66.13567 20.7842,-49.93942 66.8961,-95.01414 129.8493,-126.92865 14.9469,-7.57742 29.283
</svg>
<div>
<span class="brand">cameleer</span><span class="version">v3</span>
</div>
</div>
<div class="sidebar-search">
<div class="sidebar-search-wrap">
<span class="search-icon">&#9671;</span>
<input type="text" placeholder="Filter applications...">
</div>
</div>
<div class="sidebar-section">Navigation</div>
<div class="sidebar-items">
<div class="sidebar-item">
<span style="font-size: 13px; width: 18px; text-align: center;">&#9634;</span>
<div class="item-info"><div class="item-name">Transactions</div></div>
</div>
<div class="sidebar-item active">
<span style="font-size: 13px; width: 18px; text-align: center;">&#9670;</span>
<div class="item-info"><div class="item-name">Metrics</div></div>
</div>
<div class="sidebar-item">
<span style="font-size: 13px; width: 18px; text-align: center;">&#9740;</span>
<div class="item-info"><div class="item-name">Route Diagrams</div></div>
</div>
<div class="sidebar-divider"></div>
<div class="sidebar-section">Applications</div>
<div class="sidebar-item">
<span class="health health-live"></span>
<div class="item-info">
<div class="item-name">order-service</div>
<div class="item-meta">2 agents</div>
</div>
<span class="item-count">1,847</span>
</div>
<div class="sidebar-item">
<span class="health health-live"></span>
<div class="item-info">
<div class="item-name">payment-gateway</div>
<div class="item-meta">2 agents</div>
</div>
<span class="item-count">923</span>
</div>
<div class="sidebar-item">
<span class="health health-live"></span>
<div class="item-info">
<div class="item-name">shipment-tracker</div>
<div class="item-meta">2 agents</div>
</div>
<span class="item-count">471</span>
</div>
<div class="sidebar-item">
<span class="health health-stale"></span>
<div class="item-info">
<div class="item-name">notification-hub</div>
<div class="item-meta">1 agent</div>
</div>
<span class="item-count">128</span>
</div>
</div>
<!-- Agent health section -->
<div class="sidebar-agents-header">
<span>Agents</span>
<span class="sidebar-agent-badge">4/4 live</span>
</div>
<div style="padding: 0 0 6px; overflow-y: auto; max-height: 180px;">
<div class="agent-item">
<span class="agent-dot health-live"></span>
<div class="agent-info">
<div class="agent-name">prod-1</div>
<div class="agent-detail">order-service v3.2.1</div>
</div>
<div class="agent-stats-col">
<div class="agent-tps">14.2/s</div>
<div>12s ago</div>
</div>
</div>
<div class="agent-item">
<span class="agent-dot health-live"></span>
<div class="agent-info">
<div class="agent-name">prod-2</div>
<div class="agent-detail">payment-svc v3.2.1</div>
</div>
<div class="agent-stats-col">
<div class="agent-tps">11.8/s</div>
<div style="color: var(--error);">3 err/h</div>
</div>
</div>
<div class="agent-item">
<span class="agent-dot health-live"></span>
<div class="agent-info">
<div class="agent-name">prod-3</div>
<div class="agent-detail">shipment-svc v3.2.0</div>
</div>
<div class="agent-stats-col">
<div class="agent-tps">12.1/s</div>
<div>5s ago</div>
</div>
</div>
<div class="agent-item">
<span class="agent-dot health-live"></span>
<div class="agent-info">
<div class="agent-name">prod-4</div>
<div class="agent-detail">notif-hub v3.1.8</div>
</div>
<div class="agent-stats-col">
<div class="agent-tps">9.1/s</div>
<div>3s ago</div>
</div>
</div>
</div>
<div class="sidebar-bottom">
<div class="sidebar-item"><span style="font-size: 13px; width: 18px; text-align: center;">&#9881;</span><div class="item-info"><div class="item-name">Admin</div></div></div>
<div class="sidebar-item"><span style="font-size: 13px; width: 18px; text-align: center;">&#9776;</span><div class="item-info"><div class="item-name">API Docs</div></div></div>
</div>
</aside>
<!-- ====================================================================
MAIN CONTENT
==================================================================== -->
<div class="main">
<!-- Top bar -->
<div class="topbar">
<div class="topbar-breadcrumb">
<span>Dashboard</span>
<span class="crumb-sep">/</span>
<span class="crumb-active">Metrics</span>
</div>
<div class="topbar-right">
<span class="topbar-env">PRODUCTION</span>
<span class="topbar-shift">Shift: Day (06:00-18:00)</span>
<div class="topbar-user">
<span>hendrik</span>
<div class="topbar-avatar">H</div>
</div>
</div>
</div>
<!-- Scrollable content area -->
<div class="content">
<!-- Time range selector bar -->
<div class="time-range-bar animate-in">
<div class="time-range-pills">
<button class="time-pill">1h</button>
<button class="time-pill active">6h</button>
<button class="time-pill">24h</button>
<button class="time-pill">7d</button>
<button class="time-pill">30d</button>
<button class="time-pill">Custom</button>
</div>
<div class="refresh-group">
<div class="auto-refresh">
<span class="auto-refresh-dot"></span>
Auto-refresh: 30s
</div>
<button class="refresh-btn">
<span class="refresh-icon">&#8635;</span>
Refresh
</button>
<span class="last-updated">Updated 4s ago</span>
</div>
</div>
<!-- ================================================================
KPI CARDS — System Health Overview
================================================================ -->
<div class="kpi-strip animate-in delay-1">
<!-- Card 1: Total Throughput -->
<div class="kpi-card card-amber">
<div class="kpi-label">Total Throughput</div>
<div class="kpi-value-row">
<span class="kpi-value val-amber">2,847</span>
<span class="kpi-unit">msg/min</span>
<span class="kpi-trend trend-up-good">&#9650; +8%</span>
</div>
<div class="kpi-detail"><strong>47.5</strong> msg/s &middot; Capacity 39%</div>
<div class="kpi-sparkline">
<svg viewBox="0 0 200 32" preserveAspectRatio="none">
<polyline points="0,28 8,26 16,24 24,25 32,22 40,20 48,21 56,18 64,16 72,17 80,15 88,13 96,14 104,12 112,10 120,11 128,9 136,8 144,10 152,7 160,6 168,8 176,5 184,4 192,6 200,3" fill="none" stroke="#C6820E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<linearGradient id="sparkGrad1" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#C6820E" stop-opacity="0.2"/>
<stop offset="100%" stop-color="#C6820E" stop-opacity="0"/>
</linearGradient>
<polyline points="0,28 8,26 16,24 24,25 32,22 40,20 48,21 56,18 64,16 72,17 80,15 88,13 96,14 104,12 112,10 120,11 128,9 136,8 144,10 152,7 160,6 168,8 176,5 184,4 192,6 200,3 200,32 0,32" fill="url(#sparkGrad1)"/>
</svg>
</div>
</div>
<!-- Card 2: System Error Rate -->
<div class="kpi-card card-green">
<div class="kpi-label">System Error Rate</div>
<div class="kpi-value-row">
<span class="kpi-value val-green">0.82%</span>
<span class="kpi-trend trend-down-good">&#9660; -0.1%</span>
</div>
<div class="kpi-detail"><strong>94</strong> errors / <strong>11,482</strong> total (6h)</div>
<div class="kpi-sparkline">
<svg viewBox="0 0 200 32" preserveAspectRatio="none">
<polyline points="0,18 8,17 16,19 24,16 32,15 40,17 48,14 56,16 64,13 72,15 80,12 88,14 96,11 104,13 112,10 120,12 128,14 136,11 144,13 152,10 160,12 168,10 176,11 184,9 192,10 200,8" fill="none" stroke="#3D7C47" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<linearGradient id="sparkGrad2" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#3D7C47" stop-opacity="0.15"/>
<stop offset="100%" stop-color="#3D7C47" stop-opacity="0"/>
</linearGradient>
<polyline points="0,18 8,17 16,19 24,16 32,15 40,17 48,14 56,16 64,13 72,15 80,12 88,14 96,11 104,13 112,10 120,12 128,14 136,11 144,13 152,10 160,12 168,10 176,11 184,9 192,10 200,8 200,32 0,32" fill="url(#sparkGrad2)"/>
</svg>
</div>
</div>
<!-- Card 3: Avg Latency (P50/P95/P99) -->
<div class="kpi-card card-warn">
<div class="kpi-label">Latency Percentiles</div>
<div class="latency-values">
<div class="latency-item">
<span class="latency-label">P50</span>
<span class="latency-val lat-green">42ms</span>
<span class="latency-trend trend-down-good">&#9660;3</span>
</div>
<div class="latency-item">
<span class="latency-label">P95</span>
<span class="latency-val lat-amber">187ms</span>
<span class="latency-trend trend-up-bad">&#9650;12</span>
</div>
<div class="latency-item">
<span class="latency-label">P99</span>
<span class="latency-val lat-red">312ms</span>
<span class="latency-trend trend-up-bad">&#9650;28</span>
</div>
</div>
<div class="kpi-detail" style="margin-top:6px;">SLA: &lt;300ms P99 &middot; <strong style="color:var(--error);">BREACH</strong></div>
</div>
<!-- Card 4: Active Routes (mini donut) -->
<div class="kpi-card card-teal">
<div class="kpi-label">Active Routes</div>
<div class="kpi-value-row">
<span class="kpi-value val-teal">7</span>
<span class="kpi-unit">of 8</span>
<span class="kpi-trend trend-flat">&#8596; stable</span>
</div>
<div class="mini-donut-wrap">
<div class="mini-donut">
<svg viewBox="0 0 36 36" width="40" height="40">
<circle cx="18" cy="18" r="15.9" fill="none" stroke="var(--bg-inset)" stroke-width="3"/>
<circle cx="18" cy="18" r="15.9" fill="none" stroke="var(--running)" stroke-width="3"
stroke-dasharray="87.5 12.5" stroke-dashoffset="25" stroke-linecap="round"/>
</svg>
<span class="donut-label">88%</span>
</div>
<div class="donut-legend">
<span class="donut-legend-active">7 active</span>
<span>1 stopped</span>
</div>
</div>
</div>
<!-- Card 5: In-Flight Exchanges -->
<div class="kpi-card card-amber">
<div class="kpi-label">In-Flight Exchanges</div>
<div class="kpi-value-row">
<span class="kpi-value">23</span>
<span class="kpi-trend trend-flat">&#8596;</span>
</div>
<div class="kpi-detail">High-water: <strong>67</strong> (2h ago)</div>
<div class="kpi-sparkline">
<svg viewBox="0 0 200 32" preserveAspectRatio="none">
<polyline points="0,16 8,14 16,18 24,12 32,10 40,15 48,8 56,6 64,4 72,3 80,2 88,4 96,6 104,8 112,10 120,12 128,14 136,16 144,18 152,20 160,18 168,16 176,18 184,20 192,18 200,17" fill="none" stroke="#C6820E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<linearGradient id="sparkGrad5" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#C6820E" stop-opacity="0.15"/>
<stop offset="100%" stop-color="#C6820E" stop-opacity="0"/>
</linearGradient>
<polyline points="0,16 8,14 16,18 24,12 32,10 40,15 48,8 56,6 64,4 72,3 80,2 88,4 96,6 104,8 112,10 120,12 128,14 136,16 144,18 152,20 160,18 168,16 176,18 184,20 192,18 200,17 200,32 0,32" fill="url(#sparkGrad5)"/>
<!-- High-water mark line -->
<line x1="78" y1="0" x2="78" y2="32" stroke="var(--error)" stroke-width="0.5" stroke-dasharray="2 2" opacity="0.5"/>
<text x="80" y="7" font-family="var(--font-mono)" font-size="6" fill="var(--error)" opacity="0.6">67</text>
</svg>
</div>
</div>
</div>
<!-- ================================================================
PER-ROUTE PERFORMANCE TABLE
================================================================ -->
<div class="table-section animate-in delay-2">
<div class="table-header">
<span class="table-title">Per-Route Performance</span>
<div class="table-right">
<span class="table-meta">8 routes &middot; last 6h</span>
</div>
</div>
<div class="table-scroll">
<table>
<thead>
<tr>
<th style="width:180px">Route</th>
<th>Trend (1h)</th>
<th class="sorted">Throughput <span class="sort-arrow">&#9660;</span></th>
<th>Error Rate</th>
<th>P50</th>
<th>P95</th>
<th>P99</th>
<th style="text-align:right">Success</th>
<th style="text-align:right">Failed</th>
<th style="width:80px">Status</th>
<th style="width:30px"></th>
</tr>
</thead>
<tbody>
<!-- Row 1: order-processing — high throughput, healthy -->
<tr>
<td>
<div class="route-name-cell">order-processing</div>
<div class="route-group-cell">order-service</div>
</td>
<td>
<span class="cell-sparkline">
<svg width="80" height="20" viewBox="0 0 80 20">
<polyline points="0,14 8,12 16,10 24,11 32,8 40,9 48,7 56,6 64,7 72,5 80,4" fill="none" stroke="var(--chart-1)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</td>
<td><span class="mono" style="font-size:12px;font-weight:600;">842</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
<td><span class="err-rate err-low">0.2%</span></td>
<td><span class="lat-cell lat-fast">28ms</span></td>
<td><span class="lat-cell lat-fast">94ms</span></td>
<td><span class="lat-cell lat-normal">156ms</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">4,987</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">10</span></td>
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
<td><span class="drill-arrow">&#9656;</span></td>
</tr>
<!-- Row 2: payment-flow — higher latency, some errors -->
<tr>
<td>
<div class="route-name-cell">payment-flow</div>
<div class="route-group-cell">payment-gateway</div>
</td>
<td>
<span class="cell-sparkline">
<svg width="80" height="20" viewBox="0 0 80 20">
<polyline points="0,10 8,11 16,9 24,12 32,10 40,8 48,10 56,9 64,11 72,8 80,7" fill="none" stroke="var(--chart-2)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</td>
<td><span class="mono" style="font-size:12px;font-weight:600;">524</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
<td><span class="err-rate err-mid">2.1%</span></td>
<td><span class="lat-cell lat-normal">89ms</span></td>
<td><span class="lat-cell lat-slow">245ms</span></td>
<td><span class="lat-cell lat-breach">412ms</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">3,078</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">66</span></td>
<td><span class="status-badge status-warning"><span class="status-dot"></span>SLA</span></td>
<td><span class="drill-arrow">&#9656;</span></td>
</tr>
<!-- Row 3: shipment-notify — steady, fast -->
<tr>
<td>
<div class="route-name-cell">shipment-notify</div>
<div class="route-group-cell">shipment-tracker</div>
</td>
<td>
<span class="cell-sparkline">
<svg width="80" height="20" viewBox="0 0 80 20">
<polyline points="0,10 8,10 16,9 24,10 32,10 40,9 48,10 56,9 64,10 72,9 80,10" fill="none" stroke="var(--chart-3)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</td>
<td><span class="mono" style="font-size:12px;font-weight:600;">471</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
<td><span class="err-rate err-low">0.1%</span></td>
<td><span class="lat-cell lat-fast">18ms</span></td>
<td><span class="lat-cell lat-fast">52ms</span></td>
<td><span class="lat-cell lat-fast">87ms</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">2,823</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">3</span></td>
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
<td><span class="drill-arrow">&#9656;</span></td>
</tr>
<!-- Row 4: inventory-check — fast, low volume -->
<tr>
<td>
<div class="route-name-cell">inventory-check</div>
<div class="route-group-cell">order-service</div>
</td>
<td>
<span class="cell-sparkline">
<svg width="80" height="20" viewBox="0 0 80 20">
<polyline points="0,12 8,13 16,11 24,14 32,12 40,13 48,11 56,12 64,13 72,11 80,12" fill="none" stroke="var(--chart-4)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</td>
<td><span class="mono" style="font-size:12px;font-weight:600;">389</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
<td><span class="err-rate err-low">0.3%</span></td>
<td><span class="lat-cell lat-fast">12ms</span></td>
<td><span class="lat-cell lat-fast">34ms</span></td>
<td><span class="lat-cell lat-fast">61ms</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">2,327</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">7</span></td>
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
<td><span class="drill-arrow">&#9656;</span></td>
</tr>
<!-- Row 5: customer-validation — moderate, stable -->
<tr>
<td>
<div class="route-name-cell">customer-validation</div>
<div class="route-group-cell">order-service</div>
</td>
<td>
<span class="cell-sparkline">
<svg width="80" height="20" viewBox="0 0 80 20">
<polyline points="0,8 8,10 16,9 24,8 32,10 40,9 48,10 56,8 64,9 72,10 80,9" fill="none" stroke="var(--chart-5)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</td>
<td><span class="mono" style="font-size:12px;font-weight:600;">312</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
<td><span class="err-rate err-low">0.5%</span></td>
<td><span class="lat-cell lat-fast">35ms</span></td>
<td><span class="lat-cell lat-normal">112ms</span></td>
<td><span class="lat-cell lat-normal">178ms</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">1,863</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">9</span></td>
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
<td><span class="drill-arrow">&#9656;</span></td>
</tr>
<!-- Row 6: email-notification — low volume, very fast -->
<tr>
<td>
<div class="route-name-cell">email-notification</div>
<div class="route-group-cell">notification-hub</div>
</td>
<td>
<span class="cell-sparkline">
<svg width="80" height="20" viewBox="0 0 80 20">
<polyline points="0,14 8,15 16,13 24,14 32,16 40,14 48,15 56,13 64,14 72,15 80,14" fill="none" stroke="var(--chart-6)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</td>
<td><span class="mono" style="font-size:12px;font-weight:600;">201</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
<td><span class="err-rate err-low">0.0%</span></td>
<td><span class="lat-cell lat-fast">8ms</span></td>
<td><span class="lat-cell lat-fast">21ms</span></td>
<td><span class="lat-cell lat-fast">38ms</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">1,206</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--text-faint);">0</span></td>
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
<td><span class="drill-arrow">&#9656;</span></td>
</tr>
<!-- Row 7: retry-handler — low throughput, HIGH error rate -->
<tr>
<td>
<div class="route-name-cell">retry-handler</div>
<div class="route-group-cell">order-service</div>
</td>
<td>
<span class="cell-sparkline">
<svg width="80" height="20" viewBox="0 0 80 20">
<polyline points="0,10 8,8 16,12 24,6 32,10 40,4 48,8 56,6 64,10 72,4 80,8" fill="none" stroke="var(--chart-7)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</td>
<td><span class="mono" style="font-size:12px;font-weight:600;">78</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
<td><span class="err-rate err-high">12.8%</span></td>
<td><span class="lat-cell lat-slow">210ms</span></td>
<td><span class="lat-cell lat-breach">890ms</span></td>
<td><span class="lat-cell lat-breach">2,140ms</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">408</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">60</span></td>
<td><span class="status-badge status-warning"><span class="status-dot"></span>WARN</span></td>
<td><span class="drill-arrow">&#9656;</span></td>
</tr>
<!-- Row 8: dead-letter-processor — very low, all "errors" -->
<tr>
<td>
<div class="route-name-cell" style="color:var(--text-muted);">dead-letter-processor</div>
<div class="route-group-cell">order-service</div>
</td>
<td>
<span class="cell-sparkline">
<svg width="80" height="20" viewBox="0 0 80 20">
<polyline points="0,16 8,16 16,14 24,16 32,15 40,16 48,14 56,16 64,15 72,16 80,16" fill="none" stroke="var(--text-faint)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="3 2"/>
</svg>
</span>
</td>
<td><span class="mono" style="font-size:12px;color:var(--text-muted);">0</span> <span style="font-size:10px;color:var(--text-faint);">msg/min</span></td>
<td><span class="err-rate" style="color:var(--text-faint);">-- %</span></td>
<td><span class="lat-cell" style="color:var(--text-faint);">--</span></td>
<td><span class="lat-cell" style="color:var(--text-faint);">--</span></td>
<td><span class="lat-cell" style="color:var(--text-faint);">--</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--text-faint);">0</span></td>
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--text-faint);">0</span></td>
<td><span class="status-badge" style="background:var(--bg-inset);color:var(--text-faint);border:1px solid var(--border);"><span class="status-dot"></span>STOP</span></td>
<td><span class="drill-arrow">&#9656;</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ================================================================
TIME-SERIES CHARTS (2x2 grid)
================================================================ -->
<div class="chart-grid animate-in delay-3">
<!-- Chart 1: Throughput Over Time (stacked area) -->
<div class="chart-card">
<div class="chart-card-header">
<div>
<div class="chart-card-title">Throughput Over Time</div>
<div class="chart-card-subtitle">Messages/min by route &middot; last 6h</div>
</div>
</div>
<div class="chart-body">
<svg viewBox="0 0 600 180" preserveAspectRatio="none">
<!-- Grid lines -->
<line x1="40" y1="10" x2="580" y2="10" class="chart-grid-line"/>
<line x1="40" y1="52" x2="580" y2="52" class="chart-grid-line"/>
<line x1="40" y1="94" x2="580" y2="94" class="chart-grid-line"/>
<line x1="40" y1="136" x2="580" y2="136" class="chart-grid-line"/>
<line x1="40" y1="170" x2="580" y2="170" class="chart-axis-line"/>
<!-- Y-axis labels -->
<text x="36" y="14" class="chart-label" text-anchor="end">3k</text>
<text x="36" y="56" class="chart-label" text-anchor="end">2k</text>
<text x="36" y="98" class="chart-label" text-anchor="end">1k</text>
<text x="36" y="140" class="chart-label" text-anchor="end">500</text>
<text x="36" y="174" class="chart-label" text-anchor="end">0</text>
<!-- X-axis labels -->
<text x="40" y="180" class="chart-label">03:00</text>
<text x="130" y="180" class="chart-label">04:00</text>
<text x="220" y="180" class="chart-label">05:00</text>
<text x="310" y="180" class="chart-label">06:00</text>
<text x="400" y="180" class="chart-label">07:00</text>
<text x="490" y="180" class="chart-label">08:00</text>
<text x="565" y="180" class="chart-label">09:14</text>
<!-- Stacked areas (bottom to top: email, inventory, shipment, customer, payment, order) -->
<!-- Layer 1: order-processing (amber) -->
<polygon points="40,170 85,165 130,160 175,155 220,145 265,130 310,105 355,90 400,75 445,65 490,55 535,50 580,45 580,170" fill="#C6820E" opacity="0.3"/>
<polyline points="40,170 85,165 130,160 175,155 220,145 265,130 310,105 355,90 400,75 445,65 490,55 535,50 580,45" fill="none" stroke="#C6820E" stroke-width="1.5"/>
<!-- Layer 2: payment-flow (olive green) -->
<polygon points="40,170 85,168 130,166 175,163 220,158 265,150 310,135 355,125 400,112 445,104 490,96 535,92 580,88 580,170" fill="#3D7C47" opacity="0.25"/>
<polyline points="40,170 85,168 130,166 175,163 220,158 265,150 310,135 355,125 400,112 445,104 490,96 535,92 580,88" fill="none" stroke="#3D7C47" stroke-width="1.5"/>
<!-- Layer 3: shipment-notify (teal) -->
<polygon points="40,170 85,169 130,168 175,167 220,164 265,158 310,148 355,140 400,132 445,126 490,120 535,117 580,114 580,170" fill="#1A7F8E" opacity="0.25"/>
<polyline points="40,170 85,169 130,168 175,167 220,164 265,158 310,148 355,140 400,132 445,126 490,120 535,117 580,114" fill="none" stroke="#1A7F8E" stroke-width="1.5"/>
<!-- Layer 4: other routes combined (burnt orange, thin) -->
<polygon points="40,170 85,169 130,169 175,168 220,167 265,164 310,158 355,152 400,146 445,141 490,136 535,134 580,132 580,170" fill="#C27516" opacity="0.15"/>
<polyline points="40,170 85,169 130,169 175,168 220,167 265,164 310,158 355,152 400,146 445,141 490,136 535,134 580,132" fill="none" stroke="#C27516" stroke-width="1" stroke-dasharray="3 2"/>
</svg>
</div>
<div class="chart-legend">
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-1);"></div>order-processing</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-2);"></div>payment-flow</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-3);"></div>shipment-notify</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-4);"></div>others (combined)</div>
</div>
</div>
<!-- Chart 2: Latency Percentiles (line chart) -->
<div class="chart-card">
<div class="chart-card-header">
<div>
<div class="chart-card-title">Latency Percentiles</div>
<div class="chart-card-subtitle">P50 / P95 / P99 &middot; last 6h</div>
</div>
</div>
<div class="chart-body">
<svg viewBox="0 0 600 180" preserveAspectRatio="none">
<!-- Grid -->
<line x1="40" y1="10" x2="580" y2="10" class="chart-grid-line"/>
<line x1="40" y1="52" x2="580" y2="52" class="chart-grid-line"/>
<line x1="40" y1="94" x2="580" y2="94" class="chart-grid-line"/>
<line x1="40" y1="136" x2="580" y2="136" class="chart-grid-line"/>
<line x1="40" y1="170" x2="580" y2="170" class="chart-axis-line"/>
<!-- Y-axis (ms) -->
<text x="36" y="14" class="chart-label" text-anchor="end">500</text>
<text x="36" y="56" class="chart-label" text-anchor="end">375</text>
<text x="36" y="98" class="chart-label" text-anchor="end">250</text>
<text x="36" y="140" class="chart-label" text-anchor="end">125</text>
<text x="36" y="174" class="chart-label" text-anchor="end">0</text>
<!-- X-axis -->
<text x="40" y="180" class="chart-label">03:00</text>
<text x="130" y="180" class="chart-label">04:00</text>
<text x="220" y="180" class="chart-label">05:00</text>
<text x="310" y="180" class="chart-label">06:00</text>
<text x="400" y="180" class="chart-label">07:00</text>
<text x="490" y="180" class="chart-label">08:00</text>
<text x="565" y="180" class="chart-label">09:14</text>
<!-- SLA threshold at 300ms = y ~74 -->
<rect x="40" y="10" width="540" height="64" class="sla-zone"/>
<line x1="40" y1="74" x2="580" y2="74" class="sla-line"/>
<text x="582" y="72" class="sla-label-text">SLA 300ms</text>
<!-- P99 line (red, most volatile) -->
<polyline points="40,100 85,95 130,88 175,82 220,78 265,72 310,68 355,62 400,58 445,55 490,52 535,48 580,42" fill="none" stroke="var(--error)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<!-- P95 line (amber) -->
<polyline points="40,128 85,126 130,122 175,118 220,114 265,110 310,106 355,102 400,98 445,96 490,94 535,92 580,90" fill="none" stroke="var(--warning)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<!-- P50 line (green, stable at bottom) -->
<polyline points="40,158 85,157 130,156 175,155 220,154 265,153 310,152 355,152 400,151 445,150 490,150 535,149 580,148" fill="none" stroke="var(--success)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Current value labels -->
<text x="582" y="44" class="chart-value-label" fill="var(--error)">312ms</text>
<text x="582" y="92" class="chart-value-label" fill="var(--warning)">187ms</text>
<text x="582" y="150" class="chart-value-label" fill="var(--success)">42ms</text>
</svg>
</div>
<div class="chart-legend">
<div class="legend-item"><div class="legend-dot" style="background:var(--success);"></div>P50</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--warning);"></div>P95</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--error);"></div>P99</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--error); opacity:0.3;"></div>SLA threshold (300ms)</div>
</div>
</div>
<!-- Chart 3: Error Rate by Route (stacked bar chart) -->
<div class="chart-card">
<div class="chart-card-header">
<div>
<div class="chart-card-title">Errors by Route</div>
<div class="chart-card-subtitle">15-min buckets &middot; last 6h</div>
</div>
</div>
<div class="chart-body">
<svg viewBox="0 0 600 180" preserveAspectRatio="none">
<!-- Grid -->
<line x1="40" y1="10" x2="580" y2="10" class="chart-grid-line"/>
<line x1="40" y1="52" x2="580" y2="52" class="chart-grid-line"/>
<line x1="40" y1="94" x2="580" y2="94" class="chart-grid-line"/>
<line x1="40" y1="136" x2="580" y2="136" class="chart-grid-line"/>
<line x1="40" y1="170" x2="580" y2="170" class="chart-axis-line"/>
<!-- Y-axis -->
<text x="36" y="14" class="chart-label" text-anchor="end">20</text>
<text x="36" y="56" class="chart-label" text-anchor="end">15</text>
<text x="36" y="98" class="chart-label" text-anchor="end">10</text>
<text x="36" y="140" class="chart-label" text-anchor="end">5</text>
<text x="36" y="174" class="chart-label" text-anchor="end">0</text>
<!-- X-axis -->
<text x="40" y="180" class="chart-label">03:00</text>
<text x="130" y="180" class="chart-label">04:00</text>
<text x="220" y="180" class="chart-label">05:00</text>
<text x="310" y="180" class="chart-label">06:00</text>
<text x="400" y="180" class="chart-label">07:00</text>
<text x="490" y="180" class="chart-label">08:00</text>
<text x="565" y="180" class="chart-label">09:14</text>
<!-- Stacked bars (24 bars over 6 hours, 15-min each) -->
<!-- Low activity early morning (03:00-06:00) -->
<!-- Bar group 1: 03:00 -->
<rect x="44" y="162" width="16" height="8" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="44" y="166" width="16" height="4" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 03:15 -->
<rect x="66" y="166" width="16" height="4" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 03:30 -->
<rect x="88" y="162" width="16" height="8" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 03:45 -->
<rect x="110" y="164" width="16" height="6" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 04:00 -->
<rect x="132" y="158" width="16" height="12" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="132" y="162" width="16" height="8" rx="1" fill="var(--chart-1)" opacity="0.5"/>
<!-- 04:15 -->
<rect x="154" y="164" width="16" height="6" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 04:30 -->
<rect x="176" y="160" width="16" height="10" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 04:45 -->
<rect x="198" y="162" width="16" height="8" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 05:00 -->
<rect x="220" y="158" width="16" height="12" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="220" y="164" width="16" height="6" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 05:15 -->
<rect x="242" y="164" width="16" height="6" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 05:30 -->
<rect x="264" y="162" width="16" height="8" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 05:45 -->
<rect x="286" y="160" width="16" height="10" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<!-- 06:00 — shift start, traffic ramp up -->
<rect x="308" y="148" width="16" height="22" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="308" y="154" width="16" height="16" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<rect x="308" y="160" width="16" height="10" rx="1" fill="var(--chart-1)" opacity="0.5"/>
<!-- 06:15 -->
<rect x="330" y="152" width="16" height="18" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="330" y="158" width="16" height="12" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 06:30 -->
<rect x="352" y="144" width="16" height="26" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="352" y="150" width="16" height="20" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<rect x="352" y="158" width="16" height="12" rx="1" fill="var(--chart-1)" opacity="0.5"/>
<!-- 06:45 -->
<rect x="374" y="150" width="16" height="20" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="374" y="156" width="16" height="14" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 07:00 — peak errors -->
<rect x="396" y="130" width="16" height="40" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="396" y="140" width="16" height="30" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<rect x="396" y="150" width="16" height="20" rx="1" fill="var(--chart-1)" opacity="0.5"/>
<rect x="396" y="158" width="16" height="12" rx="1" fill="var(--chart-3)" opacity="0.5"/>
<!-- 07:15 -->
<rect x="418" y="138" width="16" height="32" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="418" y="146" width="16" height="24" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<rect x="418" y="156" width="16" height="14" rx="1" fill="var(--chart-1)" opacity="0.5"/>
<!-- 07:30 -->
<rect x="440" y="142" width="16" height="28" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="440" y="150" width="16" height="20" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 07:45 -->
<rect x="462" y="148" width="16" height="22" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="462" y="156" width="16" height="14" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 08:00 -->
<rect x="484" y="144" width="16" height="26" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="484" y="152" width="16" height="18" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<rect x="484" y="160" width="16" height="10" rx="1" fill="var(--chart-1)" opacity="0.5"/>
<!-- 08:15 -->
<rect x="506" y="150" width="16" height="20" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="506" y="158" width="16" height="12" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 08:30 -->
<rect x="528" y="154" width="16" height="16" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="528" y="160" width="16" height="10" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 08:45 -->
<rect x="550" y="156" width="16" height="14" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="550" y="162" width="16" height="8" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- 09:00 (current, partial) -->
<rect x="572" y="158" width="8" height="12" rx="1" fill="var(--chart-7)" opacity="0.8"/>
<rect x="572" y="162" width="8" height="8" rx="1" fill="var(--chart-2)" opacity="0.6"/>
<!-- Total errors overlay line -->
<polyline points="52,164 74,168 96,164 118,166 140,160 162,166 184,162 206,164 228,160 250,166 272,164 294,162 316,150 338,154 360,146 382,152 404,132 426,140 448,144 470,150 492,146 514,152 536,156 558,158 576,160" fill="none" stroke="var(--text-secondary)" stroke-width="1.5" stroke-dasharray="4 2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="chart-legend">
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-7);"></div>retry-handler</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-2);"></div>payment-flow</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-1);"></div>order-processing</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-3);"></div>shipment-notify</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--text-secondary); height:1px; border-top: 1.5px dashed var(--text-secondary); width:12px;"></div>total (overlay)</div>
</div>
</div>
<!-- Chart 4: Exchange Volume Heatmap -->
<div class="chart-card">
<div class="chart-card-header">
<div>
<div class="chart-card-title">Exchange Volume Heatmap</div>
<div class="chart-card-subtitle">Hourly volume by route &middot; last 6h</div>
</div>
</div>
<div class="chart-body" style="padding: 12px 16px;">
<div class="heatmap-grid">
<!-- Row: order-processing -->
<div class="heatmap-row">
<div class="heatmap-label">order-proc</div>
<div class="heatmap-cells">
<div class="heatmap-cell heat-2" title="03:00 — 142"></div>
<div class="heatmap-cell heat-3" title="04:00 — 287"></div>
<div class="heatmap-cell heat-4" title="05:00 — 456"></div>
<div class="heatmap-cell heat-7" title="06:00 — 812"></div>
<div class="heatmap-cell heat-8" title="07:00 — 923"></div>
<div class="heatmap-cell heat-8" title="08:00 — 891"></div>
<div class="heatmap-cell heat-6" title="09:00 — 653 (partial)"></div>
</div>
</div>
<!-- Row: payment-flow -->
<div class="heatmap-row">
<div class="heatmap-label">payment-flow</div>
<div class="heatmap-cells">
<div class="heatmap-cell heat-1" title="03:00 — 68"></div>
<div class="heatmap-cell heat-2" title="04:00 — 134"></div>
<div class="heatmap-cell heat-3" title="05:00 — 245"></div>
<div class="heatmap-cell heat-6" title="06:00 — 587"></div>
<div class="heatmap-cell heat-7" title="07:00 — 712"></div>
<div class="heatmap-cell heat-6" title="08:00 — 645"></div>
<div class="heatmap-cell heat-5" title="09:00 — 401 (partial)"></div>
</div>
</div>
<!-- Row: shipment-notify -->
<div class="heatmap-row">
<div class="heatmap-label">ship-notify</div>
<div class="heatmap-cells">
<div class="heatmap-cell heat-1" title="03:00 — 52"></div>
<div class="heatmap-cell heat-2" title="04:00 — 118"></div>
<div class="heatmap-cell heat-3" title="05:00 — 210"></div>
<div class="heatmap-cell heat-5" title="06:00 — 478"></div>
<div class="heatmap-cell heat-6" title="07:00 — 534"></div>
<div class="heatmap-cell heat-6" title="08:00 — 521"></div>
<div class="heatmap-cell heat-4" title="09:00 — 341 (partial)"></div>
</div>
</div>
<!-- Row: inventory-check -->
<div class="heatmap-row">
<div class="heatmap-label">inv-check</div>
<div class="heatmap-cells">
<div class="heatmap-cell heat-1" title="03:00 — 38"></div>
<div class="heatmap-cell heat-2" title="04:00 — 97"></div>
<div class="heatmap-cell heat-2" title="05:00 — 178"></div>
<div class="heatmap-cell heat-5" title="06:00 — 412"></div>
<div class="heatmap-cell heat-5" title="07:00 — 467"></div>
<div class="heatmap-cell heat-5" title="08:00 — 445"></div>
<div class="heatmap-cell heat-4" title="09:00 — 298 (partial)"></div>
</div>
</div>
<!-- Row: customer-validation -->
<div class="heatmap-row">
<div class="heatmap-label">cust-valid</div>
<div class="heatmap-cells">
<div class="heatmap-cell heat-1" title="03:00 — 24"></div>
<div class="heatmap-cell heat-1" title="04:00 — 76"></div>
<div class="heatmap-cell heat-2" title="05:00 — 145"></div>
<div class="heatmap-cell heat-4" title="06:00 — 356"></div>
<div class="heatmap-cell heat-5" title="07:00 — 398"></div>
<div class="heatmap-cell heat-4" title="08:00 — 367"></div>
<div class="heatmap-cell heat-3" title="09:00 — 234 (partial)"></div>
</div>
</div>
<!-- Row: email-notification -->
<div class="heatmap-row">
<div class="heatmap-label">email-notif</div>
<div class="heatmap-cells">
<div class="heatmap-cell heat-0" title="03:00 — 8"></div>
<div class="heatmap-cell heat-1" title="04:00 — 42"></div>
<div class="heatmap-cell heat-1" title="05:00 — 78"></div>
<div class="heatmap-cell heat-3" title="06:00 — 245"></div>
<div class="heatmap-cell heat-4" title="07:00 — 312"></div>
<div class="heatmap-cell heat-3" title="08:00 — 278"></div>
<div class="heatmap-cell heat-2" title="09:00 — 167 (partial)"></div>
</div>
</div>
<!-- Row: retry-handler -->
<div class="heatmap-row">
<div class="heatmap-label">retry-hdlr</div>
<div class="heatmap-cells">
<div class="heatmap-cell heat-0" title="03:00 — 4"></div>
<div class="heatmap-cell heat-1" title="04:00 — 12"></div>
<div class="heatmap-cell heat-1" title="05:00 — 18"></div>
<div class="heatmap-cell heat-2" title="06:00 — 89"></div>
<div class="heatmap-cell heat-3" title="07:00 — 134"></div>
<div class="heatmap-cell heat-2" title="08:00 — 112"></div>
<div class="heatmap-cell heat-1" title="09:00 — 67 (partial)"></div>
</div>
</div>
<!-- Row: dead-letter-processor -->
<div class="heatmap-row">
<div class="heatmap-label" style="color:var(--text-faint);">dead-letter</div>
<div class="heatmap-cells">
<div class="heatmap-cell heat-0" title="03:00 — 0"></div>
<div class="heatmap-cell heat-0" title="04:00 — 0"></div>
<div class="heatmap-cell heat-0" title="05:00 — 0"></div>
<div class="heatmap-cell heat-0" title="06:00 — 0"></div>
<div class="heatmap-cell heat-0" title="07:00 — 0"></div>
<div class="heatmap-cell heat-0" title="08:00 — 0"></div>
<div class="heatmap-cell heat-0" title="09:00 — 0"></div>
</div>
</div>
</div>
<div class="heatmap-hour-labels">
<div class="heatmap-hour-label">03:00</div>
<div class="heatmap-hour-label">04:00</div>
<div class="heatmap-hour-label">05:00</div>
<div class="heatmap-hour-label">06:00</div>
<div class="heatmap-hour-label">07:00</div>
<div class="heatmap-hour-label">08:00</div>
<div class="heatmap-hour-label">09:00</div>
</div>
</div>
<div class="heatmap-scale">
<span class="heatmap-scale-label">Low</span>
<div class="heatmap-scale-cells">
<div class="heatmap-scale-cell heat-0"></div>
<div class="heatmap-scale-cell heat-1"></div>
<div class="heatmap-scale-cell heat-2"></div>
<div class="heatmap-scale-cell heat-3"></div>
<div class="heatmap-scale-cell heat-4"></div>
<div class="heatmap-scale-cell heat-5"></div>
<div class="heatmap-scale-cell heat-6"></div>
<div class="heatmap-scale-cell heat-7"></div>
<div class="heatmap-scale-cell heat-8"></div>
</div>
<span class="heatmap-scale-label">High</span>
</div>
</div>
</div>
<!-- ================================================================
LIVE EVENT FEED
================================================================ -->
<div class="live-feed-section animate-in delay-4">
<div class="live-feed-header">
<div class="live-feed-title">
<span class="live-feed-dot"></span>
Live Event Feed
</div>
<span class="table-meta">Last 25 events</span>
</div>
<div class="live-feed-list">
<div class="feed-item">
<div class="feed-icon feed-error">&#9888;</div>
<div class="feed-content">
<div class="feed-text"><strong>Threshold breached</strong> &mdash; P99 latency exceeded SLA on <span class="feed-route">payment-flow</span> (312ms > 300ms)</div>
<div class="feed-meta">09:12:44 &middot; 2 min ago</div>
</div>
</div>
<div class="feed-item">
<div class="feed-icon feed-error">&#10005;</div>
<div class="feed-content">
<div class="feed-text"><strong>Error</strong> &mdash; <span class="feed-route">retry-handler</span> exhausted retries: CamelExecutionException after 3 attempts</div>
<div class="feed-meta">09:11:18 &middot; 3 min ago</div>
</div>
</div>
<div class="feed-item">
<div class="feed-icon feed-warn">&#9651;</div>
<div class="feed-content">
<div class="feed-text"><strong>Warning</strong> &mdash; <span class="feed-route">payment-flow</span> latency spike: P95 jumped from 198ms to 245ms</div>
<div class="feed-meta">09:08:32 &middot; 6 min ago</div>
</div>
</div>
<div class="feed-item">
<div class="feed-icon feed-error">&#10005;</div>
<div class="feed-content">
<div class="feed-text"><strong>Error</strong> &mdash; <span class="feed-route">payment-flow</span> HTTP 504 from payment gateway (timeout after 5000ms)</div>
<div class="feed-meta">09:06:11 &middot; 8 min ago</div>
</div>
</div>
<div class="feed-item">
<div class="feed-icon feed-ok">&#10003;</div>
<div class="feed-content">
<div class="feed-text"><strong>Recovery</strong> &mdash; <span class="feed-route">order-processing</span> error rate returned below 1% threshold</div>
<div class="feed-meta">09:04:55 &middot; 10 min ago</div>
</div>
</div>
<div class="feed-item">
<div class="feed-icon feed-info">&#9432;</div>
<div class="feed-content">
<div class="feed-text"><strong>In-flight high-water</strong> &mdash; 67 concurrent exchanges (capacity 78%). Auto-scaled connection pool.</div>
<div class="feed-meta">07:22:10 &middot; 1h 52m ago</div>
</div>
</div>
<div class="feed-item">
<div class="feed-icon feed-info">&#9654;</div>
<div class="feed-content">
<div class="feed-text"><strong>Shift started</strong> &mdash; Day shift (06:00-18:00). Metrics counters reset.</div>
<div class="feed-meta">06:00:00 &middot; 3h 14m ago</div>
</div>
</div>
<div class="feed-item">
<div class="feed-icon feed-warn">&#9651;</div>
<div class="feed-content">
<div class="feed-text"><strong>Route stopped</strong> &mdash; <span class="feed-route">dead-letter-processor</span> manually stopped by operator hendrik</div>
<div class="feed-meta">05:48:22 &middot; 3h 26m ago</div>
</div>
</div>
</div>
</div>
</div><!-- /content -->
</div><!-- /main -->
</div><!-- /app -->
</body>
</html>