Why & Services Bento Redesign Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Redesign the Why and Services homepage sections with bento-grid layouts, image-based cards, gold frame corners, and theatrical animations.
Architecture: Two independent sections with shared design tokens (gold frame corners, hover effects). Services uses a hero+grid bento (5 cards), Why uses a stage-column bento (4 cards with tall left column).
Tech Stack: Next.js 16, Tailwind CSS v4, framer-motion (existing FadeIn), Next/Image
File Map
| File | Action | Purpose |
|---|---|---|
apps/frontend/components/home/why-section.tsx | Modify | Full redesign with bento layout |
apps/frontend/components/home/services-section.tsx | Modify | Full redesign with bento layout |
apps/frontend/components/home/why-card.tsx | Create | Reusable image-based card for Why section |
apps/frontend/components/home/service-card.tsx | Create | Reusable image-based card for Services section |
apps/frontend/components/ui/fade-in.tsx | Modify | Add direction prop for left/right slide |
apps/frontend/lib/data/home-page.ts | Modify | Add image paths to whyFeatures data |
Task 1: Update FadeIn Component (Direction Support)
Files:
-
Modify:
apps/frontend/components/ui/fade-in.tsx:1-23 -
Step 1: Read current FadeIn component
// Current implementation - read to understand existing motion props- Step 2: Add direction prop with left/right variants
"use client";
import { motion } from "framer-motion";
interface FadeInProps {
children: React.ReactNode;
delay?: number;
className?: string;
direction?: "up" | "left" | "right" | "none";
}
const directionVariants = {
up: { initial: { opacity: 0, y: 30 }, animate: { opacity: 1, y: 0 } },
left: { initial: { opacity: 0, x: -30 }, animate: { opacity: 1, x: 0 } },
right: { initial: { opacity: 0, x: 30 }, animate: { opacity: 1, x: 0 } },
none: { initial: { opacity: 0 }, animate: { opacity: 1 } },
};
export function FadeIn({
children,
delay = 0,
className = "",
direction = "up",
}: FadeInProps) {
const variants = directionVariants[direction];
return (
<motion.div
initial={variants.initial}
whileInView={variants.animate}
viewport={{ once: true, margin: "-60px" }}
transition={{ duration: 0.6, delay, ease: [0.25, 0.46, 0.45, 0.94] }}
className={className}
>
{children}
</motion.div>
);
}- Step 3: Commit
git add apps/frontend/components/ui/fade-in.tsx
git commit -m "feat(home): add direction variants to FadeIn for theatrical entrance
Adds 'left' and 'right' direction options for slide-in animations"
---
## Task 2: Create GoldFrameCorners Component (Shared)
**Files:**
- Create: `apps/frontend/components/home/gold-frame-corners.tsx`
- [ ] **Step 1: Create reusable gold frame corners component**
```tsx
"use client";
import { cn } from "~/lib/cn";
interface GoldFrameCornersProps {
className?: string;
size?: "sm" | "md" | "lg";
opacity?: number;
}
const sizeMap = {
sm: "w-6 h-6",
md: "w-8 h-8",
lg: "w-10 h-10",
};
export function GoldFrameCorners({ className, size = "md", opacity = 0.6 }: GoldFrameCornersProps) {
const borderSize = sizeMap[size];
return (
<div className={cn("absolute inset-0 pointer-events-none", className)}>
{/* Top-left */}
<div className={`absolute top-0 left-0 ${borderSize} border-t-2 border-l-2 border-gold`} style={{ borderColor: `rgba(197, 160, 89, ${opacity})` }} />
{/* Top-right */}
<div className={`absolute top-0 right-0 ${borderSize} border-t-2 border-r-2 border-gold`} style={{ borderColor: `rgba(197, 160, 89, ${opacity})` }} />
{/* Bottom-left */}
<div className={`absolute bottom-0 left-0 ${borderSize} border-b-2 border-l-2 border-gold`} style={{ borderColor: `rgba(197, 160, 89, ${opacity})` }} />
{/* Bottom-right */}
<div className={`absolute bottom-0 right-0 ${borderSize} border-b-2 border-r-2 border-gold`} style={{ borderColor: `rgba(197, 160, 89, ${opacity})` }} />
</div>
);
}- Step 2: Export from components/home/index.ts or verify component is picked up
Note: This project uses direct imports (no barrel files per rules), so just create the component.
- Step 3: Commit
git add apps/frontend/components/home/gold-frame-corners.tsx
git commit -m "feat(home): add GoldFrameCorners reusable component
Shared gold corner frame decoration for Services, Why, and Gallery sections"Task 3: Update whyFeatures Data with Images
Files:
-
Modify:
apps/frontend/lib/data/home-page.ts:98-126 -
Step 1: Read current whyFeatures structure
// Current structure - iconKey, title, description- Step 2: Add image path to each feature
type WhyFeature = {
iconKey: "theatre" | "cabaret" | "dinner" | "cocktail";
title: string;
description: string;
image: string; // NEW: image path
};
export const whyFeatures: WhyFeature[] = [
{
iconKey: "theatre",
title: "Intimate Theatre Experience",
description: "Only 32 seats per show",
image: "/images/landing-page/dinner-theatre.jpg",
},
{
iconKey: "cabaret",
title: "Live Cabaret & Performance Nights",
description: "A curated mix of theatre, dance and live performance.",
image: "/images/landing-page/artist-performances.png",
},
{
iconKey: "dinner",
title: "Premium Fusion Dinner",
description:
"Vietnamese-Western cuisine designed for the evening experience.",
image: "/images/landing-page/dinner-theatre.jpg",
},
{
iconKey: "cocktail",
title: "Signature Cocktails",
description: "Craft cocktails in an immersive theatre atmosphere",
image: "/images/landing-page/dinner-theatre.jpg", // TODO: replace with actual cocktail image
},
];Note: Replace cocktail image path with actual image when found in /images/landing-page/.
- Step 3: Commit
git add apps/frontend/lib/data/home-page.ts
git commit -m "feat(data): add image paths to whyFeatures for bento redesign"Task 4: Create WhyCard Component
Files:
-
Create:
apps/frontend/components/home/why-card.tsx -
Step 1: Create WhyCard component with image background
"use client";
import Image from "next/image";
import { cn } from "~/lib/cn";
import { GoldFrameCorners } from "./gold-frame-corners";
interface WhyCardProps {
image: string;
title: string;
description: string;
className?: string;
aspectRatio?: "tall" | "normal";
}
export function WhyCard({
image,
title,
description,
className,
aspectRatio = "normal",
}: WhyCardProps) {
return (
<div
className={cn(
"relative overflow-hidden rounded-2xl group cursor-pointer",
aspectRatio === "tall" ? "h-full min-h-[400px]" : "aspect-[4/3]",
className,
)}
>
{/* Image background */}
<Image
src={image}
alt={title}
fill
className="object-cover transition-transform duration-500 group-hover:scale-105"
loading="lazy"
/>
{/* Gradient overlay for text readability */}
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent" />
{/* Gold frame corners */}
<GoldFrameCorners className="z-10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
{/* Content overlay */}
<div className="absolute bottom-0 left-0 right-0 p-6 z-20">
<h3 className="font-serif text-xl md:text-2xl text-white font-normal mb-1">
{title}
</h3>
<p className="font-sans text-sm text-white/80 leading-relaxed">
{description}
</p>
</div>
{/* Hover glow effect */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none">
<div className="absolute inset-0 bg-gold/10 blur-xl" />
</div>
</div>
);
}- Step 2: Commit
git add apps/frontend/components/home/why-card.tsx
git commit -m "feat(home): create WhyCard component for bento redesign
Image-based card with gradient overlay, gold frame corners on hover, and glow effect"Task 5: Redesign WhySection (Bento Layout)
Files:
-
Modify:
apps/frontend/components/home/why-section.tsx:1-38 -
Step 1: Read current implementation
// Current - uses icons in glassmorphism cards- Step 2: Implement new bento layout with WhyCard
// ipsoc checked: 2026-05-06
"use client";
import { whyFeatures } from "~/lib/data/home-page";
import { FadeIn } from "~/components/ui/fade-in";
import { WhyCard } from "./why-card";
// =============================================================================
// Why House of Legends — Bento Redesign
// =============================================================================
export function WhySection() {
// Map data to card props
const [theatre, cabaret, dinner, cocktails] = whyFeatures;
return (
<section className="bg-transparent px-4 py-20" id="why">
<div className="max-w-[1440px] mx-auto flex flex-col gap-10 items-center">
<FadeIn>
<h2 className="font-display text-4xl leading-none text-gold font-normal text-center">
Why House Of Legends
</h2>
</FadeIn>
{/* Bento grid: Stage Column layout */}
<div className="w-full max-w-[1200px] grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Left tall card - Theatre (spans full height) */}
<FadeIn
direction="left"
delay={0.1}
className="row-span-1 md:row-span-1"
>
<WhyCard
image={theatre.image}
title={theatre.title}
description={theatre.description}
className="h-[400px] md:h-[500px]"
aspectRatio="tall"
/>
</FadeIn>
{/* Right column: 3 stacked cards */}
<div className="flex flex-col gap-6">
{/* Top: Cabaret + Dinner (side by side on md+) */}
<FadeIn direction="right" delay={0.2}>
<WhyCard
image={cabaret.image}
title={cabaret.title}
description={cabaret.description}
className="h-[200px] md:h-[242px]"
/>
</FadeIn>
<div className="grid grid-cols-2 gap-6">
{/* Middle: Dinner (or could be second cabaret if swapped) */}
<FadeIn direction="right" delay={0.35}>
<WhyCard
image={dinner.image}
title={dinner.title}
description={dinner.description}
className="h-[150px]"
/>
</FadeIn>
{/* Bottom: Cocktails */}
<FadeIn direction="right" delay={0.5}>
<WhyCard
image={cocktails.image}
title={cocktails.title}
description={cocktails.description}
className="h-[150px]"
/>
</FadeIn>
</div>
</div>
</div>
</div>
</section>
);
}Note: The bento layout shows Theatre tall on left, and Cabaret/Dinner/Cocktails stacked on right. Adjust grid spans based on visual testing.
- Step 3: Commit
git add apps/frontend/components/home/why-section.tsx
git commit -m "refactor(home): redesign WhySection with bento layout
- Stage column layout with tall left card
- Image-based cards replacing icons
- Gold frame corners on hover
- Staggered entrance animations"Task 6: Create ServiceCard Component
Files:
-
Create:
apps/frontend/components/home/service-card.tsx -
Step 1: Create ServiceCard component
"use client";
import Image from "next/image";
import { LocaleLink } from "~/components/features/ui/locale-link";
import { cn } from "~/lib/cn";
import { GoldFrameCorners } from "./gold-frame-corners";
interface ServiceCardProps {
number: string;
title: string;
description: string;
image: string;
href: string;
className?: string;
}
export function ServiceCard({
number,
title,
description,
image,
href,
className,
}: ServiceCardProps) {
return (
<div
className={cn(
"relative h-[400px] overflow-hidden group cursor-pointer rounded-2xl",
className,
)}
>
{/* Image background */}
<div className="absolute inset-0 z-10">
<Image
src={image}
alt={title}
fill
className="object-cover transition-transform duration-500 group-hover:scale-105"
loading="lazy"
/>
</div>
{/* Dark overlay */}
<div className="absolute inset-0 z-20 bg-black/40" />
{/* Gold frame corners */}
<GoldFrameCorners className="z-30 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
{/* Content */}
<div className="absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 p-6 text-center">
<span className="font-serif text-3xl text-gold font-normal">
{number}
</span>
<h3 className="font-display text-2xl md:text-3xl text-white font-normal">
{title}
</h3>
<p className="font-sans text-sm text-white/80 leading-relaxed max-w-[280px]">
{description}
</p>
</div>
{/* Link arrow */}
<LocaleLink
href={href}
className="absolute top-6 right-6 z-40 inline-flex items-center justify-center w-12 h-12 bg-[#1a1a1a]/80 border-2 border-gold-light text-gold-light rounded-full no-underline hover:bg-gold-light hover:text-surface transition-colors duration-300"
>
<svg
className="w-6 h-6 transition-transform duration-300 group-hover:rotate-45"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M5 12h14M12 5l7 7-7 7"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</LocaleLink>
{/* Hover glow */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none z-[5]">
<div className="absolute inset-0 bg-gold/10 blur-xl" />
</div>
</div>
);
}- Step 2: Commit
git add apps/frontend/components/home/service-card.tsx
git commit -m "feat(home): create ServiceCard component for bento redesign
Image-based card with gold frame corners, link arrow, and hover glow"Task 7: Redesign ServicesSection (Bento Layout)
Files:
-
Modify:
apps/frontend/components/home/services-section.tsx:1-115 -
Step 1: Read current implementation
// Current - 5 cards in 3+2 grid with gold frame corners- Step 2: Implement bento layout with ServiceCard
// ipsoc checked: 2026-05-06
// SoC: This component only renders UI. Uses LocaleLink for i18n-aware routing.
"use client";
import { services } from "~/lib/data/home-page";
import { FadeIn } from "~/components/ui/fade-in";
import { ServiceCard } from "./service-card";
// =============================================================================
// Services Section — Bento Redesign
// =============================================================================
export function ServicesSection() {
return (
<section className="bg-transparent px-4 py-20" id="services">
<div className="max-w-[1440px] mx-auto flex flex-col gap-6">
{/* Bento grid layout */}
<div className="grid grid-cols-1 md:grid-cols-5 gap-6">
{/* Row 1: Hero cards (60/40 split) */}
<div className="md:col-span-3">
<FadeIn direction="left" delay={0.1}>
<ServiceCard
number={services[0].number}
title={services[0].title}
description={services[0].description}
image={services[0].image}
href={services[0].href}
className="h-[400px]"
/>
</FadeIn>
</div>
<div className="md:col-span-2">
<FadeIn direction="right" delay={0.2}>
<ServiceCard
number={services[1].number}
title={services[1].title}
description={services[1].description}
image={services[1].image}
href={services[1].href}
className="h-[400px]"
/>
</FadeIn>
</div>
{/* Row 2: 3 equal cards */}
<div className="md:col-span-1">
<FadeIn direction="left" delay={0.3}>
<ServiceCard
number={services[2].number}
title={services[2].title}
description={services[2].description}
image={services[2].image}
href={services[2].href}
className="h-[300px]"
/>
</FadeIn>
</div>
<div className="md:col-span-2">
<FadeIn direction="up" delay={0.4}>
<ServiceCard
number={services[3].number}
title={services[3].title}
description={services[3].description}
image={services[3].image}
href={services[3].href}
className="h-[300px]"
/>
</FadeIn>
</div>
<div className="md:col-span-2">
<FadeIn direction="right" delay={0.5}>
<ServiceCard
number={services[4].number}
title={services[4].title}
description={services[4].description}
image={services[4].image}
href={services[4].href}
className="h-[300px]"
/>
</FadeIn>
</div>
</div>
</div>
</section>
);
}Note: The grid uses md:col-span-3 + md:col-span-2 for the 60/40 split. Verify visual proportions in browser and adjust if needed.
- Step 3: Commit
git add apps/frontend/components/home/services-section.tsx
git commit -m "refactor(home): redesign ServicesSection with bento layout
- Hero + grid layout (60/40 split + 3 equal cards)
- Image-based cards with gold frame corners
- Staggered entrance animations"Task 8: Visual Verification
Files:
-
None (browser testing only)
-
Step 1: Start dev server
cd apps/frontend && npm run dev-
Step 2: Verify WhySection
-
Navigate to homepage, scroll to "Why" section
-
Check: Tall left card shows Theatre
-
Check: Right column shows 3 stacked cards (Cabaret, Dinner, Cocktails)
-
Check: Gold frame corners appear on hover
-
Check: Images zoom slightly on hover
-
Check: Cards lift with gold glow on hover
-
Check: Animations stagger correctly (left enters first, then right stack)
-
Step 3: Verify ServicesSection
-
Navigate to homepage, scroll to "Services" section
-
Check: First row shows 60/40 split (Dinner Theatre larger)
-
Check: Second row shows 3 equal cards
-
Check: Gold frame corners appear on hover
-
Check: Link arrows work and rotate on hover
-
Check: Images zoom on hover
-
Step 4: Verify Responsive
-
Test mobile (< 768px): Cards should stack vertically
-
Test tablet (768px - 1024px): Grid should adapt
-
Test desktop (> 1024px): Full bento layout
-
Step 5: Report findings
Note any visual issues or adjustments needed. Take screenshots if helpful.
Self-Review Checklist
-
Spec coverage: All sections from spec have tasks
- Why bento layout
- Services bento layout
- Gold frame corners
- Image-based cards
- Theatrical animations
- Responsive behavior (documented in spec, verified in Task 8)
-
Placeholder scan: No TODOs, no TBDs, no "fill in later"
-
Type consistency: All props and interfaces are consistent
WhyCardusesimage,title,description,aspectRatioServiceCardusesnumber,title,description,image,href- Both use
GoldFrameCornerswith consistent props
-
Animation consistency: FadeIn direction variants match across sections
Execution Options
Plan complete and saved to docs/superpowers/plans/2026-05-06-why-services-bento-implementation.md.
Two execution options:
1. Subagent-Driven (recommended) - I dispatch a fresh subagent per task, review between tasks, fast iteration
2. Inline Execution - Execute tasks in this session using executing-plans, batch execution with checkpoints
Which approach?