first commit

This commit is contained in:
vargha1
2026-09-21 16:18:49 +03:30
commit 5198432136
61 changed files with 9629 additions and 0 deletions

44
.gitignore vendored Normal file
View File

@@ -0,0 +1,44 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# playwright mcp
/.playwright-mcp

9
AGENTS.md Normal file
View File

@@ -0,0 +1,9 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
<!-- END:nextjs-agent-rules -->

1
CLAUDE.md Normal file
View File

@@ -0,0 +1 @@
@AGENTS.md

36
README.md Normal file
View File

@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

18
eslint.config.mjs Normal file
View File

@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);
export default eslintConfig;

8
next.config.ts Normal file
View File

@@ -0,0 +1,8 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// Pin root to this project; a stray lockfile in the home dir otherwise confuses detection
turbopack: { root: __dirname },
};
export default nextConfig;

6909
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
package.json Normal file
View File

@@ -0,0 +1,26 @@
{
"name": "voyager",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"next": "^16.3.5",
"react": "^19.3.0",
"react-dom": "^19.3.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.3.3",
"@types/node": "^26.5.1",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"eslint": "^9.39.5",
"eslint-config-next": "^16.3.5",
"tailwindcss": "^4.3.3",
"typescript": "^6.0.3"
}
}

7
postcss.config.mjs Normal file
View File

@@ -0,0 +1,7 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;

BIN
public/aboutBus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
public/box-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

BIN
public/box-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 KiB

BIN
public/customer-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

BIN
public/hero-image-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

BIN
public/hero-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

BIN
public/iphone17-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

BIN
public/iphone17-opaque.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

BIN
public/iphone17.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

BIN
public/ladder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/logo-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/platform-section.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
public/text-logo-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
public/voyager-brain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
public/voyager-diff-ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

125
src/app/globals.css Normal file
View File

