// ============================================================
// SECTION LAB — White rounded card slides in from below.
// 3 compound boxes (flavonoid, polifenol, alkaloid).
// Below: Download lab-result button + parallax field of
// sachet + test-tube illustrations (uniform speed, random heights).
// ============================================================
function CompoundIcon({ name }) {
// Stylized molecular icons per compound.
if (name === 'flavonoid') {
return (
);
}
if (name === 'polifenol') {
return (
);
}
// alkaloid
return (
);
}
// SVG sachet icon — small version for parallax field
function MiniSachet({ color = '#E85D1A' }) {
return (
);
}
// Test tube icon
function TestTube({ fill = '#E85D1A' }) {
return (
);
}
// ============================================================
// LabImageStack — stack of 5 lab certificate pages that slide in
// from the right (one-by-one with stagger when the section enters
// the viewport), then auto-rotate which one is "on top". Clicking
// the stack opens the full PDF in a new tab.
// ============================================================
function LabImageStack() {
const wrapRef = React.useRef(null);
const [entered, setEntered] = React.useState(false);
const [top, setTop] = React.useState(0);
const IMAGES = [
'assets/lab-1.webp',
'assets/lab-2.webp',
'assets/lab-3.webp',
'assets/lab-4.webp',
'assets/lab-5.webp',
];
// Trigger slide-in when section enters viewport
React.useEffect(() => {
if (!wrapRef.current) return;
const io = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) setEntered(true); });
}, { threshold: 0.18 });
io.observe(wrapRef.current);
return () => io.disconnect();
}, []);
// Auto-rotate which page is on top
React.useEffect(() => {
if (!entered) return;
const id = setInterval(() => setTop(t => (t + 1) % IMAGES.length), 3200);
return () => clearInterval(id);
}, [entered]);
// Manual cycle on click — opens the full PDF on the topmost click
const handleClick = () => {
window.open(
'https://drive.google.com/file/d/1yXF9ZxzD2h1y6FrcyWortXIfaAwFodcN/view',
'_blank',
'noopener,noreferrer'
);
};
return (
{IMAGES.map((src, i) => {
// Distance from "top" position (0 = on top, larger = further back)
const dist = (i - top + IMAGES.length) % IMAGES.length;
const offsetX = dist * 14; // px shift to the right
const offsetY = dist * -10; // px shift up
const rot = (i % 2 === 0 ? -1 : 1) * (2 + dist * 1.2);
const scale = 1 - dist * 0.04;
const opacity = dist === 0 ? 1 : Math.max(0.35, 1 - dist * 0.18);
const zIndex = IMAGES.length - dist;
// Entrance: start far to the right, slide into stacked pose with stagger
const enterX = entered ? offsetX : 480 + i * 60;
const enterRot = entered ? rot : rot + 18;
const enterOpacity = entered ? opacity : 0;
return (
Page {i + 1}/5
);
})}
Buka PDF lengkap
);
}
function SectionLab() {
const sectionRef = React.useRef(null);
const [scrollY, setScrollY] = React.useState(0);
const [intro, setIntro] = React.useState(0);
React.useEffect(() => {
let raf = 0;
const onScroll = () => {
if (!sectionRef.current) return;
const rect = sectionRef.current.getBoundingClientRect();
// Slide-in intro: 0 when top below viewport, 1 when top reaches 50% viewport.
const introT = Math.max(0, Math.min(1, 1 - rect.top / (window.innerHeight * 0.6)));
cancelAnimationFrame(raf);
raf = requestAnimationFrame(() => {
setIntro(introT);
setScrollY(window.scrollY);
});
};
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
return () => { window.removeEventListener('scroll', onScroll); cancelAnimationFrame(raf); };
}, []);
const COMPOUNDS = [
{
key: 'flavonoid',
name: 'Flavonoid',
tag: 'Senyawa Aktif 01',
text: 'Senyawa yang memiliki sifat antioksidan dan antiinflamasi.',
},
{
key: 'polifenol',
name: 'Polifenol',
tag: 'Senyawa Aktif 02',
text: 'Senyawa dengan aktivitas antibakteri dan antijamur.',
},
{
key: 'alkaloid',
name: 'Alkaloid',
tag: 'Senyawa Aktif 03',
text: 'Senyawa yang dikenal memiliki sifat analgesik dan antispasmodik alami.',
},
];
// Parallax field: 12 items distributed across the width at random heights.
// Uniform vertical speed driven by scrollY, random base offset.
const FIELD = React.useMemo(() => {
const items = [];
const n = 14;
for (let i = 0; i < n; i++) {
const isTube = i % 3 === 0;
items.push({
kind: isTube ? 'tube' : 'sachet',
leftPct: (i + 0.5) * (100 / n) + (Math.random() * 4 - 2),
baseY: Math.random() * 80 - 40, // random base vertical offset (px)
scale: isTube ? 0.85 + Math.random() * 0.3 : 0.7 + Math.random() * 0.5,
rot: (Math.random() * 16 - 8),
speed: 0.30 + Math.random() * 0.08, // ~uniform with tiny variation so it doesn't look canned
color: ['#E85D1A', '#C84A14', '#1F1612', '#6A7A5C'][i % 4],
});
}
return items;
}, []);
const slideUp = (1 - intro) * 120; // px slide-in
return (
{/* Rising white bubbles — fizz that seamlessly bubbles upward as you
scroll into the lab section. Sits behind the card; clipped by .sl
overflow so they appear contained within the section bounds. */}
Berdasarkan hasil uji laboratorium, Yojea mengandung senyawa aktif berupa
flavonoid, alkaloid, dan polifenol.
Senyawa-senyawa ini secara umum dikenal terdapat pada bahan alami dan sering
dikaitkan dengan pemeliharaan kesehatan, termasuk dalam membantu mengurangi
ketidaknyamanan yang berkaitan dengan menstruasi.
{/* Lab result image carousel — slides in from the right.
5 certificate pages auto-rotate; clicking opens the full PDF.
Replaces the prior 3D test tube. */}