-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.html
More file actions
820 lines (763 loc) · 41.9 KB
/
Copy pathindex.html
File metadata and controls
820 lines (763 loc) · 41.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DASH LAB - Home</title>
<!-- External Libraries (defer non-critical — gallery / Lottie run after parse) -->
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/flickity@2/dist/flickity.min.css">
<script src="https://unpkg.com/flickity@2/dist/flickity.pkgd.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js" defer></script>
<!-- Critical Helper: Defined in Head for immediate availability -->
<script>
window.getImg = function(path) {
if (!path) return '';
let cleanPath = path.startsWith('/') ? path.substring(1) : path;
if (!cleanPath.startsWith('http') && !cleanPath.startsWith('./') && !cleanPath.startsWith('data:')) {
cleanPath = './' + cleanPath;
}
return cleanPath;
};
</script>
<!-- Custom Shared Resources -->
<link rel="stylesheet" href="css/style.css">
<script src="js/common.js" defer></script>
<!-- Data + page logic (defer preserves order; runs before DOMContentLoaded) -->
<script src="js/newsdata.js" defer></script>
<script src="js/imagedata.js" defer></script>
<script src="js/news.js" defer></script>
</head>
<body class="bg-gray-50 flex flex-col min-h-screen">
<!-- JOIN US POPUP -->
<div id="join-modal" class="home-popup">
<div class="modal-content">
<span class="close-modal" id="close-home-modal" onclick="document.getElementById('join-modal').style.display='none'">×</span>
<!-- ENHANCED LOGO CONTAINER -->
<div class="mb-8 flex justify-center">
<div class="relative">
<!-- Decorative Background Rings -->
<div class="absolute inset-0 bg-blue-100 rounded-full scale-150 blur-2xl opacity-40 animate-pulse"></div>
<div class="relative w-40 h-40 bg-white rounded-full p-4 shadow-2xl flex items-center justify-center border-4 border-blue-50">
<img src="img/dash_logo/Dashlab_logo.jpg"
onerror="this.src='https://via.placeholder.com/150?text=DASH+LAB'; this.onerror=null;"
class="w-full h-full object-contain" alt="Logo">
</div>
</div>
</div>
<h2 class="text-3xl font-black text-blue-900 mb-2 tracking-tight">Join Our Lab</h2>
<p class="text-gray-600 mb-6 px-4">For those interested in joining our research team, please fill out the official <strong>Application Form</strong> below.</p>
<!-- Important Notice in Popup -->
<div class="mb-8 p-4 bg-red-50 border-l-4 border-red-500 rounded-r-xl text-left mx-4 shadow-sm">
<p class="text-sm text-red-600 font-bold flex items-start gap-3">
<i class="fas fa-exclamation-triangle mt-0.5 flex-shrink-0"></i>
<span>
Only the applicants who fill out the official form will have their applications reviewed.
Direct emails will be ignored.
</span>
</p>
</div>
<a href="https://docs.google.com/forms/d/e/1FAIpQLScUGAM_k4n7rT736ThnAe5jn2owVe2KFO7cib3fHsiL6I099g/viewform?usp=send_form"
target="_blank" rel="noopener noreferrer"
class="inline-block bg-blue-600 hover:bg-blue-700 text-white font-bold py-4 px-12 rounded-full shadow-xl shadow-blue-200 transform transition hover:-translate-y-1 active:scale-95 mb-4">
Apply Now
</a>
</div>
</div>
<!-- MAIN CONTENT -->
<div class="container mx-auto px-4 pt-8 max-w-6xl flex-grow">
<!-- HERO SECTION -->
<div id="hero-section" class="mb-10">
<div id="hero-overlay"></div>
<div id="hero-content" class="flex flex-col items-center">
<h1 class="text-5xl md:text-6xl font-extrabold mb-4 tracking-tight">DASH Lab</h1>
<p class="text-base md:text-xl font-light max-w-3xl mx-auto px-4 text-center">
Our Research | Computer Vision | Anomaly Detection | AI Security | DeepFakes | Machine Learning | Data Science Applications
</p>
</div>
<div id="hero-news-banner" class="flex justify-center items-center relative">
<div id="news-text" class="text-center w-full max-w-4xl px-4">
<!-- Injected by news.js -->
</div>
</div>
</div>
<!-- FEATURE BOXES -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16">
<!-- Data Driven -->
<div class="feature-box" id="dataDrivenFeatureBox">
<div class="satellite-graph-wrap feature-img">
<img src="img/dash_logo/satellite_v3.svg" alt="Satellite Icon" class="satellite-static-figure">
<div class="satellite-lottie-container" id="satelliteLottieContainer"></div>
</div>
<h3 class="feature-title">Data Science</h3>
<p class="feature-desc">
Innovating at the intersection of <strong>Applied Data Science</strong>, Data Engineering, and <strong>Satellite Systems</strong>.
</p>
</div>
<!-- A.I -->
<div class="feature-box" id="aiFeatureBox">
<div class="face-graph-wrap feature-img">
<img src="img/dash_logo/face_icon_3.svg" alt="Face Scan Icon" class="face-static-figure">
<div class="face-lottie-container" id="faceLottieContainer"></div>
</div>
<h3 class="feature-title">Artificial Intelligence</h3>
<p class="feature-desc">
Advancing the frontiers of <strong>DeepFake detection</strong> and high-performance <strong>Anomaly modeling</strong>.
</p>
</div>
<!-- Security & Privacy -->
<div class="feature-box" id="securityFeatureBox">
<div class="security-graph-wrap feature-img">
<img src="img/dash_logo/security_graph.svg" alt="Security Icon" class="security-static-figure">
<div class="security-lottie-container" id="securityLottieContainer"></div>
</div>
<h3 class="feature-title">Security & Privacy</h3>
<p class="feature-desc">
Protecting digital frontiers through <strong>Privacy Preservation</strong> and <strong>Secure Machine Learning</strong> architectures.
</p>
</div>
</div>
<!-- CAROUSEL -->
<div id="home-carousel" class="carousel">
<!-- Images injected by news.js -->
</div>
</div>
<!-- IMPROVED CALLOUT SECTION -->
<div class="container mx-auto px-4 max-w-6xl mb-16">
<div class="bg-gradient-to-br from-blue-50 to-white border border-blue-100 rounded-3xl p-8 md:p-12 shadow-xl shadow-blue-900/5 relative overflow-hidden">
<!-- Background Decorative Element -->
<div class="absolute -top-24 -right-24 w-64 h-64 bg-blue-100/50 rounded-full blur-3xl"></div>
<div class="flex flex-col md:flex-row items-center gap-10 relative z-10">
<!-- Image Column -->
<div class="w-full md:w-1/3 flex justify-center items-center">
<div class="relative group w-full">
<div class="absolute inset-0 bg-blue-200 rounded-2xl blur-xl opacity-0 group-hover:opacity-40 transition-opacity duration-500"></div>
<img loading="lazy"
src="img/dash_logo/DASH_LAB_LOGO.png"
onerror="this.src=getImg('img/dash_logo/DASH_LAB_LOGO.png')"
alt="DASH LAB Logo"
class="relative w-full h-auto object-contain drop-shadow-2xl transition-transform duration-500 group-hover:scale-105"
/>
</div>
</div>
<!-- Text Column -->
<div class="w-full md:w-2/3 text-center md:text-left">
<div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-100 text-blue-700 text-xs font-black uppercase tracking-widest mb-4">
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-blue-600"></span>
</span>
Now Recruiting
</div>
<h3 class="text-3xl md:text-4xl font-black text-gray-900 mb-4 leading-tight">
Shape the Future of <span class="text-blue-600">AI & Security</span> with us.
</h3>
<p class="text-lg text-gray-600 mb-6 leading-relaxed max-w-2xl">
We are looking for dedicated individuals whose research interests align with Computer Vision, Anomaly Detection, and Applied Data Science.
<strong>Coding proficiency and passion for research are highly valued.</strong>
</p>
<!-- IMPORTANT NOTICE IN RED -->
<div class="mb-8 p-4 bg-red-50 border-l-4 border-red-500 rounded-r-xl inline-block text-left">
<p class="text-sm md:text-base text-red-600 font-bold flex items-start gap-2">
<i class="fas fa-exclamation-triangle mt-1 flex-shrink-0"></i>
<span>
Only the applicants who fill out this form have their applications looked through.
Any emails sent directly will be ignored and only the Google Forms will be accepted.
</span>
</p>
</div>
<div class="flex flex-col sm:flex-row items-center gap-4 justify-center md:justify-start">
<a href="https://forms.gle/RYCUasAUbsFhJtyb6"
target="_blank"
rel="noopener noreferrer"
class="group flex items-center gap-3 bg-blue-600 hover:bg-blue-700 text-white font-bold py-4 px-8 rounded-2xl shadow-lg shadow-blue-200 transition-all active:scale-95">
<span>Open Application Form</span>
<i class="fas fa-arrow-right transition-transform group-hover:translate-x-1"></i>
</a>
<p class="text-sm text-gray-400 font-medium italic">
* Recommended for dedicated researchers
</p>
</div>
</div>
</div>
</div>
</div>
<!-- SIDE POSTER ADS: left = recruitment only; right = all other posters -->
<aside id="poster-ad-left"
class="poster-ad poster-ad--left"
aria-label="Recruitment poster">
<button type="button"
class="poster-ad-toggle"
id="poster-ad-toggle-left"
aria-expanded="false"
aria-controls="poster-ad-stack-left">
<i class="fas fa-user-plus" aria-hidden="true"></i>
<span class="poster-ad-toggle-label">Join Us</span>
</button>
<div class="poster-ad-rail">
<div id="poster-ad-stack-left" class="poster-ad-stack">
<div class="poster-ad-stack-header">
<span class="poster-ad-stack-title">Join Us</span>
<button type="button"
class="poster-ad-collapse"
data-poster-collapse
aria-label="Hide recruitment poster">
×
</button>
</div>
<button type="button"
class="poster-ad-trigger"
data-poster-id="recruiting"
aria-haspopup="dialog"
aria-controls="poster-ad-modal">
<span class="poster-ad-badge">Join Us</span>
<img src="img/posters/Lab_Infographics.png"
alt="DASH Lab recruiting infographic seeking PhD students"
class="poster-ad-thumb"
width="148"
height="197"
loading="lazy"
decoding="async"
fetchpriority="low">
<span class="poster-ad-hint">Click to view</span>
</button>
</div>
<div class="poster-ad-scroll-cue" hidden aria-hidden="true">
<span class="poster-ad-scroll-cue-grad"></span>
<span class="poster-ad-scroll-cue-more">
<i class="fas fa-chevron-down" aria-hidden="true"></i>
</span>
</div>
</div>
</aside>
<aside id="poster-ad-right"
class="poster-ad poster-ad--right"
aria-label="Lab poster advertisements">
<button type="button"
class="poster-ad-toggle"
id="poster-ad-toggle-right"
aria-expanded="false"
aria-controls="poster-ad-stack-right">
<i class="fas fa-images" aria-hidden="true"></i>
<span class="poster-ad-toggle-label">Posters</span>
<span class="poster-ad-toggle-count" aria-hidden="true">4</span>
</button>
<div class="poster-ad-rail">
<div id="poster-ad-stack-right" class="poster-ad-stack">
<div class="poster-ad-stack-header">
<span class="poster-ad-stack-title">Lab posters</span>
<button type="button"
class="poster-ad-collapse"
data-poster-collapse
aria-label="Hide poster thumbnails">
×
</button>
</div>
<button type="button"
class="poster-ad-trigger"
data-poster-id="securemachines"
aria-haspopup="dialog"
aria-controls="poster-ad-modal">
<span class="poster-ad-badge">Featured</span>
<img src="img/posters/poster_securemachineslab.png"
alt="Secure Machines Lab poster AI Forensics & Media Integrity"
class="poster-ad-thumb"
width="148"
height="197"
loading="lazy"
decoding="async"
fetchpriority="low">
<span class="poster-ad-hint">Click to view</span>
</button>
<button type="button"
class="poster-ad-trigger"
data-poster-id="dash"
aria-haspopup="dialog"
aria-controls="poster-ad-modal">
<span class="poster-ad-badge">DASH Lab</span>
<img src="img/posters/poster_dash.png"
alt="DASH Lab research overview poster"
class="poster-ad-thumb"
width="148"
height="197"
loading="lazy"
decoding="async"
fetchpriority="low">
<span class="poster-ad-hint">Click to view</span>
</button>
<button type="button"
class="poster-ad-trigger"
data-poster-id="skku-dash-2024"
aria-haspopup="dialog"
aria-controls="poster-ad-modal">
<span class="poster-ad-badge">2024</span>
<img src="img/posters/Poster_SKKU-DASH_LAB_2024.png"
alt="SKKU–DASH Lab 2024 research poster"
class="poster-ad-thumb"
width="148"
height="197"
loading="lazy"
decoding="async"
fetchpriority="low">
<span class="poster-ad-hint">Click to view</span>
</button>
<button type="button"
class="poster-ad-trigger"
data-poster-id="skku24"
aria-haspopup="dialog"
aria-controls="poster-ad-modal">
<span class="poster-ad-badge">2024</span>
<img src="img/posters/poster_skku24.png"
alt="DASH Lab 2024 projects poster"
class="poster-ad-thumb"
width="148"
height="197"
loading="lazy"
decoding="async"
fetchpriority="low">
<span class="poster-ad-hint">Click to view</span>
</button>
</div>
<div class="poster-ad-scroll-cue" hidden aria-hidden="true">
<span class="poster-ad-scroll-cue-grad"></span>
<span class="poster-ad-scroll-cue-more">
<i class="fas fa-chevron-down" aria-hidden="true"></i>
</span>
</div>
</div>
</aside>
<!-- SHARED POSTER DETAIL MODAL -->
<div id="poster-ad-modal" class="poster-ad-modal" role="dialog" aria-modal="true" aria-labelledby="poster-ad-title" hidden>
<div class="poster-ad-modal-backdrop" data-poster-close></div>
<div class="poster-ad-modal-panel">
<button type="button" class="poster-ad-modal-close close-modal" id="poster-ad-close" aria-label="Close poster details">×</button>
<div class="poster-ad-modal-grid">
<div class="poster-ad-modal-image-wrap">
<img src=""
alt=""
class="poster-ad-modal-image"
id="poster-ad-full-img">
</div>
<div class="poster-ad-modal-info">
<p id="poster-ad-eyebrow" class="poster-ad-modal-eyebrow"></p>
<h2 id="poster-ad-title" class="poster-ad-modal-title"></h2>
<p id="poster-ad-subtitle" class="poster-ad-modal-subtitle"></p>
<p id="poster-ad-tagline" class="poster-ad-modal-tagline"></p>
<p id="poster-ad-body" class="poster-ad-modal-body"></p>
<ul id="poster-ad-list" class="poster-ad-modal-list"></ul>
<a id="poster-ad-cta"
href="#"
class="poster-ad-modal-cta"
hidden>
<span id="poster-ad-cta-text"></span>
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
</a>
</div>
</div>
</div>
</div>
<!-- Footer is injected here by common.js -->
<!-- Page Specific Script -->
<script>
document.addEventListener('DOMContentLoaded', () => {
// Set the Hero Background Image
const heroSection = document.getElementById('hero-section');
if(heroSection) {
// Using getImg helper
heroSection.style.backgroundImage = `url(${getImg('/img/main_skku5.jpg')})`;
}
// Modal logic
if (!localStorage.getItem('dash_visited')) {
const modal = document.getElementById('join-modal');
if(modal) {
modal.style.display = 'flex';
// Trigger CSS transition if home-popup.active is defined in style.css
setTimeout(() => modal.classList.add('active'), 50);
localStorage.setItem('dash_visited', 'true');
}
}
// Poster ads: shared modal populated from catalog
const posterCatalog = {
recruiting: {
image: 'img/posters/Lab_Infographics.png',
imageAlt: 'DASH Lab recruiting infographic for PhD students',
eyebrow: 'Now recruiting',
title: 'Join DASH Lab',
subtitle: 'PhD students welcome',
tagline: 'We are looking for PhD students.',
bodyHtml: 'We are looking for people who are able to work with a team and have good problem solving skills. Research interests that align with Computer Vision, Anomaly Detection, and Applied Data Science are a strong fit. <strong>Coding proficiency and passion for research are highly valued.</strong> Only applicants who fill out the official Google Form will be reviewed direct emails are not accepted.',
list: [
'PhD student positions',
'Teamwork & problem-solving focus',
'Apply via the official Application Form'
],
ctaHref: 'https://forms.gle/RYCUasAUbsFhJtyb6',
ctaText: 'Open Application Form',
ctaExternal: true
},
securemachines: {
image: 'img/posters/poster_securemachineslab.png',
imageAlt: 'Full Secure Machines Lab poster',
eyebrow: 'Related initiative',
title: 'Secure Machines Lab',
subtitle: 'AI Forensics & Media Integrity',
tagline: 'Seeing isn’t proof.',
bodyHtml: 'Investigate deepfakes and verify digital media. Secure Machines Lab linked with DASH Lab works on image & video detection, multimedia news verification, and forensic inspection, under the theme <em>“Safer AI. Secure world.”</em>',
list: [
'Image & Video Detection',
'Multimedia News Verification',
'Forensic Inspection'
],
ctaHref: 'https://securemachineslab.com',
ctaText: 'Explore securemachineslab.com',
ctaExternal: true
},
dash: {
image: 'img/posters/poster_dash.png',
imageAlt: 'Full DASH Lab research overview poster',
eyebrow: 'Lab poster',
title: 'DASH Lab Research Overview',
subtitle: 'Data-driven AI Security & HCI',
tagline: 'Data · Machines · Humans',
bodyHtml: 'Overview poster for the Data-driven AI Security HCI Lab (DASH Lab) at Sungkyunkwan University, led by Prof. Simon S. Woo. The lab develops usable and secure technology after better understanding data, machines, and humans covering computer vision, anomaly detection, AI security, and deepfakes.',
list: [
'Deepfake detection',
'Object detection',
'Time-series & anomaly analysis',
'Benchmark datasets (e.g. VFP290K, FakeAVCeleb)'
],
ctaHref: 'Members.html',
ctaText: 'Meet the Members',
ctaExternal: false
},
'skku-dash-2024': {
image: 'img/posters/Poster_SKKU-DASH_LAB_2024.png',
imageAlt: 'Full SKKU–DASH Lab 2024 research poster',
eyebrow: 'Lab poster · 2024',
title: 'SKKU–DASH Lab 2024',
subtitle: 'Research overview',
tagline: 'Year overview poster',
bodyHtml: '2024 research overview poster for DASH Lab at Sungkyunkwan University. It summarizes recent work spanning machine unlearning, deepfake detection, diffusion-model security, time-series analysis, and vision-language models.',
list: [
'Machine unlearning',
'Deepfake detection',
'Diffusion models',
'Time-series analysis',
'Vision language models'
],
ctaHref: 'Projects.html',
ctaText: 'Browse Lab Projects',
ctaExternal: false
},
skku24: {
image: 'img/posters/poster_skku24.png',
imageAlt: 'Full DASH Lab 2024 projects poster',
eyebrow: 'Lab poster · 2024',
title: 'DASH Lab 2024 Projects',
subtitle: 'Year materials & research pillars',
tagline: 'Projects & partnerships',
bodyHtml: '2024 year materials from DASH Lab at Sungkyunkwan University, highlighting research pillars and project work including deepfake detection, diffusion-model security, machine unlearning, satellite time-series analysis, and digital human restoration alongside partner collaborations.',
list: [
'Deepfake detection & media integrity',
'Diffusion-model security',
'Machine unlearning',
'Satellite time-series analysis',
'Digital human restoration'
],
ctaHref: 'Professor.html',
ctaText: 'About the Professor',
ctaExternal: false
}
};
const posterModal = document.getElementById('poster-ad-modal');
const posterRails = Array.from(document.querySelectorAll('.poster-ad'));
const posterTriggers = document.querySelectorAll('.poster-ad-trigger[data-poster-id]');
const posterClose = document.getElementById('poster-ad-close');
const posterImg = document.getElementById('poster-ad-full-img');
const posterEyebrow = document.getElementById('poster-ad-eyebrow');
const posterTitle = document.getElementById('poster-ad-title');
const posterSubtitle = document.getElementById('poster-ad-subtitle');
const posterTagline = document.getElementById('poster-ad-tagline');
const posterBody = document.getElementById('poster-ad-body');
const posterList = document.getElementById('poster-ad-list');
const posterCta = document.getElementById('poster-ad-cta');
const posterCtaText = document.getElementById('poster-ad-cta-text');
let posterCloseTimer = null;
let activePosterTrigger = null;
const POSTER_FADE_START = 100;
const POSTER_FADE_END = 360;
const setPosterRailExpanded = (rail, expanded) => {
if (!rail) return;
const toggle = rail.querySelector('.poster-ad-toggle');
rail.classList.toggle('is-expanded', expanded);
if (toggle) toggle.setAttribute('aria-expanded', expanded ? 'true' : 'false');
if (expanded) {
requestAnimationFrame(() => updatePosterScrollCue(rail));
}
};
const collapseAllPosterRails = () => {
posterRails.forEach((rail) => setPosterRailExpanded(rail, false));
};
const updatePosterScrollCue = (rail) => {
const stack = rail.querySelector('.poster-ad-stack');
const cue = rail.querySelector('.poster-ad-scroll-cue');
if (!stack || !cue) return;
const stackVisible = stack.offsetParent !== null || getComputedStyle(stack).display !== 'none';
if (!stackVisible) {
cue.hidden = true;
rail.classList.remove('has-scroll-more');
return;
}
const overflow = stack.scrollHeight > stack.clientHeight + 6;
const atBottom = stack.scrollTop + stack.clientHeight >= stack.scrollHeight - 10;
const showCue = overflow && !atBottom;
cue.hidden = !showCue;
rail.classList.toggle('has-scroll-more', showCue);
};
const updateAllPosterScrollCues = () => {
posterRails.forEach(updatePosterScrollCue);
};
const updatePosterPageFade = () => {
const y = window.scrollY || document.documentElement.scrollTop || 0;
let opacity = 1;
if (y <= POSTER_FADE_START) opacity = 1;
else if (y >= POSTER_FADE_END) opacity = 0;
else opacity = 1 - ((y - POSTER_FADE_START) / (POSTER_FADE_END - POSTER_FADE_START));
const faded = opacity < 0.12;
posterRails.forEach((rail) => {
rail.style.opacity = String(opacity);
rail.classList.toggle('is-page-faded', faded);
if (faded) {
rail.style.pointerEvents = 'none';
if (rail.classList.contains('is-expanded')) {
setPosterRailExpanded(rail, false);
}
} else {
rail.style.pointerEvents = '';
}
});
};
const setPosterField = (el, value, useHtml = false) => {
if (!el) return;
const hasValue = !!(value && String(value).trim());
el.hidden = !hasValue;
if (!hasValue) {
el.textContent = '';
return;
}
if (useHtml) el.innerHTML = value;
else el.textContent = value;
};
const fillPosterModal = (poster) => {
if (!poster) return;
if (posterImg) {
posterImg.src = poster.image;
posterImg.alt = poster.imageAlt || poster.title || 'Poster';
}
setPosterField(posterEyebrow, poster.eyebrow);
setPosterField(posterTitle, poster.title);
setPosterField(posterSubtitle, poster.subtitle);
setPosterField(posterTagline, poster.tagline);
setPosterField(posterBody, poster.bodyHtml, true);
if (posterList) {
posterList.innerHTML = '';
const items = Array.isArray(poster.list) ? poster.list : [];
posterList.hidden = items.length === 0;
items.forEach((item) => {
const li = document.createElement('li');
li.textContent = item;
posterList.appendChild(li);
});
}
if (posterCta) {
if (poster.ctaHref && poster.ctaText) {
posterCta.hidden = false;
posterCta.href = poster.ctaHref;
if (poster.ctaExternal) {
posterCta.target = '_blank';
posterCta.rel = 'noopener noreferrer';
} else {
posterCta.removeAttribute('target');
posterCta.removeAttribute('rel');
}
if (posterCtaText) posterCtaText.textContent = poster.ctaText;
const icon = posterCta.querySelector('i');
if (icon) icon.style.display = poster.ctaExternal ? '' : 'none';
} else {
posterCta.hidden = true;
}
}
};
const openPosterModal = (trigger) => {
if (!posterModal || !trigger) return;
const poster = posterCatalog[trigger.getAttribute('data-poster-id')];
if (!poster) return;
activePosterTrigger = trigger;
fillPosterModal(poster);
collapseAllPosterRails();
if (posterCloseTimer) {
clearTimeout(posterCloseTimer);
posterCloseTimer = null;
}
posterModal.hidden = false;
void posterModal.offsetWidth;
posterModal.classList.add('active');
document.body.style.overflow = 'hidden';
if (posterClose) posterClose.focus();
};
const closePosterModal = () => {
if (!posterModal || posterModal.hidden) return;
posterModal.classList.remove('active');
document.body.style.overflow = '';
const finish = (e) => {
if (e && e.target !== posterModal) return;
posterModal.hidden = true;
posterModal.removeEventListener('transitionend', finish);
if (posterCloseTimer) {
clearTimeout(posterCloseTimer);
posterCloseTimer = null;
}
if (activePosterTrigger && activePosterTrigger.offsetParent !== null) {
activePosterTrigger.focus();
} else {
const fallbackToggle = document.querySelector('.poster-ad-toggle');
if (fallbackToggle) fallbackToggle.focus();
}
activePosterTrigger = null;
};
posterModal.addEventListener('transitionend', finish);
posterCloseTimer = setTimeout(() => finish({ target: posterModal }), 320);
};
posterTriggers.forEach((trigger) => {
trigger.addEventListener('click', () => openPosterModal(trigger));
});
if (posterClose) posterClose.addEventListener('click', closePosterModal);
if (posterModal) {
posterModal.querySelectorAll('[data-poster-close]').forEach(el => {
el.addEventListener('click', closePosterModal);
});
}
posterRails.forEach((rail) => {
const toggle = rail.querySelector('.poster-ad-toggle');
const stack = rail.querySelector('.poster-ad-stack');
if (toggle) {
toggle.addEventListener('click', () => {
setPosterRailExpanded(rail, !rail.classList.contains('is-expanded'));
});
}
rail.querySelectorAll('[data-poster-collapse]').forEach((btn) => {
btn.addEventListener('click', () => setPosterRailExpanded(rail, false));
});
if (stack) {
stack.addEventListener('scroll', () => updatePosterScrollCue(rail), { passive: true });
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && posterModal && !posterModal.hidden) {
closePosterModal();
return;
}
if (e.key === 'Escape') {
const openRail = posterRails.find((rail) => rail.classList.contains('is-expanded'));
if (openRail) setPosterRailExpanded(openRail, false);
}
});
window.addEventListener('scroll', updatePosterPageFade, { passive: true });
window.addEventListener('resize', () => {
updatePosterPageFade();
updateAllPosterScrollCues();
}, { passive: true });
updatePosterPageFade();
// Thumbs may load async and change overflow height
document.querySelectorAll('.poster-ad-thumb').forEach((img) => {
if (img.complete) return;
img.addEventListener('load', updateAllPosterScrollCues, { once: true });
});
window.addEventListener('load', updateAllPosterScrollCues);
requestAnimationFrame(updateAllPosterScrollCues);
// Feature-box Lottie: start after load / when near viewport (keeps animations, frees first paint)
const initFeatureLotties = () => {
if (typeof lottie === 'undefined') return;
const startFace = () => {
const aiBox = document.getElementById('aiFeatureBox');
const faceLottie = document.getElementById('faceLottieContainer');
if (!aiBox || !faceLottie || faceLottie.dataset.lottieReady) return;
faceLottie.dataset.lottieReady = '1';
fetch(getImg('img/dash_logo/face_anim.json'))
.then(r => r.ok ? r.json() : Promise.reject(new Error(r.status)))
.then(animationData => {
lottie.loadAnimation({
container: faceLottie,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: animationData
});
aiBox.classList.add('playing');
})
.catch(err => console.error('Face animation load failed:', err));
};
const startSecurity = () => {
const securityBox = document.getElementById('securityFeatureBox');
const securityLottie = document.getElementById('securityLottieContainer');
if (!securityBox || !securityLottie || securityLottie.dataset.lottieReady) return;
securityLottie.dataset.lottieReady = '1';
lottie.loadAnimation({
container: securityLottie,
renderer: 'svg',
loop: true,
autoplay: true,
path: getImg('img/dash_logo/security_graph_anim.json')
});
securityBox.classList.add('playing');
};
const startSatellite = () => {
const dataDrivenBox = document.getElementById('dataDrivenFeatureBox');
const satelliteLottie = document.getElementById('satelliteLottieContainer');
if (!dataDrivenBox || !satelliteLottie || satelliteLottie.dataset.lottieReady) return;
satelliteLottie.dataset.lottieReady = '1';
fetch(getImg('img/dash_logo/satellite_anim.json'))
.then(r => r.ok ? r.json() : Promise.reject(new Error(r.status)))
.then(animationData => {
lottie.loadAnimation({
container: satelliteLottie,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: animationData
});
dataDrivenBox.classList.add('playing');
})
.catch(err => console.error('Satellite animation load failed:', err));
};
const boxes = [
document.getElementById('dataDrivenFeatureBox'),
document.getElementById('aiFeatureBox'),
document.getElementById('securityFeatureBox')
].filter(Boolean);
const kickoff = () => {
startSatellite();
startFace();
startSecurity();
};
if ('IntersectionObserver' in window && boxes.length) {
const io = new IntersectionObserver((entries) => {
if (!entries.some((e) => e.isIntersecting)) return;
kickoff();
io.disconnect();
}, { rootMargin: '120px 0px', threshold: 0.05 });
boxes.forEach((el) => io.observe(el));
} else {
kickoff();
}
};
if (document.readyState === 'complete') {
setTimeout(initFeatureLotties, 0);
} else {
window.addEventListener('load', initFeatureLotties, { once: true });
}
});
</script>
</body>
</html>