# Landing Page Conversion Overhaul — Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Restructure the public landing page into a conversion funnel that drives visitors to `/services`, with tabbed services carousel, support downloads, and full contact section restored. **Architecture:** Replace static ServicesGrid with tabbed carousel (For You / For Business) using conversion-oriented cards with pricing. Restore full contact section (form + map + phone + address) at page bottom. Repurpose CTA Banner. Delete `/help` route. Update footer links. **Tech Stack:** Next.js 15, React 19, Tailwind CSS, lucide-react icons, existing `ContactForm` component **Design doc:** `docs/plans/2026-03-04-public-pages-restructuring-v2-design.md` --- ### Task 1: Extend Service Data with Conversion Card Fields **Files:** - Modify: `apps/portal/src/features/landing-page/data/services.tsx` **Step 1: Add conversion card types and data** Add a new `ConversionServiceCard` interface and update the `personalServices` and `businessServices` arrays with conversion fields. Keep existing exports intact for backward compatibility (other pages may use them). Add after the existing `ServiceItem` interface (~line 24): ```tsx export interface ConversionServiceCard { title: string; problemHook: string; keyBenefit: string; priceFrom?: string; badge?: string; icon: React.ReactNode; href: string; ctaLabel: string; } ``` Add new conversion data arrays (after `businessServices`, before `services`): ```tsx export const personalConversionCards: ConversionServiceCard[] = [ { title: "Internet Plans", problemHook: "Need reliable internet?", keyBenefit: "NTT Fiber up to 10Gbps", priceFrom: "¥3,200/mo", icon: , href: "/services/internet", ctaLabel: "View Plans", }, { title: "Phone Plans", problemHook: "Need a SIM card?", keyBenefit: "Docomo network coverage", priceFrom: "¥1,100/mo", badge: "1st month free", icon: , href: "/services/sim", ctaLabel: "View Plans", }, { title: "VPN Service", problemHook: "Missing shows from home?", keyBenefit: "Stream US & UK content", priceFrom: "¥2,500/mo", icon: , href: "/services/vpn", ctaLabel: "View Plans", }, { title: "Onsite Support", problemHook: "Need hands-on help?", keyBenefit: "English-speaking technicians", icon: , href: "/services/onsite", ctaLabel: "Learn More", }, ]; export const businessConversionCards: ConversionServiceCard[] = [ { title: "Office LAN Setup", problemHook: "Setting up an office?", keyBenefit: "Complete network infrastructure", icon: , href: "/services/business", ctaLabel: "Get a Quote", }, { title: "Tech Support", problemHook: "Need ongoing IT help?", keyBenefit: "Onsite & remote support", icon: , href: "/services/onsite", ctaLabel: "Get a Quote", }, { title: "Dedicated Internet", problemHook: "Need guaranteed bandwidth?", keyBenefit: "Enterprise-grade connectivity", icon: , href: "/services/business", ctaLabel: "Get a Quote", }, { title: "Data Center", problemHook: "Need hosting in Japan?", keyBenefit: "Secure, reliable infrastructure", icon: , href: "/services/business", ctaLabel: "Get a Quote", }, { title: "Website Services", problemHook: "Need a web presence?", keyBenefit: "Construction & maintenance", icon: , href: "/services/business", ctaLabel: "Get a Quote", }, ]; ``` **Step 2: Verify no lint errors** Run: `pnpm lint --filter @customer-portal/portal -- --no-warn` **Step 3: Commit** ``` feat: add conversion card data for landing page services carousel ``` --- ### Task 2: Create ServicesCarousel Component **Files:** - Create: `apps/portal/src/features/landing-page/components/ServicesCarousel.tsx` **Step 1: Create the tabbed carousel component** This component has: - Tab switcher ("For You" / "For Business") - Horizontal scrolling carousel with auto-scroll - Conversion-oriented service cards with problem hook, benefit, price, badge, CTA - Prev/next navigation buttons ```tsx "use client"; import { useCallback, useEffect, useRef, useState } from "react"; import Link from "next/link"; import { ArrowRight, ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/shared/utils"; import { useInView } from "@/features/landing-page/hooks"; import { personalConversionCards, businessConversionCards, type ConversionServiceCard, } from "@/features/landing-page/data"; type Tab = "personal" | "business"; function ServiceConversionCard({ card }: { card: ConversionServiceCard }) { return (
{card.badge && ( {card.badge} )}
{card.icon}

{card.problemHook}

{card.title}

{card.keyBenefit}

