elmadany commited on
Commit
1f1ccf0
·
verified ·
1 Parent(s): 8e652c5

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +176 -129
public/index.html CHANGED
@@ -14,21 +14,19 @@
14
  <style>
15
  :root {
16
  --bg-deep: #1e1b4b; /* Deep Navy */
17
- --bg-lighter: #262357; /* Slightly lighter for cards */
18
  --text-yellow: #fbbf24; /* Brand Yellow */
19
  --text-white: #ffffff;
20
  --text-mute: #94a3b8;
21
 
22
- /* Buttons Gradients */
23
  --grad-blue: linear-gradient(90deg, #3b82f6, #0ea5e9);
24
- --grad-yellow: linear-gradient(90deg, #eab308, #ca8a04);
 
 
 
25
 
26
- /* Hero Buttons */
27
- --btn-red: #ef4444;
28
- --btn-purple: #8b5cf6;
29
- --btn-orange: #f97316;
30
- --btn-blue: #3b82f6;
31
- --btn-green: #22c55e;
32
  }
33
 
34
  body {
@@ -90,7 +88,7 @@
90
  display: flex;
91
  align-items: center;
92
  max-width: 1400px;
93
- margin: 60px auto 100px;
94
  padding: 0 40px;
95
  position: relative;
96
  }
@@ -111,140 +109,139 @@
111
  }
112
  .conf-badge i { color: #cc0000; font-size: 18px; }
113
 
114
- .hero h1 {
115
- font-family: 'Rubik', sans-serif;
116
- font-size: 64px;
117
- font-weight: 800;
118
- margin: 0;
119
- line-height: 1.1;
120
- }
121
- .hero h2 {
122
- font-family: 'Rubik', sans-serif;
123
- font-size: 32px;
124
- color: var(--text-yellow);
125
- margin: 15px 0 30px;
126
- font-weight: 700;
127
- }
128
- .hero p {
129
- color: #cbd5e1;
130
- font-size: 18px;
131
- line-height: 1.6;
132
- max-width: 650px;
133
- margin-bottom: 50px;
134
- }
135
 
136
  .hero-actions { display: flex; gap: 15px; flex-wrap: wrap; }
137
- .action-btn {
138
- display: flex; align-items: center; gap: 8px;
139
- padding: 12px 24px;
140
- border-radius: 30px;
141
- color: white;
142
- font-weight: 600;
143
- font-size: 15px;
144
- transition: transform 0.2s;
145
- }
146
  .action-btn:hover { transform: translateY(-3px); color: white; }
147
 
148
- .bg-red { background: var(--btn-red); box-shadow: 0 8px 20px rgba(239, 68, 68, 0.3); }
149
- .bg-purple { background: var(--btn-purple); box-shadow: 0 8px 20px rgba(139, 92, 246, 0.3); }
150
- .bg-orange { background: var(--btn-orange); box-shadow: 0 8px 20px rgba(249, 115, 22, 0.3); }
151
- .bg-blue { background: var(--btn-blue); box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3); }
152
- .bg-green { background: var(--btn-green); box-shadow: 0 8px 20px rgba(34, 197, 94, 0.3); }
153
 
 
154
  .hero-visual {
155
  flex: 1;
156
  display: flex;
157
  justify-content: flex-end;
158
  position: relative;
 
 
159
  }
160
- .hero-visual img { max-width: 100%; height: auto; z-index: 1; }
161
-
162
- /* Wave */
163
- .wave-container {
164
  position: absolute;
165
- top: 20%;
166
  left: 0;
167
  right: 0;
168
- height: 400px;
169
- background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNDQwIDMyMCI+PHBhdGggZmlsbD0iI2ZmZmZmZiIgZmlsbC1vcGFjaXR5PSIwLjAzIiBkPSJNMCwxNjBMMzAsMTQ5LjNDNjAsMTM5LDEyMCwxMTcsMTgwLDExN0MyNDAsMTE3LDMwMCwxMzksMzYwLDE0NCxDNDIwLDE0OSw0ODAsMTM5LDU0MCwxMjIuN0M2MDAsMTA3LDY2MCw4NSw3MjAsODUuM0M3ODAsODUsODQwLDEwNyw5MDAsMTI4Qzk2MCwxNDksMTAyMCwxNzEsMTA4MCwxNzAuN0MxMTQwLDE3MSwxMjAwLDE0OSwxMjYwLDEzOC43QzEzMjAsMTI4LDEzODAsMTI4LDE0MTAsMTI4TDE0NDAsMTI4TDE0NDAsMzIwTDE0MTAsMzIwQzEzODAsMzIwLDEzMjAsMzIwLDEyNjAsMzIwQzEyMDAsMzIwLDExNDAsMzIwLDEwODAsMzIwQzEwMjAsMzIwLDk2MCwzMjAsOTAwLDMyMEM4NDAsMzIwLDc4MCwzMjAsNzIwLDMyMEM2NjAsMzIwLDYwMCwzMjAsNTQwLDMyMEM0ODAsMzIwLDQyMCwzMjAsMzYwLDMyMEMzMDAsMzIwLDI0MCwzMjAsMTgwLDMyMEMxMjAsMzIwLDYwLDMyMCwzMCwzMjBMMCwzMjBaIj48L3BhdGg+PC9zdmc+');
170
- background-size: cover;
171
- opacity: 0.5;
 
172
  z-index: 0;
173
- pointer-events: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  }
175
 
176
  /* --- MAIN CONTENT AREA --- */
177
  .main-card {
178
  background: white;
179
  border-radius: 24px;
180
- margin: 0 40px 60px;
181
  padding: 40px;
182
  color: #0f172a;
183
  min-height: 600px;
 
 
184
  }
185
 
 
186
  .tabs { display: flex; gap: 15px; border-bottom: 2px solid #e2e8f0; margin-bottom: 30px; padding-bottom: 10px; }
187
  .tab-btn {
188
- background: none; border: none;
189
- font-size: 16px; font-weight: 600; color: #64748b;
190
- padding: 10px 20px; cursor: pointer;
191
- transition: 0.3s;
192
- border-radius: 8px;
193
  }
194
  .tab-btn:hover { background: #f1f5f9; color: var(--bg-deep); }
195
  .tab-btn.active { background: var(--bg-deep); color: white; }
196
 
197
- .view { display: none; animation: fadeIn 0.4s; }
198
- .view.active { display: block; }
199
 
200
  /* Controls */
201
- .controls {
202
- background: #f8fafc; padding: 20px; border-radius: 12px;
203
- display: flex; gap: 20px; align-items: center; margin-bottom: 20px; flex-wrap: wrap;
204
- }
205
- select {
206
- padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1;
207
- font-size: 14px; min-width: 250px;
208
- }
209
 
210
  /* Table */
211
  .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; }
212
  table { width: 100%; border-collapse: collapse; font-size: 14px; }
213
- th {
214
- background: #f1f5f9; color: #0f172a; font-weight: 700;
215
- padding: 16px; text-align: center; border-bottom: 2px solid #e2e8f0;
216
- white-space: nowrap; vertical-align: middle;
217
- }
218
- td {
219
- padding: 14px; border-bottom: 1px solid #e2e8f0; text-align: center; color: #334155;
220
- }
221
  tr:hover td { background: #f8fafc; }
222
 
223
- /* Sticky First Column */
224
- th:first-child, td:first-child {
225
- position: sticky; left: 0; background: white; z-index: 2;
226
- text-align: left; font-weight: 700; color: #0f172a;
227
- border-right: 2px solid #f1f5f9;
228
- }
229
  thead th:first-child { background: #f1f5f9; z-index: 3; }
230
  th:nth-child(2), td:nth-child(2) { text-align: left; }
231
 
232
- /* Citation Box */
233
- .citation-box {
 
 
234
  background: #0f172a;
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  color: #cbd5e1;
236
  padding: 20px;
237
  border-radius: 12px;
238
  font-family: monospace;
239
  white-space: pre-wrap;
240
  margin-top: 20px;
241
- text-align: left;
 
242
  }
243
 
244
- /* Footer */
245
  .footer { text-align: center; padding-bottom: 40px; color: #94a3b8; font-size: 14px; }
246
 
247
  @keyframes fadeIn { from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)} }
 
 
 
 
 
 
 
248
  </style>
249
  </head>
250
  <body>
@@ -270,7 +267,6 @@
270
  </nav>
271
 
272
  <div class="hero">
273
- <div class="wave-container"></div>
274
  <div class="hero-content">
275
  <div class="conf-badge">
276
  <i class="fa-solid fa-location-dot"></i> EMNLP 2025 · Suzhou, China
@@ -303,7 +299,17 @@
303
  </a>
304
  </div>
305
  </div>
 
306
  <div class="hero-visual">
 
 
 
 
 
 
 
 
 
307
  <img src="https://africa.dlnlp.ai/simba/images/simbabench_header.png" alt="Africa Map">
308
  </div>
309
  </div>
@@ -314,9 +320,9 @@
314
 
315
  <div id="app-content" style="display:none;">
316
  <div class="tabs">
317
- <button class="tab-btn active" onclick="setTab('asr', this)">Automatic Speech Recognition (ASR)</button>
318
- <button class="tab-btn" onclick="setTab('tts', this)">Text-to-Speech (TTS)</button>
319
- <button class="tab-btn" onclick="setTab('slid', this)">Spoken Language Identification (SLID)</button>
320
  </div>
321
 
322
  <div id="asr" class="view active">
@@ -327,7 +333,12 @@
327
 
328
  <div style="flex-grow:1; text-align:right;">
329
  <span style="font-weight:600; margin-right:10px;">Select:</span>
330
- <select id="asr-select" onchange="renderASR()"></select>
 
 
 
 
 
331
  </div>
332
  </div>
333
 
@@ -351,17 +362,17 @@
351
  <div id="slid-table" class="table-wrap"></div>
352
  </div>
353
  </div>
 
354
 
355
- <div id="citation" style="margin-top:60px;">
356
- <h3>Citation</h3>
357
- <p>If you use Simba, please cite our <a href="https://africa.dlnlp.ai/sahara/" style="color:#d97706; font-weight:bold;">ACL2025 paper</a>.</p>
358
- <div class="citation-box">@inproceedings{simba2025,
359
  title={Simba: A Unified Benchmark for Speech Tasks in African Languages},
360
  author={Put Authors Here},
361
  booktitle={ACL},
362
  year={2025}
363
  }</div>
364
- </div>
365
  </div>
366
 
367
  <div class="footer">
@@ -371,21 +382,37 @@
371
  </div>
372
 
373
  <script>
 
 
 
 
 
 
 
 
374
  let DATA = null;
375
  let asrMode = 'family';
376
 
377
- // Fetch Data
378
  fetch('/api/data').then(r=>r.json()).then(d=>{
379
  DATA = d;
380
  document.getElementById('loader').style.display='none';
381
  document.getElementById('app-content').style.display='block';
382
  init();
 
 
383
  });
384
 
385
  function init() {
386
- populate('asr-select', DATA.metadata.families); // Default family
 
387
  populate('tts-select', DATA.metadata.tts_models);
388
- renderASR(); renderTTS(); renderSLID();
 
 
 
 
 
389
  }
390
 
391
  function populate(id, list) {
@@ -393,24 +420,31 @@
393
  list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); });
394
  }
