Merge pull request 'development' (#1) from development into main

Reviewed-on: #1
This commit is contained in:
sashaastiadi 2025-08-06 12:43:43 +00:00
commit 6759f0bd60
171 changed files with 5168 additions and 260 deletions

192
README.md
View File

@ -1,35 +1,193 @@
# Salient # 🌐 OurWorld Web (Salient Template)
Salient is a [Tailwind Plus](https://tailwindcss.com/plus) site template built using [Tailwind CSS](https://tailwindcss.com) and [Next.js](https://nextjs.org). **OurWorld Web** is a statically-exported site built using the [Salient](https://tailwindcss.com/plus) template with **Next.js**, **Tailwind CSS v4**, and **TypeScript**. It showcases people and ventures in a modular and scalable frontend.
## Getting started ---
To get started with this template, first install the npm dependencies: ## 📁 Project Structure
```bash ```
npm install www_ourworld_new/
├── .env.local # Local environment variables
├── public/ # Public static assets (images, videos)
├── scripts/ # Utility scripts (generate people)
├── src/
│ ├── app/ # App routes (people, ventures, pages)
│ │ ├── people/ # Page routes per person
│ │ └── ventures/ # Venture route pages
│ ├── components/
│ │ └── people/ # Individual people components
│ ├── data/ # Temporary or pre-generated data
│ ├── lib/ # People data registry (auto-generated)
│ ├── styles/ # Tailwind & global CSS
│ └── videos/ # Local videos
├── docs/ # Markdown docs for onboarding
├── out/ # Static export folder after build
├── package.json # Scripts and dependencies
└── README.md # You are here.
``` ```
Next, run the development server: ---
## ⚙️ Technologies
* [Next.js](https://nextjs.org/)
* [Tailwind CSS](https://tailwindcss.com/)
* [React](https://reactjs.org/)
* [TypeScript](https://www.typescriptlang.org/)
* [Headless UI](https://headlessui.dev/)
* [Heroicons](https://heroicons.com/)
* [Nodemailer](https://nodemailer.com/) (optional for contact form)
---
## 🚀 Getting Started
```bash ```bash
git clone https://github.com/your-org/www_ourworld_new.git
cd www_ourworld_new
npm install
npm run dev npm run dev
``` ```
Finally, open [http://localhost:3000](http://localhost:3000) in your browser to view the website. Visit `http://localhost:3000` to view the site.
## Customizing ---
You can start editing this template by modifying the files in the `/src` folder. The site will auto-update as you edit these files. ## ⚙️ Building the Site
## License ```bash
npm run build
```
This site template is a commercial product and is licensed under the [Tailwind Plus license](https://tailwindcss.com/plus/license). This will:
## Learn more * Run `scripts/generate-people-data.js` to update the people registry
* Build a static export into `/out`
To learn more about the technologies used in this site template, see the following resources: To preview production:
- [Tailwind CSS](https://tailwindcss.com/docs) - the official Tailwind CSS documentation ```bash
- [Next.js](https://nextjs.org/docs) - the official Next.js documentation npm run start
- [Headless UI](https://headlessui.dev) - the official Headless UI documentation ```
---
## ✨ Adding a Person
1. Create a folder: `public/images/people/jane_doe/`
2. Inside it, place:
* `index.md` (frontmatter + bio)
* `jane_doe.jpg` (image)
3. Run:
```bash
node scripts/generate-all-people.js
npm run generate-people-data
npm run build
```
**Frontmatter example:**
```md
---
title: Jane Doe
description: Community Architect
extra:
socialLinks:
LinkedIn: https://linkedin.com/in/janedoe
---
Jane builds regenerative systems that empower local communities...
```
The script will:
* Generate a component in `src/components/people/People_Jane_Doe.tsx`
* Create or update `src/app/people/jane_doe/page.tsx`
* Add them to the dynamic people registry
---
## 🧠 Anatomy of a Person
### Component (`People_Jane_Doe.tsx`)
```tsx
export const data = [{
name: 'Jane Doe',
role: 'Community Architect',
imageUrl: '/images/people/jane_doe/jane_doe.jpg',
xUrl: '#',
linkedinUrl: 'https://linkedin.com/in/janedoe',
}]
const biography = `<p class="text-lg/7">Jane builds regenerative systems...</p>`
export function People_Jane_Doe() {
return <PersonTemplate personData={data[0]} biography={biography} />
}
```
### Page (`/people/jane_doe/page.tsx`)
```tsx
export default function JaneDoePage() {
return (
<>
<Header_darkbg />
<main>
<People_Jane_Doe />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}
```
---
## 📚 Scripts
| Script | Description |
| ------------------------------------- | ------------------------------------------ |
| `npm run dev` | Start local dev server |
| `npm run build` | Export static HTML & run data generation |
| `npm run generate-people-data` | Regenerate peopleData.ts from all profiles |
| `node scripts/generate-all-people.js` | Parse markdown, images → full pages |
---
## 📄 Docs
* [`docs/adding-people.md`](docs/adding-people.md) Step-by-step onboarding
* [`docs/gmail-setup-guide.md`](docs/gmail-setup-guide.md) Email auth
---
## 🧹 Code Quality
* ESLint + Prettier + Tailwind Plugin
* TypeScript strict mode
* Modular file structure
---
## 🔐 License
This project is based on a **Tailwind Plus** license. You may modify and build on top of it, but redistribution of the template or components is **not permitted**.
See [`LICENSE.md`](./LICENSE.md) for details.
---
## 🧠 Learn More
* [Next.js Documentation](https://nextjs.org/docs)
* [Tailwind CSS Documentation](https://tailwindcss.com/docs)
* [TypeScript Docs](https://www.typescriptlang.org/docs/)
* [Headless UI](https://headlessui.dev/)
* [Heroicons](https://heroicons.com/)

186
docs/adding-people.md Normal file
View File

@ -0,0 +1,186 @@
# Adding New People to the Team Page
This guide explains how to add new team members to the website. The system now features **automated discovery** of people components, so you no longer need to manually update imports!
## 🚀 Quick Start (Automated Workflow)
### 1. Create the Person's Component
Create a new file: `src/components/people/People_[Name].tsx`
Example for John Doe:
```typescript
import { PersonTemplate } from '@/components/PersonTemplate'
export const data = [
{
name: 'John Doe',
role: 'Software Engineer',
imageUrl: '/images/people/john_doe/john_doe.jpg',
xUrl: '#',
linkedinUrl: 'https://www.linkedin.com/in/johndoe/',
},
]
const biography = `
<p class="text-lg/7">
John is a passionate software engineer with expertise in modern web technologies.
He specializes in building scalable applications and has a keen interest in user experience design.
</p>
<p class="mt-5 text-lg/7">
With over 5 years of experience in the tech industry, John has worked on various projects
ranging from startups to enterprise applications. He believes in writing clean, maintainable code
and is always eager to learn new technologies.
</p>
`
export function People_John() {
return <PersonTemplate personData={data[0]} biography={biography} />
}
```
### 2. Create the Person's Page Route
Create a directory and page: `src/app/people/John_Doe/page.tsx`
```typescript
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_John } from '@/components/people/People_John'
export default function JohnDoePage() {
return (
<>
<Header_darkbg />
<main>
<People_John />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}
```
### 3. Add Person's Images
Add the person's images to: `public/images/people/john_doe/`
- `john_doe.jpg` (or .png, .jpeg)
### 4. 🎉 Automatic Integration
**That's it!** The person will automatically appear on the people page when you build the project. The people data registry is automatically updated during the build process.
If you want to test locally during development, you can manually run:
```bash
npm run generate-people-data
```
## 🔧 How the Automation Works
The system now includes a Node.js script (`scripts/generate-people-data.js`) that:
1. **Scans** the `src/components/people/` directory for all `People_*.tsx` files
2. **Generates** the necessary imports and exports in `src/lib/peopleData.ts`
3. **Creates** both synchronous and asynchronous data loading functions
4. **Eliminates** the need for manual import management
### Available Commands
- `npm run generate-people-data` - Manually regenerate the people data registry (for development testing)
- `npm run dev` - Start the development server
- `npm run build` - Build for production (automatically runs people data generation first)
## 📁 File Structure
```
src/
├── components/people/
│ ├── People_Adnan.tsx ✅ Auto-discovered
│ ├── People_Kristof.tsx ✅ Auto-discovered
│ └── People_John.tsx ✅ Auto-discovered (after running script)
├── app/people/
│ ├── Adnan_Fatayerji/page.tsx
│ ├── kristof_de_spiegeleer/page.tsx
│ └── John_Doe/page.tsx
├── lib/
│ └── peopleData.ts 🤖 Auto-generated
└── ...
```
## 🎯 Benefits of the New System
### ✅ **No Manual Imports**
- Add a new `People_*.tsx` file
- Run `npm run generate-people-data`
- Done! No need to edit `peopleData.ts` manually
### ✅ **Error Prevention**
- No more forgotten imports
- No more typos in import statements
- Automatic error handling for each component
### ✅ **Scalable**
- Add as many people as needed
- System automatically discovers all components
- Consistent naming convention enforced
### ✅ **Developer Friendly**
- Clear feedback when running the script
- Lists all discovered components
- Easy to debug and maintain
## 🔄 Migration from Manual System
If you have existing people components that were manually imported:
1. Ensure all components follow the `People_*.tsx` naming convention
2. Run `npm run generate-people-data`
3. The script will automatically discover and include all existing components
## 📋 Naming Conventions
- **Component files**: `People_[FirstName].tsx` (e.g., `People_John.tsx`)
- **Page directories**: `[First_Last]/page.tsx` (e.g., `John_Doe/page.tsx`)
- **Image directories**: `first_last/` (e.g., `john_doe/`)
- **Export function**: `People_[FirstName]` (e.g., `People_John`)
## 🚨 Important Notes
- **Data Structure**: Each person component must export a `data` array with the exact structure shown above
- **Biography Format**: Use HTML string for rich text formatting in the biography
- **Template Usage**: All person components should use `PersonTemplate` for consistency
- **Automatic Discovery**: Components are discovered by filename pattern `People_*.tsx`
- **Script Execution**: Always run `npm run generate-people-data` after adding new people
- **URL Structure**: Individual pages will be accessible at `/people/First_Last`
## 🎨 Template System Benefits
The system uses a **centralized template** (`PersonTemplate.tsx`) for all individual people pages:
- **Global Styling**: Change image size, layout, or styling in `PersonTemplate.tsx` and it applies to ALL people's profiles
- **Consistency**: All people pages have the same structure and design
- **Easy Maintenance**: Update the template once instead of editing each person's component individually
- **Responsive Design**: Template handles all responsive breakpoints uniformly
## 📊 Current Status
- ✅ **Adnan Fatayerji** - Complete (auto-discovered)
- ✅ **Kristof De Spiegeleer** - Complete (auto-discovered)
- ✅ **Automation System** - Active and ready for new additions
## 🔮 Future Enhancements
The automation system can be extended to:
- Auto-generate page routes
- Validate image file existence
- Check for required data fields
- Generate TypeScript types from component data
- Create development-time file watchers for instant updates
---
**🎉 The new automated system makes adding team members faster, safer, and more maintainable than ever before!**

134
docs/gmail-setup-guide.md Normal file
View File

@ -0,0 +1,134 @@
# Gmail Contact Form Setup Guide
This guide will help you configure your contact form to send emails to your company Gmail inbox.
## Prerequisites
- A Gmail account for your company
- Access to Google Account settings
- Basic understanding of environment variables
## Step 1: Enable 2-Factor Authentication
1. Go to [Google Account Settings](https://myaccount.google.com/)
2. Navigate to **Security** → **2-Step Verification**
3. Follow the prompts to enable 2FA if not already enabled
4. **Note**: App passwords require 2FA to be enabled
## Step 2: Generate Gmail App Password
1. Go to [Google Account Settings](https://myaccount.google.com/)
2. Navigate to **Security****2-Step Verification** → **App passwords**
3. Select **Mail** as the app and **Other** as the device
4. Enter "OurWorld Contact Form" as the device name
5. Click **Generate**
6. **Important**: Copy the 16-character app password immediately (you won't see it again)
## Step 3: Configure Environment Variables
1. Open the `.env.local` file in your project root
2. Replace the placeholder values with your actual credentials:
```env
# Your Gmail address (the one that will send emails)
GMAIL_USER=your-company-email@gmail.com
# The 16-character app password you generated (no spaces)
GMAIL_APP_PASSWORD=abcdabcdabcdabcd
# Email address where contact form submissions will be sent
COMPANY_EMAIL=info@ourworld.tf
```
## Step 4: Security Considerations
### Environment Variables
- **Never commit `.env.local` to version control**
- The `.env.local` file is already in `.gitignore`
- Use different environment variables for production deployment
### Production Deployment
For production (Vercel, Netlify, etc.), add these environment variables in your hosting platform's dashboard:
- `GMAIL_USER`
- `GMAIL_APP_PASSWORD`
- `COMPANY_EMAIL`
## Step 5: Testing the Setup
1. Start your development server:
```bash
npm run dev
```
2. Navigate to `/contact` page
3. Fill out and submit the contact form
4. Check your Gmail inbox for the contact form submission
## Troubleshooting
### Common Issues
**"Authentication failed" error:**
- Verify 2FA is enabled on your Google account
- Double-check the app password (no spaces, 16 characters)
- Ensure you're using the app password, not your regular Gmail password
**"Invalid credentials" error:**
- Verify the `GMAIL_USER` email address is correct
- Check that the app password is correctly copied
**Emails not being received:**
- Check your spam/junk folder
- Verify the `COMPANY_EMAIL` address is correct
- Test with a different recipient email
**"Module not found" error:**
- Ensure nodemailer is installed: `npm install nodemailer @types/nodemailer`
### Email Format
The contact form sends emails with:
- **Subject**: "New Contact Form Submission from [First Name] [Last Name]"
- **From**: Your Gmail address (GMAIL_USER)
- **To**: Company email address (COMPANY_EMAIL)
- **Content**: Formatted HTML and plain text versions
## Security Best Practices
1. **Use App Passwords**: Never use your main Gmail password
2. **Limit Access**: Only share credentials with necessary team members
3. **Regular Rotation**: Consider rotating app passwords periodically
4. **Monitor Usage**: Check Gmail's security activity regularly
5. **Environment Separation**: Use different credentials for development/production
## Alternative Solutions
If you prefer not to use Gmail directly, consider these alternatives:
1. **EmailJS**: Client-side email service
2. **Formspree**: Form handling service
3. **SendGrid**: Professional email API
4. **Mailgun**: Email delivery service
5. **Resend**: Modern email API
## Support
If you encounter issues:
1. Check the browser console for error messages
2. Review the server logs for API errors
3. Verify all environment variables are set correctly
4. Test with a simple email first
## File Structure
```
project/
├── .env.local # Environment variables (not in git)
├── src/app/api/contact/ # API route for form submission
├── src/components/ContactHero.tsx # Contact form component
└── docs/gmail-setup-guide.md # This guide
```
---
**Important**: Keep your app password secure and never share it publicly. If compromised, revoke it immediately in your Google Account settings and generate a new one.

View File

@ -1,4 +1,13 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = {} const nextConfig = {
output: 'export',
trailingSlash: true,
images: {
unoptimized: true,
},
// Note: API routes in src/app/api/ will not work with static export
// The contact form API route will need to be handled separately
// Consider using a serverless function or external service for form handling
};
module.exports = nextConfig module.exports = nextConfig;

30
package-lock.json generated
View File

@ -9,13 +9,16 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@headlessui/react": "^2.1.0", "@headlessui/react": "^2.1.0",
"@heroicons/react": "^2.2.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/postcss": "^4.1.7", "@tailwindcss/postcss": "^4.1.7",
"@types/node": "^20.10.8", "@types/node": "^20.10.8",
"@types/nodemailer": "^6.4.17",
"@types/react": "^18.2.47", "@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"next": "^14.0.4", "next": "^14.0.4",
"nodemailer": "^7.0.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"tailwindcss": "^4.1.7", "tailwindcss": "^4.1.7",
@ -205,6 +208,15 @@
"react-dom": "^18" "react-dom": "^18"
} }
}, },
"node_modules/@heroicons/react": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
"integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
"license": "MIT",
"peerDependencies": {
"react": ">= 16 || ^19.0.0-rc"
}
},
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
"version": "0.11.14", "version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@ -1340,6 +1352,15 @@
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/@types/nodemailer": {
"version": "6.4.17",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz",
"integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.11", "version": "15.7.11",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
@ -3984,6 +4005,15 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/nodemailer": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz",
"integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==",
"license": "MIT-0",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",

View File

@ -4,20 +4,25 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "npm run generate-people-data && next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint",
"generate-people-data": "node scripts/generate-people-data.js",
"generate-all-people": "node scripts/generate-all-people.js"
}, },
"browserslist": "defaults, not ie <= 11", "browserslist": "defaults, not ie <= 11",
"dependencies": { "dependencies": {
"@headlessui/react": "^2.1.0", "@headlessui/react": "^2.1.0",
"@heroicons/react": "^2.2.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/postcss": "^4.1.7", "@tailwindcss/postcss": "^4.1.7",
"@types/node": "^20.10.8", "@types/node": "^20.10.8",
"@types/nodemailer": "^6.4.17",
"@types/react": "^18.2.47", "@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"next": "^14.0.4", "next": "^14.0.4",
"nodemailer": "^7.0.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"tailwindcss": "^4.1.7", "tailwindcss": "^4.1.7",

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

BIN
public/images/contact.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
public/images/home/kds.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
public/images/home/open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,21 @@
---
title: Malte Geierhos
weight: 2
description: Team Member
taxonomies:
people: [malte_geierhos]
memberships: []
categories: []
extra:
imgPath: malte_geierhos.jpeg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: #,
websites: #,
}
---
Malte Geierhos is a valued member of our team, bringing expertise and dedication to their role.

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,8 @@
---
title: "People"
paginate_by: 4
sort_by: "weight"
template: "layouts/people.html"
page_template: "partials/personCard.html"
insert_anchor_links: "left"
---

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

@ -0,0 +1,20 @@
---
title: Adnan Fateryji
weight: 2
description: Co-Founder, CEO of TF DMCC
taxonomies:
people: [adnan fatayerji]
memberships: []
categories: []
extra:
imgPath: adnan_fatayerji.jpg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/adnansf/,
}
---
Social entrepreneur and ethical technologist with experience advancing sovereign technologies.

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,22 @@
---
title: Alexandre Hannelas
weight: 3
description: Finance Manager
taxonomies:
people: [alexandre_hannelas]
memberships: []
categories: []
extra:
imgPath: alexandre_hannelas.jpeg
organizations: []
countries: [France]
cities: [Aix-en-Provence]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/alexandre-hannelas-741681112/,
github: https://github.com/AlexandreHannelas,
websites: https://threefold.io/,
}
---
Capable finance specialist with a deep passion in driving lean and innovative business models.

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -0,0 +1,20 @@
---
title: Amrit Gayan
weight: 4
description: Advisor, Finance & Operations
taxonomies:
people: [amrit_gayan]
memberships: []
categories: []
extra:
imgPath: amrit_gayan.jpeg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/amrit-gayan-mba-38083462,
}
---
+20 years experience leading award-winning banks. AI and technology enthusiast.

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,20 @@
---
title: Chris Camponovo
weight: 2
description: Director, Co-Founder of Zanzibar Digital Free Zone
taxonomies:
people: [chris_camponovo]
memberships: []
categories: []
extra:
imgPath: chris_camponovo.jpeg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/christopher-camponovo-a8a57515/,
}
---
Former white house lawyer and private equity fund founder that drives multinational asset recovery solutions.

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

View File

@ -0,0 +1,21 @@
---
title: Florian Fournier
weight: 2
description: Co-Founder
taxonomies:
people: [florian_fournier]
memberships: []
categories: []
extra:
imgPath: florian_fournier.jpeg
organizations: []
countries: [Brazil]
cities: [Rio de Janeiro]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/florianfournier/,
websites: https://www.ootopia.org/,
}
---
Experienced social entrepreneur and marketing executive with extensive network in the US and Latam.

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -0,0 +1,21 @@
---
title: Gregory Flipo
weight: 2
description: Co-Founder, CEO of Sikana
taxonomies:
people: [gregory_flipo]
memberships: []
categories: []
extra:
imgPath: gregory_flipo.jpg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/gregory-flipo-34a9947/,
websites: https://threefold.io/,
}
---
Social entrepreneur with 20 years of experience building educational content and platforms used by millions today.

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -0,0 +1,20 @@
---
title: Hugo Mathecowitsch
weight: 4
description: Advisor, Product
taxonomies:
people: [hugo_mathecowitsch]
memberships: []
categories: []
extra:
imgPath: hugo_mathecowitsch.jpeg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/hugo-mathecowitsch-9447a3213,
}
---
Launched leading fintech startups in South America. Regenerating public goods.

View File

@ -0,0 +1,22 @@
---
title: Jan De Landtsheer
weight: 4
description: Advisor, Tech
taxonomies:
people: [jan_de_landtsheer]
memberships: []
categories: []
extra:
imgPath: jan_de_landtsheer.jpeg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/jand2/,
github: https://github.com/delandtj,
websites: https://threefold.tech,
}
---
+30 years experience building deep tech. Co-Founder at ThreeFold.

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

View File

@ -0,0 +1,21 @@
---
title: Karoline Zizka
weight: 2
description: HR Coordinator
taxonomies:
people: [karoline_zizka]
memberships: []
categories: []
extra:
imgPath: karoline_zizka.jpeg
organizations: []
countries: [Austria]
cities: [Vienna]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/karoline-zizka-52a287b6/,
websites: https://threefold.io/,
}
---
Passionate about driving people-centric strategies with effective KPIs, supporting venture growth and employee satisfaction.

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,22 @@
---
title: Kristof de Spiegeleer
weight: 1
description: Co-Founder & CEO
taxonomies:
people: [kristof_de_spiegeleer]
memberships: []
categories: []
extra:
imgPath: kristof_de_spiegeleer.jpeg
organizations: []
countries: [UAE, Egypt]
cities: [Dubai, El Gouna]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/despiegk/,
github: https://github.com/despiegk,
websites: https://www.threefold.io, https://www.incubaid.com/,
}
---
Serial entrepreneur with 30 years of experience in Internet and cloud, with + USD 600m in exits.

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -0,0 +1,21 @@
---
title: Marion Ravarino
weight: 3
description: Project Management
taxonomies:
people: [marion_ravarino]
memberships: []
categories: [foundation]
extra:
imgPath: marion_ravarino.jpeg
organizations: [threefold_tech]
countries: [Germany]
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/marion-ravarino/,
websites: https://threefold.io/,
}
---
Driven by a natural curiosity for new knowledge. OurWorld's mission and values are perfectly aligned with my vision of a better future for all.

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -0,0 +1,20 @@
---
title: Michel Coucke
weight: 1
description: Director
taxonomies:
people: [michel_coucke]
memberships: []
categories: []
extra:
imgPath: michel_coucke.jpg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/michel-coucke-66112a,
}
---
CEO Lancor, a global executive search firm specializing in board-level search mandates including C-level execs and more.

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,20 @@
---
title: Nousrath Bhugeloo
weight: 1
description: Director & Executive Chairperson
taxonomies:
people: [nousrath_bhugeloo]
memberships: []
categories: []
extra:
imgPath: nousrath_bhugeloo.jpg
organizations: []
countries: []
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/nousrath-bhugeloo-78078520,
}
---
Advisor to multinationals, funds, family offices and individual entrepreneurs on their strategies and cross border business.

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,21 @@
---
title: Owen Kemp
weight: 4
description: Advisor, Business Development
taxonomies:
people: [owen_kemp]
memberships: []
categories: []
extra:
imgPath: owen_kemp.jpeg
organizations: []
countries: [Austria]
cities: [Vienna]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/owen-kemp-b6b5a113/,
websites: https://threefold.tech,
}
---
Former HPE VP Innovation & International Relations. Co-Founder at TF9.

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

