JonnaMat commited on
Commit
3e7d998
Β·
verified Β·
1 Parent(s): b955498

Initial website

Browse files
Files changed (7) hide show
  1. app.js +604 -0
  2. config.json +139 -0
  3. data.csv +134 -0
  4. embedl_logo_black_bg.svg +21 -0
  5. embedl_logo_white_bg.svg +21 -0
  6. index.html +88 -17
  7. style.css +368 -18
app.js ADDED
@@ -0,0 +1,604 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ─── Benchmark Viewer β€” config-driven from config.json + data.csv ─────────────
2
+
3
+ (async function () {
4
+
5
+ // ─── Theme-aware color helper ─────────────────────────────────────────────────
6
+
7
+ function cssVar(name) {
8
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
9
+ }
10
+
11
+ // ─── Load config + data ───────────────────────────────────────────────────────
12
+
13
+ const [config, csvText] = await Promise.all([
14
+ fetch("config.json").then(r => r.json()),
15
+ fetch("data.csv").then(r => r.text()),
16
+ ]);
17
+
18
+ // ─── Parse CSV ────────────────────────────────────────────────────────────────
19
+
20
+ function parseCSV(text) {
21
+ const lines = text.replace(/\r/g, "").trim().split("\n");
22
+ const headers = lines[0].split(",");
23
+ // Determine which columns are numeric (metrics + numeric filters + numeric display columns)
24
+ const numericCols = new Set(
25
+ config.metrics.map(m => m.column).concat(
26
+ config.filters.filter(f => f.type === "number").map(f => f.column),
27
+ (config.display_columns || []).filter(d => d.type === "number").map(d => d.column)
28
+ )
29
+ );
30
+ return lines.slice(1).map(line => {
31
+ const vals = line.split(",");
32
+ const row = {};
33
+ headers.forEach((h, i) => {
34
+ const raw = (vals[i] || "").trim();
35
+ if (raw === "") {
36
+ row[h] = numericCols.has(h) ? null : "";
37
+ } else if (numericCols.has(h)) {
38
+ row[h] = raw.toUpperCase() === "OOM" ? null : parseFloat(raw);
39
+ } else {
40
+ row[h] = raw;
41
+ }
42
+ });
43
+ return row;
44
+ });
45
+ }
46
+
47
+ const DATA = parseCSV(csvText);
48
+
49
+ // ─── Config shortcuts ─────────────────────────────────────────────────────────
50
+
51
+ const MODEL_COL = config.model_column;
52
+ const FAMILY_COL = config.model_family_column || "";
53
+ const LINK_PREFIX = config.model_link_prefix || "";
54
+ const OPT_ORG = config.optimized_org || "embedl";
55
+ const CHART_CFG = config.chart || {};
56
+ const GROUP_BY = CHART_CFG.group_by || config.filters[config.filters.length - 1]?.column || "";
57
+ const SCENARIOS = CHART_CFG.scenarios || [];
58
+
59
+ function isExternalModel(model) {
60
+ return !model.startsWith(OPT_ORG + "/");
61
+ }
62
+
63
+ // ─── Derive unique values ─────────────────────────────────────────────────────
64
+
65
+ const ALL_MODELS = [...new Set(DATA.map(r => r[MODEL_COL]))];
66
+
67
+ // ─── Model Family Detection ───────────────────────────────────────────────────
68
+
69
+ function detectFamilies() {
70
+ const families = {};
71
+
72
+ if (FAMILY_COL) {
73
+ // Use explicit family column from data
74
+ DATA.forEach(row => {
75
+ const fk = row[FAMILY_COL];
76
+ const model = row[MODEL_COL];
77
+ if (!fk) return;
78
+ if (!families[fk]) families[fk] = { base: fk, models: [] };
79
+ if (!families[fk].models.includes(model)) families[fk].models.push(model);
80
+ });
81
+ } else {
82
+ // Fallback: auto-detect from model name prefixes
83
+ const externalNames = ALL_MODELS.filter(isExternalModel).map(m => m.split("/").pop());
84
+ externalNames.sort((a, b) => b.length - a.length);
85
+
86
+ ALL_MODELS.forEach(model => {
87
+ const shortName = model.split("/").pop();
88
+ if (isExternalModel(model)) {
89
+ if (!families[shortName]) families[shortName] = { base: shortName, models: [] };
90
+ families[shortName].models.push(model);
91
+ } else {
92
+ const match = externalNames.find(base => shortName.startsWith(base));
93
+ const key = match || shortName;
94
+ if (!families[key]) families[key] = { base: key, models: [] };
95
+ families[key].models.push(model);
96
+ }
97
+ });
98
+ }
99
+
100
+ return families;
101
+ }
102
+
103
+ const MODEL_FAMILIES = detectFamilies();
104
+ const ALL_FAMILY_KEYS = Object.keys(MODEL_FAMILIES);
105
+
106
+ // ─── Model colors & short labels ──────────────────────────────────────────────
107
+
108
+ function hexToRgba(hex, alpha) {
109
+ const r = parseInt(hex.slice(1,3),16), g = parseInt(hex.slice(3,5),16), b = parseInt(hex.slice(5,7),16);
110
+ return `rgba(${r},${g},${b},${alpha})`;
111
+ }
112
+
113
+ function buildColorPalette() {
114
+ const barAlpha = 0.75;
115
+ const neutralAlpha = 0.45;
116
+ const teal = cssVar("--teal"), green = cssVar("--green"), pink = cssVar("--pink"),
117
+ purple = cssVar("--purple"), red = cssVar("--red");
118
+ return {
119
+ palette: [
120
+ { bg: hexToRgba(teal, barAlpha), border: teal },
121
+ { bg: hexToRgba(green, barAlpha), border: green },
122
+ { bg: hexToRgba(pink, barAlpha), border: pink },
123
+ { bg: hexToRgba(purple, barAlpha), border: purple },
124
+ { bg: "rgba(255,209,102," + barAlpha + ")", border: "#ffd166" },
125
+ ],
126
+ neutral: { bg: hexToRgba(cssVar("--neutral"), neutralAlpha), border: cssVar("--neutral") },
127
+ };
128
+ }
129
+
130
+ let COLOR_PALETTE, NEUTRAL_COLOR;
131
+
132
+ const MODEL_COLORS = {};
133
+ const MODEL_SHORT = {};
134
+
135
+ function assignModelColors() {
136
+ let colorIdx = 0;
137
+ const { palette, neutral } = buildColorPalette();
138
+ COLOR_PALETTE = palette; NEUTRAL_COLOR = neutral;
139
+ ALL_FAMILY_KEYS.forEach(fk => {
140
+ const family = MODEL_FAMILIES[fk];
141
+ family.models.forEach(model => {
142
+ if (isExternalModel(model)) {
143
+ MODEL_COLORS[model] = NEUTRAL_COLOR;
144
+ } else {
145
+ MODEL_COLORS[model] = COLOR_PALETTE[colorIdx % COLOR_PALETTE.length];
146
+ colorIdx++;
147
+ }
148
+ const name = model.split("/").pop();
149
+ const suffix = name.slice(family.base.length).replace(/^-/, "");
150
+ MODEL_SHORT[model] = suffix || (isExternalModel(model) ? "Original" : name);
151
+ });
152
+ });
153
+ }
154
+ assignModelColors();
155
+
156
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
157
+
158
+ function isOOMRow(row) {
159
+ return config.metrics.every(m => row[m.column] === null);
160
+ }
161
+
162
+ function familyRows(familyKey) {
163
+ const models = new Set((MODEL_FAMILIES[familyKey] || { models: [] }).models);
164
+ return DATA.filter(r => models.has(r[MODEL_COL]));
165
+ }
166
+
167
+ function availableOptions(familyKey) {
168
+ const rows = familyRows(familyKey);
169
+ const opts = {};
170
+ config.filters.forEach(f => {
171
+ const vals = [...new Set(rows.map(r => r[f.column]).filter(v => v !== "" && v !== null && v !== undefined))];
172
+ if (f.type === "number") vals.sort((a, b) => a - b);
173
+ opts[f.column] = vals;
174
+ });
175
+ return opts;
176
+ }
177
+
178
+ // Resolve display label for a filter value
179
+ function valueLabel(filterCfg, val) {
180
+ if (filterCfg.value_labels && filterCfg.value_labels[val]) return filterCfg.value_labels[val];
181
+ if (typeof val === "string") return val.charAt(0).toUpperCase() + val.slice(1);
182
+ return String(val);
183
+ }
184
+
185
+ // Sort models: external (original) first, then optimized
186
+ function sortModels(models) {
187
+ return [...models].sort((a, b) => {
188
+ const aExt = isExternalModel(a) ? 0 : 1;
189
+ const bExt = isExternalModel(b) ? 0 : 1;
190
+ return aExt - bExt || a.localeCompare(b);
191
+ });
192
+ }
193
+
194
+ // ─── Populate page from config ────────────────────────────────────────────────
195
+
196
+ // Hero
197
+ if (config.title) document.getElementById("hero-title").innerHTML = config.title.replace(/^(.*?)(\s\S+)$/, '$1 <span class="accent">$2</span>');
198
+ if (config.subtitle) document.getElementById("hero-sub").textContent = config.subtitle;
199
+
200
+ // Sidebar: model families
201
+ const familyNav = document.getElementById("family-nav");
202
+ function renderSidebar() {
203
+ familyNav.innerHTML = ALL_FAMILY_KEYS.map(fk =>
204
+ `<div class="sidebar-item${fk === filters.family ? " active" : ""}" data-family="${fk}">${fk}</div>`
205
+ ).join("");
206
+ }
207
+
208
+
209
+
210
+ familyNav.addEventListener("click", e => {
211
+ const item = e.target.closest(".sidebar-item");
212
+ if (!item) return;
213
+ filters.family = item.dataset.family;
214
+ renderSidebar();
215
+ updateDependentFilters();
216
+ render();
217
+ });
218
+
219
+ // Build filter groups container dynamically (no family filter here)
220
+ const filtersBar = document.getElementById("filters-bar");
221
+ filtersBar.innerHTML = "";
222
+
223
+ config.filters.forEach(f => {
224
+ filtersBar.appendChild(createFilterGroup(f.label, "filter-" + f.column));
225
+ });
226
+
227
+ // Metric filter (always last)
228
+ filtersBar.appendChild(createFilterGroup("Metric", "filter-metric"));
229
+
230
+ function createFilterGroup(label, id) {
231
+ const div = document.createElement("div");
232
+ div.className = "filter-group";
233
+ div.innerHTML = `<label>${label}</label><div class="btn-group" id="${id}"></div>`;
234
+ return div;
235
+ }
236
+
237
+ // Metric legend
238
+ const legendGrid = document.getElementById("legend-grid");
239
+ legendGrid.innerHTML = config.metrics.map(m =>
240
+ `<div><strong>${m.short || m.column}</strong> ${m.description || m.label}</div>`
241
+ ).join("");
242
+
243
+ // ─── State ────────────────────────────────────────────────────────────────────
244
+
245
+ const filters = { family: ALL_FAMILY_KEYS[0] || "" };
246
+ config.filters.forEach(f => { filters[f.column] = ""; });
247
+ filters.metric = CHART_CFG.default_metric || config.metrics[0]?.column || "";
248
+
249
+ // ─── Render button groups ─────────────────────────────────────────────────────
250
+
251
+ function renderBtnGroup(container, items, activeValue) {
252
+ container.innerHTML = items.map(({ value, label }) =>
253
+ `<button class="btn${String(value) === String(activeValue) ? " active" : ""}" data-value="${value}">${label}</button>`
254
+ ).join("");
255
+ }
256
+
257
+ function populateFilters() {
258
+ renderSidebar();
259
+
260
+ // Metric buttons
261
+ const metricEl = document.getElementById("filter-metric");
262
+ renderBtnGroup(metricEl,
263
+ config.metrics.map(m => ({ value: m.column, label: m.short || m.column })),
264
+ filters.metric
265
+ );
266
+ metricEl.closest(".filter-group").style.display = config.metrics.length <= 1 ? "none" : "";
267
+
268
+ updateDependentFilters();
269
+ }
270
+
271
+ function updateDependentFilters() {
272
+ const opts = availableOptions(filters.family);
273
+
274
+ config.filters.forEach(f => {
275
+ let vals = opts[f.column] || [];
276
+ // Sort by value_labels key order if defined
277
+ if (f.value_labels) {
278
+ const labelOrder = Object.keys(f.value_labels);
279
+ vals = [...vals].sort((a, b) => {
280
+ const ai = labelOrder.indexOf(String(a));
281
+ const bi = labelOrder.indexOf(String(b));
282
+ return (ai === -1 ? Infinity : ai) - (bi === -1 ? Infinity : bi);
283
+ });
284
+ }
285
+ const strVals = vals.map(String);
286
+ if (!strVals.includes(String(filters[f.column]))) {
287
+ filters[f.column] = vals[0] ?? "";
288
+ }
289
+
290
+ // For the group_by filter, add "All" option
291
+ const items = [];
292
+ if (f.column === GROUP_BY) {
293
+ items.push({ value: "all", label: "All" });
294
+ }
295
+ vals.forEach(v => items.push({ value: String(v), label: valueLabel(f, v) }));
296
+
297
+ const el = document.getElementById("filter-" + f.column);
298
+ if (el) {
299
+ renderBtnGroup(el, items, String(filters[f.column]));
300
+ // Hide the entire filter group if only one effective choice
301
+ const effectiveCount = f.column === GROUP_BY ? items.length - 1 : items.length;
302
+ el.closest(".filter-group").style.display = effectiveCount <= 1 ? "none" : "";
303
+ }
304
+ });
305
+ }
306
+
307
+ // ─── Event binding ────────────────────────────────────────────────────────────
308
+
309
+ filtersBar.addEventListener("click", e => {
310
+ const btn = e.target.closest(".btn");
311
+ if (!btn) return;
312
+ const group = btn.closest(".btn-group");
313
+ group.querySelectorAll(".btn").forEach(b => b.classList.remove("active"));
314
+ btn.classList.add("active");
315
+ const key = group.id.replace("filter-", "");
316
+ filters[key] = btn.dataset.value;
317
+ render();
318
+ });
319
+
320
+ // ─── Chart ────────────────────────────────────────────────────────────────────
321
+
322
+ let charts = [];
323
+
324
+ function buildChart(filtered) {
325
+ const section = document.getElementById("charts-section");
326
+ section.innerHTML = "";
327
+ charts.forEach(c => c.destroy());
328
+ charts = [];
329
+
330
+ const metricCol = filters.metric;
331
+ const metricCfg = config.metrics.find(m => m.column === metricCol) || {};
332
+ const groupCol = GROUP_BY;
333
+ const groupFilterCfg = config.filters.find(f => f.column === groupCol);
334
+
335
+ const groupVal = filters[groupCol];
336
+ if (groupVal === "all") return;
337
+
338
+ const groupLabel = groupFilterCfg?.value_labels?.[groupVal] || String(groupVal);
339
+ const gRows = filtered.filter(r => String(r[groupCol]) === String(groupVal));
340
+ if (!gRows.length) return;
341
+
342
+ // If no scenarios configured, show one chart with all rows
343
+ const scenarioList = SCENARIOS.length
344
+ ? SCENARIOS
345
+ : [{ label: "", match: {} }];
346
+
347
+ scenarioList.forEach(scenario => {
348
+ // Filter rows matching this scenario
349
+ const matchRows = gRows.filter(r =>
350
+ Object.entries(scenario.match || {}).every(([col, val]) =>
351
+ String(r[col]) === String(val)
352
+ )
353
+ );
354
+ // For models with only OOM rows (no scenario match), include one OOM row
355
+ // but only if it matches the active filter values (e.g. type)
356
+ const matchedModels = new Set(matchRows.map(r => r[MODEL_COL]));
357
+ const oomRows = gRows.filter(r => !matchedModels.has(r[MODEL_COL]) && isOOMRow(r)
358
+ && Object.entries(scenario.match || {}).every(([col, val]) =>
359
+ r[col] === null || r[col] === "" || r[col] === "OOM" || String(r[col]) === String(val)
360
+ )
361
+ );
362
+ const allRows = matchRows.concat(oomRows);
363
+
364
+ const models = sortModels([...new Set(allRows.map(r => r[MODEL_COL]))]);
365
+ const picked = models.map(m => allRows.find(r => r[MODEL_COL] === m)).filter(Boolean);
366
+ if (!picked.length) return;
367
+
368
+ const labels = picked.map(r => MODEL_SHORT[r[MODEL_COL]]);
369
+ const data = picked.map(r => r[metricCol] === null ? 0 : r[metricCol]);
370
+ const bgColors = picked.map(r => MODEL_COLORS[r[MODEL_COL]].bg);
371
+ const borderColors = picked.map(r => MODEL_COLORS[r[MODEL_COL]].border);
372
+
373
+ const metricHint = metricCfg.higher_is_better ? " (higher is better)" : " (lower is better)";
374
+ const yLabel = (metricCfg.label || metricCol) + metricHint;
375
+
376
+ const chartBlock = document.createElement("div");
377
+ chartBlock.className = "chart-block";
378
+
379
+ const heading = document.createElement("h3");
380
+ heading.className = "chart-heading";
381
+ heading.textContent = groupLabel;
382
+ chartBlock.appendChild(heading);
383
+
384
+ if (scenario.label) {
385
+ const sub = document.createElement("p");
386
+ sub.className = "chart-subtitle";
387
+ sub.textContent = scenario.label;
388
+ chartBlock.appendChild(sub);
389
+ }
390
+
391
+ const wrap = document.createElement("div");
392
+ wrap.className = "chart-wrap";
393
+ const canvas = document.createElement("canvas");
394
+ wrap.appendChild(canvas);
395
+ chartBlock.appendChild(wrap);
396
+ section.appendChild(chartBlock);
397
+
398
+ const c = new Chart(canvas, {
399
+ type: "bar",
400
+ data: {
401
+ labels,
402
+ datasets: [{
403
+ data,
404
+ backgroundColor: bgColors,
405
+ borderColor: borderColors,
406
+ borderWidth: 2, borderRadius: 6, minBarLength: 4,
407
+ }],
408
+ },
409
+ options: {
410
+ responsive: true, maintainAspectRatio: false,
411
+ plugins: {
412
+ legend: { display: false },
413
+ title: { display: false },
414
+ tooltip: {
415
+ backgroundColor: cssVar("--tooltip-bg"), titleColor: cssVar("--tooltip-text"), bodyColor: cssVar("--tooltip-body"),
416
+ borderColor: cssVar("--btn-active-border"), borderWidth: 1,
417
+ callbacks: {
418
+ label: ctx => {
419
+ const orig = picked[ctx.dataIndex]?.[metricCol];
420
+ return orig === null ? "OOM" : orig.toLocaleString();
421
+ },
422
+ },
423
+ },
424
+ },
425
+ scales: {
426
+ y: { beginAtZero: true, title: { display: true, text: yLabel, color: cssVar("--text-muted") }, grid: { color: cssVar("--chart-grid") }, ticks: { color: cssVar("--text-dim") } },
427
+ x: { grid: { display: false }, ticks: { color: cssVar("--text-muted"), font: { size: 14 } } },
428
+ },
429
+ },
430
+ });
431
+ charts.push(c);
432
+ });
433
+ }
434
+
435
+ // ─── Tables ───────────────────────────────────────────────────────────────────
436
+
437
+ function buildTables(filtered, chartsShown) {
438
+ const section = document.getElementById("tables-section");
439
+ section.innerHTML = "";
440
+ const groupCol = GROUP_BY;
441
+ const groupFilterCfg = config.filters.find(f => f.column === groupCol);
442
+ const groupVal = filters[groupCol];
443
+ const opts = availableOptions(filters.family);
444
+ const groupVals = groupVal === "all" ? (opts[groupCol] || []) : [groupVal];
445
+
446
+ // Determine which display columns are visible given current filter state
447
+ const visibleDisplay = (config.display_columns || []).filter(dc => {
448
+ if (!dc.visible_when) return true;
449
+ return Object.entries(dc.visible_when).every(([filterCol, allowedVals]) =>
450
+ allowedVals.includes(filters[filterCol])
451
+ );
452
+ });
453
+
454
+ // Build column list: Model + visible display cols + metrics
455
+ const colDefs = [
456
+ { key: MODEL_COL, label: "Model", isModel: true },
457
+ ...visibleDisplay.map(dc => ({ key: dc.column, label: dc.label, description: dc.description || "" })),
458
+ ...config.metrics.map(m => ({ key: m.column, label: m.short || m.column, isMetric: true, description: m.description || "" })),
459
+ ];
460
+
461
+ // Resolve table_sort: family-specific overrides global
462
+ const familyCfg = config.model_families?.[filters.family] || {};
463
+ const sortRules = familyCfg.table_sort || config.table_sort || [];
464
+ const tableGroupBy = familyCfg.table_group_by || config.table_group_by || "";
465
+
466
+ groupVals.forEach(gv => {
467
+ const rows = filtered.filter(r => String(r[groupCol]) === String(gv));
468
+ if (!rows.length) return;
469
+ rows.sort((a, b) => {
470
+ for (const rule of sortRules) {
471
+ const col = rule.column;
472
+ const mul = rule.direction === "desc" ? -1 : 1;
473
+ if (rule.external_first && col === MODEL_COL) {
474
+ const aExt = isExternalModel(a[col]) ? 0 : 1;
475
+ const bExt = isExternalModel(b[col]) ? 0 : 1;
476
+ if (aExt !== bExt) return (aExt - bExt) * mul;
477
+ }
478
+ const av = a[col], bv = b[col];
479
+ if (av === bv || (av == null && bv == null)) continue;
480
+ if (av == null) return 1;
481
+ if (bv == null) return -1;
482
+ if (typeof av === "number" && typeof bv === "number") {
483
+ if (av !== bv) return (av - bv) * mul;
484
+ } else {
485
+ const aNum = parseFloat(String(av));
486
+ const bNum = parseFloat(String(bv));
487
+ if (!isNaN(aNum) && !isNaN(bNum)) {
488
+ if (aNum !== bNum) return (aNum - bNum) * mul;
489
+ }
490
+ const cmp = String(av).localeCompare(String(bv));
491
+ if (cmp !== 0) return cmp * mul;
492
+ }
493
+ }
494
+ return 0;
495
+ });
496
+
497
+ // Track row group index for alternating backgrounds
498
+ let prevGroupVal = undefined;
499
+
500
+ const card = document.createElement("div");
501
+ card.className = "table-card";
502
+
503
+ const heading = groupFilterCfg?.value_labels?.[gv] || String(gv);
504
+
505
+ let html = chartsShown ? '' : `<h3>${heading}</h3>`;
506
+ html += `<div class="table-scroll"><table><thead><tr>`;
507
+ html += colDefs.map(c => {
508
+ const tip = c.description ? ` data-tip="${c.description.replace(/"/g, '&quot;')}"` : '';
509
+ return `<th${tip}>${c.label}</th>`;
510
+ }).join("");
511
+ html += `</tr></thead><tbody>`;
512
+
513
+ rows.forEach(r => {
514
+ const oom = isOOMRow(r);
515
+ let rowClass = "";
516
+ if (tableGroupBy) {
517
+ const curVal = String(r[tableGroupBy] ?? "");
518
+ if (prevGroupVal !== undefined && curVal !== prevGroupVal) {
519
+ rowClass = "row-group-break";
520
+ }
521
+ prevGroupVal = curVal;
522
+ }
523
+ html += `<tr class="${rowClass}">`;
524
+ colDefs.forEach(c => {
525
+ const val = r[c.key];
526
+ if (c.isModel) {
527
+ const hfUrl = LINK_PREFIX + val;
528
+ const modelColor = MODEL_COLORS[val]?.border || '#888';
529
+ html += `<td class="model-cell"><span class="model-dot" style="background:${modelColor}"></span><a href="${hfUrl}" target="_blank" rel="noopener" style="color:${modelColor}">${val}</a></td>`;
530
+ } else if (oom) {
531
+ html += `<td><span class="oom">OOM</span></td>`;
532
+ } else if (c.isMetric) {
533
+ html += `<td>${val === null ? '<span class="oom">OOM</span>' : (val ?? "β€”")}</td>`;
534
+ } else {
535
+ html += `<td>${val || "β€”"}</td>`;
536
+ }
537
+ });
538
+ html += "</tr>";
539
+ });
540
+
541
+ html += "</tbody></table></div>";
542
+ card.innerHTML = html;
543
+ section.appendChild(card);
544
+ });
545
+ }
546
+
547
+ // ─── Experiment Setup ─────────────────────────────────────────────────────────
548
+
549
+ function buildExperimentSetup() {
550
+ const section = document.getElementById("experiment-setup");
551
+ section.innerHTML = "";
552
+ const familyCfg = config.model_families?.[filters.family] || {};
553
+ const setupMap = familyCfg.experiment_setup || {};
554
+ const groupCol = GROUP_BY;
555
+ const groupVal = filters[groupCol];
556
+
557
+ const deviceVals = groupVal === "all"
558
+ ? []
559
+ : (setupMap[groupVal] ? [groupVal] : []);
560
+
561
+ if (!deviceVals.length) {
562
+ section.style.display = "none";
563
+ return;
564
+ }
565
+ section.style.display = "";
566
+
567
+ deviceVals.forEach(dv => {
568
+ const text = setupMap[dv];
569
+ if (!text) return;
570
+ const p = document.createElement("p");
571
+ p.textContent = text;
572
+ section.appendChild(p);
573
+ });
574
+ }
575
+
576
+ // ─── Render ───────────────────────────────────────────────────────────────────
577
+
578
+ function render() {
579
+ const familyModels = MODEL_FAMILIES[filters.family]
580
+ ? new Set(MODEL_FAMILIES[filters.family].models)
581
+ : new Set(ALL_MODELS);
582
+
583
+ const filtered = DATA.filter(r => {
584
+ if (!familyModels.has(r[MODEL_COL])) return false;
585
+ for (const f of config.filters) {
586
+ const fv = filters[f.column];
587
+ if (fv === "all") continue;
588
+ if (String(r[f.column]) !== String(fv)) return false;
589
+ }
590
+ return true;
591
+ });
592
+
593
+ buildChart(filtered);
594
+ const chartsShown = filters[GROUP_BY] !== "all";
595
+ buildTables(filtered, chartsShown);
596
+ buildExperimentSetup();
597
+ }
598
+
599
+ // ─── Init ─────────────────────────────────────────────────────────────────────
600
+
601
+ populateFilters();
602
+ render();
603
+
604
+ })();
config.json ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "title": "Edge Inference Benchmarks",
3
+ "subtitle": "Compare throughput and latency across devices and model variants. Use filters to focus on what matters.",
4
+ "model_column": "model",
5
+ "model_family_column": "model_family",
6
+ "model_link_prefix": "https://huggingface.co/",
7
+ "optimized_org": "embedl",
8
+ "filters": [
9
+ {
10
+ "column": "type",
11
+ "label": "Inference Type"
12
+ },
13
+ {
14
+ "column": "batch",
15
+ "label": "Batch Size",
16
+ "type": "number"
17
+ },
18
+ {
19
+ "column": "device",
20
+ "label": "Device",
21
+ "value_labels": {
22
+ "orin_nano": "Jetson Orin Nano Super",
23
+ "orin_nano_super": "Jetson Orin Nano Super",
24
+ "agx_orin": "Jetson AGX Orin",
25
+ "agx_thor": "Jetson AGX Thor"
26
+ }
27
+ }
28
+ ],
29
+ "metrics": [
30
+ {
31
+ "column": "tps",
32
+ "label": "Tokens / sec",
33
+ "short": "TPS ↑",
34
+ "higher_is_better": true,
35
+ "description": "Tokens per second (higher is better). Number of output tokens generated per second during the decoding phase. "
36
+ },
37
+ {
38
+ "column": "tpot",
39
+ "label": "Time per Output Token (ms)",
40
+ "short": "TPOT ↓",
41
+ "higher_is_better": false,
42
+ "description": "Time per output token in ms (lower is better). Average time (in milliseconds) required to generate one output token during decoding. Computed as TPOT = (last_token_ts - first_token_ts) / total_output_tokens."
43
+ },
44
+ {
45
+ "column": "ttft",
46
+ "label": "Time to First Token (ms)",
47
+ "short": "TTFT ↓",
48
+ "higher_is_better": false,
49
+ "description": "Time to first token in ms (lower is better). Time from request submission to generation of the first output token. This includes vision encoding, prompt prefill, KV cache initialization."
50
+ },
51
+ {
52
+ "column": "e2e",
53
+ "label": "End-to-End Latency (s)",
54
+ "short": "E2E ↓",
55
+ "higher_is_better": false,
56
+ "description": "End-to-end latency in seconds (lower is better). Total time from request submission to completion of the full generated response. This reflects real user-perceived latency."
57
+ }
58
+ ],
59
+ "display_columns": [
60
+ {
61
+ "column": "res",
62
+ "label": "Resolution",
63
+ "visible_when": {
64
+ "type": [
65
+ "video",
66
+ "image"
67
+ ]
68
+ }
69
+ },
70
+ {
71
+ "column": "fps",
72
+ "label": "FPS",
73
+ "type": "number",
74
+ "visible_when": {
75
+ "type": [
76
+ "video"
77
+ ]
78
+ }
79
+ },
80
+ {
81
+ "column": "frames",
82
+ "label": "Frames",
83
+ "type": "number",
84
+ "visible_when": {
85
+ "type": [
86
+ "video"
87
+ ]
88
+ }
89
+ }
90
+ ],
91
+ "chart": {
92
+ "default_metric": "tps",
93
+ "group_by": "device",
94
+ "scenarios": [
95
+ {
96
+ "label": "Text",
97
+ "match": {
98
+ "type": "text"
99
+ }
100
+ },
101
+ {
102
+ "label": "Image Β· 1280Γ—720",
103
+ "match": {
104
+ "type": "image",
105
+ "res": "1280x720"
106
+ }
107
+ },
108
+ {
109
+ "label": "Video Β· 1280Γ—720 Β· 4 FPS",
110
+ "match": {
111
+ "type": "video",
112
+ "res": "1280x720",
113
+ "fps": 4
114
+ }
115
+ }
116
+ ]
117
+ },
118
+ "table_sort": [
119
+ {
120
+ "column": "res",
121
+ "direction": "asc"
122
+ },
123
+ {
124
+ "column": "fps",
125
+ "direction": "desc"
126
+ }
127
+ ],
128
+ "table_group_by": "model",
129
+ "model_families": {
130
+ "Cosmos-Reason2-2B": {
131
+ "table_group_by": "res",
132
+ "experiment_setup": {
133
+ "agx_thor": "Measurement setup: NVIDIA vLLM 26.01, 256 tokens generated, 10 warm-up runs, averaged over 25 runs.",
134
+ "agx_orin": "Measurement setup: NVIDIA vLLM 0.14.0 for Jetson, 256 tokens generated, 10 warm-up runs, averaged over 25 runs.",
135
+ "orin_nano": "Measurement setup: NVIDIA vLLM 0.14.0 for Jetson, 256 tokens generated, 10 warm-up runs, averaged over 25 runs."
136
+ }
137
+ }
138
+ }
139
+ }
data.csv ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model_family,model,type,batch,device,res,fps,frames,e2e,tps,tpot,ttft
2
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,image,1,agx_thor,1920x1080,N/A,N/A,2.8774,88.97,9.78,28.01
3
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,image,8,agx_thor,1920x1080,N/A,N/A,7.7989,262.60,13.22,29.21
4
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,image,1,agx_thor,854x480,N/A,N/A,3.2376,79.07,10.24,28.37
5
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,image,8,agx_thor,854x480,N/A,N/A,7.0134,292.01,13.61,63.15
6
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,image,1,agx_thor,1280x720,N/A,N/A,3.0668,83.48,9.68,27.34
7
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,image,8,agx_thor,1280x720,N/A,N/A,8.0958,252.97,13.77,60.15
8
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,text,1,agx_thor,N/A,N/A,N/A,2.2147,115.59,8.61,10.17
9
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,text,8,agx_thor,N/A,N/A,N/A,2.5627,799.15,9.88,26.92
10
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,1,agx_thor,1280x720,4,12,2.8978,88.34,10.27,186.62
11
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,8,agx_thor,1280x720,2,6,5.1466,397.93,12.00,776.65
12
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,8,agx_thor,1280x720,4,12,5.1346,398.87,11.96,779.69
13
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,1,agx_thor,1920x1080,2,6,3.7115,68.98,11.99,411.17
14
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,1,agx_thor,1920x1080,4,12,3.7154,68.90,12.00,412.31
15
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,8,agx_thor,1920x1080,2,6,8.4629,242.00,16.00,1744.43
16
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,8,agx_thor,1920x1080,4,12,8.3780,244.45,15.64,1741.12
17
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,1,agx_thor,854x480,2,6,2.6527,96.50,9.63,115.92
18
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,1,agx_thor,854x480,4,12,2.6462,96.74,9.60,115.65
19
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,8,agx_thor,854x480,2,6,3.8647,529.93,11.02,349.41
20
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-NVFP4A16,video,8,agx_thor,854x480,4,12,4.0195,509.52,11.55,349.17
21
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,agx_orin,1280x720,N/A,N/A,3.2759,70.82,11.80,54.90
22
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,agx_orin,1920x1080,N/A,N/A,3.1621,73.37,11.75,53.14
23
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,agx_orin,854x480,N/A,N/A,3.2648,71.06,11.76,55.98
24
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,text,1,agx_orin,N/A,N/A,N/A,2.5601,100.00,9.85,35.56
25
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_orin,1280x720,4,12,3.4416,74.38,11.71,243.54
26
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_orin,1280x720,2,6,3.4448,74.31,11.75,248.53
27
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_orin,1920x1080,2,6,4.4439,57.61,14.06,507.71
28
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_orin,1920x1080,4,12,4.4386,57.68,14.00,508.39
29
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_orin,854x480,2,6,3.0156,84.89,10.77,143.20
30
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_orin,854x480,4,12,3.0035,85.23,10.76,144.12
31
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,agx_thor,1280x720,N/A,N/A,3.0483,83.98,9.81,15.44
32
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,8,agx_thor,1280x720,N/A,N/A,9.1849,222.97,12.98,28.81
33
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,agx_thor,1920x1080,N/A,N/A,2.8439,90.02,9.71,15.53
34
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,8,agx_thor,1920x1080,N/A,N/A,6.2494,327.71,12.81,28.80
35
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,agx_thor,854x480,N/A,N/A,3.1369,81.61,10.18,15.95
36
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,8,agx_thor,854x480,N/A,N/A,6.9387,295.15,12.96,28.91
37
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,text,1,agx_thor,N/A,N/A,N/A,2.2340,114.59,8.67,11.13
38
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,text,8,agx_thor,N/A,N/A,N/A,2.2974,891.44,8.86,24.25
39
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_thor,1280x720,4,12,2.5977,98.55,9.10,185.63
40
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,8,agx_thor,1280x720,2,6,5.0695,403.98,11.64,779.90
41
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,8,agx_thor,1280x720,4,12,5.0521,405.37,11.63,775.99
42
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_thor,1920x1080,2,6,3.5969,71.17,11.52,412.00
43
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_thor,1920x1080,4,12,3.5756,71.60,11.39,415.89
44
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,8,agx_thor,1920x1080,2,6,8.2949,246.90,15.34,1742.22
45
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,8,agx_thor,1920x1080,4,12,8.2769,247.43,15.30,1741.64
46
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_thor,854x480,2,6,2.5015,102.34,9.06,113.30
47
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,agx_thor,854x480,4,12,2.5516,100.33,9.23,116.53
48
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,8,agx_thor,854x480,2,6,3.8613,530.39,10.92,350.04
49
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,8,agx_thor,854x480,4,12,3.9974,512.34,11.43,349.00
50
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,orin_nano_super,1280x720,N/A,N/A,5.5010,42.36,20.87,105.69
51
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,orin_nano_super,1920x1080,N/A,N/A,5.4421,42.81,20.88,105.89
52
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,image,1,orin_nano_super,854x480,N/A,N/A,5.5183,42.22,20.88,107.25
53
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,text,1,orin_nano_super,N/A,N/A,N/A,4.5489,56.28,17.42,84.61
54
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,orin_nano_super,1280x720,4,12,5.8635,43.66,20.80,334.08
55
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,orin_nano_super,1280x720,2,6,5.8696,43.61,20.79,324.38
56
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,orin_nano_super,1920x1080,2,6,4.8353,31.02,25.20,650.74
57
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,orin_nano_super,1920x1080,4,12,4.8483,30.94,25.23,657.56
58
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,orin_nano_super,854x480,2,6,5.1020,50.18,18.89,154.79
59
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16,video,1,orin_nano_super,854x480,4,12,5.1017,50.18,18.88,154.10
60
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,1,agx_orin,1280x720,N/A,N/A,3.5585,71.94,11.69,57.02
61
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,8,agx_orin,1280x720,N/A,N/A,10.3196,198.46,21.81,170.71
62
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,1,agx_orin,1920x1080,N/A,N/A,3.4224,74.80,11.68,54.49
63
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,8,agx_orin,1920x1080,N/A,N/A,9.8926,207.02,21.15,160.66
64
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,1,agx_orin,854x480,N/A,N/A,3.5028,73.08,11.67,56.27
65
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,8,agx_orin,854x480,N/A,N/A,11.6759,175.40,21.40,182.25
66
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,text,1,agx_orin,N/A,N/A,N/A,2.5640,99.84,9.86,36.08
67
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,text,8,agx_orin,N/A,N/A,N/A,2.8152,727.47,10.67,67.26
68
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,agx_orin,1280x720,4,12,3.4165,74.93,11.57,248.51
69
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,agx_orin,1280x720,2,6,3.4151,74.96,11.61,247.64
70
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,agx_orin,1280x720,4,12,7.7852,263.06,19.56,914.51
71
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,agx_orin,1280x720,2,6,7.6469,267.82,19.09,913.91
72
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,agx_orin,1920x1080,2,6,4.3864,58.36,13.93,504.30
73
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,agx_orin,1920x1080,4,12,4.4294,57.80,13.95,505.19
74
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,agx_orin,1920x1080,2,6,11.4902,178.24,22.12,2020.67
75
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,agx_orin,1920x1080,4,12,11.5039,178.03,22.19,2020.49
76
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,agx_orin,854x480,2,6,2.9761,86.02,10.62,141.70
77
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,agx_orin,854x480,4,12,2.9778,85.97,10.62,143.88
78
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,agx_orin,854x480,2,6,6.4896,315.58,19.83,422.90
79
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,agx_orin,854x480,4,12,6.4374,318.14,19.69,423.10
80
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,1,orin_nano_super,1280x720,N/A,N/A,6.0481,42.33,20.90,110.47
81
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,8,orin_nano_super,1280x720,N/A,N/A,26.8477,76.28,22.36,8962.49
82
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,1,orin_nano_super,1920x1080,N/A,N/A,5.7896,44.22,20.88,101.76
83
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,8,orin_nano_super,1920x1080,N/A,N/A,27.6012,74.20,22.41,8993.50
84
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,1,orin_nano_super,854x480,N/A,N/A,5.9725,42.86,20.92,109.79
85
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,image,8,orin_nano_super,854x480,N/A,N/A,26.7869,76.46,22.36,8956.04
86
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,text,1,orin_nano_super,N/A,N/A,N/A,4.4947,56.96,17.24,77.06
87
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,text,8,orin_nano_super,N/A,N/A,N/A,18.4232,111.16,17.55,7032.02
88
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,orin_nano_super,1280x720,4,12,5.8692,43.62,20.82,335.23
89
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,orin_nano_super,1280x720,2,6,5.8388,43.84,20.77,336.45
90
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,orin_nano_super,1280x720,4,12,25.2331,81.16,22.34,8713.41
91
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,orin_nano_super,1280x720,2,6,25.2252,81.19,22.34,8712.80
92
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,orin_nano_super,1920x1080,2,6,7.5462,33.92,25.29,648.85
93
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,orin_nano_super,1920x1080,4,12,7.5770,33.79,25.35,666.99
94
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,orin_nano_super,1920x1080,2,6,35.2481,58.10,29.46,11375.65
95
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,orin_nano_super,1920x1080,4,12,35.0017,58.51,29.46,11354.12
96
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,orin_nano_super,854x480,2,6,5.1049,50.15,18.91,151.20
97
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,1,orin_nano_super,854x480,4,12,5.1000,50.20,18.90,152.69
98
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,orin_nano_super,854x480,2,6,21.5985,94.82,19.83,7764.67
99
+ Cosmos-Reason2-2B,embedl/Cosmos-Reason2-2B-W4A16-Edge2,video,8,orin_nano_super,854x480,4,12,21.5877,94.87,19.80,7750.95
100
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,1,agx_orin,1280x720,N/A,N/A,8.6497,26.47,23.64,68.63
101
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,1,agx_orin,1920x1080,N/A,N/A,5.9558,38.45,23.63,57.30
102
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,1,agx_orin,854x480,N/A,N/A,6.0213,38.03,23.66,58.22
103
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,text,1,agx_orin,N/A,N/A,N/A,5.6196,45.55,21.79,36.40
104
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_orin,1280x720,4,12,6.4673,39.58,23.56,249.17
105
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_orin,1280x720,2,6,6.4872,39.46,23.58,250.17
106
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_orin,1920x1080,2,6,7.4784,34.23,25.92,521.39
107
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_orin,1920x1080,4,12,7.5190,34.05,25.93,523.36
108
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_orin,854x480,2,6,6.0471,42.33,22.59,146.10
109
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_orin,854x480,4,12,6.0397,42.39,22.63,145.79
110
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,1,agx_thor,1280x720,N/A,N/A,4.8338,52.96,16.96,28.90
111
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,8,agx_thor,1280x720,N/A,N/A,10.6920,191.55,25.67,74.92
112
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,1,agx_thor,1920x1080,N/A,N/A,4.9062,52.18,16.89,28.89
113
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,8,agx_thor,1920x1080,N/A,N/A,11.3606,180.27,25.21,76.51
114
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,1,agx_thor,854x480,N/A,N/A,4.6755,54.75,16.79,27.95
115
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,8,agx_thor,854x480,N/A,N/A,10.4721,195.57,25.16,73.86
116
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,text,1,agx_thor,N/A,N/A,N/A,3.9263,65.20,15.27,16.72
117
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,text,8,agx_thor,N/A,N/A,N/A,5.9494,344.24,22.92,65.90
118
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_thor,1280x720,4,12,4.5537,56.22,16.73,192.67
119
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,8,agx_thor,1280x720,2,6,7.0704,289.66,19.80,778.89
120
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,8,agx_thor,1280x720,4,12,7.0242,291.56,19.61,779.39
121
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_thor,1920x1080,2,6,5.2182,49.06,18.16,408.99
122
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_thor,1920x1080,4,12,5.2752,48.53,18.38,409.24
123
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,8,agx_thor,1920x1080,2,6,10.1481,201.81,22.92,1749.06
124
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,8,agx_thor,1920x1080,4,12,10.4564,195.86,23.66,1746.96
125
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_thor,854x480,2,6,5.0284,50.91,18.87,124.78
126
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,agx_thor,854x480,4,12,4.5504,56.26,17.01,123.61
127
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,8,agx_thor,854x480,2,6,5.6951,359.61,18.47,351.83
128
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,8,agx_thor,854x480,4,12,5.6608,361.79,18.33,354.19
129
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,1,orin_nano_super,OOM,N/A,N/A,OOM,OOM,OOM,OOM
130
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,text,1,orin_nano_super,N/A,N/A,N/A,OOM,OOM,OOM,OOM
131
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,1,orin_nano_super,OOM,OOM,OOM,OOM,OOM,OOM,OOM
132
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,image,8,orin_nano_super,OOM,N/A,N/A,OOM,OOM,OOM,OOM
133
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,text,8,orin_nano_super,N/A,N/A,N/A,OOM,OOM,OOM,OOM
134
+ Cosmos-Reason2-2B,nvidia/Cosmos-Reason2-2B,video,8,orin_nano_super,OOM,OOM,OOM,OOM,OOM,OOM,OOM
embedl_logo_black_bg.svg ADDED
embedl_logo_white_bg.svg ADDED
index.html CHANGED
@@ -1,19 +1,90 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>Edge Inference Benchmarks β€” Embedl</title>
7
+ <link rel="stylesheet" href="style.css"/>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
9
+ </head>
10
+ <body>
11
+
12
+ <div class="page">
13
+ <!-- Sidebar -->
14
+ <aside class="sidebar" id="sidebar">
15
+ <a href="https://embedl.com" target="_blank" class="sidebar-logo">
16
+ <img src="embedl_logo_black_bg.svg" alt="Embedl" height="28">
17
+ </a>
18
+ <nav class="sidebar-nav" id="family-nav"></nav>
19
+ </aside>
20
+
21
+ <!-- Main -->
22
+ <main class="main">
23
+ <div class="main-inner">
24
+ <!-- Hero -->
25
+ <header class="hero">
26
+ <div class="hero-badge"><span>Embedl Models</span></div>
27
+ <h1 id="hero-title">Edge Inference <span class="accent">Benchmarks</span></h1>
28
+ <p class="hero-sub" id="hero-sub">Compare throughput and latency across devices and model variants.</p>
29
+ </header>
30
+
31
+ <!-- Filters -->
32
+ <section class="filters-bar" id="filters-bar"></section>
33
+
34
+ <!-- Charts -->
35
+ <section id="charts-section"></section>
36
+
37
+ <!-- Tables -->
38
+ <section id="tables-section"></section>
39
+
40
+ <!-- Metric Legend -->
41
+ <section class="legend-section">
42
+ <div class="legend-grid" id="legend-grid"></div>
43
+ <div class="experiment-setup" id="experiment-setup"></div>
44
+ </section>
45
+ </div>
46
+ </main>
47
+ </div>
48
+
49
+ <footer class="footer">
50
+ <div class="footer-inner">
51
+ <div class="footer-grid">
52
+ <div class="footer-col">
53
+ <a href="https://embedl.com" target="_blank" class="footer-logo-img">
54
+ <img src="embedl_logo_black_bg.svg" alt="Embedl" height="24">
55
+ </a>
56
+ <div class="footer-logo">&copy; 2026 Embedl All rights reserved. <a href="https://www.embedl.com/contact" target="_blank">Contact Us.</a></div>
57
+ <div class="footer-social">
58
+ <a href="https://www.linkedin.com/company/13991327/" target="_blank" rel="noopener" aria-label="LinkedIn">
59
+ <svg width="18" height="18" fill="currentColor" viewBox="0 0 24 24"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
60
+ </a>
61
+ <a href="https://www.instagram.com/embedl_/" target="_blank" rel="noopener" aria-label="Instagram">
62
+ <svg width="18" height="18" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>
63
+ </a>
64
+ <a href="https://www.facebook.com/people/Embedl-AB/100088717652886/" target="_blank" rel="noopener" aria-label="Facebook">
65
+ <svg width="18" height="18" fill="currentColor" viewBox="0 0 24 24"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
66
+ </a>
67
+ <a href="https://www.youtube.com/channel/UCR2uU2PnTzr4D1hOn3nsBwg" target="_blank" rel="noopener" aria-label="YouTube">
68
+ <svg width="18" height="18" fill="currentColor" viewBox="0 0 24 24"><path d="M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
69
+ </a>
70
+ <a href="https://github.com/embedl" target="_blank" rel="noopener" aria-label="GitHub">
71
+ <svg width="18" height="18" fill="currentColor" viewBox="0 0 24 24"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
72
+ </a>
73
+ </div>
74
+ </div>
75
+ <div class="footer-col">
76
+ <div class="footer-col-title">Sweden</div>
77
+ <p>Gamla AlmedalsvΓ€gen 21,<br>412 63 Gothenburg, Sweden</p>
78
+ <p>SE +46 31 105 891</p>
79
+ </div>
80
+ <div class="footer-col">
81
+ <div class="footer-col-title">USA</div>
82
+ <p>470 Ramona Street,<br>Palo Alto, CA 94301, USA</p>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </footer>
87
+
88
+ <script src="app.js"></script>
89
+ </body>
90
  </html>
