// ─── SPRAAKMAKER MEDIA ───────────────────────────────────────────────────── // Agency website — same DNA as PSA but editorial/bold layout system // ─── COPY (NL/EN) ────────────────────────────────────────────────────────── const COPY = { nl: { nav: { work: 'Podcasts', services: 'Wat we doen', process: 'Proces', studio: 'Studio', contact: 'Contact' }, cta: 'Neem contact op', ctaShort: 'Contact', hero: { eyebrow: 'Podcast Agency', lineA: 'Uitgesproken', lineB: 'ideeën,', lineC: 'spraakmakende', lineD: 'podcasts.', sub: 'Spraakmaker Media is een productiehuis voor zakelijke podcasts. We werken voor bedrijven, NGO\'s en overheidsorganisaties die hun verhaal scherp én geloofwaardig willen vertellen, van strategie tot publicatie.', primary: 'Plan een kennismaking', secondary: 'Bekijk onze podcasts' }, manifesto: { eyebrow: 'Wie we zijn', kickerPre: 'Wij maken', kickerAccent: 'zakelijke podcasts', kickerPost: 'voor bedrijven, NGO\'s en overheidsorganisaties.', kickerCaption: 'Onze leus, geen marketing-claim', sectionTag: '', // Explainer (B2) explainerEyebrow: 'Waar we voor staan', explainerTitle: ['De wereld een beetje', 'verbeteren', '.'], explainerSub: 'We werken met organisaties die ergens voor staan. Dit zijn de thema\'s die ons drijven en die we delen met de opdrachtgevers waar we het liefst mee werken.', explainerBadge: '', intro: '', history: 'Sinds 2018 maken wij podcasts die ertoe doen. We werken voor organisaties die geloven dat de wereld er anders uit kan zien en dat verhaal willen vertellen aan de mensen die het moeten horen.', approach: 'Van eerste concept tot publicatie begeleiden we het hele traject. Geen losse aflevering, maar een serie die blijft doorwerken. Op de werkvloer of daarbuiten.', values: [ { num: '01', title: 'Diversiteit en inclusie', body: 'De wereld klinkt niet als één stem. Wij ook niet. We maken podcasts die ruimte geven aan verhalen die anders niet gehoord worden en werken bewust samen met makers, gasten en opdrachtgevers die dat verschil weerspiegelen.' }, { num: '02', title: 'Democratie en vrijheid van meningsuiting', body: 'Audio is van iedereen. Een microfoon kan een podium zijn voor wie normaal geen podium krijgt. Wij geloven in het recht om te spreken, te luisteren en van mening te mogen verschillen en maken content die dat recht serieus neemt.' }, { num: '03', title: 'Persvrijheid en mediawijsheid', body: 'Goede journalistiek begint bij een open oor. Wij maken geen nieuws, maar we weten hoe verhalen werken en hoe ze misbruikt kunnen worden. Daarom staan we achter een vrije pers en helpen we organisaties hun boodschap eerlijk en transparant te vertellen.' }, { num: '04', title: 'Mentale gezondheid', body: 'Praten helpt. Écht luisteren ook. Audio heeft de unieke kracht om mensen het gevoel te geven dat ze er niet alleen in staan. We maken ruimte voor kwetsbare verhalen omdat die het hardst binnenkomen.' }, { num: '05', title: 'Klimaat en duurzaamheid', body: 'We werken voor organisaties die serieus bezig zijn met een duurzamere wereld en proberen zelf ook bewuste keuzes te maken in hoe we produceren.' }, { num: '06', title: 'Gelijkwaardigheid en respect', body: 'Geen ruimte voor discriminatie, racisme of seksisme. Niet in onze studio, niet in onze content, niet bij de opdrachtgevers waar we mee werken.' } ] }, services: { eyebrow: 'Wij werken voor', title: 'Drie soorten opdrachtgevers.', sub: 'Elke organisatie heeft een ander verhaal en een andere aanpak. Dit is hoe we werken met de partners waar we in geloven.', items: [ { num: '01', kind: 'Bedrijven', tagline: 'Van interne cultuur tot externe reputatie.', body: 'We helpen bedrijven hun verhaal scherp te krijgen: voor medewerkers, klanten of de buitenwereld. Denk aan thought leadership, employer branding of cultuurprogramma\'s.', outcome: 'Verhalen die intern verbinden en extern aantrekken.', tags: ['Thought leadership', 'Employer branding', 'Cultuur', 'Reputatie'] }, { num: '02', kind: 'NGO\'s & maatschappelijke organisaties', tagline: 'Verhalen die beweging brengen.', body: 'We werken voor organisaties die een onderwerp op de kaart willen zetten. Met podcasts die nuance toevoegen, draagvlak vergroten en doelgroepen binden.', outcome: 'Onderwerpen die niet meer te negeren zijn.', tags: ['Bewustwording', 'Draagvlak', 'Campagne', 'Doelgroepen'] }, { num: '03', kind: 'Overheid & publieke sector', tagline: 'Complexe vraagstukken, helder verteld.', body: 'Beleid, onderzoek en publieke opgaven verdienen meer dan een persbericht. Wij vertalen complexiteit naar luisterklare verhalen die ook buiten de eigen kring landen.', outcome: 'Beleid dat landt, ook buiten de eigen bubbel.', tags: ['Beleid', 'Onderzoek', 'Publieksbereik', 'Communicatie'] }] }, work: { eyebrow: 'Uitgelicht werk', title: 'Podcasts waar we trots op zijn.', sub: 'Een greep uit producties voor partners, makers en eigen titels.', cta: 'Bekijk alle podcasts' }, process: { eyebrow: 'Zo werken we', title: 'Van idee tot publicatie.', intro: 'Een goede podcast begint niet met microfoons, maar met een helder verhaal. Dit is hoe wij dat vormgeven, in zes stappen van eerste gesprek tot stevig op de platforms.', steps: [ { num: '01', title: 'Advies & strategie', body: 'Wat wil je vertellen, voor wie, en waarom juist in audio of video? We denken mee over inhoud, format, redactie en distributie. Niet vanuit trends, maar vanuit wat werkt voor jouw organisatie en doelgroep.' }, { num: '02', title: 'Inhoud & redactie', body: 'Achter elk goed gesprek zit een redactionele lijn. We bepalen kern, toon en structuur, en formuleren vragen die verder gaan dan de oppervlakte. Soms is dat schrappen in plaats van toevoegen, of vertragen waar iedereen versnelt.' }, { num: '03', title: 'Opname & montage', body: 'Onze studio is zo ingericht dat techniek naar de achtergrond verdwijnt en de aandacht volledig naar het gesprek kan. Wij bewaken geluid, beeld en tempo op de achtergrond, zodat jij je kunt richten op de inhoud.' }, { num: '04', title: 'Muziek & sounddesign', body: 'Muziek bepaalt ritme, sfeer en herkenbaarheid. Met meer dan twintig jaar ervaring als muzikant en producer zorgt Mart dat elke podcast een eigen klankwereld krijgt, van herkenbare intro’s tot subtiele audiolagen die het verhaal dragen.' }, { num: '05', title: 'Publicatie & distributie', body: 'We verzorgen publicatie op alle grote platforms zoals Spotify, Apple Podcasts en YouTube, en optimaliseren titels, beschrijvingen en metadata voor vindbaarheid. Daarnaast denken we mee over ritme, afleveringstitels en luisteraarsreis.' }, { num: '06', title: 'Doorontwikkeling', body: 'Een podcast is geen eenmalig project. We evalueren samen wat werkt, sturen bij waar nodig, en denken mee over een volgend seizoen of vervolgformat.' }] }, numbers: { items: [ { big: '2018', label: 'Opgericht met één ambitie' }, { big: '150+', label: 'Producties uitgebracht' }, { big: '25+', label: 'Partners waar we mee werken' }, { big: '1', label: 'Studio in hartje Amsterdam' }] }, studio: { eyebrow: 'Onze studio in Amsterdam', title: 'Podcast Studio Amsterdam', sub: 'Ontworpen om verhalen op hun best vast te leggen. Professioneel ingericht, comfortabel om in te werken en technisch tot in de puntjes verzorgd.', body: 'Met haarscherp beeld en kristalhelder geluid creëren we een omgeving waarin gesprekken ontspannen aanvoelen, maar eindigen als producties van hoog niveau.', cta: 'Boek de studio', link: 'podcaststudio.amsterdam' }, logos: { eyebrow: 'Vertrouwd door', title: 'Merken die met ons werken.' }, contact: { eyebrow: 'Contact', title: 'Samen iets moois maken?', sub: 'Van idee tot publicatie denken we met je mee. Laat weten waar je aan werkt, dan plannen we een kennismaking.', name: 'Naam', email: 'Mail', phone: 'Telefoon', message: 'Waar kunnen we je mee helpen?', send: 'Verstuur bericht', sending: 'Verzenden…', sent: 'Je bericht is verstuurd. We nemen snel contact met je op.', error: 'Er gaat iets mis. Stuur een mail naar info@spraakmaker.media, dan komen we er snel op terug.', or: 'Of bel ons direct', address: 'Gibraltarstraat 42, Amsterdam' }, footer: { tagline: 'Uitgesproken ideeën, spraakmakende podcasts.', address: 'Adres', email: 'Mail', phone: 'Telefoon', navHeading: 'Navigatie', socialHeading: 'Volg ons', colophon: '© 2026 Spraakmaker Media. Alle rechten voorbehouden.', kvk: 'KVK 71234567', btw: 'BTW NL123456789B01' } }, en: { nav: { work: 'Podcasts', services: 'What we do', process: 'Process', studio: 'Studio', contact: 'Contact' }, cta: 'Get in touch', ctaShort: 'Contact', hero: { eyebrow: 'Podcast Agency', lineA: 'Outspoken', lineB: 'ideas,', lineC: 'remarkable', lineD: 'podcasts.', sub: 'Spraakmaker Media is a production house for business podcasts. We work with companies, NGOs and government organisations that want to tell their story with clarity and credibility, from strategy to publication.', primary: 'Book an intro call', secondary: 'See our podcasts' }, manifesto: { eyebrow: 'What we stand for', kickerPre: 'We make', kickerAccent: 'business podcasts', kickerPost: 'for companies, NGOs and government organisations.', kickerCaption: 'Our motto, not a marketing claim', sectionTag: '', explainerEyebrow: 'What we stand for', explainerTitle: ['Making the world', 'a little better', '.'], explainerSub: 'We work with organisations that stand for something. These are the themes that drive us and that we share with the clients we love working with most.', explainerBadge: '', intro: '', history: 'We started in 2018 with the ambition to use podcasts to make the world a little better, large and small. That\'s why we work with makers, advertisers and organisations who share this ambition.', approach: 'We guide the entire journey, from concept to publication and reach. That way we build stories together that don\'t disappear after one listen, but keep working.', values: [ { num: '01', title: 'Diversity and inclusion', body: 'The world doesn\'t sound like one voice. Neither do we. We make podcasts that give space to stories that otherwise wouldn\'t be heard, and consciously work with makers, guests and clients who reflect that difference.' }, { num: '02', title: 'Democracy and freedom of speech', body: 'Audio belongs to everyone. A microphone can be a stage for those who normally don\'t get one. We believe in the right to speak, listen and disagree, and make content that takes that right seriously.' }, { num: '03', title: 'Press freedom and media literacy', body: 'Good journalism starts with an open ear. We don\'t make news, but we know how stories work and how they can be misused. That\'s why we stand behind a free press and help organisations tell their story honestly and transparently.' }, { num: '04', title: 'Mental health', body: 'Talking helps. Really listening too. Audio has the unique power to make people feel they\'re not alone. We make space for vulnerable stories because those land the hardest.' }, { num: '05', title: 'Climate and sustainability', body: 'We work for organisations that are seriously committed to a more sustainable world, and try to make conscious choices in how we produce.' }, { num: '06', title: 'Equality and respect', body: 'No room for discrimination, racism or sexism. Not in our studio, not in our content, not at the clients we work with.' } ] }, services: { eyebrow: 'We work for', title: 'Three kinds of clients.', sub: 'Every organisation has a different story and asks for a different approach. This is how we work with the partners we believe in.', items: [ { num: '01', kind: 'Companies', tagline: 'From internal culture to external reputation.', body: 'We help companies sharpen their story for employees, customers or the outside world. Think thought leadership, employer branding or culture programmes.', outcome: 'Stories that connect inside and attract outside.', tags: ['Thought leadership', 'Employer branding', 'Culture', 'Reputation'] }, { num: '02', kind: 'NGOs & social organisations', tagline: 'Stories that create movement.', body: 'We work for organisations that want to put a subject on the map. With podcasts that add nuance, build support and connect with audiences.', outcome: 'Subjects that can no longer be ignored.', tags: ['Awareness', 'Support', 'Campaign', 'Audiences'] }, { num: '03', kind: 'Government & public sector', tagline: 'Complex questions, clearly told.', body: 'Policy, research and public challenges deserve more than a press release. We translate complexity into listen-ready stories that also land beyond the usual circles.', outcome: 'Policy that lands, also outside the bubble.', tags: ['Policy', 'Research', 'Public reach', 'Communication'] }] }, work: { eyebrow: 'Featured work', title: 'Podcasts we\'re proud of.', sub: 'A selection of productions for partners, makers and our own titles.', cta: 'See all podcasts' }, process: { eyebrow: 'How we work', title: 'From idea to publication.', intro: 'A great podcast doesn’t start with microphones. It starts with a clear story. Here’s how we shape it, in six steps from first conversation to solid presence on the platforms.', steps: [ { num: '01', title: 'Advice & strategy', body: 'What do you want to tell, for whom, and why in audio or video? We think through content, format, editorial and distribution. Not chasing trends, but rooted in what works for your organisation and audience.' }, { num: '02', title: 'Content & editorial', body: 'Behind every good conversation is an editorial line. We set core, tone and structure, and shape questions that go beyond the surface. Sometimes that means cutting instead of adding, or slowing down where everyone else speeds up.' }, { num: '03', title: 'Recording & editing', body: 'Our studio is built so technology fades into the background and attention can go fully to the conversation. We watch over sound, image and pacing behind the scenes, so you can focus on the content.' }, { num: '04', title: 'Music & sound design', body: 'Music sets the rhythm, mood and identity of an episode. With over twenty years as a musician and producer, Mart gives every podcast its own sound world, from recognisable intros to subtle audio layers that carry the story.' }, { num: '05', title: 'Publication & distribution', body: 'We handle publication across all major platforms like Spotify, Apple Podcasts and YouTube, and optimise titles, descriptions and metadata for discoverability. We also think with you about cadence, episode titles and the listener journey.' }, { num: '06', title: 'Continued development', body: 'A podcast is not a one-off project. We evaluate together what works, adjust where needed, and think along about a next season or follow-up format.' }] }, numbers: { items: [ { big: '2018', label: 'Founded with one ambition' }, { big: '150+', label: 'Productions released' }, { big: '25+', label: 'Partners we work with' }, { big: '1', label: 'Studio in central Amsterdam' }] }, studio: { eyebrow: 'Our studio in Amsterdam', title: 'Podcast Studio Amsterdam', sub: 'Designed to capture stories at their best. Professionally equipped, comfortable to work in and technically impeccable.', body: 'With razor-sharp video and crystal-clear sound we create an environment where conversations feel relaxed, but end as productions of the highest standard.', cta: 'Book the studio', link: 'podcaststudio.amsterdam' }, logos: { eyebrow: 'Trusted by', title: 'Brands working with us.' }, contact: { eyebrow: 'Contact', title: 'Make something great together?', sub: 'From idea to publication we think along with you. Tell us what you\'re working on and we\'ll set up an intro call.', name: 'Name', email: 'Email', phone: 'Phone', message: 'How can we help you?', send: 'Send message', sending: 'Sending…', sent: 'Your message has been sent. We\'ll be in touch shortly.', error: 'Something went wrong. Please email info@spraakmaker.media and we\'ll get back to you.', or: 'Or call us directly', address: 'Gibraltarstraat 42, Amsterdam' }, footer: { tagline: 'Outspoken ideas, remarkable podcasts.', address: 'Address', email: 'Email', phone: 'Phone', navHeading: 'Navigation', socialHeading: 'Follow us', colophon: '© 2026 Spraakmaker Media. All rights reserved.', kvk: 'KVK 71234567', btw: 'VAT NL123456789B01' } } }; // ─── DATA ────────────────────────────────────────────────────────────────── const PODCAST_COVERS = [ { title: '7 People', cat: 'Branded', src: 'https://spraakmaker.media/wp-content/uploads/2026/01/Scherm­afbeelding-2026-01-29-om-10.19.29-300x298.png' }, { title: 'De Marketing Formule', cat: 'Partner', src: 'https://spraakmaker.media/wp-content/uploads/2026/01/Scherm­afbeelding-2026-01-29-om-10.17.21-298x300.png' }, { title: 'Over Werkgeluk Gesproken', cat: 'Partner', src: 'https://spraakmaker.media/wp-content/uploads/2026/01/Scherm­afbeelding-2026-01-29-om-10.15.18-295x300.png' }, { title: 'Kunst en Bondcast', cat: 'Eigen', src: 'https://spraakmaker.media/wp-content/uploads/2026/01/Scherm­afbeelding-2026-01-29-om-09.55.48-298x300.png' }, { title: 'Agents in Production', cat: 'Branded', src: 'https://spraakmaker.media/wp-content/uploads/2026/01/Agents-in-Production-1-300x300.png' }, { title: 'Morele Wezens', cat: 'Partner', src: 'https://spraakmaker.media/wp-content/uploads/2026/01/Morele-Wezens-Centrum-Ethiek-en-Gezondheid-300x300.png' }, { title: 'Leaders in Finance', cat: 'Partner', src: 'https://spraakmaker.media/wp-content/uploads/2026/01/Scherm­afbeelding-2026-01-29-om-10.39.27-300x300.png' }, { title: 'Schiphol Art Moments', cat: 'Branded', src: 'https://spraakmaker.media/wp-content/uploads/2025/03/SCH_schiphol_art_moments_albumcover_opt-300x300.jpg' }, { title: 'Vrij Nederland', cat: 'Partner', src: 'https://spraakmaker.media/wp-content/uploads/2025/09/ab6765630000ba8a6a87fe42a63862769026bfba-1-300x300.jpeg' }, { title: 'Eigen titel', cat: 'Eigen', src: 'https://spraakmaker.media/wp-content/uploads/2025/09/ab6765630000ba8a21d8c88b366486703e5a0861-3-300x300.jpeg' }, { title: 'Serie', cat: 'Branded', src: 'https://spraakmaker.media/wp-content/uploads/2025/02/54328cbe1909d36448e1f8f13f70ba78-300x300.webp' }, { title: 'Audio essay', cat: 'Eigen', src: 'https://spraakmaker.media/wp-content/uploads/2026/01/schermafbeelding-2026-01-26-om-10-08-58-300x298.png' }]; const CLIENT_LOGOS = [ 'logo-schiphol-zwart-wit.png', 'logo-just-eat-take-away-zwart-wit.png', 'logo-prosus-zwart-wit.png', 'logo-gemeente-amsterdam-zwart-wit.png', 'logo-van-de-amsterdamse-hogeschool-voor-de-kunsten.png', 'logo-universiteit-van-amsterdam-zwart-wit.png', 'logo-tu-delft-zwart-wit.png', 'logo-vu-zwart-wit.png', 'logo-ministerie-volkgezondheid-welzijn-en-sport-zwart-wit.png', 'logo-ey-ernst-young-zwart-wit.png', 'logo-deloitte-zwart-wit.png', 'logo-7-people-zwart-wit.png', 'logo-buma-stemra-zwart-wit.png', 'logo-port-of-amsterdam-zwart-wit.png', 'logo-sire-zwart-wit.png', 'logo-vrij-nederland-zwart-wit.png']; // ─── HOOKS ───────────────────────────────────────────────────────────────── function useIsMobile(bp = 768) { const [m, setM] = React.useState(typeof window !== 'undefined' ? window.innerWidth < bp : false); React.useEffect(() => { const o = () => setM(window.innerWidth < bp); window.addEventListener('resize', o); return () => window.removeEventListener('resize', o); }, [bp]); return m; } function useScrolled(threshold = 40) { const [s, setS] = React.useState(false); React.useEffect(() => { const o = () => setS(window.scrollY > threshold); o(); window.addEventListener('scroll', o, { passive: true }); return () => window.removeEventListener('scroll', o); }, [threshold]); return s; } // ─── NAVIGATION ──────────────────────────────────────────────────────────── function Nav({ lang, setLang, scrolled }) { const t = COPY[lang]; const isMobile = useIsMobile(1024); const [open, setOpen] = React.useState(false); const onHero = !scrolled; const linkStyle = { fontSize: 14, fontWeight: 500, color: onHero ? '#fff' : 'var(--fg)', textDecoration: 'none', letterSpacing: '-0.01em', whiteSpace: 'nowrap' }; return ( ); } // ─── HERO ────────────────────────────────────────────────────────────────── function Hero({ lang }) { const t = COPY[lang].hero; const isMobile = useIsMobile(); return (
{/* Animated bg */} {/* Soft color blobs */}
); } // ─── MANIFESTO BAND — Variant B (Statement Kicker + Explainer) ──────────── const MANIFESTO_IMAGES = { stack1: 'uploads/Schermafbeelding 2025-02-17 om 10.16-46b40001.jpeg', stack2: 'uploads/Schermafbeelding 2025-02-17 om 10.18-902bd477.jpeg', stack3: 'uploads/Schermafbeelding 2025-02-17 om 10.41-bea3f898.jpeg', portrait: 'uploads/Schermafbeelding 2025-02-17 om 10.16-46b40001.jpeg' }; // ─── PHOTO POOLS — rotate a random photo per pool on each reload ────────── // Source of truth is photo-pools.json (edited via "Foto kiezen.html" picker). // Inline defaults are a safety-net if the JSON fails to load. const PHOTO_POOLS = (window.__PHOTO_POOLS__ && window.__PHOTO_POOLS__.waar_we_voor_staan) ? window.__PHOTO_POOLS__ : { waar_we_voor_staan: ['uploads/Schermafbeelding 2025-02-17 om 10.16-46b40001.jpeg'], zo_werken_we: ['uploads/Schermafbeelding 2025-02-17 om 10.18-902bd477.jpeg'], opdrachtgevers: ['uploads/Schermafbeelding 2025-02-17 om 10.31-71f5db4e.jpeg'] }; // Fallback voor opdrachtgevers als pool nog niet bestaat if (!PHOTO_POOLS.opdrachtgevers || PHOTO_POOLS.opdrachtgevers.length === 0) { PHOTO_POOLS.opdrachtgevers = PHOTO_POOLS.waar_we_voor_staan || ['uploads/Schermafbeelding 2025-02-17 om 10.31-71f5db4e.jpeg']; } const pickFrom = (arr) => arr[Math.floor(Math.random() * arr.length)]; const MANIFESTO_PORTRAIT = { src: pickFrom(PHOTO_POOLS.waar_we_voor_staan), pos: 'center 30%' }; const PROCESS_PHOTO = { src: pickFrom(PHOTO_POOLS.zo_werken_we), pos: 'center 30%' }; const VALUES_PHOTO = { src: pickFrom(PHOTO_POOLS.waar_we_voor_staan), pos: 'center 30%' }; const SERVICES_PHOTO = { src: pickFrom(PHOTO_POOLS.opdrachtgevers), pos: 'center 30%' }; // Vaste foto's per opdrachtgever-type (Bedrijven, NGO's, Overheid) const SERVICES_PHOTOS = [ { src: 'uploads/Just-Eat-Takeaway.jpg', pos: 'center center' }, { src: 'uploads/Amsterdamse_Hogeschool_voor_de_Kunsten groot.jpeg', pos: 'center center' }, { src: 'uploads/ministerie-den-haag-turfmarkt.jpg', pos: 'center center' } ]; function Manifesto({ lang }) { const t = COPY[lang].manifesto; const isMobile = useIsMobile(); return (
{t.eyebrow} {t.sectionTag}

