# 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: <Wifi className="h-7 w-7" />,
    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: <Smartphone className="h-7 w-7" />,
    href: "/services/sim",
    ctaLabel: "View Plans",
  },
  {
    title: "VPN Service",
    problemHook: "Missing shows from home?",
    keyBenefit: "Stream US & UK content",
    priceFrom: "¥2,500/mo",
    icon: <Lock className="h-7 w-7" />,
    href: "/services/vpn",
    ctaLabel: "View Plans",
  },
  {
    title: "Onsite Support",
    problemHook: "Need hands-on help?",
    keyBenefit: "English-speaking technicians",
    icon: <Wrench className="h-7 w-7" />,
    href: "/services/onsite",
    ctaLabel: "Learn More",
  },
];

export const businessConversionCards: ConversionServiceCard[] = [
  {
    title: "Office LAN Setup",
    problemHook: "Setting up an office?",
    keyBenefit: "Complete network infrastructure",
    icon: <Server className="h-7 w-7" />,
    href: "/services/business",
    ctaLabel: "Get a Quote",
  },
  {
    title: "Tech Support",
    problemHook: "Need ongoing IT help?",
    keyBenefit: "Onsite & remote support",
    icon: <Wrench className="h-7 w-7" />,
    href: "/services/onsite",
    ctaLabel: "Get a Quote",
  },
  {
    title: "Dedicated Internet",
    problemHook: "Need guaranteed bandwidth?",
    keyBenefit: "Enterprise-grade connectivity",
    icon: <Building2 className="h-7 w-7" />,
    href: "/services/business",
    ctaLabel: "Get a Quote",
  },
  {
    title: "Data Center",
    problemHook: "Need hosting in Japan?",
    keyBenefit: "Secure, reliable infrastructure",
    icon: <Shield className="h-7 w-7" />,
    href: "/services/business",
    ctaLabel: "Get a Quote",
  },
  {
    title: "Website Services",
    problemHook: "Need a web presence?",
    keyBenefit: "Construction & maintenance",
    icon: <Code className="h-7 w-7" />,
    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 (
    <Link href={card.href} className="group flex-shrink-0 w-[260px] sm:w-[280px] snap-start">
      <article
        data-service-card
        className="h-full rounded-2xl bg-card border border-border/60 px-6 py-7 shadow-sm hover:shadow-md hover:border-primary/30 transition-all duration-300 group-hover:-translate-y-1 flex flex-col"
      >
        {card.badge && (
          <span className="inline-flex self-start items-center rounded-full bg-success/10 text-success px-2.5 py-0.5 text-xs font-semibold mb-3">
            {card.badge}
          </span>
        )}
        <div className="mb-4 text-primary">{card.icon}</div>
        <p className="text-sm text-muted-foreground mb-1">{card.problemHook}</p>
        <h3 className="text-lg font-bold text-foreground mb-1">{card.title}</h3>
        <p className="text-sm text-muted-foreground mb-3 flex-grow">{card.keyBenefit}</p>
        {card.priceFrom && (
          <p className="text-lg font-bold text-primary mb-4">from {card.priceFrom}</p>
        )}
        <span className="inline-flex items-center gap-1.5 text-sm font-semibold text-primary group-hover:gap-2.5 transition-all mt-auto">
          {card.ctaLabel}
          <ArrowRight className="h-4 w-4" />
        </span>
      </article>
    </Link>
  );
}

export function ServicesCarousel() {
  const [activeTab, setActiveTab] = useState<Tab>("personal");
  const carouselRef = useRef<HTMLDivElement>(null);
  const itemWidthRef = useRef(0);
  const isScrollingRef = useRef(false);
  const autoScrollTimerRef = useRef<ReturnType<typeof setInterval> | 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<HTMLElement>("[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 (
    <section
      ref={sectionRef as React.RefObject<HTMLElement>}
      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"
      )}
    >
      <div className="mx-auto max-w-6xl px-6 sm:px-10 lg:px-14">
        {/* Header + Tabs */}
        <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
          <div>
            <h2 className="text-2xl sm:text-3xl font-extrabold text-foreground">Our Services</h2>
            <p className="mt-1 text-base text-muted-foreground">
              Everything you need to stay connected in Japan
            </p>
          </div>
          <div className="flex bg-muted rounded-full p-1 self-start">
            <button
              type="button"
              onClick={() => setActiveTab("personal")}
              className={cn(
                "px-4 py-2 text-sm font-semibold rounded-full transition-all",
                activeTab === "personal"
                  ? "bg-foreground text-background shadow-sm"
                  : "text-muted-foreground hover:text-foreground"
              )}
            >
              For You
            </button>
            <button
              type="button"
              onClick={() => setActiveTab("business")}
              className={cn(
                "px-4 py-2 text-sm font-semibold rounded-full transition-all",
                activeTab === "business"
                  ? "bg-foreground text-background shadow-sm"
                  : "text-muted-foreground hover:text-foreground"
              )}
            >
              For Business
            </button>
          </div>
        </div>

        {/* Carousel */}
        <div className="relative" onMouseEnter={stopAutoScroll} onMouseLeave={startAutoScroll}>
          <div
            ref={carouselRef}
            className="flex gap-5 overflow-x-auto scroll-smooth pb-4 snap-x snap-mandatory"
            style={{ scrollbarWidth: "none", WebkitOverflowScrolling: "touch" }}
          >
            {cards.map(card => (
              <ServiceConversionCard key={card.title} card={card} />
            ))}
          </div>

          {/* Navigation buttons */}
          <div className="flex justify-end gap-2 mt-4">
            <button
              type="button"
              aria-label="Scroll left"
              onClick={handlePrev}
              className="h-9 w-9 rounded-full border border-border bg-card text-foreground shadow-sm hover:bg-muted transition-colors flex items-center justify-center"
            >
              <ChevronLeft className="h-4 w-4" />
            </button>
            <button
              type="button"
              aria-label="Scroll right"
              onClick={handleNext}
              className="h-9 w-9 rounded-full border border-border bg-card text-foreground shadow-sm hover:bg-muted transition-colors flex items-center justify-center"
            >
              <ChevronRight className="h-4 w-4" />
            </button>
          </div>
        </div>
      </div>
    </section>
  );
}
```

**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 (
    <section
      ref={ref as React.RefObject<HTMLElement>}
      className={cn(
        "py-14 sm:py-16 transition-all duration-700",
        isInView ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"
      )}
    >
      <div className="mx-auto max-w-5xl px-6 sm:px-10 lg:px-14">
        <h2 className="text-center text-2xl sm:text-3xl font-extrabold text-foreground tracking-tight mb-2">
          Remote Support
        </h2>
        <p className="text-center text-muted-foreground mb-8">
          Download one of these tools so our technicians can assist you remotely.
        </p>
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
          {supportDownloads.map(tool => (
            <a
              key={tool.title}
              href={tool.href}
              target="_blank"
              rel="noopener noreferrer"
              className="group bg-card rounded-2xl border border-border/60 p-6 hover:border-primary/40 hover:shadow-md transition-all duration-200"
            >
              <div className="flex items-start gap-5">
                <div className="w-16 h-16 rounded-xl bg-muted/30 flex items-center justify-center shrink-0 overflow-hidden">
                  <Image
                    src={tool.image}
                    alt={tool.title}
                    width={48}
                    height={48}
                    className="object-contain"
                  />
                </div>
                <div className="flex-1 min-w-0">
                  <div className="flex items-center gap-2 mb-1">
                    <h3 className="font-bold text-foreground group-hover:text-primary transition-colors">
                      {tool.title}
                    </h3>
                    <Download className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" />
                  </div>
                  <p className="text-sm text-muted-foreground leading-relaxed mb-2">
                    {tool.description}
                  </p>
                  <p className="text-xs font-medium text-primary">{tool.useCase}</p>
                </div>
              </div>
            </a>
          ))}
        </div>
      </div>
    </section>
  );
}
```