395
  function fmt(n) { return (n==null)?'N/A':n.toFixed(2); }
 
 
396
 
397
  // --- ASR ---
398
  function renderASR() {
399
- const val = document.getElementById('asr-select').value;
400
  const div = document.getElementById('asr-table');
401
  const legend = document.getElementById('asr-legend');
402
  let html='';
403
 
404
  if(asrMode==='family'){
405
- document.getElementById('asr-title').innerText = `Family: ${val}`;
 
 
406
  const d = DATA.asr.by_family[val];
407
  if(!d) return;
408
 
409
- // Use the ISO from data directly (languages is array of {name, iso})
410
  legend.style.display = 'block';
411
- legend.innerHTML = `<b>Codes:</b> ` + d.languages.map(l => `<span style="margin-right:12px"><b>${l.iso}</b>=${l.name}</span>`).join("");
 
 
 
412
 
413
- html = `<table><thead><tr><th>Model</th><th>Avg WER/CER <i class="fa-solid fa-arrow-down" style="font-size:10px; color:#d97706"></i></th>`;
 
414
  d.languages.forEach(l => html += `<th>${l.iso}</th>`);
415
  html += `</tr></thead><tbody>`;
416
 
@@ -423,10 +457,12 @@
423
  html += `</tr>`;
424
  });
