import React, { useRef, useState, useEffect, useCallback } from "react";
import { motion, useInView, AnimatePresence } from "motion/react";
import {
ArrowRight,
Star,
Mail,
Linkedin,
Paperclip,
ChevronLeft,
ChevronRight,
Play,
} from "lucide-react";
import {
colors,
typography,
spacing,
layout,
} from "@/app/utils/design-tokens";
import { easing, duration } from "@/app/utils/animations";
import { Button } from "../components/ui/shared/Buttons";
import { ImageWithFallback } from "../components/figma/ImageWithFallback";
import { optimizeUnsplashUrl, optimizeForSize } from "../utils/image-utils";
import { useDocumentMeta } from "../hooks/useDocumentMeta";
import { externalUrls, socialLinks } from "../config/site";
import { SectionHeader, GradientText } from "../components/shared/SectionHeader";
import { FinalCTASection } from "../components/shared/FinalCTASection";
import { AnimatedSection } from "../components/shared/AnimatedSection";
import {
specialistStats,
industries,
clientBreakdown,
whyWorkWithUs,
testimonials,
heroReviews,
aboutWhatHappensNext,
} from "../data/about";
// Data arrays imported from @/app/data/about.ts
// Tech stack imported from @/app/data/tech-stack.tsx
// ============================================================================
// MAIN COMPONENT
// ============================================================================
export function About() {
useDocumentMeta({
title: "About",
description: "Meet the Finjineers team — a 100% in-house team of mobile app developers, designers, and strategists building world-class apps for startups and enterprises.",
});
return (
);
}
// ============================================================================
// 1. HERO SECTION — About Us
// ============================================================================
function HeroSection() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
return (
{/* Background glow */}
{/* Left — Copy */}
About us
About Us
We are a premium mobile app development agency founded by
ex-Google and ex-Facebook engineers. We combine Silicon Valley
engineering standards with world-class design to build apps that
startups and SaaS founders love.
Book a free call
{/* Right — Review Ticket Carousel */}
);
}
// ============================================================================
// REVIEW TICKET CAROUSEL — used in Hero
// ============================================================================
function StarRating({ rating }: { rating: string }) {
const numRating = parseFloat(rating);
const fullStars = Math.floor(numRating);
const hasHalf = numRating % 1 >= 0.5;
return (
{Array.from({ length: 5 }).map((_, i) => (
))}
);
}
function ReviewTicketCarousel() {
const [current, setCurrent] = useState(0);
const [direction, setDirection] = useState(1);
const total = heroReviews.length;
const ticketRef = useRef(null);
const goTo = useCallback(
(next: number) => {
setDirection(next > current ? 1 : -1);
setCurrent(next);
},
[current]
);
const goNext = useCallback(() => {
goTo((current + 1) % total);
}, [current, total, goTo]);
const goPrev = useCallback(() => {
goTo((current - 1 + total) % total);
}, [current, total, goTo]);
// Auto-scroll every 4 seconds — pauses when off-screen
useEffect(() => {
const el = ticketRef.current;
if (!el) return;
let timer: ReturnType | null = null;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
timer = setInterval(goNext, 4000);
} else if (timer) {
clearInterval(timer);
timer = null;
}
},
{ threshold: 0.1 }
);
observer.observe(el);
return () => {
observer.disconnect();
if (timer) clearInterval(timer);
};
}, [goNext]);
const review = heroReviews[current];
const variants = {
enter: (dir: number) => ({ x: dir > 0 ? 80 : -80, opacity: 0 }),
center: { x: 0, opacity: 1 },
exit: (dir: number) => ({ x: dir > 0 ? -80 : 80, opacity: 0 }),
};
return (
{/* Ticket Card */}
{/* Top perforation dots */}
{Array.from({ length: 16 }).map((_, i) => (
))}
{/* Left — Quote & Date */}
The Review
“{review.quote}”
{review.date}
{/* Right — Ratings */}
{/* Overall */}
{review.overall}
{/* Detail Scores */}
{[
{ label: "Quality:", value: review.quality },
{ label: "Schedule:", value: review.schedule },
{ label: "Cost:", value: review.cost },
{ label: "Willing to refer:", value: review.willingToRefer },
].map((item) => (
{item.label}
{item.value}
))}
{/* Bottom perforation dots */}
{Array.from({ length: 16 }).map((_, i) => (
))}
{/* Navigation Arrows + Dots */}
{/* Slide dots */}
{heroReviews.map((_, i) => (
goTo(i)}
className={`w-2 h-2 rounded-full transition-all duration-300 ${
i === current ? "bg-[#f15f3d] w-5" : "bg-white/[0.15] hover:bg-white/[0.25]"
}`}
aria-label={`Go to review ${i + 1}`}
/>
))}
{/* Arrows */}
);
}
// 3. TECHNOLOGY STACK — Uses shared TechStackSection from @/app/components/shared/TechStackSection
// ============================================================================
// 4. 50+ IN-HOUSE SPECIALISTS
// ============================================================================
function SpecialistsSection() {
return (
{(isInView) => (
{/* Left — Heading */}
50+ In-House
Specialists
{/* Right — Stat Cards (2×2 grid, bounding-box style) */}
{specialistStats.map((stat, i) => (
{/* Bounding-box card */}
{/* Corner & midpoint handles */}
{[
"top-0 left-0 -translate-x-1/2 -translate-y-1/2",
"top-0 right-0 translate-x-1/2 -translate-y-1/2",
"bottom-0 left-0 -translate-x-1/2 translate-y-1/2",
"bottom-0 right-0 translate-x-1/2 translate-y-1/2",
"top-0 left-1/2 -translate-x-1/2 -translate-y-1/2",
"bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2",
"top-1/2 left-0 -translate-x-1/2 -translate-y-1/2",
"top-1/2 right-0 translate-x-1/2 -translate-y-1/2",
].map((pos, hi) => (
))}
{/* Value */}
{stat.value}
{/* Label */}
{stat.label}
))}
)}
);
}
// ============================================================================
// ANIMATED BAR CHART — used in Industries section
// ============================================================================
const yAxisLabels = [0, 10, 20, 30, 40, 50];
const CHART_HEIGHT = 200;
const Y_LABEL_W = 36;
function AnimatedBarChart({
data,
maxPercentage,
isInView,
}: {
data: typeof clientBreakdown;
maxPercentage: number;
isInView: boolean;
}) {
const [animKey, setAnimKey] = useState(0);
const chartRef = useRef(null);
// Use a local IntersectionObserver to pause/resume the replay interval
// when the chart scrolls out of view (isInView from parent is once:true
// and never resets, so the interval would run forever otherwise)
useEffect(() => {
if (!isInView) return;
const el = chartRef.current;
if (!el) return;
let interval: ReturnType | null = null;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
interval = setInterval(() => {
setAnimKey((k) => k + 1);
}, 5000);
} else if (interval) {
clearInterval(interval);
interval = null;
}
},
{ threshold: 0.1 }
);
observer.observe(el);
return () => {
observer.disconnect();
if (interval) clearInterval(interval);
};
}, [isInView]);
return (
`${d.label.replace('\n', ' ')} ${d.percentage}%`).join(', ')}`}>
{/* Chart area wrapper */}
{/* Plot area — fixed height, all elements positioned relative to this */}
{/* Grid lines — positioned absolutely within plot area */}
{yAxisLabels.map((val) => {
const bottomPx = (val / 50) * CHART_HEIGHT;
return (
{/* Y-axis label — positioned to the left of the plot */}
{val}%
{/* Horizontal grid line */}
);
})}
{/* Bars — aligned exactly to the bottom edge (0-line) */}
{data.map((client, i) => {
const barH = (client.percentage / 50) * CHART_HEIGHT;
return (
{/* Percentage label above bar */}
{client.percentage}%
{/* Bar — grows upward from the 0-line */}
{/* Shimmer / glow effect */}
);
})}
{/* X-axis labels — below chart, aligned with bars */}
{data.map((client) => (
{client.label}
))}
);
}
// ============================================================================
// 5. INDUSTRIES
// ============================================================================
function IndustriesSection() {
const maxPercentage = Math.max(...clientBreakdown.map((c) => c.percentage));
return (
{(isInView) => (
{/* Left — Title + Industry List */}
Industries
{industries.map((industry, i) => (
/
{industry}
))}
{/* Right — Our Clients Bar Chart */}
Our clients
)}
);
}
// ============================================================================
// 6. WHY WORK WITH FINJINEERS
// ============================================================================
function WhyWorkWithUsSection() {
return (
{(isInView) => (
{/* Left — Heading (spans 2 cols) */}
Why Work
With Finjineers
{/* Right — Feature Cards (spans 3 cols) */}
{whyWorkWithUs.map((item, i) => (
{String(i + 1).padStart(2, "0")}
{item.title}
{item.description}
))}
)}
);
}
// ============================================================================
// 7. TESTIMONIALS — Clients Are Saying
// ============================================================================
function TestimonialsSection() {
const [current, setCurrent] = useState(0);
const [direction, setDirection] = useState(1);
const [isPlaying, setIsPlaying] = useState(false);
const goTo = (index: number) => {
setDirection(index > current ? 1 : -1);
setCurrent(index);
setIsPlaying(false);
};
const slideVariants = {
enter: (d: number) => ({ opacity: 0, x: d > 0 ? 60 : -60 }),
center: { opacity: 1, x: 0 },
exit: (d: number) => ({ opacity: 0, x: d > 0 ? -60 : 60 }),
};
const t = testimonials[current];
return (
{(isInView) => (
{/* Ticket wrapper */}
{/* Top perforation dots */}
{Array.from({ length: 24 }).map((_, i) => (
))}
{/* Left — Quote Panel (animated) */}
{/* Large faded slide number */}
{String(current + 1).padStart(2, "0")}
{/* Section title */}
Clients
Are Saying
{/* Quote */}
“{t.quote}”
{/* Author */}
{/* Pagination — outside AnimatePresence so it stays fixed */}
{testimonials.length > 1 && (
{testimonials.map((_, i) => (
goTo(i)}
aria-label={`Go to testimonial ${i + 1}`}
aria-current={i === current ? "true" : undefined}
className={`w-8 h-8 rounded-full text-[13px] font-medium transition-all duration-300 flex items-center justify-center ${
i === current
? "bg-[#f15f3d] text-white"
: "bg-white/[0.06] text-gray-400 hover:bg-white/[0.1]"
}`}
style={{ fontFamily: typography.fonts.body }}
>
{i + 1}
))}
)}
{/* Right ��� Video Panel */}
{isPlaying ? (
VIDEO
) : (
<>
{/* Clickable overlay */}
setIsPlaying(true)}
className="absolute inset-0 w-full h-full cursor-pointer z-10 group"
aria-label="Play testimonial video"
>
Watch Video
>
)}
{/* Bottom perforation dots */}
{Array.from({ length: 24 }).map((_, i) => (
))}
)}
);
}
// ============================================================================
// 8. AWARDS
// ============================================================================
/* Award badge SVG shapes */
function HexagonBadge({ children }: { children: React.ReactNode }) {
return (
);
}
function ShieldBadge({ children }: { children: React.ReactNode }) {
return (
);
}
function OvalBadge({ children }: { children: React.ReactNode }) {
return (
);
}
function CircleBadge({ children }: { children: React.ReactNode }) {
return (
);
}
// ============================================================================
// 9. JOIN OUR TEAM — Large Banner
// ============================================================================
function JoinTeamSection() {
return (
{(isInView) => (
<>
{/* Banner */}
{/* Background Image */}
{/* Overlay */}
{/* Content */}
{/* Badge */}
We're hiring
Finjineers
Join a team of world-class engineers, designers, and product
thinkers building the next generation of mobile experiences.
View open positions
>
)}
);
}
// ============================================================================
// 10. SEND YOUR CV CTA
// ============================================================================
/* Corner selection handle used in the Send CV section */
function CornerHandle({ className }: { className?: string }) {
return (
);
}
function SendCVSection() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: "-80px" });
const [formData, setFormData] = useState({ name: "", email: "", message: "" });
const [fileName, setFileName] = useState("");
const fileInputRef = useRef(null);
const handleChange = (e: React.ChangeEvent) => {
setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const handleFileClick = () => fileInputRef.current?.click();
const handleFileChange = (e: React.ChangeEvent) => {
const file = e.target.files?.[0];
if (!file) return;
// Security: limit file size to 10MB
if (file.size > 10 * 1024 * 1024) {
e.target.value = "";
return;
}
setFileName(file.name);
};
return (
{/* ── Left column: Banner + tagline ── */}
{/* Banner flag */}
{/* Flag pole line */}
{/* Flag body */}
{/* Orange banner shape */}
{/* Corner handles on the banner */}
{/* Inner border line */}
Send Your CV
{/* Tagline below banner */}
Become A Part Of
The Finjineers Team!
{/* ── Right column: Form card ── */}
{/* Dashed border container with corner handles */}
{/* Corner handles */}
{/* Form */}
);
}
// 12. FINAL CTA — now uses shared FinalCTASection from @/app/components/shared/FinalCTASection