feat(phase2.5): SajuFortuneSection 라이트 재스킨 — 로또 아이콘 SVG, 라디얼 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 10:32:02 +09:00
parent 5e79ea9233
commit fa9cda4f50

View File

@@ -1,6 +1,7 @@
'use client';
import { useMemo } from 'react';
import { LottoIcon } from './SajuIcons';
// ── 천간 / 지지 ───────────────────────────────────────────────────────
const STEMS = ['甲','乙','丙','丁','戊','己','庚','辛','壬','癸'];
@@ -73,8 +74,33 @@ function seededRand(seed: number) {
return () => { s = (s * 1664525 + 1013904223) & 0xffffffff; return (s >>> 0) / 0xffffffff; };
}
// ── 로컬 라인 아이콘 (이모지 대체, 파일 전용 — SajuIcons.tsx 미변경) ──────
type GlyphName = 'sun' | 'great' | 'good' | 'neutral' | 'caution' | 'money' | 'love' | 'career' | 'health' | 'social';
const GLYPH_PATHS: Record<GlyphName, string> = {
sun: 'M12 8a4 4 0 100 8 4 4 0 000-8zM12 2v2M12 20v2M4 12H2M22 12h-2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41',
great: 'M12 3l2.2 5.6L20 9l-4.5 3.9L17 19l-5-3-5 3 1.5-6.1L4 9l5.8-.4z',
good: 'M4 12l5 5L20 6',
neutral: 'M4 13c2-2 4-2 6 0s4 2 6 0 4-2 6 0',
caution: 'M12 3l9 16H3zM12 10v4M12 16.5h.01',
money: 'M12 4a8 4 0 1 0 0 8a8 4 0 1 0 0-8M4 8v8a8 4 0 0 0 16 0V8',
love: 'M12 20s-7-4.4-7-9a4 4 0 0 1 7-2.6A4 4 0 0 1 19 11c0 4.6-7 9-7 9z',
career: 'M4 8h16v11H4zM9 8V5h6v3',
health: 'M3 12h4l2-5 3 10 2-6 2 1h5',
social: 'M9 11a3 3 0 100-6 3 3 0 000 6zM3 20c0-3 3-5 6-5s6 2 6 5M17 11a3 3 0 100-6M15 20c0-2.5 1.5-4.5 4-5',
};
function Glyph({ name, className }: { name: GlyphName; className?: string }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6}
strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden>
<path d={GLYPH_PATHS[name]} />
</svg>
);
}
// ── 운세 항목 빌드 ────────────────────────────────────────────────────
type Area = { icon: string; label: string; score: number; desc: string };
type Area = { icon: GlyphName; label: string; score: number; desc: string };
const DESCS: Record<string, Record<Level, string>> = {
money: {
@@ -119,7 +145,7 @@ function buildAreas(
const rand = seededRand(seed);
const roll = () => Math.round(Math.max(15, Math.min(98, rand() * 40 + overall - 20)));
const keys = ['money','love','career','health','social'] as const;
const icons = ['💰','💕','🎯','🌿','🤝'];
const icons: GlyphName[] = ['money','love','career','health','social'];
const labels = ['재물운','애정운','직업운','건강운','사회운'];
return keys.map((k, i) => {
const s = roll();
@@ -128,11 +154,11 @@ function buildAreas(
}
// ── 레벨별 색상/라벨 ─────────────────────────────────────────────────
const LEVEL_META: Record<Level, { emoji: string; label: string; bar: string; bg: string; border: string; text: string; badge: string }> = {
great: { emoji:'🌟', label:'아주 좋은 날', bar:'#f59e0b', bg:'bg-amber-50', border:'border-amber-300', text:'text-amber-800', badge:'bg-amber-100 text-amber-700 border-amber-300' },
good: { emoji:'', label:'좋은 날', bar:'#22c55e', bg:'bg-emerald-50',border:'border-emerald-300',text:'text-emerald-800',badge:'bg-emerald-100 text-emerald-700 border-emerald-300' },
neutral: { emoji:'🌤️', label:'평온한 날', bar:'#64748b', bg:'bg-slate-50', border:'border-slate-200', text:'text-slate-700', badge:'bg-slate-100 text-slate-600 border-slate-200' },
caution: { emoji:'⚠️', label:'조심하는 날', bar:'#f97316', bg:'bg-orange-50', border:'border-orange-300',text:'text-orange-800', badge:'bg-orange-100 text-orange-700 border-orange-300' },
const LEVEL_META: Record<Level, { icon: GlyphName; label: string; bar: string; bg: string; border: string; text: string; badge: string }> = {
great: { icon:'great', label:'아주 좋은 날', bar:'#f59e0b', bg:'bg-amber-50', border:'border-amber-300', text:'text-amber-800', badge:'bg-amber-100 text-amber-700 border-amber-300' },
good: { icon:'good', label:'좋은 날', bar:'#22c55e', bg:'bg-emerald-50',border:'border-emerald-300',text:'text-emerald-800',badge:'bg-emerald-100 text-emerald-700 border-emerald-300' },
neutral: { icon:'neutral', label:'평온한 날', bar:'#64748b', bg:'bg-slate-50', border:'border-slate-200', text:'text-slate-700', badge:'bg-slate-100 text-slate-600 border-slate-200' },
caution: { icon:'caution', label:'조심하는 날', bar:'#f97316', bg:'bg-orange-50', border:'border-orange-300',text:'text-orange-800', badge:'bg-orange-100 text-orange-700 border-orange-300' },
};
const REL_DESC: (yongShin: string, yongShinKr: string) => Record<Rel, string> = (y, yk) => ({
@@ -148,7 +174,7 @@ const REL_DESC: (yongShin: string, yongShinKr: string) => Record<Rel, string> =
function ScoreBar({ score, color }: { score: number; color: string }) {
return (
<div className="flex items-center gap-2 mt-1">
<div className="flex-1 h-1.5 bg-slate-100 rounded-full overflow-hidden">
<div className="flex-1 h-1.5 bg-[var(--jsm-line)] rounded-full overflow-hidden">
<div style={{ width: `${score}%`, background: color, transition: 'width 0.8s ease' }} className="h-full rounded-full" />
</div>
<span className="text-[10px] font-bold w-6 text-right" style={{ color }}>{score}</span>
@@ -185,31 +211,31 @@ export default function SajuFortuneSection({
<>
{/* ── 상단 연결 화살표 ── */}
<div className="flex flex-col items-center gap-0 py-1">
<div className="w-px h-5 bg-gradient-to-b from-blue-200 to-amber-300" />
<div className="flex items-center gap-2 px-4 py-1.5 bg-amber-50 border border-amber-200 rounded-full text-[11px] font-bold text-amber-700">
<span></span>
<div className="w-px h-5 bg-[var(--jsm-line)]" />
<div className="flex items-center gap-2 px-4 py-1.5 bg-[var(--jsm-accent-soft)] border border-[var(--jsm-line)] rounded-full text-[11px] font-bold text-[var(--jsm-accent)]">
</div>
<div className="w-px h-5 bg-gradient-to-b from-amber-300 to-amber-100" />
<div className="w-px h-5 bg-[var(--jsm-line)]" />
</div>
{/* ── 본문 카드 ── */}
<div id="today-fortune" className="bg-white rounded-2xl border border-amber-200 overflow-hidden shadow-sm">
<div id="today-fortune" className="bg-[var(--jsm-surface)] rounded-2xl border border-[var(--jsm-line)] overflow-hidden shadow-sm">
{/* 헤더 */}
<div className="bg-gradient-to-r from-[#1a0a00] via-[#3d1a00] to-[#1a0a00] px-6 py-5">
<div className="bg-[var(--jsm-navy)] px-6 py-5">
<div className="flex items-start justify-between gap-4">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center flex-shrink-0 shadow-md text-xl">
<div className="w-10 h-10 rounded-xl bg-[var(--jsm-accent)] flex items-center justify-center flex-shrink-0">
<Glyph name="sun" className="w-5 h-5 text-white" />
</div>
<div>
<h2 className="text-sm font-extrabold text-white"> </h2>
<p className="text-amber-300/70 text-[11px] mt-0.5">
<p className="text-white/60 text-[11px] mt-0.5">
{today.year} {today.month} {today.date} · {today.stem}{today.branch} ({today.stemKr}{today.branchKr})
</p>
</div>
</div>
<span className={`text-[11px] font-extrabold px-3 py-1.5 rounded-full border ${meta.badge} flex-shrink-0`}>
{meta.emoji} {meta.label}
<span className={`text-[11px] font-extrabold px-3 py-1.5 rounded-full border ${meta.badge} flex-shrink-0 flex items-center gap-1`}>
<Glyph name={meta.icon} className="w-3 h-3" /> {meta.label}
</span>
</div>
</div>
@@ -237,7 +263,7 @@ export default function SajuFortuneSection({
<span className="text-[11px] font-bold text-slate-500"> </span>
<div className="flex-1 h-2.5 bg-white/70 rounded-full overflow-hidden border border-white/50">
<div
style={{ width: `${overall}%`, background: `linear-gradient(90deg, ${meta.bar}cc, ${meta.bar})` }}
style={{ width: `${overall}%`, background: meta.bar }}
className="h-full rounded-full"
/>
</div>
@@ -247,20 +273,22 @@ export default function SajuFortuneSection({
{/* 5대 운세 그리드 */}
<div>
<h3 className="text-xs font-extrabold text-[#04102b] mb-3"> </h3>
<h3 className="text-xs font-extrabold text-[var(--jsm-ink)] mb-3"> </h3>
<div className="space-y-3">
{areas.map((area) => {
const aLevel = toLevel(area.score);
const aMeta = LEVEL_META[aLevel];
return (
<div key={area.label} className="flex gap-3 items-start">
<div className={`w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 text-sm ${aMeta.bg} border ${aMeta.border}`}>
{area.icon}
<div className={`w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 ${aMeta.bg} border ${aMeta.border} ${aMeta.text}`}>
<Glyph name={area.icon} className="w-4 h-4" />
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between mb-0.5">
<span className="text-xs font-bold text-[#04102b]">{area.label}</span>
<span className={`text-[10px] font-bold px-1.5 py-0.5 rounded-full border ${aMeta.badge}`}>{aMeta.emoji}</span>
<span className="text-xs font-bold text-[var(--jsm-ink)]">{area.label}</span>
<span className={`text-[10px] font-bold px-1.5 py-0.5 rounded-full border ${aMeta.badge} flex items-center gap-1`}>
<Glyph name={aMeta.icon} className="w-2.5 h-2.5" />
</span>
</div>
<ScoreBar score={area.score} color={aMeta.bar} />
<p className="text-[11px] text-slate-500 mt-1 leading-relaxed">{area.desc}</p>
@@ -278,18 +306,15 @@ export default function SajuFortuneSection({
</p>
{/* 로또 CTA */}
<div className="rounded-2xl bg-gradient-to-br from-[#04102b] via-[#0d1f5c] to-[#04102b] border border-[#1a3a7a] p-5 relative overflow-hidden">
<div className="absolute inset-0 opacity-[0.04]"
style={{ backgroundImage: 'radial-gradient(circle, #a78bfa 1px, transparent 1px)', backgroundSize: '20px 20px' }} />
<div className="relative">
<div className="rounded-2xl bg-[var(--jsm-navy)] p-5">
<div className="flex items-center gap-2 mb-2">
<span className="text-base">🎱</span>
<span className="text-xs font-extrabold text-amber-300">
<LottoIcon className="w-4 h-4 text-[var(--jsm-accent)]" />
<span className="text-xs font-extrabold text-white">
{level === 'great' ? '오늘 운이 아주 좋습니다! 로또도 한 번 도전해보세요.' : '사주 기반 행운 번호도 확인해보세요.'}
</span>
</div>
<p className="text-xs text-blue-200/70 leading-relaxed mb-4">
<strong className="text-amber-300">{yongShin}({yongShinKr})</strong>
<p className="text-xs text-white/70 leading-relaxed mb-4">
<strong className="text-[var(--jsm-accent-soft)]">{yongShin}({yongShinKr})</strong>
.
{hasLottoSubscription
? ' 구독 중이신 로또 서비스의 매주 최신 추천 번호도 함께 확인하세요.'
@@ -301,22 +326,21 @@ export default function SajuFortuneSection({
e.preventDefault();
document.getElementById('saju-lotto-section')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
className="block w-full text-center bg-gradient-to-r from-amber-500 to-amber-400 hover:from-amber-400 hover:to-amber-300 text-[#04102b] text-sm font-extrabold px-4 py-2.5 rounded-xl transition-all shadow-lg cursor-pointer"
className="block w-full text-center bg-white hover:bg-white/90 text-[var(--jsm-navy)] text-sm font-extrabold px-4 py-2.5 rounded-xl transition-colors cursor-pointer"
>
</a>
</div>
</div>
</div>
</div>
{/* 하단 연결 */}
<div className="flex flex-col items-center gap-0 py-1">
<div className="w-px h-5 bg-gradient-to-b from-amber-200 to-blue-300" />
<div className="flex items-center gap-2 px-4 py-1.5 bg-blue-50 border border-blue-200 rounded-full text-[11px] font-bold text-blue-700">
<span>🎱</span>
<div className="w-px h-5 bg-[var(--jsm-line)]" />
<div className="flex items-center gap-2 px-4 py-1.5 bg-[var(--jsm-accent-soft)] border border-[var(--jsm-line)] rounded-full text-[11px] font-bold text-[var(--jsm-accent)]">
<LottoIcon className="w-4 h-4 text-[var(--jsm-accent)]" />
</div>
<div className="w-px h-5 bg-gradient-to-b from-blue-200 to-transparent" />
<div className="w-px h-5 bg-[var(--jsm-line)]" />
</div>
</>
);