View File

@ -0,0 +1,22 @@
---
title: Sacha Obeegadoo
weight: 2
description: Program Manager
taxonomies:
people: [sacha_obeegadoo]
memberships: []
categories: []
extra:
imgPath: sacha_obeegadoo.jpg
organizations: []
countries: [Nomad]
cities: [Nomad]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/sachaobeegadoo/,
github: https://github.com/Sacha96,
websites: https://threefold.io/,
}
---
Business Developer with experience growing companies across various sectors.

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -0,0 +1,22 @@
---
title: Sam Taggart
weight: 4
description: Communication & Community
taxonomies:
people: [sam_taggart]
memberships: []
categories: []
extra:
imgPath: sam_taggart.jpg
organizations: []
countries: [Turkey, USA]
cities: [Akyaka, New York, Philadelphia]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/samtaggart/,
github: https://github.com/samtaggart,
websites: https://threefold.io/,
}
---
Communication strategist and global citizen, inspired by the beauty of our differences and humbled by our similarities. Driven to drive collective positive change.

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

View File

@ -0,0 +1,22 @@
---
title: Sasha Astiadi
weight: 4
description: Web Operations
taxonomies:
people: [sasha_astiadi]
memberships: []
categories: []
extra:
imgPath: sasha_astiadi.png
organizations: []
countries: [Indonesia, Spain, Germany]
cities: [Bali, Barcelona, Berlin]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/sastiadi,
github: https://github.com/sasha-astiadi,
websites: https://threefold.io/, https://threefold.tech, https://sashaastiadi.com/,
}
---
A Human-centered product manager, researcher and UX Designer who puts the planet as her main mission.

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

