[update] Home Page 수정

feature/layout
minheon 2026-03-30 16:40:16 +09:00
parent 8e1cc981fa
commit bac6b9f910
27 changed files with 798 additions and 393 deletions

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 10h12M12 6l4 4-4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 227 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" />
<path d="M9 12l2 2 4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 312 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12L6 8l4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 220 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 4l4 4-4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 218 B

View File

@ -0,0 +1,16 @@
<svg width="28" height="20" viewBox="0 0 28 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="inf-grad" x1="0" y1="0" x2="28" y2="20" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="currentColor" stop-opacity="0.9"/>
<stop offset="45%" stop-color="currentColor" stop-opacity="0.55"/>
<stop offset="100%" stop-color="currentColor" stop-opacity="0.85"/>
</linearGradient>
</defs>
<path
d="M2,10 C2,4 8,4 14,10 C20,16 26,16 26,10 C26,4 20,4 14,10 C8,16 2,16 2,10Z"
stroke="url(#inf-grad)"
stroke-width="2.8"
stroke-linejoin="round"
fill="none"
/>
</svg>

After

Width:  |  Height:  |  Size: 651 B

View File

@ -0,0 +1,8 @@
/** CTA 섹션 카피 */
export const CTA_HEADLINE = "Ready to Transform Your Marketing?";
export const CTA_DESCRIPTION =
"URL 하나로 시작하는 완벽한 마케팅 자동화. 지금 바로 무료 진단을 받아보세요.";
export const CTA_URL_PLACEHOLDER = "Enter Your URL";
export const CTA_BUTTON_LABEL = "Analyze";
export const CTA_FOOTNOTE = "네이버 블로그, 플레이스, 소셜미디어 종합 분석 리포트 받아보기";

View File

@ -0,0 +1,13 @@
/** Hero 섹션 카피 */
export const HERO_BADGE_TEXT =
"Agentic AI Marketing Automation for Premium Medical Business & Marketing Agency";
export const HERO_URL_PLACEHOLDER = "Enter Your Website URL";
export const HERO_CTA_BUTTON_LABEL = "Analyze Marketing Performance";
export const HERO_FOOTNOTE =
"네이버 블로그, 플레이스, 소셜미디어 등 Online Presence 종합 분석 리포트를 제공합니다.";
export const HERO_LEAD_EN = "Marketing that learns, improves, and accelerates — automatically.";
export const HERO_LEAD_KO =
"쓸수록 더 정교해지는 AI 마케팅 엔진. 콘텐츠 기획, 생성, 영상 제작, 채널 배포, 데이터 분석까지 하나로.";

View File

@ -0,0 +1,79 @@
/** Core Modules 섹션 — DEMO Modules */
export type CoreModule = {
step: string;
title: string;
items: string[];
highlight: string;
};
export const CORE_MODULES: CoreModule[] = [
{
step: "1",
title: "Marketing Intelligence",
items: [
"브랜딩, 마케팅 현황 분석",
"타겟 고객 분석",
"키워드 분석",
"경쟁 및 포지셔닝 분석",
"SEO 전략 & 채널별 콘텐츠 기획",
],
highlight: "AI 기반 시장 통찰력 도출",
},
{
step: "2",
title: "Content Creation",
items: [
"블로그 콘텐츠 생성",
"SEO 콘텐츠 생성",
"SNS 콘텐츠 생성",
"마케팅 카피 생성",
"Human-in-the loop 프로세스",
],
highlight: "고품질 맞춤형 콘텐츠 자동화",
},
{
step: "3",
title: "Video Automation",
items: [
"블로그 → 영상 변환",
"숏폼 콘텐츠 생성",
"유튜브 콘텐츠 제작",
"SNS 영상 제작",
"멀티모달 AI 엔진: 영상 + 음악 + 카피",
],
highlight: "원클릭 영상 제작 시스템",
},
{
step: "4",
title: "Distribution Engine",
items: [
"블로그 게시",
"SNS 자동 게시",
"유튜브 업로드",
"콘텐츠 일정 관리",
"SEO, AEO 자동 최적화",
],
highlight: "전 채널 통합 배포 및 최적화",
},
{
step: "5",
title: "Performance Intelligence",
items: [
"SEO 성과 분석",
"콘텐츠 성과 분석",
"채널 성과 분석",
"AI 콘텐츠 개선 전략 추천",
"데이터 기반 효과 검증",
],
highlight: "실시간 성과 추적 및 개선",
},
];
export const MODULE_CARD_STAGGER = [
"",
"animation-delay-100",
"animation-delay-200",
"animation-delay-300",
"animation-delay-400",
] as const;

View File

@ -0,0 +1,25 @@
/** Problem 섹션 — DEMO Problems */
export type ProblemCard = {
title: string;
desc: string;
highlight?: boolean;
};
export const PROBLEM_CARDS: ProblemCard[] = [
{
title: "콘텐츠 생산의 한계",
desc: "블로그, SEO, 유튜브, 숏폼 등 지속적 생산이 필요하지만 인력과 비용, 시간 부족으로 제한됩니다.",
},
{
title: "영상 콘텐츠 제작 비용",
desc: "영상은 중요하지만 촬영, 편집, 기획 비용이 높습니다. 특히 숏폼 콘텐츠는 지속적인 제작이 어렵습니다.",
highlight: true,
},
{
title: "데이터 기반의 마케팅 부족",
desc: "어떤 콘텐츠가 효과적인지, 어떤 키워드가 유입을 만드는지, 어떤 채널이 성과가 좋은지 알기 어렵고, 각 플랫폼들의 데이터들을 한눈에 파악할 수 없습니다.",
},
];
export const PROBLEM_CARD_STAGGER = ["", "animation-delay-100", "animation-delay-200"] as const;

View File

@ -0,0 +1,23 @@
/** Process(다크) 섹션 — AGDP 순환 다이어그램 레이아웃 */
export type AgdpSlot = "left" | "top" | "right" | "bottom";
export type AgdpNodeDef = {
letter: string;
label: string;
slot: AgdpSlot;
};
export const AGDP_NODES: readonly AgdpNodeDef[] = [
{ letter: "A", label: "Analysis", slot: "left" },
{ letter: "G", label: "Generation", slot: "top" },
{ letter: "D", label: "Distribution", slot: "right" },
{ letter: "P", label: "Performance", slot: "bottom" },
];
export const AGDP_SLOT_WRAPPER_CLASS: Record<AgdpSlot, string> = {
left: "absolute top-1/2 left-0 -translate-x-1/2 -translate-y-1/2 z-30 flex flex-col items-center gap-3 md:gap-4",
top: "absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 z-30 flex flex-col items-center gap-3 md:gap-4",
right: "absolute top-1/2 right-0 translate-x-1/2 -translate-y-1/2 z-30 flex flex-col items-center gap-3 md:gap-4",
bottom: "absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 z-30 flex flex-col items-center gap-3 md:gap-4",
};