**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 (
    <section
      id="contact"
      ref={ref as React.RefObject<HTMLElement>}
      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"
      )}
    >
      <div className="max-w-6xl mx-auto px-6 sm:px-10 lg:px-14 space-y-6">
        <h2 className="text-2xl sm:text-3xl font-extrabold text-foreground">
          Tell Us What You Need
        </h2>
        <div className="rounded-2xl bg-card border border-border/60 shadow-sm p-6 sm:p-8">
          <div className="grid grid-cols-1 lg:grid-cols-[1.1fr_0.9fr] gap-10 lg:gap-12">
            {/* Left: Form + Contact Methods */}
            <div className="space-y-6">
              <div className="flex items-center gap-2 text-primary font-bold text-lg">
                <Mail className="h-5 w-5" />
                <span>By Online Form (Anytime)</span>
              </div>
              <ContactForm className="border-0 p-0 rounded-none bg-transparent" />

              <div className="flex flex-col gap-3 pt-2">
                <div className="inline-flex items-center gap-2 text-primary font-semibold">
                  <MessageSquare className="h-5 w-5" />
                  <span>By Chat (Anytime)</span>
                </div>
                <p className="text-sm text-muted-foreground">
                  Click the bottom right &ldquo;Chat Button&rdquo; to reach our team anytime.
                </p>
                <div className="inline-flex items-center gap-2 text-primary font-semibold">
                  <PhoneCall className="h-5 w-5" />
                  <span>By Phone (9:30-18:00 JST)</span>
                </div>
                <div className="text-sm text-muted-foreground">
                  <p className="font-semibold text-foreground">Toll Free within Japan</p>
                  <p className="text-lg font-bold text-primary">0120-660-470</p>
                  <p className="font-semibold text-foreground mt-1">From Overseas</p>
                  <p className="text-lg font-bold text-primary">+81-3-3560-1006</p>
                </div>
              </div>
            </div>

            {/* Right: Map + Address */}
            <div className="space-y-6">
              <div className="w-full rounded-2xl overflow-hidden shadow-md border border-border/60 bg-card aspect-[4/3]">
                <iframe
                  title="Assist Solutions Corp Map"
                  src="https://www.google.com/maps?q=Assist+Solutions+Corp,+3-8-2+Higashi+Azabu,+Minato-ku,+Tokyo&output=embed"
                  className="w-full h-full"
                  loading="lazy"
                  allowFullScreen
                  referrerPolicy="no-referrer-when-downgrade"
                />
              </div>

              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div className="rounded-2xl bg-card shadow-sm border border-border/60 p-5 space-y-2">
                  <div className="inline-flex items-center gap-2 text-primary font-semibold">
                    <Train className="h-5 w-5" />
                    <span>Access</span>
                  </div>
                  <p className="text-sm text-muted-foreground">
                    Subway Oedo Line / Nanboku Line
                    <br />
                    Short walk from Exit 6, Azabu-Juban Station
                  </p>
                </div>

                <div className="rounded-2xl bg-card shadow-sm border border-border/60 p-5 space-y-2">
                  <div className="inline-flex items-center gap-2 text-primary font-semibold">
                    <MapPin className="h-5 w-5" />
                    <span>Address</span>
                  </div>
                  <p className="text-sm text-muted-foreground">
                    3F Azabu Maruka Bldg.,
                    <br />
                    3-8-2 Higashi Azabu, Minato-ku,
                    <br />
                    Tokyo 106-0044
                  </p>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}
