feat: 쇼핑몰 샘플 추가, 홈페이지 서비스 페이지 재디자인, 공통 UI 개선
- 모든 샘플 페이지 우측 하단 맨 위로 스크롤 버튼 추가 (인테리어, 독서) - 독서 기록 노트 상단 '홈페이지 제작 서비스로 돌아가기' 배너 추가 - 개인 쇼핑몰 샘플 (MELLOW STUDIO) 신규 생성 - 베이지/크림 라이트 톤, Cormorant Garamond + Pretendard - 히어로 스플릿 레이아웃, 상품 그리드(카테고리 필터), 브랜드 스토리, 리뷰, CTA, 푸터 - 장바구니 뱃지, 상품 찜하기, 퀵 장바구니 인터랙션 - 홈페이지 서비스 소개 페이지 재디자인 - CookieRun → Pretendard 교체로 한글 폰트 렌더링 개선 - word-break: keep-all 적용으로 이상한 개행 제거 - IntersectionObserver 스크롤 reveal 애니메이션 추가 - Trust badge 섹션, Marquee 추가 - 쇼핑몰 샘플 카드 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useState } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
const samples = [
|
const samples = [
|
||||||
{
|
{
|
||||||
@@ -74,6 +74,16 @@ const samples = [
|
|||||||
tags: ['라이프', '독서', '기록'],
|
tags: ['라이프', '독서', '기록'],
|
||||||
icon: '◻',
|
icon: '◻',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'shopping',
|
||||||
|
title: '개인 쇼핑몰',
|
||||||
|
subtitle: 'MELLOW STUDIO',
|
||||||
|
desc: '감각적인 브랜드 스토리텔링과 미니멀 레이아웃으로 완성한 패션·라이프스타일 쇼핑몰',
|
||||||
|
gradient: 'linear-gradient(135deg, #2A2018 0%, #4A3C2C 50%, #7A6A52 100%)',
|
||||||
|
accent: '#C4A882',
|
||||||
|
tags: ['쇼핑몰', '패션', '라이프'],
|
||||||
|
icon: '◇',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const processSteps = [
|
const processSteps = [
|
||||||
@@ -127,149 +137,308 @@ const faqs = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
export default function WebsiteServicePage() {
|
export default function WebsiteServicePage() {
|
||||||
const [openFaq, setOpenFaq] = useState<number | null>(null);
|
const [openFaq, setOpenFaq] = useState<number | null>(null);
|
||||||
|
const [showTop, setShowTop] = useState(false);
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<div style={{ background: '#020817', minHeight: '100vh', color: 'white' }}>
|
<div style={{ background: '#030712', minHeight: '100vh', color: 'white', fontFamily: "'Pretendard', 'Apple SD Gothic Neo', system-ui, sans-serif" }}>
|
||||||
<style>{`
|
<style dangerouslySetInnerHTML={{ __html: `
|
||||||
@keyframes fadeUp { from { opacity: 0; transform: translateY(28px); } to { opacity: 1; transform: translateY(0); } }
|
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.min.css');
|
||||||
@keyframes gridScroll { from { background-position: 0 0; } to { background-position: 48px 48px; } }
|
|
||||||
.ws-card:hover { transform: translateY(-5px); box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
|
|
||||||
.ws-card { transition: transform 0.3s ease, box-shadow 0.3s ease; }
|
|
||||||
.ws-plan:hover { transform: translateY(-3px); }
|
|
||||||
.ws-plan { transition: transform 0.3s ease; }
|
|
||||||
`}</style>
|
|
||||||
|
|
||||||
{/* Hero */}
|
* { box-sizing: border-box; }
|
||||||
<section style={{ padding: '72px 24px 56px', textAlign: 'center', position: 'relative', overflow: 'hidden' }}>
|
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-marquee {
|
||||||
|
from { transform: translateX(0); }
|
||||||
|
to { transform: translateX(-50%); }
|
||||||
|
}
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ws-marquee-track { animation: ws-marquee 30s linear infinite; display: flex; }
|
||||||
|
|
||||||
|
/* 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', textAlign: 'center', position: 'relative', overflow: 'hidden' }}>
|
||||||
|
{/* Animated grid */}
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute', inset: 0,
|
position: 'absolute', inset: 0, pointerEvents: 'none',
|
||||||
backgroundImage: 'linear-gradient(rgba(99,102,241,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(99,102,241,0.06) 1px, transparent 1px)',
|
backgroundImage: 'linear-gradient(rgba(99,102,241,0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(99,102,241,0.05) 1px, transparent 1px)',
|
||||||
backgroundSize: '48px 48px',
|
backgroundSize: '48px 48px',
|
||||||
animation: 'gridScroll 8s linear infinite',
|
animation: 'ws-gridScroll 10s linear infinite',
|
||||||
|
}} />
|
||||||
|
{/* Radial glow */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', inset: 0, pointerEvents: 'none',
|
||||||
|
background: 'radial-gradient(ellipse 75% 55% at 50% 0%, rgba(99,102,241,0.15) 0%, transparent 70%)',
|
||||||
|
}} />
|
||||||
|
{/* Ambient dot */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', top: '60%', left: '15%', width: 300, height: 300,
|
||||||
|
background: 'radial-gradient(circle, rgba(129,140,248,0.06) 0%, transparent 70%)',
|
||||||
|
borderRadius: '50%', pointerEvents: 'none', animation: 'ws-glow 5s ease-in-out infinite',
|
||||||
}} />
|
}} />
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute', inset: 0,
|
position: 'absolute', top: '30%', right: '10%', width: 200, height: 200,
|
||||||
background: 'radial-gradient(ellipse 70% 60% at 50% 0%, rgba(99,102,241,0.18) 0%, transparent 70%)',
|
background: 'radial-gradient(circle, rgba(244,114,182,0.05) 0%, transparent 70%)',
|
||||||
|
borderRadius: '50%', pointerEvents: 'none', animation: 'ws-glow 7s ease-in-out infinite 2s',
|
||||||
}} />
|
}} />
|
||||||
<div style={{ maxWidth: 820, margin: '0 auto', position: 'relative', animation: 'fadeUp 0.8s ease forwards' }}>
|
|
||||||
|
<div style={{ maxWidth: 820, margin: '0 auto', position: 'relative', animation: 'ws-fadeUp 0.9s cubic-bezier(0.16,1,0.3,1) both' }}>
|
||||||
<span style={{
|
<span style={{
|
||||||
display: 'inline-block', fontSize: 11, fontWeight: 700, letterSpacing: '0.2em',
|
display: 'inline-block', fontSize: 11, fontWeight: 700, letterSpacing: '0.2em',
|
||||||
color: '#818cf8', textTransform: 'uppercase',
|
color: '#818cf8', textTransform: 'uppercase',
|
||||||
border: '1px solid rgba(129,140,248,0.35)', padding: '5px 16px', borderRadius: 100,
|
border: '1px solid rgba(129,140,248,0.3)', padding: '5px 16px', borderRadius: 100,
|
||||||
marginBottom: 28, fontFamily: "'CookieRun', sans-serif",
|
marginBottom: 32,
|
||||||
}}>
|
}}>
|
||||||
Homepage Development Service
|
Homepage Development Service
|
||||||
</span>
|
</span>
|
||||||
<h1 style={{
|
<h1 style={{
|
||||||
fontFamily: "'CookieRun', sans-serif", fontSize: 'clamp(30px, 5vw, 58px)', fontWeight: 800,
|
fontSize: 'clamp(28px, 4.5vw, 54px)', fontWeight: 800,
|
||||||
lineHeight: 1.15, marginBottom: 20,
|
lineHeight: 1.2, marginBottom: 20,
|
||||||
|
letterSpacing: '-0.02em',
|
||||||
background: 'linear-gradient(135deg, #ffffff 0%, #c7d2fe 50%, #818cf8 100%)',
|
background: 'linear-gradient(135deg, #ffffff 0%, #c7d2fe 50%, #818cf8 100%)',
|
||||||
WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
|
WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
|
||||||
|
wordBreak: 'keep-all',
|
||||||
}}>
|
}}>
|
||||||
홈페이지 맡겼다가 연락 끊긴 경험,<br />여기선 없습니다
|
홈페이지 맡겼다가<br/>연락 끊긴 경험, 여기선 없습니다
|
||||||
</h1>
|
</h1>
|
||||||
<p style={{
|
<p style={{
|
||||||
fontSize: 16, color: '#94a3b8', lineHeight: 1.8, marginBottom: 36,
|
fontSize: 16, color: '#64748b', lineHeight: 1.85, marginBottom: 36,
|
||||||
fontFamily: "'CookieRun', sans-serif",
|
wordBreak: 'keep-all',
|
||||||
}}>
|
}}>
|
||||||
계약서 작성 → 중간 보고 → 소스코드 인도까지 단계마다 증거를 남깁니다.<br/>
|
계약서 작성 → 중간 보고 → 소스코드 인도까지 단계마다 증거를 남깁니다.<br/>
|
||||||
납기 지연 시 하루당 10만원 감면 — 그래서 안 늦습니다.
|
납기 지연 시 하루당 10만원 감면 — 그래서 안 늦습니다.
|
||||||
</p>
|
</p>
|
||||||
<div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
|
||||||
<Link href="/freelance?service=website" style={{
|
<Link href="/freelance?service=website" style={{
|
||||||
display: 'inline-block', padding: '14px 32px',
|
display: 'inline-flex', alignItems: 'center', gap: 8,
|
||||||
|
padding: '14px 28px',
|
||||||
background: 'linear-gradient(135deg, #6366f1, #818cf8)',
|
background: 'linear-gradient(135deg, #6366f1, #818cf8)',
|
||||||
borderRadius: 12, color: 'white', fontWeight: 700, fontSize: 15,
|
borderRadius: 12, color: 'white', fontWeight: 700, fontSize: 15,
|
||||||
textDecoration: 'none', fontFamily: "'CookieRun', sans-serif",
|
textDecoration: 'none',
|
||||||
boxShadow: '0 8px 32px rgba(99,102,241,0.4)',
|
boxShadow: '0 8px 32px 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 48px rgba(99,102,241,0.5)'; }}
|
||||||
|
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.transform = ''; (e.currentTarget as HTMLElement).style.boxShadow = '0 8px 32px rgba(99,102,241,0.4)'; }}>
|
||||||
무료 상담 신청 →
|
무료 상담 신청 →
|
||||||
</Link>
|
</Link>
|
||||||
<a href="#samples" style={{
|
<a href="#samples" style={{
|
||||||
display: 'inline-block', padding: '14px 32px',
|
display: 'inline-flex', alignItems: 'center', gap: 8,
|
||||||
border: '1px solid rgba(255,255,255,0.12)', borderRadius: 12,
|
padding: '14px 28px',
|
||||||
color: '#cbd5e1', fontWeight: 600, fontSize: 15,
|
border: '1px solid rgba(255,255,255,0.1)', borderRadius: 12,
|
||||||
textDecoration: 'none', fontFamily: "'CookieRun', sans-serif",
|
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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div style={{ display: 'flex', gap: 32, justifyContent: 'center', marginTop: 48, flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: 0, justifyContent: 'center', marginTop: 56, flexWrap: 'wrap' }}>
|
||||||
{[
|
{[
|
||||||
{ num: '3~5일', label: '최단 납품 (스타터)' },
|
{ num: '3~5일', label: '최단 납품 (스타터)' },
|
||||||
{ num: '20만원~', label: '시작 가격' },
|
{ num: '20만원~', label: '시작 가격' },
|
||||||
{ num: '전액환불', label: '납품 전 환불 보장' },
|
{ num: '전액환불', label: '납품 전 환불 보장' },
|
||||||
].map((s) => (
|
].map((s, i) => (
|
||||||
<div key={s.label} style={{ textAlign: 'center' }}>
|
<div key={s.label} style={{
|
||||||
<div style={{ fontSize: 24, fontWeight: 800, color: 'white', fontFamily: "'CookieRun', sans-serif" }}>{s.num}</div>
|
textAlign: 'center', padding: '0 40px',
|
||||||
<div style={{ fontSize: 12, color: '#475569', fontFamily: "'CookieRun', sans-serif", marginTop: 2 }}>{s.label}</div>
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Sample Portfolio */}
|
{/* ── Marquee ── */}
|
||||||
<section id="samples" style={{ padding: '56px 24px', maxWidth: 1100, margin: '0 auto' }}>
|
<div style={{ borderTop: '1px solid rgba(255,255,255,0.04)', borderBottom: '1px solid rgba(255,255,255,0.04)', overflow: 'hidden', padding: '12px 0' }}>
|
||||||
<div style={{ textAlign: 'center', marginBottom: 40 }}>
|
<div className="ws-marquee-track" style={{ gap: 0 }}>
|
||||||
<h2 style={{
|
{[...Array(2)].map((_, i) => (
|
||||||
fontFamily: "'CookieRun', sans-serif", fontSize: 28, fontWeight: 800,
|
<div key={i} style={{ display: 'flex', gap: 0, whiteSpace: 'nowrap', flexShrink: 0 }}>
|
||||||
color: 'white', marginBottom: 10,
|
{['반응형 디자인', 'SEO 최적화', '빠른 납품', '계약서 작성', '소스코드 제공', '1년 AS 보장', '도메인 배포'].map((t, j) => (
|
||||||
|
<span key={j} style={{ padding: '0 2rem', fontSize: '0.75rem', color: '#1e293b', letterSpacing: '0.12em', textTransform: 'uppercase' }}>
|
||||||
|
{t} <span style={{ color: 'rgba(99,102,241,0.3)', margin: '0 0.5rem' }}>·</span>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</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: 16 }}>
|
||||||
|
{[
|
||||||
|
{ icon: '📋', title: '계약서 필수 작성', desc: '모든 프로젝트 계약서 체결 후 진행' },
|
||||||
|
{ icon: '📊', title: '주간 진행 보고', desc: '매주 작업 현황 공유, 연락 두절 없음' },
|
||||||
|
{ icon: '💾', title: '소스코드 전액 제공', desc: '완성 후 전체 소스코드 인도' },
|
||||||
|
{ icon: '🔒', title: '납기 지연 패널티', desc: '지연 1일당 10만원 자동 감면' },
|
||||||
|
].map((b) => (
|
||||||
|
<div key={b.title} style={{
|
||||||
|
padding: '20px 22px', borderRadius: 14,
|
||||||
|
background: 'rgba(255,255,255,0.02)',
|
||||||
|
border: '1px solid rgba(255,255,255,0.05)',
|
||||||
|
display: 'flex', gap: 14, alignItems: 'flex-start',
|
||||||
}}>
|
}}>
|
||||||
|
<span style={{ fontSize: 24, flexShrink: 0 }}>{b.icon}</span>
|
||||||
|
<div>
|
||||||
|
<div style={{ fontSize: 13, fontWeight: 700, 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>
|
</h2>
|
||||||
<p style={{ color: '#64748b', fontFamily: "'CookieRun', sans-serif", fontSize: 15 }}>
|
<p style={{ color: '#475569', fontSize: 14 }}>
|
||||||
카드를 클릭하면 실제 완성 화면을 미리 확인할 수 있습니다
|
카드를 클릭하면 실제 완성 화면을 미리 확인할 수 있습니다
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 20 }}>
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 18 }}>
|
||||||
{samples.map((s) => (
|
{samples.map((s) => (
|
||||||
<Link key={s.type} href={`/services/website/samples/${s.type}`} style={{ textDecoration: 'none' }}>
|
<Link key={s.type} href={`/services/website/samples/${s.type}`} style={{ textDecoration: 'none' }}>
|
||||||
<div className="ws-card" style={{
|
<div className="ws-sample-card">
|
||||||
borderRadius: 20, overflow: 'hidden',
|
|
||||||
border: '1px solid rgba(255,255,255,0.07)',
|
|
||||||
background: '#0a1020', cursor: 'pointer',
|
|
||||||
}}>
|
|
||||||
<div style={{
|
<div style={{
|
||||||
height: 170, background: s.gradient, position: 'relative',
|
height: 175, background: s.gradient, position: 'relative',
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
}}>
|
}}>
|
||||||
<span style={{ fontSize: 52, filter: 'drop-shadow(0 4px 16px rgba(0,0,0,0.6))' }}>{s.icon}</span>
|
<span style={{ fontSize: 52, filter: 'drop-shadow(0 4px 20px rgba(0,0,0,0.6))' }}>{s.icon}</span>
|
||||||
<div style={{
|
<div style={{ position: 'absolute', top: 12, left: 12, display: 'flex', gap: 5 }}>
|
||||||
position: 'absolute', top: 12, left: 12,
|
|
||||||
display: 'flex', gap: 5,
|
|
||||||
}}>
|
|
||||||
{s.tags.map((tag) => (
|
{s.tags.map((tag) => (
|
||||||
<span key={tag} style={{
|
<span key={tag} style={{
|
||||||
fontSize: 10, fontWeight: 600, color: '#e2e8f0',
|
fontSize: 10, fontWeight: 600, color: '#e2e8f0',
|
||||||
background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(8px)',
|
background: 'rgba(0,0,0,0.5)', backdropFilter: 'blur(8px)',
|
||||||
border: '1px solid rgba(255,255,255,0.12)',
|
border: '1px solid rgba(255,255,255,0.1)',
|
||||||
padding: '2px 8px', borderRadius: 100,
|
padding: '2px 8px', borderRadius: 100,
|
||||||
}}>{tag}</span>
|
}}>{tag}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute', bottom: 12, right: 12,
|
position: 'absolute', bottom: 12, right: 12,
|
||||||
background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(8px)',
|
background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(8px)',
|
||||||
border: `1px solid ${s.accent}50`,
|
border: `1px solid ${s.accent}45`,
|
||||||
borderRadius: 8, padding: '5px 12px',
|
borderRadius: 8, padding: '5px 12px',
|
||||||
fontSize: 11, color: s.accent, fontFamily: "'CookieRun', sans-serif", fontWeight: 700,
|
fontSize: 11, color: s.accent, fontWeight: 700,
|
||||||
}}>
|
}}>
|
||||||
미리보기 →
|
미리보기 →
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ padding: '18px 22px 22px' }}>
|
<div style={{ padding: '18px 22px 22px' }}>
|
||||||
<div style={{ fontSize: 11, color: '#475569', fontFamily: "'CookieRun', sans-serif", marginBottom: 5, letterSpacing: '0.05em' }}>
|
<div style={{ fontSize: 11, color: '#334155', marginBottom: 5, letterSpacing: '0.05em' }}>
|
||||||
{s.subtitle}
|
{s.subtitle}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 17, fontWeight: 700, color: 'white', fontFamily: "'CookieRun', sans-serif", marginBottom: 8 }}>
|
<div style={{ fontSize: 16, fontWeight: 700, color: 'white', marginBottom: 8, letterSpacing: '-0.01em' }}>
|
||||||
{s.title}
|
{s.title}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 13, color: '#64748b', lineHeight: 1.65, fontFamily: "'CookieRun', sans-serif" }}>
|
<div style={{ fontSize: 13, color: '#475569', lineHeight: 1.65, wordBreak: 'keep-all' }}>
|
||||||
{s.desc}
|
{s.desc}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -277,43 +446,40 @@ export default function WebsiteServicePage() {
|
|||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Process */}
|
{/* ── Process ── */}
|
||||||
<section style={{ padding: '56px 24px', background: 'rgba(255,255,255,0.02)', borderTop: '1px solid rgba(255,255,255,0.05)', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
|
<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 style={{ maxWidth: 1000, margin: '0 auto' }}>
|
<div ref={processRef} className="ws-reveal" style={{ maxWidth: 1060, margin: '0 auto' }}>
|
||||||
<div style={{ textAlign: 'center', marginBottom: 40 }}>
|
<div style={{ textAlign: 'center', marginBottom: 44 }}>
|
||||||
<h2 style={{ fontFamily: "'CookieRun', sans-serif", fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 10 }}>
|
<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>
|
</h2>
|
||||||
<p style={{ color: '#64748b', fontFamily: "'CookieRun', sans-serif", fontSize: 15 }}>
|
<p style={{ color: '#475569', fontSize: 14 }}>투명하고 체계적인 5단계로 진행됩니다</p>
|
||||||
투명하고 체계적인 5단계로 진행됩니다
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', alignItems: 'stretch', flexWrap: 'wrap', justifyContent: 'center', gap: 0 }}>
|
<div style={{ display: 'flex', alignItems: 'stretch', flexWrap: 'wrap', justifyContent: 'center', gap: 0 }}>
|
||||||
{processSteps.map((p, i) => (
|
{processSteps.map((p, i) => (
|
||||||
<div key={i} style={{ display: 'flex', alignItems: 'center' }}>
|
<div key={i} style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<div style={{
|
<div className="ws-step-card" style={{
|
||||||
textAlign: 'center', padding: '24px 20px', minWidth: 130,
|
textAlign: 'center', padding: '28px 22px', minWidth: 138,
|
||||||
background: '#0f172a', borderRadius: 16,
|
background: '#080d1a', borderRadius: 16,
|
||||||
border: '1px solid rgba(255,255,255,0.06)',
|
border: '1px solid rgba(255,255,255,0.05)',
|
||||||
}}>
|
|
||||||
<div style={{ fontSize: 30, marginBottom: 10 }}>{p.icon}</div>
|
|
||||||
<div style={{
|
|
||||||
fontSize: 10, color: '#6366f1', fontFamily: "'CookieRun', sans-serif",
|
|
||||||
fontWeight: 700, letterSpacing: '0.1em', marginBottom: 6,
|
|
||||||
}}>
|
}}>
|
||||||
|
<div style={{ fontSize: 28, marginBottom: 12 }}>{p.icon}</div>
|
||||||
|
<div style={{ fontSize: 10, color: '#6366f1', fontWeight: 700, letterSpacing: '0.12em', marginBottom: 7 }}>
|
||||||
STEP {p.step}
|
STEP {p.step}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 14, fontWeight: 700, color: 'white', fontFamily: "'CookieRun', sans-serif", marginBottom: 5 }}>
|
<div style={{ fontSize: 14, fontWeight: 700, color: 'white', marginBottom: 6, wordBreak: 'keep-all' }}>
|
||||||
{p.title}
|
{p.title}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 11, color: '#475569', fontFamily: "'CookieRun', sans-serif", lineHeight: 1.5 }}>
|
<div style={{ fontSize: 11, color: '#334155', lineHeight: 1.55, wordBreak: 'keep-all' }}>
|
||||||
{p.desc}
|
{p.desc}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{i < processSteps.length - 1 && (
|
{i < processSteps.length - 1 && (
|
||||||
<div style={{ color: '#1e293b', fontSize: 22, padding: '0 6px', flexShrink: 0 }}>›</div>
|
<div style={{ color: '#1e293b', fontSize: 20, padding: '0 4px', flexShrink: 0 }}>›</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -321,142 +487,165 @@ export default function WebsiteServicePage() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Pricing */}
|
{/* ── Pricing ── */}
|
||||||
<section style={{ padding: '56px 24px', maxWidth: 1000, margin: '0 auto' }}>
|
<section style={{ padding: '64px 24px', maxWidth: 1040, margin: '0 auto' }}>
|
||||||
<div style={{ textAlign: 'center', marginBottom: 40 }}>
|
<div ref={pricingRef} className="ws-reveal">
|
||||||
<h2 style={{ fontFamily: "'CookieRun', sans-serif", fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 10 }}>
|
<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>
|
</h2>
|
||||||
<p style={{ color: '#64748b', fontFamily: "'CookieRun', sans-serif", fontSize: 15 }}>
|
<p style={{ color: '#475569', fontSize: 14 }}>프로젝트 규모에 맞는 플랜을 선택하세요</p>
|
||||||
프로젝트 규모에 맞는 플랜을 선택하세요
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: 20 }}>
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(270px, 1fr))', gap: 20 }}>
|
||||||
{plans.map((plan) => (
|
{plans.map((plan) => (
|
||||||
<div key={plan.name} className="ws-plan" style={{
|
<div key={plan.name} className="ws-plan-card" style={{
|
||||||
padding: 30, borderRadius: 20,
|
padding: 32, borderRadius: 20,
|
||||||
background: plan.featured ? 'linear-gradient(135deg, #1e1b4b, #312e81)' : '#0f172a',
|
background: plan.featured ? 'linear-gradient(135deg, #1e1b4b, #312e81)' : '#080d1a',
|
||||||
border: `1px solid ${plan.featured ? plan.color + '55' : 'rgba(255,255,255,0.06)'}`,
|
border: `1px solid ${plan.featured ? plan.color + '40' : 'rgba(255,255,255,0.05)'}`,
|
||||||
position: 'relative', overflow: 'hidden',
|
position: 'relative', overflow: 'hidden',
|
||||||
boxShadow: plan.featured ? `0 24px 64px ${plan.color}18` : 'none',
|
boxShadow: plan.featured ? `0 24px 64px ${plan.color}12` : 'none',
|
||||||
}}>
|
}}>
|
||||||
{plan.featured && (
|
{plan.featured && (
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute', top: 18, right: 18,
|
position: 'absolute', top: 20, right: 20,
|
||||||
background: plan.color, color: '#1e1b4b',
|
background: plan.color, color: '#1e1b4b',
|
||||||
fontSize: 10, fontWeight: 800, padding: '3px 10px', borderRadius: 100,
|
fontSize: 10, fontWeight: 800, padding: '3px 10px', borderRadius: 100,
|
||||||
fontFamily: "'CookieRun', sans-serif",
|
|
||||||
}}>BEST</div>
|
}}>BEST</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ fontSize: 13, color: plan.color, fontFamily: "'CookieRun', sans-serif", fontWeight: 700, marginBottom: 10 }}>
|
<div style={{ fontSize: 12, color: plan.color, fontWeight: 700, marginBottom: 12, letterSpacing: '0.05em' }}>
|
||||||
{plan.name}
|
{plan.name}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: 4, marginBottom: 4 }}>
|
<div style={{ display: 'flex', alignItems: 'baseline', gap: 4, marginBottom: 4 }}>
|
||||||
<span style={{ fontSize: 42, fontWeight: 800, color: 'white', fontFamily: "'CookieRun', sans-serif" }}>
|
<span style={{ fontSize: 40, fontWeight: 800, color: 'white', letterSpacing: '-0.03em' }}>{plan.price}</span>
|
||||||
{plan.price}
|
<span style={{ fontSize: 15, color: '#64748b' }}>{plan.unit}</span>
|
||||||
</span>
|
|
||||||
<span style={{ fontSize: 16, color: '#94a3b8', fontFamily: "'CookieRun', sans-serif" }}>
|
|
||||||
{plan.unit}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, color: '#475569', fontFamily: "'CookieRun', sans-serif", marginBottom: 22 }}>
|
<div style={{ fontSize: 12, color: '#334155', marginBottom: 24, wordBreak: 'keep-all' }}>{plan.note}</div>
|
||||||
{plan.note}
|
<div style={{ borderTop: '1px solid rgba(255,255,255,0.06)', paddingTop: 20, marginBottom: 24 }}>
|
||||||
</div>
|
|
||||||
<div style={{ borderTop: '1px solid rgba(255,255,255,0.07)', paddingTop: 20, marginBottom: 24 }}>
|
|
||||||
{plan.features.map((f) => (
|
{plan.features.map((f) => (
|
||||||
<div key={f} style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 11 }}>
|
<div key={f} style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 12 }}>
|
||||||
<span style={{ color: plan.color, fontSize: 14, flexShrink: 0, fontWeight: 700 }}>✓</span>
|
<span style={{ color: plan.color, fontSize: 13, flexShrink: 0, fontWeight: 700 }}>✓</span>
|
||||||
<span style={{ fontSize: 13, color: '#94a3b8', fontFamily: "'CookieRun', sans-serif" }}>{f}</span>
|
<span style={{ fontSize: 13, color: '#94a3b8', wordBreak: 'keep-all' }}>{f}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<Link href="/freelance?service=website" style={{
|
<Link href="/freelance?service=website" style={{
|
||||||
display: 'block', textAlign: 'center', padding: '12px',
|
display: 'block', textAlign: 'center', padding: '13px',
|
||||||
background: plan.featured ? plan.color : 'rgba(255,255,255,0.05)',
|
background: plan.featured ? plan.color : 'rgba(255,255,255,0.04)',
|
||||||
borderRadius: 10, color: plan.featured ? '#1e1b4b' : '#cbd5e1',
|
borderRadius: 10, color: plan.featured ? '#1e1b4b' : '#94a3b8',
|
||||||
fontWeight: 700, fontSize: 14, textDecoration: 'none',
|
fontWeight: 700, fontSize: 14, textDecoration: 'none',
|
||||||
fontFamily: "'CookieRun', sans-serif",
|
border: plan.featured ? 'none' : '1px solid rgba(255,255,255,0.07)',
|
||||||
border: plan.featured ? 'none' : '1px solid rgba(255,255,255,0.08)',
|
transition: 'opacity 0.2s',
|
||||||
}}>
|
}}>
|
||||||
상담 신청
|
상담 신청
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* FAQ */}
|
{/* ── FAQ ── */}
|
||||||
<section style={{ padding: '0 24px 56px', maxWidth: 720, margin: '0 auto' }}>
|
<section style={{ padding: '0 24px 64px', maxWidth: 720, margin: '0 auto' }}>
|
||||||
<div style={{ textAlign: 'center', marginBottom: 32 }}>
|
<div ref={faqRef} className="ws-reveal">
|
||||||
<h2 style={{ fontFamily: "'CookieRun', sans-serif", fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 10 }}>
|
<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>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||||
{faqs.map((faq, i) => (
|
{faqs.map((faq, i) => (
|
||||||
<div key={i} style={{
|
<div key={i} className="ws-faq-item" style={{
|
||||||
background: '#0f172a', borderRadius: 14,
|
background: '#080d1a',
|
||||||
border: `1px solid ${openFaq === i ? 'rgba(99,102,241,0.45)' : 'rgba(255,255,255,0.06)'}`,
|
border: `1px solid ${openFaq === i ? 'rgba(99,102,241,0.4)' : 'rgba(255,255,255,0.05)'}`,
|
||||||
overflow: 'hidden', transition: 'border-color 0.25s',
|
|
||||||
}}>
|
}}>
|
||||||
<button onClick={() => setOpenFaq(openFaq === i ? null : i)} style={{
|
<button onClick={() => setOpenFaq(openFaq === i ? null : i)} style={{
|
||||||
width: '100%', textAlign: 'left', padding: '18px 22px',
|
width: '100%', textAlign: 'left', padding: '18px 22px',
|
||||||
background: 'none', border: 'none', cursor: 'pointer',
|
background: 'none', border: 'none', cursor: 'pointer',
|
||||||
display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12,
|
display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12,
|
||||||
}}>
|
}}>
|
||||||
<span style={{ fontSize: 14, fontWeight: 600, color: 'white', fontFamily: "'CookieRun', sans-serif" }}>
|
<span style={{ fontSize: 14, fontWeight: 600, color: 'white', wordBreak: 'keep-all' }}>
|
||||||
{faq.q}
|
{faq.q}
|
||||||
</span>
|
</span>
|
||||||
<span style={{
|
<span style={{
|
||||||
color: '#6366f1', fontSize: 22, flexShrink: 0,
|
color: '#6366f1', fontSize: 22, flexShrink: 0,
|
||||||
transition: 'transform 0.2s',
|
transition: 'transform 0.25s',
|
||||||
transform: openFaq === i ? 'rotate(45deg)' : 'none',
|
transform: openFaq === i ? 'rotate(45deg)' : 'none',
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
}}>+</span>
|
}}>+</span>
|
||||||
</button>
|
</button>
|
||||||
{openFaq === i && (
|
{openFaq === i && (
|
||||||
<div style={{
|
<div style={{ padding: '0 22px 18px', fontSize: 14, color: '#475569', lineHeight: 1.8, wordBreak: 'keep-all' }}>
|
||||||
padding: '0 22px 18px', fontSize: 14, color: '#64748b',
|
|
||||||
lineHeight: 1.75, fontFamily: "'CookieRun', sans-serif",
|
|
||||||
}}>
|
|
||||||
{faq.a}
|
{faq.a}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* CTA */}
|
{/* ── CTA ── */}
|
||||||
<section style={{ padding: '0 24px 80px', textAlign: 'center' }}>
|
<section style={{ padding: '0 24px 80px', textAlign: 'center' }}>
|
||||||
|
<div ref={ctaRef} className="ws-reveal">
|
||||||
<div style={{
|
<div style={{
|
||||||
maxWidth: 620, margin: '0 auto',
|
maxWidth: 640, margin: '0 auto',
|
||||||
padding: '52px 40px', borderRadius: 24,
|
padding: '56px 44px', borderRadius: 24,
|
||||||
background: 'linear-gradient(135deg, #1e1b4b, #312e81)',
|
background: 'linear-gradient(135deg, #1e1b4b, #312e81)',
|
||||||
border: '1px solid rgba(129,140,248,0.3)',
|
border: '1px solid rgba(129,140,248,0.25)',
|
||||||
boxShadow: '0 24px 80px rgba(99,102,241,0.2)',
|
boxShadow: '0 24px 80px rgba(99,102,241,0.18)',
|
||||||
|
position: 'relative', overflow: 'hidden',
|
||||||
}}>
|
}}>
|
||||||
<h2 style={{ fontFamily: "'CookieRun', sans-serif", fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 14 }}>
|
<div style={{ position: 'absolute', top: -60, right: -60, width: 200, height: 200, background: 'radial-gradient(circle, rgba(129,140,248,0.15) 0%, transparent 70%)', borderRadius: '50%', pointerEvents: 'none' }} />
|
||||||
|
<h2 style={{ fontSize: 28, fontWeight: 800, color: 'white', marginBottom: 14, letterSpacing: '-0.02em', wordBreak: 'keep-all', position: 'relative' }}>
|
||||||
지금 바로 시작하세요
|
지금 바로 시작하세요
|
||||||
</h2>
|
</h2>
|
||||||
<p style={{
|
<p style={{ color: '#a5b4fc', fontSize: 15, lineHeight: 1.75, marginBottom: 32, wordBreak: 'keep-all', position: 'relative' }}>
|
||||||
color: '#a5b4fc', fontSize: 15, lineHeight: 1.7, marginBottom: 30,
|
|
||||||
fontFamily: "'CookieRun', sans-serif",
|
|
||||||
}}>
|
|
||||||
무료 상담은 24시간 이내 답변드립니다.<br/>
|
무료 상담은 24시간 이내 답변드립니다.<br/>
|
||||||
어떤 규모의 프로젝트든 환영합니다.
|
어떤 규모의 프로젝트든 환영합니다.
|
||||||
</p>
|
</p>
|
||||||
<Link href="/freelance?service=website" style={{
|
<Link href="/freelance?service=website" style={{
|
||||||
display: 'inline-block', padding: '15px 40px',
|
display: 'inline-block', padding: '15px 40px',
|
||||||
background: 'linear-gradient(135deg, #818cf8, #6366f1)',
|
background: 'linear-gradient(135deg, #818cf8, #6366f1)',
|
||||||
borderRadius: 12, color: 'white', fontWeight: 700, fontSize: 16,
|
borderRadius: 12, color: 'white', fontWeight: 700, fontSize: 15,
|
||||||
textDecoration: 'none', fontFamily: "'CookieRun', sans-serif",
|
textDecoration: 'none',
|
||||||
boxShadow: '0 8px 32px rgba(99,102,241,0.5)',
|
boxShadow: '0 8px 32px rgba(99,102,241,0.5)',
|
||||||
}}>
|
position: 'relative',
|
||||||
|
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 48px rgba(99,102,241,0.6)'; }}
|
||||||
|
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.transform = ''; (e.currentTarget as HTMLElement).style.boxShadow = '0 8px 32px rgba(99,102,241,0.5)'; }}>
|
||||||
무료 상담 신청하기 →
|
무료 상담 신청하기 →
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</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: '2rem', right: '2rem', zIndex: 200,
|
||||||
|
width: 48, height: 48, borderRadius: '50%',
|
||||||
|
background: 'linear-gradient(135deg, #6366f1, #818cf8)',
|
||||||
|
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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════
|
/* ══════════════════════════════════════════════
|
||||||
DATA
|
DATA
|
||||||
@@ -36,9 +36,12 @@ const services = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const testimonials = [
|
const testimonials = [
|
||||||
{ name: '하윤서', role: '한남동 단독주택 의뢰인', u: 'hayunseo', rating: 5, highlight: '계획보다 적은 예산', text: '처음엔 예산이 걱정됐는데, 아우라 팀이 범위를 명확히 정해줘서 오히려 계획보다 적게 들었습니다. 무엇보다 완공된 공간에서 매일 아침 커피 한 잔 하는 지금이 너무 행복해요.' },
|
{ name: '하윤서', role: '한남동 단독주택 의뢰인', u: 'hayunseo', rating: 5, highlight: '계획보다 적은 예산',
|
||||||
{ name: '박도현', role: '카페 에스프레소랩 대표', u: 'parkdohyun', rating: 5, highlight: '매출 340% 상승', text: '우리 브랜드 철학을 완벽하게 공간으로 옮겨줬습니다. 오픈 첫날부터 SNS 바이럴이 터졌고, 오픈 3개월 만에 매출이 전년 대비 340% 올랐어요.' },
|
text: '처음엔 예산이 걱정됐는데, 아우라 팀이 범위를 명확히 정해줘서 오히려 계획보다 적게 들었습니다. 무엇보다 완공된 공간에서 매일 아침 커피 한 잔 하는 지금이 너무 행복해요.' },
|
||||||
{ name: '이서진', role: '루미너스 COO', u: 'leeseojin', rating: 5, highlight: '직원 만족도 93점', text: '직원들이 출근하고 싶은 공간을 만드는 게 목표였습니다. 리모델링 후 직원 만족도 설문에서 93점, 퇴직률이 절반으로 줄었습니다.' },
|
{ 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 = [
|
const steps = [
|
||||||
@@ -71,126 +74,44 @@ const ArrowRight = ({ color = '#8B6914' }: { color?: string }) => (
|
|||||||
/* ══════════════════════════════════════════════
|
/* ══════════════════════════════════════════════
|
||||||
CONSTANTS
|
CONSTANTS
|
||||||
══════════════════════════════════════════════ */
|
══════════════════════════════════════════════ */
|
||||||
const FRAME_COUNT = 48;
|
const BANNER_H = 40;
|
||||||
const BANNER_H = 40; // px — back-banner explicit height
|
const NAV_H = 72;
|
||||||
const NAV_H = 72; // px — sticky nav height
|
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════
|
/* ══════════════════════════════════════════════
|
||||||
PAGE COMPONENT
|
PAGE COMPONENT
|
||||||
══════════════════════════════════════════════ */
|
══════════════════════════════════════════════ */
|
||||||
export default function InteriorSample() {
|
export default function InteriorSample() {
|
||||||
const [scrolled, setScrolled] = useState(false);
|
const [scrolled, setScrolled] = useState(false);
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const [showTop, setShowTop] = useState(false);
|
||||||
const scrollSectionRef = useRef<HTMLDivElement>(null);
|
|
||||||
const textOverlayRef = useRef<HTMLDivElement>(null);
|
|
||||||
const framesRef = useRef<HTMLImageElement[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
/* ─── 스크롤 컨테이너: DashboardShell의 .main-content (overflow-y: auto)
|
|
||||||
window가 아닌 이 요소에서 scroll 이벤트가 발생함 ─── */
|
|
||||||
const scroller: HTMLElement =
|
const scroller: HTMLElement =
|
||||||
(document.querySelector('.main-content') as HTMLElement | null) ??
|
(document.querySelector('.main-content') as HTMLElement | null) ??
|
||||||
document.documentElement;
|
document.documentElement;
|
||||||
|
|
||||||
/* ── nav 투명 → 불투명 전환 ── */
|
const onNavScroll = () => {
|
||||||
const onNavScroll = () => setScrolled(scroller.scrollTop > 60);
|
setScrolled(scroller.scrollTop > 60);
|
||||||
|
setShowTop(scroller.scrollTop > 400);
|
||||||
|
};
|
||||||
scroller.addEventListener('scroll', onNavScroll, { passive: true });
|
scroller.addEventListener('scroll', onNavScroll, { passive: true });
|
||||||
|
|
||||||
/* ── Intersection reveal ── */
|
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(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => entries.forEach((e) => { if (e.isIntersecting) e.target.classList.add('au-visible'); }),
|
(entries) => entries.forEach((e) => { if (e.isIntersecting) e.target.classList.add('au-visible'); }),
|
||||||
{ threshold: 0.08, root: scroller === document.documentElement ? null : scroller }
|
{ threshold: 0.08, root: scroller === document.documentElement ? null : scroller }
|
||||||
);
|
);
|
||||||
document.querySelectorAll('.au-reveal').forEach((el) => observer.observe(el));
|
document.querySelectorAll('.au-reveal').forEach((el) => observer.observe(el));
|
||||||
|
|
||||||
/* ── WebP 프레임 프리로드 ── */
|
|
||||||
const frames: HTMLImageElement[] = new Array(FRAME_COUNT);
|
|
||||||
framesRef.current = frames;
|
|
||||||
let firstLoaded = false;
|
|
||||||
|
|
||||||
const drawFrame = (index: number) => {
|
|
||||||
const canvas = canvasRef.current;
|
|
||||||
const img = frames[Math.max(0, Math.min(FRAME_COUNT - 1, index))];
|
|
||||||
if (!canvas || !img?.complete || !img.naturalWidth) return;
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
if (!ctx) return;
|
|
||||||
const cw = canvas.width, ch = canvas.height;
|
|
||||||
// cover-fit: 비율 유지하며 캔버스를 꽉 채움
|
|
||||||
const scale = Math.max(cw / img.naturalWidth, ch / img.naturalHeight);
|
|
||||||
const dx = (cw - img.naturalWidth * scale) / 2;
|
|
||||||
const dy = (ch - img.naturalHeight * scale) / 2;
|
|
||||||
ctx.clearRect(0, 0, cw, ch);
|
|
||||||
ctx.drawImage(img, dx, dy, img.naturalWidth * scale, img.naturalHeight * scale);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let i = 0; i < FRAME_COUNT; i++) {
|
|
||||||
const img = new Image();
|
|
||||||
img.src = `/interior-frames/frame-${String(i + 1).padStart(3, '0')}.webp`;
|
|
||||||
img.onload = () => { if (!firstLoaded) { firstLoaded = true; drawFrame(0); } };
|
|
||||||
frames[i] = img;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── 캔버스 크기를 컨테이너에 맞춤 ── */
|
|
||||||
const resizeCanvas = () => {
|
|
||||||
const canvas = canvasRef.current;
|
|
||||||
if (!canvas) return;
|
|
||||||
// sticky 컨테이너의 실제 크기 사용
|
|
||||||
const parent = canvas.parentElement;
|
|
||||||
canvas.width = parent?.clientWidth ?? window.innerWidth;
|
|
||||||
canvas.height = parent?.clientHeight ?? window.innerHeight;
|
|
||||||
drawFrame(0);
|
|
||||||
};
|
|
||||||
resizeCanvas();
|
|
||||||
window.addEventListener('resize', resizeCanvas);
|
|
||||||
|
|
||||||
/* ── 스크롤 스크러빙 (frame-by-frame) ── */
|
|
||||||
let rafId = 0;
|
|
||||||
const onScrub = () => {
|
|
||||||
cancelAnimationFrame(rafId);
|
|
||||||
rafId = requestAnimationFrame(() => {
|
|
||||||
const section = scrollSectionRef.current;
|
|
||||||
if (!section) return;
|
|
||||||
|
|
||||||
// getBoundingClientRect()는 viewport 기준 → scroller가 .main-content여도 정상 작동
|
|
||||||
const rect = section.getBoundingClientRect();
|
|
||||||
const vh = scroller === document.documentElement
|
|
||||||
? window.innerHeight
|
|
||||||
: scroller.clientHeight;
|
|
||||||
const scrollable = section.offsetHeight - vh;
|
|
||||||
if (scrollable <= 0) return;
|
|
||||||
|
|
||||||
const progress = Math.max(0, Math.min(1, -rect.top / scrollable));
|
|
||||||
drawFrame(Math.floor(progress * (FRAME_COUNT - 1)));
|
|
||||||
|
|
||||||
/* 텍스트 오버레이: opacity + filter만 변경 (transform 금지) */
|
|
||||||
const overlayEl = textOverlayRef.current;
|
|
||||||
if (!overlayEl) return;
|
|
||||||
overlayEl.querySelectorAll<HTMLElement>('[data-milestone]').forEach((el) => {
|
|
||||||
const s = parseFloat(el.dataset.start ?? '0');
|
|
||||||
const e2 = parseFloat(el.dataset.end ?? '1');
|
|
||||||
const vis = progress >= s && progress < e2;
|
|
||||||
el.style.opacity = vis ? '1' : '0';
|
|
||||||
el.style.filter = vis ? 'blur(0px)' : 'blur(6px)';
|
|
||||||
});
|
|
||||||
|
|
||||||
/* 진행 바 */
|
|
||||||
const bar = overlayEl.querySelector<HTMLElement>('[data-progress-bar]');
|
|
||||||
if (bar) bar.style.width = `${progress * 100}%`;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
scroller.addEventListener('scroll', onScrub, { passive: true });
|
|
||||||
onScrub(); // 초기 상태 즉시 반영
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
scroller.removeEventListener('scroll', onNavScroll);
|
scroller.removeEventListener('scroll', onNavScroll);
|
||||||
scroller.removeEventListener('scroll', onScrub);
|
if (topBtn) topBtn.removeEventListener('click', scrollToTop);
|
||||||
window.removeEventListener('resize', resizeCanvas);
|
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
cancelAnimationFrame(rafId);
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/* ── 팔레트 상수 ── */
|
|
||||||
const GOLD = '#8B6914';
|
const GOLD = '#8B6914';
|
||||||
const DARK = '#1C1A17';
|
const DARK = '#1C1A17';
|
||||||
const SAGE = '#4E5C3E';
|
const SAGE = '#4E5C3E';
|
||||||
@@ -207,10 +128,9 @@ export default function InteriorSample() {
|
|||||||
|
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
/* ── 진입 애니메이션 ── */
|
|
||||||
@keyframes au-fadeUp {
|
@keyframes au-fadeUp {
|
||||||
from { opacity: 0; transform: translateY(2rem); filter: blur(4px); }
|
from { opacity: 0; transform: translateY(2rem); filter: blur(4px); }
|
||||||
to { opacity: 1; transform: translateY(0); filter: blur(0); }
|
to { opacity: 1; transform: none; filter: blur(0); }
|
||||||
}
|
}
|
||||||
@keyframes au-float {
|
@keyframes au-float {
|
||||||
0%,100% { transform: translateY(0); }
|
0%,100% { transform: translateY(0); }
|
||||||
@@ -221,41 +141,35 @@ export default function InteriorSample() {
|
|||||||
to { transform: translateX(-50%); }
|
to { transform: translateX(-50%); }
|
||||||
}
|
}
|
||||||
@keyframes au-pulse-gold {
|
@keyframes au-pulse-gold {
|
||||||
0%,100% { box-shadow: 0 0 0 0 rgba(139,105,20,0.3); }
|
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); }
|
50% { box-shadow: 0 0 0 10px rgba(139,105,20,0); }
|
||||||
}
|
}
|
||||||
@keyframes au-hero-drift {
|
|
||||||
from { transform: scale(1.07); }
|
|
||||||
to { transform: scale(1.0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── 스크롤 reveal ── */
|
/* 스크롤 reveal */
|
||||||
.au-reveal {
|
.au-reveal {
|
||||||
opacity: 0; transform: translateY(1.5rem); filter: blur(2px);
|
opacity: 0; transform: translateY(1.5rem); filter: blur(2px);
|
||||||
transition: opacity .7s cubic-bezier(.16,1,.3,1),
|
transition: opacity .7s cubic-bezier(.16,1,.3,1),
|
||||||
transform .7s cubic-bezier(.16,1,.3,1),
|
transform .7s cubic-bezier(.16,1,.3,1),
|
||||||
filter .7s cubic-bezier(.16,1,.3,1);
|
filter .7s cubic-bezier(.16,1,.3,1);
|
||||||
}
|
}
|
||||||
.au-reveal.au-visible { opacity: 1; transform: none; filter: blur(0); }
|
.au-reveal.au-visible { opacity: 1; transform: none; filter: none; }
|
||||||
.au-reveal:nth-child(2) { transition-delay: 80ms; }
|
.au-reveal:nth-child(2) { transition-delay: 80ms; }
|
||||||
.au-reveal:nth-child(3) { transition-delay: 160ms; }
|
.au-reveal:nth-child(3) { transition-delay: 160ms; }
|
||||||
.au-reveal:nth-child(4) { transition-delay: 240ms; }
|
.au-reveal:nth-child(4) { transition-delay: 240ms; }
|
||||||
|
|
||||||
/* ── 네비게이션 ── */
|
/* 네비 */
|
||||||
.au-nav-link {
|
.au-nav-link { color: #6B6456; text-decoration: none; font-size: 14px; font-weight: 500; transition: color .3s; }
|
||||||
color: #6B6456; text-decoration: none; font-size: 14px; font-weight: 500;
|
|
||||||
transition: color .3s;
|
|
||||||
}
|
|
||||||
.au-nav-link:hover { color: #1C1A17; }
|
.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 {
|
.au-btn-primary {
|
||||||
display: inline-flex; align-items: center; gap: 10px;
|
display: inline-flex; align-items: center; gap: 10px;
|
||||||
background: #1C1A17; color: #FAF8F5; border: none; border-radius: 100px;
|
background: #1C1A17; color: #FAF8F5; border: none; border-radius: 100px;
|
||||||
padding: 14px 28px 14px 20px; font-size: 15px; font-weight: 600;
|
padding: 14px 28px 14px 20px; font-size: 15px; font-weight: 600;
|
||||||
font-family: inherit; cursor: pointer; text-decoration: none;
|
font-family: inherit; cursor: pointer; text-decoration: none;
|
||||||
transition: transform .4s cubic-bezier(.16,1,.3,1),
|
transition: transform .4s cubic-bezier(.16,1,.3,1), box-shadow .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:hover { transform: scale(1.02); box-shadow: 0 12px 36px rgba(28,26,23,.25); }
|
||||||
.au-btn-primary:active { transform: scale(.98); }
|
.au-btn-primary:active { transform: scale(.98); }
|
||||||
@@ -270,64 +184,65 @@ export default function InteriorSample() {
|
|||||||
}
|
}
|
||||||
.au-btn-ghost:hover { border-color: rgba(28,26,23,.5); background: rgba(28,26,23,.04); }
|
.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-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-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-cell:hover .au-portfolio-img { transform: scale(1.04); }
|
||||||
.au-portfolio-overlay {
|
.au-portfolio-overlay {
|
||||||
position: absolute; inset: 0;
|
position: absolute; inset: 0;
|
||||||
background: linear-gradient(to top, rgba(28,26,23,.7) 0%, transparent 50%);
|
background: linear-gradient(to top, rgba(28,26,23,.7) 0%, transparent 55%);
|
||||||
opacity: 0; transition: opacity .4s cubic-bezier(.16,1,.3,1);
|
opacity: 0; transition: opacity .4s cubic-bezier(.16,1,.3,1);
|
||||||
display: flex; flex-direction: column; justify-content: flex-end; padding: 20px;
|
display: flex; flex-direction: column; justify-content: flex-end; padding: 22px;
|
||||||
}
|
}
|
||||||
.au-portfolio-cell:hover .au-portfolio-overlay { opacity: 1; }
|
.au-portfolio-cell:hover .au-portfolio-overlay { opacity: 1; }
|
||||||
|
|
||||||
/* ── 서비스 카드 ── */
|
/* 서비스 카드 */
|
||||||
.au-service-card {
|
.au-service-card {
|
||||||
background: white; border-radius: 20px; overflow: hidden;
|
background: white; border-radius: 20px; overflow: hidden;
|
||||||
box-shadow: 0 4px 32px rgba(28,26,23,.06);
|
box-shadow: 0 4px 32px rgba(28,26,23,.06);
|
||||||
transition: transform .4s cubic-bezier(.16,1,.3,1),
|
transition: transform .4s cubic-bezier(.16,1,.3,1), box-shadow .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); }
|
.au-service-card:hover { transform: translateY(-4px); box-shadow: 0 20px 60px rgba(28,26,23,.12); }
|
||||||
|
|
||||||
/* ── Double-Bezel 후기 카드 ── */
|
/* 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 { 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: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-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-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-hero-video {
|
.au-video-frame {
|
||||||
position: absolute; inset: 0;
|
position: relative;
|
||||||
width: 100%; height: 100%; object-fit: cover;
|
border-radius: 20px;
|
||||||
animation: au-hero-drift 10s cubic-bezier(.16,1,.3,1) forwards;
|
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/transform은 부모 flex가 담당.
|
|
||||||
JS는 opacity + filter만 변경한다. ── */
|
|
||||||
.au-scrub-text {
|
|
||||||
position: absolute; inset: 0;
|
position: absolute; inset: 0;
|
||||||
display: flex; flex-direction: column; justify-content: center;
|
border-radius: 20px;
|
||||||
padding: 0 80px;
|
box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
|
||||||
opacity: 0;
|
|
||||||
filter: blur(6px);
|
|
||||||
transition: opacity .65s cubic-bezier(.16,1,.3,1),
|
|
||||||
filter .65s cubic-bezier(.16,1,.3,1);
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
will-change: opacity, filter;
|
}
|
||||||
|
.au-video-frame video {
|
||||||
|
display: block; width: 100%; height: 100%; object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 진행 바 */
|
@media (max-width: 1024px) {
|
||||||
.au-scrub-bar-track { position:absolute; bottom:40px; left:80px; right:80px; height:1px; background:rgba(250,248,245,.1); }
|
.au-hero-grid { grid-template-columns: 1fr !important; }
|
||||||
.au-scrub-bar-fill { height:100%; width:0%; background:rgba(139,105,20,.7); transition:width .08s linear; }
|
.au-hero-video-col { display: none !important; }
|
||||||
|
.au-service-grid { grid-template-columns: 1fr !important; }
|
||||||
@media (max-width: 900px) {
|
.au-process-grid { grid-template-columns: 1fr 1fr !important; }
|
||||||
.au-scrub-text { padding: 0 32px; }
|
.au-portfolio-grid { grid-template-columns: 1fr 1fr !important; }
|
||||||
.au-scrub-bar-track { left:32px; right:32px; }
|
.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; }
|
||||||
}
|
}
|
||||||
`}} />
|
`}} />
|
||||||
|
|
||||||
@@ -346,32 +261,33 @@ export default function InteriorSample() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ══ NAV (sticky within .main-content) ══ */}
|
{/* ══ NAV ══ */}
|
||||||
<nav style={{
|
<nav style={{
|
||||||
position: 'sticky', top: 0, zIndex: 50, height: NAV_H,
|
position: 'sticky', top: 0, zIndex: 50, height: NAV_H,
|
||||||
display: 'flex', alignItems: 'center', padding: '0 48px',
|
display: 'flex', alignItems: 'center', padding: '0 48px',
|
||||||
background: scrolled ? 'rgba(250,248,245,.92)' : 'rgba(250,248,245,.15)',
|
background: scrolled ? 'rgba(250,248,245,.94)' : 'rgba(10,8,6,.55)',
|
||||||
backdropFilter: scrolled ? 'blur(20px)' : 'blur(8px)',
|
backdropFilter: 'blur(20px)',
|
||||||
borderBottom: scrolled ? '1px solid rgba(139,105,20,.1)' : '1px solid transparent',
|
borderBottom: scrolled ? '1px solid rgba(139,105,20,.1)' : '1px solid rgba(255,255,255,.06)',
|
||||||
transition: 'background .5s cubic-bezier(.16,1,.3,1), border-color .5s, backdrop-filter .5s',
|
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 style={{ maxWidth: 1200, margin: '0 auto', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ fontFamily: "'Playfair Display',Georgia,serif", fontSize: 20, fontWeight: 700, color: scrolled ? DARK : '#F5EDDF', letterSpacing: '-0.01em', transition: 'color .5s' }}>
|
<div style={{ fontFamily: "'Playfair Display',Georgia,serif", fontSize: 20, fontWeight: 700, color: scrolled ? DARK : '#F5EDDF', letterSpacing: '-0.01em', transition: 'color .45s' }}>
|
||||||
Aura Interior
|
Aura Interior
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 10, color: scrolled ? '#A0917C' : 'rgba(245,237,223,.6)', letterSpacing: '0.2em', textTransform: 'uppercase', marginTop: -2, transition: 'color .5s' }}>
|
<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>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 32 }}>
|
<div style={{ display: 'flex', gap: 32 }}>
|
||||||
{['포트폴리오', '서비스', '프로세스', '고객 후기', '상담 신청'].map((l) => (
|
{['포트폴리오', '서비스', '프로세스', '고객 후기', '상담 신청'].map((l) => (
|
||||||
<a key={l} href={`#${l}`} className="au-nav-link" style={{ color: scrolled ? '#6B6456' : 'rgba(245,237,223,.75)' }}>{l}</a>
|
<a key={l} href={`#${l}`} className={scrolled ? 'au-nav-link' : 'au-nav-link-light'}>{l}</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Link href="#contact" className="au-btn-primary" style={{ background: scrolled ? DARK : 'rgba(139,105,20,.9)', fontSize: 13, padding: '10px 20px 10px 16px' }}>
|
<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' }}>
|
<span style={{ width: 28, height: 28, borderRadius: '50%', background: 'rgba(250,248,245,.12)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
<ArrowRight color="#FAF8F5" />
|
<ArrowRight color="#FAF8F5" />
|
||||||
</span>
|
</span>
|
||||||
@@ -380,154 +296,88 @@ export default function InteriorSample() {
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* ══ HERO — 풀블리드 비디오 ══
|
{/* ══ HERO — 좌: 텍스트 / 우: 영상 ══ */}
|
||||||
height = 100dvh - BANNER_H - NAV_H → 화면을 정확히 채움 */}
|
|
||||||
<section style={{
|
<section style={{
|
||||||
position: 'relative',
|
background: DARK,
|
||||||
height: `calc(100dvh - ${BANNER_H}px - ${NAV_H}px)`,
|
minHeight: `calc(100dvh - ${BANNER_H}px - ${NAV_H}px)`,
|
||||||
minHeight: 480,
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '1fr 1fr',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
|
}} className="au-hero-grid">
|
||||||
}}>
|
|
||||||
{/* 배경 비디오 */}
|
|
||||||
<video className="au-hero-video" autoPlay muted loop playsInline src="/interior-hero.mp4" />
|
|
||||||
|
|
||||||
{/* 그라디언트 오버레이 */}
|
{/* 좌: 텍스트 */}
|
||||||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(110deg,rgba(10,8,6,.75) 0%,rgba(10,8,6,.35) 55%,rgba(10,8,6,.08) 100%)' }} />
|
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: '80px 64px 80px 80px', position: 'relative' }}>
|
||||||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top,rgba(10,8,6,.8) 0%,transparent 50%)' }} />
|
{/* 미세 노이즈 */}
|
||||||
|
<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={{ position: 'absolute', top: 28, right: 40, zIndex: 2, background: 'rgba(10,8,6,.45)', backdropFilter: 'blur(16px)', borderRadius: 100, padding: '6px 16px', border: '1px solid rgba(250,248,245,.12)' }}>
|
<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 }}>
|
||||||
<span style={{ fontSize: 11, color: '#F5EDDF', fontWeight: 600, letterSpacing: '0.08em', fontFamily: "'Playfair Display',serif", fontStyle: 'italic' }}>Award Winner 2024</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 히어로 텍스트 — 좌하단 */}
|
|
||||||
<div style={{ position: 'relative', zIndex: 2, padding: '0 80px 64px', animation: 'au-fadeUp 1s cubic-bezier(.16,1,.3,1) both' }}>
|
|
||||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, background: 'rgba(139,105,20,.2)', border: '1px solid rgba(139,105,20,.4)', borderRadius: 100, padding: '5px 14px', marginBottom: 20 }}>
|
|
||||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: GOLD }} />
|
<div style={{ width: 6, height: 6, borderRadius: '50%', background: GOLD }} />
|
||||||
<span style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.15em', textTransform: 'uppercase' }}>서울 기반 인테리어 디자인</span>
|
<span style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.15em', textTransform: 'uppercase' }}>서울 기반 인테리어 디자인</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 style={{ fontFamily: "'Playfair Display',Georgia,serif", fontSize: 'clamp(38px,5.5vw,72px)', fontWeight: 700, lineHeight: 1.12, color: '#F5EDDF', letterSpacing: '-0.02em', marginBottom: 18, wordBreak: 'keep-all', maxWidth: 640 }}>
|
<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 />
|
공간이 당신의<br />이야기를<br />
|
||||||
<em style={{ color: GOLD, fontStyle: 'italic' }}>담습니다.</em>
|
<em style={{ color: GOLD, fontStyle: 'italic' }}>담습니다.</em>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p style={{ fontSize: 16, color: 'rgba(245,237,223,.6)', lineHeight: 1.85, maxWidth: 420, marginBottom: 32, wordBreak: 'keep-all' }}>
|
<p style={{ fontSize: 16, color: 'rgba(245,237,223,.58)', lineHeight: 1.85, maxWidth: 400, marginBottom: 36, wordBreak: 'keep-all' }}>
|
||||||
아우라 인테리어는 12년간 247개의 공간을 완성했습니다.
|
아우라 인테리어는 12년간 247개의 공간을 완성했습니다.<br />
|
||||||
|
주거부터 상업 공간까지, 당신의 이야기가 머무는 곳을 만듭니다.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center', marginBottom: 40 }}>
|
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center', marginBottom: 48 }}>
|
||||||
<Link href="#contact" className="au-btn-primary" style={{ background: GOLD, color: DARK }}>
|
<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 }}>
|
<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} />
|
<ArrowRight color={DARK} />
|
||||||
</span>
|
</span>
|
||||||
무료 공간 상담 시작
|
무료 공간 상담 시작
|
||||||
</Link>
|
</Link>
|
||||||
<a href="#포트폴리오" style={{ display: 'inline-flex', alignItems: 'center', gap: 6, color: 'rgba(245,237,223,.65)', fontSize: 14, textDecoration: 'none', transition: 'color .3s' }}>
|
<a href="#포트폴리오" style={{ display: 'inline-flex', alignItems: 'center', gap: 6, color: 'rgba(245,237,223,.6)', fontSize: 14, textDecoration: 'none', transition: 'color .3s' }}
|
||||||
포트폴리오 보기 <ArrowRight color="rgba(245,237,223,.65)" />
|
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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 32, paddingTop: 24, borderTop: '1px solid rgba(250,248,245,.1)' }}>
|
{/* 미니 통계 */}
|
||||||
|
<div style={{ display: 'flex', gap: 36, paddingTop: 28, borderTop: '1px solid rgba(250,248,245,.08)' }}>
|
||||||
{[['247+','완공 프로젝트'],['4.96','고객 만족도'],['12년','디자인 경력']].map(([n, l]) => (
|
{[['247+','완공 프로젝트'],['4.96','고객 만족도'],['12년','디자인 경력']].map(([n, l]) => (
|
||||||
<div key={l}>
|
<div key={l}>
|
||||||
<div style={{ fontFamily: "'Playfair Display',serif", fontSize: 24, fontWeight: 700, color: '#F5EDDF' }}>{n}</div>
|
<div style={{ fontFamily: "'Playfair Display',serif", fontSize: 26, fontWeight: 700, color: '#F5EDDF' }}>{n}</div>
|
||||||
<div style={{ fontSize: 12, color: 'rgba(245,237,223,.45)', marginTop: 2 }}>{l}</div>
|
<div style={{ fontSize: 12, color: 'rgba(245,237,223,.4)', marginTop: 3 }}>{l}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</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: 64, right: 64, zIndex: 2, background: 'rgba(255,255,255,.97)', borderRadius: 14, padding: '14px 18px', boxShadow: '0 24px 64px rgba(10,8,6,.35)', animation: 'au-float 5s ease-in-out infinite', border: '1px solid rgba(139,105,20,.1)' }}>
|
<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: 6 }}>
|
<div style={{ display: 'flex', gap: 3, marginBottom: 5 }}>
|
||||||
{[1,2,3,4,5].map(i => <StarIcon key={i} filled />)}
|
{[1,2,3,4,5].map(i => <StarIcon key={i} filled />)}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, fontWeight: 700, color: DARK }}>최근 완공 · 한남동 단독주택</div>
|
<div style={{ fontSize: 12, fontWeight: 700, color: DARK }}>최근 완공 · 한남동</div>
|
||||||
<div style={{ fontSize: 11, color: '#A0917C', marginTop: 2 }}>고객 만족도 5.0 / 5.0</div>
|
<div style={{ fontSize: 11, color: '#A0917C', marginTop: 2 }}>만족도 5.0 / 5.0</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 스크롤 유도 */}
|
|
||||||
<div style={{ position: 'absolute', bottom: 20, left: '50%', transform: 'translateX(-50%)', zIndex: 2, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
|
|
||||||
<span style={{ fontSize: 9, color: 'rgba(245,237,223,.35)', letterSpacing: '0.2em', textTransform: 'uppercase' }}>Scroll</span>
|
|
||||||
<div style={{ width: 1, height: 28, background: 'linear-gradient(to bottom,rgba(245,237,223,.3),transparent)', animation: 'au-float 2.5s ease-in-out infinite' }} />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* ══ SCROLL SCRUBBING SECTION ══ */}
|
|
||||||
<section ref={scrollSectionRef} style={{ position: 'relative', height: '400vh', background: DARK }}>
|
|
||||||
{/* sticky 컨테이너: .main-content 기준으로 top:0 에 고정 */}
|
|
||||||
<div style={{ position: 'sticky', top: 0, height: '100dvh', overflow: 'hidden' }}>
|
|
||||||
|
|
||||||
{/* 캔버스 — frame-by-frame WebP */}
|
|
||||||
<canvas
|
|
||||||
ref={canvasRef}
|
|
||||||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', display: 'block' }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 그라디언트 오버레이 */}
|
|
||||||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to right, rgba(10,8,6,.65) 0%, rgba(10,8,6,.2) 55%, transparent 100%)' }} />
|
|
||||||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top, rgba(10,8,6,.4) 0%, transparent 35%)' }} />
|
|
||||||
|
|
||||||
{/* ── 텍스트 오버레이 컨테이너 ──
|
|
||||||
각 au-scrub-text는 position:absolute inset:0 flex 레이아웃으로 수직 중앙 정렬.
|
|
||||||
JS는 opacity + filter만 변경 — transform 불변 */}
|
|
||||||
<div ref={textOverlayRef} style={{ position: 'absolute', inset: 0, paddingTop: NAV_H }}>
|
|
||||||
|
|
||||||
{/* Milestone 0: 0–33% */}
|
|
||||||
<div className="au-scrub-text" data-milestone="0" data-start="0" data-end="0.33">
|
|
||||||
<p style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 18 }}>공간 철학</p>
|
|
||||||
<h2 style={{ fontFamily: "'Playfair Display',serif", fontSize: 'clamp(36px,5vw,64px)', fontWeight: 700, lineHeight: 1.12, color: '#F5EDDF', letterSpacing: '-0.02em', wordBreak: 'keep-all', maxWidth: 520 }}>
|
|
||||||
공간이 바뀌면<br />
|
|
||||||
<em style={{ color: GOLD, fontStyle: 'italic' }}>일상이 바뀝니다.</em>
|
|
||||||
</h2>
|
|
||||||
<p style={{ fontSize: 16, color: 'rgba(245,237,223,.55)', marginTop: 20, lineHeight: 1.85, maxWidth: 380, wordBreak: 'keep-all' }}>
|
|
||||||
우리는 단순한 시공을 넘어, 당신이 매일 경험하는 삶의 배경을 설계합니다.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Milestone 1: 33–66% */}
|
|
||||||
<div className="au-scrub-text" data-milestone="1" data-start="0.33" data-end="0.66">
|
|
||||||
<p style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 18 }}>실적</p>
|
|
||||||
<h2 style={{ fontFamily: "'Playfair Display',serif", fontSize: 'clamp(36px,5vw,64px)', fontWeight: 700, lineHeight: 1.12, color: '#F5EDDF', letterSpacing: '-0.02em', wordBreak: 'keep-all', maxWidth: 520 }}>
|
|
||||||
12년간<br />
|
|
||||||
<em style={{ color: GOLD, fontStyle: 'italic' }}>247개의 공간.</em>
|
|
||||||
</h2>
|
|
||||||
<div style={{ display: 'flex', gap: 40, marginTop: 32 }}>
|
|
||||||
{[['247+','완공 프로젝트'],['4.96','고객 만족도'],['98%','재의뢰율']].map(([n, l]) => (
|
|
||||||
<div key={l}>
|
|
||||||
<div style={{ fontFamily: "'Playfair Display',serif", fontSize: 36, fontWeight: 700, color: '#F5EDDF', lineHeight: 1 }}>{n}</div>
|
|
||||||
<div style={{ fontSize: 12, color: 'rgba(245,237,223,.45)', marginTop: 6 }}>{l}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Milestone 2: 66–100% */}
|
|
||||||
<div className="au-scrub-text" data-milestone="2" data-start="0.66" data-end="1.01" style={{ pointerEvents: 'auto' }}>
|
|
||||||
<p style={{ fontSize: 11, color: GOLD, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 18 }}>지금 시작하세요</p>
|
|
||||||
<h2 style={{ fontFamily: "'Playfair Display',serif", fontSize: 'clamp(36px,5vw,64px)', fontWeight: 700, lineHeight: 1.12, color: '#F5EDDF', letterSpacing: '-0.02em', wordBreak: 'keep-all', maxWidth: 520 }}>
|
|
||||||
당신의 공간<br />
|
|
||||||
<em style={{ color: GOLD, fontStyle: 'italic' }}>이야기를 시작하세요.</em>
|
|
||||||
</h2>
|
|
||||||
<Link href="#contact" className="au-btn-primary" style={{ background: GOLD, color: DARK, marginTop: 32, alignSelf: 'flex-start' }}>
|
|
||||||
<span style={{ width: 30, height: 30, borderRadius: '50%', background: 'rgba(28,26,23,.12)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
|
|
||||||
<ArrowRight color={DARK} />
|
|
||||||
</span>
|
|
||||||
무료 상담 신청
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 진행 바 */}
|
|
||||||
<div className="au-scrub-bar-track">
|
|
||||||
<div className="au-scrub-bar-fill" data-progress-bar />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 안내 레이블 */}
|
|
||||||
<div style={{ position: 'absolute', bottom: 40, right: 80, fontSize: 10, color: 'rgba(245,237,223,.2)', fontFamily: "'Playfair Display',serif", fontStyle: 'italic', letterSpacing: '0.06em' }}>
|
|
||||||
스크롤로 영상 탐색
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -545,7 +395,7 @@ export default function InteriorSample() {
|
|||||||
<a href="#" className="au-btn-ghost" style={{ flexShrink: 0 }}>전체 보기 <ArrowRight /></a>
|
<a href="#" className="au-btn-ghost" style={{ flexShrink: 0 }}>전체 보기 <ArrowRight /></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="au-reveal" style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr', gridTemplateRows: 'auto auto', gap: 14 }}>
|
<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 }}>
|
<div className="au-portfolio-cell" style={{ gridRow: 'span 2', minHeight: 580 }}>
|
||||||
<img src={portfolio[0].img} alt={portfolio[0].title} className="au-portfolio-img" />
|
<img src={portfolio[0].img} alt={portfolio[0].title} className="au-portfolio-img" />
|
||||||
<div className="au-portfolio-overlay">
|
<div className="au-portfolio-overlay">
|
||||||
@@ -563,7 +413,7 @@ export default function InteriorSample() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div className="au-portfolio-cell" style={{ gridColumn: 'span 2', minHeight: 280 }}>
|
<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" />
|
<img src={portfolio[4].img} alt={portfolio[4].title} className="au-portfolio-img" />
|
||||||
<div className="au-portfolio-overlay">
|
<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: 11, color: 'rgba(250,248,245,.6)', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: 4 }}>{portfolio[4].cat}</div>
|
||||||
@@ -587,7 +437,7 @@ export default function InteriorSample() {
|
|||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 48 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 48 }}>
|
||||||
{services.map((svc, i) => (
|
{services.map((svc, i) => (
|
||||||
<div key={svc.title} className="au-reveal au-service-card" style={{ display: 'grid', gridTemplateColumns: i % 2 === 0 ? '1fr 1.2fr' : '1.2fr 1fr', gap: 0 }}>
|
<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' }}>
|
<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" />
|
<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>
|
||||||
@@ -621,7 +471,7 @@ export default function InteriorSample() {
|
|||||||
상담부터 준공까지
|
상담부터 준공까지
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 2 }}>
|
<div className="au-process-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 2 }}>
|
||||||
{steps.map((s, i) => (
|
{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 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 className="au-step-num">{s.num}</div>
|
||||||
@@ -643,7 +493,7 @@ export default function InteriorSample() {
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, alignItems: 'start' }}>
|
<div className="au-testimony-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, alignItems: 'start' }}>
|
||||||
{testimonials.map((t, i) => (
|
{testimonials.map((t, i) => (
|
||||||
<div key={t.name} className="au-reveal au-testimony" style={{ marginTop: i === 1 ? 40 : 0 }}>
|
<div key={t.name} className="au-reveal au-testimony" style={{ marginTop: i === 1 ? 40 : 0 }}>
|
||||||
<div className="au-testimony-inner">
|
<div className="au-testimony-inner">
|
||||||
@@ -669,8 +519,9 @@ export default function InteriorSample() {
|
|||||||
|
|
||||||
<div className="au-reveal" style={{ marginTop: 72, overflow: 'hidden', borderTop: '1px solid rgba(139,105,20,.1)', paddingTop: 40 }}>
|
<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={{ fontSize: 11, color: '#A0917C', textAlign: 'center', letterSpacing: '0.15em', textTransform: 'uppercase', marginBottom: 24 }}>함께한 브랜드들</div>
|
||||||
<div style={{ display: 'flex', animation: 'au-marquee 20s linear infinite', width: 'fit-content', gap: 64 }}>
|
<div style={{ display: 'flex', animation: 'au-marquee 22s linear infinite', width: 'fit-content', gap: 64 }}>
|
||||||
{['에스프레소랩','루미너스','플로우캔버스','스텔라랩스','넥스트비전','브릿지웍스','에스프레소랩','루미너스','플로우캔버스','스텔라랩스','넥스트비전','브릿지웍스'].map((b, i) => (
|
{['에스프레소랩','루미너스','플로우캔버스','스텔라랩스','넥스트비전','브릿지웍스',
|
||||||
|
'에스프레소랩','루미너스','플로우캔버스','스텔라랩스','넥스트비전','브릿지웍스'].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>
|
<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>
|
||||||
@@ -734,6 +585,27 @@ export default function InteriorSample() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
{/* ══ SCROLL TO TOP ══ */}
|
||||||
|
<button
|
||||||
|
id="au-top-btn"
|
||||||
|
style={{
|
||||||
|
position: 'fixed', bottom: '2rem', 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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
/* ═══════════════════════════════════════
|
/* ═══════════════════════════════════════
|
||||||
@@ -167,14 +168,25 @@ function BookCard({ book, large = false }: { book: Book; large?: boolean }) {
|
|||||||
═══════════════════════════════════════ */
|
═══════════════════════════════════════ */
|
||||||
export default function ReadingPage() {
|
export default function ReadingPage() {
|
||||||
const [activeGenre, setActiveGenre] = useState('전체');
|
const [activeGenre, setActiveGenre] = useState('전체');
|
||||||
|
const [showTop, setShowTop] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
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(
|
const obs = new IntersectionObserver(
|
||||||
entries => entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('rd-visible'); }),
|
entries => entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('rd-visible'); }),
|
||||||
{ threshold: 0.1 }
|
{ threshold: 0.1, root: scroller === document.documentElement ? null : scroller }
|
||||||
);
|
);
|
||||||
document.querySelectorAll('.rd-reveal').forEach(el => obs.observe(el));
|
document.querySelectorAll('.rd-reveal').forEach(el => obs.observe(el));
|
||||||
return () => obs.disconnect();
|
return () => {
|
||||||
|
scroller.removeEventListener('scroll', onScroll);
|
||||||
|
obs.disconnect();
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const genres = ['전체', '소설', '고전', '자기계발', '과학', '심리학'];
|
const genres = ['전체', '소설', '고전', '자기계발', '과학', '심리학'];
|
||||||
@@ -304,6 +316,24 @@ export default function ReadingPage() {
|
|||||||
}
|
}
|
||||||
`}} />
|
`}} />
|
||||||
|
|
||||||
|
{/* ── 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="/services/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' }}>
|
<div className="rd-root rd-grain" style={{ position: 'relative' }}>
|
||||||
|
|
||||||
{/* ── NAV ── */}
|
{/* ── NAV ── */}
|
||||||
@@ -604,6 +634,30 @@ export default function ReadingPage() {
|
|||||||
<p style={{ fontSize: '0.75rem', color: '#4A4035', margin: 0 }}>책은 시간을 초월한 대화다.</p>
|
<p style={{ fontSize: '0.75rem', color: '#4A4035', margin: 0 }}>책은 시간을 초월한 대화다.</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</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: '2rem', 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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
759
app/services/website/samples/shopping/page.tsx
Normal file
759
app/services/website/samples/shopping/page.tsx
Normal file
@@ -0,0 +1,759 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════
|
||||||
|
DATA
|
||||||
|
═══════════════════════════════════════════ */
|
||||||
|
type Category = '전체' | '의류' | '액세서리' | '라이프';
|
||||||
|
|
||||||
|
const products = [
|
||||||
|
{ id: 1, name: 'Linen Over Shirt', sub: '린넨 오버셔츠', price: 89000, tag: '신상', cat: '의류' as Category, seed: 'mel01', badge: 'NEW' },
|
||||||
|
{ id: 2, name: 'Capsule Tote Bag', sub: '카프슐 토트백', price: 145000, tag: '베스트', cat: '액세서리' as Category, seed: 'mel02' },
|
||||||
|
{ id: 3, name: 'Wide Crop Pants', sub: '와이드 크롭 팬츠', price: 97000, cat: '의류' as Category, seed: 'mel03' },
|
||||||
|
{ id: 4, name: 'Knit Cardigan', sub: '니트 카디건', price: 125000, cat: '의류' as Category, seed: 'mel04', badge: 'BEST' },
|
||||||
|
{ id: 5, name: 'Ceramic Mug Set', sub: '세라믹 머그 세트', price: 58000, cat: '라이프' as Category, seed: 'mel05' },
|
||||||
|
{ id: 6, name: 'Brass Ring Set', sub: '브라스 링 세트', price: 42000, cat: '액세서리' as Category, seed: 'mel06' },
|
||||||
|
{ id: 7, name: 'Minimal Slip Dress', sub: '미니멀 슬립 드레스', price: 112000, cat: '의류' as Category, seed: 'mel07', badge: 'NEW' },
|
||||||
|
{ id: 8, name: 'Linen Cushion Cover', sub: '린넨 쿠션 커버', price: 35000, cat: '라이프' as Category, seed: 'mel08' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const reviews = [
|
||||||
|
{ name: '김민서', location: '서울', rating: 5, text: '소재부터 다릅니다. 린넨 셔츠 착용감이 정말 좋아요. 세탁 후에도 형태 유지가 잘 돼서 재구매할 것 같아요.', product: 'Linen Over Shirt' },
|
||||||
|
{ name: '이하은', location: '부산', rating: 5, text: '미니멀한 디자인인데 어디에나 잘 어울립니다. 포장도 너무 예쁘게 해주셔서 선물 받은 기분이었어요.', product: 'Capsule Tote Bag' },
|
||||||
|
{ name: '박지수', location: '대구', rating: 5, text: '세라믹 머그 세트 배송이 빠르고 파손 없이 왔어요. 질감이 정말 고급스러워서 매일 아침 쓰고 있어요.', product: 'Ceramic Mug Set' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const storyImages = [
|
||||||
|
{ seed: 'story1', aspect: '3/4' },
|
||||||
|
{ seed: 'story2', aspect: '1/1' },
|
||||||
|
{ seed: 'story3', aspect: '3/4' },
|
||||||
|
];
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════
|
||||||
|
CONSTANTS
|
||||||
|
═══════════════════════════════════════════ */
|
||||||
|
const BANNER_H = 40;
|
||||||
|
const NAV_H = 64;
|
||||||
|
|
||||||
|
const BEIGE = '#FAFAF8';
|
||||||
|
const DARK = '#1A1A1A';
|
||||||
|
const TAUPE = '#8B7355';
|
||||||
|
const MUTED = '#B5A898';
|
||||||
|
const SOFT = '#F3EFE9';
|
||||||
|
const WARM = '#C4A882';
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════
|
||||||
|
SVG ICONS
|
||||||
|
═══════════════════════════════════════════ */
|
||||||
|
const CartIcon = () => (
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/>
|
||||||
|
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
const HeartIcon = ({ filled = false }: { filled?: boolean }) => (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill={filled ? WARM : 'none'} stroke={filled ? WARM : 'currentColor'} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
const SearchIcon = () => (
|
||||||
|
<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="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
const StarFull = () => (
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill={WARM} stroke={WARM} strokeWidth="0">
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════
|
||||||
|
PRODUCT CARD
|
||||||
|
═══════════════════════════════════════════ */
|
||||||
|
function ProductCard({ product, large = false }: { product: typeof products[0]; large?: boolean }) {
|
||||||
|
const [liked, setLiked] = useState(false);
|
||||||
|
const [hovered, setHovered] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="ms-reveal"
|
||||||
|
style={{
|
||||||
|
background: SOFT, borderRadius: 16, overflow: 'hidden',
|
||||||
|
transition: 'transform 0.5s cubic-bezier(0.16,1,0.3,1), box-shadow 0.5s cubic-bezier(0.16,1,0.3,1)',
|
||||||
|
transform: hovered ? 'translateY(-6px)' : 'translateY(0)',
|
||||||
|
boxShadow: hovered ? '0 24px 64px rgba(26,26,26,0.12)' : '0 2px 12px rgba(26,26,26,0.04)',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => setHovered(true)}
|
||||||
|
onMouseLeave={() => setHovered(false)}
|
||||||
|
>
|
||||||
|
{/* Image */}
|
||||||
|
<div style={{ position: 'relative', overflow: 'hidden', aspectRatio: large ? '3/4' : '1/1' }}>
|
||||||
|
<img
|
||||||
|
src={`https://picsum.photos/seed/${product.seed}/480/600`}
|
||||||
|
alt={product.name}
|
||||||
|
style={{
|
||||||
|
width: '100%', height: '100%', objectFit: 'cover',
|
||||||
|
filter: 'brightness(0.95) saturate(0.75)',
|
||||||
|
transition: 'transform 0.7s cubic-bezier(0.16,1,0.3,1)',
|
||||||
|
transform: hovered ? 'scale(1.04)' : 'scale(1)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Overlay */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', inset: 0,
|
||||||
|
background: 'linear-gradient(to bottom, transparent 60%, rgba(26,26,26,0.15))',
|
||||||
|
opacity: hovered ? 1 : 0,
|
||||||
|
transition: 'opacity 0.4s',
|
||||||
|
}}/>
|
||||||
|
|
||||||
|
{/* Badge */}
|
||||||
|
{product.badge && (
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', top: 12, left: 12,
|
||||||
|
background: product.badge === 'NEW' ? DARK : WARM,
|
||||||
|
color: product.badge === 'NEW' ? BEIGE : DARK,
|
||||||
|
fontSize: 9, fontWeight: 800, letterSpacing: '0.1em',
|
||||||
|
padding: '3px 8px', borderRadius: 4,
|
||||||
|
}}>{product.badge}</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Like */}
|
||||||
|
<button
|
||||||
|
onClick={e => { e.stopPropagation(); setLiked(!liked); }}
|
||||||
|
style={{
|
||||||
|
position: 'absolute', top: 12, right: 12,
|
||||||
|
width: 34, height: 34, borderRadius: '50%',
|
||||||
|
background: 'rgba(250,250,248,0.9)', backdropFilter: 'blur(8px)',
|
||||||
|
border: 'none', cursor: 'pointer',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
opacity: hovered || liked ? 1 : 0,
|
||||||
|
transition: 'opacity 0.3s',
|
||||||
|
}}
|
||||||
|
aria-label="찜하기"
|
||||||
|
>
|
||||||
|
<HeartIcon filled={liked}/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Quick add */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', bottom: 0, left: 0, right: 0,
|
||||||
|
opacity: hovered ? 1 : 0,
|
||||||
|
transform: hovered ? 'translateY(0)' : 'translateY(8px)',
|
||||||
|
transition: 'opacity 0.35s, transform 0.35s cubic-bezier(0.16,1,0.3,1)',
|
||||||
|
}}>
|
||||||
|
<button style={{
|
||||||
|
width: '100%', padding: '14px',
|
||||||
|
background: 'rgba(26,26,26,0.88)', backdropFilter: 'blur(8px)',
|
||||||
|
border: 'none', cursor: 'pointer',
|
||||||
|
color: BEIGE, fontSize: 12, fontWeight: 700,
|
||||||
|
letterSpacing: '0.08em', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
|
||||||
|
}}>
|
||||||
|
<CartIcon/> 장바구니 담기
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Info */}
|
||||||
|
<div style={{ padding: '14px 16px 18px' }}>
|
||||||
|
<p style={{ fontSize: 10, color: MUTED, letterSpacing: '0.12em', textTransform: 'uppercase', marginBottom: 4, fontFamily: "'Cormorant Garamond', serif" }}>{product.cat}</p>
|
||||||
|
<h3 style={{ fontSize: 14, fontWeight: 600, color: DARK, marginBottom: 2, letterSpacing: '-0.01em', wordBreak: 'keep-all' }}>{product.name}</h3>
|
||||||
|
<p style={{ fontSize: 11, color: MUTED, marginBottom: 10, wordBreak: 'keep-all' }}>{product.sub}</p>
|
||||||
|
<p style={{ fontSize: 14, fontWeight: 700, color: DARK, letterSpacing: '-0.01em' }}>
|
||||||
|
{product.price.toLocaleString()}원
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════
|
||||||
|
MAIN PAGE
|
||||||
|
═══════════════════════════════════════════ */
|
||||||
|
export default function ShoppingPage() {
|
||||||
|
const [activeCategory, setActiveCategory] = useState<Category | '전체'>('전체');
|
||||||
|
const [scrolled, setScrolled] = useState(false);
|
||||||
|
const [showTop, setShowTop] = useState(false);
|
||||||
|
const [cartCount, setCartCount] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const scroller: HTMLElement =
|
||||||
|
(document.querySelector('.main-content') as HTMLElement | null) ??
|
||||||
|
document.documentElement;
|
||||||
|
|
||||||
|
const onScroll = () => {
|
||||||
|
setScrolled(scroller.scrollTop > 60);
|
||||||
|
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('ms-visible'); }),
|
||||||
|
{ threshold: 0.08, root: scroller === document.documentElement ? null : scroller }
|
||||||
|
);
|
||||||
|
document.querySelectorAll('.ms-reveal').forEach(el => obs.observe(el));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
scroller.removeEventListener('scroll', onScroll);
|
||||||
|
obs.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const categories: (Category | '전체')[] = ['전체', '의류', '액세서리', '라이프'];
|
||||||
|
const filtered = activeCategory === '전체' ? products : products.filter(p => p.cat === activeCategory);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ background: BEIGE, 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=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');
|
||||||
|
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
@keyframes ms-fadeUp {
|
||||||
|
from { opacity: 0; transform: translateY(2rem); filter: blur(4px); }
|
||||||
|
to { opacity: 1; transform: none; filter: none; }
|
||||||
|
}
|
||||||
|
@keyframes ms-marquee {
|
||||||
|
from { transform: translateX(0); }
|
||||||
|
to { transform: translateX(-50%); }
|
||||||
|
}
|
||||||
|
@keyframes ms-float {
|
||||||
|
0%,100% { transform: translateY(0) rotate(-2deg); }
|
||||||
|
50% { transform: translateY(-10px) rotate(-2deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* scroll reveal */
|
||||||
|
.ms-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);
|
||||||
|
}
|
||||||
|
.ms-reveal.ms-visible { opacity: 1; transform: none; filter: none; }
|
||||||
|
.ms-reveal:nth-child(2) { transition-delay: 80ms; }
|
||||||
|
.ms-reveal:nth-child(3) { transition-delay: 160ms; }
|
||||||
|
.ms-reveal:nth-child(4) { transition-delay: 240ms; }
|
||||||
|
.ms-reveal:nth-child(5) { transition-delay: 320ms; }
|
||||||
|
|
||||||
|
/* nav */
|
||||||
|
.ms-nav-link {
|
||||||
|
font-size: 13px; font-weight: 500; text-decoration: none;
|
||||||
|
color: ${MUTED}; letter-spacing: 0.03em;
|
||||||
|
transition: color 0.25s;
|
||||||
|
}
|
||||||
|
.ms-nav-link:hover { color: ${DARK}; }
|
||||||
|
|
||||||
|
/* category pill */
|
||||||
|
.ms-cat-btn {
|
||||||
|
border: 1px solid rgba(26,26,26,0.15); border-radius: 100px;
|
||||||
|
background: transparent; font-family: inherit; font-size: 12px;
|
||||||
|
font-weight: 600; letter-spacing: 0.05em; padding: 7px 18px;
|
||||||
|
cursor: pointer; color: ${MUTED}; transition: all 0.3s cubic-bezier(0.16,1,0.3,1);
|
||||||
|
}
|
||||||
|
.ms-cat-btn:hover { border-color: ${DARK}; color: ${DARK}; }
|
||||||
|
.ms-cat-btn.active { background: ${DARK}; color: ${BEIGE}; border-color: ${DARK}; }
|
||||||
|
|
||||||
|
/* marquee */
|
||||||
|
.ms-marquee-track { animation: ms-marquee 35s linear infinite; display: flex; }
|
||||||
|
|
||||||
|
/* scrollbar */
|
||||||
|
::-webkit-scrollbar { width: 4px; }
|
||||||
|
::-webkit-scrollbar-track { background: ${BEIGE}; }
|
||||||
|
::-webkit-scrollbar-thumb { background: rgba(139,115,85,0.25); border-radius: 2px; }
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.ms-hero-grid { grid-template-columns: 1fr !important; }
|
||||||
|
.ms-hero-image-col { display: none !important; }
|
||||||
|
.ms-story-grid { grid-template-columns: 1fr !important; }
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.ms-product-grid { grid-template-columns: 1fr 1fr !important; }
|
||||||
|
.ms-review-grid { grid-template-columns: 1fr !important; }
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.ms-product-grid { grid-template-columns: 1fr !important; }
|
||||||
|
}
|
||||||
|
`}} />
|
||||||
|
|
||||||
|
{/* ── BACK BANNER ── */}
|
||||||
|
<div style={{
|
||||||
|
background: 'linear-gradient(135deg, #1A1A1A, #2A2018)',
|
||||||
|
height: BANNER_H, display: 'flex', alignItems: 'center',
|
||||||
|
padding: '0 24px', gap: 12, flexShrink: 0,
|
||||||
|
}}>
|
||||||
|
<Link href="/services/website" style={{ color: 'rgba(196,168,130,0.7)', fontSize: 13, textDecoration: 'none', transition: 'color 0.2s' }}
|
||||||
|
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.color = WARM)}
|
||||||
|
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = 'rgba(196,168,130,0.7)')}>
|
||||||
|
← 홈페이지 제작 서비스로 돌아가기
|
||||||
|
</Link>
|
||||||
|
<span style={{ color: 'rgba(196,168,130,0.2)' }}>|</span>
|
||||||
|
<span style={{ color: 'rgba(196,168,130,0.5)', fontSize: 12, fontFamily: "'Cormorant Garamond', serif", fontStyle: 'italic' }}>
|
||||||
|
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,250,248,.94)' : 'transparent',
|
||||||
|
backdropFilter: scrolled ? 'blur(20px)' : 'none',
|
||||||
|
borderBottom: scrolled ? '1px solid rgba(26,26,26,0.08)' : '1px solid transparent',
|
||||||
|
transition: 'background .4s cubic-bezier(.16,1,.3,1), border-color .4s, backdrop-filter .4s',
|
||||||
|
}}>
|
||||||
|
<div style={{ maxWidth: 1200, margin: '0 auto', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
|
{/* Logo */}
|
||||||
|
<div>
|
||||||
|
<div style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 22, fontWeight: 700, color: DARK, letterSpacing: '0.05em' }}>
|
||||||
|
MELLOW
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 9, color: MUTED, letterSpacing: '0.25em', textTransform: 'uppercase', marginTop: -2 }}>
|
||||||
|
studio
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Links */}
|
||||||
|
<div style={{ display: 'flex', gap: 32 }}>
|
||||||
|
{['신상품', '의류', '액세서리', '라이프', '세일'].map(l => (
|
||||||
|
<a key={l} href="#products" className="ms-nav-link">{l}</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 20 }}>
|
||||||
|
<button style={{ background: 'none', border: 'none', cursor: 'pointer', color: MUTED, display: 'flex', padding: 4 }} aria-label="검색">
|
||||||
|
<SearchIcon/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setCartCount(c => c + 1)}
|
||||||
|
style={{ background: 'none', border: 'none', cursor: 'pointer', color: DARK, display: 'flex', position: 'relative', padding: 4 }}
|
||||||
|
aria-label="장바구니"
|
||||||
|
>
|
||||||
|
<CartIcon/>
|
||||||
|
{cartCount > 0 && (
|
||||||
|
<span style={{
|
||||||
|
position: 'absolute', top: -4, right: -4,
|
||||||
|
width: 16, height: 16, borderRadius: '50%',
|
||||||
|
background: DARK, color: BEIGE,
|
||||||
|
fontSize: 9, fontWeight: 800,
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
}}>{cartCount}</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* ── HERO ── */}
|
||||||
|
<section style={{
|
||||||
|
minHeight: `calc(100dvh - ${BANNER_H}px - ${NAV_H}px)`,
|
||||||
|
display: 'grid', gridTemplateColumns: '1fr 1fr',
|
||||||
|
overflow: 'hidden', background: SOFT,
|
||||||
|
}} className="ms-hero-grid">
|
||||||
|
|
||||||
|
{/* Left: Text */}
|
||||||
|
<div style={{
|
||||||
|
display: 'flex', flexDirection: 'column', justifyContent: 'center',
|
||||||
|
padding: '80px 64px 80px 80px', position: 'relative',
|
||||||
|
}}>
|
||||||
|
<div style={{ animation: 'ms-fadeUp 0.9s cubic-bezier(0.16,1,0.3,1) both' }}>
|
||||||
|
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, marginBottom: 28 }}>
|
||||||
|
<div style={{ width: 24, height: 1, background: WARM }}/>
|
||||||
|
<span style={{ fontSize: 10, color: TAUPE, letterSpacing: '0.2em', textTransform: 'uppercase', fontFamily: "'Cormorant Garamond', serif", fontWeight: 600 }}>
|
||||||
|
2024 SS Collection
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 style={{
|
||||||
|
fontFamily: "'Cormorant Garamond', serif",
|
||||||
|
fontSize: 'clamp(3rem, 5vw, 5.5rem)',
|
||||||
|
fontWeight: 700, lineHeight: 1.05,
|
||||||
|
color: DARK, marginBottom: 28,
|
||||||
|
letterSpacing: '-0.02em', wordBreak: 'keep-all',
|
||||||
|
}}>
|
||||||
|
Wear What<br/>
|
||||||
|
<span style={{ fontStyle: 'italic', color: TAUPE }}>Feels Right.</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p style={{ fontSize: 15, color: TAUPE, lineHeight: 1.85, marginBottom: 40, maxWidth: 380, wordBreak: 'keep-all' }}>
|
||||||
|
자연스러운 소재와 절제된 실루엣.<br/>
|
||||||
|
일상에 녹아드는 옷을 만듭니다.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
|
||||||
|
<a href="#products" style={{
|
||||||
|
display: 'inline-flex', alignItems: 'center', gap: 10,
|
||||||
|
background: DARK, color: BEIGE,
|
||||||
|
borderRadius: 100, padding: '14px 28px',
|
||||||
|
fontSize: 13, fontWeight: 700, textDecoration: 'none',
|
||||||
|
letterSpacing: '0.04em', transition: 'transform .4s cubic-bezier(.16,1,.3,1), box-shadow .4s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.transform = 'scale(1.02)'; (e.currentTarget as HTMLElement).style.boxShadow = '0 12px 36px rgba(26,26,26,0.2)'; }}
|
||||||
|
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.transform = ''; (e.currentTarget as HTMLElement).style.boxShadow = ''; }}>
|
||||||
|
컬렉션 보기 →
|
||||||
|
</a>
|
||||||
|
<a href="#story" style={{
|
||||||
|
display: 'inline-flex', alignItems: 'center', gap: 10,
|
||||||
|
background: 'transparent', color: DARK,
|
||||||
|
border: '1px solid rgba(26,26,26,0.2)', borderRadius: 100, padding: '14px 24px',
|
||||||
|
fontSize: 13, fontWeight: 500, textDecoration: 'none',
|
||||||
|
transition: 'border-color .3s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.borderColor = 'rgba(26,26,26,0.5)')}
|
||||||
|
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.borderColor = 'rgba(26,26,26,0.2)')}>
|
||||||
|
브랜드 스토리
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
|
<div style={{ display: 'flex', gap: 40, marginTop: 56, paddingTop: 40, borderTop: '1px solid rgba(26,26,26,0.08)' }}>
|
||||||
|
{[
|
||||||
|
{ num: '1,200+', label: '누적 고객' },
|
||||||
|
{ num: '4.9', label: '평균 평점' },
|
||||||
|
{ num: '98%', label: '재구매율' },
|
||||||
|
].map(s => (
|
||||||
|
<div key={s.label}>
|
||||||
|
<div style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 26, fontWeight: 700, color: DARK, letterSpacing: '-0.02em' }}>{s.num}</div>
|
||||||
|
<div style={{ fontSize: 11, color: MUTED, marginTop: 2, letterSpacing: '0.05em' }}>{s.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right: Image */}
|
||||||
|
<div className="ms-hero-image-col" style={{
|
||||||
|
position: 'relative', overflow: 'hidden',
|
||||||
|
}}>
|
||||||
|
{/* Main image */}
|
||||||
|
<img
|
||||||
|
src="https://picsum.photos/seed/mellohero/800/1100"
|
||||||
|
alt="Hero"
|
||||||
|
style={{
|
||||||
|
width: '100%', height: '100%', objectFit: 'cover',
|
||||||
|
filter: 'brightness(0.92) saturate(0.7)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Overlay */}
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to right, rgba(243,239,233,0.3) 0%, transparent 40%)' }}/>
|
||||||
|
|
||||||
|
{/* Floating badge */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', top: '15%', right: '8%',
|
||||||
|
background: 'rgba(250,250,248,0.9)', backdropFilter: 'blur(12px)',
|
||||||
|
border: '1px solid rgba(26,26,26,0.08)',
|
||||||
|
borderRadius: 14, padding: '14px 18px',
|
||||||
|
animation: 'ms-float 5s ease-in-out infinite',
|
||||||
|
}}>
|
||||||
|
<div style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 11, color: TAUPE, letterSpacing: '0.12em', textTransform: 'uppercase', marginBottom: 4 }}>
|
||||||
|
New Arrival
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 13, fontWeight: 700, color: DARK }}>Linen Over Shirt</div>
|
||||||
|
<div style={{ fontSize: 13, color: TAUPE, marginTop: 2 }}>89,000원</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Rating badge */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', bottom: '15%', left: '8%',
|
||||||
|
background: DARK, borderRadius: 12, padding: '12px 16px',
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'flex', gap: 3, marginBottom: 4 }}>
|
||||||
|
{[1,2,3,4,5].map(i => <StarFull key={i}/>)}
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 11, color: 'rgba(250,250,248,0.6)', letterSpacing: '0.05em' }}>4.9 · 리뷰 1,200+</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── MARQUEE ── */}
|
||||||
|
<div style={{ background: DARK, overflow: 'hidden', padding: '14px 0' }}>
|
||||||
|
<div className="ms-marquee-track" style={{ gap: 0 }}>
|
||||||
|
{[...Array(2)].map((_, i) => (
|
||||||
|
<div key={i} style={{ display: 'flex', gap: 0, whiteSpace: 'nowrap', flexShrink: 0 }}>
|
||||||
|
{['Natural Fabric', '무료 배송', 'Handmade Detail', '7일 무료 반품', 'Minimal Design', '친환경 패키지', 'Premium Quality'].map((t, j) => (
|
||||||
|
<span key={j} style={{ padding: '0 2rem', fontSize: '0.7rem', color: 'rgba(250,250,248,0.4)', letterSpacing: '0.15em', textTransform: 'uppercase' }}>
|
||||||
|
{t} <span style={{ color: 'rgba(196,168,130,0.3)', margin: '0 0.5rem' }}>·</span>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── PRODUCTS ── */}
|
||||||
|
<section id="products" style={{ padding: '80px 48px', maxWidth: 1280, margin: '0 auto' }}>
|
||||||
|
<div className="ms-reveal" style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 44, flexWrap: 'wrap', gap: 20 }}>
|
||||||
|
<div>
|
||||||
|
<p style={{ fontSize: 10, color: TAUPE, letterSpacing: '0.2em', textTransform: 'uppercase', marginBottom: 10, fontFamily: "'Cormorant Garamond', serif", fontWeight: 600 }}>Collection</p>
|
||||||
|
<h2 style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 'clamp(1.8rem, 3vw, 2.5rem)', fontWeight: 700, color: DARK, letterSpacing: '-0.02em' }}>
|
||||||
|
{activeCategory === '전체' ? 'All Items' : activeCategory}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||||
|
{categories.map(cat => (
|
||||||
|
<button
|
||||||
|
key={cat}
|
||||||
|
className={`ms-cat-btn${activeCategory === cat ? ' active' : ''}`}
|
||||||
|
onClick={() => setActiveCategory(cat)}
|
||||||
|
>
|
||||||
|
{cat}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ms-product-grid" style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(4, 1fr)',
|
||||||
|
gap: 20,
|
||||||
|
}}>
|
||||||
|
{filtered.map(product => (
|
||||||
|
<ProductCard key={product.id} product={product} large={product.id % 3 === 1}/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ms-reveal" style={{ textAlign: 'center', marginTop: 52 }}>
|
||||||
|
<button style={{
|
||||||
|
border: `1px solid rgba(26,26,26,0.2)`, background: 'transparent',
|
||||||
|
color: DARK, fontFamily: 'inherit', fontSize: 13,
|
||||||
|
fontWeight: 600, letterSpacing: '0.05em',
|
||||||
|
padding: '14px 40px', borderRadius: 100, cursor: 'pointer',
|
||||||
|
transition: 'background .3s, color .3s, border-color .3s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = DARK; (e.currentTarget as HTMLElement).style.color = BEIGE; }}
|
||||||
|
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = 'transparent'; (e.currentTarget as HTMLElement).style.color = DARK; }}>
|
||||||
|
더 보기 (24)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── BRAND STORY ── */}
|
||||||
|
<section id="story" style={{ padding: '80px 48px', background: SOFT }}>
|
||||||
|
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
|
||||||
|
<div className="ms-story-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 64, alignItems: 'center' }}>
|
||||||
|
|
||||||
|
{/* Images */}
|
||||||
|
<div style={{ display: 'flex', gap: 16 }}>
|
||||||
|
{storyImages.map((img, i) => (
|
||||||
|
<div key={img.seed} className="ms-reveal" style={{
|
||||||
|
flex: i === 1 ? '0.85' : '1',
|
||||||
|
aspectRatio: img.aspect,
|
||||||
|
borderRadius: 14, overflow: 'hidden',
|
||||||
|
alignSelf: i === 1 ? 'flex-end' : 'flex-start',
|
||||||
|
}}>
|
||||||
|
<img
|
||||||
|
src={`https://picsum.photos/seed/${img.seed}/400/600`}
|
||||||
|
alt={`Story ${i + 1}`}
|
||||||
|
style={{ width: '100%', height: '100%', objectFit: 'cover', filter: 'brightness(0.9) saturate(0.65)' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Text */}
|
||||||
|
<div className="ms-reveal" style={{ paddingLeft: 16 }}>
|
||||||
|
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, marginBottom: 28 }}>
|
||||||
|
<div style={{ width: 20, height: 1, background: WARM }}/>
|
||||||
|
<span style={{ fontSize: 10, color: TAUPE, letterSpacing: '0.2em', textTransform: 'uppercase', fontFamily: "'Cormorant Garamond', serif" }}>Our Story</span>
|
||||||
|
</div>
|
||||||
|
<h2 style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 'clamp(2rem, 3.5vw, 3rem)', fontWeight: 700, color: DARK, lineHeight: 1.15, marginBottom: 24, wordBreak: 'keep-all', letterSpacing: '-0.02em' }}>
|
||||||
|
자연에서 영감 받은<br/>
|
||||||
|
<span style={{ fontStyle: 'italic', color: TAUPE }}>일상의 옷</span>
|
||||||
|
</h2>
|
||||||
|
<p style={{ fontSize: 14, color: TAUPE, lineHeight: 1.9, marginBottom: 16, wordBreak: 'keep-all' }}>
|
||||||
|
MELLOW STUDIO는 2019년, 작은 공방에서 시작되었습니다. 우리가 만드는 옷은 트렌드를 쫓지 않습니다.
|
||||||
|
오래 입어도 질리지 않는 형태, 몸에 닿는 감촉 하나까지 신중하게 고릅니다.
|
||||||
|
</p>
|
||||||
|
<p style={{ fontSize: 14, color: TAUPE, lineHeight: 1.9, marginBottom: 36, wordBreak: 'keep-all' }}>
|
||||||
|
린넨, 코튼, 울 — 자연 소재만 사용합니다. 매 시즌 국내 장인과 협업해 제작하며,
|
||||||
|
소량 생산을 원칙으로 하기 때문에 옷 한 벌 한 벌에 담긴 공정이 있습니다.
|
||||||
|
</p>
|
||||||
|
<div style={{ display: 'flex', gap: 32 }}>
|
||||||
|
{[
|
||||||
|
{ num: '5년', label: '브랜드 히스토리' },
|
||||||
|
{ num: '100%', label: '천연 소재' },
|
||||||
|
{ num: '국내', label: '생산 및 패키지' },
|
||||||
|
].map(s => (
|
||||||
|
<div key={s.label}>
|
||||||
|
<div style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 22, fontWeight: 700, color: DARK }}>{s.num}</div>
|
||||||
|
<div style={{ fontSize: 11, color: MUTED, marginTop: 2, wordBreak: 'keep-all' }}>{s.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── REVIEWS ── */}
|
||||||
|
<section style={{ padding: '80px 48px', maxWidth: 1200, margin: '0 auto' }}>
|
||||||
|
<div className="ms-reveal" style={{ textAlign: 'center', marginBottom: 48 }}>
|
||||||
|
<p style={{ fontSize: 10, color: TAUPE, letterSpacing: '0.2em', textTransform: 'uppercase', marginBottom: 10, fontFamily: "'Cormorant Garamond', serif", fontWeight: 600 }}>Reviews</p>
|
||||||
|
<h2 style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 'clamp(1.8rem, 3vw, 2.5rem)', fontWeight: 700, color: DARK, letterSpacing: '-0.02em' }}>
|
||||||
|
고객 후기
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="ms-review-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 20 }}>
|
||||||
|
{reviews.map((rev, i) => (
|
||||||
|
<div key={i} className="ms-reveal" style={{
|
||||||
|
background: SOFT, borderRadius: 20, padding: '6px',
|
||||||
|
border: '1px solid rgba(26,26,26,0.08)',
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
background: BEIGE, borderRadius: 15, padding: '28px 24px',
|
||||||
|
boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.8)', height: '100%',
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'flex', gap: 3, marginBottom: 14 }}>
|
||||||
|
{[1,2,3,4,5].map(i => <StarFull key={i}/>)}
|
||||||
|
</div>
|
||||||
|
<p style={{ fontSize: 13, color: TAUPE, lineHeight: 1.75, marginBottom: 18, wordBreak: 'keep-all', fontStyle: 'italic' }}>
|
||||||
|
"{rev.text}"
|
||||||
|
</p>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<div>
|
||||||
|
<div style={{ fontSize: 13, fontWeight: 700, color: DARK }}>{rev.name}</div>
|
||||||
|
<div style={{ fontSize: 11, color: MUTED, marginTop: 2 }}>{rev.location}</div>
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
fontSize: 10, color: TAUPE, letterSpacing: '0.06em',
|
||||||
|
background: 'rgba(139,115,85,0.08)', borderRadius: 6, padding: '4px 10px',
|
||||||
|
wordBreak: 'keep-all', textAlign: 'right', maxWidth: 120,
|
||||||
|
}}>{rev.product}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── CTA BANNER ── */}
|
||||||
|
<section style={{ padding: '0 48px 80px' }}>
|
||||||
|
<div className="ms-reveal" style={{
|
||||||
|
maxWidth: 1200, margin: '0 auto',
|
||||||
|
background: DARK, borderRadius: 24, overflow: 'hidden',
|
||||||
|
display: 'grid', gridTemplateColumns: '1fr 1fr', minHeight: 320,
|
||||||
|
}}>
|
||||||
|
<div style={{ padding: '56px 64px', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
||||||
|
<p style={{ fontSize: 10, color: 'rgba(250,250,248,0.4)', letterSpacing: '0.2em', textTransform: 'uppercase', marginBottom: 16, fontFamily: "'Cormorant Garamond', serif" }}>
|
||||||
|
Newsletter
|
||||||
|
</p>
|
||||||
|
<h3 style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 32, fontWeight: 700, color: BEIGE, lineHeight: 1.2, marginBottom: 14, wordBreak: 'keep-all', letterSpacing: '-0.02em' }}>
|
||||||
|
새 컬렉션 소식을<br/>가장 먼저 받아보세요
|
||||||
|
</h3>
|
||||||
|
<p style={{ fontSize: 13, color: 'rgba(250,250,248,0.45)', marginBottom: 28, lineHeight: 1.7, wordBreak: 'keep-all' }}>
|
||||||
|
구독자에게는 신상품 사전 공개 및 10% 할인 쿠폰을 드립니다.
|
||||||
|
</p>
|
||||||
|
<div style={{ display: 'flex', gap: 10 }}>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
placeholder="이메일을 입력하세요"
|
||||||
|
style={{
|
||||||
|
flex: 1, padding: '12px 18px', borderRadius: 10,
|
||||||
|
background: 'rgba(255,255,255,0.07)', border: '1px solid rgba(255,255,255,0.1)',
|
||||||
|
color: BEIGE, fontSize: 13, fontFamily: 'inherit',
|
||||||
|
outline: 'none',
|
||||||
|
}}
|
||||||
|
onFocus={e => (e.currentTarget.style.borderColor = 'rgba(196,168,130,0.5)')}
|
||||||
|
onBlur={e => (e.currentTarget.style.borderColor = 'rgba(255,255,255,0.1)')}
|
||||||
|
/>
|
||||||
|
<button style={{
|
||||||
|
background: WARM, color: DARK, border: 'none', borderRadius: 10,
|
||||||
|
padding: '12px 22px', fontSize: 13, fontWeight: 700, cursor: 'pointer',
|
||||||
|
fontFamily: 'inherit', flexShrink: 0,
|
||||||
|
transition: 'transform .3s, box-shadow .3s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.transform = 'scale(1.02)'; }}
|
||||||
|
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.transform = ''; }}>
|
||||||
|
구독하기
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right image */}
|
||||||
|
<div style={{ position: 'relative', overflow: 'hidden' }}>
|
||||||
|
<img
|
||||||
|
src="https://picsum.photos/seed/melloCTA/600/400"
|
||||||
|
alt="Newsletter"
|
||||||
|
style={{ width: '100%', height: '100%', objectFit: 'cover', filter: 'brightness(0.5) saturate(0.5)' }}
|
||||||
|
/>
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to right, rgba(26,26,26,0.5), transparent)' }}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── FOOTER ── */}
|
||||||
|
<footer style={{ background: '#100F0D', padding: '48px 80px 36px', color: 'rgba(250,250,248,0.4)' }}>
|
||||||
|
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr 1fr', gap: 48, marginBottom: 48, flexWrap: 'wrap' }}>
|
||||||
|
<div>
|
||||||
|
<div style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: 22, fontWeight: 700, color: BEIGE, letterSpacing: '0.05em', marginBottom: 4 }}>MELLOW</div>
|
||||||
|
<div style={{ fontSize: 9, color: 'rgba(250,250,248,0.3)', letterSpacing: '0.25em', textTransform: 'uppercase', marginBottom: 16 }}>studio</div>
|
||||||
|
<p style={{ fontSize: 12, lineHeight: 1.8, wordBreak: 'keep-all', maxWidth: 240 }}>
|
||||||
|
자연 소재와 절제된 디자인으로 일상을 더 아름답게.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{[
|
||||||
|
{ title: 'Shop', links: ['신상품', '의류', '액세서리', '라이프', '세일'] },
|
||||||
|
{ title: 'Brand', links: ['브랜드 스토리', '지속가능성', '장인 협업', '룩북'] },
|
||||||
|
{ title: 'Support', links: ['공지사항', '배송·반품', 'FAQ', '문의하기'] },
|
||||||
|
].map(col => (
|
||||||
|
<div key={col.title}>
|
||||||
|
<div style={{ fontSize: 10, color: 'rgba(250,250,248,0.6)', letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 18, fontWeight: 700 }}>{col.title}</div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 11 }}>
|
||||||
|
{col.links.map(l => (
|
||||||
|
<a key={l} href="#" style={{ fontSize: 13, color: 'rgba(250,250,248,0.35)', textDecoration: 'none', transition: 'color .2s' }}
|
||||||
|
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.color = 'rgba(250,250,248,0.75)')}
|
||||||
|
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = 'rgba(250,250,248,0.35)')}>
|
||||||
|
{l}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div style={{ borderTop: '1px solid rgba(255,255,255,0.05)', paddingTop: 24, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 12 }}>
|
||||||
|
<p style={{ fontSize: 12 }}>© 2024 MELLOW STUDIO. All rights reserved.</p>
|
||||||
|
<div style={{ display: 'flex', gap: 20 }}>
|
||||||
|
{['개인정보처리방침', '이용약관', 'Instagram', 'Kakao'].map(l => (
|
||||||
|
<a key={l} href="#" style={{ fontSize: 12, color: 'rgba(250,250,248,0.3)', textDecoration: 'none', transition: 'color .2s' }}
|
||||||
|
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.color = 'rgba(250,250,248,0.6)')}
|
||||||
|
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = 'rgba(250,250,248,0.3)')}>
|
||||||
|
{l}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
{/* ── 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: '2rem', right: '2rem', zIndex: 200,
|
||||||
|
width: 48, height: 48, borderRadius: '50%',
|
||||||
|
background: DARK, border: 'none', cursor: 'pointer',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
boxShadow: '0 8px 32px rgba(26,26,26,0.25)',
|
||||||
|
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={BEIGE} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<polyline points="18 15 12 9 6 15"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user