View File

@ -0,0 +1,31 @@
/** Solution(Target Audience) 섹션 — DEMO TargetAudience */
export type SolutionCard = {
title: string;
description: string;
items: string[];
cardClass: string;
tagClass: string;
animClass: string;
};
export const SOLUTION_CARDS: SolutionCard[] = [
{
title: "Premium Medical Business",
description:
"고객 LTV가 높고 브랜드 경쟁이 심해 콘텐츠 마케팅이 필수적인 프리미엄 의료 서비스 제공 병원",
items: ["피부과", "성형외과", "치과", "안과", "헬스케어 클리닉", "피트니스"],
cardClass: "bg-gradient-to-br from-navy-50 to-white border border-neutral-20",
tagClass: "border border-neutral-20",
animClass: "animate-fade-in-left animation-delay-100",
},
{
title: "Medical Marketing Agency",
description:
"콘텐츠 제작 비용 부담과 인력 의존도가 높고 영상 제작 생산성 개선이 필요한 병원 마케팅 대행사",
items: ["병원 마케팅 대행사", "콘텐츠 마케팅 Agency", "영상 마케팅 Agency", "광고 운영 대행사"],
cardClass: "bg-gradient-to-br from-status-good-bg to-white border border-status-good-border/50",
tagClass: "border border-status-good-border/50",
animClass: "animate-fade-in-right animation-delay-200",
},
];

View File

@ -0,0 +1,36 @@
/** Use Cases 섹션 — DEMO UseCases */
export type UseCaseCard = {
title: string;
items: string[];
cardClass: string;
iconClass: string;
animClass: string;
};
export const USE_CASE_CARDS: UseCaseCard[] = [
{
title: "Premium Medical Business",
items: [
"SEO 콘텐츠 자동 생산으로 검색 상위 노출 달성",
"비용 부담 없이 고품질 영상 콘텐츠 대량 확대",
"자연 검색 유입 증가로 인한 환자 전환율 상승",
],
cardClass:
"glass-card p-10 md:p-12 bg-gradient-to-br from-status-info-bg/50 to-white border border-status-info-border/30",
iconClass: "text-status-info-dot",
animClass: "animate-fade-in-left animation-delay-100",
},
{
title: "Marketing Agency",
items: [
"AI 기반 콘텐츠 제작 자동화로 생산성 극대화",
"블로그 텍스트 기반 영상 제작 자동화로 리소스 절감",
"다수 클라이언트 계정의 통합 운영 및 효율화",
],
cardClass:
"glass-card p-10 md:p-12 bg-gradient-to-br from-status-good-bg/50 to-white border border-status-good-border/30",
iconClass: "text-violet-500",
animClass: "animate-fade-in-right animation-delay-200",
},
];

View File

@ -0,0 +1,15 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
export function useAnalyze() {
const [url, setUrl] = useState("");
const navigate = useNavigate();
const handleAnalyze = () => {
if (url.trim()) {
navigate("/report/loading", { state: { url } });
}
};
return { url, setUrl, handleAnalyze };
}

View File