```

**Important note:** The `ContactForm` component renders its own card wrapper (`bg-card rounded-2xl border ...`). We pass `className="border-0 p-0 rounded-none bg-transparent"` to strip it since the outer card provides the wrapper here. Verify this works — if `ContactForm` doesn't pass `className` through to the outer wrapper correctly, you may need to adjust. Check `ContactForm.tsx:74` where `cn("bg-card rounded-2xl border border-border/60 p-6", className)` confirms it does merge classNames correctly.

**Step 2: Verify no lint errors**

Run: `pnpm lint --filter @customer-portal/portal -- --no-warn`

**Step 3: Commit**

```
feat: create ContactSection component with form, map, and contact info
```

---

### Task 5: Update HeroSection — Problem-First Copy + #contact CTA

**Files:**

- Modify: `apps/portal/src/features/landing-page/components/HeroSection.tsx`

**Step 1: Update hero heading, subtitle, and CTAs**

Change lines 46-70:

```tsx
{
  /* OLD heading (lines 46-49): */
}
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold leading-tight text-foreground">
  <span className="block">A One Stop Solution</span>
  <span className="block text-primary mt-2">for Your IT Needs</span>
</h1>;

{
  /* NEW heading: */
}
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold leading-tight text-foreground">
  <span className="block">Just Moved to Japan?</span>
  <span className="block text-primary mt-2">Get Connected in English</span>
