+
-
-
+
+
{title}
-
+ {/* Tablet/mobile only: hover overlay per Figma 2174-9260 — dark overlay, centered text, teal Read More. Hidden overlay uses pointer-events-none + invisible so the link is not focusable; revealed on hover or keyboard focus (focus-within). */}
+
+
+ {description.length > 100
+ ? `${description.slice(0, 100)}...`
+ : description}
+
+
+ Read More >
+
+
);
diff --git a/src/components/LandingPage/Gallery/index.tsx b/src/components/LandingPage/Gallery/index.tsx
index dc5b647..d8bae3d 100644
--- a/src/components/LandingPage/Gallery/index.tsx
+++ b/src/components/LandingPage/Gallery/index.tsx
@@ -1,6 +1,11 @@
"use client";
-import React from "react";
+import React, { useRef, useState, useCallback, useEffect } from "react";
+import Image from "next/image";
import UpdateCard from "./UpdateCard";
+import startIcon from '../../../../assets/icons/Start_Icon.svg'
+import pauseIcon from '../../../../assets/icons/Pause_Icon.svg'
+
+const AUTO_ROTATE_INTERVAL_MS = 5000;
export interface GalleryUpdate {
image: string;
@@ -19,27 +24,181 @@ export interface GalleryConfig {
}
interface GalleryProps {
- data: { gallery: GalleryConfig};
+ data: { gallery: GalleryConfig };
+}
+
+const CARD_WIDTH_MOBILE = 214;
+const CARD_GAP_MOBILE = 28;
+const SLIDE_STEP = CARD_WIDTH_MOBILE + CARD_GAP_MOBILE; // 242
+const MOBILE_VIEWPORT_OFFSET = -SLIDE_STEP; // -242: show indices 1,2,3
+
+/** Build initial mobile list [c, a, b, c, a, b] from updates [a, b, c] so visible indices 1,2,3 show a,b,c */
+function buildMobileRotatingList(updates: GalleryUpdate[] | undefined): GalleryUpdate[] {
+ if (!updates || updates.length < 3) return [];
+ const [a, b, c] = updates;
+ return [c, a, b, c, a, b];
}
+const MOBILE_MEDIA_QUERY = "(max-width: 767px)";
+
const Gallery: React.FC
= ({ data }) => {
const config = data?.gallery;
-
+ const [rotatingList, setRotatingList] = useState([]);
+ const [isPaused, setIsPaused] = useState(false);
+ const [slideOffset, setSlideOffset] = useState(MOBILE_VIEWPORT_OFFSET);
+ const [slideTransitionEnabled, setSlideTransitionEnabled] = useState(true);
+ const [isMobileViewport, setIsMobileViewport] = useState(false);
+ const slideDirectionRef = useRef<"next" | "prev" | null>(null);
+
+ useEffect(() => {
+ const mql = window.matchMedia(MOBILE_MEDIA_QUERY);
+ const update = () => setIsMobileViewport(mql.matches);
+ update();
+ mql.addEventListener("change", update);
+ return () => mql.removeEventListener("change", update);
+ }, []);
+
+ useEffect(() => {
+ if (config?.updates && config.updates.length >= 3) {
+ setRotatingList(buildMobileRotatingList(config.updates));
+ }
+ }, [config?.updates]);
+
+ const handleSlideTransitionEnd = useCallback(() => {
+ const dir = slideDirectionRef.current;
+ if (dir === "next") {
+ setRotatingList((prev) =>
+ prev.length === 6 ? [...prev.slice(1), prev[0]] : prev
+ );
+ } else if (dir === "prev") {
+ setRotatingList((prev) =>
+ prev.length === 6 ? [prev[5], ...prev.slice(0, 5)] : prev
+ );
+ }
+ slideDirectionRef.current = null;
+ setSlideTransitionEnabled(false);
+ setSlideOffset(MOBILE_VIEWPORT_OFFSET);
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => setSlideTransitionEnabled(true));
+ });
+ }, []);
+
+ const goToNext = useCallback(() => {
+ if (rotatingList.length !== 6 || slideDirectionRef.current) return;
+ slideDirectionRef.current = "next";
+ setSlideOffset(-2 * SLIDE_STEP); // show indices 2,3,4
+ }, [rotatingList.length]);
+
+ const goToPrev = useCallback(() => {
+ if (rotatingList.length !== 6 || slideDirectionRef.current) return;
+ slideDirectionRef.current = "prev";
+ setSlideOffset(0); // show indices 0,1,2
+ }, [rotatingList.length]);
+
+ useEffect(() => {
+ if (!isMobileViewport || rotatingList.length !== 6 || isPaused) return;
+ const id = setInterval(goToNext, AUTO_ROTATE_INTERVAL_MS);
+ return () => clearInterval(id);
+ }, [isMobileViewport, rotatingList.length, isPaused, goToNext]);
+
if (!config) return null;
-
+
return (
-
-
- {config.title}
-
-
-
-
+
+
+
+ {config.title}
+
+
+
+ {/* Mobile: 6-item sliding track; viewport shows 3 cards; smooth slide then reset for infinite */}
+
+
+
+ {rotatingList.map((update: GalleryUpdate, idx: number) => (
+
+
+
+ ))}
+
+
+ {/* Pagination: Left | Pause | Right — infinite, no disabled */}
+
+
+
+
+
setIsPaused((p) => !p)}
+ aria-label={isPaused ? "Play carousel" : "Pause carousel"}
+ className="flex items-center justify-center rounded-full border border-[#4BBFC6] bg-transparent w-[39.158px] h-[39.158px] text-[#6B7280] touch-manipulation cursor-pointer shadow-[0px_2.49px_9.32px_0px_#00000073]"
+ >
+
+
+
+
+
+
+
+
+ {/* Tablet + Desktop: row of cards; desktop only title and cards left-aligned */}
+
{config.updates.map((update: GalleryUpdate, idx: number) => (
-
);