@ -1,35 +1,64 @@
import ArrowRightIcon from "@/assets/home/arrow-right.svg?react";
import {
CTA_BUTTON_LABEL,
CTA_DESCRIPTION,
CTA_FOOTNOTE,
CTA_HEADLINE,
CTA_URL_PLACEHOLDER,
} from "@/features/home/constants/cta_contents";
import { useAnalyze } from "@/features/home/hooks/useAnalyze";
import { useInView } from "@/hooks/useInView";
export function CTASection() { export function CTASection() {
return ( const { ref, inView } = useInView<HTMLElement>();
<div className="relative flex flex-col items-center justify-center py-[96px] h-[512px] bg-[#0A1128] overflow-hidden"> const { url, setUrl, handleAnalyze } = useAnalyze();
{/* 배경 글로우 원 */} return (
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full bg-[rgba(173,70,255,0.20)] blur-[50px] pointer-events-none" /> <section ref={ref} className="py-20 md:py-24 bg-navy-900 text-white px-6 relative overflow-hidden">
<div className="absolute inset-0 -z-10 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-navy-800 via-navy-900 to-navy-900 opacity-80" />
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-violet-500/20 rounded-full blur-[100px] pointer-events-none" />
{/* 타이틀 */} <div className="max-w-4xl mx-auto text-center relative z-10">
<div className="relative flex flex-col items-center gap-4 pb-16"> <h2
<div className={`display-36 md:display-48 mb-4 leading-tight text-transparent bg-clip-text bg-gradient-to-r from-marketing-cream via-marketing-lilac to-marketing-ice break-keep ${
className="bg-gradient-to-r from-[#FFF3EB] via-[#E4CFFF] to-[#F5F9FF] bg-clip-text text-transparent text-[48px] font-bold leading-[48px]" inView ? "animate-fade-in-up" : "opacity-0"
style={{ fontFamily: "var(--font-family-display)" }} }`}
> >
Ready to Transform Your Marketing? {CTA_HEADLINE}
</div> </h2>
<div className="text-[#E9D4FF] text-[18px] leading-[29.25px] break-keep">
URL . .
</div>
</div>
{/* 버튼 */} <p
<div className="relative flex flex-col gap-4 w-[448px]"> className={`body-18-md-20 font-light text-lavender-200 mb-10 max-w-2xl mx-auto break-keep ${
<button className="flex items-center justify-center w-full h-[56px] rounded-full border border-white/20 bg-gradient-to-r from-white via-[#E4CFFF] to-[#F5F9FF] shadow text-[#0A1128]/60 text-base font-medium cursor-pointer"> inView ? "animate-fade-in-up animation-delay-100" : "opacity-0"
Enter Your URL }`}
</button> >
<button className="flex items-center justify-center w-full h-[56px] bg-gradient-to-r from-[#4F1DA1] to-[#021341] rounded-[999px] text-white text-base font-medium leading-6 cursor-pointer"> {CTA_DESCRIPTION}
Analyze </p>
</button>
<div className="text-[#E9D4FF]/80 text-center text-sm leading-5"> <div
, , className={`flex flex-col items-center justify-center gap-4 max-w-md mx-auto ${
</div> inView ? "animate-fade-in-up animation-delay-200" : "opacity-0"
</div> }`}
>
<input
type="url"
placeholder={CTA_URL_PLACEHOLDER}
value={url}
onChange={(e) => setUrl(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleAnalyze()}
className="w-full px-8 py-4 body-16-medium bg-gradient-to-r from-marketing-cream via-marketing-lilac to-marketing-ice border border-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50 shadow-sm text-center text-navy-900 placeholder:text-navy-900/60 break-keep"
/>
<button
type="button"
onClick={handleAnalyze}
className="w-full px-10 py-4 body-18 font-medium text-white rounded-full transition-all shadow-xl hover:shadow-2xl flex items-center justify-center gap-2 group cursor-pointer bg-gradient-to-r from-violet-700 to-navy-950 hover:from-violet-hover hover:to-violet-hover break-keep"
>
{CTA_BUTTON_LABEL}
<ArrowRightIcon aria-hidden="true" className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</button>
<p className="label-12 text-lavender-200/80 mt-2 break-keep">{CTA_FOOTNOTE}</p>
</div> </div>
) </div>
</section>
);
} }

View File

@ -1,55 +1,78 @@
import bg from "@/assets/home/bg_hero.png"; import InfinityLoopIcon from "@/assets/home/infinity-loop.svg?react";
import twinkle from "@/assets/home/twinkle.svg"; import ArrowRightIcon from "@/assets/home/arrow-right.svg?react";
import {
HERO_BADGE_TEXT,
HERO_CTA_BUTTON_LABEL,
HERO_FOOTNOTE,
HERO_LEAD_EN,
HERO_LEAD_KO,
HERO_URL_PLACEHOLDER,
} from "@/features/home/constants/hero_contents";
import { useAnalyze } from "@/features/home/hooks/useAnalyze";
export function HeroSection() { export function HeroSection() {
return ( const { url, setUrl, handleAnalyze } = useAnalyze();
<div className="flex flex-col items-center justify-center py-[140px] bg-cover bg-center" style={{ backgroundImage: `url(${bg})` }}>
<div className="flex flex-col items-center gap-6">
{/* 배지 */} return (
<div className="flex items-center gap-2 bg-transparent shadow rounded-[999px] px-4 py-2"> <section className="relative pt-16 pb-12 md:pt-16 md:pb-16 overflow-hidden flex flex-col items-center justify-center text-center px-6">
<img src={twinkle} alt="twinkle" />
<div className="text-[#314158] text-center text-xl font-medium leading-5">
Infinite Marketing for Premium Medical Business & Marketing Agency
</div>
</div>
{/* 타이틀 */} {/* 배경 그라디언트 */}
<div className="flex flex-col items-center gap-2"> <div className="absolute inset-0 -z-10 opacity-70 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-hero-wash-start via-hero-wash-mid to-hero-wash-end" />
<div
className="text-[#0A1128] text-center text-[72px] font-bold leading-[79.2px] tracking-[-1.8px]"
style={{ fontFamily: "var(--font-family-display)" }}
>
Marketing becomes a
</div>
<div
className="bg-gradient-to-r from-[#0A1128] to-[#3B5998] bg-clip-text text-transparent text-[72px] font-bold leading-[96px] tracking-[-1.8px]"
style={{ fontFamily: "var(--font-family-display)" }}
>
self-improving engine.
</div>
</div>
{/* 설명 */} <div className="max-w-4xl mx-auto">
<div className="flex flex-col items-center mb-4 text-[#45556C] text-center text-xl font-normal leading-[32.5px]">
<p>AI .</p>
<p> , , , , .</p>
</div>
{/* 버튼 */} {/* 배지 */}
<div className="flex flex-col items-center gap-4"> <div className="animate-fade-in-up inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white/60 backdrop-blur-sm border border-white/40 shadow-sm mb-8">
<button className="flex items-center justify-center w-full h-[56px] border border-[#E2E8F0] bg-white shadow rounded-[999px] text-[#90A1B9] text-base font-medium cursor-pointer hover:bg-[#F1F5F9] hover:text-[#64748B]"> <InfinityLoopIcon aria-hidden="true" className="text-violet-500 shrink-0" />
Enter Your URL <span className="body-14-medium text-neutral-80 break-keep">{HERO_BADGE_TEXT}</span>
</button>
<button className="flex items-center justify-center w-full h-[56px] bg-gradient-to-r from-[#4F1DA1] to-[#021341] rounded-[999px] text-white text-base font-medium leading-6 cursor-pointer">
Analyze
</button>
<div className="text-[#62748E] text-center text-lg font-normal leading-4">
, , Online Presence .
</div>
</div>
</div>
</div> </div>
)
{/* 제목 */}
<h1 className="animate-fade-in-up animation-delay-100 display-48 md:display-72 tracking-[-0.02em] text-navy-900 mb-6 break-keep">
<span className="tracking-[0.05em]">
In<span className="ml-[-0.04em]">f</span><span className="ml-[-0.04em]">inite</span>
</span>{" "}
Growth
<br className="hidden md:block" />
<span className="text-gradient">Marketing Engine.</span>
</h1>
{/* 설명 */}
<p className="animate-fade-in-up animation-delay-200 body-18-md-20 text-neutral-70 mb-10 max-w-3xl mx-auto leading-relaxed break-keep">
{HERO_LEAD_EN}
<br className="hidden md:block" />
{HERO_LEAD_KO}
</p>
{/* CTA */}
<div className="animate-fade-in-up animation-delay-300 flex flex-col items-center justify-center gap-5 max-w-lg mx-auto w-full">
<div className="relative w-full group">
<input
type="url"
placeholder={HERO_URL_PLACEHOLDER}
value={url}
onChange={(e) => setUrl(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleAnalyze()}
className="w-full px-8 py-5 body-16-medium bg-white/80 backdrop-blur-sm border border-neutral-30 rounded-2xl focus:outline-none focus:ring-2 focus:ring-violet-500/20 focus:border-violet-500/40 shadow-sm text-center text-navy-900 placeholder:text-neutral-60 transition-all group-hover:border-neutral-40 break-keep"
/>
</div>
<button
onClick={handleAnalyze}
className="w-full px-8 py-5 body-16-medium text-white rounded-2xl bg-gradient-to-r from-violet-700 to-navy-950 shadow-xl hover:shadow-2xl hover:scale-[1.02] active:scale-[0.98] flex items-center justify-center gap-3 group transition-all cursor-pointer break-keep"
>
{HERO_CTA_BUTTON_LABEL}
<ArrowRightIcon aria-hidden="true" className="group-hover:translate-x-1 transition-transform" />
</button>
<p className="label-12 text-neutral-70 mt-2 break-keep">{HERO_FOOTNOTE}</p>
</div>
</div>
{/* 블롭 데코레이션 */}
<div className="absolute top-1/4 left-10 w-64 h-64 bg-lavender-300 rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob pointer-events-none" />
<div className="absolute top-1/3 right-10 w-64 h-64 bg-marketing-blush rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob animation-delay-2000 pointer-events-none" />
<div className="absolute -bottom-8 left-1/2 -translate-x-1/2 w-64 h-64 bg-hero-wash-start rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob animation-delay-4000 pointer-events-none" />
</section>
);
} }

View File

@ -1,48 +1,45 @@
const PROBLEMS = [ import { PROBLEM_CARDS, PROBLEM_CARD_STAGGER } from "@/features/home/constants/problem_contents";
{ import { useInView } from "@/hooks/useInView";
title: "콘텐츠 생산의 한계",
description: "블로그, SEO, 유튜브, 숏폼 등 지속적 생산이 필요하지만 인력과 비용, 시간 부족으로 제한됩니다.",
gradient: false,
},
{
title: "영상 콘텐츠 제작 비용",
description: "영상은 중요하지만 촬영, 편집, 기획 비용이 높습니다. 특히 숏폼 콘텐츠는 지속적인 제작이 어렵습니다.",
gradient: true,
},
{
title: "데이터 기반의 마케팅 부족",
description: "어떤 콘텐츠가 효과적인지, 어떤 키워드가 유입을 만드는지, 어떤 채널이 성과가 좋은지 알기 어렵고, 각 플랫폼들의 데이터들을 한눈에 파악할 수 없습니다.",
gradient: false,
},
]
export function ProblemSection() { export function ProblemSection() {
return ( const { ref, inView } = useInView<HTMLElement>();
<div className="flex flex-col items-center justify-center py-[96px] bg-[#F8FAFC]">
<div className="flex flex-col items-center gap-4">
<div
className="text-[#0A1128] text-center text-[48px] font-bold leading-[48px]"
style={{ fontFamily: "var(--font-family-display)" }}
>
Premium Medical Marketing is Hard
</div>
<div className="text-[#45556C] text-[18px] leading-[29.25px]">
3
</div>
</div>
<div className="flex gap-6"> return (
{PROBLEMS.map(({ title, description, gradient }, index) => ( <section id="problem" ref={ref} className="py-24 bg-neutral-10 px-6 relative overflow-hidden">
<div key={index} className="flex flex-col w-[368px] h-[227px] gap-4 rounded-2xl bg-white shadow items-start justify-center px-10 py-12"> <div className="max-w-6xl mx-auto">
<div className={gradient ? "bg-gradient-to-r from-[#4F39F6] to-[#9810FA] bg-clip-text text-transparent text-[24px] font-bold leading-[32px]" : "text-[#0A1128] text-[24px] font-bold leading-[32px]"}>
{title} <div className={`text-center mb-16 ${inView ? "animate-fade-in-up" : "opacity-0"}`}>
</div> <h2 className="headline-30 md:display-48 text-navy-900 mb-4 break-keep">
<div className="text-[#45556C] text-[16px] leading-[20px] break-keep"> Premium Medical Marketing is Hard
{description} </h2>
</div> <p className="body-18 text-neutral-70 max-w-2xl mx-auto break-keep">
</div> 3
))} </p>
</div>
</div> </div>
)
<div className="grid md:grid-cols-3 gap-6">
{PROBLEM_CARDS.map((problem, idx) => (
<div
key={problem.title}
className={`bg-white rounded-2xl p-8 md:p-10 border border-neutral-20 shadow-sm hover:shadow-md transition-shadow flex flex-col justify-center ${
inView ? `animate-fade-in-up ${PROBLEM_CARD_STAGGER[idx]}` : "opacity-0"
}`}
>
<h3
className={`font-sans headline-24 mb-4 break-keep ${
problem.highlight
? "text-transparent bg-clip-text bg-gradient-to-r from-violet-700 to-violet-500"
: "text-navy-900"
}`}
>
{problem.title}
</h3>
<p className="body-16 text-neutral-70 leading-relaxed break-keep">{problem.desc}</p>
</div>
))}
</div>
</div>
</section>
);
} }

View File

@ -1,121 +1,20 @@
import twinkle_pink from "@/assets/home/twinkle_pink.svg"; import { AgdpCycleSummaryCard } from "@/features/home/ui/process/AgdpCycleSummaryCard";
import { AgdpEngineDiagram } from "@/features/home/ui/process/AgdpEngineDiagram";
const AGDP_ITEMS = [ import { MarketingEngineIntro } from "@/features/home/ui/process/MarketingEngineIntro";
{ letter: "G", label: "Generation", angle: 0 }, import { useInView } from "@/hooks/useInView";
{ letter: "D", label: "Distribution", angle: 90 },
{ letter: "P", label: "Performance", angle: 180 },
{ letter: "A", label: "Analysis", angle: 270 },
]
const REWARD_SIGNAL_CONFIG = {
text: "← REWARD SIGNAL",
startAngle: 158,
endAngle: 110,
radius: 200,
cx: 300,
cy: 300,
}
function RewardSignalText() {
const { text, startAngle, endAngle, radius, cx, cy } = REWARD_SIGNAL_CONFIG
const chars = text.split("")
const angleStep = (endAngle - startAngle) / (chars.length - 1)
return (
<svg className="absolute w-full h-full" viewBox="0 0 600 600">
{chars.map((char, i) => {
const angle = (startAngle + i * angleStep) * (Math.PI / 180)
return (
<text
key={i}
x={cx + radius * Math.cos(angle)}
y={cy + radius * Math.sin(angle)}
fontSize="11"
fill="rgba(255,255,255,0.4)"
textAnchor="middle"
dominantBaseline="middle"
>
{char}
</text>
)
})}
</svg>
)
}
export function ProcessSection() { export function ProcessSection() {
return ( const { ref, inView } = useInView<HTMLElement>();
<div className="relative flex flex-col items-center justify-center py-[128px] bg-[#0A1128] overflow-hidden">
{/* 배경 글로우 원 */} return (
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] rounded-full bg-[rgba(173,70,255,0.20)] blur-[50px] pointer-events-none" /> <section id="solution" ref={ref} className="py-32 bg-navy-900 text-white px-6 relative overflow-hidden">
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] bg-violet-500/20 rounded-full blur-[120px] pointer-events-none" />
<div className="relative flex flex-col items-center"> <div className="max-w-4xl mx-auto text-center relative z-10">
<MarketingEngineIntro visible={inView} />
{/* 배지 */} <AgdpEngineDiagram visible={inView} />
<div className="flex mb-8 items-center gap-2 border border-white/20 bg-white/10 backdrop-blur-[4px] rounded-[999px] px-4 py-2"> <AgdpCycleSummaryCard visible={inView} />
<img src={twinkle_pink} alt="twinkle" /> </div>
<div className="text-[#F3E8FF] text-center text-xl font-medium leading-5">AI Marketing Engine</div> </section>
</div> );
{/* 타이틀 */}
<div
className="bg-gradient-to-r from-[#FFF3EB] via-[#E4CFFF] to-[#F5F9FF] bg-clip-text text-transparent text-[60px] font-bold leading-[75px] mb-6"
style={{ fontFamily: "var(--font-family-display)" }}
>
Infinite Marketing Engine
</div>
{/* 설명 */}
<div className="flex flex-col items-center gap-1 w-[900px]" style={{ fontFamily: "var(--font-family-inter)" }}>
<span className="text-white text-2xl leading-[39px]">
Infinite Marketing for Premium Medical Business & Marketing Agency
</span>
<span className="text-[#CAD5E2] text-2xl font-light leading-[39px] break-keep">
Infinite Marketing Premium Medical Business Marketing Agency AI Marketing Automation Platform.
CLINICAD , , , , Self-Improving Marketing Engine .
</span>
</div>
{/* 원 레이어 */}
<div className="relative w-[600px] h-[600px] flex items-center justify-center mt-10 mb-16">
<div className="absolute w-[310px] h-[310px] rounded-full border border-dashed border-white/40" />
<RewardSignalText />
{/* 중앙 원 */}
<div className="z-10 w-[224px] h-[224px] rounded-full bg-[#0D0A2E] border border-white/10 flex flex-col items-center justify-center gap-1">
<span className="text-[#E9D4FF] text-2xl font-medium leading-[30px]">AGDP</span>
<span className="text-white text-[30px] font-bold leading-[37.5px] text-center" style={{ fontFamily: "var(--font-family-display)" }}>
Infinite<br />Marketing
</span>
</div>
{/* 위성 원 */}
{AGDP_ITEMS.map(({ letter, label, angle }) => (
<div
key={letter}
className="absolute flex flex-col items-center gap-2"
style={{ transform: `rotate(${angle}deg) translateY(-230px) rotate(-${angle}deg)` }}
>
<div className="w-20 h-20 rounded-full flex items-center justify-center border border-[#AD46FF]/30 bg-[#0A1128]/80 shadow-[0_0_20px_0_rgba(168,85,247,0.15)] backdrop-blur-[4px] text-[#DAB2FF] text-[36px] font-bold leading-10">
{letter}
</div>
<span className="text-[#E9D4FF] text-2xl font-medium leading-[30px]">{label}</span>
</div>
))}
</div>
{/* AGDP Cycle 설명 */}
<div className="flex max-w-[780px] px-4 py-6 rounded-2xl border border-white/10 bg-white/5 backdrop-blur-[4px]">
<span className="text-[#DAB2FF] text-base font-bold leading-6 whitespace-nowrap">AGDP Cycle: &nbsp;</span>
<div className="text-center text-base leading-6 break-keep">
<span className="text-[#CAD5E2]">(Analysis) (Generation) (Distribution) (Performance) (CTR) </span>
<span className="text-white"> </span>
<span className="text-[#CAD5E2]">.</span>
</div>
</div>
</div>
</div>
)
} }