View File

@ -0,0 +1,22 @@
---
title: Timur Gordon
weight: 4
description: Software Developer
taxonomies:
people: [timur_gordon]
memberships: []
categories: [foundation]
extra:
imgPath: timur_gordon.jpg
organizations: [threefold_foundation]
countries: [Turkey, USA]
cities: [Istanbul, Zanzibar]
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/timur-gordon/,
github: https://github.com/timurgordon,
websites: https://threefold.io,
}
---
Software developer who loves nature and believes in using tech to help the planet and people. Believer in open source and collective intelligence.

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

View File

@ -0,0 +1,21 @@
---
title: Vianney Spriet
weight: 3
description: Business Development
taxonomies:
people: [vianney_spriet]
memberships: []
categories: []
extra:
imgPath: vianney_spriet.jpeg
organizations: []
countries: [France]
cities: []
private: 0
socialLinks: {
LinkedIn: https://www.linkedin.com/in/vianney-spriet-09737526/,
websites: https://ourworld.tf,
}
---
Experienced wealth manager, business developer, and former professional skipper with a wide network in Europe.

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
public/videos/ourworld.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1,230 @@
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
// Paths
const peopleImagesDir = path.join(__dirname, '../public/images/people')
const peopleComponentsDir = path.join(__dirname, '../src/components/people')
const peopleAppDir = path.join(__dirname, '../src/app/people')
// Function to parse markdown frontmatter
function parseFrontmatter(content) {
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/
const match = content.match(frontmatterRegex)
if (!match) return null
const frontmatter = {}
const body = match[2].trim()
// Parse YAML-like frontmatter
const lines = match[1].split('\n')
let currentKey = null
for (const line of lines) {
const trimmed = line.trim()
if (!trimmed) continue
if (trimmed.includes(':') && !trimmed.startsWith(' ')) {
const [key, ...valueParts] = trimmed.split(':')
const value = valueParts.join(':').trim()
currentKey = key.trim()
if (value) {
if (value.startsWith('[') && value.endsWith(']')) {
// Array value
frontmatter[currentKey] = value.slice(1, -1).split(',').map(v => v.trim())
} else if (value.startsWith('{') && value.endsWith('}')) {
// Object value - parse simple key-value pairs
const obj = {}
const objContent = value.slice(1, -1)
const pairs = objContent.split(',')
for (const pair of pairs) {
const [k, v] = pair.split(':').map(s => s.trim())
if (k && v) {
obj[k] = v
}
}
frontmatter[currentKey] = obj
} else {
frontmatter[currentKey] = value
}
}
}
}
return { frontmatter, body }
}
// Function to convert name to component name
function nameToComponentName(name) {
return name.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '')
}
// Function to convert name to function name
function nameToFunctionName(name) {
return 'People_' + nameToComponentName(name)
}
// Function to generate people component
function generatePeopleComponent(personData) {
const { name, role, imageUrl, linkedinUrl, description } = personData
const functionName = nameToFunctionName(name)
return `import { PersonTemplate } from '@/components/PersonTemplate'
export const data = [
{
name: '${name}',
role: '${role}',
imageUrl: '${imageUrl}',
xUrl: '#',
linkedinUrl: '${linkedinUrl || '#'}',
},
]
const biography = \`
<p class="text-lg/7">
${description || `${name} is a valued member of our team, bringing expertise and dedication to their role as ${role}.`}
</p>
\`
export function ${functionName}() {
return <PersonTemplate personData={data[0]} biography={biography} />
}
`
}
// Function to generate page component
function generatePageComponent(personData) {
const { name } = personData
const functionName = nameToFunctionName(name)
const componentName = nameToComponentName(name)
const pageFunctionName = name.replace(/\s+/g, '') + 'Page'
return `import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { ${functionName} } from '@/components/people/People_${componentName}'
export default function ${pageFunctionName}() {
return (
<>
<Header_darkbg />
<main>
<${functionName} />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}
`
}
// Main function
function main() {
console.log('🔍 Scanning for people data...')
if (!fs.existsSync(peopleImagesDir)) {
console.log('❌ People images directory not found')
return
}
const peopleDirectories = fs.readdirSync(peopleImagesDir)
.filter(dir => {
const dirPath = path.join(peopleImagesDir, dir)
return fs.statSync(dirPath).isDirectory() && dir !== '_index.md'
})
console.log(`✅ Found ${peopleDirectories.length} people directories`)
let generatedCount = 0
let updatedCount = 0
for (const personDir of peopleDirectories) {
try {
const indexPath = path.join(peopleImagesDir, personDir, 'index.md')
if (!fs.existsSync(indexPath)) {
console.log(`⚠️ No index.md found for ${personDir}`)
continue
}
const content = fs.readFileSync(indexPath, 'utf8')
const parsed = parseFrontmatter(content)
if (!parsed) {
console.log(`⚠️ Could not parse frontmatter for ${personDir}`)
continue
}
const { frontmatter, body } = parsed
// Find the actual image file
const imageExtensions = ['jpg', 'jpeg', 'png']
let actualImagePath = frontmatter.extra?.imgPath
if (!actualImagePath) {
// Try to find the image file with different extensions
for (const ext of imageExtensions) {
const imagePath = path.join(peopleImagesDir, personDir, `${personDir}.${ext}`)
if (fs.existsSync(imagePath)) {
actualImagePath = `${personDir}.${ext}`
break
}
}
// Fallback to jpg if no image found
actualImagePath = actualImagePath || `${personDir}.jpg`
}
// Extract person data
const personData = {
name: frontmatter.title || personDir.replace(/_/g, ' '),
role: frontmatter.description || 'Team Member',
imageUrl: `/images/people/${personDir}/${actualImagePath}`,
linkedinUrl: frontmatter.extra?.socialLinks?.LinkedIn || '#',
description: body || `${frontmatter.title} is a valued member of our team.`
}
// Generate component
const componentName = nameToComponentName(personData.name)
const componentPath = path.join(peopleComponentsDir, `People_${componentName}.tsx`)
if (!fs.existsSync(componentPath)) {
const componentContent = generatePeopleComponent(personData)
fs.writeFileSync(componentPath, componentContent, 'utf8')
console.log(`✅ Generated component: People_${componentName}.tsx`)
generatedCount++
}
// Update page.tsx
const pagePath = path.join(peopleAppDir, personDir, 'page.tsx')
if (fs.existsSync(pagePath)) {
const pageContent = generatePageComponent(personData)
fs.writeFileSync(pagePath, pageContent, 'utf8')
console.log(`✅ Updated page: ${personDir}/page.tsx`)
updatedCount++
}
} catch (error) {
console.error(`❌ Error processing ${personDir}:`, error.message)
}
}
console.log(`\n🎉 Generation complete!`)
console.log(` - Generated ${generatedCount} new components`)
console.log(` - Updated ${updatedCount} page files`)
console.log(`\n📝 Run 'npm run generate-people-data' to update the data registry`)
}
// Run the script
if (require.main === module) {
main()
}
module.exports = { main }

