/* Veld — homepage app */ const { useState, useEffect, useRef } = React; /* Parallax scroll hook — same shape as the original useParallax */ function useParallax(speed = 0.3) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; let raf = 0; const onScroll = () => { if (raf) return; raf = requestAnimationFrame(() => { raf = 0; const rect = el.getBoundingClientRect(); const viewCenter = window.innerHeight / 2; const elCenter = rect.top + rect.height / 2; const delta = (viewCenter - elCenter) * speed; el.style.transform = `translateY(${delta}px)`; }); }; window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); return () => { window.removeEventListener('scroll', onScroll); if (raf) cancelAnimationFrame(raf); }; }, [speed]); return ref; } /* Full-bleed parallax banner */ function ParallaxBanner({ src, caption, height = '36vw', minH = '240px', maxH = '480px', speed = 0.22 }) { const ref = useParallax(speed); return (
{caption &&
{caption}
}
); } /* ---------- SCROLL PROGRESS THREAD (the golden thread) ---------- */ const WAYPOINTS = [ { id: 'top', label: 'Hearth' }, { id: 'what', label: 'Veld' }, { id: 'fireflies', label: 'Magic' }, { id: 'arc', label: 'The Arc' }, { id: 'travelers', label: 'The Room' }, { id: 'tiers', label: 'Contribution' }, { id: 'apply', label: 'Apply' }, ]; function ScrollThread() { const thumbRef = useRef(null); const wayRefs = useRef([]); const rafRef = useRef(0); const activeIdRef = useRef(null); useEffect(() => { const onScroll = () => { if (rafRef.current) return; rafRef.current = requestAnimationFrame(() => { rafRef.current = 0; const total = document.documentElement.scrollHeight - window.innerHeight; const progress = total > 0 ? Math.min(100, Math.max(0, (window.scrollY / total) * 100)) : 0; if (thumbRef.current) thumbRef.current.style.height = `${progress}%`; let newActive = null; for (const w of WAYPOINTS) { const el = document.getElementById(w.id); if (!el) continue; const r = el.getBoundingClientRect(); if (r.top < window.innerHeight / 2 && r.bottom > window.innerHeight / 2) { newActive = w.id; break; } } if (newActive !== activeIdRef.current) { activeIdRef.current = newActive; WAYPOINTS.forEach((w, i) => { const node = wayRefs.current[i]; if (!node) return; const pos = (i / (WAYPOINTS.length - 1)) * 100; const passed = progress > pos; node.className = `way ${activeIdRef.current === w.id ? 'active' : passed ? 'passed' : ''}`; }); } }); }; window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); return () => { window.removeEventListener('scroll', onScroll); if (rafRef.current) cancelAnimationFrame(rafRef.current); }; }, []); return ( ); } /* ---------- NAV ---------- */ const NAV_LINKS = [ { key: 'castle', label: 'The Castle' }, { key: 'week', label: 'The Week' }, { key: 'rooms', label: 'Rooms & Pricing' }, { key: 'who', label: 'Who Comes' }, { key: 'faq', label: 'FAQ' }, ]; function Nav({ page }) { const { visible, atTop } = useSmartNav(80); return ( ); } /* ---------- HERO ---------- */ let HERO_INTRO_DONE = false; const HERO_HOOKS = [ { primary: "You’ve spent years becoming someone.", accent: "For five days, set it down." }, { primary: "Builders, creators, thinkers, visionaries —", accent: "all here to stop performing." }, { primary: "From every field, every background.", accent: "Here to connect, not to perform." }, { primary: "Stop being your title, and see", accent: "what becomes possible." }, ]; function Hero({ hookIndex }) { const hook = HERO_HOOKS[hookIndex % HERO_HOOKS.length]; const bgRef = useParallax(0.32); const zoomRef = useRef(null); const innerRef = useRef(null); const enterVeilRef = useRef(null); const guestName = useGuestName(); const returning = useReturningVisitor(); const [greeting, setGreeting] = useState(''); useEffect(() => { setGreeting(getTimeGreeting()); }, []); // The hero PINS; scroll is consumed by a push THROUGH the gate, then releases // to the next section — you enter, and arrive on the other side. useEffect(() => { const el = zoomRef.current; if (!el) return; const inner = innerRef.current; const veil = enterVeilRef.current; let raf = 0; let active = false; const clamp = (x) => Math.min(Math.max(x, 0), 1); const smooth = (x) => { x = clamp(x); return x * x * (3 - 2 * x); }; const apply = () => { raf = 0; const wrap = el.closest('.hero-pin-wrap'); if (!wrap) return; const total = wrap.offsetHeight - window.innerHeight; const p = total > 0 ? clamp(-wrap.getBoundingClientRect().top / total) : 0; const scale = 1 + smooth(p) * 3.4; // hold + push THROUGH the gate to ~4.4x el.style.transform = `scale(${scale.toFixed(4)})`; if (inner) inner.style.opacity = (1 - smooth(p / 0.45)).toFixed(3); if (veil) veil.style.opacity = smooth((p - 0.62) / 0.38).toFixed(3); }; const onScroll = () => { if (active && !raf) raf = requestAnimationFrame(apply); }; const onEnd = () => { active = true; el.style.animation = 'none'; apply(); }; el.addEventListener('animationend', onEnd); window.addEventListener('scroll', onScroll, { passive: true }); return () => { el.removeEventListener('animationend', onEnd); window.removeEventListener('scroll', onScroll); if (raf) cancelAnimationFrame(raf); }; }, []); const showReturning = returning && !guestName; const [intro] = useState(() => { const first = !HERO_INTRO_DONE; HERO_INTRO_DONE = true; return first; }); return (
{guestName && (

A seat has been held for {guestName}.

)} {greeting && (

{greeting}

)}
October 4 — 8, 2026 · Zamek Czocha ·

Veld

{showReturning ? (

Welcome back. The gate is still open.

) : (

{hook.primary} {hook.accent}

)}

Veld is a five-day, invitation-only gathering — eighty builders, artists and thinkers, living together inside eight centuries of stone. Every bed, every feast, every fire-circle included.

Five Days · Four Nights · Eighty Souls · From €1,800, all-in

No stages. No logos. No audience.

Step over the threshold
Begin
); } /* ---------- MANIFESTO — 4 stanzas, each lands on its own scroll ---------- */ function ManifestoStanza({ glyph, html }) { const ref = useRef(null); const [visible, setVisible] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( ([e]) => { if (e.isIntersecting) { setVisible(true); io.disconnect(); } }, { threshold: 0.35, rootMargin: '0px 0px -10% 0px' } ); io.observe(el); return () => io.disconnect(); }, []); return (
{glyph}
); } const STANZAS = [ { glyph: "✦", html: "Veld is an interdisciplinary crossroads of doers, thinkers, and creators, a community of communities, where authentic conversation, deep vulnerability, and play make space for the kind of exchange that isn’t possible elsewhere." }, { glyph: "✺", html: "It is where you come to debate big ideas, pull yourself out of daily monotone, and rediscover the truth of who you really are. A place to learn and unlearn, to find your magic through meaningful connection, and to flow with serendipity, synchronicity, and surprise." }, { glyph: "◈", html: "Veld is a place out of time, where magic blends with reality and every form of life arrives as its true self. Expression, co-creation, trust, embodiment, community: not words, but ways of living." }, { glyph: "✷", html: "Veld is the place we dreamed of as children. The hope we hold for the world. The place every human being would want to come back to." }, ]; function Manifesto() { return (
— What is Veld —

{STANZAS.map((s, i) => ( ))}
); } /* ---------- WHY THIS EXISTS — founder voice ---------- */ function WhyExists() { const ref = useRef(null); const bgRef = useParallax(0.16); const [visible, setVisible] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( ([e]) => { if (e.isIntersecting) { setVisible(true); io.disconnect(); } }, { threshold: 0.3 } ); io.observe(el); return () => io.disconnect(); }, []); return (
— Why this exists —

The most alive I’ve ever felt at any event wasn’t in the programme. It was the conversation at 1am, the walk the next morning, the moment someone said something honest and the whole room shifted.

So we stopped trying to recreate the official part. We just built the part that matters.

Five days. A castle. People who show up as themselves: not their CV, not their company, not their title. Eighty humans, present and undefended, in one of the most extraordinary buildings in Europe.

That’s it. That’s the whole idea.

— The conveners
); } /* ---------- THREE MOVEMENTS ---------- */ const MOVEMENTS = [ { glyph: "✶", num: "01", titleA: "Shed the title.", titleB: "Be human first.", body: "You will not be your CEO badge, your VC fund, your TEDx talk, your accolade. For five days the room treats you as you are, not as what you have done, and you may discover that you prefer it that way." }, { glyph: "✺", num: "02", titleA: "Unlearn what", titleB: "no longer serves.", body: "Vulnerability is the entry fee. Bring the question you have been avoiding. Bring the part of yourself the work calendar has been quiet about. Walk out lighter than you walked in." }, { glyph: "❋", num: "03", titleA: "Co-create the", titleB: "experience.", body: "Veld is not plug-and-play. You are not a guest at a festival. You are part of how the week happens, through conversations you start, rooms you fill, gifts you offer. Nothing is scheduled; everything is possible. What you bring is the programme; what you leave with is up to you." }, ]; function Movements() { return (
— Three quiet promises —

What might happen if you say yes.

{MOVEMENTS.map((m, i) => (
{m.num} / 03
{m.glyph}

{m.titleA}
{m.titleB}

{m.body}

))}
); } /* ---------- KINGS HALL FEATURED BANNER ---------- */ function KingsHall() { const sectionRef = useRef(null); const zoomRef = useRef(null); const copyRef = useRef(null); const [revealed, setRevealed] = useState(false); useEffect(() => { let raf = 0; const update = () => { raf = 0; const section = sectionRef.current; const zoom = zoomRef.current; const copy = copyRef.current; if (!section || !zoom) return; const rect = section.getBoundingClientRect(); const vh = window.innerHeight; const total = rect.height + vh; const passed = vh - rect.top; const p = Math.max(0, Math.min(1, passed / total)); const scale = 1 + p * 0.75; const brightness = 0.78 - p * 0.30; zoom.style.transform = `scale(${scale.toFixed(3)})`; zoom.style.filter = `sepia(10%) contrast(1.08) brightness(${brightness.toFixed(3)}) saturate(0.92)`; if (copy) { const o = Math.max(0, Math.min(1, (0.85 - p) / 0.35)); copy.style.opacity = o.toFixed(3); copy.style.transform = `translateY(${(p * -24).toFixed(1)}px)`; } // Reveal once the section first enters substantially if (!revealed && p > 0.05) setRevealed(true); }; const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); }; window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', onScroll, { passive: true }); update(); return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); if (raf) cancelAnimationFrame(raf); }; }, [revealed]); return (
The Knight’s Hall
Here.
Eight metres of ceiling. · Candlelight. · Silence between sentences.
); } /* ---------- THE PLACE — full-bleed, scramble heading, prominent ---------- */ function Place() { const ref = useRef(null); const [visible, setVisible] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( ([e]) => { if (e.isIntersecting) { setVisible(true); io.disconnect(); } }, { threshold: 0.35 } ); io.observe(el); return () => io.disconnect(); }, []); return (
— A place out of time —

Zamek Czocha,

A 13th-century castle on a forest lake. Towers, vaults, a great hall, a library that smells of leather, hidden passageways the staff still find by accident.

For five days, it is yours. Eighty souls and eight centuries of stone, agreeing to keep each other’s secrets.

51.0322° N · 15.3018° E · Founded 1247 · Lake Leśniańskie
); } /* ---------- THE ARC ---------- */ const DAYS = [ { n: "I", label: "Day One", title: "Arrival, & the crossing of the threshold.", time: "Oct 4 · Sun", body: "The world outside falls away at the gate. The first hours are unhurried: recognised faces, new ones, the warmth of a fire someone lit before you arrived. By the time the first meal ends, the room knows its own shape.", img: "images/day1-firepit.png", }, { n: "II", label: "Day Two", title: "Immersion, & the first real conversation.", time: "Oct 5 · Mon", body: "The castle starts to feel like a body; you learn its rooms, its rhythms, its quiet corners. Conversations that began as small talk shift somewhere more honest by the third or fourth crossing. By dusk, most of the titles people walked in with have quietly come off.", img: "images/castle-hall.jpg", }, { n: "III", label: "Day Three", title: "Deepening, & the version of you nobody’s met.", time: "Oct 6 · Tue", body: "You meet a version of yourself you weren’t planning to bring. Quiet rooms invite admission you didn’t prepare for. Loud rooms invite confession you didn’t prepare for either. After dark, the castle becomes another castle entirely — and so does the room you came to find.", img: "images/concept-party.png", }, { n: "IV", label: "Day Four", title: "Culmination, & what we built without noticing.", time: "Oct 7 · Wed", body: "What has been quietly assembling all week becomes visible. Small intentions find one another and grow into bigger ones. The last feast is long, candlelit, unhurried, and the conversations at this one tend to outlive the rest of the year.", img: "images/concept-workshop.png", }, { n: "V", label: "Day Five", title: "Return, & the promise carried home.", time: "Oct 8 · Thu", body: "Departure is deliberate, not abrupt. Promises made in the dark are repeated in daylight, so they survive the journey home. The gate opens once more, and you walk back into a world that has not changed, but you have.", img: "images/castle-bridge.jpg", }, ]; function DayRow({ d, i }) { const ref = useRef(null); const imgRef = useRef(null); const [visible, setVisible] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( ([e]) => { if (e.isIntersecting) { setVisible(true); io.disconnect(); } }, { threshold: 0.3 } ); io.observe(el); return () => io.disconnect(); }, []); // Scroll-driven scale on bg + content collapse as you scroll past useEffect(() => { let raf = 0; const update = () => { raf = 0; const section = ref.current; const img = imgRef.current; if (!section || !img) return; const rect = section.getBoundingClientRect(); const vh = window.innerHeight; const total = rect.height + vh; const passed = vh - rect.top; const p = Math.max(0, Math.min(1, passed / total)); // Bg zooms in slightly as you pass through (enters at scale 1.05, leaves at 1.2) const scale = 1.05 + p * 0.15; img.style.transform = `scale(${scale.toFixed(3)})`; }; const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); }; window.addEventListener('scroll', onScroll, { passive: true }); update(); return () => { window.removeEventListener('scroll', onScroll); if (raf) cancelAnimationFrame(raf); }; }, []); return (
{d.label} · {d.time}