@@ -0,0 +1,125 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #0f1b3d;
--muted: #5b6475;
--accent: #f26b1d;
--accent-hover: #dd5c10;
--primary: #f26b1d;
--primary-foreground: #ffffff;
--primary-hover: #dd5c10;
--outline: #d9dee8;
--outline-bg: #ffffff;
--outline-hover: #f4f6fa;
--frame: #e6e9ef;
--card: #f3f6fb;
--icon: #3b6fd8;
--divider: #e3e7ee;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-muted: var(--muted);
--color-accent: var(--accent);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-primary-hover: var(--primary-hover);
--color-outline: var(--outline);
--color-outline-bg: var(--outline-bg);
--color-outline-hover: var(--outline-hover);
--color-frame: var(--frame);
--color-card: var(--card);
--color-icon: var(--icon);
--color-divider: var(--divider);
--font-sans: var(--font-archivo);
--animate-marquee: marquee 40s linear infinite;
--animate-fade-up: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) both;
--animate-float-1: float-1 5s ease-in-out infinite;
--animate-float-2: float-2 6.5s ease-in-out infinite;
--animate-float-3: float-3 5.8s ease-in-out infinite;
--animate-brain-drift: brain-drift 7s ease-in-out infinite;
@keyframes marquee {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(1.5rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes float-1 {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(0, -14px) rotate(-1deg);
}
}
@keyframes float-2 {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(0, 12px) rotate(1.5deg);
}
}
@keyframes float-3 {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(0, -10px) rotate(1deg);
}
}
@keyframes brain-drift {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-8px);
}
}
}
html {
color-scheme: light;
scroll-behavior: smooth;
}
body {
background: var(--background);
color: var(--foreground);
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

24
src/app/layout.tsx Normal file
View File

@@ -0,0 +1,24 @@
import type { Metadata } from "next";
import { Archivo } from "next/font/google";
import "./globals.css";
// Variable font: omitting `weight` loads the full weight axis
const archivo = Archivo({
variable: "--font-archivo",
subsets: ["latin"],
axes: ["wdth"],
});
export const metadata: Metadata = {
title: "VOYAGER — Book, Manage, Grow",
description: "AI-Powered Tour Operator & Transportation Management Software",
icons: { icon: "/logo-light.png" },
};
export default function RootLayout({ children }: LayoutProps<"/">) {
return (
<html lang="en" className={`${archivo.variable} h-full antialiased`}>
<body className="min-h-full flex flex-col font-sans">{children}</body>
</html>
);
}

47
src/app/page.tsx Normal file
View File

@@ -0,0 +1,47 @@
import { Navbar } from "@/components/navbar";
import { Hero } from "@/components/hero";
import { Partners } from "@/components/partners";
import { Platform } from "@/components/platform";
import { VoyagerDifference } from "@/components/voyager-difference";
import { TourBooking } from "@/components/tour-booking";
import { VoyagerAI } from "@/components/voyager-ai";
import { TourOperations } from "@/components/tour-operations";
import { CRM } from "@/components/crm";
import { OTADistribution } from "@/components/ota-distribution";
import { RentalManagement } from "@/components/rental-management";
import { PaymentsRevenue } from "@/components/payments-revenue";
import { MarketingGrowth } from "@/components/marketing-growth";
import { Solutions } from "@/components/solutions";
import { Pricing } from "@/components/pricing";
import { Integrations } from "@/components/integrations";
import { CustomerStories } from "@/components/customer-stories";
import { About } from "@/components/about";
import { Footer } from "@/components/footer";
export default function Home() {
return (
<>
<Navbar />
<main className="flex-1 overflow-x-clip">
<Hero />
<Partners />
<Platform />
<VoyagerDifference />
<TourBooking />
<VoyagerAI />
<TourOperations />
<CRM />
<OTADistribution />
<RentalManagement />
<PaymentsRevenue />
<MarketingGrowth />
<Solutions />
<Pricing />
<Integrations />
<CustomerStories />
<About />
</main>
<Footer />
</>
);
}

73
src/components/about.tsx Normal file
View File

@@ -0,0 +1,73 @@
import Image from "next/image";
import { Reveal } from "@/components/reveal";
const principles = [
{ title: "Simple", description: "Powerful technology shouldn’t feel complicated." },
{ title: "Connected", description: "Your systems should work together." },
{ title: "Scalable", description: "Your software should grow with your business." },
{ title: "Intelligence", description: "AI should help your team, not replace the people who know your business." },
];
export function About() {
return (
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="mx-auto grid max-w-6xl items-start gap-12 px-4 sm:px-6 lg:grid-cols-[7fr_5fr] lg:gap-16">
<div className="flex flex-col gap-8">
<div className="flex flex-col gap-4">
<h2 className="text-4xl font-medium tracking-tight text-[#11192d] sm:text-5xl">About Voyager</h2>
<p className="text-base text-[#11192d]/70 sm:text-justify">
<span className="font-bold">Technology That Makes Tourism Businesses Simpler</span>
<br />
Voyager is an AI-powered business operating platform for tour operators, transportation companies, rental
businesses and tourism enterprises. Voyager was created around a simple idea: Tour operators shouldn’t have
to build their business around their software. The software should adapt to the business. Voyager brings
tour booking, ticketing, operations, fleet management, CRM, payments, communication, distribution,
marketing and AI together in one connected platform.
</p>
</div>
<div className="flex flex-col gap-4">
<h3 className="text-2xl font-medium text-[#11192d]">Our Principles</h3>
<div className="grid gap-4 sm:grid-cols-2">
{principles.map((p, i) => (
<Reveal key={p.title} delay={i * 80}>
<div className="flex h-full flex-col gap-2 rounded-3xl bg-[#0160d4]/3 px-6 py-4">
<h4 className="text-2xl font-medium text-[#11192d]">{p.title}</h4>
<p className="text-base text-[#11192d]/70">{p.description}</p>
</div>
</Reveal>
))}
<Reveal delay={principles.length * 80} className="sm:col-span-2">
<div className="flex flex-col gap-2 rounded-3xl bg-[#0160d4]/3 px-6 py-4">
<h4 className="text-2xl font-medium text-[#11192d]">Affordable</h4>
<p className="text-base text-[#11192d]/70">
Enterprise-level capability shouldn’t require enterprise-level complexity.
</p>
</div>
</Reveal>
</div>
</div>
</div>
<Reveal delay={100} className="relative mx-auto w-64 sm:w-72 lg:mx-0">
{/* Blue tile with the bus (and its baked-in shadow) overflowing the top, plus a fading reflection */}
<div className="relative aspect-4/5">
<div
aria-hidden
className="absolute inset-x-[8%] bottom-0 top-[40%] bg-linear-to-b from-[#0160d4]/35 to-transparent"
/>
<div aria-hidden className="absolute inset-x-[10%] top-[3%] h-[62%] rounded-[32px] bg-[#0160d4]" />
<Image
src="/aboutBus.png"
alt="A Voyager fleet vehicle"
width={290}
height={500}
sizes="288px"
className="absolute left-4 top-0 h-auto w-[68%]"
/>
</div>
</Reveal>
</div>
</section>
);
}

26
src/components/button.tsx Normal file
View File

@@ -0,0 +1,26 @@
import Link from "next/link";
import type { ComponentProps } from "react";
type ButtonLinkProps = ComponentProps<typeof Link> & {
variant?: "primary" | "outline";
size?: "md" | "lg";
};
const variants = {
primary: "bg-primary text-primary-foreground border-primary hover:bg-primary-hover hover:border-primary-hover",
outline: "bg-outline-bg text-foreground border-outline hover:bg-outline-hover",
};
const sizes = {
md: "h-10 px-4 text-sm",
lg: "h-11 px-6 text-sm",
};
export function ButtonLink({ variant = "primary", size = "md", className = "", ...props }: ButtonLinkProps) {
return (
<Link
className={`inline-flex items-center justify-center whitespace-nowrap rounded-lg border-2 font-medium transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg active:translate-y-0 active:shadow-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent ${variants[variant]} ${sizes[size]} ${className}`}
{...props}
/>
);
}

147
src/components/crm.tsx Normal file
View File

@@ -0,0 +1,147 @@
import type { ReactNode } from "react";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Feature = { title: string; description: string; icon: ReactNode };
const features: Feature[] = [
{
title: "Communication Channels",
description: "WhatsApp | SMS | Phone | Email | QR",
icon: (
<>
<path d="M10.5 22v-2M14.5 22v-2" />
<path d="M11 20h-6.77C2.48 20 1.25 18.39 1.25 17.4v-6.15c0-3.2 2.24-6 5.25-6s5.25 2.8 5.25 6z" />
<path d="M5 16h3" />
<path d="M22 11.25v6.18C22 19.15 20.78 20.75 19 20.75h-5.79" />
<path d="M18 6h-11c-3 0-5.25 2.8-5.25 6" />
<path d="M16 5.41V2.64a.5.5 0 0 1 .4-.49l.49-.1a3.5 3.5 0 0 1 1.76.19l.08.03c.55.21 1.16.25 1.73.11l.06-.02c.28-.07.53.14.53.42v2.22a.5.5 0 0 1-.4.49l-.07.02a3.5 3.5 0 0 1-2.05-.14A3.5 3.5 0 0 0 16 5.41Z" />
</>
),
},
{
title: "Customer 360°",
description: "See the complete customer relationship.",
icon: (
<>
<circle cx="10" cy="6" r="4" />
<path d="M18 17.5c0 2.49 0 4.5-8 4.5s-8-2.01-8-4.5S5.58 13 10 13s8 2.01 8 4.5Z" />
<path d="M19 8.64C20 9.5 20 9.5 21 8.64M19 8.64c0-.95.58-1.82 1.47-2.18.9-.38 1.98-.19 2.85.68m-4.32 1.5c0 .95.58 1.82 1.47 2.18.9.38 1.98.19 2.85-.68" />
</>
),
},
{
title: "Automated Messaging",
description: "Send confirmations, reminders, pickup instructions and operational updates.",
icon: (
<>
<path d="M13.09 21.39c.24-.4.36-.6.39-.72.14-.5.53-.9 1.03-1.03.12-.03.32-.03.72-.03m-3.83 1.78c-.24-.4-.36-.6-.39-.72a1.35 1.35 0 0 0-1.03-1.03c-.12-.03-.32-.03-.72-.03m3.83 1.78-.65.38a1.5 1.5 0 0 1-1.5 0l-.65-.38" />
<path d="M10.5 2h3M22 10.5v1M2 11.5v-1" />
<path d="M2.38 15.91C2.94 17.61 4.06 18.73 5.47 19.31M21.62 15.91c-.56 1.71-1.68 2.83-3.09 3.4" />
<path d="M7.79 18.99c-1.26-.02-1.92-.1-2.44-.32m-2.98-2.76c-.35-1.05-.35-2.49-.35-5.2 0-2.71 0-4.15.35-5.2m2.98-2.76c.8-.33 1.72-.4 2.98-.42M16.2 18.99c1.26-.02 1.92-.1 2.44-.32m2.98-2.76c.35-1.05.35-2.49.35-5.2 0-2.71 0-4.15-.35-5.2m-2.98-2.76c-.8-.33-1.72-.4-2.98-.42" />
<circle cx="19" cy="5" r="3" />
</>
),
},
{
title: "QR Communication",
description: "Give customers an instant digital connection to your business.",
icon: (
<>
<path d="M2 16.9c0-1.3 0-1.96.29-2.44.16-.27.39-.5.66-.66.48-.3 1.14-.3 2.45-.3h1.1c1.89 0 2.83 0 3.42.59.58.58.58 1.53.58 3.41v1.1c0 1.31 0 1.96-.29 2.45-.16.27-.39.5-.66.66-.48.3-1.14.3-2.45.3-1.96 0-2.94 0-3.66-.44a2.9 2.9 0 0 1-.96-.96C2 20.15 2 19.16 2 16.9Z" />
<path d="M13.5 5.4c0-1.3 0-1.96.29-2.44.16-.27.39-.5.66-.66C14.94 2 15.6 2 16.9 2c1.96 0 2.94 0 3.66.44.4.24.72.56.97.96.44.72.44 1.71.44 3.68 0 1.31 0 1.96-.29 2.45-.16.27-.39.5-.66.66-.48.3-1.14.3-2.45.3h-1.1c-1.89 0-2.83 0-3.42-.59-.58-.58-.58-1.53-.58-3.41z" />
<path d="M16.5 6.25c0-.51 0-.77.13-.96.05-.06.1-.12.16-.16.18-.13.44-.13.96-.13s.77 0 .96.13c.06.04.11.1.16.16.13.19.13.45.13.96s0 .77-.13.96c-.05.06-.1.12-.16.16-.18.13-.44.13-.96.13s-.77 0-.96-.13a.5.5 0 0 1-.16-.16c-.13-.19-.13-.45-.13-.96Z" />
<path d="M12.75 22a.75.75 0 0 0 1.5 0zM14.39 13.84a.75.75 0 0 0-.83-1.25zM19 13.5v-.75h-2v.75zM13.5 19h-.75v3h.75zM17 13.5v-.75c-.69 0-1.26 0-1.72.05-.47.05-.91.15-1.31.42l.42.62.42.62c.11-.07.28-.14.63-.17.37-.04.85-.04 1.56-.04zM13.5 17h.75c0-.72 0-1.19.04-1.56.04-.35.1-.52.17-.63l-.62-.42-.62-.42c-.27.4-.37.84-.42 1.31-.05.46-.05 1.03-.05 1.72zM14.39 13.84l-.42-.62a2.2 2.2 0 0 0-.6.62z" />
<path d="M22.75 13.5a.75.75 0 0 0-1.5 0zM20.77 21.85l-.29-.69zM17 22v.75h2V22zM22 17h.75v-3.5H22zM19 22v.75c.46 0 .84 0 1.15-.02.32-.02.62-.06.9-.19l-.63-1.37a3 3 0 0 1-.22.03c-.25.02-.57.02-1.02.02zM22 19h-.75c0 .48 0 .8-.02 1.02-.02.24-.04.3-.08.4l1.37.63c.12-.29.16-.59.18-.9.02-.31.02-.7.02-1.15zM20.77 21.85l.29.69a2.75 2.75 0 0 0 1.49-1.49l-.69-.29-.69-.29a1.25 1.25 0 0 1-.68.68z" />
<path d="M2 7.1c0-1.96 0-2.94.44-3.66.24-.4.56-.72.96-.97C4.16 2 5.14 2 7.1 2c1.31 0 1.96 0 2.45.29.27.16.5.39.66.66.29.49.29 1.14.29 2.45v1.1c0 1.89 0 2.83-.59 3.42-.58.58-1.53.58-3.41.58h-1.1c-1.31 0-1.96 0-2.45-.29a1.9 1.9 0 0 1-.66-.66C2 8.06 2 7.4 2 7.1Z" />
<path d="M5 6.25c0-.51 0-.77.13-.96.05-.06.1-.12.16-.16.18-.13.44-.13.96-.13s.77 0 .96.13c.06.04.11.1.16.16.13.19.13.45.13.96s0 .77-.13.96c-.05.06-.1.12-.16.16-.18.13-.44.13-.96.13s-.77 0-.96-.13a.5.5 0 0 1-.16-.16c-.13-.19-.13-.45-.13-.96Z" />
<path d="M5 17.75c0-.51 0-.77.13-.96.05-.06.1-.12.16-.16.18-.13.44-.13.96-.13s.77 0 .96.13c.06.04.11.1.16.16.13.19.13.45.13.96s0 .77-.13.96c-.05.06-.1.12-.16.16-.18.13-.44.13-.96.13s-.77 0-.96-.13a.5.5 0 0 1-.16-.16c-.13-.19-.13-.45-.13-.96Z" />
<path d="M16 17.75c0-.7 0-1.05.17-1.31.07-.11.16-.2.27-.27.27-.17.61-.17 1.31-.17s1.05 0 1.31.17c.11.07.2.16.27.27.17.27.17.61.17 1.31s0 1.05-.17 1.31c-.07.11-.16.2-.27.27-.27.17-.61.17-1.31.17s-1.05 0-1.31-.17a.75.75 0 0 1-.27-.27c-.17-.27-.17-.61-.17-1.31Z" />
</>
),
},
{
title: "Conversation History",
description: "Keep communication connected to the customer and booking.",
icon: (
<>
<path d="M2 12c0 5.52 4.48 10 10 10s10-4.48 10-10S17.52 2 12 2" />
<path d="M12 9v4h4" />
<circle cx="12" cy="12" r="10" strokeDasharray="0.5 3.5" />
</>
),
},
{
title: "Powerful CRM",
description: "Understand booking history, preferences, revenue and customer value.",
icon: (
<>
<path d="M8.78 22a11 11 0 0 1-4-3.49" />
<path d="M14.82 2.19a5.3 5.3 0 0 1 4.62 1.21" />
<path d="M4 8.37c.6-.32 1.32-.4 2.5.5.4.3.62.87.35 1.31L4.4 12.11c-.3.5-1.17.5-1.62-.08a2.3 2.3 0 0 1 1.22-3.66Z" />
<path d="M7.7 3.37c.2.26.37.55.53.86l2.53 4.33c.32.58.16 1.24-.37 1.53a2.3 2.3 0 0 1-3.03-.94L5.05 5.26c-.44-.76.25-2.26 1.44-2.95.4-.24.9-.16 1.21.06Z" />
<path d="M17.66 5.38c.32.32.72.66 1.2.88.6.28 1.2 0 1.53-.58a2.3 2.3 0 0 0-.94-3.03l-2.53-1.61c-.76-.44-2.26.25-2.95 1.44-.24.4-.16.9.06 1.21z" />
</>
),
},
];
function FeatureIcon({ children }: { children: ReactNode }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="#0160d4" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="size-6 shrink-0">
{children}
</svg>
);
}
export function CRM() {
return (
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<Reveal className="flex flex-col items-start gap-4 sm:flex-row sm:items-center sm:gap-8">
<span
aria-hidden
className="bg-linear-to-r from-[#0160d4] via-[#5b9bf0] to-[#cfe0fb] bg-clip-text text-[130px] font-black leading-none tracking-tight text-transparent sm:text-[240px]"
>
CRM
</span>
<div className="flex max-w-md flex-col gap-2">
<h2 className="text-4xl font-medium tracking-tight text-foreground sm:text-5xl">
&amp; Customer Communication
</h2>
<p className="text-sm text-muted sm:text-base">
<span className="font-bold">Every customer. Every conversation. One place.</span> Voyager connects your
customers, bookings and communications in one platform.
</p>
</div>
</Reveal>
<div className="mt-6 grid gap-4 sm:grid-cols-2 lg:mt-8">
{features.map((f, i) => (
<Reveal key={f.title} delay={i * 80}>
<div className="group flex items-center gap-4 rounded-3xl bg-[#0160d4]/3 px-6 py-8 transition-all duration-300 hover:-translate-y-1 hover:bg-[#0160d4]/6 hover:shadow-lg">
<span className="transition-transform duration-300 group-hover:scale-110">
<FeatureIcon>{f.icon}</FeatureIcon>
</span>
<div>
<h3 className="text-xl font-bold text-foreground sm:text-2xl">{f.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-foreground/70 sm:text-base">{f.description}</p>
</div>
</div>
</Reveal>
))}
</div>
<Reveal delay={features.length * 80} className="mt-6 flex max-w-md flex-wrap items-center justify-between gap-4 rounded-3xl border border-[#0160d4]/12 bg-white p-4">
<span className="flex items-center gap-2 text-2xl font-medium text-foreground">
<svg viewBox="0 0 24 24" fill="none" stroke="#0160d4" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="size-6">
<path d="M16.57 19.2 12.64 15.26l-1.2 1.2c-1.23 1.23-1.85 1.85-2.51 1.7-.66-.14-.96-.96-1.56-2.6L5.35 10.12c-1.2-3.26-1.8-4.89-.96-5.73.84-.84 2.47-.24 5.73.96l5.45 2c1.63.6 2.45.91 2.6 1.57.14.66-.48 1.28-1.71 2.5l-1.2 1.21 3.94 3.94c.41.4.61.61.71.84.13.3.13.64 0 .95-.1.22-.3.42-.71.83-.41.41-.61.61-.84.7-.3.13-.64.13-.94 0-.23-.09-.43-.29-.84-.7Z" />
</svg>
Wanna try?
</span>
<ButtonLink href="#demo">Explore CRM</ButtonLink>
</Reveal>
</div>
</section>
);
}

View File

@@ -0,0 +1,174 @@
"use client";
import { useEffect, useRef, useState, type PointerEvent } from "react";
import Image from "next/image";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
const stories = Array.from({ length: 5 }, (_, i) => i);
const quote = "Voyager helped us bring bookings, operations and customer communication into one platform.";
function StoryCard() {
return (
<figure className="flex w-[min(27.5rem,85vw)] shrink-0 snap-center select-none gap-4 rounded-3xl bg-white/10 p-6 text-white">
<Image
src="/customer-image.png"
alt="Julius Caesar"
draggable={false}
width={64}
height={64}
className="size-16 shrink-0 rounded-2xl object-cover"
/>
<div className="flex flex-col gap-2">
<figcaption>
<p className="text-2xl font-bold">Julius Caesar</p>
<p className="text-base font-bold">from William Co.</p>
</figcaption>
<hr className="border-t border-dashed border-white/20" />
<blockquote className="text-base text-white/70">{quote}</blockquote>
</div>
</figure>
);
}
function StorySlider() {
const ref = useRef<HTMLDivElement>(null);
const drag = useRef({ active: false, moved: false, startX: 0, startScroll: 0 });
const [dragging, setDragging] = useState(false);
// Tilt each card by its distance from the viewport center: centered card is flat, neighbours lean outward.
const updateTilt = () => {
const el = ref.current;
if (!el) return;
const mid = el.scrollLeft + el.clientWidth / 2;
for (const child of Array.from(el.children) as HTMLElement[]) {
const t = Math.max(-1, Math.min(1, (child.offsetLeft + child.offsetWidth / 2 - mid) / (child.offsetWidth + 32)));
child.style.transform = `translateY(${Math.abs(t) * 24}px) rotate(${t * 6}deg)`;
}
};
useEffect(() => {
updateTilt();
window.addEventListener("resize", updateTilt);
return () => window.removeEventListener("resize", updateTilt);
}, []);
const snapToNearest = () => {
const el = ref.current;
if (!el) return;
const mid = el.scrollLeft + el.clientWidth / 2;
let best: HTMLElement | null = null;
let bestDist = Infinity;
for (const child of Array.from(el.children) as HTMLElement[]) {
const dist = Math.abs(child.offsetLeft + child.offsetWidth / 2 - mid);
if (dist < bestDist) {
bestDist = dist;
best = child;
}
}
if (best) el.scrollTo({ left: best.offsetLeft + best.offsetWidth / 2 - el.clientWidth / 2, behavior: "smooth" });
};
const step = (dir: 1 | -1) => {
const el = ref.current;
const card = el?.firstElementChild as HTMLElement | undefined;
if (!el || !card) return;
el.scrollBy({ left: dir * (card.offsetWidth + 16), behavior: "smooth" });
};
const onPointerDown = (e: PointerEvent<HTMLDivElement>) => {
if (e.pointerType !== "mouse" || e.button !== 0 || !ref.current) return;
drag.current = { active: true, moved: false, startX: e.clientX, startScroll: ref.current.scrollLeft };
};
const onPointerMove = (e: PointerEvent<HTMLDivElement>) => {
const d = drag.current;
const el = ref.current;
if (!d.active || !el) return;
const dx = e.clientX - d.startX;
if (!d.moved && Math.abs(dx) > 4) {
d.moved = true;
setDragging(true);
el.setPointerCapture(e.pointerId);
}
if (d.moved) el.scrollLeft = d.startScroll - dx;
};
const endDrag = (e: PointerEvent<HTMLDivElement>) => {
const d = drag.current;
if (!d.active) return;
d.active = false;
if (d.moved) {
ref.current?.releasePointerCapture(e.pointerId);
setDragging(false);
snapToNearest();
}
};
return (
<div className="relative mt-2">
<div
ref={ref}
onScroll={updateTilt}
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
onPointerUp={endDrag}
onPointerCancel={endDrag}
style={{ scrollSnapType: dragging ? "none" : "x mandatory" }}
className={`flex touch-pan-x items-center gap-4 overflow-x-auto px-[calc(50%-min(13.75rem,42.5vw))] py-10 [scrollbar-width:none] lg:gap-8 [&::-webkit-scrollbar]:hidden ${dragging ? "cursor-grabbing" : "cursor-grab"}`}
>
{stories.map((i) => (
<StoryCard key={i} />
))}
</div>
<div className="mt-4 flex justify-center gap-3">
{([-1, 1] as const).map((dir) => (
<button
key={dir}
type="button"
aria-label={dir === -1 ? "Previous story" : "Next story"}
onClick={() => step(dir)}
className="grid size-10 place-items-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="size-5">
<path d={dir === -1 ? "M15 6l-6 6 6 6" : "M9 6l6 6-6 6"} />
</svg>
</button>
))}
</div>
</div>
);
}
export function CustomerStories() {
return (
<section className="relative py-20 lg:py-28">
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<Reveal className="relative overflow-hidden rounded-[32px] bg-[#0160d4] pb-14 pt-10 sm:pt-12">
<div aria-hidden className="pointer-events-none absolute -right-20 -top-40 size-125 rounded-full bg-white/25 blur-3xl" />
<div aria-hidden className="pointer-events-none absolute -left-24 -top-32 size-96 rounded-full border-30 border-white/10" />
<div className="relative flex flex-col items-center gap-2 px-4 text-center text-white">
<span className="inline-flex h-4.5 items-center rounded-xl bg-white px-3 text-xs font-medium text-[#11192d]">
Customers Stories
</span>
<h2 className="text-4xl font-medium tracking-tight sm:text-5xl">Built for Real Operators.</h2>
<p className="max-w-xl text-base">
See how tour operators, transportation companies and tourism businesses use Voyager to simplify booking,
operations and growth.
</p>
</div>
<StorySlider />
</Reveal>
<div className="relative z-10 -mt-6 flex justify-center">
<ButtonLink href="#demo" size="lg" className="h-12 rounded-xl px-5 font-bold">
Become a Voyager Customer
</ButtonLink>
</div>
</div>
</section>
);
}

121
src/components/footer.tsx Normal file
View File

@@ -0,0 +1,121 @@
import Link from "next/link";
import type { ReactNode } from "react";
const resources = [
{ title: "Industry Insights", description: "Trends and technology for modern tour operators." },
{ title: "Guides", description: "Practical guides for booking, operations and growth." },
{ title: "Customer Stories", description: "See how operators use Voyager." },
{ title: "Product Updates", description: "New features and integrations." },
{ title: "Help Center", description: "Get answers and learn how Voyager works." },
{ title: "API Documentation", description: "Technical resources for developers and partners." },
];
const topics = [
{
title: "Tour Operator Software",
links: ["Best Tour Operator Software", "How to Choose Tour Booking Software", "Tour Booking Software vs. Reservation Software", "What Is Tour Management Software?"],
},
{ title: "Operations", links: ["How to Manage Tour Operations", "Fleet Management for Tour Operators", "Transportation Scheduling Software"] },
{ title: "Sales & Distribution", links: ["How to Sell Tours Online", "OTA Management for Tour Operators", "GetYourGuide vs. Viator for Tour Operators"] },
{ title: "AI", links: ["How AI Is Changing Tour Operations", "AI for Tour Operators", "Using AI to Automate Tourism Businesses"] },
{ title: "Business Growth", links: ["Tour Operator CRM", "Tour Operator Marketing", "Affiliate Programs for Tour Operators", "Increasing Direct Tour Bookings"] },
];
const socials: { label: string; icon: ReactNode }[] = [
{
label: "WhatsApp",
icon: <path d="M12 2a10 10 0 0 0-8.6 15.1L2 22l5-1.3A10 10 0 1 0 12 2Zm5.2 13.9c-.2.6-1.2 1.1-1.7 1.2-.5 0-.9.2-3-.6-2.5-1-4.1-3.6-4.2-3.8-.1-.2-1-1.3-1-2.5s.6-1.8.9-2c.2-.3.5-.3.6-.3h.5c.2 0 .4 0 .6.5l.8 1.9c.1.2.1.3 0 .5l-.3.4-.3.3c-.1.2-.3.3-.1.6.2.3.7 1.2 1.5 1.9 1 .9 1.9 1.2 2.1 1.3.3.1.4.1.6-.1l.8-1c.2-.3.4-.2.6-.1l1.8.9c.3.1.5.2.5.3.1.1.1.6-.1 1.2Z" />,
},
{
label: "Instagram",
icon: <path d="M7 2h10a5 5 0 0 1 5 5v10a5 5 0 0 1-5 5H7a5 5 0 0 1-5-5V7a5 5 0 0 1 5-5Zm0 2a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V7a3 3 0 0 0-3-3H7Zm5 3.5a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Zm0 2a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5Zm5.2-3.2a1.1 1.1 0 1 1 0 2.2 1.1 1.1 0 0 1 0-2.2Z" />,
},
{
label: "Telegram",
icon: <path d="M2.4 11.2 20.6 4c.8-.3 1.6.2 1.3 1.5l-3.100 14.500c-.2 1-.8 1.300-1.700.800l-4.700-3.500-2.300 2.200c-.3.300-.5.500-1 .500l.3-4.800 8.700-7.900c.4-.3-.1-.5-.6-.2L7.700 13.800l-4.600-1.400c-1-.3-1-1 .3-1.200Z" />,
},
{
label: "Facebook",
icon: <path d="M22 12a10 10 0 1 0-11.600 9.900v-7H7.900V12h2.500V9.800c0-2.500 1.500-3.900 3.800-3.900 1.100 0 2.200.2 2.200.2v2.500h-1.300c-1.200 0-1.600.8-1.600 1.600V12h2.800l-.4 2.900h-2.300v7A10 10 0 0 0 22 12Z" />,
},
];
function ArrowIcon() {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="#f3610c" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="size-6 shrink-0 transition-transform duration-200 group-hover:-translate-y-0.5 group-hover:translate-x-0.5">
<path d="M6 18 18 6M18 15V6H9" />
</svg>
);
}
function TopicGroup({ title, links }: { title: string; links: string[] }) {
return (
<div className="flex flex-col gap-2">
<h4 className="text-xl font-medium text-white">{title}</h4>
<ul className="flex flex-col text-base text-white/70">
{links.map((l) => (
<li key={l}>
<Link href="#" className="transition-colors hover:text-white">
{l}
</Link>
</li>
))}
</ul>
</div>
);
}
export function Footer() {
return (
<footer className="relative overflow-hidden bg-[#11192d] text-white">
<div aria-hidden className="pointer-events-none absolute -right-40 -top-72 size-200 rounded-full bg-white/10 blur-3xl" />
<div aria-hidden className="pointer-events-none absolute -bottom-40 left-0 size-125 rounded-full border-30 border-white/5" />
<div className="relative mx-auto max-w-6xl px-4 pb-10 pt-20 sm:px-6 lg:pt-28">
<div className="grid gap-16 lg:grid-cols-[4fr_8fr]">
<div className="flex flex-col gap-8">
<h3 className="text-[32px] font-medium">Resources</h3>
{resources.map((r) => (
<Link key={r.title} href="#" className="group flex flex-col gap-2">
<span className="flex items-center gap-2 text-xl font-medium">
{r.title}
<ArrowIcon />
</span>
<span className="text-base text-white/70">{r.description}</span>
</Link>
))}
</div>
<div className="flex flex-col gap-8">
<h3 className="text-[32px] font-medium">SEO Content Topics</h3>
<div className="grid gap-8 sm:grid-cols-2 lg:grid-cols-[1fr_1fr]">
<div className="flex flex-col gap-8">
{topics.slice(0, 3).map((t) => (
<TopicGroup key={t.title} {...t} />
))}
</div>
<div className="flex flex-col gap-8 lg:pt-17">
{topics.slice(3).map((t) => (
<TopicGroup key={t.title} {...t} />
))}
</div>
</div>
</div>
</div>
<div className="mt-16 flex flex-col items-start justify-between gap-6 sm:flex-row sm:items-center">
<p className="text-base font-medium">© 2026 Hop on Hop Off. All rights reserved.</p>
<div className="flex items-center gap-6">
{socials.map((s) => (
<Link key={s.label} href="#" aria-label={s.label} className="transition-opacity hover:opacity-70">
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden className="size-5">
{s.icon}
</svg>
</Link>
))}
</div>
</div>
</div>
</footer>
);
}

