Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8d7a8ef
Update navigation links and remove Give(a)Go text from Hero
sanatcodes Jan 25, 2026
2796162
Make Give(a)Go logo link to giveago.co
sanatcodes Jan 25, 2026
b78fde9
Update branding from Delphi to Give(a)Go and add favicon
sanatcodes Jan 25, 2026
9833544
Update favicon to purple Garamond style and change text selection to …
sanatcodes Jan 25, 2026
acf956a
Implement auto-scroll functionality in Hero component and update head…
giveago Jan 25, 2026
ed1d8a9
Update Hero component
giveago Jan 25, 2026
21d1a31
Update Hero and Navigation components
giveago Jan 25, 2026
ec81db3
Improve Hero section: expand tunnel opening, refine typography, and u…
sanatcodes Jan 25, 2026
f8de870
Merge branch 'main' of https://github.com/give-a-go/ai-film-landing
sanatcodes Jan 25, 2026
f6b77fd
Add morphing waitlist form with EmailOctopus integration
sanatcodes Jan 25, 2026
aafed65
Fix favicon text vertical alignment
sanatcodes Jan 25, 2026
81e1e49
fixed updates on watilist if email exists on email octopus
sanatcodes Jan 26, 2026
1d31c96
added event info
sanatcodes Feb 4, 2026
5615e56
updated teleprompted
sanatcodes Feb 8, 2026
29808d2
Refactor TeleprompterModal padding and max-height for improved layout…
sanatcodes Feb 8, 2026
fc5cbc7
Add responsive scaling to Hero component for portrait screens
sanatcodes Feb 8, 2026
1f97798
Update EmailOctopus integration to use API v2
sanatcodes Feb 8, 2026
494858a
Add waitlist functionality and enhance navigation
sanatcodes Feb 8, 2026
c69e4ec
Fix TeleprompterModal auto-scroll broken on mobile
claude Feb 8, 2026
b04af11
Merge pull request #1 from Give-a-Go/claude/fix-telepropter-mobile-sc…
sanatcodes Feb 8, 2026
4a76016
Refactor Hero and TeleprompterModal components for improved readabili…
sanatcodes Feb 19, 2026
583ab51
Fix video looping, improve tile distribution and stagger initial reveal
grahamrj7 Mar 4, 2026
6a59e58
Remove dark mode, add Cloudinary video fetch script, update components
grahamrj7 Mar 12, 2026
4e44c08
animations working
grahamrj7 Mar 17, 2026
ff65c77
Cinematic overhaul: dark tunnel, projector startup, clapboard fix
grahamrj7 Mar 17, 2026
b0c66cb
Update App and CinematicTransition
grahamrj7 Mar 17, 2026
9dc5dac
Update App, EventContent, Hero; add partner assets
grahamrj7 Mar 17, 2026
3846365
Update components, assets, and config
grahamrj7 Mar 22, 2026
a255ef1
dotted line around videos
giveago Mar 22, 2026
783be49
Update App, Hero, Navigation, and CinematicTransition components
giveago Mar 22, 2026
e1f25be
Update EventContent, Hero, and Navigation components
giveago Mar 22, 2026
348a569
Add CollisionSection component and update App, index, and packages
giveago Mar 22, 2026
02f02c1
Update App, CollisionSection, and Hero components
giveago Mar 23, 2026
a9545eb
Update App, CollisionSection, EventContent, Hero, Navigation, and index
giveago Mar 23, 2026
e7f6b95
Rebrand to Ireland AI Filmmaking Hackathon with copy polish
sanatcodes Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Email Octopus Environment Variables

## Required Environment Variables

To enable the waitlist email capture functionality, you need to configure the following environment variables:

### EMAIL_OCTOPUS_API_KEY
Your EmailOctopus API v2 key (used as a Bearer token). You can generate this in your EmailOctopus account settings under "Developer" → "API Keys".

**Important**: If your existing API key is labeled as "legacy" in EmailOctopus, you must generate a new API key for v2 compatibility. New keys work with all API versions.