style.css CHANGED
@@ -1,28 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
 
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
 
1
+ /* ── Palette ──────────────────────────────────────────── */
2
+ :root {
3
+ --bg: #0B1527;
4
+ --bg-opaque: rgba(11, 21, 39,0.45);
5
+ --bg-surface: #1a1a2e;
6
+ --teal: #58b1c3;
7
+ --green: #6fcf97;
8
+ --red: #ff4d6d;
9
+ --pink: #ff6ec7;
10
+ --purple: #9b5de5;
11
+ --blue: #007F9E;
12
+ --neutral: #667788;
13
+ --text: #e8e8e8;
14
+ --text-muted: #8899aa;
15
+ --text-dim: #5a6a7a;
16
+ --border: rgba(255,255,255,0.06);
17
+ --chart-grid: rgba(255,255,255,0.06);
18
+ --btn-hover-bg: rgba(255,255,255,0.03);
19
+ --btn-active-bg: rgba(88,177,195,0.12);
20
+ --btn-active-border: rgba(88,177,195,0.3);
21
+ --sidebar-hover-bg: rgba(255,255,255,0.02);
22
+ --sidebar-active-bg: rgba(88,177,195,0.04);
23
+ --row-hover-bg: rgba(255,255,255,0.02);
24
+ --row-border: rgba(255,255,255,0.03);
25
+ --code-bg: rgba(88,177,195,0.1);
26
+ --tooltip-bg: #2c3e50;
27
+ --tooltip-text: #f1f1f1;
28
+ --tooltip-body: #d9d9d9;
29
+ }
30
+
31
+ /* ── Reset & Base ─────────────────────────────────────── */
32
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
33
+ html { font-size: 16px; }
34
  body {
35
+ font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
36
+ background: var(--bg);
37
+ color: var(--text);
38
+ line-height: 1.6;
39
+ -webkit-font-smoothing: antialiased;
40
+ position: relative;
41
+ }
42
+ body::before {
43
+ content: "";
44
+ position: fixed;
45
+ top: -260px;
46
+ left: 50%;
47
+ transform: translateX(-50%);
48
+ width: 69rem;
49
+ height: 42rem;
50
+ background: radial-gradient(circle, rgba(0,226,213,0.25) 0%, rgba(18,208,254,0.15) 30%, transparent 70%);
51
+ filter: blur(80px);
52
+ pointer-events: none;
53
+ z-index: 0;
54
+ }
55
+ code {
56
+ background: var(--code-bg);
57
+ color: var(--teal);
58
+ padding: 0.15em 0.45em;
59
+ border-radius: 4px;
60
+ font-size: 0.95em;
61
+ }
62
+
63
+ /* ── Page Layout ─────────────────────────────────────── */
64
+ .page {
65
+ position: relative;
66
+ z-index: 1;
67
+ display: flex;
68
+ flex-direction: row;
69
+ min-height: 100vh;
70
+ max-width: 1440px;
71
+ margin: 0 auto;
72
+ }
73
+
74
+ /* ── Sidebar ─────────────────────────────────────────── */
75
+ .sidebar {
76
+ width: 280px;
77
+ max-width: 300px;
78
+ flex-shrink: 0;
79
+ padding: 2rem 0;
80
+ position: sticky;
81
+ top: 0;
82
+ height: 100vh;
83
+ overflow-y: auto;
84
+ }
85
+ .sidebar-logo {
86
+ display: block;
87
+ padding: 0 1.25rem 3.25rem;
88
+ }
89
+ .sidebar-nav {
90
+ display: flex;
91
+ flex-direction: column;
92
+ }
93
+ .sidebar-item {
94
+ display: block;
95
+ padding: 0.5rem 1.25rem;
96
+ font-size: 1.25rem;
97
+ font-weight: 400;
98
+ color: var(--text-dim);
99
+ text-decoration: none;
100
+ cursor: pointer;
101
+ border-left: 2px solid transparent;
102
+ transition: color 0.15s, background 0.15s, border-color 0.15s;
103
+ }
104
+ .sidebar-item:hover {
105
+ color: var(--text-muted);
106
+ }
107
+ .sidebar-item.active {
108
+ color: var(--teal);
109
+ font-weight: 600;
110
+ }
111
+
112
+ /* ── Main Column ─────────────────────────────────────── */
113
+ .main {
114
+ flex: 1;
115
+ min-width: 0;
116
+ }
117
+ .main-inner {
118
+ max-width: 1100px;
119
+ width: 100%;
120
+ padding: 0 2rem 2rem;
121
+ }
122
+
123
+ /* ── Hero ────────────────────────────────────────────── */
124
+ .hero {
125
+ position: relative;
126
+ padding: 2rem 0 2.5rem;
127
+ }
128
+ .hero-badge {
129
+ display: inline-block;
130
+ padding: 1px;
131
+ border-radius: 999px;
132
+ background: linear-gradient(90deg, var(--teal),var(--blue));
133
+ margin-bottom: 1rem;
134
+ }
135
+ .hero-badge span {
136
+ display: inline-block;
137
+ padding: 0.35rem 1rem;
138
+ border-radius: 999px;
139
+ background: var(--bg-opaque);
140
+ color: var(--teal);
141
+ font-size: 1rem;
142
+ font-weight: 600;
143
+ letter-spacing: 0.03em;
144
+ text-transform: uppercase;
145
+ }
146
+ .hero h1 {
147
+ font-size: clamp(1.8rem, 3.5vw, 3.0rem);
148
+ font-weight: 700;
149
+ margin-bottom: 0.5rem;
150
+ letter-spacing: -0.02em;
151
+ }
152
+ .hero .accent { color: var(--teal); }
153
+ .hero-sub {
154
+ color: var(--text-muted);
155
+ font-size: 1.1rem;
156
+ max-width: 620px;
157
+ line-height: 1.6;
158
+ }
159
+
160
+ /* ── Filters ─────────────────────────────────────────── */
161
+ .filters-bar {
162
+ display: flex;
163
+ flex-wrap: wrap;
164
+ gap: 1.25rem;
165
+ padding: 1rem 0;
166
+ top: 0;
167
+ z-index: 10;
168
+ }
169
+ .filter-group label {
170
+ display: block;
171
+ font-size: 0.8rem;
172
+ font-weight: 600;
173
+ text-transform: uppercase;
174
+ letter-spacing: 0.08em;
175
+ color: var(--text-dim);
176
+ margin-bottom: 0.25rem;
177
+ }
178
+ .btn-group { display: flex; gap: 0; }
179
+ .btn {
180
+ background: transparent;
181
+ border: 1px solid var(--border);
182
+ color: var(--text-muted);
183
+ font-size: 1rem;
184
+ font-weight: 400;
185
+ padding: 0.45rem 1rem;
186
+ cursor: pointer;
187
+ transition: all 0.15s;
188
+ }
189
+ .btn:first-child { border-radius: 6px 0 0 6px; }
190
+ .btn:last-child { border-radius: 0 6px 6px 0; }
191
+ .btn:not(:first-child) { margin-left: -1px; }
192
+ .btn:hover {
193
+ color: var(--text);
194
+ background: var(--btn-hover-bg);
195
+ }
196
+ .btn.active {
197
+ background: var(--btn-active-bg);
198
+ color: var(--teal);
199
+ border-color: var(--btn-active-border);
200
+ font-weight: 600;
201
+ z-index: 1;
202
+ position: relative;
203
  }