{t.kickerPre}{' '} {t.kickerAccent}{' '} {t.kickerPost}

); } // ─── VALUES (kernwaarden) — 3 layouts via Tweaks ───────────────────────── function Values({ lang, layout }) { const t = COPY[lang].manifesto; const isMobile = useIsMobile(); const [open, setOpen] = React.useState(0); // default eerste open const toggle = (i) => setOpen((p) => p === i ? -1 : i); const header = (

{t.explainerEyebrow}

{t.explainerTitle[0]} {t.explainerTitle[1]}{t.explainerTitle[2]}

{t.explainerSub ? (

{t.explainerSub}

) : null}
); // ── Layout 1: editorial header + accordion eronder ───────────────── if (layout === 1) { return (
{/* Header rij */}
{header}
{/* Accordion */}
{t.values.map((v, i) => { const isOpen = open === i; return (

{v.body}

); })}
); } // ── Layout 2: header bovenaan, accordion eronder (full-width) ────── if (layout === 2) { return (
{/* Left: header */}
{header}
{/* Right: accordion */}
{t.values.map((v, i) => { const isOpen = open === i; return (

{v.body}

); })}
); } // ── Layout 3: index — header met foto, daaronder typografische lijst return (
{/* Header: heading links, foto rechts breed */}
{header}
{/* Typografische index — grote regels, click reveals onder */}
{t.values.map((v, i) => { const isOpen = open === i; return (

{v.body}

); })}
); } // ─── Manifesto photo (real images) ────────────────────────────────────── function ManifestoPhoto({ src, alt, pos, fill }) { return (
{alt
); } // ─── Auto-rotating photo with crossfade ───────────────────────────────── function RotatingPhoto({ photos, alt, pos, interval, fill, scale }) { const list = (photos && photos.length) ? photos : []; // Random start so the rotation doesn't always begin on the same image const [idx, setIdx] = React.useState(() => Math.floor(Math.random() * Math.max(list.length, 1))); React.useEffect(() => { if (list.length < 2) return; const id = setInterval(() => { setIdx((cur) => { // Pick a random index that isn't the current one let next = Math.floor(Math.random() * (list.length - 1)); if (next >= cur) next += 1; return next; }); }, interval || 5000); return () => clearInterval(id); }, [list.length, interval]); if (list.length === 0) return null; const baseStyle = { width: '100%', height: '100%', ...(fill ? { position: 'absolute', inset: 0 } : {}), overflow: 'hidden', background: '#1A1934' }; return (
{list.map((src, i) => ( {i ))}
); } // ─── SERVICES — 3 client types ───────────────────────────────────────────── function Services({ lang }) { const t = COPY[lang].services; const isMobile = useIsMobile(); const [active, setActive] = React.useState(0); return (

