PageSourceSearch

https://www.acafafrica.org/

html acafafrica.org collected 2026-10-07 19:53:40 UTC 46,401 bytes, 824 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5    <meta charset="UTF-8">
6    <meta name="viewport" content="width=device-width, initial-scale=1.0">
7    <title>Africa Coalition For Academic Freedom (ACAF) - Defending Intellectual Freedom</title>
8    <meta name="description"
9        content="Promoting, defending, and advancing academic freedom, institutional autonomy, and freedom of expression in higher education across Africa.">
10    <link rel="stylesheet" href="assets/css/style.css">
11    <link rel="icon" type="image/png" href="assets/images/ACAF_logo.png">
12    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
13    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@splidejs/[email protected]/dist/css/splide.min.css">
14    
14<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
14
15    <style>
16        /* ── Latest Insights popup ─────────────────────────────── */
17        .hi-overlay {
18            display: none;
19            position: fixed;
20            inset: 0;
21            background: rgba(0,0,0,.55);
22            z-index: 9000;
23            align-items: flex-start;
24            justify-content: center;
25            padding: 2rem 1rem;
26            overflow-y: auto;
27        }
28        .hi-overlay.open { display: flex; }
29        .hi-box {
30            background: #fff;
31            border-radius: 12px;
32            width: 100%;
33            max-width: 680px;
34            margin: auto;
35            box-shadow: 0 20px 60px rgba(0,0,0,.25);
36            position: relative;
37            display: flex;
38            flex-direction: column;
39            overflow: hidden;
40        }
41        .hi-cover { width: 100%; max-height: 320px; overflow: hidden; }
42        .hi-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
43        .hi-close {
44            position: absolute;
45            top: .75rem; right: .75rem;
46            width: 34px; height: 34px;
47            border-radius: 50%;
48            background: rgba(15,23,42,.7);
49            color: #fff;
50            border: none;
51            cursor: pointer;
52            display: flex; align-items: center; justify-content: center;
53            z-index: 2;
54        }
55        .hi-close:hover { background: rgba(15,23,42,.9); }
56        .hi-body { padding: 1.5rem 1.75rem; }
57        .hi-meta-line { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-bottom: .75rem; }
58        .hi-badge {
59            display: inline-block;
60            padding: .25rem .75rem;
61            border-radius: 999px;
62            font-size: .72rem;
63            font-weight: 700;
64            letter-spacing: .04em;
65            text-transform: uppercase;
66            background: var(--color-accent);
67            color: var(--color-primary);
68        }
69        .hi-date { font-size: .85rem; color: var(--color-text-light); }
70        .hi-title { font-size: 1.45rem; color: var(--color-primary); margin: 0 0 1rem; line-height: 1.3; }
71        .hi-desc { font-size: .95rem; line-height: 1.75; color: var(--color-text-main); white-space: pre-line; }
72        .hi-desc p { margin-bottom: 1rem; white-space: normal; }
73        .hi-desc p:last-child { margin-bottom: 0; }
74        .hi-section-title {
75            font-size: .72rem;
76            font-weight: 700;
77            text-transform: uppercase;
78            letter-spacing: .07em;
79            color: var(--color-text-light);
80            margin: 1.25rem 0 .5rem;
81        }
82        .hi-links, .hi-tags { display: flex; flex-wrap: wrap; gap: .5rem; }
83        .hi-link-btn {
84            display: inline-flex;
85            align-items: center;
86            gap: .45rem;
87            padding: .55rem 1rem;
88            border: 1px solid var(--color-border);
89            border-radius: 6px;
90            background: #fff;
91            color: var(--color-primary);
92            font-size: .85rem;
93            font-weight: 500;
94            text-decoration: none;
95        }
96        .hi-link-btn:hover { background: var(--color-light); border-color: var(--color-primary); }
97        .hi-tag { padding: .3rem .7rem; border-radius: 999px; background: var(--color-light); font-size: .8rem; color: var(--color-text-main); }
98        .hi-meta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .85rem; }
99        .hi-meta-item label { display: block; font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-light); margin-bottom: .15rem; }
100        .hi-meta-item span { font-size: .92rem; color: var(--color-text-main); }
101        .hi-sources { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .4rem; }
102        .hi-video { position: relative; padding-top: 56.25%; background: #000; }
103        .hi-video iframe,
104        .hi-video video { position: absolute; inset: 0; width: 100%; height: 100%; border: none; }
105        /* Reels and shorts are 9:16 — a fixed height with the width derived from
106           it, so the video is never cropped to a strip. */
107        .hi-video.is-portrait {
108            padding-top: 0;
109            height: min(70vh, 640px);
110            max-width: calc(min(70vh, 640px) * 9 / 16);
111            margin: 0 auto;
112        }
113        /* Instagram and X serve a self-sizing card, not a bare player. */
114        .hi-video.is-social { padding-top: 0; height: min(70vh, 640px); background: #fff; }
115        .hi-video.is-social iframe { position: static; height: 100%; background: #fff; }
116        .hi-footer {
117            padding: 1rem 1.75rem 1.25rem;
118            border-top: 1px solid var(--color-border);
119            display: flex;
120            justify-content: flex-end;
121            gap: .5rem;
122            flex-wrap: wrap;
123        }
124        @media (max-width: 560px) {
125            .hi-meta-grid { grid-template-columns: 1fr; }
126        }
127    </style>
128    <style>
129    /* Admin-controlled homepage popup */
130    .home-popup-overlay {
131        position: fixed; inset: 0; z-index: 9500;
132        background: rgba(15, 23, 42, 0.6);
133        display: none; align-items: center; justify-content: center;
134        padding: 1rem; opacity: 0; transition: opacity .25s ease;
135    }
136    .home-popup-overlay.open { display: flex; opacity: 1; }
137    .home-popup-card {
138        background: #fff; border-radius: 14px; overflow: hidden;
139        width: 100%; max-width: 440px; position: relative;
140        box-shadow: 0 24px 70px rgba(0,0,0,.35);
141        transform: translateY(12px) scale(.98); transition: transform .25s ease;
142        max-height: calc(100vh - 2rem); display: flex; flex-direction: column;
143    }
144    .home-popup-overlay.open .home-popup-card { transform: translateY(0) scale(1); }
145    .home-popup-close {
146        position: absolute; top: .6rem; right: .6rem; z-index: 2;
147        width: 34px; height: 34px; border-radius: 50%; border: none; cursor: pointer;
148        background: rgba(15,23,42,.55); color: #fff; font-size: 1rem;
149        display: inline-flex; align-items: center; justify-content: center; transition: background .18s;
150    }
151    .home-popup-close:hover { background: rgba(15,23,42,.8); }
152    .home-popup-media { width: 100%; aspect-ratio: 16/9; object-fit: cover; display: block; background: #E2E8F0; }
153    .home-popup-media-fallback {
154        width: 100%; aspect-ratio: 16/9; display: flex; align-items: center; justify-content: center;
155        background: linear-gradient(135deg, var(--color-primary) 0%, #1e3a5f 100%); color: #fff; font-size: 2.4rem;
156    }
157    .home-popup-body { padding: 1.25rem 1.4rem 1.5rem; overflow-y: auto; }
158    .home-popup-badge {
159        display: inline-block; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
160        font-weight: 700; color: var(--color-primary); background: #EFF3FF;
161        padding: .2rem .6rem; border-radius: 999px; margin-bottom: .6rem;
162    }
163    .home-popup-title { font-size: 1.25rem; line-height: 1.3; margin: 0 0 .6rem; color: #0F172A; }
164    .home-popup-desc {
165        font-size: .9rem; line-height: 1.6; color: var(--color-text-light); margin: 0 0 1.1rem;
166        display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
167    }
168    .home-popup-cta {
169        display: inline-flex; align-items: center; background: var(--color-primary); color: #fff;
170        padding: .65rem 1.25rem; border-radius: 8px; font-weight: 600; font-size: .9rem;
171        text-decoration: none; transition: background .18s;
172    }
173    .home-popup-cta:hover { background: #1e3a5f; }
174    @media (max-width: 480px) { .home-popup-card { max-width: 100%; } .home-popup-title { font-size: 1.1rem; } }
175    </style>
176</head>
177
178<body>
179
180    <!-- Header / Navigation -->
181    <header>
182        <div class="container nav-container">
183            <a href="index.html" class="logo">
184                <img src="assets/images/ACAF_logo.png" alt="ACAF Logo">
185                <span class="logo-text">ACAF</span>
186            </a>
187            <!-- Socials removed -->
188            <button class="mobile-menu-btn">☰</button>
189            <nav class="nav-links">
190                <a href="index.html" class="nav-link active" data-i18n="nav.home">Home</a>
191                <div class="nav-dropdown">
192                <a href="about.html" class="nav-link" data-i18n="nav.about">About Us <span class="dropdown-arrow">&#9660;</span></a>
193                <div class="dropdown-menu">
194                    <a href="about.html" class="dropdown-item">About ACAF</a>
195                    <a href="work.html" class="dropdown-item">Our Work</a>
196                    <a href="teams.html" class="dropdown-item">Our Team</a>
197                </div>
198            </div>
199                <a href="monitoring.html" class="nav-link" data-i18n="nav.monitoring">Monitoring</a>
200                <a href="news.html" class="nav-link" data-i18n="nav.news">News & Events</a>
201                <a href="publications.html" class="nav-link">Publications &amp; Resources</a>
202                <a href="newsletter.html" class="nav-link">Newsletter</a>
203                <a href="gallery.html" class="nav-link">Gallery</a>
204                <a href="contact.html" class="nav-link" data-i18n="nav.contact">Contact</a>
205                <a href="get-involved.html" class="nav-link" data-i18n="nav.get-involved">Get Involved</a>
206            </nav>
207        </div>
208    </header>
209
210    <!-- Hero Section -->
211    <section class="hero">
212        <div class="container hero-inner">
213
214            <!-- Left: text -->
215            <div class="hero-text hero-full fade-in" id="heroText">
216                <h1 data-i18n="home.hero.title">Defending Academic Freedom Across Africa</h1>
217                <p data-i18n="home.hero.text">We are dedicated to promoting critical enquiry and the pursuit of truth in Africa's higher education and other research institutions to ensure that knowledge can flourish to promote learning and societal well-being without fear or restriction.</p>
218                <div class="hero-btns">
219                    <a href="get-involved.html" class="btn btn-primary"
220                        style="background-color:var(--color-accent);border:none;color:var(--color-primary);" data-i18n="home.hero.member">Become a Member</a>
221                </div>
222            </div>
223
224            <!-- Right: carousel (revealed once posts load) -->
225            <div class="hero-carousel-wrap" id="heroCarouselWrap" style="display:none;" aria-hidden="true">
226                <div class="hero-carousel-label">
227                    <span>Latest News &amp; Events</span>
228                    <a href="news.html">View All <i class="fas fa-arrow-right" style="font-size:0.65rem;"></i></a>
229                </div>
230                <!-- loading skeleton shown while fetching -->
231                <div class="hero-carousel-skeleton" id="heroSkeleton" aria-hidden="true">
232                    <i class="fas fa-spinner fa-spin"></i>
233                </div>
234                <div id="heroSplide" class="splide" aria-label="Latest news and events" style="display:none;">
235                    <div class="splide__track">
236                        <ul class="splide__list" id="heroSlidesList" data-i18n-dynamic></ul>
237                    </div>
238                </div>
239            </div>
240
241        </div>
242    </section>
243
244    <!-- Introduction / Mission -->
245    <section class="section bg-white">
246        <div class="container grid grid-2 fade-in" style="align-items: center;">
247            <div>
248                <h4 class="text-accent" style="text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.9rem;" data-i18n="home.who.label">Who We Are</h4>
249                <h2 data-i18n="home.who.title">Safeguarding the Future of African Education and Democracy</h2>
250                <p data-i18n="home.who.text1">Academic freedom is the cornerstone of a vibrant, innovative, and democratic society. Yet, across the continent, institutions, scholars and students face increasing pressures that threaten their ability to teach, research, produce/disseminate knowledge, speak truth to power and challenge orthodoxy.</p>
251                <p data-i18n="home.who.text2">ACAF stands as a guardian of these fundamental principles, uniting educators, researchers, students, policymakers and civil society to build resilient academic ecosystems.</p>
252                <a href="about.html" class="read-more" data-i18n="home.who.link">Read Our Full Mission →</a>
253            </div>
254            <div
255                style="height: 400px; border-radius: 8px; display: flex; align-items: center; justify-content: center;">
256                <!-- Placeholder for an image -->
257                <img src="assets/images/web_image_1.jpg" loading="lazy" class="img-subtle-border"
258                    style="width: 100%; height: 100%; object-fit: cover; border-radius: 8px;"
259                    alt="Diverse group of African scholars in discussion">
260            </div>
261        </div>
262    </section>
263
264    <!-- Key Focus Areas -->
265    <section class="section bg-light">
266        <div class="container">
267            <div class="text-center fade-in" style="max-width: 800px; margin: 0 auto 4rem;">
268                <h2 data-i18n="home.pillars.title">Our Strategic Pillars</h2>
269                <p data-i18n="home.pillars.text">We approach the defense of academic freedom through four interconnected pillars of action.</p>
270            </div>
271
272            <div class="grid grid-4 fade-in">
273                <div class="card">
274                    <div class="card-icon">👁️</div>
275                    <h3 data-i18n="home.pillars.monitoring">Monitoring</h3>
276                    <p class="text-sm" data-i18n="home.pillars.monitoring.text">Tracking and documenting violations of academic freedom to ensure accountability
277                        and transparency.</p>
278                </div>
279                <div class="card">
280                    <div class="card-icon">📢</div>
281                    <h3 data-i18n="home.pillars.advocacy">Advocacy</h3>
282                    <p class="text-sm" data-i18n="home.pillars.advocacy.text">
282Engaging with governments and international bodies to shape policies that protect
283                        scholars.</p>
284                </div>
285                <div class="card">
286                    <div class="card-icon">📚</div>
287                    <h3 data-i18n="home.pillars.research">Research</h3>
288                    <p class="text-sm" data-i18n="home.pillars.research.text">Producing data-driven reports and analyses on the state of higher education in
289                        Africa.</p>
290                </div>
291                <div class="card">
292                    <div class="card-icon">🤝</div>
293                    <h3 data-i18n="home.pillars.capacity">Capacity Building</h3>
294                    <p class="text-sm" data-i18n="home.pillars.capacity.text">Empowering institutions and individuals with the tools to defend their rights.
295                    </p>
296                </div>
297            </div>
298        </div>
299    </section>
300
301    <!-- Latest News / Publications -->
302    <section class="section bg-white">
303        <div class="container">
304            <div class="flex justify-between items-center fade-in" style="margin-bottom: 3rem;">
305                <h2 data-i18n="home.insights.title">Latest Insights</h2>
306                <a href="publications.html" class="btn btn-outline btn-sm" data-i18n="home.insights.view-all">View All Publications</a>
307            </div>
308
309            <div class="grid grid-3 fade-in" id="insightsGrid" data-i18n-dynamic>
310                <!-- loading skeleton shown while fetching -->
311                <div class="text-center" id="insightsLoading" style="grid-column:1/-1;padding:2rem;color:#94A3B8;">
312                    <i class="fas fa-spinner fa-spin"></i>
313                </div>
314            </div>
315            <p class="text-center" id="insightsEmpty" style="display:none;color:#94A3B8;">No publications to show yet. Check back soon.</p>
316        </div>
317    </section>
318
319    <!-- CTA Section -->
320    <section class="section bg-dark text-center">
321        <div class="container fade-in">
322            <h2 style="color: white; margin-bottom: 1.5rem;" data-i18n="home.cta.title">Stand With Us</h2>
323            <p style="color: #cbd5e1; max-width: 600px; margin: 0 auto 2.5rem;" data-i18n="home.cta.text">Your support enables us to provide legal
324                assistance, publish critical research, and advocate for the rights of scholars everywhere.</p>
325            <div class="flex gap-1 justify-center">
326                <a href="get-involved.html" class="btn btn-primary"
327                    style="background-color: var(--color-accent); color: var(--color-primary);" data-i18n="home.cta.join">Join ACAF</a>
328                <a href="donate.html" class="btn btn-light" data-i18n="home.cta.donate">Make a Donation</a>
329            </div>
330        </div>
331    </section>
332
333    <!-- Latest Insights popup -->
334    <div class="hi-overlay" id="hiModal" onclick="closeInsight(event)" data-i18n-dynamic>
335        <div class="hi-box" id="hiModalContent"></div>
336    </div>
337
338    <!-- Admin-controlled featured popup (assets/js/acaf-data.js -> home_popup) -->
339    <div class="home-popup-overlay" id="homePopup" aria-hidden="true">
340        <div class="home-popup-card" role="dialog" aria-modal="true" aria-labelledby="homePopupTitle">
341            <button type="button" class="home-popup-close" id="homePopupClose" aria-label="Close">
342                <i class="fas fa-times"></i>
343            </button>
344            <div id="homePopupMedia"></div>
345            <div class="home-popup-body">
346                <span class="home-popup-badge" id="homePopupBadge"></span>
347                <h3 class="home-popup-title" id="homePopupTitle"></h3>
348                <p class="home-popup-desc" id="homePopupDesc"></p>
349                <a class="home-popup-cta" id="homePopupCta" href="#">
350                    <span id="homePopupCtaText">View</span> <i class="fas fa-arrow-right" style="margin-left:.4rem;"></i>
351                </a>
352            </div>
353        </div>
354    </div>
355
356    <!-- Footer -->
357    <footer>
358        <div class="container">
359            <div class="footer-grid">
360                <div class="footer-brand">
361                    <div class="footer-brand-header">
362                        <img src="assets/images/ACAF_logo.png" alt="ACAF Logo">
363                        <span>ACAF</span>
364                    </div>
365                    <p class="footer-full-name" data-i18n="footer.org-name">Africa Coalition for Academic Freedom</p>
366                    <p data-i18n="footer.tagline">Defending academic freedom to advance democracy, science and sustainable development in Africa</p>
367                </div>
368                <div class="footer-col">
369                    <h4 data-i18n="footer.quick-links">Quick Links</h4>
370                    <ul class="footer-links">
371                        <li><a href="about.html" data-i18n="nav.about">About Us</a></li>
372                        <li><a href="contact.html" data-i18n="nav.contact">Contact</a></li>
373                    </ul>
374                </div>
375                <div class="footer-col">
376                    <h4 data-i18n="footer.contact">Contact</h4>
377                    <ul class="footer-links">
378                        <li><a href="mailto:[email protected]">[email protected]</a></li>
379                        <li>CDD – Ghana<br>No. 95 Nortei Ababio Loop<br>North Airport Residential Area</li>
380                        <li>+233(0) 25 612 1111</li>
381                    </ul>
382                </div>
383                <div class="footer-col">
384                    <h4 data-i18n="footer.stay-informed">Stay Informed</h4>
385                    <p style="font-size: 0.875rem; color: #94A3B8; margin-bottom: 1rem;" data-i18n="footer.newsletter-text">Subscribe to our periodic
386                        newsletter.</p>
387                    <form class="newsletter-form">
388                        <input type="email" placeholder="Your email" data-i18n-placeholder="footer.email-placeholder">
389                        <button type="submit" class="btn btn-primary btn-sm"
390                            style="background-color: var(--color-accent); border:none; color: var(--color-primary);" data-i18n="footer.go">Go</button>
391                    </form>
392                    <div style="margin-top: 1.5rem;">
393                        <div class="flex gap-1" style="font-size: 1.2rem;">
394                            <a href="https://web.facebook.com/people/Africa-Coalition-for-Academic-Freedom-ACAF/61567221721200/"
395                                target="_blank" style="color: #94A3B8;"><i class="fab fa-facebook"></i></a>
396                            <a href="https://x.com/acafafrica" target="_blank" style="color: #94A3B8;"><i
397                                    class="fab fa-x-twitter"></i></a>
398                            <a href="https://www.linkedin.com/company/africa-coalition-for-academic-freedom/"
399                                target="_blank" style="color: #94A3B8;"><i class="fab fa-linkedin"></i></a>
400                            <a href="https://www.youtube.com/@acafafrica" target="_blank" style="color: #94A3B8;"><i
401                                    class="fab fa-youtube"></i></a>
402                        </div>
403                    </div>
404                </div>
405            </div>
406            <div class="footer-bottom">
407                <p data-i18n="footer.copyright">&copy; 2026 Africa Coalition For Academic Freedom. All rights reserved.</p>
408            </div>
409        </div>
410    </footer>
411
412    
412<script src="assets/lang/translations.js"></script>
412
413    
413<script src="assets/js/i18n-dynamic.js?v=1"></script>
413
414    
414<script src="assets/js/i18n.js?v=2"></script>
414
415    
415<script src="assets/js/main.js"></script>
415
416    
416<script src="assets/js/analytics.js?v=1" defer></script>
416
417    
417<script src="assets/js/acaf-data.js?v=13"></script>
417
418    
418<script src="https://cdn.jsdelivr.net/npm/@splidejs/[email protected]/dist/js/splide.min.js"></script>
418
419    
419<script>
420    (function () {
421        'use strict';
422
423        function badgeClass(category) {
424            if (!category) return 'hero-badge-default';
425            const k = category.toLowerCase().replace(/\s+/g, '-');
426            if (k === 'news')          return 'hero-badge-news';
427            if (k === 'event')         return 'hero-badge-event';
428            if (k === 'press-release') return 'hero-badge-press-release';
429            if (k === 'blog')          return 'hero-badge-blog';
430            return 'hero-badge-default';
431        }
432
433        function formatDate(str) {
434            if (!str) return '';
435            try {
436                return new Date(str).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
437            } catch (e) { return ''; }
438        }
439
440        function truncate(str, max) {
441            if (!str) return '';
442            return str.length > max ? str.slice(0, max).trimEnd() + '…' : str;
443        }
444
445        function escHtml(str) {
446            if (!str) return '';
447            return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
448        }
449
450        function buildSlide(post) {
451            const url  = 'news-post?id=' + encodeURIComponent(post.id);
452            const date = formatDate(post.publishedAt);
453            const title = truncate(post.title || '', 80);
454            const badge = badgeClass(post.category);
455
456            const imgHtml = post.imageUrl
457                ? `<img src="${escHtml(post.imageUrl)}" alt="${escHtml(post.title)}" loading="lazy">`
458                : `<i class="fas fa-newspaper hero-slide-img-icon" aria-hidden="true"></i>`;
459
460            return `
461            <li class="splide__slide">
462                <a class="hero-slide-card" href="${escHtml(url)}" aria-label="${escHtml(post.title)}">
463                    <div class="hero-slide-img">${imgHtml}</div>
464                    <div class="hero-slide-body">
465                        <div class="hero-slide-meta">
466                            <span class="hero-slide-badge ${badge}">${escHtml(post.category || 'Post')}</span>
467                            ${date ? `<span class="hero-slide-date">${escHtml(date)}</span>` : ''}
468                        </div>
469                        <h3 class="hero-slide-title">${escHtml(title)}</h3>
470                        <span class="hero-slide-readmore" tabindex="-1" aria-hidden="true">
471                            Read More <i class="fas fa-arrow-right"></i>
472                        </span>
473                    </div>
474                </a>
475            </li>`;
476        }
477
478        // "Latest Insights" cards are mixed content chosen by the admin
479        // (assets/js/acaf-data.js → getHomeInsights). Each card links directly to
480        // its own detail view (full post, publication modal, incident, gallery
481        // popup, or newsletter download).
482        function buildInsightCard(item) {
483            const url    = item.href || '#';
484            const target = item.external ? ' target="_blank" rel="noopener noreferrer"' : '';
485            const date   = formatDate(item.date);
486            const meta   = [item.label, date].filter(Boolean).join(' • ');
487            const media  = item.imageUrl
488                ? `<a href="${escHtml(url)}"${target} style="display:block;height:200px;margin:-2rem -2rem 1.5rem -2rem;border-radius:8px 8px 0 0;overflow:hidden;"><img src="${escHtml(item.imageUrl)}" alt="" loading="lazy" style="width:100%;height:100%;object-fit:cover;"></a>`
489                : `<a href="${escHtml(url)}"${target} style="height:200px;background:linear-gradient(135deg,#f0f4ff 0%,#e8f0fe 100%);margin:-2rem -2rem 1.5rem -2rem;border-radius:8px 8px 0 0;display:flex;align-items:center;justify-content:center;"><i class="fas ${item.icon || 'fa-file-alt'}" style="font-size:2.5rem;color:var(--color-primary);"></i></a>`;
490            return `
491            <article class="card post-preview" data-insight data-type="${escHtml(item.type)}" data-id="${escHtml(item.id)}" style="cursor:pointer;">
492                ${media}
493                <div class="post-meta">${escHtml(meta)}</div>
494                <h3 class="post-title"><a href="${escHtml(url)}"${target}>${escHtml(item.title || '')}</a></h3>
495                <p>
495${escHtml(truncate(item.description || '', 140))}</p>
496                <a href="${escHtml(url)}"${target} class="read-more">Read More <i class="fas fa-arrow-right"></i></a>
497            </article>`;
498        }
499
500        function renderInsights(items) {
501            const gridEl    = document.getElementById('insightsGrid');
502            const loadingEl = document.getElementById('insightsLoading');
503            const emptyEl   = document.getElementById('insightsEmpty');
504            if (!gridEl) return;
505            if (loadingEl) loadingEl.remove();
506            if (!items.length) {
507                gridEl.style.display = 'none';
508                if (emptyEl) emptyEl.style.display = '';
509                return;
510            }
511            gridEl.innerHTML = items.slice(0, 3).map(buildInsightCard).join('');
512
513            // Open the clicked card in a popup ON the homepage instead of
514            // navigating away. The card's links remain as a no-JS fallback, and
515            // modifier-clicks still open the detail page in a new tab.
516            if (!gridEl.dataset.insightBound) {
517                gridEl.dataset.insightBound = '1';
518                gridEl.addEventListener('click', (e) => {
519                    if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;
520                    const card = e.target.closest('[data-insight]');
521                    if (!card) return;
522                    e.preventDefault();
523                    openInsight(card.dataset.type, card.dataset.id);
524                });
525            }
526        }
527
528        // ── Homepage "Latest Insights" popup ──────────────────────────────────
529        function paragraphs(text) {
530            const parts = String(text || '').split(/\n\n+/).map(p => p.trim()).filter(Boolean);
531            if (!parts.length) return escHtml(text || '');
532            return parts.map(p => `<p>${escHtml(p).replace(/\n/g, '<br>')}</p>`).join('');
533        }
534
535        function linkBtns(links) {
536            if (!links || !links.length) return '';
537            return `<div class="hi-section-title">Related Links</div>
538                <div class="hi-links">${links.map(l => {
539                    const u = ACAF.sanitizeUrl(l.url);
540                    return `<a class="hi-link-btn" href="${escHtml(u)}" target="_blank" rel="noopener noreferrer"><i class="fas fa-external-link-alt"></i> ${escHtml(l.title || u)}</a>`;
541                }).join('')}</div>`;
542        }
543
544        // Returns { cover, badge, date, title, body, footer } for the open modal.
545        function renderInsightDetail(type, rec) {
546            const close = `<button class="btn btn-outline btn-sm" onclick="closeInsightDirect()">Close</button>`;
547            const coverImg = (src) => src
548                ? `<div class="hi-cover"><img src="${escHtml(src)}" alt=""></div>` : '';
549
550            if (type === 'news') {
551                const links = (Array.isArray(rec.links) && rec.links.length)
552                    ? rec.links.filter(l => l && l.url)
553                    : (rec.linkUrl ? [{ title: 'View Full Article', url: rec.linkUrl }] : []);
554                return {
555                    cover: coverImg(rec.imageUrl), badge: rec.category || 'News & Events',
556                    date: formatDate(rec.publishedAt), title: rec.title,
557                    body: `<div class="hi-desc">${paragraphs(rec.content || rec.summary)}</div>${linkBtns(links)}`,
558                    footer: `<a class="btn btn-outline btn-sm" href="news-post?id=${encodeURIComponent(rec.id)}"><i class="fas fa-external-link-alt" style="margin-right:.35rem;"></i>Open full page</a>${close}`,
559                };
560            }
561
562            if (type === 'publications') {
563                if (rec.type === 'video') {
564                    const v = ACAF.resolveVideoEmbed(rec);
565                    let embed = '';
566                    let watch = '';
567                    if (v.kind === 'iframe') {
568                        embed = `<div class="hi-video is-${escHtml(v.layout)}"><iframe src="${escHtml(v.src)}" allowfullscreen allow="autoplay; encrypted-media;
568 picture-in-picture" title="${escHtml(rec.title || 'Video')}"></iframe></div>`;
569                    } else if (v.kind === 'file') {
570                        embed = `<div class="hi-video is-${escHtml(v.layout)}"><video src="${escHtml(v.src)}" controls preload="metadata"${v.thumbnail ? ` poster="${escHtml(v.thumbnail)}"` : ''}></video></div>`;
571                    } else if (v.kind === 'link') {
572                        // Platform blocks embedding — send viewers to the source.
573                        embed = v.thumbnail ? coverImg(v.thumbnail) : '';
574                        watch = `<a class="btn btn-primary btn-sm" style="background:var(--color-primary);border:none;color:#fff;" href="${escHtml(v.url)}" target="_blank" rel="noopener noreferrer"><i class="${escHtml(v.icon)}" style="margin-right:.35rem;"></i>${escHtml(v.cta)}</a>`;
575                    }
576                    return {
577                        cover: '', badge: 'Video', date: formatDate(rec.publishedAt), title: rec.title,
578                        body: `${embed}${rec.description ? `<div class="hi-desc" style="margin-top:1rem;">${escHtml(rec.description)}</div>` : ''}`,
579                        footer: `${watch}${close}`,
580                    };
581                }
582                const docs = ACAF.getDocsList(rec.documents);
583                let dl = '';
584                if (docs.length === 1) {
585                    dl = `<a class="btn btn-primary btn-sm" style="background:var(--color-primary);border:none;color:#fff;" href="${escHtml(docs[0].url)}" target="_blank" rel="noopener"><i class="fas fa-download" style="margin-right:.35rem;"></i>Download</a>`;
586                } else if (docs.length > 1) {
587                    dl = docs.map(d => `<a class="btn btn-outline btn-sm" href="${escHtml(d.url)}" target="_blank" rel="noopener"><i class="fas fa-file-alt" style="margin-right:.35rem;"></i>${escHtml(d.label)}</a>`).join('');
588                }
589                const author = rec.author ? `<p class="notranslate" style="font-size:.85rem;color:var(--color-text-light);margin:0 0 .75rem;"><i class="fas fa-user" style="margin-right:.35rem;"></i>${escHtml(rec.author)}</p>` : '';
590                return {
591                    cover: coverImg(rec.thumbnailUrl), badge: 'Publication', date: formatDate(rec.publishedAt), title: rec.title,
592                    body: `${author}<div class="hi-desc">${escHtml(rec.description || 'No description provided.')}</div>`,
593                    footer: `${dl}${close}`,
594                };
595            }
596
597            if (type === 'incidents') {
598                const meta = [
599                    ['Date', formatDate(rec.date)], ['Country', rec.country],
600                    ['Institution', rec.institution], ['City / Location', rec.city],
601                ].filter(m => m[1]).map(m =>
601 `<div class="hi-meta-item"><label>${m[0]}</label><span>${escHtml(m[1])}</span></div>`).join('');
602                const types = (rec.incidentTypes || []).map(t => `<span class="hi-tag">${escHtml(t)}</span>`).join('');
603                const sources = (rec.sources || []).filter(s => s && s.url).map(s =>
604                    `<li><a class="hi-link-btn" href="${escHtml(ACAF.sanitizeUrl(s.url))}" target="_blank" rel="noopener"><i class="fas fa-external-link-alt"></i> ${escHtml(s.title || s.url)}</a></li>`).join('');
605                return {
606                    cover: '', badge: 'Incident', date: formatDate(rec.date), title: rec.title,
607                    body: `${meta ? `<div class="hi-meta-grid">${meta}</div>` : ''}
608                        ${types ? `<div class="hi-section-title">Type of Incident</div><div class="hi-tags">${types}</div>` : ''}
609                        <div class="hi-section-title">Description</div>
610                        <div class="hi-desc">${rec.description || 'No description provided.'}</div>
611                        ${sources ? `<div class="hi-section-title">Sources</div><ul class="hi-sources">${sources}</ul>` : ''}`,
612                    footer: `<a class="btn btn-outline btn-sm" href="monitoring.html#incident=${encodeURIComponent(rec.id)}"><i class="fas fa-external-link-alt" style="margin-right:.35rem;"></i>Open in Monitoring</a>${close}`,
613                };
614            }
615
616            if (type === 'newsletter') {
617                const docs = ACAF.getDocsList(rec.documents);
618                let pdf = '';
619                if (docs.length === 1) {
620                    pdf = `<a class="btn btn-primary btn-sm" style="background:var(--color-primary);border:none;color:#fff;" href="${escHtml(docs[0].url)}" target="_blank" rel="noopener"><i class="fas fa-file-pdf" style="margin-right:.35rem;"></i>View PDF</a>`;
621                } else if (docs.length > 1) {
622                    pdf = docs.map(d => `<a class="btn btn-outline btn-sm" href="${escHtml(d.url)}" target="_blank" rel="noopener"><i class="fas fa-file-pdf" style="margin-right:.35rem;"></i>${escHtml(d.label)}</a>`).join('');
623                }
624                const readMore = rec.link_url ? `<a class="btn btn-outline btn-sm" href="${escHtml(ACAF.sanitizeUrl(rec.link_url))}" target="_blank" rel="noopener">Read More</a>` : '';
625                return {
626                    cover: coverImg(rec.image_url), badge: 'Newsletter', date: formatDate(rec.created_at), title: rec.title,
627                    body: `<div class="hi-desc">${escHtml(rec.description || 'No description provided.')}</div>`,
628                    footer: `${readMore}${pdf}${close}`,
629                };
630            }
631
632            if (type === 'gallery') {
633                const album = rec.albumUrl ? ACAF.sanitizeUrl(rec.albumUrl) : '';
634                const view = album
635                    ? `<a class="btn btn-primary btn-sm" style="background:var(--color-primary);border:none;color:#fff;" href="${escHtml(album)}" target="_blank" rel="noopener noreferrer"><i class="fas fa-images" style="margin-right:.35rem;"></i>View Photos</a>` : '';
636                return {
637                    cover: coverImg(rec.coverImageUrl), badge: 'Gallery', date: formatDate(rec.eventDate), title: rec.title,
638                    body: `<div class="hi-desc">${escHtml(rec.description || 'No description provided.')}</div>`,
639                    footer: `${view}${close}`,
640                };
641            }
642            return null;
643        }
644
645        async function openInsight(type, id) {
646            const box = document.getElementById('hiModalContent');
647            const overlay = document.getElementById('hiModal');
648            box.innerHTML = `<div style="padding:3rem;text-align:center;color:var(--color-text-light);"><i class="fas fa-spinner fa-spin" style="font-size:1.5rem;"></i></div>`;
649            overlay.classList.add('open');
650            document.body.style.overflow = 'hidden';
651
652            let rec = null;
653            try {
654                if (type === 'news')         rec = await ACAF.getNewsEventById(id);
655                else if (type === 'publications') rec = await ACAF.getPublicationById(id);
656                else if (type === 'incidents')    rec = await ACAF.getIncidentById(id);
657                else if (type === 'newsletter')   rec = await ACAF.getNewsletterIssueById(id);
658                else if (type === 'gallery')      rec = await ACAF.getGalleryItemById(id);
659            } catch (e) { rec = null; }
660
661            const d = rec ? renderInsightDetail(type, rec) : null;
662            if (!d) {
663                box.innerHTML = `<div class="hi-body" style="text-align:center;"><p style="color:var(--color-text-light);">Sorry, this item could not be loaded.</p><div class="hi-footer" style="justify-content:center;"><button class="btn btn-outline btn-sm" onclick="closeInsightDirect()">Close</button></div></div>`;
664                return;
665            }
666            const dateLine = d.date ? `<span class="hi-date">${escHtml(d.date)}</span>` : '';
667            box.innerHTML = `
668                ${d.cover}
669                <button class="hi-close" onclick="closeInsightDirect()" aria-label="Close"><i class="fas fa-times"></i></button>
670                <div class="hi-body">
671                    <div class="hi-meta-line"><span class="hi-badge">${escHtml(d.badge)}</span>${dateLine}</div>
672                    <h2 class="hi-title">${escHtml(d.title || '')}</h2>
673                    ${d.body}
673
674                </div>
675                <div class="hi-footer">${d.footer}</div>`;
676            // Keep dynamic translation in sync with the chosen language.
677            if (window.ACAF_DYN && typeof window.ACAF_DYN.translateRoot === 'function') {
678                window.ACAF_DYN.translateRoot(overlay);
679            }
680        }
681
682        function closeInsight(e) {
683            if (e.target === document.getElementById('hiModal')) closeInsightDirect();
684        }
685        function closeInsightDirect() {
686            document.getElementById('hiModal').classList.remove('open');
687            document.body.style.overflow = '';
688        }
689        document.addEventListener('keydown', e => { if (e.key === 'Escape') closeInsightDirect(); });
690        window.openInsight = openInsight;
691        window.closeInsight = closeInsight;
692        window.closeInsightDirect = closeInsightDirect;
693
694        async function initHomeContent() {
695            // Latest Insights — admin-configured mix of content types (or the
696            // latest publications by default).
697            let insights = [];
698            try {
699                insights = await ACAF.getHomeInsights(3);
700            } catch (e) { insights = []; }
701            renderInsights(insights);
702
703            // Hero carousel — latest news & events.
704            let posts = [];
705            try {
706                const result = await ACAF.getNewsEvents({ publishedOnly: true, perPage: 10 });
707                posts = result && result.items ? result.items : [];
708            } catch (e) { posts = []; }
709
710            if (!posts.length) return; // hero stays in full-width centered mode
711
712            const listEl    = document.getElementById('heroSlidesList');
713            const splideEl  = document.getElementById('heroSplide');
714            const skeletonEl = document.getElementById('heroSkeleton');
715            const wrapEl    = document.getElementById('heroCarouselWrap');
716            const textEl    = document.getElementById('heroText');
717
718            listEl.innerHTML = posts.map(buildSlide).join('');
719
720            // Reveal: switch to two-column layout
721            textEl.classList.remove('hero-full');
722            wrapEl.style.display = '';
723            wrapEl.removeAttribute('aria-hidden');
724            skeletonEl.style.display = 'none';
725            splideEl.style.display   = '';
726
727            const splide = new Splide('#heroSplide', {
728                type:         'loop',
729                perPage:      1,
730                gap:          0,
731                autoplay:     true,
732                interval:     6000,
733                pauseOnHover: true,
734                arrows:       true,
735                pagination:   true,
736                speed:        550,
737                easing:       'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
738                i18n: {
739                    prev: 'Previous post',
740                    next: 'Next post',
741                    first: 'First post',
742                    last: 'Last post',
743                    slideX: 'Post %s',
744                    pageX: 'Go to page %s',
745                    play: 'Start autoplay',
746                    pause: 'Pause autoplay',
747                },
748            });
749            splide.mount();
750        }
751
752        // ── Admin-controlled featured popup ───────────────────────────────────
753        const POPUP_DELAY_MS = 2000;
754        const POPUP_SESSION_KEY = 'acaf_home_popup_dismissed';
755
756        function dismissKey(cfg)  { return (cfg.type || '') + ':' + (cfg.id || ''); }
757        function alreadyDismissed(cfg) {
758            try { return sessionStorage.getItem(POPUP_SESSION_KEY) === dismissKey(cfg); }
759            catch (e) { return false; }   // private mode: just show it
760        }
761        function markDismissed(cfg) {
762            try { sessionStorage.setItem(POPUP_SESSION_KEY, dismissKey(cfg)); } catch (e) {}
763        }
764
765        function closeHomePopup(cfg) {
766            const ov = document.getElementById('homePopup');
767            ov.classList.remove('open');
768            ov.setAttribute('aria-hidden', 'true');
769            document.body.style.overflow = '';
770            if (cfg) markDismissed(cfg);
771        }
772
773        async function initHomePopup() {
774            let cfg;
775            try { cfg = await ACAF.getHomePopupConfig(); } catch (e) { return; }
776            if (!ACAF.isPopupActive(cfg)) return;      // disabled or out of window
777            if (alreadyDismissed(cfg)) return;         // seen this session
778
779            const item = await ACAF.getPopupItem(cfg.type, cfg.id);
780            if (!item) return;                         // content unpublished/removed
781
782            document.getElementById('homePopupBadge').textContent = item.label || '';
783            document.getElementById('homePopupTitle').textContent = item.title || '';
784            document.getElementById('homePopupDesc').textContent = item.description || '';
785
786            const media = document.getElementById('homePopupMedia');
787            media.innerHTML = item.imageUrl
788                ? `<img class="home-popup-media" src="${escHtml(item.imageUrl)}" alt="">`
789                : `<div class="home-popup-media-fallback"><i class="fas ${escHtml(item.icon || 'fa-star')}"></i></div>`;
790
791            const cta = document.getElementById('homePopupCta');
792            cta.setAttribute('href', item.href);
793            if (item.external) { cta.setAttribute('target', '_blank'); cta.setAttribute('rel', 'noopener noreferrer'); }
794            else { cta.removeAttribute('target'); cta.removeAttribute('rel'); }
795            document.getElementById('homePopupCtaText').textContent =
796                item.type === 'publications' ? 'View publication'
797                : item.type === 'news' ? 'Read more'
798                : item.type === 'gallery' ? 'View gallery'
799                : item.type === 'newsletter' ? 'Open newsletter'
800                : item.type === 'incidents' ? 'View details' : 'View';
801            // Following the CTA counts as handled — do not nag again this session.
802            cta.addEventListener('click', () => markDismissed(cfg), { once: true });
803
804            const ov = document.getElementById('homePopup');
805            const card = ov.querySelector('.home-popup-card');
806            ov.addEventListener('click', e => { if (e.target === ov) closeHomePopup(cfg); });
807            document.getElementById('homePopupClose').addEventListener('click', () => closeHomePopup(cfg));
808            document.addEventListener('keydown', e => {
809                if (e.key === 'Escape' && ov.classList.contains('open')) closeHomePopup(cfg);
810            });
811
812            setTimeout(() => {
813                ov.classList.add('open');
814                ov.setAttribute('aria-hidden', 'false');
815            }, POPUP_DELAY_MS);
816        }
817
818        document.addEventListener('DOMContentLoaded', initHomeContent);
819        document.addEventListener('DOMContentLoaded', initHomePopup);
820    })();
821    </script>
821
822</body>
823
824</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.