View File

@ -0,0 +1,127 @@
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
// Paths
const peopleComponentsDir = path.join(__dirname, '../src/components/people')
const peopleDataFile = path.join(__dirname, '../src/lib/peopleData.ts')
// Function to get all People_*.tsx files
function getPeopleComponents() {
if (!fs.existsSync(peopleComponentsDir)) {
console.log('People components directory not found')
return []
}
const files = fs.readdirSync(peopleComponentsDir)
return files
.filter(file => file.startsWith('People_') && file.endsWith('.tsx'))
.map(file => {
const componentName = file.replace('.tsx', '')
const importName = componentName.toLowerCase().replace('people_', '')
return {
fileName: file,
componentName,
importName,
importPath: `@/components/people/${componentName}`
}
})
}
// Function to generate the peopleData.ts content
function generatePeopleDataContent(components) {
const imports = components
.map(comp => `import { data as ${comp.importName}Data } from '${comp.importPath}'`)
.join('\n')
const dynamicImports = components
.map(comp => ` () => import('${comp.importPath}'),`)
.join('\n')
const syncDataPush = components
.map(comp => `
try {
allPeopleData.push(...${comp.importName}Data)
} catch (error) {
console.error('Error loading ${comp.importName} data:', error)
}`)
.join('\n')
return `// This file is auto-generated by scripts/generate-people-data.js
// When new people components are added, run: npm run generate-people-data
export interface PersonData {
name: string
role: string
imageUrl: string
xUrl: string
linkedinUrl: string
}
// Function to dynamically get all people data
export async function getAllPeopleData(): Promise<PersonData[]> {
const allPeopleData: PersonData[] = []
// Auto-generated list of all people components
const peopleComponents = [
${dynamicImports}
]
for (const importComponent of peopleComponents) {
try {
const module = await importComponent()
if (module.data && Array.isArray(module.data)) {
allPeopleData.push(...module.data)
}
} catch (error) {
console.error('Error loading people data:', error)
}
}
return allPeopleData
}
// Synchronous version using static imports for immediate data access
${imports}
export function getAllPeopleDataSync(): PersonData[] {
const allPeopleData: PersonData[] = []
${syncDataPush}
return allPeopleData
}
`
}
// Main function
function main() {
console.log('🔍 Scanning for people components...')
const components = getPeopleComponents()
if (components.length === 0) {
console.log('❌ No people components found')
return
}
console.log(`✅ Found ${components.length} people component(s):`)
components.forEach(comp => {
console.log(` - ${comp.componentName}`)
})
console.log('📝 Generating peopleData.ts...')
const content = generatePeopleDataContent(components)
fs.writeFileSync(peopleDataFile, content, 'utf8')
console.log('✅ Successfully updated src/lib/peopleData.ts')
console.log('\n🎉 All people components will now automatically appear on the people page!')
}
// Run the script
if (require.main === module) {
main()
}
module.exports = { main, getPeopleComponents, generatePeopleDataContent }