View File

@ -1,55 +1,47 @@
const SOLUTION_CARDS = [ import { SOLUTION_CARDS } from "@/features/home/constants/solution_contents";
{ import { useInView } from "@/hooks/useInView";
title: "Premium Medical Business",
description: "고객 LTV가 높고 브랜드 경쟁이 심해 콘텐츠 마케팅이 필수적인 프리미엄 의료 서비스 제공 병원",
items: ["피부과", "성형외과", "치과", "안과", "헬스케어 클리닉", "피트니스"],
gradient: "from-[#F8FAFC]",
},
{
title: "Medical Marketing Agency",
description: "콘텐츠 제작 비용 부담과 인력 의존도가 높고 영상 제작 생산성 개선이 필요한 병원 마케팅 대행사",
items: ["병원 마케팅 대행사", "콘텐츠 마케팅 Agency", "영상 마케팅 Agency", "광고 운영 대행사"],
gradient: "from-[#FAF5FF]",
},
]
export function SolutionSection() { export function SolutionSection() {
return ( const { ref, inView } = useInView<HTMLElement>();
<div className="flex flex-col items-center justify-center py-[96px]">
<div className="flex flex-col gap-16">
<div className="flex flex-col items-center gap-4">
<div
className="text-[#0A1128] text-center text-[48px] font-bold leading-[48px]"
style={{ fontFamily: "var(--font-family-display)" }}
>
Who is Infinite Marketing for
</div>
<div> </div>
</div>
<div className="flex gap-6"> return (
{SOLUTION_CARDS.map(({ title, description, items, gradient }, index) => ( <section ref={ref} className="py-24 bg-white px-6">
<div <div className="max-w-7xl mx-auto">
key={index}
className={`flex flex-col gap-6 p-[48px] w-[560px] rounded-2xl border border-white/20 bg-gradient-to-br ${gradient} to-white shadow-[0_8px_30px_0_rgba(0,0,0,0.04)] backdrop-blur-[6px]`} <div className={`text-center mb-16 ${inView ? "animate-fade-in-up" : "opacity-0"}`}>
> <h2 className="headline-30 md:display-48 text-navy-900 mb-4 break-keep">
<div className="text-[#0A1128] text-[48px] font-bold leading-[60px]" style={{ fontFamily: "var(--font-family-display)" }}> Who is Infinite Marketing for
{title} </h2>
</div> <p className="body-18 text-neutral-70 max-w-2xl mx-auto break-keep">
<div className="text-[#45556C] text-[18px] leading-[29.25px] break-keep">
{description} </p>
</div>
<div className="flex flex-wrap gap-3 w-full pr-20">
{items.map((item, i) => (
<div key={i} className="flex items-center justify-center h-[50px] px-3 py-5 rounded-[16px] shadow whitespace-nowrap bg-white">
{item}
</div>
))}
</div>
</div>
))}
</div>
</div>
</div> </div>
)
<div className="grid md:grid-cols-2 gap-8">
{SOLUTION_CARDS.map(({ title, description, items, cardClass, tagClass, animClass }) => (
<div
key={title}
className={`p-10 md:p-12 rounded-3xl backdrop-blur-xl shadow-[0_8px_32px_0_rgba(31,38,135,0.07)] ${cardClass} ${
inView ? animClass : "opacity-0"
}`}
>
<h3 className="display-36 md:display-48 leading-tight text-navy-900 mb-6 break-keep">{title}</h3>
<p className="body-18 text-neutral-70 mb-10 leading-relaxed break-keep">{description}</p>
<div className="flex flex-wrap gap-3">
{items.map((item) => (
<span
key={item}
className={`body-14-medium bg-white px-5 py-3 rounded-2xl shadow-sm text-neutral-80 hover:shadow-md transition-shadow break-keep ${tagClass}`}
>
{item}
</span>
))}
</div>
</div>
))}
</div>
</div>
</section>
);
} }

