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
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:
parent
5cd1de6859
commit
708e20f747
406 changed files with 42464 additions and 6199 deletions
994
site/public/launch-readiness.html
Normal file
994
site/public/launch-readiness.html
Normal 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>
|
||||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">·</span>VOICE
|
||||
<span className="font-mono text-sm font-bold tracking-tight text-white">
|
||||
D3RO<span className="text-brand-amber">·</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>
|
||||
|
|
|
|||
|
|
@ -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 'Try Demo' 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">
|
||||
"{typedClean}"
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Telemetry Hardware Stats */}
|
||||
<div className="grid grid-cols-3 gap-2 pt-2 border-t border-white/[0.04] font-mono text-nano text-neutral-500">
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">"{t.pipeline.sampleInput}"</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">"{t.pipeline.sampleOutput}"</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">
|
||||
"{current.raw}"
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-3 rounded-lg bg-emerald-500/10 border border-emerald-500/20 space-y-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-emerald-400 flex items-center gap-1.5">
|
||||
<Led color="green" size="sm" />
|
||||
Transformed & Injected to Active Window:
|
||||
</span>
|
||||
<span className="font-mono text-nano text-emerald-400/80 font-bold">{current.latency}</span>
|
||||
</div>
|
||||
<p className="text-sm font-sans text-emerald-200 font-semibold">
|
||||
"{current.clean}"
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex items-center justify-between pt-4 border-t border-white/[0.04]">
|
||||
<WaveBars className="h-6" />
|
||||
<span className="font-mono text-nano text-neutral-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>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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' }],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ import react from '@vitejs/plugin-react'
|
|||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
base: '/D3ROVoice/',
|
||||
base: './',
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue