MrNK2107 commited on
Commit
f204bbc
·
1 Parent(s): 28cfce8

feat(frontend): redesign UI with HH Goa neo-brutalist theme

Browse files
Files changed (1) hide show
  1. web/index.html +322 -208
web/index.html CHANGED
@@ -3,41 +3,42 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Voice RAG | HH Goa 2026</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;600;700;800&display=swap" rel="stylesheet">
10
  <style>
11
  :root {
12
- --bg: #0b0f19;
13
- --card-bg: rgba(21, 29, 46, 0.7);
14
- --card-border: rgba(255, 255, 255, 0.08);
15
- --primary: #6366f1;
16
- --primary-hover: #4f46e5;
17
- --accent: #10b981;
18
- --accent-warn: #f59e0b;
19
- --accent-danger: #ef4444;
20
- --text: #f3f4f6;
21
- --text-muted: #9ca3af;
22
  }
23
 
24
  * { box-sizing: border-box; margin: 0; padding: 0; }
25
 
26
  body {
27
- font-family: 'Inter', sans-serif;
28
- background: var(--bg);
29
  background-image:
30
- radial-gradient(circle at 15% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 40%),
31
- radial-gradient(circle at 85% 80%, rgba(16, 185, 129, 0.12) 0%, transparent 40%);
32
- color: var(--text);
33
  min-height: 100vh;
34
  display: flex;
35
  flex-direction: column;
36
  }
37
 
 
38
  header {
39
- padding: 2rem 2rem 1rem;
40
- max-width: 1200px;
41
  margin: 0 auto;
42
  width: 100%;
43
  display: flex;
@@ -45,359 +46,472 @@
45
  justify-content: space-between;
46
  }
47
 
48
- .logo-group {
49
  display: flex;
50
  align-items: center;
51
- gap: 12px;
52
  }
53
 