</h1>;
```

```tsx
{
  /* OLD subtitle (lines 50-53): */
}
<p className="mt-6 text-base sm:text-lg text-muted-foreground leading-relaxed font-semibold max-w-2xl mx-auto">
  From internet and mobile to VPN and on-site tech support — we handle it all in English so you
  don&apos;t have to.
</p>;

{
  /* NEW subtitle: */
}
<p className="mt-6 text-base sm:text-lg text-muted-foreground leading-relaxed font-semibold max-w-2xl mx-auto">
  Internet, phone, VPN and IT support — set up in days, not weeks. No Japanese needed.
</p>;
```

```tsx
{/* OLD CTAs (lines 58-69): */}
<Button as="a" href="/services" variant="pill" size="lg" rightIcon={<ArrowRight className="h-5 w-5" />}>
  Browse Services
</Button>
<Button as="a" href="/contact" variant="pillOutline" size="lg">
  Need Assistance?
</Button>

{/* NEW CTAs: */}
<Button as="a" href="/services" variant="pill" size="lg" rightIcon={<ArrowRight className="h-5 w-5" />}>
  Find Your Plan
</Button>
<Button as="a" href="#contact" variant="pillOutline" size="lg">
  Talk to Us
</Button>
```

**Step 2: Verify no lint errors**

Run: `pnpm lint --filter @customer-portal/portal -- --no-warn`

**Step 3: Commit**

```
feat: update hero to problem-first copy with #contact CTA
```

---

### Task 6: Update CTABanner — Remove Phone, Update CTAs

**Files:**

- Modify: `apps/portal/src/features/landing-page/components/CTABanner.tsx`

**Step 1: Rewrite CTABanner**

Replace the entire file content:

```tsx
import { ArrowRight } from "lucide-react";
import { Button } from "@/components/atoms/button";

export function CTABanner() {
  return (
    <section
      aria-label="Call to action"
      className="relative left-1/2 right-1/2 w-screen -translate-x-1/2 bg-primary-soft"
    >
      <div className="mx-auto max-w-3xl px-6 sm:px-10 lg:px-14 py-14 sm:py-16 text-center">
        <h2 className="text-2xl sm:text-3xl font-extrabold text-foreground">
          Ready to Get Set Up?
        </h2>
        <p className="mt-2 text-base text-muted-foreground">
          No Japanese required. Our English-speaking team is here to help.
        </p>

        <div className="mt-6 flex flex-col sm:flex-row items-center justify-center gap-3">
          <Button
            as="a"
            href="/services"
            variant="pill"
            size="lg"
            rightIcon={<ArrowRight className="h-5 w-5" />}
          >
            Find Your Plan
          </Button>
          <Button as="a" href="#contact" variant="pillOutline" size="lg">
            Talk to Us
          </Button>
        </div>
      </div>
    </section>
  );
}
```

**Step 2: Verify no lint errors**

Run: `pnpm lint --filter @customer-portal/portal -- --no-warn`

**Step 3: Commit**

```
feat: repurpose CTABanner for conversion — remove phone, update CTAs
```

---

### Task 7: Update Barrel Exports + Compose New Landing Page

**Files:**

- Modify: `apps/portal/src/features/landing-page/components/index.ts`
- Modify: `apps/portal/src/features/landing-page/views/PublicLandingView.tsx`

**Step 1: Update barrel exports**

Replace `apps/portal/src/features/landing-page/components/index.ts`:

```ts
// Landing page sections
export { HeroSection } from "./HeroSection";
export { TrustStrip } from "./TrustStrip";
export { ServicesCarousel } from "./ServicesCarousel";
export { WhyUsSection } from "./WhyUsSection";
export { CTABanner } from "./CTABanner";
export { SupportDownloadsSection } from "./SupportDownloadsSection";
export { ContactSection } from "./ContactSection";
```

Note: `ServicesGrid` export removed — replaced by `ServicesCarousel`.

**Step 2: Rewrite PublicLandingView with 7-section composition**

Replace the entire content of `apps/portal/src/features/landing-page/views/PublicLandingView.tsx`:

```tsx
"use client";

import { ArrowRight } from "lucide-react";
import { Button } from "@/components/atoms/button";
import { useStickyCta } from "@/features/landing-page/hooks";
import {
  HeroSection,
  TrustStrip,
  ServicesCarousel,
  WhyUsSection,
  CTABanner,
  SupportDownloadsSection,
  ContactSection,
} from "@/features/landing-page/components";

