opencastle 0.32.6 → 0.32.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +79 -4
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/engine.test.js +11 -9
- package/dist/cli/convoy/engine.test.js.map +1 -1
- package/dist/dashboard/scripts/etl.js +17 -2
- package/dist/dashboard/scripts/etl.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/convoy/engine.test.ts +11 -9
- package/src/cli/convoy/engine.ts +78 -4
- package/src/dashboard/dist/_astro/index.6xXNs4L2.css +1 -0
- package/src/dashboard/dist/data/convoy-list.json +27 -13
- package/src/dashboard/dist/data/convoys/demo-api-v2.json +16 -10
- package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +25 -15
- package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +35 -21
- package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +17 -11
- package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +8 -4
- package/src/dashboard/dist/data/convoys/demo-docs-update.json +13 -9
- package/src/dashboard/dist/data/convoys/demo-perf-opt.json +22 -14
- package/src/dashboard/dist/data/overall-stats.json +36 -2
- package/src/dashboard/dist/index.html +149 -93
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/public/data/convoy-list.json +27 -13
- package/src/dashboard/public/data/convoys/demo-api-v2.json +16 -10
- package/src/dashboard/public/data/convoys/demo-auth-revamp.json +25 -15
- package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +35 -21
- package/src/dashboard/public/data/convoys/demo-data-pipeline.json +17 -11
- package/src/dashboard/public/data/convoys/demo-deploy-ci.json +8 -4
- package/src/dashboard/public/data/convoys/demo-docs-update.json +13 -9
- package/src/dashboard/public/data/convoys/demo-perf-opt.json +22 -14
- package/src/dashboard/public/data/overall-stats.json +36 -2
- package/src/dashboard/scripts/etl.ts +15 -3
- package/src/dashboard/scripts/generate-demo-db.ts +42 -34
- package/src/dashboard/src/pages/index.astro +159 -112
- package/src/dashboard/src/styles/dashboard.css +58 -3
- package/src/dashboard/dist/_astro/index.wyN9vmjZ.css +0 -1
- package/src/dashboard/dist/data/convoys/demo-convoy-1.json +0 -111
- package/src/dashboard/dist/data/convoys/demo-convoy-2.json +0 -72
- package/src/dashboard/dist/data/pipelines.ndjson +0 -5285
- package/src/dashboard/public/data/convoys/demo-convoy-1.json +0 -111
- package/src/dashboard/public/data/convoys/demo-convoy-2.json +0 -72
- package/src/dashboard/public/data/pipelines.ndjson +0 -5285
|
@@ -81,6 +81,14 @@ try {
|
|
|
81
81
|
<span class="overall-kpi__label">Running Now</span>
|
|
82
82
|
<span class="overall-kpi__value">—</span>
|
|
83
83
|
</div>
|
|
84
|
+
<div class="overall-kpi" id="overall-total-tasks">
|
|
85
|
+
<span class="overall-kpi__label">Total Tasks</span>
|
|
86
|
+
<span class="overall-kpi__value">—</span>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="overall-kpi" id="overall-total-retries">
|
|
89
|
+
<span class="overall-kpi__label">Total Retries</span>
|
|
90
|
+
<span class="overall-kpi__value">—</span>
|
|
91
|
+
</div>
|
|
84
92
|
<div class="overall-kpi" id="overall-success-rate">
|
|
85
93
|
<span class="overall-kpi__label">Success Rate</span>
|
|
86
94
|
<span class="overall-kpi__value">—</span>
|
|
@@ -97,14 +105,6 @@ try {
|
|
|
97
105
|
<span class="overall-kpi__label">Total Cost</span>
|
|
98
106
|
<span class="overall-kpi__value">—</span>
|
|
99
107
|
</div>
|
|
100
|
-
<div class="overall-kpi" id="overall-total-tasks">
|
|
101
|
-
<span class="overall-kpi__label">Total Tasks</span>
|
|
102
|
-
<span class="overall-kpi__value">—</span>
|
|
103
|
-
</div>
|
|
104
|
-
<div class="overall-kpi" id="overall-total-retries">
|
|
105
|
-
<span class="overall-kpi__label">Total Retries</span>
|
|
106
|
-
<span class="overall-kpi__value">—</span>
|
|
107
|
-
</div>
|
|
108
108
|
</div>
|
|
109
109
|
</section>
|
|
110
110
|
|
|
@@ -201,7 +201,7 @@ try {
|
|
|
201
201
|
<div class="chart-card__body" id="pipeline-view"></div>
|
|
202
202
|
</section>
|
|
203
203
|
|
|
204
|
-
<!--
|
|
204
|
+
<!-- Sessions by Agent -->
|
|
205
205
|
<div class="charts-row" id="agent-section" data-nav-section>
|
|
206
206
|
<section class="chart-card">
|
|
207
207
|
<div class="chart-card__header">
|
|
@@ -210,12 +210,12 @@ try {
|
|
|
210
210
|
</div>
|
|
211
211
|
<div class="chart-card__body" id="agent-chart"></div>
|
|
212
212
|
</section>
|
|
213
|
-
<section class="chart-card">
|
|
213
|
+
<section class="chart-card" id="model-section" data-nav-section>
|
|
214
214
|
<div class="chart-card__header">
|
|
215
|
-
<h2 class="chart-card__title">
|
|
216
|
-
<p class="chart-card__desc">
|
|
215
|
+
<h2 class="chart-card__title">Model Usage</h2>
|
|
216
|
+
<p class="chart-card__desc">Tasks by model</p>
|
|
217
217
|
</div>
|
|
218
|
-
<div class="chart-card__body" id="
|
|
218
|
+
<div class="chart-card__body" id="model-chart"></div>
|
|
219
219
|
</section>
|
|
220
220
|
</div>
|
|
221
221
|
|
|
@@ -237,15 +237,6 @@ try {
|
|
|
237
237
|
</section>
|
|
238
238
|
</div>
|
|
239
239
|
|
|
240
|
-
<!-- Model Usage -->
|
|
241
|
-
<section class="chart-card" id="model-section" data-nav-section>
|
|
242
|
-
<div class="chart-card__header">
|
|
243
|
-
<h2 class="chart-card__title">Model Usage</h2>
|
|
244
|
-
<p class="chart-card__desc">Tasks by model</p>
|
|
245
|
-
</div>
|
|
246
|
-
<div class="chart-card__body" id="model-chart"></div>
|
|
247
|
-
</section>
|
|
248
|
-
|
|
249
240
|
<!-- Quality Section -->
|
|
250
241
|
<section class="chart-card" id="quality-section" data-nav-section>
|
|
251
242
|
<div class="chart-card__header">
|
|
@@ -920,6 +911,18 @@ try {
|
|
|
920
911
|
if (sumTokens > 0) {
|
|
921
912
|
setKpiValue('overall-total-tokens', formatTokens(sumTokens));
|
|
922
913
|
setKpiValue('overall-total-cost', '$' + sumCost.toFixed(2));
|
|
914
|
+
} else {
|
|
915
|
+
// Final fallback: estimate from task count (~5000 tokens/task at ~$8/1M tokens avg)
|
|
916
|
+
var taskTotal = (stats.taskTotals && stats.taskTotals.totalTasks) || 0;
|
|
917
|
+
if (taskTotal > 0) {
|
|
918
|
+
var estTokens = taskTotal * 5000;
|
|
919
|
+
var estCost = estTokens * 0.000008;
|
|
920
|
+
setKpiValue('overall-total-tokens', '~' + formatTokens(estTokens));
|
|
921
|
+
setKpiValue('overall-total-cost', '~$' + estCost.toFixed(2));
|
|
922
|
+
} else {
|
|
923
|
+
setKpiValue('overall-total-tokens', 'N/A (estimated)');
|
|
924
|
+
setKpiValue('overall-total-cost', 'N/A (estimated)');
|
|
925
|
+
}
|
|
923
926
|
}
|
|
924
927
|
}
|
|
925
928
|
}
|
|
@@ -958,14 +961,14 @@ try {
|
|
|
958
961
|
return;
|
|
959
962
|
}
|
|
960
963
|
var maxCount = Math.max.apply(null, timeline.map(function(d) { return d.count; }));
|
|
961
|
-
var html = '<div class="activity-timeline">';
|
|
964
|
+
var html = '<div class="activity-timeline activity-timeline--vertical">';
|
|
962
965
|
for (var i = 0; i < timeline.length; i++) {
|
|
963
966
|
var d = timeline[i];
|
|
964
967
|
var pct = maxCount > 0 ? Math.round((d.count / maxCount) * 100) : 0;
|
|
965
|
-
html += '<div class="
|
|
966
|
-
'<span class="
|
|
967
|
-
'<div class="
|
|
968
|
-
'<span class="
|
|
968
|
+
html += '<div class="vbar-col">' +
|
|
969
|
+
'<span class="vbar-value">' + d.count + '</span>' +
|
|
970
|
+
'<div class="vbar-track"><div class="vbar-fill" style="height:' + pct + '%"></div></div>' +
|
|
971
|
+
'<span class="vbar-label">' + formatShortDate(d.date) + '</span>' +
|
|
969
972
|
'</div>';
|
|
970
973
|
}
|
|
971
974
|
html += '</div>';
|
|
@@ -993,13 +996,22 @@ try {
|
|
|
993
996
|
for (var i = 0; i < sessions.length; i++) {
|
|
994
997
|
var s = sessions[i];
|
|
995
998
|
var outcomeClass = s.outcome === 'success' ? 'outcome-badge--success' : s.outcome === 'failed' ? 'outcome-badge--failed' : 'outcome-badge--partial';
|
|
999
|
+
var filesVal = s.files_changed;
|
|
1000
|
+
var filesCell;
|
|
1001
|
+
if (Array.isArray(s.file_partition) && s.file_partition.length > 0) {
|
|
1002
|
+
filesCell = '<span class="tooltip-trigger" data-tooltip="' + escapeHtml(s.file_partition.join('\n')) + '">' + s.file_partition.length + ' file' + (s.file_partition.length !== 1 ? 's' : '') + '</span>';
|
|
1003
|
+
} else if (typeof filesVal === 'number') {
|
|
1004
|
+
filesCell = String(filesVal);
|
|
1005
|
+
} else {
|
|
1006
|
+
filesCell = '0';
|
|
1007
|
+
}
|
|
996
1008
|
tbody += '<tr>' +
|
|
997
|
-
'<td class="td-agent">' + escapeHtml(s.agent || '\u2014') + '</td>' +
|
|
998
|
-
'<td class="td-task">' + escapeHtml(s.task || '\u2014') + '</td>' +
|
|
1009
|
+
'<td class="td-agent" title="' + escapeHtml(s.agent || '') + '">' + escapeHtml(s.agent || '\u2014') + '</td>' +
|
|
1010
|
+
'<td class="td-task" title="' + escapeHtml(s.task || '') + '">' + escapeHtml(s.task || '\u2014') + '</td>' +
|
|
999
1011
|
'<td><span class="outcome-badge ' + outcomeClass + '">' + escapeHtml(s.outcome || '\u2014') + '</span></td>' +
|
|
1000
1012
|
'<td>' + (s.duration_min != null ? s.duration_min + 'm' : '\u2014') + '</td>' +
|
|
1001
1013
|
'<td>' + (s.retries || 0) + '</td>' +
|
|
1002
|
-
'<td>' +
|
|
1014
|
+
'<td>' + filesCell + '</td>' +
|
|
1003
1015
|
'<td>' + (s.timestamp ? formatTime(s.timestamp) : '\u2014') + '</td>' +
|
|
1004
1016
|
'</tr>';
|
|
1005
1017
|
}
|
|
@@ -1033,7 +1045,6 @@ try {
|
|
|
1033
1045
|
if (tasksSection) tasksSection.style.display = '';
|
|
1034
1046
|
renderDetailPipeline(detail.tasks || []);
|
|
1035
1047
|
renderDetailAgentChart(detail.tasks || []);
|
|
1036
|
-
renderDetailOutcomeChart(detail.tasks || []);
|
|
1037
1048
|
renderDetailTierChart(detail.tasks || []);
|
|
1038
1049
|
renderDetailMechanismChart(detail.events || []);
|
|
1039
1050
|
renderDetailModelChart(detail.tasks || []);
|
|
@@ -1264,8 +1275,7 @@ try {
|
|
|
1264
1275
|
|
|
1265
1276
|
// ── Convoy Detail: Derive Tier from Model ────────────────
|
|
1266
1277
|
|
|
1267
|
-
function deriveTier(model) {
|
|
1268
|
-
if (!model) return 'unknown';
|
|
1278
|
+
function deriveTier(model, agent) {
|
|
1269
1279
|
var TIER_MAP = {
|
|
1270
1280
|
'claude-opus-4-6': 'premium',
|
|
1271
1281
|
'claude-opus-4': 'premium',
|
|
@@ -1279,15 +1289,47 @@ try {
|
|
|
1279
1289
|
'gemini-3.1-pro': 'standard',
|
|
1280
1290
|
'gemini-3.0-flash': 'economy',
|
|
1281
1291
|
};
|
|
1282
|
-
if (
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1292
|
+
if (model && model !== 'copilot' && model !== 'cursor' && model !== 'opencode' && model !== 'claude') {
|
|
1293
|
+
if (TIER_MAP[model]) return TIER_MAP[model];
|
|
1294
|
+
var m = model.toLowerCase();
|
|
1295
|
+
if (m.includes('opus')) return 'premium';
|
|
1296
|
+
if (m.includes('sonnet') || m.includes('pro')) return 'standard';
|
|
1297
|
+
if (m.includes('haiku') || m.includes('flash') || m.includes('mini')) return 'economy';
|
|
1298
|
+
}
|
|
1299
|
+
// Derive from agent name when model is a generic adapter name
|
|
1300
|
+
if (agent) {
|
|
1301
|
+
var a = (typeof agent === 'string' ? agent : '').toLowerCase().replace(/[\s\/]+/g, '-');
|
|
1302
|
+
if (a === 'architect' || a === 'team-lead' || a.includes('team-lead')) return 'premium';
|
|
1303
|
+
if (a === 'reviewer' || a === 'documentation-writer' || a === 'seo-specialist' || a === 'session-guard') return 'economy';
|
|
1304
|
+
if (['developer', 'ui-ux-expert', 'testing-expert', 'security-expert', 'performance-expert', 'devops-expert', 'data-expert', 'api-designer', 'copywriter', 'release-manager', 'content-engineer', 'database-engineer', 'researcher'].includes(a)) return 'standard';
|
|
1305
|
+
}
|
|
1306
|
+
if (!model) return 'unknown';
|
|
1288
1307
|
return 'utility';
|
|
1289
1308
|
}
|
|
1290
1309
|
|
|
1310
|
+
function deriveModel(model, agent) {
|
|
1311
|
+
if (model && model !== 'copilot' && model !== 'cursor' && model !== 'opencode' && model !== 'claude') {
|
|
1312
|
+
return model;
|
|
1313
|
+
}
|
|
1314
|
+
if (agent) {
|
|
1315
|
+
var AGENT_MODEL = {
|
|
1316
|
+
'developer': 'claude-sonnet-4-6', 'ui-ux-expert': 'claude-sonnet-4-6',
|
|
1317
|
+
'testing-expert': 'claude-sonnet-4-6', 'security-expert': 'claude-sonnet-4-6',
|
|
1318
|
+
'performance-expert': 'claude-sonnet-4-6', 'devops-expert': 'claude-sonnet-4-6',
|
|
1319
|
+
'data-expert': 'claude-sonnet-4-6', 'api-designer': 'claude-sonnet-4-6',
|
|
1320
|
+
'copywriter': 'claude-sonnet-4-6', 'release-manager': 'claude-sonnet-4-6',
|
|
1321
|
+
'content-engineer': 'claude-sonnet-4-6', 'database-engineer': 'claude-sonnet-4-6',
|
|
1322
|
+
'researcher': 'claude-sonnet-4-6',
|
|
1323
|
+
'architect': 'claude-opus-4-6', 'team-lead': 'claude-opus-4-6',
|
|
1324
|
+
'reviewer': 'claude-haiku-3-5', 'documentation-writer': 'claude-haiku-3-5',
|
|
1325
|
+
'seo-specialist': 'claude-haiku-3-5', 'session-guard': 'claude-haiku-3-5',
|
|
1326
|
+
};
|
|
1327
|
+
var a = agent.toLowerCase().replace(/[\s\/]+/g, '-');
|
|
1328
|
+
return AGENT_MODEL[a] || model || 'unknown';
|
|
1329
|
+
}
|
|
1330
|
+
return model || 'unknown';
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1291
1333
|
// ── Convoy Detail: Pipeline View ─────────────────────────
|
|
1292
1334
|
|
|
1293
1335
|
function renderDetailPipeline(tasks) {
|
|
@@ -1301,26 +1343,34 @@ try {
|
|
|
1301
1343
|
|
|
1302
1344
|
var phases = {};
|
|
1303
1345
|
tasks.forEach(function(t) {
|
|
1304
|
-
var p = t.phase != null ? t.phase :
|
|
1305
|
-
if (!phases[p]) phases[p] = 0;
|
|
1306
|
-
phases[p]++;
|
|
1346
|
+
var p = t.phase != null ? t.phase : 0;
|
|
1347
|
+
if (!phases[p]) phases[p] = { count: 0, done: 0, failed: 0, running: 0 };
|
|
1348
|
+
phases[p].count++;
|
|
1349
|
+
if (t.status === 'done') phases[p].done++;
|
|
1350
|
+
else if (['failed', 'gate-failed', 'timed-out', 'hook-failed'].includes(t.status)) phases[p].failed++;
|
|
1351
|
+
else if (t.status === 'running' || t.status === 'assigned') phases[p].running++;
|
|
1307
1352
|
});
|
|
1308
1353
|
|
|
1309
|
-
var
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
{
|
|
1313
|
-
{
|
|
1354
|
+
var phaseNums = Object.keys(phases).map(Number).sort(function(a, b) { return a - b; });
|
|
1355
|
+
var PHASE_LABELS = ['Foundation', 'Integration', 'Validation', 'QA Gate', 'Phase 5', 'Phase 6'];
|
|
1356
|
+
var PHASE_ICONS = [
|
|
1357
|
+
{ iconClass: 'pending', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><line x1="8" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="13" y2="14"/></svg>' },
|
|
1358
|
+
{ iconClass: 'active', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>' },
|
|
1359
|
+
{ iconClass: 'review', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>' },
|
|
1360
|
+
{ iconClass: 'done', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>' },
|
|
1314
1361
|
];
|
|
1315
1362
|
|
|
1316
1363
|
el.innerHTML =
|
|
1317
1364
|
'<div class="pipeline">' +
|
|
1318
|
-
|
|
1365
|
+
phaseNums.map(function(pn, i) {
|
|
1366
|
+
var label = PHASE_LABELS[pn] !== undefined ? PHASE_LABELS[pn] : ('Phase ' + pn);
|
|
1367
|
+
var iconIdx = Math.min(i, PHASE_ICONS.length - 1);
|
|
1368
|
+
var phaseData = phases[pn];
|
|
1319
1369
|
return (i > 0 ? '<div class="pipeline-arrow">\u2192</div>' : '') +
|
|
1320
1370
|
'<div class="pipeline-stage">' +
|
|
1321
|
-
'<div class="pipeline-stage__icon pipeline-stage__icon--' +
|
|
1322
|
-
'<span class="pipeline-stage__count">' +
|
|
1323
|
-
'<span class="pipeline-stage__label">' +
|
|
1371
|
+
'<div class="pipeline-stage__icon pipeline-stage__icon--' + PHASE_ICONS[iconIdx].iconClass + '">' + PHASE_ICONS[iconIdx].icon + '</div>' +
|
|
1372
|
+
'<span class="pipeline-stage__count">' + phaseData.count + '</span>' +
|
|
1373
|
+
'<span class="pipeline-stage__label">' + label + '</span>' +
|
|
1324
1374
|
'</div>';
|
|
1325
1375
|
}).join('') +
|
|
1326
1376
|
'</div>';
|
|
@@ -1380,7 +1430,7 @@ try {
|
|
|
1380
1430
|
|
|
1381
1431
|
var tierCounts = {};
|
|
1382
1432
|
tasks.forEach(function(t) {
|
|
1383
|
-
var tier = deriveTier(t.model);
|
|
1433
|
+
var tier = deriveTier(t.model, t.agent);
|
|
1384
1434
|
tierCounts[tier] = (tierCounts[tier] || 0) + 1;
|
|
1385
1435
|
});
|
|
1386
1436
|
|
|
@@ -1424,7 +1474,7 @@ try {
|
|
|
1424
1474
|
|
|
1425
1475
|
var mechCounts = {};
|
|
1426
1476
|
events.forEach(function(e) {
|
|
1427
|
-
if (e.type === 'task_assigned' || e.type === 'task_started') {
|
|
1477
|
+
if (e.type === 'task_assigned' || e.type === 'task_started' || e.type === 'delegation') {
|
|
1428
1478
|
var mech = (e.data && e.data.mechanism) || 'unknown';
|
|
1429
1479
|
mechCounts[mech] = (mechCounts[mech] || 0) + 1;
|
|
1430
1480
|
}
|
|
@@ -1461,39 +1511,6 @@ try {
|
|
|
1461
1511
|
'<div class="donut-container"><div class="donut-wrap"><svg viewBox="0 0 180 180" class="donut-svg">' + circles.join('') + '</svg><div class="donut-center"><span class="donut-total">' + total + '</span><span class="donut-total-label">total</span></div></div><div class="donut-legend">' + legend + '</div></div>';
|
|
1462
1512
|
}
|
|
1463
1513
|
|
|
1464
|
-
// ── Convoy Detail: Delegation Outcome Chart ──────────────
|
|
1465
|
-
|
|
1466
|
-
function renderDetailOutcomeChart(tasks) {
|
|
1467
|
-
var el = document.getElementById('delegation-outcome-chart');
|
|
1468
|
-
if (!el) return;
|
|
1469
|
-
|
|
1470
|
-
if (!tasks || tasks.length === 0) {
|
|
1471
|
-
el.innerHTML = emptyStateHtml('outcomes', 'No outcome data yet', 'Task outcome distribution will be shown here.');
|
|
1472
|
-
return;
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
var OUTCOME_COLORS = { done: '#22c55e', running: '#3b82f6', pending: '#64748b', assigned: '#64748b', failed: '#ef4444', 'gate-failed': '#f59e0b', 'timed-out': '#a78bfa', 'hook-failed': '#64748b', 'review-blocked': '#f59e0b', skipped: '#94a3b8' };
|
|
1476
|
-
|
|
1477
|
-
var outcomeCounts = {};
|
|
1478
|
-
tasks.forEach(function(t) {
|
|
1479
|
-
var outcome = t.status || 'unknown';
|
|
1480
|
-
outcomeCounts[outcome] = (outcomeCounts[outcome] || 0) + 1;
|
|
1481
|
-
});
|
|
1482
|
-
|
|
1483
|
-
var outcomes = Object.entries(outcomeCounts).sort(function(a, b) { return b[1] - a[1]; });
|
|
1484
|
-
var maxCount = Math.max.apply(null, outcomes.map(function(o) { return o[1]; }));
|
|
1485
|
-
|
|
1486
|
-
el.innerHTML = outcomes.map(function(entry) {
|
|
1487
|
-
var name = entry[0];
|
|
1488
|
-
var count = entry[1];
|
|
1489
|
-
return '<div class="bar-row">' +
|
|
1490
|
-
'<span class="bar-label">' + escapeHtml(name) + '</span>' +
|
|
1491
|
-
'<div class="bar-track"><div class="bar-segment" style="width: ' + ((count / maxCount) * 100).toFixed(1) + '%; background: ' + (OUTCOME_COLORS[name] || '#64748b') + '"></div></div>' +
|
|
1492
|
-
'<span class="bar-value">' + count + '</span>' +
|
|
1493
|
-
'</div>';
|
|
1494
|
-
}).join('');
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
1514
|
// ── Convoy Detail: Model Chart ───────────────────────────
|
|
1498
1515
|
|
|
1499
1516
|
function renderDetailModelChart(tasks) {
|
|
@@ -1507,7 +1524,8 @@ try {
|
|
|
1507
1524
|
|
|
1508
1525
|
var modelCounts = {};
|
|
1509
1526
|
tasks.forEach(function(t) {
|
|
1510
|
-
|
|
1527
|
+
var model = deriveModel(t.model, t.agent);
|
|
1528
|
+
if (model) modelCounts[model] = (modelCounts[model] || 0) + 1;
|
|
1511
1529
|
});
|
|
1512
1530
|
|
|
1513
1531
|
var models = Object.entries(modelCounts).sort(function(a, b) { return b[1] - a[1]; });
|
|
@@ -1831,10 +1849,13 @@ try {
|
|
|
1831
1849
|
section.style.display = '';
|
|
1832
1850
|
|
|
1833
1851
|
var d = detail.drift || {};
|
|
1834
|
-
|
|
1852
|
+
// drift_score represents on-plan confidence (0.95 = 95% on-plan). Invert to deviation.
|
|
1853
|
+
var maxScoreRaw = d.max_drift_score;
|
|
1854
|
+
var deviationPct = maxScoreRaw != null ? Math.round((1 - maxScoreRaw) * 100) : 0;
|
|
1855
|
+
var tasksWithDrift = d.tasks_with_drift != null ? d.tasks_with_drift : 0;
|
|
1835
1856
|
var driftCards = [
|
|
1836
|
-
{ label: 'Tasks With Drift', value:
|
|
1837
|
-
{ label: 'Max
|
|
1857
|
+
{ label: 'Tasks With Drift', value: tasksWithDrift, tooltip: 'Tasks where the agent was checked for plan deviation.', mod: tasksWithDrift > 0 ? 'waiting' : 'done' },
|
|
1858
|
+
{ label: 'Max Deviation', value: deviationPct + '%', tooltip: 'Highest deviation from the original plan. 0% = perfectly on-track, 100% = completely off-plan.', mod: deviationPct > 30 ? 'errors' : deviationPct > 10 ? 'waiting' : 'done' },
|
|
1838
1859
|
{ label: 'Drift Retried', value: d.drift_retried_tasks != null ? d.drift_retried_tasks : 0, tooltip: 'Tasks that were retried because drift exceeded the allowed threshold.', mod: 'running' },
|
|
1839
1860
|
];
|
|
1840
1861
|
|
|
@@ -1879,44 +1900,70 @@ try {
|
|
|
1879
1900
|
var artifacts = detail.artifacts || [];
|
|
1880
1901
|
var artifactCount = artifacts.length;
|
|
1881
1902
|
|
|
1903
|
+
// Fallback: count unique files touched across all tasks
|
|
1904
|
+
var fileSet = {};
|
|
1905
|
+
var tasks = detail.tasks || [];
|
|
1906
|
+
tasks.forEach(function(t) {
|
|
1907
|
+
if (t.files && Array.isArray(t.files)) {
|
|
1908
|
+
t.files.forEach(function(f) { fileSet[f] = true; });
|
|
1909
|
+
}
|
|
1910
|
+
});
|
|
1911
|
+
var fileCount = Object.keys(fileSet).length;
|
|
1912
|
+
var displayCount = artifactCount > 0 ? artifactCount : fileCount;
|
|
1913
|
+
var countLabel = artifactCount > 0 ? 'Artifacts' : 'Files Touched';
|
|
1914
|
+
var countTooltip = artifactCount > 0 ? 'Structured outputs produced by tasks.' : 'Files created or modified by tasks in this convoy.';
|
|
1915
|
+
|
|
1882
1916
|
var cardsEl = document.getElementById('outputs-cards');
|
|
1883
1917
|
if (cardsEl) {
|
|
1884
1918
|
cardsEl.innerHTML =
|
|
1885
1919
|
'<div class="task-summary-card task-summary-card--done">' +
|
|
1886
|
-
'<span class="task-summary-card__label">
|
|
1887
|
-
'<span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info:
|
|
1920
|
+
'<span class="task-summary-card__label">' + countLabel + ' ' +
|
|
1921
|
+
'<span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: ' + escapeHtml(countTooltip) + '" data-tooltip="' + escapeHtml(countTooltip) + '">' + INFO_ICON + '</span>' +
|
|
1888
1922
|
'</span>' +
|
|
1889
|
-
'<span class="task-summary-card__value">' +
|
|
1923
|
+
'<span class="task-summary-card__value">' + displayCount + '</span>' +
|
|
1890
1924
|
'</div>';
|
|
1891
1925
|
}
|
|
1892
1926
|
|
|
1893
1927
|
var tableEl = document.getElementById('artifact-table-wrap');
|
|
1894
1928
|
if (!tableEl) return;
|
|
1895
1929
|
|
|
1896
|
-
if (artifacts.length === 0) {
|
|
1897
|
-
tableEl.innerHTML = emptyStateHtml('pipeline', 'No
|
|
1930
|
+
if (artifacts.length === 0 && fileCount === 0) {
|
|
1931
|
+
tableEl.innerHTML = emptyStateHtml('pipeline', 'No outputs', 'No artifacts or file changes recorded.');
|
|
1898
1932
|
return;
|
|
1899
1933
|
}
|
|
1900
1934
|
|
|
1901
1935
|
var TYPE_BADGE_COLORS = { file: '#64748b', summary: '#3b82f6', json: '#a78bfa' };
|
|
1902
1936
|
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
'<
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1937
|
+
if (artifacts.length > 0) {
|
|
1938
|
+
tableEl.innerHTML =
|
|
1939
|
+
'<table class="sessions-table" style="margin-top:16px">' +
|
|
1940
|
+
'<thead><tr><th scope="col">Name</th><th scope="col">Type</th><th scope="col">Task</th><th scope="col">Created At</th></tr></thead>' +
|
|
1941
|
+
'<tbody>' +
|
|
1942
|
+
artifacts.map(function(a) {
|
|
1943
|
+
var badgeColor = TYPE_BADGE_COLORS[a.type] || '#64748b';
|
|
1944
|
+
var typeBadge = '<span class="artifact-type-badge" style="background:' + badgeColor + '">' + escapeHtml(a.type) + '</span>';
|
|
1945
|
+
var taskCell = a.task_id ? escapeHtml(a.task_id) : '<span style="opacity:0.4">\u2014</span>';
|
|
1946
|
+
var created = a.created_at ? formatTime(a.created_at) : '\u2014';
|
|
1947
|
+
return '<tr>' +
|
|
1948
|
+
'<td>' + escapeHtml(a.name) + '</td>' +
|
|
1949
|
+
'<td>' + typeBadge + '</td>' +
|
|
1950
|
+
'<td class="td-task">' + taskCell + '</td>' +
|
|
1951
|
+
'<td>' + created + '</td>' +
|
|
1952
|
+
'</tr>';
|
|
1953
|
+
}).join('') +
|
|
1954
|
+
'</tbody></table>';
|
|
1955
|
+
} else {
|
|
1956
|
+
var fileEntries = Object.keys(fileSet).sort();
|
|
1957
|
+
tableEl.innerHTML =
|
|
1958
|
+
'<table class="sessions-table" style="margin-top:16px">' +
|
|
1959
|
+
'<thead><tr><th scope="col">File</th><th scope="col">Task</th></tr></thead>' +
|
|
1960
|
+
'<tbody>' +
|
|
1961
|
+
fileEntries.map(function(f) {
|
|
1962
|
+
var ownerTask = tasks.find(function(t) { return t.files && Array.isArray(t.files) && t.files.indexOf(f) >= 0; });
|
|
1963
|
+
return '<tr><td>' + escapeHtml(f) + '</td><td class="td-task">' + (ownerTask ? escapeHtml(ownerTask.id) : '\u2014') + '</td></tr>';
|
|
1964
|
+
}).join('') +
|
|
1965
|
+
'</tbody></table>';
|
|
1966
|
+
}
|
|
1920
1967
|
}
|
|
1921
1968
|
|
|
1922
1969
|
// ── Event Timeline Section ─────────────────────────────
|
|
@@ -409,6 +409,56 @@ body {
|
|
|
409
409
|
font-variant-numeric: tabular-nums;
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
+
/* ---------- Vertical Bar Chart (Activity Timeline) ---------- */
|
|
413
|
+
.activity-timeline--vertical {
|
|
414
|
+
display: flex;
|
|
415
|
+
flex-direction: row;
|
|
416
|
+
gap: 2px;
|
|
417
|
+
align-items: flex-end;
|
|
418
|
+
overflow-x: auto;
|
|
419
|
+
padding: 8px 0 0;
|
|
420
|
+
min-height: 180px;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.vbar-col {
|
|
424
|
+
display: flex;
|
|
425
|
+
flex-direction: column;
|
|
426
|
+
align-items: center;
|
|
427
|
+
min-width: 44px;
|
|
428
|
+
flex-shrink: 0;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.vbar-value {
|
|
432
|
+
font-size: 0.7rem;
|
|
433
|
+
color: var(--text-tertiary);
|
|
434
|
+
margin-bottom: 4px;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.vbar-track {
|
|
438
|
+
width: 28px;
|
|
439
|
+
height: 120px;
|
|
440
|
+
background: var(--bg-tertiary);
|
|
441
|
+
border-radius: 4px 4px 0 0;
|
|
442
|
+
display: flex;
|
|
443
|
+
align-items: flex-end;
|
|
444
|
+
overflow: hidden;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.vbar-fill {
|
|
448
|
+
width: 100%;
|
|
449
|
+
background: var(--accent-blue);
|
|
450
|
+
border-radius: 4px 4px 0 0;
|
|
451
|
+
transition: height 0.3s ease;
|
|
452
|
+
min-height: 2px;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.vbar-label {
|
|
456
|
+
font-size: 0.65rem;
|
|
457
|
+
color: var(--text-tertiary);
|
|
458
|
+
margin-top: 4px;
|
|
459
|
+
white-space: nowrap;
|
|
460
|
+
}
|
|
461
|
+
|
|
412
462
|
/* ---------- Donut Chart (SVG-based) ---------- */
|
|
413
463
|
.donut-container {
|
|
414
464
|
display: flex;
|
|
@@ -906,12 +956,17 @@ body {
|
|
|
906
956
|
.sessions-table .td-agent {
|
|
907
957
|
font-weight: 500;
|
|
908
958
|
color: var(--text-primary);
|
|
959
|
+
max-width: 130px;
|
|
960
|
+
overflow: hidden;
|
|
961
|
+
text-overflow: ellipsis;
|
|
962
|
+
white-space: nowrap;
|
|
909
963
|
}
|
|
910
964
|
|
|
911
965
|
.sessions-table .td-task {
|
|
912
|
-
max-width:
|
|
966
|
+
max-width: 220px;
|
|
913
967
|
overflow: hidden;
|
|
914
968
|
text-overflow: ellipsis;
|
|
969
|
+
white-space: nowrap;
|
|
915
970
|
}
|
|
916
971
|
|
|
917
972
|
.outcome-badge {
|
|
@@ -1781,7 +1836,7 @@ body {
|
|
|
1781
1836
|
}
|
|
1782
1837
|
.overall-stats__grid {
|
|
1783
1838
|
display: grid;
|
|
1784
|
-
grid-template-columns: repeat(
|
|
1839
|
+
grid-template-columns: repeat(4, 1fr);
|
|
1785
1840
|
gap: 0.75rem;
|
|
1786
1841
|
}
|
|
1787
1842
|
|
|
@@ -2877,7 +2932,7 @@ body {
|
|
|
2877
2932
|
flex-shrink: 0;
|
|
2878
2933
|
}
|
|
2879
2934
|
|
|
2880
|
-
.activity-timeline {
|
|
2935
|
+
.activity-timeline:not(.activity-timeline--vertical) {
|
|
2881
2936
|
display: flex;
|
|
2882
2937
|
flex-direction: column;
|
|
2883
2938
|
gap: 4px;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--bg-primary: #0a0a0f;--bg-secondary: #111118;--bg-tertiary: #1a1a24;--bg-card: rgba(255, 255, 255, .03);--bg-card-hover: rgba(255, 255, 255, .06);--text-primary: #f0f0f5;--text-secondary: #8a8a9a;--text-tertiary: #7a7a8e;--text-accent: #a78bfa;--gradient-accent: linear-gradient(135deg, #a78bfa 0%, #6366f1 50%, #3b82f6 100%);--gradient-glow: radial-gradient(ellipse 800px 400px at 50% 0%, rgba(99, 102, 241, .12) 0%, transparent 70%);--border-color: rgba(255, 255, 255, .06);--border-accent: rgba(167, 139, 250, .3);--color-success: #22c55e;--color-partial: #f59e0b;--color-failed: #ef4444;--color-redirected: #64748b;--color-premium: #f59e0b;--color-standard: #a78bfa;--color-utility: #3b82f6;--color-economy: #64748b;--accent-blue: #3b82f6;--accent-purple: #a78bfa;--accent-indigo: #6366f1;--max-width: 1280px;--transition-fast: .15s cubic-bezier(.4, 0, .2, 1);--transition-base: .3s cubic-bezier(.4, 0, .2, 1)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Inter,Roboto,Helvetica,Arial,sans-serif;background-color:var(--bg-primary);color:var(--text-primary);line-height:1.6;overflow-x:hidden;min-height:100vh}.dash-header{position:sticky;top:0;z-index:50;background:#0a0a0fd9;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-bottom:1px solid var(--border-color)}.dash-header__inner{max-width:var(--max-width);margin:0 auto;padding:0 24px;height:56px;display:flex;align-items:center;justify-content:space-between}.dash-header__brand{display:flex;align-items:center;gap:10px}.dash-header__logo-link{display:flex;align-items:center;text-decoration:none}.dash-header__icon{width:32px;height:32px;border-radius:8px;object-fit:contain}.dash-header__title{font-size:1rem;font-weight:600;color:var(--text-primary)}.dash-layout{display:flex;max-width:var(--max-width);margin:0 auto;position:relative}.dash-sidebar{position:sticky;top:56px;height:calc(100vh - 56px);width:180px;flex-shrink:0;padding:24px 0 24px 24px;overflow-y:auto;display:none}@media(min-width:1024px){.dash-sidebar{display:block}}.dash-sidebar__list{list-style:none;display:flex;flex-direction:column;gap:2px}.dash-sidebar__link{display:block;padding:8px 16px;font-size:.8125rem;font-weight:500;color:var(--text-tertiary);text-decoration:none;border-radius:8px;transition:color var(--transition-fast),background var(--transition-fast)}.dash-sidebar__link:hover{color:var(--text-secondary);background:#ffffff0a}.dash-sidebar__link--active{color:var(--text-accent);background:#a78bfa14;font-weight:600}.dash-main{flex:1;min-width:0;max-width:var(--max-width);margin:0 auto;padding:24px;display:flex;flex-direction:column;gap:20px;position:relative}.dash-main:before{content:"";position:fixed;top:0;left:50%;transform:translate(-50%);width:100%;height:600px;background:var(--gradient-glow);pointer-events:none;z-index:0}.dash-main>*{position:relative;z-index:1}[data-nav-section]{scroll-margin-top:72px}.kpi-row{display:grid;grid-template-columns:1fr;gap:12px}@media(min-width:480px){.kpi-row{grid-template-columns:repeat(2,1fr)}}@media(min-width:960px){.kpi-row{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}}.kpi-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;padding:20px 24px;display:flex;flex-direction:column;gap:4px;transition:border-color var(--transition-fast)}.kpi-card:hover{border-color:#ffffff1a}.kpi-card__label{font-size:.75rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.kpi-card__value{font-size:2rem;font-weight:700;color:var(--text-primary);line-height:1.2;letter-spacing:-.02em}.kpi-card__sub{font-size:.75rem;color:var(--text-secondary);display:flex;align-items:center;gap:4px}.kpi-trend{font-weight:600}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-failed)}.kpi-trend--neutral{color:var(--text-tertiary)}.chart-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;transition:border-color var(--transition-fast)}.chart-card:hover{border-color:#ffffff1a}.chart-card__header{padding:20px 24px 8px;display:flex;flex-wrap:wrap;align-items:baseline;gap:6px}.chart-card__title{font-size:.9375rem;font-weight:600;color:var(--text-primary)}.chart-card__desc{font-size:.75rem;color:var(--text-tertiary);margin-top:2px;width:100%}.chart-card__body{padding:16px 24px 24px;min-height:120px}.chart-card__body--table{padding:0}.charts-row{display:grid;grid-template-columns:1fr;gap:20px}@media(min-width:768px){.charts-row{grid-template-columns:repeat(2,1fr)}}.bar-row{display:flex;align-items:center;gap:12px;padding:6px 0}.bar-row+.bar-row{border-top:1px solid rgba(255,255,255,.03)}.bar-label{font-size:.8125rem;color:var(--text-secondary);width:130px;flex-shrink:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bar-track{flex:1;height:24px;background:var(--bg-tertiary);border-radius:6px;display:flex;overflow:hidden}.bar-segment{height:100%;transition:width .8s cubic-bezier(.4,0,.2,1);min-width:0}.bar--success{background:var(--color-success)}.bar--partial{background:var(--color-partial)}.bar--failed{background:var(--color-failed)}.bar--premium{background:var(--color-premium)}.bar--standard{background:var(--color-standard)}.bar--utility{background:var(--color-utility)}.bar--economy{background:var(--color-economy)}.bar--accent{background:var(--accent-blue)}.bar-value{font-size:.8125rem;font-weight:600;color:var(--text-primary);width:36px;text-align:right;flex-shrink:0;font-variant-numeric:tabular-nums}.donut-container{display:flex;align-items:center;justify-content:center;gap:32px;flex-wrap:wrap}.donut-wrap{position:relative;width:180px;height:180px;flex-shrink:0}.donut-svg{width:100%;height:100%}.donut-svg circle{transition:stroke-dasharray .8s cubic-bezier(.4,0,.2,1),stroke-dashoffset .8s cubic-bezier(.4,0,.2,1)}.donut-center{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center}.donut-total{display:block;font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.donut-total-label{display:block;font-size:.6875rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.08em;margin-top:2px}.donut-legend{display:flex;flex-direction:column;gap:10px}.legend-item{display:flex;align-items:center;gap:8px;font-size:.8125rem}.legend-dot{width:10px;height:10px;border-radius:3px;flex-shrink:0}.legend-name{color:var(--text-secondary);text-transform:capitalize}.legend-count{color:var(--text-tertiary);font-variant-numeric:tabular-nums;margin-left:auto}.timeline-svg{width:100%;height:auto;display:block}.timeline-svg text{font-family:inherit}.timeline-legend{display:flex;gap:16px;justify-content:center;margin-top:12px}.timeline-legend__item{display:flex;align-items:center;gap:6px;font-size:.75rem;color:var(--text-tertiary)}.timeline-legend__dot{width:8px;height:8px;border-radius:2px}.pipeline{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:8px 0}.pipeline-stage{flex:1;min-width:140px;display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px 12px;position:relative}.pipeline-stage:not(:last-child):after{content:"";position:absolute;right:-1px;top:50%;transform:translateY(-50%);width:2px;height:40%;background:var(--border-color)}.pipeline-stage__icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:1rem}.pipeline-stage__icon--pending{background:#64748b26;color:#94a3b8;border:1px solid rgba(100,116,139,.2)}.pipeline-stage__icon--active{background:#3b82f626;color:#60a5fa;border:1px solid rgba(59,130,246,.3);animation:pulse-glow 2s ease-in-out infinite}.pipeline-stage__icon--review{background:#f59e0b26;color:#fbbf24;border:1px solid rgba(245,158,11,.3)}.pipeline-stage__icon--done{background:#22c55e26;color:#4ade80;border:1px solid rgba(34,197,94,.3)}@keyframes pulse-glow{0%,to{box-shadow:0 0 #3b82f633}50%{box-shadow:0 0 12px 4px #3b82f626}}.pipeline-stage__count{font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.pipeline-stage__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.04em;font-weight:500}.pipeline-arrow{display:flex;align-items:center;color:var(--text-tertiary);font-size:1.25rem;padding:0 4px;flex-shrink:0}.exec-log{display:flex;flex-direction:column}.exec-step{display:flex;gap:16px;padding:14px 0;position:relative}.exec-step+.exec-step{border-top:1px solid rgba(255,255,255,.03)}.exec-step__indicator{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:32px}.exec-step__dot{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.6875rem;font-weight:700;flex-shrink:0}.exec-step__dot--success{background:#22c55e26;color:var(--color-success);border:1.5px solid rgba(34,197,94,.3)}.exec-step__dot--partial{background:#f59e0b26;color:var(--color-partial);border:1.5px solid rgba(245,158,11,.3)}.exec-step__dot--failed{background:#ef444426;color:var(--color-failed);border:1.5px solid rgba(239,68,68,.3)}.exec-step__line{flex:1;width:1.5px;background:var(--border-color);margin-top:4px}.exec-step__content{flex:1;min-width:0}.exec-step__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.exec-step__agent{font-size:.875rem;font-weight:600;color:var(--text-primary)}.exec-step__badge{display:inline-flex;align-items:center;padding:2px 8px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.exec-step__badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.exec-step__badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.exec-step__badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.exec-step__task{font-size:.8125rem;color:var(--text-secondary);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.exec-step__meta{display:flex;gap:16px;margin-top:6px;font-size:.6875rem;color:var(--text-tertiary)}.exec-step__meta-item{display:flex;align-items:center;gap:4px}.panel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px}.panel-item{background:var(--bg-tertiary);border-radius:8px;padding:16px;display:flex;flex-direction:column;gap:8px;border:1px solid transparent;transition:border-color var(--transition-fast)}.panel-item:hover{border-color:var(--border-color)}.panel-item__header{display:flex;align-items:center;justify-content:space-between}.panel-item__key{font-size:.8125rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.panel-item__verdict{font-size:.6875rem;font-weight:700;padding:2px 8px;border-radius:4px;text-transform:uppercase;letter-spacing:.04em}.panel-item__verdict--pass{background:#22c55e26;color:var(--color-success)}.panel-item__verdict--block{background:#ef444426;color:var(--color-failed)}.panel-item__votes{display:flex;gap:4px}.panel-item__vote{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.625rem;font-weight:700}.panel-item__vote--pass{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.panel-item__vote--block{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.panel-item__fixes{font-size:.6875rem;color:var(--text-tertiary)}.panel-item__meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;padding-top:8px;border-top:1px solid var(--border-color)}.panel-item__meta-item{font-size:.625rem;color:var(--text-tertiary);white-space:nowrap}.sessions-table{width:100%;border-collapse:collapse;font-size:.8125rem}.sessions-table thead{position:sticky;top:0}.sessions-table th{padding:12px 16px;font-size:.6875rem;font-weight:600;color:var(--text-tertiary);text-align:left;text-transform:uppercase;letter-spacing:.06em;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color)}.sessions-table th:last-child,.sessions-table td:last-child{text-align:right}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5){text-align:right}.sessions-table td{padding:10px 16px;color:var(--text-secondary);border-bottom:1px solid rgba(255,255,255,.03);white-space:nowrap}.sessions-table tr:hover td{background:#ffffff05}.sessions-table .td-agent{font-weight:500;color:var(--text-primary)}.sessions-table .td-task{max-width:260px;overflow:hidden;text-overflow:ellipsis}.outcome-badge{display:inline-flex;align-items:center;padding:3px 10px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.outcome-badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.outcome-badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.outcome-badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.td-num{font-variant-numeric:tabular-nums;text-align:right}.td-issue{font-size:.75rem;color:var(--text-accent);font-weight:500;font-variant-numeric:tabular-nums}.loading-skeleton{display:flex;align-items:center;justify-content:center;min-height:200px;color:var(--text-tertiary);font-size:.8125rem}.loading-skeleton:after{content:"Loading data…";animation:fade-pulse 1.5s ease-in-out infinite}@keyframes fade-pulse{0%,to{opacity:.4}50%{opacity:1}}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;text-align:center;gap:12px}.empty-state__icon{font-size:2rem;opacity:.4}.empty-state__text{font-size:.875rem;color:var(--text-tertiary);max-width:320px}.empty-state--enhanced{padding:56px 32px;gap:16px;border:1px dashed rgba(167,139,250,.15);border-radius:12px;background:radial-gradient(ellipse 300px 200px at 50% 30%,rgba(99,102,241,.04) 0%,transparent 70%),var(--bg-tertiary);position:relative;overflow:hidden}.empty-state--enhanced:before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 23px,rgba(255,255,255,.015) 23px,rgba(255,255,255,.015) 24px);pointer-events:none}.empty-state__icon-wrap{width:64px;height:64px;display:flex;align-items:center;justify-content:center;border-radius:16px;background:#a78bfa0f;border:1px solid rgba(167,139,250,.12);color:var(--text-accent);animation:empty-breathe 4s ease-in-out infinite}@keyframes empty-breathe{0%,to{box-shadow:0 0 #a78bfa14;transform:scale(1)}50%{box-shadow:0 0 20px 4px #a78bfa0f;transform:scale(1.03)}}.empty-state__title{font-size:.9375rem;font-weight:600;color:var(--text-secondary);letter-spacing:-.01em}.empty-state__desc{font-size:.8125rem;color:var(--text-tertiary);max-width:380px;line-height:1.55}.kpi-card__hint{color:var(--text-tertiary);font-style:italic;font-size:.6875rem}.kpi-row--empty .kpi-card{border-style:dashed;border-color:#ffffff0a}.kpi-row--empty .kpi-card__value{color:var(--text-tertiary);opacity:.5}.welcome-banner{position:relative;background:var(--bg-secondary);border:1px solid transparent;border-radius:16px;padding:48px 40px;overflow:hidden;z-index:1}.welcome-banner:before{content:"";position:absolute;inset:-1px;border-radius:16px;padding:1px;background:linear-gradient(135deg,#a78bfa4d,#6366f126,#3b82f61a 60%,#a78bfa33);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none;z-index:0}.welcome-banner__glow{position:absolute;top:-60px;left:50%;transform:translate(-50%);width:500px;height:300px;background:radial-gradient(ellipse at center,rgba(167,139,250,.08) 0%,rgba(99,102,241,.04) 40%,transparent 70%);pointer-events:none;z-index:0}.welcome-banner__content{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:20px}.welcome-banner__icon{width:72px;height:72px;display:flex;align-items:center;justify-content:center;border-radius:20px;background:#a78bfa14;border:1px solid rgba(167,139,250,.15);color:var(--text-accent);animation:welcome-float 6s ease-in-out infinite}@keyframes welcome-float{0%,to{transform:translateY(0);box-shadow:0 8px 32px #a78bfa14}50%{transform:translateY(-6px);box-shadow:0 16px 48px #a78bfa1f}}.welcome-banner__title{font-size:1.375rem;font-weight:700;color:var(--text-primary);letter-spacing:-.02em;line-height:1.3}.welcome-banner__subtitle{font-size:.9375rem;color:var(--text-secondary);max-width:480px;line-height:1.6}.welcome-banner__steps{display:flex;gap:20px;margin-top:12px;flex-wrap:wrap;justify-content:center}.welcome-step{display:flex;align-items:flex-start;gap:12px;text-align:left;padding:16px 20px;background:#ffffff05;border:1px solid rgba(255,255,255,.05);border-radius:12px;min-width:200px;max-width:220px;transition:border-color var(--transition-fast),background var(--transition-fast)}.welcome-step:hover{border-color:#a78bfa26;background:#ffffff08}.welcome-step__num{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;color:var(--text-accent);background:#a78bfa1a;border:1px solid rgba(167,139,250,.2);flex-shrink:0}.welcome-step__text{display:flex;flex-direction:column;gap:3px}.welcome-step__text strong{font-size:.8125rem;font-weight:600;color:var(--text-primary)}.welcome-step__text span{font-size:.75rem;color:var(--text-tertiary);line-height:1.4}@media(max-width:640px){.welcome-banner{padding:32px 24px}.welcome-banner__steps{flex-direction:column;align-items:center}.welcome-step{max-width:100%;width:100%}}@keyframes slide-up{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.dash-main>*{animation:slide-up .5s ease-out backwards}.dash-main>*:nth-child(1){animation-delay:0ms}.dash-main>*:nth-child(2){animation-delay:60ms}.dash-main>*:nth-child(3){animation-delay:.12s}.dash-main>*:nth-child(4){animation-delay:.18s}.dash-main>*:nth-child(5){animation-delay:.24s}.dash-main>*:nth-child(6){animation-delay:.3s}.dash-main>*:nth-child(7){animation-delay:.36s}.dash-main>*:nth-child(8){animation-delay:.42s}.dash-main>*:nth-child(9){animation-delay:.48s}.dash-main>*:nth-child(10){animation-delay:.54s}.dash-main>*:nth-child(11){animation-delay:.6s}@media(max-width:640px){.bar-label{width:90px;font-size:.75rem}.donut-container{flex-direction:column;align-items:center}.donut-wrap{width:150px;height:150px}.pipeline{gap:0}.pipeline-stage{min-width:100px;padding:12px 8px}.panel-grid{grid-template-columns:1fr}.sessions-table th:nth-child(3),.sessions-table td:nth-child(3){display:none}}.filter-bar{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;padding:16px 20px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px}.filter-group{display:flex;flex-direction:column;gap:4px;min-width:0}.filter-label{font-size:.6875rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.filter-input,.filter-select{height:34px;padding:0 10px;font-size:.8125rem;color:var(--text-primary);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;outline:none;transition:border-color var(--transition-fast);font-family:inherit}.filter-input:focus,.filter-select:focus{border-color:var(--border-accent)}.filter-input{width:140px;color-scheme:dark}.filter-select{min-width:140px;cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235a5a6e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px}.filter-reset{height:34px;font-size:.75rem}.dash-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;font-size:.8125rem;font-weight:500;font-family:inherit;border:none;border-radius:8px;cursor:pointer;transition:background var(--transition-fast),color var(--transition-fast)}.dash-btn--ghost{color:var(--text-secondary);background:#ffffff0f}.dash-btn--ghost:hover{color:var(--text-primary);background:#ffffff1a}.dash-header__actions{display:flex;align-items:center;gap:8px}@media(max-width:480px){.dash-header__inner{padding:0 12px}.dash-main{padding:12px;gap:12px}.kpi-card,.chart-card__header{padding:14px 16px}.chart-card__body{padding:12px 16px 16px}.filter-bar{padding:12px;gap:8px}.filter-input,.filter-select{width:100%;min-width:unset}.filter-group{flex:1 1 calc(50% - 4px)}.filter-reset{width:100%}.dash-header__title{font-size:.875rem}.exec-step__meta{flex-direction:column;gap:2px}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5),.sessions-table th:nth-child(6),.sessions-table td:nth-child(6),.sessions-table th:nth-child(7),.sessions-table td:nth-child(7),.sessions-table th:nth-child(8),.sessions-table td:nth-child(8){display:none}}@media(max-width:768px){.charts-row{grid-template-columns:1fr}.pipeline{flex-wrap:wrap;gap:8px}.pipeline-arrow{display:none}.pipeline-stage{flex:1 1 calc(50% - 4px);min-width:100px}.tier-chart .donut-container,.donut-container{flex-direction:column;align-items:center}.sessions-table{font-size:.75rem}.sessions-table th,.sessions-table td{padding:8px 6px}}.convoy-overview{display:flex;flex-wrap:wrap;gap:24px;margin-bottom:20px}.convoy-stat{display:flex;flex-direction:column;gap:4px}.convoy-stat__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.convoy-stat__value{font-size:.95rem;color:var(--text-primary)}.convoy-stat__value--error{color:var(--color-failed)}.convoy-progress{display:flex;align-items:center;gap:12px;margin-bottom:20px}.convoy-progress__bar{flex:1;height:8px;background:var(--bg-tertiary);border-radius:4px;overflow:hidden}.convoy-progress__fill{height:100%;background:var(--gradient-accent);border-radius:4px;transition:width var(--transition-base)}.convoy-progress__label{font-size:.8rem;color:var(--text-secondary);white-space:nowrap}.convoy-tasks{margin-top:8px}.convoy-chain{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:1rem 0 1.5rem;scrollbar-width:thin;scrollbar-color:var(--border-color) transparent}.convoy-chain::-webkit-scrollbar{height:4px}.convoy-chain::-webkit-scrollbar-track{background:transparent}.convoy-chain::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:2px}.convoy-chain__connector{display:flex;align-items:center;padding:0 .5rem;color:var(--text-tertiary);font-size:1.1rem;flex-shrink:0}.convoy-chain__node{display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 16px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:10px;min-width:140px;cursor:pointer;transition:background var(--transition-fast),border-color var(--transition-fast),transform var(--transition-fast),box-shadow var(--transition-fast);flex-shrink:0}.convoy-chain__node:hover{background:var(--bg-card-hover);transform:translateY(-2px);box-shadow:0 4px 12px #0000004d}.convoy-chain__node-name{font-size:.8rem;font-weight:600;color:var(--text-primary);text-align:center;word-break:break-word;max-width:120px}.convoy-chain__node-meta{font-size:.72rem;color:var(--text-tertiary);text-align:center}.convoy-chain__node--active{border-color:var(--accent-purple);box-shadow:0 0 0 1px var(--accent-purple),0 0 12px #a78bfa33;animation:convoy-pulse 2s ease-in-out infinite}.convoy-chain__node--done{border-color:#22c55e4d}.convoy-chain__node--failed{border-color:#ef44444d}.convoy-chain__node--pending{opacity:.6}@keyframes convoy-pulse{0%,to{box-shadow:0 0 0 1px var(--accent-purple),0 0 8px #a78bfa26}50%{box-shadow:0 0 0 1px var(--accent-purple),0 0 18px #a78bfa59}}@media(max-width:768px){.convoy-chain{flex-wrap:wrap;gap:8px}.convoy-chain__connector{display:none}.convoy-chain__node{flex:1 1 calc(50% - 4px);min-width:120px}}.convoy-selector{display:flex;align-items:center;gap:.5rem}.convoy-selector__label{font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary)}.convoy-selector__select{appearance:none;background:var(--bg-tertiary);border:1px solid rgba(255,255,255,.08);border-radius:6px;color:var(--text-primary);font-size:.8125rem;padding:.375rem 2rem .375rem .75rem;cursor:pointer;max-width:320px;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%238a8a9a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right .75rem center;transition:border-color .15s}.convoy-selector__select:hover{border-color:#ffffff26}.convoy-selector__select:focus{outline:2px solid var(--accent-purple);outline-offset:2px}.overall-stats{margin-bottom:0;padding:1.25rem;background:var(--bg-secondary);border-radius:12px;border:1px solid rgba(255,255,255,.06)}.overall-stats__header{display:flex;align-items:center;gap:.5rem;margin-bottom:1rem}.overall-stats__title{font-size:1rem;font-weight:600;color:var(--text-primary);margin:0}.overall-stats__grid{display:grid;grid-template-columns:repeat(6,1fr);gap:.75rem}.overall-kpi{display:flex;flex-direction:column;gap:.25rem;padding:.75rem;background:var(--bg-tertiary);border-radius:8px;border:1px solid rgba(255,255,255,.04);transition:border-color .15s}.overall-kpi:hover{border-color:#ffffff1a}.overall-kpi__label{font-size:.6875rem;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);display:flex;align-items:center;gap:.25rem}.overall-kpi__value{font-size:1.375rem;font-weight:700;color:var(--text-primary);font-variant-numeric:tabular-nums}.convoy-detail-header{padding:1.25rem;background:var(--bg-secondary);border-radius:12px;border:1px solid rgba(255,255,255,.06)}.convoy-detail-header__top{display:flex;align-items:center;gap:.75rem;margin-bottom:.75rem}.convoy-detail-header__name{font-size:1.25rem;font-weight:700;color:var(--text-primary);margin:0}.convoy-detail-header__meta{display:flex;flex-wrap:wrap;gap:1rem}.convoy-meta__item{font-size:.8125rem;color:var(--text-secondary)}.status-badge{display:inline-flex;align-items:center;padding:.125rem .625rem;border-radius:999px;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.status-badge--done{background:#22c55e26;color:var(--color-success)}.status-badge--running{background:#3b82f626;color:var(--accent-blue)}.status-badge--failed{background:#ef444426;color:var(--color-failed)}.status-badge--gate-failed,.status-badge--gate_failed{background:#f59e0b26;color:var(--color-partial)}.tooltip-trigger{position:relative;cursor:help;font-size:.75rem;opacity:.5;transition:opacity .15s}.tooltip-trigger:hover{opacity:1}.tooltip-trigger:hover:after{content:attr(data-tooltip);position:absolute;bottom:100%;left:50%;transform:translate(-50%);padding:.5rem .875rem;background:var(--bg-primary);border:1px solid rgba(255,255,255,.12);border-radius:6px;font-size:.8125rem;color:var(--text-primary);max-width:420px;min-width:180px;white-space:normal;text-align:left;line-height:1.5;word-break:break-word;z-index:100;pointer-events:none;box-shadow:0 4px 12px #0006;text-transform:none}.tooltip-trigger:focus{opacity:1;outline:2px solid var(--accent-blue);outline-offset:2px;border-radius:2px}.tooltip-trigger:focus:after,.tooltip-trigger:focus-visible:after{content:attr(data-tooltip);position:absolute;bottom:100%;left:50%;transform:translate(-50%);padding:.5rem .875rem;background:var(--bg-primary);border:1px solid rgba(255,255,255,.12);border-radius:6px;font-size:.8125rem;color:var(--text-primary);max-width:420px;min-width:180px;white-space:normal;text-align:left;line-height:1.5;word-break:break-word;z-index:100;pointer-events:none;box-shadow:0 4px 12px #0006;text-transform:none}.status-badge--pending{background:#64748b26;color:#94a3b8}.status-badge--assigned{background:#3b82f61a;color:#60a5fa}.status-badge--timed-out{background:#ef44441f;color:#f87171}.status-badge--review-blocked{background:#f59e0b1f;color:#fbbf24}.status-badge--skipped{background:#64748b1a;color:#64748b}.status-badge--hook-failed{background:#ef44441a;color:#f87171}.status-badge--disputed{background:#a78bfa26;color:var(--accent-purple)}.status-badge--wait-for-input{background:#f59e0b1a;color:var(--color-partial)}.task-summary-cards{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:20px}.task-summary-card{flex:1 1 140px;display:flex;flex-direction:column;gap:8px;padding:14px 16px;background:var(--bg-card);border:1px solid var(--border-color);border-radius:10px;transition:border-color .15s}.task-summary-card:hover{border-color:#ffffff1f}.task-summary-card__label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--text-tertiary)}.task-summary-card__value{font-size:1.75rem;font-weight:700;line-height:1;color:var(--text-primary)}.task-summary-card--done{border-left:3px solid var(--color-success)}.task-summary-card--running{border-left:3px solid var(--accent-blue)}.task-summary-card--errors{border-left:3px solid var(--color-failed)}.task-summary-card--waiting{border-left:3px solid #94a3b8}.task-summary-card--input{border-left:3px solid var(--color-partial)}.task-table-wrap{overflow-x:auto}.task-table .td-num{text-align:right}.sortable-th{cursor:pointer;user-select:none}.sortable-th:hover{color:var(--text-secondary)}.sortable-th--active{color:var(--text-primary)}.sort-indicator{margin-left:4px;font-size:.5625rem;opacity:.5}.sortable-th--active .sort-indicator{opacity:1;color:var(--accent-blue)}.phase-breakdown{display:flex;flex-direction:column;gap:8px;margin-bottom:20px}.phase-breakdown__row{display:flex;align-items:center;gap:12px}.phase-breakdown__label{font-size:.75rem;font-weight:600;color:var(--text-tertiary);min-width:60px}.phase-breakdown__bar{flex:1;height:10px;background:#ffffff0a;border-radius:5px;overflow:hidden;display:flex}.phase-breakdown__seg{height:100%;transition:width .3s ease}.phase-breakdown__seg--done{background:var(--color-success)}.phase-breakdown__seg--running{background:var(--accent-blue)}.phase-breakdown__seg--waiting{background:#475569}.phase-breakdown__seg--failed{background:var(--color-failed)}.phase-breakdown__count{font-size:.6875rem;color:var(--text-tertiary);min-width:52px;text-align:right}@media(max-width:960px){.overall-stats__grid{grid-template-columns:repeat(3,1fr)}}@media(max-width:640px){.overall-stats__grid{grid-template-columns:repeat(2,1fr)}.convoy-detail-header__name{font-size:1rem}.convoy-selector__select{max-width:200px}}@media(max-width:480px){.overall-stats__grid{grid-template-columns:1fr}}.reliability-empty{font-size:.875rem;color:var(--text-tertiary);padding:12px 0;margin:0}.secret-leak-banner{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;background:#f59e0b1a;border:1px solid rgba(245,158,11,.3);border-radius:8px;margin-top:16px}.secret-leak-banner__icon{font-size:1.25rem;flex-shrink:0;line-height:1.4}.secret-leak-banner__text{display:flex;flex-direction:column;gap:4px}.secret-leak-banner__text strong{font-size:.875rem;font-weight:600;color:var(--color-partial)}.secret-leak-banner__text span{font-size:.8125rem;color:var(--text-secondary)}.artifact-type-badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:.75rem;font-weight:600;color:#fff;text-transform:uppercase;letter-spacing:.03em}.timeline-filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}.timeline-filter-chip{display:inline-flex;align-items:center;padding:6px 14px;border-radius:20px;border:1px solid var(--border);background:transparent;color:var(--text-secondary);font-size:.8125rem;font-weight:500;cursor:pointer;transition:all .15s ease}.timeline-filter-chip:hover{border-color:var(--accent);color:var(--text-primary)}.timeline-filter-chip--active{background:var(--accent);border-color:var(--accent);color:#fff}.event-timeline-row{padding:12px 16px;border-bottom:1px solid var(--border);cursor:pointer;transition:background .15s ease}.event-timeline-row:hover{background:#a78bfa0d}.event-timeline-row--expanded{background:#a78bfa14}.event-timeline-row__main{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.event-timeline-ts{font-size:.8125rem;color:var(--text-secondary);min-width:140px;font-variant-numeric:tabular-nums}.event-type-badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:.6875rem;font-weight:600;color:#fff;text-transform:uppercase;letter-spacing:.03em}.event-timeline-context{font-size:.8125rem;color:var(--text-tertiary, #6b7280);font-family:var(--font-mono, "SF Mono", "Fira Code", monospace)}.event-timeline-detail{margin-top:8px;padding:12px;background:var(--bg-card, #111118);border-radius:6px;border:1px solid var(--border)}.event-timeline-json{font-size:.75rem;color:var(--text-secondary);white-space:pre-wrap;word-break:break-all;margin:0;font-family:var(--font-mono, "SF Mono", "Fira Code", monospace);max-height:300px;overflow-y:auto}@media(max-width:640px){.event-timeline-ts{min-width:auto;font-size:.75rem}.event-timeline-row__main{gap:8px}.timeline-filter-chip{padding:4px 10px;font-size:.75rem}}.dash-btn:focus-visible,.convoy-selector__select:focus-visible,.filter-select:focus-visible,.filter-input:focus-visible,.dash-sidebar__link:focus-visible,.timeline-filter-chip:focus-visible{outline:2px solid var(--accent-blue);outline-offset:2px}.convoy-status-explanation{font-size:.8125rem;color:var(--text-secondary);margin-top:.25rem;margin-bottom:.5rem;font-style:italic}.view-home,.view-convoy-detail{display:flex;flex-direction:column;gap:20px}[data-view-hidden]{display:none!important}.breadcrumbs{display:flex;align-items:center;gap:6px;font-size:.8125rem;color:var(--text-tertiary);flex-wrap:wrap;padding:0;margin-bottom:-8px}.breadcrumbs__link{color:var(--text-secondary);text-decoration:none;transition:color var(--transition-fast);border-bottom:1px solid transparent}.breadcrumbs__link:hover{color:var(--text-accent);border-bottom-color:#a78bfa66}.breadcrumbs__link:focus-visible{outline:2px solid var(--accent-blue);outline-offset:2px;border-radius:2px}.breadcrumbs__separator{color:var(--text-tertiary);opacity:.5;user-select:none;font-size:.75rem}.breadcrumbs__current{color:var(--text-accent);font-weight:500;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.convoy-list-section{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;transition:border-color var(--transition-fast)}.convoy-list-section:hover{border-color:#ffffff1a}.convoy-list-section__header{padding:20px 24px 12px;border-bottom:1px solid var(--border-color)}.convoy-list-section__header h2,.convoy-list-section__header .convoy-list-section__title{font-size:.9375rem;font-weight:600;color:var(--text-primary);margin:0}.convoy-list-section__desc{font-size:.75rem;color:var(--text-tertiary);margin-top:2px}.convoy-list-filters{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;padding:14px 24px;background:#ffffff03;border-bottom:1px solid var(--border-color)}.convoy-list-filters__group{display:flex;flex-direction:column;gap:4px}.convoy-list-filters__group label{font-size:.6875rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.convoy-list-filters__input,.convoy-list-filters__select,.convoy-list-filters__date{height:34px;padding:0 10px;font-size:.8125rem;color:var(--text-primary);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;outline:none;font-family:inherit;transition:border-color var(--transition-fast);color-scheme:dark}.convoy-list-filters__input:focus,.convoy-list-filters__select:focus,.convoy-list-filters__date:focus{border-color:var(--border-accent)}.convoy-list-filters__input{width:180px}.convoy-list-filters__date{width:150px}.convoy-list-filters__select{min-width:140px;cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235a5a6e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px}.convoy-list-filters__reset{height:34px;padding:0 14px;font-size:.75rem;font-weight:500;font-family:inherit;color:var(--text-secondary);background:#ffffff0f;border:none;border-radius:8px;cursor:pointer;transition:background var(--transition-fast),color var(--transition-fast);align-self:flex-end;white-space:nowrap}.convoy-list-filters__reset:hover{background:#ffffff1a;color:var(--text-primary)}.convoy-list-filters__reset:focus-visible{outline:2px solid var(--accent-blue);outline-offset:2px}.convoy-list-table{width:100%;border-collapse:collapse;font-size:.8125rem}.convoy-list-table thead{position:sticky;top:0}.convoy-list-table th{padding:10px 16px;font-size:.6875rem;font-weight:600;color:var(--text-tertiary);text-align:left;text-transform:uppercase;letter-spacing:.06em;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color);white-space:nowrap}.convoy-list-table td{padding:10px 16px;color:var(--text-secondary);border-bottom:1px solid rgba(255,255,255,.03);white-space:nowrap}.convoy-list-table tr{cursor:pointer;transition:background var(--transition-fast)}.convoy-list-table tbody tr:hover td{background:#a78bfa0d;color:var(--text-primary)}.convoy-list-table tbody tr:hover td:first-child{color:var(--text-accent)}.convoy-list-table .td-convoy-name{font-weight:600;color:var(--text-primary);max-width:240px;overflow:hidden;text-overflow:ellipsis}.convoy-list-pagination{display:flex;align-items:center;justify-content:center;gap:12px;padding:16px 0;margin-top:4px}.convoy-list-pagination__info{font-size:.8125rem;color:var(--text-secondary);min-width:100px;text-align:center}.convoy-list-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:48px 24px;text-align:center;color:var(--text-tertiary)}.convoy-list-empty__icon{font-size:2rem;opacity:.35}.convoy-list-empty__text{font-size:.875rem;color:var(--text-tertiary);max-width:300px;line-height:1.5}.convoy-detail-hero{display:flex;flex-direction:column;gap:12px;padding:24px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;position:relative}.convoy-detail-hero:before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:var(--gradient-accent);opacity:.6;pointer-events:none}.convoy-detail-hero__top{display:flex;align-items:center;gap:14px;flex-wrap:wrap;justify-content:space-between}.convoy-chain-toggle{cursor:pointer;user-select:none;font-size:.75rem;margin-right:6px;opacity:.6}.convoy-chain-row{background:#a78bfa0a}.convoy-chain-row td:first-child{font-weight:600}.convoy-chain-child td:first-child{padding-left:28px}.convoy-chain-child td:first-child:before{content:"└ ";opacity:.4}.convoy-detail-hero__title{font-size:1.5rem;font-weight:700;color:var(--text-primary);letter-spacing:-.02em;line-height:1.25;margin:0}.convoy-detail-hero__status{flex-shrink:0}.convoy-detail-hero__status .status-badge{padding:.25rem .875rem;font-size:.75rem;border-radius:8px}.convoy-detail-hero__meta{display:flex;flex-wrap:wrap;gap:20px;padding-top:4px;border-top:1px solid var(--border-color)}.convoy-detail-hero__meta-item{display:flex;flex-direction:column;gap:2px}.convoy-detail-hero__meta-label{font-size:.6875rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.convoy-detail-hero__meta-value{font-size:.875rem;color:var(--text-secondary);font-variant-numeric:tabular-nums}.task-row--clickable{cursor:pointer;transition:background var(--transition-fast)}.task-row--clickable:hover td{background:#a78bfa0a}.task-row--clickable td:first-child{position:relative}.task-row--clickable td:first-child:before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:transparent;transition:background var(--transition-fast)}.task-row--clickable:hover td:first-child:before{background:var(--border-accent)}.task-detail-expand{background:var(--bg-tertiary);border-bottom:1px solid var(--border-color)}.task-detail-expand__inner{padding:16px 20px}.task-detail-expand__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px 24px}.task-detail-expand__field{display:flex;flex-direction:column;gap:3px}.task-detail-expand__label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-tertiary)}.task-detail-expand__value{font-size:.8125rem;color:var(--text-secondary);word-break:break-word;font-variant-numeric:tabular-nums}.task-detail-expand__value code{font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.75rem;background:#ffffff0f;padding:1px 6px;border-radius:4px;color:var(--text-accent)}@media(max-width:768px){.convoy-list-filters{padding:12px 16px;gap:8px}.convoy-list-filters__input,.convoy-list-filters__select,.convoy-list-filters__date{width:100%;min-width:unset}.convoy-list-filters__group{flex:1 1 calc(50% - 4px)}.convoy-list-filters__reset{flex:1 1 100%;width:100%}.convoy-list-section__header{padding:16px 16px 12px}}@media(max-width:480px){.convoy-list-filters__group{flex:1 1 100%}.convoy-detail-hero__title{font-size:1.25rem}.convoy-detail-hero{padding:16px}.task-detail-expand__grid{grid-template-columns:1fr}.breadcrumbs__current{max-width:180px}}.waiting-banner{text-align:center;padding:3rem 2rem;margin-bottom:2rem;border-radius:var(--radius-lg, 12px);background:var(--bg-surface, #1e1e2e);border:1px solid var(--border-subtle, rgba(255,255,255,.06))}.waiting-banner__content{display:flex;flex-direction:column;align-items:center;gap:.75rem}.waiting-banner__spinner{width:32px;height:32px;border:3px solid var(--border-subtle, rgba(255,255,255,.1));border-top-color:var(--accent, #60a5fa);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.waiting-banner__title{font-size:1.25rem;font-weight:600;margin:0;color:var(--text-primary, #e2e8f0)}.waiting-banner__subtitle{font-size:.875rem;color:var(--text-muted, #94a3b8);margin:0}.pipeline-chain-nav{display:flex;align-items:center;gap:.5rem;padding:.5rem 0;margin-bottom:.5rem;flex-wrap:wrap}.pipeline-chain__label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted, #94a3b8);margin-right:.25rem}.pipeline-chain__item{display:inline-flex;align-items:center;gap:.375rem;padding:.25rem .625rem;font-size:.8125rem;border-radius:var(--radius-sm, 6px);border:1px solid var(--border-subtle, rgba(255,255,255,.06));background:var(--bg-surface, #1e1e2e);color:var(--text-secondary, #cbd5e1);cursor:pointer;transition:border-color .15s,background .15s}.pipeline-chain__item:hover{border-color:var(--border-hover, rgba(255,255,255,.15));background:var(--bg-hover, rgba(255,255,255,.04))}.pipeline-chain__item--active{border-color:var(--accent, #60a5fa);background:#60a5fa14;color:var(--text-primary, #e2e8f0)}.pipeline-chain__dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}.activity-timeline{display:flex;flex-direction:column;gap:4px}
|