View File

@ -1,56 +1,62 @@
import bg from "@/assets/home/bg_system.png"; import { CORE_MODULES, MODULE_CARD_STAGGER } from "@/features/home/constants/modules_contents";
import { useInView } from "@/hooks/useInView";
const CORE_MODULES = [ import { CoreModuleCard } from "./system/CoreModuleCard";
{ index: 1, title: "Marketing Intelligence", description: ["브랜딩, 마케팅 현황 분석", "타겟 고객 분석", "키워드 분석", "경쟁 및 포지셔닝 분석"], footer: "SEO 전략 & 채널별 콘텐츠 기획", angle: 0 }, import { CoreModulesCenterHeading } from "./system/CoreModulesCenterHeading";
{ index: 2, title: "Content Creation", description: ["블로그 콘텐츠 생성", "SEO 콘텐츠 생성", "SNS 콘텐츠 생성", "마케팅 카피 생성"], footer: "Human-in-the loop 프로세스", angle: 78 },
{ index: 3, title: "Video Automation", description: ["블로그 → 영상 변환", "숏폼 콘텐츠 생성", "유튜브 콘텐츠 제작", "SNS 영상 제작"], footer: "멀티모달 AI 엔진: 영상 + 음악 + 카피", angle: 144 },
{ index: 4, title: "Distribution Engine", description: ["블로그 게시", "SNS 자동 게시", "유튜브 업로드", "콘텐츠 일정 관리"], footer: "SEO, AEO 자동 최적화", angle: 216 },
{ index: 5, title: "Performance Intelligence", description: ["SEO 성과 분석", "콘텐츠 성과 분석", "채널 성과 분석", "AI 콘텐츠 개선 전략 추천"], footer: "데이터 기반 효과 검증", angle: 283 },
]
export function SystemSection() { export function SystemSection() {
return ( const { ref, inView } = useInView<HTMLElement>();
<div className="flex flex-col items-center justify-center py-[128px] bg-cover bg-center" style={{ backgroundImage: `url(${bg})` }}>
<div className="flex flex-col items-center justify-center gap-6 mb-[96px]">
<div className="text-[#0A1128] text-center text-[48px] font-bold leading-[48px]" style={{ fontFamily: "var(--font-family-display)" }}>
Core Modules
</div>
<div className="text-[#45556C] text-[18px] font-normal leading-[29.25px]">
</div>
</div>
<div className="relative w-[1100px] h-[1100px] flex items-center justify-center"> return (
<div className="absolute z-10 text-[#1D293D] text-center text-[31px] font-bold leading-[38.75px] mb-30" style={{ fontFamily: "var(--font-family-display)" }}> <section id="modules" ref={ref} className="py-24 md:py-32 bg-white px-6 overflow-hidden relative">
Self-Improving<br />Growth Engine <div className="absolute top-[-10%] left-[-10%] w-[50vw] h-[50vw] min-w-[600px] min-h-[600px] rounded-full bg-marketing-cream opacity-80 blur-[120px] animate-blob-large pointer-events-none" />
</div> <div className="absolute top-[20%] right-[-10%] w-[40vw] h-[40vw] min-w-[500px] min-h-[500px] rounded-full bg-marketing-lilac opacity-40 blur-[120px] animate-blob-large animation-delay-7000 pointer-events-none" />
<div className="absolute bottom-[-10%] left-[20%] w-[60vw] h-[60vw] min-w-[700px] min-h-[700px] rounded-full bg-marketing-ice opacity-80 blur-[120px] animate-blob-large animation-delay-14000 pointer-events-none" />
{CORE_MODULES.map(({ index, title, description, footer, angle }) => ( <div className="max-w-7xl mx-auto relative z-10">
<div <div className={`text-center mb-16 md:mb-24 ${inView ? "animate-fade-in-up" : "opacity-0"}`}>
key={index} <h2 className="display-36 md:display-48 text-navy-900 mb-6 break-keep">Core Modules</h2>
className="absolute flex flex-col items-start p-[25px] rounded-[16px] bg-white shadow" <p className="body-18-md-20 text-neutral-70 max-w-2xl mx-auto break-keep">
style={{ transform: `rotate(${angle}deg) translateY(-430px) rotate(-${angle}deg)` }}
> </p>
<div className="flex items-center gap-3 mb-5">
<div className="flex items-center justify-center w-10 h-10 rounded-[16px] bg-[#021341] text-white text-lg font-bold leading-7">
{index}
</div>
<div className="text-[#1D293D] text-[24px] font-bold leading-[25px] tracking-[-0.5px]" style={{ fontFamily: "var(--font-family-display)" }}>
{title}
</div>
</div>
<div className="flex flex-col gap-1 mb-6">
{description.map((desc) => (
<div key={desc} className="text-[#45556C] text-[18px] leading-[29.25px] break-keep">{desc}</div>
))}
</div>
<div className="bg-[#F1F5F9] w-full h-[1px] mb-4" />
<div className="text-[#4F39F6] text-xl font-bold leading-[22.5px] tracking-[-0.375px] break-keep">{footer}</div>
</div>
))}
</div>
</div> </div>
)
<div className="hidden lg:block relative max-w-[1200px] mx-auto mt-10" style={{ height: "960px" }}>
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
<CoreModulesCenterHeading visible={inView} />
</div>
<div className="absolute left-1/2 -translate-x-1/2 top-0">
<CoreModuleCard mod={CORE_MODULES[0]} className="w-[300px]" visible={inView} delayClass={MODULE_CARD_STAGGER[0]} />
</div>
<div className="absolute right-[20px] top-[130px]">
<CoreModuleCard mod={CORE_MODULES[1]} className="w-[300px]" visible={inView} delayClass={MODULE_CARD_STAGGER[1]} />
</div>
<div className="absolute right-[80px] bottom-[30px]">
<CoreModuleCard mod={CORE_MODULES[2]} className="w-[300px]" visible={inView} delayClass={MODULE_CARD_STAGGER[2]} />
</div>
<div className="absolute left-[80px] bottom-[30px]">
<CoreModuleCard mod={CORE_MODULES[3]} className="w-[300px]" visible={inView} delayClass={MODULE_CARD_STAGGER[3]} />
</div>
<div className="absolute left-[20px] top-[130px]">
<CoreModuleCard mod={CORE_MODULES[4]} className="w-[300px]" visible={inView} delayClass={MODULE_CARD_STAGGER[4]} />
</div>
</div>
<div className="lg:hidden flex flex-col gap-8 items-center">
<CoreModulesCenterHeading visible={inView} />
<div className="w-full grid md:grid-cols-2 gap-6 mt-8">
{CORE_MODULES.map((mod, idx) => (
<CoreModuleCard
key={mod.step}
mod={mod}
className="w-full"
visible={inView}
delayClass={MODULE_CARD_STAGGER[idx]}
/>
))}
</div>
</div>
</div>
</section>
);
} }

