feat(site): rewrite the landing page around verified facts and accessible controls

- Replace claims that contradicted the app: the default hotkey is Right Alt
  (hold to talk), local features are free with no daily cap, there is no
  14-day trial, Team/SSO/SCIM/ZDR are not offered, and the repository is not
  public. Remove invented metrics, status badges, the competitor table, the
  ad-mediation changelog, the hash calculator and the duplicate demo.
- Seven sections: hero with one labelled example, features, how it works,
  privacy, pricing (Free / Pro 2,900 / Pro+ 8,900 KRW a month), download, FAQ.
  Footer links the privacy policy, terms and account deletion pages.
- All copy moves into i18n and every one of the 10 locales is translated.
  Pricing and cloud quotas come from site/src/pricing.ts.
- Accessibility: skip link, labelled sections, aria-expanded with Esc and
  focus return for the menu, language list and FAQ, live status for the
  example, reduced-motion support, 44px targets, AA contrast on the primary
  button. Korean keep-all line breaking is scoped to :lang(ko) because
  Tailwind's break-keep blocked wrapping in Japanese and Chinese.
- JS 111 -> 98 KB gzip, CSS 7.8 -> 5.2 KB gzip.
This commit is contained in:
Yun Chan 2026-09-26 15:04:43 +09:00
parent ad6bb70c20
commit e689683b72
44 changed files with 2198 additions and 3782 deletions

View file

