Carousels: drag + position dots, and real Become-a-member photos
Convert the Hub / Become-a-member carousels from a pure-CSS marquee to a JS-driven scroller with click-and-drag, touch swipe, and progress dots (setupHubCarousel). Turn the "ways to be part of MSOS" cards into hub-style photo cards and add the final Become-a-member photos (WebP). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
@ -76,21 +76,27 @@
|
||||||
.hub-story-text p { font-size: 17px; line-height: 28px; color: rgba(255, 255, 255, 0.9); margin: 0 0 16px; }
|
.hub-story-text p { font-size: 17px; line-height: 28px; color: rgba(255, 255, 255, 0.9); margin: 0 0 16px; }
|
||||||
.hub-story-text p:last-child { margin-bottom: 0; }
|
.hub-story-text p:last-child { margin-bottom: 0; }
|
||||||
|
|
||||||
/* ---------- Use cases (image cards, continuous auto-scrolling marquee) ---------- */
|
/* ---------- Use cases (image cards, auto-scrolling + drag + dots) ---------- */
|
||||||
.hub-carousel { position: relative; }
|
.hub-carousel { position: relative; }
|
||||||
/* masked viewport: cards fade out at both edges instead of being hard-cut */
|
/* masked, scrollable viewport: cards fade out at both edges instead of being
|
||||||
|
hard-cut. Auto-scroll, click-and-drag and touch swiping are driven by JS
|
||||||
|
(setupHubCarousel); the native scrollbar is hidden. */
|
||||||
.hub-uses {
|
.hub-uses {
|
||||||
overflow: hidden;
|
overflow-x: auto; overflow-y: hidden;
|
||||||
padding: 6px 0 10px;
|
padding: 6px 0 10px;
|
||||||
-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 2.5%, #000 97.5%, transparent 100%);
|
-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 2.5%, #000 97.5%, transparent 100%);
|
||||||
mask-image: linear-gradient(90deg, transparent 0, #000 2.5%, #000 97.5%, transparent 100%);
|
mask-image: linear-gradient(90deg, transparent 0, #000 2.5%, #000 97.5%, transparent 100%);
|
||||||
|
cursor: grab;
|
||||||
|
-webkit-user-select: none; user-select: none;
|
||||||
|
scrollbar-width: none; -ms-overflow-style: none;
|
||||||
|
scroll-behavior: auto;
|
||||||
}
|
}
|
||||||
/* the moving track = two identical groups; translateX(-50%) loops seamlessly */
|
.hub-uses::-webkit-scrollbar { display: none; }
|
||||||
|
.hub-uses.is-dragging { cursor: grabbing; }
|
||||||
|
/* the track = three identical groups so the JS loop is seamless in both directions */
|
||||||
.hub-track {
|
.hub-track {
|
||||||
display: flex; width: max-content;
|
display: flex; width: max-content;
|
||||||
animation: hub-marquee 55s linear infinite;
|
|
||||||
}
|
}
|
||||||
.hub-carousel:hover .hub-track { animation-play-state: paused; }
|
|
||||||
.hub-group {
|
.hub-group {
|
||||||
display: flex; gap: 18px; padding-right: 18px; flex: 0 0 auto;
|
display: flex; gap: 18px; padding-right: 18px; flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
@ -106,17 +112,11 @@
|
||||||
.hub-use:nth-child(3n+2) { background: linear-gradient(135deg, #FA7850 0%, #E8613B 100%); }
|
.hub-use:nth-child(3n+2) { background: linear-gradient(135deg, #FA7850 0%, #E8613B 100%); }
|
||||||
.hub-use:nth-child(3n+3) { background: linear-gradient(135deg, #3B82C4 0%, #2C6BA6 100%); }
|
.hub-use:nth-child(3n+3) { background: linear-gradient(135deg, #3B82C4 0%, #2C6BA6 100%); }
|
||||||
.hub-use:hover { transform: translateY(-4px); box-shadow: 0 18px 36px rgba(16, 24, 40, 0.18); }
|
.hub-use:hover { transform: translateY(-4px); box-shadow: 0 18px 36px rgba(16, 24, 40, 0.18); }
|
||||||
@keyframes hub-marquee {
|
|
||||||
from { transform: translateX(0); }
|
|
||||||
to { transform: translateX(-50%); }
|
|
||||||
}
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.hub-track { animation: none; }
|
|
||||||
}
|
|
||||||
.hub-use-img {
|
.hub-use-img {
|
||||||
position: absolute; inset: 0; width: 100%; height: 100%;
|
position: absolute; inset: 0; width: 100%; height: 100%;
|
||||||
object-fit: cover; display: block;
|
object-fit: cover; display: block;
|
||||||
transition: transform 0.4s ease;
|
transition: transform 0.4s ease;
|
||||||
|
-webkit-user-drag: none; user-select: none; pointer-events: none;
|
||||||
}
|
}
|
||||||
.hub-use:hover .hub-use-img { transform: scale(1.05); }
|
.hub-use:hover .hub-use-img { transform: scale(1.05); }
|
||||||
/* gradient rising from the bottom for text legibility */
|
/* gradient rising from the bottom for text legibility */
|
||||||
|
|
@ -138,6 +138,25 @@
|
||||||
.hub-use-body h3 { font-size: 18px; line-height: 1.25; color: #fff; margin: 0; font-weight: 700; }
|
.hub-use-body h3 { font-size: 18px; line-height: 1.25; color: #fff; margin: 0; font-weight: 700; }
|
||||||
.hub-use-body p { font-size: 13px; line-height: 19px; color: rgba(255, 255, 255, 0.9); margin: 0; }
|
.hub-use-body p { font-size: 13px; line-height: 19px; color: rgba(255, 255, 255, 0.9); margin: 0; }
|
||||||
|
|
||||||
|
/* ---------- Scroll-position dots ---------- */
|
||||||
|
.hub-dots {
|
||||||
|
display: flex; justify-content: center; align-items: center;
|
||||||
|
gap: 9px; margin-top: 18px;
|
||||||
|
}
|
||||||
|
.hub-dot {
|
||||||
|
width: 8px; height: 8px; padding: 0; border: 0; border-radius: 50%;
|
||||||
|
background: #CBD5DE; cursor: pointer; flex: 0 0 auto;
|
||||||
|
transition: width 0.25s ease, background-color 0.25s ease,
|
||||||
|
opacity 0.25s ease, transform 0.25s ease;
|
||||||
|
}
|
||||||
|
.hub-dot:hover { background: #9AA9B7; }
|
||||||
|
.hub-dot.is-near { width: 7px; height: 7px; }
|
||||||
|
.hub-dot.is-far { width: 6px; height: 6px; opacity: 0.6; }
|
||||||
|
.hub-dot.is-active {
|
||||||
|
width: 24px; height: 8px; border-radius: 6px;
|
||||||
|
background: #2D738C; opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Amenities ---------- */
|
/* ---------- Amenities ---------- */
|
||||||
.hub-amenities {
|
.hub-amenities {
|
||||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;
|
display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;
|
||||||
|
|
|
||||||
|
|
@ -77,27 +77,22 @@
|
||||||
.mm-numbers-section .mm-note { color: rgba(255,255,255,0.6); margin-top: 24px; }
|
.mm-numbers-section .mm-note { color: rgba(255,255,255,0.6); margin-top: 24px; }
|
||||||
|
|
||||||
/* ---- ways (hub-style auto-scrolling slider) ---- */
|
/* ---- ways (hub-style auto-scrolling slider) ---- */
|
||||||
/* Mechanics (.hub-carousel/.hub-uses/.hub-track/.hub-group) and the marquee
|
/* Mechanics (.hub-carousel/.hub-uses/.hub-track/.hub-group), the marquee
|
||||||
animation are reused from _hub.css; the JS clone step (setupHubCarousel) runs
|
animation, the photo (.hub-use-img), the legibility gradient (::after) and
|
||||||
on every page and picks up the .hub-track here. These rules only restyle the
|
the bottom-anchored body are all reused from _hub.css; the JS clone step
|
||||||
card body for icon-based cards (no photo). */
|
(setupHubCarousel) runs on every page and picks up the .hub-track here.
|
||||||
|
These rules only tweak card size and add a small icon badge over the photo. */
|
||||||
.mm-ways-carousel { margin-top: 4px; }
|
.mm-ways-carousel { margin-top: 4px; }
|
||||||
.mm-use {
|
.mm-use { width: clamp(240px, 24vw, 300px); aspect-ratio: 5 / 6; }
|
||||||
display: flex; flex-direction: column; justify-content: space-between;
|
/* Brand icon as a subtle glass badge floating over the photo. */
|
||||||
gap: 18px; padding: 24px 22px;
|
|
||||||
width: clamp(240px, 24vw, 300px);
|
|
||||||
aspect-ratio: 5 / 6;
|
|
||||||
}
|
|
||||||
/* No photo here, so drop the hub image-legibility overlay. */
|
|
||||||
.mm-use::after { display: none; }
|
|
||||||
.mm-use-ico {
|
.mm-use-ico {
|
||||||
|
position: absolute; top: 14px; left: 14px; z-index: 3;
|
||||||
display: inline-flex; align-items: center; justify-content: center;
|
display: inline-flex; align-items: center; justify-content: center;
|
||||||
width: 54px; height: 54px; border-radius: 14px;
|
width: 44px; height: 44px; border-radius: 12px;
|
||||||
background: rgba(255, 255, 255, 0.18);
|
background: rgba(10, 18, 26, 0.42);
|
||||||
color: #fff; font-size: 23px; flex: 0 0 auto;
|
-webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
|
||||||
|
color: #fff; font-size: 19px;
|
||||||
}
|
}
|
||||||
/* Body is absolute-positioned for photo cards in _hub.css; here it flows normally. */
|
|
||||||
.mm-use .hub-use-body { position: static; padding: 0; gap: 6px; }
|
|
||||||
.mm-use .hub-use-body h3 { font-size: 19px; line-height: 1.25; }
|
.mm-use .hub-use-body h3 { font-size: 19px; line-height: 1.25; }
|
||||||
.mm-use .hub-use-body p { font-size: 14px; line-height: 21px; }
|
.mm-use .hub-use-body p { font-size: 14px; line-height: 21px; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,8 @@
|
||||||
<li class="menu-item menu-item-has-children">
|
<li class="menu-item menu-item-has-children">
|
||||||
<a href="#">Who We Are</a>
|
<a href="#">Who We Are</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="../../en/timeline-and-milestones/">Timeline & Milestones</a></li>
|
|
||||||
<li><a href="../../en/about-us/">Our team</a></li>
|
<li><a href="../../en/about-us/">Our team</a></li>
|
||||||
|
<li><a href="../../en/timeline-and-milestones/">Timeline & Milestones</a></li>
|
||||||
<li><a href="../../en/msos-hub/">MSOS Hub</a></li>
|
<li><a href="../../en/msos-hub/">MSOS Hub</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -135,8 +135,8 @@
|
||||||
<li class="menu-item menu-item-has-children">
|
<li class="menu-item menu-item-has-children">
|
||||||
<a href="#">Who We Are</a>
|
<a href="#">Who We Are</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="../../en/timeline-and-milestones/">Timeline & Milestones</a></li>
|
|
||||||
<li><a href="../../en/about-us/">Our team</a></li>
|
<li><a href="../../en/about-us/">Our team</a></li>
|
||||||
|
<li><a href="../../en/timeline-and-milestones/">Timeline & Milestones</a></li>
|
||||||
<li><a href="../../en/msos-hub/">MSOS Hub</a></li>
|
<li><a href="../../en/msos-hub/">MSOS Hub</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -209,7 +209,7 @@
|
||||||
<h2 class="mm-h2">Why become a member?</h2>
|
<h2 class="mm-h2">Why become a member?</h2>
|
||||||
<div class="mm-benefits">
|
<div class="mm-benefits">
|
||||||
<figure class="mm-benefit-photo">
|
<figure class="mm-benefit-photo">
|
||||||
<img width="1425" height="950" src="../../images/event4-image2.webp" alt="Macedonian students gathered together in Ljubljana" loading="lazy">
|
<img width="1200" height="1600" src="../../images/mm-benefit-real-events.webp" alt="Macedonian students gathered together in Ljubljana" loading="lazy">
|
||||||
<figcaption>Real events, real friendships</figcaption>
|
<figcaption>Real events, real friendships</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<article class="mm-benefit">
|
<article class="mm-benefit">
|
||||||
|
|
@ -241,7 +241,7 @@
|
||||||
<h3>Feel at home away from home</h3><p>Belong to a community where you never have to explain where you come from.</p>
|
<h3>Feel at home away from home</h3><p>Belong to a community where you never have to explain where you come from.</p>
|
||||||
</article>
|
</article>
|
||||||
<figure class="mm-benefit-photo">
|
<figure class="mm-benefit-photo">
|
||||||
<img width="1087" height="586" src="../../images/msnl-3.webp" alt="Students writing their names in Cyrillic at Macedonian Student Night" loading="lazy">
|
<img width="1600" height="1067" src="../../images/mm-benefit-piece-of-home.webp" alt="Students writing their names in Cyrillic at Macedonian Student Night" loading="lazy">
|
||||||
<figcaption>A piece of home in Slovenia</figcaption>
|
<figcaption>A piece of home in Slovenia</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -266,26 +266,32 @@
|
||||||
<div class="hub-track">
|
<div class="hub-track">
|
||||||
<div class="hub-group">
|
<div class="hub-group">
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1200" height="1600" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-stay-connected.webp" alt="Stay connected">
|
||||||
<div class="mm-use-ico"><i class="fas fa-envelope-open-text" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-envelope-open-text" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Stay connected</h3><p>Receive updates and know what is happening.</p></div>
|
<div class="hub-use-body"><h3>Stay connected</h3><p>Receive updates and know what is happening.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-community.webp" alt="Join the community">
|
||||||
<div class="mm-use-ico"><i class="fas fa-mug-hot" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-mug-hot" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Join the community</h3><p>Come to events, meet people and take part when it suits you.</p></div>
|
<div class="hub-use-body"><h3>Join the community</h3><p>Come to events, meet people and take part when it suits you.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1200" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-volunteer.webp" alt="Volunteer sometimes">
|
||||||
<div class="mm-use-ico"><i class="fas fa-hands-helping" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-hands-helping" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Volunteer sometimes</h3><p>Help with selected activities without a long-term commitment.</p></div>
|
<div class="hub-use-body"><h3>Volunteer sometimes</h3><p>Help with selected activities without a long-term commitment.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-projects.webp" alt="Join projects">
|
||||||
<div class="mm-use-ico"><i class="fas fa-diagram-project" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-diagram-project" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Join projects</h3><p>Contribute to specific initiatives, campaigns, guides or collaborations.</p></div>
|
<div class="hub-use-body"><h3>Join projects</h3><p>Contribute to specific initiatives, campaigns, guides or collaborations.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-events.webp" alt="Organise events">
|
||||||
<div class="mm-use-ico"><i class="fas fa-calendar-check" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-calendar-check" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Organise events</h3><p>Help plan, coordinate, promote and deliver activities.</p></div>
|
<div class="hub-use-body"><h3>Organise events</h3><p>Help plan, coordinate, promote and deliver activities.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="900" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-responsibility.webp" alt="Take responsibility">
|
||||||
<div class="mm-use-ico"><i class="fas fa-star" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-star" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Take responsibility</h3><p>Explore leadership, governance, representation or local team roles.</p></div>
|
<div class="hub-use-body"><h3>Take responsibility</h3><p>Explore leadership, governance, representation or local team roles.</p></div>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 412 KiB |
|
After Width: | Height: | Size: 231 KiB |
172
main.js
|
|
@ -931,22 +931,174 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ===================================================================
|
* ===================================================================
|
||||||
* HUB USE-CASE CAROUSEL (auto-scrolling)
|
* HUB USE-CASE CAROUSEL (auto-scrolling + drag + position dots)
|
||||||
* ===================================================================
|
* ===================================================================
|
||||||
|
* Drives every .hub-carousel on the page (Hub "What you can do here"
|
||||||
|
* and the "ways to be part of MSOS" slider). The track is built from
|
||||||
|
* three identical .hub-group copies so the scroll loops seamlessly in
|
||||||
|
* both directions; auto-scroll is JS-driven (not a CSS animation) so it
|
||||||
|
* can co-exist with click-and-drag and touch swiping.
|
||||||
*/
|
*/
|
||||||
function setupHubCarousel() {
|
function setupHubCarousel() {
|
||||||
const trackEl = document.querySelector('.hub-track');
|
const carousels = document.querySelectorAll('.hub-carousel');
|
||||||
if (!trackEl) return;
|
if (!carousels.length) return;
|
||||||
const group = trackEl.querySelector('.hub-group');
|
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
if (!group) return;
|
const LOOP_MS = 55000; // time to travel one group width (matches old 55s marquee)
|
||||||
|
|
||||||
// Duplicate the group so translateX(-50%) loops back seamlessly (waterslide flow).
|
carousels.forEach(function (carousel) {
|
||||||
// Guard against double-cloning if the script somehow runs twice.
|
if (carousel.dataset.carouselInit) return;
|
||||||
if (trackEl.querySelectorAll('.hub-group').length < 2) {
|
carousel.dataset.carouselInit = '1';
|
||||||
const clone = group.cloneNode(true);
|
|
||||||
|
const scroller = carousel.querySelector('.hub-uses');
|
||||||
|
const track = carousel.querySelector('.hub-track');
|
||||||
|
if (!scroller || !track) return;
|
||||||
|
const firstGroup = track.querySelector('.hub-group');
|
||||||
|
if (!firstGroup) return;
|
||||||
|
|
||||||
|
// Need 3 identical groups so a full neighbour is always visible at each wrap point.
|
||||||
|
while (track.querySelectorAll('.hub-group').length < 3) {
|
||||||
|
const clone = firstGroup.cloneNode(true);
|
||||||
clone.setAttribute('aria-hidden', 'true');
|
clone.setAttribute('aria-hidden', 'true');
|
||||||
trackEl.appendChild(clone);
|
track.appendChild(clone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cards = firstGroup.children;
|
||||||
|
const cardCount = cards.length;
|
||||||
|
if (!cardCount) return;
|
||||||
|
|
||||||
|
// --- Position dots ---
|
||||||
|
const dotsWrap = document.createElement('div');
|
||||||
|
dotsWrap.className = 'hub-dots';
|
||||||
|
dotsWrap.setAttribute('role', 'tablist');
|
||||||
|
dotsWrap.setAttribute('aria-label', 'Carousel position');
|
||||||
|
const dotEls = [];
|
||||||
|
for (let i = 0; i < cardCount; i++) {
|
||||||
|
const b = document.createElement('button');
|
||||||
|
b.type = 'button';
|
||||||
|
b.className = 'hub-dot';
|
||||||
|
b.setAttribute('aria-label', 'Go to item ' + (i + 1));
|
||||||
|
b.addEventListener('click', (function (idx) {
|
||||||
|
return function () { goTo(idx); };
|
||||||
|
})(i));
|
||||||
|
dotsWrap.appendChild(b);
|
||||||
|
dotEls.push(b);
|
||||||
|
}
|
||||||
|
carousel.appendChild(dotsWrap);
|
||||||
|
|
||||||
|
// --- Measurements (recomputed on resize / image load) ---
|
||||||
|
let groupWidth = 0, step = 0;
|
||||||
|
function measure() {
|
||||||
|
groupWidth = firstGroup.offsetWidth;
|
||||||
|
step = cardCount > 1 ? (cards[1].offsetLeft - cards[0].offsetLeft) : groupWidth;
|
||||||
|
if (!step) step = groupWidth / cardCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep scroll within the middle copy [groupWidth, 2*groupWidth) so it loops both ways.
|
||||||
|
function wrap() {
|
||||||
|
if (!groupWidth) return;
|
||||||
|
if (scroller.scrollLeft >= 2 * groupWidth) scroller.scrollLeft -= groupWidth;
|
||||||
|
else if (scroller.scrollLeft < groupWidth) scroller.scrollLeft += groupWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDots() {
|
||||||
|
if (!step || !groupWidth) return;
|
||||||
|
const within = ((scroller.scrollLeft % groupWidth) + groupWidth) % groupWidth;
|
||||||
|
const active = Math.round(within / step) % cardCount;
|
||||||
|
for (let i = 0; i < cardCount; i++) {
|
||||||
|
const dist = Math.min(Math.abs(i - active), cardCount - Math.abs(i - active));
|
||||||
|
dotEls[i].classList.toggle('is-active', i === active);
|
||||||
|
dotEls[i].classList.toggle('is-near', dist === 1);
|
||||||
|
dotEls[i].classList.toggle('is-far', dist >= 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
measure();
|
||||||
|
if (groupWidth) scroller.scrollLeft = groupWidth;
|
||||||
|
updateDots();
|
||||||
|
}
|
||||||
|
reset();
|
||||||
|
window.addEventListener('load', reset);
|
||||||
|
window.addEventListener('resize', function () { measure(); wrap(); updateDots(); });
|
||||||
|
|
||||||
|
// --- Jump to a specific card when a dot is clicked ---
|
||||||
|
// Custom eased scroll (not native smooth) so it never fights the
|
||||||
|
// rAF auto-scroll loop. Target always sits in the middle copy
|
||||||
|
// [groupWidth, 2*groupWidth), so wrap() stays a no-op during it.
|
||||||
|
let programmatic = 0; // ms remaining where auto-scroll yields
|
||||||
|
let animId = 0;
|
||||||
|
function goTo(idx) {
|
||||||
|
if (!step) measure();
|
||||||
|
const start = scroller.scrollLeft;
|
||||||
|
const target = groupWidth + idx * step;
|
||||||
|
const dur = 450, t0 = performance.now();
|
||||||
|
const ease = function (t) { return 1 - Math.pow(1 - t, 3); };
|
||||||
|
cancelAnimationFrame(animId);
|
||||||
|
programmatic = dur + 80;
|
||||||
|
(function anim(now) {
|
||||||
|
const t = Math.min(1, (now - t0) / dur);
|
||||||
|
scroller.scrollLeft = start + (target - start) * ease(t);
|
||||||
|
updateDots();
|
||||||
|
if (t < 1) animId = requestAnimationFrame(anim);
|
||||||
|
})(t0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Auto-scroll loop ---
|
||||||
|
let paused = false;
|
||||||
|
let dragging = false;
|
||||||
|
let last = null;
|
||||||
|
function tick(ts) {
|
||||||
|
if (last == null) last = ts;
|
||||||
|
const dt = ts - last;
|
||||||
|
last = ts;
|
||||||
|
if (!reduceMotion && !paused && programmatic <= 0 && groupWidth) {
|
||||||
|
scroller.scrollLeft += (groupWidth / LOOP_MS) * dt;
|
||||||
|
}
|
||||||
|
if (programmatic > 0) programmatic -= dt;
|
||||||
|
wrap();
|
||||||
|
updateDots();
|
||||||
|
requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(tick);
|
||||||
|
|
||||||
|
// Pause on hover (desktop).
|
||||||
|
carousel.addEventListener('mouseenter', function () { paused = true; });
|
||||||
|
carousel.addEventListener('mouseleave', function () { if (!dragging) paused = false; });
|
||||||
|
|
||||||
|
// --- Click-and-drag (mouse) + pause during touch (native scroll handles the swipe) ---
|
||||||
|
let lastX = 0;
|
||||||
|
scroller.addEventListener('pointerdown', function (e) {
|
||||||
|
paused = true;
|
||||||
|
if (e.pointerType !== 'mouse') return; // touch/pen: let the browser scroll natively
|
||||||
|
dragging = true;
|
||||||
|
lastX = e.clientX;
|
||||||
|
scroller.classList.add('is-dragging');
|
||||||
|
try { scroller.setPointerCapture(e.pointerId); } catch (err) {}
|
||||||
|
});
|
||||||
|
scroller.addEventListener('pointermove', function (e) {
|
||||||
|
if (!dragging) return;
|
||||||
|
const dx = e.clientX - lastX;
|
||||||
|
lastX = e.clientX;
|
||||||
|
scroller.scrollLeft -= dx;
|
||||||
|
wrap();
|
||||||
|
updateDots();
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
function endDrag(e) {
|
||||||
|
if (dragging) {
|
||||||
|
dragging = false;
|
||||||
|
scroller.classList.remove('is-dragging');
|
||||||
|
try { scroller.releasePointerCapture(e.pointerId); } catch (err) {}
|
||||||
|
}
|
||||||
|
// Resume auto-scroll unless the cursor is still hovering the carousel.
|
||||||
|
if (!carousel.matches(':hover')) paused = false;
|
||||||
|
}
|
||||||
|
scroller.addEventListener('pointerup', endDrag);
|
||||||
|
scroller.addEventListener('pointercancel', endDrag);
|
||||||
|
|
||||||
|
// Prevent the browser's native image drag-ghost while dragging.
|
||||||
|
track.addEventListener('dragstart', function (e) { e.preventDefault(); });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,8 @@
|
||||||
<li class="menu-item menu-item-has-children">
|
<li class="menu-item menu-item-has-children">
|
||||||
<a href="#">За нас</a>
|
<a href="#">За нас</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="../../mk/timeline-and-milestones/">Времеплов и достигнувања</a></li>
|
|
||||||
<li><a href="../../mk/about-us/">Нашиот тим</a></li>
|
<li><a href="../../mk/about-us/">Нашиот тим</a></li>
|
||||||
|
<li><a href="../../mk/timeline-and-milestones/">Времеплов и достигнувања</a></li>
|
||||||
<li><a href="../../mk/msos-hub/">МСОС Хаб</a></li>
|
<li><a href="../../mk/msos-hub/">МСОС Хаб</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -135,8 +135,8 @@
|
||||||
<li class="menu-item menu-item-has-children">
|
<li class="menu-item menu-item-has-children">
|
||||||
<a href="#">За нас</a>
|
<a href="#">За нас</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="../../mk/timeline-and-milestones/">Времеплов и достигнувања</a></li>
|
|
||||||
<li><a href="../../mk/about-us/">Нашиот тим</a></li>
|
<li><a href="../../mk/about-us/">Нашиот тим</a></li>
|
||||||
|
<li><a href="../../mk/timeline-and-milestones/">Времеплов и достигнувања</a></li>
|
||||||
<li><a href="../../mk/msos-hub/">МСОС Хаб</a></li>
|
<li><a href="../../mk/msos-hub/">МСОС Хаб</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -209,7 +209,7 @@
|
||||||
<h2 class="mm-h2">Зошто да станеш член?</h2>
|
<h2 class="mm-h2">Зошто да станеш член?</h2>
|
||||||
<div class="mm-benefits">
|
<div class="mm-benefits">
|
||||||
<figure class="mm-benefit-photo">
|
<figure class="mm-benefit-photo">
|
||||||
<img width="1425" height="950" src="../../images/event4-image2.webp" alt="Македонски студенти собрани во Љубљана" loading="lazy">
|
<img width="1200" height="1600" src="../../images/mm-benefit-real-events.webp" alt="Македонски студенти собрани во Љубљана" loading="lazy">
|
||||||
<figcaption>Вистински настани, вистински пријателства</figcaption>
|
<figcaption>Вистински настани, вистински пријателства</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<article class="mm-benefit">
|
<article class="mm-benefit">
|
||||||
|
|
@ -241,7 +241,7 @@
|
||||||
<h3>Почувствувај се како дома</h3><p>Припаѓај на заедница каде никогаш не мораш да објаснуваш од каде доаѓаш.</p>
|
<h3>Почувствувај се како дома</h3><p>Припаѓај на заедница каде никогаш не мораш да објаснуваш од каде доаѓаш.</p>
|
||||||
</article>
|
</article>
|
||||||
<figure class="mm-benefit-photo">
|
<figure class="mm-benefit-photo">
|
||||||
<img width="1087" height="586" src="../../images/msnl-3.webp" alt="Студенти ги пишуваат своите имиња на кирилица на македонската студентска вечер" loading="lazy">
|
<img width="1600" height="1067" src="../../images/mm-benefit-piece-of-home.webp" alt="Студенти ги пишуваат своите имиња на кирилица на македонската студентска вечер" loading="lazy">
|
||||||
<figcaption>Дом далеку од дома</figcaption>
|
<figcaption>Дом далеку од дома</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -265,26 +265,32 @@
|
||||||
<div class="hub-track">
|
<div class="hub-track">
|
||||||
<div class="hub-group">
|
<div class="hub-group">
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1200" height="1600" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-stay-connected.webp" alt="Остани поврзан">
|
||||||
<div class="mm-use-ico"><i class="fas fa-envelope-open-text" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-envelope-open-text" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Остани поврзан</h3><p>Примај новости и знај што се случува.</p></div>
|
<div class="hub-use-body"><h3>Остани поврзан</h3><p>Примај новости и знај што се случува.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-community.webp" alt="Приклучи се на заедницата">
|
||||||
<div class="mm-use-ico"><i class="fas fa-mug-hot" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-mug-hot" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Приклучи се на заедницата</h3><p>Доаѓај на настани, запознавај луѓе и учествувај кога ти одговара.</p></div>
|
<div class="hub-use-body"><h3>Приклучи се на заедницата</h3><p>Доаѓај на настани, запознавај луѓе и учествувај кога ти одговара.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1200" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-volunteer.webp" alt="Волонтирај повремено">
|
||||||
<div class="mm-use-ico"><i class="fas fa-hands-helping" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-hands-helping" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Волонтирај повремено</h3><p>Помагај на избрани активности без долгорочна обврска.</p></div>
|
<div class="hub-use-body"><h3>Волонтирај повремено</h3><p>Помагај на избрани активности без долгорочна обврска.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-projects.webp" alt="Приклучи се на проекти">
|
||||||
<div class="mm-use-ico"><i class="fas fa-diagram-project" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-diagram-project" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Приклучи се на проекти</h3><p>Придонеси на конкретни иницијативи, кампањи, водичи или соработки.</p></div>
|
<div class="hub-use-body"><h3>Приклучи се на проекти</h3><p>Придонеси на конкретни иницијативи, кампањи, водичи или соработки.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-events.webp" alt="Организирај настани">
|
||||||
<div class="mm-use-ico"><i class="fas fa-calendar-check" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-calendar-check" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Организирај настани</h3><p>Помагај да се планираат, координираат, промовираат и реализираат активности.</p></div>
|
<div class="hub-use-body"><h3>Организирај настани</h3><p>Помагај да се планираат, координираат, промовираат и реализираат активности.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="900" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-responsibility.webp" alt="Преземи одговорност">
|
||||||
<div class="mm-use-ico"><i class="fas fa-star" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-star" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Преземи одговорност</h3><p>Истражи лидерство, управување, застапување или улоги во локален тим.</p></div>
|
<div class="hub-use-body"><h3>Преземи одговорност</h3><p>Истражи лидерство, управување, застапување или улоги во локален тим.</p></div>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,8 @@
|
||||||
<li class="menu-item menu-item-has-children">
|
<li class="menu-item menu-item-has-children">
|
||||||
<a href="#">O nas</a>
|
<a href="#">O nas</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="../../si/timeline-and-milestones/">Časovnica in dosežki</a></li>
|
|
||||||
<li><a href="../../si/about-us/">Naša ekipa, poslanstvo, vizija in vrednote</a></li>
|
<li><a href="../../si/about-us/">Naša ekipa, poslanstvo, vizija in vrednote</a></li>
|
||||||
|
<li><a href="../../si/timeline-and-milestones/">Časovnica in dosežki</a></li>
|
||||||
<li><a href="../../si/msos-hub/">MSOS Hub</a></li>
|
<li><a href="../../si/msos-hub/">MSOS Hub</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -135,8 +135,8 @@
|
||||||
<li class="menu-item menu-item-has-children">
|
<li class="menu-item menu-item-has-children">
|
||||||
<a href="#">O nas</a>
|
<a href="#">O nas</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="../../si/timeline-and-milestones/">Časovnica in dosežki</a></li>
|
|
||||||
<li><a href="../../si/about-us/">Naša ekipa, poslanstvo, vizija in vrednote</a></li>
|
<li><a href="../../si/about-us/">Naša ekipa, poslanstvo, vizija in vrednote</a></li>
|
||||||
|
<li><a href="../../si/timeline-and-milestones/">Časovnica in dosežki</a></li>
|
||||||
<li><a href="../../si/msos-hub/">MSOS Hub</a></li>
|
<li><a href="../../si/msos-hub/">MSOS Hub</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -209,7 +209,7 @@
|
||||||
<h2 class="mm-h2">Zakaj postati član?</h2>
|
<h2 class="mm-h2">Zakaj postati član?</h2>
|
||||||
<div class="mm-benefits">
|
<div class="mm-benefits">
|
||||||
<figure class="mm-benefit-photo">
|
<figure class="mm-benefit-photo">
|
||||||
<img width="1425" height="950" src="../../images/event4-image2.webp" alt="Makedonski študenti zbrani v Ljubljani" loading="lazy">
|
<img width="1200" height="1600" src="../../images/mm-benefit-real-events.webp" alt="Makedonski študenti zbrani v Ljubljani" loading="lazy">
|
||||||
<figcaption>Pravi dogodki, prava prijateljstva</figcaption>
|
<figcaption>Pravi dogodki, prava prijateljstva</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<article class="mm-benefit">
|
<article class="mm-benefit">
|
||||||
|
|
@ -241,7 +241,7 @@
|
||||||
<h3>Počuti se kot doma</h3><p>Pripadaj skupnosti, kjer ti nikoli ni treba pojasnjevati, od kod prihajaš.</p>
|
<h3>Počuti se kot doma</h3><p>Pripadaj skupnosti, kjer ti nikoli ni treba pojasnjevati, od kod prihajaš.</p>
|
||||||
</article>
|
</article>
|
||||||
<figure class="mm-benefit-photo">
|
<figure class="mm-benefit-photo">
|
||||||
<img width="1087" height="586" src="../../images/msnl-3.webp" alt="Študenti pišejo svoja imena v cirilici na makedonskem študentskem večeru" loading="lazy">
|
<img width="1600" height="1067" src="../../images/mm-benefit-piece-of-home.webp" alt="Študenti pišejo svoja imena v cirilici na makedonskem študentskem večeru" loading="lazy">
|
||||||
<figcaption>Košček doma v Sloveniji</figcaption>
|
<figcaption>Košček doma v Sloveniji</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -266,26 +266,32 @@
|
||||||
<div class="hub-track">
|
<div class="hub-track">
|
||||||
<div class="hub-group">
|
<div class="hub-group">
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1200" height="1600" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-stay-connected.webp" alt="Ostani povezan">
|
||||||
<div class="mm-use-ico"><i class="fas fa-envelope-open-text" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-envelope-open-text" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Ostani povezan</h3><p>Prejemaj novice in vedi, kaj se dogaja.</p></div>
|
<div class="hub-use-body"><h3>Ostani povezan</h3><p>Prejemaj novice in vedi, kaj se dogaja.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-community.webp" alt="Pridruži se skupnosti">
|
||||||
<div class="mm-use-ico"><i class="fas fa-mug-hot" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-mug-hot" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Pridruži se skupnosti</h3><p>Obišči dogodke, spoznaj ljudi in sodeluj, ko ti ustreza.</p></div>
|
<div class="hub-use-body"><h3>Pridruži se skupnosti</h3><p>Obišči dogodke, spoznaj ljudi in sodeluj, ko ti ustreza.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1200" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-volunteer.webp" alt="Občasno prostovoljči">
|
||||||
<div class="mm-use-ico"><i class="fas fa-hands-helping" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-hands-helping" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Občasno prostovoljči</h3><p>Pomagaj pri izbranih dejavnostih brez dolgoročne zaveze.</p></div>
|
<div class="hub-use-body"><h3>Občasno prostovoljči</h3><p>Pomagaj pri izbranih dejavnostih brez dolgoročne zaveze.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-projects.webp" alt="Pridruži se projektom">
|
||||||
<div class="mm-use-ico"><i class="fas fa-diagram-project" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-diagram-project" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Pridruži se projektom</h3><p>Prispevaj k pobudam, kampanjam, vodnikom ali sodelovanjem.</p></div>
|
<div class="hub-use-body"><h3>Pridruži se projektom</h3><p>Prispevaj k pobudam, kampanjam, vodnikom ali sodelovanjem.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="1067" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-events.webp" alt="Organiziraj dogodke">
|
||||||
<div class="mm-use-ico"><i class="fas fa-calendar-check" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-calendar-check" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Organiziraj dogodke</h3><p>Pomagaj načrtovati, usklajevati, promovirati in izvesti dejavnosti.</p></div>
|
<div class="hub-use-body"><h3>Organiziraj dogodke</h3><p>Pomagaj načrtovati, usklajevati, promovirati in izvesti dejavnosti.</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="hub-use mm-use">
|
<article class="hub-use mm-use">
|
||||||
|
<img width="1600" height="900" class="hub-use-img" loading="lazy" decoding="async" src="../../images/mm-way-responsibility.webp" alt="Prevzemi odgovornost">
|
||||||
<div class="mm-use-ico"><i class="fas fa-star" aria-hidden="true"></i></div>
|
<div class="mm-use-ico"><i class="fas fa-star" aria-hidden="true"></i></div>
|
||||||
<div class="hub-use-body"><h3>Prevzemi odgovornost</h3><p>Razišči vodenje, upravljanje, zastopanje ali vloge v lokalni ekipi.</p></div>
|
<div class="hub-use-body"><h3>Prevzemi odgovornost</h3><p>Razišči vodenje, upravljanje, zastopanje ali vloge v lokalni ekipi.</p></div>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
||||||