View File

@ -1,56 +1,44 @@
const CheckIcon = ({ fill }: { fill: string }) => ( import CheckCircleIcon from "@/assets/home/check-circle.svg?react";
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"> import { USE_CASE_CARDS } from "@/features/home/constants/use_cases_contents";
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke={fill} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> import { useInView } from "@/hooks/useInView";
<path d="M9 12L11 14L15 10" stroke={fill} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
)
const USE_CASES = [
{
title: "Premium Medical Business",
descriptions: ["SEO 콘텐츠 자동 생산으로 검색 상위 노출 달성", "비용 부담 없이 고품질 영상 콘텐츠 대량 확대", "자연 검색 유입 증가로 인한 환자 전환율 상승"],
color: "#2B7FFF",
gradient: "from-[#EFF6FF]",
},
{
title: "Marketing Agency",
descriptions: ["AI 기반 콘텐츠 제작 자동화로 생산성 극대화", "블로그 텍스트 기반 영상 제작 자동화로 리소스 절감", "다수 클라이언트 계정의 통합 운영 및 효율화"],
color: "#AD46FF",
gradient: "from-[#FAF5FF]",
},
]
export function UseCaseSection() { export function UseCaseSection() {
return ( const { ref, inView } = useInView<HTMLElement>();
<div className="flex flex-col items-center justify-center py-[96px] bg-white">
<div className="flex flex-col items-center gap-4 pb-16">
<div className="text-[#0A1128] text-center text-[48px] font-bold leading-[48px]" style={{ fontFamily: "var(--font-family-display)" }}>
Use Cases
</div>
<div className="text-[#45556C] text-[18px] leading-[29.25px] break-keep">
Infinite Marketing !
</div>
</div>
<div className="flex gap-6"> return (
{USE_CASES.map(({ title, descriptions, color, gradient }, index) => ( <section id="use-cases" ref={ref} className="py-24 bg-white px-6">
<div <div className="max-w-7xl mx-auto">
key={index} <div className={`text-center mb-16 ${inView ? "animate-fade-in-up" : "opacity-0"}`}>
className={`flex flex-col w-[560px] h-[227px] p-10 items-start justify-center rounded-2xl border border-white/20 bg-gradient-to-br ${gradient} to-white shadow-[0_8px_30px_0_rgba(0,0,0,0.04)] backdrop-blur-[6px]`} <h2 className="headline-30 md:display-48 text-navy-900 mb-4 break-keep">Use Cases</h2>
> <p className="body-18 font-medium text-neutral-70 max-w-2xl mx-auto break-keep">
<div className="text-[#0A1128] text-[24px] font-bold leading-[32px] pb-[22.5px]" style={{ fontFamily: "var(--font-family-display)" }}> Infinite Marketing !
{title} </p>
</div>
<div className="flex flex-col gap-4">
{descriptions.map((desc, i) => (
<div key={i} className="flex items-center gap-2">
<CheckIcon fill={color} /> {desc}
</div>
))}
</div>
</div>
))}
</div>
</div> </div>
)
<div className="grid md:grid-cols-2 gap-12">
{USE_CASE_CARDS.map((c) => (
<div
key={c.title}
className={`${c.cardClass} ${inView ? c.animClass : "opacity-0"}`}
>
<h3 className="headline-24 md:headline-30 text-navy-900 mb-8 break-keep">{c.title}</h3>
<ul className="space-y-6 list-none m-0 p-0">
{c.items.map((item) => (
<li key={item} className="flex items-start gap-4 text-neutral-80 font-medium group break-keep">
<CheckCircleIcon
aria-hidden="true"
className={`w-6 h-6 shrink-0 mt-1 ${c.iconClass}`}
/>
<span className="leading-relaxed body-18 group-hover:text-navy-900 transition-colors break-keep">
{item}
</span>
</li>
))}
</ul>
</div>
))}
</div>
</div>
</section>
);
} }