{t.eyebrow}

{t.title}

{t.sub}

{/* Tabbed accordion on desktop, stacked cards on mobile */} {!isMobile ?
{/* Left: tabs */}
{t.items.map((item, i) => )}
{/* Right: detail */}

{t.items[active].kind}

{t.items[active].body}

{t.items[active].outcome}

{t.items[active].tags.map((tag, i) => {tag} )}
:
{t.items.map((item, i) =>
{item.num}

{item.kind}

{item.tagline}

{item.body}

{item.outcome}

{item.tags.map((tag, j) => {tag} )}
)}
}
); } // ─── FEATURED WORK (podcast covers) ──────────────────────────────────────── function Work({ lang }) { const t = COPY[lang].work; const isMobile = useIsMobile(); const [cases, setCases] = React.useState([]); // Load cases & pick 8 random on mount (new set every reload — 2 rows of 4) React.useEffect(() => { const pick = (list) => { const published = list.filter(c => c.published !== false); const shuffled = [...published].sort(() => Math.random() - 0.5); setCases(shuffled.slice(0, 8)); }; if (window.SPRAAKMAKER_CASES) pick(window.SPRAAKMAKER_CASES); }, []); // Make current random selection available to the inner JSX via closure rewrite return (

{t.eyebrow}

{t.title}

