- Vite + React 19 + Tailwind CSS - 4가지 디자인 레퍼런스 통합 (CRT 스크린, 크로스헤어, 노이즈, 인스트루먼트) - 섹션 9개: Hero, Features, HowItWorks, Privacy, Pricing, FAQ, CTA, Header, Footer - 재사용 컴포넌트 8개 (Badge, Container, InstrumentCard, GlowButton 등) - i18n 10개 국어 (en/ko/ja/zh/es/fr/de/pt/ru/vi) - GitHub Pages 자동 배포 워크플로우
166 lines
6.1 KiB
TypeScript
166 lines
6.1 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
import { SectionHeader } from '../components/SectionHeader'
|
|
import { InstrumentCard } from '../components/InstrumentCard'
|
|
import { Container } from '../components/Container'
|
|
import { useI18n } from '../i18n'
|
|
|
|
interface FeatureMeta {
|
|
icon: ReactNode
|
|
tag: 'FREE' | 'PRO' | 'PRO+'
|
|
}
|
|
|
|
const featureMeta: FeatureMeta[] = [
|
|
{ icon: <MicIcon />, tag: 'FREE' },
|
|
{ icon: <SparklesIcon />, tag: 'FREE' },
|
|
{ icon: <LanguageIcon />, tag: 'FREE' },
|
|
{ icon: <CaptionIcon />, tag: 'PRO' },
|
|
{ icon: <ConversationIcon />, tag: 'PRO+' },
|
|
{ icon: <FileIcon />, tag: 'PRO+' },
|
|
{ icon: <ChainIcon />, tag: 'PRO' },
|
|
{ icon: <ContextIcon />, tag: 'PRO' },
|
|
{ icon: <MemoIcon />, tag: 'PRO' },
|
|
]
|
|
|
|
const tagStyles = {
|
|
FREE: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20',
|
|
PRO: 'text-brand-amber bg-brand-amber/10 border-brand-amber/20',
|
|
'PRO+': 'text-violet-400 bg-violet-500/10 border-violet-500/20',
|
|
} as const
|
|
|
|
export function Features() {
|
|
const { t } = useI18n()
|
|
|
|
return (
|
|
<section id="features" className="relative py-24 md:py-32">
|
|
<Container>
|
|
<SectionHeader
|
|
index={t.features.index}
|
|
title={t.features.title}
|
|
subtitle={t.features.subtitle}
|
|
/>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{t.features.items.map((item, i) => {
|
|
const meta = featureMeta[i]
|
|
return (
|
|
<InstrumentCard key={i}>
|
|
{/* Top row: icon + tag */}
|
|
<div className="flex items-start justify-between mb-6">
|
|
<div className="w-10 h-10 rounded-card bg-surface-400 border border-white/[0.06] flex items-center justify-center text-brand-amber">
|
|
{meta.icon}
|
|
</div>
|
|
<span className={`text-nano font-mono font-semibold tracking-widest uppercase px-2 py-0.5 border rounded-sm ${tagStyles[meta.tag]}`}>
|
|
{meta.tag}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<h3 className="font-display text-lg font-semibold text-neutral-100 mb-2">
|
|
{item.title}
|
|
</h3>
|
|
<p className="text-sm text-neutral-400 leading-relaxed">
|
|
{item.description}
|
|
</p>
|
|
|
|
{/* Bottom arrow */}
|
|
<div className="mt-5 flex justify-end">
|
|
<div className="w-7 h-7 rounded-full border border-white/[0.06] flex items-center justify-center text-neutral-500 group-hover:text-brand-amber group-hover:border-brand-amber/20 transition-colors">
|
|
<svg className="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
<line x1="7" y1="17" x2="17" y2="7" />
|
|
<polyline points="7 7 17 7 17 17" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</InstrumentCard>
|
|
)
|
|
})}
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
/* ── Inline SVG Icons ─────────────────────────────────── */
|
|
|
|
function MicIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<rect x="9" y="2" width="6" height="11" rx="3" />
|
|
<path d="M5 10a7 7 0 0 0 14 0" />
|
|
<line x1="12" y1="17" x2="12" y2="21" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function SparklesIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M12 2l2.4 7.2L22 12l-7.6 2.8L12 22l-2.4-7.2L2 12l7.6-2.8z" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function LanguageIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M5 8l6 6" /><path d="M4 14l6-6 2-3" /><path d="M2 5h12" /><path d="M7 2h1" />
|
|
<path d="M22 22l-5-10-5 10" /><path d="M14 18h6" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function CaptionIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<rect x="2" y="4" width="20" height="16" rx="2" />
|
|
<path d="M7 15h4" /><path d="M13 15h4" /><path d="M7 11h10" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function ConversationIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function FileIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
<polyline points="14 2 14 8 20 8" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function ChainIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
|
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function ContextIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<rect x="2" y="3" width="20" height="14" rx="2" />
|
|
<line x1="8" y1="21" x2="16" y2="21" />
|
|
<line x1="12" y1="17" x2="12" y2="21" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function MemoIcon() {
|
|
return (
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
<polyline points="14 2 14 8 20 8" />
|
|
<line x1="16" y1="13" x2="8" y2="13" />
|
|
<line x1="16" y1="17" x2="8" y2="17" />
|
|
</svg>
|
|
)
|
|
}
|