davidpomerenke commited on
Commit
15a7ddf
·
verified ·
1 Parent(s): 18acfdb

Upload from GitHub Actions: Refresh dashboard UI: design tokens, indigo accent, research footer

Browse files

Introduce a CSS design-token layer (palette, type, spacing, shadows) in index.css and apply it across the app:

- Hero: system-sans headline and flat indigo primary + outline secondary buttons, replacing the gradient/emoji buttons; quiet GitHub link
- Cool off-white background with a single restrained indigo accent on links, primary button, active filter states and the active carousel dot
- Harmonized table header tints and softened card/chart-panel chrome; data tables, score bars, medals and world-map encodings unchanged
- Refreshed About/Contribute dialogs and added a footer with the paper citation (arXiv:2507.08538) and BMZ/GIZ/DFKI credits
- Fix chart carousel overflow so the indicator dots no longer overlap the footer
- Drop the external web-font dependency (system fonts only)

frontend/public/index.html CHANGED
@@ -4,7 +4,7 @@
4
  <meta charset="utf-8" />
5
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%221em%22 font-size=%2280%22>🌍</text></svg>">
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <meta name="theme-color" content="#000000" />
8
  <meta
9
  name="description"
10
  content="AI Language Benchmarks – model evaluations for every language in the world"
 
4
  <meta charset="utf-8" />
5
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%221em%22 font-size=%2280%22>🌍</text></svg>">
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="theme-color" content="#fbfbfd" />
8
  <meta
9
  name="description"
10
  content="AI Language Benchmarks – model evaluations for every language in the world"
frontend/src/App.js CHANGED
@@ -1,6 +1,5 @@
1
  import { useState, useEffect } from 'react'
2
  import { PrimeReactProvider } from 'primereact/api'
3
- import 'primereact/resources/themes/lara-light-cyan/theme.css'
4
  import ModelTable from './components/ModelTable'
5
  import LanguageTable from './components/LanguageTable'
6
  import DatasetTable from './components/DatasetTable'
@@ -12,6 +11,7 @@ import HistoryPlot from './components/HistoryPlot'
12
  import LanguageTierHistoryPlot from './components/LanguageTierHistoryPlot'
13
  import LicenseHistoryPlot from './components/LicenseHistoryPlot'
14
  import CostPlot from './components/CostPlot'
 
15
  import { Carousel } from 'primereact/carousel'
16
  import { Dialog } from 'primereact/dialog'
17
  import { Button } from 'primereact/button'
@@ -153,7 +153,7 @@ function App () {
153
  minHeight: '100vh',
154
  display: 'flex',
155
  flexDirection: 'column',
156
- width: '100vw'
157
  }}
158
  >
159
  {/* <div
@@ -184,24 +184,7 @@ function App () {
184
  href='https://github.com/datenlabor-bmz/evals-for-every-language'
185
  target='_blank'
186
  rel='noopener noreferrer'
187
- style={{
188
- textDecoration: 'none',
189
- color: '#6c757d',
190
- fontSize: '1rem',
191
- fontWeight: '500',
192
- padding: '0.5rem 1rem',
193
- borderRadius: '0.375rem',
194
- backgroundColor: '#f8f9fa',
195
- border: '1px solid #e9ecef',
196
- display: 'flex',
197
- alignItems: 'center',
198
- gap: '0.5rem',
199
- transition: 'all 0.2s ease',
200
- ':hover': {
201
- backgroundColor: '#e9ecef',
202
- color: '#495057'
203
- }
204
- }}
205
  >
206
  <i className='pi pi-github' title='View on GitHub' />
207
  GitHub
@@ -228,21 +211,11 @@ function App () {
228
  🌍
229
  </span>
230
  </div>
231
- <h1
232
- style={{
233
- fontSize: '2.5rem',
234
- fontWeight: '600',
235
- margin: '1rem 0 0.5rem 0',
236
- color: '#333',
237
- letterSpacing: '-0.01em'
238
- }}
239
- >
240
- AI Language Benchmarks
241
- </h1>
242
  <p
243
  style={{
244
- fontSize: '1.1rem',
245
- color: '#666',
246
  margin: '0 0 2.5rem 0',
247
  fontWeight: '400',
248
  maxWidth: '700px',
@@ -263,68 +236,16 @@ function App () {
263
  >
264
  <button
265
  onClick={() => setAboutVisible(true)}
266
- style={{
267
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
268
- color: 'white',
269
- border: 'none',
270
- padding: '0.75rem 1.5rem',
271
- borderRadius: '12px',
272
- fontSize: '0.95rem',
273
- fontWeight: '500',
274
- cursor: 'pointer',
275
- display: 'flex',
276
- alignItems: 'center',
277
- gap: '0.5rem',
278
- boxShadow: '0 4px 15px rgba(102, 126, 234, 0.25)',
279
- transition: 'all 0.3s ease',
280
- ':hover': {
281
- transform: 'translateY(-2px)',
282
- boxShadow: '0 8px 25px rgba(102, 126, 234, 0.35)'
283
- }
284
- }}
285
- onMouseEnter={(e) => {
286
- e.target.style.transform = 'translateY(-2px)';
287
- e.target.style.boxShadow = '0 8px 25px rgba(102, 126, 234, 0.35)';
288
- }}
289
- onMouseLeave={(e) => {
290
- e.target.style.transform = 'translateY(0)';
291
- e.target.style.boxShadow = '0 4px 15px rgba(102, 126, 234, 0.25)';
292
- }}
293
  >
294
- <span style={{ fontSize: '1.1rem' }}>📚</span>
295
  About this tool
296
  </button>
297
 
298
  <button
299
  onClick={() => setContributeVisible(true)}
300
  title='This feature is on our roadmap and will be available soon.'
301
- style={{
302
- background: 'linear-gradient(135deg, #ff9a9e 0%, #fecfef 50%, #fecfef 100%)',
303
- color: '#6b46c1',
304
- border: 'none',
305
- padding: '0.75rem 1.5rem',
306
- borderRadius: '12px',
307
- fontSize: '0.95rem',
308
- fontWeight: '500',
309
- cursor: 'pointer',
310
- display: 'flex',
311
- alignItems: 'center',
312
- gap: '0.5rem',
313
- boxShadow: '0 4px 15px rgba(255, 154, 158, 0.25)',
314
- transition: 'all 0.3s ease',
315
- position: 'relative',
316
- overflow: 'hidden'
317
- }}
318
- onMouseEnter={(e) => {
319
- e.target.style.transform = 'translateY(-2px)';
320
- e.target.style.boxShadow = '0 8px 25px rgba(255, 154, 158, 0.35)';
321
- }}
322
- onMouseLeave={(e) => {
323
- e.target.style.transform = 'translateY(0)';
324
- e.target.style.boxShadow = '0 4px 15px rgba(255, 154, 158, 0.25)';
325
- }}
326
  >
327
- <span style={{ fontSize: '1.1rem' }}>🚀</span>
328
  Add your model/benchmark
329
  </button>
330
  </div>
@@ -428,6 +349,7 @@ function App () {
428
  </>
429
  )}
430
  </main>
 
431
 
432
  {/* About Dialog */}
