// images.jsx — curated Unsplash photo IDs. // All photos are royalty-free under the Unsplash license. The two stronger // local assets we already own (Riyadh night skyline, custom AI mark) are kept // as local files so we control them; everything else pulls from Unsplash CDN // to better match the deep-tech aesthetic. const UNSPLASH = 'https://images.unsplash.com/photo-'; const FIT = '?auto=format&fit=crop&q=80'; function img(id, w = 1600, h) { // If the value looks like a local path (contains "/" or "."), pass through. if (typeof id !== 'string') return id; if (id.startsWith('img/') || id.startsWith('/') || id.startsWith('http')) return id; return `${UNSPLASH}${id}${FIT}&w=${w}${h ? `&h=${h}` : ''}`; } const IMG = { // Hero / wide shots — gloved hands, wet bench, researchers heroLab: '1581094288338-2314dddb7ece', // gloved hand, microplate, blue light heroRobot: '1581090700227-1e37b190418e', // robotic lab equipment, neon heroCode: '1518770660439-4636190af475', // circuit board macro heroDna: '1530026405186-ed1f139313f8', // glassware on lab counter heroPortrait: '1582719471384-894fbb16e074', // researcher portrait // Pillar 1 — Lab Automation pillar1: '1581094288338-2314dddb7ece', // automation hardware pillar1Alt: '1581090700227-1e37b190418e', // Pillar 2 — Research-Focused AI pillar2: '1620712943543-bcc4688e7485', // AI / abstract neural pillar2Alt: '1677442136019-21780ecad995', // Pillar 3 — Decentralized Science pillar3: '1639762681485-074b7f938ba0', // chain / data pillar3Alt: '1639322537228-f710d846310a', // Pillar 4 — Augmented Reality pillar4: '1593508512255-86ab42a8e620', // headset / AR pillar4Alt: '1617802690992-15d93263d3a9', // People / human moments — anonymized angles scientist1: '1532187863486-abf9dbad1b69', // pipetting hands scientist2: '1576091160550-2173dba999ef', // researcher at microscope scientist3: '1581093588401-fbb62a02f120', // researcher at bench scientist4: '1576086213369-97a306d36557', // petri dishes surgeon: '1576091160399-112ba8d25d1d', // surgeon surgeon2: '1551601651-2a8555f1a136', // medical setup crowd: '1540575467063-178a50c2df87', // conference audience speaker: '1475721027785-f74eccf877e2', // person on stage expo: '1591115765373-5207764f72e7', // expo floor hackathon: '1551434678-e076c223a692', // people coding together // Riyadh / venue — keep local file (the strongest asset we own) riyadh: 'img/riyadh_night.webp', riyadhSky: 'img/riyadh_night.webp', riyadhStreet: '1591800056-c9d6cd5da4d2', desert: '1547036967-23d11aacaee0', arabic: '1564661745510-5c8d0e85d4d2', // Sci viz dna: '1581093588401-fbb62a02f120', microscope: '1532094349884-543bc11b234d', cells: '1628595351029-c2bf17511435', brain: '1559757175-5700dde675bc', // Misc pipettes: '1532187863486-abf9dbad1b69', beakers: '1581094271901-8022df4466f9', }; // ──────── Image component ──────── // Renders an Unsplash image with a striped placeholder fallback if loading fails. function Photo({ src, alt, ratio, height, caption, dark = false, style = {}, w = 1400 }) { const [failed, setFailed] = React.useState(false); const url = typeof src === 'string' ? img(src, w) : src; if (failed) { return ; } return (
{alt setFailed(true)} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', // subtle desaturation + slight contrast — knit images into the bold mono-tone aesthetic filter: 'saturate(0.85) contrast(1.02)', }} /> {caption && (
{caption}
)}
); } // Image with a colored mat — useful for sections where photos need to read against a colored card function PhotoMat({ src, alt, ratio = '4/3', caption, tag, n }) { return (
{(tag || n) && (
{tag && {tag}} {n && {n}}
)}
); } // Banner image (or video) with overlay text — for big editorial moments. // If `video` is provided, an auto-playing muted loop replaces the still photo; // `src` is still used as the poster so layout doesn't jump before the video buffers. function PhotoBand({ src, alt, video, poster, height = 520, eyebrow, title, sub, id }) { return (
{/* Parallax inner — the media translates slower than the section scrolls, producing a subtle drift behind the overlay text. Falls back to no-op when reduced-motion is set. */} {video ? (
{eyebrow && (
{eyebrow}
)} {title && (

{title}

)} {sub && (

{sub}

)}
); } Object.assign(window, { Photo, PhotoMat, PhotoBand, IMG, imgUrl: img });