/* global React, ReactDOM */ const { useState, useEffect, useRef, useCallback } = React; const D = window.PORTFOLIO_DATA; const C = D.copy; // Customizer copy — edit via Appearance → Customize → Kalligeris Copy // Parse intrinsic dims from a URL: e.g. "-1024x683.jpg" or "-683x1024.jpg". // Falls back to orientation flag when no dims present (e.g. "-scaled.jpg"). function aspectFor(item) { const m = item.src && item.src.match(/[-_](\d{2,4})x(\d{2,4})\.(jpe?g|png|webp)/i); if (m) return `${m[1]} / ${m[2]}`; if (item.o === 'p') return '2 / 3'; if (item.o === 's') return '1 / 1'; return '3 / 2'; } // ---------- Tweaks (in-design) ---------- const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "theme": "cinema", "layout": "mosaic", "cursor": false, "marquee": true, "accent": "#C2884A" } /*EDITMODE-END*/; function postEdit(edits) { try {window.parent.postMessage({ type: '__edit_mode_set_keys', edits }, '*');} catch (e) {} } function useTweaks() { const [t, setT] = useState(TWEAK_DEFAULTS); const set = (k, v) => {setT((p) => ({ ...p, [k]: v }));postEdit({ [k]: v });}; return [t, set]; } // ---------- Custom cursor ---------- function Cursor({ enabled }) { const dot = useRef(null); const ring = useRef(null); const [label, setLabel] = useState(''); useEffect(() => { if (!enabled) return; let mx = -100,my = -100,rx = -100,ry = -100,raf; const tick = () => { rx += (mx - rx) * 0.18;ry += (my - ry) * 0.18; if (dot.current) dot.current.style.transform = `translate(${mx}px,${my}px)`; if (ring.current) ring.current.style.transform = `translate(${rx}px,${ry}px)`; raf = requestAnimationFrame(tick); }; const onMove = (e) => {mx = e.clientX;my = e.clientY;}; const onOver = (e) => { const el = e.target.closest('[data-cursor]'); const v = el ? el.getAttribute('data-cursor') : ''; setLabel(v || ''); document.documentElement.classList.toggle('cursor-hover', !!v); }; window.addEventListener('mousemove', onMove); window.addEventListener('mouseover', onOver); raf = requestAnimationFrame(tick); return () => { cancelAnimationFrame(raf); window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseover', onOver); }; }, [enabled]); if (!enabled) return null; return ( <>
{label}
); } // ---------- Top nav ---------- function Nav({ now, onJump }) { const sections = [ ...D.sections.map((s) => ({ id: s.slug, label: s.kicker || s.title })), { id: 'studio', label: 'Studio' }, { id: 'contact', label: 'Contact' }, ]; return ( ); } // ---------- Marquee ---------- function Marquee({ show }) { if (!show) return null; const items = C.marquee.split(',').map((s) => s.trim()); const row = items.concat(items, items); return (
{row.map((x, i) => {x} )}
); } // ---------- Hero ---------- function Hero({ onJump }) { // heroR2 is rendered as plain text — the italic & is a design element kept in markup return (
(01) — Index {C.heroVolume}

{C.heroR1} {C.heroR2} {C.heroR3} {C.heroR4}

{C.heroFeaturingLabel}
{D.sections.map((s, i) => { const count = s.kind === 'projects' ? (s.projects || []).reduce((a, p) => a + (p.images || []).length, 0) : (s.items || []).length; const label = s.kind === 'motion' ? 'motion' : s.kind === 'projects' ? 'spaces' : 'stills'; return `${count} ${label}`; }).join(' · ')}
{C.heroRecentLabel}
{C.heroRecent}
{D.heroPicks.map((src, i) =>
)}
STK / 25 {C.tagline}
); } // ---------- Section header ---------- function SectionHead({ idx, kicker, title, count, sub }) { return (
({idx}) {kicker}

{title}

{count} {count === 1 ? 'piece' : 'pieces'} {sub ? {sub} : null}
); } // ---------- Motion ---------- function MotionGrid({ items, onPlay, layout }) { const cls = layout === 'index' ? 'motion-grid index' : layout === 'stack' ? 'motion-grid stack' : 'motion-grid'; return (
{items.map((v, i) =>
onPlay(v)} data-cursor="PLAY">
{v.title} {e.currentTarget.src = `https://img.youtube.com/vi/${v.id}/hqdefault.jpg`;}} />
Play
№ {String(i + 1).padStart(2, '0')}
{v.title} · {v.year}
{v.tag} — {v.subtitle}
)}
); } // ---------- Food masonry ---------- function FoodGrid({ slug, items, onView, layout }) { return (
{items.map((it, i) =>
onView(slug, i)} data-cursor="VIEW">
№ {String(i + 1).padStart(2, '0')}
)}
); } // ---------- Interiors ---------- function InteriorsBlock({ projects, onView, layout }) { return (
{projects.map((proj, p) =>
Project {String(p + 1).padStart(2, '0')} / {String(projects.length).padStart(2, '0')} {proj.location} · {proj.year}

{proj.name}

{proj.blurb}

{proj.images.map((im, i) =>
onView(proj.slug, i)} data-cursor="VIEW">
)}
)}
); } // ---------- Lightbox ---------- function Lightbox({ open, list, index, onClose, onPrev, onNext }) { useEffect(() => { if (!open) return; const k = (e) => { if (e.key === 'Escape') onClose(); else if (e.key === 'ArrowLeft') onPrev(); else if (e.key === 'ArrowRight') onNext(); }; window.addEventListener('keydown', k); document.body.style.overflow = 'hidden'; return () => {window.removeEventListener('keydown', k);document.body.style.overflow = '';}; }, [open, onClose, onPrev, onNext]); if (!open || !list) return null; const item = list[index]; return (
{String(index + 1).padStart(2, '0')} / {String(list.length).padStart(2, '0')}
e.stopPropagation()} data-cursor="" />
); } // ---------- Video modal ---------- function VideoModal({ video, onClose }) { useEffect(() => { if (!video) return; const k = (e) => {if (e.key === 'Escape') onClose();}; window.addEventListener('keydown', k); document.body.style.overflow = 'hidden'; return () => {window.removeEventListener('keydown', k);document.body.style.overflow = '';}; }, [video, onClose]); if (!video) return null; return (
e.stopPropagation()}>
{video.title} — {video.subtitle}