Update Home Page

This commit is contained in:
2026-08-27 11:45:02 +07:00
parent 5a95fbe6c4
commit a8c5f44de9
18 changed files with 88 additions and 14 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

+40
View File
@@ -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>
);
}
+24
View File
@@ -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',
+5 -8
View File
@@ -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); }
+19 -6
View File
@@ -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%)` }}
>
<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.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">