425
  } else {
426
- document.getElementById('asr-title').innerText = `Model: ${val}`;
 
427
  legend.style.display = 'none';
 
428
  const rows = DATA.asr.by_model[val];
429
- html = `<table><thead><tr><th>Language</th><th>Family</th><th>WER (%)</th><th>CER (%)</th></tr></thead><tbody>`;
430
  rows.forEach(r => {
431
  html += `<tr><td>${r.Language}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
432
  });
@@ -437,23 +473,31 @@
437
  // --- TTS ---
438
  function renderTTS() {
439
  const val = document.getElementById('tts-select').value;
440
- document.getElementById('tts-title').innerText = `Model: ${val}`;
 
441
  const rows = DATA.tts[val];
442
  if(!rows) return;
443
 
444
  const metrics = [
445
- {k:'wer', l:'WER', h:false}, {k:'mcd', l:'MCD', h:false}, {k:'logf0rmse', l:'LogF0', h:false},
446
- {k:'pesq', l:'PESQ', h:true}, {k:'utmos', l:'UTMOS', h:true}, {k:'speechbleu', l:'BLEU', h:true}
 
 
447
  ];
448
 
449
  let sums={}, c=0;
450
  metrics.forEach(m=>sums[m.k]=0);
451
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
452
- let sumH = metrics.map(m=> `${m.l}: <b>${fmt(sums[m.k]/c)}</b>`).join(" | ");
453
- document.getElementById('tts-summary').innerHTML = "Averages: " + sumH;
 
454
 
455
  let html = `<table><thead><tr><th>Language</th>`;
456
- metrics.forEach(m => html += `<th>${m.l} <i class="fa-solid fa-arrow-${m.h?'up':'down'}" style="color:${m.h?'green':'orange'};font-size:10px"></i></th>`);
 
 
 
 
457
  html += `</tr></thead><tbody>`;
458
  rows.forEach(r => {
459
  html += `<tr><td>${r.language}</td>`;
@@ -465,36 +509,39 @@
465
 
466
  // --- SLID ---
467
  function renderSLID() {
468
- let html = `<table><thead><tr><th>Language</th><th>MMS</th><th>Simba</th></tr></thead><tbody>`;
469
  DATA.slid.forEach(r => {
470
  let m = r['MMS-LID-1024'], s = r['Simba-SLID'];
471
- html += `<tr><td>${r.Language}</td>
472
- <td style="color:${m>s?'green':'#333'}; font-weight:${m>s?'bold':'normal'}">${fmt(m)}</td>
473
- <td style="color:${s>m?'green':'#333'}; font-weight:${s>m?'bold':'normal'}">${fmt(s)}</td></tr>`;
474
  });
475
  document.getElementById('slid-table').innerHTML = html;
476
  }
477
 
478
- // --- UI ---
479
- window.setTab = (id, btn) => {
 
480
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
481
  document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active'));
482
- document.getElementById(id).classList.add('active');
483
- btn.classList.add('active');
 
 
484
  };
485
 
486
  window.setASRMode = (m) => {
487
  asrMode = m;
 
 
488
  const b1 = document.getElementById('btn-fam');
489
  const b2 = document.getElementById('btn-mod');
490
  if(m==='family') {
491
- b1.className="action-btn bg-blue"; b1.style.color="white"; b1.style.background="";
492
- b2.className="action-btn"; b2.style.background="#e2e8f0"; b2.style.color="#334155";
493
- populate('asr-select', DATA.metadata.families);
494
  } else {
495
- b2.className="action-btn bg-blue"; b2.style.color="white"; b2.style.background="";
496
- b1.className="action-btn"; b1.style.background="#e2e8f0"; b1.style.color="#334155";
497
- populate('asr-select', DATA.metadata.models);
498
  }
499
  renderASR();
500
  }
 
14
  <style>
15
  :root {
16
  --bg-deep: #1e1b4b; /* Deep Navy */
 
17
  --text-yellow: #fbbf24; /* Brand Yellow */
18
  --text-white: #ffffff;
19
  --text-mute: #94a3b8;
20
 
21
+ /* Gradients */
22
  --grad-blue: linear-gradient(90deg, #3b82f6, #0ea5e9);
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-green: linear-gradient(135deg, #22c55e, #10b981);
27
 
28
+ --simba-navy: #0f172a;
29
+ --border-gold: #dca02a;
 
 
 
 
30
  }
31
 
32
  body {
 
88
  display: flex;
89
  align-items: center;
90
  max-width: 1400px;
91
+ margin: 40px auto 80px;
92
  padding: 0 40px;
93
  position: relative;
94
  }
 
109
  }
110
  .conf-badge i { color: #cc0000; font-size: 18px; }
111
 
112
+ .hero h1 { font-family: 'Rubik', sans-serif; font-size: 64px; font-weight: 800; margin: 0; line-height: 1.1; }
113
+ .hero h2 { font-family: 'Rubik', sans-serif; font-size: 32px; color: var(--text-yellow); margin: 15px 0 30px; font-weight: 700; }
114
+ .hero p { color: #cbd5e1; font-size: 18px; line-height: 1.6; max-width: 650px; margin-bottom: 50px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  .hero-actions { display: flex; gap: 15px; flex-wrap: wrap; }
117
+ .action-btn { display: flex; align-items: center; gap: 8px; padding: 12px 24px; border-radius: 30px; color: white; font-weight: 600; font-size: 15px; transition: transform 0.2s; }
 
 
 
 
 
 
 
 
118
  .action-btn:hover { transform: translateY(-3px); color: white; }
119
 
120
+ .bg-red { background: var(--grad-red); box-shadow: 0 8px 20px rgba(239, 68, 68, 0.3); }
121
+ .bg-purple { background: var(--grad-purple); box-shadow: 0 8px 20px rgba(139, 92, 246, 0.3); }
122
+ .bg-orange { background: var(--grad-orange); box-shadow: 0 8px 20px rgba(249, 115, 22, 0.3); }
123
+ .bg-blue { background: var(--grad-blue); box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3); }
124
+ .bg-green { background: var(--grad-green); box-shadow: 0 8px 20px rgba(34, 197, 94, 0.3); }
125
 
126
+ /* --- CSS AUDIO WAVE VISUALIZER --- */
127
  .hero-visual {
128
  flex: 1;
129
  display: flex;
130
  justify-content: flex-end;
131
  position: relative;
132
+ height: 400px;
133
+ align-items: center;
134
  }
135
+ .hero-visual img { max-width: 100%; height: auto; z-index: 2; position: relative; }
136
+
137
+ .audio-wave {
 
138
  position: absolute;
139
+ bottom: 50px;
140
  left: 0;
141
  right: 0;
142
+ height: 150px;
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ gap: 6px;
147
  z-index: 0;
148
+ opacity: 0.3;
149
+ mask-image: linear-gradient(to right, transparent, black 20%, black 80%, transparent);
150
+ }
151
+ .bar {
152
+ width: 6px;
153
+ background: #ffffff;
154
+ border-radius: 99px;
155
+ animation: bounce 1.2s ease-in-out infinite;
156
+ }
157
+ /* Randomize animation delays for wave effect */
158
+ .bar:nth-child(odd) { animation-duration: 1.5s; }
159
+ .bar:nth-child(2n) { animation-duration: 1.1s; }
160
+ .bar:nth-child(3n) { animation-duration: 1.3s; }
161
+ .bar:nth-child(4n) { animation-duration: 0.9s; }
162
+
163
+ @keyframes bounce {
164
+ 0%, 100% { height: 20px; }
165
+ 50% { height: 120px; }
166
  }
167
 
168
  /* --- MAIN CONTENT AREA --- */
169
  .main-card {
170
  background: white;
171
  border-radius: 24px;
172
+ margin: 0 40px;
173
  padding: 40px;
174
  color: #0f172a;
175
  min-height: 600px;
176
+ position: relative;
177
+ z-index: 10;
178
  }
179
 
180
+ /* Tabs */
181
  .tabs { display: flex; gap: 15px; border-bottom: 2px solid #e2e8f0; margin-bottom: 30px; padding-bottom: 10px; }
182
  .tab-btn {
183
+ background: none; border: none; font-size: 16px; font-weight: 600; color: #64748b;
184
+ padding: 10px 20px; cursor: pointer; transition: 0.3s; border-radius: 8px;
 
 
 
185
  }
186
  .tab-btn:hover { background: #f1f5f9; color: var(--bg-deep); }
187
  .tab-btn.active { background: var(--bg-deep); color: white; }
188
 
189
+ .view { display: none !important; animation: fadeIn 0.4s; }
190
+ .view.active { display: block !important; }
191
 
192
  /* Controls */
193
+ .controls { background: #f8fafc; padding: 20px; border-radius: 12px; display: flex; gap: 20px; align-items: center; margin-bottom: 20px; flex-wrap: wrap; }
194
+ select { padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1; font-size: 14px; min-width: 250px; cursor:pointer;}
 
 
 
 
 
 
195
 
196
  /* Table */
197
  .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; }
198
  table { width: 100%; border-collapse: collapse; font-size: 14px; }
199
+ th { background: #f1f5f9; color: #0f172a; font-weight: 700; padding: 16px; text-align: center; border-bottom: 2px solid #e2e8f0; white-space: nowrap; vertical-align: middle; }
200
+ td { padding: 14px; border-bottom: 1px solid #e2e8f0; text-align: center; color: #334155; }
 
 
 
 
 
 
201
  tr:hover td { background: #f8fafc; }
202
 
203
+ th:first-child, td:first-child { position: sticky; left: 0; background: white; z-index: 2; text-align: left; font-weight: 700; color: #0f172a; border-right: 2px solid #f1f5f9; }
 
 
 
 
 
204
  thead th:first-child { background: #f1f5f9; z-index: 3; }
205
  th:nth-child(2), td:nth-child(2) { text-align: left; }
206
 
207
+ /* --- SEPARATE CITATION SECTION --- */
208
+ .citation-section {
209
+ max-width: 1000px;
210
+ margin: 60px auto;
211
  background: #0f172a;
212
+ border: 1px solid #334155;
213
+ border-radius: 16px;
214
+ padding: 40px;
215
+ box-shadow: 0 20px 40px rgba(0,0,0,0.3);
216
+ }
217
+ .citation-section h3 {
218
+ color: var(--text-yellow);
219
+ font-family: 'Rubik', sans-serif;
220
+ font-size: 24px;
221
+ margin-top: 0;
222
+ }
223
+ .citation-box {
224
+ background: #1e293b;
225
  color: #cbd5e1;
226
  padding: 20px;
227
  border-radius: 12px;
228
  font-family: monospace;
229
  white-space: pre-wrap;
230
  margin-top: 20px;
231
+ overflow-x: auto;
232
+ border: 1px solid #334155;
233
  }
234
 
 
235
  .footer { text-align: center; padding-bottom: 40px; color: #94a3b8; font-size: 14px; }
236
 
237
  @keyframes fadeIn { from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)} }
238
+
239
+ /* Mobile */
240
+ @media (max-width: 900px) {
241
+ .hero { flex-direction: column; text-align: center; padding: 20px; }
242
+ .hero-visual { display: none; } /* Hide map on small screens */
243
+ .nav-links { display: none; }
244
+ }
245
  </style>
246
  </head>
247
  <body>
 
267
  </nav>
268
 
269
  <div class="hero">
 
270
  <div class="hero-content">
271
  <div class="conf-badge">
272
  <i class="fa-solid fa-location-dot"></i> EMNLP 2025 · Suzhou, China
 
299
  </a>
300
  </div>
301
  </div>
302
+
303
  <div class="hero-visual">
304
+ <div class="audio-wave">
305
+ <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
306
+ <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
307
+ <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
308
+ <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
309
+ <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
310
+ <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
311
+ <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
312
+ </div>
313
  <img src="https://africa.dlnlp.ai/simba/images/simbabench_header.png" alt="Africa Map">
314
  </div>
315
  </div>
 
320
 
321
  <div id="app-content" style="display:none;">
322
  <div class="tabs">
323
+ <button class="tab-btn active" id="tab-btn-asr" onclick="setTab('asr')">Automatic Speech Recognition (ASR)</button>
324
+ <button class="tab-btn" id="tab-btn-tts" onclick="setTab('tts')">Text-to-Speech (TTS)</button>
325
+ <button class="tab-btn" id="tab-btn-slid" onclick="setTab('slid')">Spoken Language Identification (SLID)</button>
326
  </div>
327
 
328
  <div id="asr" class="view active">
 
333
 
334
  <div style="flex-grow:1; text-align:right;">
335
  <span style="font-weight:600; margin-right:10px;">Select:</span>
336
+ <div id="grp-fam" style="display:inline-block;">
337
+ <select id="asr-select-fam" onchange="renderASR()"></select>
338
+ </div>
339
+ <div id="grp-mod" style="display:none;">
340
+ <select id="asr-select-mod" onchange="renderASR()"></select>
341
+ </div>
342
  </div>
343
  </div>
344
 
 
362
  <div id="slid-table" class="table-wrap"></div>
363
  </div>
364
  </div>
365
+ </div>
366
 
367
+ <div class="citation-section" id="citation">
368
+ <h3>Citation</h3>
369
+ <p style="color:#94a3b8; margin-bottom:20px;">If you use the Simba benchmark, please cite our <a href="https://africa.dlnlp.ai/sahara/" style="color:#fbbf24; font-weight:bold;">ACL2025 paper</a>.</p>
370
+ <div class="citation-box">@inproceedings{simba2025,
371
  title={Simba: A Unified Benchmark for Speech Tasks in African Languages},
372
  author={Put Authors Here},
373
  booktitle={ACL},
374
  year={2025}
375
  }</div>
 
376
  </div>
377
 
378
  <div class="footer">
 
382
  </div>
383
 
384
  <script>
385
+ // --- LOGIC ---
386
+ // Basic mapping for common ISO codes if missing from data
387
+ const NAME_TO_ISO = {
388
+ 'Afrikaans': 'afr', 'Akuapim-twi': 'ak', 'Amharic': 'amh', 'Asante-twi': 'tw',
389
+ 'Bemba': 'bem', 'Hausa': 'hau', 'Igbo': 'ibo', 'Yoruba': 'yor', 'Zulu': 'zul',
390
+ 'Swahili': 'swa', 'Somali': 'som', 'Wolof': 'wol', 'Xhosa': 'xho'
391
+ };
392
+
393
  let DATA = null;
394
  let asrMode = 'family';
395
 
396
+ // Load Data
397
  fetch('/api/data').then(r=>r.json()).then(d=>{
398
  DATA = d;
399
  document.getElementById('loader').style.display='none';
400
  document.getElementById('app-content').style.display='block';
401
  init();
402
+ }).catch(e=>{
403
+ document.getElementById('loader').innerHTML = `<span style="color:red">Failed to load data.</span>`;
404
  });
405
 
406
  function init() {
407
+ populate('asr-select-fam', DATA.metadata.families);
408
+ populate('asr-select-mod', DATA.metadata.models);
409
  populate('tts-select', DATA.metadata.tts_models);
410
+
411
+ // Set Initial State Explicitly
412
+ setTab('asr');
413
+ renderASR();
414
+ renderTTS();
415
+ renderSLID();
416
  }
417
 
418
  function populate(id, list) {
 
420
  list.forEach(i=>{ let o=document.createElement('option'); o.value=i; o.text=i; s.add(o); });
421
  }
422
  function fmt(n) { return (n==null)?'N/A':n.toFixed(2); }
423
+ // Fallback to name-based ISO if data property missing
424
+ function getIso(name, dataIso) { return dataIso || NAME_TO_ISO[name] || name.substring(0,3).toLowerCase(); }
425
 
426
  // --- ASR ---
427
  function renderASR() {
 
428
  const div = document.getElementById('asr-table');
429
  const legend = document.getElementById('asr-legend');
430
  let html='';
431
 
432
  if(asrMode==='family'){
433
+ const val = document.getElementById('asr-select-fam').value;
434
+ document.getElementById('asr-title').innerText = `Results for Family: ${val}`;
435
+
436
  const d = DATA.asr.by_family[val];
437
  if(!d) return;
438
 
439
+ // Legend
440
  legend.style.display = 'block';
441
+ legend.innerHTML = `<b>Codes:</b> ` + d.languages.map(l => {
442
+ // l is object {name: 'Amharic', iso: 'amh'} from server.js
443
+ return `<span style="margin-right:12px"><b>${l.iso}</b>=${l.name}</span>`;
444
+ }).join("");
445
 
446
+ // Table
447
+ html = `<table><thead><tr><th>Model</th><th>Avg WER/CER <i class="fa-solid fa-arrow-down arrow-down"></i></th>`;
448
  d.languages.forEach(l => html += `<th>${l.iso}</th>`);
449
  html += `</tr></thead><tbody>`;
450
 
 
457
  html += `</tr>`;
458
  });
459
  } else {
460
+ const val = document.getElementById('asr-select-mod').value;
461
+ document.getElementById('asr-title').innerText = `Results for Model: ${val}`;
462
  legend.style.display = 'none';
463
+
464
  const rows = DATA.asr.by_model[val];
465
+ 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>`;
466
  rows.forEach(r => {
467
  html += `<tr><td>${r.Language}</td><td>${r.Family}</td><td>${fmt(r.WER)}</td><td>${fmt(r.CER)}</td></tr>`;
468
  });
 
473
  // --- TTS ---
474
  function renderTTS() {
475
  const val = document.getElementById('tts-select').value;
476
+ document.getElementById('tts-title').innerText = `Results for Model: ${val}`;
477
+
478
  const rows = DATA.tts[val];
479
  if(!rows) return;
480
 
481
  const metrics = [
482
+ {k:'wer', l:'WER', h:false}, {k:'mcd', l:'MCD', h:false},
483
+ {k:'logf0rmse', l:'LogF0', h:false}, {k:'speech_token_distance', l:'SpTokDist', h:false},
484
+ {k:'pesq', l:'PESQ', h:true}, {k:'utmos', l:'UTMOS', h:true},
485
+ {k:'speechbleu', l:'SpBLEU', h:true}, {k:'speechbert_score', l:'SpBERT', h:true}
486
  ];
487
 
488
  let sums={}, c=0;
489
  metrics.forEach(m=>sums[m.k]=0);
490
  rows.forEach(r=>{ metrics.forEach(m=>sums[m.k]+=(r[m.k]||0)); c++; });
491
+
492
+ let sumHTML = metrics.map(m => `<span>${m.l}: <b>${fmt(sums[m.k]/c)}</b></span>`).join(" | ");
493
+ document.getElementById('tts-summary').innerHTML = "<b>Averages: </b>" + sumHTML;
494
 
495
  let html = `<table><thead><tr><th>Language</th>`;
496
+ metrics.forEach(m => {
497
+ let icon = m.h ? 'up' : 'down';
498
+ let col = m.h ? 'green' : '#d97706';
499
+ html += `<th>${m.l} <i class="fa-solid fa-arrow-${icon}" style="color:${col}"></i></th>`;
500
+ });
501
  html += `</tr></thead><tbody>`;
502
  rows.forEach(r => {
503
  html += `<tr><td>${r.language}</td>`;
 
509
 
510
  // --- SLID ---
511
  function renderSLID() {
512
+ 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>`;
513
  DATA.slid.forEach(r => {
514
  let m = r['MMS-LID-1024'], s = r['Simba-SLID'];
515
+ let mS = fmt(m), sS = fmt(s);
516
+ if(m > s) mS = `<b style="color:green">${mS}</b>`; else if(s > m) sS = `<b style="color:green">${sS}</b>`;
517
+ html += `<tr><td>${r.Language}</td><td>${mS}</td><td>${sS}</td></tr>`;
518
  });
519
  document.getElementById('slid-table').innerHTML = html;
520
  }
521
 
522
+ // --- UI INTERACTION ---
523
+ window.setTab = (id) => {
524
+ // Hide all views
525
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
526
  document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active'));
527
+
528
+ // Show selected
529
+ document.getElementById(id).classList.add('active');
530
+ document.getElementById(`tab-btn-${id}`).classList.add('active');
531
  };
532
 
533
  window.setASRMode = (m) => {
534
  asrMode = m;
535
+ document.getElementById('grp-fam').style.display = m==='family'?'inline-block':'none';
536
+ document.getElementById('grp-mod').style.display = m==='model'?'inline-block':'none';
537
  const b1 = document.getElementById('btn-fam');
538
  const b2 = document.getElementById('btn-mod');
539
  if(m==='family') {
540
+ b1.className = "action-btn bg-blue"; b1.style.color="white"; b1.style.background="";
541
+ b2.className = "action-btn"; b2.style.background="#e2e8f0"; b2.style.color="#334155";
 
542
  } else {
543
+ b2.className = "action-btn bg-blue"; b2.style.color="white"; b2.style.background="";
544
+ b1.className = "action-btn"; b1.style.background="#e2e8f0"; b1.style.color="#334155";
 
545
  }
546
  renderASR();
547
  }