/* global React */ const { useState, useEffect, useRef } = React; function Portal({ onEnter }) { const [phraseIdx, setPhraseIdx] = useState(0); const [fading, setFading] = useState(false); const phrases = window.PORTAL_PHRASES || [""]; const ringRef = useRef(null); const scrollRef = useRef(null); const [radius, setRadius] = useState(240); const [scrollY, setScrollY] = useState(0); useEffect(() => { const measure = () => { const el = ringRef.current; if (!el) return; const r = el.getBoundingClientRect(); const size = Math.min(r.width, r.height); setRadius(Math.max(150, size * 0.5)); }; measure(); window.addEventListener("resize", measure); return () => window.removeEventListener("resize", measure); }, []); useEffect(() => { const el = scrollRef.current; if (!el) return; let raf; const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(() => setScrollY(el.scrollTop)); }; el.addEventListener("scroll", onScroll, { passive: true }); return () => {el.removeEventListener("scroll", onScroll);cancelAnimationFrame(raf);}; }, []); useEffect(() => { const iv = setInterval(() => { setFading(true); setTimeout(() => { setPhraseIdx((i) => (i + 1) % phrases.length); setFading(false); }, 1100); }, 4200); return () => clearInterval(iv); }, [phrases.length]); // scroll-reveal effect on sections useEffect(() => { const obs = new IntersectionObserver((entries) => { entries.forEach((e) => {if (e.isIntersecting) e.target.classList.add("revealed");}); }, { threshold: 0.12, root: scrollRef.current }); document.querySelectorAll('.reveal').forEach((el) => obs.observe(el)); return () => obs.disconnect(); }, []); const thresholds = [ { key: "gallery", dev: "दर्शन", en: "Gallery", meta: "the archive of glances", angle: -90 }, { key: "themes", dev: "विषय", en: "Themes", meta: "philosophical explorations", angle: -30 }, { key: "prints", dev: "मुद्रण", en: "Prints", meta: "owning fragments of silence", angle: 30 }, { key: "journal", dev: "लेखन", en: "Journal", meta: "thoughts from wandering", angle: 90 }, { key: "about", dev: "परिचय", en: "About", meta: "the witness behind the lens", angle: 150 }, { key: "feedback", dev: "प्रतिक्रिया", en: "Feedback", meta: "leave emotional residue", angle: 210 }]; const all = window.DARSHAN || []; // Five curated photographs become the editorial "preface" spreads. // The remaining fifteen drift below as a horizontal ribbon (all twenty visible). const featured = [all[0], all[3], all[10], all[6], all[15]].filter(Boolean); const driftLane = all; // marquee shows all twenty const themePreviews = [ { key: "street", dev: "मार्ग", en: "Street", photo: all[10], line: "the city is a slow scripture" }, { key: "travel", dev: "यात्रा", en: "Travel", photo: all[6], line: "every road is a small pilgrimage" }, { key: "documentary", dev: "साक्ष्य", en: "Documentary", photo: all[4], line: "to witness is the oldest verb" }, { key: "nature", dev: "प्रकृति", en: "Nature", photo: all[15], line: "the divine wears weather today" }, { key: "sacred", dev: "पावन", en: "The Sacred", photo: all[11], line: "ordinary things, lit from inside" }]; return (
THESE PHOTOGRAPHS WERE MADE WITH ONE BEATING HEART, AND THEY WILL OPEN TO ANOTHER.
and they will only open to another.
MADE ON FOOT ACROSS BHARAT, BETWEEN SPANS OF 5 YEARS
five glances below — the remaining fifteen drift past at the bottom.
“each frame is asking for one breath,
— a note pinned above the darkroom
no more — and no less.
the work is gathered into five philosophical chapters — each a slow river, not a category.
each chamber holds the same archive, lit from a different angle.
I work in street, travel, and documentary photography — and I love the quiet detail shots of nature. It has been five years since I first held my Fujifilm X-T200; photography has been the ride that taught me to see life with a different lens.
This site is a souvenir to the world: a small, organised reflection of mine, so that you may see a fragment of what I have been lucky enough to witness.