### EMAIL_OCTOPUS_LIST_ID
The ID of the EmailOctopus list where waitlist emails should be added. You can find this in the URL when viewing your list (e.g., `https://emailoctopus.com/lists/{list-id}`).

## Local Development Setup

1. Create a `.env.local` file in the project root:
```bash
EMAIL_OCTOPUS_API_KEY=your_api_key_here
EMAIL_OCTOPUS_LIST_ID=your_list_id_here
```

2. Install Vercel CLI for local testing (optional):
```bash
npm install -g vercel
```

3. Run the development server:
```bash
npm run dev
# or with Vercel CLI to test serverless functions locally:
vercel dev
```

## Vercel Deployment Setup

1. Go to your Vercel project settings
2. Navigate to "Environment Variables"
3. Add both variables:
- `EMAIL_OCTOPUS_API_KEY` - Your EmailOctopus API key
- `EMAIL_OCTOPUS_LIST_ID` - Your EmailOctopus list ID
4. Make sure to add them for all environments (Production, Preview, Development)

## Getting EmailOctopus Credentials

1. Sign up for an EmailOctopus account at https://emailoctopus.com
2. Create a new list for your waitlist
3. Go to Settings → API to generate an API key
4. Copy the list ID from your list's URL or settings page

## Testing the API

Once deployed, you can test the API endpoint:

```bash
curl -X POST https://your-domain.vercel.app/api/subscribe \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com"}'
```

Expected response:
```json
{
"success": true,
"message": "Successfully added to waitlist!"
}
```
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.vercel
.env*.local
247 changes: 225 additions & 22 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,239 @@
import React, { useEffect, useState } from 'react';
import Navigation from './components/Navigation';
import Hero from './components/Hero';
import gsap from 'gsap';
import React, { useEffect, useRef } from "react";
import Navigation from "./components/Navigation";
import Hero from "./components/Hero";
import CinematicTransition from "./components/CinematicTransition";
import EventContent from "./components/EventContent";
import CollisionSection from "./components/CollisionSection";
import EventPage from "./pages/EventPage";
import gsap from "gsap";

