elmadany commited on
Commit
6e5043a
·
verified ·
1 Parent(s): 0289280

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +93 -250
public/index.html CHANGED
@@ -13,21 +13,19 @@
13
 
14
  <style>
15
  :root {
16
- /* Brand Colors from Image */
17
- --bg-deep: #1e1b4b; /* Deep Navy Background */
18
- --text-yellow: #fbbf24; /* "Mapping Africa" text */
19
  --text-white: #ffffff;
20
- --text-mute: #94a3b8; /* Description text */
21
 
22
- /* Button Gradients */
23
  --grad-red: linear-gradient(135deg, #ef4444, #f43f5e);
24
  --grad-purple: linear-gradient(135deg, #6366f1, #8b5cf6);
25
  --grad-orange: linear-gradient(135deg, #f97316, #fb923c);
26
  --grad-blue: linear-gradient(135deg, #3b82f6, #0ea5e9);
27
  --grad-green: linear-gradient(135deg, #22c55e, #10b981);
28
 
29
- /* Layout Colors */
30
- --card-bg: #ffffff;
31
  --simba-navy: #0f172a;
32
  --border-gold: #dca02a;
33
  }
@@ -35,229 +33,97 @@
35
  body {
36
  background-color: var(--bg-deep);
37
  font-family: 'Inter', sans-serif;
38
- color: var(--text-main);
39
  margin: 0;
40
  padding: 0;
41
  overflow-x: hidden;
42
  }
43
 
44
- /* --- BACKGROUND WAVE ANIMATION --- */
 
 
 
 
 
 
 
 
 
45
  .wave-bg {
46
- position: absolute;
47
- top: 150px;
48
- left: 0;
49
- width: 100%;
50
- height: 300px;
51
- background: repeating-linear-gradient(
52
- 90deg,
53
- transparent,
54
- transparent 10px,
55
- rgba(255, 255, 255, 0.05) 10px,
56
- rgba(255, 255, 255, 0.05) 12px
57
- );
58
  mask-image: linear-gradient(to right, transparent, black 20%, black 80%, transparent);
59
- z-index: 0;
60
- pointer-events: none;
61
- }
62
-
63
- .wave-bar {
64
- position: absolute;
65
- bottom: 0;
66
- width: 4px;
67
- background: rgba(255,255,255,0.1);
68
- border-radius: 4px;
69
- animation: bounce 2s infinite ease-in-out;
70
  }
71
 
72
  /* --- NAVBAR --- */
73
- .navbar {
74
- padding: 20px 40px;
75
- display: flex;
76
- justify-content: space-between;
77
- align-items: center;
78
- z-index: 10;
79
- position: relative;
80
- }
81
- .nav-logo {
82
- color: white;
83
- font-family: 'Rubik', sans-serif;
84
- font-weight: 700;
85
- font-size: 20px;
86
- display: flex;
87
- align-items: center;
88
- gap: 10px;
89
- text-decoration: none;
90
- }
91
  .nav-logo img { height: 35px; }
92
-
93
  .nav-links { display: flex; gap: 30px; align-items: center; }
94
- .nav-links a {
95
- color: #cbd5e1;
96
- text-decoration: none;
97
- font-size: 14px;
98
- font-weight: 500;
99
- transition: color 0.2s;
100
- }
101
  .nav-links a:hover { color: white; }
102
-
103
- .nav-btn-action {
104
- background: var(--grad-blue);
105
- padding: 8px 20px;
106
- border-radius: 20px;
107
- color: white !important;
108
- font-weight: 600;
109
- box-shadow: 0 4px 10px rgba(59, 130, 246, 0.3);
110
- }
111
-
112
- .login-btn {
113
- background: var(--text-yellow);
114
- color: var(--bg-deep) !important;
115
- padding: 8px 20px;
116
- border-radius: 20px;
117
- font-weight: 700;
118
- }
119
-
120
- /* --- HERO SECTION --- */
121
- .hero {
122
- display: flex;
123
- align-items: center;
124
- justify-content: space-between;
125
- max-width: 1300px;
126
- margin: 40px auto 80px;
127
- padding: 0 40px;
128
- position: relative;
129
- z-index: 2;
130
- }
131
 
 
 
132
  .hero-content { flex: 1; max-width: 650px; }
 
 
 
 
133
 
134
- .conference-tag {
135
- background: white;
136
- display: inline-block;
137
- padding: 5px 15px;
138
- border-radius: 8px;
139
- margin-bottom: 20px;
140
- }
141
- .conference-tag img { height: 25px; }
142
-
143
- .hero h1 {
144
- font-family: 'Rubik', sans-serif;
145
- font-size: 56px;
146
- font-weight: 800;
147
- color: white;
148
- margin: 0;
149
- line-height: 1.1;
150
- }
151
-
152
- .hero h2 {
153
- font-family: 'Rubik', sans-serif;
154
- font-size: 28px;
155
- font-weight: 700;
156
- color: var(--text-yellow);
157
- margin: 10px 0 20px;
158
- letter-spacing: 1px;
159
- }
160
-
161
- .hero p {
162
- color: var(--text-mute);
163
- font-size: 16px;
164
- line-height: 1.6;
165
- margin-bottom: 40px;
166
- max-width: 90%;
167
- }
168
-
169
- /* --- HERO BUTTONS --- */
170
- .hero-buttons {
171
- display: flex;
172
- flex-wrap: wrap;
173
- gap: 15px;
174
- }
175
-
176
- .h-btn {
177
- text-decoration: none;
178
- color: white;
179
- padding: 12px 24px;
180
- border-radius: 30px;
181
- font-family: 'Rubik', sans-serif;
182
- font-weight: 600;
183
- font-size: 14px;
184
- display: flex;
185
- align-items: center;
186
- gap: 8px;
187
- transition: transform 0.2s, box-shadow 0.2s;
188
- }
189
-
190
  .h-btn:hover { transform: translateY(-2px); filter: brightness(1.1); }
191
- .h-btn i { font-size: 16px; }
192
-
193
  .btn-paper { background: var(--grad-red); box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4); }
194
  .btn-pipeline { background: var(--grad-purple); box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4); }
195
  .btn-dataset { background: var(--grad-orange); box-shadow: 0 4px 15px rgba(249, 115, 22, 0.4); }
196
  .btn-models { background: var(--grad-blue); box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4); }
197
  .btn-leaderboard { background: var(--grad-green); box-shadow: 0 4px 15px rgba(34, 197, 94, 0.4); }
198
 
199
- /* --- HERO IMAGE (Map) --- */
200
- .hero-visual {
201
- flex: 1;
202
- display: flex;
203
- justify-content: flex-end;
204
- position: relative;
205
- }
206
- .hero-visual img {
207
- max-width: 100%;
208
- height: auto;
209
- /* Simple fade animation */
210
- animation: fadeIn 1.5s ease-out;
211
- }
212
 
213
  /* --- LEADERBOARD SECTION --- */
214
- .leaderboard-section {
215
- background: #f8fafc;
216
- border-radius: 24px 24px 0 0;
217
- padding: 60px 40px;
218
- min-height: 100vh;
219
- }
220
-
221
  .lb-container { max-width: 1300px; margin: 0 auto; }
222
 
223
- .content-card {
224
- background: white;
225
- border-radius: 16px;
226
- padding: 30px;
227
- box-shadow: 0 10px 30px rgba(0,0,0,0.05);
228
- margin-bottom: 40px;
229
- }
230
 
231
- /* Controls & Tables (Reusing refined styles) */
232
- .controls { display: flex; gap: 15px; margin-bottom: 20px; align-items: center; flex-wrap: wrap; background: #f1f5f9; padding: 15px; border-radius: 12px; }
233
- .tabs { display: flex; gap: 10px; margin-bottom: 25px; }
234
  .tab-btn {
235
- padding: 10px 20px; border-radius: 8px; border: none; background: transparent;
236
- font-weight: 600; color: #64748b; cursor: pointer; transition: 0.2s;
237
  }
 
238
  .tab-btn.active { background: var(--simba-navy); color: white; box-shadow: 0 4px 10px rgba(15, 23, 42, 0.2); }
239
-
240
- /* Table Styles */
241
- .table-wrap { overflow-x: auto; }
242
- table { width: 100%; border-collapse: collapse; font-size: 14px; }
243
- th { text-align: center; padding: 15px; color: var(--simba-navy); border-bottom: 2px solid #e2e8f0; vertical-align: bottom; }
 
 
 
 
244
  td { text-align: center; padding: 12px; border-bottom: 1px solid #f1f5f9; color: #334155; }
245
- th:first-child, td:first-child { text-align: left; font-weight: 700; position: sticky; left: 0; background: white; z-index: 2; }
246
- th:nth-child(2), td:nth-child(2) { text-align: left; }
247
 
248
- .arrow-up { color: #16a34a; } .arrow-down { color: #d97706; }
249
- .val { white-space: nowrap; }
 
250
 
251
- /* Animations */
252
  @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
253
- @keyframes bounce { 0%, 100% { height: 10px; } 50% { height: 40px; } }
254
-
255
- /* Mobile */
256
  @media (max-width: 900px) {
257
  .hero { flex-direction: column; text-align: center; }
258
  .hero-visual { margin-top: 40px; justify-content: center; }
259
  .hero-buttons { justify-content: center; }
260
- .nav-links { display: none; } /* Simplified for mobile */
261
  }
262
  </style>
263
  </head>
@@ -269,11 +135,11 @@
269
  Voice of a Continent
270
  </a>
271
  <div class="nav-links">
272
- <a href="https://arxiv.org/abs/2510.17998">Paper</a>
273
- <a href="https://github.com/UBC-NLP/simba">Pipeline</a>
274
- <a href="https://huggingface.co/datasets/UBC-NLP/simba">Dataset</a>
275
  <a href="#">Models</a>
276
- <a href="#">Leaderboard</a>
277
  <a href="#">Citation</a>
278
  <a href="#" class="nav-btn-action">Submit New Results</a>
279
  <a href="#" class="login-btn">Login</a>
@@ -282,67 +148,51 @@
282
 
283
  <div class="hero">
284
  <div class="wave-bg"></div>
285
-
286
  <div class="hero-content">
287
  <div class="conference-tag">
288
  <span style="font-weight:800; color:#cc0000;">EMNLP 2025</span>
289
  <span style="font-size:12px; color:#555;">Suzhou, China</span>
290
  </div>
291
-
292
  <h1>Voice of a Continent</h1>
293
  <h2>Mapping Africa’s Speech Technology</h2>
294
-
295
  <p>
296
  SimbaBench bridges the digital divide with a unified suite for African AI: the largest
297
  open-source speech benchmark covering 61 languages, best-in-class multilingual models,
298
- and an interactive leaderboard driving the future of speech technology.
299
  </p>
300
-
301
  <div class="hero-buttons">
302
- <a href="https://arxiv.org/abs/2510.17998" target="_blank" class="h-btn btn-paper">
303
- <i class="fa-regular fa-file-pdf"></i> Read Paper
304
- </a>
305
- <a href="https://github.com/UBC-NLP/simba" target="_blank" class="h-btn btn-pipeline">
306
- <i class="fa-solid fa-code-branch"></i> Pipeline
307
- </a>
308
- <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank" class="h-btn btn-dataset">
309
- <i class="fa-solid fa-database"></i> Dataset
310
- </a>
311
- <a href="#" class="h-btn btn-models">
312
- <i class="fa-solid fa-bolt"></i> Models
313
- </a>
314
- <a href="#leaderboard-anchor" class="h-btn btn-leaderboard">
315
- <i class="fa-solid fa-trophy"></i> Leaderboard
316
- </a>
317
  </div>
318
  </div>
319
-
320
  <div class="hero-visual">
321
- <img src="https://africa.dlnlp.ai/simba/images/simbabench_header.png" alt="Africa Map Graphic">
322
  </div>
323
  </div>
324
 
325
  <div class="leaderboard-section" id="leaderboard-anchor">
326
  <div class="lb-container">
327
-
328
  <div class="content-card">
 
329
  <div id="loader" style="text-align:center; padding:40px; color:#64748b;">
330
  <i class="fa-solid fa-circle-notch fa-spin fa-2x"></i><br><br>Loading Leaderboard Data...
331
  </div>
332
 
333
  <div id="content" style="display:none;">
334
-
335
  <div class="tabs">
336
- <button class="tab-btn active" onclick="setTab('asr', this)">Automatic Speech Recognition (ASR)</button>
337
- <button class="tab-btn" onclick="setTab('tts', this)">Text-to-Speech (TTS)</button>
338
- <button class="tab-btn" onclick="setTab('slid', this)">Spoken Language Identification (SLID)</button>
339
  </div>
340
 
341
  <div id="asr" class="view active">
342
  <div class="controls">
343
  <span style="font-weight:600; color:#0f172a;">View Mode:</span>
344
- <button id="btn-fam" class="h-btn btn-models" style="padding:6px 15px; font-size:12px;" onclick="setASRMode('family')">By Family</button>
345
- <button id="btn-mod" class="h-btn" style="background:#cbd5e1; color:#334155; padding:6px 15px; font-size:12px;" onclick="setASRMode('model')">By Model</button>
346
 
347
  <div style="flex-grow:1; text-align:right;">
348
  <div id="grp-fam" style="display:inline-block;">
@@ -355,7 +205,6 @@
355
  </div>
356
  </div>
357
  </div>
358
-
359
  <h3 id="asr-title" style="color:#d97706; font-size:18px;"></h3>
360
  <div id="asr-legend" style="background:#f1f5f9; padding:10px; font-size:13px; border-radius:6px; margin-bottom:15px; display:none;"></div>
361
  <div id="asr-table" class="table-wrap"></div>
@@ -374,19 +223,17 @@
374
  <div id="slid" class="view">
375
  <div id="slid-table" class="table-wrap"></div>
376
  </div>
377
-
378
  </div>
379
  </div>
380
 
381
  <div style="text-align:center; color:#64748b; margin-top:40px;">
382
  <p>&copy; 2025 Simba Benchmark. <a href="https://africa.dlnlp.ai/simba/" style="color:#d97706;">Official Website</a></p>
383
  </div>
384
-
385
  </div>
386
  </div>
387
 
388
  <script>
389
- // --- DATA & LOGIC ---
390
  const NAME_TO_ISO = {
391
  'Afrikaans': 'afr', 'Akuapim-twi': 'ak', 'Amharic': 'amh', 'Asante-twi': 'tw',
392
  'Bemba': 'bem', 'Hausa': 'hau', 'Igbo': 'ibo', 'Yoruba': 'yor', 'Zulu': 'zul',
@@ -396,6 +243,7 @@
396
  let DATA = null;
397
  let asrMode = 'family';
398
 
 
399
  fetch('/api/data').then(r=>r.json()).then(d=>{
400
  DATA = d;
401
  document.getElementById('loader').style.display='none';
@@ -409,7 +257,12 @@
409
  populate('asr-select-fam', DATA.metadata.families);
410
  populate('asr-select-mod', DATA.metadata.models);
411
  populate('tts-select', DATA.metadata.tts_models);
412
- renderASR(); renderTTS(); renderSLID();
 
 
 
 
 
413
  }
414
 
415
  function populate(id, list) {
@@ -417,9 +270,9 @@
417
  list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); });
418
  }
419
  function fmt(n) { return (n==null)?'N/A':n.toFixed(2); }
420
- function getIso(name) { return NAME_TO_ISO[name] || name.substring(0,3); }
421
 
422
- // --- ASR ---
423
  function renderASR() {
424
  const div = document.getElementById('asr-table');
425
  const legend = document.getElementById('asr-legend');
@@ -428,15 +281,12 @@
428
  if(asrMode==='family'){
429
  const val = document.getElementById('asr-select-fam').value;
430
  document.getElementById('asr-title').innerText = `Results for Family: ${val}`;
431
-
432
  const d = DATA.asr.by_family[val];
433
  if(!d) return;
434
 
435
- // Legend
436
  legend.style.display = 'block';
437
  legend.innerHTML = `<b>Codes:</b> ` + d.languages.map(l => `<span style="margin-right:10px"><b>${getIso(l)}</b>=${l}</span>`).join("");
438
 
439
- // Table
440
  html = `<table><thead><tr><th>Model</th><th>Avg WER/CER <i class="fa-solid fa-arrow-down arrow-down"></i></th>`;
441
  d.languages.forEach(l => html += `<th>${getIso(l)}</th>`);
442
  html += `</tr></thead><tbody>`;
@@ -453,22 +303,19 @@
453
  const val = document.getElementById('asr-select-mod').value;
454
  document.getElementById('asr-title').innerText = `Results for Model: ${val}`;
455
  legend.style.display = 'none';
456
-
457
  const rows = DATA.asr.by_model[val];
458
  html = `<table><thead><tr><th>Language</th><th>Family</th><th>WER (%) <i class="fa-solid fa-arrow-down arrow-down"></i></th><th>CER (%) <i class="fa-solid fa-arrow-down arrow-down"></i></th></tr></thead><tbody>`;
459
  rows.forEach(r => {
460
  html += `<tr><td>${r.Language}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
461
  });
462
  }
463
- html += `</tbody></table>`;
464
  div.innerHTML = html;
465
  }
466
 
467
- // --- TTS ---
468
  function renderTTS() {
469
  const val = document.getElementById('tts-select').value;
470
  document.getElementById('tts-title').innerText = `Results for Model: ${val}`;
471
-
472
  const rows = DATA.tts[val];
473
  if(!rows) return;
474
 
@@ -479,15 +326,12 @@
479
  {k:'speechbleu', l:'SpBLEU', h:true}, {k:'speechbert_score', l:'SpBERT', h:true}
480
  ];
481
 
482
- // Averages
483
  let sums={}, c=0;
484
  metrics.forEach(m=>sums[m.k]=0);
485
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
486
-
487
  let sumHTML = metrics.map(m => `<span>${m.l}: <b>${fmt(sums[m.k]/c)}</b></span>`).join(" | ");
488
  document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" + sumHTML;
489
 
490
- // Table
491
  let html = `<table><thead><tr><th>Language</th>`;
492
  metrics.forEach(m => {
493
  let icon = m.h ? 'up' : 'down';
@@ -500,11 +344,10 @@
500
  metrics.forEach(m => html += `<td>${fmt(r[m.k])}</td>`);
501
  html += `</tr>`;
502
  });
503
- html += `</tbody></table>`;
504
  document.getElementById('tts-table').innerHTML = html;
505
  }
506
 
507
- // --- SLID ---
508
  function renderSLID() {
509
  let html = `<table><thead><tr><th>Language</th><th>MMS <i class="fa-solid fa-arrow-up arrow-up"></i></th><th>Simba <i class="fa-solid fa-arrow-up arrow-up"></i></th></tr></thead><tbody>`;
510
  DATA.slid.forEach(r => {
@@ -513,24 +356,24 @@
513
  if(m > s) mS = `<b style="color:green">${mS}</b>`; else if(s > m) sS = `<b style="color:green">${sS}</b>`;
514
  html += `<tr><td>${r.Language}</td><td>${mS}</td><td>${sS}</td></tr>`;
515
  });
516
- html += `</tbody></table>`;
517
  document.getElementById('slid-table').innerHTML = html;
518
  }
519
 
520
- // --- UI ---
521
- window.setTab = (id, btn) => {
 
522
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
523
  document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active'));
524
- document.getElementById(id).classList.add('active');
525
- btn.classList.add('active');
 
 
526
  };
527
 
528
  window.setASRMode = (m) => {
529
  asrMode = m;
530
  document.getElementById('grp-fam').style.display = m==='family'?'inline-block':'none';
531
  document.getElementById('grp-mod').style.display = m==='model'?'inline-block':'none';
532
-
533
- // Update button styles
534
  const b1 = document.getElementById('btn-fam');
535
  const b2 = document.getElementById('btn-mod');
536
  if(m==='family') {
 
13
 
14
  <style>
15
  :root {
16
+ /* Brand Colors */
17
+ --bg-deep: #1e1b4b;
18
+ --text-yellow: #fbbf24;
19
  --text-white: #ffffff;
20
+ --text-mute: #94a3b8;
21
 
22
+ /* Gradients */
23
  --grad-red: linear-gradient(135deg, #ef4444, #f43f5e);
24
  --grad-purple: linear-gradient(135deg, #6366f1, #8b5cf6);
25
  --grad-orange: linear-gradient(135deg, #f97316, #fb923c);
26
  --grad-blue: linear-gradient(135deg, #3b82f6, #0ea5e9);
27
  --grad-green: linear-gradient(135deg, #22c55e, #10b981);
28
 
 
 
29
  --simba-navy: #0f172a;
30
  --border-gold: #dca02a;
31
  }
 
33
  body {
34
  background-color: var(--bg-deep);
35
  font-family: 'Inter', sans-serif;
36
+ color: #334155;
37
  margin: 0;
38
  padding: 0;
39
  overflow-x: hidden;
40
  }
41
 
42
+ /* --- VISIBILITY CLASSES (FIXED) --- */
43
+ .view {
44
+ display: none !important; /* Force hide by default */
45
+ animation: fadeIn 0.4s ease-in-out;
46
+ }
47
+ .view.active {
48
+ display: block !important; /* Force show if active */
49
+ }
50
+
51
+ /* --- WAVE ANIMATION --- */
52
  .wave-bg {
53
+ position: absolute; top: 150px; left: 0; width: 100%; height: 300px;
54
+ background: repeating-linear-gradient(90deg, transparent, transparent 10px, rgba(255, 255, 255, 0.05) 10px, rgba(255, 255, 255, 0.05) 12px);
 
 
 
 
 
 
 
 
 
 
55
  mask-image: linear-gradient(to right, transparent, black 20%, black 80%, transparent);
56
+ z-index: 0; pointer-events: none;
 
 
 
 
 
 
 
 
 
 
57
  }
58
 
59
  /* --- NAVBAR --- */
60
+ .navbar { padding: 20px 40px; display: flex; justify-content: space-between; align-items: center; z-index: 10; position: relative; }
61
+ .nav-logo { color: white; font-family: 'Rubik', sans-serif; font-weight: 700; font-size: 20px; display: flex; align-items: center; gap: 10px; text-decoration: none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  .nav-logo img { height: 35px; }
 
63
  .nav-links { display: flex; gap: 30px; align-items: center; }
64
+ .nav-links a { color: #cbd5e1; text-decoration: none; font-size: 14px; font-weight: 500; transition: color 0.2s; }
 
 
 
 
 
 
65
  .nav-links a:hover { color: white; }
66
+ .nav-btn-action { background: var(--grad-blue); padding: 8px 20px; border-radius: 20px; color: white !important; font-weight: 600; box-shadow: 0 4px 10px rgba(59, 130, 246, 0.3); }
67
+ .login-btn { background: var(--text-yellow); color: var(--bg-deep) !important; padding: 8px 20px; border-radius: 20px; font-weight: 700; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ /* --- HERO --- */
70
+ .hero { display: flex; align-items: center; justify-content: space-between; max-width: 1300px; margin: 40px auto 80px; padding: 0 40px; position: relative; z-index: 2; }
71
  .hero-content { flex: 1; max-width: 650px; }
72
+ .conference-tag { background: white; display: inline-block; padding: 5px 15px; border-radius: 8px; margin-bottom: 20px; }
73
+ .hero h1 { font-family: 'Rubik', sans-serif; font-size: 56px; font-weight: 800; color: white; margin: 0; line-height: 1.1; }
74
+ .hero h2 { font-family: 'Rubik', sans-serif; font-size: 28px; font-weight: 700; color: var(--text-yellow); margin: 10px 0 20px; letter-spacing: 1px; }
75
+ .hero p { color: var(--text-mute); font-size: 16px; line-height: 1.6; margin-bottom: 40px; max-width: 90%; }
76
 
77
+ .hero-buttons { display: flex; flex-wrap: wrap; gap: 15px; }
78
+ .h-btn { text-decoration: none; color: white; padding: 12px 24px; border-radius: 30px; font-family: 'Rubik', sans-serif; font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 8px; transition: transform 0.2s, box-shadow 0.2s; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  .h-btn:hover { transform: translateY(-2px); filter: brightness(1.1); }
 
 
80
  .btn-paper { background: var(--grad-red); box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4); }
81
  .btn-pipeline { background: var(--grad-purple); box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4); }
82
  .btn-dataset { background: var(--grad-orange); box-shadow: 0 4px 15px rgba(249, 115, 22, 0.4); }
83
  .btn-models { background: var(--grad-blue); box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4); }
84
  .btn-leaderboard { background: var(--grad-green); box-shadow: 0 4px 15px rgba(34, 197, 94, 0.4); }
85
 
86
+ .hero-visual { flex: 1; display: flex; justify-content: flex-end; position: relative; }
87
+ .hero-visual img { max-width: 100%; height: auto; animation: fadeIn 1.5s ease-out; }
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  /* --- LEADERBOARD SECTION --- */
90
+ .leaderboard-section { background: #f8fafc; border-radius: 24px 24px 0 0; padding: 60px 40px; min-height: 100vh; }
 
 
 
 
 
 
91
  .lb-container { max-width: 1300px; margin: 0 auto; }
92
 
93
+ .content-card { background: white; border-radius: 16px; padding: 30px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); margin-bottom: 40px; }
 
 
 
 
 
 
94
 
95
+ /* Tabs Style */
96
+ .tabs { display: flex; gap: 10px; margin-bottom: 25px; border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; }
 
97
  .tab-btn {
98
+ padding: 12px 24px; border-radius: 8px; border: none; background: transparent;
99
+ font-weight: 600; color: #64748b; cursor: pointer; transition: 0.2s; font-size: 15px;
100
  }
101
+ .tab-btn:hover { background: #f1f5f9; color: var(--simba-navy); }
102
  .tab-btn.active { background: var(--simba-navy); color: white; box-shadow: 0 4px 10px rgba(15, 23, 42, 0.2); }
103
+
104
+ /* Controls */
105
+ .controls { display: flex; gap: 15px; margin-bottom: 20px; align-items: center; flex-wrap: wrap; background: #f1f5f9; padding: 15px; border-radius: 12px; }
106
+ select { padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1; cursor: pointer; font-size: 14px; min-width: 220px; }
107
+
108
+ /* Tables */
109
+ .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 8px; }
110
+ table { width: 100%; border-collapse: collapse; font-size: 14px; background: white; }
111
+ th { text-align: center; padding: 15px; color: var(--simba-navy); border-bottom: 2px solid #e2e8f0; vertical-align: bottom; background: #f8fafc; white-space: nowrap; }
112
  td { text-align: center; padding: 12px; border-bottom: 1px solid #f1f5f9; color: #334155; }
 
 
113
 
114
+ th:first-child, td:first-child { text-align: left; font-weight: 700; position: sticky; left: 0; background: white; z-index: 2; border-right: 2px solid #f1f5f9; }
115
+ thead th:first-child { z-index: 3; background: #f8fafc; }
116
+ th:nth-child(2), td:nth-child(2) { text-align: left; }
117
 
118
+ .arrow-up { color: #16a34a; } .arrow-down { color: #d97706; }
119
  @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
120
+
121
+ /* Mobile Resp */
 
122
  @media (max-width: 900px) {
123
  .hero { flex-direction: column; text-align: center; }
124
  .hero-visual { margin-top: 40px; justify-content: center; }
125
  .hero-buttons { justify-content: center; }
126
+ .nav-links { display: none; }
127
  }
128
  </style>
129
  </head>
 
135
  Voice of a Continent
136
  </a>
137
  <div class="nav-links">
138
+ <a href="https://arxiv.org/abs/2510.17998" target="_blank">Paper</a>
139
+ <a href="https://github.com/UBC-NLP/simba" target="_blank">Pipeline</a>
140
+ <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank">Dataset</a>
141
  <a href="#">Models</a>
142
+ <a href="#leaderboard-anchor" style="color:white; font-weight:bold;">Leaderboard</a>
143
  <a href="#">Citation</a>
144
  <a href="#" class="nav-btn-action">Submit New Results</a>
145
  <a href="#" class="login-btn">Login</a>
 
148
 
149
  <div class="hero">
150
  <div class="wave-bg"></div>
 
151
  <div class="hero-content">
152
  <div class="conference-tag">
153
  <span style="font-weight:800; color:#cc0000;">EMNLP 2025</span>
154
  <span style="font-size:12px; color:#555;">Suzhou, China</span>
155
  </div>
 
156
  <h1>Voice of a Continent</h1>
157
  <h2>Mapping Africa’s Speech Technology</h2>
 
158
  <p>
159
  SimbaBench bridges the digital divide with a unified suite for African AI: the largest
160
  open-source speech benchmark covering 61 languages, best-in-class multilingual models,
161
+ and an interactive leaderboard.
162
  </p>
 
163
  <div class="hero-buttons">
164
+ <a href="https://arxiv.org/abs/2510.17998" target="_blank" class="h-btn btn-paper"><i class="fa-regular fa-file-pdf"></i> Read Paper</a>
165
+ <a href="https://github.com/UBC-NLP/simba" target="_blank" class="h-btn btn-pipeline"><i class="fa-solid fa-code-branch"></i> Pipeline</a>
166
+ <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank" class="h-btn btn-dataset"><i class="fa-solid fa-database"></i> Dataset</a>
167
+ <a href="#" class="h-btn btn-models"><i class="fa-solid fa-bolt"></i> Models</a>
168
+ <a href="#leaderboard-anchor" class="h-btn btn-leaderboard"><i class="fa-solid fa-trophy"></i> Leaderboard</a>
 
 
 
 
 
 
 
 
 
 
169
  </div>
170
  </div>
 
171
  <div class="hero-visual">
172
+ <img src="https://africa.dlnlp.ai/simba/images/simbabench_header.png" alt="Africa Graphic">
173
  </div>
174
  </div>
175
 
176
  <div class="leaderboard-section" id="leaderboard-anchor">
177
  <div class="lb-container">
 
178
  <div class="content-card">
179
+
180
  <div id="loader" style="text-align:center; padding:40px; color:#64748b;">
181
  <i class="fa-solid fa-circle-notch fa-spin fa-2x"></i><br><br>Loading Leaderboard Data...
182
  </div>
183
 
184
  <div id="content" style="display:none;">
 
185
  <div class="tabs">
186
+ <button class="tab-btn active" id="tab-btn-asr" onclick="setTab('asr')">Automatic Speech Recognition (ASR)</button>
187
+ <button class="tab-btn" id="tab-btn-tts" onclick="setTab('tts')">Text-to-Speech (TTS)</button>
188
+ <button class="tab-btn" id="tab-btn-slid" onclick="setTab('slid')">Spoken Language Identification (SLID)</button>
189
  </div>
190
 
191
  <div id="asr" class="view active">
192
  <div class="controls">
193
  <span style="font-weight:600; color:#0f172a;">View Mode:</span>
194
+ <button id="btn-fam" class="h-btn btn-models" style="padding:6px 15px; font-size:12px; cursor:pointer;" onclick="setASRMode('family')">By Family</button>
195
+ <button id="btn-mod" class="h-btn" style="background:#cbd5e1; color:#334155; padding:6px 15px; font-size:12px; cursor:pointer;" onclick="setASRMode('model')">By Model</button>
196
 
197
  <div style="flex-grow:1; text-align:right;">
198
  <div id="grp-fam" style="display:inline-block;">
 
205
  </div>
206
  </div>
207
  </div>
 
208
  <h3 id="asr-title" style="color:#d97706; font-size:18px;"></h3>
209
  <div id="asr-legend" style="background:#f1f5f9; padding:10px; font-size:13px; border-radius:6px; margin-bottom:15px; display:none;"></div>
210
  <div id="asr-table" class="table-wrap"></div>
 
223
  <div id="slid" class="view">
224
  <div id="slid-table" class="table-wrap"></div>
225
  </div>
 
226
  </div>
227
  </div>
228
 
229
  <div style="text-align:center; color:#64748b; margin-top:40px;">
230
  <p>&copy; 2025 Simba Benchmark. <a href="https://africa.dlnlp.ai/simba/" style="color:#d97706;">Official Website</a></p>
231
  </div>
 
232
  </div>
233
  </div>
234
 
235
  <script>
236
+ // --- LOGIC ---
237
  const NAME_TO_ISO = {
238
  'Afrikaans': 'afr', 'Akuapim-twi': 'ak', 'Amharic': 'amh', 'Asante-twi': 'tw',
239
  'Bemba': 'bem', 'Hausa': 'hau', 'Igbo': 'ibo', 'Yoruba': 'yor', 'Zulu': 'zul',
 
243
  let DATA = null;
244
  let asrMode = 'family';
245
 
246
+ // Load Data
247
  fetch('/api/data').then(r=>r.json()).then(d=>{
248
  DATA = d;
249
  document.getElementById('loader').style.display='none';
 
257
  populate('asr-select-fam', DATA.metadata.families);
258
  populate('asr-select-mod', DATA.metadata.models);
259
  populate('tts-select', DATA.metadata.tts_models);
260
+
261
+ // Set Initial State Explicitly
262
+ setTab('asr');
263
+ renderASR();
264
+ renderTTS();
265
+ renderSLID();
266
  }
267
 
268
  function populate(id, list) {
 
270
  list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); });
271
  }
272
  function fmt(n) { return (n==null)?'N/A':n.toFixed(2); }
273
+ function getIso(name) { return NAME_TO_ISO[name] || name.substring(0,3).toLowerCase(); }
274
 
275
+ // --- RENDER ASR ---
276
  function renderASR() {
277
  const div = document.getElementById('asr-table');
278
  const legend = document.getElementById('asr-legend');
 
281
  if(asrMode==='family'){
282
  const val = document.getElementById('asr-select-fam').value;
283
  document.getElementById('asr-title').innerText = `Results for Family: ${val}`;
 
284
  const d = DATA.asr.by_family[val];
285
  if(!d) return;
286
 
 
287
  legend.style.display = 'block';
288
  legend.innerHTML = `<b>Codes:</b> ` + d.languages.map(l => `<span style="margin-right:10px"><b>${getIso(l)}</b>=${l}</span>`).join("");
289
 
 
290
  html = `<table><thead><tr><th>Model</th><th>Avg WER/CER <i class="fa-solid fa-arrow-down arrow-down"></i></th>`;
291
  d.languages.forEach(l => html += `<th>${getIso(l)}</th>`);
292
  html += `</tr></thead><tbody>`;
 
303
  const val = document.getElementById('asr-select-mod').value;
304
  document.getElementById('asr-title').innerText = `Results for Model: ${val}`;
305
  legend.style.display = 'none';
 
306
  const rows = DATA.asr.by_model[val];
307
  html = `<table><thead><tr><th>Language</th><th>Family</th><th>WER (%) <i class="fa-solid fa-arrow-down arrow-down"></i></th><th>CER (%) <i class="fa-solid fa-arrow-down arrow-down"></i></th></tr></thead><tbody>`;
308
  rows.forEach(r => {
309
  html += `<tr><td>${r.Language}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
310
  });
311
  }
 
312
  div.innerHTML = html;
313
  }
314
 
315
+ // --- RENDER TTS ---
316
  function renderTTS() {
317
  const val = document.getElementById('tts-select').value;
318
  document.getElementById('tts-title').innerText = `Results for Model: ${val}`;
 
319
  const rows = DATA.tts[val];
320
  if(!rows) return;
321
 
 
326
  {k:'speechbleu', l:'SpBLEU', h:true}, {k:'speechbert_score', l:'SpBERT', h:true}
327
  ];
328
 
 
329
  let sums={}, c=0;
330
  metrics.forEach(m=>sums[m.k]=0);
331
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
 
332
  let sumHTML = metrics.map(m => `<span>${m.l}: <b>${fmt(sums[m.k]/c)}</b></span>`).join(" | ");
333
  document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" + sumHTML;
334
 
 
335
  let html = `<table><thead><tr><th>Language</th>`;
336
  metrics.forEach(m => {
337
  let icon = m.h ? 'up' : 'down';
 
344
  metrics.forEach(m => html += `<td>${fmt(r[m.k])}</td>`);
345
  html += `</tr>`;
346
  });
 
347
  document.getElementById('tts-table').innerHTML = html;
348
  }
349
 
350
+ // --- RENDER SLID ---
351
  function renderSLID() {
352
  let html = `<table><thead><tr><th>Language</th><th>MMS <i class="fa-solid fa-arrow-up arrow-up"></i></th><th>Simba <i class="fa-solid fa-arrow-up arrow-up"></i></th></tr></thead><tbody>`;
353
  DATA.slid.forEach(r => {
 
356
  if(m > s) mS = `<b style="color:green">${mS}</b>`; else if(s > m) sS = `<b style="color:green">${sS}</b>`;
357
  html += `<tr><td>${r.Language}</td><td>${mS}</td><td>${sS}</td></tr>`;
358
  });
 
359
  document.getElementById('slid-table').innerHTML = html;
360
  }
361
 
362
+ // --- INTERACTION ---
363
+ window.setTab = (id) => {
364
+ // Hide all views
365
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
366
  document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active'));
367
+
368
+ // Show selected
369
+ document.getElementById(id).classList.add('active');
370
+ document.getElementById(`tab-btn-${id}`).classList.add('active');
371
  };
372
 
373
  window.setASRMode = (m) => {
374
  asrMode = m;
375
  document.getElementById('grp-fam').style.display = m==='family'?'inline-block':'none';
376
  document.getElementById('grp-mod').style.display = m==='model'?'inline-block':'none';
 
 
377
  const b1 = document.getElementById('btn-fam');
378
  const b2 = document.getElementById('btn-mod');
379
  if(m==='family') {