61
src/components/hero.tsx Normal file
View File

@@ -0,0 +1,61 @@
import Image from "next/image";
import { ButtonLink } from "@/components/button";
export function Hero() {
return (
<section className="relative">
{/* Soft wave lines behind the showcase */}
<svg
aria-hidden
viewBox="0 0 1440 400"
preserveAspectRatio="none"
className="pointer-events-none absolute inset-x-0 bottom-[12%] h-[45%] w-full text-[#cfe0fb]"
>
<path d="M0 250 C 180 110, 320 110, 480 230 S 780 370, 980 240 S 1280 100, 1440 190" fill="none" stroke="currentColor" strokeWidth="28" strokeLinecap="round" />
<path d="M0 300 C 200 160, 340 170, 500 280 S 800 400, 1000 290 S 1300 150, 1440 240" fill="none" stroke="currentColor" strokeOpacity="0.6" strokeWidth="14" strokeLinecap="round" />
</svg>
<div className="relative mx-auto flex max-w-6xl flex-col gap-8 px-4 pt-10 sm:px-6 md:flex-row md:items-end md:justify-between lg:pt-16">
<div className="animate-fade-up">
<h1 className="text-4xl font-medium tracking-tight text-foreground sm:text-5xl lg:text-6xl">
Book<span className="text-accent">,</span> Manage<span className="text-accent">,</span> Grow
</h1>
<p className="mt-4 text-sm text-muted sm:text-base">
AI-Powered Tour Operator &amp; Transportation Management Software
</p>
</div>
<div className="flex animate-fade-up flex-col gap-3 [animation-delay:150ms] sm:flex-row md:flex-col">
<ButtonLink href="#demo" size="lg" className="md:w-52">Book a Demo</ButtonLink>
<ButtonLink href="#action" variant="outline" size="lg" className="md:w-52">VOYAGER in action</ButtonLink>
</div>
</div>
<div className="relative mx-auto mt-12 max-w-6xl animate-fade-up px-4 [animation-delay:300ms] sm:px-6 lg:mt-20">
<div className="relative pb-[6%] pr-[12%] [mask-image:linear-gradient(180deg,#000_65%,transparent_100%)]">
<div className="overflow-hidden rounded-xl border-4 border-frame bg-background shadow-2xl transition-shadow duration-500 sm:rounded-2xl sm:border-[6px] hover:shadow-[0_30px_80px_-20px_rgba(1,96,212,0.35)]">
<Image
src="/hero-image-light-cropped.png"
alt="VOYAGER dashboard preview"
width={1076}
height={772}
priority
sizes="(min-width: 1152px) 960px, 88vw"
className="h-auto w-full"
/>
</div>
<div className="absolute right-0 top-[12%] w-[26%] drop-shadow-2xl transition-transform duration-500 hover:-translate-y-2">
{/* Clip trims the export's square background around the rounded phone frame */}
<Image
src="/iphone17-light.png"
alt="VOYAGER mobile app preview"
width={576}
height={1104}
sizes="(min-width: 1152px) 300px, 26vw"
className="h-auto w-full [clip-path:inset(0.4%_0.4%_0_0.4%_round_11%_11%_0_0/5.8%_5.8%_0_0)]"
/>
</div>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,50 @@
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
const columns = [
{ title: "Distribution", items: ["GetYourGuide", "Viator", "TourCMS", "Other OTA Channels"] },
{ title: "Payments", items: ["Payment Gateways", "Accounting Systems", "Financial Services"] },
{ title: "Communication", items: ["Whatsapp", "SMS", "Email", "Phone"] },
{ title: "Technology", items: ["API", "Webhooks", "Custom Integrations"] },
];
export function Integrations() {
return (
<section className="relative py-20 lg:py-28">
<div className="mx-auto flex max-w-6xl flex-col gap-8 px-4 sm:px-6">
<div className="flex flex-col items-start justify-between gap-6 sm:flex-row sm:items-center sm:gap-8">
<div className="flex max-w-3xl flex-col gap-4">
<h2 className="text-4xl font-medium tracking-tight text-[#11192d] sm:text-5xl">Integrations</h2>
<p className="text-base text-[#11192d]/70">
<span className="font-bold">Tour Booking Software Integrations</span> Connect Voyager with OTAs, payment
gateways, accounting systems, communication platforms, APIs and custom business applications.
</p>
</div>
<ButtonLink href="#demo" size="lg" className="h-12 shrink-0 rounded-xl px-5 font-bold">
View all Integration
</ButtonLink>
</div>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
{columns.map((col, i) => (
<Reveal key={col.title} delay={i * 90}>
<div className="flex h-full flex-col gap-2">
<div className="flex h-11.5 items-center justify-center rounded-2xl bg-[#0160d4]/10 px-4 py-3">
<h3 className="text-xl font-bold text-[#0160d4]">{col.title}</h3>
</div>
{col.items.map((item) => (
<div
key={item}
className="flex min-h-14 grow items-center justify-center rounded-2xl border border-[#0160d4]/12 bg-white px-4 py-3 text-center text-base text-[#11192d] transition-all duration-300 hover:-translate-y-0.5 hover:shadow-md"
>
{item}
</div>
))}
</div>
</Reveal>
))}
</div>
</div>
</section>
);
}

11
src/components/logo.tsx Normal file
View File

@@ -0,0 +1,11 @@
import Image from "next/image";
import Link from "next/link";
export function Logo() {
return (
<Link href="/" aria-label="VOYAGER home" className="flex shrink-0 items-center gap-1">
<Image src="/logo-light.png" alt="" width={46} height={30} priority />
<Image src="/text-logo-light.png" alt="VOYAGER" width={116} height={29} priority />
</Link>
);
}

View File

@@ -0,0 +1,144 @@
import Image from "next/image";
import type { ReactNode } from "react";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Item = { title: string; description: string; icon: ReactNode };
const items: Item[] = [
{
title: "Turn Bookings Into Relationships",
description: "Voyager helps you grow beyond today’s transaction.",
icon: (
<>
<path d="M4 10c0-3.77 0-5.66 1.17-6.83S8.23 2 12 2s5.66 0 6.83 1.17S20 6.23 20 10v2c0 3.77 0 5.66-1.17 6.83S15.77 20 12 20s-5.66 0-6.83-1.17S4 15.77 4 12z" />
<path d="M4 13h16M15.5 16H17M7 16h1.5M19.5 5h-15" />
<path d="M6 19.5V21a1 1 0 0 0 1 1h1.5a1 1 0 0 0 1-1v-1M18 19.5V21a1 1 0 0 1-1 1h-1.5a1 1 0 0 1-1-1v-1" />
</>
),
},
{
title: "Affiliate Programs",
description: "Build hotel, agency and partner networks.",
icon: (
<>
<path d="M22 10.5V12c0 4.71 0 7.07-1.46 8.54S16.71 22 12 22s-7.07 0-8.54-1.46S2 16.71 2 12s0-7.07 1.46-8.54S7.29 2 12 2h1.5" />
<circle cx="19" cy="5" r="3" />
<path d="M7 14h9M7 17.5h6" />
</>
),
},
{
title: "Incentives",
description: "Create commissions and performance incentives.",
icon: (
<>
<path d="M4.73 16.14c-1.55-1.55-2.32-2.32-2.6-3.32-.29-1-.05-2.07.44-4.2l.28-1.23c.41-1.79.62-2.69 1.23-3.3s1.5-.82 3.3-1.24l1.23-.28c2.13-.49 3.2-.74 4.2-.45s1.77 1.07 3.32 2.61l1.83 1.83C20.66 9.25 22 10.6 22 12.26s-1.34 3.01-4.03 5.7S13.93 22 12.26 22s-3.01-1.34-3.7-4.03z" />
<circle cx="8.6" cy="8.9" r="2" />
</>
),
},
{
title: "Promotions",
description: "Create targeted offers and promotional pricing.",
icon: (
<>
<path d="M19.83 3.17C18.66 2 16.77 2 13 2h-2C7.23 2 5.34 2 4.17 3.17S3 6.23 3 10v4c0 3.77 0 5.66 1.17 6.83S7.23 22 11 22h2c3.77 0 5.66 0 6.83-1.17.94-.94 1.13-2.35 1.16-4.83" />
<path d="M8 13h2.5M8 9h6.5M8 17h1.5" />
<path d="m18.18 8.04.46-.46a1.83 1.83 0 0 1 2.78 2.78l-.46.46m-2.78-2.78s.06.98.93 1.85 1.85.93 1.85.93m0 0-4.26 4.26c-.29.29-.43.43-.59.56-.19.15-.39.27-.61.37-.18.09-.38.15-.76.28l-1.24.41-.4.13-.4.14a.4.4 0 0 1-.55-.55l.13-.4.13-.4.41-1.24c.13-.39.2-.58.28-.76.1-.22.23-.42.37-.61.12-.16.27-.3.56-.59z" />
</>
),
},
{
title: "Upselling",
description: "Promote transfers, tours, rentals and additional services.",
icon: (
<>
<path d="m20.31 12.65.52-1.93c.6-2.26.9-3.38.68-4.36a4.5 4.5 0 0 0-1.16-2.01c-.73-.69-1.86-.99-4.11-1.6s-3.38-.9-4.36-.68a4.5 4.5 0 0 0-2.01 1.16c-.59.63-.9 1.54-1.35 3.21l-.24.9-.52 1.93c-.6 2.26-.9 3.38-.68 4.36.18.77.58 1.47 1.16 2.01.73.69 1.86.99 4.11 1.6 2.03.54 3.15.84 4.06.73.1-.01.2-.03.29-.05.77-.18 1.47-.58 2.01-1.16.69-.73.99-1.86 1.59-4.11z" />
<path d="M16.42 17.97a4.5 4.5 0 0 1-1.07 1.68c-.73.69-1.86.99-4.11 1.6-2.26.6-3.38.9-4.36.68a4.5 4.5 0 0 1-2.01-1.16c-.69-.73-.99-1.86-1.6-4.11l-.52-1.93c-.6-2.26-.9-3.38-.68-4.36a4.5 4.5 0 0 1 1.16-2.01c.73-.69 1.86-.99 4.11-1.6l1.17-.31M11.78 10l4.83 1.29M11 12.9l2.9.77" />
</>
),
},
{
title: "Customer Marketing",
description: "Reconnect with previous customers.",
icon: <AnalyticsPaths />,
},
];
function AnalyticsPaths() {
return (
<>
<path d="M16 4c2.18.01 3.35.11 4.12.88C21 5.76 21 7.17 21 10v6c0 2.83 0 4.24-.88 5.12S17.83 22 15 22H9c-2.83 0-4.24 0-5.12-.88S3 18.83 3 16v-6c0-2.83 0-4.24.88-5.12C4.65 4.11 5.82 4.01 8 4" />
<path d="M8 3.5A1.5 1.5 0 0 1 9.5 2h5A1.5 1.5 0 0 1 16 3.5v1A1.5 1.5 0 0 1 14.5 6h-5A1.5 1.5 0 0 1 8 4.5z" />
<path d="m14.5 11-5 5m0-5 5 5" />
</>
);
}
function Icon({ children }: { children: ReactNode }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="#0160d4" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="size-6 shrink-0">
{children}
</svg>
);
}
function Card({ item }: { item: Item }) {
return (
<div className="flex h-full items-center gap-4 rounded-3xl border border-[#0160d4]/12 bg-white px-6 py-8 transition-all duration-300 hover:-translate-y-1 hover:shadow-lg">
<Icon>{item.icon}</Icon>
<div className="flex flex-col gap-2">
<h3 className="text-xl font-bold text-[#11192d] sm:text-2xl">{item.title}</h3>
<p className="text-base text-[#11192d]/70">{item.description}</p>
</div>
</div>
);
}
export function MarketingGrowth() {
return (
<section className="relative py-20 lg:py-28">
<div className="mx-auto flex max-w-6xl flex-col gap-6 px-4 sm:px-6">
<Reveal className="relative overflow-hidden rounded-[32px] bg-[#0160d4] px-6 py-12 sm:px-10 lg:py-14">
<div aria-hidden className="pointer-events-none absolute -right-20 -top-32 size-96 rounded-full bg-white/15 blur-3xl" />
<div aria-hidden className="pointer-events-none absolute -right-10 -top-16 size-80 rounded-full border-30 border-white/10" />
<Image
src="/ladder.png"
alt=""
aria-hidden
width={200}
height={250}
sizes="200px"
className="pointer-events-none absolute -top-7 right-[2%] h-[115%] w-auto max-w-none object-contain object-top"
/>
<div className="relative flex flex-col gap-4 text-white">
<h2 className="text-4xl font-medium tracking-tight sm:text-5xl">Marketing and Growth</h2>
<p className="text-base">Tour Operator Marketing &amp; Growth Tools</p>
</div>
</Reveal>
<div className="grid gap-4 sm:grid-cols-2">
{items.map((item, i) => (
<Reveal key={item.title} delay={i * 70}>
<Card item={item} />
</Reveal>
))}
</div>
<Reveal className="flex flex-col items-start gap-4 rounded-3xl border border-[#0160d4]/12 bg-white px-6 py-8 sm:flex-row sm:items-center">
<Icon>
<AnalyticsPaths />
</Icon>
<div className="flex grow flex-col gap-2">
<h3 className="text-xl font-bold text-[#11192d] sm:text-2xl">Analytics</h3>
<p className="text-base text-[#11192d]/70">Understand what’s actually driving revenue.</p>
</div>
<ButtonLink href="#demo" size="lg" className="h-12 rounded-xl px-5 font-bold">
Discover Growth Tools
</ButtonLink>
</Reveal>
</div>
</section>
);
}

78
src/components/navbar.tsx Normal file
View File

@@ -0,0 +1,78 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import { ButtonLink } from "@/components/button";
import { Logo } from "@/components/logo";
const links = [
{ href: "/", label: "Home" },
{ href: "#features", label: "Features" },
{ href: "#solutions", label: "Solutions" },
{ href: "#others", label: "Others" },
];
export function Navbar() {
const [open, setOpen] = useState(false);
return (
<header className="relative z-20">
<div className="mx-auto flex h-20 max-w-6xl items-center justify-between gap-6 px-4 sm:px-6 lg:h-24">
<Logo />
<nav aria-label="Primary" className="hidden items-center gap-12 lg:flex">
{links.map((link) => (
<Link
key={link.label}
href={link.href}
className="group relative text-sm text-foreground transition-colors hover:text-accent"
>
{link.label}
<span className="absolute -bottom-1 left-0 h-px w-0 bg-accent transition-all duration-300 group-hover:w-full" />
</Link>
))}
</nav>
<div className="flex items-center gap-3">
<div className="hidden items-center gap-3 lg:flex">
<ButtonLink href="#plans" variant="outline">Our Plans</ButtonLink>
<ButtonLink href="#demo">Book a Demo</ButtonLink>
</div>
<button
type="button"
aria-label={open ? "Close menu" : "Open menu"}
aria-expanded={open}
aria-controls="mobile-menu"
onClick={() => setOpen((v) => !v)}
className="grid size-10 place-items-center rounded-lg border-2 border-outline bg-outline-bg text-foreground transition-colors hover:bg-outline-hover lg:hidden"
>
<svg className="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden>
{open ? <path d="M6 6l12 12M18 6L6 18" /> : <path d="M4 7h16M4 12h16M4 17h16" />}
</svg>
</button>
</div>
</div>
{open && (
<div id="mobile-menu" className="absolute inset-x-0 top-full border-y border-outline bg-background px-4 pb-6 pt-2 shadow-lg sm:px-6 lg:hidden">
<nav aria-label="Mobile" className="flex flex-col">
{links.map((link) => (
<Link
key={link.label}
href={link.href}
onClick={() => setOpen(false)}
className="border-b border-outline py-3 text-base text-foreground"
>
{link.label}
</Link>
))}
</nav>
<div className="mt-5 grid grid-cols-2 gap-3">
<ButtonLink href="#plans" variant="outline" onClick={() => setOpen(false)}>Our Plans</ButtonLink>
<ButtonLink href="#demo" onClick={() => setOpen(false)}>Book a Demo</ButtonLink>
</div>
</div>
)}
</header>
);
}

View File

@@ -0,0 +1,68 @@
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
const channels = ["GetYourGuide", "Viator", "TourCMS", "Your Website", "Affiliates", "Travel Agents"];
const pricingTargets = ["OTAs", "Hotels", "Affiliates", "Travel Agents", "Corporate Clients", "Direct Customers"];
function ColumnHeader({ children }: { children: string }) {
return (
<div className="flex h-11.5 items-center justify-center rounded-2xl bg-[#0160d4]/10 px-4 py-3">
<p className="text-lg font-bold text-[#0160d4] sm:text-xl">{children}</p>
</div>
);
}
function Pill({ children }: { children: string }) {
return (
<div className="flex h-12 items-center justify-center rounded-2xl border border-[#0160d4]/12 bg-white px-4 py-3 transition-all duration-300 hover:-translate-y-0.5 hover:border-transparent hover:shadow-md">
<p className="text-center text-base text-foreground">{children}</p>
</div>
);
}
export function OTADistribution() {
return (
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<Reveal className="flex max-w-2xl flex-col gap-4">
<h2 className="text-4xl font-medium tracking-tight text-foreground sm:text-5xl">OTA &amp; Distribution</h2>
<p className="text-sm text-muted sm:text-base">
<span className="font-bold">OTA Management &amp; Distribution for Tour Operators.</span> Sell tours,
activities and experiences through OTAs, travel agents, hotels, affiliates and your own website while
managing inventory from one central platform.
</p>
</Reveal>
<div className="mt-12 grid gap-6 lg:mt-16 lg:grid-cols-3">
<Reveal delay={0} className="flex flex-col gap-2">
<ColumnHeader>Distribution Channels</ColumnHeader>
{channels.map((c) => (
<Pill key={c}>{c}</Pill>
))}
</Reveal>
<Reveal delay={100} className="flex flex-col gap-2">
<ColumnHeader>Channel Pricing</ColumnHeader>
<p className="text-sm text-foreground sm:text-base">Create specific rates and commissions for:</p>
{pricingTargets.map((p) => (
<Pill key={p}>{p}</Pill>
))}
</Reveal>
<Reveal delay={200} className="flex flex-col gap-2">
<ColumnHeader>Centralized Inventory</ColumnHeader>
<div className="flex flex-1 flex-col justify-between gap-6 rounded-2xl border border-[#0160d4]/12 bg-white px-8 py-5">
<p className="text-base text-foreground/80">
Manage availability and product information from Voyager instead of updating every channel
separately.
</p>
<ButtonLink href="#integrations" className="self-center">
Explore Integrations
</ButtonLink>
</div>
</Reveal>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,45 @@
import { Reveal } from "@/components/reveal";
const partners = Array.from({ length: 6 }, (_, i) => ({
id: i,
name: "Company Name",
tagline: "Partnership placeholder",
}));
function PartnerIcon() {
return (
<svg viewBox="0 0 24 24" className="size-7 shrink-0 fill-current" aria-hidden>
<rect x="3" y="3" width="7" height="7" rx="1.5" transform="rotate(12 6.5 6.5)" />
<rect x="14" y="3" width="7" height="7" rx="1.5" transform="rotate(-12 17.5 6.5)" />
<rect x="3" y="14" width="7" height="7" rx="1.5" />
<rect x="14" y="14" width="7" height="7" rx="1.5" transform="rotate(20 17.5 17.5)" />
</svg>
);
}
export function Partners() {
return (
<section aria-label="Partners" className="relative py-10 lg:py-14">
<Reveal className="overflow-hidden [mask-image:linear-gradient(90deg,transparent,#000_8%,#000_92%,transparent)]">
{/* List rendered twice so translating -50% loops seamlessly */}
<div className="flex w-max animate-marquee hover:[animation-play-state:paused]">
{[0, 1].map((copy) => (
<ul key={copy} aria-hidden={copy === 1} className="flex shrink-0">
{partners.map((p) => (
<li className="group flex items-center gap-3 px-8 transition-opacity duration-300 sm:px-12" key={p.id}>
<span className="text-foreground/70 transition-colors duration-300 group-hover:text-accent">
<PartnerIcon />
</span>
<div className="leading-tight">
<p className="text-sm font-semibold text-foreground">{p.name}</p>
<p className="text-sm text-muted">{p.tagline}</p>
</div>
</li>
))}
</ul>
))}
</div>
</Reveal>
</section>
);
}

View File

@@ -0,0 +1,155 @@
import Image from "next/image";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
const features = [
{
title: "Integrated Payments",
description:
"Connect booking transactions with your operational and customer data.",
wide: true,
},
{
title: "Fixed & Dynamic Pricing",
description:
"Create pricing strategies for products, customers and sales channels.",
wide: true,
},
{
title: "Invoicing",
description: "Manage invoices and payment information.",
},
{
title: "Deposits",
description: "Track deposits and outstanding balances.",
},
{ title: "Refunds", description: "Manage refunds and booking adjustments." },
{
title: "Commissions",
description: "Manage partner, affiliate and channel commissions.",
wide: true,
},
{
title: "Revenue Reporting",
description:
"Understand your business performance through connected reporting.",
wide: true,
},
];
const metrics = [
"Gross Revenue",
"Net Revenue",
"Bookings",
"Average Booking Value",
"Revenue by Product",
"Revenue by Channel",
"Revenue by Partner",
];
export function PaymentsRevenue() {
return (
<section className="relative py-20 lg:py-28">
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<Reveal className="relative overflow-hidden rounded-[32px] bg-[#11192d] px-4 pb-16 pt-12 sm:px-6 lg:pt-16">
<div
aria-hidden
className="pointer-events-none absolute -left-40 -top-64 size-150 rounded-full bg-white/10 blur-3xl"
/>
<div
aria-hidden
className="pointer-events-none absolute -right-32 top-40 size-100 rounded-full border-30 border-white/5"
/>
<div className="relative flex flex-col items-center gap-3 text-center text-white">
<span className="inline-flex h-4.5 items-center rounded-xl bg-white px-3 text-xs font-medium text-[#11192d]">
Payments &amp; Revenue
</span>
<h2 className="max-w-2xl text-4xl font-medium tracking-tight sm:text-5xl">
Payments &amp; Revenue Management for Tour Operators
</h2>
<p className="rounded-2xl bg-[#0160d4]/10 px-4 py-3 text-xl sm:text-2xl">
<span className="font-bold text-[#3b8cf0]">
Every Booking. Every Dollar.{" "}
</span>
<span className="italic">Connected</span>
<span className="font-bold text-[#3b8cf0]">.</span>
</p>
<p className="text-sm text-white/70 sm:text-base">
Manage your commercial operation from one system.
</p>
</div>
<div className="relative -top-8 -mb-16 flex justify-center lg:-mb-24">
<Image
src="/hand-holding-card.png"
alt="A hand holding a bank card"
width={340}
height={454}
sizes="(min-width: 640px) 340px, 260px"
className="h-auto w-65 sm:w-85"
/>
</div>
<div className="relative grid gap-4 sm:grid-cols-2 lg:grid-cols-6">
{features.map((f, i) => (
<Reveal
key={f.title}
delay={i * 70}
className={f.wide ? "lg:col-span-3" : "lg:col-span-2"}
>
<div className="flex h-full min-h-35 flex-col justify-center gap-2 rounded-3xl border border-white/8 px-6 py-8 transition-colors duration-300 hover:bg-white/5">
<h3 className="text-2xl font-bold text-white">{f.title}</h3>
<p className="text-base text-white/70">{f.description}</p>
</div>
</Reveal>
))}
<Reveal
delay={features.length * 70}
className="sm:col-span-2 lg:col-span-6"
>
<div className="flex flex-col gap-4 rounded-3xl bg-white/3 p-6">
<h3 className="text-2xl font-bold text-white">
Revenue Dashboard
</h3>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
{metrics.slice(0, 3).map((m) => (
<span
key={m}
className="flex h-10 grow items-center justify-center rounded-2xl bg-white/8 px-4 text-base text-white"
>
{m}
</span>
))}
</div>
<div className="flex gap-2">
{metrics.slice(3, 7).map((m) => (
<span
key={m}
className="flex h-10 grow items-center justify-center rounded-2xl bg-white/8 px-4 text-base text-white"
>
{m}
</span>
))}
</div>
</div>
</div>
</Reveal>
</div>
</Reveal>
<div className="relative z-10 -mt-6 flex justify-center">
<ButtonLink
href="#demo"
size="lg"
className="h-12 rounded-xl px-5 font-bold"
>
Explore Payments
</ButtonLink>
</div>
</div>
</section>
);
}

154
src/components/platform.tsx Normal file
View File

@@ -0,0 +1,154 @@
import Image from "next/image";
import type { ReactNode } from "react";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Feature = { title: string; description: string; icon: ReactNode };
// Lucide-style 24px stroke icons
const features: Feature[] = [
{
title: "Book",
description: "Tour bookings, tickets, reservations and real-time availability.",
icon: <><rect x="3" y="4" width="18" height="18" rx="2" /><path d="M16 2v4M8 2v4M3 10h18M9 16l2 2 4-4" /></>,
},
{
title: "AI",
description: "Intelligent assistance, automation and business insights.",
icon: <><rect x="4" y="4" width="16" height="16" rx="2" /><rect x="9" y="9" width="6" height="6" rx="1" /><path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3" /></>,
},
{
title: "Manage",
description: "Scheduling, daily operations and staff management.",
icon: <><rect x="8" y="2" width="8" height="4" rx="1" /><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2M9 12h6M9 16h6" /></>,
},
{
title: "Fleet",
description: "Vehicle management, drivers and fleet utilization.",
icon: <><path d="M8 6v6M16 6v6M2 12h19.6M18 18h3s.5-1.7.8-2.8c.1-.4.2-.8.2-1.2 0-.4-.1-.8-.2-1.2l-1.4-5C20.1 6.8 19.1 6 18 6H4a2 2 0 0 0-2 2v10h3" /><circle cx="7" cy="18" r="2" /><path d="M9 18h5" /><circle cx="16" cy="18" r="2" /></>,
},
{
title: "CRM",
description: "Customer management and relationships.",
icon: <><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><circle cx="9" cy="7" r="4" /><path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" /></>,
},
{
title: "Communicate",
description: "WhatsApp, SMS, phone, email and QR communication.",
icon: <path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z" />,
},
{
title: "Pay",
description: "Payments, deposits, balances and revenue.",
icon: <><rect x="2" y="5" width="20" height="14" rx="2" /><path d="M2 10h20M6 15h4" /></>,
},
{
title: "Grow",
description: "Marketing, affiliates, incentives and analytics.",
icon: <><path d="M22 7l-8.5 8.5-5-5L2 17" /><path d="M16 7h6v6" /></>,
},
];
function Icon({ children, className = "" }: { children: ReactNode; className?: string }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden className={className}>
{children}
</svg>
);
}
export function Platform() {
return (
<section id="features" className="relative overflow-hidden py-20 lg:py-28">
<div className="relative mx-auto max-w-6xl px-4 sm:px-6">
<Reveal>
<h2 className="max-w-4xl text-2xl font-normal leading-snug tracking-tight text-foreground sm:text-3xl lg:text-4xl">
Voyager is an <em className="font-normal text-accent">AI-powered</em> business operating platform for tours,
transportation, rentals and tourism businesses.
</h2>
<p className="mt-6 max-w-4xl text-sm leading-relaxed text-muted sm:text-base">
Bring booking, ticketing, AI, CRM, communication, scheduling, fleet management, staff, payments, rentals,
OTAs and marketing together in one powerful platform. One platform. One source of truth. Less software. Less
cost. More control.
</p>
</Reveal>
<div className="mt-20 grid items-start gap-12 lg:mt-28 lg:grid-cols-[5fr_7fr] lg:gap-16">
<Reveal className="relative mx-auto w-64 sm:w-75 lg:mx-0">
{/* Box on a blue tile with a fading reflection; positions measured from the Figma frame */}
<div className="relative aspect-247/319 transition-transform duration-500 hover:-translate-y-1">
<div
aria-hidden
className="absolute inset-x-0 bottom-0 top-[74.2%] bg-linear-to-b from-[#2f6fe8]/35 to-transparent"
/>
<div
aria-hidden
className="absolute inset-x-0 top-[32.5%] bottom-[25.8%] overflow-hidden rounded-2xl bg-[linear-gradient(115deg,#1f5bd8_0%,#1a4ccc_50%,#3a7ff5_100%)]"
>
{/* Shadow cast by the box's right face onto the tile */}
<div className="absolute inset-0 blur-md">
<div className="size-full bg-[#0b1f7a]/70 [clip-path:polygon(50.6%_82.7%,85%_48%,100%_62%,100%_100%,62%_100%)]" />
</div>
</div>
<Image
src="/box-light.png"
alt=""
width={1004}
height={1004}
sizes="280px"
className="absolute left-0 top-0 h-auto w-[93%]"
/>
</div>
</Reveal>
<Reveal delay={120} className="relative border-l border-divider pl-6 sm:pl-10">
<span aria-hidden className="absolute -left-px top-1 h-14 w-0.5 bg-accent" />
<h3 className="text-3xl font-normal leading-tight tracking-tight text-foreground sm:text-4xl">
Everything you need in <br className="hidden sm:block" />
One Place
</h3>
<p className="mt-4 max-w-xl text-sm leading-relaxed text-muted">
Everything You Need to Run Your Tourism Business Instead of managing multiple disconnected systems, bring
your tour bookings, ticketing, operations, fleet, customers, payments and marketing together with Voyager.
</p>
<ul className="mt-10 grid gap-3 sm:grid-cols-2 sm:gap-x-4">
{features.map((f) => (
<li
key={f.title}
className="group rounded-xl bg-card p-4 transition-all duration-300 hover:-translate-y-1 hover:bg-white hover:shadow-lg"
>
<Icon className="size-5 text-icon transition-transform duration-300 group-hover:scale-110">{f.icon}</Icon>
<h4 className="mt-3 text-base font-medium text-foreground">{f.title}</h4>
<p className="mt-1 text-sm leading-relaxed text-muted">{f.description}</p>
</li>
))}
</ul>
<div className="mt-4 flex flex-wrap items-center justify-between gap-4 rounded-xl border border-divider bg-background p-3 pl-4 shadow-sm transition-shadow duration-300 hover:shadow-md">
<span className="flex items-center gap-3 text-base font-medium text-foreground">
<Icon className="size-5 text-icon">
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.3-4.3" />
</Icon>
Wanna try?
</span>
<ButtonLink href="#demo">Book a Demo</ButtonLink>
</div>
</Reveal>
</div>
<Reveal className="mt-16 overflow-hidden rounded-xl border-4 border-frame bg-background shadow-2xl transition-shadow duration-500 hover:shadow-[0_30px_80px_-20px_rgba(1,96,212,0.35)] sm:mt-20 sm:rounded-2xl sm:border-[6px] lg:mt-24">
<Image
src="/platform-section.png"
alt="VOYAGER platform preview"
width={1200}
height={900}
sizes="(min-width: 1152px) 1152px, 100vw"
className="h-auto w-full"
/>
</Reveal>
</div>
</section>
);
}

148
src/components/pricing.tsx Normal file
View File

@@ -0,0 +1,148 @@
import type { ReactNode } from "react";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Plan = {
name: string;
tagline: ReactNode;
blurb: string;
heading: string;
items: string[];
cta: string;
popular?: boolean;
};
const plus = <span className="text-[#f3610c]">+</span>;
const plans: Plan[] = [
{
name: "Core",
tagline: "Book",
blurb: "For operators getting started.",
heading: "Includes :",
items: ["Tour management", "Online bookings", "Availability", "Fixed pricing", "Customer management", "Payments", "Basic reporting"],
cta: "Get Started",
},
{
name: "Grow",
tagline: <>Book {plus} Manage</>,
blurb: "For growing operators.",
heading: "Everything in Core plus :",
items: ["CRM", "Scheduling", "Fleet", "Staff", "Dynamic pricing", "Communication", "OTA connectivity", "Advanced reporting"],
cta: "Book a Demo",
popular: true,
},
{
name: "Scale",
tagline: <>Book {plus} Manage {plus} Grow</>,
blurb: "For established operators.",
heading: "Everything in Grow, plus :",
items: ["AI", "Advanced CRM", "Rentals", "Contracts", "Marketing", "Affiliate management", "Incentives", "Advanced analytics", "API integrations", "Multi-location capabilities"],
cta: "Talk to Sales",
},
];
const enterpriseItems = [
"Enterprise integrations",
"Custom workflows",
"Multiple locations",
"Advanced permissions",
"Dedicated support",
"Custom API requirements",
"Data migration",
"Implementation services",
];
export function Pricing() {
return (
<section id="plans" className="relative scroll-mt-4 py-20 lg:py-28">
<div className="mx-auto flex max-w-6xl flex-col items-center gap-8 px-4 sm:px-6">
<div className="flex flex-col items-center gap-6 text-center">
<h2 className="text-4xl font-medium tracking-tight text-[#11192d] sm:text-5xl">Tour Operator Software Pricing</h2>
<p className="rounded-2xl bg-[#0160d4]/10 px-4 py-3 text-xl text-[#0160d4] sm:text-2xl">
<span className="font-bold">Simple Pricing. Built Around Your </span>
<span className="italic">Business</span>
<span className="font-bold">.</span>
</p>
<p className="text-base text-[#11192d]/70">
Start with the capabilities you need. Add more as your business grows.
</p>
</div>
<div className="grid w-full gap-4 sm:grid-cols-2 lg:grid-cols-4">
{plans.map((p, i) => (
<Reveal key={p.name} delay={i * 90}>
<div className="relative flex h-full flex-col gap-4 rounded-3xl border border-[#0160d4]/12 bg-white p-4">
{p.popular && (
<span className="absolute right-4 top-5 inline-flex h-4.5 items-center rounded-xl bg-[#f3610c] px-3 text-xs font-medium text-white">
Most Popular
</span>
)}
<div className="flex flex-col gap-2">
<h3 className="text-2xl font-bold text-[#11192d]">{p.name}</h3>
<p className="text-base font-bold text-[#11192d]">{p.tagline}</p>
<p className="text-base text-[#11192d]/70">{p.blurb}</p>
</div>
<div className="flex grow flex-col gap-4 rounded-2xl bg-[#0160d4]/3 p-4">
<p className="text-base font-bold text-[#0160d4]">{p.heading}</p>
<ul className="flex flex-col gap-2 text-xs font-medium text-[#11192d]">
{p.items.map((item) => (
<li key={item} className="flex gap-2">
<span aria-hidden>•</span>
{item}
</li>
))}
</ul>
</div>
<ButtonLink href="#demo" variant="outline" size="lg" className="h-12 w-full rounded-xl border-[#0160d4]/12">
{p.cta}
</ButtonLink>
</div>
</Reveal>
))}
<Reveal delay={plans.length * 90}>
<div className="relative flex h-full flex-col gap-4 overflow-hidden rounded-3xl bg-[#f3610c] p-4 text-white">
<div aria-hidden className="pointer-events-none absolute -right-20 -top-24 size-72 rounded-full bg-white/25 blur-3xl" />
<div aria-hidden className="pointer-events-none absolute -left-24 top-40 size-64 rounded-full border-30 border-white/10" />
<div className="relative flex grow flex-col gap-4">
<div className="flex flex-col gap-2">
<h3 className="text-2xl font-bold">Enterprise</h3>
<p className="text-base">
<span className="font-bold">Your </span>
<span className="italic">Business</span>
<span className="font-bold">, your </span>
<span className="italic">Platform</span>
</p>
<p className="text-xs font-medium">Custom architecture for larger operations.</p>
</div>
<div className="flex flex-col gap-4 rounded-2xl bg-[#11192d]/6 p-4">
<p className="text-base font-bold">Includes :</p>
<ul className="flex flex-col gap-2 text-xs font-bold">
{enterpriseItems.map((item) => (
<li key={item} className="flex gap-2">
<span aria-hidden>•</span>
{item}
</li>
))}
</ul>
</div>
<p className="px-4 text-xs font-medium">
Enterprise booking, ticketing and tourism management platform for multi-location operators and larger
transportation businesses.
</p>
</div>
<ButtonLink
href="#demo"
size="lg"
className="relative h-12 w-full rounded-xl border-white bg-white text-[#11192d]! hover:border-white hover:bg-white/90"
>
Contact Sales
</ButtonLink>
</div>
</Reveal>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,198 @@
import Image from "next/image";
import type { ReactNode } from "react";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Feature = { title: string; description: string; icon: ReactNode };
const features: Feature[] = [
{
title: "Rental Calendar",
description: "Manage availability and reservations.",
icon: (
<>
<path d="M2 12c0-3.77 0-5.66 1.17-6.83C4.34 4 6.23 4 10 4h4c3.77 0 5.66 0 6.83 1.17C22 6.34 22 8.23 22 12v2c0 3.77 0 5.66-1.17 6.83C19.66 22 17.77 22 14 22h-4c-3.77 0-5.66 0-6.83-1.17C2 19.66 2 17.77 2 14z" />
<path d="M7 4V2.5M17 4V2.5M2.5 9h19" />
</>
),
},
{
title: "Digital Contracts",
description: "Create and manage rental agreements digitally.",
icon: (
<>
<path d="M8 8h8v8H8z" />
<path d="M16 16h3a3 3 0 1 1-3 3z" />
<path d="M8 16H5a3 3 0 1 0 3 3z" />
<path d="M16 8h3a3 3 0 1 0-3-3z" />
<path d="M8 8H5a3 3 0 1 1 3-3z" />
</>
),
},
{
title: "Deposits & Payments",
description: "Manage deposits, payments and balances.",
icon: (
<>
<path d="M4 10C4 6.229 4 4.343 5.172 3.172 6.343 2 8.229 2 12 2s5.657 0 6.828 1.172C20 4.343 20 6.229 20 10v2c0 3.771 0 5.657-1.172 6.828C17.657 20 15.771 20 12 20s-5.657 0-6.828-1.172C4 17.657 4 15.771 4 12z" />
<path d="M4 13h16M15.5 16H17M7 16h1.5" />
<path d="M6 19.5V21a1 1 0 0 0 1 1h1.5a1 1 0 0 0 1-1v-1M18 19.5V21a1 1 0 0 1-1 1h-1.5a1 1 0 0 1-1-1v-1" />
<path d="M20 9h1a1 1 0 0 1 1 1v1a1 1 0 0 1-.4.8L20 13M4 9H3a1 1 0 0 0-1 1v1a1 1 0 0 0 .4.8L4 13M19.5 5h-15" />
</>
),
},
{
title: "Short-term Rentals",
description: "Manage vehicle and asset rentals from one platform.",
icon: (
<>
<path d="M7.37 11.63v.93l-.56.54c-.7-.72-.7-1.87.15-2.6l1.63-1.55c.3-.29.75.15.52.48-.29.4-.47.9-.47 1.42Zm-.93-1.13c-.72.68-.85 1.85-.15 2.6" />
<path d="M12.04 7c-2.99 0-5.42 2.4-5.42 5.38v.92m0-.92 1.62-1.55" />
<path d="M15 12.5c0 2.98-2.44 5.4-5.44 5.4C7.98 17.9 6.56 17.24 5.6 16.2M16.62 11.44V12.4l-1.62 1.55" />
</>
),
},
{
title: "Pickup & Return",
description: "Coordinate vehicle pickup and return processes.",
icon: (
<>
<path d="M2 12c0-4.71 0-7.07 1.46-8.54C4.93 2 7.29 2 12 2s7.07 0 8.54 1.46C22 4.93 22 7.29 22 12s0 7.07-1.46 8.54C19.07 22 16.71 22 12 22s-7.07 0-8.54-1.46C2 19.07 2 16.71 2 12Z" />
<path d="M12 12a5 5 0 0 0 5.4-5.4M12 12a5 5 0 0 1-5.4 5.4M12 12c0 2.76-1.34 5-3 5M12 12c0-2.76 1.34-5 3-5M17.4 6.6h-5.4v5.4" />
</>
),
},
{
title: "Digital Signatures",
description: "Simplify contracts and customer approvals.",
icon: (
<>
<path d="M8 17h5.33M10.67 7H16M10.67 17 13.33 7" />
<path d="M2 12c0-5.52 4.48-10 10-10s10 4.48 10 10-4.48 10-10 10S2 17.52 2 12Z" />
</>
),
},
{
title: "Vehicle Assignment",
description: "Connect rental bookings directly with available vehicles.",
icon: (
<>
<path d="M16.5 22C18.99 22 21 17.52 21 12S18.99 2 16.5 2" />
<path d="M12 12c0 5.52-2.01 10-4.5 10S3 17.52 3 12 5.01 2 7.5 2 12 6.48 12 12Z" />
<path d="M7.5 22c2.49 0 4.5-4.48 4.5-10S9.99 2 7.5 2" />
<path d="M9 12c0 3.31-.67 6-1.5 6S6 15.31 6 12s.67-6 1.5-6 1.5 2.69 1.5 6Z" />
<path d="M7.5 2h9M7.5 22h9M9 12H8" />
</>
),
},
{
title: "Asset Utilization",
description: "Understand how your rental assets are being used.",
icon: (
<>
<path d="M17.41 10.41C18 9.83 18 8.89 18 7s0-2.83-.59-3.41M17.41 10.41C16.83 11 15.89 11 14 11h-4c-1.89 0-2.83 0-3.41-.59M17.41 3.59C16.83 3 15.89 3 14 3h-4C8.11 3 7.17 3 6.59 3.59M6.59 3.59C6 4.17 6 5.11 6 7s0 2.83.59 3.41" />
<path d="M13 7a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z" />
<path d="M18 6c-1.66 0-3-1.34-3-3M18 8c-1.66 0-3 1.34-3 3M6 6c1.66 0 3-1.34 3-3M6 8c1.66 0 3 1.34 3 3" />
<path d="M5 20.39h2.26c1.01 0 2.03.1 3.02.31 1.74.35 3.57.4 5.33.11.87-.14 1.72-.36 2.49-.73.7-.34 1.55-.81 2.12-1.34.57-.53 1.17-1.4 1.59-2.08.36-.58.19-1.29-.39-1.73-.64-.48-1.58-.48-2.22 0l-1.8 1.36c-.7.53-1.47 1.02-2.38 1.16a4 4 0 0 1-.34.03m-1.92.01c.68.07 1.29.05 1.81 0l.11-.01m0 0c.15-.03.29-.12.43-.24.64-.56.68-1.5.12-2.13a2.7 2.7 0 0 0-.45-.4c-2.8-1.67-7.15-.4-9.78 1.46" />
<rect x="2" y="14" width="3" height="8" rx="1.5" />
</>
),
},
];
function FeatureIcon({ children }: { children: ReactNode }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="#f3610c" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="size-6 shrink-0">
{children}
</svg>
);
}
function FeatureCard({ f }: { f: Feature }) {
return (
<div className="group flex h-full flex-col justify-center gap-2 rounded-3xl bg-[#f3610c]/3 px-6 py-4 transition-all duration-300 hover:-translate-y-1 hover:bg-[#f3610c]/6 hover:shadow-lg">
<FeatureIcon>{f.icon}</FeatureIcon>
<h3 className="text-xl font-medium text-foreground">{f.title}</h3>
<p className="text-sm leading-relaxed text-foreground/70">{f.description}</p>
</div>
);
}
export function RentalManagement() {
return (
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="mx-auto grid max-w-6xl gap-12 px-4 sm:px-6 lg:grid-cols-[5fr_7fr] lg:gap-16">
<Reveal className="relative mx-auto w-64 sm:w-75 lg:mx-0">
{/* Bus on an orange tile with a fading reflection, matching the Platform section's box-light treatment */}
<div className="relative aspect-247/319 transition-transform duration-500 hover:-translate-y-1">
<div
aria-hidden
className="absolute inset-x-0 bottom-0 top-[74.2%] bg-linear-to-b from-[#F3610C]/35 to-transparent"
/>
<div
aria-hidden
className="absolute inset-x-0 top-[32.5%] bottom-[25.8%] overflow-hidden rounded-2xl bg-[linear-gradient(115deg,#f3610c_0%,#dd5c10_50%,#ff9457_100%)]"
>
{/* Shadow cast by the bus onto the tile */}
<div className="absolute inset-0 blur-md">
<div className="size-full bg-[#F3610C]" />
</div>
</div>
<Image
src="/rentalManagementBus.png"
alt="A bus, representing rental fleet management"
width={380}
height={220}
sizes="280px"
className="absolute left-4 top-[25%] h-auto w-full object-contain"
/>
</div>
</Reveal>
<Reveal delay={100} className="flex flex-col gap-8">
<div className="flex flex-col gap-4 border-l-2 border-accent pl-6">
<h2 className="text-4xl font-medium tracking-tight text-foreground sm:text-5xl">
Rental Management
</h2>
<p className="max-w-xl text-sm text-muted sm:text-base">
Rental Management Software for Tourism &amp; Transportation
Businesses. Tours aren&apos;t the only thing you can manage. A
dedicated rental module allows you to manage short-term vehicle
and asset rentals inside Voyager.
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
{features.map((f, i) => (
<Reveal key={f.title} delay={i * 80}>
<FeatureCard f={f} />
</Reveal>
))}
</div>
<Reveal
delay={features.length * 80}
className="flex flex-wrap items-center justify-between gap-4 rounded-3xl border border-[#0160d4]/12 p-4"
>
<span className="flex items-center gap-2 text-2xl font-medium text-foreground">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="#f3610c"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
className="size-6"
>
<path d="M16.57 19.2 12.64 15.26l-1.2 1.2c-1.23 1.23-1.85 1.85-2.51 1.7-.66-.14-.96-.96-1.56-2.6L5.35 10.12c-1.2-3.26-1.8-4.89-.96-5.73.84-.84 2.47-.24 5.73.96l5.45 2c1.63.6 2.45.91 2.6 1.57.14.66-.48 1.28-1.71 2.5l-1.2 1.21 3.94 3.94c.41.4.61.61.71.84.13.3.13.64 0 .95-.1.22-.3.42-.71.83-.41.41-.61.61-.84.7-.3.13-.64.13-.94 0-.23-.09-.43-.29-.84-.7Z" />
</svg>
Wanna try?
</span>
<ButtonLink href="#demo">Explore Rental Management</ButtonLink>
</Reveal>
</Reveal>
</div>
</section>
);
}

42
src/components/reveal.tsx Normal file
View File

@@ -0,0 +1,42 @@
"use client";
import { useEffect, useRef, useState, type ReactNode } from "react";
type RevealProps = {
children: ReactNode;
className?: string;
/** Stagger delay in ms, applied only once the element is visible. */
delay?: number;
};
/** Fades + slides children in once they scroll into view. No-ops instantly if JS hasn't hydrated yet. */
export function Reveal({ children, className = "", delay = 0 }: RevealProps) {
const ref = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const node = ref.current;
if (!node) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setVisible(true);
observer.disconnect();
}
},
{ threshold: 0.15, rootMargin: "0px 0px -10% 0px" },
);
observer.observe(node);
return () => observer.disconnect();
}, []);
return (
<div
ref={ref}
style={{ transitionDelay: visible ? `${delay}ms` : "0ms" }}
className={`transition-all duration-700 ease-out ${visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"} ${className}`}
>
{children}
</div>
);
}