const App: React.FC = () => {
const [isDarkMode, setIsDarkMode] = useState(false);

const toggleTheme = () => {
setIsDarkMode((prev) => !prev);
};
const markerRefs = useRef<Array<HTMLDivElement | null>>([]);
const isEventPage = window.location.pathname === "/event";

useEffect(() => {
// Global GSAP settings
gsap.config({
autoSleep: 60,
force3D: true,
});
gsap.config({ autoSleep: 60, force3D: true });

const runMarkerIntro = () => {
const markers = markerRefs.current.filter(Boolean) as HTMLDivElement[];
if (!markers.length) return;

const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;

markers.forEach((marker) => {
const rect = marker.getBoundingClientRect();
const markerCenterX = rect.left + rect.width / 2;
const markerCenterY = rect.top + rect.height / 2;
const startX = centerX - markerCenterX;
const startY = centerY - markerCenterY;

gsap.set(marker, {
x: startX,
y: startY,
opacity: 0.9,
});

gsap.to(marker, {
x: 0,
y: 0,
opacity: 1,
duration: 1.2,
ease: "power3.out",
delay: 0.2,
});
});
};

// Wait a frame so fixed markers are laid out before measuring.
requestAnimationFrame(runMarkerIntro);
}, []);

if (isEventPage) {
return <EventPage />;
}

return (
<div className={`min-h-screen transition-colors duration-700 ${isDarkMode ? 'bg-[#050505] text-white selection:bg-orange-900 selection:text-orange-100' : 'bg-white text-slate-900 selection:bg-orange-100 selection:text-orange-900'} overflow-hidden`}>
<Navigation isDarkMode={isDarkMode} toggleTheme={toggleTheme} />
<div className="min-h-screen bg-[#050505] text-[#E0D5C0] selection:bg-[#C6993A] selection:text-[#050505] overflow-x-hidden">
{/* Global film grain */}
<div
style={{
position: "fixed",
inset: 0,
opacity: 0.035,
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")`,
backgroundSize: "200px",
pointerEvents: "none",
zIndex: 9999,
willChange: "transform",
contain: "strict",
}}
/>
{/* Global vignette */}
<div
style={{
position: "fixed",
inset: 0,
background:
"radial-gradient(ellipse 85% 85% at center, transparent 45%, rgba(5,5,5,0.55) 100%)",
pointerEvents: "none",
zIndex: 100,
willChange: "transform",
contain: "strict",
}}
/>
{/* Cinematic registration corner marks */}
{(
[
{
top: "1rem",
left: "1rem",
borderTop: "1px solid rgba(198,153,58,0.38)",
borderLeft: "1px solid rgba(198,153,58,0.38)",
},
{
top: "1rem",
right: "1rem",
borderTop: "1px solid rgba(198,153,58,0.38)",
borderRight: "1px solid rgba(198,153,58,0.38)",
},
{
bottom: "1rem",
left: "1rem",
borderBottom: "1px solid rgba(198,153,58,0.38)",
borderLeft: "1px solid rgba(198,153,58,0.38)",
},
{
bottom: "1rem",
right: "1rem",
borderBottom: "1px solid rgba(198,153,58,0.38)",
borderRight: "1px solid rgba(198,153,58,0.38)",
},
] as React.CSSProperties[]
).map((style, i) => (
<div
key={i}
ref={(el) => {
markerRefs.current[i] = el;
}}
style={{
position: "fixed",
width: 32,
height: 32,
pointerEvents: "none",
zIndex: 9998,
willChange: "transform",
...style,
}}
/>
))}
<Navigation />
<main>
<Hero isDarkMode={isDarkMode} />
<Hero />
<CinematicTransition />
<EventContent />
<CollisionSection />
</main>

{/* Footer fixed at bottom right or hidden for infinite feel */}
<footer className={`fixed bottom-4 right-6 text-[10px] pointer-events-none z-50 transition-colors duration-500 ${isDarkMode ? 'text-white/30' : 'text-black/30'}`}>
<p>&copy; {new Date().getFullYear()} Delphi Clone.</p>
<footer
style={{
position: "relative",
padding: "3.5rem 2rem 2.5rem",
display: "flex",
flexDirection: "column",
alignItems: "center",
textAlign: "center",
gap: "1rem",
background: "#050505",
borderTop: "1px solid rgba(198,153,58,0.12)",
}}
>
<div
style={{
fontFamily: "'IBM Plex Mono', monospace",
fontSize: "0.65rem",
color: "rgba(198,153,58,0.4)",
letterSpacing: "0.25em",
textTransform: "uppercase",
marginBottom: "-0.25rem",
}}
>
Presented by
</div>
<div
style={{
fontFamily: "'IBM Plex Serif', serif",
fontSize: "1.35rem",
fontWeight: 600,
color: "rgba(224,213,192,0.85)",
letterSpacing: "0.02em",
}}
>
<a
href="https://giveago.co"
target="_blank"
rel="noopener noreferrer"
style={{
color: "inherit",
textDecoration: "none",
transition: "color 0.2s ease",
}}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.color =
"rgba(198,153,58,1)";
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.color =
"rgba(224,213,192,0.85)";
}}
>
Give(a)Go
</a>
<span style={{ color: "rgba(198,153,58,0.35)", margin: "0 0.4em" }}>
×
</span>
<a
href="https://napkin.io"
target="_blank"
rel="noopener noreferrer"
style={{
color: "inherit",
textDecoration: "none",
transition: "color 0.2s ease",
}}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.color =
"rgba(198,153,58,1)";
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.color =
"rgba(224,213,192,0.85)";
}}
>
Napkin
</a>
</div>
<div
style={{
width: 40,
height: 1,
background:
"linear-gradient(90deg, transparent, rgba(198,153,58,0.2), transparent)",
margin: "0.5rem 0",
}}
/>
<div
style={{
fontFamily: "'IBM Plex Mono', monospace",
fontSize: "0.5rem",
color: "rgba(198,153,58,0.18)",
letterSpacing: "0.15em",
}}
>
&copy; {new Date().getFullYear()} Give(a)Go
</div>
</footer>
</div>
);
};

export default App;
export default App;
Loading