Manyue-DataScientist commited on
Commit
8c97ac8
·
1 Parent(s): e4d89ca

Update app.py

Browse files

To better version in terms of UI and also more scalable and maintainable for future changes.

Files changed (1) hide show
  1. app.py +909 -528
app.py CHANGED
@@ -1,14 +1,39 @@
1
  import gradio as gr
2
  import base64
3
  import os
 
4
 
5
  # --- Helper Functions ---
 
 
 
 
 
 
 
 
 
6
  def file_to_data_uri(filepath, mime_type="application/pdf"):
7
- with open(filepath, "rb") as f:
8
- data = f.read()
9
- b64 = base64.b64encode(data).decode("utf-8")
10
- return f"data:{mime_type};base64,{b64}"
 
 
 
 
 
11
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  # --- Navigation Functions ---
14
  def show_data_analytics():
@@ -23,6 +48,12 @@ def show_computer_vision():
23
  def go_home():
24
  return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
25
 
 
 
 
 
 
 
26
  # --- Icons (SVG) ---
27
  data_analytics_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path><path d="M8 10h.01"></path><path d="M12 10h.01"></path><path d="M16 10h.01"></path></svg>"""
28
  machine_learning_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>"""
@@ -34,17 +65,221 @@ mail_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke
34
  link_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>"""
35
  document_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><line x1="10" y1="9" x2="8" y2="9"></line></svg>"""
36
 
37
- # --- Function to encode image ---
38
- def image_to_data_uri(filepath, mime_type="image/jpeg"):
39
- with open(filepath, "rb") as f:
40
- data = f.read()
41
- b64 = base64.b64encode(data).decode("utf-8")
42
- return f"data:{mime_type};base64,{b64}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  # --- CSS ---
45
  portfolio_css = """
46
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Montserrat:wght@700;800&display=swap');
47
-
48
  :root {
49
  --primary-da: #8a2be2;
50
  --secondary-da: #2575fc;
@@ -67,7 +302,6 @@ portfolio_css = """
67
  --transition-med: 0.3s ease;
68
  --transition-slow: 0.5s ease;
69
  }