204
 
205
+ /* ── Chart ───────────────────────────────────────────── */
206
+ .chart-block {
207
+ margin: 1.5rem 0;
208
+ }
209
+ .chart-heading {
210
+ font-size: 1.25rem;
211
+ font-weight: 600;
212
+ color: var(--text);
213
+ margin-bottom: 0.25rem;
214
+ }
215
+ .chart-subtitle {
216
+ font-size: 1rem;
217
+ color: var(--text-muted);
218
+ margin-bottom: 0.75rem;
219
+ }
220
+ .chart-wrap {
221
+ height: 340px;
222
  }
223
 
224
+ /* ── Tables ──────────────────────────────────────────── */
225
+ .table-card {
226
+ margin: 1.5rem 0 2rem;
227
+ }
228
+ .table-card h3 {
229
+ font-size: 1.15rem;
230
+ font-weight: 600;
231
+ color: var(--text);
232
+ margin-bottom: 0.5rem;
233
+ padding-bottom: 0.5rem;
234
+ border-bottom: 1px solid var(--border);
235
+ }
236
+ .table-scroll {
237
+ overflow-x: auto;
238
+ scrollbar-color: var(--text-dim) var(--border);
239
+ scrollbar-width: thin;
240
+ }
241
+ .table-scroll::-webkit-scrollbar { height: 8px; }
242
+ .table-scroll::-webkit-scrollbar-track { background: var(--border); border-radius: 4px; }
243
+ .table-scroll::-webkit-scrollbar-thumb { background: var(--text-dim); border-radius: 4px; }
244
+ .table-scroll::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
245
+ table { width: 100%; border-collapse: collapse; font-size: 1rem; }
246
+ thead th {
247
+ text-align: left;
248
+ font-weight: 600;
249
+ font-size: 0.8rem;
250
+ text-transform: uppercase;
251
+ letter-spacing: 0.05em;
252
+ color: var(--text-dim);
253
+ padding: 0.5rem 0.75rem;
254
+ border-bottom: 1px solid var(--border);
255
+ white-space: nowrap;
256
+ }
257
+ tbody td {
258
+ padding: 0.5rem 0.75rem;
259
+ border-bottom: 1px solid var(--row-border);
260
+ white-space: nowrap;
261
+ color: var(--text-muted);
262
+ }
263
+ tbody tr:last-child td { border-bottom: none; }
264
+ tbody tr:hover { background: var(--row-hover-bg); }
265
+ .model-cell { display: flex; align-items: center; gap: 0.5rem; font-weight: 500; color: var(--text); }
266
+ .model-cell a { color: var(--teal); text-decoration: none; transition: color 0.15s; }
267
+ .model-cell a:hover { text-decoration: underline; }
268
+ .model-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
269
+ .oom { color: var(--red); font-weight: 600; }
270
+ tbody tr.row-group-break td { border-top: 2px solid var(--border); }
271
+
272
+ /* ── Legend ───────────────────────────────────────────── */
273
+ .legend-section {
274
+ margin: 2rem 0;
275
+ padding-top: 1.5rem;
276
+ }
277
+ .legend-grid {
278
+ display: grid;
279
+ grid-template-columns: 1fr;
280
+ gap: 0.25rem;
281
+ font-size: 0.8rem;
282
+ color: var(--text-dim);
283
+ }
284
+ .legend-grid strong { color: var(--text-muted); font-weight: 500; }
285
+ .experiment-setup {
286
+ margin-top: 0.75rem;
287
+ font-size: 0.95rem;
288
+ color: var(--text-dim);
289
+ font-style: italic;
290
+ line-height: 1.6;
291
  }