54
- .logo-badge {
55
- background: linear-gradient(135deg, #6366f1, #10b981);
56
- padding: 6px 14px;
57
- border-radius: 20px;
58
- font-family: 'Outfit', sans-serif;
59
- font-weight: 800;
60
- font-size: 0.85rem;
 
61
  text-transform: uppercase;
62
  letter-spacing: 1px;
 
63
  }
64
 
65
- h1 {
66
- font-family: 'Outfit', sans-serif;
67
- font-size: 1.8rem;
 
 
 
68
  font-weight: 700;
69
- background: linear-gradient(to right, #ffffff, #9ca3af);
70
- -webkit-background-clip: text;
71
- -webkit-text-fill-color: transparent;
 
 
 
 
 
72
  }
73
 
74
- .sub-tag {
75
- color: var(--text-muted);
76
- font-size: 0.9rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
 
 
 
 
 
 
 
 
 
 
 
79
  main {
80
- max-width: 1200px;
81
- margin: 0 auto 3rem;
82
  width: 100%;
83
  padding: 0 2rem;
84
  display: grid;
85
  grid-template-columns: 1fr 1fr;
86
- gap: 1.5rem;
87
  flex-grow: 1;
88
  }
89
 
90
- @media (max-width: 900px) {
91
  main { grid-template-columns: 1fr; }
92
  }
93
 
94
- .panel {
95
- background: var(--card-bg);
96
- border: 1px solid var(--card-border);
97
- backdrop-filter: blur(12px);
98
- border-radius: 16px;
99
- padding: 1.75rem;
100
  display: flex;
101
  flex-direction: column;
102
- gap: 1.25rem;
103
- box-shadow: 0 10px 30px rgba(0,0,0,0.3);
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  }
105
 
106
- .panel-title {
107
- font-family: 'Outfit', sans-serif;
108
- font-size: 1.2rem;
109
- font-weight: 600;
 
 
 
 
110
  display: flex;
111
  align-items: center;
112
- gap: 10px;
113
  padding-bottom: 0.75rem;
114
- border-bottom: 1px solid var(--card-border);
 
115
  }
116
 
117
- .controls-container {
 
118
  display: flex;
119
  flex-direction: column;
120
- gap: 1rem;
121
  }
122
 
123
- .btn-record {
124
- background: linear-gradient(135deg, #ef4444, #dc2626);
125
- color: white;
126
  border: none;
127
- padding: 1rem 1.5rem;
128
- border-radius: 12px;
129
- font-size: 1rem;
130
- font-weight: 600;
 
 
131
  cursor: pointer;
132
  display: flex;
133
  align-items: center;
134
  justify-content: center;
135
- gap: 10px;
136
- transition: all 0.2s ease;
137
- box-shadow: 0 4px 15px rgba(239, 68, 68, 0.3);
 
138
  }
139
 
140
- .btn-record:hover:not(:disabled) {
141
  transform: translateY(-2px);
142
- box-shadow: 0 6px 20px rgba(239, 68, 68, 0.4);
143
  }
144
 
145
- .btn-record.recording {
146
  background: linear-gradient(135deg, #f59e0b, #d97706);
147
  animation: pulse 1.5s infinite;
148
  }
149
 
150
  @keyframes pulse {
151
- 0% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.4); }
152
- 70% { box-shadow: 0 0 0 15px rgba(245, 158, 11, 0); }
153
  100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
154
  }
155
 
156
- .btn-submit {
157
- background: linear-gradient(135deg, var(--primary), var(--primary-hover));
158
- color: white;
159
- border: none;
160
- padding: 0.85rem 1.25rem;
161
- border-radius: 10px;
162
- font-size: 0.95rem;
163
- font-weight: 600;
164
- cursor: pointer;
165
- transition: all 0.2s;
166
- }
167
-
168
- .btn-submit:hover:not(:disabled) {
169
- transform: translateY(-1px);
170
- }
171
-
172
- .btn-submit:disabled, .btn-record:disabled {
173
- opacity: 0.5;
174
- cursor: not-allowed;
175
- }
176
-
177
- .text-input-group {
178
  display: flex;
179
- gap: 10px;
180
  }
181
 
182
  input[type="text"] {
183
  flex-grow: 1;
184
- background: rgba(0,0,0,0.3);
185
- border: 1px solid var(--card-border);
186
- border-radius: 10px;
187
- padding: 0.75rem 1rem;
188
- color: var(--text);
189
- font-family: inherit;
190
- font-size: 0.95rem;
191
  outline: none;
 
192
  }
193
 
194
  input[type="text"]:focus {
195
- border-color: var(--primary);
196
  }
197
 
198
- .status-badge {
199
- display: inline-flex;
200
- align-items: center;
201
- gap: 6px;
202
- padding: 4px 12px;
203
- border-radius: 12px;
204
- font-size: 0.8rem;
205
- font-weight: 600;
206
- width: fit-content;
 
 
 
 
207
  }
208
 
209
- .status-ready { background: rgba(156, 163, 175, 0.15); color: #d1d5db; }
210
- .status-recording { background: rgba(239, 68, 68, 0.2); color: #fca5a5; }
211
- .status-processing { background: rgba(99, 102, 241, 0.2); color: #a5b4fc; }
212
- .status-success { background: rgba(16, 185, 129, 0.2); color: #6ee7b7; }
213
- .status-abstained { background: rgba(245, 158, 11, 0.2); color: #fde68a; }
214
 
215
- .box-title {
216
- font-size: 0.8rem;
 
 
 
 
 
 
 
 
217
  text-transform: uppercase;
218
  letter-spacing: 1px;
219
- color: var(--text-muted);
220
  margin-bottom: 6px;
221
  }
222
 
223
- .content-box {
224
- background: rgba(0,0,0,0.25);
225
- border: 1px solid var(--card-border);
226
- border-radius: 10px;
227
- padding: 1rem;
228
- min-height: 80px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  font-size: 0.95rem;
230
- line-height: 1.5;
 
 
231
  }
232
 
233
- .citations-grid {
234
  display: flex;
235
  flex-direction: column;
236
  gap: 10px;
237
- max-height: 250px;
238
  overflow-y: auto;
239
  }
240
 
241
- .citation-card {
242
- background: rgba(255,255,255,0.03);
243
- border: 1px solid var(--card-border);
244
- border-radius: 8px;
245
- padding: 0.75rem;
246
- font-size: 0.85rem;
 
 
247
  }
248
 
249
- .citation-header {
250
  display: flex;
251
  justify-content: space-between;
252
- color: var(--text-muted);
253
  font-size: 0.75rem;
254
- margin-bottom: 4px;
 
 
 
 
255
  }
256
 
257
- .latency-grid {
 
258
  display: grid;
259
  grid-template-columns: repeat(3, 1fr);
260
- gap: 10px;
261
  }
262
 
263
- .metric-card {
264
- background: rgba(0,0,0,0.3);
265
- border: 1px solid var(--card-border);
266
  border-radius: 8px;
267
- padding: 0.75rem;
268
  text-align: center;
 
269
  }
270
 
271
- .metric-val {
272
- font-family: 'Outfit', sans-serif;
273
- font-size: 1.3rem;
 
274
  font-weight: 700;
275
- color: var(--accent);
 
 
 
 
 
 
 
276
  margin-top: 4px;
277
  }
278
 
279
- .metric-val.highlight {
280
- color: #818cf8;
281
  }
282
 
283
  .timing-table {
284
  width: 100%;
285
  border-collapse: collapse;
286
  font-size: 0.85rem;
287
- margin-top: 0.5rem;
 
 
 
 
 
288
  }
289
 
290
  .timing-table th, .timing-table td {
291
- padding: 6px 10px;
292
  text-align: left;
293
- border-bottom: 1px solid var(--card-border);
294
  }
295
 
296
  .timing-table th {
297
- color: var(--text-muted);
298
- font-weight: 500;
 
 
 
299
  }
300
 
301
  footer {
302
  text-align: center;
303
- padding: 1rem;
304
- color: var(--text-muted);
305
- font-size: 0.8rem;
306
- border-top: 1px solid var(--card-border);
 
 
307
  }
308
  </style>
309
  </head>
310
  <body>
311
 
312
  <header>
313
- <div class="logo-group">
314
- <div class="logo-badge">HH Goa 2026</div>
315
- <div>
316
- <h1>Voice-Enabled Grounded RAG</h1>
317
- <div class="sub-tag">MSMARCO-XI Multilingual Vector Pipeline</div>
 
 
 
 
 
 
 
318
  </div>
319
  </div>
320
  </header>
321
 
322
  <main>
323
- <!-- Left Column: Audio & Text Controls -->
324
- <div class="panel">
325
- <div class="panel-title">
326
- 🎤 Input Interface
 
 
 
327
  </div>
328
 
329
- <div class="controls-container">
330
- <button id="recordBtn" class="btn-record">
331
- <span id="recordIcon">🔴</span>
332
  <span id="recordText">Start Voice Recording</span>
333
  </button>
334
 
335
- <div class="text-input-group">
336
- <input type="text" id="textInput" placeholder="Or type a question for fast benchmarking..." />
337
- <button id="submitTextBtn" class="btn-submit">Ask Text</button>
338
  </div>
339
  </div>
340
 
341
  <div>
342
- <div class="box-title">System Status</div>
343
- <div id="statusBadge" class="status-badge status-ready">Ready</div>
344
  </div>
345
 
346
  <div>
347
- <div class="box-title">Speech Transcript</div>
348
- <div id="transcriptBox" class="content-box">Speech transcript will appear here after recording...</div>
349
  </div>
350
 
351
  <div>
352
- <div class="box-title">Audio Preview</div>
353
  <audio id="audioPreview" controls style="width: 100%; margin-top: 4px; display: none;"></audio>
354
  </div>
355
  </div>
356
 
357
- <!-- Right Column: Grounded Answer & Latency Analytics -->
358
- <div class="panel">
359
- <div class="panel-title">
360
- ⚡ RAG Response & Latency Breakdown
 
 
 
361
  </div>
362
 
363
  <div>
364
- <div class="box-title">Grounded Answer</div>
365
- <div id="answerBox" class="content-box">The system answer will be rendered here with citations...</div>
366
  </div>
367
 
368
  <div>
369
- <div class="box-title">Citations & Evidence Chunks</div>
370
- <div id="citationsBox" class="citations-grid">
371
- <div style="color: var(--text-muted); font-size: 0.85rem;">No citations loaded yet.</div>
372
  </div>
373
  </div>
374
 
375
  <div>
376
- <div class="box-title">Stage-wise Latency Metrics (ms)</div>
377
- <div class="latency-grid">
378
- <div class="metric-card">
379
- <div class="box-title">Post-STT RAG</div>
380
- <div id="metricPostStt" class="metric-val">0 ms</div>
381
  </div>
382
- <div class="metric-card">
383
- <div class="box-title">Dense Search</div>
384
- <div id="metricDense" class="metric-val highlight">0 ms</div>
385
  </div>
386
- <div class="metric-card">
387
- <div class="box-title">Total Latency</div>
388
- <div id="metricTotal" class="metric-val" style="color: #f43f5e;">0 ms</div>
389
  </div>
390
  </div>
391
 
392
  <table class="timing-table">
393
  <thead>
394
  <tr>
395
- <th>Pipeline Stage</th>
396
- <th>Execution Time</th>
397
  </tr>
398
  </thead>
399
  <tbody id="timingTableBody">
400
- <tr><td colspan="2" style="color: var(--text-muted);">No execution timings available</td></tr>
401
  </tbody>
402
  </table>
403
  </div>
@@ -405,7 +519,7 @@
405
  </main>
406
 
407
  <footer>
408
- Voice RAG Pipeline | HH Goa 2026 Shortlisting Task 2 | Optimized for Sub-200ms Post-STT RAG Latency
409
  </footer>
410
 
411
  <script>
@@ -431,7 +545,7 @@
431
 
432
  function setStatus(text, cls) {
433
  statusBadge.textContent = text;
434
- statusBadge.className = "status-badge " + cls;
435
  }
436
 
437
  recordBtn.onclick = async () => {
@@ -462,7 +576,7 @@
462
  mediaRecorder.stop();
463
  isRecording = false;
464
  recordBtn.classList.remove("recording");
465
- recordIcon.textContent = "🔴";
466
  recordText.textContent = "Start Voice Recording";
467
  setStatus("Processing STT & RAG...", "status-processing");
468
  }
@@ -514,8 +628,8 @@
514
  // Render Citations
515
  if (data.citations && data.citations.length > 0) {
516
  citationsBox.innerHTML = data.citations.map(c => `
517
- <div class="citation-card">
518
- <div class="citation-header">
519
  <span>Chunk ID: ${c.chunk_id.substring(0, 8)}... | Strategy: ${c.strategy || 'standard'}</span>
520
  <span>Score: ${c.score.toFixed(4)}</span>
521
  </div>
@@ -523,7 +637,7 @@
523
  </div>
524
  `).join("");
525
  } else {
526
- citationsBox.innerHTML = '<div style="color: var(--text-muted); font-size: 0.85rem;">No citations available.</div>';
527
  }
528
 
529
  // Render Metrics
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Voice RAG | Hacker House Goa 2026</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Imbue:opsz,wght@10..100,700;800;900&family=Inter:wght@400;500;600;700&family=Outfit:wght@400;600;700;800;900&family=Victor+Mono:wght@400;600;700&display=swap" rel="stylesheet">
10
  <style>
11
  :root {
12
+ --bg-green: #0b6839;
13
+ --brand-accent: #fee101; /* Electric Yellow */
14
+ --brand-pink: #ff0080; /* Hot Magenta Pink */
15
+ --brand-offwhite: #fffbe8; /* Card Cream */
16
+ --brand-dark: #073b20;
17
+ --card-shadow: 6px 8px 0px rgba(0, 0, 0, 0.35);
18
+ --card-shadow-sm: 4px 5px 0px rgba(0, 0, 0, 0.3);
19
+ --font-heading: 'Outfit', 'Imbue', sans-serif;
20
+ --font-mono: 'Victor Mono', monospace;
21
+ --font-body: 'Inter', sans-serif;
22
  }
23
 
24
  * { box-sizing: border-box; margin: 0; padding: 0; }
25
 
26
  body {
27
+ font-family: var(--font-body);
28
+ background-color: var(--bg-green);
29
  background-image:
30
+ radial-gradient(circle at 10% 15%, rgba(254, 225, 1, 0.08) 0%, transparent 35%),
31
+ radial-gradient(circle at 90% 85%, rgba(255, 0, 128, 0.08) 0%, transparent 35%);
32
+ color: #ffffff;
33
  min-height: 100vh;
34
  display: flex;
35
  flex-direction: column;
36
  }
37
 
38
+ /* Header */
39
  header {
40
+ padding: 1.75rem 2rem 1rem;
41
+ max-width: 1280px;
42
  margin: 0 auto;
43
  width: 100%;
44
  display: flex;
 
46
  justify-content: space-between;
47
  }
48
 
49
+ .brand-logo-group {
50
  display: flex;
51
  align-items: center;
52
+ gap: 14px;
53
  }
54
 
55
+ .studio-badge {
56
+ background: var(--brand-accent);
57
+ color: var(--brand-dark);
58
+ padding: 5px 12px;
59
+ border-radius: 6px;
60
+ font-family: var(--font-mono);
61
+ font-weight: 700;
62
+ font-size: 0.8rem;
63
  text-transform: uppercase;
64
  letter-spacing: 1px;
65
+ box-shadow: 3px 3px 0px rgba(0,0,0,0.3);
66
  }
67
 
68
+ .tag-pill {
69
+ background: var(--brand-pink);
70
+ color: #ffffff;
71
+ padding: 5px 14px;
72
+ border-radius: 20px;
73
+ font-family: var(--font-mono);
74
  font-weight: 700;
75
+ font-size: 0.75rem;
76
+ text-transform: uppercase;
77
+ letter-spacing: 1px;
78
+ box-shadow: 3px 3px 0px rgba(0,0,0,0.3);
79
+ }
80
+
81
+ .hero-title-container {
82
+ margin-top: 0.5rem;
83
  }
84
 
85
+ .hero-title {
86
+ font-family: var(--font-heading);
87
+ font-size: 2.2rem;
88
+ font-weight: 900;
89
+ letter-spacing: -0.5px;
90
+ color: var(--brand-accent);
91
+ text-transform: uppercase;
92
+ display: flex;
93
+ align-items: center;
94
+ gap: 10px;
95
+ }
96
+
97
+ .goa-stamp {
98
+ background: var(--brand-pink);
99
+ color: #ffffff;
100
+ padding: 2px 12px;
101
+ border-radius: 6px;
102
+ font-family: var(--font-body);
103
+ font-size: 1.1rem;
104
+ font-weight: 800;
105
+ transform: rotate(-6deg);
106
+ display: inline-block;
107
+ box-shadow: 3px 3px 0px rgba(0,0,0,0.3);
108
  }
109
 
110
+ .hero-subtitle {
111
+ font-family: var(--font-mono);
112
+ color: rgba(255, 255, 255, 0.85);
113
+ font-size: 0.85rem;
114
+ letter-spacing: 1.5px;
115
+ text-transform: uppercase;
116
+ margin-top: 2px;
117
+ }
118
+
119
+ /* Main Grid */
120
  main {
121
+ max-width: 1280px;
122
+ margin: 1.5rem auto 3rem;
123
  width: 100%;
124
  padding: 0 2rem;
125
  display: grid;
126
  grid-template-columns: 1fr 1fr;
127
+ gap: 2rem;
128
  flex-grow: 1;
129
  }
130
 
131
+ @media (max-width: 950px) {
132
  main { grid-template-columns: 1fr; }
133
  }
134
 
135
+ /* Neo-Brutalist Cards */
136
+ .hh-card {
137
+ background: var(--brand-offwhite);
138
+ color: var(--brand-dark);
139
+ border-radius: 12px;
140
+ padding: 2rem;
141
  display: flex;
142
  flex-direction: column;
143
+ gap: 1.4rem;
144
+ box-shadow: var(--card-shadow);
145
+ position: relative;
146
+ border: 1px solid rgba(0,0,0,0.1);
147
+ }
148
+
149
+ .pin-dot {
150
+ width: 14px;
151
+ height: 14px;
152
+ border-radius: 50%;
153
+ position: absolute;
154
+ top: -7px;
155
+ left: 50%;
156
+ transform: translateX(-50%);
157
+ box-shadow: 0px 2px 4px rgba(0,0,0,0.25);
158
  }
159
 
160
+ .pin-pink { background: var(--brand-pink); }
161
+ .pin-yellow { background: var(--brand-accent); }
162
+
163
+ .card-header-title {
164
+ font-family: var(--font-heading);
165
+ font-size: 1.5rem;
166
+ font-weight: 800;
167
+ color: var(--brand-dark);
168
  display: flex;
169
  align-items: center;
170
+ justify-content: space-between;
171
  padding-bottom: 0.75rem;
172
+ border-bottom: 2px solid rgba(11, 104, 57, 0.15);
173
+ text-transform: uppercase;
174
  }
175
 
176
+ /* Controls */
177
+ .controls-group {
178
  display: flex;
179
  flex-direction: column;
180
+ gap: 1.1rem;
181
  }
182
 
183
+ .btn-voice {
184
+ background: linear-gradient(135deg, var(--brand-pink), #d9006c);
185
+ color: #ffffff;
186
  border: none;
187
+ padding: 1.1rem 1.6rem;
188
+ border-radius: 10px;
189
+ font-family: var(--font-heading);
190
+ font-size: 1.15rem;
191
+ font-weight: 800;
192
+ letter-spacing: 0.5px;
193
  cursor: pointer;
194
  display: flex;
195
  align-items: center;
196
  justify-content: center;
197
+ gap: 12px;
198
+ transition: all 0.15s ease;
199
+ box-shadow: var(--card-shadow-sm);
200
+ text-transform: uppercase;
201
  }
202
 
203
+ .btn-voice:hover:not(:disabled) {
204
  transform: translateY(-2px);
205
+ box-shadow: 5px 7px 0px rgba(0, 0, 0, 0.35);
206
  }
207
 
208
+ .btn-voice.recording {
209
  background: linear-gradient(135deg, #f59e0b, #d97706);
210
  animation: pulse 1.5s infinite;
211
  }
212
 
213
  @keyframes pulse {
214
+ 0% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.5); }
215
+ 70% { box-shadow: 0 0 0 16px rgba(245, 158, 11, 0); }
216
  100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
217
  }
218
 
219
+ .text-input-row {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  display: flex;
221
+ gap: 12px;
222
  }
223
 
224
  input[type="text"] {
225
  flex-grow: 1;
226
+ background: #ffffff;
227
+ border: 2px solid rgba(11, 104, 57, 0.3);
228
+ border-radius: 8px;
229
+ padding: 0.85rem 1.1rem;
230
+ color: var(--brand-dark);
231
+ font-family: var(--font-mono);
232
+ font-size: 0.92rem;
233
  outline: none;
234
+ transition: border-color 0.2s;
235
  }
236
 
237
  input[type="text"]:focus {
238
+ border-color: var(--brand-pink);
239
  }
240
 
241
+ .btn-ask {
242
+ background: var(--brand-accent);
243
+ color: var(--brand-dark);
244
+ border: 2px solid var(--brand-dark);
245
+ padding: 0.85rem 1.4rem;
246
+ border-radius: 8px;
247
+ font-family: var(--font-heading);
248
+ font-size: 1rem;
249
+ font-weight: 800;
250
+ cursor: pointer;
251
+ box-shadow: 3px 4px 0px var(--brand-dark);
252
+ transition: all 0.15s;
253
+ text-transform: uppercase;
254
  }
255
 
256
+ .btn-ask:hover:not(:disabled) {
257
+ transform: translateY(-1px);
258
+ box-shadow: 4px 5px 0px var(--brand-dark);
259
+ }
 
260
 
261
+ .btn-ask:disabled, .btn-voice:disabled {
262
+ opacity: 0.5;
263
+ cursor: not-allowed;
264
+ }
265
+
266
+ /* Badges & Content */
267
+ .section-label {
268
+ font-family: var(--font-mono);
269
+ font-size: 0.75rem;
270
+ font-weight: 700;
271
  text-transform: uppercase;
272
  letter-spacing: 1px;
273
+ color: rgba(11, 104, 57, 0.75);
274
  margin-bottom: 6px;
275
  }
276
 
277
+ .status-capsule {
278
+ display: inline-flex;
279
+ align-items: center;
280
+ gap: 8px;
281
+ padding: 6px 14px;
282
+ border-radius: 20px;
283
+ font-family: var(--font-mono);
284
+ font-size: 0.82rem;
285
+ font-weight: 700;
286
+ width: fit-content;
287
+ box-shadow: 2px 2px 0px rgba(0,0,0,0.15);
288
+ }
289
+
290
+ .status-ready { background: #e2e8f0; color: #475569; }
291
+ .status-recording { background: #ffe4e6; color: var(--brand-pink); border: 1px solid var(--brand-pink); }
292
+ .status-processing { background: #fef9c3; color: #854d0e; border: 1px solid #ca8a04; }
293
+ .status-success { background: #dcfce7; color: #15803d; border: 1px solid #16a34a; }
294
+ .status-abstained { background: #ffedd5; color: #c2410c; border: 1px solid #ea580c; }
295
+
296
+ .content-card-box {
297
+ background: #ffffff;
298
+ border: 1px solid rgba(11, 104, 57, 0.2);
299
+ border-radius: 8px;
300
+ padding: 1.1rem;
301
+ min-height: 85px;
302
  font-size: 0.95rem;
303
+ line-height: 1.55;
304
+ color: #1e293b;
305
+ box-shadow: inset 0 1px 3px rgba(0,0,0,0.05);
306
  }
307
 
308
+ .citations-container {
309
  display: flex;
310
  flex-direction: column;
311
  gap: 10px;
312
+ max-height: 230px;
313
  overflow-y: auto;
314
  }
315
 
316
+ .citation-tile {
317
+ background: #ffffff;
318
+ border: 1px solid rgba(11, 104, 57, 0.2);
319
+ border-left: 4px solid var(--brand-pink);
320
+ border-radius: 6px;
321
+ padding: 0.85rem;
322
+ font-size: 0.86rem;
323
+ line-height: 1.45;
324
  }
325
 
326
+ .citation-meta {
327
  display: flex;
328
  justify-content: space-between;
329
+ font-family: var(--font-mono);
330
  font-size: 0.75rem;
331
+ color: var(--brand-dark);
332
+ font-weight: 700;
333
+ margin-bottom: 6px;
334
+ padding-bottom: 4px;
335
+ border-bottom: 1px dashed rgba(11, 104, 57, 0.2);
336
  }
337
 
338
+ /* Latency Cards */
339
+ .metrics-row {
340
  display: grid;
341
  grid-template-columns: repeat(3, 1fr);
342
+ gap: 12px;
343
  }
344
 
345
+ .metric-pill-card {
346
+ background: #ffffff;
347
+ border: 2px solid var(--brand-dark);
348
  border-radius: 8px;
349
+ padding: 0.85rem 0.6rem;
350
  text-align: center;
351
+ box-shadow: 3px 3px 0px var(--brand-dark);
352
  }
353
 
354
+ .metric-title {
355
+ font-family: var(--font-mono);
356
+ font-size: 0.7rem;
357
+ text-transform: uppercase;
358
  font-weight: 700;
359
+ color: rgba(11, 104, 57, 0.8);
360
+ }
361
+
362
+ .metric-number {
363
+ font-family: var(--font-heading);
364
+ font-size: 1.45rem;
365
+ font-weight: 900;
366
+ color: var(--brand-pink);
367
  margin-top: 4px;
368
  }
369
 
370
+ .metric-number.accent {
371
+ color: #0284c7;
372
  }
373
 
374
  .timing-table {
375
  width: 100%;
376
  border-collapse: collapse;
377
  font-size: 0.85rem;
378
+ font-family: var(--font-mono);
379
+ margin-top: 0.6rem;
380
+ background: #ffffff;
381
+ border-radius: 8px;
382
+ overflow: hidden;
383
+ border: 1px solid rgba(11, 104, 57, 0.2);
384
  }
385
 
386
  .timing-table th, .timing-table td {
387
+ padding: 8px 12px;
388
  text-align: left;
389
+ border-bottom: 1px solid rgba(11, 104, 57, 0.1);
390
  }
391
 
392
  .timing-table th {
393
+ background: rgba(11, 104, 57, 0.08);
394
+ color: var(--brand-dark);
395
+ font-weight: 700;
396
+ text-transform: uppercase;
397
+ font-size: 0.75rem;
398
  }
399
 
400
  footer {
401
  text-align: center;
402
+ padding: 1.5rem;
403
+ color: rgba(255, 255, 255, 0.85);
404
+ font-family: var(--font-mono);
405
+ font-size: 0.82rem;
406
+ border-top: 1px solid rgba(255, 255, 255, 0.15);
407
+ background: rgba(0, 0, 0, 0.15);
408
  }
409
  </style>
410
  </head>
411
  <body>
412
 
413
  <header>
414
+ <div>
415
+ <div class="brand-logo-group">
416
+ <div class="studio-badge">2:47 PM STUDIO</div>
417
+ <div class="tag-pill">#RAGInGoa</div>
418
+ </div>
419
+ <div class="hero-title-container">
420
+ <div class="hero-title">
421
+ Hacker House <span class="goa-stamp">गोवा</span> Voice RAG
422
+ </div>
423
+ <div class="hero-subtitle">
424
+ GOA, INDIA · 28 - 31 OCT 2026 · SUB-200MS VOICE PIPELINE
425
+ </div>
426
  </div>
427
  </div>
428
  </header>
429
 
430
  <main>
431
+ <!-- Left Card: Voice & Text Input -->
432
+ <div class="hh-card">
433
+ <div class="pin-dot pin-pink"></div>
434
+
435
+ <div class="card-header-title">
436
+ <span>🎙️ Input Interface</span>
437
+ <span style="font-size: 0.8rem; font-weight: 700; color: var(--brand-pink);">SARVAM STT</span>
438
  </div>
439
 
440
+ <div class="controls-group">
441
+ <button id="recordBtn" class="btn-voice">
442
+ <span id="recordIcon">🎙️</span>
443
  <span id="recordText">Start Voice Recording</span>
444
  </button>
445
 
446
+ <div class="text-input-row">
447
+ <input type="text" id="textInput" placeholder="Or type a query for instant benchmarking..." />
448
+ <button id="submitTextBtn" class="btn-ask">Ask Text</button>
449
  </div>
450
  </div>
451
 
452
  <div>
453
+ <div class="section-label">System Status</div>
454
+ <div id="statusBadge" class="status-capsule status-ready">System Ready</div>
455
  </div>
456
 
457
  <div>
458
+ <div class="section-label">Speech Transcript</div>
459
+ <div id="transcriptBox" class="content-card-box">Spoken speech transcript will appear here after recording...</div>
460
  </div>
461
 
462
  <div>
463
+ <div class="section-label">Audio Stream Preview</div>
464
  <audio id="audioPreview" controls style="width: 100%; margin-top: 4px; display: none;"></audio>
465
  </div>
466
  </div>
467
 
468
+ <!-- Right Card: Grounded Answer & Latency Analytics -->
469
+ <div class="hh-card">
470
+ <div class="pin-dot pin-yellow"></div>
471
+
472
+ <div class="card-header-title">
473
+ <span>⚡ Grounded RAG & Analytics</span>
474
+ <span style="font-size: 0.8rem; font-weight: 700; color: var(--bg-green);">HYBRID DENSE+LEXICAL</span>
475
  </div>
476
 
477
  <div>
478
+ <div class="section-label">Grounded Answer Output</div>
479
+ <div id="answerBox" class="content-card-box">Extracted grounded answer will render here...</div>
480
  </div>
481
 
482
  <div>
483
+ <div class="section-label">Evidence Chunks & Citations</div>
484
+ <div id="citationsBox" class="citations-container">
485
+ <div style="color: rgba(11, 104, 57, 0.6); font-size: 0.85rem; font-family: var(--font-mono);">No citations loaded yet.</div>
486
  </div>
487
  </div>
488
 
489
  <div>
490
+ <div class="section-label">Pipeline Execution Latency (ms)</div>
491
+ <div class="metrics-row">
492
+ <div class="metric-pill-card">
493
+ <div class="metric-title">POST-STT RAG</div>
494
+ <div id="metricPostStt" class="metric-number">0 ms</div>
495
  </div>
496
+ <div class="metric-pill-card">
497
+ <div class="metric-title">DENSE SEARCH</div>
498
+ <div id="metricDense" class="metric-number accent">0 ms</div>
499
  </div>
500
+ <div class="metric-pill-card">
501
+ <div class="metric-title">TOTAL TIME</div>
502
+ <div id="metricTotal" class="metric-number" style="color: var(--bg-green);">0 ms</div>
503
  </div>
504
  </div>
505
 
506
  <table class="timing-table">
507
  <thead>
508
  <tr>
509
+ <th>Pipeline Sub-Stage</th>
510
+ <th>Latency (ms)</th>
511
  </tr>
512
  </thead>
513
  <tbody id="timingTableBody">
514
+ <tr><td colspan="2" style="color: rgba(11, 104, 57, 0.6);">No execution timing data available</td></tr>
515
  </tbody>
516
  </table>
517
  </div>
 
519
  </main>
520
 
521
  <footer>
522
+ Hacker House Goa 2026 | Voice-Enabled RAG System (Task #2) | Built for sub-200ms post-STT latency | #RAGInGoa
523
  </footer>
524
 
525
  <script>
 
545
 
546
  function setStatus(text, cls) {
547
  statusBadge.textContent = text;
548
+ statusBadge.className = "status-capsule " + cls;
549
  }
550
 
551
  recordBtn.onclick = async () => {
 
576
  mediaRecorder.stop();
577
  isRecording = false;
578
  recordBtn.classList.remove("recording");
579
+ recordIcon.textContent = "🎙️";
580
  recordText.textContent = "Start Voice Recording";
581
  setStatus("Processing STT & RAG...", "status-processing");
582
  }
 
628
  // Render Citations
629
  if (data.citations && data.citations.length > 0) {
630
  citationsBox.innerHTML = data.citations.map(c => `
631
+ <div class="citation-tile">
632
+ <div class="citation-meta">
633
  <span>Chunk ID: ${c.chunk_id.substring(0, 8)}... | Strategy: ${c.strategy || 'standard'}</span>
634
  <span>Score: ${c.score.toFixed(4)}</span>
635
  </div>
 
637
  </div>
638
  `).join("");
639
  } else {
640
+ citationsBox.innerHTML = '<div style="color: rgba(11, 104, 57, 0.6); font-size: 0.85rem; font-family: var(--font-mono);">No citations available.</div>';
641
  }
642
 
643
  // Render Metrics