70
-
71
  body {
72
  font-family: 'Poppins', sans-serif;
73
  background: var(--dark-bg);
@@ -78,34 +312,31 @@ body {
78
  margin: 0;
79
  padding: 0;
80
  overflow-x: hidden;
 
 
 
81
  }
82
-
83
  .gr-container {
84
  max-width: 1200px;
85
  margin: 0 auto;
86
  padding: 20px;
87
  }
88
-
89
  /* Scrollbar styling */
90
  ::-webkit-scrollbar {
91
  width: 8px;
92
  height: 8px;
93
  }
94
-
95
  ::-webkit-scrollbar-track {
96
  background: rgba(255, 255, 255, 0.05);
97
  border-radius: 4px;
98
  }
99
-
100
  ::-webkit-scrollbar-thumb {
101
  background: rgba(255, 255, 255, 0.2);
102
  border-radius: 4px;
103
  }
104
-
105
  ::-webkit-scrollbar-thumb:hover {
106
  background: rgba(255, 255, 255, 0.3);
107
  }
108
-
109
  /* Landing section */
110
  .landing-section {
111
  text-align: center;
@@ -113,7 +344,6 @@ body {
113
  padding: 40px 20px;
114
  position: relative;
115
  }
116
-
117
  .landing-section:before {
118
  content: '';
119
  position: absolute;
@@ -124,12 +354,10 @@ body {
124
  background: linear-gradient(180deg, rgba(0,0,0,0.7) 0%, transparent 100%);
125
  z-index: -1;
126
  }
127
-
128
  .landing-section h1, .landing-section h2 {
129
  color: var(--text-primary) !important;
130
  margin-top: 0;
131
  }
132
-
133
  .landing-section h1 {
134
  font-family: 'Montserrat', sans-serif;
135
  font-size: 3.2rem;
@@ -141,13 +369,11 @@ body {
141
  color: transparent !important;
142
  letter-spacing: -0.5px;
143
  }
144
-
145
  .landing-section h2 {
146
  font-size: 2rem;
147
  font-weight: 600;
148
  margin-bottom: 1.5rem;
149
  }
150
-
151
  .profile-container {
152
  margin: 30px auto;
153
  display: flex;
@@ -155,7 +381,6 @@ body {
155
  justify-content: center;
156
  flex-direction: column;
157
  }
158
-
159
  .profile-pic {
160
  width: 180px;
161
  height: 180px;
@@ -168,33 +393,28 @@ body {
168
  background: linear-gradient(45deg, var(--primary-da), var(--primary-ml), var(--primary-cv));
169
  padding: 4px;
170
  }
171
-
172
  .profile-pic img {
173
  border-radius: 50%;
174
  width: 100%;
175
  height: 100%;
176
  object-fit: cover;
177
  }
178
-
179
  .name-text {
180
  font-size: 2.6rem;
181
  font-weight: 700;
182
  margin-top: 10px;
183
  margin-bottom: 10px;
184
  }
185
-
186
  @keyframes float {
187
  0% { transform: translateY(0px) }
188
  50% { transform: translateY(-10px) }
189
  100% { transform: translateY(0px) }
190
  }
191
-
192
  @keyframes pulse {
193
  0% { transform: scale(1); }
194
  50% { transform: scale(1.05); }
195
  100% { transform: scale(1); }
196
  }
197
-
198
  .about-text {
199
  max-width: 800px;
200
  margin: 0 auto 40px;
@@ -202,7 +422,6 @@ body {
202
  line-height: 1.6;
203
  color: var(--text-secondary);
204
  }
205
-
206
  .skills-container {
207
  margin-top: 20px;
208
  display: flex;
@@ -211,7 +430,6 @@ body {
211
  gap: 10px;
212
  margin-bottom: 40px;
213
  }
214
-
215
  .skill-pill {
216
  background: rgba(255, 255, 255, 0.1);
217
  padding: 8px 16px;
@@ -220,14 +438,12 @@ body {
220
  font-weight: 500;
221
  color: var(--text-secondary);
222
  }
223
-
224
  .social-links {
225
  display: flex;
226
  justify-content: center;
227
  gap: 20px;
228
  margin: 30px 0;
229
  }
230
-
231
  .social-button {
232
  background: rgba(255, 255, 255, 0.1);
233
  border: none;
@@ -242,22 +458,18 @@ body {
242
  font-size: 1.2rem;
243
  box-shadow: var(--shadow-sm);
244
  }
245
-
246
  .social-button:hover {
247
  transform: translateY(-5px);
248
  background: rgba(255, 255, 255, 0.2);
249
  box-shadow: var(--shadow-md);
250
  }
251
-
252
  .social-linkedin:hover { background: #0077b5; }
253
  .social-github:hover { background: #333; }
254
  .social-email:hover { background: #ea4335; }
255
-
256
  .social-button svg {
257
  width: 24px;
258
  height: 24px;
259
  }
260
-
261
  /* Card styling */
262
  .cards-grid {
263
  display: grid;
@@ -265,13 +477,12 @@ body {
265
  gap: 30px;
266
  margin: 40px 0;
267
  }
268
-
269
  .card-container {
270
  position: relative; /* Important for button positioning */
271
  margin-bottom: 20px;
272
  height: 100%;
 
273
  }
274
-
275
  .card-container.da:before {
276
  content: '';
277
  position: absolute;
@@ -283,7 +494,6 @@ body {
283
  z-index: 5;
284
  border-radius: var(--border-radius-md) var(--border-radius-md) 0 0;
285
  }
286
-
287
  .card-container.ml:before {
288
  content: '';
289
  position: absolute;
@@ -296,7 +506,6 @@ body {
296
  transition: all var(--transition-med);
297
  border-radius: var(--border-radius-md) var(--border-radius-md) 0 0;
298
  }
299
-
300
  .card-container.cv:before {
301
  content: '';
302
  position: absolute;
@@ -308,7 +517,6 @@ body {
308
  z-index: 5;
309
  border-radius: var(--border-radius-md) var(--border-radius-md) 0 0;
310
  }
311
-
312
  .card-content {
313
  padding: 30px;
314
  min-height: 200px;
@@ -322,7 +530,6 @@ body {
322
  z-index: 2;
323
  transition: all var(--transition-med);
324
  }
325
-
326
  .card-content svg {
327
  width: 60px;
328
  height: 60px;
@@ -330,7 +537,6 @@ body {
330
  opacity: 0.9;
331
  transition: all var(--transition-med);
332
  }
333
-
334
  .card-inner {
335
  transition: transform var(--transition-med), box-shadow var(--transition-med), background-color var(--transition-med);
336
  text-align: center;
@@ -342,23 +548,19 @@ body {
342
  cursor: pointer; /* Indicates the card is clickable */
343
  position: relative; /* Ensure child elements are positioned relative to the card */
344
  }
345
-
346
  .card-inner:hover {
347
  transform: translateY(-10px) scale(1.05); /* Adds a slight zoom effect */
348
  box-shadow: var(--shadow-lg); /* Increases shadow for emphasis */
349
  background: rgba(255, 255, 255, 0.1); /* Subtle background change */
350
  border: 2px solid var(--primary-da); /* Optional: Add a border to emphasize hover */
351
  }
352
-
353
  .card-inner:hover .card-content svg {
354
  transform: scale(1.1); /* Slightly enlarges the icon */
355
  opacity: 1;
356
  }
357
-
358
  .card-inner:hover .card-description {
359
  color: var(--text-primary); /* Optional: changes text color for emphasis */
360
  }
361
-
362
  /* Add a subtle glow effect */
363
  .card-inner:hover:before {
364
  content: '';
@@ -371,14 +573,12 @@ body {
371
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.3);
372
  z-index: -1;
373
  }
374
-
375
  .card-description {
376
  padding: 0 20px 20px;
377
  color: var(--text-secondary);
378
  font-size: 1.1rem;
379
  line-height: 1.5;
380
  }
381
-
382
  /* Card button styling - crucial for making cards clickable */
383
  .card-button {
384
  position: absolute !important;
@@ -397,12 +597,228 @@ body {
397
  background: none !important;
398
  }
399
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  /* Section styling */
401
  .section-container {
402
- padding: 40px 20px;
403
  position: relative;
404
  }
405
-
406
  .section-container:before {
407
  content: '';
408
  position: absolute;
@@ -413,25 +829,21 @@ body {
413
  background: radial-gradient(ellipse at top, rgba(255,255,255,0.05) 0%, transparent 70%);
414
  z-index: 0;
415
  }
416
-
417
  .da-section h1.section-heading {
418
  color: var(--primary-da);
419
  position: relative;
420
  display: inline-block;
421
  }
422
-
423
  .ml-section h1.section-heading {
424
  color: var(--primary-ml);
425
  position: relative;
426
  display: inline-block;
427
  }
428
-
429
  .cv-section h1.section-heading {
430
  color: var(--primary-cv);
431
  position: relative;
432
  display: inline-block;
433
  }
434
-
435
  .section-heading:after {
436
  content: '';
437
  position: absolute;
@@ -441,16 +853,13 @@ body {
441
  height: 3px;
442
  border-radius: 3px;
443
  }
444
-
445
  .da-section .section-heading:after { background: var(--primary-da); }
446
  .ml-section .section-heading:after { background: var(--primary-ml); }
447
  .cv-section .section-heading:after { background: var(--primary-cv); }
448
-
449
  /* Subheadings color-coded */
450
  .section-subheading.da { color: var(--primary-da); }
451
  .section-subheading.ml { color: var(--primary-ml); }
452
  .section-subheading.cv { color: var(--primary-cv); }
453
-
454
  /* Back buttons */
455
  .back-button {
456
  border: none;
@@ -465,32 +874,26 @@ body {
465
  align-items: center;
466
  gap: 8px;
467
  }
468
-
469
  .back-button:hover {
470
  transform: translateY(-3px);
471
  box-shadow: var(--shadow-md);
472
  }
473
-
474
  .back-button-da {
475
  background: linear-gradient(45deg, var(--primary-da), var(--secondary-da));
476
  color: #fff;
477
  }
478
-
479
  .back-button-ml {
480
  background: linear-gradient(45deg, var(--primary-ml), var(--secondary-ml));
481
  color: #fff;
482
  }
483
-
484
  .back-button-cv {
485
  background: linear-gradient(45deg, var(--primary-cv), var(--secondary-cv));
486
  color: #fff;
487
  }
488
-
489
  .back-button svg {
490
  width: 20px;
491
  height: 20px;
492
  }
493
-
494
  /* Contact form */
495
  .contact-container {
496
  background: var(--card-bg);
@@ -500,7 +903,6 @@ body {
500
  margin: 0 auto;
501
  box-shadow: var(--shadow-md);
502
  }
503
-
504
  .hire-me-button {
505
  background: linear-gradient(45deg, var(--primary-da), var(--primary-cv));
506
  color: white;
@@ -511,18 +913,15 @@ body {
511
  font-weight: 600;
512
  cursor: pointer;
513
  transition: all var(--transition-med);
514
- margin-top: 20px;
515
  box-shadow: var(--shadow-sm);
516
  display: inline-block;
517
  text-decoration: none;
518
  }
519
-
520
  .hire-me-button:hover {
521
  transform: translateY(-3px);
522
  box-shadow: var(--shadow-md);
523
  filter: brightness(1.1);
524
  }
525
-
526
  /* Project cards */
527
  .project-card {
528
  background: var(--card-bg);
@@ -533,16 +932,13 @@ body {
533
  transition: all var(--transition-med);
534
  border-left: 4px solid transparent;
535
  }
536
-
537
  .da-section .project-card { border-left-color: var(--primary-da); }
538
  .ml-section .project-card { border-left-color: var(--primary-ml); }
539
  .cv-section .project-card { border-left-color: var(--primary-cv); }
540
-
541
  .project-card:hover {
542
  transform: translateX(5px);
543
  box-shadow: var(--shadow-md);
544
  }
545
-
546
  .project-title {
547
  font-size: 1.3rem;
548
  font-weight: 600;
@@ -551,11 +947,9 @@ body {
551
  align-items: center;
552
  justify-content: space-between;
553
  }
554
-
555
  .project-title-text {
556
  flex: 1;
557
  }
558
-
559
  .project-link {
560
  color: var(--text-secondary);
561
  transition: all var(--transition-med);
@@ -564,33 +958,26 @@ body {
564
  align-items: center;
565
  margin-left: 10px;
566
  }
567
-
568
  .project-link svg {
569
  width: 16px;
570
  height: 16px;
571
- margin-right: 5px;
572
  }
573
-
574
  .da-section .project-title-text { color: var(--primary-da); }
575
  .ml-section .project-title-text { color: var(--primary-ml); }
576
  .cv-section .project-title-text { color: var(--primary-cv); }
577
-
578
  .da-section .project-link:hover { color: var(--primary-da); }
579
  .ml-section .project-link:hover { color: var(--primary-ml); }
580
  .cv-section .project-link:hover { color: var(--primary-cv); }
581
-
582
  .project-description {
583
  color: var(--text-secondary);
584
  line-height: 1.5;
585
  }
586
-
587
  .tech-stack {
588
  display: block;
589
  margin-top: 10px;
590
  font-style: italic;
591
  color: var(--text-muted);
592
  }
593
-
594
  /* Skills list */
595
  .skills-list {
596
  display: grid;
@@ -599,39 +986,32 @@ body {
599
  margin-top: 20px;
600
  margin-bottom: 40px; /* Added margin to create space between skills and projects */
601
  }
602
-
603
  .skill-category {
604
  background: rgba(255, 255, 255, 0.05);
605
  border-radius: var(--border-radius-sm);
606
  padding: 15px;
607
  transition: all var(--transition-med);
608
  }
609
-
610
  .skill-category:hover {
611
  background: rgba(255, 255, 255, 0.08);
612
  transform: translateY(-3px);
613
  }
614
-
615
  .skill-category h4 {
616
  margin-top: 0;
617
  margin-bottom: 10px;
618
  font-size: 1.1rem;
619
  }
620
-
621
  .da-section .skill-category h4 { color: var(--primary-da); }
622
  .ml-section .skill-category h4 { color: var(--primary-ml); }
623
  .cv-section .skill-category h4 { color: var(--primary-cv); }
624
-
625
  .skill-category ul {
626
  margin: 0;
627
  padding-left: 20px;
628
  color: var(--text-secondary);
629
  }
630
-
631
  .skill-category li {
632
  margin-bottom: 5px;
633
  }
634
-
635
  /* Section intro text */
636
  .section-intro {
637
  max-width: 800px;
@@ -640,7 +1020,6 @@ body {
640
  color: var(--text-secondary);
641
  font-size: 1.1rem;
642
  }
643
-
644
  /* Footer */
645
  .footer {
646
  text-align: center;
@@ -649,7 +1028,6 @@ body {
649
  color: var(--text-muted);
650
  font-size: 0.9rem;
651
  }
652
-
653
  /* Animations for scroll */
654
  .animate-on-scroll {
655
  opacity: 0;
@@ -658,521 +1036,524 @@ body {
658
  }
659
 
660
  .animate-on-scroll.show {
661
- opacity: 1;
662
- transform: translateY(0);
663
  }
664
 
665
- /* Responsive design */
666
- @media (max-width: 768px) {
667
- .landing-section h1 {
668
- font-size: 2.5rem;
669
- }
670
-
671
- .landing-section h2 {
672
- font-size: 1.5rem;
673
- }
674
-
675
- .about-text {
676
- font-size: 1.1rem;
677
- }
678
-
679
- .cards-grid {
680
- grid-template-columns: 1fr;
681
- }
682
-
683
- .skills-list {
684
- grid-template-columns: 1fr;
685
  }
686
-
687
- .profile-pic {
688
- width: 150px;
689
- height: 150px;
690
  }
691
  }
692
 
693
- @media (max-width: 480px) {
694
- .landing-section h1 {
695
- font-size: 2rem;
696
- }
697
-
698
- .landing-section h2 {
699
- font-size: 1.2rem;
700
- }
701
-
702
- .card-content {
703
- min-height: 150px;
704
- font-size: 22px;
 
705
  }
706
-
707
- .social-links {
708
- gap: 15px;
709
  }
710
-
711
- .social-button {
712
- width: 40px;
713
- height: 40px;
714
- font-size: 1rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
  }
717
  """
718
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
719
  # --- Portfolio Layout ---
720
- with gr.Blocks(title="Manyue's Portfolio", css=portfolio_css) as demo:
721
  # Create sections
722
  # Data Analytics Section (initially hidden)
723
  with gr.Row(visible=False, elem_classes="section-container da-section") as da_section:
724
  with gr.Column():
725
  # Back button
726
  back_from_da = gr.Button("← Back to Home", elem_classes="back-button back-button-da")
727
- gr.HTML("""
728
- <h1 class="section-heading">Data Analytics</h1>
729
- <div class="section-intro">
730
- I specialize in transforming raw data into actionable business insights that drive strategic decision-making.
731
- With a strong background in both data analytics and commerce, I bridge the gap between business needs and technical solutions.
732
- My approach combines statistical analysis with compelling data visualization to tell stories that solve real-world problems.
733
- I've developed expertise in designing dashboards that make complex data accessible and creating end-to-end analysis
734
- workflows that uncover hidden patterns and trends.
735
- </div>
736
- """)
737
-
738
- gr.HTML("""
739
- <h3 class="section-subheading da">Skills</h3>
740
-
741
- <div class="skills-list">
742
- <div class="skill-category">
743
- <h4>Data Visualization</h4>
744
- <ul>
745
- <li>Power BI</li>
746
- <li>Tableau</li>
747
- <li>Matplotlib/Seaborn</li>
748
- <li>Plotly/Dash</li>
749
- </ul>
750
- </div>
751
-
752
- <div class="skill-category">
753
- <h4>Data Manipulation</h4>
754
- <ul>
755
- <li>SQL</li>
756
- <li>Pandas</li>
757
- <li>NumPy</li>
758
- <li>ETL Pipelines</li>
759
- </ul>
760
- </div>
761
-
762
- <div class="skill-category">
763
- <h4>Analysis Techniques</h4>
764
- <ul>
765
- <li>Statistical Analysis</li>
766
- <li>A/B Testing</li>
767
- <li>Time Series Analysis</li>
768
- <li>Customer Segmentation</li>
769
- </ul>
770
- </div>
771
-
772
- <div class="skill-category">
773
- <h4>Business Intelligence</h4>
774
- <ul>
775
- <li>KPI Development</li>
776
- <li>Executive Reporting</li>
777
- <li>Data Storytelling</li>
778
- <li>Process Optimization</li>
779
- </ul>
780
- </div>
781
- </div>
782
-
783
- <h3 class="section-subheading da">Projects</h3>
784
-
785
- <div class="project-card">
786
- <div class="project-title">
787
- <span class="project-title-text">Northwind Sales Insight Dashboard</span>
788
- <a href="https://github.com/Manyue-datascientist/northwind-retail-analysis" target="_blank" class="project-link">
789
- """ + link_icon + """
790
- <span>View Project</span>
791
- </a>
792
- </div>
793
- <div class="project-description">
794
- A business-driven case study where I performed in-depth EDA on the classic Northwind dataset. I uncovered key trends in sales, customer behavior, and product performance, and built a professional dashboard for storytelling using Power BI and SQL.
795
- <span class="tech-stack"><strong>Tech Stack:</strong> SQL, Power BI, Pandas</span>
796
- </div>
797
- </div>
798
-
799
- <div class="project-card">
800
- <div class="project-title">
801
- <span class="project-title-text">Loan Default Risk Analysis</span>
802
- <a href="#" target="_blank" class="project-link">
803
- """ + link_icon + """
804
- <span>View Project</span>
805
- </a>
806
- </div>
807
- <div class="project-description">
808
- A feature-driven analytics project where I identified critical drivers of loan defaults. I applied statistical analysis and visual storytelling to assist in better loan disbursement strategies.
809
- <span class="tech-stack"><strong>Tech Stack:</strong> Python, Matplotlib, Pandas</span>
810
- </div>
811
- </div>
812
- """)
813
 
814
  # Machine Learning Section (initially hidden)
815
  with gr.Row(visible=False, elem_classes="section-container ml-section") as ml_section:
816
  with gr.Column():
817
  # Back button
818
  back_from_ml = gr.Button("← Back to Home", elem_classes="back-button back-button-ml")
819
- gr.HTML("""
820
- <h1 class="section-heading">Machine Learning</h1>
821
- <div class="section-intro">
822
- My machine learning expertise spans from traditional algorithms to deep learning systems that solve real business challenges.
823
- I've built end-to-end ML pipelines that deliver measurable impact, combining the right models with appropriate feature engineering
824
- techniques. I focus on creating solutions that are not only technically sound but also deployable, maintainable,
825
- and integrated with business workflows. With a solid foundation in Python-based ML frameworks and cloud
826
- deployment platforms, I develop models that generate actionable predictions and insights.
827
- </div>
828
- """)
829
-
830
- gr.HTML("""
831
- <h3 class="section-subheading ml">Skills</h3>
832
-
833
- <div class="skills-list">
834
- <div class="skill-category">
835
- <h4>Frameworks & Libraries</h4>
836
- <ul>
837
- <li>TensorFlow/Keras</li>
838
- <li>PyTorch</li>
839
- <li>Scikit-Learn</li>
840
- <li>XGBoost/LightGBM</li>
841
- </ul>
842
- </div>
843
-
844
- <div class="skill-category">
845
- <h4>ML Techniques</h4>
846
- <ul>
847
- <li>Supervised Learning</li>
848
- <li>Unsupervised Learning</li>
849
- <li>Deep Learning</li>
850
- <li>Natural Language Processing</li>
851
- </ul>
852
- </div>
853
-
854
- <div class="skill-category">
855
- <h4>MLOps</h4>
856
- <ul>
857
- <li>ML Pipelines</li>
858
- <li>Model Monitoring</li>
859
- <li>Deployment Strategies</li>
860
- <li>Version Control (DVC)</li>
861
- </ul>
862
- </div>
863
-
864
- <div class="skill-category">
865
- <h4>Cloud ML Services</h4>
866
- <ul>
867
- <li>AWS SageMaker</li>
868
- <li>Google AI Platform</li>
869
- <li>Azure ML</li>
870
- <li>MLflow</li>
871
- </ul>
872
- </div>
873
- </div>
874
-
875
- <h3 class="section-subheading ml">Projects</h3>
876
-
877
- <div class="project-card">
878
- <div class="project-title">
879
- <span class="project-title-text">University Admission Predictor</span>
880
- <a href="#" target="_blank" class="project-link">
881
- """ + link_icon + """
882
- <span>Try the Predictor</span>
883
- </a>
884
- </div>
885
- <div class="project-description">
886
- Built a regression model to predict the chances of a student getting admitted to top universities based on academic profiles. The project includes feature importance analysis, model tuning, and a live demo deployed with Streamlit.
887
- <span class="tech-stack"><strong>Tech Stack:</strong> Scikit-learn, Streamlit, NumPy</span>
888
- </div>
889
- </div>
890
-
891
- <div class="project-card">
892
- <div class="project-title">
893
- <span class="project-title-text">AI Chat Assistant for Recruiters</span>
894
- <a href="https://huggingface.co/spaces/Manyue-DataScientist/AI-Assistant" target="_blank" class="project-link">
895
- """ + link_icon + """
896
- <span>Chat with Assistant</span>
897
- </a>
898
- </div>
899
- <div class="project-description">
900
- A custom-trained assistant that answers queries about my resume and portfolio using NLP and retrieval techniques. Built to simulate real-time interactions with hiring teams, this project showcases my ability to work with large language models and create practical AI applications.
901
- <span class="tech-stack"><strong>Tech Stack:</strong> LangChain, OpenAI, Gradio</span>
902
- </div>
903
- </div>
904
-
905
- <div class="project-card">
906
- <div class="project-title">
907
- <span class="project-title-text">Speaker Diarization Application</span>
908
- <a href="https://huggingface.co/spaces/Manyue-DataScientist/speaker-diarization-app-v2" target="_blank" class="project-link">
909
- """ + link_icon + """
910
- <span>Try the Application</span>
911
- </a>
912
- </div>
913
- <div class="project-description">
914
- Developed an advanced multi-speaker audio processing system that performs speaker diarization, transcription, and summarization to extract meaningful insights from multi-speaker conversations.
915
- <span class="tech-stack"><strong>Tech Stack:</strong> PyTorch, Hugging Face Transformers, Gradio</span>
916
- </div>
917
- </div>
918
- """)
919
 
920
  # Computer Vision Section (initially hidden)
921
  with gr.Row(visible=False, elem_classes="section-container cv-section") as cv_section:
922
  with gr.Column():
923
  # Back button
924
  back_from_cv = gr.Button("← Back to Home", elem_classes="back-button back-button-cv")
925
- gr.HTML("""
926
- <h1 class="section-heading">Computer Vision</h1>
927
- <div class="section-intro">
928
- I'm passionate about developing computer vision systems that can perceive and understand visual information in ways that benefit humans.
929
- My experience spans from implementing state-of-the-art algorithms to deploying them in real-world scenarios. I've worked on projects
930
- that enable machines to "see" and interpret their environment through image processing, object detection, and image classification.
931
- I focus particularly on applications that improve accessibility and solve tangible problems, creating CV solutions
932
- that operate efficiently even with hardware constraints.
933
- </div>
934
- """)
935
-
936
- gr.HTML("""
937
- <h3 class="section-subheading cv">Skills</h3>
938
-
939
- <div class="skills-list">
940
- <div class="skill-category">
941
- <h4>CV Techniques</h4>
942
- <ul>
943
- <li>Object Detection</li>
944
- <li>Image Segmentation</li>
945
- <li>Feature Extraction</li>
946
- <li>Image Classification</li>
947
- </ul>
948
- </div>
949
-
950
- <div class="skill-category">
951
- <h4>CV Libraries</h4>
952
- <ul>
953
- <li>OpenCV</li>
954
- <li>PIL/Pillow</li>
955
- <li>TorchVision</li>
956
- <li>TF Computer Vision</li>
957
- </ul>
958
- </div>
959
-
960
- <div class="skill-category">
961
- <h4>Deep Learning for CV</h4>
962
- <ul>
963
- <li>CNNs</li>
964
- <li>YOLO frameworks</li>
965
- <li>Transfer Learning</li>
966
- <li>Object Recognition</li>
967
- </ul>
968
- </div>
969
-
970
- <div class="skill-category">
971
- <h4>Applications</h4>
972
- <ul>
973
- <li>Accessibility Solutions</li>
974
- <li>OCR/Document Analysis</li>
975
- <li>Motion Tracking</li>
976
- <li>Edge Deployment</li>
977
- </ul>
978
- </div>
979
- </div>
980
-
981
- <h3 class="section-subheading cv">Projects</h3>
982
-
983
- <div class="project-card">
984
- <div class="project-title">
985
- <span class="project-title-text">Smart Shopping Assistant for the Blind</span>
986
- <a href="https://github.com/Manyue-datascientist/smart_glove_project" target="_blank" class="project-link">
987
- """ + link_icon + """
988
- <span>View Project</span>
989
- </a>
990
- </div>
991
- <div class="project-description">
992
- Designed a system using object detection and OCR to help visually impaired individuals find products and navigate shopping aisles. Developed with real-time feedback on Raspberry Pi and OAK-D camera, this project demonstrates my commitment to creating technology that solves real accessibility challenges.
993
- <span class="tech-stack"><strong>Tech Stack:</strong> YOLOv8, OpenCV, Raspberry Pi</span>
994
- </div>
995
- </div>
996
-
997
- <div class="project-card">
998
- <div class="project-title">
999
- <span class="project-title-text">Traffic Flow Counter (Upcoming)</span>
1000
- <a href="#" target="_blank" class="project-link">
1001
- """ + link_icon + """
1002
- <span>Coming Soon</span>
1003
- </a>
1004
- </div>
1005
- <div class="project-description">
1006
- An edge solution using Raspberry Pi to monitor and count vehicles at intersections, providing real-time traffic flow analytics. This project demonstrates efficient deployment of CV models on resource-constrained devices.
1007
- <span class="tech-stack"><strong>Tech Stack:</strong> YOLOv5, Raspberry Pi, OpenCV</span>
1008
- </div>
1009
- </div>
1010
- """)
1011
 
1012
  with gr.Row(visible=True, elem_classes="landing-section") as landing_section:
1013
  with gr.Column():
1014
- # Profile section with picture - using the actual image from data folder
1015
- try:
1016
- # Get the image as data URI
1017
- profile_img_uri = image_to_data_uri("data/My_photo.jpeg")
1018
- gr.HTML(f"""
1019
- <div class="profile-container">
1020
- <div class="profile-pic">
1021
- <img src="{profile_img_uri}" alt="Manyue Javvadi" />
1022
- </div>
1023
- <div class="name-text">Manyue Javvadi</div>
1024
- </div>
1025
- <h2>AI/ML Engineer & Data Scientist</h2>
1026
- <div class="about-text">
1027
- I'm a software engineer turned AI/ML practitioner with a strong foundation in Commerce and experience in ML, computer vision, and data analytics.
1028
- I blend business understanding with data-driven thinking to create real-world solutions. Currently open to roles in Data Science, Machine Learning Engineering, and Computer Vision.
1029
- </div>
1030
-
1031
- <div class="skills-container">
1032
- <div class="skill-pill">Python</div>
1033
- <div class="skill-pill">Machine Learning</div>
1034
- <div class="skill-pill">TensorFlow</div>
1035
- <div class="skill-pill">PyTorch</div>
1036
- <div class="skill-pill">Computer Vision</div>
1037
- <div class="skill-pill">Data Analytics</div>
1038
- <div class="skill-pill">SQL</div>
1039
- <div class="skill-pill">Power BI</div>
1040
- </div>
1041
-
1042
- <div class="social-links">
1043
- <a href="https://www.linkedin.com/in/manyue-javvadi-datascientist/" target="_blank" class="social-button social-linkedin" aria-label="LinkedIn">
1044
- """ + linkedin_icon + """
1045
- </a>
1046
- <a href="https://github.com/Manyue-datascientist" target="_blank" class="social-button social-github" aria-label="GitHub">
1047
- """ + github_icon + """
1048
- </a>
1049
- <a href="mailto:[email protected]" class="social-button social-email" aria-label="Contact Me" id="contact_btn">
1050
- """ + mail_icon + """
1051
- </a>
1052
- </div>
1053
-
1054
- <h2>My Specializations</h2>
1055
- """)
1056
- except Exception as e:
1057
- # Fallback if image cannot be loaded
1058
- gr.HTML("""
1059
- <div class="profile-container">
1060
- <div class="profile-pic">
1061
- <img src="/api/placeholder/400/400" alt="Manyue Javvadi" />
1062
- </div>
1063
- <div class="name-text">Manyue Javvadi</div>
1064
- </div>
1065
- <h2>AI/ML Engineer & Data Scientist</h2>
1066
- <div class="about-text">
1067
- I'm a software engineer turned AI/ML practitioner with a strong foundation in Commerce and experience in ML, computer vision, and data analytics.
1068
- I blend business understanding with data-driven thinking to create real-world solutions. Currently open to roles in Data Science, Machine Learning Engineering, and Computer Vision.
1069
- </div>
1070
-
1071
- <div class="skills-container">
1072
- <div class="skill-pill">Python</div>
1073
- <div class="skill-pill">Machine Learning</div>
1074
- <div class="skill-pill">TensorFlow</div>
1075
- <div class="skill-pill">PyTorch</div>
1076
- <div class="skill-pill">Computer Vision</div>
1077
- <div class="skill-pill">Data Analytics</div>
1078
- <div class="skill-pill">SQL</div>
1079
- <div class="skill-pill">Power BI</div>
1080
- </div>
1081
-
1082
- <div class="social-links">
1083
- <a href="https://www.linkedin.com/in/manyue-javvadi-datascientist/" target="_blank" class="social-button social-linkedin" aria-label="LinkedIn">
1084
- """ + linkedin_icon + """
1085
- </a>
1086
- <a href="https://github.com/Manyue-datascientist" target="_blank" class="social-button social-github" aria-label="GitHub">
1087
- """ + github_icon + """
1088
- </a>
1089
- <a href="mailto:[email protected]" class="social-button social-email" aria-label="Contact Me" id="contact_btn">
1090
- """ + mail_icon + """
1091
- </a>
1092
- </div>
1093
-
1094
- <h2>My Specializations</h2>
1095
- """)
1096
 
1097
  # Cards Grid with proper structure
1098
  with gr.Row(elem_classes="cards-grid"):
1099
  with gr.Column():
1100
- # Data Analytics Card
1101
  gr.HTML('<div class="card-container da">')
1102
  da_button = gr.Button("Data Analytics", elem_classes="card-button")
1103
- gr.HTML("""
1104
  <div class="card-inner">
1105
  <div class="card-content">
1106
- """ + data_analytics_icon + """
1107
- <span>Data Analytics</span>
1108
  </div>
1109
  <div class="card-description">
1110
- Data storytelling, insights extraction, interactive dashboards & business problem-solving
 
1111
  </div>
1112
  </div>
1113
- </div>
1114
- """)
1115
 
1116
  with gr.Column():
1117
  # Machine Learning Card
1118
  gr.HTML('<div class="card-container ml">')
1119
  ml_button = gr.Button("Machine Learning", elem_classes="card-button")
1120
- gr.HTML("""
1121
  <div class="card-inner">
1122
  <div class="card-content">
1123
- """ + machine_learning_icon + """
1124
- <span>Machine Learning</span>
1125
  </div>
1126
  <div class="card-description">
1127
- Feature engineering, model training, deployment & automation pipelines
 
1128
  </div>
1129
  </div>
1130
- </div>
1131
- """)
1132
 
1133
  with gr.Column():
1134
  # Computer Vision Card
1135
  gr.HTML('<div class="card-container cv">')
1136
  cv_button = gr.Button("Computer Vision", elem_classes="card-button")
1137
- gr.HTML("""
1138
  <div class="card-inner">
1139
  <div class="card-content">
1140
- """ + computer_vision_icon + """
1141
- <span>Computer Vision</span>
1142
  </div>
1143
  <div class="card-description">
1144
- Object detection, image recognition, edge AI & accessibility applications
 
1145
  </div>
1146
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1147
  </div>
1148
- """)
1149
-
1150
- # Contact section - Updated to "Hire Me" with email link
1151
- gr.HTML("""
1152
- <!-- Contact section -->
1153
- <div id="contact_section">
1154
- <h2>Contact Me</h2>
1155
- <div class="contact-container">
1156
- <p>Looking for a data scientist or ML engineer for your team?</p>
1157
- <a href="mailto:[email protected]" class="hire-me-button">Hire Me</a>
1158
- </div>
1159
- </div>
1160
-
1161
- <!-- Footer -->
1162
- <div class="footer">
1163
- <p>© 2025 Manyue Javvadi. All rights reserved.</p>
1164
- <p>Made with Gradio</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1165
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1166
  """)
1167
-
 
 
 
 
 
 
1168
  # Set up click events for navigation
1169
  da_button.click(show_data_analytics, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1170
  ml_button.click(show_machine_learning, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1171
  cv_button.click(show_computer_vision, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1172
-
1173
  back_from_da.click(go_home, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1174
  back_from_ml.click(go_home, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1175
  back_from_cv.click(go_home, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1177
  # Launch the app
1178
- demo.launch()
 
 
1
  import gradio as gr
2
  import base64
3
  import os
4
+ import json
5
 
6
  # --- Helper Functions ---
7
+ def load_json(filename):
8
+ """Load JSON data from content folder"""
9
+ try:
10
+ with open(f"content/{filename}.json", "r", encoding="utf-8") as f:
11
+ return json.load(f)
12
+ except Exception as e:
13
+ print(f"Error loading {filename}.json: {e}")
14
+ return {}
15
+
16
  def file_to_data_uri(filepath, mime_type="application/pdf"):
17
+ """Convert file to data URI"""
18
+ try:
19
+ with open(filepath, "rb") as f:
20
+ data = f.read()
21
+ b64 = base64.b64encode(data).decode("utf-8")
22
+ return f"data:{mime_type};base64,{b64}"
23
+ except Exception as e:
24
+ print(f"Error converting file to data URI: {e}")
25
+ return None
26
 
27
+ def image_to_data_uri(filepath, mime_type="image/jpeg"):
28
+ """Convert image to data URI"""
29
+ try:
30
+ with open(filepath, "rb") as f:
31
+ data = f.read()
32
+ b64 = base64.b64encode(data).decode("utf-8")
33
+ return f"data:{mime_type};base64,{b64}"
34
+ except Exception as e:
35
+ print(f"Error converting image to data URI: {e}")
36
+ return None
37
 
38
  # --- Navigation Functions ---
39
  def show_data_analytics():
 
48
  def go_home():
49
  return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
50
 
51
+ def toggle_resume(is_visible):
52
+ """Toggle the visibility of the resume section."""
53
+ new_state = not is_visible
54
+ new_label = "Hide Resume" if new_state else "View Resume"
55
+ return new_state, gr.update(visible=new_state), gr.update(value=new_label)
56
+
57
  # --- Icons (SVG) ---
58
  data_analytics_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path><path d="M8 10h.01"></path><path d="M12 10h.01"></path><path d="M16 10h.01"></path></svg>"""
59
  machine_learning_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>"""
 
65
  link_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>"""
66
  document_icon = """<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><line x1="10" y1="9" x2="8" y2="9"></line></svg>"""
67
 
68
+ # Dictionary for icon access
69
+ icons = {
70
+ "data_analytics_icon": data_analytics_icon,
71
+ "machine_learning_icon": machine_learning_icon,
72
+ "computer_vision_icon": computer_vision_icon,
73
+ "home_icon": home_icon,
74
+ "linkedin_icon": linkedin_icon,
75
+ "github_icon": github_icon,
76
+ "mail_icon": mail_icon,
77
+ "link_icon": link_icon,
78
+ "document_icon": document_icon
79
+ }
80
+
81
+ # --- Helper functions for generating HTML ---
82
+ def generate_profile_html():
83
+ """Generate HTML for the profile section"""
84
+ try:
85
+ profile_img_uri = image_to_data_uri(profile_data.get("photo", "data/My_photo.jpeg"))
86
+
87
+ # Skills HTML
88
+ skills_html = ""
89
+ for skill in profile_data.get("skills", []):
90
+ skills_html += f'<div class="skill-pill">{skill}</div>\n'
91
+
92
+ # Social links HTML
93
+ social_links_html = ""
94
+ for link in profile_data.get("social_links", []):
95
+ icon = icons.get(link.get("icon", ""), "")
96
+ id_attr = f' id="{link["id"]}"' if "id" in link else ""
97
+ social_links_html += f'''
98
+ <a href="{link["url"]}" target="_blank" class="social-button social-{link["name"].lower()}" aria-label="{link.get("aria_label", link["name"])}"{ id_attr }>
99
+ { icon }
100
+ </a>
101
+ '''
102
+
103
+ return f'''
104
+ <div class="profile-container">
105
+ <div class="profile-pic">
106
+ <img src="{profile_img_uri}" alt="{profile_data.get("name", "Profile")}" />
107
+ </div>
108
+ <div class="name-text">{profile_data.get("name", "")}</div>
109
+ </div>
110
+ <h2>{profile_data.get("title", "")}</h2>
111
+ <div class="about-text">
112
+ {profile_data.get("about", "")}
113
+ </div>
114
+
115
+ <div class="skills-container">
116
+ {skills_html}
117
+ </div>
118
+
119
+ <div class="social-links">
120
+ {social_links_html}
121
+ </div>
122
+ '''
123
+ except Exception as e:
124
+ # Fallback if error occurs
125
+ print(f"Error generating profile HTML: {e}")
126
+ return f'''
127
+ <div class="profile-container">
128
+ <div class="profile-pic">
129
+ <img src="/api/placeholder/400/400" alt="Profile" />
130
+ </div>
131
+ <div class="name-text">{profile_data.get("name", "Name")}</div>
132
+ </div>
133
+ <h2>{profile_data.get("title", "Title")}</h2>
134
+ <div class="about-text">
135
+ {profile_data.get("about", "About text")}</div>
136
+
137
+ <div class="skills-container">
138
+ {', '.join(profile_data.get("skills", ["Skills"]))}
139
+ </div>
140
+ '''
141
+
142
+ def generate_resume_html():
143
+ """Generate HTML for the collapsible resume section"""
144
+ return '''
145
+ <div class="resume-section glass-container">
146
+ <button class="resume-toggle-button" onclick="toggleResume()">View Resume</button>
147
+ <div id="resume-content" class="resume-content" style="display: none;">
148
+ <p>Download my resume or view it below:</p>
149
+ <a id="resume-download-link" class="resume-download-button" target="_blank">Download Resume</a>
150
+ <div class="resume-preview">
151
+ <iframe id="resume-iframe" frameborder="0" class="resume-iframe"></iframe>
152
+ </div>
153
+ </div>
154
+ </div>
155
+ <script>
156
+ function toggleResume() {
157
+ const resumeContent = document.getElementById('resume-content');
158
+ const isHidden = resumeContent.style.display === 'none';
159
+ resumeContent.style.display = isHidden ? 'block' : 'none';
160
+
161
+ if (isHidden) {
162
+ const resumeIframe = document.getElementById('resume-iframe');
163
+ const resumeDownloadLink = document.getElementById('resume-download-link');
164
+ fetch('/file/data/resume.pdf')
165
+ .then(response => response.blob())
166
+ .then(blob => {
167
+ const url = URL.createObjectURL(blob);
168
+ resumeIframe.src = url;
169
+ resumeDownloadLink.href = url;
170
+ })
171
+ .catch(err => console.error('Error loading resume:', err));
172
+ }
173
+ }
174
+ </script>
175
+ '''
176
+
177
+ def generate_cards_html():
178
+ """Generate HTML for the specialization cards"""
179
+ cards_html = ""
180
+ for card in sections_data.get("cards", []):
181
+ icon = icons.get(card.get("icon", ""), "")
182
+ cards_html += f'''
183
+ <div class="card-container {card.get("class", "")}">
184
+ <div class="card-inner">
185
+ <div class="card-content">
186
+ {icon}
187
+ <span>{card.get("title", "")}</span>
188
+ </div>
189
+ <div class="card-description">
190
+ {card.get("description", "")}
191
+ </div>
192
+ </div>
193
+ </div>
194
+ '''
195
+ return cards_html
196
+
197
+ def generate_contact_html():
198
+ """Generate HTML for the contact section"""
199
+ contact = profile_data.get("contact", {})
200
+ footer = profile_data.get("footer", {})
201
+
202
+ return f'''
203
+ <!-- Contact section -->
204
+ <div id="contact_section">
205
+ <h2>{contact.get("heading", "Contact Me")}</h2>
206
+ <div class="contact-container">
207
+ <p>{contact.get("text", "")}</p>
208
+ <a href="mailto:{contact.get("email", "")}" class="hire-me-button">{contact.get("button_text", "Contact")}</a>
209
+ </div>
210
+ </div>
211
+
212
+ <!-- Footer -->
213
+ <div class="footer">
214
+ <p>{footer.get("copyright", "")}</p>
215
+ <p>{footer.get("credits", "")}</p>
216
+ </div>
217
+ '''
218
+
219
+ def generate_skills_html(skills_data, section_class):
220
+ """Generate HTML for skills in a section"""
221
+ skills_html = ""
222
+ for skill_category in skills_data:
223
+ items_html = ""
224
+ for item in skill_category.get("items", []):
225
+ items_html += f"<li>{item}</li>\n"
226
+
227
+ skills_html += f'''
228
+ <div class="skill-category">
229
+ <h4>{skill_category.get("category", "")}</h4>
230
+ <ul>
231
+ {items_html}
232
+ </ul>
233
+ </div>
234
+ '''
235
+ return skills_html
236
+
237
+ def generate_projects_html(projects_data, section_class):
238
+ """Generate HTML for projects in a section"""
239
+ projects_html = ""
240
+ for project in projects_data:
241
+ projects_html += f'''
242
+ <div class="project-card">
243
+ <div class="project-title">
244
+ <span class="project-title-text">{project.get("title", "")}</span>
245
+ <a href="{project.get("url", "#")}" target="_blank" class="project-link">
246
+ {icons.get("link_icon", "")}
247
+ <span>View Project</span>
248
+ </a>
249
+ </div>
250
+ <div class="project-description">
251
+ {project.get("description", "")}
252
+ <span class="tech-stack"><strong>Tech Stack:</strong> {project.get("tech_stack", "")}</span>
253
+ </div>
254
+ </div>
255
+ '''
256
+ return projects_html
257
+
258
+ def generate_section_html(section_data, section_class):
259
+ """Generate HTML for a complete section"""
260
+ skills_html = generate_skills_html(section_data.get("skills", []), section_class)
261
+ projects_html = generate_projects_html(section_data.get("projects", []), section_class)
262
+
263
+ return f'''
264
+ <h1 class="section-heading">{section_data.get("heading", "")}</h1>
265
+ <div class="section-intro">
266
+ {section_data.get("intro", "")}
267
+ </div>
268
+
269
+ <h3 class="section-subheading {section_class}">Skills</h3>
270
+
271
+ <div class="skills-list">
272
+ {skills_html}
273
+ </div>
274
+
275
+ <h3 class="section-subheading {section_class}">Projects</h3>
276
+
277
+ {projects_html}
278
+ '''
279
 
280
  # --- CSS ---
281
  portfolio_css = """
282
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Montserrat:wght@700;800&display=swap');
 
283
  :root {
284
  --primary-da: #8a2be2;
285
  --secondary-da: #2575fc;
 
302
  --transition-med: 0.3s ease;
303
  --transition-slow: 0.5s ease;
304
  }
 
305
  body {
306
  font-family: 'Poppins', sans-serif;
307
  background: var(--dark-bg);
 
312
  margin: 0;
313
  padding: 0;
314
  overflow-x: hidden;
315
+ line-height: 1.6;
316
+ letter-spacing: 0.5px;
317
+ font-weight: 400;
318
  }
 
319
  .gr-container {
320
  max-width: 1200px;
321
  margin: 0 auto;
322
  padding: 20px;
323
  }
 
324
  /* Scrollbar styling */
325
  ::-webkit-scrollbar {
326
  width: 8px;
327
  height: 8px;
328
  }
 
329
  ::-webkit-scrollbar-track {
330
  background: rgba(255, 255, 255, 0.05);
331
  border-radius: 4px;
332
  }
 
333
  ::-webkit-scrollbar-thumb {
334
  background: rgba(255, 255, 255, 0.2);
335
  border-radius: 4px;
336
  }
 
337
  ::-webkit-scrollbar-thumb:hover {
338
  background: rgba(255, 255, 255, 0.3);
339
  }
 
340
  /* Landing section */
341
  .landing-section {
342
  text-align: center;
 
344
  padding: 40px 20px;
345
  position: relative;
346
  }
 
347
  .landing-section:before {
348
  content: '';
349
  position: absolute;
 
354
  background: linear-gradient(180deg, rgba(0,0,0,0.7) 0%, transparent 100%);
355
  z-index: -1;
356
  }
 
357
  .landing-section h1, .landing-section h2 {
358
  color: var(--text-primary) !important;
359
  margin-top: 0;
360
  }
 
361
  .landing-section h1 {
362
  font-family: 'Montserrat', sans-serif;
363
  font-size: 3.2rem;
 
369
  color: transparent !important;
370
  letter-spacing: -0.5px;
371
  }
 
372
  .landing-section h2 {
373
  font-size: 2rem;
374
  font-weight: 600;
375
  margin-bottom: 1.5rem;
376
  }
 
377
  .profile-container {
378
  margin: 30px auto;
379
  display: flex;
 
381
  justify-content: center;
382
  flex-direction: column;
383
  }
 
384
  .profile-pic {
385
  width: 180px;
386
  height: 180px;
 
393
  background: linear-gradient(45deg, var(--primary-da), var(--primary-ml), var(--primary-cv));
394
  padding: 4px;
395
  }
 
396
  .profile-pic img {
397
  border-radius: 50%;
398
  width: 100%;
399
  height: 100%;
400
  object-fit: cover;
401
  }
 
402
  .name-text {
403
  font-size: 2.6rem;
404
  font-weight: 700;
405
  margin-top: 10px;
406
  margin-bottom: 10px;
407
  }
 
408
  @keyframes float {
409
  0% { transform: translateY(0px) }
410
  50% { transform: translateY(-10px) }
411
  100% { transform: translateY(0px) }
412
  }
 
413
  @keyframes pulse {
414
  0% { transform: scale(1); }
415
  50% { transform: scale(1.05); }
416
  100% { transform: scale(1); }
417
  }
 
418
  .about-text {
419
  max-width: 800px;
420
  margin: 0 auto 40px;
 
422
  line-height: 1.6;
423
  color: var(--text-secondary);
424
  }
 
425
  .skills-container {
426
  margin-top: 20px;
427
  display: flex;
 
430
  gap: 10px;
431
  margin-bottom: 40px;
432
  }
 
433
  .skill-pill {
434
  background: rgba(255, 255, 255, 0.1);
435
  padding: 8px 16px;
 
438
  font-weight: 500;
439
  color: var(--text-secondary);
440
  }
 
441
  .social-links {
442
  display: flex;
443
  justify-content: center;
444
  gap: 20px;
445
  margin: 30px 0;
446
  }
 
447
  .social-button {
448
  background: rgba(255, 255, 255, 0.1);
449
  border: none;
 
458
  font-size: 1.2rem;
459
  box-shadow: var(--shadow-sm);
460
  }
 
461
  .social-button:hover {
462
  transform: translateY(-5px);
463
  background: rgba(255, 255, 255, 0.2);
464
  box-shadow: var(--shadow-md);
465
  }
 
466
  .social-linkedin:hover { background: #0077b5; }
467
  .social-github:hover { background: #333; }
468
  .social-email:hover { background: #ea4335; }
 
469
  .social-button svg {
470
  width: 24px;
471
  height: 24px;
472
  }
 
473
  /* Card styling */
474
  .cards-grid {
475
  display: grid;
 
477
  gap: 30px;
478
  margin: 40px 0;
479
  }
 
480
  .card-container {
481
  position: relative; /* Important for button positioning */
482
  margin-bottom: 20px;
483
  height: 100%;
484
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
485
  }
 
486
  .card-container.da:before {
487
  content: '';
488
  position: absolute;
 
494
  z-index: 5;
495
  border-radius: var(--border-radius-md) var(--border-radius-md) 0 0;
496
  }
 
497
  .card-container.ml:before {
498
  content: '';
499
  position: absolute;
 
506
  transition: all var(--transition-med);
507
  border-radius: var(--border-radius-md) var(--border-radius-md) 0 0;
508
  }
 
509
  .card-container.cv:before {
510
  content: '';
511
  position: absolute;
 
517
  z-index: 5;
518
  border-radius: var(--border-radius-md) var(--border-radius-md) 0 0;
519
  }
 
520
  .card-content {
521
  padding: 30px;
522
  min-height: 200px;
 
530
  z-index: 2;
531
  transition: all var(--transition-med);
532
  }
 
533
  .card-content svg {
534
  width: 60px;
535
  height: 60px;
 
537
  opacity: 0.9;
538
  transition: all var(--transition-med);
539
  }
 
540
  .card-inner {
541
  transition: transform var(--transition-med), box-shadow var(--transition-med), background-color var(--transition-med);
542
  text-align: center;
 
548
  cursor: pointer; /* Indicates the card is clickable */
549
  position: relative; /* Ensure child elements are positioned relative to the card */
550
  }
 
551
  .card-inner:hover {
552
  transform: translateY(-10px) scale(1.05); /* Adds a slight zoom effect */
553
  box-shadow: var(--shadow-lg); /* Increases shadow for emphasis */
554
  background: rgba(255, 255, 255, 0.1); /* Subtle background change */
555
  border: 2px solid var(--primary-da); /* Optional: Add a border to emphasize hover */
556
  }
 
557
  .card-inner:hover .card-content svg {
558
  transform: scale(1.1); /* Slightly enlarges the icon */
559
  opacity: 1;
560
  }
 
561
  .card-inner:hover .card-description {
562
  color: var(--text-primary); /* Optional: changes text color for emphasis */
563
  }
 
564
  /* Add a subtle glow effect */
565
  .card-inner:hover:before {
566
  content: '';
 
573
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.3);
574
  z-index: -1;
575
  }
 
576
  .card-description {
577
  padding: 0 20px 20px;
578
  color: var(--text-secondary);
579
  font-size: 1.1rem;
580
  line-height: 1.5;
581
  }
 
582
  /* Card button styling - crucial for making cards clickable */
583
  .card-button {
584
  position: absolute !important;
 
597
  background: none !important;
598
  }
599
 
600
+ .click-to-view {
601
+ margin-top: 15px;
602
+ padding: 8px 15px;
603
+ border-radius: 20px;
604
+ display: inline-block;
605
+ font-weight: 600;
606
+ font-size: 0.9rem;
607
+ transition: all var(--transition-med);
608
+ background: rgba(255, 255, 255, 0.1);
609
+ box-shadow: var(--shadow-sm);
610
+ margin-left: auto;
611
+ margin-right: auto;
612
+ color: var(--text-primary);
613
+ text-align: center;
614
+ }
615
+
616
+ /* Add glow effect */
617
+ .click-to-view:hover {
618
+ transform: translateY(-2px);
619
+ box-shadow: 0 0 15px rgba(255, 255, 255, 0.5), var(--shadow-md);
620
+ background: rgba(255, 255, 255, 0.2);
621
+ color: var(--text-primary);
622
+ filter: brightness(1.2);
623
+ }
624
+
625
+ /* Add animation for subtle pulsing effect */
626
+ @keyframes glow-pulse {
627
+ 0% {
628
+ box-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
629
+ }
630
+ 50% {
631
+ box-shadow: 0 0 15px rgba(255, 255, 255, 0.4);
632
+ }
633
+ 100% {
634
+ box-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
635
+ }
636
+ }
637
+
638
+ .click-to-view {
639
+ animation: glow-pulse 3s infinite;
640
+ }
641
+
642
+ .card-description p {
643
+ margin-bottom: 15px;
644
+ margin-top: 0;
645
+ }
646
+
647
+ .card-description {
648
+ display: flex;
649
+ flex-direction: column;
650
+ align-items: center;
651
+ justify-content: space-between;
652
+ height: 100%;
653
+ padding: 0 20px 20px;
654
+ color: var(--text-secondary);
655
+ font-size: 1.1rem;
656
+ line-height: 1.5;
657
+ }
658
+
659
+ /* Different colors for each card type */
660
+ .da-click {
661
+ color: var(--primary-da);
662
+ border: 1px solid var(--primary-da);
663
+ }
664
+
665
+ .ml-click {
666
+ color: var(--primary-ml);
667
+ border: 1px solid var(--primary-ml);
668
+ }
669
+
670
+ .cv-click {
671
+ color: var(--primary-cv);
672
+ border: 1px solid var(--primary-cv);
673
+ }
674
+
675
+ /* Hover effects */
676
+ .click-to-view:hover {
677
+ transform: translateY(-2px);
678
+ box-shadow: var(--shadow-md);
679
+ background: rgba(255, 255, 255, 0.15);
680
+ }
681
+
682
+ .card-inner:hover .click-to-view {
683
+ transform: translateY(-2px);
684
+ box-shadow: var(--shadow-md);
685
+ }
686
+
687
+ .card-inner:hover .da-click {
688
+ background-color: rgba(138, 43, 226, 0.1);
689
+ }
690
+
691
+ .card-inner:hover .ml-click {
692
+ background-color: rgba(0, 180, 219, 0.1);
693
+ }
694
+
695
+ .card-inner:hover .cv-click {
696
+ background-color: rgba(255, 77, 126, 0.1);
697
+ }
698
+
699
+ .experience-timeline {
700
+ margin: 80px 0 60px;
701
+ padding: 20px;
702
+ position: relative;
703
+ }
704
+
705
+ .experience-timeline h2 {
706
+ text-align: center;
707
+ margin-bottom: 60px;
708
+ position: relative;
709
+ display: inline-block;
710
+ left: 50%;
711
+ transform: translateX(-50%);
712
+ font-family: 'Montserrat', sans-serif;
713
+ font-size: 2.5rem;
714
+ font-weight: 700;
715
+ background: linear-gradient(90deg, var(--primary-da), var(--primary-ml), var(--primary-cv));
716
+ -webkit-background-clip: text;
717
+ background-clip: text;
718
+ color: transparent;
719
+ }
720
+
721
+ .experience-timeline h2:after {
722
+ content: '';
723
+ position: absolute;
724
+ bottom: -10px;
725
+ left: 0;
726
+ width: 100%;
727
+ height: 3px;
728
+ border-radius: 3px;
729
+ background: linear-gradient(90deg, var(--primary-da), var(--primary-ml), var(--primary-cv));
730
+ }
731
+
732
+ /* Horizontal Timeline Styling */
733
+ .timeline-container {
734
+ display: flex;
735
+ justify-content: space-between;
736
+ align-items: flex-start;
737
+ position: relative;
738
+ margin: 40px auto;
739
+ max-width: 100%;
740
+ overflow-x: auto;
741
+ padding: 20px 0;
742
+ }
743
+
744
+ .timeline-track {
745
+ position: absolute;
746
+ top: 50%;
747
+ left: 0;
748
+ right: 0;
749
+ height: 4px;
750
+ background: linear-gradient(to right, var(--primary-da), var(--primary-ml), var(--primary-cv));
751
+ border-radius: 2px;
752
+ z-index: 1;
753
+ }
754
+
755
+ .timeline-node {
756
+ position: relative;
757
+ display: flex;
758
+ flex-direction: column;
759
+ align-items: center;
760
+ width: 150px;
761
+ margin: 0 20px;
762
+ z-index: 2;
763
+ }
764
+
765
+ .timeline-dot {
766
+ width: 20px;
767
+ height: 20px;
768
+ background: var(--primary-da);
769
+ border-radius: 50%;
770
+ box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
771
+ margin-bottom: 10px;
772
+ transition: transform 0.3s ease;
773
+ }
774
+
775
+ .timeline-year {
776
+ font-size: 1rem;
777
+ font-weight: 600;
778
+ margin-bottom: 10px;
779
+ color: var(--text-primary);
780
+ }
781
+
782
+ .timeline-content {
783
+ background: var(--card-bg);
784
+ border-radius: var(--border-radius-md);
785
+ box-shadow: var(--shadow-sm);
786
+ padding: 15px;
787
+ text-align: center;
788
+ width: 100%;
789
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
790
+ }
791
+
792
+ .timeline-node:hover .timeline-dot {
793
+ transform: scale(1.3);
794
+ }
795
+
796
+ .timeline-node:hover .timeline-content {
797
+ transform: translateY(-10px);
798
+ box-shadow: var(--shadow-md);
799
+ }
800
+
801
+ /* Responsive adjustments for smaller screens */
802
+ @media (max-width: 768px) {
803
+ .timeline-container {
804
+ flex-direction: column;
805
+ align-items: center;
806
+ }
807
+
808
+ .timeline-track {
809
+ display: none;
810
+ }
811
+
812
+ .timeline-node {
813
+ margin: 20px 0;
814
+ }
815
+ }
816
+
817
  /* Section styling */
818
  .section-container {
819
+ padding: 60px 20px;
820
  position: relative;
821
  }
 
822
  .section-container:before {
823
  content: '';
824
  position: absolute;
 
829
  background: radial-gradient(ellipse at top, rgba(255,255,255,0.05) 0%, transparent 70%);
830
  z-index: 0;
831
  }
 
832
  .da-section h1.section-heading {
833
  color: var(--primary-da);
834
  position: relative;
835
  display: inline-block;
836
  }
 
837
  .ml-section h1.section-heading {
838
  color: var(--primary-ml);
839
  position: relative;
840
  display: inline-block;
841
  }
 
842
  .cv-section h1.section-heading {
843
  color: var(--primary-cv);
844
  position: relative;
845
  display: inline-block;
846
  }
 
847
  .section-heading:after {
848
  content: '';
849
  position: absolute;
 
853
  height: 3px;
854
  border-radius: 3px;
855
  }
 
856
  .da-section .section-heading:after { background: var(--primary-da); }
857
  .ml-section .section-heading:after { background: var(--primary-ml); }
858
  .cv-section .section-heading:after { background: var(--primary-cv); }
 
859
  /* Subheadings color-coded */
860
  .section-subheading.da { color: var(--primary-da); }
861
  .section-subheading.ml { color: var(--primary-ml); }
862
  .section-subheading.cv { color: var(--primary-cv); }
 
863
  /* Back buttons */
864
  .back-button {
865
  border: none;
 
874
  align-items: center;
875
  gap: 8px;
876
  }
 
877
  .back-button:hover {
878
  transform: translateY(-3px);
879
  box-shadow: var(--shadow-md);
880
  }
 
881
  .back-button-da {
882
  background: linear-gradient(45deg, var(--primary-da), var(--secondary-da));
883
  color: #fff;
884
  }
 
885
  .back-button-ml {
886
  background: linear-gradient(45deg, var(--primary-ml), var(--secondary-ml));
887
  color: #fff;
888
  }
 
889
  .back-button-cv {
890
  background: linear-gradient(45deg, var(--primary-cv), var(--secondary-cv));
891
  color: #fff;
892
  }
 
893
  .back-button svg {
894
  width: 20px;
895
  height: 20px;
896
  }
 
897
  /* Contact form */
898
  .contact-container {
899
  background: var(--card-bg);
 
903
  margin: 0 auto;
904
  box-shadow: var(--shadow-md);
905
  }
 
906
  .hire-me-button {
907
  background: linear-gradient(45deg, var(--primary-da), var(--primary-cv));
908
  color: white;
 
913
  font-weight: 600;
914
  cursor: pointer;
915
  transition: all var(--transition-med);
 
916
  box-shadow: var(--shadow-sm);
917
  display: inline-block;
918
  text-decoration: none;
919
  }
 
920
  .hire-me-button:hover {
921
  transform: translateY(-3px);
922
  box-shadow: var(--shadow-md);
923
  filter: brightness(1.1);
924
  }
 
925
  /* Project cards */
926
  .project-card {
927
  background: var(--card-bg);
 
932
  transition: all var(--transition-med);
933
  border-left: 4px solid transparent;
934
  }
 
935
  .da-section .project-card { border-left-color: var(--primary-da); }
936
  .ml-section .project-card { border-left-color: var(--primary-ml); }
937
  .cv-section .project-card { border-left-color: var(--primary-cv); }
 
938
  .project-card:hover {
939
  transform: translateX(5px);
940
  box-shadow: var(--shadow-md);
941
  }
 
942
  .project-title {
943
  font-size: 1.3rem;
944
  font-weight: 600;
 
947
  align-items: center;
948
  justify-content: space-between;
949
  }
 
950
  .project-title-text {
951
  flex: 1;
952
  }
 
953
  .project-link {
954
  color: var(--text-secondary);
955
  transition: all var(--transition-med);
 
958
  align-items: center;
959
  margin-left: 10px;
960
  }
 
961
  .project-link svg {
962
  width: 16px;
963
  height: 16px;
 
964
  }
 
965
  .da-section .project-title-text { color: var(--primary-da); }
966
  .ml-section .project-title-text { color: var(--primary-ml); }
967
  .cv-section .project-title-text { color: var(--primary-cv); }
 
968
  .da-section .project-link:hover { color: var(--primary-da); }
969
  .ml-section .project-link:hover { color: var(--primary-ml); }
970
  .cv-section .project-link:hover { color: var(--primary-cv); }
 
971
  .project-description {
972
  color: var(--text-secondary);
973
  line-height: 1.5;
974
  }
 
975
  .tech-stack {
976
  display: block;
977
  margin-top: 10px;
978
  font-style: italic;
979
  color: var(--text-muted);
980
  }
 
981
  /* Skills list */
982
  .skills-list {
983
  display: grid;
 
986
  margin-top: 20px;
987
  margin-bottom: 40px; /* Added margin to create space between skills and projects */
988
  }
 
989
  .skill-category {
990
  background: rgba(255, 255, 255, 0.05);
991
  border-radius: var(--border-radius-sm);
992
  padding: 15px;
993
  transition: all var(--transition-med);
994
  }
 
995
  .skill-category:hover {
996
  background: rgba(255, 255, 255, 0.08);
997
  transform: translateY(-3px);
998
  }
 
999
  .skill-category h4 {
1000
  margin-top: 0;
1001
  margin-bottom: 10px;
1002
  font-size: 1.1rem;
1003
  }
 
1004
  .da-section .skill-category h4 { color: var(--primary-da); }
1005
  .ml-section .skill-category h4 { color: var(--primary-ml); }
1006
  .cv-section .skill-category h4 { color: var(--primary-cv); }
 
1007
  .skill-category ul {
1008
  margin: 0;
1009
  padding-left: 20px;
1010
  color: var(--text-secondary);
1011
  }
 
1012
  .skill-category li {
1013
  margin-bottom: 5px;
1014
  }
 
1015
  /* Section intro text */
1016
  .section-intro {
1017
  max-width: 800px;
 
1020
  color: var(--text-secondary);
1021
  font-size: 1.1rem;
1022
  }
 
1023
  /* Footer */
1024
  .footer {
1025
  text-align: center;
 
1028
  color: var(--text-muted);
1029
  font-size: 0.9rem;
1030
  }
 
1031
  /* Animations for scroll */
1032
  .animate-on-scroll {
1033
  opacity: 0;
 
1036
  }
1037
 
1038
  .animate-on-scroll.show {
1039
+ animation: fadeInUp 0.6s ease forwards;
 
1040
  }
1041
 
1042
+ @keyframes fadeInUp {
1043
+ 0% {
1044
+ opacity: 0;
1045
+ transform: translateY(20px);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1046
  }
1047
+ 100% {
1048
+ opacity: 1;
1049
+ transform: translateY(0);
 
1050
  }
1051
  }
1052
 
1053
+ /* Loading states */
1054
+ .loading-spinner {
1055
+ border: 4px solid rgba(255, 255, 255, 0.2);
1056
+ border-top: 4px solid var(--primary-da);
1057
+ border-radius: 50%;
1058
+ width: 40px;
1059
+ height: 40px;
1060
+ animation: spin 1s linear infinite;
1061
+ }
1062
+
1063
+ @keyframes spin {
1064
+ 0% {
1065
+ transform: rotate(0deg);
1066
  }
1067
+ 100% {
1068
+ transform: rotate(360deg);
 
1069
  }
1070
+ }
1071
+
1072
+ /* Glassmorphism effects */
1073
+ .glass-container {
1074
+ background: rgba(255, 255, 255, 0.1);
1075
+ backdrop-filter: blur(10px);
1076
+ border: 1px solid rgba(255, 255, 255, 0.2);
1077
+ border-radius: var(--border-radius-md);
1078
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
1079
+ }
1080
+
1081
+ /* Subtle background patterns */
1082
+ body {
1083
+ background-image: url('https://www.transparenttextures.com/patterns/noise.png');
1084
+ }
1085
+
1086
+ /* Gradient refinements */
1087
+ button {
1088
+ background: linear-gradient(45deg, var(--primary-da), var(--primary-ml));
1089
+ }
1090
+
1091
+ /* Shadow depth variations */
1092
+ .card-container:hover {
1093
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
1094
+ }
1095
+
1096
+ /* ARIA attributes */
1097
+ button {
1098
+ aria-label: "Interactive button";
1099
+ }
1100
+
1101
+ /* Keyboard navigation */
1102
+ button:focus {
1103
+ outline: 2px solid var(--primary-da);
1104
+ outline-offset: 2px;
1105
+ }
1106
+
1107
+ /* Mobile-first refinements */
1108
+ @media (max-width: 768px) {
1109
+ .cards-grid {
1110
+ grid-template-columns: 1fr;
1111
+ }
1112
+ }
1113
+
1114
+ /* Touch-friendly targets */
1115
+ button {
1116
+ padding: 12px 20px;
1117
+ }
1118
+
1119
+ /* Responsive typography */
1120
+ h1 {
1121
+ font-size: calc(2rem + 1vw);
1122
+ }
1123
+
1124
+ /* Parallax scrolling effects */
1125
+ .section-container:before {
1126
+ content: '';
1127
+ position: absolute;
1128
+ top: 0;
1129
+ left: 0;
1130
+ width: 100%;
1131
+ height: 300px;
1132
+ background: linear-gradient(180deg, rgba(0, 0, 0, 0.5), transparent);
1133
+ transform: translateY(-50%);
1134
+ will-change: transform;
1135
+ transition: transform 0.3s ease;
1136
+ }
1137
+
1138
+ body.onscroll .section-container:before {
1139
+ transform: translateY(0);
1140
+ }
1141
+
1142
+ /* Mouse-follow glow effect */
1143
+ .interactive:hover {
1144
+ box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
1145
+ }
1146
+
1147
+ /* Staggered animation sequences */
1148
+ @keyframes staggeredFadeIn {
1149
+ 0% {
1150
+ opacity: 0;
1151
+ transform: translateY(20px);
1152
  }
1153
+ 100% {
1154
+ opacity: 1;
1155
+ transform: translateY(0);
1156
+ }
1157
+ }
1158
+
1159
+ .list-item {
1160
+ opacity: 0;
1161
+ animation: staggeredFadeIn 0.6s ease forwards;
1162
+ }
1163
+
1164
+ .list-item:nth-child(1) {
1165
+ animation-delay: 0.2s;
1166
+ }
1167
+
1168
+ .list-item:nth-child(2) {
1169
+ animation-delay: 0.4s;
1170
+ }
1171
+
1172
+ .list-item:nth-child(3) {
1173
+ animation-delay: 0.6s;
1174
+ }
1175
+
1176
+ /* Interactive particles background */
1177
+ .particles-bg {
1178
+ position: absolute;
1179
+ top: 0;
1180
+ left: 0;
1181
+ width: 100%;
1182
+ height: 100%;
1183
+ background: url('https://www.transparenttextures.com/patterns/cubes.png');
1184
+ opacity: 0.1;
1185
+ }
1186
+
1187
+ /* Smooth page section transitions */
1188
+ html {
1189
+ scroll-behavior: smooth;
1190
+ }
1191
+
1192
+ /* Custom cursor styles */
1193
+ body {
1194
+ cursor: url('https://example.com/custom-cursor.png'), auto;
1195
+ }
1196
+
1197
+ button:hover {
1198
+ cursor: pointer;
1199
+ }
1200
+
1201
+ /* Resume Section */
1202
+ .resume-section {
1203
+ margin: 40px auto;
1204
+ padding: 20px;
1205
+ text-align: center;
1206
+ max-width: 800px;
1207
+ box-shadow: var(--shadow-md);
1208
+ }
1209
+
1210
+ .resume-toggle-button {
1211
+ background: linear-gradient(45deg, var(--primary-da), var(--primary-cv));
1212
+ color: white;
1213
+ border: none;
1214
+ border-radius: var(--border-radius-lg);
1215
+ padding: 10px 20px;
1216
+ font-size: 1rem;
1217
+ font-weight: 600;
1218
+ cursor: pointer;
1219
+ transition: all var(--transition-med);
1220
+ box-shadow: var(--shadow-sm);
1221
+ width: auto; /* Adjust width to fit the text */
1222
+ min-width: 150px; /* Optional: Set a minimum width for consistency */
1223
+ }
1224
+
1225
+ .resume-toggle-button:hover {
1226
+ transform: translateY(-3px);
1227
+ box-shadow: var(--shadow-md);
1228
+ filter: brightness(1.1);
1229
+ }
1230
+
1231
+ .resume-content {
1232
+ margin-top: 20px;
1233
+ text-align: center;
1234
+ }
1235
+
1236
+ .resume-download-button {
1237
+ display: inline-block;
1238
+ margin: 20px 0;
1239
+ padding: 10px 20px;
1240
+ font-size: 1rem;
1241
+ font-weight: 600;
1242
+ color: white;
1243
+ background: linear-gradient(45deg, var(--primary-da), var(--primary-cv));
1244
+ border: none;
1245
+ border-radius: var(--border-radius-lg);
1246
+ text-decoration: none;
1247
+ transition: all var(--transition-med);
1248
+ box-shadow: var(--shadow-sm);
1249
+ }
1250
+
1251
+ .resume-download-button:hover {
1252
+ transform: translateY(-3px);
1253
+ box-shadow: var(--shadow-md);
1254
+ filter: brightness(1.1);
1255
+ }
1256
+
1257
+ .resume-preview {
1258
+ margin-top: 20px;
1259
+ border: 1px solid rgba(255, 255, 255, 0.2);
1260
+ border-radius: var(--border-radius-md);
1261
+ overflow: hidden;
1262
+ box-shadow: var(--shadow-sm);
1263
+ }
1264
+
1265
+ .resume-iframe {
1266
+ width: 100%;
1267
+ height: 500px;
1268
+ border: none;
1269
+ }
1270
+
1271
+ /* Performance optimizations */
1272
+ img {
1273
+ loading: lazy;
1274
+ }
1275
+
1276
+ /* Micro-interactions */
1277
+ button:hover {
1278
+ transform: translateY(-3px);
1279
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
1280
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
1281
+ }
1282
+
1283
+ button:active {
1284
+ transform: scale(0.95);
1285
+ transition: transform 0.1s ease;
1286
+ }
1287
+
1288
+ /* Scroll to Top Button */
1289
+ .scroll-to-top {
1290
+ position: fixed;
1291
+ bottom: 20px;
1292
+ right: 20px;
1293
+ background: linear-gradient(45deg, var(--primary-da), var(--primary-cv));
1294
+ color: white;
1295
+ border: none;
1296
+ border-radius: 50%;
1297
+ width: 50px;
1298
+ height: 50px;
1299
+ font-size: 1.5rem;
1300
+ font-weight: bold;
1301
+ cursor: pointer;
1302
+ box-shadow: var(--shadow-md);
1303
+ display: none; /* Initially hidden */
1304
+ align-items: center;
1305
+ justify-content: center;
1306
+ transition: all var(--transition-med);
1307
+ z-index: 1000;
1308
+ }
1309
+
1310
+ .scroll-to-top:hover {
1311
+ transform: translateY(-3px);
1312
+ box-shadow: var(--shadow-lg);
1313
+ filter: brightness(1.1);
1314
  }
1315
  """
1316
 
1317
+ # Load all content from JSON files
1318
+ try:
1319
+ profile_data = load_json("profile")
1320
+ sections_data = load_json("sections")
1321
+ data_analytics_data = load_json("data_analytics")
1322
+ machine_learning_data = load_json("machine_learning")
1323
+ computer_vision_data = load_json("computer_vision")
1324
+ except Exception as e:
1325
+ print(f"Error loading content: {e}")
1326
+ # Default values in case of error
1327
+ profile_data = {}
1328
+ sections_data = {"cards": []}
1329
+ data_analytics_data = {}
1330
+ machine_learning_data = {}
1331
+ computer_vision_data = {}
1332
+
1333
  # --- Portfolio Layout ---
1334
+ with gr.Blocks(title=f"{profile_data.get('name', 'Portfolio')}", css=portfolio_css) as demo:
1335
  # Create sections
1336
  # Data Analytics Section (initially hidden)
1337
  with gr.Row(visible=False, elem_classes="section-container da-section") as da_section:
1338
  with gr.Column():
1339
  # Back button
1340
  back_from_da = gr.Button("← Back to Home", elem_classes="back-button back-button-da")
1341
+ gr.HTML(generate_section_html(data_analytics_data, "da"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1342
 
1343
  # Machine Learning Section (initially hidden)
1344
  with gr.Row(visible=False, elem_classes="section-container ml-section") as ml_section:
1345
  with gr.Column():
1346
  # Back button
1347
  back_from_ml = gr.Button("← Back to Home", elem_classes="back-button back-button-ml")
1348
+ gr.HTML(generate_section_html(machine_learning_data, "ml"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1349
 
1350
  # Computer Vision Section (initially hidden)
1351
  with gr.Row(visible=False, elem_classes="section-container cv-section") as cv_section:
1352
  with gr.Column():
1353
  # Back button
1354
  back_from_cv = gr.Button("← Back to Home", elem_classes="back-button back-button-cv")
1355
+ gr.HTML(generate_section_html(computer_vision_data, "cv"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1356
 
1357
  with gr.Row(visible=True, elem_classes="landing-section") as landing_section:
1358
  with gr.Column():
1359
+ # Profile section with picture
1360
+ gr.HTML(generate_profile_html())
1361
+
1362
+ # Resume Section (moved above "My Specializations")
1363
+ resume_state = gr.State(value=False)
1364
+ with gr.Group(visible=False) as resume_container:
1365
+ resume_pdf = file_to_data_uri("data/resume.pdf")
1366
+ gr.HTML(f"""<iframe src="{resume_pdf}" width="100%" height="600px" style="border:none;"></iframe>""")
1367
+ resume_toggle_btn = gr.Button("View Resume")
1368
+ resume_toggle_btn.click(fn=toggle_resume, inputs=[resume_state], outputs=[resume_state, resume_container, resume_toggle_btn])
1369
+
1370
+ # Specializations heading
1371
+ gr.HTML("<h2>My Specializations</h2>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1372
 
1373
  # Cards Grid with proper structure
1374
  with gr.Row(elem_classes="cards-grid"):
1375
  with gr.Column():
1376
+ # Data Analytics Card
1377
  gr.HTML('<div class="card-container da">')
1378
  da_button = gr.Button("Data Analytics", elem_classes="card-button")
1379
+ gr.HTML(f'''
1380
  <div class="card-inner">
1381
  <div class="card-content">
1382
+ {icons.get(sections_data.get("cards", [])[0].get("icon", ""), "")}
1383
+ <span>{sections_data.get("cards", [])[0].get("title", "Data Analytics")}</span>
1384
  </div>
1385
  <div class="card-description">
1386
+ {sections_data.get("cards", [])[0].get("description", "")}
1387
+ <div class="click-to-view da-click">Click to view</div>
1388
  </div>
1389
  </div>
1390
+ ''')
 
1391
 
1392
  with gr.Column():
1393
  # Machine Learning Card
1394
  gr.HTML('<div class="card-container ml">')
1395
  ml_button = gr.Button("Machine Learning", elem_classes="card-button")
1396
+ gr.HTML(f'''
1397
  <div class="card-inner">
1398
  <div class="card-content">
1399
+ {icons.get(sections_data.get("cards", [])[1].get("icon", ""), "")}
1400
+ <span>{sections_data.get("cards", [])[1].get("title", "Machine Learning")}</span>
1401
  </div>
1402
  <div class="card-description">
1403
+ {sections_data.get("cards", [])[1].get("description", "")}
1404
+ <div class="click-to-view ml-click">Click to view</div>
1405
  </div>
1406
  </div>
1407
+ ''')
 
1408
 
1409
  with gr.Column():
1410
  # Computer Vision Card
1411
  gr.HTML('<div class="card-container cv">')
1412
  cv_button = gr.Button("Computer Vision", elem_classes="card-button")
1413
+ gr.HTML(f'''
1414
  <div class="card-inner">
1415
  <div class="card-content">
1416
+ {icons.get(sections_data.get("cards", [])[2].get("icon", ""), "")}
1417
+ <span>{sections_data.get("cards", [])[2].get("title", "Computer Vision")}</span>
1418
  </div>
1419
  <div class="card-description">
1420
+ {sections_data.get("cards", [])[2].get("description", "")}
1421
+ <div class="click-to-view cv-click">Click to view</div>
1422
  </div>
1423
  </div>
1424
+ ''')
1425
+
1426
+ gr.HTML(f'''
1427
+ <div class="experience-timeline">
1428
+ <h2>My Journey</h2>
1429
+
1430
+ <div class="timeline-container">
1431
+ <div class="timeline-track"></div>
1432
+
1433
+ <div class="timeline-node">
1434
+ <div class="timeline-dot"></div>
1435
+ <div class="timeline-year">2018-2021</div>
1436
+ <div class="timeline-content">
1437
+ <div class="timeline-title">Bachelor's in Commerce (89%)</div>
1438
+ <div class="timeline-details">
1439
+ <p class="timeline-location">SRM University Chennai</p>
1440
+ <p>Graduated with honors focusing on Business and Finance.</p>
1441
+ </div>
1442
+ </div>
1443
  </div>
1444
+
1445
+ <div class="timeline-node">
1446
+ <div class="timeline-dot"></div>
1447
+ <div class="timeline-year">2021-2023</div>
1448
+ <div class="timeline-content">
1449
+ <div class="timeline-title">Junior Software Engineer at Cognizant</div>
1450
+ <div class="timeline-details">
1451
+ <p class="timeline-location">Chennai India</p>
1452
+ <p>Developed and maintained enterprise-grade Java applications for the Insurance domain</p>
1453
+ </div>
1454
+ </div>
1455
+ </div>
1456
+
1457
+ <div class="timeline-node">
1458
+ <div class="timeline-dot"></div>
1459
+ <div class="timeline-year">2023-2024</div>
1460
+ <div class="timeline-content">
1461
+ <div class="timeline-title">Post-Graduation in AI/ML (97%)</div>
1462
+ <div class="timeline-details">
1463
+ <p class="timeline-location">George Brown college,Canada</p>
1464
+ <p>Advanced studies in AI/ML and data science.</p>
1465
+ </div>
1466
+ </div>
1467
+ </div>
1468
+
1469
+ <div class="timeline-node">
1470
+ <div class="timeline-dot"></div>
1471
+ <div class="timeline-year">2025</div>
1472
+ <div class="timeline-content">
1473
+ <div class="timeline-title">Seeking ML Engineer/Data Scientist Role</div>
1474
+ <div class="timeline-details">
1475
+ <p>Ready for opportunities in ML and Data Science in Canada.</p>
1476
+ </div>
1477
+ </div>
1478
+ </div>
1479
+ </div>
1480
  </div>
1481
+ ''')
1482
+
1483
+ gr.HTML("""
1484
+ <script>
1485
+ document.addEventListener('DOMContentLoaded', function() {
1486
+ // Get all timeline nodes
1487
+ const timelineNodes = document.querySelectorAll('.timeline-node');
1488
+
1489
+ // Add click event listener to each node
1490
+ timelineNodes.forEach(node => {
1491
+ node.addEventListener('click', function() {
1492
+ // Check if this node is already active
1493
+ const isActive = this.classList.contains('active');
1494
+
1495
+ // Remove active class from all nodes
1496
+ timelineNodes.forEach(n => n.classList.remove('active'));
1497
+
1498
+ // If node wasn't active before, make it active
1499
+ if (!isActive) {
1500
+ this.classList.add('active');
1501
+ }
1502
+ });
1503
+ });
1504
+
1505
+ // Make the first node active by default
1506
+ if (timelineNodes.length > 0) {
1507
+ timelineNodes[0].classList.add('active');
1508
+ }
1509
+ });
1510
+ </script>
1511
  """)
1512
+
1513
+ # Contact section
1514
+ gr.HTML(generate_contact_html())
1515
+
1516
+ # Add a Gradio File component to serve the resume file
1517
+ gr.File(value="data/resume.pdf", label="Resume", interactive=False, visible=False)
1518
+
1519
  # Set up click events for navigation
1520
  da_button.click(show_data_analytics, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1521
  ml_button.click(show_machine_learning, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1522
  cv_button.click(show_computer_vision, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1523
+
1524
  back_from_da.click(go_home, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1525
  back_from_ml.click(go_home, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1526
  back_from_cv.click(go_home, inputs=None, outputs=[landing_section, da_section, ml_section, cv_section])
1527
 
1528
+ # Scroll to Top Button
1529
+ gr.HTML("""
1530
+ <button id="scrollToTop" class="scroll-to-top" aria-label="Scroll to Top">
1531
+
1532
+ </button>
1533
+ <script>
1534
+ document.addEventListener('DOMContentLoaded', function () {
1535
+ const scrollToTopButton = document.getElementById('scrollToTop');
1536
+
1537
+ // Show or hide the button based on scroll position
1538
+ window.addEventListener('scroll', function () {
1539
+ if (window.scrollY > 300) {
1540
+ scrollToTopButton.style.display = 'flex'; // Use 'flex' for proper alignment
1541
+ } else {
1542
+ scrollToTopButton.style.display = 'none';
1543
+ }
1544
+ });
1545
+
1546
+ // Scroll to top when the button is clicked
1547
+ scrollToTopButton.addEventListener('click', function () {
1548
+ window.scrollTo({
1549
+ top: 0,
1550
+ behavior: 'smooth'
1551
+ });
1552
+ });
1553
+ });
1554
+ </script>
1555
+ """)
1556
+
1557
  # Launch the app
1558
+ if __name__ == "__main__":
1559
+ demo.launch()