feat(home): Liquid Glass + Jua 폰트로 헤더·푸터·홈·뮤직 페이지 전환

- layout.tsx: Bagel/Inter/Manrope/SpaceGrotesk → Jua 단일화 + GlassFilter 마운트
- globals.css: 글래스 효과·Jua 폰트 변수
- TopNav: 알약형 글래스 헤더 (스크롤 시 max-w-3xl 축소)
- PublicShell: 푸터 정돈
- 홈 page.tsx: hero 영상 배경 + GlassButton CTA + 트윗 마퀴
- 뮤직 page.tsx: SparklesOverlay + 3D card effect

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 02:12:29 +09:00
parent 7ee75f1511
commit 32dce9ea1e
6 changed files with 493 additions and 457 deletions

View File

@@ -2,15 +2,10 @@
import { useState } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import ContactModal from './components/ContactModal';
import { GlassButton } from './components/LiquidGlass';
import { trackCTAClick } from '../lib/gtag';
/* ═══════════════════════════════════════════════════
쟁승메이드 홈 v6 — AI Music Creator Landing
참조: Downloads/stitch_ai_mv/ai_music_creator_landing_page
═══════════════════════════════════════════════════ */
const BEFORE = [
'작곡 공부에만 최소 6개월 소요',
'영상 편집 프로그램 학습의 높은 장벽',
@@ -18,6 +13,28 @@ const BEFORE = [
'곡 하나 완성에 드는 수백만 원의 외주비',
];
const TWEETS_ROW_A = [
{ name: '김민재', handle: 'minjae_shorts', time: '2h', body: '작곡 하나 못 하던 내가 3일 만에 쇼츠 채널 열었다. 프롬프트북 반칙 수준 ㄹㅇ' },
{ name: '이소영', handle: 'cafe_sohyang', time: '5h', body: '매장 BGM 직접 만들어요. 저작권 고민 없이 매달 플레이리스트 갈아끼우는 게 신기함.' },
{ name: '박도현', handle: 'dohyun_side', time: '1d', body: '퇴근 후 1시간 = 쇼츠 한 편. 애드센스 첫 수익이 3주 만에 꽂혔습니다. 팩값 회수 완료.' },
{ name: '정유진', handle: 'yujin_indie', time: '2d', body: '데모 작업 시간이 1/5로. 레퍼런스 탐색 → MV까지 한 번에. 인디 뮤지션들 다 써야 함.' },
{ name: '최현우', handle: 'hyunwoo_tube', time: '3d', body: '구독자 정체기였는데 AI 뮤비 시리즈로 알고리즘 탑승. 조회수 월 +320%.' },
{ name: '한지원', handle: 'jiwon_studio', time: '4d', body: '팩 안에 든 저작권 체크리스트가 실질적. Suno 약관 읽는 시간 아꼈다.' },
{ name: '오세린', handle: 'serin_mv', time: '5d', body: 'Runway 프리셋 그대로 써도 퀄 나옴. 프롬프트 설계가 반이네요.' },
{ name: '강태윤', handle: 'taeyun_ads', time: '6d', body: '광고 BGM 10개 찍어서 외주 드렸더니 클라이언트 반응이 달라졌습니다.' },
];
const TWEETS_ROW_B = [
{ name: '문가은', handle: 'gaeun_beats', time: '3h', body: '가사 생성 템플릿이 진짜 핵심. 한글 랩 가사 붙일 때 막히던 거 뚫렸어요.' },
{ name: '류현석', handle: 'hyun_creator', time: '7h', body: '쇼츠 업로드 루틴이 1시간 안에 끝남. 주말마다 10편씩 쌓고 있습니다.' },
{ name: '배수진', handle: 'sujin_pop', time: '1d', body: 'K-POP 스타일 프롬프트 조합 충격. 레퍼런스 없이도 그 느낌이 나옴.' },
{ name: '송재훈', handle: 'jaehun_lab', time: '2d', body: '1:1 Q&A 답변 속도 미쳤어요. 당일 회신 + 실무 디테일까지.' },
{ name: '조은비', handle: 'eunbi_vlog', time: '3d', body: '브이로그 BGM 자작하니까 조회수 + 체류시간 둘 다 올라감. 데이터가 말함.' },
{ name: '신도윤', handle: 'doyoon_snd', time: '4d', body: '스템 분리본이 포함된 게 진짜 크다. 믹싱 작업 훨씬 편해짐.' },
{ name: '윤채원', handle: 'chaewon_art', time: '5d', body: 'Midjourney 프롬프트 풀 가치가 팩값 넘음. 그냥 사세요.' },
{ name: '임준혁', handle: 'junhyuk_tune', time: '6d', body: '업데이트 진짜로 오네요. 2주 만에 V4.5 프롬프트 가이드 추가됨.' },
];
const AFTER = [
'단 1시간 만에 프로급 음원 & 영상 완성',
'드래그 앤 드롭 수준의 직관적인 워크플로우',
@@ -29,7 +46,7 @@ export default function Home() {
const [modalOpen, setModalOpen] = useState(false);
return (
<div className="relative overflow-x-hidden">
<div className="relative overflow-x-hidden bg-black text-white">
<ContactModal
isOpen={modalOpen}
onClose={() => setModalOpen(false)}
@@ -38,184 +55,170 @@ export default function Home() {
/>
{/* 1. Hero */}
<section className="relative px-6 py-24 lg:py-32 overflow-hidden">
<Image
src="/hero_back.webp"
alt=""
fill
priority
sizes="100vw"
className="object-cover object-center pointer-events-none select-none"
style={{ opacity: 0.35 }}
/>
<div
className="absolute inset-0 pointer-events-none"
style={{
background:
'radial-gradient(circle at 50% 30%, rgba(156,72,234,0.28) 0%, transparent 60%), linear-gradient(180deg, rgba(6,14,32,0.35) 0%, rgba(6,14,32,0.85) 75%, var(--kx-surface) 100%)',
}}
<section className="relative w-full h-screen min-h-[640px] flex items-center justify-center px-6 bg-black border-b border-white/10 overflow-hidden">
<video
className="absolute inset-0 w-full h-full object-cover pointer-events-none"
src="/hero-bg.mp4"
autoPlay
loop
muted
playsInline
preload="auto"
aria-hidden
/>
<div className="absolute inset-0 bg-black/55 pointer-events-none" aria-hidden />
<div className="max-w-7xl mx-auto text-center relative z-10">
<span className="kx-label inline-block mb-6">AI Music Pack · 2026</span>
<h1 className="kx-display text-4xl md:text-6xl lg:text-7xl font-bold mb-6 leading-[1.05]" style={{ wordBreak: 'keep-all', letterSpacing: '-0.02em' }}>
,
<h1
className="kx-display text-4xl md:text-6xl lg:text-7xl font-bold mb-6 leading-[1.05]"
style={{ wordBreak: 'keep-all', letterSpacing: '-0.02em' }}
>
<br />
<span className="kx-gradient-text"> .</span>
.
</h1>
<p className="text-lg md:text-xl mb-10 max-w-2xl mx-auto leading-relaxed" style={{ color: 'var(--kx-on-variant)' }}>
<p className="text-lg md:text-xl mb-10 max-w-2xl mx-auto leading-relaxed text-white/70">
.
<br className="sm:hidden" />
{' '}AI .
</p>
<div className="flex flex-col sm:flex-row justify-center gap-4 mb-16">
<Link
<GlassButton
href="/services/music#pricing"
onClick={() => trackCTAClick('home_v6_hero_primary')}
className="kx-btn-primary px-8 py-4 rounded-full text-base inline-flex items-center justify-center gap-2"
tint="rgba(255,255,255,0.18)"
className="text-base"
>
39,000
</Link>
<Link
href="/services/music/samples"
className="px-8 py-4 rounded-full text-base font-bold inline-flex items-center justify-center"
style={{
background: 'rgba(25,37,64,0.4)',
border: '1px solid rgba(64,72,93,0.3)',
color: 'var(--kx-secondary)',
backdropFilter: 'blur(8px)',
textDecoration: 'none',
}}
>
</Link>
<span className="text-white">Try now</span>
</GlassButton>
<GlassButton href="/services/music/samples" tint="rgba(255,255,255,0.08)" className="text-base">
<span className="text-white"> </span>
</GlassButton>
</div>
{/* Price tier mini summary */}
<div className="flex flex-col sm:flex-row sm:flex-wrap justify-center items-stretch sm:items-center gap-2 sm:gap-3 mb-16 text-sm max-w-md mx-auto sm:max-w-none">
<span
className="px-4 py-2 rounded-full"
style={{ background: 'rgba(25,37,64,0.5)', border: '1px solid rgba(255,255,255,0.06)', color: 'var(--kx-on-variant)' }}
</div>
</section>
{/* 2. Features */}
<section className="py-24 px-6 bg-white text-black border-b border-black/10">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-20">
<p className="font-mono text-[11px] tracking-widest uppercase text-black/50 mb-4">
Features
</p>
<h2
className="kx-display text-3xl md:text-5xl font-bold text-black"
style={{ wordBreak: 'keep-all', letterSpacing: '-0.02em' }}
>
<strong style={{ color: 'var(--kx-on-surface)' }}>39,000</strong>
</span>
<span
className="px-4 py-2 rounded-full"
style={{
background: 'rgba(156,72,234,0.15)',
border: '1px solid rgba(204,151,255,0.4)',
color: 'var(--kx-on-surface)',
boxShadow: '0 0 20px rgba(156,72,234,0.25)',
}}
>
🔥 <strong>99,000</strong> <span style={{ color: 'var(--kx-primary)' }}> </span>
</span>
<span
className="px-4 py-2 rounded-full"
style={{ background: 'rgba(25,37,64,0.5)', border: '1px solid rgba(255,255,255,0.06)', color: 'var(--kx-on-variant)' }}
>
<strong style={{ color: 'var(--kx-on-surface)' }}>149,000</strong>
</span>
3 .
</h2>
</div>
{/* Demo Showcase Card */}
<div
className="relative max-w-5xl mx-auto overflow-hidden"
style={{
borderRadius: '0.75rem 0.75rem 0.125rem 0.125rem',
border: '1px solid rgba(64,72,93,0.15)',
background: 'var(--kx-surface-low)',
boxShadow: '0 20px 80px 0 rgba(156,72,234,0.15)',
}}
>
<div className="aspect-video relative flex items-center justify-center group cursor-pointer">
{/* 배경 그라디언트 + 그리드 */}
<div
className="absolute inset-0"
style={{
background:
'linear-gradient(135deg, #0b0b2b 0%, #1a0840 50%, #061228 100%)',
opacity: 0.9,
}}
/>
<svg className="absolute inset-0 w-full h-full opacity-30" preserveAspectRatio="none" viewBox="0 0 1200 500">
<defs>
<linearGradient id="wg" x1="0%" x2="100%">
<stop offset="0%" stopColor="#cc97ff" />
<stop offset="100%" stopColor="#53ddfc" />
</linearGradient>
</defs>
{[...Array(40)].map((_, i) => (
<rect
key={i}
x={i * 30 + 10}
y={Number((250 - Math.abs(Math.sin(i * 0.6) * 120)).toFixed(3))}
width="10"
height={Number((Math.abs(Math.sin(i * 0.6) * 240) + 20).toFixed(3))}
fill="url(#wg)"
opacity={Number((0.4 + Math.sin(i * 0.3) * 0.3).toFixed(3))}
/>
))}
</svg>
<div
className="relative z-10 p-8 rounded-full border group-hover:scale-110 transition-transform"
style={{
background: 'rgba(204,151,255,0.15)',
borderColor: 'rgba(204,151,255,0.5)',
backdropFilter: 'blur(20px)',
}}
>
<svg className="w-12 h-12" fill="currentColor" viewBox="0 0 24 24" style={{ color: 'var(--kx-primary)' }}>
<path d="M8 5v14l11-7z" />
</svg>
</div>
<div
className="absolute bottom-0 left-0 right-0 p-6 text-left"
style={{
background: 'linear-gradient(to top, var(--kx-surface), transparent)',
}}
>
<span className="kx-label block mb-2">DEMO SHOWCASE</span>
<h3 className="kx-display text-xl md:text-2xl font-bold" style={{ color: 'var(--kx-on-surface)' }}>
K-POP MV
</h3>
</div>
</div>
<div className="space-y-20 lg:space-y-28">
{[
{
label: '01 · Prompt',
title: '프롬프트 한 줄이 곡이 됩니다.',
desc: '장르·무드·보컬 톤을 조합한 20+종 프롬프트 북. 복붙 한 번으로 Suno가 이해하는 구조로 변환됩니다.',
video: '/feature-prompt.mp4',
},
{
label: '02 · Visual',
title: '비트에 맞춰 영상이 붙습니다.',
desc: 'Midjourney·Runway·Luma 워크플로우. 가사와 비트를 싱크하는 9:16 쇼츠·16:9 풀버전을 바로 뽑아낼 수 있습니다.',
video: '/feature-visual.mp4',
},
{
label: '03 · Publish',
title: '업로드 직전까지 마무리됩니다.',
desc: '유튜브 제목·해시태그·설명란 SEO 템플릿. 복사-붙여넣기만으로 첫 쇼츠가 당일 채널에 올라갑니다.',
video: null,
},
].map((f, i) => {
const reverse = i % 2 === 1;
return (
<div
key={f.label}
className={`grid lg:grid-cols-2 gap-10 lg:gap-16 items-center ${reverse ? 'lg:[&>*:first-child]:order-2' : ''}`}
>
<div>
<p className="font-mono text-[11px] tracking-widest uppercase text-black/50 mb-4">
{f.label}
</p>
<h3
className="kx-display text-2xl md:text-4xl font-bold mb-5 text-black"
style={{ wordBreak: 'keep-all', letterSpacing: '-0.02em' }}
>
{f.title}
</h3>
<p className="text-base md:text-lg text-black/70 leading-relaxed max-w-lg">
{f.desc}
</p>
</div>
<div className="relative aspect-video rounded-2xl border border-black/15 bg-black/5 overflow-hidden">
{f.video ? (
<video
className="absolute inset-0 w-full h-full object-cover"
src={f.video}
autoPlay
loop
muted
playsInline
preload="auto"
aria-hidden
/>
) : (
<div className="absolute inset-0 flex items-center justify-center">
<span className="font-mono text-[11px] tracking-widest uppercase text-black/40">
Video Placeholder
</span>
</div>
)}
</div>
</div>
);
})}
</div>
</div>
</section>
{/* 2. Before / After */}
<section className="py-24 px-6" style={{ background: 'var(--kx-surface)' }}>
{/* 3. Before / After */}
<section className="py-24 px-6 bg-black text-white border-b border-white/10">
<div className="max-w-7xl mx-auto">
<h2 className="kx-display text-3xl md:text-4xl font-bold text-center mb-16" style={{ color: 'var(--kx-on-surface)' }}>
</h2>
<div className="text-center mb-14">
<p className="font-mono text-[11px] tracking-widest uppercase text-white/50 mb-4">
Efficiency
</p>
<h2
className="kx-display text-3xl md:text-5xl font-bold"
style={{ wordBreak: 'keep-all', letterSpacing: '-0.02em' }}
>
.
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="kx-glass kx-folder relative" style={{ borderTop: '2px solid rgba(255,110,132,0.4)' }}>
<span className="kx-label absolute top-4 right-4" style={{ color: '#d73357' }}>MANUAL PROCESS</span>
<h3 className="kx-display text-2xl font-bold mb-6 flex items-center gap-2" style={{ color: '#ff6e84' }}>
Before
</h3>
<div className="p-8 rounded-2xl border border-white/15 bg-white/[0.02]">
<p className="font-mono text-[11px] tracking-widest uppercase text-white/50 mb-3">
Manual Process
</p>
<h3 className="text-2xl font-bold mb-6 text-white/60">Before</h3>
<ul className="space-y-3">
{BEFORE.map((t) => (
<li key={t} className="flex items-start gap-3 text-sm" style={{ color: 'var(--kx-on-variant)' }}>
<span style={{ color: '#d73357' }}></span>
<li key={t} className="flex items-start gap-3 text-sm text-white/60">
<span className="text-white/40">·</span>
<span>{t}</span>
</li>
))}
</ul>
</div>
<div className="kx-glass kx-folder kx-glow relative" style={{ borderTop: '2px solid rgba(83,221,252,0.4)' }}>
<span className="kx-label absolute top-4 right-4">AI POWERED</span>
<h3 className="kx-display text-2xl font-bold mb-6 flex items-center gap-2" style={{ color: 'var(--kx-secondary)' }}>
After
</h3>
<div className="p-8 rounded-2xl border border-white bg-white text-black">
<p className="font-mono text-[11px] tracking-widest uppercase text-black/60 mb-3">
AI Powered
</p>
<h3 className="text-2xl font-bold mb-6">After</h3>
<ul className="space-y-3">
{AFTER.map((t) => (
<li key={t} className="flex items-start gap-3 text-sm" style={{ color: 'var(--kx-on-surface)' }}>
<span style={{ color: 'var(--kx-secondary)' }}></span>
<li key={t} className="flex items-start gap-3 text-sm text-black/80">
<span className="text-black/50">·</span>
<span>{t}</span>
</li>
))}
@@ -225,83 +228,94 @@ export default function Home() {
</div>
</section>
{/* 3. Other Products + CTA */}
<section className="py-24 px-6" style={{ background: 'var(--kx-surface-low)' }}>
<div className="max-w-7xl mx-auto">
<div className="text-center mb-12">
<span className="kx-label">OTHER PRODUCTS</span>
<h2 className="kx-display text-3xl md:text-4xl font-bold mt-2" style={{ color: 'var(--kx-on-surface)' }}>
{/* 4. Use Cases — Tweet Marquee */}
<section className="py-24 bg-white text-black border-b border-black/10 overflow-hidden">
<div className="px-6 max-w-6xl mx-auto">
<div className="text-center mb-14">
<p className="font-mono text-[11px] tracking-widest uppercase text-black/50 mb-4">
Use Cases
</p>
<h2
className="kx-display text-3xl md:text-5xl font-bold"
style={{ wordBreak: 'keep-all', letterSpacing: '-0.02em' }}
>
.
</h2>
<p className="mt-3 text-sm max-w-xl mx-auto" style={{ color: 'var(--kx-on-variant)' }}>
. ·CS도 .
</p>
</div>
<div className="grid md:grid-cols-2 gap-5 mb-16">
<Link
href="/services/blog"
className="p-8 transition-all hover:-translate-y-1"
style={{
background: 'var(--kx-surface-high)',
borderTop: '2px solid var(--kx-secondary)',
borderRadius: '0.75rem 0.75rem 0.125rem 0.125rem',
textDecoration: 'none',
}}
>
<span className="kx-label" style={{ color: 'var(--kx-secondary)' }}>DIGITAL PACK · 29,000</span>
<h3 className="kx-display text-2xl font-bold mt-3 mb-2" style={{ color: 'var(--kx-on-surface)' }}> </h3>
<p className="text-sm" style={{ color: 'var(--kx-on-variant)' }}>
+ 릿 PDF + . · .
</p>
</Link>
<Link
href="/saju"
className="p-8 transition-all hover:-translate-y-1"
style={{
background: 'var(--kx-surface-high)',
borderTop: '2px solid #40ceed',
borderRadius: '0.75rem 0.75rem 0.125rem 0.125rem',
textDecoration: 'none',
}}
>
<span className="kx-label" style={{ color: '#40ceed' }}>FREE TOOL</span>
<h3 className="kx-display text-2xl font-bold mt-3 mb-2" style={{ color: 'var(--kx-on-surface)' }}>AI </h3>
<p className="text-sm" style={{ color: 'var(--kx-on-variant)' }}>
+ Gemini . .
</p>
</Link>
</div>
</div>
{/* Final CTA — 음악 팩 단일 전환 */}
<div
className="text-center p-10 kx-glass"
style={{ border: '1px solid rgba(204,151,255,0.12)' }}
<div className="space-y-5 marquee-mask">
{[TWEETS_ROW_A, TWEETS_ROW_B].map((row, rowIdx) => (
<div key={rowIdx} className="marquee-viewport overflow-hidden">
<div className={`marquee-track ${rowIdx === 1 ? 'marquee-track-reverse' : ''}`}>
{[...row, ...row].map((t, i) => (
<article
key={`${rowIdx}-${i}`}
className="shrink-0 w-[320px] sm:w-[360px] p-5 rounded-2xl border border-black/15 bg-black/[0.02]"
>
<header className="flex items-center gap-3 mb-3">
<div className="w-10 h-10 rounded-full bg-black/5 border border-black/15 flex items-center justify-center font-bold text-black">
{t.name.charAt(0)}
</div>
<div className="min-w-0 flex-1">
<p className="font-bold text-black text-sm truncate">{t.name}</p>
<p className="font-mono text-[11px] text-black/50 truncate">@{t.handle}</p>
</div>
<span className="font-mono text-[10px] text-black/40 shrink-0">{t.time}</span>
</header>
<p
className="text-sm leading-relaxed text-black/80"
style={{ wordBreak: 'keep-all' }}
>
{t.body}
</p>
</article>
))}
</div>
</div>
))}
</div>
</section>
{/* 5. Final CTA — Full-width video */}
<section className="relative w-full min-h-[560px] flex items-center justify-center px-6 py-32 bg-black overflow-hidden">
<video
className="absolute inset-0 w-full h-full object-cover pointer-events-none"
src="/hero-bg.mp4"
autoPlay
loop
muted
playsInline
preload="auto"
aria-hidden
style={{ filter: 'blur(6px)', transform: 'scale(1.06)' }}
/>
<div className="absolute inset-0 bg-black/55 pointer-events-none" aria-hidden />
<div className="relative z-10 max-w-2xl mx-auto text-center text-white">
<p className="font-mono text-[11px] tracking-widest uppercase text-white/60 mb-5">
Start Today
</p>
<h2
className="kx-display text-3xl md:text-5xl font-bold mb-5 leading-[1.1]"
style={{ wordBreak: 'keep-all', letterSpacing: '-0.02em' }}
>
<span className="kx-label">START TODAY</span>
<h3 className="kx-display text-2xl md:text-3xl font-bold mt-2 mb-3" style={{ color: 'var(--kx-on-surface)' }}>
, AI
</h3>
<p className="text-sm mb-6 max-w-xl mx-auto" style={{ color: 'var(--kx-on-variant)' }}>
39,000 . 1 + 릿 + .
</p>
<Link
,
<br />
AI .
</h2>
<p className="text-base md:text-lg text-white/75 mb-10 max-w-lg mx-auto">
39,000 . 1 + 릿 + .
</p>
<div className="flex justify-center">
<GlassButton
href="/services/music#pricing"
onClick={() => trackCTAClick('home_v6_final_music')}
className="kx-btn-primary px-8 py-3.5 rounded-full text-sm inline-flex"
tint="rgba(255,255,255,0.18)"
className="text-base"
>
39,000
</Link>
<div className="mt-5 text-xs" style={{ color: 'var(--kx-on-variant)' }}>
<button
onClick={() => {
trackCTAClick('home_v6_final_contact');
setModalOpen(true);
}}
className="underline underline-offset-4 hover:text-white transition"
>
·
</button>
</div>
<span className="text-white">Try now</span>
</GlassButton>
</div>
</div>
</section>