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:
popovskikandClaude Opus 4.8 committed 2026-08-03 14:06:53 +02:00
1 parent 7c32f416f5
commit f5dfd8830e
249 files changed
+2259 -7319

No files matched your search

+131
View File
@@ -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">&times;</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();
});