292
+ .experiment-setup p { margin-bottom: 0.25rem; }
293
 
294
+ /* ── Footer ──────────────────────────────────────────── */
295
+ .footer {
296
+ padding: 2.5rem 0;
297
+ margin-top: 2rem;
298
+ }
299
+ .footer-inner {
300
+ max-width: 1440px;
301
+ margin: 0 auto;
302
+ padding: 0 2rem;
303
+ }
304
+ .footer-grid {
305
+ display: flex;
306
+ gap: 3rem;
307
+ flex-wrap: wrap;
308
+ }
309
+ .footer-col {
310
+ font-size: 1rem;
311
+ color: var(--text-dim);
312
+ line-height: 1.7;
313
+ }
314
+ .footer-col p { margin: 0; }
315
+ .footer-col-title {
316
+ font-size: 0.85rem;
317
+ font-weight: 600;
318
+ text-transform: uppercase;
319
+ letter-spacing: 0.08em;
320
+ color: var(--text);
321
+ }
322
+ .footer-logo-img {
323
+ display: block;
324
+ margin-bottom: 0.5rem;
325
+ }
326
+ .footer-logo {
327
+ font-size: 1rem;
328
+ color: var(--text-muted);
329
+ margin-bottom: 1.2rem;
330
+ }
331
+ .footer-logo a { color: var(--text-muted); text-decoration: none; }
332
+ .footer-logo a:hover { text-decoration: underline; }
333
+ .footer-social {
334
+ display: flex;
335
+ gap: 0.75rem;
336
+ }
337
+ .footer-social a {
338
+ color: var(--text-dim);
339
+ transition: color 0.15s;
340
+ display: flex;
341
+ align-items: center;
342
+ }
343
+ .footer-social a:hover {
344
+ color: var(--teal);
345
  }
346
 
347
+ /* ── Responsive ──────────────────────────────────────── */
348
+ @media (max-width: 768px) {
349
+ .page { flex-direction: column; }
350
+ .sidebar {
351
+ width: 100%;
352
+ max-width: none;
353
+ height: auto;
354
+ position: static;
355
+ border-right: none;
356
+ border-bottom: 1px solid var(--border);
357
+ padding: 0.75rem 0;
358
+ }
359
+ .sidebar-nav {
360
+ flex-direction: row;
361
+ flex-wrap: wrap;
362
+ padding: 0 0.75rem;
363
+ }
364
+ .sidebar-item {
365
+ border-left: none;
366
+ border-bottom: 2px solid transparent;
367
+ padding: 0.4rem 0.75rem;
368
+ font-size: 0.9rem;
369
+ }
370
+ .sidebar-item.active {
371
+ border-left-color: transparent;
372
+ border-bottom-color: var(--teal);
373
+ }
374
+ .main-inner { padding: 0 1rem 2rem; }
375
+ .filters-bar { gap: 0.75rem; }
376
+ .btn { font-size: 0.85rem; padding: 0.35rem 0.7rem; }
377
+ .chart-wrap { height: 260px; }
378
  }