{d.body}

); } function Arc() { const preludeRef = useRef(null); const [preludeVisible, setPreludeVisible] = useState(false); useEffect(() => { const el = preludeRef.current; if (!el) return; const io = new IntersectionObserver( ([e]) => { if (e.isIntersecting) { setPreludeVisible(true); io.disconnect(); } }, { threshold: 0.4 } ); io.observe(el); return () => io.disconnect(); }, []); return (
— The arc —
Seven centuries of stone.

What follows is not a schedule. It is the shape of the week as it has been told by everyone who has ever lived it.

{DAYS.map((d, i) => )}

And on the sixth morning, the gate opens once more.

); } /* ---------- WHO / WHO NOT ---------- */ const YES = [ "You’re tired of events where everyone is performing a version of themselves.", "You have a hunger for conversation that actually goes somewhere.", "You’re ready to be surprised, by others and by yourself.", "You want to connect with people across disciplines, not just your own world.", "You’re willing to contribute, not just consume.", ]; const NO = [ "You need a stage or a logo to feel present.", "You’re looking to pitch, sell, or network transactionally.", "You want a programme handed to you.", "You’re not ready to be a little vulnerable.", ]; function Who() { const ref = useRef(null); const [visible, setVisible] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( ([e]) => { if (e.isIntersecting) { setVisible(true); io.disconnect(); } }, { threshold: 0.25 } ); io.observe(el); return () => io.disconnect(); }, []); // Interleave the two streams so they meet at the seam like a zipper. const ZIP = []; const max = Math.max(YES.length, NO.length); for (let i = 0; i < max; i++) { if (YES[i]) ZIP.push({ side: 'come', text: YES[i] }); if (NO[i]) ZIP.push({ side: 'avoid', text: NO[i] }); } return (
— Who this is for —

