feat(work): /work/website + 8 samples — 현 /services/website 컨텐츠 이동
- 메인 페이지 + layout - 8 sample 페이지: bakery, corporate, dashboard, game, interior, portfolio, reading, shopping - import @/ 절대 경로 변환 - 내부 Link href 새 URL로 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
31
app/work/website/layout.tsx
Normal file
31
app/work/website/layout.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '홈페이지·쇼핑몰·랜딩페이지 제작 | 반응형 웹 개발 외주',
|
||||
description:
|
||||
'소상공인·스타트업·기업 홈페이지, 쇼핑몰, 랜딩페이지 제작. 템플릿 없이 직접 개발. Next.js 기반 반응형 웹, SEO 기본 적용. 스타터 20만원~, 계약서 포함, 3개월 유지보수.',
|
||||
keywords: [
|
||||
'홈페이지 제작 외주',
|
||||
'쇼핑몰 제작 외주',
|
||||
'랜딩페이지 제작',
|
||||
'소상공인 홈페이지',
|
||||
'스타트업 웹사이트',
|
||||
'반응형 홈페이지 제작',
|
||||
'Next.js 개발 외주',
|
||||
'기업 홈페이지 제작',
|
||||
'웹사이트 제작 비용',
|
||||
'SEO 최적화 홈페이지',
|
||||
'홈페이지 개발 프리랜서',
|
||||
],
|
||||
openGraph: {
|
||||
title: '홈페이지·쇼핑몰 제작 | 쟁승메이드',
|
||||
description:
|
||||
'소상공인·스타트업 홈페이지 제작. 템플릿 없이 직접 개발, SEO 포함, 20만원~.',
|
||||
url: 'https://jaengseung-made.com/work/website',
|
||||
},
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
export default function WebsiteLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
962
app/work/website/page.tsx
Normal file
962
app/work/website/page.tsx
Normal file
@@ -0,0 +1,962 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import ContactModal from '@/app/components/ContactModal';
|
||||
import { trackCTAClick } from '@/lib/gtag';
|
||||
|
||||
const samples = [
|
||||
{
|
||||
type: 'corporate',
|
||||
title: '기업 홈페이지',
|
||||
subtitle: '테크솔루션㈜',
|
||||
desc: '"홈페이지가 없어서 B2B 영업 때마다 명함만 건넸다"는 고민을 해결한 기업 브랜드 사이트',
|
||||
gradient: 'linear-gradient(135deg, #0a192f 0%, #112240 50%, #1a3a6c 100%)',
|
||||
accent: '#4fc3f7',
|
||||
tags: ['기업', 'B2B', '신뢰'],
|
||||
icon: '🏢',
|
||||
},
|
||||
{
|
||||
type: 'bakery',
|
||||
title: '베이커리 홈페이지',
|
||||
subtitle: '르 쁘띠 포르',
|
||||
desc: '"인스타 팔로워는 많은데 실제 방문 예약이 없다"는 F&B 매장의 전환율 문제를 해결한 사이트',
|
||||
gradient: 'linear-gradient(135deg, #78350f 0%, #92400e 50%, #d97706 100%)',
|
||||
accent: '#fbbf24',
|
||||
tags: ['F&B', '로컬', '예약'],
|
||||
icon: '🥐',
|
||||
},
|
||||
{
|
||||
type: 'portfolio',
|
||||
title: '개인 포트폴리오',
|
||||
subtitle: 'Kim Jisu',
|
||||
desc: '"링크드인에 PDF 올리면 아무도 안 보더라"는 문제를 해결한 채용·수주 전환형 포트폴리오',
|
||||
gradient: 'linear-gradient(135deg, #000000 0%, #0d0d0d 50%, #001a00 100%)',
|
||||
accent: '#00ff88',
|
||||
tags: ['크리에이터', '디자이너', '수주'],
|
||||
icon: '✦',
|
||||
},
|
||||
{
|
||||
type: 'dashboard',
|
||||
title: '관리자 대시보드',
|
||||
subtitle: 'DataFlow SaaS',
|
||||
desc: '"엑셀로 수기 집계하다가 오류가 나서 야근"을 없애는 실시간 데이터 대시보드',
|
||||
gradient: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f2a3a 100%)',
|
||||
accent: '#38bdf8',
|
||||
tags: ['SaaS', '자동화', '관리'],
|
||||
icon: '📊',
|
||||
},
|
||||
{
|
||||
type: 'game',
|
||||
title: '게임 매칭 시스템',
|
||||
subtitle: 'NEXUS ARENA',
|
||||
desc: '"디스코드에서 수동으로 팀 짜다가 싸움 났다"는 커뮤니티의 매칭·랭킹 자동화 플랫폼',
|
||||
gradient: 'linear-gradient(135deg, #000000 0%, #0a0a1a 50%, #0d0029 100%)',
|
||||
accent: '#a855f7',
|
||||
tags: ['게임', '커뮤니티', '자동화'],
|
||||
icon: '⚡',
|
||||
},
|
||||
{
|
||||
type: 'interior',
|
||||
title: '인테리어 업체 소개',
|
||||
subtitle: 'AURUM Interior',
|
||||
desc: '"포트폴리오 사진을 카톡으로만 보내다가 고급 고객을 놓쳤다"는 문제를 해결한 브랜드 사이트',
|
||||
gradient: 'linear-gradient(135deg, #2C1810 0%, #4A3728 50%, #6B4E37 100%)',
|
||||
accent: '#D4A853',
|
||||
tags: ['인테리어', '포트폴리오', '고급'],
|
||||
icon: '◈',
|
||||
},
|
||||
{
|
||||
type: 'reading',
|
||||
title: '독서 기록 노트',
|
||||
subtitle: '나의 독서 기록',
|
||||
desc: '읽은 책과 감상을 아름답게 기록하는 나만의 독서 저널 — 이런 것도 만들 수 있습니다',
|
||||
gradient: 'linear-gradient(135deg, #0C0B09 0%, #1A1710 50%, #2A2218 100%)',
|
||||
accent: '#D4A853',
|
||||
tags: ['라이프', '독서', '기록'],
|
||||
icon: '◻',
|
||||
},
|
||||
{
|
||||
type: 'shopping',
|
||||
title: '개인 쇼핑몰',
|
||||
subtitle: 'MELLOW STUDIO',
|
||||
desc: '"카페24 기본 테마가 너무 흔해서 브랜드가 안 살아난다"는 고민을 해결한 커스텀 쇼핑몰',
|
||||
gradient: 'linear-gradient(135deg, #2A2018 0%, #4A3C2C 50%, #7A6A52 100%)',
|
||||
accent: '#C4A882',
|
||||
tags: ['쇼핑몰', '패션', '라이프'],
|
||||
icon: '◇',
|
||||
},
|
||||
];
|
||||
|
||||
const processSteps = [
|
||||
{ step: '01', title: '무료 상담', desc: '요구사항 파악 및 방향성 논의', icon: '💬' },
|
||||
{ step: '02', title: '기획', desc: '사이트맵 & 와이어프레임', icon: '📋' },
|
||||
{ step: '03', title: '디자인', desc: 'UI/UX 시안 제작', icon: '🎨' },
|
||||
{ step: '04', title: '개발', desc: '반응형 퍼블리싱 & 기능 구현', icon: '⚙️' },
|
||||
{ step: '05', title: '납품', desc: '검수 완료 후 도메인 배포', icon: '🚀' },
|
||||
];
|
||||
|
||||
const plans = [
|
||||
{
|
||||
name: '스타터',
|
||||
price: '20',
|
||||
unit: '만원~',
|
||||
color: '#38bdf8',
|
||||
features: ['5페이지 이내', '반응형 디자인', '기본 SEO 설정', '1개월 유지보수', '3~5영업일 납품'],
|
||||
note: '개인 블로그, 소규모 소개 사이트',
|
||||
productId: 'website_starter',
|
||||
},
|
||||
{
|
||||
name: '비즈니스',
|
||||
price: '100',
|
||||
unit: '만원~',
|
||||
color: '#818cf8',
|
||||
featured: true,
|
||||
features: ['10페이지 이내', '반응형 디자인', '관리자 페이지', 'SEO 최적화', '3개월 유지보수', '1~2주 납품'],
|
||||
note: '기업 사이트, 브랜드 페이지',
|
||||
productId: 'website_business',
|
||||
},
|
||||
{
|
||||
name: '프리미엄',
|
||||
price: '200',
|
||||
unit: '만원~',
|
||||
color: '#f472b6',
|
||||
features: ['페이지 수 무제한', '맞춤 디자인', '결제/회원 시스템', 'DB 연동', '6개월 유지보수', '일정 협의'],
|
||||
note: '쇼핑몰, SaaS, 복합 시스템',
|
||||
productId: 'website_premium',
|
||||
},
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
q: '제작 기간은 얼마나 걸리나요?',
|
||||
a: '규모에 따라 다르지만, 스타터는 3~5영업일, 비즈니스는 1~2주, 프리미엄은 협의 후 결정합니다. 빠른 납품이 필요한 경우 별도 상담해 주세요.',
|
||||
},
|
||||
{
|
||||
q: '수정은 몇 번까지 가능한가요?',
|
||||
a: '기획 확정 후 디자인 시안 수정은 2회, 개발 완료 후 기능 수정은 유지보수 기간 내 자유롭게 가능합니다. 추가 기능 개발은 별도 견적으로 진행합니다.',
|
||||
},
|
||||
{
|
||||
q: '도메인과 호스팅도 도와주시나요?',
|
||||
a: '네, 도메인 구매부터 서버 세팅, 배포까지 전 과정을 도와드립니다. Vercel, AWS, 카페24 등 원하시는 플랫폼에 맞춰 배포해 드립니다.',
|
||||
},
|
||||
{
|
||||
q: '앱(모바일 앱)이나 쇼핑몰도 개발 가능한가요?',
|
||||
a: '네. iOS/Android 앱(React Native), 모바일 웹앱, 결제 연동 쇼핑몰, 회원/관리자 시스템 등 모두 개발 가능합니다. 프리미엄 플랜 이상이거나 규모에 따라 별도 견적으로 진행됩니다. 먼저 어떤 기능이 필요한지 상담해 주세요.',
|
||||
},
|
||||
{
|
||||
q: '계약금은 어떻게 되나요? 중간에 취소하면 어떻게 되나요?',
|
||||
a: '계약서 체결 후 착수금 30%를 먼저 입금받고 개발을 시작합니다. 납품 전 취소 시 완성 비율에 따라 정산하며, 착수 전 전액 환불됩니다. 모든 조건은 계약서에 명시됩니다.',
|
||||
},
|
||||
];
|
||||
|
||||
function useReveal() {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const scroller = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement;
|
||||
const obs = new IntersectionObserver(
|
||||
([entry]) => { if (entry.isIntersecting) { el.classList.add('ws-visible'); obs.disconnect(); } },
|
||||
{ threshold: 0.1, root: scroller === document.documentElement ? null : scroller }
|
||||
);
|
||||
obs.observe(el);
|
||||
return () => obs.disconnect();
|
||||
}, []);
|
||||
return ref;
|
||||
}
|
||||
|
||||
function SampleMiniPreview({ type }: { type: string }) {
|
||||
const W = 700, H = 350;
|
||||
const inner = (content: React.ReactNode, bg: string) => (
|
||||
<div style={{ height: 175, overflow: 'hidden', position: 'relative', background: bg, borderRadius: '20px 20px 0 0' }}>
|
||||
<div style={{ width: W, height: H, transform: 'scale(0.5)', transformOrigin: 'top left', position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }}>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
switch (type) {
|
||||
case 'corporate':
|
||||
return inner(
|
||||
<div style={{ background: '#fff', width: '100%', height: '100%', fontFamily: 'system-ui' }}>
|
||||
<div style={{ height: 50, background: '#0a192f', display: 'flex', alignItems: 'center', padding: '0 28px', justifyContent: 'space-between' }}>
|
||||
<div style={{ fontSize: 15, fontWeight: 900, color: '#4fc3f7', letterSpacing: '0.1em' }}>TECHSOLUTION</div>
|
||||
<div style={{ display: 'flex', gap: 22 }}>
|
||||
{['서비스','솔루션','고객사','연락처'].map(t => <span key={t} style={{ fontSize: 11, color: '#475569' }}>{t}</span>)}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, background: '#1d4ed8', color: '#fff', padding: '7px 18px', borderRadius: 4, fontWeight: 700 }}>상담 신청</div>
|
||||
</div>
|
||||
<div style={{ padding: '36px 32px', backgroundImage: 'linear-gradient(rgba(29,78,216,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(29,78,216,0.04) 1px, transparent 1px)', backgroundSize: '24px 24px' }}>
|
||||
<div style={{ fontSize: 10, color: '#1d4ed8', letterSpacing: '0.18em', marginBottom: 12, fontWeight: 700 }}>ENTERPRISE IT SOLUTIONS</div>
|
||||
<div style={{ fontSize: 36, fontWeight: 900, color: '#0a192f', lineHeight: 1.1, marginBottom: 14 }}>기업 IT 인프라,<br/>처음부터 끝까지</div>
|
||||
<div style={{ fontSize: 12, color: '#64748b', marginBottom: 22, lineHeight: 1.6 }}>15년 경험의 엔터프라이즈 IT 전문 기업.<br/>클라우드·보안·DX 통합 솔루션을 제공합니다.</div>
|
||||
<div style={{ display: 'flex', gap: 10, marginBottom: 28 }}>
|
||||
<div style={{ background: '#1d4ed8', color: '#fff', fontSize: 12, padding: '10px 22px', borderRadius: 6, fontWeight: 700 }}>무료 상담 신청</div>
|
||||
<div style={{ border: '1px solid #cbd5e1', color: '#475569', fontSize: 12, padding: '10px 22px', borderRadius: 6 }}>서비스 소개서</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 36 }}>
|
||||
{[['15+','년 업력'],['340+','완료 프로젝트'],['180+','기업 고객'],['99.9%','가동률']].map(([n,l]) => (
|
||||
<div key={l}><div style={{ fontSize: 22, fontWeight: 800, color: '#0a192f', letterSpacing: '-0.02em' }}>{n}</div><div style={{ fontSize: 9, color: '#94a3b8', marginTop: 3 }}>{l}</div></div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>, '#fff'
|
||||
);
|
||||
|
||||
case 'bakery':
|
||||
return inner(
|
||||
<div style={{ background: '#fffbf5', width: '100%', height: '100%', fontFamily: 'Georgia, serif' }}>
|
||||
<div style={{ height: 52, background: 'rgba(255,251,245,0.96)', borderBottom: '1px solid #fde8c8', display: 'flex', alignItems: 'center', padding: '0 28px', justifyContent: 'space-between' }}>
|
||||
<div><div style={{ fontSize: 18, fontStyle: 'italic', color: '#78350f', fontWeight: 700 }}>Le Petit Fort</div><div style={{ fontSize: 8, color: '#b45309', letterSpacing: '0.2em' }}>ARTISAN BOULANGERIE</div></div>
|
||||
<div style={{ display: 'flex', gap: 20 }}>
|
||||
{['메뉴','스토리','예약'].map(t => <span key={t} style={{ fontSize: 11, color: '#92400e', fontFamily: 'system-ui' }}>{t}</span>)}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, background: '#b45309', color: '#fff', padding: '7px 18px', borderRadius: 100, fontFamily: 'system-ui', fontWeight: 700 }}>방문 예약</div>
|
||||
</div>
|
||||
<div style={{ padding: '28px 32px 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 28, alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: 10, color: '#b45309', letterSpacing: '0.2em', marginBottom: 12, fontFamily: 'system-ui', fontWeight: 600 }}>Since 2018 · Paris Recipe</div>
|
||||
<div style={{ fontSize: 38, fontWeight: 700, color: '#1c1008', lineHeight: 1.05, marginBottom: 14 }}>매일 아침<br/><em>구워내는</em><br/>정직한 빵</div>
|
||||
<div style={{ fontSize: 11, color: '#92400e', marginBottom: 18, lineHeight: 1.7, fontFamily: 'system-ui' }}>프랑스산 에슈레 버터와 천연 발효종만으로<br/>만드는 정직한 아르티장 베이커리.</div>
|
||||
<div style={{ display: 'flex', gap: 10 }}>
|
||||
<div style={{ background: '#b45309', color: '#fff', fontSize: 11, padding: '9px 20px', borderRadius: 100, fontFamily: 'system-ui', fontWeight: 700 }}>오늘의 빵 보기</div>
|
||||
<div style={{ border: '1px solid #d97706', color: '#92400e', fontSize: 11, padding: '9px 20px', borderRadius: 100, fontFamily: 'system-ui' }}>매장 안내</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
|
||||
{[{n:'버터 크루아상',p:'3,200',c:'#d97706'},{n:'소금빵',p:'2,800',c:'#b45309'},{n:'딸기 케이크',p:'7,500',c:'#be185d'},{n:'캄파뉴',p:'8,900',c:'#065f46'}].map(item => (
|
||||
<div key={item.n} style={{ background: '#fff8f0', borderRadius: 10, padding: 10, border: '1px solid #fde8c8' }}>
|
||||
<div style={{ height: 38, background: 'linear-gradient(135deg, #fde68a, #fbbf24)', borderRadius: 6, marginBottom: 6 }} />
|
||||
<div style={{ fontSize: 9, color: '#1c1008', fontFamily: 'system-ui', fontWeight: 600 }}>{item.n}</div>
|
||||
<div style={{ fontSize: 10, color: item.c, fontFamily: 'system-ui', fontWeight: 700 }}>₩{item.p}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>, '#fffbf5'
|
||||
);
|
||||
|
||||
case 'portfolio':
|
||||
return inner(
|
||||
<div style={{ background: '#000', width: '100%', height: '100%' }}>
|
||||
<div style={{ position: 'absolute', top: -40, left: '25%', width: 320, height: 320, background: 'radial-gradient(circle, rgba(0,255,136,0.06) 0%, transparent 70%)', pointerEvents: 'none' }} />
|
||||
<div style={{ height: 50, background: 'rgba(0,0,0,0.95)', borderBottom: '1px solid rgba(0,255,136,0.1)', display: 'flex', alignItems: 'center', padding: '0 32px', justifyContent: 'space-between', position: 'relative', zIndex: 2 }}>
|
||||
<div style={{ fontFamily: 'monospace', fontSize: 16, fontWeight: 700, color: '#00ff88' }}>KJ<span style={{ color: 'rgba(0,255,136,0.3)' }}>_</span></div>
|
||||
<div style={{ display: 'flex', gap: 24 }}>
|
||||
{['About','Work','Skills','Contact'].map(t => <span key={t} style={{ fontFamily: 'system-ui', fontSize: 10, color: '#374151', letterSpacing: '0.08em', textTransform: 'uppercase' }}>{t}</span>)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#00ff88' }} />
|
||||
<span style={{ fontFamily: 'monospace', fontSize: 9, color: '#00ff88' }}>Available</span>
|
||||
<div style={{ marginLeft: 8, border: '1px solid #00ff88', color: '#00ff88', fontSize: 9, padding: '5px 12px', borderRadius: 3, fontFamily: 'monospace', fontWeight: 700 }}>HIRE ME</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ position: 'absolute', inset: 0, top: 50, backgroundImage: 'linear-gradient(rgba(0,255,136,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(0,255,136,0.04) 1px, transparent 1px)', backgroundSize: '32px 32px', pointerEvents: 'none' }} />
|
||||
<div style={{ padding: '38px 32px', position: 'relative', zIndex: 2, display: 'grid', gridTemplateColumns: '1fr auto', gap: 32, alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'monospace', fontSize: 10, color: '#00ff88', letterSpacing: '0.15em', marginBottom: 16, border: '1px solid rgba(0,255,136,0.2)', display: 'inline-block', padding: '3px 10px' }}>FULL-STACK DEVELOPER</div>
|
||||
<div style={{ fontSize: 56, fontWeight: 900, color: '#fff', lineHeight: 0.9, letterSpacing: '-0.03em', marginBottom: 18, fontFamily: 'system-ui' }}>Kim<br/><span style={{ color: '#00ff88' }}>Jisu</span></div>
|
||||
<div style={{ fontSize: 11, color: '#4b5563', lineHeight: 1.7, marginBottom: 22, fontFamily: 'system-ui' }}>React · Next.js · TypeScript · Node.js<br/>디자인과 코드의 경계를 탐험합니다.</div>
|
||||
<div style={{ display: 'flex', gap: 10 }}>
|
||||
<div style={{ background: '#00ff88', color: '#000', fontSize: 11, padding: '9px 22px', borderRadius: 4, fontWeight: 800, fontFamily: 'monospace' }}>VIEW WORK</div>
|
||||
<div style={{ border: '1px solid rgba(0,255,136,0.3)', color: '#00ff88', fontSize: 11, padding: '9px 22px', borderRadius: 4, fontFamily: 'monospace' }}>CONTACT</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ width: 130, height: 160, background: 'linear-gradient(135deg, #001a0d, #003322)', border: '1px solid rgba(0,255,136,0.2)', borderRadius: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
|
||||
<div style={{ width: 80, height: 80, borderRadius: '50%', border: '2px solid rgba(0,255,136,0.3)', background: 'rgba(0,255,136,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<span style={{ fontSize: 26, color: '#00ff88', fontFamily: 'monospace', fontWeight: 700 }}>KJ</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>, '#000'
|
||||
);
|
||||
|
||||
case 'dashboard':
|
||||
return inner(
|
||||
<div style={{ background: '#0f172a', width: '100%', height: '100%', display: 'flex', fontFamily: 'system-ui' }}>
|
||||
<div style={{ width: 140, background: '#020617', borderRight: '1px solid rgba(255,255,255,0.05)', padding: '20px 14px' }}>
|
||||
<div style={{ fontSize: 14, fontWeight: 800, color: '#38bdf8', marginBottom: 24, letterSpacing: '-0.02em' }}>DataFlow</div>
|
||||
{['대시보드','분석','리포트','사용자','설정'].map((item, i) => (
|
||||
<div key={item} style={{ fontSize: 10, color: i === 0 ? '#38bdf8' : '#475569', padding: '8px 10px', borderRadius: 6, marginBottom: 4, background: i === 0 ? 'rgba(56,189,248,0.1)' : 'transparent' }}>{item}</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ flex: 1, padding: '20px 22px' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: '#e2e8f0' }}>실시간 현황</div>
|
||||
<div style={{ fontSize: 10, color: '#475569', background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.06)', padding: '4px 12px', borderRadius: 6 }}>이번 달</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10, marginBottom: 14 }}>
|
||||
{[{l:'총 매출',v:'₩48.2M',c:'#38bdf8',u:true},{l:'신규 사용자',v:'1,247',c:'#34d399',u:true},{l:'전환율',v:'12.4%',c:'#a78bfa',u:false}].map(s => (
|
||||
<div key={s.l} style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.06)', borderRadius: 8, padding: '12px 14px' }}>
|
||||
<div style={{ fontSize: 8, color: '#475569', marginBottom: 6 }}>{s.l}</div>
|
||||
<div style={{ fontSize: 20, fontWeight: 800, color: s.c, letterSpacing: '-0.02em' }}>{s.v}</div>
|
||||
<div style={{ fontSize: 8, color: s.u ? '#34d399' : '#f87171', marginTop: 4 }}>{s.u ? '↑ +8.3%' : '↓ -1.2%'}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid rgba(255,255,255,0.05)', borderRadius: 8, padding: 14, height: 110 }}>
|
||||
<div style={{ fontSize: 9, color: '#475569', marginBottom: 10 }}>월간 매출 추이</div>
|
||||
<div style={{ display: 'flex', alignItems: 'flex-end', gap: 6, height: 72 }}>
|
||||
{[40,55,35,65,80,60,90,75,85,95,70,100].map((h, i) => (
|
||||
<div key={i} style={{ flex: 1, height: `${h}%`, background: i === 11 ? '#38bdf8' : 'rgba(56,189,248,0.22)', borderRadius: '2px 2px 0 0' }} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>, '#0f172a'
|
||||
);
|
||||
|
||||
case 'game':
|
||||
return inner(
|
||||
<div style={{ background: '#000', width: '100%', height: '100%' }}>
|
||||
<div style={{ position: 'absolute', top: -60, left: '30%', width: 340, height: 340, background: 'radial-gradient(circle, rgba(168,85,247,0.14) 0%, transparent 70%)', pointerEvents: 'none' }} />
|
||||
<div style={{ position: 'absolute', top: -20, right: '10%', width: 200, height: 200, background: 'radial-gradient(circle, rgba(6,182,212,0.1) 0%, transparent 70%)', pointerEvents: 'none' }} />
|
||||
<div style={{ height: 50, background: 'rgba(0,0,0,0.9)', borderBottom: '1px solid rgba(6,182,212,0.2)', display: 'flex', alignItems: 'center', padding: '0 28px', justifyContent: 'space-between', position: 'relative', zIndex: 2 }}>
|
||||
<div style={{ fontFamily: 'monospace', fontSize: 15, fontWeight: 900, color: '#06b6d4', letterSpacing: '0.15em' }}>NEXUS<span style={{ color: '#a855f7' }}>ARENA</span></div>
|
||||
<div style={{ display: 'flex', gap: 20 }}>
|
||||
{['랭킹','매칭','챔피언','스토어'].map(t => <span key={t} style={{ fontFamily: 'system-ui', fontSize: 10, color: '#374151', letterSpacing: '0.08em' }}>{t}</span>)}
|
||||
</div>
|
||||
<div style={{ background: 'linear-gradient(90deg, #06b6d4, #a855f7)', color: '#000', fontSize: 10, padding: '7px 18px', borderRadius: 3, fontWeight: 800, fontFamily: 'monospace' }}>PLAY NOW</div>
|
||||
</div>
|
||||
<div style={{ padding: '32px 32px', position: 'relative', zIndex: 2, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'monospace', fontSize: 9, color: '#06b6d4', letterSpacing: '0.2em', marginBottom: 14 }}>SEASON 7 · COMPETITIVE</div>
|
||||
<div style={{ fontSize: 50, fontWeight: 900, color: '#fff', lineHeight: 0.88, letterSpacing: '-0.03em', marginBottom: 18, fontFamily: 'system-ui' }}>NEXUS<br/><span style={{ background: 'linear-gradient(90deg, #06b6d4, #a855f7)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>ARENA</span></div>
|
||||
<div style={{ fontSize: 10, color: '#4b5563', lineHeight: 1.65, marginBottom: 22, fontFamily: 'system-ui' }}>실시간 매칭 · 랭크 시스템 · 글로벌 토너먼트<br/>지금 바로 전장에 참전하세요.</div>
|
||||
<div style={{ display: 'flex', gap: 10 }}>
|
||||
<div style={{ background: 'linear-gradient(90deg, #06b6d4, #a855f7)', color: '#fff', fontSize: 11, padding: '10px 22px', borderRadius: 4, fontWeight: 800, fontFamily: 'monospace' }}>PLAY NOW</div>
|
||||
<div style={{ border: '1px solid rgba(6,182,212,0.4)', color: '#06b6d4', fontSize: 11, padding: '10px 22px', borderRadius: 4, fontFamily: 'monospace' }}>랭킹 보기</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
|
||||
{[{name:'VIPER',role:'Assassin',c:'#06b6d4'},{name:'NOVA',role:'Mage',c:'#a855f7'},{name:'IRON',role:'Tank',c:'#94a3b8'},{name:'KIRA',role:'Support',c:'#ec4899'}].map(ch => (
|
||||
<div key={ch.name} style={{ background: 'rgba(255,255,255,0.03)', border: `1px solid ${ch.c}30`, borderRadius: 8, padding: 10 }}>
|
||||
<div style={{ height: 34, background: `linear-gradient(135deg, ${ch.c}20, ${ch.c}05)`, borderRadius: 4, marginBottom: 6, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<div style={{ width: 24, height: 24, borderRadius: '50%', background: `${ch.c}30`, border: `1px solid ${ch.c}60` }} />
|
||||
</div>
|
||||
<div style={{ fontFamily: 'monospace', fontSize: 9, color: ch.c, fontWeight: 700 }}>{ch.name}</div>
|
||||
<div style={{ fontSize: 8, color: '#374151', fontFamily: 'system-ui' }}>{ch.role}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>, '#000'
|
||||
);
|
||||
|
||||
case 'interior':
|
||||
return inner(
|
||||
<div style={{ background: '#faf8f4', width: '100%', height: '100%' }}>
|
||||
<div style={{ height: 50, background: '#2C1810', display: 'flex', alignItems: 'center', padding: '0 28px', justifyContent: 'space-between' }}>
|
||||
<div><div style={{ fontFamily: 'Georgia, serif', fontSize: 14, color: '#D4A853', fontWeight: 700, letterSpacing: '0.12em' }}>AURUM</div><div style={{ fontSize: 7, color: '#6B4E37', letterSpacing: '0.25em' }}>INTERIOR DESIGN</div></div>
|
||||
<div style={{ display: 'flex', gap: 18 }}>
|
||||
{['포트폴리오','서비스','견적 문의'].map(t => <span key={t} style={{ fontSize: 9, color: '#9a8070', fontFamily: 'system-ui' }}>{t}</span>)}
|
||||
</div>
|
||||
<div style={{ border: '1px solid #D4A853', color: '#D4A853', fontSize: 9, padding: '6px 14px', fontFamily: 'system-ui', letterSpacing: '0.08em' }}>CONTACT</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', height: 300 }}>
|
||||
<div style={{ padding: '32px 28px', display: 'flex', flexDirection: 'column', justifyContent: 'center', background: '#2C1810' }}>
|
||||
<div style={{ fontSize: 9, color: '#D4A853', letterSpacing: '0.25em', marginBottom: 14, fontFamily: 'system-ui', textTransform: 'uppercase' }}>Premium Interior Design</div>
|
||||
<div style={{ fontFamily: 'Georgia, serif', fontSize: 34, color: '#faf8f4', lineHeight: 1.1, marginBottom: 18 }}>공간이<br/><em>이야기가</em><br/>되는 순간</div>
|
||||
<div style={{ fontSize: 10, color: '#9a8070', lineHeight: 1.7, fontFamily: 'system-ui', marginBottom: 22 }}>20년 경험의 인테리어 전문가가<br/>당신만의 공간을 완성합니다.</div>
|
||||
<div style={{ display: 'inline-flex' }}><div style={{ background: '#D4A853', color: '#2C1810', fontSize: 10, padding: '10px 22px', fontFamily: 'system-ui', fontWeight: 700 }}>포트폴리오 보기</div></div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gridTemplateRows: '1fr 1fr', gap: 3 }}>
|
||||
{['linear-gradient(135deg, #c9b99a, #a8927a)','linear-gradient(135deg, #8B7355, #6B5A47)','linear-gradient(135deg, #D4C5A9, #B8A88A)','linear-gradient(135deg, #7C6555, #5C4A3A)'].map((bg, i) => (
|
||||
<div key={i} style={{ background: bg }}><div style={{ width: '100%', height: '100%', background: 'rgba(44,24,16,0.08)' }} /></div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>, '#faf8f4'
|
||||
);
|
||||
|
||||
case 'reading':
|
||||
return inner(
|
||||
<div style={{ background: '#0C0B09', width: '100%', height: '100%' }}>
|
||||
<div style={{ height: 46, background: '#0C0B09', borderBottom: '1px solid rgba(212,168,83,0.1)', display: 'flex', alignItems: 'center', padding: '0 28px', justifyContent: 'space-between' }}>
|
||||
<div style={{ fontFamily: 'Georgia, serif', fontSize: 14, fontStyle: 'italic', color: '#D4A853', fontWeight: 600 }}>나의 독서 기록</div>
|
||||
<div style={{ display: 'flex', gap: 18 }}>
|
||||
{['서재','월별 기록','통계'].map(t => <span key={t} style={{ fontSize: 9, color: '#5c5040', fontFamily: 'system-ui' }}>{t}</span>)}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ padding: '32px 32px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 28, alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: 10, color: '#D4A853', letterSpacing: '0.2em', marginBottom: 14, fontFamily: 'system-ui', textTransform: 'uppercase' }}>My Reading Journal</div>
|
||||
<div style={{ fontFamily: 'Georgia, serif', fontSize: 40, color: '#faf8f4', lineHeight: 1.05, marginBottom: 16 }}>읽은 책들이<br/><em style={{ color: '#D4A853' }}>별처럼</em><br/>빛나는 공간</div>
|
||||
<div style={{ fontSize: 10, color: '#5c5040', lineHeight: 1.7, fontFamily: 'system-ui', marginBottom: 22 }}>독서 기록을 아름답게.<br/>감상과 인용구를 나만의 서재에 담아보세요.</div>
|
||||
<div style={{ display: 'inline-flex', background: '#D4A853', color: '#0C0B09', fontSize: 10, padding: '9px 22px', fontFamily: 'system-ui', fontWeight: 700 }}>기록 시작하기</div>
|
||||
<div style={{ display: 'flex', gap: 24, marginTop: 22 }}>
|
||||
{[['47','완독'],['1,240','페이지'],['12','이번 달']].map(([n,l]) => (
|
||||
<div key={l}><div style={{ fontSize: 20, fontWeight: 800, color: '#D4A853', fontFamily: 'Georgia, serif' }}>{n}</div><div style={{ fontSize: 8, color: '#5c5040', fontFamily: 'system-ui', marginTop: 2 }}>{l}</div></div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8, alignItems: 'flex-end', justifyContent: 'center' }}>
|
||||
{[{h:130,bg:'linear-gradient(180deg,#1e3a5f,#0a1628)',sp:'#2563eb'},{h:152,bg:'linear-gradient(180deg,#2C1810,#1a0e0a)',sp:'#D4A853'},{h:118,bg:'linear-gradient(180deg,#1a1a1a,#0d0d0d)',sp:'#6b7280'},{h:142,bg:'linear-gradient(180deg,#1e1b4b,#0f0d2e)',sp:'#7c3aed'},{h:120,bg:'linear-gradient(180deg,#064e3b,#022c22)',sp:'#10b981'}].map((b, i) => (
|
||||
<div key={i} style={{ width: 38, height: b.h, background: b.bg, borderRadius: '3px 3px 0 0', borderLeft: `3px solid ${b.sp}40`, boxShadow: '2px 0 8px rgba(0,0,0,0.4)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<div style={{ width: 1, height: '80%', background: `${b.sp}30` }} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>, '#0C0B09'
|
||||
);
|
||||
|
||||
case 'shopping':
|
||||
return inner(
|
||||
<div style={{ background: '#F4F2EF', width: '100%', height: '100%' }}>
|
||||
<div style={{ height: 52, background: '#F4F2EF', borderBottom: '1px solid #E0DDD8', display: 'flex', alignItems: 'center', padding: '0 28px', justifyContent: 'space-between' }}>
|
||||
<div style={{ fontSize: 16, fontWeight: 900, color: '#0C0B09', letterSpacing: '0.2em', textTransform: 'uppercase', fontFamily: 'system-ui' }}>MELLOW<span style={{ fontWeight: 300 }}> STUDIO</span></div>
|
||||
<div style={{ display: 'flex', gap: 20 }}>
|
||||
{['NEW','OUTER','TOP','BOTTOM'].map(t => <span key={t} style={{ fontSize: 9, color: '#7C7870', fontFamily: 'system-ui', letterSpacing: '0.1em' }}>{t}</span>)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 14, fontSize: 12, color: '#0C0B09', fontFamily: 'system-ui' }}><span>🔍</span><span>🛍 2</span></div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', height: 298 }}>
|
||||
<div style={{ background: 'linear-gradient(135deg, #2A2018, #4A3C2C)', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ width: 120, height: 200, background: 'linear-gradient(180deg, #c8b89a, #9a8a72)', borderRadius: 4, boxShadow: '16px 16px 40px rgba(0,0,0,0.35)' }} />
|
||||
<div style={{ position: 'absolute', bottom: 16, left: 16 }}>
|
||||
<div style={{ fontSize: 9, color: 'rgba(244,242,239,0.5)', letterSpacing: '0.2em', fontFamily: 'system-ui' }}>NEW ARRIVAL</div>
|
||||
<div style={{ fontSize: 17, fontWeight: 900, color: '#F4F2EF', fontFamily: 'system-ui', letterSpacing: '-0.01em' }}>SS 2025</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ padding: 20, display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: 9, color: '#7C7870', letterSpacing: '0.2em', marginBottom: 10, fontFamily: 'system-ui' }}>COLLECTION</div>
|
||||
<div style={{ fontSize: 30, fontWeight: 900, color: '#0C0B09', lineHeight: 1.05, fontFamily: 'system-ui', letterSpacing: '-0.02em', marginBottom: 12 }}>Quiet<br/>Luxury</div>
|
||||
<div style={{ fontSize: 10, color: '#7C7870', lineHeight: 1.65, fontFamily: 'system-ui', marginBottom: 18 }}>소음 없이 존재하는 옷.<br/>절제된 아름다움을 입으세요.</div>
|
||||
<div style={{ display: 'inline-flex', background: '#0C0B09', color: '#F4F2EF', fontSize: 9, padding: '9px 20px', letterSpacing: '0.15em', fontFamily: 'system-ui', fontWeight: 700 }}>SHOP NOW</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 }}>
|
||||
{[['#c8b89a','₩328K'],['#8a7860','₩498K'],['#d4c5a9','₩218K']].map(([bg, p], i) => (
|
||||
<div key={i} style={{ borderRadius: 3, overflow: 'hidden' }}>
|
||||
<div style={{ height: 52, background: `linear-gradient(160deg, ${bg}, rgba(0,0,0,0.08))` }} />
|
||||
<div style={{ fontSize: 8, color: '#7C7870', fontFamily: 'system-ui', paddingTop: 4 }}>{p}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>, '#F4F2EF'
|
||||
);
|
||||
|
||||
default:
|
||||
return <div style={{ height: 175, background: '#0a0f1e', borderRadius: '20px 20px 0 0' }} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default function WebsiteServicePage() {
|
||||
const [openFaq, setOpenFaq] = useState<number | null>(null);
|
||||
const [showTop, setShowTop] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalService, setModalService] = useState('홈페이지 제작');
|
||||
|
||||
const openModal = (service: string) => {
|
||||
trackCTAClick(service, '/work/website');
|
||||
setModalService(service);
|
||||
setModalOpen(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const scroller = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement;
|
||||
const onScroll = () => setShowTop(scroller.scrollTop > 400);
|
||||
scroller.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => scroller.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
|
||||
const samplesRef = useReveal();
|
||||
const processRef = useReveal();
|
||||
const pricingRef = useReveal();
|
||||
const faqRef = useReveal();
|
||||
const ctaRef = useReveal();
|
||||
|
||||
return (
|
||||
<div style={{ background: '#030712', minHeight: '100vh', color: 'white', fontFamily: "'Pretendard', 'Apple SD Gothic Neo', system-ui, sans-serif" }}>
|
||||
<ContactModal
|
||||
isOpen={modalOpen}
|
||||
onClose={() => setModalOpen(false)}
|
||||
service={modalService}
|
||||
checklist={[
|
||||
'원하시는 홈페이지 종류 (소개/쇼핑몰/SaaS 등)',
|
||||
'참고하고 싶은 사이트 URL (있으면)',
|
||||
'필요한 주요 페이지 및 기능',
|
||||
'희망 납품 일정 및 예산 범위',
|
||||
'디자인 선호 스타일 (모던/감성/심플 등)',
|
||||
]}
|
||||
accentColor="text-indigo-400"
|
||||
headerFrom="#0a0a1a"
|
||||
headerTo="#1e1b4b"
|
||||
/>
|
||||
<style dangerouslySetInnerHTML={{ __html: `
|
||||
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.min.css');
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
word-break { word-break: keep-all; }
|
||||
|
||||
/* scroll reveal */
|
||||
.ws-reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(32px);
|
||||
filter: blur(3px);
|
||||
transition: opacity 0.7s cubic-bezier(0.16,1,0.3,1),
|
||||
transform 0.7s cubic-bezier(0.16,1,0.3,1),
|
||||
filter 0.7s cubic-bezier(0.16,1,0.3,1);
|
||||
}
|
||||
.ws-reveal.ws-visible { opacity: 1; transform: translateY(0); filter: blur(0); }
|
||||
.ws-reveal > *:nth-child(1) { transition-delay: 0ms; }
|
||||
.ws-reveal > *:nth-child(2) { transition-delay: 80ms; }
|
||||
.ws-reveal > *:nth-child(3) { transition-delay: 160ms; }
|
||||
.ws-reveal > *:nth-child(4) { transition-delay: 240ms; }
|
||||
.ws-reveal > *:nth-child(5) { transition-delay: 320ms; }
|
||||
|
||||
@keyframes ws-fadeUp {
|
||||
from { opacity: 0; transform: translateY(28px); filter: blur(4px); }
|
||||
to { opacity: 1; transform: translateY(0); filter: blur(0); }
|
||||
}
|
||||
@keyframes ws-gridScroll {
|
||||
from { background-position: 0 0; }
|
||||
to { background-position: 48px 48px; }
|
||||
}
|
||||
@keyframes ws-glow {
|
||||
0%,100% { opacity: 0.5; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
.ws-sample-card {
|
||||
border-radius: 20px; overflow: hidden;
|
||||
border: 1px solid rgba(255,255,255,0.07);
|
||||
background: #0a0f1e; cursor: pointer;
|
||||
transition: transform 0.45s cubic-bezier(0.16,1,0.3,1),
|
||||
box-shadow 0.45s cubic-bezier(0.16,1,0.3,1),
|
||||
border-color 0.3s;
|
||||
}
|
||||
.ws-sample-card:hover {
|
||||
transform: translateY(-6px);
|
||||
box-shadow: 0 24px 64px rgba(0,0,0,0.5);
|
||||
border-color: rgba(255,255,255,0.14);
|
||||
}
|
||||
|
||||
.ws-plan-card {
|
||||
transition: transform 0.4s cubic-bezier(0.16,1,0.3,1), box-shadow 0.4s;
|
||||
}
|
||||
.ws-plan-card:hover { transform: translateY(-4px); }
|
||||
|
||||
.ws-faq-item {
|
||||
border-radius: 14px; overflow: hidden;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
|
||||
.ws-step-card {
|
||||
transition: transform 0.4s cubic-bezier(0.16,1,0.3,1), box-shadow 0.4s;
|
||||
}
|
||||
.ws-step-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 16px 48px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* 모바일 반응형 */
|
||||
@media (max-width: 640px) {
|
||||
.ws-portfolio-grid { grid-template-columns: 1fr !important; }
|
||||
.ws-process-steps { flex-direction: column !important; align-items: stretch !important; }
|
||||
.ws-process-divider { display: none !important; }
|
||||
.ws-pricing-grid { grid-template-columns: 1fr !important; }
|
||||
.ws-hero-stats { gap: 0 !important; flex-wrap: nowrap !important; }
|
||||
.ws-hero-stats > div { padding: 0 16px !important; }
|
||||
.ws-cta-box { padding: 36px 24px !important; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.ws-hero-buttons { flex-direction: column !important; align-items: stretch !important; }
|
||||
.ws-hero-buttons a, .ws-hero-buttons button { text-align: center !important; justify-content: center !important; }
|
||||
}
|
||||
|
||||
/* scrollbar */
|
||||
::-webkit-scrollbar { width: 4px; }
|
||||
::-webkit-scrollbar-track { background: #030712; }
|
||||
::-webkit-scrollbar-thumb { background: rgba(99,102,241,0.3); border-radius: 2px; }
|
||||
`}} />
|
||||
|
||||
{/* ── Hero ── */}
|
||||
<section style={{ padding: '80px 24px 60px', position: 'relative', overflow: 'hidden' }}>
|
||||
{/* Diagonal pattern */}
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, pointerEvents: 'none',
|
||||
backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 40px)',
|
||||
}} />
|
||||
|
||||
<div style={{ maxWidth: 820, margin: '0 auto', position: 'relative', animation: 'ws-fadeUp 0.9s cubic-bezier(0.16,1,0.3,1) both' }}>
|
||||
<p style={{
|
||||
fontSize: 11, fontWeight: 700, letterSpacing: '0.18em',
|
||||
color: '#6366f1', textTransform: 'uppercase',
|
||||
fontFamily: 'monospace',
|
||||
marginBottom: 24,
|
||||
}}>
|
||||
홈페이지 제작 서비스
|
||||
</p>
|
||||
<h1 style={{
|
||||
fontSize: 'clamp(28px, 4.5vw, 54px)', fontWeight: 800,
|
||||
lineHeight: 1.2, marginBottom: 20,
|
||||
letterSpacing: '-0.02em',
|
||||
color: '#ffffff',
|
||||
wordBreak: 'keep-all',
|
||||
}}>
|
||||
홈페이지·웹앱·앱 개발,<br/>연락 끊기는 일 없습니다
|
||||
</h1>
|
||||
<p style={{
|
||||
fontSize: 16, color: '#64748b', lineHeight: 1.85, marginBottom: 36,
|
||||
wordBreak: 'keep-all',
|
||||
}}>
|
||||
소개 사이트부터 SaaS·쇼핑몰·모바일웹까지 — 계약서부터 소스코드 인도까지<br/>
|
||||
단계마다 증거를 남깁니다. 납기 지연 시 하루당 10만원 감면.
|
||||
</p>
|
||||
<div className="ws-hero-buttons" style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
|
||||
<Link href="/work/freelance?service=website" style={{
|
||||
display: 'inline-flex', alignItems: 'center', gap: 8,
|
||||
padding: '14px 28px',
|
||||
background: '#6366f1',
|
||||
borderRadius: 12, color: 'white', fontWeight: 700, fontSize: 15,
|
||||
textDecoration: 'none',
|
||||
transition: 'background 0.2s',
|
||||
}}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = '#4f46e5'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = '#6366f1'; }}>
|
||||
무료 상담 신청 →
|
||||
</Link>
|
||||
<a href="#samples" style={{
|
||||
display: 'inline-flex', alignItems: 'center', gap: 8,
|
||||
padding: '14px 28px',
|
||||
border: '1px solid rgba(255,255,255,0.1)', borderRadius: 12,
|
||||
color: '#94a3b8', fontWeight: 600, fontSize: 15,
|
||||
textDecoration: 'none',
|
||||
transition: 'border-color 0.3s, color 0.3s',
|
||||
}}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.borderColor = 'rgba(255,255,255,0.25)'; (e.currentTarget as HTMLElement).style.color = '#e2e8f0'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.borderColor = 'rgba(255,255,255,0.1)'; (e.currentTarget as HTMLElement).style.color = '#94a3b8'; }}>
|
||||
샘플 보기
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="ws-hero-stats" style={{ display: 'flex', gap: 0, justifyContent: 'center', marginTop: 56, flexWrap: 'wrap' }}>
|
||||
{[
|
||||
{ num: '3~5일', label: '최단 납품 (스타터)' },
|
||||
{ num: '20만원~', label: '시작 가격' },
|
||||
{ num: '전액환불', label: '납품 전 환불 보장' },
|
||||
].map((s, i) => (
|
||||
<div key={s.label} style={{
|
||||
textAlign: 'center', padding: '0 40px',
|
||||
borderRight: i < 2 ? '1px solid rgba(255,255,255,0.08)' : 'none',
|
||||
}}>
|
||||
<div style={{ fontSize: 22, fontWeight: 800, color: 'white', letterSpacing: '-0.02em' }}>{s.num}</div>
|
||||
<div style={{ fontSize: 12, color: '#475569', marginTop: 4, letterSpacing: '0.02em' }}>{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Feature tags ── */}
|
||||
<div style={{ borderTop: '1px solid rgba(255,255,255,0.05)', borderBottom: '1px solid rgba(255,255,255,0.05)', padding: '14px 24px' }}>
|
||||
<div style={{ maxWidth: 1000, margin: '0 auto', display: 'flex', gap: 8, flexWrap: 'wrap', justifyContent: 'center' }}>
|
||||
{['반응형 디자인', 'SEO 최적화', '웹앱·모바일웹', '계약서 작성', '소스코드 제공', '납기 패널티 보장', '도메인 배포'].map((t) => (
|
||||
<span key={t} style={{ padding: '4px 12px', fontSize: '11px', color: '#475569', letterSpacing: '0.06em', border: '1px solid rgba(255,255,255,0.07)', borderRadius: 4, fontFamily: 'monospace' }}>
|
||||
{t}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Trust badges ── */}
|
||||
<section style={{ padding: '48px 24px', maxWidth: 1000, margin: '0 auto' }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 1, border: '1px solid rgba(255,255,255,0.06)', borderRadius: 12, overflow: 'hidden' }}>
|
||||
{[
|
||||
{
|
||||
title: '계약서 필수 작성', desc: '모든 프로젝트 계약서 체결 후 진행',
|
||||
icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} style={{ width: 20, height: 20 }}><path strokeLinecap="round" strokeLinejoin="round" d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m0 0H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V9.375c0-.621-.504-1.125-1.125-1.125H8.25zM6.75 12h.008v.008H6.75V12zm0 3h.008v.008H6.75V15zm0 3h.008v.008H6.75V18z" /></svg>,
|
||||
},
|
||||
{
|
||||
title: '주간 진행 보고', desc: '매주 작업 현황 공유, 연락 두절 없음',
|
||||
icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} style={{ width: 20, height: 20 }}><path strokeLinecap="round" strokeLinejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z" /></svg>,
|
||||
},
|
||||
{
|
||||
title: '소스코드 전액 제공', desc: '완성 후 전체 소스코드 인도',
|
||||
icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} style={{ width: 20, height: 20 }}><path strokeLinecap="round" strokeLinejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" /></svg>,
|
||||
},
|
||||
{
|
||||
title: '납기 지연 패널티', desc: '지연 1일당 10만원 자동 감면',
|
||||
icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} style={{ width: 20, height: 20 }}><path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" /></svg>,
|
||||
},
|
||||
{
|
||||
title: '실시간 진행 현황', desc: '마이페이지에서 7단계 진행 상황 직접 확인',
|
||||
icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} style={{ width: 20, height: 20 }}><path strokeLinecap="round" strokeLinejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /></svg>,
|
||||
},
|
||||
].map((b) => (
|
||||
<div key={b.title} style={{
|
||||
padding: '20px 22px',
|
||||
background: 'rgba(255,255,255,0.02)',
|
||||
display: 'flex', gap: 14, alignItems: 'flex-start',
|
||||
}}>
|
||||
<span style={{ color: '#6366f1', flexShrink: 0, marginTop: 2 }}>{b.icon}</span>
|
||||
<div>
|
||||
<div style={{ fontSize: 13, fontWeight: 600, color: '#e2e8f0', marginBottom: 4, wordBreak: 'keep-all' }}>{b.title}</div>
|
||||
<div style={{ fontSize: 12, color: '#475569', lineHeight: 1.6, wordBreak: 'keep-all' }}>{b.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Sample Portfolio ── */}
|
||||
<section id="samples" style={{ padding: '56px 24px', maxWidth: 1160, margin: '0 auto' }}>
|
||||
<div ref={samplesRef} className="ws-reveal">
|
||||
<div style={{ textAlign: 'center', marginBottom: 44 }}>
|
||||
<p style={{ fontSize: 11, color: '#6366f1', letterSpacing: '0.2em', textTransform: 'uppercase', marginBottom: 12, fontWeight: 700 }}>Portfolio Samples</p>
|
||||
<h2 style={{ fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 10, letterSpacing: '-0.02em' }}>
|
||||
포트폴리오 샘플
|
||||
</h2>
|
||||
<p style={{ color: '#475569', fontSize: 14 }}>
|
||||
카드를 클릭하면 실제 완성 화면을 미리 확인할 수 있습니다
|
||||
</p>
|
||||
</div>
|
||||
<div className="ws-portfolio-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 18 }}>
|
||||
{samples.map((s) => (
|
||||
<Link key={s.type} href={`/work/website/samples/${s.type}`} style={{ textDecoration: 'none' }}>
|
||||
<div className="ws-sample-card">
|
||||
<div style={{ position: 'relative' }}>
|
||||
<SampleMiniPreview type={s.type} />
|
||||
<div style={{ position: 'absolute', top: 12, left: 12, display: 'flex', gap: 5, zIndex: 10 }}>
|
||||
{s.tags.map((tag) => (
|
||||
<span key={tag} style={{
|
||||
fontSize: 10, fontWeight: 600, color: '#e2e8f0',
|
||||
background: 'rgba(0,0,0,0.52)', backdropFilter: 'blur(8px)',
|
||||
border: '1px solid rgba(255,255,255,0.13)',
|
||||
padding: '2px 8px', borderRadius: 100,
|
||||
}}>{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 12, right: 12, zIndex: 10,
|
||||
background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(8px)',
|
||||
border: `1px solid ${s.accent}45`,
|
||||
borderRadius: 8, padding: '5px 12px',
|
||||
fontSize: 11, color: s.accent, fontWeight: 700,
|
||||
}}>
|
||||
미리보기 →
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ padding: '18px 22px 22px' }}>
|
||||
<div style={{ fontSize: 11, color: '#334155', marginBottom: 5, letterSpacing: '0.05em' }}>
|
||||
{s.subtitle}
|
||||
</div>
|
||||
<div style={{ fontSize: 16, fontWeight: 700, color: 'white', marginBottom: 8, letterSpacing: '-0.01em' }}>
|
||||
{s.title}
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: '#475569', lineHeight: 1.65, wordBreak: 'keep-all' }}>
|
||||
{s.desc}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Process ── */}
|
||||
<section style={{ padding: '56px 24px', background: 'rgba(255,255,255,0.015)', borderTop: '1px solid rgba(255,255,255,0.04)', borderBottom: '1px solid rgba(255,255,255,0.04)' }}>
|
||||
<div ref={processRef} className="ws-reveal" style={{ maxWidth: 1060, margin: '0 auto' }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 44 }}>
|
||||
<p style={{ fontSize: 11, color: '#6366f1', letterSpacing: '0.2em', textTransform: 'uppercase', marginBottom: 12, fontWeight: 700 }}>Process</p>
|
||||
<h2 style={{ fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 10, letterSpacing: '-0.02em' }}>
|
||||
제작 프로세스
|
||||
</h2>
|
||||
<p style={{ color: '#475569', fontSize: 14 }}>투명하고 체계적인 5단계로 진행됩니다</p>
|
||||
</div>
|
||||
<div className="ws-process-steps" style={{ display: 'flex', alignItems: 'stretch', flexWrap: 'wrap', justifyContent: 'center', gap: 0 }}>
|
||||
{processSteps.map((p, i) => (
|
||||
<div key={i} style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<div className="ws-step-card" style={{
|
||||
textAlign: 'center', padding: '28px 22px', minWidth: 138,
|
||||
background: '#080d1a', borderRadius: 16,
|
||||
border: '1px solid rgba(255,255,255,0.05)',
|
||||
}}>
|
||||
<div style={{ fontSize: 22, fontWeight: 800, color: '#6366f1', fontFamily: 'monospace', marginBottom: 12, letterSpacing: '-0.02em' }}>
|
||||
{p.step}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: 'white', marginBottom: 6, wordBreak: 'keep-all' }}>
|
||||
{p.title}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: '#334155', lineHeight: 1.55, wordBreak: 'keep-all' }}>
|
||||
{p.desc}
|
||||
</div>
|
||||
</div>
|
||||
{i < processSteps.length - 1 && (
|
||||
<div className="ws-process-divider" style={{ color: '#1e293b', fontSize: 20, padding: '0 4px', flexShrink: 0 }}>›</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Pricing ── */}
|
||||
<section style={{ padding: '64px 24px', maxWidth: 1040, margin: '0 auto' }}>
|
||||
<div ref={pricingRef} className="ws-reveal">
|
||||
<div style={{ textAlign: 'center', marginBottom: 44 }}>
|
||||
<p style={{ fontSize: 11, color: '#6366f1', letterSpacing: '0.2em', textTransform: 'uppercase', marginBottom: 12, fontWeight: 700 }}>Pricing</p>
|
||||
<h2 style={{ fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 10, letterSpacing: '-0.02em' }}>
|
||||
가격 플랜
|
||||
</h2>
|
||||
<p style={{ color: '#475569', fontSize: 14 }}>프로젝트 규모에 맞는 플랜을 선택하세요</p>
|
||||
</div>
|
||||
<div className="ws-pricing-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(270px, 1fr))', gap: 20 }}>
|
||||
{plans.map((plan) => (
|
||||
<div key={plan.name} className="ws-plan-card" style={{
|
||||
padding: 32, borderRadius: 20,
|
||||
background: plan.featured ? '#0d1240' : '#080d1a',
|
||||
border: `1px solid ${plan.featured ? plan.color + '40' : 'rgba(255,255,255,0.05)'}`,
|
||||
position: 'relative', overflow: 'hidden',
|
||||
boxShadow: plan.featured ? `0 24px 64px ${plan.color}12` : 'none',
|
||||
}}>
|
||||
{plan.featured && (
|
||||
<div style={{
|
||||
position: 'absolute', top: 20, right: 20,
|
||||
background: plan.color, color: '#1e1b4b',
|
||||
fontSize: 10, fontWeight: 800, padding: '3px 10px', borderRadius: 100,
|
||||
}}>BEST</div>
|
||||
)}
|
||||
<div style={{ fontSize: 12, color: plan.color, fontWeight: 700, marginBottom: 12, letterSpacing: '0.05em' }}>
|
||||
{plan.name}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: 4, marginBottom: 4 }}>
|
||||
<span style={{ fontSize: 40, fontWeight: 800, color: 'white', letterSpacing: '-0.03em' }}>{plan.price}</span>
|
||||
<span style={{ fontSize: 15, color: '#64748b' }}>{plan.unit}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: '#334155', marginBottom: 24, wordBreak: 'keep-all' }}>{plan.note}</div>
|
||||
<div style={{ borderTop: '1px solid rgba(255,255,255,0.06)', paddingTop: 20, marginBottom: 24 }}>
|
||||
{plan.features.map((f) => (
|
||||
<div key={f} style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 12 }}>
|
||||
<span style={{ color: plan.color, fontSize: 13, flexShrink: 0, fontWeight: 700 }}>✓</span>
|
||||
<span style={{ fontSize: 13, color: '#94a3b8', wordBreak: 'keep-all' }}>{f}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => openModal(`홈페이지 제작 - ${plan.name}`)}
|
||||
style={{
|
||||
display: 'block', width: '100%', textAlign: 'center', padding: '13px',
|
||||
background: plan.featured ? plan.color : 'rgba(255,255,255,0.04)',
|
||||
borderRadius: 10, color: plan.featured ? '#1e1b4b' : '#94a3b8',
|
||||
fontWeight: 700, fontSize: 14, border: plan.featured ? 'none' : '1px solid rgba(255,255,255,0.07)',
|
||||
transition: 'opacity 0.2s', cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
견적 문의
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── FAQ ── */}
|
||||
<section style={{ padding: '0 24px 64px', maxWidth: 720, margin: '0 auto' }}>
|
||||
<div ref={faqRef} className="ws-reveal">
|
||||
<div style={{ textAlign: 'center', marginBottom: 36 }}>
|
||||
<p style={{ fontSize: 11, color: '#6366f1', letterSpacing: '0.2em', textTransform: 'uppercase', marginBottom: 12, fontWeight: 700 }}>FAQ</p>
|
||||
<h2 style={{ fontSize: 28, fontWeight: 800, color: 'white', letterSpacing: '-0.02em' }}>
|
||||
자주 묻는 질문
|
||||
</h2>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||
{faqs.map((faq, i) => (
|
||||
<div key={i} className="ws-faq-item" style={{
|
||||
background: '#080d1a',
|
||||
border: `1px solid ${openFaq === i ? 'rgba(99,102,241,0.4)' : 'rgba(255,255,255,0.05)'}`,
|
||||
}}>
|
||||
<button onClick={() => setOpenFaq(openFaq === i ? null : i)} style={{
|
||||
width: '100%', textAlign: 'left', padding: '18px 22px',
|
||||
background: 'none', border: 'none', cursor: 'pointer',
|
||||
display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12,
|
||||
}}>
|
||||
<span style={{ fontSize: 14, fontWeight: 600, color: 'white', wordBreak: 'keep-all' }}>
|
||||
{faq.q}
|
||||
</span>
|
||||
<span style={{
|
||||
color: '#6366f1', fontSize: 22, flexShrink: 0,
|
||||
transition: 'transform 0.25s',
|
||||
transform: openFaq === i ? 'rotate(45deg)' : 'none',
|
||||
display: 'inline-block',
|
||||
}}>+</span>
|
||||
</button>
|
||||
{openFaq === i && (
|
||||
<div style={{ padding: '0 22px 18px', fontSize: 14, color: '#475569', lineHeight: 1.8, wordBreak: 'keep-all' }}>
|
||||
{faq.a}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── CTA ── */}
|
||||
<section style={{ padding: '0 24px 80px', textAlign: 'center' }}>
|
||||
<div ref={ctaRef} className="ws-reveal">
|
||||
<div className="ws-cta-box" style={{
|
||||
maxWidth: 640, margin: '0 auto',
|
||||
padding: '56px 44px', borderRadius: 24,
|
||||
background: '#04102b',
|
||||
backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.012) 0px, rgba(255,255,255,0.012) 1px, transparent 1px, transparent 40px)',
|
||||
border: '1px solid rgba(99,102,241,0.3)',
|
||||
boxShadow: '0 24px 80px rgba(0,0,0,0.3)',
|
||||
}}>
|
||||
<h2 style={{ fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 14, letterSpacing: '-0.02em', wordBreak: 'keep-all' }}>
|
||||
내일도 고민만 하실 건가요?
|
||||
</h2>
|
||||
<p style={{ color: '#94a3b8', fontSize: 15, lineHeight: 1.75, marginBottom: 32, wordBreak: 'keep-all' }}>
|
||||
상담 신청 후 24시간 이내 답변드립니다.<br/>
|
||||
소개 사이트·웹앱·쇼핑몰·모바일앱, 규모 무관하게 검토해드립니다.
|
||||
</p>
|
||||
<Link href="/work/freelance?service=website" style={{
|
||||
display: 'inline-block', padding: '15px 40px',
|
||||
background: '#6366f1',
|
||||
borderRadius: 12, color: 'white', fontWeight: 700, fontSize: 15,
|
||||
textDecoration: 'none',
|
||||
boxShadow: '0 8px 24px rgba(99,102,241,0.4)',
|
||||
transition: 'transform 0.3s, box-shadow 0.3s',
|
||||
}}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.transform = 'translateY(-2px)'; (e.currentTarget as HTMLElement).style.boxShadow = '0 16px 40px rgba(99,102,241,0.5)'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.transform = ''; (e.currentTarget as HTMLElement).style.boxShadow = '0 8px 24px rgba(99,102,241,0.4)'; }}>
|
||||
무료 상담 신청하기 →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Scroll to Top ── */}
|
||||
<button
|
||||
onClick={() => {
|
||||
const scroller = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement;
|
||||
scroller.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
style={{
|
||||
position: 'fixed', bottom: '5.5rem', right: '2rem', zIndex: 200,
|
||||
width: 48, height: 48, borderRadius: '50%',
|
||||
background: '#6366f1',
|
||||
border: 'none', cursor: 'pointer',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
boxShadow: '0 8px 32px rgba(99,102,241,0.4)',
|
||||
opacity: showTop ? 1 : 0,
|
||||
transform: showTop ? 'translateY(0) scale(1)' : 'translateY(12px) scale(0.9)',
|
||||
transition: 'opacity 0.35s cubic-bezier(0.16,1,0.3,1), transform 0.35s cubic-bezier(0.16,1,0.3,1)',
|
||||
pointerEvents: showTop ? 'auto' : 'none',
|
||||
}}
|
||||
aria-label="맨 위로"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="18 15 12 9 6 15"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
539
app/work/website/samples/bakery/page.tsx
Normal file
539
app/work/website/samples/bakery/page.tsx
Normal file
@@ -0,0 +1,539 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
const menuItems = [
|
||||
{ name: '버터 크루아상', price: '3,200', tag: '인기', tagColor: '#dc2626', desc: '프랑스산 에슈레 버터만 사용한 겹겹이 살아있는 크루아상', ingredients: ['에슈레 버터', '천연 발효', 'T65 밀가루'] },
|
||||
{ name: '소금빵', price: '2,800', tag: '베스트', tagColor: '#d97706', desc: '오키나와 천연 소금과 발효 버터가 만나는 완벽한 짭조름함', ingredients: ['오키나와 소금', '발효 버터', '홋카이도 밀'] },
|
||||
{ name: '딸기 쇼트케이크', price: '7,500', tag: '신메뉴', tagColor: '#7c3aed', desc: '국내산 딸기와 생크림이 만나는 클래식 케이크', ingredients: ['국내산 딸기', '생크림', '제노아즈'] },
|
||||
{ name: '캄파뉴', price: '8,900', tag: '장인', tagColor: '#065f46', desc: '72시간 저온 발효로 만든 시큼하고 깊은 맛의 통밀빵', ingredients: ['통밀', '사워도우', '72h 발효'] },
|
||||
];
|
||||
|
||||
const reviews = [
|
||||
{ name: '김민서', rating: 5, text: '매일 아침 출근 전 들르게 되는 마법 같은 빵집. 크루아상은 그냥 인생 최고입니다.', date: '2024.08', verified: true },
|
||||
{ name: '이준혁', rating: 5, text: '파리 유학시절 먹던 그 맛과 거의 흡사합니다. 국내에 이런 퀄리티가 있다니 놀랍습니다.', date: '2024.07', verified: true },
|
||||
{ name: '박지은', rating: 5, text: '웨딩 케이크 주문했는데 하객들이 케이크 맛있다고 난리났어요. 다음에도 꼭 맡기겠습니다!', date: '2024.06', verified: true },
|
||||
];
|
||||
|
||||
const specials = [
|
||||
{ season: '봄', item: '딸기 타르틀렛', desc: '4월 한정', color: '#fda4af' },
|
||||
{ season: '여름', item: '레몬 틸레', desc: '7–8월 한정', color: '#fde68a' },
|
||||
{ season: '가을', item: '밤 크루아상', desc: '10월 한정', color: '#d97706' },
|
||||
{ season: '겨울', item: '뱅쇼 브리오슈', desc: '12월 한정', color: '#c4b5fd' },
|
||||
];
|
||||
|
||||
const hours = [
|
||||
{ day: '월~금', time: '07:00 – 20:00' },
|
||||
{ day: '토요일', time: '07:00 – 21:00' },
|
||||
{ day: '일·공휴일', time: '09:00 – 18:00' },
|
||||
];
|
||||
|
||||
export default function BakerySample() {
|
||||
return (
|
||||
<div style={{ background: '#fffbf5', minHeight: '100vh', color: '#1c1008' }}>
|
||||
<style>{`
|
||||
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400;1,700&family=Noto+Sans+KR:wght@300;400;500;600;700&display=swap');
|
||||
@keyframes fadeUp { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: translateY(0); } }
|
||||
@keyframes float { 0%, 100% { transform: translateY(0px) rotate(-1deg); } 50% { transform: translateY(-12px) rotate(1deg); } }
|
||||
@keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }
|
||||
@keyframes sway { 0%, 100% { transform: rotate(-2deg); } 50% { transform: rotate(2deg); } }
|
||||
.menu-card:hover { transform: translateY(-8px); box-shadow: 0 32px 80px rgba(120,53,15,0.15) !important; }
|
||||
.menu-card { transition: transform 0.4s cubic-bezier(.23,1,.32,1), box-shadow 0.4s; }
|
||||
.review-card:hover { border-color: #fbbf24 !important; }
|
||||
.review-card { transition: border-color 0.3s; }
|
||||
.bk-btn-primary:hover { background: #92400e !important; transform: translateY(-1px); box-shadow: 0 12px 32px rgba(180,83,9,0.45) !important; }
|
||||
.bk-btn-primary { transition: background 0.2s, transform 0.2s, box-shadow 0.2s; }
|
||||
.bk-btn-ghost:hover { background: rgba(180,83,9,0.06) !important; }
|
||||
.bk-btn-ghost { transition: background 0.2s; }
|
||||
.special-card:hover { transform: scale(1.04); }
|
||||
.special-card { transition: transform 0.3s; }
|
||||
.ingredient-tag { background: rgba(180,83,9,0.07); color: #92400e; border: 1px solid rgba(180,83,9,0.15); border-radius: 100px; padding: 2px 10px; font-size: 11px; font-family: 'Noto Sans KR', sans-serif; white-space: nowrap; }
|
||||
`}</style>
|
||||
|
||||
{/* Back Banner */}
|
||||
<div style={{ background: 'linear-gradient(135deg, #1e1b4b, #312e81)', padding: '10px 24px', display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<Link href="/work/website" style={{ color: '#a5b4fc', fontSize: 13, textDecoration: 'none', fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
← 홈페이지 제작 서비스로 돌아가기
|
||||
</Link>
|
||||
<span style={{ color: '#4c1d95' }}>|</span>
|
||||
<span style={{ color: '#fcd34d', fontSize: 12, fontFamily: 'Playfair Display, serif', fontStyle: 'italic', fontWeight: 700 }}>
|
||||
SAMPLE · 베이커리 홈페이지
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Navbar */}
|
||||
<nav style={{
|
||||
background: 'rgba(255,251,245,0.96)', backdropFilter: 'blur(16px)',
|
||||
borderBottom: '1px solid #fde8c8',
|
||||
padding: '0 48px', height: 68,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
position: 'sticky', top: 0, zIndex: 100,
|
||||
}}>
|
||||
{/* Logo with wheat SVG */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none">
|
||||
<ellipse cx="14" cy="8" rx="5" ry="7" fill="#d97706" opacity="0.9" />
|
||||
<ellipse cx="8" cy="14" rx="5" ry="4" fill="#b45309" opacity="0.7" transform="rotate(-30 8 14)" />
|
||||
<ellipse cx="20" cy="14" rx="5" ry="4" fill="#b45309" opacity="0.7" transform="rotate(30 20 14)" />
|
||||
<line x1="14" y1="28" x2="14" y2="8" stroke="#78350f" strokeWidth="1.5" strokeLinecap="round" />
|
||||
</svg>
|
||||
<div>
|
||||
<div style={{ fontSize: 19, fontFamily: 'Playfair Display, serif', fontStyle: 'italic', color: '#78350f', fontWeight: 700, lineHeight: 1 }}>
|
||||
Le Petit Fort
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: '#c9a87c', letterSpacing: '0.18em', textTransform: 'uppercase', fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
Artisan Boulangerie
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 28 }}>
|
||||
{['메뉴', '스토리', '시즌 메뉴', '매장안내', '예약'].map((item) => (
|
||||
<span key={item} style={{ fontSize: 14, color: '#78350f', cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif", fontWeight: 500 }}>{item}</span>
|
||||
))}
|
||||
</div>
|
||||
<button className="bk-btn-primary" style={{
|
||||
background: '#b45309', color: 'white', border: 'none',
|
||||
padding: '9px 22px', borderRadius: 24, fontSize: 13,
|
||||
fontWeight: 600, cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif",
|
||||
}}>
|
||||
예약하기
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
{/* Hero */}
|
||||
<section style={{
|
||||
background: 'linear-gradient(160deg, #fef3c7 0%, #fde68a 40%, #fbbf24 75%, #d97706 100%)',
|
||||
padding: '80px 48px', position: 'relative', overflow: 'hidden', minHeight: 500,
|
||||
display: 'flex', alignItems: 'center',
|
||||
}}>
|
||||
{/* Decorative circles */}
|
||||
<div style={{ position: 'absolute', right: -80, top: -80, width: 480, height: 480, borderRadius: '50%', background: 'rgba(180,83,9,0.07)' }} />
|
||||
<div style={{ position: 'absolute', right: 60, bottom: -60, width: 280, height: 280, borderRadius: '50%', background: 'rgba(180,83,9,0.1)' }} />
|
||||
|
||||
{/* SVG Croissant illustration */}
|
||||
<div style={{ position: 'absolute', right: '8%', top: '50%', transform: 'translateY(-50%)', animation: 'float 5s ease-in-out infinite' }}>
|
||||
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" style={{ filter: 'drop-shadow(0 20px 40px rgba(120,53,15,0.3))' }}>
|
||||
{/* Croissant shape */}
|
||||
<path d="M90 30 C50 30, 20 60, 20 90 C20 110, 30 130, 50 145 C65 155, 75 158, 90 155 C105 158, 115 155, 130 145 C150 130, 160 110, 160 90 C160 60, 130 30, 90 30Z" fill="#d97706" />
|
||||
<path d="M90 40 C55 40, 30 65, 30 90 C30 108, 38 124, 55 136 C68 145, 78 148, 90 145 C102 148, 112 145, 125 136 C142 124, 150 108, 150 90 C150 65, 125 40, 90 40Z" fill="#b45309" />
|
||||
{/* Layered flakes */}
|
||||
<path d="M60 80 Q90 70 120 80" stroke="#fde68a" strokeWidth="2.5" strokeLinecap="round" opacity="0.6" />
|
||||
<path d="M55 92 Q90 82 125 92" stroke="#fef3c7" strokeWidth="2" strokeLinecap="round" opacity="0.5" />
|
||||
<path d="M60 104 Q90 94 120 104" stroke="#fde68a" strokeWidth="2" strokeLinecap="round" opacity="0.4" />
|
||||
{/* Shine */}
|
||||
<ellipse cx="70" cy="65" rx="15" ry="8" fill="white" opacity="0.15" transform="rotate(-20 70 65)" />
|
||||
{/* Steam */}
|
||||
<path d="M75 28 Q78 20 75 12" stroke="#fbbf24" strokeWidth="2" strokeLinecap="round" opacity="0.4" />
|
||||
<path d="M90 24 Q93 14 90 5" stroke="#fbbf24" strokeWidth="2" strokeLinecap="round" opacity="0.5" />
|
||||
<path d="M105 28 Q108 20 105 12" stroke="#fbbf24" strokeWidth="2" strokeLinecap="round" opacity="0.4" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Wheat decoration top right */}
|
||||
<svg width="80" height="120" viewBox="0 0 80 120" fill="none" style={{ position: 'absolute', right: '28%', top: 20, opacity: 0.15, animation: 'sway 4s ease-in-out infinite' }}>
|
||||
<line x1="40" y1="120" x2="40" y2="0" stroke="#78350f" strokeWidth="2" />
|
||||
{[15, 35, 55, 75].map((y, i) => (
|
||||
<g key={y}>
|
||||
<ellipse cx="28" cy={y} rx="10" ry="5" fill="#78350f" transform={`rotate(-40 28 ${y})`} />
|
||||
<ellipse cx="52" cy={y} rx="10" ry="5" fill="#78350f" transform={`rotate(40 52 ${y})`} />
|
||||
</g>
|
||||
))}
|
||||
</svg>
|
||||
|
||||
<div style={{ maxWidth: 560, animation: 'fadeUp 0.8s ease forwards', position: 'relative', zIndex: 1 }}>
|
||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'rgba(255,255,255,0.5)', backdropFilter: 'blur(8px)', borderRadius: 100, padding: '5px 14px', marginBottom: 20 }}>
|
||||
<svg width="10" height="10" viewBox="0 0 10 10"><circle cx="5" cy="5" r="5" fill="#16a34a" /><circle cx="5" cy="5" r="2" fill="white" /></svg>
|
||||
<span style={{ fontSize: 12, color: '#78350f', fontFamily: "'Noto Sans KR', sans-serif", fontWeight: 600 }}>오늘도 새벽 4시부터 굽고 있습니다</span>
|
||||
</div>
|
||||
|
||||
<div style={{ fontSize: 13, color: '#78350f', fontFamily: 'Playfair Display, serif', fontStyle: 'italic', marginBottom: 14, letterSpacing: '0.05em' }}>
|
||||
“매일 아침, 정성을 굽습니다”
|
||||
</div>
|
||||
<h1 style={{ fontFamily: 'Playfair Display, serif', fontSize: 'clamp(40px, 5vw, 64px)', fontWeight: 700, color: '#451a03', lineHeight: 1.15, marginBottom: 18 }}>
|
||||
갓 구운 빵의<br />
|
||||
<span style={{ color: '#b45309' }}>따뜻한 향기</span>가<br />
|
||||
기다립니다
|
||||
</h1>
|
||||
<p style={{ color: '#78350f', fontSize: 16, lineHeight: 1.85, marginBottom: 32, fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
파리 전통 방식으로 매일 새벽 4시부터<br />정성껏 굽는 르 쁘띠 포르의 빵을 만나보세요.
|
||||
</p>
|
||||
|
||||
{/* Social proof */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 28 }}>
|
||||
<div style={{ display: 'flex' }}>
|
||||
{['#d97706', '#b45309', '#92400e', '#78350f'].map((c, i) => (
|
||||
<div key={i} style={{ width: 30, height: 30, borderRadius: '50%', background: c, border: '2px solid white', marginLeft: i > 0 ? -8 : 0 }} />
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ display: 'flex', gap: 2 }}>
|
||||
{[1,2,3,4,5].map(i => (
|
||||
<svg key={i} width="12" height="12" viewBox="0 0 12 12"><path d="M6 1l1.3 2.6L10 4.1 8 6.1l.5 2.9L6 7.6 3.5 9l.5-2.9-2-2 2.7-.5z" fill="#d97706" /></svg>
|
||||
))}
|
||||
</div>
|
||||
<span style={{ fontSize: 12, color: '#78350f', fontFamily: "'Noto Sans KR', sans-serif" }}>4.9점 · 1,200+ 리뷰</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
|
||||
<button className="bk-btn-primary" style={{
|
||||
background: '#b45309', color: 'white', border: 'none',
|
||||
padding: '14px 32px', borderRadius: 28, fontSize: 15, fontWeight: 700,
|
||||
cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif",
|
||||
boxShadow: '0 8px 24px rgba(180,83,9,0.35)',
|
||||
}}>
|
||||
오늘의 메뉴 보기
|
||||
</button>
|
||||
<button className="bk-btn-ghost" style={{
|
||||
background: 'rgba(255,255,255,0.5)', color: '#78350f',
|
||||
border: '1px solid rgba(180,83,9,0.25)',
|
||||
padding: '14px 32px', borderRadius: 28, fontSize: 15, fontWeight: 600,
|
||||
cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif",
|
||||
backdropFilter: 'blur(8px)',
|
||||
}}>
|
||||
매장 찾아오기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Season Special Banner */}
|
||||
<div style={{ background: '#451a03', padding: '14px 48px', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 32 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M8 1l1.8 3.6L14 5.6l-3 2.9.7 4.1L8 10.5 4.3 12.6l.7-4.1L2 5.6l4.2-.6z" fill="#fbbf24" /></svg>
|
||||
<span style={{ color: '#fde68a', fontSize: 13, fontFamily: 'Playfair Display, serif', fontStyle: 'italic', fontWeight: 600 }}>Spring Limited</span>
|
||||
</div>
|
||||
<span style={{ color: '#78350f' }}>|</span>
|
||||
<span style={{ color: 'white', fontSize: 14, fontFamily: "'Noto Sans KR', sans-serif", fontWeight: 600 }}>
|
||||
🌸 딸기 타르틀렛 — 4월 한정 메뉴 출시
|
||||
</span>
|
||||
<span style={{ color: '#78350f' }}>|</span>
|
||||
<span style={{ color: '#fcd34d', fontSize: 13, fontFamily: "'Noto Sans KR', sans-serif", cursor: 'pointer', textDecoration: 'underline' }}>자세히 보기 →</span>
|
||||
</div>
|
||||
|
||||
{/* Menu */}
|
||||
<section style={{ padding: '80px 48px', background: '#fffbf5' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 52 }}>
|
||||
<div style={{ fontSize: 11, color: '#b45309', fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', fontFamily: 'Playfair Display, serif', marginBottom: 12 }}>
|
||||
Today's Menu
|
||||
</div>
|
||||
<h2 style={{ fontFamily: 'Playfair Display, serif', fontSize: 40, color: '#451a03', marginBottom: 12 }}>
|
||||
오늘의 추천 메뉴
|
||||
</h2>
|
||||
<p style={{ color: '#a78060', fontSize: 15, fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
매일 새벽 구운 신선한 빵을 만나보세요
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))', gap: 24 }}>
|
||||
{menuItems.map((item) => (
|
||||
<div key={item.name} className="menu-card" style={{
|
||||
background: 'white', borderRadius: 20, border: '1px solid #fde8c8', overflow: 'hidden',
|
||||
boxShadow: '0 4px 20px rgba(120,53,15,0.06)',
|
||||
}}>
|
||||
{/* Card visual — SVG pattern instead of emoji */}
|
||||
<div style={{ height: 150, background: 'linear-gradient(135deg, #fef3c7, #fde68a)', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative', overflow: 'hidden' }}>
|
||||
{/* Subtle dotted pattern */}
|
||||
<svg width="100%" height="100%" style={{ position: 'absolute', inset: 0 }}>
|
||||
<pattern id={`dots-${item.name}`} x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
|
||||
<circle cx="10" cy="10" r="1.5" fill="rgba(180,83,9,0.1)" />
|
||||
</pattern>
|
||||
<rect width="100%" height="100%" fill={`url(#dots-${item.name})`} />
|
||||
</svg>
|
||||
{/* Bread silhouette SVG */}
|
||||
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" style={{ position: 'relative', zIndex: 1, filter: 'drop-shadow(0 4px 12px rgba(120,53,15,0.25))' }}>
|
||||
<ellipse cx="40" cy="45" rx="30" ry="18" fill="#d97706" />
|
||||
<ellipse cx="40" cy="38" rx="24" ry="16" fill="#b45309" />
|
||||
<path d="M20 42 Q40 30 60 42" stroke="#fde68a" strokeWidth="2" strokeLinecap="round" opacity="0.5" />
|
||||
<path d="M24 48 Q40 38 56 48" stroke="#fef3c7" strokeWidth="1.5" strokeLinecap="round" opacity="0.4" />
|
||||
<ellipse cx="30" cy="32" rx="8" ry="5" fill="white" opacity="0.1" transform="rotate(-20 30 32)" />
|
||||
</svg>
|
||||
<span style={{
|
||||
position: 'absolute', top: 12, right: 12,
|
||||
background: item.tagColor, color: 'white',
|
||||
fontSize: 10, fontWeight: 700, padding: '3px 9px', borderRadius: 100,
|
||||
fontFamily: "'Noto Sans KR', sans-serif",
|
||||
}}>{item.tag}</span>
|
||||
</div>
|
||||
<div style={{ padding: '18px 20px' }}>
|
||||
<div style={{ fontSize: 17, fontWeight: 700, color: '#451a03', fontFamily: 'Playfair Display, serif', marginBottom: 6 }}>
|
||||
{item.name}
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: '#a78060', lineHeight: 1.65, fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 12 }}>
|
||||
{item.desc}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 14 }}>
|
||||
{item.ingredients.map(ig => (
|
||||
<span key={ig} className="ingredient-tag">{ig}</span>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span style={{ fontSize: 19, fontWeight: 700, color: '#b45309', fontFamily: 'Playfair Display, serif' }}>
|
||||
₩{item.price}
|
||||
</span>
|
||||
<button style={{
|
||||
background: '#b45309', color: 'white', border: 'none',
|
||||
padding: '7px 16px', borderRadius: 12, fontSize: 12,
|
||||
fontWeight: 700, cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif",
|
||||
}}>담기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Seasonal Specials */}
|
||||
<section style={{ padding: '72px 48px', background: '#fef9f0', borderTop: '1px solid #fde8c8' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 44 }}>
|
||||
<div style={{ fontSize: 11, color: '#b45309', fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', fontFamily: 'Playfair Display, serif', marginBottom: 10 }}>
|
||||
Seasonal Specials
|
||||
</div>
|
||||
<h2 style={{ fontFamily: 'Playfair Display, serif', fontSize: 36, color: '#451a03' }}>
|
||||
계절마다 새로운 감동
|
||||
</h2>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
|
||||
{specials.map((s) => (
|
||||
<div key={s.season} className="special-card" style={{
|
||||
borderRadius: 16, padding: '28px 20px', textAlign: 'center',
|
||||
background: 'white', border: '1px solid #fde8c8',
|
||||
cursor: 'pointer', overflow: 'hidden', position: 'relative',
|
||||
}}>
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 4, background: s.color, borderRadius: '16px 16px 0 0' }} />
|
||||
<div style={{
|
||||
width: 48, height: 48, borderRadius: '50%', margin: '0 auto 14px',
|
||||
background: s.color + '30', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
}}>
|
||||
<div style={{ width: 20, height: 20, borderRadius: '50%', background: s.color }} />
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: '#a78060', fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 8 }}>{s.season} 한정</div>
|
||||
<div style={{ fontSize: 16, fontWeight: 700, color: '#451a03', fontFamily: 'Playfair Display, serif', marginBottom: 6 }}>{s.item}</div>
|
||||
<div style={{ fontSize: 11, color: '#b45309', fontWeight: 600, fontFamily: "'Noto Sans KR', sans-serif", background: s.color + '25', borderRadius: 100, padding: '2px 10px', display: 'inline-block' }}>{s.desc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Story */}
|
||||
<section style={{ padding: '80px 48px', background: '#fffbf5', borderTop: '1px solid #fde8c8' }}>
|
||||
<div style={{ maxWidth: 960, margin: '0 auto', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 64, alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: 11, color: '#b45309', fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', fontFamily: 'Playfair Display, serif', marginBottom: 14 }}>
|
||||
Our Story
|
||||
</div>
|
||||
<h2 style={{ fontFamily: 'Playfair Display, serif', fontSize: 36, color: '#451a03', lineHeight: 1.3, marginBottom: 22 }}>
|
||||
2009년부터<br />한 자리를 지켜온<br />
|
||||
<em>우리 동네 빵집</em>
|
||||
</h2>
|
||||
<p style={{ color: '#78350f', fontSize: 15, lineHeight: 1.9, fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 16 }}>
|
||||
파리에서 5년간 수업한 오너 셰프가 고향 서울로 돌아와 차린 작은 베이커리. 대기업 프랜차이즈가 넘쳐나는 세상에서도 손으로 빚고, 눈으로 확인하는 전통 방식을 고집합니다.
|
||||
</p>
|
||||
<p style={{ color: '#78350f', fontSize: 15, lineHeight: 1.9, fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 28 }}>
|
||||
밀가루, 버터, 소금, 물. 단 네 가지 재료로 만드는 우리의 빵에는 흉내낼 수 없는 진심이 담겨있습니다.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 32 }}>
|
||||
{[{ n: '15+', l: '년 경력' }, { n: '200+', l: '종류의 빵' }, { n: '4시', l: '매일 기상' }].map((s) => (
|
||||
<div key={s.l}>
|
||||
<div style={{ fontSize: 24, fontWeight: 800, color: '#b45309', fontFamily: 'Playfair Display, serif' }}>{s.n}</div>
|
||||
<div style={{ fontSize: 12, color: '#a78060', fontFamily: "'Noto Sans KR', sans-serif" }}>{s.l}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Chef card with SVG illustration */}
|
||||
<div style={{ background: 'linear-gradient(135deg, #fde68a, #fbbf24)', borderRadius: 24, padding: '44px 32px', textAlign: 'center', border: '2px solid #fcd34d', position: 'relative', overflow: 'hidden' }}>
|
||||
{/* Pattern overlay */}
|
||||
<svg width="100%" height="100%" style={{ position: 'absolute', inset: 0, opacity: 0.07 }}>
|
||||
<pattern id="chef-dots" x="0" y="0" width="24" height="24" patternUnits="userSpaceOnUse">
|
||||
<circle cx="12" cy="12" r="2" fill="#78350f" />
|
||||
</pattern>
|
||||
<rect width="100%" height="100%" fill="url(#chef-dots)" />
|
||||
</svg>
|
||||
{/* Chef SVG illustration */}
|
||||
<div style={{ position: 'relative', zIndex: 1 }}>
|
||||
<div style={{ width: 96, height: 96, borderRadius: '50%', background: 'rgba(255,255,255,0.4)', margin: '0 auto 20px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '3px solid rgba(255,255,255,0.6)' }}>
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none">
|
||||
{/* Chef hat */}
|
||||
<ellipse cx="28" cy="20" rx="16" ry="12" fill="white" />
|
||||
<rect x="12" y="18" width="32" height="8" rx="2" fill="white" />
|
||||
<ellipse cx="28" cy="18" rx="8" ry="5" fill="#f3f4f6" />
|
||||
{/* Face */}
|
||||
<circle cx="28" cy="34" r="12" fill="#fbbf24" />
|
||||
<circle cx="24" cy="32" r="1.5" fill="#78350f" />
|
||||
<circle cx="32" cy="32" r="1.5" fill="#78350f" />
|
||||
<path d="M23 38 Q28 42 33 38" stroke="#78350f" strokeWidth="1.5" strokeLinecap="round" fill="none" />
|
||||
</svg>
|
||||
</div>
|
||||
<div style={{ fontSize: 20, fontFamily: 'Playfair Display, serif', color: '#451a03', fontWeight: 700, marginBottom: 6 }}>
|
||||
Chef Kim Dongwoo
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: '#78350f', fontFamily: "'Noto Sans KR', sans-serif", lineHeight: 1.7, marginBottom: 16 }}>
|
||||
Le Cordon Bleu Paris 졸업<br />
|
||||
2009년 르 쁘띠 포르 창업
|
||||
</div>
|
||||
{/* Awards */}
|
||||
<div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>
|
||||
{['미쉐린 추천', '블루리본', '로컬 레전드'].map(award => (
|
||||
<span key={award} style={{
|
||||
background: 'rgba(255,255,255,0.5)', color: '#78350f',
|
||||
fontSize: 10, fontWeight: 700, padding: '3px 10px',
|
||||
borderRadius: 100, border: '1px solid rgba(255,255,255,0.7)',
|
||||
fontFamily: "'Noto Sans KR', sans-serif",
|
||||
}}>{award}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Customer Reviews */}
|
||||
<section style={{ padding: '80px 48px', background: '#fef9f0', borderTop: '1px solid #fde8c8' }}>
|
||||
<div style={{ maxWidth: 1000, margin: '0 auto' }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 48 }}>
|
||||
<div style={{ fontSize: 11, color: '#b45309', fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', fontFamily: 'Playfair Display, serif', marginBottom: 10 }}>
|
||||
Guest Reviews
|
||||
</div>
|
||||
<h2 style={{ fontFamily: 'Playfair Display, serif', fontSize: 36, color: '#451a03', marginBottom: 12 }}>
|
||||
고객들의 이야기
|
||||
</h2>
|
||||
<div style={{ display: 'flex', gap: 4, justifyContent: 'center', alignItems: 'center', marginBottom: 8 }}>
|
||||
{[1,2,3,4,5].map(i => (
|
||||
<svg key={i} width="18" height="18" viewBox="0 0 18 18"><path d="M9 1.5l2 4L16 7l-3.5 3.4.8 4.8L9 13l-4.3 2.3.8-4.8L2 7l5-.5z" fill="#d97706" /></svg>
|
||||
))}
|
||||
<span style={{ marginLeft: 8, fontSize: 15, color: '#78350f', fontFamily: 'Playfair Display, serif', fontWeight: 700 }}>4.9</span>
|
||||
<span style={{ fontSize: 13, color: '#a78060', fontFamily: "'Noto Sans KR', sans-serif" }}> (1,243개 리뷰)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 20 }}>
|
||||
{reviews.map((r) => (
|
||||
<div key={r.name} className="review-card" style={{
|
||||
background: 'white', borderRadius: 20, padding: '28px 24px',
|
||||
border: '1px solid #fde8c8', boxShadow: '0 2px 12px rgba(120,53,15,0.04)',
|
||||
}}>
|
||||
{/* Stars */}
|
||||
<div style={{ display: 'flex', gap: 3, marginBottom: 14 }}>
|
||||
{[1,2,3,4,5].map(i => (
|
||||
<svg key={i} width="13" height="13" viewBox="0 0 13 13"><path d="M6.5 1l1.5 3 3.3.5-2.4 2.3.6 3.3L6.5 9 3 10.1l.6-3.3L1.2 4.5 4.5 4z" fill="#d97706" /></svg>
|
||||
))}
|
||||
{r.verified && (
|
||||
<span style={{ marginLeft: 6, fontSize: 10, color: '#16a34a', fontFamily: "'Noto Sans KR', sans-serif", fontWeight: 600 }}>
|
||||
✓ 인증 방문
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{/* Quote SVG */}
|
||||
<svg width="24" height="18" viewBox="0 0 24 18" fill="none" style={{ marginBottom: 10, opacity: 0.3 }}>
|
||||
<path d="M0 18V10.5C0 4.7 3.5 1.3 10.5 0l1.5 2.5C8.3 3.7 6.3 5.7 6 9h4.5V18H0zm12 0V10.5C12 4.7 15.5 1.3 22.5 0L24 2.5C20.3 3.7 18.3 5.7 18 9h4.5V18H12z" fill="#b45309" />
|
||||
</svg>
|
||||
<p style={{ fontSize: 14, color: '#78350f', lineHeight: 1.75, fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 18 }}>
|
||||
{r.text}
|
||||
</p>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{ width: 36, height: 36, borderRadius: '50%', background: 'linear-gradient(135deg, #fde68a, #d97706)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 14, fontWeight: 700, color: '#451a03', fontFamily: 'Playfair Display, serif' }}>
|
||||
{r.name[0]}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: 14, fontWeight: 600, color: '#451a03', fontFamily: "'Noto Sans KR', sans-serif" }}>{r.name}</div>
|
||||
<div style={{ fontSize: 11, color: '#c9a87c', fontFamily: "'Noto Sans KR', sans-serif" }}>{r.date}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Hours & Location */}
|
||||
<section style={{ padding: '72px 48px', background: '#451a03' }}>
|
||||
<div style={{ maxWidth: 960, margin: '0 auto', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 64 }}>
|
||||
<div>
|
||||
<div style={{ fontSize: 11, color: '#fbbf24', fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', fontFamily: 'Playfair Display, serif', marginBottom: 16 }}>Hours</div>
|
||||
<h3 style={{ fontFamily: 'Playfair Display, serif', fontSize: 28, color: 'white', marginBottom: 28 }}>운영 시간</h3>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{hours.map((h) => (
|
||||
<div key={h.day} style={{ display: 'flex', justifyContent: 'space-between', borderBottom: '1px solid rgba(255,255,255,0.08)', paddingBottom: 12 }}>
|
||||
<span style={{ fontSize: 14, color: '#fde68a', fontFamily: "'Noto Sans KR', sans-serif", fontWeight: 600 }}>{h.day}</span>
|
||||
<span style={{ fontSize: 14, color: '#fcd34d', fontFamily: 'Playfair Display, serif' }}>{h.time}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ marginTop: 20, padding: '12px 16px', background: 'rgba(251,191,36,0.1)', borderRadius: 10, border: '1px solid rgba(251,191,36,0.2)' }}>
|
||||
<p style={{ fontSize: 13, color: '#fde68a', fontFamily: "'Noto Sans KR', sans-serif", lineHeight: 1.6 }}>
|
||||
⚡ 품절 시 조기 마감될 수 있습니다.<br />
|
||||
인스타그램에서 당일 재고를 확인하세요.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: 11, color: '#fbbf24', fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', fontFamily: 'Playfair Display, serif', marginBottom: 16 }}>Location</div>
|
||||
<h3 style={{ fontFamily: 'Playfair Display, serif', fontSize: 28, color: 'white', marginBottom: 20 }}>매장 위치</h3>
|
||||
<p style={{ color: '#fde68a', fontSize: 15, lineHeight: 1.7, fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 20 }}>
|
||||
서울특별시 마포구 연남동 224-14<br />연남로 68 르 쁘띠 포르
|
||||
</p>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||
{[
|
||||
{ icon: 'M', text: '2호선 홍대입구역 3번 출구 도보 5분' },
|
||||
{ icon: 'T', text: '02-334-5678' },
|
||||
{ icon: '@', text: '@lepetitfort_seoul' },
|
||||
].map((info) => (
|
||||
<div key={info.text} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{ width: 24, height: 24, borderRadius: '50%', background: 'rgba(251,191,36,0.15)', border: '1px solid rgba(251,191,36,0.3)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, fontWeight: 700, color: '#fbbf24', fontFamily: 'Playfair Display, serif', flexShrink: 0 }}>
|
||||
{info.icon}
|
||||
</div>
|
||||
<span style={{ color: '#fcd34d', fontSize: 13, fontFamily: "'Noto Sans KR', sans-serif" }}>{info.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA — Custom Cake */}
|
||||
<section style={{ background: 'linear-gradient(135deg, #fef3c7, #fde68a)', padding: '72px 48px', textAlign: 'center' }}>
|
||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'rgba(180,83,9,0.1)', borderRadius: 100, padding: '5px 14px', marginBottom: 20 }}>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12"><path d="M6 1l1.3 2.6L10 4.1 8 6.1l.5 2.9L6 7.6 3.5 9l.5-2.9-2-2 2.7-.5z" fill="#b45309" /></svg>
|
||||
<span style={{ fontSize: 12, color: '#b45309', fontFamily: "'Noto Sans KR', sans-serif", fontWeight: 700 }}>Custom Order</span>
|
||||
</div>
|
||||
<h2 style={{ fontFamily: 'Playfair Display, serif', fontSize: 38, color: '#451a03', marginBottom: 14 }}>
|
||||
특별한 날을 위한 케이크
|
||||
</h2>
|
||||
<p style={{ color: '#78350f', fontSize: 16, lineHeight: 1.75, marginBottom: 12, fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
생일, 기념일, 웨딩 케이크까지.<br />
|
||||
최소 3일 전 예약 시 원하시는 케이크를 제작해드립니다.
|
||||
</p>
|
||||
<p style={{ color: '#a78060', fontSize: 13, fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 32 }}>
|
||||
가격 상담 무료 · 사진 참고 가능 · 직접 수령 또는 배달 가능
|
||||
</p>
|
||||
<button className="bk-btn-primary" style={{
|
||||
background: '#b45309', color: 'white', border: 'none',
|
||||
padding: '15px 44px', borderRadius: 28, fontSize: 16, fontWeight: 700,
|
||||
cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif",
|
||||
boxShadow: '0 8px 28px rgba(180,83,9,0.4)',
|
||||
}}>
|
||||
케이크 주문 예약하기 →
|
||||
</button>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer style={{ background: '#1c1008', padding: '28px 48px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ color: '#78350f', fontSize: 14, fontFamily: 'Playfair Display, serif', fontStyle: 'italic', marginBottom: 4 }}>
|
||||
Le Petit Fort — Artisan Boulangerie
|
||||
</div>
|
||||
<div style={{ color: '#3c1a08', fontSize: 12, fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
© 2024 르 쁘띠 포르. All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 16 }}>
|
||||
{['Instagram', 'Naver Map', 'Kakao'].map((s) => (
|
||||
<span key={s} style={{ fontSize: 12, color: '#78350f', fontFamily: "'Noto Sans KR', sans-serif", cursor: 'pointer' }}>{s}</span>
|
||||
))}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
279
app/work/website/samples/corporate/page.tsx
Normal file
279
app/work/website/samples/corporate/page.tsx
Normal file
@@ -0,0 +1,279 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function CorporateSample() {
|
||||
const services = [
|
||||
{
|
||||
title: 'IT 인프라 구축',
|
||||
desc: '기업 맞춤형 서버 환경 설계부터 클라우드 마이그레이션까지, 안정적인 IT 기반을 구축합니다.',
|
||||
detail: '온프레미스 · 하이브리드 클라우드 · AWS · Azure',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} style={{ width: 28, height: 28 }}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 12H3a9 9 0 1018 0h-2M12 3v4m0 10v4M4.22 4.22l2.83 2.83m9.9 9.9l2.83 2.83M3 12h.01M20.99 12H21M4.22 19.78l2.83-2.83m9.9-9.9l2.83-2.83" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '사이버 보안 솔루션',
|
||||
desc: '최신 위협에 대응하는 엔터프라이즈급 보안 시스템. 침해사고 예방부터 대응까지 통합 관리합니다.',
|
||||
detail: 'ISMS · 취약점 분석 · 보안 모니터링 · 컴플라이언스',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} style={{ width: 28, height: 28 }}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '디지털 전환 (DX)',
|
||||
desc: '레거시 시스템을 현대화하고 비즈니스 프로세스를 자동화하는 DX 컨설팅을 제공합니다.',
|
||||
detail: 'ERP 연동 · 프로세스 자동화 · 데이터 시각화 · AI 도입',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} style={{ width: 28, height: 28 }}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3v11.25A2.25 2.25 0 006 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0118 16.5h-2.25m-7.5 0h7.5m-7.5 0l-1 3m8.5-3l1 3m0 0l.5 1.5m-.5-1.5h-9.5m0 0l-.5 1.5m.75-9l3-3 2.148 2.148A12.061 12.061 0 0116.5 7.605" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const stats = [
|
||||
{ num: '15+', label: '년 업력' },
|
||||
{ num: '340+', label: '완료 프로젝트' },
|
||||
{ num: '180+', label: '기업 고객사' },
|
||||
{ num: '99.9%', label: '서비스 가동률' },
|
||||
];
|
||||
|
||||
const certs = ['AWS Partner', 'ISO 27001', 'ISMS-P 인증', 'Microsoft Partner', 'Google Cloud'];
|
||||
|
||||
const clients = ['삼성전자', 'LG유플러스', '현대모비스', 'SK하이닉스', 'KT', '신한은행', 'NH농협', '롯데정보통신'];
|
||||
|
||||
const testimonials = [
|
||||
{
|
||||
quote: '마이그레이션 기간 동안 단 한 번의 서비스 중단도 없었습니다. 완벽한 이전이었습니다.',
|
||||
name: '이○○ CTO',
|
||||
company: '핀테크 스타트업',
|
||||
},
|
||||
{
|
||||
quote: '보안 감사 이후 취약점 제로. 컴플라이언스 대응이 이렇게 빠를 수 있다는 게 놀라웠습니다.',
|
||||
name: '박○○ IT팀장',
|
||||
company: '중견 제조사',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ background: '#ffffff', minHeight: '100vh', color: '#1e293b', fontFamily: 'system-ui, sans-serif' }}>
|
||||
<style>{`
|
||||
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700;800&family=Noto+Sans+KR:wght@300;400;500;700&display=swap');
|
||||
@keyframes fadeInUp { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: translateY(0); } }
|
||||
@keyframes floatDot { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
|
||||
@keyframes gridPan { from { background-position: 0 0; } to { background-position: 60px 60px; } }
|
||||
.corp-card:hover { transform: translateY(-5px); box-shadow: 0 24px 64px rgba(29,78,216,0.1); }
|
||||
.corp-card { transition: transform 0.3s, box-shadow 0.3s; }
|
||||
.corp-client:hover { background: #eff6ff !important; border-color: #bfdbfe !important; color: #1d4ed8 !important; }
|
||||
.corp-client { transition: all 0.2s; }
|
||||
.corp-cert:hover { border-color: #93c5fd !important; }
|
||||
.corp-cert { transition: border-color 0.2s; }
|
||||
.corp-btn-primary:hover { background: #1d4ed8 !important; box-shadow: 0 8px 24px rgba(29,78,216,0.4) !important; }
|
||||
.corp-btn-primary { transition: all 0.2s; }
|
||||
`}</style>
|
||||
|
||||
{/* Back Banner */}
|
||||
<div style={{ background: 'linear-gradient(135deg, #1e1b4b, #312e81)', padding: '10px 24px', display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<Link href="/work/website" style={{ color: '#a5b4fc', fontSize: 13, textDecoration: 'none', fontFamily: "'Noto Sans KR', sans-serif", display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
← 홈페이지 제작 서비스로 돌아가기
|
||||
</Link>
|
||||
<span style={{ color: '#4c1d95' }}>|</span>
|
||||
<span style={{ color: '#6366f1', fontSize: 12, fontFamily: 'Montserrat, sans-serif', fontWeight: 700 }}>SAMPLE · 기업 홈페이지</span>
|
||||
</div>
|
||||
|
||||
{/* Navbar */}
|
||||
<nav style={{ background: '#fff', borderBottom: '1px solid #e2e8f0', padding: '0 48px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', height: 68, position: 'sticky', top: 0, zIndex: 100, boxShadow: '0 2px 16px rgba(0,0,0,0.05)' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{ width: 38, height: 38, borderRadius: 10, background: 'linear-gradient(135deg, #1d4ed8, #1e40af)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white', fontSize: 16, fontWeight: 800, fontFamily: 'Montserrat, sans-serif' }}>T</div>
|
||||
<div>
|
||||
<div style={{ fontSize: 15, fontWeight: 800, color: '#0f172a', fontFamily: 'Montserrat, sans-serif', lineHeight: 1 }}>TechSolution</div>
|
||||
<div style={{ fontSize: 10, color: '#94a3b8', letterSpacing: '0.1em', fontFamily: 'Montserrat, sans-serif' }}>ENTERPRISE IT</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 28, alignItems: 'center' }}>
|
||||
{['회사소개', '서비스', '포트폴리오', '고객사', '채용', '연락처'].map((item) => (
|
||||
<span key={item} style={{ fontSize: 13, color: '#475569', cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif", fontWeight: 500 }}>{item}</span>
|
||||
))}
|
||||
<button className="corp-btn-primary" style={{ background: '#2563eb', color: 'white', border: 'none', padding: '9px 22px', borderRadius: 8, fontSize: 13, fontWeight: 700, cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif", boxShadow: '0 4px 16px rgba(37,99,235,0.3)' }}>
|
||||
문의하기
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Hero */}
|
||||
<section style={{ background: 'linear-gradient(150deg, #0a192f 0%, #0d2757 50%, #0a2060 100%)', padding: '96px 48px 80px', position: 'relative', overflow: 'hidden' }}>
|
||||
{/* Animated grid */}
|
||||
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(59,130,246,0.08) 1px, transparent 1px), linear-gradient(90deg, rgba(59,130,246,0.08) 1px, transparent 1px)', backgroundSize: '60px 60px', animation: 'gridPan 12s linear infinite' }} />
|
||||
<div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse 70% 60% at 75% 50%, rgba(59,130,246,0.12) 0%, transparent 70%)' }} />
|
||||
|
||||
{/* Floating tech nodes */}
|
||||
{[
|
||||
{ x: '68%', y: '20%', delay: '0s', size: 6, color: '#60a5fa' },
|
||||
{ x: '78%', y: '55%', delay: '1s', size: 4, color: '#a78bfa' },
|
||||
{ x: '85%', y: '35%', delay: '2s', size: 8, color: '#34d399' },
|
||||
{ x: '72%', y: '72%', delay: '0.5s', size: 5, color: '#60a5fa' },
|
||||
].map((dot, i) => (
|
||||
<div key={i} style={{ position: 'absolute', left: dot.x, top: dot.y, width: dot.size, height: dot.size, borderRadius: '50%', background: dot.color, boxShadow: `0 0 12px ${dot.color}`, animation: `floatDot 3s ease-in-out ${dot.delay} infinite` }} />
|
||||
))}
|
||||
|
||||
{/* SVG Tech Illustration */}
|
||||
<svg style={{ position: 'absolute', right: 60, top: '50%', transform: 'translateY(-50%)', width: 320, height: 280, opacity: 0.12 }} viewBox="0 0 320 280">
|
||||
<rect x="40" y="40" width="100" height="70" rx="8" stroke="#60a5fa" strokeWidth="1.5" fill="none"/>
|
||||
<rect x="180" y="40" width="100" height="70" rx="8" stroke="#a78bfa" strokeWidth="1.5" fill="none"/>
|
||||
<rect x="110" y="170" width="100" height="70" rx="8" stroke="#34d399" strokeWidth="1.5" fill="none"/>
|
||||
<line x1="140" y1="110" x2="160" y2="170" stroke="#60a5fa" strokeWidth="1" strokeDasharray="4 4"/>
|
||||
<line x1="180" y1="110" x2="160" y2="170" stroke="#a78bfa" strokeWidth="1" strokeDasharray="4 4"/>
|
||||
<circle cx="90" cy="75" r="16" stroke="#60a5fa" strokeWidth="1" fill="rgba(96,165,250,0.1)"/>
|
||||
<circle cx="230" cy="75" r="16" stroke="#a78bfa" strokeWidth="1" fill="rgba(167,139,250,0.1)"/>
|
||||
<circle cx="160" cy="205" r="20" stroke="#34d399" strokeWidth="1" fill="rgba(52,211,153,0.1)"/>
|
||||
</svg>
|
||||
|
||||
<div style={{ maxWidth: 680, position: 'relative', animation: 'fadeInUp 0.8s ease forwards' }}>
|
||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, background: 'rgba(96,165,250,0.1)', border: '1px solid rgba(96,165,250,0.25)', padding: '6px 16px', borderRadius: 4, marginBottom: 28, fontFamily: 'Montserrat, sans-serif' }}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#34d399', boxShadow: '0 0 8px rgba(52,211,153,0.8)' }} />
|
||||
<span style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', color: '#60a5fa', textTransform: 'uppercase' }}>Enterprise IT Solutions</span>
|
||||
</div>
|
||||
<h1 style={{ fontFamily: 'Montserrat, sans-serif', fontSize: 'clamp(34px, 4vw, 54px)', fontWeight: 800, color: 'white', lineHeight: 1.2, marginBottom: 20 }}>
|
||||
디지털 혁신으로<br />
|
||||
<span style={{ background: 'linear-gradient(90deg, #60a5fa, #a78bfa)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>비즈니스의 미래</span>를<br />
|
||||
설계합니다
|
||||
</h1>
|
||||
<p style={{ color: '#94a3b8', fontSize: 16, lineHeight: 1.85, marginBottom: 36, fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
15년의 경험과 검증된 기술력으로 기업의 IT 인프라를 혁신합니다.<br />
|
||||
클라우드, 보안, 디지털 전환까지 원스톱으로 제공합니다.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 14, flexWrap: 'wrap' }}>
|
||||
<button className="corp-btn-primary" style={{ background: '#2563eb', color: 'white', border: 'none', padding: '14px 32px', borderRadius: 8, fontSize: 15, fontWeight: 700, cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif", boxShadow: '0 8px 24px rgba(37,99,235,0.35)' }}>
|
||||
서비스 상담 신청 →
|
||||
</button>
|
||||
<button style={{ background: 'transparent', color: '#cbd5e1', border: '1px solid rgba(255,255,255,0.18)', padding: '14px 32px', borderRadius: 8, fontSize: 15, fontWeight: 600, cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
회사 소개서 다운로드
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats Bar */}
|
||||
<section style={{ background: '#1d4ed8', padding: '36px 48px' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0 }}>
|
||||
{stats.map((s, i) => (
|
||||
<div key={i} style={{ textAlign: 'center', padding: '12px 24px', borderRight: i < 3 ? '1px solid rgba(255,255,255,0.2)' : 'none' }}>
|
||||
<div style={{ fontSize: 38, fontWeight: 800, color: 'white', fontFamily: 'Montserrat, sans-serif', lineHeight: 1 }}>{s.num}</div>
|
||||
<div style={{ fontSize: 13, color: '#bfdbfe', fontFamily: "'Noto Sans KR', sans-serif", marginTop: 6 }}>{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Certifications */}
|
||||
<section style={{ background: '#f8fafc', padding: '28px 48px', borderBottom: '1px solid #e2e8f0' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto', display: 'flex', alignItems: 'center', gap: 24, flexWrap: 'wrap', justifyContent: 'center' }}>
|
||||
<span style={{ fontSize: 11, fontWeight: 700, color: '#94a3b8', letterSpacing: '0.15em', textTransform: 'uppercase', fontFamily: 'Montserrat, sans-serif', flexShrink: 0 }}>인증 · 파트너십</span>
|
||||
{certs.map((c) => (
|
||||
<div key={c} className="corp-cert" style={{ padding: '8px 18px', border: '1px solid #e2e8f0', borderRadius: 6, background: 'white', fontSize: 12, fontWeight: 700, color: '#475569', fontFamily: 'Montserrat, sans-serif', letterSpacing: '0.03em' }}>
|
||||
{c}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Services */}
|
||||
<section style={{ padding: '88px 48px', background: 'white' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 56 }}>
|
||||
<div style={{ fontSize: 11, color: '#2563eb', fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', fontFamily: 'Montserrat, sans-serif', marginBottom: 12 }}>Our Services</div>
|
||||
<h2 style={{ fontFamily: 'Montserrat, sans-serif', fontSize: 36, fontWeight: 800, color: '#0f172a', marginBottom: 14 }}>핵심 서비스</h2>
|
||||
<p style={{ color: '#64748b', fontSize: 16, fontFamily: "'Noto Sans KR', sans-serif" }}>기업의 성장을 이끄는 검증된 IT 솔루션</p>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: 28 }}>
|
||||
{services.map((svc, i) => (
|
||||
<div key={svc.title} className="corp-card" style={{ padding: 36, borderRadius: 18, background: 'white', border: '1px solid #e2e8f0', boxShadow: '0 4px 24px rgba(0,0,0,0.05)', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 3, background: ['linear-gradient(90deg, #2563eb, #60a5fa)', 'linear-gradient(90deg, #7c3aed, #a78bfa)', 'linear-gradient(90deg, #059669, #34d399)'][i] }} />
|
||||
<div style={{ width: 56, height: 56, borderRadius: 14, background: ['#eff6ff', '#f5f3ff', '#f0fdf4'][i], border: `1px solid ${['#bfdbfe', '#ddd6fe', '#bbf7d0'][i]}`, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 22, color: ['#2563eb', '#7c3aed', '#059669'][i] }}>
|
||||
{svc.icon}
|
||||
</div>
|
||||
<h3 style={{ fontSize: 19, fontWeight: 700, color: '#0f172a', fontFamily: 'Montserrat, sans-serif', marginBottom: 12 }}>{svc.title}</h3>
|
||||
<p style={{ fontSize: 14, color: '#64748b', lineHeight: 1.8, fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 16 }}>{svc.desc}</p>
|
||||
<div style={{ padding: '10px 14px', background: '#f8fafc', borderRadius: 8, fontSize: 12, color: '#94a3b8', fontFamily: 'Montserrat, sans-serif', letterSpacing: '0.02em' }}>{svc.detail}</div>
|
||||
<div style={{ marginTop: 20, color: '#2563eb', fontSize: 13, fontWeight: 700, cursor: 'pointer', fontFamily: 'Montserrat, sans-serif', display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
자세히 보기
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Testimonials */}
|
||||
<section style={{ padding: '80px 48px', background: 'linear-gradient(135deg, #f0f7ff, #f8f5ff)' }}>
|
||||
<div style={{ maxWidth: 900, margin: '0 auto' }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 48 }}>
|
||||
<div style={{ fontSize: 11, color: '#2563eb', fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', fontFamily: 'Montserrat, sans-serif', marginBottom: 12 }}>Client Reviews</div>
|
||||
<h2 style={{ fontFamily: 'Montserrat, sans-serif', fontSize: 32, fontWeight: 800, color: '#0f172a' }}>고객 후기</h2>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24 }}>
|
||||
{testimonials.map((t, i) => (
|
||||
<div key={i} style={{ background: 'white', borderRadius: 16, padding: '32px 28px', border: '1px solid #e2e8f0', boxShadow: '0 4px 20px rgba(0,0,0,0.05)' }}>
|
||||
<div style={{ fontSize: 32, color: '#bfdbfe', fontFamily: 'Georgia, serif', lineHeight: 1, marginBottom: 16 }}>"</div>
|
||||
<p style={{ fontSize: 15, color: '#475569', lineHeight: 1.8, fontFamily: "'Noto Sans KR', sans-serif", marginBottom: 20 }}>{t.quote}</p>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<div style={{ width: 40, height: 40, borderRadius: '50%', background: 'linear-gradient(135deg, #2563eb, #7c3aed)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white', fontSize: 15, fontWeight: 700 }}>
|
||||
{t.name[0]}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: 13, fontWeight: 700, color: '#0f172a', fontFamily: "'Noto Sans KR', sans-serif" }}>{t.name}</div>
|
||||
<div style={{ fontSize: 12, color: '#94a3b8', fontFamily: "'Noto Sans KR', sans-serif" }}>{t.company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Clients */}
|
||||
<section style={{ padding: '72px 48px', background: 'white' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto', textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 11, color: '#94a3b8', fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', fontFamily: 'Montserrat, sans-serif', marginBottom: 36 }}>Trusted By Leading Companies</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 12 }}>
|
||||
{clients.map((c) => (
|
||||
<div key={c} className="corp-client" style={{ padding: '14px 28px', borderRadius: 10, border: '1px solid #e2e8f0', background: '#f8fafc', fontSize: 14, fontWeight: 600, color: '#475569', fontFamily: "'Noto Sans KR', sans-serif", cursor: 'pointer' }}>{c}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact CTA */}
|
||||
<section style={{ background: 'linear-gradient(150deg, #0a192f, #0d2757)', padding: '88px 48px', textAlign: 'center' }}>
|
||||
<div style={{ maxWidth: 600, margin: '0 auto' }}>
|
||||
<div style={{ fontSize: 11, color: '#60a5fa', fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', fontFamily: 'Montserrat, sans-serif', marginBottom: 20 }}>Get Started</div>
|
||||
<h2 style={{ fontFamily: 'Montserrat, sans-serif', fontSize: 38, fontWeight: 800, color: 'white', marginBottom: 16, lineHeight: 1.2 }}>
|
||||
프로젝트를 시작하세요
|
||||
</h2>
|
||||
<p style={{ color: '#94a3b8', fontSize: 16, lineHeight: 1.8, marginBottom: 36, fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
IT 솔루션이 필요하시면 언제든지 연락해주세요.<br />
|
||||
전담 컨설턴트가 최적의 방안을 제안해드립니다.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
|
||||
<button className="corp-btn-primary" style={{ background: '#2563eb', color: 'white', border: 'none', padding: '15px 40px', borderRadius: 8, fontSize: 15, fontWeight: 700, cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif", boxShadow: '0 8px 24px rgba(37,99,235,0.4)' }}>
|
||||
무료 상담 신청
|
||||
</button>
|
||||
<button style={{ background: 'transparent', color: '#cbd5e1', border: '1px solid rgba(255,255,255,0.18)', padding: '15px 40px', borderRadius: 8, fontSize: 15, fontWeight: 600, cursor: 'pointer', fontFamily: "'Noto Sans KR', sans-serif" }}>
|
||||
02-1234-5678
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer style={{ background: '#020817', padding: '28px 48px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 12 }}>
|
||||
<div style={{ color: '#334155', fontSize: 13, fontFamily: "'Noto Sans KR', sans-serif" }}>© 2024 ㈜테크솔루션. All rights reserved.</div>
|
||||
<div style={{ color: '#1e293b', fontSize: 12, fontFamily: 'Montserrat, sans-serif' }}>서울특별시 강남구 테헤란로 123 테크타워 15F</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
436
app/work/website/samples/dashboard/page.tsx
Normal file
436
app/work/website/samples/dashboard/page.tsx
Normal file
@@ -0,0 +1,436 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
const kpis = [
|
||||
{ label: '월 활성 사용자', value: '124,832', change: '+12.4%', up: true, color: '#3b82f6', sparkline: [40, 55, 45, 70, 60, 85, 100] },
|
||||
{ label: '월 매출', value: '₩284M', change: '+8.7%', up: true, color: '#10b981', sparkline: [50, 60, 55, 75, 80, 78, 100] },
|
||||
{ label: '전환율', value: '3.62%', change: '-0.3%', up: false, color: '#f59e0b', sparkline: [90, 80, 85, 75, 70, 72, 68] },
|
||||
{ label: '고객 만족도', value: '4.8', change: '+0.2', up: true, color: '#8b5cf6', sparkline: [70, 72, 74, 76, 78, 80, 85] },
|
||||
];
|
||||
|
||||
const lineData = [
|
||||
{ month: 'Jan', revenue: 65, users: 48 },
|
||||
{ month: 'Feb', revenue: 78, users: 55 },
|
||||
{ month: 'Mar', revenue: 72, users: 52 },
|
||||
{ month: 'Apr', revenue: 89, users: 64 },
|
||||
{ month: 'May', revenue: 95, users: 71 },
|
||||
{ month: 'Jun', revenue: 82, users: 66 },
|
||||
{ month: 'Jul', revenue: 110, users: 88 },
|
||||
{ month: 'Aug', revenue: 128, users: 100 },
|
||||
];
|
||||
|
||||
const activities = [
|
||||
{ user: 'lee@company.com', action: '프리미엄 플랜 구독', time: '2분 전', status: 'success', avatar: 'L' },
|
||||
{ user: 'park@startup.io', action: 'API 한도 초과 경고', time: '14분 전', status: 'warning', avatar: 'P' },
|
||||
{ user: 'kim@corp.kr', action: '팀 멤버 5명 초대', time: '31분 전', status: 'info', avatar: 'K' },
|
||||
{ user: 'choi@brand.com', action: '결제 실패 (카드 만료)', time: '1시간 전', status: 'error', avatar: 'C' },
|
||||
{ user: 'jung@agency.co', action: '새 워크스페이스 생성', time: '2시간 전', status: 'success', avatar: 'J' },
|
||||
];
|
||||
|
||||
const menus = [
|
||||
{ id: 'overview', label: 'Overview', dot: '#3b82f6' },
|
||||
{ id: 'analytics', label: 'Analytics', dot: '#10b981' },
|
||||
{ id: 'users', label: 'Users', dot: null },
|
||||
{ id: 'revenue', label: 'Revenue', dot: null },
|
||||
{ id: 'reports', label: 'Reports', dot: null },
|
||||
{ id: 'settings', label: 'Settings', dot: null },
|
||||
];
|
||||
|
||||
const channels = [
|
||||
{ label: 'Organic Search', val: 78, color: '#3b82f6' },
|
||||
{ label: 'Direct', val: 55, color: '#10b981' },
|
||||
{ label: 'Social Media', val: 42, color: '#a855f7' },
|
||||
{ label: 'Email', val: 34, color: '#f59e0b' },
|
||||
{ label: 'Referral', val: 20, color: '#ec4899' },
|
||||
];
|
||||
|
||||
const alerts = [
|
||||
{ type: 'error', msg: 'API 응답 지연 (p99 > 2s)', time: '5분 전' },
|
||||
{ type: 'warning', msg: '스토리지 사용량 85% 초과', time: '32분 전' },
|
||||
{ type: 'success', msg: '일일 백업 완료', time: '1시간 전' },
|
||||
];
|
||||
|
||||
const statusColor: Record<string, string> = { success: '#10b981', warning: '#f59e0b', error: '#ef4444', info: '#3b82f6' };
|
||||
|
||||
function SparkLine({ data, color }: { data: number[]; color: string }) {
|
||||
const max = Math.max(...data);
|
||||
const min = Math.min(...data);
|
||||
const h = 28;
|
||||
const w = 72;
|
||||
const step = w / (data.length - 1);
|
||||
const pts = data.map((v, i) => {
|
||||
const x = i * step;
|
||||
const y = h - ((v - min) / (max - min || 1)) * h;
|
||||
return `${x},${y}`;
|
||||
}).join(' ');
|
||||
return (
|
||||
<svg width={w} height={h} viewBox={`0 0 ${w} ${h}`}>
|
||||
<polyline points={pts} fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.7" />
|
||||
<polyline points={`${pts} ${w},${h} 0,${h}`} fill={color} fillOpacity="0.08" stroke="none" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function LineChart({ data }: { data: typeof lineData }) {
|
||||
const chartH = 130;
|
||||
const chartW = 440;
|
||||
const padL = 32;
|
||||
const padB = 24;
|
||||
const innerW = chartW - padL;
|
||||
const innerH = chartH - padB;
|
||||
const maxRev = Math.max(...data.map(d => d.revenue));
|
||||
|
||||
const revPts = data.map((d, i) => {
|
||||
const x = padL + (i / (data.length - 1)) * innerW;
|
||||
const y = (1 - d.revenue / maxRev) * innerH;
|
||||
return `${x},${y}`;
|
||||
}).join(' ');
|
||||
|
||||
const usrPts = data.map((d, i) => {
|
||||
const x = padL + (i / (data.length - 1)) * innerW;
|
||||
const y = (1 - d.users / maxRev) * innerH;
|
||||
return `${x},${y}`;
|
||||
}).join(' ');
|
||||
|
||||
return (
|
||||
<svg width="100%" viewBox={`0 0 ${chartW} ${chartH}`} preserveAspectRatio="xMidYMid meet">
|
||||
{/* Grid lines */}
|
||||
{[0, 0.25, 0.5, 0.75, 1].map((t, i) => (
|
||||
<g key={i}>
|
||||
<line x1={padL} y1={t * innerH} x2={chartW} y2={t * innerH} stroke="rgba(255,255,255,0.04)" strokeWidth="1" />
|
||||
<text x={padL - 4} y={t * innerH + 4} textAnchor="end" fontSize="8" fill="#1e3a5f">
|
||||
{Math.round(maxRev * (1 - t))}
|
||||
</text>
|
||||
</g>
|
||||
))}
|
||||
{/* Area fills */}
|
||||
<defs>
|
||||
<linearGradient id="grad-rev" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#3b82f6" stopOpacity="0.25" />
|
||||
<stop offset="100%" stopColor="#3b82f6" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id="grad-usr" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#10b981" stopOpacity="0.2" />
|
||||
<stop offset="100%" stopColor="#10b981" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<polyline points={`${revPts} ${chartW},${innerH} ${padL},${innerH}`} fill="url(#grad-rev)" stroke="none" />
|
||||
<polyline points={`${usrPts} ${chartW},${innerH} ${padL},${innerH}`} fill="url(#grad-usr)" stroke="none" />
|
||||
{/* Lines */}
|
||||
<polyline points={revPts} fill="none" stroke="#3b82f6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<polyline points={usrPts} fill="none" stroke="#10b981" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" strokeDasharray="4 2" />
|
||||
{/* Dots at last point */}
|
||||
{[{ pts: revPts, color: '#3b82f6' }, { pts: usrPts, color: '#10b981' }].map(({ pts: p, color }) => {
|
||||
const last = p.split(' ').pop()!;
|
||||
const [lx, ly] = last.split(',').map(Number);
|
||||
return <circle key={color} cx={lx} cy={ly} r="3.5" fill={color} stroke="#0f172a" strokeWidth="2" />;
|
||||
})}
|
||||
{/* X axis labels */}
|
||||
{data.map((d, i) => (
|
||||
<text key={i} x={padL + (i / (data.length - 1)) * innerW} y={innerH + 16} textAnchor="middle" fontSize="8" fill="#334155">{d.month}</text>
|
||||
))}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DashboardSample() {
|
||||
const [activeMenu, setActiveMenu] = useState('overview');
|
||||
const [alertsVisible, setAlertsVisible] = useState(true);
|
||||
|
||||
return (
|
||||
<div style={{ background: '#0a0f1e', minHeight: '100vh', color: 'white' }}>
|
||||
<style>{`
|
||||
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700&family=DM+Mono:wght@400;500&display=swap');
|
||||
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||
@keyframes pulse-dot { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
|
||||
.dash-menu-item:hover { background: rgba(255,255,255,0.05) !important; }
|
||||
.dash-menu-item { transition: background 0.15s; }
|
||||
.dash-kpi:hover { border-color: rgba(255,255,255,0.1) !important; transform: translateY(-2px); }
|
||||
.dash-kpi { transition: border-color 0.2s, transform 0.2s; cursor: default; }
|
||||
.dash-row:hover { background: rgba(255,255,255,0.03) !important; }
|
||||
.dash-row { transition: background 0.15s; }
|
||||
.quick-action:hover { background: rgba(59,130,246,0.12) !important; border-color: rgba(59,130,246,0.3) !important; }
|
||||
.quick-action { transition: background 0.2s, border-color 0.2s; cursor: pointer; }
|
||||
`}</style>
|
||||
|
||||
{/* Back Banner */}
|
||||
<div style={{ background: 'linear-gradient(135deg, #1e1b4b, #312e81)', padding: '10px 24px', display: 'flex', alignItems: 'center', gap: 12, position: 'relative', zIndex: 200 }}>
|
||||
<Link href="/work/website" style={{ color: '#a5b4fc', fontSize: 13, textDecoration: 'none', fontFamily: 'DM Sans, sans-serif' }}>
|
||||
← 홈페이지 제작 서비스로 돌아가기
|
||||
</Link>
|
||||
<span style={{ color: '#4c1d95' }}>|</span>
|
||||
<span style={{ color: '#38bdf8', fontSize: 12, fontFamily: 'DM Mono, monospace', fontWeight: 500 }}>
|
||||
SAMPLE · 관리자 대시보드
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', height: 'calc(100vh - 40px)' }}>
|
||||
{/* Sidebar */}
|
||||
<aside style={{ width: 220, background: '#060b18', borderRight: '1px solid rgba(255,255,255,0.05)', display: 'flex', flexDirection: 'column', flexShrink: 0 }}>
|
||||
{/* Logo */}
|
||||
<div style={{ padding: '18px 18px 14px', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{ width: 34, height: 34, borderRadius: 9, background: 'linear-gradient(135deg, #3b82f6, #06b6d4)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
{/* DF logo SVG */}
|
||||
<svg width="16" height="14" viewBox="0 0 16 14" fill="none">
|
||||
<rect x="0" y="0" width="7" height="6" rx="1" fill="white" opacity="0.9" />
|
||||
<rect x="0" y="8" width="7" height="6" rx="1" fill="white" opacity="0.6" />
|
||||
<rect x="9" y="0" width="7" height="14" rx="1" fill="white" opacity="0.4" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: 'white', fontFamily: 'DM Sans, sans-serif' }}>DataFlow</div>
|
||||
<div style={{ fontSize: 10, color: '#334155', fontFamily: 'DM Mono, monospace' }}>v2.4.1 · PRO</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div style={{ padding: '12px 10px 8px' }}>
|
||||
<div style={{ fontSize: 9, color: '#1e3a5f', fontFamily: 'DM Mono, monospace', letterSpacing: '0.15em', padding: '4px 10px 6px', textTransform: 'uppercase' }}>QUICK ACTIONS</div>
|
||||
<div style={{ display: 'flex', gap: 6, padding: '0 2px' }}>
|
||||
{[
|
||||
{ label: 'Export', color: '#3b82f6' },
|
||||
{ label: 'Invite', color: '#10b981' },
|
||||
{ label: 'Alert', color: '#f59e0b' },
|
||||
].map(a => (
|
||||
<button key={a.label} className="quick-action" style={{ flex: 1, padding: '6px 4px', borderRadius: 6, border: '1px solid rgba(255,255,255,0.05)', background: 'transparent', color: a.color, fontSize: 10, fontFamily: 'DM Mono, monospace', fontWeight: 500 }}>
|
||||
{a.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Nav */}
|
||||
<nav style={{ flex: 1, padding: '4px 10px', overflowY: 'auto' }}>
|
||||
<div style={{ fontSize: 9, color: '#1e3a5f', fontFamily: 'DM Mono, monospace', letterSpacing: '0.15em', padding: '8px 10px 4px', textTransform: 'uppercase' }}>MAIN</div>
|
||||
{menus.map((m) => (
|
||||
<button key={m.id} className="dash-menu-item" onClick={() => setActiveMenu(m.id)} style={{
|
||||
width: '100%', textAlign: 'left', padding: '9px 12px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
||||
display: 'flex', alignItems: 'center', gap: 10, marginBottom: 2,
|
||||
background: activeMenu === m.id ? 'rgba(59,130,246,0.15)' : 'transparent',
|
||||
color: activeMenu === m.id ? '#60a5fa' : '#475569',
|
||||
}}>
|
||||
{/* Menu icon SVG */}
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" style={{ flexShrink: 0 }}>
|
||||
<rect x="1" y="1" width="5" height="5" rx="1.5" fill={activeMenu === m.id ? '#60a5fa' : '#1e3a5f'} />
|
||||
<rect x="8" y="1" width="5" height="5" rx="1.5" fill={activeMenu === m.id ? '#60a5fa' : '#1e3a5f'} opacity="0.6" />
|
||||
<rect x="1" y="8" width="5" height="5" rx="1.5" fill={activeMenu === m.id ? '#60a5fa' : '#1e3a5f'} opacity="0.6" />
|
||||
<rect x="8" y="8" width="5" height="5" rx="1.5" fill={activeMenu === m.id ? '#60a5fa' : '#1e3a5f'} opacity="0.4" />
|
||||
</svg>
|
||||
<span style={{ fontSize: 13, fontWeight: activeMenu === m.id ? 600 : 400, fontFamily: 'DM Sans, sans-serif', flex: 1 }}>
|
||||
{m.label}
|
||||
</span>
|
||||
{m.dot && <div style={{ width: 4, height: 4, borderRadius: '50%', background: m.dot, animation: 'pulse-dot 2s infinite' }} />}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* User */}
|
||||
<div style={{ padding: '12px 14px', borderTop: '1px solid rgba(255,255,255,0.05)' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{ width: 32, height: 32, borderRadius: '50%', background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700 }}>A</div>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 12, fontWeight: 600, color: '#e2e8f0', fontFamily: 'DM Sans, sans-serif', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>Admin</div>
|
||||
<div style={{ fontSize: 10, color: '#334155', fontFamily: 'DM Mono, monospace' }}>Super Admin</div>
|
||||
</div>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#10b981', flexShrink: 0 }} />
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Main */}
|
||||
<main style={{ flex: 1, overflowY: 'auto', animation: 'fadeIn 0.4s ease' }}>
|
||||
{/* Top bar */}
|
||||
<div style={{ padding: '14px 24px', borderBottom: '1px solid rgba(255,255,255,0.05)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: '#080d1a', position: 'sticky', top: 0, zIndex: 10 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" style={{ position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)' }}>
|
||||
<circle cx="7" cy="7" r="5" stroke="#334155" strokeWidth="1.5" />
|
||||
<line x1="11" y1="11" x2="14" y2="14" stroke="#334155" strokeWidth="1.5" strokeLinecap="round" />
|
||||
</svg>
|
||||
<input placeholder="검색..." style={{ background: '#0f172a', border: '1px solid rgba(255,255,255,0.06)', color: '#94a3b8', padding: '7px 12px 7px 32px', borderRadius: 8, fontSize: 12, fontFamily: 'DM Sans, sans-serif', outline: 'none', width: 180 }} />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
|
||||
<select style={{ background: '#0f172a', border: '1px solid rgba(255,255,255,0.07)', color: '#94a3b8', padding: '7px 12px', borderRadius: 8, fontSize: 12, fontFamily: 'DM Sans, sans-serif', cursor: 'pointer' }}>
|
||||
<option>최근 30일</option>
|
||||
</select>
|
||||
{/* Alert bell */}
|
||||
<button onClick={() => setAlertsVisible(!alertsVisible)} style={{ position: 'relative', background: '#0f172a', border: '1px solid rgba(255,255,255,0.07)', padding: '7px 10px', borderRadius: 8, cursor: 'pointer', display: 'flex' }}>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<path d="M7 1.5C4.5 1.5 3 3.2 3 5.5v3L1.5 10.5h11L11 8.5v-3C11 3.2 9.5 1.5 7 1.5z" stroke="#94a3b8" strokeWidth="1.2" fill="none" />
|
||||
<path d="M5.5 10.5a1.5 1.5 0 003 0" stroke="#94a3b8" strokeWidth="1.2" />
|
||||
</svg>
|
||||
<div style={{ position: 'absolute', top: 5, right: 5, width: 7, height: 7, borderRadius: '50%', background: '#ef4444', border: '1.5px solid #080d1a' }} />
|
||||
</button>
|
||||
<button style={{ background: '#3b82f6', border: 'none', color: 'white', padding: '7px 16px', borderRadius: 8, fontSize: 12, fontWeight: 600, cursor: 'pointer', fontFamily: 'DM Sans, sans-serif' }}>
|
||||
리포트 내보내기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: '20px 24px' }}>
|
||||
{/* Alerts panel */}
|
||||
{alertsVisible && (
|
||||
<div style={{ marginBottom: 20, padding: '14px 18px', borderRadius: 12, background: '#0f172a', border: '1px solid rgba(255,255,255,0.06)' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 600, color: 'white', fontFamily: 'DM Sans, sans-serif' }}>시스템 알림</div>
|
||||
<button onClick={() => setAlertsVisible(false)} style={{ background: 'none', border: 'none', color: '#334155', fontSize: 11, cursor: 'pointer', fontFamily: 'DM Sans, sans-serif' }}>닫기</button>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
{alerts.map((a, i) => (
|
||||
<div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', borderRadius: 8, background: statusColor[a.type] + '10', border: `1px solid ${statusColor[a.type]}25`, flex: 1 }}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: statusColor[a.type], flexShrink: 0 }} />
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 12, color: '#e2e8f0', fontFamily: 'DM Sans, sans-serif', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{a.msg}</div>
|
||||
<div style={{ fontSize: 10, color: '#334155', fontFamily: 'DM Mono, monospace' }}>{a.time}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Page header */}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
|
||||
<div>
|
||||
<h1 style={{ fontFamily: 'DM Sans, sans-serif', fontSize: 22, fontWeight: 700, color: 'white', marginBottom: 2 }}>Overview</h1>
|
||||
<div style={{ fontSize: 12, color: '#334155', fontFamily: 'DM Mono, monospace' }}>2024.08.14 · 오전 10:32 업데이트</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* KPI Cards */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14, marginBottom: 18 }}>
|
||||
{kpis.map((kpi) => (
|
||||
<div key={kpi.label} className="dash-kpi" style={{ padding: '16px 18px', borderRadius: 14, background: '#0f172a', border: '1px solid rgba(255,255,255,0.06)' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8 }}>
|
||||
<div style={{ fontSize: 11, color: '#475569', fontFamily: 'DM Sans, sans-serif', fontWeight: 500, maxWidth: 80 }}>{kpi.label}</div>
|
||||
<SparkLine data={kpi.sparkline} color={kpi.color} />
|
||||
</div>
|
||||
<div style={{ fontSize: 24, fontWeight: 700, color: 'white', fontFamily: 'DM Sans, sans-serif', marginBottom: 6 }}>{kpi.value}</div>
|
||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 3, background: kpi.up ? 'rgba(16,185,129,0.12)' : 'rgba(239,68,68,0.12)', borderRadius: 6, padding: '2px 7px', fontSize: 11, fontWeight: 700, color: kpi.up ? '#10b981' : '#ef4444', fontFamily: 'DM Mono, monospace' }}>
|
||||
{kpi.up ? '↑' : '↓'} {kpi.change}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Charts row */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr', gap: 14, marginBottom: 18 }}>
|
||||
{/* Line Chart */}
|
||||
<div style={{ padding: '20px 22px', borderRadius: 14, background: '#0f172a', border: '1px solid rgba(255,255,255,0.06)' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: 'white', fontFamily: 'DM Sans, sans-serif' }}>월별 매출 & 사용자 추이</div>
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
|
||||
<div style={{ width: 20, height: 2, background: '#3b82f6', borderRadius: 2 }} />
|
||||
<span style={{ fontSize: 10, color: '#475569', fontFamily: 'DM Mono, monospace' }}>매출</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
|
||||
<div style={{ width: 20, height: 2, background: '#10b981', borderRadius: 2, backgroundImage: 'repeating-linear-gradient(90deg, #10b981 0, #10b981 4px, transparent 4px, transparent 6px)' }} />
|
||||
<span style={{ fontSize: 10, color: '#475569', fontFamily: 'DM Mono, monospace' }}>사용자</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
{['1M', '3M', '6M', '1Y'].map((p) => (
|
||||
<button key={p} style={{ background: p === '6M' ? '#1e3a5f' : 'transparent', border: 'none', color: p === '6M' ? '#60a5fa' : '#334155', padding: '3px 7px', borderRadius: 5, fontSize: 10, cursor: 'pointer', fontFamily: 'DM Mono, monospace' }}>{p}</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LineChart data={lineData} />
|
||||
</div>
|
||||
|
||||
{/* Channel Progress */}
|
||||
<div style={{ padding: '20px 22px', borderRadius: 14, background: '#0f172a', border: '1px solid rgba(255,255,255,0.06)' }}>
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: 'white', fontFamily: 'DM Sans, sans-serif', marginBottom: 18 }}>채널별 전환율</div>
|
||||
{channels.map((p) => (
|
||||
<div key={p.label} style={{ marginBottom: 13 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 5 }}>
|
||||
<span style={{ fontSize: 12, color: '#94a3b8', fontFamily: 'DM Sans, sans-serif' }}>{p.label}</span>
|
||||
<span style={{ fontSize: 11, color: p.color, fontFamily: 'DM Mono, monospace', fontWeight: 500 }}>{p.val}%</span>
|
||||
</div>
|
||||
<div style={{ height: 4, background: '#1e293b', borderRadius: 2, overflow: 'hidden', position: 'relative' }}>
|
||||
<div style={{ height: '100%', borderRadius: 2, background: `linear-gradient(90deg, ${p.color}90, ${p.color})`, width: `${p.val}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* Donut summary */}
|
||||
<div style={{ marginTop: 18, display: 'flex', alignItems: 'center', gap: 14 }}>
|
||||
<svg width="52" height="52" viewBox="0 0 52 52">
|
||||
{(() => {
|
||||
const total = channels.reduce((s, c) => s + c.val, 0);
|
||||
let offset = 0;
|
||||
const r = 20;
|
||||
const circ = 2 * Math.PI * r;
|
||||
return channels.map((c) => {
|
||||
const dash = (c.val / total) * circ;
|
||||
const el = (
|
||||
<circle key={c.label} cx="26" cy="26" r={r} fill="none" stroke={c.color} strokeWidth="8"
|
||||
strokeDasharray={`${dash} ${circ - dash}`}
|
||||
strokeDashoffset={-offset}
|
||||
transform="rotate(-90 26 26)"
|
||||
style={{ transition: 'stroke-dasharray 0.5s' }} />
|
||||
);
|
||||
offset += dash;
|
||||
return el;
|
||||
});
|
||||
})()}
|
||||
<circle cx="26" cy="26" r="12" fill="#0f172a" />
|
||||
<text x="26" y="29" textAnchor="middle" fontSize="8" fill="#60a5fa" fontWeight="700">ALL</text>
|
||||
</svg>
|
||||
<div style={{ fontSize: 11, color: '#475569', fontFamily: 'DM Sans, sans-serif', lineHeight: 1.6 }}>
|
||||
Organic 채널이<br />
|
||||
<span style={{ color: '#3b82f6', fontWeight: 700 }}>최고 전환율 78%</span><br />
|
||||
달성 중
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Activity Table */}
|
||||
<div style={{ borderRadius: 14, background: '#0f172a', border: '1px solid rgba(255,255,255,0.06)', overflow: 'hidden' }}>
|
||||
<div style={{ padding: '14px 18px', borderBottom: '1px solid rgba(255,255,255,0.05)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: 'white', fontFamily: 'DM Sans, sans-serif' }}>최근 활동</div>
|
||||
<button style={{ background: 'none', border: '1px solid rgba(255,255,255,0.08)', color: '#475569', padding: '5px 12px', borderRadius: 6, fontSize: 11, cursor: 'pointer', fontFamily: 'DM Sans, sans-serif' }}>전체 보기</button>
|
||||
</div>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: '1px solid rgba(255,255,255,0.04)' }}>
|
||||
{['사용자', '활동', '시간', '상태'].map((h) => (
|
||||
<th key={h} style={{ padding: '9px 18px', textAlign: 'left', fontSize: 9, color: '#334155', fontFamily: 'DM Mono, monospace', letterSpacing: '0.12em', textTransform: 'uppercase', fontWeight: 500 }}>{h}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{activities.map((a, i) => (
|
||||
<tr key={i} className="dash-row" style={{ borderBottom: '1px solid rgba(255,255,255,0.025)' }}>
|
||||
<td style={{ padding: '11px 18px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<div style={{ width: 26, height: 26, borderRadius: '50%', background: `hsl(${i * 60}, 60%, 40%)`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, fontWeight: 700, flexShrink: 0 }}>{a.avatar}</div>
|
||||
<span style={{ fontFamily: 'DM Mono, monospace', fontSize: 11, color: '#60a5fa' }}>{a.user}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td style={{ padding: '11px 18px', fontFamily: 'DM Sans, sans-serif', fontSize: 13, color: '#94a3b8' }}>{a.action}</td>
|
||||
<td style={{ padding: '11px 18px', fontFamily: 'DM Mono, monospace', fontSize: 11, color: '#334155' }}>{a.time}</td>
|
||||
<td style={{ padding: '11px 18px' }}>
|
||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 5, background: statusColor[a.status] + '12', borderRadius: 6, padding: '3px 8px' }}>
|
||||
<div style={{ width: 5, height: 5, borderRadius: '50%', background: statusColor[a.status] }} />
|
||||
<span style={{ fontSize: 10, color: statusColor[a.status], fontFamily: 'DM Mono, monospace', fontWeight: 500 }}>
|
||||
{a.status === 'success' ? 'OK' : a.status === 'warning' ? 'WARN' : a.status === 'error' ? 'ERR' : 'INFO'}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
428
app/work/website/samples/game/page.tsx
Normal file
428
app/work/website/samples/game/page.tsx
Normal file
@@ -0,0 +1,428 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const rankings = [
|
||||
{ rank: 1, name: 'ShadowViper_KR', score: 9_842, tier: 'GRANDMASTER', wins: 312, kda: '18.4', change: '+2' },
|
||||
{ rank: 2, name: 'NightFalcon', score: 9_610, tier: 'GRANDMASTER', wins: 289, kda: '15.2', change: '0' },
|
||||
{ rank: 3, name: 'Xenon_X', score: 9_241, tier: 'MASTER', wins: 267, kda: '12.9', change: '+1' },
|
||||
{ rank: 4, name: 'KR_Dominator', score: 8_970, tier: 'MASTER', wins: 251, kda: '11.7', change: '-1' },
|
||||
{ rank: 5, name: 'Pulse_Wave', score: 8_834, tier: 'DIAMOND', wins: 238, kda: '10.3', change: '+3' },
|
||||
];
|
||||
|
||||
const modes = [
|
||||
{ id: 'solo', name: 'SOLO', sub: '1 vs 1', desc: '순수한 실력으로 맞붙는 1대1 대결', color: '#06b6d4', players: '12,400', season: 'S7' },
|
||||
{ id: 'duo', name: 'DUO', sub: '2 vs 2', desc: '파트너와 함께하는 전략적 팀플레이', color: '#a855f7', players: '28,700', season: 'S7' },
|
||||
{ id: 'squad', name: 'SQUAD', sub: '5 vs 5', desc: '전략과 팀워크로 승리를 쟁취', color: '#f59e0b', players: '7,100', season: 'S7' },
|
||||
];
|
||||
|
||||
const recentMatches = [
|
||||
{ result: 'WIN', mode: 'DUO', duration: '18:32', kills: 12, deaths: 2, assists: 8, rating: '+32' },
|
||||
{ result: 'WIN', mode: 'SOLO', duration: '22:11', kills: 8, deaths: 1, assists: 4, rating: '+24' },
|
||||
{ result: 'LOSS', mode: 'SQUAD', duration: '31:45', kills: 5, deaths: 5, assists: 12, rating: '-18' },
|
||||
{ result: 'WIN', mode: 'DUO', duration: '15:20', kills: 15, deaths: 3, assists: 6, rating: '+40' },
|
||||
];
|
||||
|
||||
const champions = [
|
||||
{ name: 'VIPER', role: 'Assassin', color: '#06b6d4', power: 92, winRate: '63%' },
|
||||
{ name: 'KIRA', role: 'Support', color: '#ec4899', power: 78, winRate: '58%' },
|
||||
{ name: 'IRON', role: 'Tank', color: '#94a3b8', power: 85, winRate: '55%' },
|
||||
{ name: 'NOVA', role: 'Mage', color: '#a855f7', power: 88, winRate: '61%' },
|
||||
];
|
||||
|
||||
const tierColor: Record<string, string> = {
|
||||
GRANDMASTER: '#fbbf24',
|
||||
MASTER: '#a855f7',
|
||||
DIAMOND: '#60a5fa',
|
||||
};
|
||||
|
||||
const tierIcon = (tier: string) => (
|
||||
<svg width="14" height="16" viewBox="0 0 14 16" fill="none">
|
||||
<path d="M7 0L9 4L13 5L10 8.5L11 13L7 11L3 13L4 8.5L1 5L5 4Z" fill={tierColor[tier] || '#6b7280'} opacity="0.9" />
|
||||
<path d="M7 2L8.5 5L12 5.8L9.5 8.2L10.2 11.8L7 10L3.8 11.8L4.5 8.2L2 5.8L5.5 5Z" fill="white" opacity="0.2" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default function GameSample() {
|
||||
const [onlinePlayers, setOnlinePlayers] = useState(48_219);
|
||||
const [matchingCount, setMatchingCount] = useState(1_342);
|
||||
const [matchingActive, setMatchingActive] = useState(false);
|
||||
const [matchTimer, setMatchTimer] = useState(0);
|
||||
const [selectedChampion, setSelectedChampion] = useState(0);
|
||||
const [seasonProgress] = useState(67);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setOnlinePlayers((p) => p + Math.floor(Math.random() * 6 - 2));
|
||||
setMatchingCount((c) => c + Math.floor(Math.random() * 4 - 1));
|
||||
}, 2000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let timer: ReturnType<typeof setInterval>;
|
||||
if (matchingActive) {
|
||||
timer = setInterval(() => setMatchTimer((t) => t + 1), 1000);
|
||||
} else {
|
||||
setMatchTimer(0);
|
||||
}
|
||||
return () => clearInterval(timer);
|
||||
}, [matchingActive]);
|
||||
|
||||
return (
|
||||
<div style={{ background: '#000000', minHeight: '100vh', color: 'white', overflowX: 'hidden' }}>
|
||||
<style>{`
|
||||
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Rajdhani:wght@400;500;600;700&display=swap');
|
||||
@keyframes pulse-ring { 0% { transform: scale(0.97); box-shadow: 0 0 0 0 rgba(6,182,212,0.6); } 70% { transform: scale(1); box-shadow: 0 0 0 14px rgba(6,182,212,0); } 100% { transform: scale(0.97); } }
|
||||
@keyframes scan { 0% { top: 0; } 100% { top: 100%; } }
|
||||
@keyframes glitch { 0%, 90%, 100% { transform: none; clip-path: none; } 92% { transform: translate(-2px, 1px); clip-path: inset(30% 0 50% 0); } 94% { transform: translate(2px, -1px); clip-path: inset(60% 0 10% 0); } 96% { transform: translate(-1px, 0); clip-path: inset(10% 0 70% 0); } }
|
||||
@keyframes neonFlicker { 0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% { opacity: 1; } 20%, 24%, 55% { opacity: 0.4; } }
|
||||
@keyframes matchPulse { 0%, 100% { box-shadow: 0 0 20px rgba(6,182,212,0.4); } 50% { box-shadow: 0 0 50px rgba(6,182,212,0.9), 0 0 100px rgba(6,182,212,0.3); } }
|
||||
@keyframes float-particle { 0%, 100% { transform: translateY(0) rotate(0deg); opacity: 0.6; } 50% { transform: translateY(-20px) rotate(180deg); opacity: 1; } }
|
||||
@keyframes progress-fill { from { width: 0; } to { width: var(--w); } }
|
||||
.mode-card:hover { border-color: var(--card-color) !important; transform: translateY(-4px); box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
|
||||
.mode-card { transition: border-color 0.3s, transform 0.3s, box-shadow 0.3s; }
|
||||
.rank-row:hover { background: rgba(6,182,212,0.04) !important; }
|
||||
.rank-row { transition: background 0.15s; }
|
||||
.champ-card:hover { border-color: var(--champ-color) !important; transform: scale(1.04); }
|
||||
.champ-card { transition: border-color 0.3s, transform 0.3s; cursor: pointer; }
|
||||
.match-row:hover { background: rgba(255,255,255,0.03) !important; }
|
||||
.match-row { transition: background 0.15s; }
|
||||
`}</style>
|
||||
|
||||
{/* Back Banner */}
|
||||
<div style={{ background: 'linear-gradient(135deg, #1e1b4b, #312e81)', padding: '10px 24px', display: 'flex', alignItems: 'center', gap: 12, position: 'relative', zIndex: 200 }}>
|
||||
<Link href="/work/website" style={{ color: '#a5b4fc', fontSize: 13, textDecoration: 'none', fontFamily: 'Rajdhani, sans-serif' }}>
|
||||
← 홈페이지 제작 서비스로 돌아가기
|
||||
</Link>
|
||||
<span style={{ color: '#4c1d95' }}>|</span>
|
||||
<span style={{ color: '#06b6d4', fontSize: 12, fontFamily: 'Orbitron, sans-serif', fontWeight: 700 }}>
|
||||
SAMPLE · 게임 매칭 시스템
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Navbar */}
|
||||
<nav style={{ position: 'sticky', top: 0, zIndex: 100, background: 'rgba(0,0,0,0.95)', backdropFilter: 'blur(20px)', borderBottom: '1px solid rgba(6,182,212,0.15)', padding: '0 48px', height: 60, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 18, fontWeight: 900, background: 'linear-gradient(90deg, #06b6d4, #a855f7)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', letterSpacing: '0.1em', animation: 'neonFlicker 8s infinite' }}>
|
||||
NEXUS ARENA
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 28, alignItems: 'center' }}>
|
||||
{['HOME', 'MATCH', 'RANK', 'SHOP', 'CLAN'].map((item) => (
|
||||
<span key={item} style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 11, fontWeight: 700, color: '#1e3a5f', cursor: 'pointer', letterSpacing: '0.1em' }}>{item}</span>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
{/* Season pass mini */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<span style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 9, color: '#f59e0b', letterSpacing: '0.1em' }}>S7 PASS</span>
|
||||
<div style={{ width: 60, height: 4, background: '#1a1a2e', borderRadius: 2, overflow: 'hidden' }}>
|
||||
<div style={{ height: '100%', width: `${seasonProgress}%`, background: 'linear-gradient(90deg, #f59e0b, #fbbf24)', borderRadius: 2 }} />
|
||||
</div>
|
||||
<span style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 9, color: '#f59e0b' }}>{seasonProgress}%</span>
|
||||
</div>
|
||||
<div style={{ width: 1, height: 20, background: 'rgba(6,182,212,0.2)' }} />
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#10b981', boxShadow: '0 0 8px rgba(16,185,129,0.8)' }} />
|
||||
<span style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 13, color: '#10b981', fontWeight: 600 }}>
|
||||
{onlinePlayers.toLocaleString()} ONLINE
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Hero */}
|
||||
<section style={{ minHeight: '88vh', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '60px 48px', background: 'radial-gradient(ellipse 80% 70% at 50% 40%, rgba(6,182,212,0.09) 0%, rgba(168,85,247,0.06) 50%, transparent 100%)' }}>
|
||||
{/* Grid */}
|
||||
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(6,182,212,0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(6,182,212,0.05) 1px, transparent 1px)', backgroundSize: '60px 60px' }} />
|
||||
{/* Scan */}
|
||||
<div style={{ position: 'absolute', left: 0, right: 0, height: '1px', background: 'linear-gradient(90deg, transparent, rgba(6,182,212,0.5), transparent)', animation: 'scan 6s linear infinite', pointerEvents: 'none' }} />
|
||||
|
||||
{/* Floating particles */}
|
||||
{[
|
||||
{ left: '10%', top: '20%', size: 4, delay: '0s', color: '#06b6d4' },
|
||||
{ left: '85%', top: '30%', size: 6, delay: '1s', color: '#a855f7' },
|
||||
{ left: '20%', top: '70%', size: 3, delay: '2s', color: '#06b6d4' },
|
||||
{ left: '75%', top: '65%', size: 5, delay: '0.5s', color: '#f59e0b' },
|
||||
{ left: '50%', top: '15%', size: 3, delay: '1.5s', color: '#a855f7' },
|
||||
{ left: '60%', top: '80%', size: 4, delay: '2.5s', color: '#10b981' },
|
||||
].map((p, i) => (
|
||||
<div key={i} style={{ position: 'absolute', left: p.left, top: p.top, width: p.size, height: p.size, borderRadius: '50%', background: p.color, boxShadow: `0 0 ${p.size * 3}px ${p.color}`, animation: `float-particle 4s ${p.delay} ease-in-out infinite` }} />
|
||||
))}
|
||||
|
||||
{/* Corner brackets */}
|
||||
{[
|
||||
{ top: 40, left: 40, borderTop: '2px solid #06b6d4', borderLeft: '2px solid #06b6d4' },
|
||||
{ top: 40, right: 40, borderTop: '2px solid #a855f7', borderRight: '2px solid #a855f7' },
|
||||
{ bottom: 40, left: 40, borderBottom: '2px solid #06b6d4', borderLeft: '2px solid #06b6d4' },
|
||||
{ bottom: 40, right: 40, borderBottom: '2px solid #a855f7', borderRight: '2px solid #a855f7' },
|
||||
].map((s, i) => (
|
||||
<div key={i} style={{ position: 'absolute', width: 40, height: 40, ...s }} />
|
||||
))}
|
||||
|
||||
<div style={{ textAlign: 'center', position: 'relative', zIndex: 1 }}>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 11, fontWeight: 700, letterSpacing: '0.3em', color: '#06b6d4', marginBottom: 20, textTransform: 'uppercase' }}>
|
||||
Season 7 · RANKED MATCH
|
||||
</div>
|
||||
<h1 style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 'clamp(48px, 8vw, 96px)', fontWeight: 900, lineHeight: 1.0, marginBottom: 16, background: 'linear-gradient(180deg, #ffffff 0%, rgba(255,255,255,0.55) 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', animation: 'glitch 12s infinite' }}>
|
||||
NEXUS<br />
|
||||
<span style={{ background: 'linear-gradient(90deg, #06b6d4, #a855f7)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>ARENA</span>
|
||||
</h1>
|
||||
<p style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 18, color: '#475569', letterSpacing: '0.05em', marginBottom: 44 }}>
|
||||
ENTER THE ARENA. CLAIM YOUR GLORY.
|
||||
</p>
|
||||
|
||||
{/* Live Stats */}
|
||||
<div style={{ display: 'flex', gap: 24, justifyContent: 'center', marginBottom: 44 }}>
|
||||
{[
|
||||
{ label: 'ONLINE', val: onlinePlayers.toLocaleString(), color: '#06b6d4' },
|
||||
{ label: 'IN MATCH', val: matchingCount.toLocaleString(), color: '#a855f7' },
|
||||
{ label: 'SERVERS', val: '24', color: '#10b981' },
|
||||
{ label: 'AVG WAIT', val: '< 30s', color: '#f59e0b' },
|
||||
].map((s) => (
|
||||
<div key={s.label} style={{ padding: '14px 20px', background: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(8px)', border: `1px solid ${s.color}30`, borderTop: `2px solid ${s.color}`, textAlign: 'center', minWidth: 90 }}>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 22, fontWeight: 900, color: s.color }}>{s.val}</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 10, color: '#334155', letterSpacing: '0.2em', marginTop: 4 }}>{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Match button */}
|
||||
{!matchingActive ? (
|
||||
<button onClick={() => setMatchingActive(true)} style={{ background: 'linear-gradient(135deg, #06b6d4, #0891b2)', border: 'none', color: 'white', padding: '18px 56px', fontSize: 16, fontWeight: 900, cursor: 'pointer', fontFamily: 'Orbitron, sans-serif', letterSpacing: '0.1em', clipPath: 'polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px))', animation: 'pulse-ring 2s infinite' }}>
|
||||
FIND MATCH
|
||||
</button>
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
|
||||
<div style={{ padding: '18px 56px', border: '2px solid #06b6d4', color: '#06b6d4', fontFamily: 'Orbitron, sans-serif', fontSize: 16, fontWeight: 900, letterSpacing: '0.1em', animation: 'matchPulse 1.5s infinite', clipPath: 'polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px))' }}>
|
||||
MATCHING... {String(Math.floor(matchTimer / 60)).padStart(2, '0')}:{String(matchTimer % 60).padStart(2, '0')}
|
||||
</div>
|
||||
<button onClick={() => setMatchingActive(false)} style={{ background: 'none', border: 'none', color: '#334155', fontFamily: 'Rajdhani, sans-serif', fontSize: 13, cursor: 'pointer', letterSpacing: '0.1em', textDecoration: 'underline' }}>
|
||||
CANCEL SEARCH
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Season Pass */}
|
||||
<section style={{ padding: '40px 48px', background: 'rgba(0,0,0,0.9)', borderTop: '1px solid rgba(251,191,36,0.15)', borderBottom: '1px solid rgba(251,191,36,0.15)' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 32 }}>
|
||||
<div style={{ flexShrink: 0 }}>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 11, fontWeight: 700, color: '#f59e0b', letterSpacing: '0.2em', marginBottom: 4 }}>SEASON 7 PASS</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 28, fontWeight: 700, color: 'white' }}>
|
||||
Lv. <span style={{ color: '#f59e0b' }}>42</span>
|
||||
<span style={{ fontSize: 14, color: '#475569', marginLeft: 8 }}>/ 100</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
|
||||
<span style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 12, color: '#475569' }}>{seasonProgress}% 완료</span>
|
||||
<span style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 10, color: '#f59e0b' }}>다음 보상까지 1,840 XP</span>
|
||||
</div>
|
||||
<div style={{ height: 8, background: '#0d0d1a', borderRadius: 4, overflow: 'hidden', border: '1px solid rgba(251,191,36,0.15)' }}>
|
||||
<div style={{ height: '100%', width: `${seasonProgress}%`, background: 'linear-gradient(90deg, #d97706, #fbbf24, #f59e0b)', borderRadius: 4, boxShadow: '0 0 12px rgba(251,191,36,0.4)' }} />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12, flexShrink: 0 }}>
|
||||
{['스킨 3개', '이모트 5개', '칭호 2개', '골드 5,000'].map((reward) => (
|
||||
<div key={reward} style={{ padding: '6px 12px', background: 'rgba(251,191,36,0.08)', border: '1px solid rgba(251,191,36,0.2)', borderRadius: 4 }}>
|
||||
<span style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 11, color: '#fbbf24', fontWeight: 600 }}>{reward}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Champion Select */}
|
||||
<section style={{ padding: '60px 48px', background: '#000000' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 28 }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 9, color: '#06b6d4', letterSpacing: '0.2em', marginBottom: 8 }}>// ROSTER</div>
|
||||
<h2 style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 22, fontWeight: 900, letterSpacing: '0.05em', color: 'white' }}>
|
||||
CHAMPIONS<span style={{ color: '#06b6d4' }}>.</span>
|
||||
</h2>
|
||||
</div>
|
||||
<button style={{ background: 'none', border: '1px solid rgba(6,182,212,0.3)', color: '#06b6d4', padding: '7px 16px', borderRadius: 3, fontFamily: 'Orbitron, sans-serif', fontSize: 10, cursor: 'pointer', letterSpacing: '0.1em' }}>
|
||||
ALL CHAMPIONS →
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14 }}>
|
||||
{champions.map((c, i) => (
|
||||
<div
|
||||
key={c.name}
|
||||
className="champ-card"
|
||||
onClick={() => setSelectedChampion(i)}
|
||||
// @ts-expect-error CSS variable
|
||||
style={{ '--champ-color': c.color, border: `1px solid ${selectedChampion === i ? c.color : c.color + '25'}`, borderRadius: 6, padding: '22px 18px', background: selectedChampion === i ? c.color + '10' : 'rgba(0,0,0,0.6)', position: 'relative', overflow: 'hidden' }}
|
||||
>
|
||||
{selectedChampion === i && (
|
||||
<div style={{ position: 'absolute', top: 8, right: 8 }}>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 8, color: c.color, border: `1px solid ${c.color}60`, padding: '1px 6px', borderRadius: 2, letterSpacing: '0.1em' }}>SELECTED</div>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 2, background: `linear-gradient(90deg, transparent, ${c.color}, transparent)` }} />
|
||||
{/* Champion SVG silhouette */}
|
||||
<div style={{ width: 56, height: 56, borderRadius: '50%', background: c.color + '20', border: `2px solid ${c.color}40`, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 14 }}>
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none">
|
||||
<circle cx="14" cy="8" r="5" fill={c.color} opacity="0.8" />
|
||||
<path d="M5 28C5 21 9 18 14 18C19 18 23 21 23 28" fill={c.color} opacity="0.6" />
|
||||
<rect x="10" y="18" width="8" height="2" rx="1" fill={c.color} opacity="0.4" />
|
||||
</svg>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 16, fontWeight: 900, color: 'white', marginBottom: 4 }}>{c.name}</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 12, color: c.color, letterSpacing: '0.1em', fontWeight: 600, marginBottom: 14 }}>{c.role}</div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
|
||||
<span style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 8, color: '#334155', letterSpacing: '0.1em' }}>POWER</span>
|
||||
<span style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 9, color: c.color }}>{c.power}</span>
|
||||
</div>
|
||||
<div style={{ height: 3, background: '#0d0d1a', borderRadius: 2, overflow: 'hidden' }}>
|
||||
<div style={{ height: '100%', background: c.color, width: `${c.power}%`, boxShadow: `0 0 6px ${c.color}` }} />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 12, color: '#475569' }}>
|
||||
Win Rate: <span style={{ color: '#10b981', fontWeight: 700 }}>{c.winRate}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Game Modes */}
|
||||
<section style={{ padding: '56px 48px', background: 'rgba(0,0,0,0.85)' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 36 }}>
|
||||
<h2 style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 26, fontWeight: 900, color: 'white', letterSpacing: '0.05em', marginBottom: 8 }}>
|
||||
GAME MODES
|
||||
</h2>
|
||||
<div style={{ width: 60, height: 2, background: 'linear-gradient(90deg, #06b6d4, #a855f7)', margin: '0 auto' }} />
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 20 }}>
|
||||
{modes.map((mode) => (
|
||||
<div key={mode.id} className="mode-card"
|
||||
// @ts-expect-error CSS variable
|
||||
style={{ '--card-color': mode.color, border: `1px solid ${mode.color}25`, borderRadius: 4, padding: '28px 24px', background: 'rgba(0,0,0,0.7)', cursor: 'pointer', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 2, background: `linear-gradient(90deg, transparent, ${mode.color}, transparent)` }} />
|
||||
{/* Mode icon */}
|
||||
<div style={{ width: 44, height: 44, borderRadius: '50%', background: mode.color + '15', border: `1px solid ${mode.color}35`, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 16 }}>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
{mode.id === 'solo' && <polygon points="10,2 18,18 2,18" fill={mode.color} opacity="0.9" />}
|
||||
{mode.id === 'duo' && <><circle cx="7" cy="10" r="5" fill={mode.color} opacity="0.9" /><circle cx="13" cy="10" r="5" fill={mode.color} opacity="0.5" /></>}
|
||||
{mode.id === 'squad' && <><rect x="2" y="2" width="7" height="7" rx="2" fill={mode.color} opacity="0.9" /><rect x="11" y="2" width="7" height="7" rx="2" fill={mode.color} opacity="0.6" /><rect x="2" y="11" width="7" height="7" rx="2" fill={mode.color} opacity="0.6" /><rect x="11" y="11" width="7" height="7" rx="2" fill={mode.color} opacity="0.4" /></>}
|
||||
</svg>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 8 }}>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 22, fontWeight: 900, color: 'white' }}>{mode.name}</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 13, color: mode.color, fontWeight: 700, letterSpacing: '0.1em' }}>{mode.sub}</div>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 15, color: '#475569', lineHeight: 1.5, marginBottom: 20 }}>{mode.desc}</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 11, color: mode.color, letterSpacing: '0.08em' }}>{mode.players} IN QUEUE</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 10, color: '#1e3a5f', letterSpacing: '0.1em' }}>AVG WAIT: < 30s</div>
|
||||
</div>
|
||||
<button style={{ background: `${mode.color}20`, border: `1px solid ${mode.color}50`, color: mode.color, padding: '7px 18px', borderRadius: 2, fontSize: 11, fontWeight: 700, cursor: 'pointer', fontFamily: 'Orbitron, sans-serif', letterSpacing: '0.08em' }}>
|
||||
PLAY →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Rankings + Recent Matches */}
|
||||
<section style={{ padding: '56px 48px', background: '#000000' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto', display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: 24 }}>
|
||||
{/* Rankings */}
|
||||
<div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
|
||||
<h2 style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 20, fontWeight: 900, letterSpacing: '0.05em', color: 'white' }}>
|
||||
GLOBAL RANKING<span style={{ color: '#06b6d4' }}>.</span>
|
||||
</h2>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 11, color: '#1e3a5f', letterSpacing: '0.1em' }}>Season 7 · Top 100</div>
|
||||
</div>
|
||||
<div style={{ border: '1px solid rgba(6,182,212,0.12)', borderRadius: 4, overflow: 'hidden' }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '52px 1fr 88px 60px 60px', padding: '9px 16px', borderBottom: '1px solid rgba(6,182,212,0.1)', background: 'rgba(6,182,212,0.05)' }}>
|
||||
{['RANK', 'PLAYER', 'SCORE', 'WINS', 'K/D/A'].map((h) => (
|
||||
<div key={h} style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 8, color: '#06b6d4', letterSpacing: '0.15em' }}>{h}</div>
|
||||
))}
|
||||
</div>
|
||||
{rankings.map((r, i) => (
|
||||
<div key={i} className="rank-row" style={{ display: 'grid', gridTemplateColumns: '52px 1fr 88px 60px 60px', padding: '13px 16px', borderBottom: i < rankings.length - 1 ? '1px solid rgba(255,255,255,0.03)' : 'none', alignItems: 'center' }}>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 18, fontWeight: 900, color: i === 0 ? '#fbbf24' : i === 1 ? '#9ca3af' : i === 2 ? '#cd7c2f' : '#1e3a5f' }}>
|
||||
{r.rank < 10 ? `0${r.rank}` : r.rank}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 3 }}>
|
||||
{tierIcon(r.tier)}
|
||||
<span style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 14, fontWeight: 700, color: 'white' }}>{r.name}</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
|
||||
<span style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 7, color: tierColor[r.tier] || '#6b7280', border: `1px solid ${tierColor[r.tier] || '#6b7280'}40`, padding: '1px 5px', letterSpacing: '0.08em' }}>{r.tier}</span>
|
||||
<span style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 9, color: r.change.startsWith('+') ? '#10b981' : r.change === '0' ? '#334155' : '#ef4444' }}>
|
||||
{r.change === '0' ? '–' : r.change}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 13, fontWeight: 700, color: '#06b6d4' }}>{r.score.toLocaleString()}</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 13, color: '#475569', fontWeight: 600 }}>{r.wins}</div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 12, color: '#10b981' }}>{r.kda}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Matches */}
|
||||
<div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
|
||||
<h2 style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 20, fontWeight: 900, letterSpacing: '0.05em', color: 'white' }}>
|
||||
RECENT MATCHES<span style={{ color: '#a855f7' }}>.</span>
|
||||
</h2>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 11, color: '#1e3a5f', letterSpacing: '0.1em' }}>Last 10 games</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||
{recentMatches.map((m, i) => (
|
||||
<div key={i} className="match-row" style={{ display: 'grid', gridTemplateColumns: '64px 1fr auto', gap: 12, alignItems: 'center', padding: '14px 16px', border: `1px solid ${m.result === 'WIN' ? 'rgba(16,185,129,0.15)' : 'rgba(239,68,68,0.15)'}`, borderLeft: `3px solid ${m.result === 'WIN' ? '#10b981' : '#ef4444'}`, borderRadius: 4, background: m.result === 'WIN' ? 'rgba(16,185,129,0.04)' : 'rgba(239,68,68,0.04)', cursor: 'default' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 13, fontWeight: 900, color: m.result === 'WIN' ? '#10b981' : '#ef4444' }}>{m.result}</div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 8, color: '#334155', letterSpacing: '0.1em', marginTop: 2 }}>{m.mode}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 15, color: 'white', fontWeight: 700 }}>
|
||||
{m.kills} / {m.deaths} / {m.assists}
|
||||
<span style={{ fontSize: 11, color: '#475569', marginLeft: 6 }}>K/D/A</span>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 9, color: '#334155', marginTop: 2 }}>{m.duration}</div>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 14, fontWeight: 900, color: m.rating.startsWith('+') ? '#10b981' : '#ef4444', textAlign: 'right' }}>
|
||||
{m.rating}
|
||||
<div style={{ fontSize: 8, color: '#334155', letterSpacing: '0.1em' }}>RATING</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer style={{ background: '#000000', borderTop: '1px solid rgba(6,182,212,0.08)', padding: '24px 48px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div style={{ fontFamily: 'Orbitron, sans-serif', fontSize: 14, fontWeight: 900, color: '#1e3a5f', letterSpacing: '0.1em' }}>NEXUS ARENA</div>
|
||||
<div style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 11, color: '#1e293b', letterSpacing: '0.1em' }}>
|
||||
© 2024 NEXUS ARENA STUDIOS. ALL RIGHTS RESERVED.
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 16 }}>
|
||||
{['Twitter', 'Discord', 'YouTube', 'Twitch'].map((s) => (
|
||||
<span key={s} style={{ fontFamily: 'Rajdhani, sans-serif', fontSize: 12, color: '#1e3a5f', cursor: 'pointer', letterSpacing: '0.05em' }}>{s}</span>
|
||||
))}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
612
app/work/website/samples/interior/page.tsx
Normal file
612
app/work/website/samples/interior/page.tsx
Normal file
@@ -0,0 +1,612 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
/* ══════════════════════════════════════════════
|
||||
DATA
|
||||
══════════════════════════════════════════════ */
|
||||
const portfolio = [
|
||||
{ title: '한남동 단독주택', cat: '주거 인테리어', area: '245㎡', img: 'https://picsum.photos/seed/interior-hannam/800/600' },
|
||||
{ title: '청담 파인다이닝', cat: '상업 공간', area: '190㎡', img: 'https://picsum.photos/seed/interior-dining/800/600' },
|
||||
{ title: '성수 브랜드 오피스', cat: '업무 공간', area: '380㎡', img: 'https://picsum.photos/seed/interior-office/800/600' },
|
||||
{ title: '용산 아파트 리모델링', cat: '리모델링', area: '95㎡', img: 'https://picsum.photos/seed/interior-remodel/800/600' },
|
||||
{ title: '강남 카페 에스프레소랩', cat: '상업 공간', area: '120㎡', img: 'https://picsum.photos/seed/interior-cafe/800/600' },
|
||||
];
|
||||
|
||||
const services = [
|
||||
{
|
||||
title: '주거 인테리어', sub: 'Residential',
|
||||
desc: '생활의 리듬에 맞춘 공간을 설계합니다. 단독주택부터 아파트까지, 당신의 일상이 더 아름다워지도록 모든 디테일을 손수 고릅니다.',
|
||||
details: ['공간 기획 및 3D 시뮬레이션', '자재 선정 동행 서비스', '시공 전 과정 PM', '준공 후 AS 1년'],
|
||||
img: 'https://picsum.photos/seed/interior-residential/800/600',
|
||||
},
|
||||
{
|
||||
title: '상업 공간 디자인', sub: 'Commercial',
|
||||
desc: '브랜드의 철학이 공간 언어로 번역됩니다. 첫 방문객이 문을 열었을 때 느끼는 그 감정까지 설계의 범위입니다.',
|
||||
details: ['브랜드 아이덴티티 반영', '동선 및 고객 UX 설계', '조명·음향 플래닝', '설비 협력사 연계'],
|
||||
img: 'https://picsum.photos/seed/interior-commercial/800/600',
|
||||
},
|
||||
{
|
||||
title: '리모델링 & 재생', sub: 'Remodeling',
|
||||
desc: '기존 공간의 가능성을 새로운 시선으로 바라봅니다. 구조적 변경부터 마감재 교체까지, 완전한 변신을 지원합니다.',
|
||||
details: ['현장 실측 및 구조 분석', '철거~완공 원스톱', '예산 내 최적 시공', '친환경 자재 우선 적용'],
|
||||
img: 'https://picsum.photos/seed/interior-remodeling/800/600',
|
||||
},
|
||||
];
|
||||
|
||||
const testimonials = [
|
||||
{ name: '하윤서', role: '한남동 단독주택 의뢰인', u: 'hayunseo', rating: 5, highlight: '계획보다 적은 예산',
|
||||
text: '처음엔 예산이 걱정됐는데, 아우라 팀이 범위를 명확히 정해줘서 오히려 계획보다 적게 들었습니다. 무엇보다 완공된 공간에서 매일 아침 커피 한 잔 하는 지금이 너무 행복해요.' },
|
||||
{ name: '박도현', role: '카페 에스프레소랩 대표', u: 'parkdohyun', rating: 5, highlight: '매출 340% 상승',
|
||||
text: '우리 브랜드 철학을 완벽하게 공간으로 옮겨줬습니다. 오픈 첫날부터 SNS 바이럴이 터졌고, 오픈 3개월 만에 매출이 전년 대비 340% 올랐어요.' },
|
||||
{ name: '이서진', role: '루미너스 COO', u: 'leeseojin', rating: 5, highlight: '직원 만족도 93점',
|
||||
text: '직원들이 출근하고 싶은 공간을 만드는 게 목표였습니다. 리모델링 후 직원 만족도 설문에서 93점, 퇴직률이 절반으로 줄었습니다.' },
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ num: '01', title: '무료 상담', desc: '공간 사진, 예산, 취향을 공유해 주세요. 72시간 내 맞춤 제안서를 드립니다.' },
|
||||
{ num: '02', title: '콘셉트 기획', desc: '무드보드와 3D 시뮬레이션으로 완공 이후를 미리 경험합니다.' },
|
||||
{ num: '03', title: '시공', desc: '전담 PM이 공정마다 현장을 점검하고 일일 리포트를 공유합니다.' },
|
||||
{ num: '04', title: '준공 & AS', desc: '완공 후 1년간 무상 AS. 공간이 오래 아름답도록 함께합니다.' },
|
||||
];
|
||||
|
||||
/* ══════════════════════════════════════════════
|
||||
SVG ICONS
|
||||
══════════════════════════════════════════════ */
|
||||
const StarIcon = ({ filled }: { filled: boolean }) => (
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill={filled ? '#8B6914' : 'none'} stroke="#8B6914" strokeWidth="1">
|
||||
<path d="M6.5 1l1.5 3 3.5.5-2.5 2.4.6 3.4L6.5 9 3 10.3l.6-3.4L1 4.9 4.5 4.4z" />
|
||||
</svg>
|
||||
);
|
||||
const CheckIcon = () => (
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<circle cx="7" cy="7" r="6.5" stroke="#4E5C3E" strokeWidth="1" />
|
||||
<path d="M4.5 7l2 2 3-3" stroke="#4E5C3E" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
const ArrowRight = ({ color = '#8B6914' }: { color?: string }) => (
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M3 8h10M9 4l4 4-4 4" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
/* ══════════════════════════════════════════════
|
||||
CONSTANTS
|
||||
══════════════════════════════════════════════ */
|
||||
const BANNER_H = 40;
|
||||
const NAV_H = 72;
|
||||
|
||||
/* ══════════════════════════════════════════════
|
||||
PAGE COMPONENT
|
||||
══════════════════════════════════════════════ */
|
||||
export default function InteriorSample() {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [showTop, setShowTop] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const scroller: HTMLElement =
|
||||
(document.querySelector('.main-content') as HTMLElement | null) ??
|
||||
document.documentElement;
|
||||
|
||||
const onNavScroll = () => {
|
||||
setScrolled(scroller.scrollTop > 60);
|
||||
setShowTop(scroller.scrollTop > 400);
|
||||
};
|
||||
scroller.addEventListener('scroll', onNavScroll, { passive: true });
|
||||
|
||||
const scrollToTop = () => scroller.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
const topBtn = document.getElementById('au-top-btn');
|
||||
if (topBtn) topBtn.addEventListener('click', scrollToTop);
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => entries.forEach((e) => { if (e.isIntersecting) e.target.classList.add('au-visible'); }),
|
||||
{ threshold: 0.08, root: scroller === document.documentElement ? null : scroller }
|
||||
);
|
||||
document.querySelectorAll('.au-reveal').forEach((el) => observer.observe(el));
|
||||
|
||||
return () => {
|
||||
scroller.removeEventListener('scroll', onNavScroll);
|
||||
if (topBtn) topBtn.removeEventListener('click', scrollToTop);
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const GOLD = '#8B6914';
|
||||
const DARK = '#1C1A17';
|
||||
const SAGE = '#4E5C3E';
|
||||
const CREAM = '#FAF8F5';
|
||||
const SURFACE = '#F0ECE4';
|
||||
|
||||
return (
|
||||
<div className="au-page" style={{ background: CREAM, color: DARK, fontFamily: "'Pretendard','Apple SD Gothic Neo',system-ui,sans-serif", overflowX: 'hidden' }}>
|
||||
|
||||
{/* ══ 폰트 + 전역 CSS ══ */}
|
||||
<style dangerouslySetInnerHTML={{ __html: `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400;1,600&display=swap');
|
||||
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.min.css');
|
||||
|
||||
/* 전역 리셋을 .au-page 하위로 한정 — 사이드바 오염 방지 */
|
||||
.au-page, .au-page *, .au-page *::before, .au-page *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
@keyframes au-fadeUp {
|
||||
from { opacity: 0; transform: translateY(2rem); filter: blur(4px); }
|
||||
to { opacity: 1; transform: none; filter: blur(0); }
|
||||
}
|
||||
@keyframes au-float {
|
||||
0%,100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-10px); }
|
||||
}
|
||||
@keyframes au-marquee {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(-50%); }
|
||||
}
|
||||
@keyframes au-pulse-gold {
|
||||
0%,100% { box-shadow: 0 0 0 0 rgba(139,105,20,.3); }
|
||||
50% { box-shadow: 0 0 0 10px rgba(139,105,20,0); }
|
||||
}
|
||||
|
||||
/* 스크롤 reveal */
|
||||
.au-reveal {
|
||||
opacity: 0; transform: translateY(1.5rem); filter: blur(2px);
|
||||
transition: opacity .7s cubic-bezier(.16,1,.3,1),
|
||||
transform .7s cubic-bezier(.16,1,.3,1),
|
||||
filter .7s cubic-bezier(.16,1,.3,1);
|
||||
}
|
||||
.au-reveal.au-visible { opacity: 1; transform: none; filter: none; }
|
||||
.au-reveal:nth-child(2) { transition-delay: 80ms; }
|
||||
.au-reveal:nth-child(3) { transition-delay: 160ms; }
|
||||
.au-reveal:nth-child(4) { transition-delay: 240ms; }
|
||||
|
||||
/* 네비 */
|
||||
.au-nav-link { color: #6B6456; text-decoration: none; font-size: 14px; font-weight: 500; transition: color .3s; }
|
||||
.au-nav-link:hover { color: #1C1A17; }
|
||||
.au-nav-link-light { color: rgba(245,237,223,.7); text-decoration: none; font-size: 14px; font-weight: 500; transition: color .3s; }
|
||||
.au-nav-link-light:hover { color: #F5EDDF; }
|
||||
|
||||
/* 버튼 */
|
||||
.au-btn-primary {
|
||||
display: inline-flex; align-items: center; gap: 10px;
|
||||
background: #1C1A17; color: #FAF8F5; border: none; border-radius: 100px;
|
||||
padding: 14px 28px 14px 20px; font-size: 15px; font-weight: 600;
|
||||
font-family: inherit; cursor: pointer; text-decoration: none;
|
||||
transition: transform .4s cubic-bezier(.16,1,.3,1), box-shadow .4s cubic-bezier(.16,1,.3,1);
|
||||
}
|
||||
.au-btn-primary:hover { transform: scale(1.02); box-shadow: 0 12px 36px rgba(28,26,23,.25); }
|
||||
.au-btn-primary:active { transform: scale(.98); }
|
||||
|
||||
.au-btn-ghost {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
background: transparent; color: #1C1A17;
|
||||
border: 1px solid rgba(28,26,23,.2); border-radius: 100px;
|
||||
padding: 13px 24px; font-size: 14px; font-weight: 500;
|
||||
font-family: inherit; cursor: pointer; text-decoration: none;
|
||||
transition: border-color .3s, background .3s;
|
||||
}
|
||||
.au-btn-ghost:hover { border-color: rgba(28,26,23,.5); background: rgba(28,26,23,.04); }
|
||||
|
||||
/* 포트폴리오 */
|
||||
.au-portfolio-cell { overflow: hidden; border-radius: 16px; position: relative; cursor: pointer; }
|
||||
.au-portfolio-img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform .7s cubic-bezier(.16,1,.3,1); }
|
||||
.au-portfolio-cell:hover .au-portfolio-img { transform: scale(1.04); }
|
||||
.au-portfolio-overlay {
|
||||
position: absolute; inset: 0;
|
||||
background: linear-gradient(to top, rgba(28,26,23,.7) 0%, transparent 55%);
|
||||
opacity: 0; transition: opacity .4s cubic-bezier(.16,1,.3,1);
|
||||
display: flex; flex-direction: column; justify-content: flex-end; padding: 22px;
|
||||
}
|
||||
.au-portfolio-cell:hover .au-portfolio-overlay { opacity: 1; }
|
||||
|
||||
/* 서비스 카드 */
|
||||
.au-service-card {
|
||||
background: white; border-radius: 20px; overflow: hidden;
|
||||
box-shadow: 0 4px 32px rgba(28,26,23,.06);
|
||||
transition: transform .4s cubic-bezier(.16,1,.3,1), box-shadow .4s cubic-bezier(.16,1,.3,1);
|
||||
}
|
||||
.au-service-card:hover { transform: translateY(-4px); box-shadow: 0 20px 60px rgba(28,26,23,.12); }
|
||||
|
||||
/* Double-Bezel 후기 */
|
||||
.au-testimony { background: rgba(240,236,228,.5); border-radius: 24px; padding: 6px; border: 1px solid rgba(139,105,20,.12); transition: border-color .3s; }
|
||||
.au-testimony:hover { border-color: rgba(139,105,20,.3); }
|
||||
.au-testimony-inner { background: white; border-radius: 18px; padding: 28px 26px; box-shadow: inset 0 1px 1px rgba(255,255,255,.8); height: 100%; }
|
||||
|
||||
/* 프로세스 번호 */
|
||||
.au-step-num { font-family:'Playfair Display',Georgia,serif; font-size:48px; font-weight:700; color:rgba(139,105,20,.15); line-height:1; margin-bottom:12px; }
|
||||
|
||||
/* 히어로 영상 프레임 */
|
||||
.au-video-frame {
|
||||
position: relative;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 40px 100px rgba(10,8,6,.45), 0 0 0 1px rgba(139,105,20,.2);
|
||||
}
|
||||
.au-video-frame::after {
|
||||
content: '';
|
||||
position: absolute; inset: 0;
|
||||
border-radius: 20px;
|
||||
box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
|
||||
pointer-events: none;
|
||||
}
|
||||
.au-video-frame video {
|
||||
display: block; width: 100%; height: 100%; object-fit: cover;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.au-hero-grid { grid-template-columns: 1fr !important; }
|
||||
.au-hero-video-col { display: none !important; }
|
||||
.au-service-grid { grid-template-columns: 1fr !important; }
|
||||
.au-process-grid { grid-template-columns: 1fr 1fr !important; }
|
||||
.au-portfolio-grid { grid-template-columns: 1fr 1fr !important; }
|
||||
.au-portfolio-span2 { grid-column: span 1 !important; }
|
||||
.au-testimony-grid { grid-template-columns: 1fr 1fr !important; }
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.au-process-grid { grid-template-columns: 1fr !important; }
|
||||
.au-testimony-grid { grid-template-columns: 1fr !important; }
|
||||
.au-portfolio-grid { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
`}} />
|
||||
|
||||
{/* ══ BACK BANNER ══ */}
|
||||
<div style={{
|
||||
background: 'linear-gradient(135deg,#1e1b4b,#312e81)',
|
||||
height: BANNER_H, display: 'flex', alignItems: 'center',
|
||||
padding: '0 24px', gap: 12, flexShrink: 0,
|
||||
}}>
|
||||
<Link href="/work/website" style={{ color: '#a5b4fc', fontSize: 13, textDecoration: 'none' }}>
|
||||
← 홈페이지 제작 서비스로 돌아가기
|
||||
</Link>
|
||||
<span style={{ color: '#4c1d95' }}>|</span>
|
||||
<span style={{ color: '#fcd34d', fontSize: 12, fontFamily: "'Playfair Display',serif", fontStyle: 'italic', fontWeight: 700 }}>
|
||||
SAMPLE · 인테리어 업체 홈페이지
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* ══ NAV ══ */}
|
||||
<nav style={{
|
||||
position: 'sticky', top: 0, zIndex: 50, height: NAV_H,
|
||||
display: 'flex', alignItems: 'center', padding: '0 48px',
|
||||
background: scrolled ? 'rgba(250,248,245,.94)' : 'rgba(10,8,6,.55)',
|
||||
backdropFilter: 'blur(20px)',
|
||||
borderBottom: scrolled ? '1px solid rgba(139,105,20,.1)' : '1px solid rgba(255,255,255,.06)',
|
||||
transition: 'background .45s cubic-bezier(.16,1,.3,1), border-color .45s',
|
||||
}}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: "'Playfair Display',Georgia,serif", fontSize: 20, fontWeight: 700, color: scrolled ? DARK : '#F5EDDF', letterSpacing: '-0.01em', transition: 'color .45s' }}>
|
||||
Aura Interior
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: scrolled ? '#A0917C' : 'rgba(245,237,223,.5)', letterSpacing: '0.2em', textTransform: 'uppercase', marginTop: -2, transition: 'color .45s' }}>
|
||||
아우라 인테리어
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 32 }}>
|
||||
{['포트폴리오', '서비스', '프로세스', '고객 후기', '상담 신청'].map((l) => (
|
||||
<a key={l} href={`#${l}`} className={scrolled ? 'au-nav-link' : 'au-nav-link-light'}>{l}</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Link href="#contact" className="au-btn-primary"
|
||||
style={{ background: scrolled ? DARK : 'rgba(139,105,20,.9)', fontSize: 13, padding: '10px 20px 10px 16px', transition: 'background .45s, transform .4s, box-shadow .4s' }}>
|
||||
<span style={{ width: 28, height: 28, borderRadius: '50%', background: 'rgba(250,248,245,.12)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<ArrowRight color="#FAF8F5" />
|
||||
</span>
|
||||
무료 상담 신청
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* ══ HERO — 좌: 텍스트 / 우: 영상 ══ */}
|
||||
<section style={{
|
||||
background: DARK,
|
||||
minHeight: `calc(100dvh - ${BANNER_H}px - ${NAV_H}px)`,
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
overflow: 'hidden',
|
||||
}} className="au-hero-grid">
|
||||
|
||||
{/* 좌: 텍스트 */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: '80px 64px 80px 80px', position: 'relative' }}>
|
||||
{/* 미세 노이즈 */}
|
||||
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'url("data:image/svg+xml,%3Csvg viewBox=\'0 0 200 200\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cfilter id=\'n\'%3E%3CfeTurbulence type=\'fractalNoise\' baseFrequency=\'0.8\' numOctaves=\'4\' stitchTiles=\'stitch\'/%3E%3C/filter%3E%3Crect width=\'100%25\' height=\'100%25\' filter=\'url(%23n)\'/%3E%3C/svg%3E")', opacity: 0.035, pointerEvents: 'none' }} />
|
||||
|
||||
<div style={{ animation: 'au-fadeUp .9s cubic-bezier(.16,1,.3,1) both', position: 'relative' }}>
|
||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, background: 'rgba(139,105,20,.18)', border: '1px solid rgba(139,105,20,.4)', borderRadius: 100, padding: '5px 14px', marginBottom: 24 }}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: GOLD }} />
|
||||
<span style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.15em', textTransform: 'uppercase' }}>서울 기반 인테리어 디자인</span>
|
||||
</div>
|
||||
|
||||
<h1 style={{ fontFamily: "'Playfair Display',Georgia,serif", fontSize: 'clamp(36px,4.5vw,64px)', fontWeight: 700, lineHeight: 1.14, color: '#F5EDDF', letterSpacing: '-0.02em', marginBottom: 20, wordBreak: 'keep-all' }}>
|
||||
공간이 당신의<br />이야기를<br />
|
||||
<em style={{ color: GOLD, fontStyle: 'italic' }}>담습니다.</em>
|
||||
</h1>
|
||||
|
||||
<p style={{ fontSize: 16, color: 'rgba(245,237,223,.58)', lineHeight: 1.85, maxWidth: 400, marginBottom: 36, wordBreak: 'keep-all' }}>
|
||||
아우라 인테리어는 12년간 247개의 공간을 완성했습니다.<br />
|
||||
주거부터 상업 공간까지, 당신의 이야기가 머무는 곳을 만듭니다.
|
||||
</p>
|
||||
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center', marginBottom: 48 }}>
|
||||
<Link href="#contact" className="au-btn-primary" style={{ background: GOLD, color: DARK }}>
|
||||
<span style={{ width: 32, height: 32, borderRadius: '50%', background: 'rgba(28,26,23,.12)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
|
||||
<ArrowRight color={DARK} />
|
||||
</span>
|
||||
무료 공간 상담 시작
|
||||
</Link>
|
||||
<a href="#포트폴리오" style={{ display: 'inline-flex', alignItems: 'center', gap: 6, color: 'rgba(245,237,223,.6)', fontSize: 14, textDecoration: 'none', transition: 'color .3s' }}
|
||||
onMouseEnter={e => (e.currentTarget.style.color = '#F5EDDF')}
|
||||
onMouseLeave={e => (e.currentTarget.style.color = 'rgba(245,237,223,.6)')}>
|
||||
포트폴리오 보기 <ArrowRight color="rgba(245,237,223,.6)" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* 미니 통계 */}
|
||||
<div style={{ display: 'flex', gap: 36, paddingTop: 28, borderTop: '1px solid rgba(250,248,245,.08)' }}>
|
||||
{[['247+','완공 프로젝트'],['4.96','고객 만족도'],['12년','디자인 경력']].map(([n, l]) => (
|
||||
<div key={l}>
|
||||
<div style={{ fontFamily: "'Playfair Display',serif", fontSize: 26, fontWeight: 700, color: '#F5EDDF' }}>{n}</div>
|
||||
<div style={{ fontSize: 12, color: 'rgba(245,237,223,.4)', marginTop: 3 }}>{l}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 우: 영상 패널 */}
|
||||
<div className="au-hero-video-col" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '48px 64px 48px 32px', background: 'linear-gradient(135deg,rgba(10,8,6,0) 0%,rgba(139,105,20,.04) 100%)' }}>
|
||||
{/* 배경 글로우 */}
|
||||
<div style={{ position: 'absolute', width: 360, height: 500, borderRadius: '50%', background: 'radial-gradient(ellipse,rgba(139,105,20,.12) 0%,transparent 70%)', pointerEvents: 'none' }} />
|
||||
|
||||
{/* 영상 프레임 — 세로형 영상을 자연스럽게 표시 */}
|
||||
<div style={{ position: 'relative', zIndex: 1 }}>
|
||||
{/* Award 뱃지 */}
|
||||
<div style={{ position: 'absolute', top: -16, right: -20, zIndex: 2, background: 'rgba(250,248,245,.97)', borderRadius: 100, padding: '7px 16px', boxShadow: '0 8px 32px rgba(10,8,6,.3)', border: '1px solid rgba(139,105,20,.15)', display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: GOLD }} />
|
||||
<span style={{ fontSize: 11, fontFamily: "'Playfair Display',serif", fontStyle: 'italic', fontWeight: 700, color: DARK, whiteSpace: 'nowrap' }}>Award Winner 2024</span>
|
||||
</div>
|
||||
|
||||
{/* 영상 컨테이너: 세로 비율, 적당한 크기 */}
|
||||
<div className="au-video-frame" style={{ width: 'clamp(240px,22vw,320px)', aspectRatio: '9/16' }}>
|
||||
<video autoPlay muted loop playsInline src="/interior-hero.mp4" />
|
||||
</div>
|
||||
|
||||
{/* 플로팅 리뷰 배지 */}
|
||||
<div style={{ position: 'absolute', bottom: -20, left: -28, background: 'white', borderRadius: 14, padding: '14px 18px', boxShadow: '0 20px 60px rgba(10,8,6,.3)', animation: 'au-float 5s ease-in-out infinite', border: '1px solid rgba(139,105,20,.1)', zIndex: 2 }}>
|
||||
<div style={{ display: 'flex', gap: 3, marginBottom: 5 }}>
|
||||
{[1,2,3,4,5].map(i => <StarIcon key={i} filled />)}
|
||||
</div>
|
||||
<div style={{ fontSize: 12, fontWeight: 700, color: DARK }}>최근 완공 · 한남동</div>
|
||||
<div style={{ fontSize: 11, color: '#A0917C', marginTop: 2 }}>만족도 5.0 / 5.0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ══ PORTFOLIO BENTO ══ */}
|
||||
<section id="포트폴리오" style={{ padding: '100px 80px', background: SURFACE }}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
|
||||
<div className="au-reveal" style={{ marginBottom: 56, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 12 }}>Portfolio</div>
|
||||
<h2 style={{ fontFamily: "'Playfair Display',serif", fontSize: 'clamp(32px,3.5vw,48px)', fontWeight: 700, color: DARK, letterSpacing: '-0.02em', lineHeight: 1.2, wordBreak: 'keep-all' }}>
|
||||
공간이 말하는<br />우리의 언어
|
||||
</h2>
|
||||
</div>
|
||||
<a href="#" className="au-btn-ghost" style={{ flexShrink: 0 }}>전체 보기 <ArrowRight /></a>
|
||||
</div>
|
||||
|
||||
<div className="au-reveal au-portfolio-grid" style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr', gridTemplateRows: 'auto auto', gap: 14 }}>
|
||||
<div className="au-portfolio-cell" style={{ gridRow: 'span 2', minHeight: 580 }}>
|
||||
<img src={portfolio[0].img} alt={portfolio[0].title} className="au-portfolio-img" />
|
||||
<div className="au-portfolio-overlay">
|
||||
<div style={{ fontSize: 11, color: 'rgba(250,248,245,.6)', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: 4 }}>{portfolio[0].cat}</div>
|
||||
<div style={{ fontSize: 18, fontWeight: 700, color: 'white', fontFamily: "'Playfair Display',serif" }}>{portfolio[0].title}</div>
|
||||
<div style={{ fontSize: 12, color: 'rgba(250,248,245,.7)', marginTop: 4 }}>{portfolio[0].area}</div>
|
||||
</div>
|
||||
</div>
|
||||
{[1, 2].map((idx) => (
|
||||
<div key={idx} className="au-portfolio-cell" style={{ minHeight: 280 }}>
|
||||
<img src={portfolio[idx].img} alt={portfolio[idx].title} className="au-portfolio-img" />
|
||||
<div className="au-portfolio-overlay">
|
||||
<div style={{ fontSize: 11, color: 'rgba(250,248,245,.6)', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: 4 }}>{portfolio[idx].cat}</div>
|
||||
<div style={{ fontSize: 16, fontWeight: 700, color: 'white', fontFamily: "'Playfair Display',serif" }}>{portfolio[idx].title}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="au-portfolio-cell au-portfolio-span2" style={{ gridColumn: 'span 2', minHeight: 280 }}>
|
||||
<img src={portfolio[4].img} alt={portfolio[4].title} className="au-portfolio-img" />
|
||||
<div className="au-portfolio-overlay">
|
||||
<div style={{ fontSize: 11, color: 'rgba(250,248,245,.6)', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: 4 }}>{portfolio[4].cat}</div>
|
||||
<div style={{ fontSize: 18, fontWeight: 700, color: 'white', fontFamily: "'Playfair Display',serif" }}>{portfolio[4].title}</div>
|
||||
<div style={{ fontSize: 12, color: 'rgba(250,248,245,.7)', marginTop: 4 }}>{portfolio[4].area}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ══ SERVICES ZIG-ZAG ══ */}
|
||||
<section id="서비스" style={{ padding: '100px 80px', background: CREAM }}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
|
||||
<div className="au-reveal" style={{ textAlign: 'center', marginBottom: 72 }}>
|
||||
<div style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 12 }}>Services</div>
|
||||
<h2 style={{ fontFamily: "'Playfair Display',serif", fontSize: 'clamp(32px,3.5vw,48px)', fontWeight: 700, color: DARK, letterSpacing: '-0.02em', wordBreak: 'keep-all' }}>
|
||||
우리가 잘하는 세 가지
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 48 }}>
|
||||
{services.map((svc, i) => (
|
||||
<div key={svc.title} className="au-reveal au-service-card au-service-grid" style={{ display: 'grid', gridTemplateColumns: i % 2 === 0 ? '1fr 1.2fr' : '1.2fr 1fr', gap: 0 }}>
|
||||
<div style={{ order: i % 2 === 0 ? 2 : 1, position: 'relative', minHeight: 400, overflow: 'hidden', borderRadius: i % 2 === 0 ? '0 20px 20px 0' : '20px 0 0 20px' }}>
|
||||
<img src={svc.img} alt={svc.title} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', transition: 'transform .7s cubic-bezier(.16,1,.3,1)' }} loading="lazy" />
|
||||
</div>
|
||||
<div style={{ order: i % 2 === 0 ? 1 : 2, padding: '52px', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
||||
<div style={{ fontSize: 11, color: SAGE, fontWeight: 700, letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: 14 }}>{svc.sub}</div>
|
||||
<h3 style={{ fontFamily: "'Playfair Display',serif", fontSize: 32, fontWeight: 700, color: DARK, letterSpacing: '-0.02em', marginBottom: 18, lineHeight: 1.2, wordBreak: 'keep-all' }}>{svc.title}</h3>
|
||||
<p style={{ fontSize: 15, color: '#6B6456', lineHeight: 1.85, marginBottom: 28, wordBreak: 'keep-all' }}>{svc.desc}</p>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 36 }}>
|
||||
{svc.details.map((d) => (
|
||||
<div key={d} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<CheckIcon /><span style={{ fontSize: 14, color: '#5A5148' }}>{d}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<a href="#contact" className="au-btn-ghost" style={{ alignSelf: 'flex-start' }}>
|
||||
상담 신청하기 <ArrowRight />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ══ PROCESS (dark) ══ */}
|
||||
<section id="프로세스" style={{ padding: '100px 80px', background: DARK }}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
|
||||
<div className="au-reveal" style={{ textAlign: 'center', marginBottom: 72 }}>
|
||||
<div style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 12 }}>Process</div>
|
||||
<h2 style={{ fontFamily: "'Playfair Display',serif", fontSize: 'clamp(32px,3.5vw,48px)', fontWeight: 700, color: '#F5EDDF', letterSpacing: '-0.02em', wordBreak: 'keep-all' }}>
|
||||
상담부터 준공까지
|
||||
</h2>
|
||||
</div>
|
||||
<div className="au-process-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 2 }}>
|
||||
{steps.map((s, i) => (
|
||||
<div key={s.num} className="au-reveal" style={{ padding: '40px 32px', borderLeft: i > 0 ? '1px solid rgba(250,248,245,.06)' : 'none' }}>
|
||||
<div className="au-step-num">{s.num}</div>
|
||||
<div style={{ fontFamily: "'Playfair Display',serif", fontSize: 20, fontWeight: 600, color: '#F5EDDF', marginBottom: 12, lineHeight: 1.3 }}>{s.title}</div>
|
||||
<p style={{ fontSize: 14, color: '#8A7E70', lineHeight: 1.8, wordBreak: 'keep-all' }}>{s.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ══ TESTIMONIALS MASONRY ══ */}
|
||||
<section id="고객 후기" style={{ padding: '100px 80px', background: SURFACE }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div className="au-reveal" style={{ textAlign: 'center', marginBottom: 64 }}>
|
||||
<div style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 12 }}>Reviews</div>
|
||||
<h2 style={{ fontFamily: "'Playfair Display',serif", fontSize: 'clamp(32px,3.5vw,48px)', fontWeight: 700, color: DARK, letterSpacing: '-0.02em', wordBreak: 'keep-all' }}>
|
||||
공간이 바꾼 이야기들
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="au-testimony-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, alignItems: 'start' }}>
|
||||
{testimonials.map((t, i) => (
|
||||
<div key={t.name} className="au-reveal au-testimony" style={{ marginTop: i === 1 ? 40 : 0 }}>
|
||||
<div className="au-testimony-inner">
|
||||
<div style={{ display: 'inline-block', background: 'rgba(139,105,20,.08)', borderRadius: 100, padding: '4px 12px', marginBottom: 18, border: '1px solid rgba(139,105,20,.15)' }}>
|
||||
<span style={{ fontSize: 11, color: GOLD, fontWeight: 700 }}>{t.highlight}</span>
|
||||
</div>
|
||||
<div style={{ fontFamily: "'Playfair Display',serif", fontSize: 48, color: 'rgba(139,105,20,.15)', lineHeight: 0.8, marginBottom: 14 }}>"</div>
|
||||
<p style={{ fontSize: 15, color: '#4A4440', lineHeight: 1.85, marginBottom: 24, wordBreak: 'keep-all' }}>{t.text}</p>
|
||||
<div style={{ display: 'flex', gap: 3, marginBottom: 16 }}>
|
||||
{[1,2,3,4,5].map(j => <StarIcon key={j} filled={j <= t.rating} />)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, paddingTop: 16, borderTop: '1px solid rgba(139,105,20,.08)' }}>
|
||||
<img src={`https://i.pravatar.cc/80?u=${t.u}`} alt={t.name} style={{ width: 40, height: 40, borderRadius: '50%', objectFit: 'cover' }} loading="lazy" />
|
||||
<div>
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: DARK }}>{t.name}</div>
|
||||
<div style={{ fontSize: 12, color: '#A0917C' }}>{t.role}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="au-reveal" style={{ marginTop: 72, overflow: 'hidden', borderTop: '1px solid rgba(139,105,20,.1)', paddingTop: 40 }}>
|
||||
<div style={{ fontSize: 11, color: '#A0917C', textAlign: 'center', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: 24 }}>함께한 브랜드들</div>
|
||||
<div style={{ display: 'flex', animation: 'au-marquee 22s linear infinite', width: 'fit-content', gap: 64 }}>
|
||||
{['에스프레소랩','루미너스','플로우캔버스','스텔라랩스','넥스트비전','브릿지웍스',
|
||||
'에스프레소랩','루미너스','플로우캔버스','스텔라랩스','넥스트비전','브릿지웍스'].map((b, i) => (
|
||||
<span key={i} style={{ fontFamily: "'Playfair Display',serif", fontSize: 18, fontWeight: 600, color: 'rgba(28,26,23,.18)', whiteSpace: 'nowrap', fontStyle: 'italic' }}>{b}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ══ CTA ══ */}
|
||||
<section id="contact" style={{ padding: '120px 80px', background: DARK, position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', right: -100, top: '50%', transform: 'translateY(-50%)', width: 600, height: 600, borderRadius: '50%', background: 'radial-gradient(circle,rgba(139,105,20,.1),transparent 70%)', pointerEvents: 'none' }} />
|
||||
|
||||
<div style={{ maxWidth: 720, margin: '0 auto', textAlign: 'center', position: 'relative', zIndex: 1 }}>
|
||||
<div className="au-reveal" style={{ display: 'inline-flex', alignItems: 'center', gap: 8, background: 'rgba(139,105,20,.12)', border: '1px solid rgba(139,105,20,.25)', borderRadius: 100, padding: '5px 14px', marginBottom: 32 }}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: GOLD }} />
|
||||
<span style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.15em', textTransform: 'uppercase' }}>72시간 내 제안서 발송</span>
|
||||
</div>
|
||||
|
||||
<h2 className="au-reveal" style={{ fontFamily: "'Playfair Display',serif", fontSize: 'clamp(36px,5vw,56px)', fontWeight: 700, color: '#F5EDDF', letterSpacing: '-0.02em', lineHeight: 1.2, marginBottom: 20, wordBreak: 'keep-all' }}>
|
||||
당신의 공간 이야기를<br />
|
||||
<em style={{ color: GOLD }}>지금 시작하세요.</em>
|
||||
</h2>
|
||||
|
||||
<p className="au-reveal" style={{ fontSize: 16, color: '#8A7E70', lineHeight: 1.85, marginBottom: 44, wordBreak: 'keep-all' }}>
|
||||
사진 한 장과 예산만 알려주세요.<br />
|
||||
72시간 내에 맞춤 제안서와 무드보드를 드립니다. 무료입니다.
|
||||
</p>
|
||||
|
||||
<div className="au-reveal" style={{ display: 'flex', gap: 14, justifyContent: 'center', flexWrap: 'wrap' }}>
|
||||
<Link href="/work/freelance?service=website" className="au-btn-primary" style={{ background: GOLD, color: DARK, fontSize: 16, padding: '16px 36px 16px 28px', animation: 'au-pulse-gold 3s infinite' }}>
|
||||
<span style={{ width: 34, height: 34, borderRadius: '50%', background: 'rgba(28,26,23,.15)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<ArrowRight color={DARK} />
|
||||
</span>
|
||||
무료 상담 신청하기
|
||||
</Link>
|
||||
<a href="tel:02-1234-5678" className="au-btn-ghost" style={{ color: '#F5EDDF', borderColor: 'rgba(245,237,223,.15)' }}>
|
||||
02-1234-5678
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="au-reveal" style={{ display: 'flex', gap: 24, justifyContent: 'center', marginTop: 40 }}>
|
||||
{['완공 보장','계약서 필수','AS 1년'].map((b) => (
|
||||
<div key={b} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<CheckIcon /><span style={{ fontSize: 13, color: '#8A7E70' }}>{b}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ══ FOOTER ══ */}
|
||||
<footer style={{ background: '#100E0B', padding: '40px 80px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 16 }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: "'Playfair Display',serif", fontSize: 16, fontWeight: 700, color: '#F5EDDF', fontStyle: 'italic' }}>Aura Interior</div>
|
||||
<div style={{ fontSize: 12, color: '#4A4035', marginTop: 4 }}>© 2024 아우라 인테리어. All rights reserved.</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 24 }}>
|
||||
{['포트폴리오','서비스 안내','상담 신청','Instagram'].map((l) => (
|
||||
<a key={l} href="#" style={{ fontSize: 13, color: '#4A4035', textDecoration: 'none', transition: 'color .3s' }}
|
||||
onMouseEnter={e => (e.currentTarget.style.color = '#A0917C')}
|
||||
onMouseLeave={e => (e.currentTarget.style.color = '#4A4035')}>{l}</a>
|
||||
))}
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{/* ══ SCROLL TO TOP ══ */}
|
||||
<button
|
||||
id="au-top-btn"
|
||||
style={{
|
||||
position: 'fixed', bottom: '5.5rem', right: '2rem', zIndex: 200,
|
||||
width: 48, height: 48, borderRadius: '50%',
|
||||
background: GOLD, border: 'none', cursor: 'pointer',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
boxShadow: '0 8px 32px rgba(139,105,20,0.35)',
|
||||
opacity: showTop ? 1 : 0,
|
||||
transform: showTop ? 'translateY(0) scale(1)' : 'translateY(12px) scale(0.9)',
|
||||
transition: 'opacity 0.35s cubic-bezier(0.16,1,0.3,1), transform 0.35s cubic-bezier(0.16,1,0.3,1)',
|
||||
pointerEvents: showTop ? 'auto' : 'none',
|
||||
}}
|
||||
aria-label="맨 위로"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#FAF8F5" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="18 15 12 9 6 15"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
408
app/work/website/samples/portfolio/page.tsx
Normal file
408
app/work/website/samples/portfolio/page.tsx
Normal file
@@ -0,0 +1,408 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
const awards = [
|
||||
{ name: 'Awwwards SOTD', count: '× 2', color: '#f59e0b' },
|
||||
{ name: 'CSS Design Awards', count: 'Winner', color: '#06b6d4' },
|
||||
{ name: 'Adobe Design Award', count: '은상', color: '#ec4899' },
|
||||
{ name: 'Google UX Cert.', count: '2023', color: '#34d399' },
|
||||
];
|
||||
|
||||
const services = [
|
||||
{ title: 'UI/UX Design', desc: '사용자 중심의 인터페이스 설계와 프로토타입', icon: 'M', color: '#00ff88' },
|
||||
{ title: 'Creative Development', desc: 'Three.js · WebGL · 몰입형 웹 경험 구현', icon: 'C', color: '#a855f7' },
|
||||
{ title: 'Brand Identity', desc: '로고부터 브랜드 시스템 전체 디자인', icon: 'B', color: '#06b6d4' },
|
||||
{ title: 'Motion Design', desc: 'After Effects · Lottie 모션 에셋 제작', icon: 'M', color: '#f59e0b' },
|
||||
];
|
||||
|
||||
const projects = [
|
||||
{ title: 'NEON CITY UI', desc: '사이버펑크 게임 인터페이스 디자인. 넥슨 신작 게임의 전체 UI 시스템 구축', gradient: 'linear-gradient(135deg, #00ff88, #00d4ff)', tag: 'UI/UX', year: '2024', featured: true },
|
||||
{ title: 'FLOW BRAND', desc: '핀테크 스타트업 브랜딩 — 시리즈 B 투자유치 직전 리브랜딩', gradient: 'linear-gradient(135deg, #a855f7, #ec4899)', tag: 'Branding', year: '2024', featured: false },
|
||||
{ title: 'ORBITAL APP', desc: '위성 추적 실시간 대시보드 — NASA 오픈 API 활용', gradient: 'linear-gradient(135deg, #3b82f6, #06b6d4)', tag: 'Web App', year: '2023', featured: false },
|
||||
{ title: 'TERRA SHOP', desc: '친환경 D2C 쇼핑몰 — 전환율 340% 개선 달성', gradient: 'linear-gradient(135deg, #22c55e, #84cc16)', tag: 'E-commerce', year: '2023', featured: false },
|
||||
{ title: 'PULSE MOTION', desc: '글로벌 광고 에이전시를 위한 모션 그래픽 패키지', gradient: 'linear-gradient(135deg, #f59e0b, #ef4444)', tag: 'Motion', year: '2023', featured: false },
|
||||
{ title: 'AXIS SYSTEM', desc: '스타트업 물류 관리 SaaS — 현재 MAU 12만', gradient: 'linear-gradient(135deg, #64748b, #475569)', tag: 'Dashboard', year: '2022', featured: false },
|
||||
];
|
||||
|
||||
const skills = [
|
||||
{ name: 'Figma', level: 98, category: 'Design' },
|
||||
{ name: 'Framer', level: 92, category: 'Design' },
|
||||
{ name: 'After Effects', level: 88, category: 'Motion' },
|
||||
{ name: 'React', level: 90, category: 'Code' },
|
||||
{ name: 'TypeScript', level: 85, category: 'Code' },
|
||||
{ name: 'Three.js', level: 75, category: 'Code' },
|
||||
];
|
||||
|
||||
const timeline = [
|
||||
{ year: '2023', event: 'Google UX Design Certificate 취득', type: 'award' },
|
||||
{ year: '2022', event: 'Awwwards SOTD 2회 수상', type: 'award' },
|
||||
{ year: '2021', event: 'LINE Corp. UI 디자이너 입사', type: 'career' },
|
||||
{ year: '2020', event: 'Hongik University 시각디자인과 졸업', type: 'edu' },
|
||||
{ year: '2019', event: 'Adobe Design Award Korea 은상', type: 'award' },
|
||||
];
|
||||
|
||||
const testimonials = [
|
||||
{ client: 'Joon Park', role: 'CEO, FlowTech', text: 'Jisu가 리디자인한 결과물로 투자 피칭에서 VC들의 반응이 완전히 달라졌습니다. 결과적으로 시리즈 B 클로징에 큰 역할을 했어요.' },
|
||||
{ client: 'Yuna Kim', role: 'Product Lead, Orbital', text: '기술적인 복잡함을 이렇게 아름다운 인터페이스로 풀어낼 수 있다는 게 놀라웠습니다. 유저 피드백 NPS가 34점 올랐습니다.' },
|
||||
];
|
||||
|
||||
export default function PortfolioSample() {
|
||||
const [hoveredProject, setHoveredProject] = useState<number | null>(null);
|
||||
const [activeCategory, setActiveCategory] = useState<string>('All');
|
||||
|
||||
const categories = ['All', 'UI/UX', 'Branding', 'Web App', 'Motion'];
|
||||
const filteredProjects = activeCategory === 'All' ? projects : projects.filter(p => p.tag === activeCategory);
|
||||
|
||||
return (
|
||||
<div style={{ background: '#000000', minHeight: '100vh', color: 'white' }}>
|
||||
<style>{`
|
||||
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap');
|
||||
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
|
||||
@keyframes scanline { 0% { top: -10%; } 100% { top: 110%; } }
|
||||
@keyframes glow { 0%, 100% { text-shadow: 0 0 20px rgba(0,255,136,0.5); } 50% { text-shadow: 0 0 40px rgba(0,255,136,0.9), 0 0 80px rgba(0,255,136,0.3); } }
|
||||
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
||||
@keyframes slideBar { from { width: 0; } to { width: 100%; } }
|
||||
@keyframes marquee { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
|
||||
@keyframes pulse-green { 0%, 100% { box-shadow: 0 0 0 0 rgba(0,255,136,0.4); } 50% { box-shadow: 0 0 0 6px rgba(0,255,136,0); } }
|
||||
.proj-card { transition: border-color 0.3s, transform 0.3s; }
|
||||
.proj-card:hover { border-color: rgba(0,255,136,0.3) !important; transform: translateY(-4px); }
|
||||
.service-card { transition: background 0.3s, border-color 0.3s; }
|
||||
.service-card:hover { background: rgba(255,255,255,0.04) !important; }
|
||||
.cat-btn { transition: background 0.2s, color 0.2s; cursor: pointer; }
|
||||
.cat-btn:hover { color: white !important; }
|
||||
.award-badge { transition: transform 0.2s; }
|
||||
.award-badge:hover { transform: translateY(-2px); }
|
||||
`}</style>
|
||||
|
||||
{/* Back Banner */}
|
||||
<div style={{ background: 'linear-gradient(135deg, #1e1b4b, #312e81)', padding: '10px 24px', display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<Link href="/work/website" style={{ color: '#a5b4fc', fontSize: 13, textDecoration: 'none', fontFamily: 'Space Grotesk, sans-serif' }}>
|
||||
← 홈페이지 제작 서비스로 돌아가기
|
||||
</Link>
|
||||
<span style={{ color: '#4c1d95' }}>|</span>
|
||||
<span style={{ color: '#00ff88', fontSize: 12, fontFamily: 'Space Mono, monospace', fontWeight: 700 }}>
|
||||
SAMPLE · 개인 포트폴리오
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Awards Marquee */}
|
||||
<div style={{ background: '#050505', borderBottom: '1px solid rgba(0,255,136,0.12)', padding: '10px 0', overflow: 'hidden' }}>
|
||||
<div style={{ display: 'flex', gap: 48, animation: 'marquee 18s linear infinite', width: 'fit-content' }}>
|
||||
{[...awards, ...awards].map((a, i) => (
|
||||
<div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12"><path d="M6 1l1.3 2.6L10 4.1 8 6.1l.5 2.9L6 7.6 3.5 9l.5-2.9-2-2 2.7-.5z" fill={a.color} /></svg>
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: 11, color: a.color, fontWeight: 700, letterSpacing: '0.1em', whiteSpace: 'nowrap' }}>{a.name}</span>
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: 10, color: '#374151' }}>{a.count}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navbar */}
|
||||
<nav style={{
|
||||
position: 'sticky', top: 0, zIndex: 100,
|
||||
background: 'rgba(0,0,0,0.92)', backdropFilter: 'blur(20px)',
|
||||
borderBottom: '1px solid rgba(0,255,136,0.1)',
|
||||
padding: '0 48px', height: 64,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
}}>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 15, fontWeight: 700, color: '#00ff88', letterSpacing: '-0.02em', animation: 'glow 3s ease-in-out infinite' }}>
|
||||
KJ<span style={{ color: 'rgba(0,255,136,0.35)' }}>_</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 32 }}>
|
||||
{['About', 'Work', 'Skills', 'Contact'].map((item) => (
|
||||
<span key={item} style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 13, fontWeight: 600, color: '#374151', cursor: 'pointer', letterSpacing: '0.05em', textTransform: 'uppercase' }}>{item}</span>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginRight: 8 }}>
|
||||
<div style={{ width: 7, height: 7, borderRadius: '50%', background: '#00ff88', animation: 'pulse-green 2s infinite' }} />
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: 11, color: '#00ff88' }}>Available for work</span>
|
||||
</div>
|
||||
<button style={{
|
||||
background: 'transparent', border: '1px solid #00ff88', color: '#00ff88',
|
||||
padding: '8px 20px', borderRadius: 4, fontSize: 12, fontWeight: 700,
|
||||
cursor: 'pointer', fontFamily: 'Space Mono, monospace', letterSpacing: '0.08em',
|
||||
}}>
|
||||
HIRE ME
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Hero */}
|
||||
<section style={{ padding: '100px 48px 80px', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', left: 0, right: 0, height: '2px', background: 'linear-gradient(90deg, transparent, rgba(0,255,136,0.4), transparent)', animation: 'scanline 8s linear infinite', pointerEvents: 'none' }} />
|
||||
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(0,255,136,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(0,255,136,0.04) 1px, transparent 1px)', backgroundSize: '40px 40px' }} />
|
||||
|
||||
<div style={{ maxWidth: 1000, display: 'grid', gridTemplateColumns: '1fr auto', gap: 48, alignItems: 'center', position: 'relative', animation: 'fadeUp 0.8s ease forwards' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 12, color: 'rgba(0,255,136,0.6)', letterSpacing: '0.2em', marginBottom: 20, textTransform: 'uppercase' }}>
|
||||
{'> Hello, World. I am'}
|
||||
</div>
|
||||
<h1 style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 'clamp(52px, 7vw, 88px)', fontWeight: 700, lineHeight: 1.0, marginBottom: 16, letterSpacing: '-0.03em' }}>
|
||||
Kim<br />
|
||||
<span style={{ color: '#00ff88' }}>Jisu</span>
|
||||
<span style={{ display: 'inline-block', width: 6, height: 'clamp(52px, 7vw, 88px)', background: '#00ff88', marginLeft: 8, verticalAlign: 'middle', animation: 'blink 1.2s step-end infinite' }} />
|
||||
</h1>
|
||||
<div style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 20, color: '#4b5563', fontWeight: 500, marginBottom: 20 }}>
|
||||
Product Designer & Creative Developer
|
||||
</div>
|
||||
<p style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 16, color: '#6b7280', lineHeight: 1.8, maxWidth: 520, marginBottom: 36 }}>
|
||||
픽셀 하나하나에 의미를 담는 디자이너. 아름다움과 기능의 교차점에서 디지털 경험을 설계합니다.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<button style={{
|
||||
background: '#00ff88', color: '#000000', border: 'none', padding: '14px 32px',
|
||||
fontSize: 14, fontWeight: 700, cursor: 'pointer', fontFamily: 'Space Mono, monospace',
|
||||
letterSpacing: '0.05em', clipPath: 'polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px))',
|
||||
}}>
|
||||
VIEW WORK →
|
||||
</button>
|
||||
<button style={{
|
||||
background: 'transparent', color: '#9ca3af', border: '1px solid #1f2937',
|
||||
padding: '14px 32px', fontSize: 14, fontWeight: 600, cursor: 'pointer',
|
||||
fontFamily: 'Space Grotesk, sans-serif', borderRadius: 4,
|
||||
}}>
|
||||
Download CV
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats panel */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{[
|
||||
{ val: '6+', label: 'YEARS EXP.', color: '#00ff88' },
|
||||
{ val: '30+', label: 'PROJECTS', color: '#a855f7' },
|
||||
{ val: '2×', label: 'AWWWARDS', color: '#f59e0b' },
|
||||
{ val: '340%', label: 'MAX CVR LIFT', color: '#06b6d4' },
|
||||
].map((s) => (
|
||||
<div key={s.label} style={{ padding: '14px 18px', border: '1px solid #111827', borderLeft: `3px solid ${s.color}`, borderRadius: 4, background: '#050505', minWidth: 140 }}>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 24, fontWeight: 700, color: s.color }}>{s.val}</div>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 9, color: '#374151', letterSpacing: '0.15em', marginTop: 2 }}>{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Services */}
|
||||
<section style={{ padding: '72px 48px', background: '#050505', borderTop: '1px solid rgba(0,255,136,0.08)' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ marginBottom: 40 }}>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 10, color: 'rgba(0,255,136,0.5)', letterSpacing: '0.2em', marginBottom: 10 }}>// WHAT I DO</div>
|
||||
<h2 style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 32, fontWeight: 700, letterSpacing: '-0.02em' }}>
|
||||
Services<span style={{ color: '#00ff88' }}>.</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
|
||||
{services.map((svc) => (
|
||||
<div key={svc.title} className="service-card" style={{
|
||||
padding: '24px 20px', border: '1px solid #111827', borderRadius: 8,
|
||||
background: 'transparent', cursor: 'pointer', position: 'relative', overflow: 'hidden',
|
||||
}}>
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, width: 3, height: '100%', background: svc.color, opacity: 0.6 }} />
|
||||
<div style={{ width: 36, height: 36, borderRadius: 8, background: svc.color + '15', border: `1px solid ${svc.color}30`, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 16 }}>
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: 12, fontWeight: 700, color: svc.color }}>{svc.icon}</span>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 15, fontWeight: 700, color: 'white', marginBottom: 8 }}>{svc.title}</div>
|
||||
<div style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 13, color: '#4b5563', lineHeight: 1.6 }}>{svc.desc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Awards Detail */}
|
||||
<section style={{ padding: '60px 48px', background: '#000000', borderTop: '1px solid rgba(0,255,136,0.08)' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
||||
{awards.map((a) => (
|
||||
<div key={a.name} className="award-badge" style={{
|
||||
padding: '12px 20px', border: `1px solid ${a.color}30`, borderRadius: 8,
|
||||
background: a.color + '08', display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer',
|
||||
}}>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14"><path d="M7 1l1.5 3 3.5.5-2.5 2.4.6 3.4L7 8.8 3.9 10.3l.6-3.4L2 4.5 5.5 4z" fill={a.color} /></svg>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 13, fontWeight: 700, color: 'white' }}>{a.name}</div>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 10, color: a.color }}>{a.count}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Projects */}
|
||||
<section style={{ padding: '80px 48px', background: '#050505', borderTop: '1px solid rgba(0,255,136,0.08)' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 32 }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 10, color: 'rgba(0,255,136,0.5)', letterSpacing: '0.2em', marginBottom: 8 }}>// SELECTED WORK</div>
|
||||
<h2 style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 36, fontWeight: 700, letterSpacing: '-0.02em' }}>
|
||||
Projects<span style={{ color: '#00ff88' }}>.</span>
|
||||
</h2>
|
||||
</div>
|
||||
<span style={{ color: '#374151', fontSize: 12, fontFamily: 'Space Mono, monospace' }}>2019 — 2024</span>
|
||||
</div>
|
||||
{/* Category filter */}
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 32 }}>
|
||||
{categories.map((cat) => (
|
||||
<button key={cat} className="cat-btn" onClick={() => setActiveCategory(cat)} style={{
|
||||
background: activeCategory === cat ? '#00ff88' : 'transparent',
|
||||
color: activeCategory === cat ? '#000' : '#374151',
|
||||
border: '1px solid ' + (activeCategory === cat ? '#00ff88' : '#1f2937'),
|
||||
padding: '6px 16px', borderRadius: 4, fontSize: 11,
|
||||
fontWeight: 700, fontFamily: 'Space Mono, monospace', letterSpacing: '0.06em',
|
||||
}}>{cat}</button>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 16 }}>
|
||||
{filteredProjects.map((proj, i) => (
|
||||
<div
|
||||
key={proj.title}
|
||||
className="proj-card"
|
||||
onMouseEnter={() => setHoveredProject(i)}
|
||||
onMouseLeave={() => setHoveredProject(null)}
|
||||
style={{ border: '1px solid #111827', borderRadius: 12, overflow: 'hidden', cursor: 'pointer', background: '#0a0a0a', position: 'relative' }}
|
||||
>
|
||||
<div style={{ height: 200, background: proj.gradient, position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<div style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 22, fontWeight: 700, color: 'rgba(0,0,0,0.35)', letterSpacing: '-0.02em' }}>{proj.title}</div>
|
||||
</div>
|
||||
{/* Hover overlay */}
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(4px)',
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12,
|
||||
transition: 'opacity 0.3s', opacity: hoveredProject === i ? 1 : 0,
|
||||
}}>
|
||||
<div style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 14, color: 'white', fontWeight: 600, textAlign: 'center', padding: '0 20px', lineHeight: 1.5 }}>{proj.desc}</div>
|
||||
<button style={{ background: '#00ff88', color: '#000', border: 'none', padding: '8px 20px', borderRadius: 4, fontSize: 11, fontWeight: 700, cursor: 'pointer', fontFamily: 'Space Mono, monospace', letterSpacing: '0.06em' }}>
|
||||
VIEW PROJECT →
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ position: 'absolute', top: 12, left: 12, background: 'rgba(0,0,0,0.5)', backdropFilter: 'blur(8px)', border: '1px solid rgba(255,255,255,0.15)', borderRadius: 4, padding: '3px 10px', fontSize: 10, fontWeight: 700, color: 'white', fontFamily: 'Space Mono, monospace', letterSpacing: '0.1em' }}>
|
||||
{proj.tag}
|
||||
</div>
|
||||
<div style={{ position: 'absolute', top: 12, right: 12, fontFamily: 'Space Mono, monospace', fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>
|
||||
{proj.year}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ padding: '16px 18px' }}>
|
||||
<div style={{ fontSize: 15, fontWeight: 700, color: 'white', fontFamily: 'Space Grotesk, sans-serif', marginBottom: 4 }}>{proj.title}</div>
|
||||
<div style={{ fontSize: 13, color: '#4b5563', fontFamily: 'Space Grotesk, sans-serif', lineHeight: 1.5 }}>{proj.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Skills + Timeline */}
|
||||
<section style={{ padding: '80px 48px', background: '#000000', borderTop: '1px solid rgba(0,255,136,0.08)' }}>
|
||||
<div style={{ maxWidth: 1000, margin: '0 auto', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 64, alignItems: 'start' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 10, color: 'rgba(0,255,136,0.5)', letterSpacing: '0.2em', marginBottom: 10 }}>// EXPERTISE</div>
|
||||
<h2 style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 32, fontWeight: 700, marginBottom: 36, letterSpacing: '-0.02em' }}>
|
||||
Skills<span style={{ color: '#00ff88' }}>.</span>
|
||||
</h2>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
|
||||
{skills.map((s) => (
|
||||
<div key={s.name}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 7 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: 12, color: '#9ca3af', letterSpacing: '0.05em' }}>{s.name}</span>
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: 9, color: '#1f2937', letterSpacing: '0.1em', border: '1px solid #1f2937', padding: '1px 6px', borderRadius: 2 }}>{s.category}</span>
|
||||
</div>
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: 11, color: '#00ff88' }}>{s.level}%</span>
|
||||
</div>
|
||||
<div style={{ height: 3, background: '#111827', borderRadius: 2, overflow: 'hidden' }}>
|
||||
<div style={{ height: '100%', borderRadius: 2, background: 'linear-gradient(90deg, #00ff88, #00d4ff)', width: `${s.level}%`, animation: 'slideBar 1.5s ease forwards' }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 10, color: 'rgba(0,255,136,0.5)', letterSpacing: '0.2em', marginBottom: 10 }}>// JOURNEY</div>
|
||||
<h2 style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 32, fontWeight: 700, marginBottom: 36, letterSpacing: '-0.02em' }}>
|
||||
Timeline<span style={{ color: '#00ff88' }}>.</span>
|
||||
</h2>
|
||||
<div style={{ position: 'relative', paddingLeft: 20 }}>
|
||||
<div style={{ position: 'absolute', left: 4, top: 8, bottom: 8, width: 1, background: '#1f2937' }} />
|
||||
{timeline.map((t, i) => (
|
||||
<div key={i} style={{ position: 'relative', paddingBottom: 24 }}>
|
||||
<div style={{
|
||||
position: 'absolute', left: -20, top: 4, width: 8, height: 8, borderRadius: '50%',
|
||||
background: t.type === 'award' ? '#00ff88' : t.type === 'career' ? '#3b82f6' : '#a855f7',
|
||||
boxShadow: `0 0 8px ${t.type === 'award' ? 'rgba(0,255,136,0.6)' : t.type === 'career' ? 'rgba(59,130,246,0.6)' : 'rgba(168,85,247,0.6)'}`,
|
||||
}} />
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 11, color: '#374151', marginBottom: 3 }}>{t.year}</div>
|
||||
<div style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 14, color: '#d1d5db', lineHeight: 1.5 }}>{t.event}</div>
|
||||
{t.type === 'award' && (
|
||||
<div style={{ marginTop: 4 }}>
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: 9, color: '#00ff88', border: '1px solid rgba(0,255,136,0.3)', padding: '1px 6px', borderRadius: 2 }}>AWARD</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Testimonials */}
|
||||
<section style={{ padding: '72px 48px', background: '#050505', borderTop: '1px solid rgba(0,255,136,0.08)' }}>
|
||||
<div style={{ maxWidth: 1000, margin: '0 auto' }}>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 10, color: 'rgba(0,255,136,0.5)', letterSpacing: '0.2em', marginBottom: 10 }}>// CLIENT VOICES</div>
|
||||
<h2 style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 32, fontWeight: 700, marginBottom: 36, letterSpacing: '-0.02em' }}>
|
||||
Testimonials<span style={{ color: '#00ff88' }}>.</span>
|
||||
</h2>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
|
||||
{testimonials.map((t) => (
|
||||
<div key={t.client} style={{ padding: '28px 24px', border: '1px solid #111827', borderRadius: 8, background: '#0a0a0a', position: 'relative' }}>
|
||||
<svg width="28" height="20" viewBox="0 0 28 20" fill="none" style={{ marginBottom: 16, opacity: 0.25 }}>
|
||||
<path d="M0 20V12C0 5.3 4 1.5 12 0l1.7 3C9.5 4.3 7.3 6.7 7 10h5.3V20H0zm14.7 0V12C14.7 5.3 18.7 1.5 26.7 0l1.3 3c-4.2 1.3-6.4 3.7-6.7 7H27V20H14.7z" fill="#00ff88" />
|
||||
</svg>
|
||||
<p style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 14, color: '#9ca3af', lineHeight: 1.8, marginBottom: 20 }}>{t.text}</p>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{ width: 36, height: 36, borderRadius: '50%', background: 'linear-gradient(135deg, #00ff88, #00d4ff)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, fontWeight: 700, color: '#000', fontFamily: 'Space Mono, monospace' }}>
|
||||
{t.client[0]}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 14, fontWeight: 600, color: 'white' }}>{t.client}</div>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 10, color: '#374151' }}>{t.role}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact */}
|
||||
<section style={{ padding: '96px 48px', textAlign: 'center', background: 'linear-gradient(180deg, #000000, #001a00)', borderTop: '1px solid rgba(0,255,136,0.1)' }}>
|
||||
<div style={{ fontFamily: 'Space Mono, monospace', fontSize: 12, color: 'rgba(0,255,136,0.5)', letterSpacing: '0.2em', marginBottom: 20 }}>
|
||||
{'> LET\'S COLLABORATE'}
|
||||
</div>
|
||||
<h2 style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 'clamp(36px, 5vw, 60px)', fontWeight: 700, letterSpacing: '-0.03em', marginBottom: 16, animation: 'glow 3s ease-in-out infinite' }}>
|
||||
Have a project<span style={{ color: '#00ff88' }}>?</span>
|
||||
</h2>
|
||||
<p style={{ fontFamily: 'Space Grotesk, sans-serif', fontSize: 16, color: '#4b5563', marginBottom: 8 }}>
|
||||
jisu.kim@design.studio
|
||||
</p>
|
||||
<p style={{ fontFamily: 'Space Mono, monospace', fontSize: 12, color: '#1f2937', marginBottom: 36 }}>
|
||||
@jisu_creates · Response within 24h
|
||||
</p>
|
||||
<button style={{
|
||||
background: '#00ff88', color: '#000000', border: 'none', padding: '16px 48px',
|
||||
fontSize: 14, fontWeight: 700, cursor: 'pointer', fontFamily: 'Space Mono, monospace',
|
||||
letterSpacing: '0.08em', clipPath: 'polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px))',
|
||||
}}>
|
||||
START A PROJECT →
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
663
app/work/website/samples/reading/page.tsx
Normal file
663
app/work/website/samples/reading/page.tsx
Normal file
@@ -0,0 +1,663 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
/* ═══════════════════════════════════════
|
||||
SVG ICONS
|
||||
═══════════════════════════════════════ */
|
||||
const BookOpenIcon = () => (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
);
|
||||
const StarIcon = ({ filled = true }: { filled?: boolean }) => (
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill={filled ? '#D4A853' : 'none'} stroke="#D4A853" strokeWidth="1.5">
|
||||
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26"/>
|
||||
</svg>
|
||||
);
|
||||
const QuoteIcon = () => (
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||
<path d="M8 8C4 10.5 2 14 2 18c0 3.3 2 5 4 5s3.5-1.5 3.5-4c0-2.3-1.2-3.5-2.5-3.5C5.5 15.5 5 15 6 13c.8-1.5 2.5-3 4-3.5L8 8zm16 0c-4 2.5-6 6-6 10 0 3.3 2 5 4 5s3.5-1.5 3.5-4c0-2.3-1.2-3.5-2.5-3.5C21.5 15.5 21 15 22 13c.8-1.5 2.5-3 4-3.5L24 8z" fill="#D4A853" opacity="0.6"/>
|
||||
</svg>
|
||||
);
|
||||
const CheckIcon = () => (
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#D4A853" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>
|
||||
);
|
||||
const ArrowUpRight = () => (
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="7" y1="17" x2="17" y2="7"/><polyline points="7 7 17 7 17 17"/>
|
||||
</svg>
|
||||
);
|
||||
const ChevronDown = () => (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
/* ═══════════════════════════════════════
|
||||
COUNTUP HOOK
|
||||
═══════════════════════════════════════ */
|
||||
function useCountUp(target: number, duration = 1600) {
|
||||
const [count, setCount] = useState(0);
|
||||
const ref = useRef<HTMLSpanElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const obs = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (!entry.isIntersecting) return;
|
||||
obs.disconnect();
|
||||
const start = performance.now();
|
||||
const tick = (now: number) => {
|
||||
const elapsed = now - start;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
const ease = 1 - Math.pow(1 - progress, 3);
|
||||
setCount(Math.round(target * ease));
|
||||
if (progress < 1) requestAnimationFrame(tick);
|
||||
};
|
||||
requestAnimationFrame(tick);
|
||||
},
|
||||
{ threshold: 0.4 }
|
||||
);
|
||||
obs.observe(el);
|
||||
return () => obs.disconnect();
|
||||
}, [target, duration]);
|
||||
|
||||
return { count, ref };
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════
|
||||
STAT ITEM
|
||||
═══════════════════════════════════════ */
|
||||
function StatItem({ value, suffix, label }: { value: number; suffix?: string; label: string }) {
|
||||
const { count, ref } = useCountUp(value);
|
||||
return (
|
||||
<div style={{ textAlign: 'center', padding: '0 2rem' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'center', gap: '2px' }}>
|
||||
<span ref={ref} style={{ fontSize: '3rem', fontFamily: "'Cormorant Garamond', serif", fontWeight: 700, color: '#D4A853', lineHeight: 1 }}>{count}</span>
|
||||
{suffix && <span style={{ fontSize: '1.5rem', fontFamily: "'Cormorant Garamond', serif", color: '#D4A853' }}>{suffix}</span>}
|
||||
</div>
|
||||
<p style={{ fontSize: '0.78rem', color: '#8A8070', marginTop: '0.5rem', letterSpacing: '0.1em', textTransform: 'uppercase', fontFamily: "'Pretendard', sans-serif" }}>{label}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════
|
||||
BOOK CARD
|
||||
═══════════════════════════════════════ */
|
||||
interface Book {
|
||||
seed: string; title: string; author: string; rating: number; genre: string; year: number; note?: string;
|
||||
}
|
||||
|
||||
const books: Book[] = [
|
||||
{ seed: 'book1', title: '파친코', author: '이민진', rating: 5, genre: '소설', year: 2024, note: '역사 속에 묻힌 삶의 무게를 느꼈다' },
|
||||
{ seed: 'book2', title: '어린 왕자', author: '생텍쥐페리', rating: 5, genre: '고전', year: 2023 },
|
||||
{ seed: 'book3', title: '원씽', author: '게리 켈러', rating: 4, genre: '자기계발', year: 2024 },
|
||||
{ seed: 'book4', title: '채식주의자', author: '한강', rating: 5, genre: '소설', year: 2023, note: '불편함 속의 아름다움' },
|
||||
{ seed: 'book5', title: '지능의 본질', author: '제프 호킨스', rating: 4, genre: '과학', year: 2024 },
|
||||
{ seed: 'book6', title: '82년생 김지영', author: '조남주', rating: 4, genre: '소설', year: 2022 },
|
||||
{ seed: 'book7', title: '노인과 바다', author: '헤밍웨이', rating: 5, genre: '고전', year: 2022 },
|
||||
{ seed: 'book8', title: '생각에 관한 생각', author: '다니엘 카너먼', rating: 4, genre: '심리학', year: 2023 },
|
||||
];
|
||||
|
||||
const genreColors: Record<string, string> = {
|
||||
'소설': '#6B5B95', '고전': '#D4A853', '자기계발': '#4A7C59', '과학': '#2E6EA6', '심리학': '#8B4E62'
|
||||
};
|
||||
|
||||
function BookCard({ book, large = false }: { book: Book; large?: boolean }) {
|
||||
return (
|
||||
<div
|
||||
className="rd-reveal"
|
||||
style={{
|
||||
background: 'rgba(255,255,255,0.03)',
|
||||
border: '1px solid rgba(212,168,83,0.15)',
|
||||
borderRadius: '12px',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
height: large ? '420px' : '340px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
transition: 'transform 0.5s cubic-bezier(0.16,1,0.3,1), box-shadow 0.5s cubic-bezier(0.16,1,0.3,1)',
|
||||
cursor: 'default',
|
||||
}}
|
||||
onMouseEnter={e => {
|
||||
(e.currentTarget as HTMLElement).style.transform = 'translateY(-6px)';
|
||||
(e.currentTarget as HTMLElement).style.boxShadow = '0 20px 60px rgba(212,168,83,0.12)';
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
(e.currentTarget as HTMLElement).style.transform = 'translateY(0)';
|
||||
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
|
||||
}}
|
||||
>
|
||||
{/* cover */}
|
||||
<div style={{ flex: 1, overflow: 'hidden', position: 'relative' }}>
|
||||
<img
|
||||
src={`https://picsum.photos/seed/${book.seed}/300/450`}
|
||||
alt={book.title}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover', filter: 'brightness(0.75) saturate(0.7)' }}
|
||||
/>
|
||||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to bottom, transparent 40%, rgba(12,11,9,0.95))' }}/>
|
||||
{/* genre badge */}
|
||||
<div style={{
|
||||
position: 'absolute', top: '0.75rem', left: '0.75rem',
|
||||
background: genreColors[book.genre] || '#6B5B95',
|
||||
color: 'white', fontSize: '0.65rem', fontFamily: "'Pretendard', sans-serif",
|
||||
padding: '3px 8px', borderRadius: '4px', fontWeight: 600, letterSpacing: '0.05em'
|
||||
}}>{book.genre}</div>
|
||||
{/* rating */}
|
||||
<div style={{ position: 'absolute', top: '0.75rem', right: '0.75rem', display: 'flex', gap: '2px' }}>
|
||||
{[1,2,3,4,5].map(i => <StarIcon key={i} filled={i <= book.rating}/>)}
|
||||
</div>
|
||||
</div>
|
||||
{/* info */}
|
||||
<div style={{ padding: '0.875rem', background: 'rgba(12,11,9,0.95)' }}>
|
||||
<p style={{ fontSize: '0.65rem', color: '#8A8070', fontFamily: "'Pretendard', sans-serif", marginBottom: '0.25rem' }}>{book.author} · {book.year}</p>
|
||||
<h3 style={{ fontSize: large ? '1.1rem' : '0.95rem', fontFamily: "'Cormorant Garamond', serif", fontWeight: 700, color: '#F0EAD8', margin: 0, wordBreak: 'keep-all', lineHeight: 1.3 }}>{book.title}</h3>
|
||||
{book.note && <p style={{ fontSize: '0.72rem', color: '#6A6050', fontFamily: "'Pretendard', sans-serif", marginTop: '0.4rem', wordBreak: 'keep-all', fontStyle: 'italic' }}>"{book.note}"</p>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════
|
||||
MAIN PAGE
|
||||
═══════════════════════════════════════ */
|
||||
export default function ReadingPage() {
|
||||
const [activeGenre, setActiveGenre] = useState('전체');
|
||||
const [showTop, setShowTop] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const scroller: HTMLElement =
|
||||
(document.querySelector('.main-content') as HTMLElement | null) ??
|
||||
document.documentElement;
|
||||
|
||||
const onScroll = () => setShowTop(scroller.scrollTop > 400);
|
||||
scroller.addEventListener('scroll', onScroll, { passive: true });
|
||||
|
||||
const obs = new IntersectionObserver(
|
||||
entries => entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('rd-visible'); }),
|
||||
{ threshold: 0.1, root: scroller === document.documentElement ? null : scroller }
|
||||
);
|
||||
document.querySelectorAll('.rd-reveal').forEach(el => obs.observe(el));
|
||||
return () => {
|
||||
scroller.removeEventListener('scroll', onScroll);
|
||||
obs.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const genres = ['전체', '소설', '고전', '자기계발', '과학', '심리학'];
|
||||
const filteredBooks = activeGenre === '전체' ? books : books.filter(b => b.genre === activeGenre);
|
||||
|
||||
const quotes = [
|
||||
{ text: '당신이 무엇을 읽느냐가 당신이 누구인지를 말해준다. 책은 당신이 선택한 삶의 궤적이다.', author: '파친코 — 이민진', genre: '소설' },
|
||||
{ text: '어른들은 숫자를 좋아한다. 새 친구 이야기를 해줄 때, 중요한 것을 절대 묻지 않는다. 그의 목소리가 어떤지, 어떤 놀이를 좋아하는지.', author: '어린 왕자 — 생텍쥐페리', genre: '고전' },
|
||||
{ text: '성공한 사람들은 하나에 집중하고 나머지를 내려놓는 법을 배웠다. 한 가지를 잘하면 나머지가 따라온다.', author: '원씽 — 게리 켈러', genre: '자기계발' },
|
||||
{ text: '빠른 생각은 직관이고 느린 생각은 이성이다. 우리는 대부분 빠른 생각이 옳다고 착각하며 살아간다.', author: '생각에 관한 생각 — 다니엘 카너먼', genre: '심리학' },
|
||||
];
|
||||
|
||||
const currentlyReading = {
|
||||
seed: 'current1', title: '도둑맞은 집중력', author: '요한 하리', genre: '자기계발',
|
||||
progress: 67, totalPages: 380, currentPage: 255,
|
||||
startDate: '2024.03.10', note: '현대 사회가 어떻게 우리의 집중력을 앗아가는지 설득력 있게 풀어냄'
|
||||
};
|
||||
|
||||
const tbr = [
|
||||
{ seed: 'tbr1', title: '총, 균, 쇠', author: '재레드 다이아몬드', genre: '역사', priority: '높음' },
|
||||
{ seed: 'tbr2', title: '코스모스', author: '칼 세이건', genre: '과학', priority: '높음' },
|
||||
{ seed: 'tbr3', title: '사피엔스', author: '유발 하라리', genre: '역사', priority: '중간' },
|
||||
];
|
||||
|
||||
const monthlyData = [
|
||||
{ month: '10월', books: 2 }, { month: '11월', books: 3 }, { month: '12월', books: 1 },
|
||||
{ month: '1월', books: 4 }, { month: '2월', books: 3 }, { month: '3월', books: 2 },
|
||||
];
|
||||
const maxBooks = Math.max(...monthlyData.map(d => d.books));
|
||||
|
||||
return (
|
||||
<>
|
||||
<style dangerouslySetInnerHTML={{ __html: `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&display=swap');
|
||||
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.min.css');
|
||||
|
||||
.rd-root { font-family: 'Pretendard', sans-serif; background: #0C0B09; color: #F0EAD8; min-height: 100dvh; }
|
||||
.rd-serif { font-family: 'Cormorant Garamond', serif; }
|
||||
|
||||
/* grain overlay */
|
||||
.rd-grain::before {
|
||||
content: '';
|
||||
position: fixed; inset: 0; z-index: 0; pointer-events: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.07'/%3E%3C/svg%3E");
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* scroll reveal */
|
||||
.rd-reveal {
|
||||
opacity: 0; transform: translateY(2rem); filter: blur(2px);
|
||||
transition: opacity 0.7s cubic-bezier(0.16,1,0.3,1), transform 0.7s cubic-bezier(0.16,1,0.3,1), filter 0.7s cubic-bezier(0.16,1,0.3,1);
|
||||
}
|
||||
.rd-reveal.rd-visible { opacity: 1; transform: translateY(0); filter: blur(0); }
|
||||
.rd-reveal:nth-child(2) { transition-delay: 0.08s; }
|
||||
.rd-reveal:nth-child(3) { transition-delay: 0.16s; }
|
||||
.rd-reveal:nth-child(4) { transition-delay: 0.24s; }
|
||||
.rd-reveal:nth-child(5) { transition-delay: 0.32s; }
|
||||
|
||||
/* nav */
|
||||
.rd-nav {
|
||||
position: fixed; top: 1rem; left: 50%; transform: translateX(-50%); z-index: 100;
|
||||
background: rgba(12,11,9,0.7); backdrop-filter: blur(20px) saturate(180%);
|
||||
border: 1px solid rgba(212,168,83,0.2); border-radius: 100px;
|
||||
padding: 0.75rem 2rem; display: flex; align-items: center; gap: 2.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* double-bezel cards */
|
||||
.rd-bezel {
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid rgba(212,168,83,0.2);
|
||||
border-radius: 16px; padding: 1px;
|
||||
box-shadow: inset 0 1px 0 rgba(212,168,83,0.1), 0 0 0 1px rgba(0,0,0,0.5);
|
||||
}
|
||||
.rd-bezel-inner {
|
||||
background: rgba(20,18,14,0.9);
|
||||
border: 1px solid rgba(255,255,255,0.04);
|
||||
border-radius: 15px; padding: 1.75rem;
|
||||
}
|
||||
|
||||
/* genre filter */
|
||||
.rd-genre-btn {
|
||||
background: transparent; border: 1px solid rgba(212,168,83,0.2);
|
||||
color: #8A8070; font-family: 'Pretendard', sans-serif; font-size: 0.8rem;
|
||||
padding: 0.4rem 1rem; border-radius: 100px; cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.16,1,0.3,1);
|
||||
}
|
||||
.rd-genre-btn:hover { border-color: rgba(212,168,83,0.5); color: #D4A853; }
|
||||
.rd-genre-btn.active { background: rgba(212,168,83,0.15); border-color: #D4A853; color: #D4A853; }
|
||||
|
||||
/* progress bar */
|
||||
.rd-progress-track { background: rgba(255,255,255,0.06); border-radius: 100px; height: 4px; overflow: hidden; }
|
||||
.rd-progress-fill { height: 100%; border-radius: 100px; background: linear-gradient(90deg, #D4A853, #F5C842); transition: width 1s cubic-bezier(0.16,1,0.3,1); }
|
||||
|
||||
/* marquee */
|
||||
@keyframes rd-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
|
||||
.rd-marquee-track { animation: rd-marquee 25s linear infinite; display: flex; gap: 0; }
|
||||
|
||||
/* hero pulse */
|
||||
@keyframes rd-pulse { 0%,100% { opacity:0.3; transform: scale(1); } 50% { opacity:0.6; transform: scale(1.05); } }
|
||||
.rd-orb { animation: rd-pulse 4s ease-in-out infinite; }
|
||||
|
||||
/* CTA glow */
|
||||
.rd-cta-btn {
|
||||
background: #D4A853; color: #0C0B09; border: none;
|
||||
font-family: 'Pretendard', sans-serif; font-weight: 700; font-size: 0.9rem;
|
||||
padding: 0.875rem 2.5rem; border-radius: 100px; cursor: pointer;
|
||||
letter-spacing: 0.05em;
|
||||
transition: all 0.3s cubic-bezier(0.16,1,0.3,1);
|
||||
box-shadow: 0 0 40px rgba(212,168,83,0.3);
|
||||
}
|
||||
.rd-cta-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 40px rgba(212,168,83,0.5); background: #E8BC60; }
|
||||
|
||||
/* scrollbar */
|
||||
::-webkit-scrollbar { width: 4px; }
|
||||
::-webkit-scrollbar-track { background: #0C0B09; }
|
||||
::-webkit-scrollbar-thumb { background: rgba(212,168,83,0.3); border-radius: 2px; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.rd-nav { padding: 0.6rem 1.25rem; gap: 1.5rem; }
|
||||
.rd-bento { grid-template-columns: 1fr !important; }
|
||||
.rd-hero-title { font-size: clamp(3.5rem, 12vw, 8rem) !important; }
|
||||
.rd-stats-grid { grid-template-columns: 1fr 1fr !important; gap: 1.5rem !important; }
|
||||
.rd-quote-grid { grid-template-columns: 1fr !important; }
|
||||
.rd-currently { grid-template-columns: 1fr !important; }
|
||||
.rd-monthly-bar { height: 80px !important; }
|
||||
}
|
||||
`}} />
|
||||
|
||||
{/* ── BACK BANNER ── */}
|
||||
<div style={{
|
||||
background: 'linear-gradient(135deg,#0C0B09,#1A1710)',
|
||||
borderBottom: '1px solid rgba(212,168,83,0.12)',
|
||||
height: 40, display: 'flex', alignItems: 'center',
|
||||
padding: '0 24px', gap: 12, flexShrink: 0, position: 'relative', zIndex: 300,
|
||||
}}>
|
||||
<Link href="/work/website" style={{ color: 'rgba(212,168,83,0.7)', fontSize: 13, textDecoration: 'none', display: 'flex', alignItems: 'center', gap: 6, transition: 'color 0.2s' }}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.color = '#D4A853')}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = 'rgba(212,168,83,0.7)')}>
|
||||
← 홈페이지 제작 서비스로 돌아가기
|
||||
</Link>
|
||||
<span style={{ color: 'rgba(212,168,83,0.2)' }}>|</span>
|
||||
<span style={{ color: 'rgba(212,168,83,0.4)', fontSize: 12, fontFamily: "'Cormorant Garamond', serif", fontStyle: 'italic' }}>
|
||||
SAMPLE · 독서 기록 노트
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="rd-root rd-grain" style={{ position: 'relative' }}>
|
||||
|
||||
{/* ── NAV ── */}
|
||||
<nav className="rd-nav">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
<BookOpenIcon/>
|
||||
<span className="rd-serif" style={{ fontSize: '1rem', fontWeight: 600, color: '#D4A853' }}>나의 독서 기록</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1.5rem', fontSize: '0.8rem', color: '#8A8070' }}>
|
||||
{['컬렉션', '독서록', '통계'].map(item => (
|
||||
<a key={item} href={`#${item}`} style={{ color: 'inherit', textDecoration: 'none', transition: 'color 0.2s' }}
|
||||
onMouseEnter={e => (e.currentTarget.style.color = '#D4A853')}
|
||||
onMouseLeave={e => (e.currentTarget.style.color = '#8A8070')}>
|
||||
{item}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* ── HERO ── */}
|
||||
<section style={{ minHeight: '100dvh', display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: '0 clamp(1.5rem, 6vw, 6rem)', position: 'relative', overflow: 'hidden' }}>
|
||||
{/* ambient orbs */}
|
||||
<div className="rd-orb" style={{ position: 'absolute', top: '20%', right: '10%', width: '500px', height: '500px', background: 'radial-gradient(circle, rgba(212,168,83,0.06) 0%, transparent 70%)', borderRadius: '50%', pointerEvents: 'none' }}/>
|
||||
<div style={{ position: 'absolute', bottom: '15%', left: '5%', width: '300px', height: '300px', background: 'radial-gradient(circle, rgba(107,91,149,0.06) 0%, transparent 70%)', borderRadius: '50%', pointerEvents: 'none' }}/>
|
||||
|
||||
<div style={{ maxWidth: '1200px', margin: '0 auto', width: '100%', position: 'relative', zIndex: 1 }}>
|
||||
{/* eyebrow */}
|
||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem', border: '1px solid rgba(212,168,83,0.3)', borderRadius: '100px', padding: '0.35rem 0.875rem', marginBottom: '2.5rem' }}>
|
||||
<div style={{ width: '6px', height: '6px', borderRadius: '50%', background: '#D4A853' }}/>
|
||||
<span style={{ fontSize: '0.72rem', color: '#D4A853', letterSpacing: '0.15em', textTransform: 'uppercase', fontFamily: "'Pretendard', sans-serif" }}>Personal Reading Journal</span>
|
||||
</div>
|
||||
|
||||
{/* hero title */}
|
||||
<h1 className="rd-hero-title" style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 'clamp(4.5rem, 13vw, 10rem)', fontWeight: 700, lineHeight: 0.9, letterSpacing: '-0.02em', color: '#F0EAD8', margin: '0 0 2rem', wordBreak: 'keep-all' }}>
|
||||
책이 쌓이면<br/>
|
||||
<span style={{ color: '#D4A853', fontStyle: 'italic' }}>삶이 된다.</span>
|
||||
</h1>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'flex-end', gap: '3rem', flexWrap: 'wrap' }}>
|
||||
<p style={{ fontSize: '1.1rem', color: '#8A8070', maxWidth: '360px', lineHeight: 1.8, wordBreak: 'keep-all', margin: 0 }}>
|
||||
읽은 책마다 남긴 생각들, 좋아하는 문장들, 그리고 아직 읽지 못한 설렘들을 기록하는 공간.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: '1rem' }}>
|
||||
<button className="rd-cta-btn">기록 시작하기</button>
|
||||
<button style={{ background: 'transparent', border: '1px solid rgba(212,168,83,0.3)', color: '#D4A853', fontFamily: "'Pretendard', sans-serif", fontSize: '0.9rem', padding: '0.875rem 2rem', borderRadius: '100px', cursor: 'pointer', transition: 'all 0.3s cubic-bezier(0.16,1,0.3,1)' }}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = 'rgba(212,168,83,0.08)'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = 'transparent'; }}>
|
||||
둘러보기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* scroll indicator */}
|
||||
<div style={{ marginTop: '5rem', display: 'flex', alignItems: 'center', gap: '0.75rem', color: '#4A4035' }}>
|
||||
<div style={{ width: '40px', height: '1px', background: 'rgba(212,168,83,0.3)' }}/>
|
||||
<span style={{ fontSize: '0.72rem', letterSpacing: '0.15em', textTransform: 'uppercase' }}>Scroll to explore</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── STATS BAR ── */}
|
||||
<section style={{ padding: '3rem clamp(1.5rem, 6vw, 6rem)', borderTop: '1px solid rgba(212,168,83,0.1)', borderBottom: '1px solid rgba(212,168,83,0.1)' }}>
|
||||
<div className="rd-stats-grid rd-reveal" style={{ maxWidth: '1200px', margin: '0 auto', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '2rem', position: 'relative', zIndex: 1 }}>
|
||||
<StatItem value={47} label="완독한 책" />
|
||||
<div style={{ width: '1px', background: 'rgba(212,168,83,0.1)', margin: '-0.5rem 0' }}/>
|
||||
<StatItem value={2340} suffix="p" label="올해 읽은 페이지" />
|
||||
<div style={{ width: '1px', background: 'rgba(212,168,83,0.1)', margin: '-0.5rem 0' }}/>
|
||||
<StatItem value={128} label="저장한 문장" />
|
||||
<div style={{ width: '1px', background: 'rgba(212,168,83,0.1)', margin: '-0.5rem 0' }}/>
|
||||
<StatItem value={12} label="독서 중인 달" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── CURRENTLY READING ── */}
|
||||
<section style={{ padding: '5rem clamp(1.5rem, 6vw, 6rem)' }} id="독서록">
|
||||
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
|
||||
<div className="rd-reveal" style={{ display: 'flex', alignItems: 'baseline', gap: '1rem', marginBottom: '3rem' }}>
|
||||
<h2 className="rd-serif" style={{ fontSize: '2.5rem', fontWeight: 700, color: '#F0EAD8', margin: 0 }}>지금 읽는 중</h2>
|
||||
<span style={{ fontSize: '0.75rem', color: '#8A8070', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Currently reading</span>
|
||||
</div>
|
||||
|
||||
<div className="rd-currently rd-reveal" style={{ display: 'grid', gridTemplateColumns: '280px 1fr', gap: '2.5rem', alignItems: 'stretch' }}>
|
||||
{/* book cover */}
|
||||
<div style={{ position: 'relative' }}>
|
||||
<img src={`https://picsum.photos/seed/${currentlyReading.seed}/280/420`} alt={currentlyReading.title}
|
||||
style={{ width: '100%', aspectRatio: '2/3', objectFit: 'cover', borderRadius: '12px', filter: 'brightness(0.85) saturate(0.8)', display: 'block' }}/>
|
||||
<div style={{ position: 'absolute', inset: 0, borderRadius: '12px', boxShadow: 'inset 0 0 0 1px rgba(212,168,83,0.2), -12px 20px 60px rgba(0,0,0,0.6)' }}/>
|
||||
{/* reading badge */}
|
||||
<div style={{ position: 'absolute', top: '1rem', left: '-0.5rem', background: '#D4A853', color: '#0C0B09', fontSize: '0.65rem', fontWeight: 700, padding: '4px 10px', borderRadius: '4px', letterSpacing: '0.05em' }}>READING</div>
|
||||
</div>
|
||||
|
||||
{/* info */}
|
||||
<div className="rd-bezel" style={{ flex: 1 }}>
|
||||
<div className="rd-bezel-inner" style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
<div style={{ marginBottom: 'auto' }}>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', marginBottom: '1rem', flexWrap: 'wrap' }}>
|
||||
<span style={{ fontSize: '0.7rem', color: '#D4A853', border: '1px solid rgba(212,168,83,0.3)', padding: '2px 8px', borderRadius: '4px' }}>{currentlyReading.genre}</span>
|
||||
<span style={{ fontSize: '0.7rem', color: '#8A8070', border: '1px solid rgba(255,255,255,0.08)', padding: '2px 8px', borderRadius: '4px' }}>시작 {currentlyReading.startDate}</span>
|
||||
</div>
|
||||
<h3 className="rd-serif" style={{ fontSize: '2rem', fontWeight: 700, color: '#F0EAD8', margin: '0 0 0.25rem', wordBreak: 'keep-all' }}>{currentlyReading.title}</h3>
|
||||
<p style={{ fontSize: '0.875rem', color: '#8A8070', margin: '0 0 1.5rem' }}>{currentlyReading.author}</p>
|
||||
|
||||
<div style={{ background: 'rgba(212,168,83,0.06)', border: '1px solid rgba(212,168,83,0.15)', borderRadius: '8px', padding: '1rem', marginBottom: '1.5rem' }}>
|
||||
<p style={{ fontSize: '0.875rem', color: '#C4A060', fontStyle: 'italic', wordBreak: 'keep-all', margin: 0, lineHeight: 1.7 }}>"{currentlyReading.note}"</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* progress */}
|
||||
<div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '0.5rem' }}>
|
||||
<span style={{ fontSize: '0.75rem', color: '#8A8070' }}>읽은 진도</span>
|
||||
<span style={{ fontSize: '0.75rem', color: '#D4A853', fontFamily: "'Cormorant Garamond', serif", fontWeight: 600 }}>
|
||||
{currentlyReading.currentPage} / {currentlyReading.totalPages} p — {currentlyReading.progress}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="rd-progress-track">
|
||||
<div className="rd-progress-fill" style={{ width: `${currentlyReading.progress}%` }}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* TBR list */}
|
||||
<div style={{ marginTop: '2.5rem' }}>
|
||||
<h3 className="rd-serif rd-reveal" style={{ fontSize: '1.5rem', fontWeight: 600, color: '#6A5840', marginBottom: '1.25rem', margin: '0 0 1.25rem' }}>다음으로 읽을 책 <span style={{ fontSize: '1rem', fontStyle: 'italic' }}>(TBR)</span></h3>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
||||
{tbr.map((book, i) => (
|
||||
<div key={i} className="rd-reveal" style={{ display: 'flex', alignItems: 'center', gap: '1rem', padding: '1rem 1.25rem', background: 'rgba(255,255,255,0.02)', border: '1px solid rgba(255,255,255,0.05)', borderRadius: '10px', transition: 'background 0.3s cubic-bezier(0.16,1,0.3,1)' }}
|
||||
onMouseEnter={e => (e.currentTarget as HTMLElement).style.background = 'rgba(212,168,83,0.04)'}
|
||||
onMouseLeave={e => (e.currentTarget as HTMLElement).style.background = 'rgba(255,255,255,0.02)'}>
|
||||
<img src={`https://picsum.photos/seed/${book.seed}/40/60`} alt={book.title} style={{ width: '36px', height: '54px', objectFit: 'cover', borderRadius: '4px', filter: 'brightness(0.8)' }}/>
|
||||
<div style={{ flex: 1 }}>
|
||||
<p className="rd-serif" style={{ fontSize: '1rem', fontWeight: 600, color: '#F0EAD8', margin: '0 0 0.2rem' }}>{book.title}</p>
|
||||
<p style={{ fontSize: '0.75rem', color: '#6A5840', margin: 0 }}>{book.author}</p>
|
||||
</div>
|
||||
<span style={{ fontSize: '0.65rem', color: book.priority === '높음' ? '#D4A853' : '#8A8070', border: `1px solid ${book.priority === '높음' ? 'rgba(212,168,83,0.4)' : 'rgba(255,255,255,0.1)'}`, padding: '2px 8px', borderRadius: '4px' }}>{book.priority}</span>
|
||||
<ArrowUpRight/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── BOOK COLLECTION ── */}
|
||||
<section style={{ padding: '2rem clamp(1.5rem, 6vw, 6rem) 5rem' }} id="컬렉션">
|
||||
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
|
||||
<div className="rd-reveal" style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: '2.5rem', flexWrap: 'wrap', gap: '1rem' }}>
|
||||
<h2 className="rd-serif" style={{ fontSize: '2.5rem', fontWeight: 700, color: '#F0EAD8', margin: 0 }}>완독 컬렉션</h2>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
||||
{genres.map(g => (
|
||||
<button key={g} className={`rd-genre-btn ${activeGenre === g ? 'active' : ''}`} onClick={() => setActiveGenre(g)}>{g}</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* bento grid */}
|
||||
<div className="rd-bento" style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr 1fr', gap: '1rem' }}>
|
||||
{filteredBooks.slice(0, 1).map((book, i) => (
|
||||
<div key={book.seed} style={{ gridRow: 'span 2' }}>
|
||||
<BookCard book={book} large />
|
||||
</div>
|
||||
))}
|
||||
{filteredBooks.slice(1, 3).map(book => (
|
||||
<BookCard key={book.seed} book={book} />
|
||||
))}
|
||||
{filteredBooks.slice(3, 5).map(book => (
|
||||
<BookCard key={book.seed} book={book} />
|
||||
))}
|
||||
{filteredBooks.slice(5, 7).map((book, i) => (
|
||||
<div key={book.seed} style={i === 1 ? { gridColumn: 'span 2' } : {}}>
|
||||
<BookCard book={book} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filteredBooks.length === 0 && (
|
||||
<div style={{ textAlign: 'center', padding: '4rem', color: '#4A4035' }}>
|
||||
<p className="rd-serif" style={{ fontSize: '1.5rem' }}>이 장르에 읽은 책이 없습니다.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── QUOTES MASONRY ── */}
|
||||
<section style={{ padding: '5rem clamp(1.5rem, 6vw, 6rem)', background: 'rgba(212,168,83,0.03)', borderTop: '1px solid rgba(212,168,83,0.08)', borderBottom: '1px solid rgba(212,168,83,0.08)' }}>
|
||||
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
|
||||
<div className="rd-reveal" style={{ marginBottom: '3rem' }}>
|
||||
<p style={{ fontSize: '0.72rem', color: '#D4A853', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: '0.75rem' }}>Highlighted Quotes</p>
|
||||
<h2 className="rd-serif" style={{ fontSize: '2.5rem', fontWeight: 700, color: '#F0EAD8', margin: 0 }}>마음에 남은 문장들</h2>
|
||||
</div>
|
||||
|
||||
<div className="rd-quote-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '1.25rem' }}>
|
||||
{quotes.map((quote, i) => (
|
||||
<div key={i} className={`rd-bezel rd-reveal ${i === 1 ? 'rd-reveal' : ''}`}
|
||||
style={{ marginTop: i % 2 === 1 ? '2rem' : '0' }}>
|
||||
<div className="rd-bezel-inner">
|
||||
<QuoteIcon/>
|
||||
<p className="rd-serif" style={{ fontSize: '1.2rem', color: '#C4B090', lineHeight: 1.8, margin: '1rem 0 1.25rem', fontStyle: 'italic', wordBreak: 'keep-all' }}>
|
||||
{quote.text}
|
||||
</p>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<p style={{ fontSize: '0.75rem', color: '#6A5840', margin: 0 }}>{quote.author}</p>
|
||||
<span style={{ fontSize: '0.65rem', color: '#D4A853', border: '1px solid rgba(212,168,83,0.3)', padding: '2px 8px', borderRadius: '4px' }}>{quote.genre}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── MONTHLY STATS ── */}
|
||||
<section style={{ padding: '5rem clamp(1.5rem, 6vw, 6rem)' }} id="통계">
|
||||
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
|
||||
<div className="rd-reveal" style={{ marginBottom: '3rem' }}>
|
||||
<p style={{ fontSize: '0.72rem', color: '#D4A853', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: '0.75rem' }}>Reading Rhythm</p>
|
||||
<h2 className="rd-serif" style={{ fontSize: '2.5rem', fontWeight: 700, color: '#F0EAD8', margin: 0 }}>월별 독서 페이스</h2>
|
||||
</div>
|
||||
|
||||
<div className="rd-bezel rd-reveal">
|
||||
<div className="rd-bezel-inner">
|
||||
<div style={{ display: 'flex', alignItems: 'flex-end', gap: '1rem', height: '160px' }}>
|
||||
{monthlyData.map((d, i) => (
|
||||
<div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.5rem', height: '100%', justifyContent: 'flex-end' }}>
|
||||
<span style={{ fontSize: '0.75rem', color: '#D4A853', fontFamily: "'Cormorant Garamond', serif", fontWeight: 600 }}>{d.books}</span>
|
||||
<div style={{ width: '100%', background: 'rgba(212,168,83,0.12)', borderRadius: '6px 6px 0 0', height: `${(d.books / maxBooks) * 100}%`, position: 'relative', transition: 'height 0.8s cubic-bezier(0.16,1,0.3,1)', minHeight: '8px' }}>
|
||||
<div style={{ position: 'absolute', inset: 0, borderRadius: '6px 6px 0 0', background: `linear-gradient(to top, rgba(212,168,83,0.6), rgba(212,168,83,0.2))` }}/>
|
||||
</div>
|
||||
<span style={{ fontSize: '0.72rem', color: '#6A5840' }}>{d.month}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '2rem', paddingTop: '1.5rem', borderTop: '1px solid rgba(255,255,255,0.05)', display: 'flex', gap: '2.5rem', flexWrap: 'wrap' }}>
|
||||
{[
|
||||
{ label: '총 완독 권수', value: '15권', sub: '최근 6개월' },
|
||||
{ label: '평균', value: '2.5권', sub: '월 평균' },
|
||||
{ label: '최다 독서 달', value: '1월', sub: '4권 완독' },
|
||||
].map((item, i) => (
|
||||
<div key={i}>
|
||||
<p className="rd-serif" style={{ fontSize: '1.75rem', fontWeight: 700, color: '#D4A853', margin: '0 0 0.2rem' }}>{item.value}</p>
|
||||
<p style={{ fontSize: '0.75rem', color: '#8A8070', margin: 0 }}>{item.label} · <span style={{ color: '#6A5840' }}>{item.sub}</span></p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── BRAND MARQUEE ── */}
|
||||
<div style={{ borderTop: '1px solid rgba(212,168,83,0.08)', borderBottom: '1px solid rgba(212,168,83,0.08)', overflow: 'hidden', padding: '1rem 0' }}>
|
||||
<div className="rd-marquee-track">
|
||||
{[...Array(2)].map((_, i) => (
|
||||
<div key={i} style={{ display: 'flex', gap: '0', whiteSpace: 'nowrap' }}>
|
||||
{['파친코', '어린 왕자', '원씽', '채식주의자', '노인과 바다', '사피엔스', '총·균·쇠', '코스모스', '82년생 김지영'].map((title, j) => (
|
||||
<span key={j} style={{ padding: '0 2rem', fontSize: '0.8rem', color: '#4A4035', letterSpacing: '0.1em', textTransform: 'uppercase' }}>
|
||||
{title} <span style={{ color: 'rgba(212,168,83,0.3)', margin: '0 1rem' }}>·</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── CTA ── */}
|
||||
<section style={{ padding: '6rem clamp(1.5rem, 6vw, 6rem)', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: '600px', height: '600px', background: 'radial-gradient(circle, rgba(212,168,83,0.08) 0%, transparent 70%)', pointerEvents: 'none' }}/>
|
||||
|
||||
<div className="rd-reveal" style={{ maxWidth: '700px', margin: '0 auto', textAlign: 'center', position: 'relative', zIndex: 1 }}>
|
||||
<p style={{ fontSize: '0.72rem', color: '#D4A853', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: '1.5rem' }}>당신의 독서 기록</p>
|
||||
<h2 className="rd-serif" style={{ fontSize: 'clamp(2.5rem, 6vw, 4rem)', fontWeight: 700, color: '#F0EAD8', margin: '0 0 1.5rem', lineHeight: 1.1, wordBreak: 'keep-all' }}>
|
||||
오늘 읽은 한 페이지가<br/>
|
||||
<span style={{ color: '#D4A853', fontStyle: 'italic' }}>내일의 나를 만든다.</span>
|
||||
</h2>
|
||||
<p style={{ fontSize: '1rem', color: '#6A5840', maxWidth: '400px', margin: '0 auto 2.5rem', lineHeight: 1.8, wordBreak: 'keep-all' }}>
|
||||
읽은 책을 기록하고, 좋아하는 문장을 저장하고, 나만의 독서 여정을 쌓아가세요.
|
||||
</p>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', gap: '1rem', flexWrap: 'wrap' }}>
|
||||
<button className="rd-cta-btn">무료로 시작하기</button>
|
||||
<button style={{ background: 'transparent', border: '1px solid rgba(212,168,83,0.3)', color: '#D4A853', fontFamily: "'Pretendard', sans-serif", fontSize: '0.9rem', padding: '0.875rem 2rem', borderRadius: '100px', cursor: 'pointer', transition: 'all 0.3s cubic-bezier(0.16,1,0.3,1)' }}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = 'rgba(212,168,83,0.08)'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = 'transparent'; }}>
|
||||
샘플 둘러보기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── FOOTER ── */}
|
||||
<footer style={{ borderTop: '1px solid rgba(212,168,83,0.08)', padding: '2rem clamp(1.5rem, 6vw, 6rem)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: '1rem' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
<BookOpenIcon/>
|
||||
<span className="rd-serif" style={{ color: '#D4A853', fontWeight: 600 }}>나의 독서 기록</span>
|
||||
</div>
|
||||
<p style={{ fontSize: '0.75rem', color: '#4A4035', margin: 0 }}>책은 시간을 초월한 대화다.</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
{/* ── SCROLL TO TOP ── */}
|
||||
<button
|
||||
onClick={() => {
|
||||
const scroller = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement;
|
||||
scroller.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
style={{
|
||||
position: 'fixed', bottom: '5.5rem', right: '2rem', zIndex: 400,
|
||||
width: 48, height: 48, borderRadius: '50%',
|
||||
background: '#D4A853', border: 'none', cursor: 'pointer',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
boxShadow: '0 8px 32px rgba(212,168,83,0.4)',
|
||||
opacity: showTop ? 1 : 0,
|
||||
transform: showTop ? 'translateY(0) scale(1)' : 'translateY(12px) scale(0.9)',
|
||||
transition: 'opacity 0.35s cubic-bezier(0.16,1,0.3,1), transform 0.35s cubic-bezier(0.16,1,0.3,1)',
|
||||
pointerEvents: showTop ? 'auto' : 'none',
|
||||
}}
|
||||
aria-label="맨 위로"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#0C0B09" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="18 15 12 9 6 15"/>
|
||||
</svg>
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
770
app/work/website/samples/shopping/page.tsx
Normal file
770
app/work/website/samples/shopping/page.tsx
Normal file
@@ -0,0 +1,770 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
DESIGN TOKEN — 인테리어 페이지와 완전히 다른 팔레트
|
||||
No gold, no cream, no warm amber.
|
||||
Editorial B&W + cool stone.
|
||||
═══════════════════════════════════════════════════ */
|
||||
const T = {
|
||||
ink: '#0C0B09',
|
||||
paper: '#F4F2EF',
|
||||
sand: '#E9E5DF',
|
||||
stone: '#7C7870',
|
||||
chalk: '#B8B3AB',
|
||||
white: '#FAFAF8',
|
||||
} as const;
|
||||
|
||||
const BANNER_H = 40;
|
||||
const NAV_H = 56;
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
FASHION IMAGES — picsum.photos (안정적, 항상 로드됨)
|
||||
seed 값은 항상 동일한 이미지를 반환하므로 일관성 유지
|
||||
═══════════════════════════════════════════════════ */
|
||||
const IMG = {
|
||||
/* 히어로 — 세로형 풀스크린 */
|
||||
hero: 'https://picsum.photos/seed/mellow-hero/900/1200',
|
||||
/* 상품 — 세로 portrait (3:4) */
|
||||
p1: 'https://picsum.photos/seed/mellow-p1/480/640',
|
||||
p2: 'https://picsum.photos/seed/mellow-p2/480/640',
|
||||
p3: 'https://picsum.photos/seed/mellow-p3/480/640',
|
||||
p4: 'https://picsum.photos/seed/mellow-p4/480/640',
|
||||
p5: 'https://picsum.photos/seed/mellow-p5/480/640',
|
||||
p6: 'https://picsum.photos/seed/mellow-p6/480/640',
|
||||
p7: 'https://picsum.photos/seed/mellow-p7/480/640',
|
||||
p8: 'https://picsum.photos/seed/mellow-p8/480/640',
|
||||
/* 에디토리얼 배너 — 와이드 */
|
||||
edit1: 'https://picsum.photos/seed/mellow-edit/1400/700',
|
||||
/* 브랜드 스토리 이미지 */
|
||||
feat1: 'https://picsum.photos/seed/mellow-feat1/600/800',
|
||||
feat2: 'https://picsum.photos/seed/mellow-feat2/600/800',
|
||||
/* 룩북 — portrait strip */
|
||||
lb1: 'https://picsum.photos/seed/mellow-lb1/400/533',
|
||||
lb2: 'https://picsum.photos/seed/mellow-lb2/400/533',
|
||||
lb3: 'https://picsum.photos/seed/mellow-lb3/400/533',
|
||||
lb4: 'https://picsum.photos/seed/mellow-lb4/400/533',
|
||||
lb5: 'https://picsum.photos/seed/mellow-lb5/400/533',
|
||||
} as const;
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
DATA
|
||||
═══════════════════════════════════════════════════ */
|
||||
type Cat = '전체' | '아우터' | '상의' | '하의' | '액세서리';
|
||||
|
||||
const products = [
|
||||
{ id: 1, name: 'Structured Blazer', kr: '스트럭처드 블레이저', price: 328000, cat: '아우터' as Cat, img: IMG.p1, isNew: true },
|
||||
{ id: 2, name: 'Cocoon Coat', kr: '코쿤 코트', price: 498000, cat: '아우터' as Cat, img: IMG.p2, isBest: true },
|
||||
{ id: 3, name: 'Slip Midi Dress', kr: '슬립 미디 드레스', price: 218000, cat: '상의' as Cat, img: IMG.p3, isNew: true },
|
||||
{ id: 4, name: 'Relaxed Oxford', kr: '릴랙스드 옥스포드', price: 145000, cat: '상의' as Cat, img: IMG.p4 },
|
||||
{ id: 5, name: 'Wide Trousers', kr: '와이드 트라우저', price: 185000, cat: '하의' as Cat, img: IMG.p5 },
|
||||
{ id: 6, name: 'Barrel Denim', kr: '배럴 데님', price: 198000, cat: '하의' as Cat, img: IMG.p6, isBest: true },
|
||||
{ id: 7, name: 'Leather Mini Bag', kr: '레더 미니백', price: 278000, cat: '액세서리' as Cat, img: IMG.p7, isNew: true },
|
||||
{ id: 8, name: 'Square Sunglasses', kr: '스퀘어 선글라스', price: 128000, cat: '액세서리' as Cat, img: IMG.p8 },
|
||||
];
|
||||
|
||||
const reviews = [
|
||||
{ quote: '입고 나서 동료가 어디서 샀냐고 계속 물어봐요. 핏이 정말 완벽합니다.', name: '하윤서', city: '서울', item: 'Structured Blazer' },
|
||||
{ quote: '코쿤 코트, 사진보다 실물이 훨씬 좋아요. 소재감이 기대 이상입니다.', name: '이서진', city: '제주', item: 'Cocoon Coat' },
|
||||
{ quote: '슬립 드레스를 받았는데 바느질 하나하나에서 정성이 느껴져요.', name: '박도현', city: '부산', item: 'Slip Midi Dress' },
|
||||
];
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
MAIN PAGE
|
||||
═══════════════════════════════════════════════════ */
|
||||
export default function ShoppingPage() {
|
||||
const [activeCat, setActiveCat] = useState<Cat | '전체'>('전체');
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [showTop, setShowTop] = useState(false);
|
||||
const [cart, setCart] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const sc: HTMLElement =
|
||||
(document.querySelector('.main-content') as HTMLElement | null) ??
|
||||
document.documentElement;
|
||||
|
||||
const onScroll = () => {
|
||||
setScrolled(sc.scrollTop > 40);
|
||||
setShowTop(sc.scrollTop > 500);
|
||||
};
|
||||
sc.addEventListener('scroll', onScroll, { passive: true });
|
||||
|
||||
/* reveal */
|
||||
const obs = new IntersectionObserver(
|
||||
(entries) => entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('ml-in'); }),
|
||||
{ threshold: 0.07, root: sc === document.documentElement ? null : sc }
|
||||
);
|
||||
document.querySelectorAll('.ml-reveal').forEach(el => obs.observe(el));
|
||||
|
||||
return () => {
|
||||
sc.removeEventListener('scroll', onScroll);
|
||||
obs.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const cats: (Cat | '전체')[] = ['전체', '아우터', '상의', '하의', '액세서리'];
|
||||
const filtered = activeCat === '전체' ? products : products.filter(p => p.cat === activeCat);
|
||||
|
||||
const scrollTop = () => {
|
||||
const sc = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement;
|
||||
sc.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ml-page" style={{ fontFamily: "'Pretendard','Apple SD Gothic Neo',system-ui,sans-serif", background: T.paper, color: T.ink, overflowX: 'hidden' }}>
|
||||
|
||||
{/* ── FONTS + GLOBAL ── */}
|
||||
<style dangerouslySetInnerHTML={{ __html: `
|
||||
@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&display=swap');
|
||||
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.min.css');
|
||||
|
||||
/* 전역 리셋을 .ml-page 하위로 한정 — 사이드바 오염 방지 */
|
||||
.ml-page, .ml-page *, .ml-page *::before, .ml-page *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
/* scroll reveal */
|
||||
.ml-reveal {
|
||||
opacity: 0; transform: translateY(28px);
|
||||
transition: opacity .75s cubic-bezier(.16,1,.3,1), transform .75s cubic-bezier(.16,1,.3,1);
|
||||
}
|
||||
.ml-reveal.ml-in { opacity: 1; transform: none; }
|
||||
.ml-reveal > *:nth-child(2) { transition-delay: 90ms; }
|
||||
.ml-reveal > *:nth-child(3) { transition-delay: 180ms; }
|
||||
.ml-reveal > *:nth-child(4) { transition-delay: 270ms; }
|
||||
.ml-reveal > *:nth-child(5) { transition-delay: 360ms; }
|
||||
.ml-reveal > *:nth-child(6) { transition-delay: 450ms; }
|
||||
.ml-reveal > *:nth-child(7) { transition-delay: 540ms; }
|
||||
.ml-reveal > *:nth-child(8) { transition-delay: 630ms; }
|
||||
|
||||
@keyframes ml-fadeUp {
|
||||
from { opacity: 0; transform: translateY(2rem); }
|
||||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
@keyframes ml-ticker {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(-50%); }
|
||||
}
|
||||
@keyframes ml-float {
|
||||
0%,100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-8px); }
|
||||
}
|
||||
|
||||
/* product card hover */
|
||||
.ml-card-img {
|
||||
transition: transform .6s cubic-bezier(.16,1,.3,1), filter .4s;
|
||||
}
|
||||
.ml-card:hover .ml-card-img { transform: scale(1.04); filter: brightness(0.92); }
|
||||
|
||||
/* lookbook scroll */
|
||||
.ml-lb { display: flex; gap: 16px; overflow-x: auto; padding-bottom: 8px; scrollbar-width: none; }
|
||||
.ml-lb::-webkit-scrollbar { display: none; }
|
||||
|
||||
/* nav underline */
|
||||
.ml-nav-a {
|
||||
position: relative; font-size: 12px; font-weight: 500;
|
||||
letter-spacing: .06em; text-decoration: none; color: inherit;
|
||||
transition: opacity .25s;
|
||||
}
|
||||
.ml-nav-a::after {
|
||||
content: ''; position: absolute; bottom: -2px; left: 0; right: 0;
|
||||
height: 1px; background: currentColor; transform: scaleX(0);
|
||||
transform-origin: left; transition: transform .35s cubic-bezier(.16,1,.3,1);
|
||||
}
|
||||
.ml-nav-a:hover::after { transform: scaleX(1); }
|
||||
|
||||
/* cat pill */
|
||||
.ml-pill {
|
||||
border: 1px solid rgba(12,11,9,.18); border-radius: 0;
|
||||
background: transparent; font-family: inherit; font-size: 11px;
|
||||
font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
|
||||
padding: 8px 20px; cursor: pointer; color: ${T.stone};
|
||||
transition: all .3s cubic-bezier(.16,1,.3,1);
|
||||
}
|
||||
.ml-pill:hover { border-color: ${T.ink}; color: ${T.ink}; }
|
||||
.ml-pill.on { background: ${T.ink}; color: ${T.white}; border-color: ${T.ink}; }
|
||||
|
||||
/* ticker */
|
||||
.ml-ticker-track { animation: ml-ticker 32s linear infinite; display: flex; white-space: nowrap; }
|
||||
|
||||
/* scrollbar */
|
||||
::-webkit-scrollbar { width: 3px; }
|
||||
::-webkit-scrollbar-track { background: ${T.paper}; }
|
||||
::-webkit-scrollbar-thumb { background: ${T.chalk}; border-radius: 2px; }
|
||||
|
||||
@media (max-width:1024px) {
|
||||
.ml-hero-grid { grid-template-columns: 1fr !important; }
|
||||
.ml-hero-img { display: none !important; }
|
||||
.ml-feat-grid { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
@media (max-width:768px) {
|
||||
.ml-prod-grid { grid-template-columns: 1fr 1fr !important; }
|
||||
}
|
||||
@media (max-width:480px) {
|
||||
.ml-prod-grid { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
`}} />
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
BACK BANNER
|
||||
════════════════════════════════════════════ */}
|
||||
<div style={{
|
||||
background: T.ink, height: BANNER_H,
|
||||
display: 'flex', alignItems: 'center', padding: '0 32px', gap: 14,
|
||||
}}>
|
||||
<Link href="/work/website"
|
||||
style={{ fontSize: 12, color: T.chalk, textDecoration: 'none', letterSpacing: '.04em', transition: 'color .2s' }}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.color = T.white)}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = T.chalk)}>
|
||||
← 홈페이지 제작 서비스로 돌아가기
|
||||
</Link>
|
||||
<span style={{ color: T.stone, fontSize: 11 }}>|</span>
|
||||
<span style={{ fontSize: 11, color: T.stone, fontStyle: 'italic', fontFamily: "'DM Serif Display', serif" }}>
|
||||
SAMPLE · 개인 쇼핑몰
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
NAV — minimal hairline
|
||||
════════════════════════════════════════════ */}
|
||||
<nav style={{
|
||||
position: 'sticky', top: 0, zIndex: 50, height: NAV_H,
|
||||
display: 'flex', alignItems: 'center', padding: '0 48px',
|
||||
background: scrolled ? `rgba(244,242,239,.96)` : T.ink,
|
||||
backdropFilter: 'blur(16px)',
|
||||
borderBottom: `1px solid ${scrolled ? 'rgba(12,11,9,.1)' : 'rgba(255,255,255,.06)'}`,
|
||||
transition: 'background .4s cubic-bezier(.16,1,.3,1), border-color .4s',
|
||||
}}>
|
||||
<div style={{ maxWidth: 1280, margin: '0 auto', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
|
||||
{/* wordmark */}
|
||||
<div style={{ fontFamily: "'DM Serif Display', serif", fontSize: 18, fontWeight: 400, letterSpacing: '.12em', color: scrolled ? T.ink : T.white, transition: 'color .4s' }}>
|
||||
MELLOW
|
||||
</div>
|
||||
|
||||
{/* links */}
|
||||
<div style={{ display: 'flex', gap: 36 }}>
|
||||
{['신상품', '아우터', '상의 / 하의', '액세서리', '세일'].map(l => (
|
||||
<a key={l} href="#collection" className="ml-nav-a"
|
||||
style={{ color: scrolled ? T.stone : 'rgba(244,242,239,.6)' }}>{l}</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* actions */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 24 }}>
|
||||
{/* search */}
|
||||
<button style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 4,
|
||||
color: scrolled ? T.stone : 'rgba(244,242,239,.6)', transition: 'color .3s' }}
|
||||
aria-label="검색">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="15.65" y2="15.65"/>
|
||||
</svg>
|
||||
</button>
|
||||
{/* cart */}
|
||||
<button onClick={() => setCart(c => c + 1)}
|
||||
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 4, position: 'relative',
|
||||
color: scrolled ? T.ink : T.white, transition: 'color .3s' }}
|
||||
aria-label="장바구니">
|
||||
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"/>
|
||||
<line x1="3" y1="6" x2="21" y2="6"/>
|
||||
<path d="M16 10a4 4 0 01-8 0"/>
|
||||
</svg>
|
||||
{cart > 0 && (
|
||||
<span style={{
|
||||
position: 'absolute', top: -5, right: -5,
|
||||
width: 16, height: 16, borderRadius: '50%',
|
||||
background: scrolled ? T.ink : T.white,
|
||||
color: scrolled ? T.white : T.ink,
|
||||
fontSize: 9, fontWeight: 800,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
}}>{cart}</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
HERO — editorial dark split
|
||||
════════════════════════════════════════════ */}
|
||||
<section style={{
|
||||
background: T.ink,
|
||||
minHeight: `calc(100dvh - ${BANNER_H}px - ${NAV_H}px)`,
|
||||
display: 'grid', gridTemplateColumns: '1fr 1fr',
|
||||
overflow: 'hidden',
|
||||
}} className="ml-hero-grid">
|
||||
|
||||
{/* Left */}
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
|
||||
padding: '80px 64px 72px 80px',
|
||||
animation: 'ml-fadeUp .9s cubic-bezier(.16,1,.3,1) both',
|
||||
}}>
|
||||
<p style={{ fontSize: 10, color: T.stone, letterSpacing: '.22em', textTransform: 'uppercase', marginBottom: 40 }}>
|
||||
2025 — Spring / Summer Collection
|
||||
</p>
|
||||
<h1 style={{
|
||||
fontFamily: "'DM Serif Display', serif", fontStyle: 'italic',
|
||||
fontSize: 'clamp(3.5rem, 6vw, 6.5rem)',
|
||||
lineHeight: 1.0, color: T.white, marginBottom: 40,
|
||||
letterSpacing: '-.02em', wordBreak: 'keep-all',
|
||||
}}>
|
||||
Clothes<br/>that last<br/>
|
||||
<span style={{ color: T.sand, fontStyle: 'normal' }}>longer than</span><br/>
|
||||
trends.
|
||||
</h1>
|
||||
<p style={{ fontSize: 14, color: T.stone, lineHeight: 1.9, marginBottom: 44, maxWidth: 360, wordBreak: 'keep-all' }}>
|
||||
자연 소재, 정직한 제작, 긴 수명.<br/>
|
||||
유행 대신 오래가는 옷을 만듭니다.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 14 }}>
|
||||
<a href="#collection" style={{
|
||||
display: 'inline-block', padding: '14px 32px',
|
||||
background: T.white, color: T.ink,
|
||||
fontSize: 12, fontWeight: 700, letterSpacing: '.1em', textTransform: 'uppercase',
|
||||
textDecoration: 'none',
|
||||
transition: 'opacity .25s',
|
||||
}}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.opacity = '.82')}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.opacity = '1')}>
|
||||
컬렉션 보기
|
||||
</a>
|
||||
<a href="#story" style={{
|
||||
display: 'inline-block', padding: '14px 28px',
|
||||
border: '1px solid rgba(244,242,239,.18)', color: 'rgba(244,242,239,.6)',
|
||||
fontSize: 12, fontWeight: 500, letterSpacing: '.08em', textTransform: 'uppercase',
|
||||
textDecoration: 'none', transition: 'border-color .3s, color .3s',
|
||||
}}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.borderColor = 'rgba(244,242,239,.4)'; (e.currentTarget as HTMLElement).style.color = T.white; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.borderColor = 'rgba(244,242,239,.18)'; (e.currentTarget as HTMLElement).style.color = 'rgba(244,242,239,.6)'; }}>
|
||||
브랜드 스토리
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* inline stats */}
|
||||
<div style={{ display: 'flex', gap: 48, marginTop: 64, paddingTop: 40, borderTop: '1px solid rgba(244,242,239,.08)' }}>
|
||||
{[['1,247+','누적 고객'],['4.87','평점'],['98%','재구매율']].map(([n,l]) => (
|
||||
<div key={l}>
|
||||
<div style={{ fontFamily: "'DM Serif Display', serif", fontSize: 22, color: T.white, letterSpacing: '-.01em' }}>{n}</div>
|
||||
<div style={{ fontSize: 10, color: T.stone, marginTop: 4, letterSpacing: '.08em' }}>{l}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right — image */}
|
||||
<div className="ml-hero-img" style={{ position: 'relative', overflow: 'hidden' }}>
|
||||
<img src={IMG.hero} alt="SS25 Collection"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center top', filter: 'brightness(.75)' }}
|
||||
loading="eager"
|
||||
/>
|
||||
{/* Caption */}
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 32, left: 32, right: 32,
|
||||
display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end',
|
||||
}}>
|
||||
<div style={{ fontFamily: "'DM Serif Display', serif", fontSize: 11, color: 'rgba(244,242,239,.5)', letterSpacing: '.18em', textTransform: 'uppercase' }}>
|
||||
Look 01 / Structured Blazer
|
||||
</div>
|
||||
<div style={{ fontFamily: "'DM Serif Display', serif", fontSize: 13, color: T.white }}>
|
||||
₩328,000
|
||||
</div>
|
||||
</div>
|
||||
{/* Floating badge */}
|
||||
<div style={{
|
||||
position: 'absolute', top: 32, right: 32,
|
||||
padding: '10px 14px',
|
||||
background: 'rgba(12,11,9,.7)', backdropFilter: 'blur(12px)',
|
||||
border: '1px solid rgba(244,242,239,.1)',
|
||||
animation: 'ml-float 5s ease-in-out infinite',
|
||||
}}>
|
||||
<div style={{ fontSize: 9, color: T.stone, letterSpacing: '.18em', textTransform: 'uppercase', marginBottom: 4 }}>신규 입고</div>
|
||||
<div style={{ fontSize: 12, color: T.white, fontWeight: 600 }}>2025 S/S</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
TICKER
|
||||
════════════════════════════════════════════ */}
|
||||
<div style={{ background: T.sand, borderTop: '1px solid rgba(12,11,9,.08)', borderBottom: '1px solid rgba(12,11,9,.08)', overflow: 'hidden', padding: '11px 0' }}>
|
||||
<div className="ml-ticker-track">
|
||||
{[...Array(2)].map((_, i) => (
|
||||
<span key={i} style={{ flexShrink: 0 }}>
|
||||
{['Free Shipping Over 50,000', '무료 배송', '7일 무료 반품', '국내 장인 제작', '천연 소재만 사용', 'No Trend — Just Quality'].map((t, j) => (
|
||||
<span key={j} style={{ padding: '0 2rem', fontSize: 11, color: T.stone, letterSpacing: '.12em', textTransform: 'uppercase' }}>
|
||||
{t}<span style={{ color: T.chalk, margin: '0 .5rem' }}> · </span>
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
COLLECTION GRID
|
||||
════════════════════════════════════════════ */}
|
||||
<section id="collection" style={{ padding: '80px 48px', maxWidth: 1360, margin: '0 auto' }}>
|
||||
|
||||
{/* Header row */}
|
||||
<div className="ml-reveal" style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 40, flexWrap: 'wrap', gap: 20 }}>
|
||||
<div>
|
||||
<h2 style={{ fontFamily: "'DM Serif Display', serif", fontSize: 'clamp(1.8rem,3vw,2.8rem)', fontWeight: 400, color: T.ink, letterSpacing: '-.01em' }}>
|
||||
{activeCat === '전체' ? 'All Collection' : activeCat}
|
||||
</h2>
|
||||
<p style={{ fontSize: 12, color: T.stone, marginTop: 4, letterSpacing: '.04em' }}>
|
||||
{filtered.length}개 제품
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
{cats.map(c => (
|
||||
<button key={c} className={`ml-pill${activeCat === c ? ' on' : ''}`}
|
||||
onClick={() => setActiveCat(c)}>{c}</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Grid — asymmetric sizing */}
|
||||
<div className="ml-prod-grid ml-reveal" style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(4, 1fr)',
|
||||
gap: '4px',
|
||||
}}>
|
||||
{filtered.map((p, i) => (
|
||||
<div key={p.id} className="ml-card" style={{
|
||||
cursor: 'pointer',
|
||||
gridRow: (i === 0 || i === 5) ? 'span 2' : 'span 1',
|
||||
}}>
|
||||
{/* Image */}
|
||||
<div style={{
|
||||
overflow: 'hidden',
|
||||
aspectRatio: (i === 0 || i === 5) ? '3/5' : '3/4',
|
||||
background: T.sand, position: 'relative',
|
||||
}}>
|
||||
<img src={p.img} alt={p.kr} className="ml-card-img"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
loading="lazy"
|
||||
/>
|
||||
{/* Badges */}
|
||||
{(p.isNew || p.isBest) && (
|
||||
<div style={{
|
||||
position: 'absolute', top: 14, left: 14,
|
||||
background: p.isBest ? T.ink : 'transparent',
|
||||
border: `1px solid ${p.isBest ? T.ink : 'rgba(12,11,9,.35)'}`,
|
||||
color: p.isBest ? T.white : T.ink,
|
||||
fontSize: 9, fontWeight: 700, letterSpacing: '.12em', textTransform: 'uppercase',
|
||||
padding: '4px 9px',
|
||||
}}>
|
||||
{p.isBest ? 'BEST' : 'NEW'}
|
||||
</div>
|
||||
)}
|
||||
{/* Quick add */}
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 0, left: 0, right: 0,
|
||||
background: 'rgba(12,11,9,.82)', backdropFilter: 'blur(4px)',
|
||||
padding: '13px 0', textAlign: 'center',
|
||||
opacity: 0, transition: 'opacity .3s',
|
||||
}} className="ml-quick">
|
||||
<button
|
||||
onClick={() => setCart(c => c + 1)}
|
||||
style={{
|
||||
background: 'none', border: 'none', cursor: 'pointer',
|
||||
color: T.white, fontSize: 11, fontWeight: 600, letterSpacing: '.1em', textTransform: 'uppercase',
|
||||
fontFamily: 'inherit',
|
||||
}}>
|
||||
장바구니 추가 +
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
<div style={{ padding: '14px 2px 24px' }}>
|
||||
<p style={{ fontSize: 10, color: T.stone, letterSpacing: '.1em', textTransform: 'uppercase', marginBottom: 5 }}>{p.cat}</p>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }}>
|
||||
<div>
|
||||
<p style={{ fontSize: 14, fontWeight: 500, color: T.ink, marginBottom: 2, wordBreak: 'keep-all', letterSpacing: '-.01em' }}>{p.name}</p>
|
||||
<p style={{ fontSize: 11, color: T.chalk, wordBreak: 'keep-all' }}>{p.kr}</p>
|
||||
</div>
|
||||
<p style={{ fontSize: 14, fontWeight: 600, color: T.ink, flexShrink: 0, letterSpacing: '-.01em' }}>
|
||||
{p.price.toLocaleString()}<span style={{ fontSize: 11, fontWeight: 400 }}>원</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="ml-reveal" style={{ textAlign: 'center', marginTop: 56 }}>
|
||||
<button style={{
|
||||
border: '1px solid rgba(12,11,9,.2)', background: 'transparent',
|
||||
color: T.ink, fontFamily: 'inherit', fontSize: 11, fontWeight: 600,
|
||||
letterSpacing: '.12em', textTransform: 'uppercase', padding: '14px 48px',
|
||||
cursor: 'pointer', transition: 'background .3s, color .3s',
|
||||
}}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = T.ink; (e.currentTarget as HTMLElement).style.color = T.white; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = 'transparent'; (e.currentTarget as HTMLElement).style.color = T.ink; }}>
|
||||
더 보기 (24)
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
EDITORIAL BANNER — full-bleed dark
|
||||
════════════════════════════════════════════ */}
|
||||
<section style={{ position: 'relative', height: 560, overflow: 'hidden', margin: '0 0 0 0' }}>
|
||||
<img src={IMG.edit1} alt="Editorial"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 30%', filter: 'brightness(.45)' }}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="ml-reveal" style={{
|
||||
position: 'absolute', inset: 0, display: 'flex',
|
||||
flexDirection: 'column', justifyContent: 'center', alignItems: 'center',
|
||||
padding: '40px',
|
||||
}}>
|
||||
<p style={{ fontSize: 10, color: T.chalk, letterSpacing: '.22em', textTransform: 'uppercase', marginBottom: 24 }}>
|
||||
Look Book — 2025 S/S
|
||||
</p>
|
||||
<h2 style={{
|
||||
fontFamily: "'DM Serif Display', serif", fontStyle: 'italic',
|
||||
fontSize: 'clamp(2.5rem,5vw,5rem)', color: T.white,
|
||||
textAlign: 'center', lineHeight: 1.1, wordBreak: 'keep-all', maxWidth: 800,
|
||||
}}>
|
||||
Dressed for<br/>the life you live.
|
||||
</h2>
|
||||
<a href="#collection" style={{
|
||||
marginTop: 40, display: 'inline-block', padding: '13px 36px',
|
||||
border: '1px solid rgba(244,242,239,.4)', color: T.white,
|
||||
fontSize: 11, letterSpacing: '.14em', textTransform: 'uppercase', textDecoration: 'none',
|
||||
transition: 'background .3s, border-color .3s',
|
||||
}}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = 'rgba(244,242,239,.12)'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = 'transparent'; }}>
|
||||
룩북 보기
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
BRAND STORY — 2-col asymmetric
|
||||
════════════════════════════════════════════ */}
|
||||
<section id="story" style={{ padding: '100px 80px', maxWidth: 1280, margin: '0 auto' }}>
|
||||
<div className="ml-feat-grid ml-reveal" style={{ display: 'grid', gridTemplateColumns: '5fr 4fr', gap: '80px', alignItems: 'center' }}>
|
||||
|
||||
{/* Images side-by-side */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }}>
|
||||
<div style={{ overflow: 'hidden', aspectRatio: '3/4', background: T.sand }}>
|
||||
<img src={IMG.feat1} alt="Brand story 1"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover', filter: 'saturate(.7) brightness(.95)', transition: 'transform .7s cubic-bezier(.16,1,.3,1)' }}
|
||||
loading="lazy"
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLImageElement).style.transform = 'scale(1.03)')}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLImageElement).style.transform = 'scale(1)')}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ overflow: 'hidden', aspectRatio: '3/4', background: T.sand, marginTop: 40 }}>
|
||||
<img src={IMG.feat2} alt="Brand story 2"
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover', filter: 'saturate(.7) brightness(.95)', transition: 'transform .7s cubic-bezier(.16,1,.3,1)' }}
|
||||
loading="lazy"
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLImageElement).style.transform = 'scale(1.03)')}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLImageElement).style.transform = 'scale(1)')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Text */}
|
||||
<div style={{ paddingLeft: 16 }}>
|
||||
<p style={{ fontSize: 10, color: T.stone, letterSpacing: '.22em', textTransform: 'uppercase', marginBottom: 28 }}>
|
||||
About MELLOW
|
||||
</p>
|
||||
<h2 style={{ fontFamily: "'DM Serif Display', serif", fontSize: 'clamp(2rem,3.5vw,3.2rem)', fontWeight: 400, lineHeight: 1.15, color: T.ink, marginBottom: 28, wordBreak: 'keep-all', letterSpacing: '-.01em' }}>
|
||||
옷을 만드는<br/>철학이 있습니다.
|
||||
</h2>
|
||||
<p style={{ fontSize: 14, color: T.stone, lineHeight: 1.95, marginBottom: 18, wordBreak: 'keep-all' }}>
|
||||
멜로우는 2019년, 옷 한 벌의 수명을 늘리고 싶다는 생각에서 시작했습니다.
|
||||
계절이 바뀌어도 입을 수 있고, 5년 후에도 유행에서 벗어나지 않는 옷을 만드는 것이 목표입니다.
|
||||
</p>
|
||||
<p style={{ fontSize: 14, color: T.stone, lineHeight: 1.95, marginBottom: 40, wordBreak: 'keep-all' }}>
|
||||
모든 제품은 국내 협력 공방에서 소량 생산합니다. 린넨·코튼·울 등 천연 소재만 사용하며,
|
||||
염색에도 저자극 공정만 택합니다.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 40 }}>
|
||||
{[['6년', '브랜드 히스토리'],['100%','천연 소재'],['소량','국내 생산']].map(([n,l]) => (
|
||||
<div key={l}>
|
||||
<div style={{ fontFamily: "'DM Serif Display', serif", fontSize: 24, color: T.ink }}>{n}</div>
|
||||
<div style={{ fontSize: 11, color: T.chalk, marginTop: 4, wordBreak: 'keep-all', letterSpacing: '.04em' }}>{l}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
LOOKBOOK — horizontal scroll strip
|
||||
════════════════════════════════════════════ */}
|
||||
<section style={{ padding: '0 48px 80px', maxWidth: 1360, margin: '0 auto' }}>
|
||||
<div className="ml-reveal" style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 24 }}>
|
||||
<h3 style={{ fontFamily: "'DM Serif Display', serif", fontSize: 20, fontWeight: 400, color: T.ink }}>Lookbook</h3>
|
||||
<a href="#" style={{ fontSize: 11, color: T.stone, letterSpacing: '.1em', textTransform: 'uppercase', textDecoration: 'none', transition: 'color .2s' }}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.color = T.ink)}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = T.stone)}>
|
||||
전체 보기 →
|
||||
</a>
|
||||
</div>
|
||||
<div className="ml-lb">
|
||||
{[IMG.lb1, IMG.lb2, IMG.lb3, IMG.lb4, IMG.lb5].map((src, i) => (
|
||||
<div key={i} style={{
|
||||
flexShrink: 0, width: 240, aspectRatio: '3/4', overflow: 'hidden',
|
||||
background: T.sand, cursor: 'pointer',
|
||||
}}>
|
||||
<img src={src} alt={`Lookbook ${i+1}`}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover', filter: 'saturate(.65)', transition: 'transform .6s cubic-bezier(.16,1,.3,1), filter .4s' }}
|
||||
loading="lazy"
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLImageElement).style.transform = 'scale(1.04)'; (e.currentTarget as HTMLImageElement).style.filter = 'saturate(.85)'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLImageElement).style.transform = 'scale(1)'; (e.currentTarget as HTMLImageElement).style.filter = 'saturate(.65)'; }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
REVIEWS — typographic, no card bg
|
||||
════════════════════════════════════════════ */}
|
||||
<section style={{ background: T.ink, padding: '96px 80px' }}>
|
||||
<div style={{ maxWidth: 1100, margin: '0 auto' }}>
|
||||
<p className="ml-reveal" style={{ fontSize: 10, color: T.stone, letterSpacing: '.22em', textTransform: 'uppercase', marginBottom: 64 }}>
|
||||
Customer Notes
|
||||
</p>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: '1px', background: 'rgba(244,242,239,.06)' }}>
|
||||
{reviews.map((r, i) => (
|
||||
<div key={i} className="ml-reveal" style={{ padding: '40px 36px', background: T.ink }}>
|
||||
<div style={{ fontFamily: "'DM Serif Display', serif", fontSize: 11, color: T.stone, letterSpacing: '.14em', textTransform: 'uppercase', marginBottom: 24 }}>
|
||||
{r.item}
|
||||
</div>
|
||||
<p style={{ fontSize: 16, color: 'rgba(244,242,239,.85)', lineHeight: 1.75, marginBottom: 32, wordBreak: 'keep-all', fontStyle: 'italic', fontFamily: "'DM Serif Display', serif" }}>
|
||||
"{r.quote}"
|
||||
</p>
|
||||
<div style={{ borderTop: '1px solid rgba(244,242,239,.07)', paddingTop: 20 }}>
|
||||
<div style={{ fontSize: 12, fontWeight: 600, color: T.chalk }}>{r.name}</div>
|
||||
<div style={{ fontSize: 11, color: T.stone, marginTop: 2 }}>{r.city}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
CTA — minimal centered on paper
|
||||
════════════════════════════════════════════ */}
|
||||
<section style={{ padding: '100px 48px', textAlign: 'center', background: T.paper }}>
|
||||
<div className="ml-reveal" style={{ maxWidth: 540, margin: '0 auto' }}>
|
||||
<p style={{ fontSize: 10, color: T.stone, letterSpacing: '.22em', textTransform: 'uppercase', marginBottom: 24 }}>
|
||||
Newsletter
|
||||
</p>
|
||||
<h2 style={{ fontFamily: "'DM Serif Display', serif", fontSize: 'clamp(2rem,4vw,3rem)', fontWeight: 400, color: T.ink, lineHeight: 1.2, marginBottom: 20, wordBreak: 'keep-all' }}>
|
||||
신상품 소식을<br/>가장 먼저 받아보세요
|
||||
</h2>
|
||||
<p style={{ fontSize: 13, color: T.stone, lineHeight: 1.8, marginBottom: 36, wordBreak: 'keep-all' }}>
|
||||
구독자에게는 새 컬렉션 사전 공개와 10% 할인 쿠폰을 드립니다.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 0, maxWidth: 420, margin: '0 auto' }}>
|
||||
<input type="email" placeholder="이메일 주소"
|
||||
style={{
|
||||
flex: 1, padding: '13px 18px',
|
||||
border: '1px solid rgba(12,11,9,.18)', borderRight: 'none',
|
||||
background: 'transparent', fontFamily: 'inherit', fontSize: 13,
|
||||
color: T.ink, outline: 'none',
|
||||
}}
|
||||
onFocus={e => (e.currentTarget.style.borderColor = T.ink)}
|
||||
onBlur={e => (e.currentTarget.style.borderColor = 'rgba(12,11,9,.18)')}
|
||||
/>
|
||||
<button style={{
|
||||
padding: '13px 22px', background: T.ink, color: T.white,
|
||||
border: '1px solid transparent', fontFamily: 'inherit', fontSize: 11,
|
||||
fontWeight: 700, letterSpacing: '.1em', textTransform: 'uppercase',
|
||||
cursor: 'pointer', flexShrink: 0, transition: 'opacity .2s',
|
||||
}}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.opacity = '.8')}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.opacity = '1')}>
|
||||
구독
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
FOOTER
|
||||
════════════════════════════════════════════ */}
|
||||
<footer style={{ background: T.ink, padding: '64px 80px 36px', color: T.stone }}>
|
||||
<div style={{ maxWidth: 1280, margin: '0 auto' }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr 1fr', gap: 48, marginBottom: 56 }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: "'DM Serif Display', serif", fontSize: 20, color: T.white, letterSpacing: '.1em', marginBottom: 6 }}>MELLOW</div>
|
||||
<div style={{ fontSize: 9, color: T.stone, letterSpacing: '.22em', textTransform: 'uppercase', marginBottom: 18 }}>studio</div>
|
||||
<p style={{ fontSize: 12, lineHeight: 1.85, wordBreak: 'keep-all', maxWidth: 220, color: 'rgba(244,242,239,.4)' }}>
|
||||
자연 소재와 정직한 제작으로<br/>오래가는 옷을 만듭니다.
|
||||
</p>
|
||||
</div>
|
||||
{[
|
||||
{ title: 'Shop', links: ['신상품', '아우터', '상의', '하의', '액세서리', '세일'] },
|
||||
{ title: 'Brand', links: ['브랜드 스토리', '지속가능성', '룩북', '장인 협업'] },
|
||||
{ title: 'Support', links: ['배송·반품', '사이즈 가이드', 'FAQ', '문의하기'] },
|
||||
].map(col => (
|
||||
<div key={col.title}>
|
||||
<div style={{ fontSize: 9, color: 'rgba(244,242,239,.4)', letterSpacing: '.2em', textTransform: 'uppercase', marginBottom: 20, fontWeight: 700 }}>{col.title}</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{col.links.map(l => (
|
||||
<a key={l} href="#" style={{ fontSize: 12, color: T.stone, textDecoration: 'none', transition: 'color .2s' }}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.color = T.chalk)}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = T.stone)}>
|
||||
{l}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ borderTop: '1px solid rgba(244,242,239,.06)', paddingTop: 28, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 14 }}>
|
||||
<p style={{ fontSize: 11, color: 'rgba(244,242,239,.3)', letterSpacing: '.03em' }}>© 2025 MELLOW STUDIO. All rights reserved.</p>
|
||||
<div style={{ display: 'flex', gap: 24 }}>
|
||||
{['개인정보처리방침', '이용약관', 'Instagram', 'Pinterest'].map(l => (
|
||||
<a key={l} href="#" style={{ fontSize: 11, color: T.stone, textDecoration: 'none', transition: 'color .2s' }}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.color = T.chalk)}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = T.stone)}>
|
||||
{l}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
QUICK-ADD HOVER — global CSS patch
|
||||
════════════════════════════════════════════ */}
|
||||
<style dangerouslySetInnerHTML={{ __html: `.ml-card:hover .ml-quick { opacity: 1 !important; }` }} />
|
||||
|
||||
{/* ════════════════════════════════════════════
|
||||
SCROLL TO TOP
|
||||
════════════════════════════════════════════ */}
|
||||
<button onClick={scrollTop} aria-label="맨 위로"
|
||||
style={{
|
||||
position: 'fixed', bottom: '5.5rem', right: '2rem', zIndex: 200,
|
||||
width: 46, height: 46,
|
||||
background: T.ink, border: 'none', cursor: 'pointer',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
boxShadow: '0 8px 28px rgba(12,11,9,.25)',
|
||||
opacity: showTop ? 1 : 0,
|
||||
transform: showTop ? 'translateY(0)' : 'translateY(12px)',
|
||||
transition: 'opacity .35s cubic-bezier(.16,1,.3,1), transform .35s cubic-bezier(.16,1,.3,1)',
|
||||
pointerEvents: showTop ? 'auto' : 'none',
|
||||
}}>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={T.white} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="18 15 12 9 6 15"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user