{card.priceFrom && (

from {card.priceFrom}

)} {card.ctaLabel}
); } export function ServicesCarousel() { const [activeTab, setActiveTab] = useState("personal"); const carouselRef = useRef(null); const itemWidthRef = useRef(0); const isScrollingRef = useRef(false); const autoScrollTimerRef = useRef | null>(null); const [sectionRef, isInView] = useInView(); const cards = activeTab === "personal" ? personalConversionCards : businessConversionCards; const computeItemWidth = useCallback(() => { const container = carouselRef.current; if (!container) return; const card = container.querySelector("[data-service-card]"); if (!card) return; const style = getComputedStyle(container); const gap = parseFloat(style.columnGap || style.gap || "0") || 24; itemWidthRef.current = card.getBoundingClientRect().width + gap; }, []); const scrollByOne = useCallback((direction: 1 | -1) => { const container = carouselRef.current; if (!container || !itemWidthRef.current || isScrollingRef.current) return; isScrollingRef.current = true; container.scrollBy({ left: direction * itemWidthRef.current, behavior: "smooth" }); setTimeout(() => { isScrollingRef.current = false; }, 500); }, []); const startAutoScroll = useCallback(() => { if (autoScrollTimerRef.current) clearInterval(autoScrollTimerRef.current); autoScrollTimerRef.current = setInterval(() => scrollByOne(1), 5000); }, [scrollByOne]); const stopAutoScroll = useCallback(() => { if (autoScrollTimerRef.current) { clearInterval(autoScrollTimerRef.current); autoScrollTimerRef.current = null; } }, []); useEffect(() => { computeItemWidth(); window.addEventListener("resize", computeItemWidth); startAutoScroll(); return () => { window.removeEventListener("resize", computeItemWidth); stopAutoScroll(); }; }, [computeItemWidth, startAutoScroll, stopAutoScroll]); // Reset scroll position when tab changes useEffect(() => { if (carouselRef.current) { carouselRef.current.scrollTo({ left: 0, behavior: "smooth" }); } computeItemWidth(); }, [activeTab, computeItemWidth]); const handlePrev = useCallback(() => { scrollByOne(-1); startAutoScroll(); }, [scrollByOne, startAutoScroll]); const handleNext = useCallback(() => { scrollByOne(1); startAutoScroll(); }, [scrollByOne, startAutoScroll]); return (
} className={cn( "relative left-1/2 right-1/2 w-screen -translate-x-1/2 bg-surface-sunken/30 py-14 sm:py-16 transition-all duration-700", isInView ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8" )} >
{/* Header + Tabs */}

Our Services

Everything you need to stay connected in Japan

{/* Carousel */}
{cards.map(card => ( ))}
{/* Navigation buttons */}
); } ``` **Step 2: Verify no lint errors** Run: `pnpm lint --filter @customer-portal/portal -- --no-warn` **Step 3: Commit** ``` feat: create ServicesCarousel with tabbed conversion cards ``` --- ### Task 3: Create SupportDownloadsSection Component **Files:** - Create: `apps/portal/src/features/landing-page/components/SupportDownloadsSection.tsx` **Step 1: Create the component** Extracted from old landing page and from the support page pattern. Uses existing `supportDownloads` data. ```tsx "use client"; import Image from "next/image"; import { Download } from "lucide-react"; import { cn } from "@/shared/utils"; import { useInView } from "@/features/landing-page/hooks"; import { supportDownloads } from "@/features/landing-page/data"; export function SupportDownloadsSection() { const [ref, isInView] = useInView(); return (
} className={cn( "py-14 sm:py-16 transition-all duration-700", isInView ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8" )} >

Remote Support

Download one of these tools so our technicians can assist you remotely.

{supportDownloads.map(tool => (
{tool.title}

{tool.title}

{tool.description}

{tool.useCase}

))}
); } ``` **Step 2: Verify no lint errors** Run: `pnpm lint --filter @customer-portal/portal -- --no-warn` **Step 3: Commit** ``` feat: create SupportDownloadsSection component for landing page ``` --- ### Task 4: Create ContactSection Component **Files:** - Create: `apps/portal/src/features/landing-page/components/ContactSection.tsx` **Step 1: Create the full contact section** Restored from the old landing page layout — form on the left with chat/phone info, map + address on the right. Uses the existing `ContactForm` component. ```tsx "use client"; import { Mail, MapPin, MessageSquare, PhoneCall, Train } from "lucide-react"; import { cn } from "@/shared/utils"; import { useInView } from "@/features/landing-page/hooks"; import { ContactForm } from "@/features/support/components"; export function ContactSection() { const [ref, isInView] = useInView(); return (
} className={cn( "relative left-1/2 right-1/2 w-screen -translate-x-1/2 bg-surface-sunken/30 py-14 sm:py-16 transition-all duration-700", isInView ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8" )} >

Tell Us What You Need

{/* Left: Form + Contact Methods */}
By Online Form (Anytime)
By Chat (Anytime)

Click the bottom right “Chat Button” to reach our team anytime.

By Phone (9:30-18:00 JST)

Toll Free within Japan

0120-660-470

From Overseas

+81-3-3560-1006

{/* Right: Map + Address */}