34
src/app/about/page.tsx Normal file
View File

@ -0,0 +1,34 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { HomeAbout } from '@/components/HomeAbout'
import { Hero } from '@/components/Hero'
import { Pricing } from '@/components/Pricing'
import { PrimaryFeatures } from '@/components/PrimaryFeatures'
import { SecondaryFeatures } from '@/components/SecondaryFeatures'
import { Testimonials } from '@/components/Testimonials'
import { HomePrinciples } from '@/components/HomePrinciples'
import { HomeMilestones } from '@/components/HomeMilestones'
import { HomeVentures } from '@/components/HomeVentures'
import { Quote } from '@/components/Quote'
import { AboutHero } from '@/components/AboutHero'
import { AboutMission } from '@/components/AboutMission'
import { AboutExperience } from '@/components/AboutExperience'
export default function About() {
return (
<>
<Header_darkbg />
<main>
<AboutHero />
<AboutMission />
<AboutExperience />
<Quote />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,80 @@
import { NextRequest, NextResponse } from 'next/server'
import nodemailer from 'nodemailer'
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { firstName, lastName, email, phoneNumber, message } = body
// Validate required fields
if (!firstName || !lastName || !email || !message) {
return NextResponse.json(
{ error: 'Missing required fields' },
{ status: 400 }
)
}
// Create transporter using Gmail SMTP
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.GMAIL_USER,
pass: process.env.GMAIL_APP_PASSWORD,
},
})
// Email content
const mailOptions = {
from: process.env.GMAIL_USER,
to: process.env.COMPANY_EMAIL || process.env.GMAIL_USER,
subject: `New Contact Form Submission from ${firstName} ${lastName}`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #4f46e5;">New Contact Form Submission</h2>
<div style="background-color: #f8fafc; padding: 20px; border-radius: 8px; margin: 20px 0;">
<h3 style="margin-top: 0; color: #374151;">Contact Information</h3>
<p><strong>Name:</strong> ${firstName} ${lastName}</p>
<p><strong>Email:</strong> <a href="mailto:${email}">${email}</a></p>
${phoneNumber ? `<p><strong>Phone:</strong> ${phoneNumber}</p>` : ''}
</div>
<div style="background-color: #f8fafc; padding: 20px; border-radius: 8px; margin: 20px 0;">
<h3 style="margin-top: 0; color: #374151;">Message</h3>
<p style="white-space: pre-wrap;">${message}</p>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e5e7eb; color: #6b7280; font-size: 14px;">
<p>This email was sent from the OurWorld contact form on ${new Date().toLocaleString()}.</p>
</div>
</div>
`,
text: `
New Contact Form Submission
Name: ${firstName} ${lastName}
Email: ${email}
${phoneNumber ? `Phone: ${phoneNumber}` : ''}
Message:
${message}
Sent on: ${new Date().toLocaleString()}
`,
}
// Send email
await transporter.sendMail(mailOptions)
return NextResponse.json(
{ message: 'Email sent successfully' },
{ status: 200 }
)
} catch (error) {
console.error('Error sending email:', error)
return NextResponse.json(
{ error: 'Failed to send email' },
{ status: 500 }
)
}
}

33
src/app/contact/page.tsx Normal file
View File

@ -0,0 +1,33 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { HomeAbout } from '@/components/HomeAbout'
import { Hero } from '@/components/Hero'
import { Pricing } from '@/components/Pricing'
import { PrimaryFeatures } from '@/components/PrimaryFeatures'
import { SecondaryFeatures } from '@/components/SecondaryFeatures'
import { Testimonials } from '@/components/Testimonials'
import { HomePrinciples } from '@/components/HomePrinciples'
import { HomeMilestones } from '@/components/HomeMilestones'
import { HomeVentures } from '@/components/HomeVentures'
import { Quote } from '@/components/Quote'
import { AboutHero } from '@/components/AboutHero'
import { AboutMission } from '@/components/AboutMission'
import { AboutExperience } from '@/components/AboutExperience'
import { ContactHero } from '@/components/ContactHero'
export default function Contact() {
return (
<>
<Header_darkbg />
<main>
<ContactHero />
<Quote />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -1,5 +1,4 @@
import { type Metadata } from 'next' import { type Metadata } from 'next'
import { Inter, Lexend } from 'next/font/google'
import clsx from 'clsx' import clsx from 'clsx'
import '@/styles/tailwind.css' import '@/styles/tailwind.css'
@ -10,20 +9,13 @@ export const metadata: Metadata = {
default: 'OurWorld - Accounting made simple for small businesses', default: 'OurWorld - Accounting made simple for small businesses',
}, },
description: description:
'Most bookkeeping software is accurate, but hard to use. We make the opposite trade-off, and hope you dont get audited.', 'Most bookkeeping software is accurate, but hard to use. We make the opposite trade-off, and hope you don\'t get audited.',
} }
const inter = Inter({ // Avenir font configuration with fallbacks
subsets: ['latin'], const avenir = {
display: 'swap', variable: '--font-avenir',
variable: '--font-inter', }
})
const lexend = Lexend({
subsets: ['latin'],
display: 'swap',
variable: '--font-lexend',
})
export default function RootLayout({ export default function RootLayout({
children, children,
@ -35,8 +27,7 @@ export default function RootLayout({
lang="en" lang="en"
className={clsx( className={clsx(
'h-full scroll-smooth bg-white antialiased', 'h-full scroll-smooth bg-white antialiased',
inter.variable, avenir.variable,
lexend.variable,
)} )}
> >
<body className="flex h-full flex-col">{children}</body> <body className="flex h-full flex-col">{children}</body>

View File

@ -1,24 +1,31 @@
import { CallToAction } from '@/components/CallToAction' import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs' import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer' import { Footer } from '@/components/Footer'
import { Header } from '@/components/Header' import { HomeAbout } from '@/components/HomeAbout'
import { Hero } from '@/components/Hero' import { Hero } from '@/components/Hero'
import { Pricing } from '@/components/Pricing' import { Pricing } from '@/components/Pricing'
import { PrimaryFeatures } from '@/components/PrimaryFeatures' import { PrimaryFeatures } from '@/components/PrimaryFeatures'
import { SecondaryFeatures } from '@/components/SecondaryFeatures' import { SecondaryFeatures } from '@/components/SecondaryFeatures'
import { Testimonials } from '@/components/Testimonials' import { Testimonials } from '@/components/Testimonials'
import { HomePrinciples } from '@/components/HomePrinciples'
import { HomeMilestones } from '@/components/HomeMilestones'
import { HomeVentures } from '@/components/HomeVentures'
import { Quote } from '@/components/Quote'
import { Header } from '@/components/Header'
import { Header_darkbg } from '@/components/Header_darkbg'
export default function Home() { export default function Home() {
return ( return (
<> <>
<Header /> <Header_darkbg />
<main> <main>
<Hero /> <Hero />
<PrimaryFeatures /> <HomeAbout />
<SecondaryFeatures /> <HomePrinciples />
<HomeMilestones />
<HomeVentures />
<Quote />
<CallToAction /> <CallToAction />
<Testimonials />
<Pricing />
<Faqs /> <Faqs />
</main> </main>
<Footer /> <Footer />

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Adnan_Fateryji } from '@/components/people/People_Adnan_Fateryji'
export default function AdnanFateryjiPage() {
return (
<>
<Header_darkbg />
<main>
<People_Adnan_Fateryji />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Malte_Geierhos } from '@/components/people/People_Malte_Geierhos'
export default function MalteGeierhosPage() {
return (
<>
<Header_darkbg />
<main>
<People_Malte_Geierhos />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Alexandre_Hannelas } from '@/components/people/People_Alexandre_Hannelas'
export default function AlexandreHannelasPage() {
return (
<>
<Header_darkbg />
<main>
<People_Alexandre_Hannelas />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Amrit_Gayan } from '@/components/people/People_Amrit_Gayan'
export default function AmritGayanPage() {
return (
<>
<Header_darkbg />
<main>
<People_Amrit_Gayan />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Chris_Camponovo } from '@/components/people/People_Chris_Camponovo'
export default function ChrisCamponovoPage() {
return (
<>
<Header_darkbg />
<main>
<People_Chris_Camponovo />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Florian_Fournier } from '@/components/people/People_Florian_Fournier'
export default function FlorianFournierPage() {
return (
<>
<Header_darkbg />
<main>
<People_Florian_Fournier />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Gregory_Flipo } from '@/components/people/People_Gregory_Flipo'
export default function GregoryFlipoPage() {
return (
<>
<Header_darkbg />
<main>
<People_Gregory_Flipo />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Hugo_Mathecowitsch } from '@/components/people/People_Hugo_Mathecowitsch'
export default function HugoMathecowitschPage() {
return (
<>
<Header_darkbg />
<main>
<People_Hugo_Mathecowitsch />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Jan_De_Landtsheer } from '@/components/people/People_Jan_De_Landtsheer'
export default function JanDeLandtsheerPage() {
return (
<>
<Header_darkbg />
<main>
<People_Jan_De_Landtsheer />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Karoline_Zizka } from '@/components/people/People_Karoline_Zizka'
export default function KarolineZizkaPage() {
return (
<>
<Header_darkbg />
<main>
<People_Karoline_Zizka />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Kristof_de_Spiegeleer } from '@/components/people/People_Kristof_de_Spiegeleer'
export default function KristofdeSpiegeleerPage() {
return (
<>
<Header_darkbg />
<main>
<People_Kristof_de_Spiegeleer />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Marion_Ravarino } from '@/components/people/People_Marion_Ravarino'
export default function MarionRavarinoPage() {
return (
<>
<Header_darkbg />
<main>
<People_Marion_Ravarino />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Michel_Coucke } from '@/components/people/People_Michel_Coucke'
export default function MichelCouckePage() {
return (
<>
<Header_darkbg />
<main>
<People_Michel_Coucke />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Nousrath_Bhugeloo } from '@/components/people/People_Nousrath_Bhugeloo'
export default function NousrathBhugelooPage() {
return (
<>
<Header_darkbg />
<main>
<People_Nousrath_Bhugeloo />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Owen_Kemp } from '@/components/people/People_Owen_Kemp'
export default function OwenKempPage() {
return (
<>
<Header_darkbg />
<main>
<People_Owen_Kemp />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

32
src/app/people/page.tsx Normal file
View File

@ -0,0 +1,32 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { HomeAbout } from '@/components/HomeAbout'
import { Hero } from '@/components/Hero'
import { Pricing } from '@/components/Pricing'
import { PrimaryFeatures } from '@/components/PrimaryFeatures'
import { SecondaryFeatures } from '@/components/SecondaryFeatures'
import { Testimonials } from '@/components/Testimonials'
import { HomePrinciples } from '@/components/HomePrinciples'
import { HomeMilestones } from '@/components/HomeMilestones'
import { HomeVentures } from '@/components/HomeVentures'
import { Quote } from '@/components/Quote'
import { AboutHero } from '@/components/AboutHero'
import { AboutMission } from '@/components/AboutMission'
import { AboutExperience } from '@/components/AboutExperience'
import { PeopleHero } from '@/components/PeopleHero'
export default function People() {
return (
<>
<Header_darkbg />
<main>
<PeopleHero />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Sacha_Obeegadoo } from '@/components/people/People_Sacha_Obeegadoo'
export default function SachaObeegadooPage() {
return (
<>
<Header_darkbg />
<main>
<People_Sacha_Obeegadoo />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Sam_Taggart } from '@/components/people/People_Sam_Taggart'
export default function SamTaggartPage() {
return (
<>
<Header_darkbg />
<main>
<People_Sam_Taggart />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Sasha_Astiadi } from '@/components/people/People_Sasha_Astiadi'
export default function SashaAstiadiPage() {
return (
<>
<Header_darkbg />
<main>
<People_Sasha_Astiadi />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Timur_Gordon } from '@/components/people/People_Timur_Gordon'
export default function TimurGordonPage() {
return (
<>
<Header_darkbg />
<main>
<People_Timur_Gordon />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

Some files were not shown because too many files have changed in this diff Show More