@ -2,60 +2,79 @@
@tailwind components;
@tailwind utilities;
/* ── Base Reset & Globals ──────────────────────────────── */
/* ── Base ─────────────────────────────────────────────── */
@layer base {
:root {
--font-sans: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", sans-serif;
--font-display: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
--brand-blue: #3b82f6;
--brand-blue-soft: rgba(255, 255, 255, 0.25);
--brand-glow: rgba(59, 130, 246, 0.7);
}
html {
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
font-family: var(--font-sans);
}
::selection {
background-color: rgba(59, 130, 246, 0.35);
color: #ffffff;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #08090c;
}
::-webkit-scrollbar-thumb {
background: #222838;
border-radius: 4px;
border: 2px solid #08090c;
}
::-webkit-scrollbar-thumb:hover {
background: #363f57;
@apply bg-surface-950 font-sans text-neutral-100;
}
body {
overflow-x: hidden;
background-color: #08090c;
color: #f4f4f5;
}
/* Accessibility Focus Rings */
::selection {
@apply bg-brand-blue/35 text-white;
}
:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
@apply outline outline-2 outline-offset-2 outline-brand-blue-light;
}
/* 고정 헤더(64px)가 앵커 대상의 머리를 가리지 않게 한다. */
[id] {
scroll-margin-top: 5rem;
}
/* 의미적 강조는 세미볼드까지 — 볼드(700) 금지 (design.md 타이포 원칙 1) */
strong, b { font-weight: 600; }
[hidden] { display: none !important; }
}
/* ── Components ───────────────────────────────────────── */
@layer components {
.section {
@apply py-20 md:py-28;
}
/* 버튼: 최소 44px 터치 타깃. 흰 글자 대비 4.5:1 이상을 위해 채움색은 blue-dark 계열. */
.btn {
@apply inline-flex min-h-[44px] flex-wrap items-center justify-center gap-x-2 gap-y-1 rounded-xl px-5 py-2 text-center text-base font-medium leading-snug transition-colors duration-150;
}
.btn-sm {
@apply min-h-[40px] px-4 text-sm;
}
.btn-lg {
@apply min-h-[52px] px-7;
}
.btn-primary {
@apply bg-brand-blue-dark text-white hover:bg-brand-blue-deep;
}
.btn-secondary {
@apply border border-white/15 bg-surface-800 text-neutral-100 hover:border-white/30 hover:bg-surface-700;
}
.link {
@apply text-brand-blue-light underline decoration-brand-blue-light/40 underline-offset-4 transition-colors hover:decoration-brand-blue-light;
}
.kbd {
@apply inline-flex min-h-[32px] items-center rounded-md border border-white/15 border-b-white/25 bg-surface-700 px-2.5 font-sans text-sm text-neutral-100;
}
.kbd-accent {
@apply border-brand-blue/50 border-b-brand-blue bg-brand-blue/15 text-brand-blue-light;
}
.skip-link {
@apply fixed left-4 top-3 z-[60] -translate-y-24 rounded-lg bg-surface-800 px-4 py-3 text-sm text-white shadow-chassis focus:translate-y-0;
}
}
/* ── Grid Background Pattern ──────────────────────────── */
/* ── Grid Background Pattern (히어로) ─────────────────── */
.bg-grid {
background-image:
linear-gradient(rgba(59, 130, 246, 0.035) 1px, transparent 1px),
@ -63,24 +82,18 @@
background-size: 64px 64px;
}
.bg-grid-dense {
background-image:
linear-gradient(rgba(255, 255, 255, 0.025) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.025) 1px, transparent 1px);
background-size: 24px 24px;
}
/* ── CRT Screen Effect (Crisp High-Contrast) ─────────── */
/* ── CRT Screen (히어로 예시 화면의 계기판 재질) ─────── */
.crt-screen {
position: relative;
overflow: hidden;
background: linear-gradient(180deg, #05070a 0%, #0a0e14 100%);
border: 1px solid rgba(59, 130, 246, 0.2);
border: 1px solid rgba(59, 130, 246, 0.25);
box-shadow:
inset 0 2px 10px rgba(0, 0, 0, 0.9),
0 0 32px rgba(59, 130, 246, 0.08);
}
/* 정지된 주사선만 남긴다. 움직이는 스캔 효과는 읽기를 방해해 제거했다. */
.crt-screen::before {
content: '';
position: absolute;
@ -89,172 +102,67 @@
0deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.2) 2px,
rgba(0, 0, 0, 0.2) 4px
rgba(0, 0, 0, 0.18) 2px,
rgba(0, 0, 0, 0.18) 4px
);
pointer-events: none;
z-index: 2;
}
.crt-screen::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
180deg,
rgba(59, 130, 246, 0.03) 0%,
transparent 50%,
rgba(59, 130, 246, 0.015) 100%
);
animation: scan 8s linear infinite;
pointer-events: none;
z-index: 3;
}
@keyframes scan {
0% { transform: translateY(-100%); }
100% { transform: translateY(100%); }
}
/* ── Crosshair Decorations ─────────────────────────────── */
.crosshair-box {
--ch-size: 16px;
--ch-color: rgba(59, 130, 246, 0.4);
--ch-color: rgba(59, 130, 246, 0.45);
position: relative;
}
.crosshair-box::before {
content: '';
position: absolute;
top: -1px;
left: -1px;
width: var(--ch-size);
height: var(--ch-size);
border-top: 1.5px solid var(--ch-color);
border-left: 1.5px solid var(--ch-color);
pointer-events: none;
}
.crosshair-box::before,
.crosshair-box::after {
content: '';
position: absolute;
bottom: -1px;
right: -1px;
width: var(--ch-size);
height: var(--ch-size);
pointer-events: none;
}
.crosshair-box::before {
top: -6px;
left: -6px;
border-top: 1.5px solid var(--ch-color);
border-left: 1.5px solid var(--ch-color);
}
.crosshair-box::after {
bottom: -6px;
right: -6px;
border-bottom: 1.5px solid var(--ch-color);
border-right: 1.5px solid var(--ch-color);
pointer-events: none;
}
/* ── Instrument Noise Texture ──────────────────────────── */
.noise-texture {
position: relative;
}
.noise-texture::before {
content: '';
position: absolute;
inset: 0;
opacity: 0.03;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
background-repeat: repeat;
background-size: 256px 256px;
pointer-events: none;
border-radius: inherit;
}
/* ── Glass Surfaces & Double Bezel ─────────────────────── */
.glass-panel {
background: rgba(19, 22, 31, 0.75);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 12px 32px -4px rgba(0, 0, 0, 0.5);
}
.glass-panel-hover {
transition: all 250ms cubic-bezier(0.16, 1, 0.3, 1);
}
.glass-panel-hover:hover {
border-color: rgba(59, 130, 246, 0.35);
background: rgba(26, 30, 43, 0.85);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 0 20px rgba(59, 130, 246, 0.15);
}
/* ── Glow Button ───────────────────────────────────────── */
.glow-btn {
position: relative;
overflow: hidden;
transition: all 250ms cubic-bezier(0.16, 1, 0.3, 1);
}
.glow-btn::before {
content: '';
position: absolute;
inset: -2px;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.45), rgba(59, 130, 246, 0) 65%);
border-radius: inherit;
opacity: 0;
transition: opacity 250ms;
z-index: 0;
}
.glow-btn:hover::before {
opacity: 1;
}
.glow-btn:hover {
box-shadow: 0 0 24px rgba(59, 130, 246, 0.35), 0 0 48px rgba(59, 130, 246, 0.12);
transform: translateY(-1px);
}
.glow-btn:active {
transform: translateY(0) scale(0.98);
}
/* ── Accordion ─────────────────────────────────────────── */
.accordion-content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 250ms cubic-bezier(0.16, 1, 0.3, 1), opacity 200ms ease;
overflow: hidden;
opacity: 0;
visibility: hidden;
}
.accordion-content.open {
grid-template-rows: 1fr;
opacity: 1;
visibility: visible;
}
.accordion-content > div {
overflow: hidden;
min-height: 0;
}
/* ── Reveal Animation ──────────────────────────────────── */
.reveal {
opacity: 0;
transform: translateY(16px);
}
.reveal.visible {
animation: fade-in-up 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes fade-in-up {
0% { opacity: 0; transform: translateY(16px); }
100% { opacity: 1; transform: translateY(0); }
}
/* ── Utilities ─────────────────────────────────────────── */
@layer base {
/* 의미적 강조는 세미볼드까지 — 볼드(700) 헤드라인 금지 (한글 과중 방지) */
strong, b { font-weight: 600; }
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
.break-keep {
word-break: keep-all;
/* 한글 어절 단위 줄바꿈. 중국어·일본어에 keep-all을 걸면 줄바꿈 기회가 사라지므로 한국어에만 적용한다. */
.keep-ko {
overflow-wrap: break-word;
}
.border-hairline {
border-color: rgba(255, 255, 255, 0.08);
.keep-ko:lang(ko) {
word-break: keep-all;
}
}
/* ── Reduced motion ───────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}