d3ro-voice/site/src/sections/Features.tsx
Yun Chan 5892125740 랜딩 페이지: D3RO 브랜드 프로모션 사이트 + GitHub Pages 배포
- 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 자동 배포 워크플로우
2026-04-05 23:55:13 +09:00

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>
)
}