Veld is not for everyone.

Two streams meet at the gate. Read down the seam, and feel which side you fall on.

Come, if… Don’t, if…
{ZIP.map((z, i) => (
))}
); } /* ---------- WHAT IT WILL MEAN ---------- */ const MEAN = [ { num: "i.", text: "Refresh, reset, reboot. Time for your soul to catch up with your week." }, { num: "ii.", text: "Freedom from everyday norms, and from the identity you wear at work." }, { num: "iii.", text: "Authentic relating, open communication, new vibrations." }, { num: "iv.", text: "Joy of being. The kind that arrives unannounced and stays a while." }, { num: "v.", text: "Trust in yourself, and in humanity, restored a few degrees." }, { num: "vi.", text: "A return to innate humanness, in an increasingly digital world." }, { num: "vii.", text: "Care and responsibility, for yourself, for others, for the shared space." }, { num: "viii.", text: "The need to replicate this more often. Hosts welcome." }, ]; /* ---------- MEAN — scrollytelling: each item big, then settles into the grid in place ---------- */ function Mean() { const wrapRef = useRef(null); const stageRefs = useRef([]); const settledRefs = useRef([]); const dotsRef = useRef([]); useEffect(() => { const wrap = wrapRef.current; if (!wrap) return; let raf = 0; let lastActive = -1; const N = MEAN.length; const update = () => { raf = 0; const rect = wrap.getBoundingClientRect(); const vh = window.innerHeight; const total = rect.height - vh; if (total <= 0) return; const scrolled = Math.max(0, -rect.top); const p = Math.max(0, Math.min(1, scrolled / total)); const stagePos = p * N; const active = Math.min(N - 1, Math.max(0, Math.floor(stagePos))); for (let i = 0; i < N; i++) { const ip = stagePos - i; // 0 = stage start, 1 = stage end // STAGE (big, centered): fades in at -0.12, full at 0.05, holds until 0.7, fades out by 0.95 let stageOp = 0; if (ip > -0.12 && ip < 0.05) stageOp = (ip + 0.12) / 0.17; else if (ip >= 0.05 && ip <= 0.7) stageOp = 1; else if (ip > 0.7 && ip < 0.95) stageOp = 1 - (ip - 0.7) / 0.25; // SETTLED (small, grid position): starts appearing as stage fades, full by 1.1 let settledOp = 0; if (ip > 0.7 && ip < 1.1) settledOp = (ip - 0.7) / 0.4; else if (ip >= 1.1) settledOp = 1; // settled also slides up a few px as it appears const ty = settledOp < 1 ? (1 - settledOp) * 12 : 0; const sEl = stageRefs.current[i]; if (sEl) { sEl.style.opacity = stageOp.toFixed(3); // Subtle scale: big at peak, slight downsize at end of its window const scale = ip < 0.05 ? (0.97 + (1 - Math.max(0, (0.05 - ip) / 0.17)) * 0.03) : ip > 0.7 ? (1 - Math.min(1, (ip - 0.7) / 0.25) * 0.12) : 1; sEl.style.transform = `scale(${scale.toFixed(3)})`; } const gEl = settledRefs.current[i]; if (gEl) { gEl.style.opacity = settledOp.toFixed(3); gEl.style.transform = `translateY(${ty.toFixed(1)}px)`; } } if (active !== lastActive) { lastActive = active; for (let i = 0; i < N; i++) { const d = dotsRef.current[i]; if (!d) continue; d.className = `dot ${i === active ? 'active' : (i < active ? 'passed' : '')}`; } } }; const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); }; window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', onScroll, { passive: true }); update(); return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); if (raf) cancelAnimationFrame(raf); }; }, []); return (
— What it may mean —

What it might mean for you to attend.

{MEAN.map((m, i) => (
stageRefs.current[i] = el} className="mean-stage"> {m.num}
))}
{MEAN.map((m, i) => (
settledRefs.current[i] = el} className="settled-item"> {m.num}
))}
{MEAN.map((_, i) => ( dotsRef.current[i] = el} className="dot" /> ))}
); } /* ---------- APPLY (full form, POSTs to /api/apply — same shape as the original) ---------- */ const AFFILIATIONS = [ // Codes preserved so the existing Resend handler keeps working. // Grouped visually — no group is privileged over another. { group: 'Lists you might recognise', items: [ { value: 'forbes', label: 'Forbes 30 Under 30' }, { value: 'tedx', label: 'TEDx / TED speaker' }, { value: 'shapers', label: 'WEF Global Shaper' }, { value: 'davos', label: 'Davos / WEF speaker' }, { value: 'burningman', label: 'Burning Man / Edge / Summit community' }, ], }, { group: 'Other ways in (just as welcome)', items: [ { value: 'vouch', label: 'Someone in the room is vouching for me' }, { value: 'artist', label: 'Artist, creator, or storyteller' }, { value: 'builder', label: 'Builder, founder, or operator' }, { value: 'activist', label: 'Activist, organiser, or civic builder' }, { value: 'thinker', label: 'Researcher, healer, teacher, or therapist' }, { value: 'other', label: 'Many worlds — let me explain in the letter' }, ], }, ]; const TIER_OPTIONS = [ { value: 'classic', label: 'A bed in a shared chamber — €1,800 to €2,800' }, { value: 'deluxe', label: 'A larger shared room (2 per room) — €2,500 to €3,900' }, { value: 'suite', label: 'A named themed room — €4,750' }, { value: 'artist', label: 'Castle Artist Exchange — €500 (practising artists)' }, { value: 'patronage', label: 'I want to support / contribute beyond a seat' }, { value: 'undecided', label: 'I would like to talk it through first' }, ]; function Apply() { const [affiliation, setAffiliation] = useState(''); const [tier, setTier] = useState(''); const [message, setMessage] = useState(''); const [carrying, setCarrying] = useState(''); const [state, setState] = useState('idle'); // idle | loading | success | error const [errorMsg, setErrorMsg] = useState(''); async function handleSubmit(e) { e.preventDefault(); setState('loading'); setErrorMsg(''); const formData = new FormData(e.currentTarget); const data = Object.fromEntries(formData); try { const resp = await fetch('/api/apply', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); const payload = await resp.json().catch(() => null); if (!resp.ok) { throw new Error(payload?.error?.message ?? payload?.message ?? 'Submission failed. Please try again.'); } setTimeout(() => setState('success'), 900); } catch (err) { console.error('Apply failed:', err); setState('error'); setErrorMsg(err.message || 'Something went wrong. Please try again or email us directly.'); } } if (state === 'success') { return (

Your letter has been received.

A human will read it and write back within seven days, usually sooner. Until then, look at the moon now and then; we will be reading.

); } return (
— Request a seat —

Tell us who you are.

Veld is open to anyone who is honest about why they want to be here. Selection happens through the letter, not the résumé. A human reads every one.

WhatsApp or Signal, for last-minute logistics, not marketing.
LinkedIn, personal site, anywhere we can begin to know you.
A starting point only. None of these are gates; your letter is what we read.
{(affiliation === 'vouch') && (
The person already in the room who knows you well.
)}
All rooms are shared. Three private rooms exist as a paid add-on.
Since rooms are shared, this helps us pair you with compatible roommates.
We’ll do our best to place you together. Both of you must be accepted.
Berlin (≈2.5 hrs), Wrocław (≈1.5 hrs), and Prague (≈3 hrs) are the usual gateways. Flight details help us plan transport — rough is fine.