433
  <Dialog
@@ -441,7 +363,7 @@ function App () {
441
  <p>
442
  <i>languagebench</i> provides AI model evaluations for every language in the world.
443
  </p>
444
- <h4>👥 Who is this for?</h4>
445
  <ul>
446
  <li>
447
  <b>Practitioners</b> can pick the best model for a given
@@ -455,7 +377,7 @@ function App () {
455
  <b>Model developers</b> can compete on our benchmarks.
456
  </li>
457
  </ul>
458
- <h4>Live Updates</h4>
459
  <p>
460
  Benchmark results automatically refresh every night and include
461
  the most popular models from{' '}
@@ -468,11 +390,11 @@ function App () {
468
  </a>
469
  , plus community-submitted models.
470
  </p>
471
- <h4>⚠️ Note on interpretation</h4>
472
  <p>
473
  Results are currently based on a sample of 10 sentences per language and task to keep computation affordable. For this reason, we report confidence intervals and recommend treating small differences between models with caution. In future iterations, we plan to add more benchmark datasets and richer visualisations, with large-scale evaluations across many more prompts and tasks as a longer-term goal.
474
  </p>
475
- <h4>✍️ Authors</h4>
476
  <p>
477
  languagebench is a collaboration between
478
  BMZ's{' '}
@@ -513,7 +435,7 @@ function App () {
513
  </a>{' '}
514
  of DFKI's Multilinguality and Language Technology Lab.
515
  </p>
516
- <h4>🔗 Links</h4>
517
  <p>
518
  <a
519
  href='https://github.com/datenlabor-bmz/evals-for-every-language'
@@ -543,7 +465,7 @@ function App () {
543
  header='Contribute your Model/Benchmark'
544
  >
545
  <div>
546
- <h4>🚀 Submit Your Model</h4>
547
  <p>
548
  Have a custom fine-tuned model you'd like to see on the
549
  leaderboard or a new benchmark you think should be added?
@@ -553,13 +475,13 @@ function App () {
553
  href='https://forms.gle/ckvY9pS7XLcHYnaV8'
554
  target='_blank'
555
  rel='noopener noreferrer'
556
- style={{ color: '#28a745', fontWeight: 'bold' }}
557
  >
558
  → Submit your model here
559
  </a>
560
  </p>
561
 
562
- <h4>🔧 Contribute to Development</h4>
563
  <p>
564
  Help us expand language coverage and add new evaluation tasks:
565
  </p>
@@ -568,7 +490,7 @@ function App () {
568
  href='https://github.com/datenlabor-bmz/evals-for-every-language/blob/main/CONTRIBUTING.md'
569
  target='_blank'
570
  rel='noopener noreferrer'
571
- style={{ color: '#007bff', fontWeight: 'bold' }}
572
  >
573
  → Contribution guidelines
574
  </a>
 
1
  import { useState, useEffect } from 'react'
2
  import { PrimeReactProvider } from 'primereact/api'
 
3
  import ModelTable from './components/ModelTable'
4
  import LanguageTable from './components/LanguageTable'
5
  import DatasetTable from './components/DatasetTable'
 
11
  import LanguageTierHistoryPlot from './components/LanguageTierHistoryPlot'
12
  import LicenseHistoryPlot from './components/LicenseHistoryPlot'
13
  import CostPlot from './components/CostPlot'
14
+ import Footer from './components/Footer'
15
  import { Carousel } from 'primereact/carousel'
16
  import { Dialog } from 'primereact/dialog'
17
  import { Button } from 'primereact/button'
 
153
  minHeight: '100vh',
154
  display: 'flex',
155
  flexDirection: 'column',
156
+ width: '100%'
157
  }}
158
  >
159
  {/* <div
 
184
  href='https://github.com/datenlabor-bmz/evals-for-every-language'
185
  target='_blank'
186
  rel='noopener noreferrer'
187
+ className='ghost-link'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  >
189
  <i className='pi pi-github' title='View on GitHub' />
190
  GitHub
 
211
  🌍
212
  </span>
213
  </div>
214
+ <h1 className='hero-title'>AI Language Benchmarks</h1>
 
 
 
 
 
 
 
 
 
 
215
  <p
216
  style={{
217
+ fontSize: '1.15rem',
218
+ color: 'var(--ink-muted)',
219
  margin: '0 0 2.5rem 0',
220
  fontWeight: '400',
221
  maxWidth: '700px',
 
236
  >
237
  <button
238
  onClick={() => setAboutVisible(true)}
239
+ className='btn btn-primary'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  >
 
241
  About this tool
242
  </button>
243
 
244
  <button
245
  onClick={() => setContributeVisible(true)}
246
  title='This feature is on our roadmap and will be available soon.'
247
+ className='btn btn-secondary'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  >
 
249
  Add your model/benchmark
250
  </button>
251
  </div>
 
349
  </>
350
  )}
351
  </main>
352
+ <Footer />
353
 
354
  {/* About Dialog */}
355
  <Dialog
 
363
  <p>
364
  <i>languagebench</i> provides AI model evaluations for every language in the world.
365
  </p>
366
+ <h4>Who is this for?</h4>
367
  <ul>
368
  <li>
369
  <b>Practitioners</b> can pick the best model for a given
 
377
  <b>Model developers</b> can compete on our benchmarks.
378
  </li>
379
  </ul>
380
+ <h4>Live Updates</h4>
381
  <p>
382
  Benchmark results automatically refresh every night and include
383
  the most popular models from{' '}
 
390
  </a>
391
  , plus community-submitted models.
392
  </p>
393
+ <h4>Note on interpretation</h4>
394
  <p>
395
  Results are currently based on a sample of 10 sentences per language and task to keep computation affordable. For this reason, we report confidence intervals and recommend treating small differences between models with caution. In future iterations, we plan to add more benchmark datasets and richer visualisations, with large-scale evaluations across many more prompts and tasks as a longer-term goal.
396
  </p>
397
+ <h4>Authors</h4>
398
  <p>
399
  languagebench is a collaboration between
400
  BMZ's{' '}
 
435
  </a>{' '}
436
  of DFKI's Multilinguality and Language Technology Lab.
437
  </p>
438
+ <h4>Links</h4>
439
  <p>
440
  <a
441
  href='https://github.com/datenlabor-bmz/evals-for-every-language'
 
465
  header='Contribute your Model/Benchmark'
466
  >
467
  <div>
468
+ <h4>Submit Your Model</h4>
469
  <p>
470
  Have a custom fine-tuned model you'd like to see on the
471
  leaderboard or a new benchmark you think should be added?
 
475
  href='https://forms.gle/ckvY9pS7XLcHYnaV8'
476
  target='_blank'
477
  rel='noopener noreferrer'
478
+ style={{ color: 'var(--accent)', fontWeight: 'bold' }}
479
  >
480
  → Submit your model here
481
  </a>
482
  </p>
483
 
484
+ <h4>Contribute to Development</h4>
485
  <p>
486
  Help us expand language coverage and add new evaluation tasks:
487
  </p>
 
490
  href='https://github.com/datenlabor-bmz/evals-for-every-language/blob/main/CONTRIBUTING.md'
491
  target='_blank'
492
  rel='noopener noreferrer'
493
+ style={{ color: 'var(--accent)', fontWeight: 'bold' }}
494
  >
495
  → Contribution guidelines
496
  </a>
frontend/src/components/AutoComplete.js CHANGED
@@ -114,12 +114,12 @@ const AutoComplete = ({ languages, onComplete }) => {
114
  }
115
 
116
  const buttonStyle = {
117
- color: selectedLanguage ? '#333' : '#666',
118
- border: '1px solid #ddd',
119
  padding: '0.75rem 1rem',
120
- borderRadius: '4px',
121
  fontSize: '0.95rem',
122
- backgroundColor: '#fff',
123
  cursor: 'pointer',
124
  display: 'flex',
125
  alignItems: 'center',
@@ -135,7 +135,7 @@ const AutoComplete = ({ languages, onComplete }) => {
135
  fontSize: '0.95rem',
136
  width: '100%',
137
  backgroundColor: 'transparent',
138
- color: '#333'
139
  }
140
 
141
  const dropdownStyle = {
@@ -143,20 +143,20 @@ const AutoComplete = ({ languages, onComplete }) => {
143
  top: '100%',
144
  left: 0,
145
  right: 0,
146
- backgroundColor: '#fff',
147
- border: '1px solid #ddd',
148
  borderTop: 'none',
149
- borderRadius: '0 0 4px 4px',
150
  maxHeight: '300px',
151
  overflowY: 'auto',
152
  zIndex: 1000,
153
- boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
154
  }
155
 
156
  const itemStyle = {
157
  padding: '0.75rem 1rem',
158
  cursor: 'pointer',
159
- borderBottom: '1px solid #f0f0f0',
160
  display: 'flex',
161
  justifyContent: 'space-between',
162
  alignItems: 'center',
@@ -166,7 +166,7 @@ const AutoComplete = ({ languages, onComplete }) => {
166
  const clearButtonStyle = {
167
  background: 'none',
168
  border: 'none',
169
- color: '#999',
170
  cursor: 'pointer',
171
  padding: '0.25rem',
172
  borderRadius: '50%',
@@ -186,11 +186,11 @@ const AutoComplete = ({ languages, onComplete }) => {
186
  onClick={handleContainerClick}
187
  onMouseEnter={e => {
188
  if (!selectedLanguage) {
189
- e.target.style.borderColor = '#bbb'
190
  }
191
  }}
192
  onMouseLeave={e => {
193
- e.target.style.borderColor = '#ddd'
194
  }}
195
  >
196
  {selectedLanguage && !isOpen ? (
@@ -202,7 +202,7 @@ const AutoComplete = ({ languages, onComplete }) => {
202
  style={clearButtonStyle}
203
  onClick={handleClear}
204
  onMouseEnter={e => {
205
- e.target.style.backgroundColor = '#f0f0f0'
206
  }}
207
  onMouseLeave={e => {
208
  e.target.style.backgroundColor = 'transparent'
@@ -229,7 +229,7 @@ const AutoComplete = ({ languages, onComplete }) => {
229
  <span>Go to leaderboard for specific language...</span>
230
  )}
231
  {(!selectedLanguage || isOpen) && (
232
- <span style={{ color: '#999', fontSize: '12px' }}>
233
  {isOpen ? '▲' : '▼'}
234
  </span>
235
  )}
@@ -238,7 +238,7 @@ const AutoComplete = ({ languages, onComplete }) => {
238
  {isOpen && (
239
  <div style={dropdownStyle}>
240
  {filteredLanguages.length === 0 ? (
241
- <div style={{ ...itemStyle, color: '#999', cursor: 'default' }}>
242
  No languages found
243
  </div>
244
  ) : (
@@ -248,7 +248,7 @@ const AutoComplete = ({ languages, onComplete }) => {
248
  style={itemStyle}
249
  onClick={() => handleSelect(language)}
250
  onMouseEnter={e => {
251
- e.target.style.backgroundColor = '#f8f9fa'
252
  }}
253
  onMouseLeave={e => {
254
  e.target.style.backgroundColor = 'transparent'
@@ -258,11 +258,11 @@ const AutoComplete = ({ languages, onComplete }) => {
258
  <div style={{ fontWeight: '500', marginBottom: '2px' }}>
259
  {language.language_name} Leaderboard
260
  </div>
261
- <div style={{ fontSize: '0.85rem', color: '#666' }}>
262
  {language.autonym}
263
  </div>
264
  </div>
265
- <div style={{ color: '#999', fontSize: '0.8rem' }}>
266
  {language.bcp_47}
267
  </div>
268
  </div>
@@ -272,7 +272,7 @@ const AutoComplete = ({ languages, onComplete }) => {
272
  <div
273
  style={{
274
  ...itemStyle,
275
- color: '#999',
276
  cursor: 'default',
277
  fontStyle: 'italic'
278
  }}
 
114
  }
115
 
116
  const buttonStyle = {
117
+ color: selectedLanguage ? 'var(--ink)' : 'var(--ink-muted)',
118
+ border: '1px solid var(--border)',
119
  padding: '0.75rem 1rem',
120
+ borderRadius: 'var(--radius-sm)',
121
  fontSize: '0.95rem',
122
+ backgroundColor: 'var(--surface)',
123
  cursor: 'pointer',
124
  display: 'flex',
125
  alignItems: 'center',
 
135
  fontSize: '0.95rem',
136
  width: '100%',
137
  backgroundColor: 'transparent',
138
+ color: 'var(--ink)'
139
  }
140
 
141
  const dropdownStyle = {
 
143
  top: '100%',
144
  left: 0,
145
  right: 0,
146
+ backgroundColor: 'var(--surface)',
147
+ border: '1px solid var(--border)',
148
  borderTop: 'none',
149
+ borderRadius: '0 0 var(--radius-sm) var(--radius-sm)',
150
  maxHeight: '300px',
151
  overflowY: 'auto',
152
  zIndex: 1000,
153
+ boxShadow: 'var(--shadow-md)'
154
  }
155
 
156
  const itemStyle = {
157
  padding: '0.75rem 1rem',
158
  cursor: 'pointer',
159
+ borderBottom: '1px solid var(--hairline)',
160
  display: 'flex',
161
  justifyContent: 'space-between',
162
  alignItems: 'center',
 
166
  const clearButtonStyle = {
167
  background: 'none',
168
  border: 'none',
169
+ color: 'var(--ink-subtle)',
170
  cursor: 'pointer',
171
  padding: '0.25rem',
172
  borderRadius: '50%',
 
186
  onClick={handleContainerClick}
187
  onMouseEnter={e => {
188
  if (!selectedLanguage) {
189
+ e.target.style.borderColor = 'var(--accent)'
190
  }
191
  }}
192
  onMouseLeave={e => {
193
+ e.target.style.borderColor = 'var(--border)'
194
  }}
195
  >
196
  {selectedLanguage && !isOpen ? (
 
202
  style={clearButtonStyle}
203
  onClick={handleClear}
204
  onMouseEnter={e => {
205
+ e.target.style.backgroundColor = 'var(--accent-wash)'
206
  }}
207
  onMouseLeave={e => {
208
  e.target.style.backgroundColor = 'transparent'
 
229
  <span>Go to leaderboard for specific language...</span>
230
  )}
231
  {(!selectedLanguage || isOpen) && (
232
+ <span style={{ color: 'var(--ink-subtle)', fontSize: '12px' }}>
233
  {isOpen ? '▲' : '▼'}
234
  </span>
235
  )}
 
238
  {isOpen && (
239
  <div style={dropdownStyle}>
240
  {filteredLanguages.length === 0 ? (
241
+ <div style={{ ...itemStyle, color: 'var(--ink-subtle)', cursor: 'default' }}>
242
  No languages found
243
  </div>
244
  ) : (
 
248
  style={itemStyle}
249
  onClick={() => handleSelect(language)}
250
  onMouseEnter={e => {
251
+ e.target.style.backgroundColor = 'var(--accent-wash)'
252
  }}
253
  onMouseLeave={e => {
254
  e.target.style.backgroundColor = 'transparent'
 
258
  <div style={{ fontWeight: '500', marginBottom: '2px' }}>
259
  {language.language_name} Leaderboard
260
  </div>
261
+ <div style={{ fontSize: '0.85rem', color: 'var(--ink-muted)' }}>
262
  {language.autonym}
263
  </div>
264
  </div>
265
+ <div style={{ color: 'var(--ink-subtle)', fontSize: '0.8rem' }}>
266
  {language.bcp_47}
267
  </div>
268
  </div>
 
272
  <div
273
  style={{
274
  ...itemStyle,
275
+ color: 'var(--ink-subtle)',
276
  cursor: 'default',
277
  fontStyle: 'italic'
278
  }}
frontend/src/components/DatasetTable.js CHANGED
@@ -106,7 +106,7 @@ const DatasetTable = ({ data }) => {
106
  groupRowsBy='group'
107
  header={
108
  <span>
109
- <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>Datasets</span>
110
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
111
  Multilingual datasets used for this evaluation, and beyond
112
  </span>
 
106
  groupRowsBy='group'
107
  header={
108
  <span>
109
+ <span style={{ fontWeight: 600, fontSize: '1.15em', color: 'var(--ink)' }}>Datasets</span>
110
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
111
  Multilingual datasets used for this evaluation, and beyond
112
  </span>
frontend/src/components/Footer.js ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const linkStyle = {
2
+ color: 'var(--accent)',
3
+ textDecoration: 'none',
4
+ fontWeight: 500
5
+ }
6
+
7
+ const Footer = () => {
8
+ return (
9
+ <footer
10
+ style={{
11
+ borderTop: '1px solid var(--border)',
12
+ background: 'var(--surface-warm)',
13
+ padding: '3rem 15vw'
14
+ }}
15
+ >
16
+ <div
17
+ style={{
18
+ maxWidth: '900px',
19
+ margin: '0 auto',
20
+ display: 'flex',
21
+ flexDirection: 'column',
22
+ gap: '1.25rem'
23
+ }}
24
+ >
25
+ <div>
26
+ <div
27
+ style={{
28
+ fontWeight: 600,
29
+ fontSize: '1.2rem',
30
+ letterSpacing: '-0.01em',
31
+ color: 'var(--ink)'
32
+ }}
33
+ >
34
+ AI Language Benchmarks
35
+ </div>
36
+ <div
37
+ style={{
38
+ color: 'var(--ink-muted)',
39
+ fontSize: '0.95rem',
40
+ marginTop: '0.25rem'
41
+ }}
42
+ >
43
+ AI model evaluations for every language in the world.
44
+ </div>
45
+ </div>
46
+
47
+ <p
48
+ style={{
49
+ color: 'var(--ink-muted)',
50
+ fontSize: '0.9rem',
51
+ lineHeight: 1.6,
52
+ margin: 0
53
+ }}
54
+ >
55
+ Pomerenke, D., Nothnagel, J., &amp; Ostermann, S. (2025).{' '}
56
+ <a
57
+ href='https://arxiv.org/abs/2507.08538'
58
+ target='_blank'
59
+ rel='noopener noreferrer'
60
+ style={{ ...linkStyle, fontStyle: 'italic' }}
61
+ >
62
+ The AI Language Proficiency Monitor – Tracking the Progress of LLMs
63
+ on Multilingual Benchmarks
64
+ </a>
65
+ . arXiv:2507.08538.
66
+ </p>
67
+
68
+ <p
69
+ style={{
70
+ color: 'var(--ink-muted)',
71
+ fontSize: '0.9rem',
72
+ lineHeight: 1.6,
73
+ margin: 0
74
+ }}
75
+ >
76
+ A collaboration of{' '}
77
+ <a
78
+ href='https://www.bmz-digital.global/en/overview-of-initiatives/the-bmz-data-lab/'
79
+ target='_blank'
80
+ rel='noopener noreferrer'
81
+ style={linkStyle}
82
+ >
83
+ BMZ Data Lab
84
+ </a>
85
+ ,{' '}
86
+ <a
87
+ href='https://www.bmz-digital.global/en/overview-of-initiatives/fair-forward/'
88
+ target='_blank'
89
+ rel='noopener noreferrer'
90
+ style={linkStyle}
91
+ >
92
+ GIZ Fair Forward
93
+ </a>
94
+ , and the{' '}
95
+ <a
96
+ href='https://www.dfki.de/en/web/research/research-departments/multilinguality-and-language-technology/ee-team'
97
+ target='_blank'
98
+ rel='noopener noreferrer'
99
+ style={linkStyle}
100
+ >
101
+ DFKI
102
+ </a>{' '}
103
+ Multilinguality and Language Technology Lab.
104
+ </p>
105
+
106
+ <div
107
+ style={{
108
+ display: 'flex',
109
+ flexWrap: 'wrap',
110
+ gap: '1.75rem',
111
+ fontSize: '0.9rem',
112
+ paddingTop: '1rem',
113
+ borderTop: '1px solid var(--border)'
114
+ }}
115
+ >
116
+ <a
117
+ href='https://github.com/datenlabor-bmz/evals-for-every-language'
118
+ target='_blank'
119
+ rel='noopener noreferrer'
120
+ style={linkStyle}
121
+ >
122
+ <i className='pi pi-github' style={{ marginRight: '0.4rem' }} />
123
+ GitHub
124
+ </a>
125
+ <a
126
+ href='https://huggingface.co/spaces/fair-forward/languagebench'
127
+ target='_blank'
128
+ rel='noopener noreferrer'
129
+ style={linkStyle}
130
+ >
131
+ Hugging Face Space
132
+ </a>
133
+ <a
134
+ href='https://arxiv.org/abs/2507.08538'
135
+ target='_blank'
136
+ rel='noopener noreferrer'
137
+ style={linkStyle}
138
+ >
139
+ Read the paper
140
+ </a>
141
+ </div>
142
+ </div>
143
+ </footer>
144
+ )
145
+ }
146
+
147
+ export default Footer
frontend/src/components/LanguageTable.js CHANGED
@@ -125,7 +125,7 @@ const LanguageTable = ({ data, selectedLanguages, setSelectedLanguages, totalMod
125
  dataKey='bcp_47'
126
  header={
127
  <span>
128
- <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>Languages</span>
129
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
130
  Average performance of {totalModels} evaluated AI models
131
  </span>
 
125
  dataKey='bcp_47'
126
  header={
127
  <span>
128
+ <span style={{ fontWeight: 600, fontSize: '1.15em', color: 'var(--ink)' }}>Languages</span>
129
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
130
  Average performance of {totalModels} evaluated AI models
131
  </span>
frontend/src/components/ModelTable.js CHANGED
@@ -194,7 +194,7 @@ const ModelTable = ({ data, selectedLanguages = [], allLanguages = [], machineTr
194
  if (selectedLanguages.length === 0) {
195
  return (
196
  <span>
197
- <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>AI Models</span>
198
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
199
  Performance across {evaluatedLanguagesCount} evaluated languages
200
  </span>
@@ -203,7 +203,7 @@ const ModelTable = ({ data, selectedLanguages = [], allLanguages = [], machineTr
203
  } else if (selectedLanguages.length === 1) {
204
  return (
205
  <span>
206
- <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>AI Models</span>
207
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
208
  {selectedLanguages[0].language_name} performance
209
  </span>
@@ -213,7 +213,7 @@ const ModelTable = ({ data, selectedLanguages = [], allLanguages = [], machineTr
213
  const languageNames = selectedLanguages.map(lang => lang.language_name).join(', ')
214
  return (
215
  <span>
216
- <span style={{ fontWeight: 'bold', fontSize: '1.1em' }}>AI Models</span>
217
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
218
  Performance for {languageNames}
219
  </span>
 
194
  if (selectedLanguages.length === 0) {
195
  return (
196
  <span>
197
+ <span style={{ fontWeight: 600, fontSize: '1.15em', color: 'var(--ink)' }}>AI Models</span>
198
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
199
  Performance across {evaluatedLanguagesCount} evaluated languages
200
  </span>
 
203
  } else if (selectedLanguages.length === 1) {
204
  return (
205
  <span>
206
+ <span style={{ fontWeight: 600, fontSize: '1.15em', color: 'var(--ink)' }}>AI Models</span>
207
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
208
  {selectedLanguages[0].language_name} performance
209
  </span>
 
213
  const languageNames = selectedLanguages.map(lang => lang.language_name).join(', ')
214
  return (
215
  <span>
216
+ <span style={{ fontWeight: 600, fontSize: '1.15em', color: 'var(--ink)' }}>AI Models</span>
217
  <span style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}>
218
  Performance for {languageNames}
219
  </span>
frontend/src/index.css CHANGED
@@ -1,21 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  body {
2
  margin: 0;
3
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5
- sans-serif;
6
  -webkit-font-smoothing: antialiased;
7
  -moz-osx-font-smoothing: grayscale;
8
  }
9
 
10
  code {
11
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12
- monospace;
13
  }
14
 
15
  html, body, #root {
16
  height: 100%;
17
  }
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  .p-component, .pi {
20
  font-size: 0.8rem;
21
  }
@@ -26,7 +167,7 @@ html, body, #root {
26
  }
27
 
28
  .p-datatable-wrapper {
29
- scrollbar-color: #f0f0f0 rgba(0, 0, 0, 0);
30
  border-radius: 0 0 6px 6px;
31
  }
32
 
@@ -35,92 +176,187 @@ html, body, #root {
35
  }
36
 
37
  .p-datatable .p-datatable-tbody > tr:hover {
38
- background-color: #fafafa !important;
39
  }
40
 
41
  .p-datatable .p-datatable-thead > tr > th {
42
- border-bottom: 1px solid #e0e0e0;
43
- background-color: #fafafa;
44
  font-weight: 500;
45
- color: #555;
46
  padding: 0.75rem 1rem;
47
  }
48
 
49
  .p-datatable .p-datatable-tbody > tr > td {
50
  padding: 0.75rem 1rem;
51
- border-bottom: 1px solid #f5f5f5;
52
  }
53
 
54
- #model-table {
55
- border: 2px solid #d1d5ff;
56
- border-radius: 8px;
57
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
 
 
 
58
  }
59
 
60
- #model-table .p-datatable-header {
61
- border-top-width: 0px;
 
 
 
 
 
 
62
  border-radius: 6px 6px 0 0;
63
- background: #f0f3ff;
64
- color: #333;
65
- border-bottom: 2px solid #d1d5ff;
66
  padding: 1rem 1.25rem;
67
  font-weight: 500;
68
  }
69
 
70
- #language-table {
71
- border: 2px solid #c8f2c8;
72
- border-radius: 8px;
73
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
74
  }
75
 
76
  #language-table .p-datatable-header {
77
- border-top-width: 0px;
78
- border-radius: 6px 6px 0 0;
79
- background: #f0fff0;
80
- color: #333;
81
- border-bottom: 2px solid #c8f2c8;
82
- padding: 1rem 1.25rem;
83
- font-weight: 500;
84
- }
85
-
86
- #dataset-table {
87
- border: 2px solid #ffe4cc;
88
- border-radius: 8px;
89
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
90
  }
91
 
92
  #dataset-table .p-datatable-header {
93
- border-top-width: 0px;
94
- border-radius: 6px 6px 0 0;
95
- background: #fff8f0;
96
- color: #333;
97
- border-bottom: 2px solid #ffe4cc;
98
- padding: 1rem 1.25rem;
99
- font-weight: 500;
100
  }
101
 
 
102
  #figure {
103
  width: 100%;
104
  min-width: 600px;
105
- height: 600px;
106
- border: 2px solid #e0e0e0;
107
- border-radius: 8px;
108
- padding: 1rem;
109
- background: #fafafa;
110
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
 
 
111
  }
112
 
 
113
  .p-autocomplete-input {
114
  width: max(100%, 40vw);
115
  margin-top: 2vh;
116
- border-radius: 0px;
117
- border: 1px solid rgba(0, 0, 0, 0.7);
118
- box-shadow: 3px 3px 1px 0px rgba(0, 0, 0, 0.2);;
119
  }
120
 
 
121
  .p-carousel-indicators button.p-link {
122
- border-radius: 5px;
123
- width: 10px;
124
- height: 10px;
125
- background-color: #000000;
126
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ============================================================
2
+ Design tokens
3
+ ============================================================ */
4
+ :root {
5
+ /* Surfaces */
6
+ --bg: #fbfbfd; /* soft cool off-white page background */
7
+ --surface: #ffffff; /* cards, tables */
8
+ --surface-warm: #f7f8fc; /* chart panel, subtle fills, row hover */
9
+
10
+ /* Ink */
11
+ --ink: #1b1d24; /* headings + primary text (slate-ink) */
12
+ --ink-muted: #5e626e; /* subtitles, secondary */
13
+ --ink-subtle: #9296a1; /* tertiary labels */
14
+
15
+ /* Accent — indigo, used sparingly */
16
+ --accent: #4350c4;
17
+ --accent-hover: #343fa0;
18
+ --accent-wash: rgba(67, 80, 196, 0.08);
19
+
20
+ /* Lines */
21
+ --border: #e5e6ec; /* structural borders, card edges */
22
+ --border-strong: #d4d6de;
23
+ --hairline: #eff0f4; /* faint inner row separators */
24
+
25
+ /* Warmed, ivory-friendly table accents (replace blue/green/orange) */
26
+ --tbl-model-border: #d7daec; --tbl-model-head: #eef0f8; /* soft periwinkle */
27
+ --tbl-lang-border: #cde3cd; --tbl-lang-head: #eff6ee; /* soft sage */
28
+ --tbl-data-border: #ecd9c5; --tbl-data-head: #faf2e9; /* soft sand */
29
+
30
+ /* Soft, warm-tinted shadows */
31
+ --shadow-sm: 0 1px 2px rgba(40, 33, 28, 0.05);
32
+ --shadow-md: 0 6px 20px rgba(40, 33, 28, 0.07);
33
+
34
+ /* Radius */
35
+ --radius: 12px;
36
+ --radius-sm: 8px;
37
+
38
+ /* Type */
39
+ --font-serif: ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif; /* clean system serif, headings */
40
+ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
41
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
42
+ --font-mono: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
43
+ }
44
+
45
+ /* ============================================================
46
+ Base
47
+ ============================================================ */
48
  body {
49
  margin: 0;
50
+ font-family: var(--font-sans);
51
+ background: var(--bg);
52
+ color: var(--ink);
53
  -webkit-font-smoothing: antialiased;
54
  -moz-osx-font-smoothing: grayscale;
55
  }
56
 
57
  code {
58
+ font-family: var(--font-mono);
 
59
  }
60
 
61
  html, body, #root {
62
  height: 100%;
63
  }
64
 
65
+ a {
66
+ color: var(--accent);
67
+ text-decoration: none;
68
+ transition: color 0.15s ease;
69
+ }
70
+
71
+ a:hover {
72
+ color: var(--accent-hover);
73
+ text-decoration: underline;
74
+ }
75
+
76
+ :focus-visible {
77
+ outline: 2px solid var(--accent);
78
+ outline-offset: 2px;
79
+ }
80
+
81
+ /* Serif display headline (hero) */
82
+ .hero-title {
83
+ font-family: var(--font-sans);
84
+ font-weight: 600;
85
+ font-size: clamp(2.25rem, 4vw, 2.75rem);
86
+ line-height: 1.12;
87
+ letter-spacing: -0.02em;
88
+ color: var(--ink);
89
+ margin: 1.25rem 0 0.75rem 0;
90
+ }
91
+
92
+ /* ============================================================
93
+ Buttons (replace inline gradients + JS hover handlers)
94
+ ============================================================ */
95
+ .btn {
96
+ display: inline-flex;
97
+ align-items: center;
98
+ gap: 0.5rem;
99
+ padding: 0.7rem 1.4rem;
100
+ border-radius: 10px;
101
+ font-family: var(--font-sans);
102
+ font-size: 0.95rem;
103
+ font-weight: 500;
104
+ line-height: 1;
105
+ cursor: pointer;
106
+ border: 1px solid transparent;
107
+ transition: background-color 0.15s ease, border-color 0.15s ease,
108
+ color 0.15s ease;
109
+ }
110
+
111
+ .btn-primary {
112
+ background: var(--accent);
113
+ color: #fff;
114
+ box-shadow: var(--shadow-sm);
115
+ }
116
+
117
+ .btn-primary:hover {
118
+ background: var(--accent-hover);
119
+ color: #fff;
120
+ }
121
+
122
+ .btn-secondary {
123
+ background: transparent;
124
+ color: var(--ink);
125
+ border-color: var(--border-strong);
126
+ }
127
+
128
+ .btn-secondary:hover {
129
+ background: var(--accent-wash);
130
+ border-color: var(--accent);
131
+ color: var(--accent-hover);
132
+ }
133
+
134
+ /* Quiet ghost link (top-right GitHub) */
135
+ .ghost-link {
136
+ display: inline-flex;
137
+ align-items: center;
138
+ gap: 0.5rem;
139
+ padding: 0.5rem 0.9rem;
140
+ border-radius: var(--radius-sm);
141
+ font-size: 0.9rem;
142
+ font-weight: 500;
143
+ color: var(--ink-muted);
144
+ background: var(--surface);
145
+ border: 1px solid var(--border);
146
+ transition: background-color 0.15s ease, color 0.15s ease,
147
+ border-color 0.15s ease;
148
+ }
149
+
150
+ .ghost-link:hover {
151
+ background: var(--accent-wash);
152
+ border-color: var(--accent);
153
+ color: var(--accent-hover);
154
+ text-decoration: none;
155
+ }
156
+
157
+ /* ============================================================
158
+ PrimeReact base tweaks
159
+ ============================================================ */
160
  .p-component, .pi {
161
  font-size: 0.8rem;
162
  }
 
167
  }
168
 
169
  .p-datatable-wrapper {
170
+ scrollbar-color: var(--border) transparent;
171
  border-radius: 0 0 6px 6px;
172
  }
173
 
 
176
  }
177
 
178
  .p-datatable .p-datatable-tbody > tr:hover {
179
+ background-color: var(--surface-warm) !important;
180
  }
181
 
182
  .p-datatable .p-datatable-thead > tr > th {
183
+ border-bottom: 1px solid var(--border);
184
+ background-color: var(--surface-warm);
185
  font-weight: 500;
186
+ color: var(--ink-muted);
187
  padding: 0.75rem 1rem;
188
  }
189
 
190
  .p-datatable .p-datatable-tbody > tr > td {
191
  padding: 0.75rem 1rem;
192
+ border-bottom: 1px solid var(--hairline);
193
  }
194
 
195
+ /* ============================================================
196
+ Table cards IDs keep specificity over the PrimeReact theme
197
+ ============================================================ */
198
+ #model-table, #language-table, #dataset-table {
199
+ border-radius: var(--radius-sm);
200
+ box-shadow: var(--shadow-sm);
201
+ background: var(--surface);
202
  }
203
 
204
+ #model-table { border: 1px solid var(--tbl-model-border); }
205
+ #language-table { border: 1px solid var(--tbl-lang-border); }
206
+ #dataset-table { border: 1px solid var(--tbl-data-border); }
207
+
208
+ #model-table .p-datatable-header,
209
+ #language-table .p-datatable-header,
210
+ #dataset-table .p-datatable-header {
211
+ border-top-width: 0;
212
  border-radius: 6px 6px 0 0;
213
+ color: var(--ink);
 
 
214
  padding: 1rem 1.25rem;
215
  font-weight: 500;
216
  }
217
 
218
+ #model-table .p-datatable-header {
219
+ background: var(--tbl-model-head);
220
+ border-bottom: 1px solid var(--tbl-model-border);
 
221
  }
222
 
223
  #language-table .p-datatable-header {
224
+ background: var(--tbl-lang-head);
225
+ border-bottom: 1px solid var(--tbl-lang-border);
 
 
 
 
 
 
 
 
 
 
 
226
  }
227
 
228
  #dataset-table .p-datatable-header {
229
+ background: var(--tbl-data-head);
230
+ border-bottom: 1px solid var(--tbl-data-border);
 
 
 
 
 
231
  }
232
 
233
+ /* Chart panel */
234
  #figure {
235
  width: 100%;
236
  min-width: 600px;
237
+ height: auto;
238
+ min-height: 700px;
239
+ border: 1px solid var(--border);
240
+ border-radius: var(--radius);
241
+ padding: 1rem 1rem 1.5rem;
242
+ background: var(--surface-warm);
243
+ box-shadow: var(--shadow-md);
244
+ font-family: var(--font-sans);
245
  }
246
 
247
+ /* Legacy PrimeReact autocomplete input — soften the dated hard shadow */
248
  .p-autocomplete-input {
249
  width: max(100%, 40vw);
250
  margin-top: 2vh;
251
+ border-radius: var(--radius-sm);
252
+ border: 1px solid var(--border);
253
+ box-shadow: var(--shadow-sm);
254
  }
255
 
256
+ /* Carousel indicators */
257
  .p-carousel-indicators button.p-link {
258
+ border-radius: 999px;
259
+ width: 8px;
260
+ height: 8px;
261
+ background-color: var(--border-strong);
262
+ }
263
+
264
+ .p-carousel-indicators .p-carousel-indicator.p-highlight button.p-link {
265
+ background-color: var(--accent);
266
+ }
267
+
268
+ /* ============================================================
269
+ PrimeReact accent harmonization (cyan -> clay).
270
+ index.css is imported AFTER theme.css (see index.js), so these
271
+ equal-specificity rules win without !important.
272
+ ============================================================ */
273
+ .p-checkbox .p-checkbox-box.p-highlight {
274
+ background: var(--accent);
275
+ border-color: var(--accent);
276
+ }
277
+
278
+ .p-checkbox .p-checkbox-box.p-highlight:hover {
279
+ background: var(--accent-hover);
280
+ border-color: var(--accent-hover);
281
+ }
282
+
283
+ .p-radiobutton .p-radiobutton-box.p-highlight {
284
+ background: var(--accent);
285
+ border-color: var(--accent);
286
+ }
287
+
288
+ .p-slider .p-slider-range {
289
+ background: var(--accent);
290
+ }
291
+
292
+ .p-slider .p-slider-handle {
293
+ border-color: var(--accent);
294
+ }
295
+
296
+ .p-slider .p-slider-handle:hover {
297
+ background: var(--accent);
298
+ }
299
+
300
+ .p-multiselect-panel .p-multiselect-items .p-multiselect-item.p-highlight,
301
+ .p-dropdown-panel .p-dropdown-items .p-dropdown-item.p-highlight {
302
+ background: var(--accent-wash);
303
+ color: var(--accent-hover);
304
+ }
305
+
306
+ .p-multiselect .p-multiselect-token {
307
+ background: var(--accent-wash);
308
+ color: var(--accent-hover);
309
+ }
310
+
311
+ .p-datatable .p-datatable-tbody > tr.p-highlight {
312
+ background: var(--accent-wash);
313
+ color: var(--ink);
314
+ }
315
+
316
+ .p-inputtext:enabled:focus {
317
+ border-color: var(--accent);
318
+ box-shadow: 0 0 0 2px var(--accent-wash);
319
+ }
320
+
321
+ /* ============================================================
322
+ Dialogs — cohesive with the hero (serif headings, clean chrome)
323
+ ============================================================ */
324
+ .p-dialog {
325
+ border-radius: var(--radius);
326
+ border: 1px solid var(--border);
327
+ box-shadow: var(--shadow-md);
328
+ overflow: hidden;
329
+ }
330
+
331
+ .p-dialog .p-dialog-header {
332
+ background: var(--surface);
333
+ border-bottom: 1px solid var(--border);
334
+ padding: 1.25rem 1.5rem;
335
+ }
336
+
337
+ .p-dialog .p-dialog-header .p-dialog-title {
338
+ font-family: var(--font-sans);
339
+ font-weight: 600;
340
+ font-size: 1.3rem;
341
+ letter-spacing: -0.01em;
342
+ color: var(--ink);
343
+ }
344
+
345
+ .p-dialog .p-dialog-content {
346
+ padding: 0 1.5rem 1.5rem;
347
+ color: var(--ink);
348
+ }
349
+
350
+ .p-dialog .p-dialog-content h4 {
351
+ font-family: var(--font-sans);
352
+ font-weight: 600;
353
+ font-size: 1.05rem;
354
+ color: var(--ink);
355
+ margin: 1.6rem 0 0.5rem;
356
+ }
357
+
358
+ .p-dialog .p-dialog-content p,
359
+ .p-dialog .p-dialog-content li {
360
+ color: var(--ink-muted);
361
+ line-height: 1.6;
362
+ }
frontend/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
  import React from 'react';
2
  import ReactDOM from 'react-dom/client';
 
3
  import './index.css';
4
  import App from './App';
5
  import reportWebVitals from './reportWebVitals';
 
1
  import React from 'react';
2
  import ReactDOM from 'react-dom/client';
3
+ import 'primereact/resources/themes/lara-light-cyan/theme.css';
4
  import './index.css';
5
  import App from './App';
6
  import reportWebVitals from './reportWebVitals';