first commit
44
.gitignore
vendored
Normal 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
@@ -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 -->
|
||||
36
README.md
Normal 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
@@ -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
@@ -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
26
package.json
Normal 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
@@ -0,0 +1,7 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
BIN
public/aboutBus.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/box-dark.png
Normal file
|
After Width: | Height: | Size: 400 KiB |
BIN
public/box-light.png
Normal file
|
After Width: | Height: | Size: 610 KiB |
BIN
public/customer-image.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
public/hand-holding-card.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
public/hero-image-light-cropped.png
Normal file
|
After Width: | Height: | Size: 307 KiB |
BIN
public/hero-image-light.png
Normal file
|
After Width: | Height: | Size: 357 KiB |
BIN
public/hero-image.png
Normal file
|
After Width: | Height: | Size: 330 KiB |
BIN
public/iphone17-light.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
public/iphone17-opaque.png
Normal file
|
After Width: | Height: | Size: 321 KiB |
BIN
public/iphone17.png
Normal file
|
After Width: | Height: | Size: 268 KiB |
BIN
public/ladder.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/logo-light.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/platform-section.png
Normal file
|
After Width: | Height: | Size: 357 KiB |
BIN
public/rentalManagementBus.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
public/text-logo-light.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/tourOperationPhone.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
public/voyager-brain.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
public/voyager-diff-ai.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
public/voyager-diff-cost.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
public/voyager-diff-industry.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
public/voyager-diff-modular.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/voyager-diff-scalable.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/voyager-diff-traditional.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
public/voyager-diff-voyager.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
125
src/app/globals.css
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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">
|
||||
& 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>
|
||||
);
|
||||
}
|
||||
174
src/components/customer-stories.tsx
Normal 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
@@ -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
@@ -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 & 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>
|
||||
);
|
||||
}
|
||||
50
src/components/integrations.tsx
Normal 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
@@ -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>
|
||||
);
|
||||
}
|
||||
144
src/components/marketing-growth.tsx
Normal 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 & 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
@@ -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>
|
||||
);
|
||||
}
|
||||
68
src/components/ota-distribution.tsx
Normal 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 & Distribution</h2>
|
||||
<p className="text-sm text-muted sm:text-base">
|
||||
<span className="font-bold">OTA Management & 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>
|
||||
);
|
||||
}
|
||||
45
src/components/partners.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
155
src/components/payments-revenue.tsx
Normal 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 & Revenue
|
||||
</span>
|
||||
<h2 className="max-w-2xl text-4xl font-medium tracking-tight sm:text-5xl">
|
||||
Payments & 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
@@ -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
@@ -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>
|
||||
);
|
||||
}
|
||||
198
src/components/rental-management.tsx
Normal 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 & Transportation
|
||||
Businesses. Tours aren'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
@@ -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>
|
||||
);
|
||||
}
|
||||
55
src/components/solutions.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
155
src/components/tour-booking.tsx
Normal 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 & 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>
|
||||
);
|
||||
}
|
||||
161
src/components/tour-operations.tsx
Normal 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 & 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'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>
|
||||
);
|
||||
}
|
||||
101
src/components/voyager-ai.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
174
src/components/voyager-difference.tsx
Normal 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
@@ -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
|
After Width: | Height: | Size: 50 KiB |