diff --git a/apps/portal/src/components/molecules/ServiceCard/ServiceCard.stories.tsx b/apps/portal/src/components/molecules/ServiceCard/ServiceCard.stories.tsx index 9d0df430..284b3386 100644 --- a/apps/portal/src/components/molecules/ServiceCard/ServiceCard.stories.tsx +++ b/apps/portal/src/components/molecules/ServiceCard/ServiceCard.stories.tsx @@ -17,7 +17,7 @@ const meta: Meta = { }, accentColor: { control: "select", - options: ["blue", "green", "purple", "orange", "cyan", "pink", "amber", "rose"], + options: ["blue", "emerald", "violet", "amber", "slate"], }, }, }; @@ -107,26 +107,26 @@ export const BentoGrid: Story = { icon={} title="Mobile" description="SIM & eSIM plans" - accentColor="green" + accentColor="emerald" /> } title="VPN" description="Secure browsing" - accentColor="purple" + accentColor="violet" /> } title="Hosting" - accentColor="orange" + accentColor="amber" /> } title="WiFi Router" - accentColor="cyan" + accentColor="slate" /> ), @@ -135,17 +135,15 @@ export const BentoGrid: Story = { export const AccentColors: Story = { render: () => (
- {(["blue", "green", "purple", "orange", "cyan", "pink", "amber", "rose"] as const).map( - color => ( - } - title={color.charAt(0).toUpperCase() + color.slice(1)} - description={`${color} accent`} - accentColor={color} - /> - ) - )} + {(["blue", "emerald", "violet", "amber", "slate"] as const).map(color => ( + } + title={color.charAt(0).toUpperCase() + color.slice(1)} + description={`${color} accent`} + accentColor={color} + /> + ))}
), }; diff --git a/apps/portal/src/components/molecules/ServiceCard/ServiceCard.tsx b/apps/portal/src/components/molecules/ServiceCard/ServiceCard.tsx index 48c56dbe..cb12e2ea 100644 --- a/apps/portal/src/components/molecules/ServiceCard/ServiceCard.tsx +++ b/apps/portal/src/components/molecules/ServiceCard/ServiceCard.tsx @@ -7,15 +7,7 @@ import React from "react"; /** * Accent color options for ServiceCard */ -export type ServiceCardAccentColor = - | "blue" - | "green" - | "purple" - | "orange" - | "cyan" - | "pink" - | "amber" - | "rose"; +export type ServiceCardAccentColor = "blue" | "emerald" | "violet" | "amber" | "slate"; /** * Variant options for ServiceCard @@ -86,45 +78,21 @@ const accentColorStyles: Record< hoverBorder: "hover:border-blue-500/40", cardBg: "from-blue-500/10 via-card to-card", }, - green: { - bg: "bg-green-500/10", - text: "text-green-600 dark:text-green-400", - border: "border-green-500/20", - gradient: "from-green-500/30 to-transparent", - hoverBorder: "hover:border-green-500/40", - cardBg: "from-green-500/10 via-card to-card", + emerald: { + bg: "bg-emerald-500/10", + text: "text-emerald-600 dark:text-emerald-400", + border: "border-emerald-500/20", + gradient: "from-emerald-500/30 to-transparent", + hoverBorder: "hover:border-emerald-500/40", + cardBg: "from-emerald-500/10 via-card to-card", }, - purple: { - bg: "bg-purple-500/10", - text: "text-purple-600 dark:text-purple-400", - border: "border-purple-500/20", - gradient: "from-purple-500/30 to-transparent", - hoverBorder: "hover:border-purple-500/40", - cardBg: "from-purple-500/10 via-card to-card", - }, - orange: { - bg: "bg-orange-500/10", - text: "text-orange-600 dark:text-orange-400", - border: "border-orange-500/20", - gradient: "from-orange-500/30 to-transparent", - hoverBorder: "hover:border-orange-500/40", - cardBg: "from-orange-500/10 via-card to-card", - }, - cyan: { - bg: "bg-cyan-500/10", - text: "text-cyan-600 dark:text-cyan-400", - border: "border-cyan-500/20", - gradient: "from-cyan-500/30 to-transparent", - hoverBorder: "hover:border-cyan-500/40", - cardBg: "from-cyan-500/10 via-card to-card", - }, - pink: { - bg: "bg-pink-500/10", - text: "text-pink-600 dark:text-pink-400", - border: "border-pink-500/20", - gradient: "from-pink-500/30 to-transparent", - hoverBorder: "hover:border-pink-500/40", - cardBg: "from-pink-500/10 via-card to-card", + violet: { + bg: "bg-violet-500/10", + text: "text-violet-600 dark:text-violet-400", + border: "border-violet-500/20", + gradient: "from-violet-500/30 to-transparent", + hoverBorder: "hover:border-violet-500/40", + cardBg: "from-violet-500/10 via-card to-card", }, amber: { bg: "bg-amber-500/10", @@ -134,13 +102,13 @@ const accentColorStyles: Record< hoverBorder: "hover:border-amber-500/40", cardBg: "from-amber-500/10 via-card to-card", }, - rose: { - bg: "bg-rose-500/10", - text: "text-rose-600 dark:text-rose-400", - border: "border-rose-500/20", - gradient: "from-rose-500/30 to-transparent", - hoverBorder: "hover:border-rose-500/40", - cardBg: "from-rose-500/10 via-card to-card", + slate: { + bg: "bg-slate-500/10", + text: "text-slate-600 dark:text-slate-400", + border: "border-slate-500/20", + gradient: "from-slate-500/30 to-transparent", + hoverBorder: "hover:border-slate-500/40", + cardBg: "from-slate-500/10 via-card to-card", }, }; diff --git a/apps/portal/src/features/landing-page/components/HeroSection.tsx b/apps/portal/src/features/landing-page/components/HeroSection.tsx index 9e87ff39..cf54f505 100644 --- a/apps/portal/src/features/landing-page/components/HeroSection.tsx +++ b/apps/portal/src/features/landing-page/components/HeroSection.tsx @@ -1,13 +1,243 @@ "use client"; import { useRef } from "react"; -import { LayoutGroup, motion, useInView } from "framer-motion"; +import { LayoutGroup, motion, useInView, useScroll, useTransform } from "framer-motion"; import { ArrowRight } from "lucide-react"; import { Button } from "@/components/atoms/button"; import TextRotate from "@/components/fancy/text/text-rotate"; const SERVICE_WORDS = ["Internet", "Phone Plans", "VPN", "IT Support", "Business"]; +/* ------------------------------------------------------------------ */ +/* Placeholder silhouettes — replace with final illustrations later */ +/* ------------------------------------------------------------------ */ + +function HouseSilhouette({ className }: { className?: string }) { + return ( + + ); +} + +function PersonWithPhoneSilhouette({ className }: { className?: string }) { + return ( + + ); +} + +function PersonWithRouterSilhouette({ className }: { className?: string }) { + return ( + + ); +} + +/* ------------------------------------------------------------------ */ +/* Floating wrapper — ambient bob + scroll parallax */ +/* ------------------------------------------------------------------ */ + +function FloatingIllustration({ + children, + floatDelay, + floatDuration, + floatDistance, + parallaxRange, + scrollProgress, + className, +}: { + children: React.ReactNode; + floatDelay: number; + floatDuration: number; + floatDistance: number; + parallaxRange: [number, number]; + scrollProgress: import("framer-motion").MotionValue; + className?: string; +}) { + const parallaxY = useTransform(scrollProgress, [0, 1], parallaxRange); + + return ( + + {children} + + ); +} + +/* ------------------------------------------------------------------ */ +/* Hero Section */ +/* ------------------------------------------------------------------ */ + interface HeroSectionProps { heroCTARef: React.RefObject; } @@ -16,6 +246,11 @@ export function HeroSection({ heroCTARef }: HeroSectionProps) { const heroRef = useRef(null); const heroInView = useInView(heroRef, { once: true, amount: 0.1 }); + const { scrollYProgress } = useScroll({ + target: heroRef, + offset: ["start start", "end start"], + }); + return ( -
- -

- Seamless IT Solutions - - for - + {/* Left — Text Content */} +
+ +

+ Seamless IT Solutions + - -

-
-

- From connectivity to communication, we handle the complexity so you can focus on what - matters — with dedicated English support across Japan. -

-
-

+
+

+ From connectivity to communication, we handle the complexity so you can focus on what + matters — with dedicated English support across Japan. +

+
- Find Your Plan - - + + +
+
+ + {/* Right — Floating Illustrations */} +
+ {/* House — center back, largest */} + + + + + {/* Person with phone — left front */} + + + + + {/* Person with router — right front */} + + + + + {/* Decorative floating dots */} + + +
diff --git a/apps/portal/src/features/landing-page/components/ServicesCarousel.tsx b/apps/portal/src/features/landing-page/components/ServicesCarousel.tsx index 1e1d317c..18c9fb52 100644 --- a/apps/portal/src/features/landing-page/components/ServicesCarousel.tsx +++ b/apps/portal/src/features/landing-page/components/ServicesCarousel.tsx @@ -13,96 +13,25 @@ import { type ConversionServiceCard, type CarouselAccent, } from "@/features/landing-page/data"; +import { SERVICE_COLORS, type ServiceColorId } from "@/shared/constants/service-colors"; type Tab = "personal" | "business"; -/* ─── Accent color system ─── */ +/* ─── Accent color system (derived from canonical SERVICE_COLORS) ─── */ -interface AccentStyles { - iconBg: string; - iconText: string; - ctaBg: string; - dotBg: string; - border: string; - glowFrom: string; - cssVar: string; -} - -const ACCENTS: Record = { - blue: { - iconBg: "bg-blue-500/12", - iconText: "text-blue-600", - ctaBg: "bg-blue-600 hover:bg-blue-700", - dotBg: "bg-blue-600", - border: "border-blue-500/20", - glowFrom: "from-blue-500/5", - cssVar: "var(--color-blue-500)", - }, - emerald: { - iconBg: "bg-emerald-500/12", - iconText: "text-emerald-600", - ctaBg: "bg-emerald-600 hover:bg-emerald-700", - dotBg: "bg-emerald-600", - border: "border-emerald-500/20", - glowFrom: "from-emerald-500/5", - cssVar: "var(--color-emerald-500)", - }, - violet: { - iconBg: "bg-violet-500/12", - iconText: "text-violet-600", - ctaBg: "bg-violet-600 hover:bg-violet-700", - dotBg: "bg-violet-600", - border: "border-violet-500/20", - glowFrom: "from-violet-500/5", - cssVar: "var(--color-violet-500)", - }, - amber: { - iconBg: "bg-amber-500/12", - iconText: "text-amber-600", - ctaBg: "bg-amber-600 hover:bg-amber-700", - dotBg: "bg-amber-600", - border: "border-amber-500/20", - glowFrom: "from-amber-500/5", - cssVar: "var(--color-amber-500)", - }, - indigo: { - iconBg: "bg-indigo-500/12", - iconText: "text-indigo-600", - ctaBg: "bg-indigo-600 hover:bg-indigo-700", - dotBg: "bg-indigo-600", - border: "border-indigo-500/20", - glowFrom: "from-indigo-500/5", - cssVar: "var(--color-indigo-500)", - }, - cyan: { - iconBg: "bg-cyan-500/12", - iconText: "text-cyan-600", - ctaBg: "bg-cyan-600 hover:bg-cyan-700", - dotBg: "bg-cyan-600", - border: "border-cyan-500/20", - glowFrom: "from-cyan-500/5", - cssVar: "var(--color-cyan-500)", - }, - rose: { - iconBg: "bg-rose-500/12", - iconText: "text-rose-600", - ctaBg: "bg-rose-600 hover:bg-rose-700", - dotBg: "bg-rose-600", - border: "border-rose-500/20", - glowFrom: "from-rose-500/5", - cssVar: "var(--color-rose-500)", - }, - slate: { - iconBg: "bg-slate-500/12", - iconText: "text-slate-600", - ctaBg: "bg-slate-600 hover:bg-slate-700", - dotBg: "bg-slate-600", - border: "border-slate-500/20", - glowFrom: "from-slate-500/5", - cssVar: "var(--color-slate-500)", - }, +/** Map carousel accent names to canonical service IDs */ +const ACCENT_SERVICE: Record = { + blue: "internet", + emerald: "sim", + violet: "vpn", + amber: "onsite", + slate: "business", }; +function getAccent(accent: CarouselAccent) { + return SERVICE_COLORS[ACCENT_SERVICE[accent]]; +} + /* ─── Framer Motion variants ─── */ const tabContentVariants = { @@ -126,7 +55,7 @@ const ServiceCard = memo(function ServiceCard({ card: ConversionServiceCard; wasDragging: () => boolean; }) { - const a = ACCENTS[card.accent]; + const a = getAccent(card.accent); return (
{cards.map((card, i) => { - const styles = ACCENTS[card.accent]; + const styles = getAccent(card.accent); return (