Update Home Page
|
After Width: | Height: | Size: 6.0 MiB |
|
After Width: | Height: | Size: 277 KiB |
|
After Width: | Height: | Size: 4.4 MiB |
|
After Width: | Height: | Size: 5.8 MiB |
|
After Width: | Height: | Size: 368 KiB |
|
After Width: | Height: | Size: 6.3 MiB |
|
After Width: | Height: | Size: 21 MiB |
|
After Width: | Height: | Size: 12 MiB |
|
After Width: | Height: | Size: 806 KiB |
|
After Width: | Height: | Size: 15 MiB |
|
After Width: | Height: | Size: 26 MiB |
|
After Width: | Height: | Size: 9.6 MiB |
|
After Width: | Height: | Size: 6.8 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
@@ -0,0 +1,40 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { AnimatePresence, motion, useReducedMotion } from 'framer-motion';
|
||||
|
||||
interface CoverSlideshowProps {
|
||||
images: string[];
|
||||
intervalMs?: number;
|
||||
}
|
||||
|
||||
/** Foto latar kartu ekosistem — kalau lebih dari satu, bergantian dengan crossfade + blur. */
|
||||
export default function CoverSlideshow({ images, intervalMs = 4200 }: CoverSlideshowProps) {
|
||||
const [index, setIndex] = useState(0);
|
||||
const prefersReduced = useReducedMotion();
|
||||
|
||||
useEffect(() => {
|
||||
if (images.length <= 1) return;
|
||||
const t = setInterval(() => setIndex(i => (i + 1) % images.length), intervalMs);
|
||||
return () => clearInterval(t);
|
||||
}, [images.length, intervalMs]);
|
||||
|
||||
return (
|
||||
<div style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}>
|
||||
<AnimatePresence>
|
||||
<motion.img
|
||||
key={index}
|
||||
src={images[index]}
|
||||
alt=""
|
||||
initial={prefersReduced ? { opacity: 0 } : { opacity: 0, filter: 'blur(18px)', scale: 1.1 }}
|
||||
animate={{ opacity: 1, filter: 'blur(0px)', scale: 1 }}
|
||||
exit={prefersReduced ? { opacity: 0 } : { opacity: 0, filter: 'blur(18px)', scale: 1.05 }}
|
||||
transition={{ duration: 1.2, ease: [0.22, 1, 0.36, 1] }}
|
||||
style={{
|
||||
position: 'absolute', inset: 0,
|
||||
width: '100%', height: '100%',
|
||||
objectFit: 'cover',
|
||||
}}
|
||||
/>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,8 @@ export interface Module {
|
||||
contact?: string[];
|
||||
/** Logo resmi modul (jika sudah tersedia) — dipakai di kartu ekosistem menggantikan ikon custom. */
|
||||
logoImage?: string;
|
||||
/** Foto dokumentasi modul (jika sudah tersedia) — mengisi latar kartu ekosistem, bergantian dengan efek blur bila lebih dari satu. */
|
||||
coverImages?: string[];
|
||||
}
|
||||
|
||||
const modules: Module[] = [
|
||||
@@ -38,6 +40,11 @@ const modules: Module[] = [
|
||||
],
|
||||
contact: ['IG @bhumi.anindha', '+123-456-7890 (kontak sedang diperbarui)'],
|
||||
logoImage: '/assets/logo/ekosistem/bhumi-anindha/mark.png',
|
||||
coverImages: [
|
||||
'/assets/images/ekosistem/bhumi-anindha/01-BhumiAnindha.jpg',
|
||||
'/assets/images/ekosistem/bhumi-anindha/02-BhumiAnindha.jpg',
|
||||
'/assets/images/ekosistem/bhumi-anindha/03-BhumiAnindha.jpg',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'paon',
|
||||
@@ -60,6 +67,7 @@ const modules: Module[] = [
|
||||
{ icon: '🍷', title: 'Cerita Rasa', desc: 'Thematic dinner 5–7 course, maksimal 20 tamu, sebulan sekali.' },
|
||||
],
|
||||
logoImage: '/assets/logo/ekosistem/paon/mark.png',
|
||||
coverImages: ['/assets/images/ekosistem/paon/01-Paon.JPG'],
|
||||
},
|
||||
{
|
||||
id: 'kaind',
|
||||
@@ -81,6 +89,7 @@ const modules: Module[] = [
|
||||
{ icon: '✂️', title: 'Made to Order', desc: 'Pesan pakaian yang dibuat khusus sesuai ukuran dan pilihan kain Anda.' },
|
||||
{ icon: '🎨', title: 'Warna dari Bumi', desc: 'Pewarna alami dari indigo, mengkudu, kulit kayu, dan rempah lokal.' },
|
||||
],
|
||||
coverImages: ['/assets/images/ekosistem/kaind/01-Kaind.jpg'],
|
||||
},
|
||||
{
|
||||
id: 'citta-rahayu',
|
||||
@@ -101,6 +110,7 @@ const modules: Module[] = [
|
||||
{ icon: '👥', title: 'Kumpul', desc: 'Pertemuan komunitas untuk berbagi dan tumbuh bersama.' },
|
||||
{ icon: '🏔️', title: 'Pulang', desc: 'Retreat mendalam untuk pemulihan dan penyatuan diri yang utuh.' },
|
||||
],
|
||||
coverImages: ['/assets/images/ekosistem/citta-rahayu/01-CittaRahayu.JPG'],
|
||||
},
|
||||
{
|
||||
id: 'pasar-bahagia',
|
||||
@@ -125,6 +135,11 @@ const modules: Module[] = [
|
||||
],
|
||||
contact: ['+62 815 7330 5808 (Welin Cakra)', '@anindhaloka_id', '@kreasi.nin'],
|
||||
logoImage: '/assets/logo/ekosistem/pasar-bahagia/mark.png',
|
||||
coverImages: [
|
||||
'/assets/images/ekosistem/pasar-bahagia/01-PasarBahagia.JPG',
|
||||
'/assets/images/ekosistem/pasar-bahagia/02-PasarBahagia.JPG',
|
||||
'/assets/images/ekosistem/pasar-bahagia/03-PasarBahagia.jpg',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'learnnin',
|
||||
@@ -142,6 +157,10 @@ const modules: Module[] = [
|
||||
{ icon: '🌱', title: 'Ruang yang Sedang Tumbuh', desc: 'Daftar program LearnNIN sedang kami tumbuhkan bersama komunitas. Hubungi kami untuk info terbaru atau mengusulkan kolaborasi program.' },
|
||||
],
|
||||
logoImage: '/assets/logo/ekosistem/learnin/mark.png',
|
||||
coverImages: [
|
||||
'/assets/images/ekosistem/learnin/01-Learnnin.JPG',
|
||||
'/assets/images/ekosistem/learnin/02-Learnnin.jpg',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'lokanin',
|
||||
@@ -162,6 +181,11 @@ const modules: Module[] = [
|
||||
{ icon: '📸', title: 'Ruang yang Sedang Tumbuh', desc: 'Nama ruang, foto, dan contoh kegiatan sedang kami lengkapi bersama komunitas. Hubungi kami untuk info dan ketersediaan terkini.' },
|
||||
],
|
||||
contact: ['+62 815 7330 5808 (Welin Cakra)', '@anindhaloka_id', '@kreasi.nin'],
|
||||
coverImages: [
|
||||
'/assets/images/ekosistem/lokanin/01-Lokanin.JPG',
|
||||
'/assets/images/ekosistem/lokanin/02-Lokanin.JPG',
|
||||
'/assets/images/ekosistem/lokanin/03-Lokanin.JPG',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'temannin',
|
||||
|
||||
@@ -400,6 +400,7 @@ h4 { font-size: 1.15rem; font-weight: 600; }
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
grid-auto-flow: row dense;
|
||||
gap: 24px;
|
||||
}
|
||||
.ecosystem-mosaic__item--featured { grid-column: span 2; }
|
||||
@@ -442,21 +443,17 @@ h4 { font-size: 1.15rem; font-weight: 600; }
|
||||
}
|
||||
.card:hover .card__cover-emoji { transform: scale(1.08); }
|
||||
|
||||
/* Badge putih untuk logo resmi modul (kontras terjaga di atas cover gelap apa pun warnanya) */
|
||||
/* Logo resmi modul, kecil di pojok — tanpa frame putih, drop-shadow saja untuk kontras */
|
||||
.card__cover-logo-badge {
|
||||
position: relative;
|
||||
position: absolute;
|
||||
top: 14px; left: 16px;
|
||||
z-index: 3;
|
||||
background: #fff;
|
||||
border-radius: var(--r-lg);
|
||||
padding: 12px 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,.22);
|
||||
max-width: 76%;
|
||||
max-height: 68%;
|
||||
transition: transform .3s var(--ease);
|
||||
}
|
||||
.card__cover-logo-badge img { display: block; max-width: 100%; object-fit: contain; filter: drop-shadow(0 2px 6px rgba(0,0,0,.45)); }
|
||||
.card__cover-logo-badge img { display: block; max-width: 100%; object-fit: contain; }
|
||||
.card:hover .card__cover-logo-badge { transform: scale(1.06); }
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import ArcDivider from '../components/ArcDivider';
|
||||
import Reveal from '../components/Reveal';
|
||||
import TiltCard from '../components/TiltCard';
|
||||
import CountUp from '../components/CountUp';
|
||||
import CoverSlideshow from '../components/CoverSlideshow';
|
||||
|
||||
const EASE_ORGANIC = [0.22, 1, 0.36, 1] as const;
|
||||
|
||||
@@ -52,7 +53,7 @@ const jalurList = [
|
||||
},
|
||||
];
|
||||
|
||||
const FEATURED_IDX = [0, 4];
|
||||
const FEATURED_IDX = [0, 1, 2, 4];
|
||||
|
||||
const heroContainer = {
|
||||
hidden: {},
|
||||
@@ -267,15 +268,27 @@ export default function Home() {
|
||||
{/* ── Cover ── */}
|
||||
<div
|
||||
className={`card__cover${featured ? ' card__cover--tall' : ''}`}
|
||||
style={{ background: `linear-gradient(140deg, ${m.gradientFrom} 0%, ${m.gradientTo} 55%, ${m.color} 100%)` }}
|
||||
style={{ background: m.coverImages ? m.gradientFrom : `linear-gradient(140deg, ${m.gradientFrom} 0%, ${m.gradientTo} 55%, ${m.color} 100%)` }}
|
||||
>
|
||||
{m.coverImages && <CoverSlideshow images={m.coverImages} />}
|
||||
|
||||
{!m.coverImages && (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute', inset: 0,
|
||||
background: `linear-gradient(140deg, ${m.gradientFrom} 0%, ${m.gradientTo} 55%, ${m.color} 100%)`,
|
||||
}}
|
||||
/>
|
||||
<div className="card__cover-blob" style={{ width: 140, height: 140, top: -40, right: -30, background: m.color, opacity: .28 }} />
|
||||
<div className="card__cover-blob" style={{ width: 90, height: 90, bottom: -20, left: 16, background: 'rgba(255,255,255,.08)' }} />
|
||||
<div className="card__cover-blob" style={{ width: 60, height: 60, top: 20, left: 20, background: 'rgba(255,255,255,.06)' }} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{m.logoImage ? (
|
||||
<div className="card__cover-logo-badge">
|
||||
<img src={m.logoImage} alt={m.name} style={{ height: featured ? 68 : 50 }} />
|
||||
<img src={m.logoImage} alt={m.name} style={{ height: featured ? 36 : 28 }} />
|
||||
</div>
|
||||
) : (
|
||||
<span className="card__cover-emoji">
|
||||
|
||||