View File

@@ -0,0 +1,55 @@
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Solution = { letter: string; title: string; description: string; cta: string; tone: "blue" | "orange" };
const solutions: Solution[] = [
{ letter: "H", title: "Hop-on Hop-off Tour Software", description: "Manage routes, stops, schedules, tickets, capacity, vehicles and daily operations.", cta: "Explore HOHO", tone: "blue" },
{ letter: "S", title: "Sightseeing Tour Software", description: "Manage guided tours, departures, guides, vehicles, pickups and customers.", cta: "Explore Sightseeing", tone: "orange" },
{ letter: "C", title: "Charter & Transportation Management Software", description: "Manage quotes, group bookings, vehicles, drivers, itineraries, contracts and payments.", cta: "Explore Charters", tone: "blue" },
{ letter: "V", title: "Vehicle Rental Management Software", description: "Manage rental availability, contracts, payments, vehicle assignments and utilization.", cta: "Explore Rentals", tone: "orange" },
{ letter: "G", title: "Growing Tour Operator Software", description: "Start with booking and add CRM, fleet, scheduling, payments, marketing and AI as you grow.", cta: "See How Voyager Scales", tone: "blue" },
{ letter: "E", title: "Enterprise Tourism Management Software", description: "Centralize multiple products, locations, fleets, teams and business operations.", cta: "Talk to Sales", tone: "orange" },
];
const tones = {
blue: { card: "bg-[#0160d4]/3", tile: "bg-[#0160d4]/5", letter: "from-[#0160d4] to-[#5ab0ff]" },
orange: { card: "bg-[#f3610c]/3", tile: "bg-[#f3610c]/5", letter: "from-[#f3610c] to-[#ffa66b]" },
};
export function Solutions() {
return (
<section id="solutions" className="relative scroll-mt-4 py-20 lg:py-28">
<div className="mx-auto flex max-w-6xl flex-col items-center gap-8 px-4 sm:px-6">
<div className="flex flex-col items-center gap-4 text-center">
<h2 className="text-5xl font-medium tracking-tight text-[#11192d] sm:text-[64px] sm:leading-tight">Solutions</h2>
<p className="max-w-sm text-base text-[#11192d]/70">Tour Operator Software Built for Your Business</p>
</div>
<div className="grid w-full gap-4 sm:grid-cols-2 lg:grid-cols-3">
{solutions.map((s, i) => {
const t = tones[s.tone];
return (
<Reveal key={s.letter} delay={(i % 3) * 90}>
<div className={`flex h-full flex-col gap-4 rounded-3xl p-6 transition-all duration-300 hover:-translate-y-1 hover:shadow-lg ${t.card}`}>
<div className={`grid size-20 place-items-center rounded-3xl ${t.tile}`}>
<span className={`bg-linear-to-br ${t.letter} bg-clip-text text-[40px] font-black leading-none text-transparent`}>
{s.letter}
</span>
</div>
<div className="flex grow flex-col gap-2">
<h3 className="text-2xl font-bold text-[#11192d]">{s.title}</h3>
<p className="text-sm text-[#11192d]/70">{s.description}</p>
</div>
<ButtonLink href="#demo" variant="outline" size="lg" className="h-12 w-full rounded-xl border-[#0160d4]/12">
{s.cta}
</ButtonLink>
</div>
</Reveal>
);
})}
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,155 @@
import type { ReactNode } from "react";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Feature = { title: string; description: string; icon: ReactNode; full?: boolean };
// Lucide-style 24px stroke icons, ported from the Figma export
const features: Feature[] = [
{
title: "Tour Builder",
description: "Create tours, routes, stops, schedules, departures and products.",
icon: (
<>
<path d="M4 10C4 6.229 4 4.343 5.172 3.172 6.343 2 8.229 2 12 2s5.657 0 6.828 1.172C20 4.343 20 6.229 20 10v2c0 3.771 0 5.657-1.172 6.828C17.657 20 15.771 20 12 20s-5.657 0-6.828-1.172C4 17.657 4 15.771 4 12z" />
<path d="M4 13h16M15.5 16H17M7 16h1.5" />
<path d="M6 19.5V21a1 1 0 0 0 1 1h1.5a1 1 0 0 0 1-1v-1M18 19.5V21a1 1 0 0 1-1 1h-1.5a1 1 0 0 1-1-1v-1" />
<path d="M20 9h1a1 1 0 0 1 1 1v1a1 1 0 0 1-.4.8L20 13M4 9H3a1 1 0 0 0-1 1v1a1 1 0 0 0 .4.8L4 13M19.5 5h-15" />
</>
),
},
{
title: "Real-Time Availability",
description: "Manage capacity, inventory and availability in real time.",
icon: (
<>
<path d="M22 10.5V12c0 4.714 0 7.071-1.464 8.536C19.071 22 16.714 22 12 22s-7.071 0-8.536-1.464C2 19.071 2 16.714 2 12s0-7.071 1.464-8.536C4.929 2 7.286 2 12 2h1.5" />
<circle cx="19" cy="5" r="3" />
<path d="M7 14h9M7 17.5h6" />
</>
),
},
{
title: "Flexible Pricing",
description: "Create fixed, dynamic, seasonal, group and channel-specific pricing.",
icon: (
<>
<path d="M4.73 16.14 2.57 13.98c-.7-.7-1.05-1.06-1.19-1.48s0-.87.14-1.75l.28-1.72c.28-1.79.42-2.68.9-3.16.49-.49 1.38-.62 3.17-.9l1.72-.28c.88-.14 1.32-.21 1.75-.14s.78.49 1.48 1.19l1.83 1.83c2.13 2.13 3.2 3.2 3.2 4.53s-1.07 2.4-3.2 4.54c-2.14 2.13-3.2 3.2-4.54 3.2s-2.4-1.07-4.53-3.2Z" />
<path d="m15.39 15.39.35.35M15.39 15.39c-.4.4-.94.57-1.39.47m1.39-.47c.59-.59.67-1.46.18-1.94-.49-.49-1.36-.4-1.94.18-.59.59-1.46.67-1.94.18s-.4-1.36.18-1.94M15.39 15.39c.35.4.44.86.31 1.28m-3.87-4.53.35.35" />
<circle cx="8.61" cy="8.88" r="2" transform="rotate(-45 8.61 8.88)" />
</>
),
},
{
title: "Preservation",
description: "Manage individual, group, private, corporate and wholesale bookings.",
icon: (
<>
<path d="m18.18 8.04.46-.46a1.75 1.75 0 0 1 2.47 2.47l-.46.46m-2.47-2.47c0 1 .87 1.86 1.75 1.86m.72-1.86-4.26 4.26c-.6.6-.9.9-1.11 1.24-.19.32-.33.66-.42 1.02l-.7 2.61-.2.33m0 0-.4.13c-.19.06-.4.02-.55-.16-.14-.14-.19-.35-.13-.55l.53-1.5" />
<path d="M8 13h2.5M8 9h6.5M8 17h1.5" />
<path d="M19.83 3.17C18.66 2 16.77 2 13 2h-2C7.23 2 5.34 2 4.17 3.17 3 4.34 3 6.23 3 10v4c0 3.77 0 5.66 1.17 6.83C5.34 22 7.23 22 11 22h2c3.77 0 5.66 0 6.83-1.17.94-.94 1.13-2.35 1.16-4.83" />
</>
),
},
{
title: "Digital Tickets",
description: "Issue digital tickets, confirmations, vouchers and booking information.",
icon: (
<>
<path d="M20.31 12.65 20.83 10.72c.6-2.26.91-3.38.68-4.36a4.5 4.5 0 0 0-1.16-2.02c-.73-.68-1.86-1-4.12-1.6-2.25-.6-3.38-.9-4.35-.68-.77.18-1.47.58-2.01 1.16-.59.62-.9 1.54-1.35 3.2l-.52 1.93c-.6 2.25-.91 3.38-.68 4.35.18.77.58 1.47 1.16 2.02.73.68 1.86.98 4.12 1.59 2.03.55 3.15.85 4.06.73.1-.01.2-.03.29-.05.77-.18 1.47-.58 2.01-1.16.68-.73.98-1.86 1.59-4.11Z" />
<path d="M16.42 17.97c-.21.64-.58 1.22-1.07 1.68-.73.68-1.86.98-4.12 1.59-2.25.6-3.38.9-4.35.67a4.5 4.5 0 0 1-2.01-1.16c-.68-.73-.98-1.86-1.59-4.11l-.52-1.93c-.6-2.25-.9-3.38-.68-4.35.18-.77.58-1.47 1.16-2.02.73-.68 1.86-.98 4.12-1.59.43-.11.81-.22 1.16-.31" />
<path d="m11.78 10 4.83 1.29M11 12.9l2.9.78" />
</>
),
},
{
title: "Changes & Cancellation",
description: "Manage amendments, extensions, cancellations and customer changes efficiently.",
icon: (
<>
<path d="M16 4c2.17.01 3.35.11 4.12.88.88.88.88 2.29.88 5.12v6c0 2.83 0 4.24-.88 5.12-.88.88-2.29.88-5.12.88H9c-2.83 0-4.24 0-5.12-.88C3 20.24 3 18.83 3 16v-6c0-2.83 0-4.24.88-5.12C4.65 4.11 5.82 4.01 8 4" />
<path d="M8 3.5A1.5 1.5 0 0 1 9.5 2h5A1.5 1.5 0 0 1 16 3.5v1A1.5 1.5 0 0 1 14.5 6h-5A1.5 1.5 0 0 1 8 4.5Z" />
<path d="M14.5 11 9.5 16M9.5 11l5 5" />
</>
),
},
];
const platform: Feature = {
title: "Enterprise Ticketing Platform",
description: "Manage high-volume ticketing, reservations, inventory and customer transactions from one centralized platform.",
icon: (
<>
<path d="M21.61 13.56 21.84 11.13c.18-1.91.27-2.87-.06-3.27a1.05 1.05 0 0 0-.68-.36c-.48-.04-1.07.63-2.27 1.99-.62.7-.93 1.06-1.28 1.11a.9.9 0 0 1-.57-.09c-.32-.16-.53-.6-.95-1.46L13.81 4.46C13.01 2.82 12.61 2 12 2s-1.01.82-1.81 2.46l-2.24 4.58c-.42.87-.63 1.3-.95 1.46-.18.09-.38.12-.57.09-.34-.05-.65-.41-1.27-1.11C3.97 8.13 3.37 7.45 2.9 7.5c-.26.02-.5.15-.68.36-.33.4-.23 1.36-.06 3.27l.23 2.43c.38 4.01.57 6.02 1.74 7.23S7.1 22 10.64 22h2.72c3.54 0 5.32 0 6.5-1.21s1.36-3.22 1.74-7.23Z" />
<path d="M9 18h6" />
</>
),
full: true,
};
function FeatureIcon({ children }: { children: ReactNode }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="#0160d4" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="size-6 shrink-0">
{children}
</svg>
);
}
function FeatureCard({ f }: { f: Feature }) {
return (
<div
className={`group flex items-center gap-4 rounded-3xl bg-[#0160d4]/3 px-6 py-8 transition-all duration-300 hover:-translate-y-1 hover:bg-[#0160d4]/6 hover:shadow-lg ${f.full ? "sm:col-span-2" : ""}`}
>
<span className="transition-transform duration-300 group-hover:scale-110">
<FeatureIcon>{f.icon}</FeatureIcon>
</span>
<div>
<h3 className="text-xl font-bold text-foreground sm:text-2xl">{f.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-foreground/70 sm:text-base">{f.description}</p>
</div>
</div>
);
}
export function TourBooking() {
return (
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<Reveal className="flex max-w-md flex-col gap-4">
<h2 className="text-4xl font-medium tracking-tight text-foreground sm:text-5xl">
Tour Booking &amp; Management Software
</h2>
<p className="max-w-md text-sm text-muted sm:text-base">
Voyager gives tour operators and activity businesses the tools to create, sell and manage virtually any
tourism product from one platform.
</p>
</Reveal>
<Reveal delay={100} className="mt-10 flex items-center justify-center rounded-2xl bg-accent/10 px-4 py-3 lg:mt-14">
<p className="text-center text-3xl font-medium text-foreground sm:text-4xl lg:text-5xl">
Sell <em className="font-normal text-accent italic">More</em>. Manage{" "}
<em className="font-normal text-accent italic">Less</em>.
</p>
</Reveal>
<div className="mt-6 grid gap-4 sm:grid-cols-2">
{features.map((f, i) => (
<Reveal key={f.title} delay={i * 90}>
<FeatureCard f={f} />
</Reveal>
))}
<Reveal delay={features.length * 90} className="sm:col-span-2">
<FeatureCard f={platform} />
</Reveal>
</div>
<Reveal delay={100} className="mt-10 flex flex-wrap items-center justify-center gap-4 lg:mt-14">
<p className="text-xl font-medium text-foreground sm:text-2xl">Now lets see</p>
<ButtonLink href="#tour-booking" size="lg">
Tour Booking Software
</ButtonLink>
</Reveal>
</div>
</section>
);
}

View File

@@ -0,0 +1,161 @@
import Image from "next/image";
import type { ReactNode } from "react";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Feature = { title: string; description: string; icon: ReactNode };
const features: Feature[] = [
{
title: "Fleet Management",
description: "Manage vehicles, availability, assignments, utilization and operational status.",
icon: (
<>
<path d="M4 10C4 6.229 4 4.343 5.172 3.172 6.343 2 8.229 2 12 2s5.657 0 6.828 1.172C20 4.343 20 6.229 20 10v2c0 3.771 0 5.657-1.172 6.828C17.657 20 15.771 20 12 20s-5.657 0-6.828-1.172C4 17.657 4 15.771 4 12z" />
<path d="M4 13h16M15.5 16H17M7 16h1.5" />
<path d="M6 19.5V21a1 1 0 0 0 1 1h1.5a1 1 0 0 0 1-1v-1M18 19.5V21a1 1 0 0 1-1 1h-1.5a1 1 0 0 1-1-1v-1" />
<path d="M20 9h1a1 1 0 0 1 1 1v1a1 1 0 0 1-.4.8L20 13M4 9H3a1 1 0 0 0-1 1v1a1 1 0 0 0 .4.8L4 13M19.5 5h-15" />
</>
),
},
{
title: "Scheduling",
description: "Coordinate departures, routes, vehicles, drivers and staff.",
icon: (
<>
<path d="M2 12c0-3.77 0-5.66 1.17-6.83C4.34 4 6.23 4 10 4h4c3.77 0 5.66 0 6.83 1.17C22 6.34 22 8.23 22 12v2c0 3.77 0 5.66-1.17 6.83C19.66 22 17.77 22 14 22h-4c-3.77 0-5.66 0-6.83-1.17C2 19.66 2 17.77 2 14z" />
<path d="M7 4V2.5M17 4V2.5M2.5 9h19" />
</>
),
},
{
title: "Driver Management",
description: "Assign drivers and keep operational information connected.",
icon: (
<>
<path d="M8 9v6M12 9v6" />
<path d="M8 12h5c.93 0 1.4 0 1.77-.15.49-.2.88-.6 1.08-1.09.15-.37.15-.83.15-1.76" />
<rect x="2" y="2" width="20" height="20" rx="5" />
</>
),
},
{
title: "Staff Assignment",
description: "Coordinate teams across tours, transportation services and daily operations.",
icon: (
<>
<circle cx="9" cy="6" r="4" />
<path d="M15 9c1.66 0 3-1.34 3-3s-1.34-3-3-3" />
<ellipse cx="9" cy="17" rx="7" ry="4" />
<path d="M18 14c1.75.38 3 1.36 3 2.5 0 1.03-1.01 1.92-2.5 2.37" />
</>
),
},
{
title: "Pickup Management",
description: "Manage pickup locations, times, passenger information and communications.",
icon: (
<>
<circle cx="12" cy="12" r="3" />
<path d="M13.77 2.15C13.4 2 12.93 2 12 2s-1.4 0-1.77.15a2.4 2.4 0 0 0-1.08 1.08c-.09.22-.13.48-.14.86a1.8 1.8 0 0 1-.91 1.5c-.48.28-1.07.27-1.56 0-.34-.18-.58-.27-.82-.3-.53-.07-1.06.07-1.48.4-.31.24-.55.65-1.01 1.45-.47.81-.7 1.21-.75 1.61-.07.53.07 1.06.4 1.48.14.19.35.35.67.55.47.3.77.8.77 1.36 0 .56-.3 1.06-.77 1.36-.32.2-.53.36-.67.55-.32.42-.46.95-.4 1.48.05.4.28.8.75 1.6.46.8.69 1.21 1 1.45.42.32.95.46 1.48.4.24-.03.48-.13.82-.3.49-.27 1.08-.27 1.56 0 .48.28.77.8.79 1.36.01.38.05.64.14.86.2.5.6.88 1.08 1.08.37.15.83.15 1.77.15.93 0 1.4 0 1.77-.15a2.4 2.4 0 0 0 1.08-1.08c.09-.22.13-.48.14-.86a1.8 1.8 0 0 1 .91-1.5c.48-.28 1.07-.27 1.56 0 .34.18.58.27.82.3.53.07 1.06-.07 1.48-.4.31-.24.55-.65 1-1.45.47-.8.7-1.21.75-1.6a1.9 1.9 0 0 0-.4-1.48c-.14-.19-.35-.35-.67-.55A1.6 1.6 0 0 1 19.56 12c0-.56.3-1.06.77-1.36.32-.2.53-.36.67-.55.32-.42.46-.95.4-1.48-.05-.4-.28-.8-.75-1.6-.46-.8-.69-1.21-1-1.45a1.9 1.9 0 0 0-1.48-.4c-.24.03-.48.12-.82.3-.49.27-1.08.28-1.56 0a1.8 1.8 0 0 1-.91-1.5c-.01-.38-.05-.64-.14-.86a2.4 2.4 0 0 0-1.08-1.08z" />
</>
),
},
{
title: "Operational Alerts",
description: "Identify issues and exceptions before they become problems.",
icon: (
<>
<path d="M5.31 10.76C8.23 5.59 9.69 3 12 3s3.77 2.59 6.69 7.76l.36.65c2.43 4.3 3.64 6.45 2.54 8.02C20.5 21 17.79 21 12.36 21h-.73c-5.43 0-8.14 0-9.23-1.57-1.1-1.57.11-3.72 2.54-8.02z" />
<path d="M12 8v5" />
<circle cx="12" cy="16" r="1" fill="currentColor" stroke="none" />
</>
),
},
{
title: "Utilization",
description: "Understand how your vehicles, staff and capacity are being used.",
icon: (
<>
<path d="M17.41 10.41C18 9.83 18 8.89 18 7s0-2.83-.59-3.41M17.41 10.41C16.83 11 15.89 11 14 11h-4c-1.89 0-2.83 0-3.41-.59M17.41 3.59C16.83 3 15.89 3 14 3h-4C8.11 3 7.17 3 6.59 3.59M6.59 3.59C6 4.17 6 5.11 6 7s0 2.83.59 3.41" />
<path d="M13 7a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z" />
<path d="M18 6c-1.66 0-3-1.34-3-3M18 8c-1.66 0-3 1.34-3 3M6 6c1.66 0 3-1.34 3-3M6 8c1.66 0 3 1.34 3 3" />
<path d="M5 20.39h2.26c1.01 0 2.03.1 3.02.31 1.74.35 3.57.4 5.33.11.87-.14 1.72-.36 2.49-.73.7-.34 1.55-.81 2.12-1.34.57-.53 1.17-1.4 1.59-2.08.36-.58.19-1.29-.39-1.73-.64-.48-1.58-.48-2.22 0l-1.8 1.36c-.7.53-1.47 1.02-2.38 1.16a4 4 0 0 1-.34.03m-1.92.01c.68.07 1.29.05 1.81 0l.11-.01m0 0c.15-.03.29-.12.43-.24.64-.56.68-1.5.12-2.13a2.7 2.7 0 0 0-.45-.4c-2.8-1.67-7.15-.4-9.78 1.46" />
<rect x="2" y="14" width="3" height="8" rx="1.5" />
</>
),
},
];
function FeatureIcon({ children }: { children: ReactNode }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="#0160d4" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="size-6 shrink-0">
{children}
</svg>
);
}
function FeatureCard({ f }: { f: Feature }) {
return (
<div className="group flex h-full flex-col justify-center gap-2 rounded-3xl border border-[#0160d4]/12 bg-white px-6 py-4 transition-all duration-300 hover:-translate-y-1 hover:border-transparent hover:shadow-lg">
<FeatureIcon>{f.icon}</FeatureIcon>
<h3 className="text-xl font-medium text-foreground">{f.title}</h3>
<p className="text-sm leading-relaxed text-foreground/70">{f.description}</p>
</div>
);
}
export function TourOperations() {
return (
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="mx-auto grid max-w-6xl gap-12 px-4 sm:px-6 lg:grid-cols-[7fr_5fr] lg:gap-16">
<Reveal className="order-2 flex flex-col gap-8 lg:order-1">
<div className="flex flex-col gap-6">
<h2 className="text-4xl font-medium tracking-tight text-foreground sm:text-5xl">
Tour Operations &amp; Fleet Management Software
</h2>
<span className="w-fit items-center rounded-2xl bg-[#0160D4]/10 px-4 py-3 font-bold text-[#0160D4] text-xl">
From Booking to Operation. Automatically{" "}
<em className="font-normal italic">Connected</em>.
</span>
<p className="max-w-xl text-sm text-muted sm:text-base">
Know exactly what&apos;s happening across your business with
connected scheduling, fleet, driver, staff and pickup management.
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
{features.map((f, i) => (
<Reveal key={f.title} delay={i * 80}>
<FeatureCard f={f} />
</Reveal>
))}
<Reveal
delay={features.length * 80}
className="flex flex-col items-center justify-center gap-4 rounded-3xl border border-[#0160d4]/12 bg-white px-6 py-4 text-center"
>
<p className="text-xl font-medium text-foreground">
Lets see the
</p>
<ButtonLink href="#operations">Operations</ButtonLink>
</Reveal>
</div>
</Reveal>
<Reveal
delay={120}
className="order-1 mx-auto w-full max-w-sm lg:order-2 lg:mx-0"
>
<Image
src="/tourOperationPhone.png"
alt="VOYAGER mobile app showing today's revenue, bookings, departures, vehicles operating and staff assigned"
width={400}
height={520}
sizes="(min-width: 1024px) 380px, 70vw"
className="h-auto w-full object-contain transition-transform duration-500 hover:-translate-y-1"
/>
</Reveal>
</div>
</section>
);
}

View File

@@ -0,0 +1,101 @@
import Image from "next/image";
import { ButtonLink } from "@/components/button";
import { Reveal } from "@/components/reveal";
type Pillar = { title: string; description: string };
const pillars: Pillar[] = [
{ title: "Ask", description: "Get answers using your business data." },
{ title: "Assist", description: "Help staff perform repetitive tasks." },
{ title: "Communicate", description: "Assist with customer responses and notifications." },
{ title: "Analyze", description: "Identify trends, opportunities and operational issues." },
{ title: "Automate", description: "Reduce repetitive administrative work and improve operational efficiency." },
];
const bubbles = [
{
text: "How many passengers are booked tomorrow?",
className: "right-[8%] top-[8%] w-52 animate-float-1",
},
{
text: "Which vehicles are available at 2 PM?",
className: "left-[2%] top-[38%] w-44 animate-float-2 [animation-delay:-1.2s]",
},
{
text: "Show me today's charter operations.",
className: "right-[10%] bottom-[10%] w-44 animate-float-3 [animation-delay:-2.4s]",
},
];
export function VoyagerAI() {
return (
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<Reveal className="relative overflow-hidden rounded-[32px] bg-[#0160d4] px-6 py-16 sm:px-10 lg:px-16 lg:py-20">
{/* Decorative glow blobs, echoing the Figma frame's soft light streaks */}
<div aria-hidden className="pointer-events-none absolute -right-40 -top-56 size-125 rounded-full bg-white/10 blur-3xl" />
<div aria-hidden className="pointer-events-none absolute -bottom-56 -left-32 size-100 rounded-full bg-white/10 blur-3xl" />
<div className="relative flex flex-col items-center gap-3 text-center">
<span className="inline-flex h-4.5 items-center rounded-xl bg-white px-3 text-xs font-medium text-foreground">
AI Platform
</span>
<h2 className="max-w-xl text-4xl font-medium tracking-tight text-white sm:text-5xl">
Meet Your New Business Assistant
</h2>
<p className="max-w-2xl text-sm text-white/80 sm:text-base">
AI-Powered Tour Operator Software. Voyager AI turns your booking, customer, fleet, scheduling, payment
and operational data into useful answers and actionable insights.
</p>
<p className="text-sm font-bold text-white sm:text-base">
Instead of searching through reports and screens, simply ask:
</p>
</div>
<div className="relative mx-auto mt-12 h-80 w-full max-w-lg sm:h-96">
<div className="absolute inset-0 flex items-center justify-center">
<div className="relative animate-brain-drift">
<div aria-hidden className="absolute inset-x-8 -bottom-4 h-8 rounded-full bg-black/25 blur-xl" />
<Image
src="/voyager-brain.png"
alt="Voyager AI, represented by a brain"
width={340}
height={280}
sizes="(min-width: 640px) 420px, 75vw"
className="relative h-auto w-72 object-contain drop-shadow-2xl sm:w-96"
/>
</div>
</div>
{bubbles.map((b) => (
<div
key={b.text}
className={`absolute rounded-xl bg-white px-3 py-1.5 text-sm text-foreground shadow-[0_20px_28px_0_rgba(0,0,0,0.15)] ${b.className}`}
>
{b.text}
</div>
))}
</div>
<div className="relative mt-12 grid gap-4 sm:grid-cols-2 lg:mt-16 lg:grid-cols-3">
{pillars.map((p, i) => (
<Reveal key={p.title} delay={i * 90} className={p.title === "Communicate" || p.title === "Automate" ? "lg:col-span-1" : ""}>
<div className="flex h-full flex-col gap-2 rounded-3xl bg-white/5 px-6 py-8 transition-colors duration-300 hover:bg-white/10">
<h3 className="text-2xl font-bold text-white">{p.title}</h3>
<p className="text-base leading-relaxed text-white/70">{p.description}</p>
</div>
</Reveal>
))}
<Reveal delay={pillars.length * 90} className="flex flex-col items-center justify-center gap-4 rounded-3xl bg-white px-6 py-8 text-center transition-transform duration-300 hover:-translate-y-1">
<p className="text-2xl font-bold text-foreground">Its time to start!</p>
<ButtonLink href="#demo" size="lg">
Explore Voyager AI
</ButtonLink>
</Reveal>
</div>
</Reveal>
</div>
</section>
);
}

View File

@@ -0,0 +1,174 @@
import Image from "next/image";
import { Reveal } from "@/components/reveal";
type DiffCard = {
title: string;
description: string;
image: string;
imageAlt: string;
eyebrow?: string;
aiBadge?: boolean;
/** Voyager card ships blue by default; every other card turns blue on hover. */
alwaysBlue?: boolean;
};
const topCards: DiffCard[] = [
{
eyebrow: "With",
title: "Traditional Way",
description: "Traditional tour booking software manages reservations.",
image: "/voyager-diff-traditional.png",
imageAlt: "A tortoise, representing the traditional, slower way of managing a tourism business",
},
{
eyebrow: "With",
title: "Voyager",
description:
"Voyager connects the entire operation from the first customer interaction to the final payment and operational report.",
image: "/voyager-diff-voyager.png",
imageAlt: "A hare leaping forward, representing how much faster Voyager moves",
alwaysBlue: true,
},
];
const featureCards: DiffCard[] = [
{
title: "Real Industry Knowledge",
description:
"Built around the actual workflows of tour operators, sightseeing companies, transportation businesses and rental operators.",
image: "/voyager-diff-industry.png",
imageAlt: "A bus, representing transportation and tour operations",
},
{
title: "AI at the core",
description: "Use your business data to answer questions, automate tasks and make better operational decisions.",
image: "/voyager-diff-ai.png",
imageAlt: "A brain, representing Voyager's built-in AI",
aiBadge: false,
},
];
const pillarCards: DiffCard[] = [
{
title: "Modular Architecture",
description: "Start with booking software and add CRM, fleet, rentals, marketing and other modules as your business grows.",
image: "/voyager-diff-modular.png",
imageAlt: "Stacked cube modules, representing modular architecture",
},
{
title: "Scalable Technology",
description: "Designed for small operators, midsize companies and enterprise tourism businesses.",
image: "/voyager-diff-scalable.png",
imageAlt: "A metallic ring, representing scalable technology",
},
{
title: "Lower Total Cost",
description: "Replace multiple disconnected systems with one integrated tourism business management platform.",
image: "/voyager-diff-cost.png",
imageAlt: "Stacked coins and bills, representing lower total cost",
},
];
const cardBase =
"group relative flex flex-col justify-end overflow-hidden rounded-[40px] transition-all duration-300 ease-out hover:-translate-y-1.5 hover:shadow-2xl hover:shadow-[#0160d4]/20";
const cardBg = (alwaysBlue?: boolean) =>
alwaysBlue ? "bg-[#0160d4]" : "bg-card hover:bg-[#0160d4]";
const cardTitle = (alwaysBlue?: boolean) =>
`text-foreground transition-colors duration-300 ${alwaysBlue ? "text-white" : "group-hover:text-white"}`;
const cardDesc = (alwaysBlue?: boolean) =>
`text-foreground/70 transition-colors duration-300 ${alwaysBlue ? "text-white/70" : "group-hover:text-white/70"}`;
export function VoyagerDifference() {
return (
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<Reveal className="mx-auto flex max-w-md flex-col items-center gap-4 text-center">
<h2 className="text-4xl font-medium tracking-tight text-foreground sm:text-5xl">The Voyager Difference</h2>
<p className="max-w-88 text-sm text-muted sm:text-base">
More Than Tour Booking Software Your Entire Tourism Business, Connected.
</p>
</Reveal>
{/* Traditional vs. Voyager */}
<div className="mt-16 grid gap-6 sm:grid-cols-2 lg:mt-20">
{topCards.map((c, i) => (
<Reveal key={c.title} delay={i * 120}>
<div className={`h-115 p-8 ${cardBase} ${cardBg(c.alwaysBlue)}`}>
<Image
src={c.image}
alt={c.imageAlt}
width={449}
height={283}
sizes="(min-width: 640px) 45vw, 90vw"
className="pointer-events-none h-auto w-[calc(85%-4rem)] self-center object-contain transition-transform duration-300 group-hover:scale-105"
/>
<div className="relative flex flex-col items-start gap-2">
<span
className={`inline-flex h-4.5 items-center rounded-xl px-3 text-xs font-medium ${
c.alwaysBlue ? "bg-white text-foreground" : "bg-accent text-white"
}`}
>
{c.eyebrow}
</span>
<h3 className={`text-2xl font-bold ${cardTitle(c.alwaysBlue)}`}>{c.title}</h3>
<p className={`text-base leading-relaxed ${cardDesc(c.alwaysBlue)}`}>{c.description}</p>
</div>
</div>
</Reveal>
))}
</div>
{/* Industry knowledge & AI */}
<div className="mt-6 grid gap-6 sm:grid-cols-2">
{featureCards.map((c, i) => (
<Reveal key={c.title} delay={i * 120}>
<div className={`h-115 p-8 ${cardBase} ${cardBg()}`}>
<div className="pointer-events-none absolute inset-x-8 top-8 h-[calc(100%-9rem)] transition-transform duration-300 group-hover:scale-105">
<Image
src={c.image}
alt={c.imageAlt}
width={449}
height={283}
sizes="(min-width: 640px) 45vw, 90vw"
className="h-full w-full object-contain"
/>
{c.aiBadge && (
<span className="absolute bottom-6 left-0 flex size-12 items-center justify-center rounded-full bg-linear-to-b from-[#02adf7] to-[#0160d4] text-2xl font-medium text-white">
AI
</span>
)}
</div>
<div className="relative flex flex-col gap-2">
<h3 className={`text-2xl font-bold ${cardTitle()}`}>{c.title}</h3>
<p className={`text-base leading-relaxed ${cardDesc()}`}>{c.description}</p>
</div>
</div>
</Reveal>
))}
</div>
{/* Pillars */}
<div className="mt-6 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{pillarCards.map((c, i) => (
<Reveal key={c.title} delay={i * 120}>
<div className={`h-100 p-6 ${cardBase} ${cardBg()}`}>
<Image
src={c.image}
alt={c.imageAlt}
width={245}
height={216}
sizes="(min-width: 1024px) 22vw, (min-width: 640px) 45vw, 90vw"
className="pointer-events-none absolute inset-x-6 top-6 h-[calc(100%-8rem)] w-[calc(100%-3rem)] object-contain transition-transform duration-300 group-hover:scale-105"
/>
<div className="relative flex flex-col gap-2">
<h3 className={`text-xl font-bold ${cardTitle()}`}>{c.title}</h3>
<p className={`text-base leading-relaxed ${cardDesc()}`}>{c.description}</p>
</div>
</div>
</Reveal>
))}
</div>
</div>
</section>
);
}

34
tsconfig.json Normal file
View File

@@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}

BIN
voyager-diff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB