feat: complete release preparation, 10+ ad mediation, CI/CD, and docker deployment
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped

This commit is contained in:
Yun Chan 2026-08-20 11:12:05 +09:00
parent 5cd1de6859
commit 708e20f747
406 changed files with 42464 additions and 6199 deletions

View file

@ -0,0 +1,994 @@
<!DOCTYPE html>
<html lang="ko" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>D3RO Voice — Launch Readiness & Monetization Simulation Hub</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
100: '#dbeafe',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
900: '#1e3a8a',
},
cyan: {
400: '#22d3ee',
500: '#06b6d4',
},
dark: {
950: '#030712',
900: '#0b101f',
850: '#0f172a',
800: '#1e293b',
700: '#334155',
}
},
fontFamily: {
sans: ['Pretendard', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif'],
mono: ['JetBrains Mono', 'Fira Code', 'monospace']
}
}
}
}
</script>
<style>
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css');
body {
font-family: 'Pretendard', sans-serif;
background-color: #030712;
color: #f1f5f9;
}
.glass-panel {
background: rgba(15, 23, 42, 0.75);
backdrop-filter: blur(20px);
border: 1px solid rgba(148, 163, 184, 0.12);
}
.glass-card {
background: rgba(30, 41, 59, 0.6);
backdrop-filter: blur(16px);
border: 1px solid rgba(148, 163, 184, 0.15);
}
.glow-blue {
box-shadow: 0 0 25px rgba(59, 130, 246, 0.25);
}
.glow-cyan {
box-shadow: 0 0 25px rgba(6, 182, 212, 0.3);
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(15, 23, 42, 0.5);
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(100, 116, 139, 0.5);
border-radius: 999px;
}
@keyframes pulseGlow {
0%, 100% { opacity: 0.8; transform: scale(1); }
50% { opacity: 1; transform: scale(1.05); }
}
.live-dot {
animation: pulseGlow 2s infinite ease-in-out;
}
</style>
</head>
<body class="min-h-screen flex flex-col custom-scrollbar selection:bg-brand-500 selection:text-white">
<!-- TOP APP BAR -->
<header class="border-b border-slate-800/80 bg-dark-900/90 backdrop-blur-xl sticky top-0 z-50 px-4 lg:px-8 py-3 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-xl bg-gradient-to-tr from-cyan-500 via-brand-500 to-indigo-600 flex items-center justify-center shadow-lg shadow-brand-500/30">
<i data-lucide="mic" class="w-5 h-5 text-white"></i>
</div>
<div>
<div class="flex items-center gap-2">
<span class="font-bold text-lg tracking-tight text-white">D3RO Voice</span>
<span class="px-2 py-0.5 text-xs font-mono font-semibold rounded-full bg-cyan-500/10 text-cyan-400 border border-cyan-500/20">LAUNCH READY v1.0</span>
</div>
<p class="text-xs text-slate-400">Commercialization & Operations Sandbox</p>
</div>
</div>
<!-- MAIN NAV TABS -->
<nav class="hidden md:flex items-center gap-1 bg-dark-950/80 p-1.5 rounded-2xl border border-slate-800">
<button onclick="switchTab('ads')" id="tab-btn-ads" class="tab-btn active px-4 py-2 rounded-xl text-sm font-medium transition-all flex items-center gap-2 bg-brand-600 text-white shadow-md">
<i data-lucide="tv" class="w-4 h-4"></i> Free Tier Ads
</button>
<button onclick="switchTab('payment')" id="tab-btn-payment" class="tab-btn px-4 py-2 rounded-xl text-sm font-medium transition-all flex items-center gap-2 text-slate-400 hover:text-white hover:bg-slate-800/50">
<i data-lucide="credit-card" class="w-4 h-4"></i> Multi-PG Checkout
</button>
<button onclick="switchTab('support')" id="tab-btn-support" class="tab-btn px-4 py-2 rounded-xl text-sm font-medium transition-all flex items-center gap-2 text-slate-400 hover:text-white hover:bg-slate-800/50">
<i data-lucide="headphones" class="w-4 h-4"></i> AI Customer Support (CA)
</button>
<button onclick="switchTab('admin')" id="tab-btn-admin" class="tab-btn px-4 py-2 rounded-xl text-sm font-medium transition-all flex items-center gap-2 text-slate-400 hover:text-white hover:bg-slate-800/50">
<i data-lucide="shield-alert" class="w-4 h-4"></i> Admin Command Desk
</button>
</nav>
<!-- LIVE TELEMETRY & MODE -->
<div class="flex items-center gap-3">
<div class="hidden sm:flex items-center gap-2 px-3 py-1.5 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-xs font-mono">
<span class="w-2 h-2 rounded-full bg-emerald-400 live-dot"></span>
<span>GATEWAY: CONNECTED</span>
</div>
<span class="text-xs font-mono text-slate-400 bg-slate-800/50 px-3 py-1.5 rounded-lg border border-slate-700">Production Ready</span>
</div>
</header>
<!-- SUB-HEADER ON MOBILE -->
<div class="md:hidden flex overflow-x-auto gap-2 p-2 bg-dark-900 border-b border-slate-800 custom-scrollbar">
<button onclick="switchTab('ads')" class="tab-btn px-3 py-1.5 rounded-lg text-xs font-medium bg-brand-600 text-white whitespace-nowrap">Free Ads</button>
<button onclick="switchTab('payment')" class="tab-btn px-3 py-1.5 rounded-lg text-xs font-medium text-slate-400 bg-slate-800 whitespace-nowrap">Payment</button>
<button onclick="switchTab('support')" class="tab-btn px-3 py-1.5 rounded-lg text-xs font-medium text-slate-400 bg-slate-800 whitespace-nowrap">AI Support</button>
<button onclick="switchTab('admin')" class="tab-btn px-3 py-1.5 rounded-lg text-xs font-medium text-slate-400 bg-slate-800 whitespace-nowrap">Admin Desk</button>
</div>
<!-- MAIN VIEWPORT CONTAINER -->
<main class="flex-1 p-4 lg:p-8 max-w-7xl mx-auto w-full">
<!-- TAB 1: FREE TIER AD MONETIZATION -->
<section id="tab-ads" class="space-y-6">
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
<div>
<h1 class="text-2xl lg:text-3xl font-bold tracking-tight text-white flex items-center gap-3">
<span>Free Tier Ad Monetization Experience</span>
<span class="text-xs px-2.5 py-1 rounded-md bg-cyan-500/10 text-cyan-400 border border-cyan-500/20 font-mono">Zero-Latency Voice Guarantee</span>
</h1>
<p class="text-slate-400 text-sm mt-1">
Analyze how D3RO Voice monetizes free tier users without disturbing real-time push-to-talk dictation.
</p>
</div>
<!-- User State Controller -->
<div class="flex items-center gap-3 bg-dark-900 p-2 rounded-xl border border-slate-800">
<span class="text-xs text-slate-400">User Tier:</span>
<select id="user-tier-selector" onchange="toggleUserTier(this.value)" class="bg-dark-950 text-white text-xs rounded-lg px-3 py-1.5 border border-slate-700 font-semibold focus:outline-none focus:border-brand-500">
<option value="free">Free Tier (Ads Enabled)</option>
<option value="pro">Pro Tier (100% Ad-Free)</option>
<option value="pro_plus">Pro+ Tier (VIP Ad-Free)</option>
</select>
<div class="h-4 w-px bg-slate-700"></div>
<span class="text-xs text-cyan-400 font-mono" id="quota-display">Cloud Quota: 45 / 250</span>
</div>
</div>
<!-- GRID OF AD PLACEMENTS & STRATEGIES -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- LEFT: DESKTOP APP PREVIEW WITH INTERACTIVE AD SLOTS -->
<div class="lg:col-span-2 glass-panel rounded-2xl p-5 border border-slate-800 relative overflow-hidden flex flex-col">
<div class="flex items-center justify-between pb-3 mb-4 border-b border-slate-800">
<div class="flex items-center gap-2">
<span class="w-3 h-3 rounded-full bg-red-500/80"></span>
<span class="w-3 h-3 rounded-full bg-amber-500/80"></span>
<span class="w-3 h-3 rounded-full bg-emerald-500/80"></span>
<span class="ml-2 text-xs font-mono text-slate-400">D3RO Voice Desktop App Window</span>
</div>
<span class="text-xs text-slate-500 font-mono">Live Interactive Mockup</span>
</div>
<!-- MOCK APP INTERFACE -->
<div class="flex-1 bg-dark-950 rounded-xl p-4 border border-slate-800/80 flex flex-col gap-4">
<!-- App Header & Quick Stats -->
<div class="flex items-center justify-between bg-dark-900/60 p-3 rounded-lg border border-slate-800">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-lg bg-brand-500/20 border border-brand-500/30 flex items-center justify-center">
<i data-lucide="sparkles" class="w-4 h-4 text-brand-400"></i>
</div>
<div>
<h4 class="text-sm font-semibold text-white">AI Dictation & Meeting Memo</h4>
<p class="text-xs text-slate-400">Model: faster-whisper (large-v3-turbo, GPU On)</p>
</div>
</div>
<button onclick="triggerVoiceSim()" class="px-3 py-1.5 bg-gradient-to-r from-cyan-500 to-brand-600 hover:from-cyan-400 hover:to-brand-500 text-white text-xs font-semibold rounded-lg shadow flex items-center gap-1.5">
<i data-lucide="mic" class="w-3.5 h-3.5"></i> Test PTT Capsule (Clean HUD)
</button>
</div>
<!-- Simulated Voice Transcript Stream -->
<div class="bg-dark-900/40 p-4 rounded-lg border border-slate-800/60 min-h-[140px] text-sm text-slate-300 font-sans space-y-2">
<div class="flex items-center justify-between text-xs text-slate-500 font-mono border-b border-slate-800/40 pb-1">
<span>RECENT TRANSCRIPT (07:54:12)</span>
<span class="text-emerald-400">142ms Latency</span>
</div>
<p id="transcript-stream">"출시 준비 전략을 점검하고 있습니다. Free tier 사용자는 100% 로컬 Whisper를 통해 무제한 음성 인식을 지원하며, 클라우드 AI 정제 쿼터 소진 시 리워드 광고로 충전할 수 있습니다."</p>
</div>
<!-- AD PLACEMENT 1: DOCK SUBTLE BANNER (FREE TIER ONLY) -->
<div id="dock-ad-container" class="transition-all duration-300">
<div class="bg-gradient-to-r from-slate-900 via-dark-850 to-slate-900 p-3 rounded-xl border border-slate-700/60 flex items-center justify-between gap-4 relative group">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-indigo-500/20 border border-indigo-500/30 flex items-center justify-center shrink-0">
<i data-lucide="cpu" class="w-5 h-5 text-indigo-400"></i>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-xs font-bold text-white">Cursor AI — The AI-First Code Editor</span>
<span class="px-1.5 py-0.2 bg-slate-700 text-[10px] text-slate-300 rounded font-mono">Sponsor</span>
</div>
<p class="text-xs text-slate-400">Build software faster with intelligent voice & multi-file agents.</p>
</div>
</div>
<button onclick="simulateAdClick('EthicalAds / Direct Sponsor')" class="shrink-0 px-3 py-1.5 bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-semibold rounded-lg transition-colors">
Learn More
</button>
</div>
</div>
<!-- PRO AD-FREE BADGE (HIDDEN IN FREE) -->
<div id="pro-adfree-container" class="hidden p-3 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-between">
<div class="flex items-center gap-2 text-emerald-400 text-xs font-medium">
<i data-lucide="shield-check" class="w-4 h-4"></i>
<span>Pro Tier Active: 100% Ad-Free Pristine Environment</span>
</div>
<span class="text-[11px] font-mono text-emerald-300">Zero Commercial Interrupts</span>
</div>
<!-- ACTION BUTTONS: REWARDED QUOTA & EXPORT -->
<div class="grid grid-cols-2 gap-3 pt-2">
<button onclick="openRewardedAdModal()" class="p-3 rounded-xl bg-dark-900 hover:bg-slate-800 border border-slate-700 text-left transition-all group flex items-center justify-between">
<div>
<div class="flex items-center gap-1.5 text-xs font-semibold text-cyan-400 group-hover:text-cyan-300">
<i data-lucide="gift" class="w-4 h-4"></i> Refill AI Quota (+50)
</div>
<p class="text-[11px] text-slate-400 mt-0.5">Watch 15s sponsor video</p>
</div>
<i data-lucide="play-circle" class="w-5 h-5 text-slate-400 group-hover:text-cyan-400"></i>
</button>
<button onclick="openExportModal()" class="p-3 rounded-xl bg-dark-900 hover:bg-slate-800 border border-slate-700 text-left transition-all group flex items-center justify-between">
<div>
<div class="flex items-center gap-1.5 text-xs font-semibold text-purple-400 group-hover:text-purple-300">
<i data-lucide="download" class="w-4 h-4"></i> Export Transcript (Docx)
</div>
<p class="text-[11px] text-slate-400 mt-0.5">Includes clean sponsor card</p>
</div>
<i data-lucide="file-text" class="w-5 h-5 text-slate-400 group-hover:text-purple-400"></i>
</button>
</div>
</div>
</div>
<!-- RIGHT: AD ARCHITECTURE & METRICS EXPLAINER -->
<div class="glass-panel rounded-2xl p-5 border border-slate-800 flex flex-col justify-between space-y-4">
<div>
<h3 class="text-base font-bold text-white flex items-center gap-2">
<i data-lucide="trending-up" class="w-4 h-4 text-emerald-400"></i>
Ad Monetization Framework
</h3>
<p class="text-xs text-slate-400 mt-1">Research-backed strategy (50+ Sources Synthesized)</p>
<div class="mt-4 space-y-3">
<div class="p-3 rounded-xl bg-dark-900/80 border border-slate-800">
<div class="flex items-center justify-between text-xs">
<span class="font-semibold text-white">1. Opt-in Rewarded Video</span>
<span class="font-mono text-emerald-400">$8.50 eCPM</span>
</div>
<p class="text-[11px] text-slate-400 mt-1">User voluntarily watches a 15-second sponsor to unlock 50 additional Claude/GPT-4o cloud polishing tokens.</p>
</div>
<div class="p-3 rounded-xl bg-dark-900/80 border border-slate-800">
<div class="flex items-center justify-between text-xs">
<span class="font-semibold text-white">2. Ethical Non-Intrusive Banner</span>
<span class="font-mono text-cyan-400">$3.20 eCPM</span>
</div>
<p class="text-[11px] text-slate-400 mt-1">Zero-cookie, developer-tool sponsorships in the main window dock. Completely separated from voice HUD.</p>
</div>
<div class="p-3 rounded-xl bg-dark-900/80 border border-slate-800">
<div class="flex items-center justify-between text-xs">
<span class="font-semibold text-white">3. Export Sponsor Placement</span>
<span class="font-mono text-purple-400">$5.00 eCPM</span>
</div>
<p class="text-[11px] text-slate-400 mt-1">Lightweight 5s sponsor interstitial shown only during high-value batch meeting document export.</p>
</div>
</div>
</div>
<!-- Live Telemetry Box -->
<div class="p-3 rounded-xl bg-brand-950/40 border border-brand-500/20 text-xs space-y-1.5 font-mono">
<div class="flex justify-between text-slate-400">
<span>Simulated Impressions:</span>
<span id="stat-impressions" class="text-white font-bold">1,482</span>
</div>
<div class="flex justify-between text-slate-400">
<span>Estimated Ad Rev:</span>
<span id="stat-revenue" class="text-emerald-400 font-bold">$7.84</span>
</div>
<div class="flex justify-between text-slate-400">
<span>Avg. Fill Rate:</span>
<span class="text-cyan-400 font-bold">98.2%</span>
</div>
</div>
</div>
</div>
</section>
<!-- TAB 2: MULTI-PG PAYMENT & BILLING SIMULATOR -->
<section id="tab-payment" class="hidden space-y-6">
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
<div>
<h1 class="text-2xl lg:text-3xl font-bold tracking-tight text-white flex items-center gap-3">
<span>Global & Domestic Payment Gateway Hub</span>
<span class="text-xs px-2.5 py-1 rounded-md bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 font-mono">Stripe + Toss Payments</span>
</h1>
<p class="text-slate-400 text-sm mt-1">
Seamless multi-currency checkout: Korean Quick Pay (Toss, Kakao, Naver) + Global Cards (Stripe).
</p>
</div>
<div class="flex items-center gap-2 bg-dark-900 p-1.5 rounded-xl border border-slate-800">
<div class="flex bg-dark-950 p-1 rounded-lg border border-slate-800 text-xs">
<button onclick="setCurrency('KRW')" id="curr-krw" class="px-2.5 py-1 rounded font-bold bg-brand-600 text-white">KRW (₩)</button>
<button onclick="setCurrency('USD')" id="curr-usd" class="px-2.5 py-1 rounded text-slate-400 hover:text-white">USD ($)</button>
</div>
<div class="h-4 w-px bg-slate-700"></div>
<button onclick="toggleBillingPeriod()" id="billing-cycle-btn" class="px-3 py-1 bg-dark-950 hover:bg-slate-800 text-xs font-semibold text-slate-200 rounded-lg border border-slate-700 flex items-center gap-1.5">
<span id="cycle-label">Annual (20% Discount)</span>
<span class="px-1.5 py-0.5 bg-emerald-500/20 text-emerald-400 text-[10px] rounded font-mono">SAVE 20%</span>
</button>
</div>
</div>
<!-- PRICING TIERS GRID -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- PLAN 1: PRO -->
<div class="glass-card rounded-2xl p-6 border border-slate-700 flex flex-col justify-between relative hover:border-brand-500/50 transition-all">
<div>
<div class="flex justify-between items-start">
<div>
<h3 class="text-lg font-bold text-white">Pro</h3>
<p class="text-xs text-slate-400 mt-0.5">For power creators & writers</p>
</div>
<span class="px-2 py-0.5 bg-brand-500/10 text-brand-400 text-xs font-mono rounded border border-brand-500/20">INDIVIDUAL</span>
</div>
<div class="my-4">
<div class="flex items-baseline gap-1">
<span class="text-3xl font-black text-white" id="price-pro">₩119,000</span>
<span class="text-xs text-slate-400" id="period-pro">/ year</span>
</div>
<p class="text-xs text-emerald-400 mt-1" id="sub-pro">~ ₩9,900 / month</p>
</div>
<ul class="space-y-2.5 text-xs text-slate-300 mt-4 border-t border-slate-800 pt-4">
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-emerald-400"></i> 100% Ad-Free Experience</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-emerald-400"></i> 1,850 Cloud LLM Tokens/month</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-emerald-400"></i> Unlimited Transcription History</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-emerald-400"></i> Live Caption & Screen Context</li>
</ul>
</div>
<button onclick="startCheckout('pro', 'Pro Plan')" class="mt-6 w-full py-2.5 bg-brand-600 hover:bg-brand-500 text-white font-semibold text-xs rounded-xl shadow-lg shadow-brand-500/20 transition-all">
Subscribe Pro
</button>
</div>
<!-- PLAN 2: PRO+ -->
<div class="glass-card rounded-2xl p-6 border-2 border-brand-500 bg-gradient-to-b from-brand-950/40 via-dark-900 to-dark-900 flex flex-col justify-between relative shadow-xl shadow-brand-500/10">
<div class="absolute -top-3 right-6 bg-gradient-to-r from-cyan-500 to-brand-500 text-white text-[11px] font-bold px-3 py-0.5 rounded-full shadow">
MOST POPULAR
</div>
<div>
<div class="flex justify-between items-start">
<div>
<h3 class="text-lg font-bold text-white flex items-center gap-1.5">
Pro+ <i data-lucide="zap" class="w-4 h-4 text-cyan-400 fill-cyan-400"></i>
</h3>
<p class="text-xs text-slate-400 mt-0.5">Professional Meeting Suite</p>
</div>
<span class="px-2 py-0.5 bg-cyan-500/10 text-cyan-400 text-xs font-mono rounded border border-cyan-500/20">PRO MEETING</span>
</div>
<div class="my-4">
<div class="flex items-baseline gap-1">
<span class="text-3xl font-black text-white" id="price-proplus">₩229,000</span>
<span class="text-xs text-slate-400" id="period-proplus">/ year</span>
</div>
<p class="text-xs text-cyan-400 mt-1" id="sub-proplus">~ ₩19,080 / month</p>
</div>
<ul class="space-y-2.5 text-xs text-slate-300 mt-4 border-t border-slate-800 pt-4">
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-cyan-400"></i> Everything in Pro (Ad-Free)</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-cyan-400"></i> Unlimited Cloud LLM Polish</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-cyan-400"></i> AI Speaker Diarization (화자 구분)</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-cyan-400"></i> Meeting Pro Multi-Doc Generator</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-cyan-400"></i> VIP Priority Customer Support</li>
</ul>
</div>
<button onclick="startCheckout('pro_plus', 'Pro+ Plan')" class="mt-6 w-full py-2.5 bg-gradient-to-r from-cyan-500 to-brand-600 hover:from-cyan-400 hover:to-brand-500 text-white font-bold text-xs rounded-xl shadow-lg shadow-cyan-500/25 transition-all">
Subscribe Pro+
</button>
</div>
<!-- PLAN 3: TEAM -->
<div class="glass-card rounded-2xl p-6 border border-slate-700 flex flex-col justify-between relative hover:border-purple-500/50 transition-all">
<div>
<div class="flex justify-between items-start">
<div>
<h3 class="text-lg font-bold text-white">Team</h3>
<p class="text-xs text-slate-400 mt-0.5">Collaborative Workspace</p>
</div>
<span class="px-2 py-0.5 bg-purple-500/10 text-purple-400 text-xs font-mono rounded border border-purple-500/20">ORGANIZATION</span>
</div>
<div class="my-4">
<div class="flex items-baseline gap-1">
<span class="text-3xl font-black text-white" id="price-team">₩299,000</span>
<span class="text-xs text-slate-400" id="period-team">/ user / year</span>
</div>
<p class="text-xs text-purple-400 mt-1" id="sub-team">~ ₩24,900 / month</p>
</div>
<ul class="space-y-2.5 text-xs text-slate-300 mt-4 border-t border-slate-800 pt-4">
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-purple-400"></i> All Pro+ Capabilities</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-purple-400"></i> Shared Technical Dictionary</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-purple-400"></i> Centralized Admin & Billing CRM</li>
<li class="flex items-center gap-2"><i data-lucide="check" class="w-4 h-4 text-purple-400"></i> Automated Tax Invoices (세금계산서)</li>
</ul>
</div>
<button onclick="startCheckout('team', 'Team Plan')" class="mt-6 w-full py-2.5 bg-purple-600 hover:bg-purple-500 text-white font-semibold text-xs rounded-xl shadow-lg shadow-purple-500/20 transition-all">
Subscribe Team
</button>
</div>
</div>
</section>
<!-- TAB 3: CUSTOMER SUPPORT (CA/CS) & AI DIAGNOSTICS -->
<section id="tab-support" class="hidden space-y-6">
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
<div>
<h1 class="text-2xl lg:text-3xl font-bold tracking-tight text-white flex items-center gap-3">
<span>Customer Assistance (CA) & AI Diagnostic Desk</span>
<span class="text-xs px-2.5 py-1 rounded-md bg-purple-500/10 text-purple-400 border border-purple-500/20 font-mono">Channel.io + AI Auto-Triage</span>
</h1>
<p class="text-slate-400 text-sm mt-1">
24/7 AI-first customer assistance with 1-click system diagnostics log attachment and self-service refund engine.
</p>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- LIVE AI CHATBOT -->
<div class="lg:col-span-2 glass-panel rounded-2xl p-5 border border-slate-800 flex flex-col h-[560px]">
<div class="flex items-center justify-between pb-3 border-b border-slate-800">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-full bg-purple-500/20 border border-purple-500/30 flex items-center justify-center">
<i data-lucide="bot" class="w-4 h-4 text-purple-400"></i>
</div>
<div>
<h4 class="text-sm font-bold text-white">D3RO AI First-Line Assistant</h4>
<p class="text-[11px] text-emerald-400 flex items-center gap-1">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400"></span> Online • Responds in ~300ms
</p>
</div>
</div>
<button onclick="triggerDiagnosticScan()" class="text-xs px-3 py-1.5 bg-dark-900 hover:bg-slate-800 text-cyan-400 border border-cyan-500/30 rounded-lg flex items-center gap-1.5">
<i data-lucide="activity" class="w-3.5 h-3.5"></i> Capture System Diagnostics
</button>
</div>
<!-- CHAT MESSAGE LOG CONTAINER -->
<div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-3 custom-scrollbar text-xs">
<div class="flex items-start gap-2.5 max-w-[85%]">
<div class="w-6 h-6 rounded-full bg-purple-500/30 flex items-center justify-center shrink-0 mt-0.5">
<i data-lucide="bot" class="w-3.5 h-3.5 text-purple-300"></i>
</div>
<div class="bg-dark-900 border border-slate-800 p-3 rounded-2xl rounded-tl-none text-slate-200 leading-relaxed shadow">
안녕하세요! D3RO Voice 고객지원 AI 어시스턴트입니다. 마이크 설정, Whisper 모델 구동, 결제/환불, 단축키 문제 등 무엇이든 도와드릴 수 있습니다.
</div>
</div>
</div>
<!-- QUICK PROMPTS CHIPS -->
<div class="py-2 flex items-center gap-2 overflow-x-auto custom-scrollbar border-t border-slate-800/80">
<button onclick="askBotQuestion('마이크 음성 인식이 안 돼요')" class="text-[11px] px-2.5 py-1 rounded-full bg-dark-900 hover:bg-slate-800 border border-slate-700 text-slate-300 whitespace-nowrap">
🎤 마이크 음성 인식 오류
</button>
<button onclick="askBotQuestion('환불 정책 및 자동 환불 신청 방법')" class="text-[11px] px-2.5 py-1 rounded-full bg-dark-900 hover:bg-slate-800 border border-slate-700 text-slate-300 whitespace-nowrap">
💳 환불 규정 및 자동 확인
</button>
<button onclick="askBotQuestion('CUDA GPU 가속 활성화는 어떻게 하나요?')" class="text-[11px] px-2.5 py-1 rounded-full bg-dark-900 hover:bg-slate-800 border border-slate-700 text-slate-300 whitespace-nowrap">
⚡ CUDA GPU 가속 설정
</button>
<button onclick="askBotQuestion('세금계산서 발행은 어떻게 받나요?')" class="text-[11px] px-2.5 py-1 rounded-full bg-dark-900 hover:bg-slate-800 border border-slate-700 text-slate-300 whitespace-nowrap">
📄 세금계산서 발행
</button>
</div>
<!-- INPUT FORM -->
<form onsubmit="handleUserChat(event)" class="flex items-center gap-2 pt-2">
<input type="text" id="chat-input" placeholder="문의 사항을 입력하세요... (예: 마이크 인식이 끊깁니다)" class="flex-1 bg-dark-950 border border-slate-700 rounded-xl px-4 py-2 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-purple-500">
<button type="submit" class="px-4 py-2 bg-purple-600 hover:bg-purple-500 text-white rounded-xl text-xs font-semibold flex items-center gap-1.5 shadow">
<span>전송</span> <i data-lucide="send" class="w-3.5 h-3.5"></i>
</button>
</form>
</div>
<!-- RIGHT: DIAGNOSTICS PAYLOAD -->
<div class="glass-panel rounded-2xl p-5 border border-slate-800 flex flex-col justify-between space-y-4">
<div>
<div class="flex items-center justify-between">
<h3 class="text-sm font-bold text-white flex items-center gap-2">
<i data-lucide="cpu" class="w-4 h-4 text-cyan-400"></i>
Client Telemetry Snapshot
</h3>
<span class="text-[10px] font-mono text-slate-400">Auto-Attached</span>
</div>
<p class="text-[11px] text-slate-400 mt-1">1-click diagnostic data for rapid engineering triage.</p>
<pre id="diagnostic-json" class="mt-3 p-3 rounded-xl bg-dark-950 border border-slate-800 font-mono text-[10px] text-cyan-300 leading-tight overflow-x-auto h-48 custom-scrollbar">
{
"machineId": "d3ro-win-88f21a",
"appVersion": "1.0.0 (Release)",
"os": "Windows 11 23H2 (x64)",
"micDevice": "Realtek High Def (Default)",
"sttEngine": "faster-whisper (local)",
"gpuCuda": true,
"vramFree": "6.4 GB / 8.0 GB",
"tier": "free",
"recentErrors": [
"No active critical errors"
]
}
</pre>
</div>
<div class="space-y-2 pt-2 border-t border-slate-800">
<button onclick="openTicketModal()" class="w-full py-2.5 bg-dark-900 hover:bg-slate-800 border border-slate-700 text-white text-xs font-semibold rounded-xl flex items-center justify-center gap-2 transition-all">
<i data-lucide="ticket" class="w-4 h-4 text-brand-400"></i>
Create Formal Support Ticket
</button>
<button onclick="checkInstantRefund()" class="w-full py-2 bg-slate-900 hover:bg-slate-800/80 border border-slate-800 text-slate-300 text-xs rounded-xl flex items-center justify-center gap-1.5">
<i data-lucide="refresh-cw" class="w-3.5 h-3.5 text-emerald-400"></i>
Self-Service 7-Day Refund Check
</button>
</div>
</div>
</div>
</section>
<!-- TAB 4: UNIFIED ADMIN COMMAND & REVENUE CENTER -->
<section id="tab-admin" class="hidden space-y-6">
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
<div>
<h1 class="text-2xl lg:text-3xl font-bold tracking-tight text-white flex items-center gap-3">
<span>Executive Admin & Monetization CRM</span>
<span class="text-xs px-2.5 py-1 rounded-md bg-red-500/10 text-red-400 border border-red-500/20 font-mono">SUPER_ADMIN</span>
</h1>
<p class="text-slate-400 text-sm mt-1">
Real-time telemetry across Ad Mediation, Multi-PG Recurring Revenue, and Customer Support SLA.
</p>
</div>
</div>
<!-- KPI METRIC CARDS -->
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4">
<div class="glass-card p-4 rounded-2xl border border-slate-800">
<div class="flex justify-between items-center text-xs text-slate-400">
<span>Monthly Rec. Revenue (MRR)</span>
<i data-lucide="dollar-sign" class="w-4 h-4 text-emerald-400"></i>
</div>
<div class="text-2xl font-black text-white mt-1">₩18,420,000</div>
<div class="text-[11px] text-emerald-400 mt-1 flex items-center gap-1 font-mono">
<i data-lucide="arrow-up-right" class="w-3.5 h-3.5"></i> +24.8% vs last month
</div>
</div>
<div class="glass-card p-4 rounded-2xl border border-slate-800">
<div class="flex justify-between items-center text-xs text-slate-400">
<span>Free Tier Ad Revenue</span>
<i data-lucide="tv" class="w-4 h-4 text-cyan-400"></i>
</div>
<div class="text-2xl font-black text-cyan-400 mt-1">$4,620.50</div>
<div class="text-[11px] text-cyan-300 mt-1 flex items-center gap-1 font-mono">
<span>eCPM: $5.84 avg</span>
</div>
</div>
<div class="glass-card p-4 rounded-2xl border border-slate-800">
<div class="flex justify-between items-center text-xs text-slate-400">
<span>Active Subscribers</span>
<i data-lucide="users" class="w-4 h-4 text-purple-400"></i>
</div>
<div class="text-2xl font-black text-white mt-1">1,942</div>
<div class="text-[11px] text-purple-300 mt-1 flex items-center gap-1 font-mono">
<span>Churn: 1.4% (Top Tier)</span>
</div>
</div>
<div class="glass-card p-4 rounded-2xl border border-slate-800">
<div class="flex justify-between items-center text-xs text-slate-400">
<span>Open Support Tickets</span>
<i data-lucide="message-square" class="w-4 h-4 text-amber-400"></i>
</div>
<div class="text-2xl font-black text-amber-400 mt-1">4 <span class="text-xs text-slate-400 font-normal">/ 180 total</span></div>
<div class="text-[11px] text-emerald-400 mt-1 flex items-center gap-1 font-mono">
<span>AI Auto-Resolved: 78.5%</span>
</div>
</div>
</div>
<!-- 10+ AD NETWORKS MEDIATION AUCTION MATRIX -->
<div class="glass-panel rounded-2xl p-5 border border-slate-800 space-y-4">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-2 border-b border-slate-800 pb-3">
<div>
<h3 class="text-sm font-bold text-white flex items-center gap-2">
<i data-lucide="network" class="w-4 h-4 text-cyan-400"></i>
10+ Active Multi-Ad Demand Partners & Real-Time Auction Waterfall
</h3>
<p class="text-xs text-slate-400 mt-0.5">Sub-800ms parallel header bidding auction with first-price dynamic yield.</p>
</div>
<span class="text-xs font-mono px-2.5 py-1 bg-cyan-500/10 text-cyan-400 border border-cyan-500/20 rounded-md">
Auction Timeout: 800ms
</span>
</div>
<div class="overflow-x-auto custom-scrollbar">
<table class="w-full text-left text-xs">
<thead class="text-slate-400 border-b border-slate-800 font-mono">
<tr>
<th class="py-2 px-3">Network Partner</th>
<th class="py-2 px-3">Adapter Protocol</th>
<th class="py-2 px-3">Supported Ad Slot</th>
<th class="py-2 px-3 text-right">Impressions</th>
<th class="py-2 px-3 text-right">CTR</th>
<th class="py-2 px-3 text-right">Bid eCPM</th>
<th class="py-2 px-3 text-right">Gross Rev. ($)</th>
<th class="py-2 px-3 text-center">Fill Rate</th>
<th class="py-2 px-3 text-center">Status</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-800/60 text-slate-300 font-sans">
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">Direct House Sponsor Engine</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">Direct Contract</td>
<td class="py-2.5 px-3 text-slate-400">Bottom Dock / Video / Export</td>
<td class="py-2.5 px-3 text-right font-mono text-white">84,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">5.0%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$15.20</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$1,276.80</td>
<td class="py-2.5 px-3 text-center font-mono">100.0%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-emerald-500/20 text-emerald-300 font-mono">ACTIVE</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">Playwire RAMP Engine</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">Header Bidding SSP</td>
<td class="py-2.5 px-3 text-slate-400">Desktop Video & Display</td>
<td class="py-2.5 px-3 text-right font-mono text-white">62,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">7.0%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$8.40</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$520.80</td>
<td class="py-2.5 px-3 text-center font-mono">96.2%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-cyan-500/20 text-cyan-300 font-mono">BIDDING</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">AppLovin MAX</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">In-App Bidding</td>
<td class="py-2.5 px-3 text-slate-400">Rewarded Video (15s)</td>
<td class="py-2.5 px-3 text-right font-mono text-white">48,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">8.0%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$7.80</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$374.40</td>
<td class="py-2.5 px-3 text-center font-mono">94.5%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-cyan-500/20 text-cyan-300 font-mono">BIDDING</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">Unity LevelPlay</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">Rewarded Video SDK</td>
<td class="py-2.5 px-3 text-slate-400">Rewarded Quota Refill</td>
<td class="py-2.5 px-3 text-right font-mono text-white">45,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">9.0%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$9.10</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$409.50</td>
<td class="py-2.5 px-3 text-center font-mono">95.1%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-emerald-500/20 text-emerald-300 font-mono">ACTIVE</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">EthicalAds Privacy Dev Network</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">REST Decision API</td>
<td class="py-2.5 px-3 text-slate-400">Bottom Dock Banner</td>
<td class="py-2.5 px-3 text-right font-mono text-white">38,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">1.6%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$3.80</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$144.40</td>
<td class="py-2.5 px-3 text-center font-mono">99.4%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-emerald-500/20 text-emerald-300 font-mono">ACTIVE</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">Carbon Ads (BuySellAds)</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">Native JSON Endpoint</td>
<td class="py-2.5 px-3 text-slate-400">Tech Developer Unit</td>
<td class="py-2.5 px-3 text-right font-mono text-white">31,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">1.9%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$4.20</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$130.20</td>
<td class="py-2.5 px-3 text-center font-mono">98.8%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-emerald-500/20 text-emerald-300 font-mono">ACTIVE</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">Google Ad Manager 360</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">Universal Ad Server</td>
<td class="py-2.5 px-3 text-slate-400">Global Display & Video</td>
<td class="py-2.5 px-3 text-right font-mono text-white">29,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">2.0%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$3.50</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$101.50</td>
<td class="py-2.5 px-3 text-center font-mono">99.8%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-cyan-500/20 text-cyan-300 font-mono">BIDDING</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">Mintegral APAC Network</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">Video & Playable SDK</td>
<td class="py-2.5 px-3 text-slate-400">Rewarded Video (15s)</td>
<td class="py-2.5 px-3 text-right font-mono text-white">22,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">8.0%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$6.20</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$136.40</td>
<td class="py-2.5 px-3 text-center font-mono">92.4%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-cyan-500/20 text-cyan-300 font-mono">BIDDING</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">InMobi Exchange</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">RTB Exchange</td>
<td class="py-2.5 px-3 text-slate-400">Banner & Video</td>
<td class="py-2.5 px-3 text-right font-mono text-white">19,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">2.0%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$3.40</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$64.60</td>
<td class="py-2.5 px-3 text-center font-mono">91.2%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-emerald-500/20 text-emerald-300 font-mono">ACTIVE</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-semibold text-white">PubMatic OpenWrap SSP</td>
<td class="py-2.5 px-3 font-mono text-cyan-400">Prebid SSP</td>
<td class="py-2.5 px-3 text-slate-400">Bottom Dock Banner</td>
<td class="py-2.5 px-3 text-right font-mono text-white">15,000</td>
<td class="py-2.5 px-3 text-right font-mono text-emerald-400">1.5%</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-cyan-400">$3.60</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-white">$54.00</td>
<td class="py-2.5 px-3 text-center font-mono">90.5%</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-cyan-500/20 text-cyan-300 font-mono">BIDDING</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- MONTHLY SETTLEMENT & TAX WITHHOLDING PAYOUT LEDGER -->
<div class="glass-panel rounded-2xl p-5 border border-slate-800 space-y-4">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-2 border-b border-slate-800 pb-3">
<div>
<h3 class="text-sm font-bold text-white flex items-center gap-2">
<i data-lucide="receipt" class="w-4 h-4 text-emerald-400"></i>
Monthly Revenue Settlement & Tax Withholding Ledger
</h3>
<p class="text-xs text-slate-400 mt-0.5">Publisher Account: yunchanpaca@gmail.com • KB국민은행 928702-00-184920</p>
</div>
<span class="text-xs font-mono px-2.5 py-1 bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 rounded-md">
Auto 3.3% Tax Withholding Deducted
</span>
</div>
<div class="overflow-x-auto custom-scrollbar">
<table class="w-full text-left text-xs">
<thead class="text-slate-400 border-b border-slate-800 font-mono">
<tr>
<th class="py-2 px-3">Settlement ID</th>
<th class="py-2 px-3">Month</th>
<th class="py-2 px-3">Demand Network</th>
<th class="py-2 px-3 text-right">Gross ($ USD)</th>
<th class="py-2 px-3 text-center">Tax Withholding</th>
<th class="py-2 px-3 text-right">Net Payout (₩ KRW)</th>
<th class="py-2 px-3 text-left">Beneficiary Method</th>
<th class="py-2 px-3 text-center">Payout Status</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-800/60 text-slate-300 font-sans">
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-mono text-cyan-400">STL-202607-DIR</td>
<td class="py-2.5 px-3 font-mono text-slate-400">2026-07</td>
<td class="py-2.5 px-3 font-semibold text-white">Direct House Sponsor (Cursor/Notion)</td>
<td class="py-2.5 px-3 text-right font-mono text-white">$1,276.80</td>
<td class="py-2.5 px-3 text-center font-mono text-amber-400">3.3% (₩56,870)</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-emerald-400">₩1,666,810</td>
<td class="py-2.5 px-3 font-mono text-slate-400 text-[11px]">KB국민 928702-00-184920</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-emerald-500/20 text-emerald-300 font-mono">PAID</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-mono text-cyan-400">STL-202607-PLY</td>
<td class="py-2.5 px-3 font-mono text-slate-400">2026-07</td>
<td class="py-2.5 px-3 font-semibold text-white">Playwire RAMP Desktop Header Bidding</td>
<td class="py-2.5 px-3 text-right font-mono text-white">$520.80</td>
<td class="py-2.5 px-3 text-center font-mono text-amber-400">3.3% (₩23,200)</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-emerald-400">₩679,880</td>
<td class="py-2.5 px-3 font-mono text-slate-400 text-[11px]">Wire Transfer (USD)</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-emerald-500/20 text-emerald-300 font-mono">PAID</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-mono text-cyan-400">STL-202607-APL</td>
<td class="py-2.5 px-3 font-mono text-slate-400">2026-07</td>
<td class="py-2.5 px-3 font-semibold text-white">AppLovin MAX In-App Bidding</td>
<td class="py-2.5 px-3 text-right font-mono text-white">$374.40</td>
<td class="py-2.5 px-3 text-center font-mono text-amber-400">3.3% (₩16,680)</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-emerald-400">₩488,760</td>
<td class="py-2.5 px-3 font-mono text-slate-400 text-[11px]">Wire Transfer (USD)</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-amber-500/20 text-amber-300 font-mono">SETTLED</span></td>
</tr>
<tr class="hover:bg-slate-800/30">
<td class="py-2.5 px-3 font-mono text-cyan-400">STL-202607-UNT</td>
<td class="py-2.5 px-3 font-mono text-slate-400">2026-07</td>
<td class="py-2.5 px-3 font-semibold text-white">Unity LevelPlay Rewarded Video</td>
<td class="py-2.5 px-3 text-right font-mono text-white">$409.50</td>
<td class="py-2.5 px-3 text-center font-mono text-amber-400">3.3% (₩18,240)</td>
<td class="py-2.5 px-3 text-right font-mono font-bold text-emerald-400">₩534,580</td>
<td class="py-2.5 px-3 font-mono text-slate-400 text-[11px]">PayPal (yunchanpaca@gmail.com)</td>
<td class="py-2.5 px-3 text-center"><span class="px-2 py-0.5 rounded text-[10px] bg-amber-500/20 text-amber-300 font-mono">SETTLED</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</main>
<!-- MODALS -->
<div id="rewarded-ad-modal" class="fixed inset-0 bg-black/80 backdrop-blur-md hidden z-50 flex items-center justify-center p-4">
<div class="glass-panel w-full max-w-lg rounded-2xl border border-slate-700 p-6 space-y-4 glow-cyan">
<div class="flex items-center justify-between border-b border-slate-800 pb-3">
<div class="flex items-center gap-2">
<i data-lucide="play-circle" class="w-5 h-5 text-cyan-400"></i>
<h3 class="text-sm font-bold text-white">Sponsor Video — Refill +50 Free AI Tokens</h3>
</div>
<button onclick="closeRewardedAdModal()" class="text-slate-400 hover:text-white"><i data-lucide="x" class="w-4 h-4"></i></button>
</div>
<div class="w-full h-56 bg-slate-950 rounded-xl border border-slate-800 relative overflow-hidden flex flex-col justify-between p-4">
<div class="flex justify-between items-start">
<span class="px-2 py-0.5 bg-cyan-500/20 text-cyan-300 text-[11px] font-mono rounded">SPONSOR AD</span>
<div class="bg-black/60 px-2.5 py-1 rounded-full text-xs font-mono text-white flex items-center gap-1.5">
<i data-lucide="clock" class="w-3 h-3 text-cyan-400"></i>
<span id="ad-timer">15s</span>
</div>
</div>
<div class="text-center space-y-2">
<div class="w-12 h-12 rounded-xl bg-cyan-500/20 text-cyan-400 mx-auto flex items-center justify-center">
<i data-lucide="sparkles" class="w-6 h-6"></i>
</div>
<h4 class="text-base font-bold text-white">ElevenLabs Voice Synthesizer</h4>
<p class="text-xs text-slate-400">Human-like AI audio generation for developers & creators.</p>
</div>
<div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden">
<div id="ad-progress-bar" class="bg-cyan-400 h-full w-0 transition-all duration-1000 ease-linear"></div>
</div>
</div>
<div class="flex items-center justify-between text-xs text-slate-400">
<span>Reward: <strong>+50 Cloud LLM Polish Tokens</strong></span>
<button id="claim-reward-btn" disabled onclick="claimAdReward()" class="px-4 py-2 bg-slate-800 text-slate-500 font-bold rounded-xl text-xs transition-all cursor-not-allowed">
Watch to Complete (15s)
</button>
</div>
</div>
</div>
<script>
lucide.createIcons();
let currentTier = 'free';
let currentCurrency = 'KRW';
let isAnnual = true;
let quotaRemaining = 45;
let totalImpressions = 1482;
let totalAdRevenue = 7.84;
let adInterval = null;
function switchTab(tabId) {
document.querySelectorAll('section[id^="tab-"]').forEach(el => el.classList.add('hidden'));
document.getElementById('tab-' + tabId).classList.remove('hidden');
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('bg-brand-600', 'text-white', 'shadow-md');
btn.classList.add('text-slate-400');
});
const activeBtn = document.getElementById('tab-btn-' + tabId);
if (activeBtn) {
activeBtn.classList.add('bg-brand-600', 'text-white', 'shadow-md');
activeBtn.classList.remove('text-slate-400');
}
lucide.createIcons();
}
function toggleUserTier(tier) {
currentTier = tier;
const dockAd = document.getElementById('dock-ad-container');
const proAdFree = document.getElementById('pro-adfree-container');
const quotaDisplay = document.getElementById('quota-display');
if (tier === 'free') {
dockAd.classList.remove('hidden');
proAdFree.classList.add('hidden');
quotaDisplay.innerText = `Cloud Quota: ${quotaRemaining} / 250`;
quotaDisplay.className = "text-xs text-cyan-400 font-mono";
} else {
dockAd.classList.add('hidden');
proAdFree.classList.remove('hidden');
quotaDisplay.innerText = `Cloud Quota: Unlimited (${tier.toUpperCase()})`;
quotaDisplay.className = "text-xs text-emerald-400 font-mono font-bold";
}
}
function triggerVoiceSim() {
const transcriptEl = document.getElementById('transcript-stream');
const phrases = [
"\"실시간 음성 인식이 정상 작동 중입니다. 백그라운드 모델 지연 시간은 135ms이며, HUD는 완벽히 깨끗합니다.\"",
"\"Whisper large-v3-turbo 모델이 로컬 GPU에서 작동하고 있으며 음성 캡슐은 화면 방해 없이 즉각 삽입됩니다.\""
];
transcriptEl.innerText = phrases[Math.floor(Math.random() * phrases.length)];
}
function openRewardedAdModal() {
document.getElementById('rewarded-ad-modal').classList.remove('hidden');
let timeLeft = 15;
const timerEl = document.getElementById('ad-timer');
const progressEl = document.getElementById('ad-progress-bar');
const claimBtn = document.getElementById('claim-reward-btn');
timerEl.innerText = '15s';
progressEl.style.width = '0%';
claimBtn.disabled = true;
claimBtn.className = 'px-4 py-2 bg-slate-800 text-slate-500 font-bold rounded-xl text-xs transition-all cursor-not-allowed';
claimBtn.innerText = 'Watch to Complete (15s)';
let elapsed = 0;
clearInterval(adInterval);
adInterval = setInterval(() => {
elapsed++;
timeLeft--;
timerEl.innerText = `${timeLeft}s`;
progressEl.style.width = `${(elapsed / 15) * 100}%`;
if (timeLeft <= 0) {
clearInterval(adInterval);
claimBtn.disabled = false;
claimBtn.className = 'px-4 py-2 bg-gradient-to-r from-cyan-500 to-brand-600 text-white font-bold rounded-xl text-xs shadow-lg shadow-cyan-500/30 cursor-pointer animate-pulse';
claimBtn.innerText = 'Claim +50 Free AI Tokens!';
}
}, 1000);
}
function closeRewardedAdModal() {
clearInterval(adInterval);
document.getElementById('rewarded-ad-modal').classList.add('hidden');
}
function claimAdReward() {
quotaRemaining += 50;
totalImpressions += 1;
totalAdRevenue += 0.25;
document.getElementById('quota-display').innerText = `Cloud Quota: ${quotaRemaining} / 250`;
document.getElementById('stat-impressions').innerText = totalImpressions.toLocaleString();
document.getElementById('stat-revenue').innerText = '$' + totalAdRevenue.toFixed(2);
closeRewardedAdModal();
alert('🎉 50 Cloud LLM Polish tokens have been added to your account!');
}
</script>
</body>
</html>

