// Main app — routing, state, chrome wiring const { useState, useEffect } = React; function CustomCursor() { const dotRef = React.useRef(null); const ringRef = React.useRef(null); const mx = React.useRef(-100), my = React.useRef(-100); const rx = React.useRef(-100), ry = React.useRef(-100); const raf = React.useRef(null); const stateRef = React.useRef('default'); const [state, setState] = React.useState('default'); const [ripples, setRipples] = React.useState([]); const [visible, setVisible] = React.useState(true); React.useEffect(() => { if (!window.matchMedia('(hover: hover) and (pointer: fine)').matches) return; const onMove = (e) => { mx.current = e.clientX; my.current = e.clientY; setVisible(true); }; const onLeave = () => setVisible(false); const onEnter = () => setVisible(true); const onDown = () => { stateRef.current = 'click'; setState('click'); }; const onUp = () => { stateRef.current = 'default'; setState('default'); }; const onOver = (e) => { if (stateRef.current === 'click') return; const s = e.target.closest('button,a,input,select,[role="button"]') ? 'hover' : 'default'; stateRef.current = s; setState(s); }; const onClick = (e) => { const id = Date.now() + Math.random(); setRipples(r => [...r, { x: e.clientX, y: e.clientY, id }]); setTimeout(() => setRipples(r => r.filter(p => p.id !== id)), 650); }; const loop = () => { rx.current += (mx.current - rx.current) * 0.18; ry.current += (my.current - ry.current) * 0.18; if (dotRef.current) dotRef.current.style.transform = `translate(calc(${mx.current}px - 50%), calc(${my.current}px - 50%))`; if (ringRef.current) ringRef.current.style.transform = `translate(calc(${rx.current}px - 50%), calc(${ry.current}px - 50%))`; raf.current = requestAnimationFrame(loop); }; document.addEventListener('mousemove', onMove, { passive: true }); document.addEventListener('mousedown', onDown); document.addEventListener('mouseup', onUp); document.addEventListener('mouseover', onOver, { passive: true }); document.addEventListener('click', onClick); document.documentElement.addEventListener('mouseleave', onLeave); document.documentElement.addEventListener('mouseenter', onEnter); raf.current = requestAnimationFrame(loop); return () => { document.removeEventListener('mousemove', onMove); document.removeEventListener('mousedown', onDown); document.removeEventListener('mouseup', onUp); document.removeEventListener('mouseover', onOver); document.removeEventListener('click', onClick); document.documentElement.removeEventListener('mouseleave', onLeave); document.documentElement.removeEventListener('mouseenter', onEnter); cancelAnimationFrame(raf.current); }; }, []); if (!window.matchMedia('(hover: hover) and (pointer: fine)').matches) return null; const dotSize = state === 'click' ? 12 : 7; const ringSize = state === 'hover' ? 44 : state === 'click' ? 20 : 32; return ( <>
{ripples.map(r => (
))} ); }; function App() { const [tweaks, setTweak] = window.useTweaks({ theme: 'light', accent: '#7a2628', density: 'comfortable', buttonShape: 'pill', }); const [viewport, setViewport] = useState('desktop'); const getInitialPage = () => { const path = (window.WP_PATH || '/').toLowerCase(); if (path.includes('order-received')) return 'ThankYou'; if (path.includes('order-pay')) return 'Home'; // let woocommerce.php handle CC Avenue pay page if (path.includes('/checkout')) return 'Checkout'; if (path.includes('/cart')) return 'Cart'; if (path.includes('/shop')) return 'Shop'; if (path.includes('/my-account')) return 'Account'; return 'Home'; }; // Extract order ID from URL like /checkout/order-received/12345/ const getOrderId = () => { const match = (window.WP_PATH || '').match(/order-received\/(\d+)/); return match ? match[1] : null; }; const [page, setPage] = useState(getInitialPage()); const [productId, setProductId] = useState('classic-chyawanprash'); const [cart, setCart] = useState([]); const [wishlist, setWishlist] = useState([]); const [cartOpen, setCartOpen] = useState(false); const [searchOpen, setSearchOpen] = useState(false); const [quizOpen, setQuizOpen] = useState(false); const [chatOpen, setChatOpen] = useState(false); const [stickyATC, setStickyATC] = useState(null); const [stickyVisible, setStickyVisible] = useState(false); const [selectedIngredient, setSelectedIngredient] = useState(null); const [ingredientClosing, setIngredientClosing] = useState(false); const closeIngredient = () => { setIngredientClosing(true); setTimeout(() => { setIngredientClosing(false); setSelectedIngredient(null); }, 250); }; const [zoomImg, setZoomImg] = useState(null); const [zoomClosing, setZoomClosing] = useState(false); const closeZoom = () => { setZoomClosing(true); setTimeout(() => { setZoomClosing(false); setZoomImg(null); }, 250); }; const stickyTimerRef = React.useRef(null); const handleStickyATC = (data) => { if (data) { clearTimeout(stickyTimerRef.current); setStickyATC(data); setStickyVisible(true); } else { setStickyVisible(false); stickyTimerRef.current = setTimeout(() => setStickyATC(null), 280); } }; // Theme application useEffect(() => { const root = document.documentElement; root.setAttribute('data-theme', tweaks.theme || 'light'); // accent const a = { accent: tweaks.accent && tweaks.accent.startsWith('#') ? tweaks.accent : '#7a2628', gold: '#b8945e' }; // Apply on viewport element rather than root so dark theme tokens still win document.querySelectorAll('.viewport').forEach(v => { v.style.setProperty('--accent', a.accent); v.style.setProperty('--gold', a.gold); }); }, [tweaks.theme, tweaks.accent]); const goto = (p, pid) => { setPage(p); if (pid) setProductId(pid); window.scrollTo({ top: 0, behavior: 'instant' }); const v = document.querySelector('.viewport'); if (v) v.scrollTop = 0; }; const addToCart = (p, qty = 1, sub = false) => { setCart(curr => { const existing = curr.find(x => x.id === p.id); if (existing) { return curr.map(x => x.id === p.id ? { ...x, qty: x.qty + qty } : x); } return [...curr, { ...p, qty, sub }]; }); setCartOpen(true); }; const updateQty = (id, qty) => setCart(c => c.map(x => x.id === id ? { ...x, qty } : x)); const removeItem = (id) => setCart(c => c.filter(x => x.id !== id)); const clearCart = () => setCart([]); const toggleWish = (id) => setWishlist(w => w.includes(id) ? w.filter(x => x !== id) : [...w, id]); const cartCount = cart.reduce((s, i) => s + i.qty, 0); const renderPage = () => { const common = { vp: viewport, goto, addToCart, wishlist, toggleWish, openQuiz: () => setQuizOpen(true) }; switch (page) { case 'Home': return ; case 'Shop': return ; case 'Product': return setQuizOpen(true)} onZoom={setZoomImg}/>; case 'Cart': return ; case 'Checkout': return ; case 'ThankYou': return ; case 'About': return ; case 'Ingredients': return ; case 'Journal': return ; case 'Contact': return ; case 'Account': return ; default: return ; } }; const Tweaks = window.TweaksPanel; const TS = window.TweakSection; const TR = window.TweakRadio; const TC = window.TweakColor; // WordPress mode: no design chrome, full-page layout if (window.IS_WORDPRESS) { return ( <>
setCartOpen(true)} openSearch={() => setSearchOpen(true)} cartCount={cartCount} openQuiz={() => setQuizOpen(true)}/>
{renderPage()}
{/* Modals outside main div */} {selectedIngredient && (
e.stopPropagation()} style={{ width: 'min(480px, 100%)', background: 'var(--bg)', borderRadius: 20, padding: 0, animation: ingredientClosing ? 'slideUpOut 0.25s forwards' : 'slideUp 0.3s', overflow: 'hidden' }}>
{selectedIngredient.img ? {selectedIngredient.name} :
{selectedIngredient.name}
}
{selectedIngredient.role}

{selectedIngredient.name}

{selectedIngredient.latin}
Sourcing
{selectedIngredient.note}
)} {zoomImg && (
Product zoom e.stopPropagation()} style={{ maxWidth: '90vw', maxHeight: '90vh', objectFit: 'contain', borderRadius: 12, animation: zoomClosing ? 'slideUpOut 0.25s forwards' : 'imageReveal 0.35s cubic-bezier(0.22,1,0.36,1)', cursor: 'default', boxShadow: '0 32px 80px rgba(0,0,0,0.4)' }}/>
)} {stickyATC && (
{stickyATC.p.name}
₹{stickyATC.price.toLocaleString('en-IN')}
)} setCartOpen(false)} items={cart} updateQty={updateQty} removeItem={removeItem} goto={(p) => { setCartOpen(false); goto(p); }}/> setSearchOpen(false)} goto={(p) => { setSearchOpen(false); goto(p); }}/> setQuizOpen(false)} goto={goto} addToCart={addToCart}/> ); } return ( <> setTweak('theme', tweaks.theme === 'dark' ? 'light' : 'dark')}/>
setCartOpen(true)} openSearch={() => setSearchOpen(true)} cartCount={cartCount} openQuiz={() => setQuizOpen(true)}/>
{renderPage()}
{/* WhatsApp FAB */}
{/* Image zoom lightbox */} {zoomImg && (
Product zoom e.stopPropagation()} style={{ maxWidth: '90vw', maxHeight: '90vh', objectFit: 'contain', borderRadius: 12, animation: zoomClosing ? 'slideUpOut 0.25s forwards' : 'imageReveal 0.35s cubic-bezier(0.22,1,0.36,1)', cursor: 'default', boxShadow: '0 32px 80px rgba(0,0,0,0.4)' }}/>
)} {/* Ingredient detail modal — outside viewport so position:fixed works correctly */} {selectedIngredient && (
e.stopPropagation()} style={{ width: 'min(480px, 100%)', background: 'var(--bg)', borderRadius: 20, padding: 0, animation: ingredientClosing ? 'slideUpOut 0.25s forwards' : 'slideUp 0.3s', overflow: 'hidden' }}>
{selectedIngredient.img ? {selectedIngredient.name} :
{selectedIngredient.name}
}
{selectedIngredient.role}

{selectedIngredient.name}

{selectedIngredient.latin}
Sourcing
{selectedIngredient.note}
)} {/* Sticky ATC bar — outside viewport so position:fixed works correctly */} {stickyATC && (
{stickyATC.p.name}
₹{stickyATC.price.toLocaleString('en-IN')}
)} {/* Global overlays (outside viewport) */} setCartOpen(false)} items={cart} updateQty={updateQty} removeItem={removeItem} goto={(p) => { setCartOpen(false); goto(p); }}/> setSearchOpen(false)} goto={(p) => { setSearchOpen(false); goto(p); }}/> setQuizOpen(false)} goto={goto} addToCart={addToCart}/> {/* Tweaks Panel */} {Tweaks && ( setTweak('theme', v)} options={['light','dark']}/> setTweak('accent', v)} options={['#7a2628','#8a4d3e','#b8945e','#3d5240']}/> setTweak('density', v)} options={['compact','comfortable']}/> setQuizOpen(true)}/> setChatOpen(true)}/> addToCart(window.PRODUCTS[0])}/> )} ); }; ReactDOM.createRoot(document.getElementById('root')).render();