View File

@ -0,0 +1,22 @@
type Props = { visible: boolean };
export function AgdpCycleSummaryCard({ visible }: Props) {
return (
<div
className={`max-w-3xl mx-auto mt-12 text-center ${
visible ? "animate-fade-in-up animation-delay-500" : "opacity-0"
}`}
>
<div className="inline-block bg-white/5 border border-white/10 rounded-2xl px-6 py-4 backdrop-blur-sm">
<p className="body-14-md-16 text-neutral-40 break-keep">
<span className="font-bold text-lavender-300 break-keep">AGDP Cycle:</span>{" "}
<span className="break-keep">
(Analysis) (Generation) (Distribution) (Performance) (CTR){" "}
</span>
<span className="text-white font-medium break-keep"> </span>
<span className="break-keep">.</span>
</p>
</div>
</div>
);
}

View File

@ -0,0 +1,55 @@
import { AGDP_NODES } from "@/features/home/constants/process_contents";
import { AgdpOrbitNode } from "./AgdpOrbitNode";
import { AgdpRewardPathLabel } from "./AgdpRewardPathLabel";
type Props = { visible: boolean };
export function AgdpEngineDiagram({ visible }: Props) {
return (
<div
className={`relative w-full max-w-[320px] md:max-w-[500px] aspect-square mx-auto mt-16 mb-24 md:mb-32 ${
visible ? "animate-fade-in-scale animation-delay-300" : "opacity-0"
}`}
>
<div className="absolute top-[20%] left-[20%] right-[20%] bottom-[20%] rounded-full border border-white/5 shadow-[0_0_40px_rgba(255,255,255,0.02)_inset]" />
<svg className="absolute inset-0 w-full h-full animate-[spin_20s_linear_infinite]" viewBox="0 0 100 100" aria-hidden="true">
<defs>
<linearGradient id="agdp-ring-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="var(--color-violet-500)" stopOpacity="0.1" />
<stop offset="50%" stopColor="var(--color-lavender-300)" stopOpacity="0.5" />
<stop offset="100%" stopColor="var(--color-violet-500)" stopOpacity="0.1" />
</linearGradient>
</defs>
<circle
cx="50"
cy="50"
r="30"
fill="none"
stroke="url(#agdp-ring-grad)"
strokeWidth="0.5"
strokeDasharray="2 2"
/>
</svg>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-20">
<div className="w-40 h-40 md:w-56 md:h-56 rounded-full bg-navy-900/90 backdrop-blur-xl border border-white/5 flex flex-col items-center justify-center shadow-[0_0_60px_rgba(167,139,250,0.1)]">
<span className="display-36 md:display-48 text-transparent bg-clip-text bg-gradient-to-r from-marketing-cream via-marketing-lilac to-marketing-ice mb-2 break-keep">
AGDP
</span>
<h3 className="headline-20 md:headline-30 text-white text-center leading-tight break-keep">
Infinite
<br />
Marketing
</h3>
</div>
</div>
{AGDP_NODES.map((node) => (
<AgdpOrbitNode key={node.letter} node={node} />
))}
<AgdpRewardPathLabel />
</div>
);
}

