feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
|
|
@ -10,24 +10,24 @@ export function CTA() {
|
|||
const clientOS = useClientOS()
|
||||
|
||||
return (
|
||||
<section className="relative py-28 md:py-36 overflow-hidden">
|
||||
<section className="relative py-24 md:py-32 overflow-hidden">
|
||||
{/* Background radial glow */}
|
||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[500px] bg-brand-amber/[0.045] rounded-full blur-[140px] pointer-events-none" />
|
||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[700px] h-[550px] bg-brand-blue/[0.06] rounded-full blur-[160px] pointer-events-none" />
|
||||
|
||||
<Container className="relative text-center">
|
||||
<Crosshair className="inline-block mb-8">
|
||||
<WaveBars className="h-9 px-3" />
|
||||
<Crosshair className="inline-block mb-7">
|
||||
<WaveBars className="h-8 px-4" />
|
||||
</Crosshair>
|
||||
|
||||
<h2
|
||||
className="font-display text-display font-extrabold text-neutral-50 mb-6 max-w-3xl mx-auto tracking-tight break-keep"
|
||||
className="text-3xl sm:text-4xl md:text-5xl font-medium text-neutral-50 mb-5 max-w-3xl mx-auto tracking-tight leading-[1.2] break-keep"
|
||||
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
|
||||
>
|
||||
{t.cta.title1} <span className="text-brand-amber">{t.cta.title2}</span>
|
||||
{t.cta.title1} <span className="text-brand-blue font-medium">{t.cta.title2}</span>
|
||||
</h2>
|
||||
|
||||
<p
|
||||
className="text-base md:text-lg text-neutral-400 max-w-xl mx-auto mb-8 leading-relaxed break-keep"
|
||||
className="text-base sm:text-lg text-neutral-300 max-w-xl mx-auto mb-7 leading-relaxed font-normal break-keep"
|
||||
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
|
||||
>
|
||||
{t.cta.subtitle1}
|
||||
|
|
@ -36,40 +36,40 @@ export function CTA() {
|
|||
</p>
|
||||
|
||||
{/* Hotkey Hint Box */}
|
||||
<div className="inline-flex items-center gap-2 mb-10 px-4 py-2 rounded-xl bg-surface-800/80 border border-white/[0.08] shadow-inner">
|
||||
<span className="font-mono text-nano text-neutral-400 uppercase tracking-widest">
|
||||
Push-to-Talk:
|
||||
<div className="inline-flex items-center gap-3 mb-9 px-4.5 py-2.5 rounded-2xl bg-surface-800/90 border border-white/10 shadow-glass-card">
|
||||
<span className="text-xs text-neutral-400 font-normal">
|
||||
푸시투톡 단축키:
|
||||
</span>
|
||||
<div className="flex items-center gap-1 font-mono text-xs font-semibold text-neutral-200">
|
||||
<span className="px-2 py-0.5 rounded bg-surface-600 border border-white/[0.1]">Ctrl</span>
|
||||
<span className="text-neutral-500">+</span>
|
||||
<span className="px-2 py-0.5 rounded bg-surface-600 border border-white/[0.1]">Shift</span>
|
||||
<span className="text-neutral-500">+</span>
|
||||
<span className="px-2.5 py-0.5 rounded bg-brand-amber/20 border border-brand-amber/40 text-brand-amber">Space</span>
|
||||
<div className="flex items-center gap-1.5 font-mono text-xs font-medium text-neutral-200">
|
||||
<span className="px-2.5 py-1 rounded-md bg-surface-700 border border-white/10 shadow-sm">Ctrl</span>
|
||||
<span className="text-neutral-400">+</span>
|
||||
<span className="px-2.5 py-1 rounded-md bg-surface-700 border border-white/10 shadow-sm">Shift</span>
|
||||
<span className="text-neutral-400">+</span>
|
||||
<span className="px-3 py-1 rounded-md bg-brand-blue/20 border border-brand-blue/40 text-brand-blue shadow-sm">Space</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className="flex flex-col items-center gap-3.5">
|
||||
<GlowButton
|
||||
href={clientOS.downloadUrl}
|
||||
download={clientOS.downloadFilename || undefined}
|
||||
variant="primary"
|
||||
size="lg"
|
||||
className="shadow-glow-md hover:shadow-glow-lg"
|
||||
className="shadow-glow-md hover:shadow-glow-lg text-base sm:text-lg font-medium px-10 sm:px-12 py-4.5 rounded-2xl"
|
||||
>
|
||||
{locale === 'ko' ? clientOS.buttonLabelKo : clientOS.buttonLabelEn}
|
||||
</GlowButton>
|
||||
<div className="flex items-center gap-2 font-mono text-xs text-neutral-400">
|
||||
<div className="flex items-center gap-2 text-xs text-neutral-400 font-normal">
|
||||
<span>{locale === 'ko' ? clientOS.subtextKo : clientOS.subtextEn}</span>
|
||||
<span>·</span>
|
||||
<a href="#download" className="underline hover:text-brand-amber transition-colors">
|
||||
{locale === 'ko' ? '다른 OS 다운로드' : 'Other Platforms'}
|
||||
<a href="#download" className="underline text-brand-blue hover:text-brand-blue-light transition-colors font-medium">
|
||||
{locale === 'ko' ? '릴리스 준비 상태' : 'Release readiness'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="font-mono text-nano text-neutral-500 mt-8 uppercase tracking-widest">
|
||||
{t.cta.systemReq} · 100% OPEN ARCHITECTURE
|
||||
<p className="text-xs text-neutral-400 mt-9 font-normal">
|
||||
{t.cta.systemReq} · 100% 오픈 아키텍처
|
||||
</p>
|
||||
</Container>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,42 +1,17 @@
|
|||
import { useState, useEffect, type ChangeEvent, type DragEvent } from 'react'
|
||||
import { useState, type ChangeEvent, type DragEvent } from 'react'
|
||||
import { Container } from '../components/Container'
|
||||
import { SectionHeader } from '../components/SectionHeader'
|
||||
import { Led } from '../components/Led'
|
||||
import { useI18n } from '../i18n'
|
||||
|
||||
const OFFICIAL_HASH = 'b0ac051443151a2e34e8192f1fcf795586bbafca6eb21f01a79170c079a53ba2'
|
||||
|
||||
export function Download() {
|
||||
const { locale } = useI18n()
|
||||
const isKo = locale === 'ko'
|
||||
|
||||
const [detectedOs, setDetectedOs] = useState<'windows' | 'mac' | 'linux'>('windows')
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [verifyStatus, setVerifyStatus] = useState<'idle' | 'computing' | 'match' | 'custom'>('idle')
|
||||
const [verifyStatus, setVerifyStatus] = useState<'idle' | 'computing' | 'custom'>('idle')
|
||||
const [computedHash, setComputedHash] = useState('')
|
||||
const [fileName, setFileName] = useState('')
|
||||
const [isDragOver, setIsDragOver] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const ua = window.navigator.userAgent.toLowerCase()
|
||||
if (ua.includes('mac') || ua.includes('darwin')) {
|
||||
setDetectedOs('mac')
|
||||
} else if (ua.includes('linux')) {
|
||||
setDetectedOs('linux')
|
||||
} else {
|
||||
setDetectedOs('windows')
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const copyHash = (hash: string) => {
|
||||
navigator.clipboard.writeText(hash).then(() => {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
})
|
||||
}
|
||||
|
||||
const computeFileHash = async (file: File) => {
|
||||
setFileName(`${file.name} (${(file.size / (1024 * 1024)).toFixed(1)} MB)`)
|
||||
setVerifyStatus('computing')
|
||||
|
|
@ -48,11 +23,7 @@ export function Download() {
|
|||
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
|
||||
|
||||
setComputedHash(hashHex)
|
||||
if (hashHex.toLowerCase() === OFFICIAL_HASH.toLowerCase()) {
|
||||
setVerifyStatus('match')
|
||||
} else {
|
||||
setVerifyStatus('custom')
|
||||
}
|
||||
setVerifyStatus('custom')
|
||||
} catch {
|
||||
setVerifyStatus('custom')
|
||||
}
|
||||
|
|
@ -85,287 +56,238 @@ export function Download() {
|
|||
}
|
||||
|
||||
return (
|
||||
<section id="download" className="py-24 md:py-32 bg-surface-900/60 border-t border-white/[0.04] relative">
|
||||
<section id="download" className="py-20 md:py-28 bg-surface-900/60 border-t border-white/[0.04] relative">
|
||||
<Container>
|
||||
<SectionHeader
|
||||
index="06 / DOWNLOAD"
|
||||
title={isKo ? 'D3RO Voice 다운로드 및 배포 센터' : 'Download D3RO Voice Desktop'}
|
||||
title={isKo ? 'D3RO Voice 릴리스 준비 센터' : 'D3RO Voice Release Preparation'}
|
||||
subtitle={
|
||||
isKo
|
||||
? '100% 로컬 Whisper 전사 엔진과 AI 음성 비서를 데스크톱에 무료로 설치하세요. 클라우드 전송 없이 프라이버시가 완벽히 보장됩니다.'
|
||||
: 'Zero-latency on-device speech assistant with offline Whisper model. Instant installation for Windows, macOS, and private servers.'
|
||||
? '다음 안정 버전 1.1.0의 설치 파일, 서명, 업데이트 경로를 검증하고 있습니다. 검증이 끝나기 전에는 바이너리를 제공하지 않습니다.'
|
||||
: 'Version 1.1.0 installers, signatures, and update paths are being verified. No binary is offered before verification completes.'
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Primary Recommended OS Download Card */}
|
||||
<div className="max-w-3xl mx-auto mb-16 p-8 md:p-10 rounded-2xl bg-surface-800/90 border border-brand-amber/40 shadow-glow-md relative overflow-hidden backdrop-blur-md">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-brand-amber/10 rounded-full blur-3xl pointer-events-none" />
|
||||
<div className="max-w-3xl mx-auto mb-16 p-7 md:p-9 rounded-3xl bg-surface-800/90 border border-brand-blue/40 shadow-glow-md relative overflow-hidden backdrop-blur-2xl noise-texture">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-brand-blue/10 rounded-full blur-3xl pointer-events-none" />
|
||||
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-6 mb-8">
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-6 mb-7">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 rounded-xl bg-brand-amber/15 border border-brand-amber/30 flex items-center justify-center text-brand-amber shadow-sm">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<div className="w-13 h-13 rounded-2xl bg-brand-blue/15 border border-brand-blue/30 flex items-center justify-center text-brand-blue shadow-sm p-3">
|
||||
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
|
||||
<path d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className="font-display text-xl font-bold text-neutral-100">
|
||||
{detectedOs === 'mac' ? 'macOS Apple Silicon' : 'Windows 64-bit Installer'}
|
||||
<div className="flex items-center gap-2.5 mb-1">
|
||||
<h3 className="text-xl sm:text-2xl font-medium text-neutral-50">
|
||||
D3RO Voice Desktop 1.1.0
|
||||
</h3>
|
||||
<span className="px-2 py-0.5 rounded-full text-[10px] font-mono font-bold bg-emerald-500/20 text-emerald-400 border border-emerald-500/30">
|
||||
{isKo ? '추천 빌드' : 'RECOMMENDED'}
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-500/20 text-emerald-300 border border-emerald-500/35">
|
||||
{isKo ? '배포 준비 중' : 'RELEASE PENDING'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-mono text-xs text-neutral-400">
|
||||
{detectedOs === 'mac'
|
||||
? 'D3RO-Voice-1.0.0-arm64.dmg · 98 MB · Metal Hardware Accel'
|
||||
: 'D3RO-Voice-Setup-1.0.0-x64.exe · 102 MB · NSIS Installer'}
|
||||
<p className="text-xs sm:text-sm text-neutral-300 font-normal">
|
||||
{isKo
|
||||
? 'Windows x64 및 macOS Apple Silicon 후보 빌드 검증 중'
|
||||
: 'Windows x64 and macOS Apple Silicon candidate builds under verification'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Led color="green" pulse />
|
||||
<span className="font-mono text-xs text-neutral-400">v1.0.0 STABLE</span>
|
||||
<div className="flex items-center gap-2 bg-surface-700/80 px-3 py-1 rounded-full border border-white/10">
|
||||
<span className="w-2 h-2 rounded-full bg-amber-400" />
|
||||
<span className="text-xs font-medium text-neutral-200">v1.1.0 VERIFICATION</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Primary Action Button */}
|
||||
<div className="mb-6">
|
||||
<a
|
||||
href={
|
||||
detectedOs === 'mac'
|
||||
? 'https://git.chanpaca.net/yunchan/d3ro-voice/releases'
|
||||
: 'https://git.chanpaca.net/attachments/c281a224-2538-496a-bf74-bc188938c734'
|
||||
}
|
||||
download={detectedOs === 'mac' ? undefined : 'D3RO-Voice-Setup-1.0.0-x64.exe'}
|
||||
className="w-full py-4 px-8 rounded-xl bg-brand-amber hover:bg-brand-amber-light text-white font-mono text-sm font-bold uppercase tracking-wider flex items-center justify-center gap-3 shadow-glow-sm hover:shadow-glow-md transition-all active:scale-[0.99]"
|
||||
<div className="mb-5">
|
||||
<div
|
||||
aria-disabled="true"
|
||||
className="w-full py-4.5 sm:py-5 px-8 sm:px-10 rounded-2xl bg-surface-700 text-neutral-300 text-base sm:text-lg font-medium flex items-center justify-center gap-3 leading-normal border border-white/10 cursor-not-allowed"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
|
||||
<path d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
<span>
|
||||
{detectedOs === 'mac'
|
||||
? isKo
|
||||
? 'macOS용 다운로드 (v1.0.0 DMG)'
|
||||
: 'Download for macOS (v1.0.0)'
|
||||
: isKo
|
||||
? 'Windows용 다운로드 (v1.0.0 NSIS)'
|
||||
: 'Download for Windows (v1.0.0)'}
|
||||
</span>
|
||||
</a>
|
||||
<span>{isKo ? '설치 파일 검증 후 제공' : 'Installer available after verification'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SHA-256 Quick Verification Strip */}
|
||||
<div className="pt-4 border-t border-white/[0.08] flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 text-xs">
|
||||
<div className="flex items-center gap-2 font-mono text-neutral-400">
|
||||
<span>SHA-256:</span>
|
||||
<span className="text-neutral-200 truncate max-w-[280px]">b0ac051443151a2e34e8192f1fcf7955...</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => copyHash(OFFICIAL_HASH)}
|
||||
className="text-brand-amber hover:text-brand-amber-light underline font-sans text-xs ml-1"
|
||||
>
|
||||
{copied ? (isKo ? '복사됨!' : 'Copied!') : isKo ? '복사' : 'Copy'}
|
||||
</button>
|
||||
<div className="pt-3.5 border-t border-white/10 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 text-xs">
|
||||
<div className="flex items-center gap-2 text-neutral-300">
|
||||
<span className="font-medium text-neutral-400">{isKo ? '릴리스 증거:' : 'Release evidence:'}</span>
|
||||
<span className="text-neutral-200">{isKo ? '아티팩트 생성 전' : 'Artifact not yet published'}</span>
|
||||
</div>
|
||||
<span className="font-mono text-[11px] text-emerald-400 flex items-center gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse" />
|
||||
{isKo ? '코드 서명 및 무결성 검증 완료' : 'Signed & Verified'}
|
||||
<span className="text-xs text-amber-300 font-medium flex items-center gap-1.5">
|
||||
<span className="w-2 h-2 rounded-full bg-amber-400" />
|
||||
{isKo ? '서명 및 업데이트 검증 대기' : 'Signing and update verification pending'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* All Platform Packages Bento Grid */}
|
||||
<div className="mb-20">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h3 className="font-display text-lg font-bold text-neutral-200">
|
||||
<div className="mb-16">
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<h3 className="text-xl font-medium text-neutral-100">
|
||||
{isKo ? '전체 플랫폼 설치 패키지' : 'All Platform Packages'}
|
||||
</h3>
|
||||
<span className="font-mono text-xs text-neutral-500">Auto-Update Feed: latest.yml Active</span>
|
||||
<span className="text-xs text-neutral-400 font-normal">
|
||||
{isKo ? '자동 업데이트: 서명된 피드 검증 후 활성화' : 'Auto-update: activates after signed feed verification'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{/* Windows Package */}
|
||||
<div className="p-6 rounded-xl bg-surface-800/60 border border-white/[0.06] hover:border-brand-amber/30 transition-all flex flex-col justify-between">
|
||||
<div className="p-6 rounded-2xl bg-surface-800/80 border border-white/10 hover:border-brand-blue/40 shadow-glass-card backdrop-blur-xl transition-all flex flex-col justify-between">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="font-mono text-xs font-semibold text-brand-amber">WINDOWS 10 / 11</span>
|
||||
<span className="font-mono text-[10px] text-neutral-500">x64</span>
|
||||
<span className="text-xs font-medium text-brand-blue">WINDOWS 10 / 11</span>
|
||||
<span className="font-mono text-xs text-neutral-400">x64</span>
|
||||
</div>
|
||||
<h4 className="font-display text-base font-bold text-neutral-100 mb-1">Windows Setup</h4>
|
||||
<p className="text-xs text-neutral-400 mb-6 leading-relaxed">
|
||||
<h4 className="text-lg font-medium text-neutral-50 mb-1.5">Windows Setup</h4>
|
||||
<p className="text-xs sm:text-sm text-neutral-300 mb-5 leading-relaxed font-normal">
|
||||
{isKo
|
||||
? '원클릭 NSIS 설치 프로그램. 백그라운드 자동 업데이트 및 DirectML GPU 가속 지원.'
|
||||
: 'One-click installer with automatic background delta updates and DirectML acceleration.'}
|
||||
? 'Windows x64 후보 빌드의 설치, 서명, 업데이트 복구 경로를 검증 중입니다.'
|
||||
: 'The Windows x64 candidate is undergoing installation, signing, and update-recovery verification.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<a
|
||||
href="https://git.chanpaca.net/attachments/c281a224-2538-496a-bf74-bc188938c734"
|
||||
download="D3RO-Voice-Setup-1.0.0-x64.exe"
|
||||
className="w-full py-2.5 px-4 rounded-lg bg-surface-700 hover:bg-surface-600 border border-white/[0.08] hover:border-brand-amber/40 text-neutral-200 font-mono text-xs flex items-center justify-between transition-all"
|
||||
<div
|
||||
aria-disabled="true"
|
||||
className="w-full py-2.5 px-3.5 rounded-xl bg-surface-900/60 border border-white/10 text-neutral-400 text-xs font-medium flex items-center justify-between"
|
||||
>
|
||||
<span className="font-semibold">Setup Installer (.exe)</span>
|
||||
<span className="text-neutral-400">102 MB</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://git.chanpaca.net/yunchan/d3ro-voice/releases"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="w-full py-2 px-4 rounded-lg bg-surface-900/40 text-neutral-500 font-mono text-[11px] flex items-center justify-between"
|
||||
>
|
||||
<span>Blockmap (.blockmap)</span>
|
||||
<span>105 KB</span>
|
||||
</a>
|
||||
<span>Windows x64 installer</span>
|
||||
<span>{isKo ? '검증 대기' : 'Verification pending'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* macOS Package */}
|
||||
<div className="p-6 rounded-xl bg-surface-800/60 border border-white/[0.06] hover:border-brand-amber/30 transition-all flex flex-col justify-between">
|
||||
<div className="p-6 rounded-2xl bg-surface-800/80 border border-white/10 hover:border-purple-400/40 shadow-glass-card backdrop-blur-xl transition-all flex flex-col justify-between">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="font-mono text-xs font-semibold text-purple-400">MACOS 12.0+</span>
|
||||
<span className="font-mono text-[10px] text-neutral-500">Apple Silicon & Intel</span>
|
||||
<span className="text-xs font-medium text-purple-300">MACOS 12.0+</span>
|
||||
<span className="font-mono text-xs text-neutral-400">Apple Silicon</span>
|
||||
</div>
|
||||
<h4 className="font-display text-base font-bold text-neutral-100 mb-1">macOS DMG</h4>
|
||||
<p className="text-xs text-neutral-400 mb-6 leading-relaxed">
|
||||
<h4 className="text-lg font-medium text-neutral-50 mb-1.5">macOS DMG</h4>
|
||||
<p className="text-xs sm:text-sm text-neutral-300 mb-5 leading-relaxed font-normal">
|
||||
{isKo
|
||||
? 'Apple Silicon M1~M4 Metal 가속 지원 및 Universal DMG 설치 패키지.'
|
||||
: 'Universal package optimized for Apple Silicon (M1/M2/M3/M4) Metal framework.'}
|
||||
? 'Apple Silicon 후보 빌드의 코드 서명과 설치 동작을 검증 중입니다.'
|
||||
: 'The Apple Silicon candidate is undergoing code-signing and installation verification.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<a
|
||||
href="https://git.chanpaca.net/yunchan/d3ro-voice/releases"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="w-full py-2.5 px-4 rounded-lg bg-surface-700 hover:bg-surface-600 border border-white/[0.08] hover:border-purple-400/40 text-neutral-200 font-mono text-xs flex items-center justify-between transition-all"
|
||||
<div
|
||||
aria-disabled="true"
|
||||
className="w-full py-2.5 px-3.5 rounded-xl bg-surface-900/60 border border-white/10 text-neutral-400 text-xs font-medium flex items-center justify-between"
|
||||
>
|
||||
<span className="font-semibold">Apple Silicon DMG (.dmg)</span>
|
||||
<span className="text-neutral-400">98 MB</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://git.chanpaca.net/yunchan/d3ro-voice/releases"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="w-full py-2 px-4 rounded-lg bg-surface-900/40 text-neutral-500 font-mono text-[11px] flex items-center justify-between"
|
||||
>
|
||||
<span>Portable (.zip)</span>
|
||||
<span>96 MB</span>
|
||||
</a>
|
||||
<span>Apple Silicon DMG</span>
|
||||
<span>{isKo ? '검증 대기' : 'Verification pending'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Android & iOS Mobile App Package */}
|
||||
<div className="p-6 rounded-xl bg-surface-800/60 border border-white/[0.06] hover:border-brand-amber/30 transition-all flex flex-col justify-between">
|
||||
{/* Android / iOS Mobile */}
|
||||
<div className="p-6 rounded-2xl bg-surface-800/80 border border-white/10 hover:border-emerald-500/40 shadow-glass-card backdrop-blur-xl transition-all flex flex-col justify-between">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="font-mono text-xs font-semibold text-emerald-400">ANDROID & IOS</span>
|
||||
<span className="font-mono text-[10px] text-neutral-500">Mobile Edition</span>
|
||||
<span className="text-xs font-medium text-emerald-400">ANDROID</span>
|
||||
<span className="text-xs text-neutral-400">Mobile Edition</span>
|
||||
</div>
|
||||
<h4 className="font-display text-base font-bold text-neutral-100 mb-1">D3RO Voice Mobile</h4>
|
||||
<p className="text-xs text-neutral-400 mb-6 leading-relaxed">
|
||||
<h4 className="text-lg font-medium text-neutral-50 mb-1.5">D3RO Voice Mobile</h4>
|
||||
<p className="text-xs sm:text-sm text-neutral-300 mb-5 leading-relaxed font-normal">
|
||||
{isKo
|
||||
? '스마트폰에서도 동일하게 작동하는 로컬 음성 비서 및 실시간 마이크 연동. 이동 중에도 끊김 없는 고속 음성 기록.'
|
||||
: 'On-device voice assistant for smartphones with instant mic streaming and seamless cloud sync.'}
|
||||
? 'Google Play용 AAB와 스토어 메타데이터의 검증을 마친 뒤 Android 배포를 시작합니다.'
|
||||
: 'Android distribution begins after the Play AAB and store metadata are verified.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<a
|
||||
href="https://git.chanpaca.net/attachments/0b015367-dd8b-488c-8cc0-4db413b51792"
|
||||
download="d3ro-voice-v1.0.0-signed.apk"
|
||||
className="w-full py-2.5 px-4 rounded-lg bg-surface-700 hover:bg-surface-600 border border-white/[0.08] hover:border-emerald-400/40 text-neutral-200 font-mono text-xs flex items-center justify-between transition-all"
|
||||
<div
|
||||
aria-disabled="true"
|
||||
className="w-full py-2.5 px-3.5 rounded-xl bg-surface-900/60 border border-white/10 text-neutral-400 text-xs font-medium flex items-center justify-between"
|
||||
>
|
||||
<span className="font-semibold">Android Release APK (.apk)</span>
|
||||
<span className="text-emerald-400">Download (47.5MB) →</span>
|
||||
</a>
|
||||
<a
|
||||
href="#download"
|
||||
className="w-full py-2 px-4 rounded-lg bg-surface-900/40 text-neutral-400 font-mono text-[11px] flex items-center justify-between"
|
||||
>
|
||||
<span>{isKo ? 'iOS App Store / TestFlight' : 'iOS TestFlight'}</span>
|
||||
<span className="text-neutral-500">Coming Soon</span>
|
||||
</a>
|
||||
<span>{isKo ? 'Android 공식 APK' : 'Official Android APK'}</span>
|
||||
<span>{isKo ? '서명 증거 확인 대기' : 'Unavailable — evidence pending'}</span>
|
||||
</div>
|
||||
<div className="w-full py-2 px-3.5 rounded-xl bg-surface-900/50 text-neutral-500 text-xs flex items-center justify-between">
|
||||
<span>iOS App Store / TestFlight</span>
|
||||
<span>{isKo ? '검증된 배포 없음' : 'No verified distribution'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Live Client-Side SHA-256 Verifier */}
|
||||
<div className="mb-20 p-8 rounded-2xl bg-surface-800/40 border border-white/[0.06] backdrop-blur-sm">
|
||||
{/* Client-Side Cryptographic Verifier Box */}
|
||||
<div className="p-7 md:p-8 rounded-3xl bg-surface-800/70 border border-white/10 shadow-glass-card backdrop-blur-xl mb-16">
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<span className="font-mono text-nano text-brand-amber uppercase tracking-widest block mb-1">
|
||||
CRYPTOGRAPHIC INTEGRITY
|
||||
<span className="text-xs font-medium text-brand-blue uppercase tracking-wider block mb-1">
|
||||
LOCAL FILE UTILITY
|
||||
</span>
|
||||
<h3 className="font-display text-xl font-bold text-neutral-100">
|
||||
{isKo ? '실시간 SHA-256 무결성 검증기' : 'SHA-256 Binary Integrity Verifier'}
|
||||
<h3 className="text-lg sm:text-xl font-medium text-neutral-50 mb-1">
|
||||
{isKo ? '로컬 SHA-256 계산기' : 'Client-Side SHA-256 Calculator'}
|
||||
</h3>
|
||||
<p className="text-xs text-neutral-400">
|
||||
<p className="text-xs sm:text-sm text-neutral-300 font-normal">
|
||||
{isKo
|
||||
? '다운로드한 파일을 브라우저로 끌어다 놓아 위변조 및 해시 일치를 로컬에서 즉시 확인하세요.'
|
||||
: 'Drag and drop your installer file to compute SHA-256 locally via browser WebCrypto API.'}
|
||||
? '선택한 파일의 SHA-256 값을 브라우저에서 계산합니다. 공식 릴리스 판정은 제공하지 않습니다.'
|
||||
: 'Compute a selected file hash locally with WebCrypto. This does not certify an official release.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<label className="px-4 py-2 rounded-lg bg-surface-700 hover:bg-surface-600 border border-white/[0.1] text-neutral-200 font-mono text-xs font-semibold cursor-pointer transition-all">
|
||||
{isKo ? '파일 선택하여 검증' : 'Select File to Verify'}
|
||||
<input type="file" className="hidden" onChange={handleFileChange} />
|
||||
<label className="px-4 py-2 rounded-xl bg-surface-700 hover:bg-surface-600 border border-white/10 hover:border-brand-blue/40 text-neutral-200 text-xs font-medium cursor-pointer transition-all flex items-center gap-2">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||
<polyline points="17 8 12 3 7 8" />
|
||||
<line x1="12" y1="3" x2="12" y2="15" />
|
||||
</svg>
|
||||
<span>{isKo ? '파일 선택하여 검증' : 'Select File'}</span>
|
||||
<input type="file" onChange={handleFileChange} className="hidden" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Drag & Drop Target Area */}
|
||||
<div
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
className={`border-2 border-dashed rounded-xl p-8 text-center transition-all ${
|
||||
isDragOver ? 'border-brand-amber bg-brand-amber/10' : 'border-white/[0.08] bg-surface-900/40'
|
||||
className={`p-7 rounded-2xl border-2 border-dashed transition-all text-center ${
|
||||
isDragOver
|
||||
? 'border-brand-blue bg-brand-blue/10'
|
||||
: 'border-white/15 bg-surface-900/50 hover:border-white/25'
|
||||
}`}
|
||||
>
|
||||
{verifyStatus === 'idle' && (
|
||||
<div>
|
||||
<p className="font-mono text-xs text-neutral-300 mb-1">
|
||||
{isKo
|
||||
? '설치 파일(.exe / .dmg / .zip)을 이곳에 끌어다 놓으세요'
|
||||
: 'Drop installer file (.exe / .dmg / .zip) here'}
|
||||
<div className="space-y-1.5 text-neutral-400">
|
||||
<p className="text-xs sm:text-sm font-normal text-neutral-200">
|
||||
{isKo ? '설치 파일(.exe / .dmg / .zip)을 이곳에 끌어다 놓으세요' : 'Drop installer file here to compute cryptographic hash'}
|
||||
</p>
|
||||
<p className="font-mono text-[11px] text-neutral-500">
|
||||
{isKo ? '파일은 서버로 전송되지 않고 로컬에서 안전하게 계산됩니다.' : 'Processed locally in browser.'}
|
||||
<p className="text-xs text-neutral-400 font-normal">
|
||||
{isKo ? '파일은 서버로 전송되지 않고 로컬 브라우저에서 안전하게 계산됩니다.' : 'Calculated in-memory locally via browser WebCrypto.'}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{verifyStatus !== 'idle' && (
|
||||
<div className="text-left font-mono text-xs space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-bold text-neutral-200">{fileName}</span>
|
||||
{verifyStatus === 'computing' && (
|
||||
<span className="px-2 py-0.5 rounded bg-amber-500/20 text-amber-300">Computing...</span>
|
||||
)}
|
||||
{verifyStatus === 'match' && (
|
||||
<span className="px-2 py-0.5 rounded bg-emerald-500/20 text-emerald-400 font-bold">
|
||||
✓ {isKo ? '공식 릴리스 일치 (정상)' : 'OFFICIAL MATCH (GENUINE)'}
|
||||
</span>
|
||||
)}
|
||||
{verifyStatus === 'custom' && (
|
||||
<span className="px-2 py-0.5 rounded bg-brand-amber/20 text-brand-amber font-bold">
|
||||
✓ {isKo ? '로컬 해시 계산 완료' : 'LOCAL HASH COMPUTED'}
|
||||
</span>
|
||||
)}
|
||||
{verifyStatus === 'computing' && (
|
||||
<div className="flex items-center justify-center gap-3 text-brand-blue text-xs sm:text-sm font-medium">
|
||||
<span className="w-2 h-2 rounded-full bg-brand-blue animate-ping" />
|
||||
<span>{isKo ? 'SHA-256 해시 계산 중...' : 'Computing SHA-256 hash locally...'}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{verifyStatus === 'custom' && (
|
||||
<div className="space-y-2 text-left bg-surface-950 p-4 rounded-xl border border-white/10">
|
||||
<div className="flex items-center justify-between text-xs text-neutral-300">
|
||||
<span className="font-medium text-brand-blue">{isKo ? '로컬 해시 계산 완료' : 'Local Hash Computed'}</span>
|
||||
<span>{fileName}</span>
|
||||
</div>
|
||||
<div className="p-3 rounded bg-surface-950/60 border border-white/[0.04] space-y-1">
|
||||
<div className="text-neutral-400">
|
||||
Calculated:{' '}
|
||||
<span className="text-brand-amber">{computedHash || (isKo ? '계산 중...' : 'Hashing...')}</span>
|
||||
</div>
|
||||
<div className="text-neutral-400">
|
||||
Official:{' '}<span className="text-emerald-400">{OFFICIAL_HASH}</span>
|
||||
</div>
|
||||
<div className="font-mono text-xs text-neutral-200 break-all pt-1 border-t border-white/10">
|
||||
{computedHash}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -373,78 +295,81 @@ export function Download() {
|
|||
</div>
|
||||
|
||||
{/* Release History Section */}
|
||||
<div id="releases">
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<div>
|
||||
<span className="font-mono text-nano text-brand-amber uppercase tracking-widest block mb-1">
|
||||
<span className="text-xs font-medium text-brand-blue uppercase tracking-wider block mb-1">
|
||||
VERSION ARCHIVE
|
||||
</span>
|
||||
<h3 className="font-display text-xl font-bold text-neutral-100">
|
||||
{isKo ? '릴리스 버전 히스토리' : 'Release Version History'}
|
||||
<h3 className="text-xl font-medium text-neutral-50">
|
||||
{isKo ? '릴리스 버전 히스토리' : 'Release Archive & Changelog'}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="https://git.chanpaca.net/yunchan/d3ro-voice/releases"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-mono text-xs text-brand-amber hover:underline flex items-center gap-1"
|
||||
className="text-xs text-brand-blue hover:text-brand-blue-light transition-colors font-medium flex items-center gap-1.5"
|
||||
>
|
||||
<span>{isKo ? 'Forgejo Git 릴리스 전체 보기' : 'Forgejo Git Releases'}</span>
|
||||
<span>Forgejo Git 릴리스 전체 보기</span>
|
||||
<span>→</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* v1.0.0 */}
|
||||
<div className="p-6 rounded-xl bg-surface-800/80 border-l-4 border-l-brand-amber border border-white/[0.06]">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-3">
|
||||
{/* v1.0.0 Release */}
|
||||
<div className="p-6 rounded-2xl bg-surface-800/80 border-l-4 border-l-brand-blue border border-white/10 shadow-glass-card">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-3 pb-3 border-b border-white/[0.06]">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="font-mono text-base font-bold text-neutral-100">v1.0.0</span>
|
||||
<span className="px-2 py-0.5 rounded text-[10px] font-mono font-bold bg-brand-amber/20 text-brand-amber border border-brand-amber/30">
|
||||
LATEST STABLE
|
||||
<span className="text-base font-medium text-neutral-100">v1.0.0</span>
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-medium bg-brand-blue/15 text-brand-blue border border-brand-blue/30">
|
||||
ARCHIVED · DOWNLOAD UNAVAILABLE
|
||||
</span>
|
||||
<span className="font-mono text-xs text-neutral-500">2026-08-20</span>
|
||||
<span className="text-xs text-neutral-400">2026-08-20</span>
|
||||
</div>
|
||||
<a
|
||||
href="/releases/1.0.0/D3RO-Voice-Setup-1.0.0-x64.exe"
|
||||
download
|
||||
className="px-3 py-1.5 rounded-lg bg-brand-amber/15 hover:bg-brand-amber/25 text-brand-amber font-mono text-xs font-bold border border-brand-amber/30 transition-all text-center"
|
||||
<span
|
||||
aria-disabled="true"
|
||||
className="px-3.5 py-1.5 rounded-xl bg-surface-900/60 text-neutral-400 border border-white/10 text-xs font-medium self-start sm:self-auto"
|
||||
>
|
||||
Installer (.exe)
|
||||
</a>
|
||||
{isKo ? '과거 바이너리 미제공' : 'Historical binary unavailable'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul className="text-xs text-neutral-300 space-y-1.5 mb-4 list-disc list-inside">
|
||||
<li>
|
||||
<strong className="text-brand-amber">10+ 글로벌 광고 미디에이션</strong>: EthicalAds, Carbon Ads, GAM, Playwire, AppLovin, Unity Ads 실시간 입찰.
|
||||
<ul className="space-y-1.5 text-xs sm:text-sm text-neutral-300 mb-3.5 font-normal leading-relaxed">
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-brand-blue font-medium">•</span>
|
||||
<span><strong className="text-neutral-100 font-medium">10+ 글로벌 광고 미디에이션</strong>: EthicalAds, Carbon Ads, GAM, Playwire, AppLovin, Unity Ads 실시간 입찰.</span>
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-brand-amber">무료 티어 리워드 충전</strong>: 15초 스폰서 비디오 시청 완료 시 +50 Cloud AI 토큰 자동 리필.
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-brand-blue font-medium">•</span>
|
||||
<span><strong className="text-neutral-100 font-medium">무료 티어 리워드 충전</strong>: 15초 스폰서 비디오 시청 완료 시 +50 Cloud AI 토큰 자동 리필.</span>
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-brand-amber">100% 로컬 Whisper 전사 엔진</strong>: 네트워크 없이 실시간 고속 오프라인 음성 텍스트 변환.
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-brand-blue font-medium">•</span>
|
||||
<span><strong className="text-neutral-100 font-medium">100% 로컬 Whisper 전사 엔진</strong>: 네트워크 연결 없이 실시간 고속 오프라인 음성 텍스트 변환.</span>
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-brand-amber">Android & iOS 모바일 앱</strong>: 스마트폰 음성 입력 및 클라우드 AI 실시간 계정 동기화.
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-brand-blue font-medium">•</span>
|
||||
<span><strong className="text-neutral-100 font-medium">모바일 TEST-DEMO</strong>: 공식 서명 증거와 스토어 심사가 끝날 때까지 일반 다운로드는 제공하지 않습니다.</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="p-2.5 rounded bg-surface-950/60 font-mono text-[11px] text-neutral-400 flex items-center justify-between">
|
||||
<span className="truncate max-w-[450px]">SHA-256: b0ac051443151a2e34e8192f1fcf795586bbafca6eb21f01a79170c079a53ba2</span>
|
||||
<span className="text-brand-amber">102 MB</span>
|
||||
<div className="text-xs text-neutral-400 p-2.5 rounded-lg bg-surface-950 border border-white/[0.04]">
|
||||
{isKo
|
||||
? '이 항목은 변경 이력으로만 보존합니다. 설치 파일, 고정 해시, 용량 정보는 현재 릴리스로 제공하지 않습니다.'
|
||||
: 'This entry is retained only as history. Its installer, fixed hash, and size are not offered as a current release.'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* v0.2.1-alpha */}
|
||||
<div className="p-5 rounded-xl bg-surface-800/40 border-l-4 border-l-neutral-700 border border-white/[0.04] opacity-80">
|
||||
<div className="p-5 rounded-2xl bg-surface-800/50 border-l-4 border-l-surface-600 border border-white/5 opacity-75">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="font-mono text-sm font-bold text-neutral-200">v0.2.1-alpha</span>
|
||||
<span className="px-2 py-0.5 rounded text-[10px] font-mono bg-white/5 text-neutral-400">PRE-RELEASE</span>
|
||||
<span className="font-mono text-xs text-neutral-500">2026-08-15</span>
|
||||
<span className="text-sm font-medium text-neutral-200">v0.2.1-alpha</span>
|
||||
<span className="px-2 py-0.5 rounded-full text-xs font-normal bg-white/5 text-neutral-400 border border-white/10">PRE-RELEASE</span>
|
||||
<span className="text-xs text-neutral-500 font-mono">2026-08-15</span>
|
||||
</div>
|
||||
<p className="text-xs text-neutral-400">
|
||||
Deepgram, AssemblyAI, Groq 멀티 클라우드 STT 드라이버 디스패치 및 암호화 라이선스 키 검증 엔진 탑재.
|
||||
<p className="text-xs text-neutral-300 font-normal">
|
||||
Deepgram, AssemblyAI, Groq 멀티 클라우드 STT 드라이버 디스패처 및 암호화 라이선스 키 검증 엔진 탑재.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,54 +8,63 @@ export function FAQ() {
|
|||
const [openIndex, setOpenIndex] = useState<number | null>(null)
|
||||
|
||||
return (
|
||||
<section id="faq" className="relative py-24 md:py-32">
|
||||
<section id="faq" className="relative py-20 md:py-28">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-surface-800/30 to-transparent pointer-events-none" />
|
||||
|
||||
<Container className="relative max-w-3xl">
|
||||
<Container className="relative max-w-4xl">
|
||||
<SectionHeader
|
||||
index={t.faq.index}
|
||||
title={t.faq.title}
|
||||
/>
|
||||
|
||||
<div className="space-y-px">
|
||||
<div className="space-y-3.5">
|
||||
{t.faq.items.map((faq, i) => {
|
||||
const isOpen = openIndex === i
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className="border-b border-white/[0.04]"
|
||||
className={`rounded-2xl border transition-all duration-200 overflow-hidden ${
|
||||
isOpen
|
||||
? 'bg-surface-800/90 border-brand-blue/40 shadow-glow-sm'
|
||||
: 'bg-surface-800/60 border-white/10 hover:border-white/20'
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpenIndex(isOpen ? null : i)}
|
||||
className="w-full flex items-start justify-between gap-4 py-5 text-left group"
|
||||
className="w-full flex items-start justify-between gap-4 p-5 sm:p-6 text-left group"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="font-mono text-nano text-neutral-600 mt-1 flex-shrink-0">
|
||||
<div className="flex items-start gap-3.5">
|
||||
<span className="font-mono text-xs font-medium text-brand-blue mt-0.5 flex-shrink-0 bg-brand-blue/10 px-2.5 py-0.5 rounded-md border border-brand-blue/25">
|
||||
{String(i + 1).padStart(2, '0')}
|
||||
</span>
|
||||
<span className="text-sm font-medium text-neutral-200 group-hover:text-white transition-colors">
|
||||
<span className="text-base sm:text-lg font-medium sm:font-medium text-neutral-100 group-hover:text-brand-blue transition-colors leading-snug">
|
||||
{faq.q}
|
||||
</span>
|
||||
</div>
|
||||
<svg
|
||||
className={`w-4 h-4 text-neutral-500 flex-shrink-0 mt-1 transition-transform duration-200 ${
|
||||
isOpen ? 'rotate-45' : ''
|
||||
}`}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
>
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</svg>
|
||||
<div className="w-7 h-7 rounded-full bg-surface-700/80 border border-white/10 flex items-center justify-center flex-shrink-0 mt-0.5 text-neutral-300 group-hover:text-brand-blue group-hover:border-brand-blue/40 transition-colors">
|
||||
<svg
|
||||
className={`w-3.5 h-3.5 transition-transform duration-200 ${
|
||||
isOpen ? 'rotate-45 text-brand-blue' : ''
|
||||
}`}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
>
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div className={`accordion-content ${isOpen ? 'open' : ''}`}>
|
||||
<div>
|
||||
<p className="pl-10 pb-5 text-sm text-neutral-400 leading-relaxed">
|
||||
{faq.a}
|
||||
</p>
|
||||
<div className="overflow-hidden min-h-0">
|
||||
<div className="px-5 sm:px-6 pb-5 pt-0">
|
||||
<p className="pl-10 text-sm sm:text-base text-neutral-300 leading-relaxed font-normal border-t border-white/[0.06] pt-3.5">
|
||||
{faq.a}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ interface FeatureTile {
|
|||
}
|
||||
|
||||
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',
|
||||
FREE: 'text-emerald-400 bg-emerald-500/15 border-emerald-500/30',
|
||||
PRO: 'text-brand-blue bg-brand-blue/15 border-brand-blue/30',
|
||||
'PRO+': 'text-purple-300 bg-purple-500/15 border-purple-500/30',
|
||||
} as const
|
||||
|
||||
export function Features() {
|
||||
|
|
@ -32,21 +32,21 @@ export function Features() {
|
|||
description: locale === 'ko'
|
||||
? 'VS Code, Notion, Slack, Word 등 모든 프로그램 위에서 Ctrl+Shift+Space로 즉시 호출되는 초경량 플로팅 캡슐. 타이핑 없이 생각의 속도로 입력하세요.'
|
||||
: 'Lightweight floating HUD triggered with Ctrl+Shift+Space across any application. Dictate and transcribe at the speed of thought without leaving your workflow.',
|
||||
highlight: 'Global Hotkey & Floating Overlays',
|
||||
highlight: '글로벌 핫키 & 플로팅 오버레이',
|
||||
icon: <MicIcon />,
|
||||
previewNode: (
|
||||
<div className="mt-4 p-3 rounded-xl bg-surface-950 border border-white/[0.06] flex items-center justify-between shadow-inner">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-brand-amber animate-ping" />
|
||||
<div className="flex items-center gap-1 h-5">
|
||||
{[4, 12, 20, 16, 24, 14, 8, 18, 6].map((h, i) => (
|
||||
<div key={i} className="w-1 bg-brand-amber rounded-full" style={{ height: `${h}px` }} />
|
||||
<div className="mt-4 p-3.5 rounded-xl bg-surface-950/90 border border-white/10 flex items-center justify-between shadow-inner">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-brand-blue animate-ping" />
|
||||
<div className="flex items-center gap-1.5 h-6">
|
||||
{[6, 14, 22, 18, 24, 16, 10, 20, 8].map((h, i) => (
|
||||
<div key={i} className="w-1.5 bg-brand-blue rounded-full shadow-sm" style={{ height: `${h}px` }} />
|
||||
))}
|
||||
</div>
|
||||
<span className="font-mono text-nano text-neutral-400 ml-2">0:04</span>
|
||||
<span className="font-mono text-xs font-medium text-neutral-300 ml-2">0:04</span>
|
||||
</div>
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-emerald-400 bg-emerald-500/10 px-2 py-0.5 rounded border border-emerald-500/20">
|
||||
AUTO-INJECTING
|
||||
<span className="text-xs font-medium text-emerald-400 bg-emerald-500/15 px-2.5 py-0.5 rounded-full border border-emerald-500/30">
|
||||
자동 커서 입력 중
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
|
|
@ -59,7 +59,7 @@ export function Features() {
|
|||
description: locale === 'ko'
|
||||
? '음성 및 전사 텍스트가 외부 클라우드 서버로 단 1바이트도 전송되지 않습니다. 에어갭 환경 및 사내 보안 규정 100% 준수.'
|
||||
: 'Zero cloud telemetry. Voice audio and transcriptions are strictly processed on your local machine using faster-whisper and Ollama.',
|
||||
highlight: 'Zero Cloud Egress',
|
||||
highlight: '클라우드 유출 0바이트',
|
||||
icon: <ShieldIcon />,
|
||||
},
|
||||
{
|
||||
|
|
@ -70,7 +70,7 @@ export function Features() {
|
|||
description: locale === 'ko'
|
||||
? '장시간 회의 녹음, 화자 자동 분리, 실시간 스크래치패드 및 마크다운 회의록(액션 아이템, 결정 사항) 자동 생성.'
|
||||
: 'Long-form meeting recording with speaker diarization, real-time memo scratchpad, and automatic structured Markdown summary generation.',
|
||||
highlight: 'Auto Markdown Action Items',
|
||||
highlight: '자동 마크다운 회의록',
|
||||
icon: <UsersIcon />,
|
||||
},
|
||||
{
|
||||
|
|
@ -81,25 +81,25 @@ export function Features() {
|
|||
description: locale === 'ko'
|
||||
? '음성 입력 → 추임새 제거 → 전문 용어 사전 보정 → LLM 톤앤매너 변환 → 활성 창 자동 입력까지 1회 발화로 연속 실행하는 고성능 자동화 엔진.'
|
||||
: 'Chain multi-step prompts in a single voice command: Transcribe → Clean Fillers → Phonetic Dictionary Fix → LLM Transform → Active Cursor Injection.',
|
||||
highlight: 'Custom Prompts & Tone Shift',
|
||||
highlight: '맞춤형 프롬프트 & 톤 변환',
|
||||
icon: <ChainIcon />,
|
||||
previewNode: (
|
||||
<div className="mt-4 grid grid-cols-4 gap-2 font-mono text-nano text-neutral-400 bg-surface-950 p-2.5 rounded-lg border border-white/[0.04]">
|
||||
<div className="text-center p-1.5 rounded bg-surface-800 border border-white/[0.05]">
|
||||
<span className="text-brand-amber block">01. STT</span>
|
||||
<span className="text-neutral-500">Whisper</span>
|
||||
<div className="mt-4 grid grid-cols-2 sm:grid-cols-4 gap-2.5 text-xs text-neutral-300 bg-surface-950/90 p-3 rounded-xl border border-white/10">
|
||||
<div className="text-center p-2 rounded-lg bg-surface-800 border border-white/10">
|
||||
<span className="text-brand-blue font-medium block text-xs">01. STT</span>
|
||||
<span className="text-neutral-400 text-[11px]">Whisper</span>
|
||||
</div>
|
||||
<div className="text-center p-1.5 rounded bg-surface-800 border border-white/[0.05]">
|
||||
<span className="text-emerald-400 block">02. DICT</span>
|
||||
<span className="text-neutral-500">Phonetic</span>
|
||||
<div className="text-center p-2 rounded-lg bg-surface-800 border border-white/10">
|
||||
<span className="text-emerald-400 font-medium block text-xs">02. 사전</span>
|
||||
<span className="text-neutral-400 text-[11px]">음성 발음 보정</span>
|
||||
</div>
|
||||
<div className="text-center p-1.5 rounded bg-surface-800 border border-white/[0.05]">
|
||||
<span className="text-violet-400 block">03. LLM</span>
|
||||
<span className="text-neutral-500">Ollama</span>
|
||||
<div className="text-center p-2 rounded-lg bg-surface-800 border border-white/10">
|
||||
<span className="text-purple-300 font-medium block text-xs">03. LLM</span>
|
||||
<span className="text-neutral-400 text-[11px]">Ollama Local</span>
|
||||
</div>
|
||||
<div className="text-center p-1.5 rounded bg-surface-800 border border-white/[0.05]">
|
||||
<span className="text-neutral-200 block">04. INJECT</span>
|
||||
<span className="text-neutral-500">Cursor</span>
|
||||
<div className="text-center p-2 rounded-lg bg-surface-800 border border-white/10">
|
||||
<span className="text-neutral-200 font-medium block text-xs">04. 입력</span>
|
||||
<span className="text-neutral-400 text-[11px]">활성 커서 삽입</span>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
|
|
@ -112,7 +112,7 @@ export function Features() {
|
|||
description: locale === 'ko'
|
||||
? '내 PC의 PDF, 메모, 사내 문서를 로컬 벡터 DB에 인덱싱하여 음성으로 검색하고 대화할 수 있는 비공개 지식 베이스.'
|
||||
: 'Index local PDFs, notes, and private documents into a local vector DB. Query your private knowledge base using voice commands.',
|
||||
highlight: 'sqlite-vec & Local Embeddings',
|
||||
highlight: 'sqlite-vec 로컬 임베딩',
|
||||
icon: <DatabaseIcon />,
|
||||
},
|
||||
{
|
||||
|
|
@ -123,13 +123,13 @@ export function Features() {
|
|||
description: locale === 'ko'
|
||||
? 'C++ 기반 faster-whisper 백엔드로 일반 노트북 CPU에서도 1.2초 미만의 경이로운 응답 속도를 실현.'
|
||||
: 'Optimized C++ faster-whisper runtime delivers sub-1.2s transcription latency even on standard laptop CPUs without dedicated GPU.',
|
||||
highlight: '< 1.2s Real-time Latency',
|
||||
highlight: '1.2초 미만 실시간 응답',
|
||||
icon: <ZapIcon />,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<section id="features" className="relative py-24 md:py-32">
|
||||
<section id="features" className="relative py-20 md:py-28">
|
||||
<Container>
|
||||
<SectionHeader
|
||||
index={t.features.index}
|
||||
|
|
@ -138,28 +138,28 @@ export function Features() {
|
|||
/>
|
||||
|
||||
{/* Asymmetric Bento Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{bentoTiles.map((tile) => (
|
||||
<div
|
||||
key={tile.id}
|
||||
className={`relative noise-texture bg-surface-700/40 border border-white/[0.06] rounded-2xl p-6 md:p-7 shadow-chassis transition-all duration-300 hover:border-brand-amber/30 hover:bg-surface-700/60 hover:shadow-glow-sm flex flex-col justify-between ${tile.span}`}
|
||||
className={`relative noise-texture bg-surface-800/80 border border-white/10 rounded-3xl p-6 md:p-7 shadow-glass-card transition-all duration-300 hover:border-brand-blue/40 hover:bg-surface-700/85 hover:shadow-glow-sm flex flex-col justify-between backdrop-blur-xl ${tile.span}`}
|
||||
>
|
||||
<div>
|
||||
{/* Top row: icon + tag */}
|
||||
<div className="flex items-start justify-between mb-5">
|
||||
<div className="w-11 h-11 rounded-xl bg-surface-600 border border-white/[0.08] flex items-center justify-center text-brand-amber shadow-sm">
|
||||
<div className="w-11 h-11 rounded-2xl bg-surface-700/80 border border-white/10 flex items-center justify-center text-brand-blue shadow-sm">
|
||||
{tile.icon}
|
||||
</div>
|
||||
<span className={`text-nano font-mono font-semibold tracking-widest uppercase px-2.5 py-0.5 border rounded-md ${tagStyles[tile.tag]}`}>
|
||||
<span className={`text-xs font-medium px-2.5 py-0.5 border rounded-full shadow-sm ${tagStyles[tile.tag]}`}>
|
||||
{tile.tag}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<h3 className="font-display text-xl font-bold text-neutral-100 mb-2.5">
|
||||
<h3 className="text-lg sm:text-xl font-medium text-neutral-50 mb-2.5 tracking-tight">
|
||||
{tile.title}
|
||||
</h3>
|
||||
<p className="text-sm text-neutral-400 leading-relaxed">
|
||||
<p className="text-sm text-neutral-300 leading-relaxed font-normal">
|
||||
{tile.description}
|
||||
</p>
|
||||
|
||||
|
|
@ -169,11 +169,11 @@ export function Features() {
|
|||
|
||||
{/* Bottom tag / detail */}
|
||||
{tile.highlight && (
|
||||
<div className="mt-6 pt-4 border-t border-white/[0.04] flex items-center justify-between">
|
||||
<span className="font-mono text-nano text-neutral-500 uppercase tracking-wider">
|
||||
<div className="mt-5 pt-3.5 border-t border-white/[0.08] flex items-center justify-between">
|
||||
<span className="text-xs font-normal text-neutral-400">
|
||||
{tile.highlight}
|
||||
</span>
|
||||
<div className="w-6 h-6 rounded-full border border-white/[0.08] flex items-center justify-center text-neutral-500 hover:text-brand-amber hover:border-brand-amber/30 transition-colors">
|
||||
<div className="w-6 h-6 rounded-full bg-surface-700/60 border border-white/10 flex items-center justify-center text-neutral-400 hover:text-brand-blue hover:border-brand-blue/40 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" />
|
||||
|
|
|
|||
|
|
@ -11,35 +11,35 @@ export function Footer() {
|
|||
const { t } = useI18n()
|
||||
|
||||
return (
|
||||
<footer className="border-t border-white/[0.04] py-12 md:py-16">
|
||||
<footer className="border-t border-white/10 py-12 md:py-16 bg-surface-950/80">
|
||||
<Container>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-10">
|
||||
{/* Left: brand */}
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Led color="amber" size="sm" />
|
||||
<span className="font-mono text-sm font-semibold tracking-tight text-neutral-300">
|
||||
D3RO·VOICE
|
||||
<div className="flex items-center gap-2 mb-2.5">
|
||||
<Led color="blue" size="sm" />
|
||||
<span className="text-base font-medium tracking-tight text-white">
|
||||
D3RO<span className="text-brand-blue font-medium">·</span>VOICE
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-500 leading-relaxed max-w-sm">
|
||||
<p className="text-sm text-neutral-400 leading-relaxed max-w-sm font-normal">
|
||||
{t.footer.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Right: links */}
|
||||
<div className="flex flex-wrap gap-x-8 gap-y-3 md:justify-end">
|
||||
<a href="https://github.com/user/D3ROVoice" className="font-mono text-nano uppercase tracking-widest text-neutral-500 hover:text-neutral-300 transition-colors flex items-center gap-1.5">
|
||||
<div className="flex flex-wrap gap-x-7 gap-y-2.5 md:justify-end items-center">
|
||||
<a href="https://git.chanpaca.net/yunchan/d3ro-voice" target="_blank" rel="noreferrer" className="text-xs text-neutral-400 hover:text-brand-blue transition-colors font-medium flex items-center gap-1.5">
|
||||
<GithubIcon />
|
||||
GitHub
|
||||
Repository
|
||||
</a>
|
||||
<a href="#features" className="font-mono text-nano uppercase tracking-widest text-neutral-500 hover:text-neutral-300 transition-colors">
|
||||
<a href="#features" className="text-xs text-neutral-400 hover:text-brand-blue transition-colors font-medium">
|
||||
{t.nav.features}
|
||||
</a>
|
||||
<a href="#pricing" className="font-mono text-nano uppercase tracking-widest text-neutral-500 hover:text-neutral-300 transition-colors">
|
||||
<a href="#pricing" className="text-xs text-neutral-400 hover:text-brand-blue transition-colors font-medium">
|
||||
{t.nav.pricing}
|
||||
</a>
|
||||
<a href="#faq" className="font-mono text-nano uppercase tracking-widest text-neutral-500 hover:text-neutral-300 transition-colors">
|
||||
<a href="#faq" className="text-xs text-neutral-400 hover:text-brand-blue transition-colors font-medium">
|
||||
{t.nav.faq}
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -50,7 +50,7 @@ export function Footer() {
|
|||
{techStack.map((tech) => (
|
||||
<span
|
||||
key={tech}
|
||||
className="font-mono text-nano uppercase tracking-widest text-neutral-600 bg-surface-700/50 px-2.5 py-1 rounded-sm border border-white/[0.03]"
|
||||
className="text-xs text-neutral-400 bg-surface-800/80 px-2.5 py-1 rounded-lg border border-white/10 font-normal font-mono"
|
||||
>
|
||||
{tech}
|
||||
</span>
|
||||
|
|
@ -58,11 +58,11 @@ export function Footer() {
|
|||
</div>
|
||||
|
||||
{/* Bottom bar */}
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 pt-6 border-t border-white/[0.04]">
|
||||
<span className="font-mono text-nano text-neutral-600 uppercase tracking-widest">
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 pt-5 border-t border-white/10 text-xs text-neutral-400 font-normal">
|
||||
<span>
|
||||
{t.footer.copyright.replace('{year}', String(new Date().getFullYear()))}
|
||||
</span>
|
||||
<span className="font-mono text-nano text-neutral-600 uppercase tracking-widest">
|
||||
<span>
|
||||
{t.footer.builtWith}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,16 +19,19 @@ export function Header() {
|
|||
<header
|
||||
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
|
||||
scrolled
|
||||
? 'bg-surface-950/85 backdrop-blur-xl border-b border-white/[0.06] shadow-lg'
|
||||
: 'bg-transparent'
|
||||
? 'bg-surface-950/90 backdrop-blur-2xl border-b border-white/10 shadow-2xl py-3'
|
||||
: 'bg-transparent py-4'
|
||||
}`}
|
||||
>
|
||||
<div className="mx-auto max-w-7xl px-5 md:px-8 lg:px-12 h-16 flex items-center justify-between">
|
||||
<div className="mx-auto max-w-7xl px-5 md:px-8 lg:px-12 flex items-center justify-between">
|
||||
{/* Logo */}
|
||||
<a href="#" className="flex items-center gap-2 group">
|
||||
<Led color="amber" pulse size="md" />
|
||||
<span className="font-mono text-sm font-bold tracking-tight text-white">
|
||||
D3RO<span className="text-brand-amber">·</span>VOICE
|
||||
<a href="#" className="flex items-center gap-2.5 group">
|
||||
<Led color="blue" pulse size="md" />
|
||||
<span className="text-base font-medium tracking-tight text-white flex items-center">
|
||||
D3RO<span className="text-brand-blue font-medium">·</span>VOICE
|
||||
</span>
|
||||
<span className="hidden sm:inline-block px-2 py-0.5 rounded-full text-xs font-medium bg-brand-blue/15 text-brand-blue border border-brand-blue/30">
|
||||
v1.0
|
||||
</span>
|
||||
</a>
|
||||
|
||||
|
|
@ -38,7 +41,7 @@ export function Header() {
|
|||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="font-mono text-nano uppercase tracking-widest text-neutral-400 hover:text-white transition-colors"
|
||||
className="text-sm font-medium text-neutral-300 hover:text-white transition-colors"
|
||||
>
|
||||
{t.nav[link.key]}
|
||||
</a>
|
||||
|
|
@ -46,48 +49,51 @@ export function Header() {
|
|||
</nav>
|
||||
|
||||
{/* Right side: language + CTA */}
|
||||
<div className="hidden md:flex items-center gap-3">
|
||||
<div className="hidden md:flex items-center gap-3.5">
|
||||
<LanguageSwitcher />
|
||||
<a
|
||||
href="#download"
|
||||
className="glow-btn inline-flex items-center gap-2 px-4 py-2 font-mono text-xs uppercase tracking-wider text-white bg-brand-amber rounded-panel shadow-sm hover:bg-brand-amber-light transition-colors"
|
||||
className="glow-btn inline-flex items-center gap-2 px-5 py-2.5 text-sm font-medium text-white bg-brand-blue rounded-xl shadow-glow-sm hover:bg-brand-blue-light transition-all"
|
||||
>
|
||||
<DownloadSmallIcon />
|
||||
{t.nav.download}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu button */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMobileOpen(!mobileOpen)}
|
||||
className="md:hidden w-9 h-9 flex items-center justify-center text-neutral-300"
|
||||
aria-label="Menu"
|
||||
className="md:hidden w-10 h-10 rounded-lg bg-surface-800 border border-white/10 flex items-center justify-center text-neutral-200"
|
||||
aria-label="Toggle Navigation Menu"
|
||||
>
|
||||
<div className="space-y-1.5">
|
||||
<span className={`block w-5 h-px bg-white transition-all ${mobileOpen ? 'rotate-45 translate-y-[3.5px]' : ''}`} />
|
||||
<span className={`block w-5 h-px bg-white transition-all ${mobileOpen ? '-rotate-45 -translate-y-[3.5px]' : ''}`} />
|
||||
<div className="space-y-1.5 w-5">
|
||||
<span className={`block w-5 h-0.5 bg-white transition-all ${mobileOpen ? 'rotate-45 translate-y-2' : ''}`} />
|
||||
<span className={`block w-5 h-0.5 bg-white transition-all ${mobileOpen ? 'opacity-0' : ''}`} />
|
||||
<span className={`block w-5 h-0.5 bg-white transition-all ${mobileOpen ? '-rotate-45 -translate-y-2' : ''}`} />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Nav */}
|
||||
{mobileOpen && (
|
||||
<div className="md:hidden bg-surface-900/95 backdrop-blur-2xl border-t border-white/[0.06] px-5 py-6 space-y-1 shadow-2xl">
|
||||
<div className="md:hidden bg-surface-900/95 backdrop-blur-2xl border-t border-white/10 px-6 py-6 space-y-2 shadow-2xl">
|
||||
{navItems.map((link) => (
|
||||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className="block font-mono text-xs uppercase tracking-widest text-neutral-400 hover:text-brand-amber py-3 border-b border-white/[0.03]"
|
||||
className="block text-base font-medium text-neutral-200 hover:text-brand-blue py-3 border-b border-white/[0.05]"
|
||||
>
|
||||
{t.nav[link.key]}
|
||||
</a>
|
||||
))}
|
||||
<div className="flex items-center justify-between mt-4 gap-3">
|
||||
<div className="flex items-center justify-between pt-4 gap-3">
|
||||
<LanguageSwitcher />
|
||||
<a
|
||||
href="#download"
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className="flex-1 text-center px-5 py-2.5 font-mono text-xs uppercase tracking-wider text-white bg-brand-amber rounded-panel shadow-sm"
|
||||
className="flex-1 text-center px-5 py-3 text-sm font-medium text-white bg-brand-blue rounded-xl shadow-glow-sm"
|
||||
>
|
||||
{t.nav.download}
|
||||
</a>
|
||||
|
|
@ -97,3 +103,12 @@ export function Header() {
|
|||
</header>
|
||||
)
|
||||
}
|
||||
function DownloadSmallIcon() {
|
||||
return (
|
||||
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||
<polyline points="7 10 12 15 17 10" />
|
||||
<line x1="12" y1="15" x2="12" y2="3" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,13 @@ export function Hero() {
|
|||
? '이번 프로젝트 배포 일정은 다음 주 금요일로 확정하겠습니다.'
|
||||
: 'The project release is scheduled for next Friday.'
|
||||
|
||||
const heroDataPoints = [
|
||||
{ label: t.hero.dataProcessing, value: '100%', unit: 'local' },
|
||||
{ label: t.hero.dataCloudTraffic, value: '0', unit: 'bytes' },
|
||||
{ label: t.hero.dataLatency, value: '<1.2', unit: 's' },
|
||||
{ label: t.hero.dataPrivacy, value: '10/10', unit: '' },
|
||||
]
|
||||
|
||||
// Waveform animation during recording
|
||||
useEffect(() => {
|
||||
if (simState !== 'recording') return
|
||||
|
|
@ -72,69 +79,69 @@ export function Hero() {
|
|||
}
|
||||
|
||||
return (
|
||||
<section className="relative min-h-screen flex flex-col justify-center overflow-hidden bg-grid pt-28 pb-16">
|
||||
<section className="relative min-h-[90vh] flex flex-col justify-center overflow-hidden bg-grid pt-28 pb-16">
|
||||
{/* Ambient background glow */}
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[800px] h-[800px] rounded-full bg-brand-amber/[0.035] blur-[160px]" />
|
||||
<div className="absolute top-1/2 right-10 w-[400px] h-[400px] rounded-full bg-blue-500/[0.02] blur-[140px]" />
|
||||
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-brand-amber/20 to-transparent" />
|
||||
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[800px] h-[800px] rounded-full bg-brand-blue/[0.045] blur-[160px]" />
|
||||
<div className="absolute top-1/2 right-10 w-[500px] h-[500px] rounded-full bg-blue-500/[0.03] blur-[160px]" />
|
||||
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-brand-blue/30 to-transparent" />
|
||||
</div>
|
||||
|
||||
<Container className="relative flex-1 flex flex-col justify-center">
|
||||
{/* Hero main grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-8 items-center pt-4 mb-14">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-10 items-center pt-4 mb-14">
|
||||
{/* Left Column: Typography & CTAs (7 cols) */}
|
||||
<div className="lg:col-span-7 max-w-2xl">
|
||||
{/* Status & Protocol Pill Row in Natural Document Flow */}
|
||||
<div className="flex flex-wrap items-center gap-2.5 mb-6">
|
||||
{/* Status & Protocol Pill Row */}
|
||||
<div className="flex flex-wrap items-center gap-2 mb-6">
|
||||
<Badge led ledColor="green">{t.hero.systemStatus}</Badge>
|
||||
<Badge led>{t.hero.badge}</Badge>
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-neutral-400 bg-surface-700/60 px-2.5 py-1 rounded border border-white/[0.06]">
|
||||
ON-DEVICE V3
|
||||
<span className="text-xs font-medium text-neutral-300 bg-surface-800/80 px-3 py-1 rounded-full border border-white/10 shadow-sm">
|
||||
온디바이스 V3
|
||||
</span>
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber bg-brand-amber/10 px-2.5 py-1 rounded border border-brand-amber/20">
|
||||
<span className="text-xs font-medium text-brand-blue bg-brand-blue/10 px-3 py-1 rounded-full border border-brand-blue/25 shadow-sm">
|
||||
{t.hero.protocol}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Main Headline (2 Clean Lines with keep-all) */}
|
||||
{/* Main Headline */}
|
||||
<h1
|
||||
className="font-display text-4xl sm:text-5xl lg:text-[3.4rem] font-bold text-neutral-50 mb-6 tracking-tight leading-[1.18] break-keep"
|
||||
className="text-3xl sm:text-4xl lg:text-[3.25rem] font-medium text-neutral-50 mb-5 tracking-tight leading-[1.18] break-keep"
|
||||
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
|
||||
>
|
||||
<span className="block text-neutral-100">{t.hero.title1}</span>
|
||||
<span className="block text-brand-amber font-extrabold">{t.hero.title2}</span>
|
||||
<span className="block text-brand-blue font-medium">{t.hero.title2}</span>
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className="text-base md:text-lg text-neutral-400 leading-relaxed mb-8 max-w-xl break-keep"
|
||||
className="text-base sm:text-lg text-neutral-300 leading-relaxed mb-7 max-w-xl break-keep font-normal"
|
||||
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
|
||||
>
|
||||
{t.hero.subtitle}
|
||||
</p>
|
||||
|
||||
{/* Hotkey Callout */}
|
||||
<div className="flex items-center gap-3 mb-8 p-3 rounded-card bg-surface-700/40 border border-white/[0.05] max-w-md">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-neutral-400">
|
||||
GLOBAL PUSH-TO-TALK:
|
||||
<div className="flex items-center gap-3.5 mb-7 p-3 rounded-xl bg-surface-800/80 border border-white/10 max-w-md shadow-sm">
|
||||
<span className="text-xs font-medium text-neutral-400">
|
||||
글로벌 푸시투톡 단축키:
|
||||
</span>
|
||||
<div className="flex items-center gap-1.5 font-mono text-xs font-semibold text-neutral-200">
|
||||
<span className="px-2 py-0.5 rounded bg-surface-500 border border-white/[0.1] shadow-sm">Ctrl</span>
|
||||
<span className="text-neutral-500">+</span>
|
||||
<span className="px-2 py-0.5 rounded bg-surface-500 border border-white/[0.1] shadow-sm">Shift</span>
|
||||
<span className="text-neutral-500">+</span>
|
||||
<span className="px-2.5 py-0.5 rounded bg-brand-amber/20 border border-brand-amber/40 text-brand-amber shadow-sm">Space</span>
|
||||
<div className="flex items-center gap-1.5 text-xs font-medium text-neutral-200">
|
||||
<span className="px-2.5 py-1 rounded-md bg-surface-700 border border-white/10 font-mono text-neutral-200 shadow-sm">Ctrl</span>
|
||||
<span className="text-neutral-400">+</span>
|
||||
<span className="px-2.5 py-1 rounded-md bg-surface-700 border border-white/10 font-mono text-neutral-200 shadow-sm">Shift</span>
|
||||
<span className="text-neutral-400">+</span>
|
||||
<span className="px-3 py-1 rounded-md bg-brand-blue/20 border border-brand-blue/40 text-brand-blue font-mono shadow-sm">Space</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA Action Buttons with OS Negotiation */}
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-4 mb-3">
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3.5 mb-4">
|
||||
<a
|
||||
href={clientOS.downloadUrl}
|
||||
download={clientOS.downloadFilename || undefined}
|
||||
className="glow-btn inline-flex items-center justify-center gap-2.5 px-8 py-4 font-mono text-sm font-semibold uppercase tracking-wider text-white bg-brand-amber rounded-panel shadow-glow-sm hover:shadow-glow-md"
|
||||
className="glow-btn inline-flex items-center justify-center gap-3 px-8 sm:px-9 py-3.5 sm:py-4 text-base font-medium text-white bg-brand-blue rounded-2xl shadow-glow-sm hover:shadow-glow-md active:scale-[0.98] transition-all leading-normal"
|
||||
>
|
||||
<span className="relative z-10 flex items-center gap-2.5">
|
||||
<span className="relative z-10 flex items-center gap-2">
|
||||
{clientOS.os === 'android' ? (
|
||||
<AndroidIcon />
|
||||
) : clientOS.os === 'ios' || clientOS.os === 'macos' ? (
|
||||
|
|
@ -147,7 +154,7 @@ export function Hero() {
|
|||
</a>
|
||||
<a
|
||||
href="#features"
|
||||
className="inline-flex items-center justify-center gap-2 px-7 py-4 font-mono text-sm uppercase tracking-wider text-neutral-300 border border-white/[0.08] rounded-panel hover:border-brand-amber/30 hover:text-white bg-surface-800/40 backdrop-blur-sm transition-all"
|
||||
className="inline-flex items-center justify-center gap-2.5 px-7 sm:px-8 py-3.5 sm:py-4 text-base font-medium text-neutral-200 border border-white/10 rounded-2xl hover:border-brand-blue/40 hover:bg-surface-700 hover:text-white bg-surface-800/60 backdrop-blur-sm transition-all leading-normal"
|
||||
>
|
||||
{t.hero.viewFeatures}
|
||||
<ArrowIcon />
|
||||
|
|
@ -155,21 +162,21 @@ export function Hero() {
|
|||
</div>
|
||||
|
||||
{/* Platform Subtext & Other OS Navigator */}
|
||||
<div className="flex flex-wrap items-center gap-y-2 gap-x-4 mb-3 font-mono text-xs">
|
||||
<span className="text-brand-amber font-medium flex items-center gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-brand-amber animate-pulse" />
|
||||
<div className="flex flex-wrap items-center gap-y-2 gap-x-4 mb-3.5 text-xs font-normal">
|
||||
<span className="text-brand-blue font-medium flex items-center gap-1.5">
|
||||
<span className="w-2 h-2 rounded-full bg-brand-blue animate-pulse" />
|
||||
{locale === 'ko' ? clientOS.subtextKo : clientOS.subtextEn}
|
||||
</span>
|
||||
<a
|
||||
href="#download"
|
||||
className="text-neutral-400 hover:text-neutral-200 underline decoration-neutral-600 underline-offset-4 transition-colors"
|
||||
className="text-neutral-400 hover:text-neutral-200 underline decoration-neutral-600 underline-offset-4 transition-colors font-normal"
|
||||
>
|
||||
{locale === 'ko' ? '다른 OS 다운로드 (Win / Mac / Android / iOS) →' : 'Other Platforms (Win / Mac / Android / iOS) →'}
|
||||
{locale === 'ko' ? '플랫폼별 릴리스 준비 상태 →' : 'Platform release readiness →'}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Friction-Reduction Trust Microcopy */}
|
||||
<div className="flex items-center gap-2 font-mono text-nano text-neutral-500">
|
||||
<div className="flex items-center gap-2 text-xs text-neutral-400 font-normal">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
||||
<span>{locale === 'ko' ? '영구 무료 · 회원가입 불필요 · 100% 오프라인 작동' : 'Free Forever · No Signup Required · 100% Offline'}</span>
|
||||
</div>
|
||||
|
|
@ -177,23 +184,23 @@ export function Hero() {
|
|||
|
||||
{/* Right Column: Live Interactive Voice Capsule (5 cols) */}
|
||||
<div className="lg:col-span-5">
|
||||
<div className="relative p-6 md:p-7 rounded-2xl bg-surface-800/90 border border-white/[0.08] shadow-2xl backdrop-blur-2xl noise-texture">
|
||||
<div className="relative p-6 md:p-7 rounded-3xl bg-surface-800/90 border border-white/10 shadow-glass-card backdrop-blur-2xl noise-texture">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-5 pb-3 border-b border-white/[0.06]">
|
||||
<div className="flex items-center justify-between mb-4 pb-3 border-b border-white/[0.08]">
|
||||
<div className="flex items-center gap-2">
|
||||
<Led color={simState === 'recording' ? 'amber' : simState === 'processing' ? 'amber' : 'green'} pulse={simState !== 'idle'} />
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-neutral-300">
|
||||
{simState === 'idle' ? 'INTERACTIVE SIMULATOR' : simState === 'recording' ? 'LISTENING & STREAMING' : simState === 'processing' ? 'AI POLISHING...' : 'PIPELINE COMPLETED'}
|
||||
<Led color={simState === 'recording' ? 'blue' : simState === 'processing' ? 'blue' : 'green'} pulse={simState !== 'idle'} />
|
||||
<span className="text-xs font-medium text-neutral-200">
|
||||
{simState === 'idle' ? '인터랙티브 시뮬레이터' : simState === 'recording' ? '음성 듣는 중...' : simState === 'processing' ? 'AI 문장 정제 중...' : '전사 & 다듬기 완료'}
|
||||
</span>
|
||||
</div>
|
||||
<span className="font-mono text-nano text-neutral-500 uppercase">
|
||||
ZERO CLOUD EGRESS
|
||||
<span className="text-xs font-medium text-emerald-400 bg-emerald-500/10 px-2.5 py-0.5 rounded-full border border-emerald-500/20">
|
||||
클라우드 유출 0바이트
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Interactive Audio Capsule UI */}
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between p-4 rounded-xl bg-surface-950 border border-white/[0.06] shadow-inner">
|
||||
<div className="flex flex-col gap-3.5">
|
||||
<div className="flex items-center justify-between p-3.5 rounded-xl bg-surface-950 border border-white/10 shadow-inner">
|
||||
{/* Waveform Bars */}
|
||||
<div className="flex items-center gap-1.5 h-8">
|
||||
{waveLevels.map((lvl, idx) => (
|
||||
|
|
@ -202,8 +209,8 @@ export function Hero() {
|
|||
className="w-1.5 rounded-full transition-all duration-75"
|
||||
style={{
|
||||
height: `${Math.max(6, lvl * 32)}px`,
|
||||
backgroundColor: simState === 'recording' ? '#f25b29' : 'rgba(255,255,255,0.2)',
|
||||
boxShadow: simState === 'recording' ? '0 0 8px rgba(242,91,41,0.6)' : 'none',
|
||||
backgroundColor: simState === 'recording' ? '#3b82f6' : 'rgba(255,255,255,0.25)',
|
||||
boxShadow: simState === 'recording' ? '0 0 10px rgba(59,130,246,0.7)' : 'none',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
|
@ -211,71 +218,67 @@ export function Hero() {
|
|||
|
||||
{/* Trigger Button */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={simState === 'idle' || simState === 'done' ? startSimulation : resetSimulation}
|
||||
className={`px-4 py-2 rounded-lg font-mono text-xs font-semibold uppercase tracking-wider transition-all flex items-center gap-2 ${
|
||||
className={`px-4 py-2 rounded-xl text-xs font-medium transition-all flex items-center gap-2 shadow-sm ${
|
||||
simState === 'recording'
|
||||
? 'bg-red-500/20 text-red-400 border border-red-500/30 animate-pulse'
|
||||
: 'bg-brand-amber text-white hover:bg-brand-amber-light shadow-glow-sm'
|
||||
: 'bg-brand-blue text-white hover:bg-brand-blue-light shadow-glow-sm active:scale-[0.98]'
|
||||
}`}
|
||||
>
|
||||
<MicIcon />
|
||||
{simState === 'idle' ? 'Try Demo' : simState === 'recording' ? 'Listening...' : simState === 'processing' ? 'Polishing' : 'Test Again'}
|
||||
<MicSmallIcon />
|
||||
<span>{simState === 'recording' ? (locale === 'ko' ? '녹음 중지' : 'Stop') : simState === 'processing' ? (locale === 'ko' ? '처리 중...' : 'Polishing...') : (locale === 'ko' ? '데모 시작' : 'Try Demo')}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Real-time Transcription Stream */}
|
||||
<div className="min-h-[110px] p-4 rounded-xl bg-surface-900 border border-white/[0.04] flex flex-col justify-center">
|
||||
{/* Real-time Streaming Transcript Output Area */}
|
||||
<div className="min-h-[140px] p-4 rounded-xl bg-surface-950/80 border border-white/10 flex flex-col justify-center">
|
||||
{simState === 'idle' && (
|
||||
<div className="text-center py-3">
|
||||
<p className="text-xs font-mono text-neutral-500">
|
||||
👆 Click 'Try Demo' to watch on-device speech-to-text & AI polish in action
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-xs sm:text-sm text-neutral-400 text-center leading-relaxed font-normal">
|
||||
👆 <strong className="text-neutral-200 font-medium">{locale === 'ko' ? "'데모 시작'" : "'Try Demo'"}</strong>{locale === 'ko' ? ' 버튼을 클릭하여 로컬 음성 전사 및 AI 다듬기 과정을 체험해보세요.' : ' to watch on-device speech-to-text & AI polish in action.'}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{(simState === 'recording' || simState === 'processing') && (
|
||||
<div className="space-y-1.5">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber/80 flex items-center gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-brand-amber animate-ping" />
|
||||
Live Transcription:
|
||||
</span>
|
||||
<p className="text-sm font-sans text-neutral-200 leading-relaxed font-medium">
|
||||
{typedRaw}
|
||||
<span className="inline-block w-2 h-4 ml-1 bg-brand-amber animate-pulse align-middle" />
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-xs font-medium text-brand-blue">
|
||||
<span className="w-2 h-2 rounded-full bg-brand-blue animate-ping" />
|
||||
<span>{locale === 'ko' ? '실시간 음성 전사 (faster-whisper)' : 'Streaming STT (faster-whisper)'}</span>
|
||||
</div>
|
||||
<p className="text-sm sm:text-base text-neutral-100 font-normal leading-relaxed">
|
||||
"{typedRaw}"
|
||||
<span className="inline-block w-1.5 h-4 ml-1 bg-brand-blue animate-pulse align-middle" />
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{simState === 'done' && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-emerald-400 flex items-center gap-1">
|
||||
✓ AI Polish & Context Applied:
|
||||
</span>
|
||||
<span className="font-mono text-nano text-neutral-500">
|
||||
Inference: 0.84s
|
||||
</span>
|
||||
<div className="space-y-3">
|
||||
<div className="p-2 rounded-lg bg-surface-900 border border-white/[0.05]">
|
||||
<span className="text-[11px] text-neutral-400 block mb-0.5">{locale === 'ko' ? '원문 입력' : 'Raw Audio'}</span>
|
||||
<p className="text-xs text-neutral-400 line-through font-normal leading-normal">{sampleRawText}</p>
|
||||
</div>
|
||||
<div className="p-2.5 rounded-lg bg-emerald-500/10 border border-emerald-500/25">
|
||||
<span className="text-xs font-medium text-emerald-400 block mb-0.5">✨ {locale === 'ko' ? 'AI 완벽 정제본 (Ollama 로컬 LLM)' : 'Polished by Local LLM'}</span>
|
||||
<p className="text-sm font-medium text-emerald-200 leading-relaxed">{typedClean}</p>
|
||||
</div>
|
||||
<p className="text-sm font-sans text-emerald-300 leading-relaxed font-medium bg-emerald-500/10 p-2.5 rounded-lg border border-emerald-500/20">
|
||||
"{typedClean}"
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Telemetry Hardware Stats */}
|
||||
<div className="grid grid-cols-3 gap-2 pt-2 border-t border-white/[0.04] font-mono text-nano text-neutral-500">
|
||||
{/* Telemetry Stats Bar */}
|
||||
<div className="grid grid-cols-3 gap-2 pt-2 border-t border-white/[0.06] text-xs font-normal text-neutral-400">
|
||||
<div>
|
||||
<span className="block text-neutral-600">STT ENGINE</span>
|
||||
<span className="text-neutral-300 font-semibold">faster-whisper</span>
|
||||
<span className="block text-[11px] text-neutral-500">STT 엔진</span>
|
||||
<span className="font-mono text-neutral-300">faster-whisper</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="block text-neutral-600">LLM MODEL</span>
|
||||
<span className="text-neutral-300 font-semibold">Ollama Local</span>
|
||||
<span className="block text-[11px] text-neutral-500">LLM 모델</span>
|
||||
<span className="font-mono text-neutral-300">Ollama Local</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="block text-neutral-600">CLOUD LEAK</span>
|
||||
<span className="text-emerald-400 font-semibold">0.00 KB</span>
|
||||
<span className="block text-[11px] text-neutral-500">클라우드 유출</span>
|
||||
<span className="font-mono text-emerald-400 font-medium">0.00 KB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -283,55 +286,42 @@ export function Hero() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Data Telemetry Strip */}
|
||||
<div className="border-t border-white/[0.06] pt-8 mt-auto">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
<DataPoint label={t.hero.dataProcessing} value="100%" unit="local" />
|
||||
<DataPoint label={t.hero.dataCloudTraffic} value="0" unit="bytes" />
|
||||
<DataPoint label={t.hero.dataLatency} value="<1.2s" />
|
||||
<DataPoint label={t.hero.dataPrivacy} value="10/10" />
|
||||
</div>
|
||||
{/* Bottom DataPoints Row */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 pt-10 border-t border-white/10">
|
||||
{heroDataPoints.map((dp, i) => (
|
||||
<DataPoint key={i} label={dp.label} value={dp.value} unit={dp.unit} />
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function AndroidIcon() {
|
||||
function WindowsIcon() {
|
||||
return (
|
||||
<svg className="w-4 h-4 text-emerald-400" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17.523 15.3414c-.5511 0-.9993-.4486-.9993-.9997s.4482-.9993.9993-.9993c.551 0 .9993.4482.9993.9993.0001.5511-.4483.9997-.9993.9997m-11.046 0c-.5511 0-.9993-.4486-.9993-.9997s.4482-.9993.9993-.9993c.5511 0 .9993.4482.9993.9993 0 .5511-.4482.9997-.9993.9997m11.4045-6.02l1.996-3.4572a.416.416 0 0 0-.1521-.5676.416.416 0 0 0-.5676.1521l-2.0223 3.503C15.5902 8.411 13.856 8.125 12 8.125s-3.5902.286-5.1315.8267L4.8462 5.4487a.4161.4161 0 0 0-.5677-.1521.4157.4157 0 0 0-.1521.5676l1.996 3.4572C2.6889 11.1867.3432 14.6589 0 18.775h24c-.3432-4.1161-2.6889-7.5883-6.1185-9.4536" />
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M0 3.449L9.75 2.1v9.451H0m10.949-9.602L24 0v11.4H10.949M0 12.6h9.75v9.451L0 20.699M10.949 12.6H24V24l-12.9-1.801" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function AppleIcon() {
|
||||
return (
|
||||
<svg className="w-4 h-4 text-neutral-200" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M15.97 6.37c.61-.75 1.04-1.8 1.01-2.87-.96.04-2.13.64-2.79 1.43-.58.68-.99 1.74-.94 2.82 1.08.08 2.11-.63 2.72-1.38z" />
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M15.97 6.37c.61-.75 1.04-1.8 1.01-2.87-.96.04-2.12.64-2.77 1.41-.58.68-1.08 1.74-.95 2.78 1.07.08 2.14-.54 2.71-1.32z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function WindowsIcon() {
|
||||
function AndroidIcon() {
|
||||
return (
|
||||
<svg className="w-4 h-4 text-sky-400" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M0 3.449L9.75 2.1v9.451H0m10.949-9.602L24 0v11.551H10.949M0 12.6h9.75v9.451L0 20.699M10.949 12.6H24V24l-13.051-1.851" />
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17.523 15.3414c-.5511 0-.9993-.4486-.9993-.9997s.4482-.9993.9993-.9993c.551 0 .9993.4482.9993.9993.0001.5511-.4483.9997-.9993.9997m-11.046 0c-.5511 0-.9993-.4486-.9993-.9997s.4482-.9993.9993-.9993c.5511 0 .9993.4482.9993.9993 0 .5511-.4482.9997-.9993.9997m11.4045-6.02l1.9973-3.4592a.416.416 0 00-.1521-.5676.416.416 0 00-.5676.1521l-2.0223 3.503C15.5902 8.4111 13.8533 8.0805 12 8.0805s-3.5902.3306-5.1367.8692L4.841 5.4467a.4161.4161 0 00-.5677-.1521.4157.4157 0 00-.1521.5676l1.9973 3.4592C2.6889 11.1867.3432 14.6589 0 18.761h24c-.3432-4.1021-2.6889-7.5743-6.1185-9.4396" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function ArrowIcon() {
|
||||
return (
|
||||
<svg className="w-3.5 h-3.5" 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>
|
||||
)
|
||||
}
|
||||
|
||||
function MicIcon() {
|
||||
function MicSmallIcon() {
|
||||
return (
|
||||
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect x="9" y="2" width="6" height="11" rx="3" />
|
||||
|
|
@ -341,3 +331,11 @@ function MicIcon() {
|
|||
)
|
||||
}
|
||||
|
||||
function ArrowIcon() {
|
||||
return (
|
||||
<svg className="w-3.5 h-3.5 text-neutral-400" 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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export function HowItWorks() {
|
|||
const current = presets[activePreset]
|
||||
|
||||
return (
|
||||
<section id="pipeline" className="relative py-24 md:py-32">
|
||||
<section id="pipeline" className="relative py-20 md:py-28">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-surface-800/40 to-transparent pointer-events-none" />
|
||||
|
||||
<Container className="relative">
|
||||
|
|
@ -74,122 +74,107 @@ export function HowItWorks() {
|
|||
/>
|
||||
|
||||
{/* Preset Selector Bar */}
|
||||
<div className="flex items-center justify-center gap-2 mb-10 overflow-x-auto pb-2">
|
||||
<div className="flex items-center justify-center gap-2.5 mb-10 overflow-x-auto pb-2">
|
||||
{presets.map((p, idx) => (
|
||||
<button
|
||||
key={p.id}
|
||||
type="button"
|
||||
onClick={() => setActivePreset(idx)}
|
||||
className={`px-4 py-2 rounded-panel font-mono text-xs uppercase tracking-wider transition-all flex items-center gap-2 ${
|
||||
className={`px-4.5 py-2 rounded-xl text-xs sm:text-sm font-medium transition-all flex items-center gap-2 shadow-sm active:scale-[0.98] ${
|
||||
activePreset === idx
|
||||
? 'bg-brand-amber text-white shadow-glow-sm font-semibold'
|
||||
: 'bg-surface-700/60 text-neutral-400 hover:text-neutral-200 border border-white/[0.04]'
|
||||
? 'bg-brand-blue text-white shadow-glow-sm font-medium'
|
||||
: 'bg-surface-800/80 text-neutral-300 hover:text-white hover:bg-surface-700 border border-white/10'
|
||||
}`}
|
||||
>
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-current" />
|
||||
<span className={`w-2 h-2 rounded-full ${activePreset === idx ? 'bg-white' : 'bg-brand-blue'}`} />
|
||||
{p.title}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-start">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10 lg:gap-12 items-start">
|
||||
{/* Left: Interactive CRT Studio Screen */}
|
||||
<Crosshair className="order-2 lg:order-1">
|
||||
<div className="crt-screen rounded-card p-6 md:p-8 aspect-[4/3] flex flex-col justify-between noise-texture shadow-2xl">
|
||||
<div className="relative z-10 flex items-center justify-between mb-4">
|
||||
<div className="crt-screen rounded-3xl p-6 md:p-7 flex flex-col justify-between noise-texture shadow-2xl border border-white/15">
|
||||
<div className="relative z-10 flex items-center justify-between mb-4 pb-3 border-b border-white/[0.08]">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber/80">
|
||||
{current.tag} PIPELINE
|
||||
<span className="text-xs font-medium text-brand-blue">
|
||||
{current.tag} 파이프라인
|
||||
</span>
|
||||
<span className="font-mono text-nano text-neutral-500 bg-surface-600/60 px-2 py-0.5 rounded border border-white/[0.04]">
|
||||
<span className="font-mono text-xs text-neutral-300 bg-surface-700/80 px-2 py-0.5 rounded-md border border-white/10">
|
||||
{current.model}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Led color="green" pulse />
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-emerald-400">{t.pipeline.panelActive}</span>
|
||||
<span className="text-xs font-medium text-emerald-400">{t.pipeline.panelActive}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex flex-col justify-center space-y-4">
|
||||
<div className="relative z-10 flex-1 flex flex-col justify-center space-y-3.5">
|
||||
{/* Simulated live pipeline flow */}
|
||||
<div className="font-mono text-xs text-neutral-500 leading-relaxed space-y-3">
|
||||
<div className="flex items-center justify-between text-neutral-400 bg-surface-950/60 p-2.5 rounded border border-white/[0.04]">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="text-xs text-neutral-400 leading-relaxed space-y-3 font-normal">
|
||||
<div className="flex items-center justify-between text-neutral-200 bg-surface-950/80 p-3 rounded-xl border border-white/10">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Led color="green" size="sm" />
|
||||
<span className="text-emerald-400/90 font-semibold">{t.pipeline.sttReady}</span>
|
||||
<span className="text-neutral-500">faster-whisper base</span>
|
||||
<span className="text-emerald-400 font-medium text-xs">{t.pipeline.sttReady}</span>
|
||||
</div>
|
||||
<span className="text-nano text-neutral-600 font-mono">0ms</span>
|
||||
<WaveBars className="h-4" />
|
||||
</div>
|
||||
|
||||
<div className="p-3 rounded-lg bg-surface-900/90 border border-white/[0.06] space-y-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber/70 flex items-center gap-1.5">
|
||||
<Led color="amber" size="sm" pulse />
|
||||
Raw Voice STT Output:
|
||||
</span>
|
||||
<span className="font-mono text-nano text-neutral-600">~0.42s</span>
|
||||
<div className="p-3.5 rounded-xl bg-surface-950/90 border border-white/10 space-y-2">
|
||||
<div className="flex items-center justify-between text-[11px] text-neutral-400 pb-1.5 border-b border-white/[0.06]">
|
||||
<span>1단계: RAW 음성 전사 (faster-whisper)</span>
|
||||
<span className="font-mono text-neutral-400">~0.42s</span>
|
||||
</div>
|
||||
<p className="text-sm font-sans text-neutral-300 font-medium">
|
||||
"{current.raw}"
|
||||
<p className="text-neutral-300 font-normal leading-normal italic text-xs">
|
||||
"{current.raw}"
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-3 rounded-lg bg-emerald-500/10 border border-emerald-500/20 space-y-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-emerald-400 flex items-center gap-1.5">
|
||||
<Led color="green" size="sm" />
|
||||
Transformed & Injected to Active Window:
|
||||
</span>
|
||||
<span className="font-mono text-nano text-emerald-400/80 font-bold">{current.latency}</span>
|
||||
<div className="p-3.5 rounded-xl bg-brand-blue/[0.08] border border-brand-blue/30 space-y-2">
|
||||
<div className="flex items-center justify-between text-[11px] text-brand-blue pb-1.5 border-b border-brand-blue/20">
|
||||
<span className="font-medium">2단계: LLM 문맥 정제 및 커서 자동 삽입</span>
|
||||
<span className="font-mono text-emerald-400 font-medium">{current.latency}</span>
|
||||
</div>
|
||||
<p className="text-sm font-sans text-emerald-200 font-semibold">
|
||||
"{current.clean}"
|
||||
<p className="text-white font-medium leading-relaxed text-sm">
|
||||
"{current.clean}"
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex items-center justify-between pt-4 border-t border-white/[0.04]">
|
||||
<WaveBars className="h-6" />
|
||||
<span className="font-mono text-nano text-neutral-400">
|
||||
Total Engine Latency: <strong className="text-white">{current.latency}</strong> (Zero Cloud Traffic)
|
||||
</span>
|
||||
<div className="pt-3 border-t border-white/[0.08] flex items-center justify-between text-xs text-neutral-400 font-normal">
|
||||
<span>총 레이턴시: <strong className="text-emerald-400 font-mono font-medium">{current.latency}</strong></span>
|
||||
<span className="text-emerald-400 font-medium">클라우드 유출 0바이트</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Crosshair>
|
||||
|
||||
{/* Right: Numbered Architecture Steps */}
|
||||
<div className="order-1 lg:order-2 space-y-2">
|
||||
{/* Right: Architecture Steps Breakdown */}
|
||||
<div className="order-1 lg:order-2 space-y-4">
|
||||
{t.pipeline.steps.map((step, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="group relative flex gap-5 p-5 rounded-card border border-white/[0.04] bg-surface-700/20 hover:border-brand-amber/30 hover:bg-surface-700/40 transition-all"
|
||||
className="group relative p-5 rounded-2xl bg-surface-800/80 border border-white/10 hover:border-brand-blue/40 hover:bg-surface-700/80 shadow-glass-card transition-all flex gap-4 backdrop-blur-xl"
|
||||
>
|
||||
{/* Number */}
|
||||
<div className="flex-shrink-0 w-12 h-12 rounded-card bg-surface-600 border border-white/[0.06] flex items-center justify-center shadow-sm">
|
||||
<span className="font-mono text-lg font-bold text-brand-amber">{String(i + 1).padStart(2, '0')}</span>
|
||||
<div className="w-11 h-11 rounded-xl bg-surface-700 border border-white/10 flex items-center justify-center flex-shrink-0 text-brand-blue font-medium text-sm group-hover:border-brand-blue/40 group-hover:bg-brand-blue/10 transition-colors shadow-sm">
|
||||
{String(i + 1).padStart(2, '0')}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber/70 block mb-1">
|
||||
{step.label}
|
||||
</span>
|
||||
<h3 className="font-display text-lg font-semibold text-neutral-100 mb-1.5">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="text-[11px] text-brand-blue font-medium">{step.label}</span>
|
||||
</div>
|
||||
<h3 className="text-base sm:text-lg font-medium text-neutral-50 mb-1.5 tracking-tight group-hover:text-white transition-colors">
|
||||
{step.title}
|
||||
</h3>
|
||||
<p className="text-sm text-neutral-400 leading-relaxed mb-2">
|
||||
<p className="text-sm text-neutral-300 leading-relaxed font-normal">
|
||||
{step.description}
|
||||
</p>
|
||||
<span className="inline-block font-mono text-nano text-neutral-400 bg-surface-600/80 px-2.5 py-1 rounded-sm border border-white/[0.05]">
|
||||
<span className="mt-2.5 inline-block text-xs text-neutral-400 font-mono bg-surface-900/60 px-2.5 py-0.5 rounded border border-white/[0.05]">
|
||||
{step.detail}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Connector */}
|
||||
{i < t.pipeline.steps.length - 1 && (
|
||||
<div className="absolute left-[42px] top-[72px] w-px h-6 bg-gradient-to-b from-brand-amber/30 to-transparent hidden lg:block" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ function CellValue({ value }: { value: string | boolean }) {
|
|||
if (value === true) {
|
||||
return (
|
||||
<span className="flex items-center justify-center">
|
||||
<span className="w-2 h-2 rounded-full bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.8)]" />
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-emerald-400 shadow-[0_0_8px_rgba(52,211,153,0.9)]" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ function CellValue({ value }: { value: string | boolean }) {
|
|||
</span>
|
||||
)
|
||||
}
|
||||
return <span className="font-mono text-xs text-neutral-300">{value}</span>
|
||||
return <span className="text-xs font-medium text-neutral-200">{value}</span>
|
||||
}
|
||||
|
||||
const PRICES = {
|
||||
|
|
@ -112,8 +112,8 @@ export function Pricing() {
|
|||
id: 'team',
|
||||
name: locale === 'ko' ? 'Team' : 'Team',
|
||||
price: `${currencySymbol}${formatPrice(teamPrice)}`,
|
||||
period: isAnnual ? `${t.pricing.annual} / ${locale === 'ko' ? '인' : 'seat'}` : `${t.pricing.monthly} / ${locale === 'ko' ? '인' : 'seat'}`,
|
||||
subPrice: isAnnual ? `${currencySymbol}${formatPrice(Math.round(activePrices.team.annual / 12))}${t.pricing.perMonth} / ${locale === 'ko' ? '인' : 'seat'}` : null,
|
||||
period: isAnnual ? `${periodLabel} (1인)` : `${periodLabel} (1인)`,
|
||||
subPrice: isAnnual ? `${currencySymbol}${formatPrice(Math.round(activePrices.team.annual / 12))}${t.pricing.perMonth}` : null,
|
||||
desc: locale === 'ko' ? '팀 공유 사전 & 지식 협업 & 엔터프라이즈 거버넌스' : 'Team shared dictionaries, knowledge & governance',
|
||||
popular: false,
|
||||
buttonText: locale === 'ko' ? '팀 도입 문의' : 'Contact Sales',
|
||||
|
|
@ -129,7 +129,7 @@ export function Pricing() {
|
|||
]
|
||||
|
||||
return (
|
||||
<section id="pricing" className="relative py-24 md:py-32">
|
||||
<section id="pricing" className="relative py-20 md:py-28">
|
||||
<Container>
|
||||
<SectionHeader
|
||||
index={t.pricing.index}
|
||||
|
|
@ -138,21 +138,23 @@ export function Pricing() {
|
|||
/>
|
||||
|
||||
{/* Reverse Trial Banner */}
|
||||
<div className="mb-10 p-4 rounded-xl bg-gradient-to-r from-brand-amber/20 via-surface-700/60 to-brand-amber/20 border border-brand-amber/40 shadow-glow-sm flex flex-col md:flex-row items-center justify-between gap-4 max-w-4xl mx-auto">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">✨</span>
|
||||
<div className="mb-12 px-6 sm:px-8 py-5 sm:py-6 rounded-2xl sm:rounded-3xl bg-gradient-to-r from-brand-blue/15 via-surface-800/90 to-brand-blue/15 border border-brand-blue/40 shadow-glow-sm flex flex-col md:flex-row items-start md:items-center justify-between gap-5 max-w-4xl mx-auto backdrop-blur-xl">
|
||||
<div className="flex items-center gap-4 sm:gap-5">
|
||||
<div className="w-10 h-10 rounded-2xl bg-brand-blue/20 border border-brand-blue/40 flex items-center justify-center text-brand-blue flex-shrink-0 shadow-sm text-lg">
|
||||
✨
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="font-mono text-xs font-bold uppercase tracking-wider text-brand-amber">
|
||||
<p className="text-sm sm:text-base font-medium text-brand-blue">
|
||||
{locale === 'ko' ? '14일 Reverse-Trial 무료 체험' : '14-Day Free Reverse Trial'}
|
||||
</p>
|
||||
<p className="text-xs text-neutral-300">
|
||||
<p className="text-xs sm:text-sm text-neutral-300 mt-1 font-normal leading-relaxed">
|
||||
{locale === 'ko'
|
||||
? '앱 설치 즉시 신용카드 등록 없이 Pro+의 모든 AI 기능을 14일 동안 무료로 체험할 수 있습니다.'
|
||||
: 'Experience full Pro+ capabilities for 14 days immediately after install — no credit card required.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className="px-3 py-1 rounded-full bg-brand-amber text-neutral-950 font-mono text-[10px] font-bold uppercase tracking-widest whitespace-nowrap">
|
||||
<span className="px-3.5 py-1.5 rounded-full bg-brand-blue/20 text-brand-blue border border-brand-blue/35 text-xs font-medium whitespace-nowrap shadow-sm self-start md:self-auto flex-shrink-0">
|
||||
{locale === 'ko' ? '자동 활성화' : 'Zero Friction'}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -160,23 +162,25 @@ export function Pricing() {
|
|||
{/* Currency and Billing Toggles */}
|
||||
<div className="flex flex-wrap items-center justify-center gap-4 mb-12">
|
||||
{/* Currency Toggle */}
|
||||
<div className="flex items-center rounded-panel bg-surface-800 p-1 border border-white/[0.08]">
|
||||
<div className="flex items-center rounded-xl bg-surface-800 p-1 border border-white/10 shadow-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCurrency('usd')}
|
||||
className={`font-mono text-xs px-3.5 py-1.5 rounded transition-all ${
|
||||
className={`text-xs px-3.5 py-1.5 rounded-lg transition-all ${
|
||||
currency === 'usd'
|
||||
? 'bg-surface-500 text-neutral-100 font-bold shadow-sm'
|
||||
: 'text-neutral-500 hover:text-neutral-300'
|
||||
? 'bg-surface-600 text-neutral-50 font-medium shadow-sm'
|
||||
: 'text-neutral-400 hover:text-white font-normal'
|
||||
}`}
|
||||
>
|
||||
USD ($)
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCurrency('krw')}
|
||||
className={`font-mono text-xs px-3.5 py-1.5 rounded transition-all ${
|
||||
className={`text-xs px-3.5 py-1.5 rounded-lg transition-all ${
|
||||
currency === 'krw'
|
||||
? 'bg-surface-500 text-neutral-100 font-bold shadow-sm'
|
||||
: 'text-neutral-500 hover:text-neutral-300'
|
||||
? 'bg-surface-600 text-neutral-50 font-medium shadow-sm'
|
||||
: 'text-neutral-400 hover:text-white font-normal'
|
||||
}`}
|
||||
>
|
||||
KRW (₩)
|
||||
|
|
@ -184,28 +188,30 @@ export function Pricing() {
|
|||
</div>
|
||||
|
||||
{/* Billing Cycle Toggle */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsAnnual(false)}
|
||||
className={`font-mono text-xs uppercase tracking-widest px-5 py-2.5 rounded-panel transition-all ${
|
||||
className={`text-xs px-4 py-2 rounded-xl transition-all ${
|
||||
!isAnnual
|
||||
? 'bg-surface-500 text-neutral-100 border border-white/[0.12] shadow-sm'
|
||||
: 'text-neutral-500 hover:text-neutral-300'
|
||||
? 'bg-surface-600 text-neutral-50 border border-white/15 shadow-sm font-medium'
|
||||
: 'text-neutral-400 hover:text-white border border-transparent font-normal'
|
||||
}`}
|
||||
>
|
||||
{t.pricing.billingToggleMonthly}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsAnnual(true)}
|
||||
className={`font-mono text-xs uppercase tracking-widest px-5 py-2.5 rounded-panel transition-all relative ${
|
||||
className={`text-xs px-4 py-2 rounded-xl transition-all relative ${
|
||||
isAnnual
|
||||
? 'bg-surface-500 text-neutral-100 border border-brand-amber/40 shadow-glow-sm'
|
||||
: 'text-neutral-500 hover:text-neutral-300'
|
||||
? 'bg-surface-600 text-neutral-50 border border-brand-blue/40 shadow-glow-sm font-medium'
|
||||
: 'text-neutral-400 hover:text-white border border-transparent font-normal'
|
||||
}`}
|
||||
>
|
||||
{t.pricing.billingToggleAnnual}
|
||||
{isAnnual && (
|
||||
<span className="absolute -top-2.5 -right-2.5 px-2 py-0.5 rounded-full bg-brand-amber text-white text-[9px] font-mono font-bold uppercase tracking-wider shadow-sm">
|
||||
<span className="absolute -top-2 -right-2 px-2 py-0.5 rounded-full bg-brand-blue text-white text-[10px] font-medium shadow-sm">
|
||||
{t.pricing.savePercent}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -214,128 +220,98 @@ export function Pricing() {
|
|||
</div>
|
||||
|
||||
{/* 4 Tier Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-5 mb-16">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 xl:gap-5 mb-14">
|
||||
{tiers.map((tier) => (
|
||||
<div
|
||||
key={tier.id}
|
||||
className={`relative rounded-2xl p-6 flex flex-col justify-between noise-texture transition-all duration-300 ${
|
||||
className={`relative rounded-3xl p-5 xl:p-6 flex flex-col justify-between noise-texture backdrop-blur-xl transition-all duration-300 ${
|
||||
tier.popular
|
||||
? 'bg-surface-700/70 border-2 border-brand-amber shadow-glow-md'
|
||||
: 'bg-surface-700/40 border border-white/[0.06] hover:border-white/[0.15]'
|
||||
? 'bg-surface-800/90 border-2 border-brand-blue shadow-glow-md'
|
||||
: 'bg-surface-800/70 border border-white/10 hover:border-brand-blue/35 hover:bg-surface-700/80 shadow-glass-card'
|
||||
}`}
|
||||
>
|
||||
{tier.popular && (
|
||||
<div className="absolute -top-3.5 left-1/2 -translate-x-1/2">
|
||||
<span className="px-3.5 py-1 rounded-full bg-brand-amber text-white text-nano font-mono font-bold uppercase tracking-widest shadow-md">
|
||||
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
||||
<span className="px-3.5 py-0.5 rounded-full bg-brand-blue text-white text-xs font-medium shadow-md">
|
||||
{t.pricing.popular}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className={`font-mono text-xs font-bold uppercase tracking-widest ${tier.popular ? 'text-brand-amber' : 'text-neutral-400'}`}>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-lg font-medium text-neutral-50">
|
||||
{tier.name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="mb-2 flex items-baseline gap-1.5">
|
||||
<span className="font-display text-3xl font-extrabold text-neutral-50">
|
||||
{tier.price}
|
||||
</span>
|
||||
<span className="font-sans text-xs text-neutral-400 font-normal">
|
||||
{tier.period}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{tier.subPrice && (
|
||||
<div className="font-mono text-nano text-neutral-500 mb-4">
|
||||
{tier.subPrice}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-xs text-neutral-400 mb-5 leading-relaxed min-h-[38px]">
|
||||
<p className="text-xs text-neutral-300 min-h-[36px] mb-4 font-normal leading-relaxed">
|
||||
{tier.desc}
|
||||
</p>
|
||||
|
||||
<div className="h-px bg-white/[0.06] mb-5" />
|
||||
<div className="mb-5 pb-4 border-b border-white/[0.08]">
|
||||
<div className="flex items-baseline flex-wrap gap-1.5">
|
||||
<span className="text-2xl sm:text-[1.65rem] xl:text-[1.85rem] font-medium text-neutral-50 tracking-tight whitespace-nowrap">
|
||||
{tier.price}
|
||||
</span>
|
||||
<span className="text-xs text-neutral-400 font-normal whitespace-nowrap">
|
||||
{tier.period}
|
||||
</span>
|
||||
</div>
|
||||
{tier.subPrice && (
|
||||
<span className="block text-xs text-neutral-400 font-normal mt-1">
|
||||
(월 {tier.subPrice})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ul className="space-y-2.5 mb-7">
|
||||
{tier.features.map((feat, idx) => (
|
||||
<li key={idx} className="flex items-start gap-2.5 text-xs text-neutral-300">
|
||||
<span className="text-emerald-400 font-bold mt-0.5">✓</span>
|
||||
<span>{feat}</span>
|
||||
<ul className="space-y-2.5 mb-6 text-xs sm:text-sm text-neutral-300 font-normal">
|
||||
{tier.features.map((feat, i) => (
|
||||
<li key={i} className="flex items-start gap-2">
|
||||
<span className="text-emerald-400 font-medium flex-shrink-0 mt-0.5">✓</span>
|
||||
<span className="leading-snug">{feat}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<GlowButton href="#" variant={tier.buttonVariant} size="md" className="w-full text-xs">
|
||||
<GlowButton
|
||||
href="#download"
|
||||
variant={tier.buttonVariant}
|
||||
size="md"
|
||||
className="w-full text-xs sm:text-sm font-medium py-3 sm:py-3.5"
|
||||
>
|
||||
{tier.buttonText}
|
||||
</GlowButton>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Detailed Comparison Table */}
|
||||
{/* Feature Comparison Matrix */}
|
||||
<div className="overflow-x-auto -mx-5 md:mx-0">
|
||||
<table className="w-full min-w-[700px] border-collapse bg-surface-800/30 rounded-xl overflow-hidden border border-white/[0.04]">
|
||||
{/* Header */}
|
||||
<table className="w-full min-w-[700px] text-left border-collapse rounded-2xl overflow-hidden bg-surface-800/60 border border-white/10 shadow-glass-card backdrop-blur-xl">
|
||||
<thead>
|
||||
<tr className="border-b border-white/[0.08] bg-surface-700/50">
|
||||
<th className="text-left py-4 px-5 font-mono text-nano uppercase tracking-widest text-neutral-400 w-[35%]">
|
||||
{t.pricing.featureLabel}
|
||||
</th>
|
||||
<th className="text-center py-4 px-3 w-[15%] font-mono text-nano uppercase tracking-widest text-neutral-300">
|
||||
{t.pricing.free}
|
||||
</th>
|
||||
<th className="text-center py-4 px-3 w-[18%] font-mono text-nano uppercase tracking-widest text-brand-amber bg-brand-amber/[0.04]">
|
||||
{t.pricing.pro}
|
||||
</th>
|
||||
<th className="text-center py-4 px-3 w-[16%] font-mono text-nano uppercase tracking-widest text-neutral-300">
|
||||
{t.pricing.proPlus}
|
||||
</th>
|
||||
<th className="text-center py-4 px-3 w-[16%] font-mono text-nano uppercase tracking-widest text-neutral-300">
|
||||
Team
|
||||
</th>
|
||||
<tr className="border-b border-white/10 bg-surface-700/80 text-xs text-neutral-200 font-medium">
|
||||
<th className="p-4 w-2/5">기능 명세</th>
|
||||
<th className="p-4 text-center w-[15%]">FREE</th>
|
||||
<th className="p-4 text-center w-[15%] text-brand-blue font-medium">PRO</th>
|
||||
<th className="p-4 text-center w-[15%] text-purple-300">PRO+</th>
|
||||
<th className="p-4 text-center w-[15%]">TEAM</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{/* Body */}
|
||||
<tbody>
|
||||
{t.pricing.rows.map((row, i) => (
|
||||
<tr
|
||||
key={i}
|
||||
className={`border-b border-white/[0.03] hover:bg-surface-700/30 transition-colors ${
|
||||
i % 2 === 0 ? 'bg-transparent' : 'bg-surface-800/20'
|
||||
}`}
|
||||
>
|
||||
<td className="py-3 px-5 font-sans text-xs text-neutral-300 font-medium">
|
||||
{row.feature}
|
||||
</td>
|
||||
<td className="py-3 px-3 text-center">
|
||||
<CellValue value={row.free} />
|
||||
</td>
|
||||
<td className="py-3 px-3 text-center bg-brand-amber/[0.04]">
|
||||
<CellValue value={row.pro} />
|
||||
</td>
|
||||
<td className="py-3 px-3 text-center">
|
||||
<CellValue value={row.proPlus} />
|
||||
</td>
|
||||
<td className="py-3 px-3 text-center">
|
||||
<CellValue value={true} />
|
||||
</td>
|
||||
<tbody className="divide-y divide-white/[0.05] text-xs sm:text-sm">
|
||||
{t.pricing.rows.map((row, idx) => (
|
||||
<tr key={idx} className="hover:bg-surface-700/30 transition-colors">
|
||||
<td className="p-3.5 text-neutral-300 font-normal">{row.feature}</td>
|
||||
<td className="p-3.5 text-center text-neutral-400"><CellValue value={row.free} /></td>
|
||||
<td className="p-3.5 text-center bg-brand-blue/[0.03]"><CellValue value={row.pro} /></td>
|
||||
<td className="p-3.5 text-center bg-purple-500/[0.03]"><CellValue value={row.proPlus} /></td>
|
||||
<td className="p-3.5 text-center"><CellValue value={row.proPlus} /></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p className="text-center font-mono text-nano text-neutral-500 mt-8 uppercase tracking-widest">
|
||||
{locale === 'ko'
|
||||
? '모든 가격은 세금 별도입니다. Stripe & Toss Payments / PortOne / Payple을 통한 안전한 결제 및 세금계산서 자동 발행.'
|
||||
: 'All prices exclude tax. Secure global checkout via Stripe and localized recurring payments.'}
|
||||
</p>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,20 +21,20 @@ export function Privacy() {
|
|||
},
|
||||
{
|
||||
feature: locale === 'ko' ? '네트워크 전송 지연 (RTT)' : 'Network Roundtrip Latency',
|
||||
d3ro: '0ms (On-Device Memory)',
|
||||
d3ro: '0ms (온디바이스 메모리)',
|
||||
cloud: '600ms ~ 2,500ms',
|
||||
d3roOk: true,
|
||||
},
|
||||
{
|
||||
feature: locale === 'ko' ? '무제한 딕테이션 비용' : 'Unlimited Voice Dictation Cost',
|
||||
d3ro: locale === 'ko' ? '무료 ($0 Forever)' : '$0 Forever Free',
|
||||
cloud: '$15 ~ $30 / Month',
|
||||
d3ro: locale === 'ko' ? '영구 무료 (₩0)' : '$0 Forever Free',
|
||||
cloud: '월 2만 ~ 4만원',
|
||||
d3roOk: true,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<section id="privacy" className="relative py-24 md:py-32 overflow-hidden">
|
||||
<section id="privacy" className="relative py-20 md:py-28 overflow-hidden">
|
||||
<Container>
|
||||
<SectionHeader
|
||||
index={t.privacy.index}
|
||||
|
|
@ -43,31 +43,31 @@ export function Privacy() {
|
|||
/>
|
||||
|
||||
{/* CRT instrument panel */}
|
||||
<div className="crt-screen rounded-2xl p-6 md:p-10 noise-texture mb-12 shadow-2xl border border-white/[0.08]">
|
||||
<div className="crt-screen rounded-3xl p-6 md:p-8 noise-texture mb-12 shadow-2xl border border-white/15">
|
||||
<div className="relative z-10">
|
||||
{/* Panel header */}
|
||||
<div className="flex items-center justify-between mb-8 pb-4 border-b border-white/[0.06]">
|
||||
<div className="flex items-center justify-between mb-6 pb-3.5 border-b border-white/[0.08]">
|
||||
<div className="flex items-center gap-2">
|
||||
<Led color="green" pulse />
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-emerald-400">
|
||||
<span className="text-xs font-medium text-emerald-400">
|
||||
{t.privacy.monitorTitle}
|
||||
</span>
|
||||
</div>
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-neutral-400 bg-surface-600/60 px-2.5 py-1 rounded border border-white/[0.04]">
|
||||
AIR-GAP COMPLIANT
|
||||
<span className="text-xs font-medium text-neutral-300 bg-surface-700/80 px-3 py-1 rounded-full border border-white/10">
|
||||
에어갭 규정 준수
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Metric grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-6 md:gap-8">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{t.privacy.metrics.map((metric, i) => (
|
||||
<div key={i} className="flex flex-col gap-2 p-3 rounded-lg bg-surface-900/60 border border-white/[0.03]">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-neutral-500">
|
||||
<div key={i} className="flex flex-col gap-1.5 p-3.5 rounded-xl bg-surface-900/80 border border-white/10 shadow-inner">
|
||||
<span className="text-xs text-neutral-400 font-normal">
|
||||
{metric.label}
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<Led color="green" size="sm" />
|
||||
<span className="font-mono text-sm font-semibold text-neutral-100 tracking-wide">
|
||||
<span className="text-sm sm:text-base font-medium text-neutral-100">
|
||||
{metric.value}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -78,23 +78,23 @@ export function Privacy() {
|
|||
</div>
|
||||
|
||||
{/* Comparison Matrix: D3RO Voice vs Cloud Services */}
|
||||
<div className="mb-12 overflow-x-auto">
|
||||
<table className="w-full text-left border-collapse rounded-xl overflow-hidden bg-surface-800/40 border border-white/[0.06]">
|
||||
<div className="mb-12 overflow-x-auto -mx-5 md:mx-0">
|
||||
<table className="w-full min-w-[650px] text-left border-collapse rounded-2xl overflow-hidden bg-surface-800/60 border border-white/10 shadow-glass-card backdrop-blur-xl">
|
||||
<thead>
|
||||
<tr className="border-b border-white/[0.08] bg-surface-700/50 font-mono text-nano uppercase tracking-wider text-neutral-400">
|
||||
<tr className="border-b border-white/10 bg-surface-700/80 text-xs text-neutral-300 font-medium">
|
||||
<th className="p-4 w-1/2">{locale === 'ko' ? '보안 및 프라이버시 비교 기준' : 'Security & Privacy Criterion'}</th>
|
||||
<th className="p-4 text-brand-amber font-bold w-1/4">D3RO VOICE (LOCAL)</th>
|
||||
<th className="p-4 text-neutral-500 w-1/4">CLOUD STT SERVICES</th>
|
||||
<th className="p-4 text-brand-blue font-medium w-1/4">D3RO VOICE (로컬)</th>
|
||||
<th className="p-4 text-neutral-400 w-1/4">일반 클라우드 STT</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-white/[0.04] text-xs">
|
||||
<tbody className="divide-y divide-white/[0.06] text-sm">
|
||||
{comparisonRows.map((row, idx) => (
|
||||
<tr key={idx} className="hover:bg-surface-700/30 transition-colors">
|
||||
<td className="p-4 font-sans font-medium text-neutral-200">{row.feature}</td>
|
||||
<td className="p-4 font-mono font-bold text-emerald-400 bg-emerald-500/[0.04]">
|
||||
<tr key={idx} className="hover:bg-surface-700/40 transition-colors">
|
||||
<td className="p-4 text-neutral-200 font-normal">{row.feature}</td>
|
||||
<td className="p-4 font-medium text-emerald-400 bg-emerald-500/10">
|
||||
✓ {row.d3ro}
|
||||
</td>
|
||||
<td className="p-4 font-mono text-neutral-500">
|
||||
<td className="p-4 text-neutral-400 font-normal">
|
||||
{row.cloud}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -103,19 +103,19 @@ export function Privacy() {
|
|||
</table>
|
||||
</div>
|
||||
|
||||
{/* Guarantees list */}
|
||||
{/* Guarantee bullet cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{t.privacy.guarantees.map((item, i) => (
|
||||
{t.privacy.guarantees.map((g, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-start gap-3 p-4 rounded-xl bg-surface-700/40 border border-white/[0.04]"
|
||||
className="flex items-center gap-3.5 px-5 py-4 rounded-2xl bg-surface-800/70 border border-white/10 backdrop-blur-sm shadow-sm"
|
||||
>
|
||||
<div className="w-5 h-5 rounded-full bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||
<svg className="w-3 h-3 text-emerald-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
|
||||
<div className="w-5 h-5 rounded-full bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400 flex-shrink-0">
|
||||
<svg className="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="20 6 9 17 4 12" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-sm text-neutral-300 leading-relaxed font-medium">{item}</span>
|
||||
<span className="text-sm text-neutral-200 font-normal">{g}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue