feat: 라이트 테마 전면 전환 (흰 배경 + 네이버그린 #03C75A)
- 전역 CSS를 다크 테마 → 화이트 테마로 재작성, legacy warm/ink/sage 토큰을 네이버그린 팔레트로 원사이트 리매핑 - 홈 페이지: 모바일 앱 스타일 카테고리 그리드(매장검색 大 / 매장등록 小), 네이버그린 그라데이션 CTA, 하이라이트 재구성 - Layout: 상단 네비/하단 푸터 흰색 리뉴얼 + 모바일 고정 탭바(홈/매장/매칭/지원금) - 매장 검색 필터: 상태에 '거래 완료' (CLOSED) 추가, 상태별 컬러 배지 - 매물 상세: 직방/다방 스타일 사진 우선 레이아웃 (히어로 + 썸네일 스트립), 핵심 KPI 타일, 사이드바 액션 - 매칭/가격 정보 등 레거시 warm/ink 클래스는 CSS override로 라이트화 - 더미 매장 16건 + 매칭 요청 16건 시드 스크립트 추가 (seeds/demo-data.ts, pnpm prisma:seed:demo) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ export function AuthButtons({ session }: AuthButtonsProps) {
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Link
|
<Link
|
||||||
href="/auth/login"
|
href="/auth/login"
|
||||||
className="text-sm font-medium text-cloud-2 hover:text-cloud-0 transition-colors"
|
className="text-sm font-medium text-ink-light transition-colors hover:text-ink"
|
||||||
>
|
>
|
||||||
로그인
|
로그인
|
||||||
</Link>
|
</Link>
|
||||||
@@ -37,9 +37,8 @@ export function AuthButtons({ session }: AuthButtonsProps) {
|
|||||||
href="/auth/register"
|
href="/auth/register"
|
||||||
className="rounded-full px-4 py-1.5 text-sm font-semibold text-white transition-transform hover:-translate-y-0.5"
|
className="rounded-full px-4 py-1.5 text-sm font-semibold text-white transition-transform hover:-translate-y-0.5"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background: 'linear-gradient(135deg,#03C75A 0%,#02A149 55%,#018f40 100%)',
|
||||||
'linear-gradient(135deg, #6366f1 0%, #a855f7 50%, #ec4899 100%)',
|
boxShadow: '0 8px 20px -6px rgba(3, 199, 90, 0.5)',
|
||||||
boxShadow: '0 6px 20px -6px rgba(168, 85, 247, 0.55)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
회원가입
|
회원가입
|
||||||
@@ -50,15 +49,16 @@ export function AuthButtons({ session }: AuthButtonsProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<span className="text-sm text-cloud-2">
|
<span className="text-sm text-ink-light">
|
||||||
{session.user.name}
|
{session.user.name}
|
||||||
<span className="ml-1.5 font-mono text-[10px] text-cloud-4">
|
<span className="ml-1.5 font-mono text-[10px] text-ink-muted">
|
||||||
({ROLE_LABELS[session.user.primaryRole] || session.user.primaryRole})
|
({ROLE_LABELS[session.user.primaryRole] || session.user.primaryRole})
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => signOut({ callbackUrl: '/' })}
|
onClick={() => signOut({ callbackUrl: '/' })}
|
||||||
className="rounded-full border border-white/10 px-3 py-1.5 text-sm text-cloud-2 transition-colors hover:border-white/25 hover:bg-white/[0.05] hover:text-cloud-0"
|
className="rounded-full border border-line px-3 py-1.5 text-sm text-ink-light transition-colors hover:border-naver hover:text-naver"
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
>
|
>
|
||||||
로그아웃
|
로그아웃
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
+326
-273
@@ -8,60 +8,81 @@
|
|||||||
--font-serif: 'Instrument Serif', Georgia, serif;
|
--font-serif: 'Instrument Serif', Georgia, serif;
|
||||||
--font-mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', monospace;
|
--font-mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', monospace;
|
||||||
|
|
||||||
/* Legacy warm palette — still referenced on sub-pages (blog, stores, about, etc.) */
|
/* Naver Green signature system */
|
||||||
--color-warm-50: #fefcf9;
|
--color-naver: #03C75A;
|
||||||
--color-warm-100: #fdf6ec;
|
--color-naver-dark: #02A149;
|
||||||
--color-warm-200: #f9e8cf;
|
--color-naver-deep: #018f40;
|
||||||
--color-warm-300: #f2d1a5;
|
--color-naver-light: #2BD673;
|
||||||
--color-warm-400: #e8b06e;
|
--color-naver-soft: #E4F9EC;
|
||||||
--color-warm-500: #d4874a;
|
--color-naver-tint: #F2FBF6;
|
||||||
--color-warm-600: #b8622e;
|
|
||||||
--color-warm-700: #8e4a22;
|
|
||||||
--color-warm-800: #6b3a1e;
|
|
||||||
--color-warm-900: #3d2213;
|
|
||||||
--color-ink: #1a1410;
|
|
||||||
--color-ink-light: #4a4035;
|
|
||||||
--color-ink-muted: #8a7e72;
|
|
||||||
--color-sage-500: #6b8f71;
|
|
||||||
--color-sage-600: #527a58;
|
|
||||||
|
|
||||||
/* invyone-inspired dark foundation */
|
/* Gradient complements */
|
||||||
--color-night-0: #05050f;
|
--color-mint: #00C4A7;
|
||||||
--color-night-1: #0a0a18;
|
--color-lime-accent: #84E1A1;
|
||||||
--color-night-2: #10101f;
|
--color-emerald: #0AB070;
|
||||||
--color-night-3: #1a1a2e;
|
|
||||||
--color-night-4: #252540;
|
|
||||||
--color-night-5: #353848;
|
|
||||||
--color-cloud-0: #ffffff;
|
|
||||||
--color-cloud-1: rgba(255, 255, 255, 0.92);
|
|
||||||
--color-cloud-2: rgba(255, 255, 255, 0.68);
|
|
||||||
--color-cloud-3: rgba(255, 255, 255, 0.48);
|
|
||||||
--color-cloud-4: rgba(255, 255, 255, 0.28);
|
|
||||||
|
|
||||||
/* Rainbow accent palette */
|
/* Neutral (white-first) palette */
|
||||||
--color-iris: #6366f1;
|
--color-ink: #0F1D17; /* near-black text */
|
||||||
--color-iris-light: #818cf8;
|
--color-ink-light: #3E4A44; /* secondary text */
|
||||||
--color-violet: #a855f7;
|
--color-ink-muted: #7B8581; /* muted text */
|
||||||
--color-violet-light: #c084fc;
|
--color-line: #E4E8E6; /* subtle border */
|
||||||
--color-rose: #ec4899;
|
--color-line-soft: #EFF2F0; /* softer border */
|
||||||
--color-rose-light: #f472b6;
|
--color-surface: #FFFFFF;
|
||||||
--color-tangerine: #f97316;
|
--color-surface-soft: #F7FAF8;
|
||||||
--color-tangerine-light: #fb923c;
|
--color-surface-mute: #F0F5F2;
|
||||||
--color-azure: #60a5fa;
|
|
||||||
--color-lime: #22c55e;
|
/* Legacy warm palette remap → light neutrals so old pages stay readable */
|
||||||
--color-lime-light: #34d399;
|
--color-warm-50: #ffffff;
|
||||||
--color-amber: #fbbf24;
|
--color-warm-100: #F7FAF8;
|
||||||
|
--color-warm-200: #EFF2F0;
|
||||||
|
--color-warm-300: #E4E8E6;
|
||||||
|
--color-warm-400: #84E1A1;
|
||||||
|
--color-warm-500: #03C75A;
|
||||||
|
--color-warm-600: #02A149;
|
||||||
|
--color-warm-700: #018f40;
|
||||||
|
--color-warm-800: #0F1D17;
|
||||||
|
--color-warm-900: #0A1410;
|
||||||
|
|
||||||
|
--color-sage-500: #03C75A;
|
||||||
|
--color-sage-600: #02A149;
|
||||||
|
|
||||||
|
/* Legacy dark night palette kept as neutrals (in case referenced) */
|
||||||
|
--color-night-0: #ffffff;
|
||||||
|
--color-night-1: #F7FAF8;
|
||||||
|
--color-night-2: #EFF2F0;
|
||||||
|
--color-night-3: #E4E8E6;
|
||||||
|
--color-night-4: #D3D9D6;
|
||||||
|
--color-night-5: #B6BEB9;
|
||||||
|
--color-cloud-0: #0F1D17;
|
||||||
|
--color-cloud-1: rgba(15, 29, 23, 0.92);
|
||||||
|
--color-cloud-2: rgba(15, 29, 23, 0.68);
|
||||||
|
--color-cloud-3: rgba(15, 29, 23, 0.48);
|
||||||
|
--color-cloud-4: rgba(15, 29, 23, 0.32);
|
||||||
|
|
||||||
|
/* Accent palette remapped toward green family */
|
||||||
|
--color-iris: #03C75A;
|
||||||
|
--color-iris-light: #2BD673;
|
||||||
|
--color-violet: #00C4A7;
|
||||||
|
--color-violet-light: #4FD9C3;
|
||||||
|
--color-rose: #0AB070;
|
||||||
|
--color-rose-light: #34D399;
|
||||||
|
--color-tangerine: #84E1A1;
|
||||||
|
--color-tangerine-light: #A5EBB8;
|
||||||
|
--color-azure: #34D399;
|
||||||
|
--color-lime: #03C75A;
|
||||||
|
--color-lime-light: #2BD673;
|
||||||
|
--color-amber: #02A149;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
* Global body defaults
|
* Global body defaults — LIGHT theme
|
||||||
* ----------------------------------------------------------------- */
|
* ----------------------------------------------------------------- */
|
||||||
html {
|
html {
|
||||||
background: #05050f;
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
background: #05050f;
|
background: #ffffff;
|
||||||
color: rgba(255, 255, 255, 0.92);
|
color: #0F1D17;
|
||||||
font-feature-settings: "cv11", "ss01";
|
font-feature-settings: "cv11", "ss01";
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
@@ -69,8 +90,8 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
background: rgba(168, 85, 247, 0.35);
|
background: rgba(3, 199, 90, 0.22);
|
||||||
color: #ffffff;
|
color: #0F1D17;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
@@ -102,8 +123,8 @@ body {
|
|||||||
50% { background-position: 100% 50%; }
|
50% { background-position: 100% 50%; }
|
||||||
}
|
}
|
||||||
@keyframes pulse-glow {
|
@keyframes pulse-glow {
|
||||||
0%, 100% { opacity: 0.55; transform: scale(1); }
|
0%, 100% { opacity: 0.45; transform: scale(1); }
|
||||||
50% { opacity: 0.95; transform: scale(1.07); }
|
50% { opacity: 0.85; transform: scale(1.06); }
|
||||||
}
|
}
|
||||||
@keyframes marquee {
|
@keyframes marquee {
|
||||||
0% { transform: translateX(0); }
|
0% { transform: translateX(0); }
|
||||||
@@ -134,94 +155,88 @@ body {
|
|||||||
.animate-spin-slow { animation: spin-slow 30s linear infinite; }
|
.animate-spin-slow { animation: spin-slow 30s linear infinite; }
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
* Gradient / text utilities
|
* Gradient / text utilities — Naver green family
|
||||||
* ----------------------------------------------------------------- */
|
* ----------------------------------------------------------------- */
|
||||||
.text-grad-primary {
|
.text-grad-primary {
|
||||||
background: linear-gradient(135deg, #6366f1 0%, #a855f7 40%, #ec4899 70%, #f97316 100%);
|
background: linear-gradient(135deg, #03C75A 0%, #00C4A7 55%, #0AB070 100%);
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
}
|
}
|
||||||
.text-grad-subtle {
|
.text-grad-subtle {
|
||||||
background: linear-gradient(120deg, #818cf8 0%, #c084fc 30%, #f472b6 60%, #fb923c 100%);
|
background: linear-gradient(120deg, #2BD673 0%, #4FD9C3 50%, #84E1A1 100%);
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
}
|
}
|
||||||
.text-grad-rainbow {
|
.text-grad-rainbow {
|
||||||
background: linear-gradient(120deg, #60a5fa, #c084fc, #f472b6, #fb923c, #fbbf24, #34d399, #60a5fa);
|
background: linear-gradient(120deg, #03C75A, #00C4A7, #2BD673, #0AB070, #84E1A1, #03C75A);
|
||||||
background-size: 200% auto;
|
background-size: 200% auto;
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
animation: shimmer 8s linear infinite;
|
animation: shimmer 10s linear infinite;
|
||||||
}
|
|
||||||
.text-grad-iris {
|
|
||||||
background: linear-gradient(135deg, #818cf8 0%, #6366f1 50%, #4f46e5 100%);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
.text-grad-rose {
|
|
||||||
background: linear-gradient(135deg, #f472b6 0%, #ec4899 50%, #db2777 100%);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
color: transparent;
|
|
||||||
}
|
}
|
||||||
|
.text-grad-iris,
|
||||||
|
.text-grad-rose,
|
||||||
.text-grad-tangerine {
|
.text-grad-tangerine {
|
||||||
background: linear-gradient(135deg, #fbbf24 0%, #f97316 50%, #ea580c 100%);
|
background: linear-gradient(135deg, #03C75A 0%, #00C4A7 60%, #0AB070 100%);
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-grad-primary {
|
||||||
|
background: linear-gradient(135deg, #03C75A 0%, #00C4A7 55%, #0AB070 100%);
|
||||||
|
}
|
||||||
|
.bg-grad-soft {
|
||||||
|
background: linear-gradient(135deg, #E4F9EC 0%, #F2FBF6 60%, #FFFFFF 100%);
|
||||||
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
* Backgrounds / patterns
|
* Backgrounds / patterns (light)
|
||||||
* ----------------------------------------------------------------- */
|
* ----------------------------------------------------------------- */
|
||||||
.bg-grid {
|
.bg-grid {
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(to right, rgba(255, 255, 255, 0.045) 1px, transparent 1px),
|
linear-gradient(to right, rgba(15, 29, 23, 0.04) 1px, transparent 1px),
|
||||||
linear-gradient(to bottom, rgba(255, 255, 255, 0.045) 1px, transparent 1px);
|
linear-gradient(to bottom, rgba(15, 29, 23, 0.04) 1px, transparent 1px);
|
||||||
background-size: 48px 48px;
|
background-size: 48px 48px;
|
||||||
}
|
}
|
||||||
.bg-grid-animated {
|
.bg-grid-animated {
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(to right, rgba(255, 255, 255, 0.045) 1px, transparent 1px),
|
linear-gradient(to right, rgba(3, 199, 90, 0.07) 1px, transparent 1px),
|
||||||
linear-gradient(to bottom, rgba(255, 255, 255, 0.045) 1px, transparent 1px);
|
linear-gradient(to bottom, rgba(3, 199, 90, 0.07) 1px, transparent 1px);
|
||||||
background-size: 48px 48px;
|
background-size: 48px 48px;
|
||||||
animation: grid-drift 20s linear infinite;
|
animation: grid-drift 20s linear infinite;
|
||||||
}
|
}
|
||||||
.bg-dot {
|
.bg-dot {
|
||||||
background-image: radial-gradient(circle, rgba(255, 255, 255, 0.1) 1px, transparent 1px);
|
background-image: radial-gradient(circle, rgba(3, 199, 90, 0.18) 1px, transparent 1px);
|
||||||
background-size: 24px 24px;
|
background-size: 24px 24px;
|
||||||
}
|
}
|
||||||
.bg-radial-fade {
|
.bg-radial-fade {
|
||||||
background: radial-gradient(ellipse at center, transparent 0%, #05050f 75%);
|
background: radial-gradient(ellipse at center, transparent 0%, #ffffff 75%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Glow orbs */
|
/* Glow orbs — green family */
|
||||||
.orb {
|
.orb {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
filter: blur(80px);
|
filter: blur(90px);
|
||||||
opacity: 0.55;
|
opacity: 0.35;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
animation: mesh-float 18s ease-in-out infinite, pulse-glow 9s ease-in-out infinite;
|
animation: mesh-float 18s ease-in-out infinite, pulse-glow 9s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
.orb-iris { background: radial-gradient(circle, #6366f1 0%, transparent 70%); }
|
.orb-iris { background: radial-gradient(circle, #03C75A 0%, transparent 70%); }
|
||||||
.orb-violet { background: radial-gradient(circle, #a855f7 0%, transparent 70%); }
|
.orb-violet { background: radial-gradient(circle, #00C4A7 0%, transparent 70%); }
|
||||||
.orb-rose { background: radial-gradient(circle, #ec4899 0%, transparent 70%); }
|
.orb-rose { background: radial-gradient(circle, #2BD673 0%, transparent 70%); }
|
||||||
.orb-tangerine{ background: radial-gradient(circle, #f97316 0%, transparent 70%); }
|
.orb-tangerine { background: radial-gradient(circle, #84E1A1 0%, transparent 70%); }
|
||||||
.orb-azure { background: radial-gradient(circle, #60a5fa 0%, transparent 70%); }
|
.orb-azure { background: radial-gradient(circle, #4FD9C3 0%, transparent 70%); }
|
||||||
.orb-lime { background: radial-gradient(circle, #34d399 0%, transparent 70%); }
|
.orb-lime { background: radial-gradient(circle, #03C75A 0%, transparent 70%); }
|
||||||
|
|
||||||
/* Legacy mesh-blob preserved for other pages */
|
|
||||||
.mesh-blob {
|
.mesh-blob {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -231,27 +246,24 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
* Glass / gradient-border cards
|
* Card surfaces (light glass)
|
||||||
* ----------------------------------------------------------------- */
|
* ----------------------------------------------------------------- */
|
||||||
.glass-dark {
|
.glass-dark,
|
||||||
background: rgba(16, 16, 31, 0.6);
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
||||||
}
|
|
||||||
.glass-darker {
|
.glass-darker {
|
||||||
background: rgba(10, 10, 24, 0.72);
|
background: rgba(255, 255, 255, 0.82);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(18px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(18px);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
border: 1px solid rgba(15, 29, 23, 0.06);
|
||||||
|
box-shadow: 0 6px 30px -18px rgba(15, 29, 23, 0.18);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grad-border {
|
.grad-border {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: rgba(16, 16, 31, 0.65);
|
background: #ffffff;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
|
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
|
||||||
background 0.4s ease;
|
box-shadow 0.4s ease;
|
||||||
|
box-shadow: 0 4px 18px -10px rgba(15, 29, 23, 0.12);
|
||||||
}
|
}
|
||||||
.grad-border::before {
|
.grad-border::before {
|
||||||
content: '';
|
content: '';
|
||||||
@@ -260,10 +272,9 @@ body {
|
|||||||
padding: 1px;
|
padding: 1px;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
background: linear-gradient(135deg,
|
background: linear-gradient(135deg,
|
||||||
rgba(99, 102, 241, 0.5) 0%,
|
rgba(3, 199, 90, 0.55) 0%,
|
||||||
rgba(168, 85, 247, 0.35) 35%,
|
rgba(0, 196, 167, 0.38) 50%,
|
||||||
rgba(236, 72, 153, 0.25) 65%,
|
rgba(10, 176, 112, 0.30) 100%);
|
||||||
rgba(249, 115, 22, 0.18) 100%);
|
|
||||||
-webkit-mask:
|
-webkit-mask:
|
||||||
linear-gradient(#000 0 0) content-box,
|
linear-gradient(#000 0 0) content-box,
|
||||||
linear-gradient(#000 0 0);
|
linear-gradient(#000 0 0);
|
||||||
@@ -277,14 +288,13 @@ body {
|
|||||||
}
|
}
|
||||||
.grad-border:hover {
|
.grad-border:hover {
|
||||||
transform: translateY(-6px);
|
transform: translateY(-6px);
|
||||||
background: rgba(24, 24, 48, 0.8);
|
box-shadow: 0 16px 40px -18px rgba(3, 199, 90, 0.35);
|
||||||
}
|
}
|
||||||
.grad-border:hover::before {
|
.grad-border:hover::before {
|
||||||
background: linear-gradient(135deg,
|
background: linear-gradient(135deg,
|
||||||
rgba(99, 102, 241, 0.9) 0%,
|
rgba(3, 199, 90, 0.95) 0%,
|
||||||
rgba(168, 85, 247, 0.75) 35%,
|
rgba(0, 196, 167, 0.75) 50%,
|
||||||
rgba(236, 72, 153, 0.6) 65%,
|
rgba(10, 176, 112, 0.60) 100%);
|
||||||
rgba(249, 115, 22, 0.45) 100%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
@@ -292,51 +302,53 @@ body {
|
|||||||
* ----------------------------------------------------------------- */
|
* ----------------------------------------------------------------- */
|
||||||
.btn-grad {
|
.btn-grad {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(135deg, #6366f1 0%, #a855f7 50%, #ec4899 100%);
|
background: linear-gradient(135deg, #03C75A 0%, #00C4A7 60%, #0AB070 100%);
|
||||||
color: white;
|
color: #ffffff;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
padding: 14px 28px;
|
padding: 14px 28px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
box-shadow: 0 10px 32px -8px rgba(168, 85, 247, 0.55),
|
box-shadow: 0 12px 28px -10px rgba(3, 199, 90, 0.5),
|
||||||
0 0 0 1px rgba(255, 255, 255, 0.08) inset;
|
0 0 0 1px rgba(255, 255, 255, 0.14) inset;
|
||||||
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||||||
}
|
}
|
||||||
.btn-grad:hover {
|
.btn-grad:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 16px 40px -8px rgba(168, 85, 247, 0.7),
|
box-shadow: 0 18px 40px -10px rgba(3, 199, 90, 0.65),
|
||||||
0 0 0 1px rgba(255, 255, 255, 0.14) inset;
|
0 0 0 1px rgba(255, 255, 255, 0.2) inset;
|
||||||
}
|
}
|
||||||
.btn-ghost {
|
.btn-ghost {
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: #ffffff;
|
||||||
color: rgba(255, 255, 255, 0.92);
|
color: #0F1D17;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
border: 1px solid rgba(15, 29, 23, 0.12);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
padding: 14px 28px;
|
padding: 14px 28px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
transition: background 0.2s ease, border-color 0.2s ease, transform 0.25s ease;
|
transition: background 0.2s ease, border-color 0.2s ease, transform 0.25s ease;
|
||||||
}
|
}
|
||||||
.btn-ghost:hover {
|
.btn-ghost:hover {
|
||||||
background: rgba(255, 255, 255, 0.08);
|
background: #F2FBF6;
|
||||||
border-color: rgba(255, 255, 255, 0.28);
|
border-color: #03C75A;
|
||||||
|
color: #02A149;
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
* Card lift (legacy + dark variant)
|
* Card lift
|
||||||
* ----------------------------------------------------------------- */
|
* ----------------------------------------------------------------- */
|
||||||
.card-lift {
|
.card-lift {
|
||||||
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
|
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
|
||||||
box-shadow 0.4s ease,
|
box-shadow 0.4s ease,
|
||||||
background 0.3s ease;
|
background 0.3s ease;
|
||||||
|
box-shadow: 0 2px 12px -6px rgba(15, 29, 23, 0.1);
|
||||||
}
|
}
|
||||||
.card-lift:hover {
|
.card-lift:hover {
|
||||||
transform: translateY(-6px);
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 18px 40px -18px rgba(3, 199, 90, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
* Link underline (legacy)
|
* Link underline
|
||||||
* ----------------------------------------------------------------- */
|
* ----------------------------------------------------------------- */
|
||||||
.link-underline {
|
.link-underline {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -349,14 +361,13 @@ body {
|
|||||||
bottom: -2px;
|
bottom: -2px;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: var(--color-warm-600);
|
background: #03C75A;
|
||||||
transition: width 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
transition: width 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
}
|
}
|
||||||
.link-underline:hover::after {
|
.link-underline:hover::after {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gradient underline for dark theme */
|
|
||||||
.link-grad {
|
.link-grad {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -368,7 +379,7 @@ body {
|
|||||||
bottom: -3px;
|
bottom: -3px;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: linear-gradient(90deg, #6366f1, #a855f7, #ec4899, #f97316);
|
background: linear-gradient(90deg, #03C75A, #00C4A7, #0AB070);
|
||||||
transition: width 0.35s cubic-bezier(0.22, 1, 0.36, 1);
|
transition: width 0.35s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
}
|
}
|
||||||
.link-grad:hover::after {
|
.link-grad:hover::after {
|
||||||
@@ -391,21 +402,19 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------
|
/* -----------------------------------------------------------------
|
||||||
* Conic/orbit decorative ring
|
* Conic decorative ring
|
||||||
* ----------------------------------------------------------------- */
|
* ----------------------------------------------------------------- */
|
||||||
.conic-ring {
|
.conic-ring {
|
||||||
background: conic-gradient(
|
background: conic-gradient(
|
||||||
from 0deg,
|
from 0deg,
|
||||||
#6366f1 0deg,
|
#03C75A 0deg,
|
||||||
#a855f7 90deg,
|
#00C4A7 120deg,
|
||||||
#ec4899 180deg,
|
#2BD673 240deg,
|
||||||
#f97316 270deg,
|
#03C75A 360deg
|
||||||
#6366f1 360deg
|
|
||||||
);
|
);
|
||||||
filter: blur(2px);
|
filter: blur(2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reduce motion preference */
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.reveal,
|
.reveal,
|
||||||
.animate-fade-up,
|
.animate-fade-up,
|
||||||
@@ -424,128 +433,138 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* =================================================================
|
/* =================================================================
|
||||||
* SITE-WIDE DARK THEME OVERRIDES
|
* SITE-WIDE LIGHT THEME OVERRIDES
|
||||||
* Legacy pages (stores/blog/about/auth/faq/contact/admin/etc.)
|
* Remap legacy color tokens (warm / ink / white / sage) to the
|
||||||
* use warm-*/ink/white color classes. Remap them to dark equivalents
|
* new white + Naver green palette. All sub-pages inherit this
|
||||||
* here so every page inherits the new look without touching code.
|
* without code changes.
|
||||||
* ================================================================= */
|
* ================================================================= */
|
||||||
|
|
||||||
/* --- Page / section backgrounds (warm-50, warm-100) --- */
|
/* --- Page / section backgrounds --- */
|
||||||
.bg-warm-50 { background-color: #05050f !important; }
|
.bg-warm-50,
|
||||||
.bg-warm-50\/50 { background-color: rgba(5, 5, 15, 0.6) !important; }
|
.bg-warm-50\/50,
|
||||||
.bg-warm-50\/60 { background-color: rgba(5, 5, 15, 0.7) !important; }
|
.bg-warm-50\/60,
|
||||||
.bg-warm-50\/70 { background-color: rgba(5, 5, 15, 0.75) !important; }
|
.bg-warm-50\/70,
|
||||||
.bg-warm-50\/80 { background-color: rgba(5, 5, 15, 0.8) !important; }
|
.bg-warm-50\/80 { background-color: #ffffff !important; }
|
||||||
|
|
||||||
.bg-warm-100 { background-color: #0a0a18 !important; }
|
.bg-warm-100 { background-color: #F7FAF8 !important; }
|
||||||
.bg-warm-100\/40 { background-color: rgba(10, 10, 24, 0.45) !important; }
|
.bg-warm-100\/40 { background-color: rgba(247, 250, 248, 0.6) !important; }
|
||||||
.bg-warm-100\/50 { background-color: rgba(10, 10, 24, 0.55) !important; }
|
.bg-warm-100\/50 { background-color: rgba(247, 250, 248, 0.7) !important; }
|
||||||
.bg-warm-100\/60 { background-color: rgba(10, 10, 24, 0.65) !important; }
|
.bg-warm-100\/60 { background-color: rgba(247, 250, 248, 0.82) !important; }
|
||||||
.bg-warm-100\/70 { background-color: rgba(10, 10, 24, 0.75) !important; }
|
.bg-warm-100\/70 { background-color: rgba(247, 250, 248, 0.9) !important; }
|
||||||
|
|
||||||
.bg-warm-200 { background-color: #10101f !important; }
|
.bg-warm-200 { background-color: #EFF2F0 !important; }
|
||||||
.bg-warm-200\/50 { background-color: rgba(16, 16, 31, 0.6) !important; }
|
.bg-warm-200\/50 { background-color: rgba(239, 242, 240, 0.7) !important; }
|
||||||
|
|
||||||
/* --- Accent color tints (warm-400/500/600 → violet/iris/rose) --- */
|
/* --- Accent tints (warm-400/500/600/700) → Naver green family --- */
|
||||||
.bg-warm-400\/15 { background-color: rgba(192, 132, 252, 0.14) !important; }
|
.bg-warm-400\/15 { background-color: rgba(3, 199, 90, 0.12) !important; color: #02A149 !important; }
|
||||||
.bg-warm-400\/30 { background-color: rgba(192, 132, 252, 0.28) !important; }
|
.bg-warm-400\/30 { background-color: rgba(3, 199, 90, 0.22) !important; color: #02A149 !important; }
|
||||||
.bg-warm-400\/40 { background-color: rgba(192, 132, 252, 0.38) !important; }
|
.bg-warm-400\/40 { background-color: rgba(3, 199, 90, 0.32) !important; color: #02A149 !important; }
|
||||||
.bg-warm-500 { background-color: #a855f7 !important; color: white !important; }
|
.bg-warm-500 { background-color: #03C75A !important; color: #ffffff !important; }
|
||||||
.bg-warm-500\/10 { background-color: rgba(168, 85, 247, 0.12) !important; }
|
.bg-warm-500\/10 { background-color: rgba(3, 199, 90, 0.10) !important; }
|
||||||
.bg-warm-500\/15 { background-color: rgba(168, 85, 247, 0.18) !important; }
|
.bg-warm-500\/15 { background-color: rgba(3, 199, 90, 0.16) !important; }
|
||||||
.bg-warm-500\/20 { background-color: rgba(168, 85, 247, 0.24) !important; }
|
.bg-warm-500\/20 { background-color: rgba(3, 199, 90, 0.22) !important; }
|
||||||
.bg-warm-600 { background-color: #ec4899 !important; color: white !important; }
|
.bg-warm-600 { background-color: #02A149 !important; color: #ffffff !important; }
|
||||||
.bg-warm-600\/90 { background-color: rgba(236, 72, 153, 0.92) !important; color: white !important; }
|
.bg-warm-600\/90 { background-color: rgba(2, 161, 73, 0.92) !important; color: #ffffff !important; }
|
||||||
.bg-warm-700 { background-color: #6366f1 !important; color: white !important; }
|
.bg-warm-700 { background-color: #018f40 !important; color: #ffffff !important; }
|
||||||
.bg-warm-800 { background-color: #0f0f22 !important; }
|
.bg-warm-800 { background-color: #0F1D17 !important; color: #ffffff !important; }
|
||||||
.bg-warm-900 { background-color: #050510 !important; }
|
.bg-warm-900 { background-color: #0A1410 !important; color: #ffffff !important; }
|
||||||
|
|
||||||
/* --- Ink (dark) backgrounds — now map to darkest navy --- */
|
/* --- Ink (dark) backgrounds → near-black text surfaces remain, tint utilities soft gray --- */
|
||||||
.bg-ink { background-color: #05050f !important; }
|
.bg-ink { background-color: #0F1D17 !important; color: #ffffff !important; }
|
||||||
.bg-ink\/5 { background-color: rgba(255, 255, 255, 0.04) !important; }
|
.bg-ink\/5 { background-color: rgba(15, 29, 23, 0.04) !important; }
|
||||||
.bg-ink\/10 { background-color: rgba(255, 255, 255, 0.07) !important; }
|
.bg-ink\/10 { background-color: rgba(15, 29, 23, 0.08) !important; }
|
||||||
.bg-ink\/20 { background-color: rgba(255, 255, 255, 0.12) !important; }
|
.bg-ink\/20 { background-color: rgba(15, 29, 23, 0.14) !important; }
|
||||||
|
|
||||||
/* --- Sage (green) tints --- */
|
/* --- Sage → Naver green (preserve semantic meaning) --- */
|
||||||
.bg-sage-500 { background-color: #34d399 !important; color: #05050f !important; }
|
.bg-sage-500 { background-color: #03C75A !important; color: #ffffff !important; }
|
||||||
.bg-sage-500\/5 { background-color: rgba(52, 211, 153, 0.08) !important; }
|
.bg-sage-500\/5 { background-color: rgba(3, 199, 90, 0.06) !important; }
|
||||||
.bg-sage-500\/10 { background-color: rgba(52, 211, 153, 0.14) !important; }
|
.bg-sage-500\/10 { background-color: rgba(3, 199, 90, 0.12) !important; }
|
||||||
.bg-sage-500\/15 { background-color: rgba(52, 211, 153, 0.2) !important; }
|
.bg-sage-500\/15 { background-color: rgba(3, 199, 90, 0.18) !important; }
|
||||||
.bg-sage-500\/90 { background-color: rgba(52, 211, 153, 0.88) !important; color: #05050f !important; }
|
.bg-sage-500\/90 { background-color: rgba(3, 199, 90, 0.92) !important; color: #ffffff !important; }
|
||||||
|
|
||||||
/* --- White backgrounds → dark glass cards --- */
|
/* --- White backgrounds — keep truly white --- */
|
||||||
.bg-white {
|
.bg-white { background-color: #ffffff !important; color: #0F1D17 !important; }
|
||||||
background-color: rgba(16, 16, 31, 0.85) !important;
|
.bg-white\/50 { background-color: rgba(255, 255, 255, 0.7) !important; backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
|
||||||
color: rgba(255, 255, 255, 0.92) !important;
|
.bg-white\/60 { background-color: rgba(255, 255, 255, 0.78) !important; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); }
|
||||||
}
|
.bg-white\/70 { background-color: rgba(255, 255, 255, 0.85) !important; backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); }
|
||||||
.bg-white\/50 {
|
.bg-white\/80 { background-color: rgba(255, 255, 255, 0.9) !important; backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
|
||||||
background-color: rgba(16, 16, 31, 0.55) !important;
|
.bg-white\/90 { background-color: rgba(255, 255, 255, 0.95) !important; }
|
||||||
backdrop-filter: blur(16px);
|
|
||||||
-webkit-backdrop-filter: blur(16px);
|
/* --- Night backgrounds (dark legacy) → light --- */
|
||||||
}
|
.bg-night-0 { background-color: #ffffff !important; }
|
||||||
.bg-white\/60 {
|
.bg-night-0\/70 { background-color: rgba(255, 255, 255, 0.82) !important; }
|
||||||
background-color: rgba(16, 16, 31, 0.6) !important;
|
.bg-night-1 { background-color: #F7FAF8 !important; }
|
||||||
backdrop-filter: blur(18px);
|
.bg-night-2 { background-color: #EFF2F0 !important; }
|
||||||
-webkit-backdrop-filter: blur(18px);
|
.bg-night-3 { background-color: #E4E8E6 !important; }
|
||||||
}
|
|
||||||
.bg-white\/70 {
|
|
||||||
background-color: rgba(16, 16, 31, 0.65) !important;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
}
|
|
||||||
.bg-white\/80 {
|
|
||||||
background-color: rgba(16, 16, 31, 0.75) !important;
|
|
||||||
backdrop-filter: blur(22px);
|
|
||||||
-webkit-backdrop-filter: blur(22px);
|
|
||||||
}
|
|
||||||
.bg-white\/90 {
|
|
||||||
background-color: rgba(16, 16, 31, 0.85) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Text colors --- */
|
/* --- Text colors --- */
|
||||||
.text-ink { color: #ffffff !important; }
|
.text-ink { color: #0F1D17 !important; }
|
||||||
.text-ink-light { color: rgba(255, 255, 255, 0.68) !important; }
|
.text-ink-light { color: #3E4A44 !important; }
|
||||||
.text-ink-muted { color: rgba(255, 255, 255, 0.45) !important; }
|
.text-ink-muted { color: #7B8581 !important; }
|
||||||
.text-warm-50 { color: #ffffff !important; }
|
.text-warm-50 { color: #ffffff !important; }
|
||||||
.text-warm-100 { color: rgba(255, 255, 255, 0.9) !important; }
|
.text-warm-100 { color: #F7FAF8 !important; }
|
||||||
.text-warm-200 { color: rgba(255, 255, 255, 0.72) !important; }
|
.text-warm-200 { color: #7B8581 !important; }
|
||||||
.text-warm-400 { color: #c084fc !important; }
|
.text-warm-400 { color: #03C75A !important; }
|
||||||
.text-warm-500 { color: #a855f7 !important; }
|
.text-warm-500 { color: #03C75A !important; }
|
||||||
.text-warm-600 { color: #ec4899 !important; }
|
.text-warm-600 { color: #02A149 !important; }
|
||||||
.text-warm-700 { color: #818cf8 !important; }
|
.text-warm-700 { color: #018f40 !important; }
|
||||||
.text-warm-800 { color: #c084fc !important; }
|
.text-warm-800 { color: #0F1D17 !important; }
|
||||||
.text-warm-900 { color: #ffffff !important; }
|
.text-warm-900 { color: #0A1410 !important; }
|
||||||
.text-sage-500 { color: #34d399 !important; }
|
.text-sage-500 { color: #03C75A !important; }
|
||||||
.text-sage-600 { color: #10b981 !important; }
|
.text-sage-600 { color: #02A149 !important; }
|
||||||
|
|
||||||
|
.text-cloud-0 { color: #0F1D17 !important; }
|
||||||
|
.text-cloud-1 { color: rgba(15, 29, 23, 0.92) !important; }
|
||||||
|
.text-cloud-2 { color: rgba(15, 29, 23, 0.68) !important; }
|
||||||
|
.text-cloud-3 { color: rgba(15, 29, 23, 0.5) !important; }
|
||||||
|
.text-cloud-4 { color: rgba(15, 29, 23, 0.35) !important; }
|
||||||
|
|
||||||
/* --- Borders --- */
|
/* --- Borders --- */
|
||||||
.border-ink { border-color: rgba(255, 255, 255, 0.92) !important; }
|
.border-ink { border-color: rgba(15, 29, 23, 0.85) !important; }
|
||||||
.border-ink\/5 { border-color: rgba(255, 255, 255, 0.05) !important; }
|
.border-ink\/5 { border-color: rgba(15, 29, 23, 0.06) !important; }
|
||||||
.border-ink\/10 { border-color: rgba(255, 255, 255, 0.08) !important; }
|
.border-ink\/10 { border-color: rgba(15, 29, 23, 0.1) !important; }
|
||||||
.border-ink\/15 { border-color: rgba(255, 255, 255, 0.12) !important; }
|
.border-ink\/15 { border-color: rgba(15, 29, 23, 0.14) !important; }
|
||||||
.border-ink\/20 { border-color: rgba(255, 255, 255, 0.16) !important; }
|
.border-ink\/20 { border-color: rgba(15, 29, 23, 0.18) !important; }
|
||||||
.border-ink\/40 { border-color: rgba(255, 255, 255, 0.3) !important; }
|
.border-ink\/40 { border-color: rgba(15, 29, 23, 0.3) !important; }
|
||||||
.border-warm-200 { border-color: rgba(255, 255, 255, 0.1) !important; }
|
.border-warm-200 { border-color: #EFF2F0 !important; }
|
||||||
.border-warm-200\/30{ border-color: rgba(255, 255, 255, 0.12) !important; }
|
.border-warm-200\/30 { border-color: rgba(239, 242, 240, 0.6) !important; }
|
||||||
.border-warm-200\/60{ border-color: rgba(255, 255, 255, 0.2) !important; }
|
.border-warm-200\/60 { border-color: rgba(228, 232, 230, 0.9) !important; }
|
||||||
.border-warm-500 { border-color: #a855f7 !important; }
|
.border-warm-300\/40 { border-color: rgba(3, 199, 90, 0.28) !important; }
|
||||||
|
.border-warm-500 { border-color: #03C75A !important; }
|
||||||
|
|
||||||
|
.border-white { border-color: #ffffff !important; }
|
||||||
|
.border-white\/5 { border-color: rgba(15, 29, 23, 0.05) !important; }
|
||||||
|
.border-white\/\[0\.06\] { border-color: rgba(15, 29, 23, 0.06) !important; }
|
||||||
|
.border-white\/\[0\.08\] { border-color: rgba(15, 29, 23, 0.08) !important; }
|
||||||
|
.border-white\/10 { border-color: rgba(15, 29, 23, 0.08) !important; }
|
||||||
|
.border-white\/15 { border-color: rgba(15, 29, 23, 0.1) !important; }
|
||||||
|
.border-white\/20 { border-color: rgba(15, 29, 23, 0.12) !important; }
|
||||||
|
.border-white\/30 { border-color: rgba(15, 29, 23, 0.18) !important; }
|
||||||
|
|
||||||
|
/* bg white/0.02, 0.03 etc (glass tints on dark theme) → very subtle gray */
|
||||||
|
.bg-white\/\[0\.02\] { background-color: #FAFCFB !important; }
|
||||||
|
.bg-white\/\[0\.03\] { background-color: #F7FAF8 !important; }
|
||||||
|
.bg-white\/\[0\.04\] { background-color: #F5F8F6 !important; }
|
||||||
|
.bg-white\/\[0\.05\] { background-color: #F2F6F4 !important; }
|
||||||
|
.bg-white\/\[0\.08\] { background-color: #EEF3F0 !important; }
|
||||||
|
|
||||||
/* --- Hover variants --- */
|
/* --- Hover variants --- */
|
||||||
.hover\:bg-warm-50:hover { background-color: rgba(255, 255, 255, 0.03) !important; }
|
.hover\:bg-warm-50:hover { background-color: #F7FAF8 !important; }
|
||||||
.hover\:bg-warm-100:hover { background-color: rgba(255, 255, 255, 0.05) !important; }
|
.hover\:bg-warm-100:hover { background-color: #EFF2F0 !important; }
|
||||||
.hover\:bg-warm-200:hover { background-color: rgba(255, 255, 255, 0.08) !important; }
|
.hover\:bg-warm-200:hover { background-color: #E4E8E6 !important; }
|
||||||
.hover\:bg-warm-400:hover { background-color: #c084fc !important; }
|
.hover\:bg-warm-400:hover { background-color: #2BD673 !important; color: #ffffff !important; }
|
||||||
.hover\:bg-warm-500:hover { background-color: #a855f7 !important; }
|
.hover\:bg-warm-500:hover { background-color: #02A149 !important; color: #ffffff !important; }
|
||||||
.hover\:bg-warm-600:hover { background-color: #ec4899 !important; }
|
.hover\:bg-warm-600:hover { background-color: #018f40 !important; color: #ffffff !important; }
|
||||||
.hover\:bg-warm-700:hover { background-color: #6366f1 !important; }
|
.hover\:bg-warm-700:hover { background-color: #0F1D17 !important; color: #ffffff !important; }
|
||||||
.hover\:bg-warm-800:hover { background-color: #10101f !important; }
|
.hover\:bg-warm-800:hover { background-color: #0A1410 !important; color: #ffffff !important; }
|
||||||
.hover\:text-warm-600:hover { color: #ec4899 !important; }
|
.hover\:text-warm-600:hover { color: #02A149 !important; }
|
||||||
.hover\:text-warm-700:hover { color: #818cf8 !important; }
|
.hover\:text-warm-700:hover { color: #018f40 !important; }
|
||||||
.hover\:text-warm-800:hover { color: #c084fc !important; }
|
.hover\:text-warm-800:hover { color: #0F1D17 !important; }
|
||||||
.hover\:border-ink\/40:hover{ border-color: rgba(255, 255, 255, 0.3) !important; }
|
.hover\:border-ink\/40:hover { border-color: rgba(15, 29, 23, 0.3) !important; }
|
||||||
.hover\:border-warm-200:hover { border-color: rgba(255, 255, 255, 0.2) !important; }
|
.hover\:border-warm-200:hover { border-color: #03C75A !important; }
|
||||||
|
.hover\:text-cloud-0:hover { color: #03C75A !important; }
|
||||||
|
|
||||||
/* --- Form inputs: dark theme by default --- */
|
/* --- Form inputs: light theme --- */
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="email"],
|
input[type="email"],
|
||||||
input[type="password"],
|
input[type="password"],
|
||||||
@@ -559,64 +578,98 @@ input[type="datetime-local"],
|
|||||||
input:not([type]),
|
input:not([type]),
|
||||||
textarea,
|
textarea,
|
||||||
select {
|
select {
|
||||||
background-color: rgba(16, 16, 31, 0.6) !important;
|
background-color: #ffffff !important;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
border: 1px solid #E4E8E6 !important;
|
||||||
color: #ffffff !important;
|
color: #0F1D17 !important;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
}
|
}
|
||||||
input::placeholder,
|
input::placeholder,
|
||||||
textarea::placeholder {
|
textarea::placeholder {
|
||||||
color: rgba(255, 255, 255, 0.35) !important;
|
color: rgba(15, 29, 23, 0.42) !important;
|
||||||
}
|
}
|
||||||
input:focus,
|
input:focus,
|
||||||
textarea:focus,
|
textarea:focus,
|
||||||
select:focus {
|
select:focus {
|
||||||
border-color: #a855f7 !important;
|
border-color: #03C75A !important;
|
||||||
box-shadow: 0 0 0 4px rgba(168, 85, 247, 0.18) !important;
|
box-shadow: 0 0 0 4px rgba(3, 199, 90, 0.18) !important;
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
}
|
}
|
||||||
/* Checkbox / radio — keep accent purple */
|
|
||||||
input[type="checkbox"],
|
input[type="checkbox"],
|
||||||
input[type="radio"] {
|
input[type="radio"] {
|
||||||
accent-color: #a855f7;
|
accent-color: #03C75A;
|
||||||
}
|
}
|
||||||
/* Option elements inside select */
|
|
||||||
select option {
|
select option {
|
||||||
background-color: #10101f;
|
background-color: #ffffff;
|
||||||
color: #ffffff;
|
color: #0F1D17;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Blog article prose (if used) --- */
|
/* --- Blog article prose --- */
|
||||||
.prose,
|
.prose,
|
||||||
.prose-lg,
|
.prose-lg,
|
||||||
.prose-xl {
|
.prose-xl {
|
||||||
--tw-prose-body: rgba(255, 255, 255, 0.78);
|
--tw-prose-body: #3E4A44;
|
||||||
--tw-prose-headings: #ffffff;
|
--tw-prose-headings: #0F1D17;
|
||||||
--tw-prose-lead: rgba(255, 255, 255, 0.72);
|
--tw-prose-lead: #3E4A44;
|
||||||
--tw-prose-links: #c084fc;
|
--tw-prose-links: #02A149;
|
||||||
--tw-prose-bold: #ffffff;
|
--tw-prose-bold: #0F1D17;
|
||||||
--tw-prose-counters: rgba(255, 255, 255, 0.5);
|
--tw-prose-counters: #7B8581;
|
||||||
--tw-prose-bullets: rgba(255, 255, 255, 0.35);
|
--tw-prose-bullets: #B6BEB9;
|
||||||
--tw-prose-hr: rgba(255, 255, 255, 0.1);
|
--tw-prose-hr: #E4E8E6;
|
||||||
--tw-prose-quotes: rgba(255, 255, 255, 0.85);
|
--tw-prose-quotes: #0F1D17;
|
||||||
--tw-prose-quote-borders: rgba(168, 85, 247, 0.5);
|
--tw-prose-quote-borders: #03C75A;
|
||||||
--tw-prose-captions: rgba(255, 255, 255, 0.5);
|
--tw-prose-captions: #7B8581;
|
||||||
--tw-prose-code: #f472b6;
|
--tw-prose-code: #02A149;
|
||||||
--tw-prose-pre-code: #e4e4e7;
|
--tw-prose-pre-code: #0F1D17;
|
||||||
--tw-prose-pre-bg: rgba(16, 16, 31, 0.75);
|
--tw-prose-pre-bg: #F7FAF8;
|
||||||
--tw-prose-th-borders: rgba(255, 255, 255, 0.15);
|
--tw-prose-th-borders: #E4E8E6;
|
||||||
--tw-prose-td-borders: rgba(255, 255, 255, 0.08);
|
--tw-prose-td-borders: #EFF2F0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Placeholder/disabled states --- */
|
|
||||||
*:disabled {
|
*:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Ensure html/body stay dark for non-home routes too --- */
|
|
||||||
html, body {
|
html, body {
|
||||||
background-color: #05050f;
|
background-color: #ffffff;
|
||||||
color-scheme: dark;
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------
|
||||||
|
* Mobile-first helpers for the app-style home layout
|
||||||
|
* ----------------------------------------------------------------- */
|
||||||
|
.category-tile {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 24px;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
.category-tile:active { transform: scale(0.98); }
|
||||||
|
.category-tile.primary {
|
||||||
|
background: linear-gradient(135deg, #03C75A 0%, #02A149 55%, #018f40 100%);
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: 0 16px 40px -12px rgba(3, 199, 90, 0.45);
|
||||||
|
}
|
||||||
|
.category-tile.soft {
|
||||||
|
background: #F2FBF6;
|
||||||
|
color: #02A149;
|
||||||
|
border: 1px solid #D5EEE0;
|
||||||
|
}
|
||||||
|
.category-tile.neutral {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #0F1D17;
|
||||||
|
border: 1px solid #E4E8E6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Photo hero for property detail */
|
||||||
|
.photo-hero {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 4 / 3;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #EFF2F0;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.photo-hero { aspect-ratio: 16 / 9; border-radius: 24px; }
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-26
@@ -75,22 +75,32 @@ const NAV_LINKS = [
|
|||||||
{ href: '/faq', label: 'FAQ' },
|
{ href: '/faq', label: 'FAQ' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const MOBILE_TABS = [
|
||||||
|
{ href: '/', label: '홈', icon: 'M3 12l9-9 9 9M5 10v10h14V10' },
|
||||||
|
{ href: '/stores', label: '매장', icon: 'M21 21l-4.35-4.35M16 10a6 6 0 10-12 0 6 6 0 0012 0z' },
|
||||||
|
{ href: '/matching', label: '매칭', icon: 'M7 8h10M7 12h6M7 16h10M3 4h18v16H3z' },
|
||||||
|
{ href: '/subsidies', label: '지원금', icon: 'M12 3v18M6 8h9a3 3 0 010 6H9a3 3 0 000 6h9' },
|
||||||
|
];
|
||||||
|
|
||||||
async function Navigation() {
|
async function Navigation() {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
const isOperator = session?.user && OPERATOR_ROLES.includes(session.user.primaryRole);
|
const isOperator = session?.user && OPERATOR_ROLES.includes(session.user.primaryRole);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="sticky top-0 z-50 border-b border-white/[0.06] bg-night-0/70 backdrop-blur-xl">
|
<nav
|
||||||
|
className="sticky top-0 z-50 border-b bg-white/85 backdrop-blur-xl"
|
||||||
|
style={{ borderColor: '#EFF2F0' }}
|
||||||
|
>
|
||||||
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-5">
|
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-5">
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="group flex items-center gap-2 font-display text-xl font-black tracking-tight text-cloud-0"
|
className="group flex items-center gap-2 font-display text-xl font-black tracking-tight text-ink"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-hidden
|
aria-hidden
|
||||||
className="relative inline-flex h-7 w-7 items-center justify-center rounded-md"
|
className="relative inline-flex h-8 w-8 items-center justify-center rounded-lg shadow-[0_6px_14px_-6px_rgba(3,199,90,0.55)]"
|
||||||
style={{
|
style={{
|
||||||
background: 'linear-gradient(135deg,#6366f1 0%,#a855f7 45%,#ec4899 80%,#f97316 100%)',
|
background: 'linear-gradient(135deg,#03C75A 0%,#02A149 55%,#018f40 100%)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="text-sm font-black text-white">S</span>
|
<span className="text-sm font-black text-white">S</span>
|
||||||
@@ -104,7 +114,7 @@ async function Navigation() {
|
|||||||
<Link
|
<Link
|
||||||
key={l.href}
|
key={l.href}
|
||||||
href={l.href}
|
href={l.href}
|
||||||
className="text-sm font-medium text-cloud-2 transition-colors hover:text-cloud-0"
|
className="text-sm font-medium text-ink-light transition-colors hover:text-ink"
|
||||||
>
|
>
|
||||||
{l.label}
|
{l.label}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -112,71 +122,97 @@ async function Navigation() {
|
|||||||
{isOperator && (
|
{isOperator && (
|
||||||
<Link
|
<Link
|
||||||
href="/admin"
|
href="/admin"
|
||||||
className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-sm text-cloud-2 transition-colors hover:border-white/20 hover:bg-white/[0.08] hover:text-cloud-0"
|
className="rounded-full border px-3 py-1.5 text-sm text-ink-light transition-colors hover:border-naver hover:text-naver"
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
>
|
>
|
||||||
운영자
|
운영자
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<AuthButtons session={session} />
|
<AuthButtons session={session} />
|
||||||
</div>
|
</div>
|
||||||
|
{/* Mobile auth shortcut */}
|
||||||
|
<div className="flex items-center gap-2 md:hidden">
|
||||||
|
<AuthButtons session={session} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MobileTabBar() {
|
||||||
|
return (
|
||||||
|
<nav
|
||||||
|
className="fixed bottom-0 left-0 right-0 z-40 grid grid-cols-4 border-t bg-white/95 backdrop-blur-xl md:hidden"
|
||||||
|
style={{ borderColor: '#EFF2F0' }}
|
||||||
|
>
|
||||||
|
{MOBILE_TABS.map((t) => (
|
||||||
|
<Link
|
||||||
|
key={t.href}
|
||||||
|
href={t.href}
|
||||||
|
className="flex flex-col items-center gap-1 py-2.5 text-[10px] font-semibold text-ink-light active:text-naver"
|
||||||
|
>
|
||||||
|
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d={t.icon} />
|
||||||
|
</svg>
|
||||||
|
{t.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function Footer() {
|
function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="relative mt-24 border-t border-white/5 bg-night-0 font-body">
|
<footer className="relative mt-24 border-t bg-white font-body" style={{ borderColor: '#EFF2F0' }}>
|
||||||
{/* decorative glow */}
|
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-40">
|
||||||
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-60">
|
|
||||||
<div className="orb orb-iris absolute -left-32 top-10 h-72 w-72" />
|
<div className="orb orb-iris absolute -left-32 top-10 h-72 w-72" />
|
||||||
<div className="orb orb-rose absolute right-0 bottom-0 h-72 w-72" />
|
<div className="orb orb-rose absolute right-0 bottom-0 h-72 w-72" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative mx-auto grid max-w-7xl grid-cols-2 gap-10 px-6 py-16 md:grid-cols-4">
|
<div className="relative mx-auto grid max-w-7xl grid-cols-2 gap-10 px-6 py-16 md:grid-cols-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-display text-xl font-black tracking-tight text-cloud-0">
|
<p className="font-display text-xl font-black tracking-tight text-ink">
|
||||||
start<span className="text-grad-primary">over</span>
|
start<span className="text-grad-primary">over</span>
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-3 text-xs leading-relaxed text-cloud-3">
|
<p className="mt-3 text-xs leading-relaxed text-ink-muted">
|
||||||
폐업 · 양도 · 창업을 잇는
|
폐업 · 양도 · 창업을 잇는
|
||||||
<br />
|
<br />
|
||||||
소상공인 중개 플랫폼
|
소상공인 중개 플랫폼
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-cloud-4">
|
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-ink-muted">
|
||||||
Service
|
Service
|
||||||
</p>
|
</p>
|
||||||
<ul className="mt-4 space-y-2.5 text-sm">
|
<ul className="mt-4 space-y-2.5 text-sm">
|
||||||
<li><Link href="/stores" className="text-cloud-2 hover:text-cloud-0 transition-colors">매장 검색</Link></li>
|
<li><Link href="/stores" className="text-ink-light hover:text-ink transition-colors">매장 검색</Link></li>
|
||||||
<li><Link href="/stores/new" className="text-cloud-2 hover:text-cloud-0 transition-colors">매장 등록</Link></li>
|
<li><Link href="/stores/new" className="text-ink-light hover:text-ink transition-colors">매장 등록</Link></li>
|
||||||
<li><Link href="/subsidies" className="text-cloud-2 hover:text-cloud-0 transition-colors">지원금</Link></li>
|
<li><Link href="/subsidies" className="text-ink-light hover:text-ink transition-colors">지원금</Link></li>
|
||||||
<li><Link href="/vendors" className="text-cloud-2 hover:text-cloud-0 transition-colors">업체 인증</Link></li>
|
<li><Link href="/vendors" className="text-ink-light hover:text-ink transition-colors">업체 인증</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-cloud-4">
|
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-ink-muted">
|
||||||
Learn
|
Learn
|
||||||
</p>
|
</p>
|
||||||
<ul className="mt-4 space-y-2.5 text-sm">
|
<ul className="mt-4 space-y-2.5 text-sm">
|
||||||
<li><Link href="/about" className="text-cloud-2 hover:text-cloud-0 transition-colors">회사 소개</Link></li>
|
<li><Link href="/about" className="text-ink-light hover:text-ink transition-colors">회사 소개</Link></li>
|
||||||
<li><Link href="/blog" className="text-cloud-2 hover:text-cloud-0 transition-colors">블로그</Link></li>
|
<li><Link href="/blog" className="text-ink-light hover:text-ink transition-colors">블로그</Link></li>
|
||||||
<li><Link href="/faq" className="text-cloud-2 hover:text-cloud-0 transition-colors">자주 묻는 질문</Link></li>
|
<li><Link href="/faq" className="text-ink-light hover:text-ink transition-colors">자주 묻는 질문</Link></li>
|
||||||
<li><Link href="/contact" className="text-cloud-2 hover:text-cloud-0 transition-colors">문의하기</Link></li>
|
<li><Link href="/contact" className="text-ink-light hover:text-ink transition-colors">문의하기</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-cloud-4">
|
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-ink-muted">
|
||||||
Legal
|
Legal
|
||||||
</p>
|
</p>
|
||||||
<ul className="mt-4 space-y-2.5 text-sm">
|
<ul className="mt-4 space-y-2.5 text-sm">
|
||||||
<li><Link href="/terms" className="text-cloud-2 hover:text-cloud-0 transition-colors">이용약관</Link></li>
|
<li><Link href="/terms" className="text-ink-light hover:text-ink transition-colors">이용약관</Link></li>
|
||||||
<li><Link href="/privacy" className="text-cloud-2 hover:text-cloud-0 transition-colors">개인정보처리방침</Link></li>
|
<li><Link href="/privacy" className="text-ink-light hover:text-ink transition-colors">개인정보처리방침</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative border-t border-white/5 px-6 py-5 text-center font-mono text-[11px] tracking-[0.15em] text-cloud-4">
|
<div className="relative border-t px-6 py-5 text-center font-mono text-[11px] tracking-[0.15em] text-ink-muted" style={{ borderColor: '#EFF2F0' }}>
|
||||||
© 2026 STARTOVER · ALL RIGHTS RESERVED
|
© 2026 STARTOVER · ALL RIGHTS RESERVED
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -191,6 +227,7 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html lang="ko">
|
<html lang="ko">
|
||||||
<head>
|
<head>
|
||||||
|
<meta name="theme-color" content="#03C75A" />
|
||||||
<Script
|
<Script
|
||||||
async
|
async
|
||||||
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9505789508299290"
|
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9505789508299290"
|
||||||
@@ -198,10 +235,11 @@ export default function RootLayout({
|
|||||||
strategy="afterInteractive"
|
strategy="afterInteractive"
|
||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body className="min-h-screen bg-night-0 font-body text-cloud-1">
|
<body className="min-h-screen bg-white font-body text-ink pb-16 md:pb-0">
|
||||||
<Navigation />
|
<Navigation />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
|
<MobileTabBar />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
+203
-293
@@ -7,24 +7,21 @@ const PROCESS_STEPS = [
|
|||||||
num: '01',
|
num: '01',
|
||||||
title: '매장 등록',
|
title: '매장 등록',
|
||||||
desc: '업종·지역·권리금·월매출 등 기본 정보를 입력하면 운영팀 검수를 거쳐 공개됩니다.',
|
desc: '업종·지역·권리금·월매출 등 기본 정보를 입력하면 운영팀 검수를 거쳐 공개됩니다.',
|
||||||
accent: 'from-iris via-violet to-rose',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: '02',
|
num: '02',
|
||||||
title: '자동 매칭',
|
title: '자동 매칭',
|
||||||
desc: '등록된 매장은 인수 희망자와 인증된 철거·인테리어 업체에 동시에 노출됩니다.',
|
desc: '등록된 매장은 인수 희망자와 인증된 철거·인테리어 업체에 동시에 노출됩니다.',
|
||||||
accent: 'from-violet via-rose to-tangerine',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: '03',
|
num: '03',
|
||||||
title: '계약·정산',
|
title: '계약·정산',
|
||||||
desc: '표준 계약서, 에스크로 결제, 단계별 검수 승인으로 안전하게 거래가 마무리됩니다.',
|
desc: '표준 계약서, 에스크로 결제, 단계별 검수 승인으로 안전하게 거래가 마무리됩니다.',
|
||||||
accent: 'from-rose via-tangerine to-amber',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const STATS = [
|
const STATS = [
|
||||||
{ value: '1', unit: '회', label: '매장 등록', sub: '인수자·철거·인테리어 동시 매칭' },
|
{ value: '128', unit: '건', label: '누적 매장 등록', sub: '인수자·철거·인테리어 동시 매칭' },
|
||||||
{ value: '7', unit: '개', label: '업종 대분류', sub: '49개 소분류까지 세분화' },
|
{ value: '7', unit: '개', label: '업종 대분류', sub: '49개 소분류까지 세분화' },
|
||||||
{ value: '2–4', unit: '주', label: '평균 매칭 기간', sub: '운영팀 검수 포함' },
|
{ value: '2–4', unit: '주', label: '평균 매칭 기간', sub: '운영팀 검수 포함' },
|
||||||
{ value: '3', unit: '단계', label: '에스크로 정산', sub: '사전·중간·최종 검수' },
|
{ value: '3', unit: '단계', label: '에스크로 정산', sub: '사전·중간·최종 검수' },
|
||||||
@@ -35,7 +32,6 @@ const BENEFITS = [
|
|||||||
label: '매도인',
|
label: '매도인',
|
||||||
english: 'for sellers',
|
english: 'for sellers',
|
||||||
title: '깔끔한 마무리',
|
title: '깔끔한 마무리',
|
||||||
accent: 'iris',
|
|
||||||
points: [
|
points: [
|
||||||
'매장 한 번 등록으로 인수자·철거업체 동시 매칭',
|
'매장 한 번 등록으로 인수자·철거업체 동시 매칭',
|
||||||
'원상복구 비용을 인수자에게 이전하는 양도 우선 시도',
|
'원상복구 비용을 인수자에게 이전하는 양도 우선 시도',
|
||||||
@@ -46,7 +42,6 @@ const BENEFITS = [
|
|||||||
label: '매수인',
|
label: '매수인',
|
||||||
english: 'for founders',
|
english: 'for founders',
|
||||||
title: '검증된 시작',
|
title: '검증된 시작',
|
||||||
accent: 'violet',
|
|
||||||
points: [
|
points: [
|
||||||
'운영팀 검수를 통과한 매물만 공개',
|
'운영팀 검수를 통과한 매물만 공개',
|
||||||
'권리금·월매출·월수익·회수기간까지 한 페이지에 공개',
|
'권리금·월매출·월수익·회수기간까지 한 페이지에 공개',
|
||||||
@@ -57,7 +52,6 @@ const BENEFITS = [
|
|||||||
label: '업체',
|
label: '업체',
|
||||||
english: 'for partners',
|
english: 'for partners',
|
||||||
title: '안정적 수주',
|
title: '안정적 수주',
|
||||||
accent: 'rose',
|
|
||||||
points: [
|
points: [
|
||||||
'인증 통과 후 지역·업종 맞춤 안건이 자동 전달',
|
'인증 통과 후 지역·업종 맞춤 안건이 자동 전달',
|
||||||
'에스크로 선입금으로 대금 회수 리스크 최소화',
|
'에스크로 선입금으로 대금 회수 리스크 최소화',
|
||||||
@@ -81,66 +75,6 @@ const FAQ_PREVIEW = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const FEATURES = [
|
|
||||||
{
|
|
||||||
label: '폐업자',
|
|
||||||
english: 'Closing owners',
|
|
||||||
title: '깔끔한 마무리,\n새로운 시작',
|
|
||||||
desc: '매장 정보를 등록하면 철거비 절감, 시설 처분, 지원금 신청까지 한 번에 해결됩니다.',
|
|
||||||
href: '/stores/new',
|
|
||||||
cta: '매장 등록',
|
|
||||||
num: '01',
|
|
||||||
glow: 'iris',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '창업자',
|
|
||||||
english: 'Founders',
|
|
||||||
title: '검증된 매장,\n합리적 시작',
|
|
||||||
desc: '검증된 매장을 검색하고 매칭 요청을 보내 시설 인수와 인테리어 비용을 절감하세요.',
|
|
||||||
href: '/stores',
|
|
||||||
cta: '매장 검색',
|
|
||||||
num: '02',
|
|
||||||
glow: 'violet',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '철거·인테리어 업체',
|
|
||||||
english: 'Partners',
|
|
||||||
title: '안정적 수주,\n정확한 정산',
|
|
||||||
desc: '인증 업체로 등록하면 안정적인 수주와 정산을 보장받을 수 있습니다.',
|
|
||||||
href: '/vendors',
|
|
||||||
cta: '업체 인증',
|
|
||||||
num: '03',
|
|
||||||
glow: 'rose',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const SERVICES = [
|
|
||||||
{
|
|
||||||
title: '정부 지원금 가이드',
|
|
||||||
desc: '폐업 관련 정부 지원금 자격을 확인하고, 체크리스트와 서류 준비를 도와드립니다.',
|
|
||||||
href: '/subsidies',
|
|
||||||
cta: '지원금 확인',
|
|
||||||
tag: 'Guide',
|
|
||||||
icon: (
|
|
||||||
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m-3-2.818.879.659c1.171.879 3.07.879 4.242 0 1.172-.879 1.172-2.303 0-3.182C13.536 12.219 12.768 12 12 12c-.725 0-1.45-.22-2.003-.659-1.106-.879-1.106-2.303 0-3.182s2.9-.879 4.006 0l.415.33M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '안전한 거래',
|
|
||||||
desc: '표준 계약서, 에스크로 결제, 검수 승인 시스템으로 안전한 거래를 보장합니다.',
|
|
||||||
href: '/contracts',
|
|
||||||
cta: '계약 관리',
|
|
||||||
tag: 'Escrow',
|
|
||||||
icon: (
|
|
||||||
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const MARQUEE_ITEMS = [
|
const MARQUEE_ITEMS = [
|
||||||
'매장 한 번 등록',
|
'매장 한 번 등록',
|
||||||
'인수자 · 철거 · 인테리어 동시 매칭',
|
'인수자 · 철거 · 인테리어 동시 매칭',
|
||||||
@@ -155,16 +89,93 @@ export default function HomePage() {
|
|||||||
const latestPosts = getPostsSortedByDate().slice(0, 4);
|
const latestPosts = getPostsSortedByDate().slice(0, 4);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="relative overflow-hidden bg-night-0 text-cloud-1 font-body">
|
<main className="relative overflow-hidden bg-white text-ink font-body">
|
||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* HERO
|
* MOBILE APP-STYLE QUICK ACCESS (visible only on small screens)
|
||||||
|
* - 매장 검색: 큰 박스 (primary)
|
||||||
|
* - 매장 등록: 작은 박스 (soft)
|
||||||
|
* - 매칭 / 지원금: 컴팩트 타일
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative min-h-[calc(100vh-4rem)] overflow-hidden">
|
<section className="md:hidden px-5 pt-6 pb-2">
|
||||||
{/* Animated grid */}
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="inline-flex h-9 w-9 items-center justify-center rounded-xl text-white font-black" style={{ background: 'linear-gradient(135deg,#03C75A,#02A149)' }}>S</span>
|
||||||
|
<div>
|
||||||
|
<p className="text-[11px] font-semibold tracking-[0.18em] text-naver-dark" style={{ color: '#02A149' }}>START OVER</p>
|
||||||
|
<p className="text-[13px] text-ink-muted">폐업 · 양도 · 창업을 잇다</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 grid grid-cols-3 grid-rows-2 gap-3">
|
||||||
|
{/* 매장 검색 — 큰 박스 (2×2) */}
|
||||||
|
<Link
|
||||||
|
href="/stores"
|
||||||
|
className="category-tile primary col-span-2 row-span-2 flex flex-col justify-between p-6"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p className="text-[11px] font-semibold tracking-[0.2em] opacity-90">SEARCH</p>
|
||||||
|
<h2 className="mt-2 font-display text-2xl font-extrabold leading-tight">
|
||||||
|
매장 검색
|
||||||
|
</h2>
|
||||||
|
<p className="mt-2 text-[13px] leading-relaxed opacity-95">
|
||||||
|
검증된 매물을 지역·업종·권리금으로 찾아보세요
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex items-center justify-between">
|
||||||
|
<span className="text-[11px] font-semibold opacity-90">128건 공개</span>
|
||||||
|
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-white/20 text-xl">→</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* 매장 등록 — 작은 박스 */}
|
||||||
|
<Link
|
||||||
|
href="/stores/new"
|
||||||
|
className="category-tile soft flex flex-col justify-between p-4"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p className="text-[10px] font-semibold tracking-[0.2em] opacity-80">REGISTER</p>
|
||||||
|
<h3 className="mt-1 font-display text-base font-extrabold">매장 등록</h3>
|
||||||
|
</div>
|
||||||
|
<span className="text-[11px] font-semibold">3분이면 완료 →</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* 매칭 */}
|
||||||
|
<Link
|
||||||
|
href="/matching"
|
||||||
|
className="category-tile neutral flex flex-col justify-between p-4"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p className="text-[10px] font-semibold tracking-[0.2em] text-ink-muted">MATCH</p>
|
||||||
|
<h3 className="mt-1 font-display text-base font-extrabold">매칭 요청</h3>
|
||||||
|
</div>
|
||||||
|
<span className="text-[11px] font-semibold" style={{ color: '#02A149' }}>→</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 grid grid-cols-4 gap-2">
|
||||||
|
{[
|
||||||
|
{ href: '/subsidies', label: '지원금', emoji: '💰' },
|
||||||
|
{ href: '/vendors', label: '업체', emoji: '🛠' },
|
||||||
|
{ href: '/blog', label: '가이드', emoji: '📖' },
|
||||||
|
{ href: '/faq', label: 'FAQ', emoji: '💬' },
|
||||||
|
].map((q) => (
|
||||||
|
<Link
|
||||||
|
key={q.href}
|
||||||
|
href={q.href}
|
||||||
|
className="flex flex-col items-center gap-1 rounded-2xl border border-line bg-white px-2 py-3 text-center"
|
||||||
|
>
|
||||||
|
<span className="text-lg leading-none">{q.emoji}</span>
|
||||||
|
<span className="text-[11px] font-semibold text-ink">{q.label}</span>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ==========================================================
|
||||||
|
* HERO (desktop focus, mobile still shows it below quick-access)
|
||||||
|
* ========================================================== */}
|
||||||
|
<section className="relative overflow-hidden md:min-h-[calc(100vh-4rem)]">
|
||||||
<div className="pointer-events-none absolute inset-0 bg-grid-animated opacity-80" />
|
<div className="pointer-events-none absolute inset-0 bg-grid-animated opacity-80" />
|
||||||
{/* Radial fade to edges */}
|
|
||||||
<div className="pointer-events-none absolute inset-0 bg-radial-fade" />
|
<div className="pointer-events-none absolute inset-0 bg-radial-fade" />
|
||||||
{/* Glow orbs */}
|
|
||||||
<div className="pointer-events-none absolute inset-0">
|
<div className="pointer-events-none absolute inset-0">
|
||||||
<div className="orb orb-iris absolute -top-20 -left-20 h-[520px] w-[520px]" style={{ animationDelay: '0s' }} />
|
<div className="orb orb-iris absolute -top-20 -left-20 h-[520px] w-[520px]" style={{ animationDelay: '0s' }} />
|
||||||
<div className="orb orb-violet absolute top-1/4 right-0 h-[440px] w-[440px]" style={{ animationDelay: '-4s' }} />
|
<div className="orb orb-violet absolute top-1/4 right-0 h-[440px] w-[440px]" style={{ animationDelay: '-4s' }} />
|
||||||
@@ -172,30 +183,25 @@ export default function HomePage() {
|
|||||||
<div className="orb orb-tangerine absolute -bottom-24 right-1/3 h-[320px] w-[320px] opacity-40" style={{ animationDelay: '-12s' }} />
|
<div className="orb orb-tangerine absolute -bottom-24 right-1/3 h-[320px] w-[320px] opacity-40" style={{ animationDelay: '-12s' }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Hero content */}
|
<div className="relative mx-auto flex md:min-h-[calc(100vh-4rem)] max-w-7xl flex-col justify-center px-6 py-16 md:py-24">
|
||||||
<div className="relative mx-auto flex min-h-[calc(100vh-4rem)] max-w-7xl flex-col justify-center px-6 py-24">
|
<p className="reveal d1 font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">
|
||||||
<p className="reveal d1 font-mono text-xs tracking-[0.3em] uppercase text-cloud-3">
|
<span className="inline-block h-1.5 w-1.5 translate-y-[-2px] rounded-full align-middle mr-2 animate-pulse" style={{ background: '#03C75A' }} />
|
||||||
<span className="inline-block h-1.5 w-1.5 translate-y-[-2px] rounded-full bg-lime-light align-middle mr-2 animate-pulse" />
|
|
||||||
폐업 · 양도 · 창업을 잇다
|
폐업 · 양도 · 창업을 잇다
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 className="reveal d2 mt-6 font-display text-[clamp(3.5rem,11vw,10rem)] font-black leading-[0.95] tracking-tight text-cloud-0">
|
<h1 className="reveal d2 mt-6 font-display text-[clamp(3rem,10vw,9rem)] font-black leading-[0.95] tracking-tight text-ink">
|
||||||
Start<br />
|
Start<br />
|
||||||
<span className="text-grad-rainbow">
|
<span className="text-grad-rainbow">over</span>
|
||||||
over
|
<span className="font-serif italic text-ink" style={{ fontWeight: 400 }}>.</span>
|
||||||
</span>
|
|
||||||
<span className="font-serif italic text-cloud-1" style={{ fontWeight: 400 }}>
|
|
||||||
.
|
|
||||||
</span>
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="reveal d3 mt-10 max-w-2xl text-lg leading-relaxed text-cloud-2 sm:text-xl">
|
<p className="reveal d3 mt-8 max-w-2xl text-lg leading-relaxed text-ink-light sm:text-xl">
|
||||||
<span className="font-serif italic text-cloud-0">매장 한 번 등록</span>으로
|
<span className="font-serif italic text-ink">매장 한 번 등록</span>으로
|
||||||
창업자 · 철거 · 인테리어 업체를 동시에 연결하고,
|
창업자 · 철거 · 인테리어 업체를 동시에 연결하고,
|
||||||
정부 지원금 신청까지 한 흐름으로 이어드립니다.
|
정부 지원금 신청까지 한 흐름으로 이어드립니다.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="reveal d4 mt-12 flex flex-wrap items-center gap-4">
|
<div className="reveal d4 mt-10 flex flex-wrap items-center gap-4">
|
||||||
<Link href="/stores" className="btn-grad group inline-flex items-center gap-2 text-base">
|
<Link href="/stores" className="btn-grad group inline-flex items-center gap-2 text-base">
|
||||||
매장 둘러보기
|
매장 둘러보기
|
||||||
<span className="inline-block transition-transform group-hover:translate-x-1">→</span>
|
<span className="inline-block transition-transform group-hover:translate-x-1">→</span>
|
||||||
@@ -206,8 +212,7 @@ export default function HomePage() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Floating stats pills */}
|
<div className="reveal d5 mt-16 flex flex-wrap items-center gap-3">
|
||||||
<div className="reveal d5 mt-20 flex flex-wrap items-center gap-3">
|
|
||||||
{[
|
{[
|
||||||
{ k: '업종', v: '7개 대분류' },
|
{ k: '업종', v: '7개 대분류' },
|
||||||
{ k: '세분류', v: '49개' },
|
{ k: '세분류', v: '49개' },
|
||||||
@@ -216,32 +221,26 @@ export default function HomePage() {
|
|||||||
].map((p) => (
|
].map((p) => (
|
||||||
<span
|
<span
|
||||||
key={p.k}
|
key={p.k}
|
||||||
className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.03] px-4 py-2 text-xs text-cloud-2 backdrop-blur-md"
|
className="inline-flex items-center gap-2 rounded-full border border-line bg-white px-4 py-2 text-xs text-ink-light"
|
||||||
>
|
>
|
||||||
<span className="font-mono text-[10px] uppercase tracking-widest text-cloud-4">{p.k}</span>
|
<span className="font-mono text-[10px] uppercase tracking-widest text-ink-muted">{p.k}</span>
|
||||||
<span className="text-cloud-1">{p.v}</span>
|
<span className="text-ink">{p.v}</span>
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Scroll indicator */}
|
|
||||||
<div className="reveal d6 absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2">
|
|
||||||
<span className="font-mono text-[10px] uppercase tracking-[0.3em] text-cloud-4">scroll</span>
|
|
||||||
<span className="h-10 w-px bg-gradient-to-b from-cloud-3 to-transparent" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* MARQUEE
|
* MARQUEE
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative border-y border-white/5 bg-night-1 py-8">
|
<section className="relative border-y border-line bg-surface-soft py-8" style={{ background: '#F7FAF8' }}>
|
||||||
<div className="marquee-mask overflow-hidden">
|
<div className="marquee-mask overflow-hidden">
|
||||||
<div className="marquee-track">
|
<div className="marquee-track">
|
||||||
{[...MARQUEE_ITEMS, ...MARQUEE_ITEMS].map((item, i) => (
|
{[...MARQUEE_ITEMS, ...MARQUEE_ITEMS].map((item, i) => (
|
||||||
<span key={i} className="flex items-center gap-12 font-serif text-2xl italic text-cloud-2 md:text-3xl">
|
<span key={i} className="flex items-center gap-12 font-serif text-2xl italic text-ink-light md:text-3xl">
|
||||||
{item}
|
{item}
|
||||||
<span className="inline-block h-1.5 w-1.5 rounded-full bg-gradient-to-r from-iris to-rose" />
|
<span className="inline-block h-1.5 w-1.5 rounded-full" style={{ background: 'linear-gradient(90deg,#03C75A,#00C4A7)' }} />
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -249,62 +248,48 @@ export default function HomePage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* FOR WHO — 3 big role cards
|
* FOR WHO — 3 role cards (desktop primary)
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative mx-auto max-w-7xl px-6 py-28">
|
<section className="relative mx-auto max-w-7xl px-6 py-20 md:py-28">
|
||||||
<div className="mb-16 flex flex-col items-start justify-between gap-6 md:flex-row md:items-end">
|
<div className="mb-12 flex flex-col items-start justify-between gap-6 md:flex-row md:items-end">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-4">
|
<p className="font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">
|
||||||
<span className="text-grad-primary">◆</span> 01 · For Who
|
<span className="text-grad-primary">◆</span> 01 · For Who
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-cloud-0 sm:text-5xl md:text-6xl">
|
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-ink sm:text-5xl md:text-6xl">
|
||||||
<span className="font-serif italic font-normal text-cloud-2">누구</span>를 위한
|
<span className="font-serif italic font-normal text-ink-light">누구</span>를 위한
|
||||||
<br />
|
<br />
|
||||||
서비스인가요
|
서비스인가요
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="max-w-md text-sm leading-relaxed text-cloud-2">
|
<p className="max-w-md text-sm leading-relaxed text-ink-light">
|
||||||
폐업자 · 창업자 · 업체 — 매장 거래의 세 주체가 한 플랫폼에서
|
폐업자 · 창업자 · 업체 — 매장 거래의 세 주체가 한 플랫폼에서
|
||||||
안전하게 연결됩니다.
|
안전하게 연결됩니다.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
|
||||||
{FEATURES.map((f, i) => (
|
{[
|
||||||
|
{ label: '폐업자', english: 'Closing owners', title: '깔끔한 마무리,\n새로운 시작', desc: '매장 정보를 등록하면 철거비 절감, 시설 처분, 지원금 신청까지 한 번에 해결됩니다.', href: '/stores/new', cta: '매장 등록', num: '01' },
|
||||||
|
{ label: '창업자', english: 'Founders', title: '검증된 매장,\n합리적 시작', desc: '검증된 매장을 검색하고 매칭 요청을 보내 시설 인수와 인테리어 비용을 절감하세요.', href: '/stores', cta: '매장 검색', num: '02' },
|
||||||
|
{ label: '철거·인테리어 업체', english: 'Partners', title: '안정적 수주,\n정확한 정산', desc: '인증 업체로 등록하면 안정적인 수주와 정산을 보장받을 수 있습니다.', href: '/vendors', cta: '업체 인증', num: '03' },
|
||||||
|
].map((f) => (
|
||||||
<Link
|
<Link
|
||||||
key={f.num}
|
key={f.num}
|
||||||
href={f.href}
|
href={f.href}
|
||||||
className="grad-border group relative overflow-hidden p-8"
|
className="grad-border group relative overflow-hidden p-8"
|
||||||
style={{ minHeight: 380 }}
|
style={{ minHeight: 360 }}
|
||||||
>
|
>
|
||||||
{/* Glow accent */}
|
<div className="pointer-events-none absolute -top-20 -right-20 h-48 w-48 rounded-full opacity-50 blur-3xl transition-opacity group-hover:opacity-80" style={{ background: 'radial-gradient(circle,#03C75A,transparent 70%)' }} />
|
||||||
<div
|
<span className="font-mono text-[10px] tracking-[0.25em] uppercase text-ink-muted">{f.english}</span>
|
||||||
className={`pointer-events-none absolute -top-20 -right-20 h-48 w-48 rounded-full opacity-30 blur-3xl transition-opacity group-hover:opacity-70 ${
|
<p className="mt-2 inline-flex items-center gap-2 text-xs font-medium tracking-widest uppercase text-ink-light">
|
||||||
f.glow === 'iris' ? 'bg-iris' : f.glow === 'violet' ? 'bg-violet' : 'bg-rose'
|
<span className="font-serif text-5xl font-normal not-italic leading-none text-grad-primary">{f.num}</span>
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className="font-mono text-[10px] tracking-[0.25em] uppercase text-cloud-4">
|
|
||||||
{f.english}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<p className="mt-2 inline-flex items-center gap-2 text-xs font-medium tracking-widest uppercase text-cloud-2">
|
|
||||||
<span className="font-serif text-5xl font-normal not-italic leading-none text-grad-primary">
|
|
||||||
{f.num}
|
|
||||||
</span>
|
|
||||||
<span className="ml-2">{f.label}</span>
|
<span className="ml-2">{f.label}</span>
|
||||||
</p>
|
</p>
|
||||||
|
<h3 className="mt-8 font-display text-2xl font-bold leading-[1.2] whitespace-pre-line text-ink">{f.title}</h3>
|
||||||
<h3 className="mt-8 font-display text-2xl font-bold leading-[1.2] whitespace-pre-line text-cloud-0">
|
<p className="mt-4 text-sm leading-relaxed text-ink-light">{f.desc}</p>
|
||||||
{f.title}
|
<span className="absolute bottom-8 left-8 inline-flex items-center gap-2 text-sm font-semibold text-ink transition-transform group-hover:translate-x-1">
|
||||||
</h3>
|
{f.cta}<span className="text-grad-primary text-base">→</span>
|
||||||
<p className="mt-4 text-sm leading-relaxed text-cloud-2">
|
|
||||||
{f.desc}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<span className="absolute bottom-8 left-8 inline-flex items-center gap-2 text-sm font-semibold text-cloud-1 transition-transform group-hover:translate-x-1">
|
|
||||||
{f.cta}
|
|
||||||
<span className="text-grad-primary text-base">→</span>
|
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
@@ -314,24 +299,23 @@ export default function HomePage() {
|
|||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* PROCESS
|
* PROCESS
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative border-y border-white/5 bg-night-1">
|
<section className="relative border-y border-line" style={{ background: '#F7FAF8' }}>
|
||||||
{/* subtle decoration */}
|
|
||||||
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-50">
|
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-50">
|
||||||
<div className="orb orb-iris absolute -top-20 left-0 h-80 w-80" />
|
<div className="orb orb-iris absolute -top-20 left-0 h-80 w-80" />
|
||||||
<div className="orb orb-tangerine absolute -bottom-20 right-0 h-80 w-80" />
|
<div className="orb orb-tangerine absolute -bottom-20 right-0 h-80 w-80" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative mx-auto max-w-7xl px-6 py-28">
|
<div className="relative mx-auto max-w-7xl px-6 py-20 md:py-28">
|
||||||
<div className="mb-16 max-w-3xl">
|
<div className="mb-12 max-w-3xl">
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-4">
|
<p className="font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">
|
||||||
<span className="text-grad-primary">◆</span> 02 · Process
|
<span className="text-grad-primary">◆</span> 02 · Process
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-cloud-0 sm:text-5xl md:text-6xl">
|
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-ink sm:text-5xl md:text-6xl">
|
||||||
매장 한 번,
|
매장 한 번,
|
||||||
<br />
|
<br />
|
||||||
<span className="text-grad-rainbow">세 단계</span>로 완료
|
<span className="text-grad-rainbow">세 단계</span>로 완료
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-6 text-cloud-2">
|
<p className="mt-6 text-ink-light">
|
||||||
인수 · 양도 · 철거 · 인테리어 · 지원금까지 흩어진 절차를
|
인수 · 양도 · 철거 · 인테리어 · 지원금까지 흩어진 절차를
|
||||||
하나의 흐름으로 묶었습니다.
|
하나의 흐름으로 묶었습니다.
|
||||||
</p>
|
</p>
|
||||||
@@ -340,24 +324,20 @@ export default function HomePage() {
|
|||||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
|
||||||
{PROCESS_STEPS.map((step, i) => (
|
{PROCESS_STEPS.map((step, i) => (
|
||||||
<div key={step.num} className="relative">
|
<div key={step.num} className="relative">
|
||||||
{/* Connector line (only between cards on desktop) */}
|
|
||||||
{i < PROCESS_STEPS.length - 1 && (
|
{i < PROCESS_STEPS.length - 1 && (
|
||||||
<div className="hidden md:block absolute top-14 left-full z-0 h-px w-6 bg-gradient-to-r from-white/10 to-transparent" />
|
<div className="hidden md:block absolute top-14 left-full z-0 h-px w-6 bg-gradient-to-r from-naver to-transparent" style={{ background: 'linear-gradient(90deg,rgba(3,199,90,0.35),transparent)' }} />
|
||||||
)}
|
)}
|
||||||
<div className="glass-darker relative z-10 rounded-2xl p-8">
|
<div className="relative z-10 rounded-2xl border border-line bg-white p-8 shadow-[0_2px_12px_-8px_rgba(15,29,23,0.1)]">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<span
|
<span
|
||||||
className={`inline-flex h-14 w-14 items-center justify-center rounded-xl bg-gradient-to-br ${step.accent} font-mono text-lg font-bold text-white shadow-[0_8px_32px_-8px_rgba(168,85,247,0.45)]`}
|
className="inline-flex h-14 w-14 items-center justify-center rounded-xl font-mono text-lg font-bold text-white shadow-[0_10px_24px_-8px_rgba(3,199,90,0.5)]"
|
||||||
|
style={{ background: 'linear-gradient(135deg,#03C75A 0%,#02A149 60%,#018f40 100%)' }}
|
||||||
>
|
>
|
||||||
{step.num}
|
{step.num}
|
||||||
</span>
|
</span>
|
||||||
<h3 className="font-display text-2xl font-bold text-cloud-0">
|
<h3 className="font-display text-2xl font-bold text-ink">{step.title}</h3>
|
||||||
{step.title}
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-5 text-sm leading-relaxed text-cloud-2">
|
<p className="mt-5 text-sm leading-relaxed text-ink-light">{step.desc}</p>
|
||||||
{step.desc}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -368,24 +348,24 @@ export default function HomePage() {
|
|||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* STATS
|
* STATS
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative mx-auto max-w-7xl px-6 py-28">
|
<section className="relative mx-auto max-w-7xl px-6 py-20 md:py-28">
|
||||||
<div className="mb-14">
|
<div className="mb-12">
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-4">
|
<p className="font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">
|
||||||
<span className="text-grad-primary">◆</span> 03 · Numbers
|
<span className="text-grad-primary">◆</span> 03 · Numbers
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-cloud-0 sm:text-5xl">
|
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-ink sm:text-5xl">
|
||||||
숫자로 보는 <span className="font-serif italic font-normal text-cloud-2">startover</span>
|
숫자로 보는 <span className="font-serif italic font-normal text-ink-light">startover</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-6 md:grid-cols-4">
|
<div className="grid grid-cols-2 gap-6 md:grid-cols-4">
|
||||||
{STATS.map((s, i) => (
|
{STATS.map((s) => (
|
||||||
<div key={s.label} className="glass-darker rounded-2xl p-7">
|
<div key={s.label} className="rounded-2xl border border-line bg-white p-7 shadow-[0_2px_12px_-8px_rgba(15,29,23,0.1)]">
|
||||||
<p className="flex items-baseline gap-1 font-display">
|
<p className="flex items-baseline gap-1 font-display">
|
||||||
<span className="text-6xl font-black tracking-tight text-grad-primary">{s.value}</span>
|
<span className="text-6xl font-black tracking-tight text-grad-primary">{s.value}</span>
|
||||||
<span className="text-xl font-bold text-cloud-2">{s.unit}</span>
|
<span className="text-xl font-bold text-ink-light">{s.unit}</span>
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-4 text-sm font-semibold text-cloud-1">{s.label}</p>
|
<p className="mt-4 text-sm font-semibold text-ink">{s.label}</p>
|
||||||
<p className="mt-1 text-xs text-cloud-3">{s.sub}</p>
|
<p className="mt-1 text-xs text-ink-muted">{s.sub}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -394,40 +374,41 @@ export default function HomePage() {
|
|||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* CATEGORIES
|
* CATEGORIES
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative border-y border-white/5 bg-night-1">
|
<section className="relative border-y border-line" style={{ background: '#F7FAF8' }}>
|
||||||
<div className="pointer-events-none absolute inset-0 bg-dot opacity-40" />
|
<div className="pointer-events-none absolute inset-0 bg-dot opacity-40" />
|
||||||
<div className="relative mx-auto max-w-7xl px-6 py-28">
|
<div className="relative mx-auto max-w-7xl px-6 py-20 md:py-28">
|
||||||
<div className="mb-14 flex flex-col items-start justify-between gap-6 md:flex-row md:items-end">
|
<div className="mb-12 flex flex-col items-start justify-between gap-6 md:flex-row md:items-end">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-4">
|
<p className="font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">
|
||||||
<span className="text-grad-primary">◆</span> 04 · Categories
|
<span className="text-grad-primary">◆</span> 04 · Categories
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-cloud-0 sm:text-5xl">
|
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-ink sm:text-5xl">
|
||||||
지원 <span className="text-grad-rose">업종</span>
|
지원 <span className="text-grad-primary">업종</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-5 max-w-xl text-cloud-2">
|
<p className="mt-5 max-w-xl text-ink-light">
|
||||||
휴게음식점부터 기타업종까지 7개 대분류,
|
휴게음식점부터 기타업종까지 7개 대분류,
|
||||||
49개 소분류를 지원합니다.
|
49개 소분류를 지원합니다.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href="/stores"
|
href="/stores"
|
||||||
className="link-grad hidden text-sm font-semibold text-cloud-1 md:inline-flex md:items-center md:gap-2"
|
className="link-grad hidden text-sm font-semibold text-ink md:inline-flex md:items-center md:gap-2"
|
||||||
>
|
>
|
||||||
전체 매장 보기 <span className="text-grad-primary">→</span>
|
전체 매장 보기 <span className="text-grad-primary">→</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-3 md:grid-cols-4 lg:grid-cols-7">
|
<div className="grid grid-cols-2 gap-3 md:grid-cols-4 lg:grid-cols-7">
|
||||||
{INDUSTRY_MAJORS.map((m, i) => (
|
{INDUSTRY_MAJORS.map((m) => (
|
||||||
<Link
|
<Link
|
||||||
key={m.code}
|
key={m.code}
|
||||||
href={`/stores?industryMajor=${m.code}`}
|
href={`/stores?industryMajor=${m.code}`}
|
||||||
className="group relative overflow-hidden rounded-2xl border border-white/[0.08] bg-white/[0.02] p-5 text-center transition-all hover:-translate-y-1 hover:border-white/20 hover:bg-white/[0.05]"
|
className="group relative overflow-hidden rounded-2xl border border-line bg-white p-5 text-center transition-all hover:-translate-y-1 hover:border-naver hover:shadow-[0_12px_24px_-12px_rgba(3,199,90,0.3)]"
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
>
|
>
|
||||||
<p className="font-display text-base font-bold text-cloud-0 transition-colors group-hover:text-grad-primary">
|
<p className="font-display text-base font-bold text-ink transition-colors group-hover:text-grad-primary">
|
||||||
{m.label}
|
{m.label}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-1 font-mono text-[10px] text-cloud-4">
|
<p className="mt-1 font-mono text-[10px] text-ink-muted">
|
||||||
{m.children.length} sub
|
{m.children.length} sub
|
||||||
</p>
|
</p>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -439,12 +420,12 @@ export default function HomePage() {
|
|||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* BENEFITS
|
* BENEFITS
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative mx-auto max-w-7xl px-6 py-28">
|
<section className="relative mx-auto max-w-7xl px-6 py-20 md:py-28">
|
||||||
<div className="mb-16 max-w-3xl">
|
<div className="mb-12 max-w-3xl">
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-4">
|
<p className="font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">
|
||||||
<span className="text-grad-primary">◆</span> 05 · Benefits
|
<span className="text-grad-primary">◆</span> 05 · Benefits
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-cloud-0 sm:text-5xl md:text-6xl">
|
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-ink sm:text-5xl md:text-6xl">
|
||||||
각자에게 필요한
|
각자에게 필요한
|
||||||
<br />
|
<br />
|
||||||
<span className="text-grad-primary">혜택</span>
|
<span className="text-grad-primary">혜택</span>
|
||||||
@@ -453,35 +434,18 @@ export default function HomePage() {
|
|||||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
|
||||||
{BENEFITS.map((b) => (
|
{BENEFITS.map((b) => (
|
||||||
<div key={b.label} className="grad-border group relative overflow-hidden p-8">
|
<div key={b.label} className="grad-border group relative overflow-hidden p-8">
|
||||||
<div
|
<div className="pointer-events-none absolute -top-24 -right-16 h-48 w-48 rounded-full opacity-40 blur-3xl transition-opacity group-hover:opacity-70" style={{ background: 'radial-gradient(circle,#03C75A,transparent 70%)' }} />
|
||||||
className={`pointer-events-none absolute -top-24 -right-16 h-48 w-48 rounded-full opacity-25 blur-3xl transition-opacity group-hover:opacity-60 ${
|
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-ink-muted">{b.english}</p>
|
||||||
b.accent === 'iris' ? 'bg-iris' : b.accent === 'violet' ? 'bg-violet' : 'bg-rose'
|
<p className="mt-1 text-xs font-semibold tracking-widest uppercase text-ink-light">{b.label}</p>
|
||||||
}`}
|
<h3 className="mt-4 font-display text-3xl font-bold text-ink">{b.title}</h3>
|
||||||
/>
|
|
||||||
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-cloud-4">
|
|
||||||
{b.english}
|
|
||||||
</p>
|
|
||||||
<p className="mt-1 text-xs font-semibold tracking-widest uppercase text-cloud-2">
|
|
||||||
{b.label}
|
|
||||||
</p>
|
|
||||||
<h3 className="mt-4 font-display text-3xl font-bold text-cloud-0">
|
|
||||||
{b.title}
|
|
||||||
</h3>
|
|
||||||
<ul className="mt-7 space-y-4">
|
<ul className="mt-7 space-y-4">
|
||||||
{b.points.map((p, idx) => (
|
{b.points.map((p) => (
|
||||||
<li key={p} className="flex gap-3 text-sm leading-relaxed text-cloud-2">
|
<li key={p} className="flex gap-3 text-sm leading-relaxed text-ink-light">
|
||||||
<span
|
<span
|
||||||
className="mt-1.5 h-2 w-2 shrink-0 rounded-full"
|
className="mt-1.5 h-2 w-2 shrink-0 rounded-full"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background: 'linear-gradient(135deg,#03C75A,#02A149)',
|
||||||
b.accent === 'iris'
|
boxShadow: '0 0 8px rgba(3,199,90,0.6)',
|
||||||
? 'linear-gradient(135deg,#818cf8,#6366f1)'
|
|
||||||
: b.accent === 'violet'
|
|
||||||
? 'linear-gradient(135deg,#c084fc,#a855f7)'
|
|
||||||
: 'linear-gradient(135deg,#f472b6,#ec4899)',
|
|
||||||
boxShadow: `0 0 8px ${
|
|
||||||
b.accent === 'iris' ? '#6366f1' : b.accent === 'violet' ? '#a855f7' : '#ec4899'
|
|
||||||
}`,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{p}
|
{p}
|
||||||
@@ -493,76 +457,26 @@ export default function HomePage() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ==========================================================
|
|
||||||
* SERVICES
|
|
||||||
* ========================================================== */}
|
|
||||||
<section className="relative border-y border-white/5 bg-night-1">
|
|
||||||
<div className="relative mx-auto max-w-7xl px-6 py-28">
|
|
||||||
<div className="mb-16 max-w-3xl">
|
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-4">
|
|
||||||
<span className="text-grad-primary">◆</span> 06 · Services
|
|
||||||
</p>
|
|
||||||
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-cloud-0 sm:text-5xl">
|
|
||||||
더 나아간 지원
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
||||||
{SERVICES.map((s) => (
|
|
||||||
<Link
|
|
||||||
key={s.title}
|
|
||||||
href={s.href}
|
|
||||||
className="grad-border group relative flex gap-6 p-8"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl text-white shadow-[0_10px_30px_-8px_rgba(168,85,247,0.5)]"
|
|
||||||
style={{
|
|
||||||
background:
|
|
||||||
'linear-gradient(135deg, #6366f1 0%, #a855f7 50%, #ec4899 100%)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{s.icon}
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<p className="font-mono text-[10px] tracking-[0.25em] uppercase text-cloud-4">
|
|
||||||
{s.tag}
|
|
||||||
</p>
|
|
||||||
<h3 className="mt-1 font-display text-2xl font-bold text-cloud-0">
|
|
||||||
{s.title}
|
|
||||||
</h3>
|
|
||||||
<p className="mt-3 text-sm leading-relaxed text-cloud-2">
|
|
||||||
{s.desc}
|
|
||||||
</p>
|
|
||||||
<span className="link-grad mt-5 inline-flex items-center gap-2 text-sm font-semibold text-cloud-1">
|
|
||||||
{s.cta}
|
|
||||||
<span className="text-grad-primary">→</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* BLOG
|
* BLOG
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative mx-auto max-w-7xl px-6 py-28">
|
<section className="relative mx-auto max-w-7xl px-6 py-20 md:py-28">
|
||||||
<div className="mb-14 flex flex-col items-start justify-between gap-6 md:flex-row md:items-end">
|
<div className="mb-12 flex flex-col items-start justify-between gap-6 md:flex-row md:items-end">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-4">
|
<p className="font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">
|
||||||
<span className="text-grad-primary">◆</span> 07 · Journal
|
<span className="text-grad-primary">◆</span> 06 · Journal
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-cloud-0 sm:text-5xl">
|
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-ink sm:text-5xl">
|
||||||
실무 <span className="font-serif italic font-normal text-cloud-2">가이드</span>
|
실무 <span className="font-serif italic font-normal text-ink-light">가이드</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-5 max-w-xl text-cloud-2">
|
<p className="mt-5 max-w-xl text-ink-light">
|
||||||
폐업 절차, 지원금 신청, 상권 분석 — 현장에서 자주 묻는
|
폐업 절차, 지원금 신청, 상권 분석 — 현장에서 자주 묻는
|
||||||
질문을 글로 정리합니다.
|
질문을 글로 정리합니다.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href="/blog"
|
href="/blog"
|
||||||
className="link-grad hidden text-sm font-semibold text-cloud-1 md:inline-flex md:items-center md:gap-2"
|
className="link-grad hidden text-sm font-semibold text-ink md:inline-flex md:items-center md:gap-2"
|
||||||
>
|
>
|
||||||
블로그 전체 보기 <span className="text-grad-primary">→</span>
|
블로그 전체 보기 <span className="text-grad-primary">→</span>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -572,18 +486,19 @@ export default function HomePage() {
|
|||||||
<Link
|
<Link
|
||||||
key={post.slug}
|
key={post.slug}
|
||||||
href={`/blog/${post.slug}`}
|
href={`/blog/${post.slug}`}
|
||||||
className="group flex flex-col rounded-2xl border border-white/[0.08] bg-white/[0.02] p-6 transition-all hover:-translate-y-1 hover:border-white/20 hover:bg-white/[0.05]"
|
className="group flex flex-col rounded-2xl border border-line bg-white p-6 transition-all hover:-translate-y-1 hover:border-naver hover:shadow-[0_18px_30px_-18px_rgba(3,199,90,0.35)]"
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
>
|
>
|
||||||
<span className="inline-flex w-fit rounded-full border border-white/10 bg-white/[0.04] px-2.5 py-1 font-mono text-[10px] tracking-widest uppercase text-cloud-2">
|
<span className="inline-flex w-fit rounded-full border border-line bg-surface-soft px-2.5 py-1 font-mono text-[10px] tracking-widest uppercase text-ink-light" style={{ background: '#F2FBF6', borderColor: '#D5EEE0' }}>
|
||||||
{post.category}
|
{post.category}
|
||||||
</span>
|
</span>
|
||||||
<h3 className="mt-4 flex-1 font-display font-bold leading-snug text-cloud-0 transition-colors group-hover:text-grad-primary line-clamp-2">
|
<h3 className="mt-4 flex-1 font-display font-bold leading-snug text-ink transition-colors group-hover:text-grad-primary line-clamp-2">
|
||||||
{post.title}
|
{post.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mt-3 text-xs leading-relaxed text-cloud-3 line-clamp-3">
|
<p className="mt-3 text-xs leading-relaxed text-ink-muted line-clamp-3">
|
||||||
{post.description}
|
{post.description}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-5 font-mono text-[10px] tracking-wider text-cloud-4">
|
<p className="mt-5 font-mono text-[10px] tracking-wider text-ink-muted">
|
||||||
{post.publishedAt} · {post.readMinutes}min
|
{post.publishedAt} · {post.readMinutes}min
|
||||||
</p>
|
</p>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -594,17 +509,17 @@ export default function HomePage() {
|
|||||||
{/* ==========================================================
|
{/* ==========================================================
|
||||||
* FAQ
|
* FAQ
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative border-y border-white/5 bg-night-1">
|
<section className="relative border-y border-line" style={{ background: '#F7FAF8' }}>
|
||||||
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-60">
|
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-60">
|
||||||
<div className="orb orb-violet absolute top-0 right-0 h-96 w-96" />
|
<div className="orb orb-violet absolute top-0 right-0 h-96 w-96" />
|
||||||
<div className="orb orb-iris absolute bottom-0 left-0 h-96 w-96" />
|
<div className="orb orb-iris absolute bottom-0 left-0 h-96 w-96" />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative mx-auto max-w-4xl px-6 py-28">
|
<div className="relative mx-auto max-w-4xl px-6 py-20 md:py-28">
|
||||||
<div className="mb-14 text-center">
|
<div className="mb-12 text-center">
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-4">
|
<p className="font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">
|
||||||
<span className="text-grad-primary">◆</span> 08 · FAQ
|
<span className="text-grad-primary">◆</span> 07 · FAQ
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-cloud-0 sm:text-5xl">
|
<h2 className="mt-4 font-display text-4xl font-black tracking-tight text-ink sm:text-5xl">
|
||||||
자주 묻는 <span className="text-grad-primary">질문</span>
|
자주 묻는 <span className="text-grad-primary">질문</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -612,22 +527,21 @@ export default function HomePage() {
|
|||||||
{FAQ_PREVIEW.map((f, i) => (
|
{FAQ_PREVIEW.map((f, i) => (
|
||||||
<details
|
<details
|
||||||
key={i}
|
key={i}
|
||||||
className="group glass-darker rounded-2xl p-6 open:border-white/20 transition-colors"
|
className="group rounded-2xl border border-line bg-white p-6 open:border-naver transition-colors shadow-[0_2px_12px_-8px_rgba(15,29,23,0.08)]"
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
>
|
>
|
||||||
<summary className="flex cursor-pointer items-start justify-between gap-4 list-none">
|
<summary className="flex cursor-pointer items-start justify-between gap-4 list-none">
|
||||||
<span className="font-display text-base font-semibold text-cloud-0">{f.q}</span>
|
<span className="font-display text-base font-semibold text-ink">{f.q}</span>
|
||||||
<span className="mt-1 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full border border-white/15 text-sm text-cloud-2 transition-transform group-open:rotate-45 group-open:border-white/30 group-open:text-cloud-0">
|
<span className="mt-1 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full border border-line text-sm text-ink-light transition-transform group-open:rotate-45" style={{ borderColor: '#E4E8E6' }}>+</span>
|
||||||
+
|
|
||||||
</span>
|
|
||||||
</summary>
|
</summary>
|
||||||
<p className="mt-4 text-sm leading-relaxed text-cloud-2">{f.a}</p>
|
<p className="mt-4 text-sm leading-relaxed text-ink-light">{f.a}</p>
|
||||||
</details>
|
</details>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-12 text-center">
|
<div className="mt-10 text-center">
|
||||||
<Link
|
<Link
|
||||||
href="/faq"
|
href="/faq"
|
||||||
className="inline-flex items-center gap-2 font-mono text-xs tracking-widest uppercase text-cloud-2 hover:text-cloud-0 transition-colors"
|
className="inline-flex items-center gap-2 font-mono text-xs tracking-widest uppercase text-ink-light hover:text-naver transition-colors"
|
||||||
>
|
>
|
||||||
전체 FAQ 보기 <span className="text-grad-primary">→</span>
|
전체 FAQ 보기 <span className="text-grad-primary">→</span>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -639,26 +553,22 @@ export default function HomePage() {
|
|||||||
* FINAL CTA
|
* FINAL CTA
|
||||||
* ========================================================== */}
|
* ========================================================== */}
|
||||||
<section className="relative overflow-hidden">
|
<section className="relative overflow-hidden">
|
||||||
{/* Massive gradient background */}
|
|
||||||
<div
|
<div
|
||||||
className="pointer-events-none absolute inset-0 opacity-90"
|
className="pointer-events-none absolute inset-0 opacity-95"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
'radial-gradient(ellipse at 30% 40%, #6366f1 0%, transparent 55%), radial-gradient(ellipse at 70% 60%, #ec4899 0%, transparent 55%), radial-gradient(ellipse at 50% 100%, #f97316 0%, transparent 65%), #05050f',
|
'radial-gradient(ellipse at 30% 40%, rgba(3,199,90,0.2) 0%, transparent 55%), radial-gradient(ellipse at 70% 60%, rgba(0,196,167,0.18) 0%, transparent 55%), radial-gradient(ellipse at 50% 100%, rgba(43,214,115,0.14) 0%, transparent 65%), #ffffff',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="pointer-events-none absolute inset-0 bg-grid opacity-30" />
|
<div className="pointer-events-none absolute inset-0 bg-grid opacity-40" />
|
||||||
<div className="pointer-events-none absolute inset-0 bg-radial-fade opacity-50" />
|
|
||||||
|
|
||||||
<div className="relative mx-auto max-w-5xl px-6 py-32 text-center">
|
<div className="relative mx-auto max-w-5xl px-6 py-24 md:py-32 text-center">
|
||||||
<p className="font-mono text-xs tracking-[0.3em] uppercase text-cloud-3">
|
<p className="font-mono text-xs tracking-[0.3em] uppercase text-ink-muted">Get started</p>
|
||||||
Get started
|
<h2 className="mt-5 font-display text-5xl font-black tracking-tight text-ink sm:text-6xl md:text-7xl">
|
||||||
</p>
|
<span className="font-serif italic font-normal text-ink">지금</span>,<br />
|
||||||
<h2 className="mt-5 font-display text-5xl font-black tracking-tight text-cloud-0 sm:text-6xl md:text-7xl">
|
|
||||||
<span className="font-serif italic font-normal text-cloud-0">지금</span>,<br />
|
|
||||||
<span className="text-grad-rainbow">새로 시작</span>하세요
|
<span className="text-grad-rainbow">새로 시작</span>하세요
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mx-auto mt-8 max-w-2xl text-lg leading-relaxed text-cloud-2">
|
<p className="mx-auto mt-8 max-w-2xl text-lg leading-relaxed text-ink-light">
|
||||||
한 번의 등록으로 인수자 · 철거 · 인테리어 · 지원금까지.
|
한 번의 등록으로 인수자 · 철거 · 인테리어 · 지원금까지.
|
||||||
<br className="hidden md:block" />
|
<br className="hidden md:block" />
|
||||||
흩어진 절차를 한 흐름으로 정돈합니다.
|
흩어진 절차를 한 흐름으로 정돈합니다.
|
||||||
|
|||||||
@@ -62,6 +62,15 @@ function formatPaybackMonths(premium?: number | null, profit?: number | null): s
|
|||||||
return `${months.toFixed(1)}개월`;
|
return `${months.toFixed(1)}개월`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STATUS_META: Record<string, { label: string; bg: string; fg: string }> = {
|
||||||
|
OPEN: { label: '거래 가능', bg: '#03C75A', fg: '#ffffff' },
|
||||||
|
MATCHING: { label: '매칭 중', bg: '#00C4A7', fg: '#ffffff' },
|
||||||
|
RESERVED: { label: '예약', bg: '#84E1A1', fg: '#0F1D17' },
|
||||||
|
CONTRACTED: { label: '계약 진행 중', bg: '#0AB070', fg: '#ffffff' },
|
||||||
|
CLOSED: { label: '거래 완료', bg: '#7B8581', fg: '#ffffff' },
|
||||||
|
CANCELLED: { label: '취소', bg: '#E4E8E6', fg: '#0F1D17' },
|
||||||
|
};
|
||||||
|
|
||||||
export default async function StoreDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
export default async function StoreDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
const prisma = createPrismaClient();
|
const prisma = createPrismaClient();
|
||||||
@@ -86,21 +95,7 @@ export default async function StoreDetailPage({ params }: { params: Promise<{ id
|
|||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusLabel =
|
const status = STATUS_META[store.dealStatus] ?? { label: store.dealStatus, bg: '#E4E8E6', fg: '#0F1D17' };
|
||||||
store.dealStatus === 'OPEN'
|
|
||||||
? '거래 가능'
|
|
||||||
: store.dealStatus === 'MATCHING'
|
|
||||||
? '매칭 중'
|
|
||||||
: store.dealStatus === 'CONTRACTED'
|
|
||||||
? '계약 진행 중'
|
|
||||||
: store.dealStatus;
|
|
||||||
|
|
||||||
const statusClass =
|
|
||||||
store.dealStatus === 'OPEN'
|
|
||||||
? 'bg-sage-500/10 text-sage-600'
|
|
||||||
: store.dealStatus === 'MATCHING'
|
|
||||||
? 'bg-warm-400/15 text-warm-700'
|
|
||||||
: 'bg-ink/5 text-ink-muted';
|
|
||||||
|
|
||||||
const industryLabel = [
|
const industryLabel = [
|
||||||
store.industryLeaf?.parent?.nameKo,
|
store.industryLeaf?.parent?.nameKo,
|
||||||
@@ -114,182 +109,129 @@ export default async function StoreDetailPage({ params }: { params: Promise<{ id
|
|||||||
const profit = store.sale?.monthlyProfitAmount ?? null;
|
const profit = store.sale?.monthlyProfitAmount ?? null;
|
||||||
const startup = store.sale?.startupCostAmount ?? null;
|
const startup = store.sale?.startupCostAmount ?? null;
|
||||||
|
|
||||||
|
const heroPhoto = store.photos.find((p) => p.isRepresentative) ?? store.photos[0] ?? null;
|
||||||
|
const restPhotos = store.photos.filter((p) => p.id !== heroPhoto?.id).slice(0, 8);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="mx-auto max-w-4xl px-6 py-10 font-body">
|
<main className="mx-auto max-w-5xl pb-12 font-body">
|
||||||
<div className="mb-6">
|
{/* ========================================================== *
|
||||||
<Link href="/stores" className="text-sm text-warm-600 hover:text-warm-700">
|
* PHOTO HERO (mobile first — 직방/다방 style)
|
||||||
← 매장 목록으로
|
* ========================================================== */}
|
||||||
</Link>
|
<section className="relative">
|
||||||
|
<div className="photo-hero">
|
||||||
|
{heroPhoto ? (
|
||||||
|
/* eslint-disable-next-line @next/next/no-img-element */
|
||||||
|
<img
|
||||||
|
src={heroPhoto.storageKey}
|
||||||
|
alt={store.listingTitle}
|
||||||
|
className="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center text-sm text-ink-muted">
|
||||||
|
사진 준비 중
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className="animate-fade-up rounded-2xl border border-ink/5 bg-white/70 backdrop-blur-sm p-8">
|
<span
|
||||||
<div className="flex items-start justify-between">
|
className="absolute left-4 top-4 rounded-full px-3 py-1 text-xs font-semibold shadow-[0_6px_14px_-6px_rgba(15,29,23,0.25)]"
|
||||||
<div>
|
style={{ background: status.bg, color: status.fg }}
|
||||||
<h1 className="font-display text-3xl font-bold text-ink">{store.listingTitle}</h1>
|
>
|
||||||
<p className="mt-1 text-sm text-ink-muted">매장 ID: {store.publicId}</p>
|
{status.label}
|
||||||
</div>
|
|
||||||
<span className={`rounded-full px-3 py-1 text-sm font-medium ${statusClass}`}>
|
|
||||||
{statusLabel}
|
|
||||||
</span>
|
</span>
|
||||||
|
<Link
|
||||||
|
href="/stores"
|
||||||
|
className="absolute right-4 top-4 inline-flex h-9 w-9 items-center justify-center rounded-full bg-white/90 text-ink shadow-[0_6px_14px_-6px_rgba(15,29,23,0.3)] backdrop-blur"
|
||||||
|
aria-label="목록으로"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</Link>
|
||||||
|
{store.photos.length > 0 && (
|
||||||
|
<span className="absolute bottom-4 right-4 inline-flex items-center gap-1 rounded-full bg-black/55 px-3 py-1 text-xs font-semibold text-white">
|
||||||
|
📷 {store.photos.length}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 기본 정보 */}
|
{/* Thumbnail strip (photos 2..9) */}
|
||||||
<section className="mt-8">
|
{restPhotos.length > 0 && (
|
||||||
<h2 className="font-display text-xl font-bold text-ink mb-4">기본 정보</h2>
|
<div className="mt-3 flex gap-2 overflow-x-auto px-4 pb-1 md:px-0">
|
||||||
<div className="grid grid-cols-2 gap-4">
|
{restPhotos.map((photo) => (
|
||||||
<InfoItem label="지역" value={store.regionCluster?.nameKo ?? '-'} />
|
<div
|
||||||
<InfoItem label="업종" value={industryLabel || '-'} />
|
key={photo.id.toString()}
|
||||||
<InfoItem label="주소" value={store.roadAddress} />
|
className="h-20 w-28 shrink-0 overflow-hidden rounded-xl"
|
||||||
<InfoItem label="등록일" value={store.createdAt.toLocaleDateString('ko-KR')} />
|
style={{ background: '#EFF2F0' }}
|
||||||
|
>
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img
|
||||||
|
src={photo.storageKey}
|
||||||
|
alt="매장 사진"
|
||||||
|
className="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* 매매 정보 */}
|
{/* ========================================================== *
|
||||||
<section className="mt-8 rounded-2xl border border-warm-300/40 bg-warm-50/70 p-6">
|
* SUMMARY BAR (price, status, title)
|
||||||
<h2 className="font-display text-xl font-bold text-ink mb-4">매매 정보</h2>
|
* ========================================================== */}
|
||||||
|
<section className="px-5 pt-5 md:px-0">
|
||||||
|
<p className="text-xs text-ink-muted">
|
||||||
|
{store.regionCluster?.nameKo ?? '-'}
|
||||||
|
{industryLabel ? ` · ${industryLabel}` : ''}
|
||||||
|
</p>
|
||||||
|
<h1 className="mt-1 font-display text-2xl font-extrabold tracking-tight text-ink md:text-3xl">
|
||||||
|
{store.listingTitle}
|
||||||
|
</h1>
|
||||||
|
|
||||||
{/* 핵심 지표 (첫번째 캡쳐) */}
|
<div className="mt-5 grid grid-cols-2 gap-3 md:grid-cols-4">
|
||||||
<div className="grid grid-cols-2 gap-4 md:grid-cols-3">
|
<KpiTile label="권리금" value={formatKRW(premium != null ? Number(premium) : null)} accent />
|
||||||
<BigInfoItem label="권리금" value={formatKRW(premium != null ? Number(premium) : null)} accent />
|
<KpiTile label="월수익" value={formatKRW(profit != null ? Number(profit) : null)} accent />
|
||||||
<BigInfoItem label="창업비용" value={formatKRW(startup != null ? Number(startup) : null)} />
|
<KpiTile label="월매출" value={formatKRW(sales != null ? Number(sales) : null)} />
|
||||||
<BigInfoItem label="월매출" value={formatKRW(sales != null ? Number(sales) : null)} />
|
<KpiTile
|
||||||
<BigInfoItem label="월수익" value={formatKRW(profit != null ? Number(profit) : null)} accent />
|
label="회수기간"
|
||||||
<BigInfoItem
|
|
||||||
label="월수익률"
|
|
||||||
value={formatMargin(
|
|
||||||
sales != null ? Number(sales) : undefined,
|
|
||||||
profit != null ? Number(profit) : undefined,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<BigInfoItem
|
|
||||||
label="권리금 회수기간"
|
|
||||||
value={formatPaybackMonths(
|
value={formatPaybackMonths(
|
||||||
premium != null ? Number(premium) : undefined,
|
premium != null ? Number(premium) : undefined,
|
||||||
profit != null ? Number(profit) : undefined,
|
profit != null ? Number(profit) : undefined,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
{/* 매물설명 + 매장사진 (두번째 캡쳐) */}
|
{/* ========================================================== *
|
||||||
{(store.sale?.listingDescription || store.photos.length > 0) && (
|
* BODY
|
||||||
<div className="mt-8">
|
* ========================================================== */}
|
||||||
<h3 className="font-display text-base font-bold text-ink mb-2">매물 설명</h3>
|
<section className="mt-6 grid gap-4 px-5 md:grid-cols-3 md:gap-6 md:px-0">
|
||||||
{store.sale?.listingDescription ? (
|
<div className="md:col-span-2 space-y-6">
|
||||||
|
{/* 매물 설명 */}
|
||||||
|
{store.sale?.listingDescription && (
|
||||||
|
<SectionCard title="매물 설명">
|
||||||
<p className="text-sm leading-relaxed text-ink whitespace-pre-line">
|
<p className="text-sm leading-relaxed text-ink whitespace-pre-line">
|
||||||
{store.sale.listingDescription}
|
{store.sale.listingDescription}
|
||||||
</p>
|
</p>
|
||||||
) : (
|
</SectionCard>
|
||||||
<p className="text-sm text-ink-muted">설명이 등록되지 않았습니다</p>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{store.photos[0] && (
|
{/* 입지 특징 */}
|
||||||
<div className="mt-4 overflow-hidden rounded-xl bg-warm-100">
|
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
||||||
<img
|
|
||||||
src={store.photos[0].storageKey}
|
|
||||||
alt="매장 대표 사진"
|
|
||||||
className="aspect-[16/10] w-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 매출/월수익 */}
|
|
||||||
{(sales != null || profit != null) && (
|
|
||||||
<div className="mt-8">
|
|
||||||
<h3 className="font-display text-base font-bold text-ink mb-3">매출 / 월수익</h3>
|
|
||||||
<div className="rounded-xl bg-white/70 p-4">
|
|
||||||
<div className="grid grid-cols-2 gap-4 md:grid-cols-3">
|
|
||||||
<InfoItem
|
|
||||||
label="월매출"
|
|
||||||
value={formatKRW(sales != null ? Number(sales) : null)}
|
|
||||||
/>
|
|
||||||
<InfoItem
|
|
||||||
label="월수익"
|
|
||||||
value={formatKRW(profit != null ? Number(profit) : null)}
|
|
||||||
/>
|
|
||||||
<InfoItem
|
|
||||||
label="월수익률"
|
|
||||||
value={formatMargin(
|
|
||||||
sales != null ? Number(sales) : undefined,
|
|
||||||
profit != null ? Number(profit) : undefined,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 입지특징 */}
|
|
||||||
{store.sale?.locationHighlight && (
|
{store.sale?.locationHighlight && (
|
||||||
<div className="mt-6">
|
<SectionCard title="입지 특징">
|
||||||
<h3 className="font-display text-base font-bold text-ink mb-2">입지 특징</h3>
|
<p className="text-sm leading-relaxed text-ink whitespace-pre-line">
|
||||||
<p className="rounded-xl bg-white/70 p-4 text-sm text-ink whitespace-pre-line">
|
|
||||||
{store.sale.locationHighlight}
|
{store.sale.locationHighlight}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</SectionCard>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 매매사유 */}
|
{/* 매매 사유 */}
|
||||||
{store.sale?.saleReason && (
|
{store.sale?.saleReason && (
|
||||||
<div className="mt-6">
|
<SectionCard title="매매 사유">
|
||||||
<h3 className="font-display text-base font-bold text-ink mb-2">매매 사유</h3>
|
<p className="text-sm leading-relaxed text-ink whitespace-pre-line">
|
||||||
<p className="rounded-xl bg-white/70 p-4 text-sm text-ink whitespace-pre-line">
|
|
||||||
{store.sale.saleReason}
|
{store.sale.saleReason}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</SectionCard>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 현장사진 (대표 사진 제외한 나머지) */}
|
|
||||||
{store.photos.length > 1 && (
|
|
||||||
<div className="mt-8">
|
|
||||||
<h3 className="font-display text-base font-bold text-ink mb-3">현장 사진</h3>
|
|
||||||
<div className="grid grid-cols-3 gap-2 md:grid-cols-4">
|
|
||||||
{store.photos.slice(1).map((photo) => (
|
|
||||||
<div
|
|
||||||
key={photo.id.toString()}
|
|
||||||
className="aspect-square overflow-hidden rounded-lg bg-warm-100"
|
|
||||||
>
|
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
||||||
<img
|
|
||||||
src={photo.storageKey}
|
|
||||||
alt="현장 사진"
|
|
||||||
className="h-full w-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* 임대 정보 */}
|
|
||||||
<section className="mt-8">
|
|
||||||
<h2 className="font-display text-xl font-bold text-ink mb-4">임대 정보</h2>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<InfoItem
|
|
||||||
label="보증금"
|
|
||||||
value={formatKRW(store.lease?.depositAmount != null ? Number(store.lease.depositAmount) : null)}
|
|
||||||
/>
|
|
||||||
<InfoItem
|
|
||||||
label="월세"
|
|
||||||
value={formatKRW(store.lease?.monthlyRentAmount != null ? Number(store.lease.monthlyRentAmount) : null)}
|
|
||||||
/>
|
|
||||||
<InfoItem
|
|
||||||
label="임대 잔여 기간"
|
|
||||||
value={
|
|
||||||
store.lease?.remainingLeaseMonths != null
|
|
||||||
? `${store.lease.remainingLeaseMonths}개월`
|
|
||||||
: '-'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* 시설 정보 */}
|
{/* 시설 정보 */}
|
||||||
<section className="mt-8">
|
<SectionCard title="시설 정보">
|
||||||
<h2 className="font-display text-xl font-bold text-ink mb-4">시설 정보</h2>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<InfoItem
|
<InfoItem
|
||||||
label="면적"
|
label="면적"
|
||||||
@@ -306,21 +248,70 @@ export default async function StoreDetailPage({ params }: { params: Promise<{ id
|
|||||||
</div>
|
</div>
|
||||||
{store.facility?.kitchenEquipmentSummary && (
|
{store.facility?.kitchenEquipmentSummary && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<p className="text-sm text-ink-muted">시설 설명</p>
|
<p className="text-xs text-ink-muted">시설 설명</p>
|
||||||
<p className="mt-1 text-sm text-ink">{store.facility.kitchenEquipmentSummary}</p>
|
<p className="mt-1 text-sm text-ink">{store.facility.kitchenEquipmentSummary}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</SectionCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sticky sidebar */}
|
||||||
|
<aside className="md:col-span-1 md:sticky md:top-24 md:self-start">
|
||||||
|
<div
|
||||||
|
className="rounded-2xl border bg-white p-5 shadow-[0_2px_14px_-10px_rgba(15,29,23,0.15)]"
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
|
>
|
||||||
|
<p className="text-xs text-ink-muted">매장 ID</p>
|
||||||
|
<p className="mt-0.5 font-mono text-xs text-ink">{store.publicId}</p>
|
||||||
|
|
||||||
|
<div className="mt-5 space-y-3">
|
||||||
|
<RowInfo label="지역" value={store.regionCluster?.nameKo ?? '-'} />
|
||||||
|
<RowInfo label="업종" value={industryLabel || '-'} />
|
||||||
|
<RowInfo label="주소" value={store.roadAddress} />
|
||||||
|
<RowInfo label="등록" value={store.createdAt.toLocaleDateString('ko-KR')} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 임대 정보 */}
|
||||||
|
<div className="mt-5 border-t pt-5" style={{ borderColor: '#EFF2F0' }}>
|
||||||
|
<p className="font-display text-sm font-bold text-ink">임대 정보</p>
|
||||||
|
<div className="mt-3 space-y-2">
|
||||||
|
<RowInfo
|
||||||
|
label="보증금"
|
||||||
|
value={formatKRW(store.lease?.depositAmount != null ? Number(store.lease.depositAmount) : null)}
|
||||||
|
/>
|
||||||
|
<RowInfo
|
||||||
|
label="월세"
|
||||||
|
value={formatKRW(store.lease?.monthlyRentAmount != null ? Number(store.lease.monthlyRentAmount) : null)}
|
||||||
|
/>
|
||||||
|
<RowInfo
|
||||||
|
label="잔여 계약"
|
||||||
|
value={
|
||||||
|
store.lease?.remainingLeaseMonths != null
|
||||||
|
? `${store.lease.remainingLeaseMonths}개월`
|
||||||
|
: '-'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<RowInfo label="창업비용" value={formatKRW(startup != null ? Number(startup) : null)} />
|
||||||
|
<RowInfo
|
||||||
|
label="월수익률"
|
||||||
|
value={formatMargin(
|
||||||
|
sales != null ? Number(sales) : undefined,
|
||||||
|
profit != null ? Number(profit) : undefined,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 액션 버튼 */}
|
{/* 액션 버튼 */}
|
||||||
<div className="mt-10 flex flex-wrap gap-3 border-t border-ink/5 pt-6">
|
<div className="mt-6 space-y-3">
|
||||||
{store.reviewStatus === 'DRAFT' && (
|
{store.reviewStatus === 'DRAFT' && (
|
||||||
<>
|
<>
|
||||||
<form action={handleSubmitForReview}>
|
<form action={handleSubmitForReview}>
|
||||||
<input type="hidden" name="storePublicId" value={store.publicId} />
|
<input type="hidden" name="storePublicId" value={store.publicId} />
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="rounded-full bg-ink px-6 py-3 text-sm font-medium text-warm-50 hover:bg-warm-800 transition-colors"
|
className="w-full rounded-full py-3 text-sm font-semibold text-white"
|
||||||
|
style={{ background: 'linear-gradient(135deg,#03C75A,#02A149)' }}
|
||||||
>
|
>
|
||||||
검토 제출
|
검토 제출
|
||||||
</button>
|
</button>
|
||||||
@@ -329,29 +320,39 @@ export default async function StoreDetailPage({ params }: { params: Promise<{ id
|
|||||||
<input type="hidden" name="storePublicId" value={store.publicId} />
|
<input type="hidden" name="storePublicId" value={store.publicId} />
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="rounded-full border-2 border-red-300 px-6 py-3 text-sm text-red-600 hover:bg-red-50"
|
className="w-full rounded-full border-2 border-red-300 py-3 text-sm text-red-600 hover:bg-red-50"
|
||||||
>
|
>
|
||||||
삭제
|
삭제
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{store.publicationStatus === 'PUBLISHED' && (
|
{store.publicationStatus === 'PUBLISHED' && store.dealStatus !== 'CLOSED' && (
|
||||||
<>
|
<>
|
||||||
<Link
|
<Link
|
||||||
href={`/matching?storeId=${id}`}
|
href={`/matching?storeId=${id}`}
|
||||||
className="rounded-full bg-ink px-6 py-3 text-sm font-medium text-warm-50 hover:bg-warm-800 transition-colors"
|
className="block w-full rounded-full py-3 text-center text-sm font-semibold text-white"
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(135deg,#03C75A,#02A149)',
|
||||||
|
boxShadow: '0 10px 24px -10px rgba(3,199,90,0.55)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
매칭 요청 보내기
|
매칭 요청 보내기
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={`/subsidies?storeId=${id}`}
|
href={`/subsidies?storeId=${id}`}
|
||||||
className="rounded-full border-2 border-ink/15 px-6 py-3 text-sm font-medium text-ink hover:border-ink/40 transition-colors"
|
className="block w-full rounded-full border-2 py-3 text-center text-sm font-semibold text-ink"
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
>
|
>
|
||||||
지원금 확인
|
지원금 확인
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{store.dealStatus === 'CLOSED' && (
|
||||||
|
<p className="rounded-xl border px-3 py-3 text-center text-sm text-ink-muted" style={{ borderColor: '#E4E8E6', background: '#F7FAF8' }}>
|
||||||
|
이미 거래가 완료된 매장입니다
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
{store.reviewStatus === 'SUBMITTED' && (
|
{store.reviewStatus === 'SUBMITTED' && (
|
||||||
<p className="text-sm text-ink-muted">검토 대기 중입니다</p>
|
<p className="text-sm text-ink-muted">검토 대기 중입니다</p>
|
||||||
)}
|
)}
|
||||||
@@ -361,34 +362,59 @@ export default async function StoreDetailPage({ params }: { params: Promise<{ id
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="mt-6 rounded-2xl bg-warm-100/60 px-5 py-4 text-xs leading-relaxed text-ink-muted">
|
<p className="mt-4 rounded-xl border px-4 py-3 text-[11px] leading-relaxed text-ink-muted" style={{ borderColor: '#EFF2F0', background: '#F7FAF8' }}>
|
||||||
주의: 창업에이전트는 법률에 따라 부동산 중개 정보를 제공할 수 없으며, 권리금과 창업비용만 표시
|
창업에이전트는 법률에 따라 부동산 중개 정보를 제공할 수 없으며, 권리금과 창업비용만
|
||||||
가능합니다. 매출·수익 정보는 매도인 제공 자료이며, 법적 근거로 사용될 수 없습니다. 권리금 계약은
|
표시 가능합니다. 매출·수익 정보는 매도인 제공 자료입니다.
|
||||||
행정사가, 임대차 계약은 공인중개사가 작성합니다. 이 문구는 법률상 면책적 자료로 사용될 수
|
|
||||||
있습니다.
|
|
||||||
</p>
|
</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function InfoItem({ label, value }: { label: string; value: string }) {
|
function KpiTile({ label, value, accent }: { label: string; value: string; accent?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
<p className="text-sm text-ink-muted">{label}</p>
|
className="rounded-2xl border bg-white p-4 shadow-[0_2px_10px_-8px_rgba(15,29,23,0.15)]"
|
||||||
<p className="mt-0.5 text-sm font-medium text-ink">{value}</p>
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
</div>
|
>
|
||||||
);
|
<p className="text-[11px] font-semibold tracking-wider uppercase text-ink-muted">{label}</p>
|
||||||
}
|
|
||||||
|
|
||||||
function BigInfoItem({ label, value, accent }: { label: string; value: string; accent?: boolean }) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-ink-muted">{label}</p>
|
|
||||||
<p
|
<p
|
||||||
className={`mt-1 font-display text-lg font-bold ${accent ? 'text-warm-700' : 'text-ink'}`}
|
className={`mt-1 font-display text-lg font-extrabold tracking-tight ${accent ? '' : 'text-ink'}`}
|
||||||
|
style={accent ? { color: '#02A149' } : undefined}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SectionCard({ title, children }: { title: string; children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className="rounded-2xl border bg-white p-5 shadow-[0_2px_10px_-8px_rgba(15,29,23,0.1)]"
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
|
>
|
||||||
|
<h3 className="font-display text-base font-bold text-ink">{title}</h3>
|
||||||
|
<div className="mt-3">{children}</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function InfoItem({ label, value }: { label: string; value: string }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-ink-muted">{label}</p>
|
||||||
|
<p className="mt-0.5 text-sm font-medium text-ink">{value}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RowInfo({ label, value }: { label: string; value: string }) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-xs text-ink-muted">{label}</span>
|
||||||
|
<span className="font-medium text-ink">{value}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,15 @@ function formatKRWShort(value: number | null | undefined): string {
|
|||||||
return `${v.toLocaleString('ko-KR')}원`;
|
return `${v.toLocaleString('ko-KR')}원`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STATUS_META: Record<string, { label: string; bg: string; fg: string }> = {
|
||||||
|
OPEN: { label: '거래 가능', bg: '#03C75A', fg: '#ffffff' },
|
||||||
|
MATCHING: { label: '매칭 중', bg: '#00C4A7', fg: '#ffffff' },
|
||||||
|
RESERVED: { label: '예약', bg: '#84E1A1', fg: '#0F1D17' },
|
||||||
|
CONTRACTED: { label: '계약 진행 중', bg: '#0AB070', fg: '#ffffff' },
|
||||||
|
CLOSED: { label: '거래 완료', bg: '#7B8581', fg: '#ffffff' },
|
||||||
|
CANCELLED: { label: '취소', bg: '#E4E8E6', fg: '#0F1D17' },
|
||||||
|
};
|
||||||
|
|
||||||
export default async function StoresPage({
|
export default async function StoresPage({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
@@ -68,18 +77,25 @@ export default async function StoresPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="mx-auto max-w-7xl px-6 py-10 font-body">
|
<main className="mx-auto max-w-7xl px-6 py-10 font-body">
|
||||||
<div className="animate-fade-up flex items-center justify-between">
|
<div className="animate-fade-up flex flex-wrap items-center justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="font-display text-3xl font-bold text-ink">매장 검색</h1>
|
<p className="font-mono text-[11px] tracking-[0.25em] uppercase text-ink-muted">
|
||||||
|
<span className="text-grad-primary">◆</span> Store · Marketplace
|
||||||
|
</p>
|
||||||
|
<h1 className="mt-2 font-display text-3xl font-black tracking-tight text-ink">매장 검색</h1>
|
||||||
<p className="mt-1 text-sm text-ink-muted">
|
<p className="mt-1 text-sm text-ink-muted">
|
||||||
공개된 매장을 검색하고 매칭 요청을 보내보세요
|
공개된 매물 {stores.length.toLocaleString('ko-KR')}건 · 운영팀 검수를 통과한 매장만 노출됩니다
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href="/stores/new"
|
href="/stores/new"
|
||||||
className="rounded-full bg-ink px-6 py-3 text-sm font-medium text-warm-50 hover:bg-warm-800 transition-colors"
|
className="rounded-full px-6 py-3 text-sm font-semibold text-white transition-transform hover:-translate-y-0.5"
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(135deg,#03C75A 0%,#02A149 55%,#018f40 100%)',
|
||||||
|
boxShadow: '0 10px 24px -10px rgba(3,199,90,0.5)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
매장 등록
|
매장 등록 →
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -90,12 +106,13 @@ export default async function StoresPage({
|
|||||||
<div className="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 animate-fade-up">
|
<div className="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 animate-fade-up">
|
||||||
{stores.length === 0 ? (
|
{stores.length === 0 ? (
|
||||||
<div className="col-span-full py-16 text-center text-sm text-ink-muted">
|
<div className="col-span-full py-16 text-center text-sm text-ink-muted">
|
||||||
등록된 매장이 없습니다
|
조건에 맞는 매장이 없습니다
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
stores.map((store) => {
|
stores.map((store) => {
|
||||||
const premium = store.sale?.premiumAmount ?? store.lease?.premiumAmount ?? null;
|
const premium = store.sale?.premiumAmount ?? store.lease?.premiumAmount ?? null;
|
||||||
const profit = store.sale?.monthlyProfitAmount ?? null;
|
const profit = store.sale?.monthlyProfitAmount ?? null;
|
||||||
|
const sales = store.sale?.monthlySalesAmount ?? null;
|
||||||
const industryLabel = [
|
const industryLabel = [
|
||||||
store.industryLeaf?.parent?.nameKo,
|
store.industryLeaf?.parent?.nameKo,
|
||||||
store.industryLeaf?.nameKo,
|
store.industryLeaf?.nameKo,
|
||||||
@@ -103,21 +120,23 @@ export default async function StoresPage({
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join('/');
|
.join('/');
|
||||||
const photoSrc = store.photos[0]?.storageKey ?? null;
|
const photoSrc = store.photos[0]?.storageKey ?? null;
|
||||||
|
const meta = STATUS_META[store.dealStatus] ?? { label: store.dealStatus, bg: '#E4E8E6', fg: '#0F1D17' };
|
||||||
|
const isClosed = store.dealStatus === 'CLOSED';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={store.publicId}
|
key={store.publicId}
|
||||||
href={`/stores/${store.publicId}`}
|
href={`/stores/${store.publicId}`}
|
||||||
className="block overflow-hidden rounded-2xl border border-ink/5 bg-white/70 backdrop-blur-sm card-lift"
|
className={`block overflow-hidden rounded-2xl border bg-white card-lift ${isClosed ? 'opacity-80' : ''}`}
|
||||||
|
style={{ borderColor: '#E4E8E6' }}
|
||||||
>
|
>
|
||||||
{/* 사진 영역 */}
|
<div className="relative aspect-[4/3] w-full" style={{ background: '#EFF2F0' }}>
|
||||||
<div className="relative aspect-[4/3] w-full bg-warm-100">
|
|
||||||
{photoSrc ? (
|
{photoSrc ? (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
/* eslint-disable-next-line @next/next/no-img-element */
|
||||||
<img
|
<img
|
||||||
src={photoSrc}
|
src={photoSrc}
|
||||||
alt={store.listingTitle}
|
alt={store.listingTitle}
|
||||||
className="h-full w-full object-cover"
|
className={`h-full w-full object-cover ${isClosed ? 'grayscale' : ''}`}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex h-full w-full items-center justify-center text-xs text-ink-muted">
|
<div className="flex h-full w-full items-center justify-center text-xs text-ink-muted">
|
||||||
@@ -125,23 +144,13 @@ export default async function StoresPage({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<span
|
<span
|
||||||
className={`absolute right-3 top-3 rounded-full px-2 py-0.5 text-xs font-medium ${
|
className="absolute right-3 top-3 rounded-full px-2.5 py-1 text-[11px] font-semibold shadow-[0_4px_10px_-4px_rgba(15,29,23,0.25)]"
|
||||||
store.dealStatus === 'OPEN'
|
style={{ background: meta.bg, color: meta.fg }}
|
||||||
? 'bg-sage-500/90 text-white'
|
|
||||||
: store.dealStatus === 'MATCHING'
|
|
||||||
? 'bg-warm-600/90 text-white'
|
|
||||||
: 'bg-ink/70 text-white'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{store.dealStatus === 'OPEN'
|
{meta.label}
|
||||||
? '거래 가능'
|
|
||||||
: store.dealStatus === 'MATCHING'
|
|
||||||
? '매칭 중'
|
|
||||||
: store.dealStatus}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 정보 영역 */}
|
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<p className="text-xs text-ink-muted">
|
<p className="text-xs text-ink-muted">
|
||||||
{store.regionCluster?.nameKo ?? '-'}
|
{store.regionCluster?.nameKo ?? '-'}
|
||||||
@@ -150,10 +159,10 @@ export default async function StoresPage({
|
|||||||
<h3 className="mt-1 line-clamp-1 font-display font-bold text-ink">
|
<h3 className="mt-1 line-clamp-1 font-display font-bold text-ink">
|
||||||
{store.listingTitle}
|
{store.listingTitle}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="mt-3 space-y-1">
|
<div className="mt-3 space-y-1.5">
|
||||||
<div className="flex items-baseline justify-between">
|
<div className="flex items-baseline justify-between">
|
||||||
<span className="text-xs text-ink-muted">권리금</span>
|
<span className="text-xs text-ink-muted">권리금</span>
|
||||||
<span className="font-display font-bold text-warm-700">
|
<span className="font-display font-bold" style={{ color: '#02A149' }}>
|
||||||
{formatKRWShort(premium != null ? Number(premium) : null)}
|
{formatKRWShort(premium != null ? Number(premium) : null)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -163,6 +172,12 @@ export default async function StoresPage({
|
|||||||
{formatKRWShort(profit != null ? Number(profit) : null)}
|
{formatKRWShort(profit != null ? Number(profit) : null)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-baseline justify-between">
|
||||||
|
<span className="text-xs text-ink-muted">월매출</span>
|
||||||
|
<span className="text-sm text-ink-light">
|
||||||
|
{formatKRWShort(sales != null ? Number(sales) : null)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -171,7 +186,7 @@ export default async function StoresPage({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 text-center text-sm text-ink-muted">
|
<div className="mt-8 text-center text-xs text-ink-muted">
|
||||||
매출·수익 정보는 매도인이 제공한 자료이며 법적 근거로 사용될 수 없습니다
|
매출·수익 정보는 매도인이 제공한 자료이며 법적 근거로 사용될 수 없습니다
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const STATUS_OPTIONS = [
|
|||||||
{ value: 'OPEN', label: '거래 가능' },
|
{ value: 'OPEN', label: '거래 가능' },
|
||||||
{ value: 'MATCHING', label: '매칭 중' },
|
{ value: 'MATCHING', label: '매칭 중' },
|
||||||
{ value: 'CONTRACTED', label: '계약 진행 중' },
|
{ value: 'CONTRACTED', label: '계약 진행 중' },
|
||||||
|
{ value: 'CLOSED', label: '거래 완료' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function StoreFilters() {
|
export function StoreFilters() {
|
||||||
@@ -46,15 +47,21 @@ export function StoreFilters() {
|
|||||||
[router],
|
[router],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const selectCls =
|
||||||
|
'rounded-xl border px-4 py-3 text-sm text-ink bg-white focus:outline-none focus:border-[#03C75A] focus:ring-2 focus:ring-[#03C75A]/20';
|
||||||
|
const borderStyle = { borderColor: '#E4E8E6' } as const;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className="mt-6 flex flex-wrap gap-3 rounded-2xl border border-ink/5 bg-white/70 backdrop-blur-sm p-5"
|
className="mt-6 flex flex-wrap gap-3 rounded-2xl border bg-white p-5 shadow-[0_2px_14px_-10px_rgba(15,29,23,0.15)]"
|
||||||
|
style={borderStyle}
|
||||||
>
|
>
|
||||||
<select
|
<select
|
||||||
name="region"
|
name="region"
|
||||||
defaultValue={searchParams.get('region') ?? ''}
|
defaultValue={searchParams.get('region') ?? ''}
|
||||||
className="rounded-xl border border-ink/10 bg-white/70 px-4 py-3 text-sm text-ink focus:border-warm-500 focus:ring-2 focus:ring-warm-500/20 focus:outline-none"
|
className={selectCls}
|
||||||
|
style={borderStyle}
|
||||||
>
|
>
|
||||||
{REGION_OPTIONS.map((opt) => (
|
{REGION_OPTIONS.map((opt) => (
|
||||||
<option key={opt.value} value={opt.value}>
|
<option key={opt.value} value={opt.value}>
|
||||||
@@ -66,7 +73,8 @@ export function StoreFilters() {
|
|||||||
name="industryMajor"
|
name="industryMajor"
|
||||||
value={majorCode}
|
value={majorCode}
|
||||||
onChange={(e) => setMajorCode(e.target.value)}
|
onChange={(e) => setMajorCode(e.target.value)}
|
||||||
className="rounded-xl border border-ink/10 bg-white/70 px-4 py-3 text-sm text-ink focus:border-warm-500 focus:ring-2 focus:ring-warm-500/20 focus:outline-none"
|
className={selectCls}
|
||||||
|
style={borderStyle}
|
||||||
>
|
>
|
||||||
<option value="">전체 대분류</option>
|
<option value="">전체 대분류</option>
|
||||||
{INDUSTRY_MAJORS.map((m) => (
|
{INDUSTRY_MAJORS.map((m) => (
|
||||||
@@ -79,7 +87,8 @@ export function StoreFilters() {
|
|||||||
name="industry"
|
name="industry"
|
||||||
defaultValue={searchParams.get('industry') ?? ''}
|
defaultValue={searchParams.get('industry') ?? ''}
|
||||||
disabled={!majorCode}
|
disabled={!majorCode}
|
||||||
className="rounded-xl border border-ink/10 bg-white/70 px-4 py-3 text-sm text-ink focus:border-warm-500 focus:ring-2 focus:ring-warm-500/20 focus:outline-none disabled:opacity-50"
|
className={`${selectCls} disabled:opacity-50`}
|
||||||
|
style={borderStyle}
|
||||||
>
|
>
|
||||||
<option value="">전체 소분류</option>
|
<option value="">전체 소분류</option>
|
||||||
{subOptions.map((c) => (
|
{subOptions.map((c) => (
|
||||||
@@ -91,7 +100,8 @@ export function StoreFilters() {
|
|||||||
<select
|
<select
|
||||||
name="status"
|
name="status"
|
||||||
defaultValue={searchParams.get('status') ?? ''}
|
defaultValue={searchParams.get('status') ?? ''}
|
||||||
className="rounded-xl border border-ink/10 bg-white/70 px-4 py-3 text-sm text-ink focus:border-warm-500 focus:ring-2 focus:ring-warm-500/20 focus:outline-none"
|
className={selectCls}
|
||||||
|
style={borderStyle}
|
||||||
>
|
>
|
||||||
{STATUS_OPTIONS.map((opt) => (
|
{STATUS_OPTIONS.map((opt) => (
|
||||||
<option key={opt.value} value={opt.value}>
|
<option key={opt.value} value={opt.value}>
|
||||||
@@ -101,7 +111,11 @@ export function StoreFilters() {
|
|||||||
</select>
|
</select>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="rounded-full bg-ink px-6 py-3 text-sm font-medium text-warm-50 hover:bg-warm-800 transition-colors"
|
className="rounded-full px-6 py-3 text-sm font-semibold text-white transition-transform hover:-translate-y-0.5"
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(135deg,#03C75A 0%,#02A149 60%,#018f40 100%)',
|
||||||
|
boxShadow: '0 10px 24px -10px rgba(3,199,90,0.55)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
검색
|
검색
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
"prisma:generate": "prisma generate",
|
"prisma:generate": "prisma generate",
|
||||||
"prisma:push": "prisma db push",
|
"prisma:push": "prisma db push",
|
||||||
"prisma:migrate": "prisma migrate dev",
|
"prisma:migrate": "prisma migrate dev",
|
||||||
"prisma:seed": "tsx seeds/seed.ts"
|
"prisma:seed": "tsx seeds/seed.ts",
|
||||||
|
"prisma:seed:demo": "tsx seeds/demo-data.ts"
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"seed": "tsx seeds/seed.ts"
|
"seed": "tsx seeds/seed.ts"
|
||||||
|
|||||||
@@ -0,0 +1,626 @@
|
|||||||
|
/**
|
||||||
|
* Demo data seeder
|
||||||
|
* - Creates dummy stores (published) spanning all deal statuses including CLOSED (거래 완료)
|
||||||
|
* - Creates dummy match requests spanning OPEN / REVIEWING / ACCEPTED / COMPLETED
|
||||||
|
*
|
||||||
|
* Run: pnpm --filter @startover/database exec tsx seeds/demo-data.ts
|
||||||
|
*/
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
const DEMO_OWNER_EMAIL = 'demo-owner@startover.co.kr';
|
||||||
|
const DEMO_FOUNDER_EMAIL = 'demo-founder@startover.co.kr';
|
||||||
|
const DEMO_PASSWORD_HASH =
|
||||||
|
'$argon2id$v=19$m=65536,t=3,p=4$MMkWVs1hKzKPp5P93KLITw$fhiUqTZMkR0ucwQNteHskMn9UIrD/7aUXZPUdzWYloE';
|
||||||
|
|
||||||
|
type DemoStore = {
|
||||||
|
title: string;
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE' | 'KR.BETA.MAPO_CORE';
|
||||||
|
industry: string;
|
||||||
|
dealStatus: 'OPEN' | 'MATCHING' | 'RESERVED' | 'CONTRACTED' | 'CLOSED';
|
||||||
|
roadAddress: string;
|
||||||
|
premium: number;
|
||||||
|
monthlySales: number;
|
||||||
|
monthlyProfit: number;
|
||||||
|
startup: number;
|
||||||
|
deposit: number;
|
||||||
|
monthlyRent: number;
|
||||||
|
remainingMonths: number;
|
||||||
|
area: number;
|
||||||
|
floor: number;
|
||||||
|
description: string;
|
||||||
|
locationHighlight: string;
|
||||||
|
saleReason: string;
|
||||||
|
photoUrl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEMO_STORES: DemoStore[] = [
|
||||||
|
{
|
||||||
|
title: '역삼역 1분거리 스타벅스 대체 카페 · 권리양도',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'REST_LIGHT.CAFE',
|
||||||
|
dealStatus: 'OPEN',
|
||||||
|
roadAddress: '서울특별시 강남구 역삼로 112',
|
||||||
|
premium: 180_000_000,
|
||||||
|
monthlySales: 42_000_000,
|
||||||
|
monthlyProfit: 9_800_000,
|
||||||
|
startup: 40_000_000,
|
||||||
|
deposit: 100_000_000,
|
||||||
|
monthlyRent: 6_500_000,
|
||||||
|
remainingMonths: 28,
|
||||||
|
area: 56,
|
||||||
|
floor: 1,
|
||||||
|
description:
|
||||||
|
'역삼역 3번 출구 도보 1분.\n오피스 밀집지역 점심 피크 시 대기 발생.\n시설 2년 사용(에스프레소 머신 2조, 제빙기 포함).',
|
||||||
|
locationHighlight: '오피스/호텔 2,200세대 · 배달앱 리뷰 2,300+',
|
||||||
|
saleReason: '개인 사정으로 수도권 외 이전 예정',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1453614512568-c4024d13c247?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '선릉역 직장인 한식 백반 전문점',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'REST_FULL.KOREAN',
|
||||||
|
dealStatus: 'MATCHING',
|
||||||
|
roadAddress: '서울특별시 강남구 테헤란로 315',
|
||||||
|
premium: 120_000_000,
|
||||||
|
monthlySales: 55_000_000,
|
||||||
|
monthlyProfit: 12_400_000,
|
||||||
|
startup: 35_000_000,
|
||||||
|
deposit: 80_000_000,
|
||||||
|
monthlyRent: 4_800_000,
|
||||||
|
remainingMonths: 18,
|
||||||
|
area: 72,
|
||||||
|
floor: 2,
|
||||||
|
description:
|
||||||
|
'점심 회전 3.5회, 저녁 단체석 운영.\n주방 2인 + 홀 2인 체계.\n직원 전원 승계 가능.',
|
||||||
|
locationHighlight: '반경 300m 오피스 직장인 1.2만명 · 카드매출 안정',
|
||||||
|
saleReason: '은퇴 준비',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1559339352-11d035aa65de?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '홍대입구 골목 와인바 · 임대승계 가능',
|
||||||
|
region: 'KR.BETA.MAPO_CORE',
|
||||||
|
industry: 'BAR.WINE',
|
||||||
|
dealStatus: 'OPEN',
|
||||||
|
roadAddress: '서울특별시 마포구 와우산로 33',
|
||||||
|
premium: 95_000_000,
|
||||||
|
monthlySales: 31_000_000,
|
||||||
|
monthlyProfit: 7_100_000,
|
||||||
|
startup: 25_000_000,
|
||||||
|
deposit: 70_000_000,
|
||||||
|
monthlyRent: 3_900_000,
|
||||||
|
remainingMonths: 34,
|
||||||
|
area: 45,
|
||||||
|
floor: 1,
|
||||||
|
description:
|
||||||
|
'홍대 골목상권, 20-30대 여성 주요 고객층.\n인스타그램 팔로워 1.4만, 네이버 플레이스 별점 4.7.\n와인 셀러·디캔터 포함.',
|
||||||
|
locationHighlight: 'SNS 상권, 연남/홍대 유동인구 도보 5분',
|
||||||
|
saleReason: '해외 거주 예정',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1470337458703-46ad1756a187?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '합정역 2번출구 프랜차이즈 치킨 · 권리양도',
|
||||||
|
region: 'KR.BETA.MAPO_CORE',
|
||||||
|
industry: 'REST_LIGHT.CHICKEN_PIZZA',
|
||||||
|
dealStatus: 'CONTRACTED',
|
||||||
|
roadAddress: '서울특별시 마포구 양화로 45',
|
||||||
|
premium: 130_000_000,
|
||||||
|
monthlySales: 47_000_000,
|
||||||
|
monthlyProfit: 8_900_000,
|
||||||
|
startup: 30_000_000,
|
||||||
|
deposit: 60_000_000,
|
||||||
|
monthlyRent: 4_100_000,
|
||||||
|
remainingMonths: 22,
|
||||||
|
area: 40,
|
||||||
|
floor: 1,
|
||||||
|
description: '브랜드 본사 승인 완료, 로얄티 월 80만원. 오토 체제.',
|
||||||
|
locationHighlight: '배달 상권 상위 5% · 배민 별점 4.9',
|
||||||
|
saleReason: '타 업종 전환',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1562967914-608f82629710?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '논현동 24시 헬스장 · 회원권 승계',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'LEISURE.GYM',
|
||||||
|
dealStatus: 'MATCHING',
|
||||||
|
roadAddress: '서울특별시 강남구 논현로 88',
|
||||||
|
premium: 260_000_000,
|
||||||
|
monthlySales: 72_000_000,
|
||||||
|
monthlyProfit: 18_300_000,
|
||||||
|
startup: 120_000_000,
|
||||||
|
deposit: 200_000_000,
|
||||||
|
monthlyRent: 11_000_000,
|
||||||
|
remainingMonths: 40,
|
||||||
|
area: 520,
|
||||||
|
floor: 3,
|
||||||
|
description:
|
||||||
|
'지하 전용면적 520㎡ · 회원 820명 활성 유지.\nPT 전담 트레이너 6명 승계 가능.',
|
||||||
|
locationHighlight: '오피스텔/아파트 3,500세대 배후',
|
||||||
|
saleReason: '프랜차이즈 본사 사업 확장',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1534438327276-14e5300c3a48?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '연남동 베이커리 · 권리양도',
|
||||||
|
region: 'KR.BETA.MAPO_CORE',
|
||||||
|
industry: 'REST_LIGHT.BAKERY',
|
||||||
|
dealStatus: 'OPEN',
|
||||||
|
roadAddress: '서울특별시 마포구 연남로 22',
|
||||||
|
premium: 75_000_000,
|
||||||
|
monthlySales: 28_000_000,
|
||||||
|
monthlyProfit: 6_200_000,
|
||||||
|
startup: 20_000_000,
|
||||||
|
deposit: 50_000_000,
|
||||||
|
monthlyRent: 3_100_000,
|
||||||
|
remainingMonths: 16,
|
||||||
|
area: 38,
|
||||||
|
floor: 1,
|
||||||
|
description: '연남동 핵심 상권. 주말 줄서는 가게. 시설 1년 사용.',
|
||||||
|
locationHighlight: '연남동 경의선숲길 도보 2분',
|
||||||
|
saleReason: '건강상의 이유',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1509440159596-0249088772ff?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '강남역 11번 출구 편의점 · 양수양도',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'RETAIL.CVS',
|
||||||
|
dealStatus: 'OPEN',
|
||||||
|
roadAddress: '서울특별시 강남구 강남대로 420',
|
||||||
|
premium: 220_000_000,
|
||||||
|
monthlySales: 95_000_000,
|
||||||
|
monthlyProfit: 8_100_000,
|
||||||
|
startup: 60_000_000,
|
||||||
|
deposit: 150_000_000,
|
||||||
|
monthlyRent: 8_800_000,
|
||||||
|
remainingMonths: 44,
|
||||||
|
area: 46,
|
||||||
|
floor: 1,
|
||||||
|
description: '24시 운영, 야간 매출 비중 35%. 알바 6명 승계.',
|
||||||
|
locationHighlight: '강남역 도보 2분 · 오피스 빌딩 직원 2.3만명',
|
||||||
|
saleReason: '다른 매장 운영 집중',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1604719312566-8912e9227c6a?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '홍대 피씨방 80석 · 권리양도',
|
||||||
|
region: 'KR.BETA.MAPO_CORE',
|
||||||
|
industry: 'LEISURE.PCCAFE',
|
||||||
|
dealStatus: 'RESERVED',
|
||||||
|
roadAddress: '서울특별시 마포구 홍익로 12',
|
||||||
|
premium: 140_000_000,
|
||||||
|
monthlySales: 38_000_000,
|
||||||
|
monthlyProfit: 9_200_000,
|
||||||
|
startup: 80_000_000,
|
||||||
|
deposit: 90_000_000,
|
||||||
|
monthlyRent: 5_400_000,
|
||||||
|
remainingMonths: 25,
|
||||||
|
area: 210,
|
||||||
|
floor: 3,
|
||||||
|
description: 'RTX 4070 고사양 80석. 시설 투자 3년차. 월평균 이용자 8천명.',
|
||||||
|
locationHighlight: '홍대 상권 · 학생/직장인 혼합',
|
||||||
|
saleReason: '해외 이민',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1542751371-adc38448a05e?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '역삼동 미용실 · 단골 400명 승계',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'SERVICE.HAIR',
|
||||||
|
dealStatus: 'MATCHING',
|
||||||
|
roadAddress: '서울특별시 강남구 테헤란로 205',
|
||||||
|
premium: 60_000_000,
|
||||||
|
monthlySales: 22_000_000,
|
||||||
|
monthlyProfit: 5_400_000,
|
||||||
|
startup: 18_000_000,
|
||||||
|
deposit: 40_000_000,
|
||||||
|
monthlyRent: 2_700_000,
|
||||||
|
remainingMonths: 20,
|
||||||
|
area: 33,
|
||||||
|
floor: 2,
|
||||||
|
description: '디자이너 2명 승계 가능. 인스타 프로모션 노하우 공유.',
|
||||||
|
locationHighlight: '오피스 여성 직장인 중심',
|
||||||
|
saleReason: '결혼 · 타지역 이전',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1560066984-138dadb4c035?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '선릉 일식 오마카세 8석 · 예약제',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'REST_FULL.JAPANESE',
|
||||||
|
dealStatus: 'OPEN',
|
||||||
|
roadAddress: '서울특별시 강남구 선릉로 512',
|
||||||
|
premium: 210_000_000,
|
||||||
|
monthlySales: 48_000_000,
|
||||||
|
monthlyProfit: 14_200_000,
|
||||||
|
startup: 90_000_000,
|
||||||
|
deposit: 120_000_000,
|
||||||
|
monthlyRent: 7_500_000,
|
||||||
|
remainingMonths: 33,
|
||||||
|
area: 52,
|
||||||
|
floor: 2,
|
||||||
|
description: '예약제 100% 운영. 객단가 18만원. 캐치테이블 노출 상위.',
|
||||||
|
locationHighlight: '강남 미쉐린 리스트 주변 상권',
|
||||||
|
saleReason: '일본 본점 복귀',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1553621042-f6e147245754?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '합정 칵테일 이자카야 · 인테리어 2년',
|
||||||
|
region: 'KR.BETA.MAPO_CORE',
|
||||||
|
industry: 'BAR.IZAKAYA',
|
||||||
|
dealStatus: 'OPEN',
|
||||||
|
roadAddress: '서울특별시 마포구 토정로 19',
|
||||||
|
premium: 85_000_000,
|
||||||
|
monthlySales: 27_000_000,
|
||||||
|
monthlyProfit: 6_300_000,
|
||||||
|
startup: 22_000_000,
|
||||||
|
deposit: 55_000_000,
|
||||||
|
monthlyRent: 3_400_000,
|
||||||
|
remainingMonths: 29,
|
||||||
|
area: 42,
|
||||||
|
floor: 1,
|
||||||
|
description: '금/토 만석 운영. 안주 원가율 28%.',
|
||||||
|
locationHighlight: '합정-상수 도보 축 상권',
|
||||||
|
saleReason: '공동대표 독립',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1514933651103-005eec06c04b?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '논현 요가·필라테스 스튜디오',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'LEISURE.YOGA',
|
||||||
|
dealStatus: 'OPEN',
|
||||||
|
roadAddress: '서울특별시 강남구 도산대로 150',
|
||||||
|
premium: 90_000_000,
|
||||||
|
monthlySales: 26_500_000,
|
||||||
|
monthlyProfit: 7_800_000,
|
||||||
|
startup: 35_000_000,
|
||||||
|
deposit: 70_000_000,
|
||||||
|
monthlyRent: 4_200_000,
|
||||||
|
remainingMonths: 27,
|
||||||
|
area: 88,
|
||||||
|
floor: 5,
|
||||||
|
description: '강사 4명 승계. 회원 320명 활성. 리포머 6대.',
|
||||||
|
locationHighlight: '오피스/압구정 도보 10분',
|
||||||
|
saleReason: '2호점 집중',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1545205597-3d9d02c29597?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '마포 세차장·카센터 · 2,000평 부지',
|
||||||
|
region: 'KR.BETA.MAPO_CORE',
|
||||||
|
industry: 'SERVICE.CARWASH',
|
||||||
|
dealStatus: 'CLOSED',
|
||||||
|
roadAddress: '서울특별시 마포구 성암로 89',
|
||||||
|
premium: 180_000_000,
|
||||||
|
monthlySales: 43_000_000,
|
||||||
|
monthlyProfit: 11_800_000,
|
||||||
|
startup: 60_000_000,
|
||||||
|
deposit: 150_000_000,
|
||||||
|
monthlyRent: 8_900_000,
|
||||||
|
remainingMonths: 12,
|
||||||
|
area: 620,
|
||||||
|
floor: 1,
|
||||||
|
description: '(거래 완료) 기계 세차 4라인 + 정비 3라인 풀패키지.',
|
||||||
|
locationHighlight: '성산IC 인접 · 차량 접근성 최상',
|
||||||
|
saleReason: '양도 완료',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1605515298946-d0573716f4a9?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '강남 네일아트 프리미엄 숍',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'SERVICE.NAIL',
|
||||||
|
dealStatus: 'CLOSED',
|
||||||
|
roadAddress: '서울특별시 강남구 역삼로 88',
|
||||||
|
premium: 55_000_000,
|
||||||
|
monthlySales: 19_000_000,
|
||||||
|
monthlyProfit: 5_100_000,
|
||||||
|
startup: 15_000_000,
|
||||||
|
deposit: 40_000_000,
|
||||||
|
monthlyRent: 2_600_000,
|
||||||
|
remainingMonths: 14,
|
||||||
|
area: 28,
|
||||||
|
floor: 4,
|
||||||
|
description: '(거래 완료) 단골 600명 · 예약 시스템 승계 완료.',
|
||||||
|
locationHighlight: '오피스 여성 직장인 중심',
|
||||||
|
saleReason: '양도 완료',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1604654894610-df63bc536371?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '홍대 분식 · 테이크아웃 전문',
|
||||||
|
region: 'KR.BETA.MAPO_CORE',
|
||||||
|
industry: 'REST_LIGHT.SNACK',
|
||||||
|
dealStatus: 'CLOSED',
|
||||||
|
roadAddress: '서울특별시 마포구 와우산로 15',
|
||||||
|
premium: 40_000_000,
|
||||||
|
monthlySales: 21_000_000,
|
||||||
|
monthlyProfit: 6_900_000,
|
||||||
|
startup: 12_000_000,
|
||||||
|
deposit: 30_000_000,
|
||||||
|
monthlyRent: 1_900_000,
|
||||||
|
remainingMonths: 10,
|
||||||
|
area: 19,
|
||||||
|
floor: 1,
|
||||||
|
description: '(거래 완료) 테이크아웃 비중 70% · 배달 탑텐.',
|
||||||
|
locationHighlight: '학생 상권',
|
||||||
|
saleReason: '양도 완료',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1534766555764-ce878a5e3a2b?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '논현동 스크린골프 8타석',
|
||||||
|
region: 'KR.BETA.GANGNAM_CORE',
|
||||||
|
industry: 'LEISURE.SCREENGOLF',
|
||||||
|
dealStatus: 'OPEN',
|
||||||
|
roadAddress: '서울특별시 강남구 논현로 240',
|
||||||
|
premium: 195_000_000,
|
||||||
|
monthlySales: 44_000_000,
|
||||||
|
monthlyProfit: 13_200_000,
|
||||||
|
startup: 120_000_000,
|
||||||
|
deposit: 150_000_000,
|
||||||
|
monthlyRent: 9_800_000,
|
||||||
|
remainingMonths: 36,
|
||||||
|
area: 198,
|
||||||
|
floor: 4,
|
||||||
|
description: '카카오 스크린 최신 기종 8타석. 법인/단골 비중 높음.',
|
||||||
|
locationHighlight: '강남역 6번출구 도보 7분',
|
||||||
|
saleReason: '개인 사정',
|
||||||
|
photoUrl:
|
||||||
|
'https://images.unsplash.com/photo-1535131749006-b7f58c99034b?auto=format&fit=crop&w=1200&q=80',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function ensureUser(email: string, name: string, role: 'CLOSING_OWNER' | 'FOUNDER'): Promise<bigint> {
|
||||||
|
const emailNormalized = email.toLowerCase().trim();
|
||||||
|
const existing = await prisma.user.findFirst({ where: { emailNormalized } });
|
||||||
|
if (existing) return existing.id;
|
||||||
|
const created = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
email,
|
||||||
|
emailNormalized,
|
||||||
|
name,
|
||||||
|
passwordHash: DEMO_PASSWORD_HASH,
|
||||||
|
primaryRole: role,
|
||||||
|
status: 'ACTIVE',
|
||||||
|
emailVerifiedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log(`Demo user: ${email} (id: ${created.id})`);
|
||||||
|
return created.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureDemoStores(ownerUserId: bigint): Promise<void> {
|
||||||
|
// Cache region & industry lookups
|
||||||
|
const regionCodes = Array.from(new Set(DEMO_STORES.map((s) => s.region)));
|
||||||
|
const industryCodes = Array.from(new Set(DEMO_STORES.map((s) => s.industry)));
|
||||||
|
|
||||||
|
const regions = await prisma.regionHierarchy.findMany({
|
||||||
|
where: { code: { in: regionCodes } },
|
||||||
|
select: { id: true, code: true },
|
||||||
|
});
|
||||||
|
const industries = await prisma.industryTaxonomy.findMany({
|
||||||
|
where: { code: { in: industryCodes } },
|
||||||
|
select: { id: true, code: true },
|
||||||
|
});
|
||||||
|
const regionByCode = new Map(regions.map((r) => [r.code, r.id] as const));
|
||||||
|
const industryByCode = new Map(industries.map((i) => [i.code, i.id] as const));
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
let created = 0;
|
||||||
|
let skipped = 0;
|
||||||
|
|
||||||
|
for (const s of DEMO_STORES) {
|
||||||
|
// Skip if a store with the same title already exists (idempotent)
|
||||||
|
const existing = await prisma.store.findFirst({
|
||||||
|
where: { listingTitle: s.title, ownerUserId },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
if (existing) {
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const regionClusterId = regionByCode.get(s.region) ?? null;
|
||||||
|
const industryLeafId = industryByCode.get(s.industry) ?? null;
|
||||||
|
if (!regionClusterId || !industryLeafId) {
|
||||||
|
console.warn(`Skipping "${s.title}": region or industry not found (${s.region} / ${s.industry})`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = await prisma.store.create({
|
||||||
|
data: {
|
||||||
|
ownerUserId,
|
||||||
|
listingTitle: s.title,
|
||||||
|
publicSummary: s.description.split('\n')[0]?.slice(0, 140),
|
||||||
|
regionClusterId,
|
||||||
|
industryLeafId,
|
||||||
|
roadAddress: s.roadAddress,
|
||||||
|
reviewStatus: 'APPROVED',
|
||||||
|
publicationStatus: 'PUBLISHED',
|
||||||
|
dealStatus: s.dealStatus,
|
||||||
|
publishedAt: now,
|
||||||
|
approvedAt: now,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.storeLease.create({
|
||||||
|
data: {
|
||||||
|
storeId: store.id,
|
||||||
|
depositAmount: s.deposit,
|
||||||
|
monthlyRentAmount: s.monthlyRent,
|
||||||
|
premiumAmount: s.premium,
|
||||||
|
transferable: true,
|
||||||
|
remainingLeaseMonths: s.remainingMonths,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.storeSale.create({
|
||||||
|
data: {
|
||||||
|
storeId: store.id,
|
||||||
|
premiumAmount: s.premium,
|
||||||
|
monthlySalesAmount: s.monthlySales,
|
||||||
|
monthlyProfitAmount: s.monthlyProfit,
|
||||||
|
startupCostAmount: s.startup,
|
||||||
|
listingDescription: s.description,
|
||||||
|
locationHighlight: s.locationHighlight,
|
||||||
|
saleReason: s.saleReason,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.storeFacility.create({
|
||||||
|
data: {
|
||||||
|
storeId: store.id,
|
||||||
|
exclusiveAreaSqm: s.area,
|
||||||
|
floorLevel: s.floor,
|
||||||
|
hasGas: true,
|
||||||
|
hasDrainage: true,
|
||||||
|
hasDuct: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.storePhoto.create({
|
||||||
|
data: {
|
||||||
|
storeId: store.id,
|
||||||
|
storageKey: s.photoUrl,
|
||||||
|
photoCategory: 'EXTERIOR',
|
||||||
|
visibilityScope: 'PUBLIC_SUMMARY',
|
||||||
|
sortOrder: 0,
|
||||||
|
uploadedByUserId: ownerUserId,
|
||||||
|
isRepresentative: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
created++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Demo stores: created=${created}, skipped(existing)=${skipped}, total=${DEMO_STORES.length}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureDemoMatchRequests(requesterUserId: bigint): Promise<void> {
|
||||||
|
const stores = await prisma.store.findMany({
|
||||||
|
where: { publicationStatus: 'PUBLISHED' },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
take: 20,
|
||||||
|
select: { id: true, listingTitle: true },
|
||||||
|
});
|
||||||
|
if (stores.length === 0) return;
|
||||||
|
|
||||||
|
const scenarios: Array<{
|
||||||
|
matchType: 'ACQUISITION' | 'DEMOLITION' | 'INTERIOR';
|
||||||
|
status: 'OPEN' | 'REVIEWING' | 'ACCEPTED' | 'COMPLETED';
|
||||||
|
message: string;
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
matchType: 'ACQUISITION',
|
||||||
|
status: 'OPEN',
|
||||||
|
message: '인수 희망합니다. 실사 가능한 시점 알려주세요.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matchType: 'ACQUISITION',
|
||||||
|
status: 'REVIEWING',
|
||||||
|
message: '권리금 협의 가능 여부 확인 부탁드립니다.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matchType: 'DEMOLITION',
|
||||||
|
status: 'OPEN',
|
||||||
|
message: '철거 견적 요청합니다. 현장 방문 가능한가요?',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matchType: 'INTERIOR',
|
||||||
|
status: 'ACCEPTED',
|
||||||
|
message: '인테리어 재시공 협의 완료. 도면 공유 드립니다.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matchType: 'ACQUISITION',
|
||||||
|
status: 'COMPLETED',
|
||||||
|
message: '인수 계약 완료. 정산 대기.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matchType: 'ACQUISITION',
|
||||||
|
status: 'OPEN',
|
||||||
|
message: '실 운영자 승계 가능 여부 문의 드립니다.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matchType: 'DEMOLITION',
|
||||||
|
status: 'REVIEWING',
|
||||||
|
message: '원상복구 범위 확인 후 최종 견적 드릴게요.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matchType: 'INTERIOR',
|
||||||
|
status: 'OPEN',
|
||||||
|
message: '카페 → 와인바 컨셉 전환 인테리어 문의',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let created = 0;
|
||||||
|
let skipped = 0;
|
||||||
|
|
||||||
|
// Round-robin scenarios over stores
|
||||||
|
for (let i = 0; i < Math.min(stores.length, scenarios.length * 2); i++) {
|
||||||
|
const store = stores[i]!;
|
||||||
|
const scenario = scenarios[i % scenarios.length]!;
|
||||||
|
|
||||||
|
// Idempotency: skip if we already created a demo request for this store+type+user
|
||||||
|
const existing = await prisma.matchRequest.findFirst({
|
||||||
|
where: {
|
||||||
|
storeId: store.id,
|
||||||
|
matchType: scenario.matchType,
|
||||||
|
requesterUserId,
|
||||||
|
},
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
if (existing) {
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const acceptedAt = scenario.status === 'ACCEPTED' || scenario.status === 'COMPLETED' ? now : null;
|
||||||
|
const closedAt = scenario.status === 'COMPLETED' ? now : null;
|
||||||
|
|
||||||
|
await prisma.matchRequest.create({
|
||||||
|
data: {
|
||||||
|
storeId: store.id,
|
||||||
|
matchType: scenario.matchType,
|
||||||
|
requesterUserId,
|
||||||
|
sourceType: 'USER_REQUEST',
|
||||||
|
status: scenario.status,
|
||||||
|
message: scenario.message,
|
||||||
|
acceptedAt,
|
||||||
|
closedAt,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
created++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Demo match requests: created=${created}, skipped(existing)=${skipped}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
console.log('Starting DEMO seed...');
|
||||||
|
const ownerId = await ensureUser(DEMO_OWNER_EMAIL, '데모 매도인', 'CLOSING_OWNER');
|
||||||
|
const founderId = await ensureUser(DEMO_FOUNDER_EMAIL, '데모 창업자', 'FOUNDER');
|
||||||
|
|
||||||
|
await ensureDemoStores(ownerId);
|
||||||
|
await ensureDemoMatchRequests(founderId);
|
||||||
|
|
||||||
|
console.log('DEMO seed completed.');
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error('Demo seed failed:', e);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user