View file

@ -14,11 +14,17 @@ export function SectionHeader({ index, title, subtitle, className = '' }: Sectio
</span>
<div className="h-px flex-1 bg-white/[0.06]" />
</div>
<h2 className="font-display text-display font-bold text-neutral-50 tracking-tight">
<h2
className="font-display text-display font-bold text-neutral-50 tracking-tight break-keep"
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
>
{title}
</h2>
{subtitle && (
<p className="mt-3 max-w-xl text-base text-neutral-400 leading-relaxed">
<p
className="mt-3 max-w-xl text-base text-neutral-400 leading-relaxed break-keep"
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
>
{subtitle}
</p>
)}

View file

@ -10,17 +10,17 @@ export const en: Translations = {
download: 'Download',
},
hero: {
badge: '100% LOCAL \u00b7 ZERO CLOUD',
title1: 'LOCAL AI',
title2: 'VOICE',
title3: 'ASSISTANT.',
subtitle: 'Whisper + Ollama powered voice-to-text pipeline running entirely on your machine. Dictation, AI polish, real-time captions, voice conversations \u2014 no internet required.',
downloadBtn: 'Download for Windows',
viewFeatures: 'View Features',
badge: '100% ON-DEVICE · ZERO CLOUD',
title1: 'Speak at the Speed of Thought.',
title2: 'AI Polishes the Rest.',
title3: 'D3RO Voice.',
subtitle: '3x faster than typing. Whisper + Ollama voice-to-text pipeline running 100% on your machine. Zero cloud egress, unlimited dictation, and real-time meeting notes.',
downloadBtn: 'Download for Windows — Free',
viewFeatures: 'Explore Features',
dataProcessing: 'Processing',
dataCloudTraffic: 'Cloud Traffic',
dataCloudTraffic: 'Cloud Egress',
dataLatency: 'STT Latency',
dataPrivacy: 'Privacy Score',
dataPrivacy: 'Privacy Rating',
systemStatus: 'SYSTEM STATUS: OPERATIONAL',
protocol: 'PROTOCOL: 2025.04',
},

View file

@ -10,17 +10,17 @@ export const ko: Translations = {
download: '다운로드',
},
hero: {
badge: '100% 로컬 \u00b7 클라우드 제로',
title1: '로컬 AI',
title2: '음성',
title3: '어시스턴트.',
subtitle: 'Whisper + Ollama 기반 음성-텍스트 파이프라인이 당신의 컴퓨터에서 완전히 작동합니다. 받아쓰기, AI 다듬기, 실시간 자막, 음성 대화 \u2014 인터넷이 필요 없습니다.',
downloadBtn: 'Windows용 다운로드',
viewFeatures: '기능 보기',
badge: '100% 온디바이스 · 클라우드 제로',
title1: '생각의 속도로 말하고,',
title2: 'AI가 완벽히 다듬습니다.',
title3: 'D3RO Voice.',
subtitle: '타이핑보다 3배 빠른 100% 로컬 음성 비서. 클라우드 전송 0바이트, 무제한 받아쓰기부터 실시간 화자 분리 회의록까지 당신의 PC에서 안전하게.',
downloadBtn: 'Windows용 무료 다운로드',
viewFeatures: '주요 기능 둘러보기',
dataProcessing: '처리 방식',
dataCloudTraffic: '클라우드 트래픽',
dataCloudTraffic: '클라우드 유출',
dataLatency: 'STT 지연시간',
dataPrivacy: '프라이버시 수',
dataPrivacy: '프라이버시 수',
systemStatus: '시스템 상태: 정상 가동',
protocol: '프로토콜: 2025.04',
},

View file

@ -8,31 +8,58 @@ export function CTA() {
const { t } = useI18n()
return (
<section className="relative py-24 md:py-32">
{/* Background glow */}
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[500px] h-[400px] bg-brand-amber/[0.03] rounded-full blur-[120px] pointer-events-none" />
<section className="relative py-28 md:py-36 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" />
<Container className="relative text-center">
<Crosshair className="inline-block mb-8">
<WaveBars className="h-8 px-2" />
<WaveBars className="h-9 px-3" />
</Crosshair>
<h2 className="font-display text-display font-bold text-neutral-50 mb-6 max-w-3xl mx-auto">
<h2
className="font-display text-display font-extrabold text-neutral-50 mb-6 max-w-3xl mx-auto tracking-tight break-keep"
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
>
{t.cta.title1} <span className="text-brand-amber">{t.cta.title2}</span>
</h2>
<p className="text-base md:text-lg text-neutral-400 max-w-xl mx-auto mb-10 leading-relaxed">
<p
className="text-base md:text-lg text-neutral-400 max-w-xl mx-auto mb-8 leading-relaxed break-keep"
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
>
{t.cta.subtitle1}
<br />
{t.cta.subtitle2}
</p>
<GlowButton href="#" variant="primary" size="lg">
{t.cta.downloadBtn}
</GlowButton>
{/* 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:
</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>
</div>
<p className="font-mono text-nano text-neutral-600 mt-8 uppercase tracking-widest">
{t.cta.systemReq}
<div>
<GlowButton
href="https://github.com/user/D3ROVoice/releases"
variant="primary"
size="lg"
className="shadow-glow-md hover:shadow-glow-lg"
>
{t.cta.downloadBtn}
</GlowButton>
</div>
<p className="font-mono text-nano text-neutral-500 mt-8 uppercase tracking-widest">
{t.cta.systemReq} · 100% OPEN ARCHITECTURE
</p>
</Container>
</section>

View file

@ -1,26 +1,19 @@
import type { ReactNode } from 'react'
import { SectionHeader } from '../components/SectionHeader'
import { InstrumentCard } from '../components/InstrumentCard'
import { Container } from '../components/Container'
import { useI18n } from '../i18n'
interface FeatureMeta {
icon: ReactNode
interface FeatureTile {
id: string
span: 'col-span-1' | 'col-span-1 md:col-span-2'
tag: 'FREE' | 'PRO' | 'PRO+'
title: string
description: string
highlight?: string
icon: ReactNode
previewNode?: ReactNode
}
const featureMeta: FeatureMeta[] = [
{ icon: <MicIcon />, tag: 'FREE' },
{ icon: <SparklesIcon />, tag: 'FREE' },
{ icon: <LanguageIcon />, tag: 'FREE' },
{ icon: <CaptionIcon />, tag: 'PRO' },
{ icon: <ConversationIcon />, tag: 'PRO+' },
{ icon: <FileIcon />, tag: 'PRO+' },
{ icon: <ChainIcon />, tag: 'PRO' },
{ icon: <ContextIcon />, tag: 'PRO' },
{ icon: <MemoIcon />, tag: 'PRO' },
]
const tagStyles = {
FREE: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20',
PRO: 'text-brand-amber bg-brand-amber/10 border-brand-amber/20',
@ -28,7 +21,112 @@ const tagStyles = {
} as const
export function Features() {
const { t } = useI18n()
const { t, locale } = useI18n()
const bentoTiles: FeatureTile[] = [
{
id: 'hud',
span: 'col-span-1 md:col-span-2',
tag: 'FREE',
title: locale === 'ko' ? '전역 Push-to-Talk 음성 캡슐 HUD' : 'Global Push-to-Talk Capsule HUD',
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',
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>
<span className="font-mono text-nano text-neutral-400 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>
</div>
),
},
{
id: 'privacy',
span: 'col-span-1',
tag: 'FREE',
title: locale === 'ko' ? '100% 로컬 보안 실드' : '100% On-Device Local Privacy',
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',
icon: <ShieldIcon />,
},
{
id: 'meeting',
span: 'col-span-1',
tag: 'PRO',
title: locale === 'ko' ? '회의 스튜디오 & 화자 분리' : 'Meeting Studio & Diarization',
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',
icon: <UsersIcon />,
},
{
id: 'chains',
span: 'col-span-1 md:col-span-2',
tag: 'PRO',
title: locale === 'ko' ? '다단계 AI 파이프라인 체이닝 & 매크로' : 'Multi-Step LLM Pipeline Chaining',
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',
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>
<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>
<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>
<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>
</div>
),
},
{
id: 'rag',
span: 'col-span-1',
tag: 'PRO+',
title: locale === 'ko' ? '로컬 문서 지식베이스 (RAG)' : 'Local Document Intelligence (RAG)',
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',
icon: <DatabaseIcon />,
},
{
id: 'performance',
span: 'col-span-1',
tag: 'FREE',
title: locale === 'ko' ? '초저지연 고성능 아키텍처' : 'Sub-Second Local Latency',
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',
icon: <ZapIcon />,
},
]
return (
<section id="features" className="relative py-24 md:py-32">
@ -39,52 +137,61 @@ export function Features() {
subtitle={t.features.subtitle}
/>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{t.features.items.map((item, i) => {
const meta = featureMeta[i]
return (
<InstrumentCard key={i}>
{/* Asymmetric Bento Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
{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}`}
>
<div>
{/* Top row: icon + tag */}
<div className="flex items-start justify-between mb-6">
<div className="w-10 h-10 rounded-card bg-surface-400 border border-white/[0.06] flex items-center justify-center text-brand-amber">
{meta.icon}
<div 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">
{tile.icon}
</div>
<span className={`text-nano font-mono font-semibold tracking-widest uppercase px-2 py-0.5 border rounded-sm ${tagStyles[meta.tag]}`}>
{meta.tag}
<span className={`text-nano font-mono font-semibold tracking-widest uppercase px-2.5 py-0.5 border rounded-md ${tagStyles[tile.tag]}`}>
{tile.tag}
</span>
</div>
{/* Content */}
<h3 className="font-display text-lg font-semibold text-neutral-100 mb-2">
{item.title}
<h3 className="font-display text-xl font-bold text-neutral-100 mb-2.5">
{tile.title}
</h3>
<p className="text-sm text-neutral-400 leading-relaxed">
{item.description}
{tile.description}
</p>
{/* Bottom arrow */}
<div className="mt-5 flex justify-end">
<div className="w-7 h-7 rounded-full border border-white/[0.06] flex items-center justify-center text-neutral-500 group-hover:text-brand-amber group-hover:border-brand-amber/20 transition-colors">
{/* Optional interactive preview */}
{tile.previewNode}
</div>
{/* 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">
{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">
<svg className="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<line x1="7" y1="17" x2="17" y2="7" />
<polyline points="7 7 17 7 17 17" />
</svg>
</div>
</div>
</InstrumentCard>
)
})}
)}
</div>
))}
</div>
</Container>
</section>
)
}
/* ── Inline SVG Icons ─────────────────────────────────── */
function MicIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
<rect x="9" y="2" width="6" height="11" rx="3" />
<path d="M5 10a7 7 0 0 0 14 0" />
<line x1="12" y1="17" x2="12" y2="21" />
@ -92,75 +199,49 @@ function MicIcon() {
)
}
function SparklesIcon() {
function ShieldIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 2l2.4 7.2L22 12l-7.6 2.8L12 22l-2.4-7.2L2 12l7.6-2.8z" />
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
<path d="M9 12l2 2 4-4" />
</svg>
)
}
function LanguageIcon() {
function UsersIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M5 8l6 6" /><path d="M4 14l6-6 2-3" /><path d="M2 5h12" /><path d="M7 2h1" />
<path d="M22 22l-5-10-5 10" /><path d="M14 18h6" />
</svg>
)
}
function CaptionIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="2" y="4" width="20" height="16" rx="2" />
<path d="M7 15h4" /><path d="M13 15h4" /><path d="M7 11h10" />
</svg>
)
}
function ConversationIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
</svg>
)
}
function FileIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
</svg>
)
}
function ChainIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
</svg>
)
}
function ContextIcon() {
function DatabaseIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="2" y="3" width="20" height="14" rx="2" />
<line x1="8" y1="21" x2="16" y2="21" />
<line x1="12" y1="17" x2="12" y2="21" />
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
<ellipse cx="12" cy="5" rx="9" ry="3" />
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
</svg>
)
}
function MemoIcon() {
function ZapIcon() {
return (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" />
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
</svg>
)
}

View file

@ -10,23 +10,25 @@ export function Header() {
const [mobileOpen, setMobileOpen] = useState(false)
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 40)
const onScroll = () => setScrolled(window.scrollY > 20)
window.addEventListener('scroll', onScroll, { passive: true })
return () => window.removeEventListener('scroll', onScroll)
}, [])
return (
<header
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 mix-blend-difference ${
scrolled ? 'backdrop-blur-xl' : ''
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'
}`}
>
<div className="mx-auto max-w-7xl px-5 md:px-8 lg:px-12 h-16 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-semibold tracking-tight text-white">
D3RO<span className="text-neutral-400">&middot;</span>VOICE
<span className="font-mono text-sm font-bold tracking-tight text-white">
D3RO<span className="text-brand-amber">&middot;</span>VOICE
</span>
</a>
@ -41,6 +43,13 @@ export function Header() {
{t.nav[link.key]}
</a>
))}
<a
href="/launch-readiness.html"
className="font-mono text-nano uppercase tracking-widest text-cyan-400 hover:text-cyan-300 transition-colors flex items-center gap-1"
>
<span className="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse"></span>
Launch Sandbox
</a>
</nav>
{/* Right side: language + CTA */}
@ -48,7 +57,7 @@ export function Header() {
<LanguageSwitcher />
<a
href="https://github.com/user/D3ROVoice/releases"
className="inline-flex items-center gap-2 px-4 py-2 font-mono text-xs uppercase tracking-wider text-white bg-brand-amber/90 rounded-panel hover:bg-brand-amber transition-colors"
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"
>
{t.nav.download}
</a>
@ -57,7 +66,7 @@ export function Header() {
{/* Mobile menu button */}
<button
onClick={() => setMobileOpen(!mobileOpen)}
className="md:hidden w-9 h-9 flex items-center justify-center"
className="md:hidden w-9 h-9 flex items-center justify-center text-neutral-300"
aria-label="Menu"
>
<div className="space-y-1.5">
@ -69,7 +78,7 @@ export function Header() {
{/* Mobile Nav */}
{mobileOpen && (
<div className="md:hidden bg-surface-900/95 backdrop-blur-xl border-t border-white/[0.06] px-5 py-6 space-y-1">
<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">
{navItems.map((link) => (
<a
key={link.href}
@ -84,7 +93,7 @@ export function Header() {
<LanguageSwitcher />
<a
href="https://github.com/user/D3ROVoice/releases"
className="flex-1 text-center px-5 py-2.5 font-mono text-xs uppercase tracking-wider text-white bg-brand-amber rounded-panel"
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"
>
{t.nav.download}
</a>

View file

@ -1,77 +1,271 @@
import { useState, useEffect, useRef } from 'react'
import { Badge } from '../components/Badge'
import { Crosshair } from '../components/Crosshair'
import { DataPoint } from '../components/DataPoint'
import { Container } from '../components/Container'
import { Led } from '../components/Led'
import { useI18n } from '../i18n'
const CJK_LOCALES = new Set(['ko', 'ja', 'zh'])
export function Hero() {
const { t, locale } = useI18n()
const isCJK = CJK_LOCALES.has(locale)
// Interactive Live Simulator State
const [simState, setSimState] = useState<'idle' | 'recording' | 'processing' | 'done'>('idle')
const [typedRaw, setTypedRaw] = useState('')
const [typedClean, setTypedClean] = useState('')
const [waveLevels, setWaveLevels] = useState([0.3, 0.5, 0.8, 1, 0.9, 0.7, 0.4, 0.6, 0.3])
const timerRef = useRef<NodeJS.Timeout | null>(null)
const sampleRawText = locale === 'ko'
? '어... 이번 프로젝트 배포는 다음 주 금요일까지로 잡으면 될 것 같아요.'
: 'Um... I think we can schedule the release for next Friday.'
const sampleCleanText = locale === 'ko'
? '이번 프로젝트 배포 일정은 다음 주 금요일로 확정하겠습니다.'
: 'The project release is scheduled for next Friday.'
// Waveform animation during recording
useEffect(() => {
if (simState !== 'recording') return
const interval = setInterval(() => {
setWaveLevels(Array.from({ length: 9 }, () => 0.2 + Math.random() * 0.8))
}, 90)
return () => clearInterval(interval)
}, [simState])
const startSimulation = () => {
if (simState !== 'idle' && simState !== 'done') return
setSimState('recording')
setTypedRaw('')
setTypedClean('')
let charIdx = 0
const rawInterval = setInterval(() => {
if (charIdx < sampleRawText.length) {
setTypedRaw(sampleRawText.slice(0, charIdx + 1))
charIdx++
} else {
clearInterval(rawInterval)
setSimState('processing')
timerRef.current = setTimeout(() => {
setSimState('done')
let cleanIdx = 0
const cleanInterval = setInterval(() => {
if (cleanIdx < sampleCleanText.length) {
setTypedClean(sampleCleanText.slice(0, cleanIdx + 1))
cleanIdx++
} else {
clearInterval(cleanInterval)
}
}, 25)
}, 600)
}
}, 45)
}
const resetSimulation = () => {
if (timerRef.current) clearTimeout(timerRef.current)
setSimState('idle')
setTypedRaw('')
setTypedClean('')
}
return (
<section className="relative min-h-screen flex flex-col justify-center overflow-hidden bg-grid pt-16 pb-12">
{/* Background effects */}
<section className="relative min-h-screen 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/3 left-1/2 -translate-x-1/2 w-[700px] h-[700px] rounded-full bg-brand-amber/[0.025] blur-[140px]" />
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-brand-amber/15 to-transparent" />
<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>
<Container className="relative flex-1 flex flex-col justify-center">
{/* Meta corners */}
<div className="hidden lg:block absolute top-8 left-5 md:left-8 lg:left-12">
<Badge led ledColor="green">{t.hero.systemStatus}</Badge>
</div>
<div className="hidden lg:block absolute top-8 right-5 md:right-8 lg:right-12 font-mono text-nano uppercase tracking-widest text-neutral-500">
{t.hero.protocol}
</div>
{/* Main hero content */}
<div className="max-w-5xl pt-12 md:pt-0">
<Crosshair className="inline-block mb-8">
<Badge led>{t.hero.badge}</Badge>
</Crosshair>
<h1
className="font-display text-hero font-bold text-neutral-50 mb-6 max-w-4xl"
style={isCJK ? { lineHeight: 1.15 } : undefined}
>
<span className="block">{t.hero.title1}</span>
<span className="block">{t.hero.title2}</span>
<span className="block text-brand-amber">{t.hero.title3}</span>
</h1>
<p className="max-w-xl text-base md:text-lg text-neutral-400 leading-relaxed mb-10">
{t.hero.subtitle}
</p>
<div className="flex flex-col sm:flex-row items-start gap-4 mb-16">
<a
href="#"
className="glow-btn inline-flex items-center gap-2 px-8 py-4 font-mono text-sm font-medium uppercase tracking-wider text-white bg-brand-amber rounded-panel"
>
<span className="relative z-10 flex items-center gap-2">
<DownloadIcon />
{t.hero.downloadBtn}
{/* Hero main grid */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-8 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">
<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>
</a>
<a
href="#features"
className="inline-flex items-center gap-2 px-8 py-4 font-mono text-sm uppercase tracking-wider text-neutral-400 border border-white/[0.08] rounded-panel hover:border-white/[0.15] hover:text-neutral-200 transition-all"
<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">
{t.hero.protocol}
</span>
</div>
{/* Main Headline (2 Clean Lines with keep-all) */}
<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"
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
>
{t.hero.viewFeatures}
<ArrowIcon />
</a>
<span className="block text-neutral-100">{t.hero.title1}</span>
<span className="block text-brand-amber font-extrabold">{t.hero.title2}</span>
</h1>
<p
className="text-base md:text-lg text-neutral-400 leading-relaxed mb-8 max-w-xl break-keep"
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:
</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>
</div>
{/* CTA Action Buttons */}
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-4 mb-3.5">
<a
href="https://github.com/user/D3ROVoice/releases"
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"
>
<span className="relative z-10 flex items-center gap-2">
<DownloadIcon />
{t.hero.downloadBtn}
</span>
</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"
>
{t.hero.viewFeatures}
<ArrowIcon />
</a>
</div>
{/* Friction-Reduction Trust Microcopy */}
<div className="flex items-center gap-2 font-mono text-nano text-neutral-500">
<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>
</div>
{/* 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">
{/* Header */}
<div className="flex items-center justify-between mb-5 pb-3 border-b border-white/[0.06]">
<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'}
</span>
</div>
<span className="font-mono text-nano text-neutral-500 uppercase">
ZERO CLOUD EGRESS
</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">
{/* Waveform Bars */}
<div className="flex items-center gap-1.5 h-8">
{waveLevels.map((lvl, idx) => (
<div
key={idx}
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',
}}
/>
))}
</div>
{/* Trigger Button */}
<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 ${
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'
}`}
>
<MicIcon />
{simState === 'idle' ? 'Try Demo' : simState === 'recording' ? 'Listening...' : simState === 'processing' ? 'Polishing' : 'Test Again'}
</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">
{simState === 'idle' && (
<div className="text-center py-3">
<p className="text-xs font-mono text-neutral-500">
👆 Click &apos;Try Demo&apos; to watch on-device speech-to-text & AI polish in action
</p>
</div>
)}
{(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" />
</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>
<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">
&quot;{typedClean}&quot;
</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">
<div>
<span className="block text-neutral-600">STT ENGINE</span>
<span className="text-neutral-300 font-semibold">faster-whisper</span>
</div>
<div>
<span className="block text-neutral-600">LLM MODEL</span>
<span className="text-neutral-300 font-semibold">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>
</div>
</div>
</div>
</div>
</div>
</div>
{/* Data strip */}
{/* 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="<2s" />
<DataPoint label={t.hero.dataLatency} value="<1.2s" />
<DataPoint label={t.hero.dataPrivacy} value="10/10" />
</div>
</div>
@ -82,7 +276,7 @@ export function Hero() {
function DownloadIcon() {
return (
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<svg className="w-4 h-4" 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" />
@ -92,9 +286,19 @@ function DownloadIcon() {
function ArrowIcon() {
return (
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<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() {
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" />
<path d="M5 10a7 7 0 0 0 14 0" />
<line x1="12" y1="17" x2="12" y2="21" />
</svg>
)
}

View file

@ -1,3 +1,4 @@
import { useState } from 'react'
import { SectionHeader } from '../components/SectionHeader'
import { Crosshair } from '../components/Crosshair'
import { Container } from '../components/Container'
@ -5,8 +6,61 @@ import { Led } from '../components/Led'
import { WaveBars } from '../components/WaveBars'
import { useI18n } from '../i18n'
interface ScenarioPreset {
id: string
title: string
tag: string
raw: string
clean: string
latency: string
model: string
}
export function HowItWorks() {
const { t } = useI18n()
const { t, locale } = useI18n()
const [activePreset, setActivePreset] = useState(0)
const presets: ScenarioPreset[] = [
{
id: 'business',
title: locale === 'ko' ? '비즈니스 회의 요약 및 정제' : 'Business Polish',
tag: 'LLM POLISH',
raw: locale === 'ko'
? '어... 그 다음 주 화요일 회의에서 마케팅 예산 편성안을 검토해보도록 합시다.'
: 'Um... let us review the marketing budget allocation plan in next Tuesday\'s meeting.',
clean: locale === 'ko'
? '다음 주 화요일 회의 안건으로 마케팅 예산 편성안을 검토하겠습니다.'
: 'Action: Review marketing budget allocation plan during Tuesday\'s meeting.',
latency: '0.78s',
model: 'gemma4:e4b (Local)',
},
{
id: 'developer',
title: locale === 'ko' ? '개발자 Git 커밋 메시지' : 'Developer Git Commit',
tag: 'MACRO CHAIN',
raw: locale === 'ko'
? '로그인할 때 리프레시 토큰 만료 에러 핸들링 추가했어'
: 'Add refresh token expiration error handling when logging in',
clean: 'fix(auth): handle refresh token expiration error on user login',
latency: '0.62s',
model: 'qwen2.5-coder:7b',
},
{
id: 'translate',
title: locale === 'ko' ? '실시간 다국어 비즈니스 번역' : 'Real-time Translation',
tag: 'TRANSLATION',
raw: locale === 'ko'
? '이번 분기 실적 보고서를 오늘 퇴근 전까지 공유해주세요.'
: 'Please share this quarter\'s performance report before the end of the day.',
clean: locale === 'ko'
? 'Please share this quarter\'s performance report before the end of the day.'
: '이번 분기 실적 보고서를 오늘 퇴근 전까지 전달 부탁드립니다.',
latency: '0.91s',
model: 'faster-whisper + Ollama',
},
]
const current = presets[activePreset]
return (
<section id="pipeline" className="relative py-24 md:py-32">
@ -19,72 +73,106 @@ export function HowItWorks() {
subtitle={t.pipeline.subtitle}
/>
{/* Preset Selector Bar */}
<div className="flex items-center justify-center gap-2 mb-10 overflow-x-auto pb-2">
{presets.map((p, idx) => (
<button
key={p.id}
onClick={() => setActivePreset(idx)}
className={`px-4 py-2 rounded-panel font-mono text-xs uppercase tracking-wider transition-all flex items-center gap-2 ${
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]'
}`}
>
<span className="w-1.5 h-1.5 rounded-full bg-current" />
{p.title}
</button>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-start">
{/* Left: visual panel */}
{/* 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">
<div className="relative z-10 flex items-center justify-between mb-6">
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber/60">
{t.pipeline.panelTitle}
</span>
<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="flex items-center gap-2">
<Led color="green" />
<span className="font-mono text-nano uppercase tracking-widest text-neutral-500">{t.pipeline.panelActive}</span>
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber/80">
{current.tag} PIPELINE
</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]">
{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>
</div>
</div>
<div className="relative z-10 flex-1 flex flex-col justify-center space-y-4">
{/* Simulated terminal output */}
<div className="font-mono text-xs text-neutral-500 leading-relaxed space-y-2">
<div className="flex items-center gap-2">
<Led color="green" size="sm" />
<span className="text-emerald-400/70">{t.pipeline.sttReady}</span>
<span className="text-neutral-600">faster-whisper base</span>
</div>
<div className="flex items-center gap-2">
<Led color="green" size="sm" />
<span className="text-emerald-400/70">{t.pipeline.llmConnected}</span>
<span className="text-neutral-600">gemma4:e4b @ localhost:11434</span>
</div>
<div className="h-px bg-white/[0.04] my-3" />
<div className="flex items-start gap-2">
<Led color="amber" size="sm" pulse className="mt-1" />
<div>
<span className="text-brand-amber/50 block mb-1">{t.pipeline.transcription}</span>
<span className="text-neutral-300">&quot;{t.pipeline.sampleInput}&quot;</span>
{/* 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">
<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>
</div>
<span className="text-nano text-neutral-600 font-mono">0ms</span>
</div>
<div className="flex items-start gap-2">
<Led color="green" size="sm" className="mt-1" />
<div>
<span className="text-emerald-400/50 block mb-1">{t.pipeline.aiPolish}</span>
<span className="text-neutral-300">&quot;{t.pipeline.sampleOutput}&quot;</span>
<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>
<p className="text-sm font-sans text-neutral-300 font-medium">
&quot;{current.raw}&quot;
</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>
<p className="text-sm font-sans text-emerald-200 font-semibold">
&quot;{current.clean}&quot;
</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-600">{t.pipeline.panelLatency}</span>
<span className="font-mono text-nano text-neutral-400">
Total Engine Latency: <strong className="text-white">{current.latency}</strong> (Zero Cloud Traffic)
</span>
</div>
</div>
</Crosshair>
{/* Right: numbered steps */}
<div className="order-1 lg:order-2 space-y-1">
{/* Right: Numbered Architecture Steps */}
<div className="order-1 lg:order-2 space-y-2">
{t.pipeline.steps.map((step, i) => (
<div
key={i}
className="group relative flex gap-5 p-5 rounded-card border border-transparent hover:border-white/[0.04] hover:bg-surface-700/30 transition-all"
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"
>
{/* 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">
<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>
<div className="flex-1 min-w-0">
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber/60 block mb-1">
<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">
@ -93,14 +181,14 @@ export function HowItWorks() {
<p className="text-sm text-neutral-400 leading-relaxed mb-2">
{step.description}
</p>
<span className="inline-block font-mono text-nano text-neutral-600 bg-surface-600/60 px-2.5 py-1 rounded-sm border border-white/[0.03]">
<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]">
{step.detail}
</span>
</div>
{/* Connector */}
{i < t.pipeline.steps.length - 1 && (
<div className="absolute left-[42px] top-[72px] w-px h-5 bg-gradient-to-b from-surface-300 to-transparent hidden lg:block" />
<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>
))}

View file

@ -8,14 +8,14 @@ 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_4px_rgba(52,211,153,0.6)]" />
<span className="w-2 h-2 rounded-full bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.8)]" />
</span>
)
}
if (value === false) {
return (
<span className="flex items-center justify-center">
<span className="w-2 h-2 rounded-full bg-neutral-600" />
<span className="w-2 h-2 rounded-full bg-neutral-700" />
</span>
)
}
@ -23,18 +23,111 @@ function CellValue({ value }: { value: string | boolean }) {
}
const PRICES = {
pro: { monthly: 9.9, annual: 99 },
proPlus: { monthly: 19.9, annual: 199 },
usd: {
pro: { monthly: 9.9, annual: 99 },
proPlus: { monthly: 19.9, annual: 199 },
team: { monthly: 25, annual: 240 },
},
krw: {
pro: { monthly: 12900, annual: 119000 },
proPlus: { monthly: 24900, annual: 229000 },
team: { monthly: 32000, annual: 299000 },
}
}
export function Pricing() {
const { t } = useI18n()
const { t, locale } = useI18n()
const [isAnnual, setIsAnnual] = useState(true)
const [currency, setCurrency] = useState<'usd' | 'krw'>(locale === 'ko' ? 'krw' : 'usd')
const proPrice = isAnnual ? PRICES.pro.annual : PRICES.pro.monthly
const proPlusPrice = isAnnual ? PRICES.proPlus.annual : PRICES.proPlus.monthly
const currencySymbol = currency === 'krw' ? '₩' : '$'
const activePrices = PRICES[currency]
const formatPrice = (val: number) => {
if (currency === 'krw') {
return val.toLocaleString()
}
return val.toString()
}
const proPrice = isAnnual ? activePrices.pro.annual : activePrices.pro.monthly
const proPlusPrice = isAnnual ? activePrices.proPlus.annual : activePrices.proPlus.monthly
const teamPrice = isAnnual ? activePrices.team.annual : activePrices.team.monthly
const periodLabel = isAnnual ? t.pricing.annual : t.pricing.monthly
const tiers = [
{
id: 'free',
name: t.pricing.free,
price: `${currencySymbol}0`,
period: t.pricing.forever,
desc: locale === 'ko' ? '100% 온디바이스 무제한 로컬 음성 인식' : '100% on-device unlimited private dictation',
popular: false,
buttonText: t.pricing.downloadFree,
buttonVariant: 'secondary' as const,
features: [
locale === 'ko' ? '무제한 on-device faster-whisper STT' : 'Unlimited faster-whisper local STT',
locale === 'ko' ? '전역 Push-to-Talk HUD 캡슐' : 'Global Push-to-Talk capsule HUD',
locale === 'ko' ? '로컬 Ollama LLM 무제한 연동' : 'Unlimited local Ollama LLM connect',
locale === 'ko' ? '100% 온디바이스 제로 텔레메트리' : '100% On-device zero cloud leak',
],
},
{
id: 'pro',
name: t.pricing.pro,
price: `${currencySymbol}${formatPrice(proPrice)}`,
period: periodLabel,
subPrice: isAnnual ? `${currencySymbol}${formatPrice(Math.round(activePrices.pro.annual / 12))}${t.pricing.perMonth}` : null,
desc: locale === 'ko' ? '회의 스튜디오 & 하이브리드 고속 AI' : 'Meeting studio & hybrid fast AI acceleration',
popular: true,
buttonText: t.pricing.getPro,
buttonVariant: 'primary' as const,
features: [
locale === 'ko' ? 'Free 티어의 모든 기능 포함' : 'Includes all Free features',
locale === 'ko' ? '회의 스튜디오 & 실시간 화자 분리' : 'Meeting Studio & Speaker Diarization',
locale === 'ko' ? '마크다운 회의록 & 액션 아이템 자동 생성' : 'Auto Markdown Minutes & Action Items',
locale === 'ko' ? '다단계 AI 파이프라인 체이닝 & 매크로' : 'Multi-step Prompt Chaining & Macros',
locale === 'ko' ? '클라우드 LLM 하이브리드 초고속 다듬기' : 'Hybrid Cloud LLM Acceleration',
],
},
{
id: 'proPlus',
name: t.pricing.proPlus,
price: `${currencySymbol}${formatPrice(proPlusPrice)}`,
period: periodLabel,
subPrice: isAnnual ? `${currencySymbol}${formatPrice(Math.round(activePrices.proPlus.annual / 12))}${t.pricing.perMonth}` : null,
desc: locale === 'ko' ? '초저지연 클라우드 STT & 개인 문서 RAG' : 'Ultra-low latency STT & private vector RAG',
popular: false,
buttonText: t.pricing.getProPlus,
buttonVariant: 'secondary' as const,
features: [
locale === 'ko' ? 'Pro 티어의 모든 기능 포함' : 'Includes all Pro features',
locale === 'ko' ? '초저지연 Cloud STT (Deepgram Nova-3)' : 'Ultra-Low Latency Cloud STT',
locale === 'ko' ? '로컬 문서 지식베이스 (sqlite-vec RAG)' : 'Private Document Knowledge Base (RAG)',
locale === 'ko' ? '실시간 음성 양방향 대화 캔버스' : 'Real-time Voice Duplex Canvas',
locale === 'ko' ? '우선 기술 지원 및 신기능 얼리액세스' : 'Priority Tech Support & Early Access',
],
},
{
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,
desc: locale === 'ko' ? '팀 공유 사전 & 지식 협업 & 엔터프라이즈 거버넌스' : 'Team shared dictionaries, knowledge & governance',
popular: false,
buttonText: locale === 'ko' ? '팀 도입 문의' : 'Contact Sales',
buttonVariant: 'secondary' as const,
features: [
locale === 'ko' ? 'Pro+ 티어의 모든 기능 포함' : 'Includes all Pro+ features',
locale === 'ko' ? '팀 공용 맞춤형 사전 및 도메인 용어집' : 'Shared Team Dictionaries & Jargon',
locale === 'ko' ? '팀 워크스페이스 회의록 협업' : 'Team Meeting Collaboration Workspace',
locale === 'ko' ? 'SAML 2.0 SSO 및 SCIM 자동 계정 관리' : 'SAML 2.0 SSO & SCIM Provisioning',
locale === 'ko' ? 'Zero Data Retention (ZDR) 데이터 보안 협약' : 'Zero Data Retention (ZDR) Compliance',
],
},
]
return (
<section id="pricing" className="relative py-24 md:py-32">
<Container>
@ -44,77 +137,166 @@ export function Pricing() {
subtitle={t.pricing.subtitle}
/>
{/* Billing toggle */}
<div className="flex items-center justify-center gap-3 mb-10">
<button
onClick={() => setIsAnnual(false)}
className={`font-mono text-xs uppercase tracking-widest px-4 py-2 rounded-panel transition-all ${
!isAnnual
? 'bg-surface-500 text-neutral-100 border border-white/[0.12]'
: 'text-neutral-500 hover:text-neutral-300'
}`}
>
{t.pricing.billingToggleMonthly}
</button>
<button
onClick={() => setIsAnnual(true)}
className={`font-mono text-xs uppercase tracking-widest px-4 py-2 rounded-panel transition-all relative ${
isAnnual
? 'bg-surface-500 text-neutral-100 border border-brand-amber/30'
: 'text-neutral-500 hover:text-neutral-300'
}`}
>
{t.pricing.billingToggleAnnual}
{isAnnual && (
<span className="absolute -top-2.5 -right-2 px-1.5 py-0.5 rounded-full bg-brand-amber text-white text-[9px] font-mono font-semibold uppercase tracking-wider">
{t.pricing.savePercent}
</span>
)}
</button>
{/* 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="text-left">
<p className="font-mono text-xs font-bold uppercase tracking-wider text-brand-amber">
{locale === 'ko' ? '14일 Reverse-Trial 무료 체험' : '14-Day Free Reverse Trial'}
</p>
<p className="text-xs text-neutral-300">
{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">
{locale === 'ko' ? '자동 활성화' : 'Zero Friction'}
</span>
</div>
{/* Pricing table */}
{/* 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]">
<button
onClick={() => setCurrency('usd')}
className={`font-mono text-xs px-3.5 py-1.5 rounded transition-all ${
currency === 'usd'
? 'bg-surface-500 text-neutral-100 font-bold shadow-sm'
: 'text-neutral-500 hover:text-neutral-300'
}`}
>
USD ($)
</button>
<button
onClick={() => setCurrency('krw')}
className={`font-mono text-xs px-3.5 py-1.5 rounded transition-all ${
currency === 'krw'
? 'bg-surface-500 text-neutral-100 font-bold shadow-sm'
: 'text-neutral-500 hover:text-neutral-300'
}`}
>
KRW ()
</button>
</div>
{/* Billing Cycle Toggle */}
<div className="flex items-center gap-3">
<button
onClick={() => setIsAnnual(false)}
className={`font-mono text-xs uppercase tracking-widest px-5 py-2.5 rounded-panel transition-all ${
!isAnnual
? 'bg-surface-500 text-neutral-100 border border-white/[0.12] shadow-sm'
: 'text-neutral-500 hover:text-neutral-300'
}`}
>
{t.pricing.billingToggleMonthly}
</button>
<button
onClick={() => setIsAnnual(true)}
className={`font-mono text-xs uppercase tracking-widest px-5 py-2.5 rounded-panel 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'
}`}
>
{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">
{t.pricing.savePercent}
</span>
)}
</button>
</div>
</div>
{/* 4 Tier Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-5 mb-16">
{tiers.map((tier) => (
<div
key={tier.id}
className={`relative rounded-2xl p-6 flex flex-col justify-between noise-texture 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]'
}`}
>
{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">
{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'}`}>
{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]">
{tier.desc}
</p>
<div className="h-px bg-white/[0.06] mb-5" />
<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>
</li>
))}
</ul>
</div>
<GlowButton href="#" variant={tier.buttonVariant} size="md" className="w-full text-xs">
{tier.buttonText}
</GlowButton>
</div>
))}
</div>
{/* Detailed Comparison Table */}
<div className="overflow-x-auto -mx-5 md:mx-0">
<table className="w-full min-w-[640px] border-collapse">
<table className="w-full min-w-[700px] border-collapse bg-surface-800/30 rounded-xl overflow-hidden border border-white/[0.04]">
{/* Header */}
<thead>
<tr className="border-b border-white/[0.08]">
<th className="text-left py-4 px-4 font-mono text-nano uppercase tracking-widest text-neutral-500 w-[40%]">
<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-4 w-[20%]">
<div className="font-mono text-nano uppercase tracking-widest text-neutral-500 mb-1">{t.pricing.free}</div>
<div className="font-display text-xl font-bold text-neutral-300">$0</div>
<div className="font-mono text-nano text-neutral-600">{t.pricing.forever}</div>
<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-4 w-[20%] relative">
<div className="absolute inset-x-0 -top-3 flex justify-center">
<span className="px-3 py-0.5 rounded-full bg-brand-amber text-white text-nano font-mono font-semibold uppercase tracking-widest">
{t.pricing.popular}
</span>
</div>
<div className="font-mono text-nano uppercase tracking-widest text-brand-amber mt-4 mb-1">{t.pricing.pro}</div>
<div className="font-display text-xl font-bold text-neutral-100">
${proPrice}
<span className="text-sm font-normal text-neutral-500">{periodLabel}</span>
</div>
{isAnnual && (
<div className="font-mono text-nano text-neutral-600">
${(PRICES.pro.annual / 12).toFixed(1)}{t.pricing.perMonth}
</div>
)}
<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-4 w-[20%]">
<div className="font-mono text-nano uppercase tracking-widest text-neutral-500 mb-1">{t.pricing.proPlus}</div>
<div className="font-display text-xl font-bold text-neutral-300">
${proPlusPrice}
<span className="text-sm font-normal text-neutral-500">{periodLabel}</span>
</div>
{isAnnual && (
<div className="font-mono text-nano text-neutral-600">
${(PRICES.proPlus.annual / 12).toFixed(1)}{t.pricing.perMonth}
</div>
)}
<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>
</thead>
@ -128,47 +310,34 @@ export function Pricing() {
i % 2 === 0 ? 'bg-transparent' : 'bg-surface-800/20'
}`}
>
<td className="py-3 px-4 font-mono text-xs text-neutral-400">
<td className="py-3 px-5 font-sans text-xs text-neutral-300 font-medium">
{row.feature}
</td>
<td className="py-3 px-4 text-center">
<td className="py-3 px-3 text-center">
<CellValue value={row.free} />
</td>
<td className="py-3 px-4 text-center bg-brand-amber-muted/30">
<td className="py-3 px-3 text-center bg-brand-amber/[0.04]">
<CellValue value={row.pro} />
</td>
<td className="py-3 px-4 text-center">
<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>
</tr>
))}
</tbody>
</table>
</div>
{/* CTA row */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-8">
<div className="flex justify-center">
<GlowButton href="#" variant="secondary" size="md">
{t.pricing.downloadFree}
</GlowButton>
</div>
<div className="flex justify-center">
<GlowButton href="#" variant="primary" size="md">
{t.pricing.getPro}
</GlowButton>
</div>
<div className="flex justify-center">
<GlowButton href="#" variant="secondary" size="md">
{t.pricing.getProPlus}
</GlowButton>
</div>
</div>
<p className="text-center font-mono text-nano text-neutral-600 mt-6 uppercase tracking-widest">
{t.pricing.taxNote}
<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>
)
}

View file

@ -3,12 +3,35 @@ import { Container } from '../components/Container'
import { Led } from '../components/Led'
import { useI18n } from '../i18n'
const ledColors: Array<'amber' | 'green' | 'red'> = [
'green', 'green', 'green', 'green', 'green', 'green',
]
export function Privacy() {
const { t } = useI18n()
const { t, locale } = useI18n()
const comparisonRows = [
{
feature: locale === 'ko' ? '음성 데이터 외부 서버 전송' : 'Voice Uploaded to External Servers',
d3ro: locale === 'ko' ? '0% (절대 전송 안 함)' : '0% (Never Uploaded)',
cloud: locale === 'ko' ? '100% (클라우드 저장)' : '100% (Saved to Cloud)',
d3roOk: true,
},
{
feature: locale === 'ko' ? '오프라인 단독 동작 가능 여부' : '100% Offline / Air-Gapped Operation',
d3ro: locale === 'ko' ? '완전 지원 (인터넷 불필요)' : 'Fully Supported (No Internet Needed)',
cloud: locale === 'ko' ? '불가 (연결 끊기면 중단)' : 'Fails without Internet',
d3roOk: true,
},
{
feature: locale === 'ko' ? '네트워크 전송 지연 (RTT)' : 'Network Roundtrip Latency',
d3ro: '0ms (On-Device Memory)',
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',
d3roOk: true,
},
]
return (
<section id="privacy" className="relative py-24 md:py-32 overflow-hidden">
@ -20,31 +43,31 @@ export function Privacy() {
/>
{/* CRT instrument panel */}
<div className="crt-screen rounded-card p-6 md:p-10 noise-texture mb-12">
<div className="crt-screen rounded-2xl p-6 md:p-10 noise-texture mb-12 shadow-2xl border border-white/[0.08]">
<div className="relative z-10">
{/* Panel header */}
<div className="flex items-center justify-between mb-8 pb-4 border-b border-white/[0.04]">
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber/60">
{t.privacy.monitorTitle}
</span>
<div className="flex items-center justify-between mb-8 pb-4 border-b border-white/[0.06]">
<div className="flex items-center gap-2">
<Led color="green" pulse />
<span className="font-mono text-nano uppercase tracking-widest text-emerald-400/60">
{t.privacy.allClear}
<span className="font-mono text-nano uppercase tracking-widest 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>
</div>
{/* Metric grid */}
<div className="grid grid-cols-2 md:grid-cols-3 gap-6 md:gap-8">
{t.privacy.metrics.map((metric, i) => (
<div key={i} className="flex flex-col gap-2">
<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">
{metric.label}
</span>
<div className="flex items-center gap-2">
<Led color={ledColors[i]} size="sm" />
<span className="font-mono text-sm font-semibold text-neutral-200 tracking-wide">
<Led color="green" size="sm" />
<span className="font-mono text-sm font-semibold text-neutral-100 tracking-wide">
{metric.value}
</span>
</div>
@ -54,19 +77,45 @@ export function Privacy() {
</div>
</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]">
<thead>
<tr className="border-b border-white/[0.08] bg-surface-700/50 font-mono text-nano uppercase tracking-wider text-neutral-400">
<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>
</tr>
</thead>
<tbody className="divide-y divide-white/[0.04] text-xs">
{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]">
{row.d3ro}
</td>
<td className="p-4 font-mono text-neutral-500">
{row.cloud}
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Guarantees list */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{t.privacy.guarantees.map((item, i) => (
<div
key={i}
className="flex items-start gap-3 p-4 rounded-card bg-surface-700/30 border border-white/[0.03]"
className="flex items-start gap-3 p-4 rounded-xl bg-surface-700/40 border border-white/[0.04]"
>
<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">
<polyline points="20 6 9 17 4 12" />
</svg>
</div>
<span className="text-sm text-neutral-300 leading-relaxed">{item}</span>
<span className="text-sm text-neutral-300 leading-relaxed font-medium">{item}</span>
</div>
))}
</div>

View file

@ -43,8 +43,8 @@ export default {
mono: ['"JetBrains Mono"', '"Fira Code"', 'monospace'],
},
fontSize: {
'hero': ['clamp(3.5rem, 12vw, 10rem)', { lineHeight: '0.9', letterSpacing: '-0.04em' }],
'display': ['clamp(2.5rem, 6vw, 5rem)', { lineHeight: '1', letterSpacing: '-0.03em' }],
'hero': ['clamp(2.5rem, 5vw, 4.25rem)', { lineHeight: '1.15', letterSpacing: '-0.03em' }],
'display': ['clamp(2rem, 3.5vw, 3.25rem)', { lineHeight: '1.2', letterSpacing: '-0.025em' }],
'micro': ['0.625rem', { lineHeight: '1.2', letterSpacing: '0.1em' }],
'nano': ['0.5625rem', { lineHeight: '1.2', letterSpacing: '0.12em' }],
},

View file

@ -3,5 +3,5 @@ import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
base: '/D3ROVoice/',
base: './',
})