로또 종합 추론 번호 추천 기능 추가
5가지 통계 기법(빈도Z-score·조합지문·갭분석·공동출현·다양성)을 기법별 가중치(30/25/20/15/10%)로 투표 집계하여 최적 6개 번호 도출. - 기법별 추천 번호 시각화 (최종 번호 하이라이트) - 투표 참여 기법 수 점 표시 (최대 5개) - 조합 품질 점수 5차원 바 차트 - 추천 이력 히스토리 누적 저장 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
11
src/api.js
11
src/api.js
@@ -304,6 +304,17 @@ export function getPersonalAnalysis() {
|
|||||||
return apiGet('/api/lotto/analysis/personal');
|
return apiGet('/api/lotto/analysis/personal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 종합 추론 추천 ──────────────────────────────────────────────────────────
|
||||||
|
// GET /api/lotto/recommend/combined
|
||||||
|
export function getCombinedRecommend() {
|
||||||
|
return apiGet('/api/lotto/recommend/combined');
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/lotto/recommend/combined/history
|
||||||
|
export function getCombinedHistory(limit = 30) {
|
||||||
|
return apiGet(`/api/lotto/recommend/combined/history?limit=${limit}`);
|
||||||
|
}
|
||||||
|
|
||||||
// GET /api/lotto/purchase?draw_no=N&days=N
|
// GET /api/lotto/purchase?draw_no=N&days=N
|
||||||
export function getPurchases({ draw_no, days } = {}) {
|
export function getPurchases({ draw_no, days } = {}) {
|
||||||
const qs = new URLSearchParams();
|
const qs = new URLSearchParams();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
getPerformanceStats, getLatestReport, getReportHistory,
|
getPerformanceStats, getLatestReport, getReportHistory,
|
||||||
getPersonalAnalysis, getPurchases, getPurchaseStats,
|
getPersonalAnalysis, getPurchases, getPurchaseStats,
|
||||||
addPurchase, updatePurchase, deletePurchase,
|
addPurchase, updatePurchase, deletePurchase,
|
||||||
|
getCombinedRecommend, getCombinedHistory,
|
||||||
} from '../../api';
|
} from '../../api';
|
||||||
|
|
||||||
/* ─────────────────────────────────────────────
|
/* ─────────────────────────────────────────────
|
||||||
@@ -286,6 +287,179 @@ const ConfidenceRing = ({ score }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────
|
||||||
|
종합 추론 추천 패널
|
||||||
|
───────────────────────────────────────────── */
|
||||||
|
const METHOD_META = {
|
||||||
|
frequency: { label: '빈도 Z-score', desc: '역대 출현 빈도가 기댓값보다 높은 번호', color: '#818cf8', icon: '📊' },
|
||||||
|
fingerprint: { label: '조합 지문', desc: '역대 당첨 조합의 합계·홀짝·구간 분포에 맞는 번호', color: '#fbbf24', icon: '🔏' },
|
||||||
|
gap: { label: '갭 분석', desc: '가장 오래 등장하지 않은 오버듀 번호', color: '#34d399', icon: '⏳' },
|
||||||
|
cooccur: { label: '공동 출현', desc: '역대에 함께 출현한 빈도가 높은 번호', color: '#f472b6', icon: '🔗' },
|
||||||
|
diversity: { label: '다양성', desc: '구간 커버리지와 번호 범위를 극대화한 번호', color: '#fb923c', icon: '🌈' },
|
||||||
|
};
|
||||||
|
const METHOD_ORDER = ['fingerprint', 'frequency', 'gap', 'cooccur', 'diversity'];
|
||||||
|
const SCORE_META = [
|
||||||
|
{ key: 'score_fingerprint', label: '조합 지문', color: '#fbbf24', weight: 30 },
|
||||||
|
{ key: 'score_frequency', label: '빈도 Z', color: '#818cf8', weight: 25 },
|
||||||
|
{ key: 'score_gap', label: '갭 분석', color: '#34d399', weight: 20 },
|
||||||
|
{ key: 'score_cooccur', label: '공동 출현', color: '#f472b6', weight: 15 },
|
||||||
|
{ key: 'score_diversity', label: '다양성', color: '#fb923c', weight: 10 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const CombinedRecommendPanel = ({ combined, history, loading, histLoading, onRun, onCopy }) => {
|
||||||
|
const [histExpand, setHistExpand] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="lotto-panel lotto-panel--wide lotto-combined">
|
||||||
|
<div className="lotto-panel__head">
|
||||||
|
<div>
|
||||||
|
<p className="lotto-panel__eyebrow">AI · 종합 추론</p>
|
||||||
|
<h3>종합 추론 번호 추천</h3>
|
||||||
|
<p className="lotto-panel__sub">
|
||||||
|
5가지 통계 기법(빈도·지문·갭·공동출현·다양성)을 가중 투표로 합산해
|
||||||
|
최적 6개 번호를 도출합니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="lotto-panel__actions">
|
||||||
|
{loading && <span className="lotto-chip">분석 중…</span>}
|
||||||
|
<button className="button primary small" onClick={onRun} disabled={loading}>
|
||||||
|
{loading ? '추론 중…' : '🔮 종합 추론 실행'}
|
||||||
|
</button>
|
||||||
|
{history.length > 0 && (
|
||||||
|
<button className="button ghost small" onClick={() => setHistExpand(p => !p)}>
|
||||||
|
이력 {history.length}건 {histExpand ? '▲' : '▼'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!combined && !loading && (
|
||||||
|
<p className="lotto-empty">버튼을 눌러 종합 추론을 실행하세요.</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{combined && (
|
||||||
|
<>
|
||||||
|
{/* 기법별 추천 번호 */}
|
||||||
|
<div className="lotto-combined__methods">
|
||||||
|
{METHOD_ORDER.map((key) => {
|
||||||
|
const meta = METHOD_META[key];
|
||||||
|
const m = combined.methods?.[key];
|
||||||
|
if (!m) return null;
|
||||||
|
return (
|
||||||
|
<div key={key} className="lotto-combined__method">
|
||||||
|
<div className="lotto-combined__method-head">
|
||||||
|
<span className="lotto-combined__method-icon">{meta.icon}</span>
|
||||||
|
<div>
|
||||||
|
<p className="lotto-combined__method-name" style={{ color: meta.color }}>
|
||||||
|
{meta.label}
|
||||||
|
<span className="lotto-combined__method-weight"> ({m.weight_pct}%)</span>
|
||||||
|
</p>
|
||||||
|
<p className="lotto-combined__method-desc">{meta.desc}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="lotto-combined__method-nums">
|
||||||
|
{m.numbers.map((n) => {
|
||||||
|
const inFinal = combined.final_numbers.includes(n);
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
key={n}
|
||||||
|
className={`lotto-ball ${ballClass(n).replace('lotto-ball ', '')} ${inFinal ? 'is-final' : 'is-dim'}`}
|
||||||
|
>
|
||||||
|
{n}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 최종 추론 결과 */}
|
||||||
|
<div className="lotto-combined__final">
|
||||||
|
<div className="lotto-combined__final-head">
|
||||||
|
<span className="lotto-combined__final-badge">종합 추론 결과</span>
|
||||||
|
{combined.deduped && (
|
||||||
|
<span className="lotto-chip lotto-chip--muted">중복 (이미 저장됨)</span>
|
||||||
|
)}
|
||||||
|
<button className="button ghost small" onClick={() => onCopy(combined.final_numbers)}>
|
||||||
|
복사
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="lotto-combined__final-balls">
|
||||||
|
{combined.final_numbers.map((n) => {
|
||||||
|
const votes = combined.vote_counts?.[String(n)] ?? 0;
|
||||||
|
return (
|
||||||
|
<div key={n} className="lotto-combined__final-ball-wrap">
|
||||||
|
<span className={ballClass(n)}>{n}</span>
|
||||||
|
<span className="lotto-combined__vote-dots">
|
||||||
|
{Array.from({ length: 5 }).map((_, i) => (
|
||||||
|
<span key={i} className={`lotto-combined__vote-dot ${i < votes ? 'is-on' : ''}`} />
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<p className="lotto-combined__final-sub">
|
||||||
|
● 점은 해당 번호가 채택된 기법 수 (최대 5개)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 점수 바 */}
|
||||||
|
<div className="lotto-combined__scores">
|
||||||
|
<p className="lotto-combined__scores-title">조합 품질 점수</p>
|
||||||
|
{SCORE_META.map(({ key, label, color, weight }) => {
|
||||||
|
const val = combined.scores?.[key] ?? 0;
|
||||||
|
const pct = Math.round(val * 100);
|
||||||
|
return (
|
||||||
|
<div key={key} className="lotto-combined__score-row">
|
||||||
|
<span className="lotto-combined__score-label">{label}</span>
|
||||||
|
<span className="lotto-combined__score-weight">{weight}%</span>
|
||||||
|
<div className="lotto-combined__score-bar-wrap">
|
||||||
|
<div
|
||||||
|
className="lotto-combined__score-bar"
|
||||||
|
style={{ width: `${pct}%`, background: color }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="lotto-combined__score-val">{pct}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<div className="lotto-combined__score-total">
|
||||||
|
종합 점수 <strong>{Math.round((combined.scores?.score_total ?? 0) * 100)}</strong> / 100
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="lotto-combined__disclaimer">
|
||||||
|
※ 이 추천은 역대 통계 패턴 기반 참고 자료이며, 당첨을 보장하지 않습니다.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 추천 이력 */}
|
||||||
|
{histExpand && (
|
||||||
|
<div className="lotto-combined__history">
|
||||||
|
<p className="lotto-combined__history-title">종합 추론 이력</p>
|
||||||
|
{histLoading && <p className="lotto-empty">로딩 중…</p>}
|
||||||
|
{history.map((item) => (
|
||||||
|
<div key={item.id} className="lotto-combined__history-item">
|
||||||
|
<div className="lotto-combined__history-meta">
|
||||||
|
<span>#{item.id}</span>
|
||||||
|
<span>{fmtKST(item.created_at)}</span>
|
||||||
|
<span>기준 {item.based_on_draw ?? '-'}회</span>
|
||||||
|
</div>
|
||||||
|
<NumberRow nums={item.numbers} />
|
||||||
|
<button className="button ghost small" onClick={() => onCopy(item.numbers)}>복사</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* 공략 리포트 패널 */
|
/* 공략 리포트 패널 */
|
||||||
const ReportPanel = ({ report, history, loading, onRefresh, onSelectDrw }) => {
|
const ReportPanel = ({ report, history, loading, onRefresh, onSelectDrw }) => {
|
||||||
const [histExpand, setHistExpand] = useState(false);
|
const [histExpand, setHistExpand] = useState(false);
|
||||||
@@ -710,6 +884,11 @@ export default function Functions() {
|
|||||||
const [simResult, setSimResult] = useState(null);
|
const [simResult, setSimResult] = useState(null);
|
||||||
|
|
||||||
// ── 신규 상태 ──────────────────────────────────────────────────────────────
|
// ── 신규 상태 ──────────────────────────────────────────────────────────────
|
||||||
|
const [combined, setCombined] = useState(null);
|
||||||
|
const [combinedLoading, setCombinedLoading] = useState(false);
|
||||||
|
const [combinedHistory, setCombinedHistory] = useState([]);
|
||||||
|
const [combinedHistLoading, setCombinedHistLoading] = useState(false);
|
||||||
|
|
||||||
const [perfStats, setPerfStats] = useState(null);
|
const [perfStats, setPerfStats] = useState(null);
|
||||||
const [report, setReport] = useState(null);
|
const [report, setReport] = useState(null);
|
||||||
const [reportHistory, setReportHistory] = useState([]);
|
const [reportHistory, setReportHistory] = useState([]);
|
||||||
@@ -811,6 +990,27 @@ export default function Functions() {
|
|||||||
finally { setReportLoading(false); }
|
finally { setReportLoading(false); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const runCombinedRecommend = async () => {
|
||||||
|
setCombinedLoading(true);
|
||||||
|
try {
|
||||||
|
const data = await getCombinedRecommend();
|
||||||
|
setCombined(data);
|
||||||
|
// 이력도 새로고침
|
||||||
|
const hist = await getCombinedHistory(30);
|
||||||
|
setCombinedHistory(hist?.items ?? []);
|
||||||
|
} catch (e) { setError(e?.message ?? String(e)); }
|
||||||
|
finally { setCombinedLoading(false); }
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadCombinedHistory = async () => {
|
||||||
|
setCombinedHistLoading(true);
|
||||||
|
try {
|
||||||
|
const hist = await getCombinedHistory(30);
|
||||||
|
setCombinedHistory(hist?.items ?? []);
|
||||||
|
} catch {}
|
||||||
|
finally { setCombinedHistLoading(false); }
|
||||||
|
};
|
||||||
|
|
||||||
const refreshPersonalAnalysis = async () => {
|
const refreshPersonalAnalysis = async () => {
|
||||||
setPersonalLoading(true);
|
setPersonalLoading(true);
|
||||||
try { setPersonalAnalysis(await getPersonalAnalysis()); }
|
try { setPersonalAnalysis(await getPersonalAnalysis()); }
|
||||||
@@ -943,6 +1143,7 @@ export default function Functions() {
|
|||||||
refreshReport();
|
refreshReport();
|
||||||
refreshPersonalAnalysis();
|
refreshPersonalAnalysis();
|
||||||
refreshPurchases();
|
refreshPurchases();
|
||||||
|
loadCombinedHistory();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -970,6 +1171,16 @@ export default function Functions() {
|
|||||||
{/* ── 신뢰도 배너 ── */}
|
{/* ── 신뢰도 배너 ── */}
|
||||||
<PerformanceBanner perf={perfStats} />
|
<PerformanceBanner perf={perfStats} />
|
||||||
|
|
||||||
|
{/* ── 종합 추론 번호 추천 ── */}
|
||||||
|
<CombinedRecommendPanel
|
||||||
|
combined={combined}
|
||||||
|
history={combinedHistory}
|
||||||
|
loading={combinedLoading}
|
||||||
|
histLoading={combinedHistLoading}
|
||||||
|
onRun={runCombinedRecommend}
|
||||||
|
onCopy={copyNumbers}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* ── 최신 회차 + 시뮬레이션 추천 ── */}
|
{/* ── 최신 회차 + 시뮬레이션 추천 ── */}
|
||||||
<div className="lotto-grid">
|
<div className="lotto-grid">
|
||||||
{/* Latest Draw */}
|
{/* Latest Draw */}
|
||||||
|
|||||||
@@ -1195,3 +1195,283 @@
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════
|
||||||
|
종합 추론 패널
|
||||||
|
═══════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.lotto-combined {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 기법별 추천 행 */
|
||||||
|
.lotto-combined__methods {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid var(--line, rgba(255,255,255,0.08));
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__method {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__method-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__method-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__method-name {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__method-weight {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.6;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__method-desc {
|
||||||
|
margin: 2px 0 0;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.45));
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__method-nums {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 최종 결과 */
|
||||||
|
.lotto-combined__final {
|
||||||
|
padding: 20px;
|
||||||
|
background: rgba(129, 140, 248, 0.06);
|
||||||
|
border: 1px solid rgba(129, 140, 248, 0.25);
|
||||||
|
border-radius: 14px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__final-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__final-badge {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: .04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #818cf8;
|
||||||
|
background: rgba(129, 140, 248, 0.15);
|
||||||
|
border: 1px solid rgba(129, 140, 248, 0.3);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__final-balls {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__final-ball-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__final-ball-wrap .lotto-ball {
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__vote-dots {
|
||||||
|
display: flex;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__vote-dot {
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__vote-dot.is-on {
|
||||||
|
background: #818cf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__final-sub {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 볼 상태 */
|
||||||
|
.lotto-ball.is-dim {
|
||||||
|
opacity: 0.35;
|
||||||
|
transform: scale(0.92);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-ball.is-final {
|
||||||
|
opacity: 1;
|
||||||
|
box-shadow: 0 0 0 2px rgba(129, 140, 248, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 점수 바 */
|
||||||
|
.lotto-combined__scores {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__scores-title {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.5));
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__score-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__score-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.5));
|
||||||
|
width: 72px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__score-weight {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.35));
|
||||||
|
width: 28px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__score-bar-wrap {
|
||||||
|
flex: 1;
|
||||||
|
height: 6px;
|
||||||
|
background: rgba(255,255,255,0.06);
|
||||||
|
border-radius: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__score-bar {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__score-val {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-bright, #fff);
|
||||||
|
width: 28px;
|
||||||
|
text-align: right;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__score-total {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.5));
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__score-total strong {
|
||||||
|
color: #818cf8;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__disclaimer {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.35));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 이력 */
|
||||||
|
.lotto-combined__history {
|
||||||
|
border-top: 1px solid var(--line, rgba(255,255,255,0.08));
|
||||||
|
padding-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__history-title {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.45));
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__history-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
background: rgba(255,255,255,0.02);
|
||||||
|
border: 1px solid var(--line, rgba(255,255,255,0.06));
|
||||||
|
border-radius: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__history-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted, rgba(255,255,255,0.4));
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.lotto-combined__method {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__final-ball-wrap .lotto-ball {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lotto-combined__final-balls {
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user