Compare commits

..

5 Commits

Author SHA1 Message Date
a5b47a0278 docs(phase2.6): CLAUDE.md — 사주 전 화면 라이트 재스킨 반영
- 사주 시스템 섹션: 결과 화면 → 전 화면(랜딩·입력·결과)
- 2026-07-03 라이트 재스킨 완료 명시

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 11:32:25 +09:00
d5be617eb2 feat(phase2.6): 사주 입력 화면·폼 라이트 재스킨
app/work/saju/input/page.tsx: 다크 히어로(#04102b + repeating-linear
-gradient 텍스처) → bg-[var(--jsm-navy)] 플랫 밴드, violet 배지 →
navy 위 accent-soft, 하드코드 hex(#dbe8ff/#1a56db/#7c3aed/#04102b) →
jsm-line/jsm-accent/jsm-ink 토큰.

SajuForm.tsx: 제출 버튼 gradient(#1a56db→#7c3aed) → 플랫
bg-[var(--jsm-accent)] hover:bg-[var(--jsm-accent-hover)]. 폼 필드
보더·포커스·선택 버튼·체크박스의 하드코드 hex를 동일 역할의
--jsm-line/--jsm-accent/--jsm-ink 토큰으로 통일.

useSajuForm 상태·핸들러·submit·라우팅 로직은 라인 단위로 100% 동일
(className/style만 변경). grep gradient|violet|purple|blur 게이트
0건, npm run build 성공, npm test 30/30 유지.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 11:25:53 +09:00
abec100a73 feat(phase2.6): 사주 랜딩 라이트 재스킨 — gradient/보라→--jsm, 텍스처 제거
app/work/saju/page.tsx 순수 시각 변경(className/style만). 히어로·MY RECORDS·
바로시작 CTA·PRICING 비교표·FAQ 전 구간의 #04102b/violet/gradient 하드코드를
--jsm-navy/ink/accent/accent-soft/line/surface-alt 토큰으로 치환하고
repeating-linear-gradient 텍스처와 다크 카드 테두리(#1a3a7a)를 제거해
result 페이지(Phase 2.5)와 동일한 navy 밴드 무테두리 flat 관용구로 정렬.
데이터 조회·상태·JSX 구조는 변경 없음.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 11:18:43 +09:00
fc55e6a928 @
docs(phase2.6): 사주 랜딩·입력 재스킨 구현 플랜 (3 Task)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAtcmKKtqDUe4NyVgy1aLQ
@
2026-07-03 11:13:36 +09:00
4f41f09a8c docs(phase2.6): 사주 랜딩·입력 라이트 재스킨 설계 — 2.5 패턴 연속 (WS1~3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAtcmKKtqDUe4NyVgy1aLQ
2026-07-03 11:11:05 +09:00
6 changed files with 268 additions and 70 deletions

View File

@@ -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`)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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(무변) |

View File

@@ -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 + 수동 확인