View File

@ -0,0 +1,21 @@
import type { AgdpNodeDef } from "@/features/home/constants/process_contents";
import { AGDP_SLOT_WRAPPER_CLASS } from "@/features/home/constants/process_contents";
type Props = { node: AgdpNodeDef };
export function AgdpOrbitNode({ node }: Props) {
const wrapperClass = AGDP_SLOT_WRAPPER_CLASS[node.slot];
return (
<div className={wrapperClass}>
<div className="w-16 h-16 md:w-20 md:h-20 rounded-full border border-violet-500/30 bg-navy-900/80 backdrop-blur-sm flex items-center justify-center shadow-[0_0_20px_rgba(168,85,247,0.15)]">
<span className="font-sans font-bold text-3xl md:text-4xl text-lavender-300">{node.letter}</span>
</div>
<div className="text-center">
<span className="block body-18-md-20 font-medium text-lavender-200 leading-tight break-keep">
{node.label}
</span>
</div>
</div>
);
}

View File

@ -0,0 +1,17 @@
/** P→A 호 SVG 경로 위 "Reward" 라벨 (DEMO Solution.tsx) */
export function AgdpRewardPathLabel() {
const pathId = "agdp-reward-path";
return (
<svg className="absolute inset-0 w-full h-full pointer-events-none z-20" viewBox="0 0 100 100" aria-hidden="true">
<defs>
<path id={pathId} d="M 10.6 56.9 A 40 40 0 0 0 43.1 89.4" fill="none" />
</defs>
<text fontSize="3.5" className="break-keep font-medium uppercase tracking-widest fill-lavender-300" opacity="0.8">
<textPath href={`#${pathId}`} startOffset="50%" textAnchor="middle">
Reward SIGNAL
</textPath>
</text>
</svg>
);
}

View File

@ -0,0 +1,40 @@
import twinklePink from "@/assets/home/twinkle_pink.svg";
type Props = { visible: boolean };
export function MarketingEngineIntro({ visible }: Props) {
return (
<>
<div
className={`inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white/10 backdrop-blur-sm border border-white/20 mb-8 ${
visible ? "animate-fade-in-up" : "opacity-0"
}`}
>
<img src={twinklePink} alt="" className="w-4 h-4 shrink-0" width={16} height={16} />
<span className="body-14-medium text-lavender-100 break-keep">AI Marketing Engine</span>
</div>
<h2
className={`display-36 md:display-48 mb-6 leading-tight text-transparent bg-clip-text bg-gradient-to-r from-marketing-cream via-marketing-lilac to-marketing-ice break-keep ${
visible ? "animate-fade-in-up animation-delay-100" : "opacity-0"
}`}
>
Infinite Marketing Engine
</h2>
<p
className={`body-18-md-20 text-neutral-40 mb-12 max-w-3xl mx-auto leading-relaxed font-light break-keep ${
visible ? "animate-fade-in-up animation-delay-200" : "opacity-0"
}`}
>
<span className="body-20-medium text-neutral-00 break-keep">
Infinite Marketing for Premium Medical Business &amp; Marketing Agency
</span>
<br className="hidden md:block" />
Infinite Marketing Premium Medical Business Marketing Agency AI Marketing Automation Platform.
INFINITH , , , , Self-Improving Marketing
Engine .
</p>
</>
);
}

View File

@ -0,0 +1,37 @@
import type { CoreModule } from "@/features/home/constants/modules_contents";
type Props = {
mod: CoreModule;
className?: string;
visible: boolean;
delayClass?: string;
};
export function CoreModuleCard({ mod, className = "", visible, delayClass = "" }: Props) {
return (
<div
className={`bg-white rounded-2xl p-5 md:p-6 shadow-xl shadow-neutral-30/50 border border-neutral-20 flex flex-col ${className} ${
visible ? `animate-fade-in-up ${delayClass}`.trim() : "opacity-0"
}`}
>
<div className="flex items-center gap-3 mb-5">
<div className="w-10 h-10 shrink-0 rounded-xl flex items-center justify-center text-white title-18 bg-navy-950">
{mod.step}
</div>
<h3 className="font-sans title-18-md-20 text-navy-800 leading-tight tracking-tight break-keep">{mod.title}</h3>
</div>
<ul className="space-y-3 mb-6 flex-grow list-none m-0 p-0">
{mod.items.map((item) => (
<li key={item} className="body-14-md-16 text-neutral-70 leading-relaxed break-keep">
{item}
</li>
))}
</ul>
<div className="mt-auto pt-4 border-t border-neutral-20 font-bold body-14-md-16 tracking-tight text-violet-700 break-keep">
{mod.highlight}
</div>
</div>
);
}

View File

@ -0,0 +1,20 @@
type Props = { visible: boolean };
/** Core Modules 섹션 중앙 — Self-Improving Growth Engine 타이틀 */
export function CoreModulesCenterHeading({ visible }: Props) {
return (
<div
className={`relative w-72 h-72 md:w-[400px] md:h-[400px] flex items-center justify-center ${
visible ? "animate-fade-in-up" : "opacity-0"
}`}
>
<div className="absolute inset-0 flex flex-col items-center justify-center text-center z-10">
<h3 className="headline-24 md:headline-30 text-navy-800 leading-tight break-keep">
Self-Improving
<br />
Growth Engine
</h3>
</div>
</div>
);
}