export function PublicLandingView() {
  const { heroCTARef, showStickyCTA } = useStickyCta();

  return (
    <div className="space-y-0 pb-8 pt-0">
      <HeroSection heroCTARef={heroCTARef} />
      <TrustStrip />
      <ServicesCarousel />
      <WhyUsSection />
      <CTABanner />
      <SupportDownloadsSection />
      <ContactSection />

      {/* Sticky Mobile CTA */}
      {showStickyCTA && (
        <div className="fixed bottom-0 left-0 right-0 bg-background/95 backdrop-blur-sm border-t border-border p-4 z-50 md:hidden animate-in slide-in-from-bottom-4 duration-300">
          <Button
            as="a"
            href="/services"
            variant="pill"
            size="lg"
            rightIcon={<ArrowRight className="h-5 w-5" />}
            className="w-full shadow-lg"
          >
            Find Your Plan
          </Button>
        </div>
      )}
    </div>
  );
}
```

**Step 3: Verify no lint errors**

Run: `pnpm lint --filter @customer-portal/portal -- --no-warn`

**Step 4: Commit**

```
feat: compose landing page with 7-section conversion funnel
```

---

### Task 8: Delete ServicesGrid + /help Route

**Files:**

- Delete: `apps/portal/src/features/landing-page/components/ServicesGrid.tsx`
- Delete: `apps/portal/src/app/(public)/(site)/help/page.tsx`

**Step 1: Delete ServicesGrid.tsx**

This file is no longer imported anywhere (barrel export was updated in Task 7).

Run: `rm apps/portal/src/features/landing-page/components/ServicesGrid.tsx`

**Step 2: Delete /help route**

Run: `rm apps/portal/src/app/\(public\)/\(site\)/help/page.tsx`

Then check if the help directory is empty and remove it:

Run: `rmdir apps/portal/src/app/\(public\)/\(site\)/help/ 2>/dev/null || true`

**Step 3: Verify no lingering imports of ServicesGrid**

Run grep to confirm no file imports `ServicesGrid`:

```bash
pnpm exec grep -r "ServicesGrid" apps/portal/src/ --include="*.ts" --include="*.tsx"
```

Expected: no results.

**Step 4: Verify no lint errors**

Run: `pnpm lint --filter @customer-portal/portal -- --no-warn`

**Step 5: Commit**

```
refactor: remove ServicesGrid and /help route
```

---

### Task 9: Update SiteFooter Links

**Files:**

- Modify: `apps/portal/src/components/organisms/SiteFooter/SiteFooter.tsx`

**Step 1: Split "Support & Contact" into separate links**

In the Company links section (lines 85-110), change the "Support & Contact" link:

```tsx
{/* OLD (lines 94-101): */}
<li>
  <Link
    href="/contact"
    className="text-muted-foreground hover:text-foreground transition-colors"
  >
    Support & Contact
  </Link>
</li>

{/* NEW — replace with two separate links: */}
<li>
  <Link
    href="/support"
    className="text-muted-foreground hover:text-foreground transition-colors"
  >
    Support
  </Link>
</li>
<li>
  <Link
    href="/contact"
    className="text-muted-foreground hover:text-foreground transition-colors"
  >
    Contact
  </Link>
</li>
```

**Step 2: Verify no lint errors**

Run: `pnpm lint --filter @customer-portal/portal -- --no-warn`

**Step 3: Commit**

```
refactor: split footer "Support & Contact" into separate links
```

---

### Task 10: Final Verification

**Step 1: Run full type check**

Run: `pnpm type-check`

Expected: no errors related to landing-page, support, or footer components.

**Step 2: Run full lint check**

Run: `pnpm lint`

Expected: no new errors. Warnings are acceptable.

**Step 3: Verify no dangling imports**

Run grep to confirm:

- No imports of `ServicesGrid` exist
- No imports reference `/help` route
- `ContactSection` is only imported in `PublicLandingView`

```bash
pnpm exec grep -rn "ServicesGrid\|from.*help/page\|/help" apps/portal/src/ --include="*.ts" --include="*.tsx" | grep -v node_modules | grep -v ".next"
```

**Step 4: Commit any fixes**

If there are type or lint errors, fix them and commit:

```
fix: resolve lint/type errors from landing page restructuring
```
