Compare commits
5 Commits
5ace251b58
...
a5b47a0278
| Author | SHA1 | Date | |
|---|---|---|---|
| a5b47a0278 | |||
| d5be617eb2 | |||
| abec100a73 | |||
| fc55e6a928 | |||
| 4f41f09a8c |
@@ -225,7 +225,7 @@ lib/
|
|||||||
## 사주 시스템 (`/app/work/saju`, `/lib/saju-*.ts`)
|
## 사주 시스템 (`/app/work/saju`, `/lib/saju-*.ts`)
|
||||||
|
|
||||||
> **공개 서비스 — 로그인 시 AI 해석 무료(1회/일)**
|
> **공개 서비스 — 로그인 시 AI 해석 무료(1회/일)**
|
||||||
> 결과 화면 `--jsm` 라이트 재스킨 완료(2026-07-03) — 디자인 가드레일 준수
|
> 전 화면(랜딩·입력·결과) `--jsm` 라이트 재스킨 완료(2026-07-03) — 디자인 가드레일 준수
|
||||||
|
|
||||||
### AI 연동 (`app/api/saju/analyze/route.ts`)
|
### AI 연동 (`app/api/saju/analyze/route.ts`)
|
||||||
- **AI**: Google Gemini (`@google/generative-ai`)
|
- **AI**: Google Gemini (`@google/generative-ai`)
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ export default function SajuForm() {
|
|||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
{/* 생년월일 */}
|
{/* 생년월일 */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-left text-sm font-bold text-[#04102b] mb-3">
|
<label className="block text-left text-sm font-bold text-[var(--jsm-ink)] mb-3">
|
||||||
생년월일
|
생년월일
|
||||||
</label>
|
</label>
|
||||||
<div className="grid grid-cols-3 gap-3">
|
<div className="grid grid-cols-3 gap-3">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="년 (예: 1990)"
|
placeholder="년 (예: 1990)"
|
||||||
className="px-4 py-3 border-2 border-[#dbe8ff] rounded-xl focus:border-[#1a56db] focus:outline-none transition bg-white text-[#04102b]"
|
className="px-4 py-3 border-2 border-[var(--jsm-line)] rounded-xl focus:border-[var(--jsm-accent)] focus:outline-none transition bg-white text-[var(--jsm-ink)]"
|
||||||
min="1900"
|
min="1900"
|
||||||
max="2100"
|
max="2100"
|
||||||
value={year}
|
value={year}
|
||||||
@@ -83,7 +83,7 @@ export default function SajuForm() {
|
|||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="월 (1-12)"
|
placeholder="월 (1-12)"
|
||||||
className="px-4 py-3 border-2 border-[#dbe8ff] rounded-xl focus:border-[#1a56db] focus:outline-none transition bg-white text-[#04102b]"
|
className="px-4 py-3 border-2 border-[var(--jsm-line)] rounded-xl focus:border-[var(--jsm-accent)] focus:outline-none transition bg-white text-[var(--jsm-ink)]"
|
||||||
min="1"
|
min="1"
|
||||||
max="12"
|
max="12"
|
||||||
value={month}
|
value={month}
|
||||||
@@ -93,7 +93,7 @@ export default function SajuForm() {
|
|||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="일 (1-31)"
|
placeholder="일 (1-31)"
|
||||||
className="px-4 py-3 border-2 border-[#dbe8ff] rounded-xl focus:border-[#1a56db] focus:outline-none transition bg-white text-[#04102b]"
|
className="px-4 py-3 border-2 border-[var(--jsm-line)] rounded-xl focus:border-[var(--jsm-accent)] focus:outline-none transition bg-white text-[var(--jsm-ink)]"
|
||||||
min="1"
|
min="1"
|
||||||
max="31"
|
max="31"
|
||||||
value={day}
|
value={day}
|
||||||
@@ -105,11 +105,11 @@ export default function SajuForm() {
|
|||||||
|
|
||||||
{/* 태어난 시간 */}
|
{/* 태어난 시간 */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-left text-sm font-bold text-[#04102b] mb-3">
|
<label className="block text-left text-sm font-bold text-[var(--jsm-ink)] mb-3">
|
||||||
태어난 시간 (선택)
|
태어난 시간 (선택)
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
className="w-full px-4 py-3 border-2 border-[#dbe8ff] rounded-xl focus:border-[#1a56db] focus:outline-none transition bg-white text-[#04102b]"
|
className="w-full px-4 py-3 border-2 border-[var(--jsm-line)] rounded-xl focus:border-[var(--jsm-accent)] focus:outline-none transition bg-white text-[var(--jsm-ink)]"
|
||||||
value={hour}
|
value={hour}
|
||||||
onChange={(e) => setHour(e.target.value)}
|
onChange={(e) => setHour(e.target.value)}
|
||||||
>
|
>
|
||||||
@@ -131,7 +131,7 @@ export default function SajuForm() {
|
|||||||
|
|
||||||
{/* 양력/음력 선택 */}
|
{/* 양력/음력 선택 */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-left text-sm font-bold text-[#04102b] mb-3">
|
<label className="block text-left text-sm font-bold text-[var(--jsm-ink)] mb-3">
|
||||||
생일 구분
|
생일 구분
|
||||||
</label>
|
</label>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
@@ -140,8 +140,8 @@ export default function SajuForm() {
|
|||||||
onClick={() => setCalendarType('solar')}
|
onClick={() => setCalendarType('solar')}
|
||||||
className={`px-6 py-3 rounded-xl font-bold transition ${
|
className={`px-6 py-3 rounded-xl font-bold transition ${
|
||||||
calendarType === 'solar'
|
calendarType === 'solar'
|
||||||
? 'bg-[#1a56db] text-white shadow-lg'
|
? 'bg-[var(--jsm-accent)] text-white shadow-lg'
|
||||||
: 'bg-white border-2 border-[#dbe8ff] text-[#04102b] hover:border-[#1a56db]'
|
: 'bg-white border-2 border-[var(--jsm-line)] text-[var(--jsm-ink)] hover:border-[var(--jsm-accent)]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
양력
|
양력
|
||||||
@@ -151,8 +151,8 @@ export default function SajuForm() {
|
|||||||
onClick={() => setCalendarType('lunar')}
|
onClick={() => setCalendarType('lunar')}
|
||||||
className={`px-6 py-3 rounded-xl font-bold transition ${
|
className={`px-6 py-3 rounded-xl font-bold transition ${
|
||||||
calendarType === 'lunar'
|
calendarType === 'lunar'
|
||||||
? 'bg-[#1a56db] text-white shadow-lg'
|
? 'bg-[var(--jsm-accent)] text-white shadow-lg'
|
||||||
: 'bg-white border-2 border-[#dbe8ff] text-[#04102b] hover:border-[#1a56db]'
|
: 'bg-white border-2 border-[var(--jsm-line)] text-[var(--jsm-ink)] hover:border-[var(--jsm-accent)]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
음력
|
음력
|
||||||
@@ -165,7 +165,7 @@ export default function SajuForm() {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={isLeapMonth}
|
checked={isLeapMonth}
|
||||||
onChange={(e) => setIsLeapMonth(e.target.checked)}
|
onChange={(e) => setIsLeapMonth(e.target.checked)}
|
||||||
className="w-4 h-4 text-[#1a56db] border-gray-300 rounded focus:ring-[#1a56db]"
|
className="w-4 h-4 text-[var(--jsm-accent)] border-gray-300 rounded focus:ring-[var(--jsm-accent)]"
|
||||||
/>
|
/>
|
||||||
<span>윤달</span>
|
<span>윤달</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -175,7 +175,7 @@ export default function SajuForm() {
|
|||||||
|
|
||||||
{/* 성별 선택 */}
|
{/* 성별 선택 */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-left text-sm font-bold text-[#04102b] mb-3">
|
<label className="block text-left text-sm font-bold text-[var(--jsm-ink)] mb-3">
|
||||||
성별
|
성별
|
||||||
</label>
|
</label>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
@@ -184,8 +184,8 @@ export default function SajuForm() {
|
|||||||
onClick={() => setGender('male')}
|
onClick={() => setGender('male')}
|
||||||
className={`px-6 py-3 rounded-xl font-bold transition ${
|
className={`px-6 py-3 rounded-xl font-bold transition ${
|
||||||
gender === 'male'
|
gender === 'male'
|
||||||
? 'bg-[#1a56db] text-white shadow-lg'
|
? 'bg-[var(--jsm-accent)] text-white shadow-lg'
|
||||||
: 'bg-white border-2 border-[#dbe8ff] text-[#04102b] hover:border-[#1a56db]'
|
: 'bg-white border-2 border-[var(--jsm-line)] text-[var(--jsm-ink)] hover:border-[var(--jsm-accent)]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
남성
|
남성
|
||||||
@@ -195,8 +195,8 @@ export default function SajuForm() {
|
|||||||
onClick={() => setGender('female')}
|
onClick={() => setGender('female')}
|
||||||
className={`px-6 py-3 rounded-xl font-bold transition ${
|
className={`px-6 py-3 rounded-xl font-bold transition ${
|
||||||
gender === 'female'
|
gender === 'female'
|
||||||
? 'bg-[#1a56db] text-white shadow-lg'
|
? 'bg-[var(--jsm-accent)] text-white shadow-lg'
|
||||||
: 'bg-white border-2 border-[#dbe8ff] text-[#04102b] hover:border-[#1a56db]'
|
: 'bg-white border-2 border-[var(--jsm-line)] text-[var(--jsm-ink)] hover:border-[var(--jsm-accent)]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
여성
|
여성
|
||||||
@@ -207,7 +207,7 @@ export default function SajuForm() {
|
|||||||
{/* 제출 버튼 */}
|
{/* 제출 버튼 */}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="w-full bg-gradient-to-r from-[#1a56db] to-[#7c3aed] hover:from-[#1e4fc2] hover:to-[#6d28d9] text-white py-4 rounded-xl text-lg font-bold transition shadow-lg hover:shadow-xl hover:scale-[1.02]"
|
className="w-full bg-[var(--jsm-accent)] hover:bg-[var(--jsm-accent-hover)] text-white py-4 rounded-xl text-lg font-bold transition shadow-lg hover:shadow-xl hover:scale-[1.02]"
|
||||||
>
|
>
|
||||||
내 사주 보기 →
|
내 사주 보기 →
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ import SajuForm from '@/app/work/saju/components/SajuForm';
|
|||||||
|
|
||||||
export default function SajuInputPage() {
|
export default function SajuInputPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-full bg-[#f0f5ff]">
|
<div className="min-h-full bg-[var(--jsm-bg)]">
|
||||||
{/* Hero */}
|
{/* Hero */}
|
||||||
<div className="relative overflow-hidden px-6 py-12"
|
<div className="relative overflow-hidden px-6 py-12 bg-[var(--jsm-navy)]">
|
||||||
style={{ background: '#04102b', backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.012) 0px, rgba(255,255,255,0.012) 1px, transparent 1px, transparent 40px)' }}>
|
|
||||||
|
|
||||||
<div className="relative max-w-xl mx-auto text-center">
|
<div className="relative max-w-xl mx-auto text-center">
|
||||||
<div className="inline-flex items-center gap-2 bg-violet-400/10 border border-violet-400/25 text-violet-300 text-xs font-semibold px-4 py-1.5 rounded-full mb-4 tracking-wide">
|
<div className="inline-flex items-center gap-2 bg-white/10 border border-white/20 text-[var(--jsm-accent-soft)] text-xs font-semibold px-4 py-1.5 rounded-full mb-4 tracking-wide">
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-amber-400 animate-pulse" />
|
<span className="w-1.5 h-1.5 rounded-full bg-amber-400 animate-pulse" />
|
||||||
AI 사주 분석 · 생년월일 입력
|
AI 사주 분석 · 생년월일 입력
|
||||||
</div>
|
</div>
|
||||||
@@ -24,10 +23,10 @@ export default function SajuInputPage() {
|
|||||||
|
|
||||||
{/* Form 영역 */}
|
{/* Form 영역 */}
|
||||||
<div className="px-6 py-10 max-w-2xl mx-auto">
|
<div className="px-6 py-10 max-w-2xl mx-auto">
|
||||||
<div className="bg-white rounded-2xl border border-[#dbe8ff] p-8 shadow-lg">
|
<div className="bg-white rounded-2xl border border-[var(--jsm-line)] p-8 shadow-lg">
|
||||||
<div className="flex items-center gap-2 mb-6">
|
<div className="flex items-center gap-2 mb-6">
|
||||||
<div className="w-1 h-5 bg-gradient-to-b from-[#1a56db] to-[#7c3aed] rounded-full" />
|
<div className="w-1 h-5 bg-[var(--jsm-accent)] rounded-full" />
|
||||||
<h2 className="font-bold text-[#04102b] text-base">기본 정보 입력</h2>
|
<h2 className="font-bold text-[var(--jsm-ink)] text-base">기본 정보 입력</h2>
|
||||||
</div>
|
</div>
|
||||||
<SajuForm />
|
<SajuForm />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,14 +74,14 @@ export default function SajuPage() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-full bg-[#f0f5ff]">
|
<div className="min-h-full bg-[var(--jsm-bg)]">
|
||||||
{/* ─── Hero ─── */}
|
{/* ─── Hero ─── */}
|
||||||
<div className="relative overflow-hidden bg-[#04102b] px-6 py-14 lg:px-12" style={{ backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 40px)' }}>
|
<div className="relative overflow-hidden bg-[var(--jsm-navy)] px-6 py-14 lg:px-12">
|
||||||
|
|
||||||
<div className="relative max-w-3xl mx-auto">
|
<div className="relative max-w-3xl mx-auto">
|
||||||
<div className="flex items-center gap-2 mb-5">
|
<div className="flex items-center gap-2 mb-5">
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-amber-400 animate-pulse" />
|
<span className="w-1.5 h-1.5 rounded-full bg-amber-400 animate-pulse" />
|
||||||
<span className="text-violet-300/70 text-xs font-mono tracking-widest uppercase">전통 명리학 × AI 해석 · 무료</span>
|
<span className="text-[var(--jsm-accent-soft)] text-xs font-mono tracking-widest uppercase">전통 명리학 × AI 해석 · 무료</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-4xl md:text-5xl font-extrabold text-white leading-tight mb-5 tracking-tight">
|
<h1 className="text-4xl md:text-5xl font-extrabold text-white leading-tight mb-5 tracking-tight">
|
||||||
AI가 분석하는<br />
|
AI가 분석하는<br />
|
||||||
@@ -97,7 +97,7 @@ export default function SajuPage() {
|
|||||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-3">
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-3">
|
||||||
<Link
|
<Link
|
||||||
href="/work/saju/input"
|
href="/work/saju/input"
|
||||||
className="inline-flex items-center gap-2 bg-[#1a56db] hover:bg-[#1e4fc2] text-white px-7 py-3.5 rounded-xl font-semibold text-base transition-all"
|
className="inline-flex items-center gap-2 bg-[var(--jsm-accent)] hover:bg-[var(--jsm-accent-hover)] text-white px-7 py-3.5 rounded-xl font-semibold text-base transition-all"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
@@ -117,7 +117,7 @@ export default function SajuPage() {
|
|||||||
) : (
|
) : (
|
||||||
<Link
|
<Link
|
||||||
href="/work/saju/input"
|
href="/work/saju/input"
|
||||||
className="inline-flex items-center gap-2 bg-gradient-to-r from-[#1a56db] to-[#7c3aed] hover:from-[#1e4fc2] hover:to-[#6d28d9] text-white px-8 py-3.5 rounded-xl font-semibold text-base transition-all shadow-lg shadow-violet-900/40"
|
className="inline-flex items-center gap-2 bg-[var(--jsm-accent)] hover:bg-[var(--jsm-accent-hover)] text-white px-8 py-3.5 rounded-xl font-semibold text-base transition-all shadow-lg"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" />
|
||||||
@@ -135,19 +135,19 @@ export default function SajuPage() {
|
|||||||
{hasPaid && paidRecords.length > 0 && (
|
{hasPaid && paidRecords.length > 0 && (
|
||||||
<div id="past-records">
|
<div id="past-records">
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-6">
|
||||||
<p className="text-violet-600 text-xs font-bold uppercase tracking-widest mb-2">MY RECORDS</p>
|
<p className="text-[var(--jsm-accent)] text-xs font-bold uppercase tracking-widest mb-2">MY RECORDS</p>
|
||||||
<h2 className="text-2xl font-extrabold text-[#04102b]">이전 AI 사주 기록</h2>
|
<h2 className="text-2xl font-extrabold text-[var(--jsm-ink)]">이전 AI 사주 기록</h2>
|
||||||
<p className="text-slate-500 text-sm mt-1">결제한 사주 기록을 다시 확인하세요</p>
|
<p className="text-slate-500 text-sm mt-1">결제한 사주 기록을 다시 확인하세요</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid md:grid-cols-2 gap-4">
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
{paidRecords.map((rec) => (
|
{paidRecords.map((rec) => (
|
||||||
<div key={rec.id} className="bg-white rounded-2xl border border-[#dbe8ff] p-5 hover:border-violet-300 transition-colors">
|
<div key={rec.id} className="bg-white rounded-2xl border border-[var(--jsm-line)] p-5 hover:border-[var(--jsm-accent-soft)] transition-colors">
|
||||||
<div className="flex items-start justify-between mb-3">
|
<div className="flex items-start justify-between mb-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs text-slate-400 mb-1">
|
<div className="text-xs text-slate-400 mb-1">
|
||||||
{new Date(rec.created_at).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', day: 'numeric' })}
|
{new Date(rec.created_at).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-bold text-[#04102b] text-base">
|
<div className="font-bold text-[var(--jsm-ink)] text-base">
|
||||||
{rec.saju_data.birth_year ?? '?'}년{' '}
|
{rec.saju_data.birth_year ?? '?'}년{' '}
|
||||||
{rec.saju_data.birth_month ?? '?'}월{' '}
|
{rec.saju_data.birth_month ?? '?'}월{' '}
|
||||||
{rec.saju_data.birth_day ?? '?'}일생
|
{rec.saju_data.birth_day ?? '?'}일생
|
||||||
@@ -168,7 +168,7 @@ export default function SajuPage() {
|
|||||||
)}
|
)}
|
||||||
<Link
|
<Link
|
||||||
href={buildResultUrl(rec)}
|
href={buildResultUrl(rec)}
|
||||||
className="block w-full text-center py-2 rounded-xl text-sm font-bold bg-[#04102b] hover:bg-[#0a1f5c] text-white border border-[#1a3a7a] transition"
|
className="block w-full text-center py-2 rounded-xl text-sm font-bold bg-[var(--jsm-navy)] hover:opacity-90 text-white transition"
|
||||||
>
|
>
|
||||||
다시 보기 →
|
다시 보기 →
|
||||||
</Link>
|
</Link>
|
||||||
@@ -179,18 +179,12 @@ export default function SajuPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ─── 바로 시작하기 CTA ─── */}
|
{/* ─── 바로 시작하기 CTA ─── */}
|
||||||
<div
|
<div className="rounded-2xl p-8 text-center bg-[var(--jsm-navy)]">
|
||||||
className="rounded-2xl border border-[#1a3a7a] p-8 text-center"
|
|
||||||
style={{
|
|
||||||
background: '#04102b',
|
|
||||||
backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 30px)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<h3 className="text-2xl font-extrabold text-white mb-2">지금 무료로 시작하세요</h3>
|
<h3 className="text-2xl font-extrabold text-white mb-2">지금 무료로 시작하세요</h3>
|
||||||
<p className="text-blue-200/60 text-sm mb-6">회원가입 없이, 생년월일만 입력하면 바로 확인 가능합니다</p>
|
<p className="text-blue-200/60 text-sm mb-6">회원가입 없이, 생년월일만 입력하면 바로 확인 가능합니다</p>
|
||||||
<Link
|
<Link
|
||||||
href="/work/saju/input"
|
href="/work/saju/input"
|
||||||
className="inline-flex items-center gap-2 bg-amber-400 hover:bg-amber-300 text-[#04102b] px-8 py-3.5 rounded-xl font-bold text-base transition-all"
|
className="inline-flex items-center gap-2 bg-amber-400 hover:bg-amber-300 text-[var(--jsm-ink)] px-8 py-3.5 rounded-xl font-bold text-base transition-all"
|
||||||
>
|
>
|
||||||
사주 입력하러 가기 →
|
사주 입력하러 가기 →
|
||||||
</Link>
|
</Link>
|
||||||
@@ -199,23 +193,23 @@ export default function SajuPage() {
|
|||||||
{/* ─── 무료 vs 유료 비교표 ─── */}
|
{/* ─── 무료 vs 유료 비교표 ─── */}
|
||||||
<div>
|
<div>
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<p className="text-[#1a56db] text-xs font-bold uppercase tracking-widest mb-2">PRICING</p>
|
<p className="text-[var(--jsm-accent)] text-xs font-bold uppercase tracking-widest mb-2">PRICING</p>
|
||||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b] tracking-tight">무엇을 분석해드리나요</h2>
|
<h2 className="text-2xl md:text-3xl font-extrabold text-[var(--jsm-ink)] tracking-tight">무엇을 분석해드리나요</h2>
|
||||||
<p className="text-slate-500 text-sm mt-2">기본 원국은 무료, AI 상세 해석은 1,000원</p>
|
<p className="text-slate-500 text-sm mt-2">기본 원국은 무료, AI 상세 해석은 1,000원</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
{/* 무료 */}
|
{/* 무료 */}
|
||||||
<div className="bg-white rounded-2xl border border-[#dbe8ff] p-6 shadow-sm">
|
<div className="bg-white rounded-2xl border border-[var(--jsm-line)] p-6 shadow-sm">
|
||||||
<div className="flex items-center gap-3 mb-5">
|
<div className="flex items-center gap-3 mb-5">
|
||||||
<div className="w-10 h-10 rounded-xl bg-[#f0f5ff] border border-[#dbe8ff] flex items-center justify-center">
|
<div className="w-10 h-10 rounded-xl bg-[var(--jsm-surface-alt)] border border-[var(--jsm-line)] flex items-center justify-center">
|
||||||
<svg className="w-5 h-5 text-[#1a56db]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 text-[var(--jsm-accent)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs font-bold text-slate-500 uppercase tracking-wide">FREE</div>
|
<div className="text-xs font-bold text-slate-500 uppercase tracking-wide">FREE</div>
|
||||||
<div className="text-lg font-extrabold text-[#04102b]">무료 기본 분석</div>
|
<div className="text-lg font-extrabold text-[var(--jsm-ink)]">무료 기본 분석</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul className="space-y-3">
|
<ul className="space-y-3">
|
||||||
@@ -229,18 +223,18 @@ export default function SajuPage() {
|
|||||||
].map((item) => (
|
].map((item) => (
|
||||||
<li key={item} className="flex items-center gap-2.5 text-sm text-slate-700">
|
<li key={item} className="flex items-center gap-2.5 text-sm text-slate-700">
|
||||||
<div className="w-4 h-4 rounded-full bg-blue-100 border border-blue-200 flex items-center justify-center flex-shrink-0">
|
<div className="w-4 h-4 rounded-full bg-blue-100 border border-blue-200 flex items-center justify-center flex-shrink-0">
|
||||||
<div className="w-1.5 h-1.5 rounded-full bg-[#1a56db]" />
|
<div className="w-1.5 h-1.5 rounded-full bg-[var(--jsm-accent)]" />
|
||||||
</div>
|
</div>
|
||||||
{item}
|
{item}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<div className="mt-6 pt-5 border-t border-slate-100">
|
<div className="mt-6 pt-5 border-t border-slate-100">
|
||||||
<div className="text-2xl font-extrabold text-[#04102b]">무료</div>
|
<div className="text-2xl font-extrabold text-[var(--jsm-ink)]">무료</div>
|
||||||
<div className="text-xs text-slate-500 mt-1">회원가입 불필요</div>
|
<div className="text-xs text-slate-500 mt-1">회원가입 불필요</div>
|
||||||
<Link
|
<Link
|
||||||
href="/work/saju/input"
|
href="/work/saju/input"
|
||||||
className="mt-4 block w-full text-center py-2.5 rounded-xl text-sm font-bold bg-[#f0f5ff] border border-[#dbe8ff] text-[#1a56db] hover:bg-blue-50 transition"
|
className="mt-4 block w-full text-center py-2.5 rounded-xl text-sm font-bold bg-[var(--jsm-surface-alt)] border border-[var(--jsm-line)] text-[var(--jsm-accent)] hover:bg-blue-50 transition"
|
||||||
>
|
>
|
||||||
무료로 시작하기
|
무료로 시작하기
|
||||||
</Link>
|
</Link>
|
||||||
@@ -248,24 +242,18 @@ export default function SajuPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* AI 해석 (현재 무료) */}
|
{/* AI 해석 (현재 무료) */}
|
||||||
<div
|
<div className="rounded-2xl p-6 shadow-lg relative overflow-hidden bg-[var(--jsm-navy)]">
|
||||||
className="rounded-2xl border border-[#1a3a7a] p-6 shadow-lg relative overflow-hidden"
|
<div className="absolute top-4 right-4 bg-amber-400 text-[var(--jsm-ink)] text-xs font-bold px-2 py-0.5 rounded-lg">
|
||||||
style={{
|
|
||||||
background: '#04102b',
|
|
||||||
backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 30px)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="absolute top-4 right-4 bg-amber-400 text-[#04102b] text-xs font-bold px-2 py-0.5 rounded-lg">
|
|
||||||
1,000원
|
1,000원
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 mb-5 relative">
|
<div className="flex items-center gap-3 mb-5 relative">
|
||||||
<div className="w-10 h-10 rounded-xl bg-violet-500/20 border border-violet-400/30 flex items-center justify-center">
|
<div className="w-10 h-10 rounded-xl bg-white/10 border border-white/20 flex items-center justify-center">
|
||||||
<svg className="w-5 h-5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs font-bold text-violet-300 uppercase tracking-wide">AI PREMIUM</div>
|
<div className="text-xs font-bold text-[var(--jsm-accent-soft)] uppercase tracking-wide">AI PREMIUM</div>
|
||||||
<div className="text-lg font-extrabold text-white">AI 상세 해석</div>
|
<div className="text-lg font-extrabold text-white">AI 상세 해석</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -294,7 +282,7 @@ export default function SajuPage() {
|
|||||||
<div className="text-xs text-blue-300/70 mt-1 mb-4">로그인 후 결제 · 12가지 항목 AI 해석</div>
|
<div className="text-xs text-blue-300/70 mt-1 mb-4">로그인 후 결제 · 12가지 항목 AI 해석</div>
|
||||||
<Link
|
<Link
|
||||||
href="/work/saju/input"
|
href="/work/saju/input"
|
||||||
className="block w-full text-center py-3 rounded-xl text-sm font-bold transition bg-amber-400 text-[#04102b] hover:bg-amber-300"
|
className="block w-full text-center py-3 rounded-xl text-sm font-bold transition bg-amber-400 text-[var(--jsm-ink)] hover:bg-amber-300"
|
||||||
>
|
>
|
||||||
사주 분석 시작하기 →
|
사주 분석 시작하기 →
|
||||||
</Link>
|
</Link>
|
||||||
@@ -306,18 +294,18 @@ export default function SajuPage() {
|
|||||||
{/* ─── FAQ ─── */}
|
{/* ─── FAQ ─── */}
|
||||||
<div>
|
<div>
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<p className="text-[#1a56db] text-xs font-bold uppercase tracking-widest mb-2">FAQ</p>
|
<p className="text-[var(--jsm-accent)] text-xs font-bold uppercase tracking-widest mb-2">FAQ</p>
|
||||||
<h2 className="text-2xl font-extrabold text-[#04102b]">자주 묻는 질문</h2>
|
<h2 className="text-2xl font-extrabold text-[var(--jsm-ink)]">자주 묻는 질문</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{faqItems.map((item, i) => (
|
{faqItems.map((item, i) => (
|
||||||
<div key={i} className="bg-white rounded-2xl border border-[#dbe8ff] p-6">
|
<div key={i} className="bg-white rounded-2xl border border-[var(--jsm-line)] p-6">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="w-6 h-6 rounded-full bg-[#f0f5ff] border border-[#dbe8ff] flex items-center justify-center flex-shrink-0 mt-0.5">
|
<div className="w-6 h-6 rounded-full bg-[var(--jsm-surface-alt)] border border-[var(--jsm-line)] flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||||
<span className="text-[#1a56db] text-xs font-bold">Q</span>
|
<span className="text-[var(--jsm-accent)] text-xs font-bold">Q</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-bold text-[#04102b] text-sm mb-2">{item.q}</p>
|
<p className="font-bold text-[var(--jsm-ink)] text-sm mb-2">{item.q}</p>
|
||||||
<p className="text-slate-600 text-sm leading-relaxed">{item.a}</p>
|
<p className="text-slate-600 text-sm leading-relaxed">{item.a}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
143
docs/superpowers/plans/2026-07-03-phase2_6-saju-entry-reskin.md
Normal file
143
docs/superpowers/plans/2026-07-03-phase2_6-saju-entry-reskin.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# Phase 2.6 사주 랜딩·입력 라이트 재스킨 Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 사주 진입 화면 3파일(랜딩 page.tsx·input/page.tsx·SajuForm.tsx)을 `--jsm` 라이트로 재스킨해 사주 서비스 전 화면 톤을 통일한다.
|
||||||
|
|
||||||
|
**Architecture:** Phase 2.5와 동일한 순수 시각 재스킨 — 로직·데이터·props 무변경, 다크 hex·gradient·보라 className을 `--jsm-*` 토큰으로 치환. 대상 3파일엔 이모지가 없어 아이콘 작업 불필요. 각 파일 done-condition은 금지 패턴 grep 0건 + 빌드 성공.
|
||||||
|
|
||||||
|
**Tech Stack:** Next.js 16 (App Router, TS), Tailwind v4(`--jsm-*` 토큰), vitest
|
||||||
|
|
||||||
|
**Spec:** `docs/superpowers/specs/2026-07-03-phase2_6-saju-entry-reskin-design.md`
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- **순수 시각 변경**: 로직·조건분기·fetch·상태·props·데이터·라우팅 라인은 건드리지 않는다(className/style만)
|
||||||
|
- 신규 색 토큰 추가 금지 — 아래 11개 `--jsm-*`만:
|
||||||
|
`--jsm-bg --jsm-surface --jsm-surface-alt --jsm-ink --jsm-ink-soft --jsm-ink-faint --jsm-line --jsm-navy --jsm-accent --jsm-accent-soft --jsm-accent-hover`
|
||||||
|
- 대상 3파일에서 `gradient` / `violet` / `purple` / `blur` / 이모지 **0건**(각 Task 종료 게이트)
|
||||||
|
- navy 밴드 = 무테두리 flat + 흰 CTA(2.5·`app/page.tsx`에서 확립된 사이트 관용구). navy 배경 위 강조 텍스트는 `--jsm-accent-soft`(대비 확보)
|
||||||
|
- 의미색(상태 신호색)은 있으면 2.5 선례대로 보존
|
||||||
|
- next.config.ts·로직 파일 수정 금지
|
||||||
|
- 커밋은 스코프 파일만 스테이징 — **`git add -A`·`git commit -a` 금지**, 커밋 전 `git status` 확인
|
||||||
|
- 각 Task 종료 시 `npm run build` 성공 + `npm test`(30) 유지 후 커밋
|
||||||
|
- 커밋 트레일러: `Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>`
|
||||||
|
|
||||||
|
## 색상 매핑 (모든 Task 공통 — 이 표대로 치환)
|
||||||
|
|
||||||
|
| 현재(제거 대상) | 교체(→) |
|
||||||
|
|------|------|
|
||||||
|
| `#04102b` 다크 배경 + `repeating-linear-gradient` 텍스처 | `bg-[var(--jsm-navy)]` 플랫(히어로 밴드) / `bg-[var(--jsm-bg)]`(페이지). 텍스처 style 삭제 |
|
||||||
|
| `bg-gradient-to-r from-[#1a56db] to-[#7c3aed]`(+hover) CTA | `bg-[var(--jsm-accent)] hover:bg-[var(--jsm-accent-hover)]` 흰 텍스트 |
|
||||||
|
| `violet-300/500/600`·`#7c3aed`·`shadow-violet-*` | `var(--jsm-accent)`; navy 밴드 위 텍스트는 `var(--jsm-accent-soft)` |
|
||||||
|
| amber 강조 | `var(--jsm-accent)` 또는 `var(--jsm-ink-soft)` |
|
||||||
|
| 흰색·`blue-xx` 본문 텍스트 | `text-[var(--jsm-ink)]` / `ink-soft` / `ink-faint` |
|
||||||
|
| 다크 카드/테두리 `#dbe8ff`·`#1a3a7a` 등 | `bg-[var(--jsm-surface)]` + `border-[var(--jsm-line)]` |
|
||||||
|
|
||||||
|
참고 관용구: `app/work/saju/result/page.tsx`(2.5 완료본), `app/showcase/page.tsx`, `app/page.tsx`(navy CTA 밴드).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: 랜딩 page.tsx 재스킨
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `app/work/saju/page.tsx` (333줄, 위반 9건)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: 없음 (기존 --jsm 토큰만)
|
||||||
|
- Produces: 없음
|
||||||
|
|
||||||
|
- [ ] **Step 1: 전체 Read 후 색상 매핑 치환**
|
||||||
|
|
||||||
|
`app/work/saju/page.tsx`를 먼저 전체 Read. 다음 위반 지점을 색상 매핑 표대로 치환(className/style만):
|
||||||
|
- 히어로(79행): `bg-[#04102b]` + `repeating-linear-gradient` style → `bg-[var(--jsm-navy)]`(밴드) + 텍스처 style 삭제
|
||||||
|
- 부제(84행): `text-violet-300/70` → `text-[var(--jsm-accent-soft)]`(navy 위)
|
||||||
|
- CTA 버튼(120행): `bg-gradient-to-r from-[#1a56db] to-[#7c3aed] hover:… shadow-violet-900/40` → `bg-[var(--jsm-accent)] hover:bg-[var(--jsm-accent-hover)]`(shadow는 기본 유지 또는 제거)
|
||||||
|
- MY RECORDS 라벨(138행): `text-violet-600` → `text-[var(--jsm-accent)]`
|
||||||
|
- 레코드 카드(144행): `border-[#dbe8ff] hover:border-violet-300` → `border-[var(--jsm-line)] hover:border-[var(--jsm-accent-soft)]`
|
||||||
|
- 하단 섹션 텍스처(186·255행): `repeating-linear-gradient` style 삭제, 다크 배경 → navy/surface
|
||||||
|
- AI PREMIUM 카드(262·268행): `bg-violet-500/20 border-violet-400/30`·`text-violet-300` → navy 밴드 위 `bg-white/10 border-white/20`·`text-[var(--jsm-accent-soft)]`
|
||||||
|
- 그 외 `#04102b` 텍스트·다크 카드 → `--jsm-ink`/`surface`/`line`
|
||||||
|
- **서버/클라 로직·데이터 조회(MY RECORDS)·JSX 구조 미변경**
|
||||||
|
|
||||||
|
- [ ] **Step 2: 게이트 검증**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -nE "gradient|violet|purple|blur" app/work/saju/page.tsx # 기대: 0
|
||||||
|
npm run build && npm test # build 성공 + 30 PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 커밋** — `git add app/work/saju/page.tsx && git commit -m "feat(phase2.6): 사주 랜딩 라이트 재스킨 — gradient/보라→--jsm, 텍스처 제거"`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: input/page.tsx + SajuForm.tsx 재스킨
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `app/work/saju/input/page.tsx` (41줄, 위반 3건)
|
||||||
|
- Modify: `app/work/saju/components/SajuForm.tsx` (220줄, 위반 1건)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: 없음
|
||||||
|
- Produces: 없음
|
||||||
|
|
||||||
|
- [ ] **Step 1: 두 파일 Read 후 치환**
|
||||||
|
|
||||||
|
각 파일 전체 Read. 다크/gradient/violet/purple 지점을 색상 매핑 표대로 치환:
|
||||||
|
- `input/page.tsx`: 입력 화면 컨테이너·헤더·다크 배경 → `bg-[var(--jsm-bg)]`/navy 밴드, violet → accent, 텍스트 → ink
|
||||||
|
- `SajuForm.tsx`: 폼 필드·버튼·강조 색의 gradient/violet → 플랫 accent, 다크 → surface/line. **`useSajuForm` 등 폼 상태·핸들러·submit 로직 미변경**
|
||||||
|
|
||||||
|
- [ ] **Step 2: 게이트 검증**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -nE "gradient|violet|purple|blur" app/work/saju/input/page.tsx app/work/saju/components/SajuForm.tsx # 0
|
||||||
|
npm run build && npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 커밋** — `git add app/work/saju/input/page.tsx app/work/saju/components/SajuForm.tsx && git commit -m "feat(phase2.6): 사주 입력 화면·폼 라이트 재스킨"`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: 사주 전체 게이트 + CLAUDE.md
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `CLAUDE.md`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: Task 1~2 완료
|
||||||
|
- Produces: 문서 정합
|
||||||
|
|
||||||
|
- [ ] **Step 1: 사주 전체 게이트 검증**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -rnE "gradient|violet|purple|blur" app/work/saju --include="*.tsx" # result 포함 사주 전체 0건
|
||||||
|
```
|
||||||
|
2.5 재스킨된 result 파일(SajuIcons·SajuAISection·SajuFortuneSection·result/page)과 이번 3파일 모두 0건이어야 함. 이모지도 Grep 유니코드로 0건 확인.
|
||||||
|
|
||||||
|
- [ ] **Step 2: CLAUDE.md 갱신**
|
||||||
|
|
||||||
|
`## 사주 시스템` 섹션의 2.5 재스킨 안내 문구("결과 화면 --jsm 라이트 재스킨 완료(2026-07-03)")를 "전 화면(랜딩·입력·결과) --jsm 라이트 재스킨 완료(2026-07-03)"로 갱신. 무관 섹션 미변경.
|
||||||
|
|
||||||
|
- [ ] **Step 3: 최종 빌드·테스트**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build # 성공, /work/saju·/work/saju/input·/work/saju/result 라우트 존재
|
||||||
|
npm test # 30 PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 커밋** — `git add CLAUDE.md && git commit -m "docs(phase2.6): CLAUDE.md — 사주 전 화면 라이트 재스킨 반영"`
|
||||||
|
|
||||||
|
- [ ] **Step 5: 보고**
|
||||||
|
- 수동 확인: `/work/saju` 랜딩→입력→결과 전 플로우가 라이트로 일관 렌더
|
||||||
|
- 사주 서비스 전 화면 라이트 완결 — 재스킨 후속 없음
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 검증 요약
|
||||||
|
|
||||||
|
| 검증 | 명령 | 기대 |
|
||||||
|
|------|------|------|
|
||||||
|
| 사주 전체 가드레일 | `grep -rnE "gradient\|violet\|purple\|blur" app/work/saju --include="*.tsx"` | 0건 |
|
||||||
|
| 이모지 | grep 유니코드 범위 | 0건 |
|
||||||
|
| 빌드 | `npm run build` | 성공 |
|
||||||
|
| 테스트 | `npm test` | 30 PASS(무변) |
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Phase 2.6 사주 랜딩·입력 라이트 재스킨 설계
|
||||||
|
|
||||||
|
- 날짜: 2026-07-03
|
||||||
|
- 선행: Phase 2.5(사주 결과 화면 라이트 재스킨) main 머지 완료(5ace251)
|
||||||
|
- 배경: 2.5가 결과 화면만 라이트로 전환 → 공개 진입점(`/work/saju` 랜딩·입력)이 아직 다크로 남아 톤 불일치. 남은 3파일을 동일 방식으로 전환해 사주 서비스 전 화면 라이트 완결.
|
||||||
|
|
||||||
|
## 결정 사항 (CEO 확정, 2026-07-03)
|
||||||
|
|
||||||
|
| 결정 | 내용 |
|
||||||
|
|------|------|
|
||||||
|
| 방식 | **Phase 2.5와 완전 동일** — --jsm 라이트 순수 시각 재스킨, 색상 매핑 표 재사용 |
|
||||||
|
| 범위 | 랜딩 `page.tsx` + 입력 `input/page.tsx` + `components/SajuForm.tsx` (3파일) |
|
||||||
|
| 이모지 | **대상 3파일에 이모지 없음(grep 0)** — 아이콘 교체 불필요, 순수 색 재스킨 |
|
||||||
|
|
||||||
|
## 대상 (위반 분포, gradient/violet/purple/blur)
|
||||||
|
|
||||||
|
- `app/work/saju/page.tsx` — 랜딩(333줄, **9건**): 히어로 `bg-[#04102b]`+`repeating-linear-gradient` 텍스처, `bg-gradient-to-r from-[#1a56db] to-[#7c3aed]` CTA 버튼, `violet-300/600/500` 텍스트·뱃지·테두리, AI PREMIUM 카드, MY RECORDS 섹션
|
||||||
|
- `app/work/saju/input/page.tsx` — 입력(41줄, **3건**)
|
||||||
|
- `app/work/saju/components/SajuForm.tsx` — 폼(220줄, **1건**)
|
||||||
|
|
||||||
|
## 원칙 (2.5 스펙 그대로)
|
||||||
|
|
||||||
|
- **순수 시각 변경**: 로직·데이터·props·라우팅 무변경. className/style만
|
||||||
|
- 신규 색 토큰 금지 — 기존 11개 `--jsm-*`(bg/surface/surface-alt/ink/ink-soft/ink-faint/line/navy/accent/accent-soft/accent-hover)만
|
||||||
|
- gradient / blur / 보라(violet/purple) / 이모지 **0건**(대상 3파일)
|
||||||
|
- navy 밴드 = 무테두리 flat + 흰 CTA(2.5·`app/page.tsx`에서 확립된 사이트 관용구)
|
||||||
|
- 의미색(있다면 상태 신호색)은 2.5 선례대로 보존
|
||||||
|
|
||||||
|
## 색상 매핑 (2.5와 동일 — 이 표대로 치환)
|
||||||
|
|
||||||
|
| 현재(제거) | 교체(→) |
|
||||||
|
|------|------|
|
||||||
|
| `#04102b` 다크 배경 + `repeating-linear-gradient` 텍스처 | `bg-[var(--jsm-navy)]` 플랫(히어로 밴드) / `bg-[var(--jsm-bg)]`(페이지) — 텍스처 제거 |
|
||||||
|
| `bg-gradient-to-r from-[#1a56db] to-[#7c3aed]` CTA | `bg-[var(--jsm-accent)]` 단색 + `hover:bg-[var(--jsm-accent-hover)]`, 흰 텍스트 |
|
||||||
|
| `violet-300/500/600`·`#7c3aed`·`shadow-violet-*` | `var(--jsm-accent)` / navy 밴드 위 텍스트는 `var(--jsm-accent-soft)`(대비) |
|
||||||
|
| amber 강조 | `var(--jsm-accent)` 또는 `var(--jsm-ink-soft)` |
|
||||||
|
| 흰색/`blue-xx` 본문 텍스트 | `text-[var(--jsm-ink)]` / `ink-soft` / `ink-faint` |
|
||||||
|
| 다크 카드/테두리 `#dbe8ff` 등 | `bg-[var(--jsm-surface)]` + `border-[var(--jsm-line)]` |
|
||||||
|
|
||||||
|
참고 관용구: `app/work/saju/result/page.tsx`(2.5 재스킨 완료본), `app/showcase/page.tsx`, `app/page.tsx`(navy CTA 밴드).
|
||||||
|
|
||||||
|
## 워크스트림 3개 (순차)
|
||||||
|
|
||||||
|
### WS1. 랜딩 page.tsx 재스킨 (9건)
|
||||||
|
- 히어로 다크 밴드+repeating-linear-gradient 텍스처 → `bg-[var(--jsm-navy)]` 플랫, 페이지 배경 라이트
|
||||||
|
- CTA 버튼 gradient → 플랫 accent
|
||||||
|
- `violet-*` 텍스트/뱃지/테두리(부제·MY RECORDS 라벨·레코드 카드 hover·AI PREMIUM 카드) → --jsm 토큰
|
||||||
|
- 서버/클라 로직·데이터 조회·JSX 구조 미변경
|
||||||
|
|
||||||
|
### WS2. input/page.tsx + SajuForm.tsx 재스킨 (4건)
|
||||||
|
- 입력 화면·폼의 다크/gradient/violet → --jsm 토큰. 폼 필드·버튼 스타일 라이트
|
||||||
|
- `useSajuForm` 등 로직·상태·핸들러 미변경
|
||||||
|
|
||||||
|
### WS3. 최종 검증 + CLAUDE.md
|
||||||
|
- **사주 전체 게이트**: `grep -rnE "gradient|violet|purple|blur" app/work/saju/**/*.tsx`(result 포함) → 0건, 이모지 0건
|
||||||
|
- `npm run build` 성공, `npm test`(30) 유지
|
||||||
|
- CLAUDE.md 사주 시스템 섹션: "결과 화면"→"전 화면(랜딩·입력·결과) --jsm 라이트 재스킨 완료(2026-07-03)"로 갱신
|
||||||
|
|
||||||
|
## 범위 밖
|
||||||
|
|
||||||
|
- 로직·계산·프롬프트·데이터 무변경(순수 시각)
|
||||||
|
- 이로써 사주 서비스 전 화면 라이트 완결 — 추가 재스킨 후속 없음
|
||||||
|
|
||||||
|
## 리스크·주의
|
||||||
|
|
||||||
|
- 랜딩 `page.tsx`가 클라이언트에서 saju 기록(MY RECORDS)을 조회하는 로직이 있으면 그 fetch/상태는 미변경 — className만
|
||||||
|
- navy 밴드 위 accent 텍스트는 대비상 `accent-soft` 사용(2.5 선례)
|
||||||
|
- 시각 회귀 자동 테스트 불가 → 빌드+게이트 grep + 수동 확인
|
||||||
Reference in New Issue
Block a user