{t.sub}

{!isMobile && {t.cta} }
{cases.map((p, i) =>
{`${p.title} e.target.style.transform = 'scale(1.04)'} onMouseLeave={(e) => e.target.style.transform = 'scale(1)'} />
{p.client}

{p.title}

{p.summary}

)}
{t.cta}
); } // ─── LOGOS CAROUSEL — vertrouwd door ─────────────────────────────────────── const LOGOS = [ { src: 'uploads/logos/logo-7-people-zwart-wit.png', alt: '7People' }, { src: 'uploads/logos/logo-buma-stemra-zwart-wit.png', alt: 'Buma Stemra' }, { src: 'uploads/logos/logo-deloitte-zwart-wit.png', alt: 'Deloitte' }, { src: 'uploads/logos/logo-ey-ernst-young-zwart-wit.png', alt: 'EY' }, { src: 'uploads/logos/logo-gemeente-amsterdam-zwart-wit.png', alt: 'Gemeente Amsterdam' }, { src: 'uploads/logos/logo-just-eat-take-away-zwart-wit.png', alt: 'Just Eat Takeaway' }, { src: 'uploads/logos/logo-ministerie-volkgezondheid-welzijn-en-sport-zwart-wit.png', alt: 'Ministerie van VWS' }, { src: 'uploads/logos/logo-port-of-amsterdam-zwart-wit.png', alt: 'Port of Amsterdam' }, { src: 'uploads/logos/logo-prosus-zwart-wit.png', alt: 'Prosus' }, { src: 'uploads/logos/logo-schiphol-zwart-wit.png', alt: 'Schiphol' }, { src: 'uploads/logos/logo-sire-zwart-wit.png', alt: 'SIRE' }, { src: 'uploads/logos/logo-tu-delft-zwart-wit.png', alt: 'TU Delft' }, { src: 'uploads/logos/logo-universiteit-van-amsterdam-zwart-wit.png', alt: 'Universiteit van Amsterdam' }, { src: 'uploads/logos/logo-van-de-amsterdamse-hogeschool-voor-de-kunsten.png', alt: 'Amsterdamse Hogeschool voor de Kunsten' }, { src: 'uploads/logos/logo-van-sprints-en-sneakers.png', alt: 'Sprints & Sneakers' }, { src: 'uploads/logos/logo-vrij-nederland-zwart-wit.png', alt: 'Vrij Nederland' }, { src: 'uploads/logos/logo-vu-zwart-wit.png', alt: 'VU Amsterdam' } ]; function LogosCarousel({ lang }) { const t = COPY[lang].logos; const isMobile = useIsMobile(); const doubled = [...LOGOS, ...LOGOS]; return (

{t.eyebrow}

{doubled.map((logo, i) => ( {logo.alt} ))}
); } // ─── PROCESS ─────────────────────────────────────────────────────────────── function Process({ lang }) { const t = COPY[lang].process; const isMobile = useIsMobile(); return (
{/* Soft blush blob accent */}
); } // ─── NUMBERS BAND ────────────────────────────────────────────────────────── function Numbers({ lang }) { const items = COPY[lang].numbers.items; const isMobile = useIsMobile(); return (
{items.map((n, i) =>
{n.big}

{n.label}

)}
); } // ─── LAZY HERO VIDEO ─────────────────────────────────────────────────────── // Renders a low-cost WebP poster immediately so visitors see something // straight away, then swaps in the autoplaying MP4 only when the section // scrolls near the viewport. Cuts initial payload from ~14 MB to ~40 KB. function LazyHeroVideo({ isMobile }) { const containerRef = React.useRef(null); const videoRef = React.useRef(null); const [shouldLoad, setShouldLoad] = React.useState(false); const [videoReady, setVideoReady] = React.useState(false); React.useEffect(() => { if (!containerRef.current) return; // If IntersectionObserver isn't available, just load. if (typeof IntersectionObserver === 'undefined') { setShouldLoad(true); return; } const io = new IntersectionObserver((entries) => { for (const entry of entries) { if (entry.isIntersecting) { setShouldLoad(true); io.disconnect(); break; } } }, { rootMargin: '400px 0px' /* start loading slightly before in view */ }); io.observe(containerRef.current); return () => io.disconnect(); }, []); React.useEffect(() => { if (!shouldLoad || !videoRef.current) return; const v = videoRef.current; const onPlaying = () => setVideoReady(true); v.addEventListener('playing', onPlaying); // Kick off load + play v.load(); const playPromise = v.play(); if (playPromise && playPromise.catch) playPromise.catch(() => {/* autoplay blocked, poster stays */}); return () => v.removeEventListener('playing', onPlaying); }, [shouldLoad]); const poster = isMobile ? 'uploads/banner-poster-mobile.webp' : 'uploads/banner-poster.webp'; return ( ); } // ─── STUDIO (PSA band) ───────────────────────────────────────────────────── function StudioBand({ lang }) { const t = COPY[lang].studio; const isMobile = useIsMobile(); const isNL = lang === 'nl'; return (
{/* Video background — lazy loaded, poster shown immediately */} {/* Dark overlay for readability */}
); } // ─── CLIENT LOGOS ────────────────────────────────────────────────────────── function ClientLogos({ lang }) { const t = COPY[lang].logos; const isMobile = useIsMobile(); const logos = [...CLIENT_LOGOS, ...CLIENT_LOGOS]; return (

{t.eyebrow}

{t.title}

{logos.map((src, i) => {src.replace(/-/g, e.target.style.opacity = '1'} onMouseLeave={(e) => e.target.style.opacity = '0.55'} /> )}
); } // ─── CONTACT ─────────────────────────────────────────────────────────────── function Contact({ lang }) { const t = COPY[lang].contact; const isMobile = useIsMobile(); const [form, setForm] = React.useState({ name: '', email: '', phone: '', message: '', _gotcha: '' }); const [status, setStatus] = React.useState('idle'); // idle | sending | sent | error const submit = async (e) => { e.preventDefault(); setStatus('sending'); try { const res = await fetch('api/contact.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...form, lang }) }); const data = await res.json().catch(() => ({})); if (res.ok && data.success) { setStatus('sent'); } else { setStatus('error'); } } catch (err) { setStatus('error'); } }; const input = { width: '100%', padding: '16px 0', background: 'transparent', border: 'none', borderBottom: '1.5px solid var(--border)', fontSize: 16, fontFamily: 'Inter', color: 'var(--fg)', outline: 'none', transition: 'border-color 0.2s' }; return (
{/* Left: heading + contact info */}

{t.eyebrow}

{t.title}

{t.sub}

{t.or}

020 2117870 info@spraakmaker.media

{t.address}

{/* Right: form */}
{status === 'sent' ?

{t.sent}

:
{/* Honeypot — verborgen veld voor bots */} setForm({ ...form, _gotcha: e.target.value })} style={{ position: 'absolute', left: '-9999px', width: 1, height: 1, opacity: 0 }} aria-hidden="true" />