Hub redesign, per-language logos, partner carousel, footer + menu fixes
MSOS Hub page: - "What you can do here": Porsche-style vertical image cards in a continuous auto-scrolling marquee with edge fades (real photos placeholders) - "A glimpse of the space": full-bleed band of 4 connected photos - "A space we built together": full-bleed image banner with blue gradient overlay - "Made possible by": partner logo tiles (Cultural Association + Ministry); processed Ministry + Cultural Association logos from source art - Copy/spacing polish; removed em-dashes; larger section spacing; deeper final-CTA gradient so it no longer blends into the footer Branding / partners: - Per-language MSOS logos (SVG) in header + footer across all pages - Partner carousel: added Ministry (wide variant), Cultural Association, Embassy of North Macedonia, and Spletni Mojster (width-capped); de-duplicated logos within each marquee group so new logos appear sooner Footer: - Build credit line (Spletni Mojster + Kristijan), clean centered text - Synced MK/SI footers to EN (added missing About us + Timeline links) Landing page: - FAQ short CTA already avatar design (FAQ page kept its Email/Viber block) - Swapped blurry stock video thumbnail for the real team photo - Removed the intentional hero-background blur Navigation: - Mobile menu: use 100dvh so the language selector is reachable with submenus open - Desktop: dropdown parents no longer navigate to dead "/#" links Language suggestion banner: - Dismissible, browser-language based banner suggesting the matching locale; remembers choice in localStorage and waits for the cookie-consent choice to avoid overlapping the consent card Removed stale *.html.bak backup files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7c32f416f5
commit
f5dfd8830e
249 files changed
+2259
-7319
No files matched your search
@@ -67,6 +67,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
console.error('Error in Mobile Accordion Menu:', error);
|
||||
}
|
||||
|
||||
// Desktop: top-level dropdown parents (Who We Are, What We Do, For Students) are
|
||||
// triggers, not real pages. Their href="#" caused a dead "/en/#" navigation on click.
|
||||
// The submenu already opens on hover, so just cancel the navigation.
|
||||
try {
|
||||
document.querySelectorAll('.desktop-nav .menu-item-has-children > a').forEach((a) => {
|
||||
a.addEventListener('click', (e) => { e.preventDefault(); });
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error disabling desktop dropdown parent links:', error);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ===================================================================
|
||||
@@ -129,6 +140,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const newUrl = `/${targetLang}${pathWithoutLang}`;
|
||||
link.setAttribute('href', newUrl.replace(/\/{2,}/g, '/'));
|
||||
link.classList.toggle('is-current', targetLang === docLang);
|
||||
// Manually choosing a language is a deliberate preference — don't nag with the banner afterwards.
|
||||
link.addEventListener('click', () => {
|
||||
try { localStorage.setItem('msos-lang-banner', 'dismissed'); } catch (e) {}
|
||||
});
|
||||
});
|
||||
|
||||
const desktopSelectors = document.querySelectorAll('.header-right-wrapper .language-selector');
|
||||
@@ -724,6 +739,120 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ===================================================================
|
||||
* LANGUAGE SUGGESTION BANNER (browser-language based, dismissible)
|
||||
* ===================================================================
|
||||
*/
|
||||
function setupLanguageSuggestion() {
|
||||
try {
|
||||
var STORE = 'msos-lang-banner';
|
||||
if (localStorage.getItem(STORE) === 'dismissed') return;
|
||||
|
||||
var validLangs = ['en', 'si', 'mk'];
|
||||
|
||||
// Current page language (<html lang>; site uses /si/ for Slovene "sl").
|
||||
var docLang = (document.documentElement.getAttribute('lang') || 'en').toLowerCase();
|
||||
if (docLang === 'sl') docLang = 'si';
|
||||
|
||||
// Detect the visitor's preferred language from the browser.
|
||||
var navLangs = navigator.languages || [navigator.language || ''];
|
||||
var detected = null;
|
||||
for (var i = 0; i < navLangs.length; i++) {
|
||||
var l = (navLangs[i] || '').toLowerCase();
|
||||
if (l.indexOf('mk') === 0) { detected = 'mk'; break; }
|
||||
if (l.indexOf('sl') === 0) { detected = 'si'; break; }
|
||||
if (l.indexOf('en') === 0) { detected = 'en'; break; }
|
||||
}
|
||||
if (!detected || detected === docLang) return; // unknown or already correct
|
||||
|
||||
// Build the equivalent URL in the detected language (same logic as the switcher).
|
||||
var path = window.location.pathname;
|
||||
var parts = path.split('/').filter(Boolean);
|
||||
var pathWithoutLang = path;
|
||||
if (parts.length > 0 && validLangs.indexOf(parts[0]) !== -1) {
|
||||
pathWithoutLang = '/' + parts.slice(1).join('/');
|
||||
if (path.endsWith('/') && pathWithoutLang !== '/') pathWithoutLang += '/';
|
||||
}
|
||||
var targetUrl = ('/' + detected + pathWithoutLang).replace(/\/{2,}/g, '/');
|
||||
|
||||
var MSG = {
|
||||
en: { text: 'This page is also available in English.', btn: 'View in English', flag: 'gb', aria: 'Language suggestion' },
|
||||
mk: { text: 'Оваа страница е достапна и на македонски.', btn: 'Прикажи на македонски', flag: 'mk', aria: 'Предлог за јазик' },
|
||||
si: { text: 'Ta stran je na voljo tudi v slovenščini.', btn: 'Prikaži v slovenščini', flag: 'si', aria: 'Predlog jezika' }
|
||||
};
|
||||
var m = MSG[detected];
|
||||
|
||||
function showBanner() {
|
||||
if (document.querySelector('.lang-suggest')) return;
|
||||
var bar = document.createElement('div');
|
||||
bar.className = 'lang-suggest';
|
||||
bar.setAttribute('role', 'region');
|
||||
bar.setAttribute('aria-label', m.aria);
|
||||
bar.innerHTML =
|
||||
'<span class="lang-flag lang-flag-' + m.flag + '" aria-hidden="true"></span>' +
|
||||
'<p class="lang-suggest-text">' + m.text + '</p>' +
|
||||
'<a class="lang-suggest-btn" href="' + targetUrl + '">' + m.btn + '</a>' +
|
||||
'<button type="button" class="lang-suggest-close" aria-label="Dismiss">×</button>';
|
||||
document.body.appendChild(bar);
|
||||
requestAnimationFrame(function () { bar.classList.add('is-visible'); });
|
||||
|
||||
function remember() { try { localStorage.setItem(STORE, 'dismissed'); } catch (e) {} }
|
||||
bar.querySelector('.lang-suggest-btn').addEventListener('click', remember);
|
||||
bar.querySelector('.lang-suggest-close').addEventListener('click', function () {
|
||||
remember();
|
||||
bar.classList.remove('is-visible');
|
||||
setTimeout(function () { bar.remove(); }, 300);
|
||||
});
|
||||
}
|
||||
|
||||
// Don't collide with the cookie-consent card (also a bottom-centred fixed card).
|
||||
// If the visitor hasn't answered consent yet, wait until they do, then show.
|
||||
var consentDecided;
|
||||
try {
|
||||
var c = localStorage.getItem('msos-consent');
|
||||
consentDecided = (c === 'granted' || c === 'denied');
|
||||
} catch (e) { consentDecided = true; }
|
||||
|
||||
if (consentDecided && !document.querySelector('.msos-cc')) {
|
||||
showBanner();
|
||||
} else if ('MutationObserver' in window) {
|
||||
var obs = new MutationObserver(function () {
|
||||
if (!document.querySelector('.msos-cc')) { obs.disconnect(); showBanner(); }
|
||||
});
|
||||
obs.observe(document.body, { childList: true });
|
||||
} else {
|
||||
// Fallback: poll until the consent card is gone.
|
||||
var tries = 0;
|
||||
var iv = setInterval(function () {
|
||||
if (!document.querySelector('.msos-cc') || ++tries > 120) { clearInterval(iv); showBanner(); }
|
||||
}, 500);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in language suggestion banner:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ===================================================================
|
||||
* HUB USE-CASE CAROUSEL (auto-scrolling)
|
||||
* ===================================================================
|
||||
*/
|
||||
function setupHubCarousel() {
|
||||
const trackEl = document.querySelector('.hub-track');
|
||||
if (!trackEl) return;
|
||||
const group = trackEl.querySelector('.hub-group');
|
||||
if (!group) return;
|
||||
|
||||
// Duplicate the group so translateX(-50%) loops back seamlessly (waterslide flow).
|
||||
// Guard against double-cloning if the script somehow runs twice.
|
||||
if (trackEl.querySelectorAll('.hub-group').length < 2) {
|
||||
const clone = group.cloneNode(true);
|
||||
clone.setAttribute('aria-hidden', 'true');
|
||||
trackEl.appendChild(clone);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ===================================================================
|
||||
* 8. ZAGON FUNKCIJ PO NALOŽITVI STRANI
|
||||
@@ -731,6 +860,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
*/
|
||||
highlightActiveLink();
|
||||
setupLanguageSwitcher();
|
||||
setupLanguageSuggestion();
|
||||
setupNewsletter();
|
||||
setupHubCarousel();
|
||||
|
||||
});
|
||||
Reference in new issue
Block a user