diff --git a/docs/PRODUCTION_CONTRACT.md b/docs/PRODUCTION_CONTRACT.md index b06e755c..baf30f58 100644 --- a/docs/PRODUCTION_CONTRACT.md +++ b/docs/PRODUCTION_CONTRACT.md @@ -38,6 +38,18 @@ Fly 실시간 주소가 502/타임아웃을 내자 프론트가 정상적인 정 - 라이브 중에는 고정된 사전 확률과 현재 점수·남은 시간을 결합한 현재 적중확률을 `LIVE` 옆에 표시한다. +### 경기 진행률 표시 + +- 접힌 경기 미리보기, 상세, 상단 추천 박스는 같은 `LiveProgress` 표시를 사용한다. +- 경기 진행률은 적중확률이 아니다. 저장된 픽·확률·점수 유무와 무관하게 최신 + 중계의 이닝·시간으로 표시하며, 사전 예측이나 기존 추정 확률 산식은 바꾸지 않는다. +- 야구는 정규 9회 중 완료된 반이닝, 축구는 정규 90분, 농구는 확인된 정규 4쿼터 + 단위의 대략적인 진행이다. 각 화면에 기준을 명시한다. +- 연장·세트 경기·정보 누락은 임의로 50%나 100%를 만들지 않는다. 연장/현재 세트 + 등 확인된 상태만 표시한다. 종료 전 숫자 표시는 99%를 넘지 않는다. +- 개별 중계 관측이 10분 넘게 낡았으면 마지막 상태와 갱신 지연을 표시하고 진행률 + 막대는 숨긴다. 새 문서 시각으로 낡은 경기 행을 최신으로 간주하지 않는다. + ## 3. 추천 표시 - 경기별 픽은 각 경기의 최종 방향이다. `오늘의 추천 픽`은 그중 운영 기준을 통과한 diff --git a/docs/assets/AiDisclosure-BFwnE-uI.js b/docs/assets/AiDisclosure-BIoGQ_Ky.js similarity index 99% rename from docs/assets/AiDisclosure-BFwnE-uI.js rename to docs/assets/AiDisclosure-BIoGQ_Ky.js index f056bfbe..b2ea542e 100644 --- a/docs/assets/AiDisclosure-BFwnE-uI.js +++ b/docs/assets/AiDisclosure-BIoGQ_Ky.js @@ -1 +1 @@ -import{j as s}from"./cache-guard-cBqh6aGv.js";import{A as o,c as _,g as b}from"./decision-view-model-Bfii5tWW.js";const p=a=>a!=null&&a!==""&&Number.isFinite(Number(a)),m=(a,i=1)=>p(a)?`${(Number(a)*100).toFixed(i)}%`:"–",g=a=>p(a)?`${Number(a)>=0?"+":""}${(Number(a)*100).toFixed(1)}%p`:"–",N={model_not_promoted:"미래 홀드아웃에서 아직 승격되지 않음",no_first_seen_prediction_ledger:"최초 관측 시각이 고정된 예측 원장이 없음",market_anchor_policy:"현재 운영식은 시장 기준",not_in_operating_formula:"운영 수식에 아직 포함되지 않음",context_not_probability:"확률이 아니라 확인용 문맥",diagnostic_not_promoted:"교차 마켓 진단은 연구 단계",diagnostic_only:"선택을 바꾸지 않는 진단값"},u={no_eligible_market_reference:"유효한 배당·시장확률을 갖춘 자동 비교 후보가 없습니다.",lower_odds_fallback:"최종 적중확률은 가장 높지만 1.50 미만 저배당이라 적중 시 수익폭이 작습니다.",qualified_market_reversal:"이전 역배 전환 판정입니다. 현재 정책에서는 시장 최유력으로 다시 계산합니다.",not_auto_recommendable:"시장 분석은 유지하지만 자동 투입 안전조건을 통과하지 못했습니다.",no_operating_selection:"운영 판정이 없어 선택을 보류합니다.",invalid_decision_contract:"판정 자료의 시각·식별자·스키마가 맞지 않아 보류합니다."},d=a=>`ai-status ai-status-${a||"missing"}`;function w({id:a="ai-method",showLink:i=!0}){return s.jsxs("section",{id:a,className:"ai-method","aria-labelledby":`${a}-title`,children:[s.jsxs("div",{className:"ai-method-heading",children:[s.jsxs("div",{children:[s.jsx("p",{children:"현재 AI 사용 범위"}),s.jsx("h2",{id:`${a}-title`,children:"최종 추천은 예상 적중확률로 고르고, 검증된 AI만 확률에 반영합니다"})]}),i&&s.jsx("a",{href:"research.html#ai-model",children:"검증 방법 보기"})]}),s.jsx("ol",{className:"ai-stage-grid",children:b.map((l,r)=>s.jsxs("li",{children:[s.jsxs("div",{className:"ai-stage-top",children:[s.jsx("span",{className:"tnum",children:String(r+1).padStart(2,"0")}),s.jsx("b",{children:l.label}),s.jsx("span",{className:d(l.status),children:o[l.status]})]}),s.jsx("p",{children:l.summary})]},l.id))}),s.jsx("p",{className:"ai-method-foot",children:"경기별 방향은 2.20 미만 유효 후보의 최종 적중확률로 고릅니다. 1.50 경계는 저배당 수익폭 표시이며, 다폴 조합의 배당칸은 별도로 1.50~2.20을 사용합니다. 검증된 AI 보정이 없으면 배당 기반 시장확률로 복귀합니다."})]})}function f({decision:a}){const i=a?.probability||{},l=["recalculating","closed"].includes(a?.action);return s.jsxs("div",{className:"probability-provenance",role:"group","aria-label":"최종 확률 계산 경로",children:[s.jsxs("div",{children:[s.jsx("span",{children:"배당 기반 확률"}),s.jsx("b",{children:l?"재계산 대기":m(i.market)})]}),s.jsx("span",{className:"probability-arrow","aria-hidden":"true",children:"+"}),s.jsxs("div",{children:[s.jsx("span",{children:"AI 반영"}),s.jsx("b",{children:l?"–":g(i.aiDeltaApplied)})]}),s.jsx("span",{className:"probability-arrow","aria-hidden":"true",children:"="}),s.jsxs("div",{children:[s.jsx("span",{children:"최종 확률"}),s.jsx("b",{children:l?"숨김":m(i.final)})]})]})}const x=a=>{const i=_(a).find(l=>l?.reason_code);return N[i?.reason_code]||i?.reason_code||null};function v({decision:a}){if(!a)return null;const i=a.evidenceCounts||{},l=a.asOf?new Date(a.asOf):null,r=l&&!Number.isNaN(l.getTime())?l.toLocaleString("ko-KR",{timeZone:"Asia/Seoul",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null,j=new Map((a.sources||[]).map(e=>[e.id,e])),c=(a.gateCodes||[]).find(e=>u[e]);return s.jsxs("div",{className:"ai-decision-path",children:[s.jsx(f,{decision:a}),a.action==="recalculating"&&s.jsx("p",{className:"ai-revision-warning",role:"status","aria-live":"polite",children:"실시간 배당이 바뀌어 이전 확률과 해설을 숨겼습니다. 다음 계산이 끝나면 같은 시점 값으로 다시 표시합니다."}),a.action==="closed"&&s.jsx("p",{className:"ai-revision-warning",role:"status",children:"실시간 중계에서 경기 시작을 확인해 이 판정은 더 이상 구매 후보로 사용하지 않습니다."}),a.contractReconstructed&&s.jsx("p",{className:"ai-revision-warning",role:"status",children:a.contractRecoveredErrors?.length?"저장된 선택 식별자나 당시 확률이 현재 배당과 어긋나 이전 값을 버렸습니다. 현재 유효 배당의 최종 예상 적중확률로 자동 복구했으며, 이전 픽과 미검증 AI 값은 사용하지 않았습니다.":a.policyRecalculated?a.recommendationPriority==="fallback"?"현재 배당에서 1.50 미만 시장 최유력을 경기 방향으로 다시 판정했습니다.":"현재 배당에서 1.50~2.20 후보 중 최종 예상 적중확률이 가장 높은 선택으로 다시 판정했습니다.":a.liveOddsRecalculated?"실시간 배당으로 배당 기반 시장확률과 최종 판정을 다시 계산했습니다. 구조 AI와 이전 가격의 추천값은 반영하지 않았습니다.":"판정 원장이 늦게 갱신되어 현재 배당의 시장 최유력 후보를 안전 규칙으로 다시 계산했습니다. 구조 AI와 레거시 추천값은 반영하지 않았습니다."}),a.action==="withhold"&&!!a.withholdReasons?.length&&s.jsxs("div",{className:"ai-revision-warning",role:"status",children:[s.jsx("b",{children:"이 픽을 가지 말아야 하는 이유"}),s.jsx("ul",{className:"mt-1 list-disc space-y-1 pl-4",children:a.withholdReasons.map(e=>s.jsxs("li",{children:[s.jsx("strong",{children:e.title})," · ",e.body]},`${e.title}-${e.body}`))})]}),a.action!=="withhold"&&["lower_odds_fallback","qualified_market_reversal"].includes(c)&&c&&s.jsx("p",{className:"ai-revision-warning",role:"status",children:u[c]}),s.jsx("div",{className:"ai-stage-list","aria-label":"AI 단계별 반영 상태",children:a.stages.map(e=>s.jsx("article",{children:s.jsxs("div",{children:[s.jsx("b",{children:e.label}),s.jsx("span",{className:d(e.status),children:o[e.status]||e.status})]})},e.id))}),s.jsxs("details",{className:"evidence-usage",children:[s.jsxs("summary",{children:["자료 사용 내역 · 최종 반영 ",i.used||0," · 연구 중 ",i.shadow||0," · 설명만 ",i.context_only||0," · 미반영 ",i.ignored||0," · 없음 ",i.missing||0]}),s.jsx("ul",{children:a.evidence.map(e=>s.jsxs("li",{children:[s.jsxs("span",{children:[s.jsx("b",{children:e.label}),x(e)?` · ${x(e)}`:"",(e.source_ids||[]).map(t=>{const n=j.get(t);if(!n)return null;const h=` · 출처 ${n.name||t}`;return n.url?s.jsx("a",{href:n.url,target:"_blank",rel:"noreferrer",children:h},t):s.jsx("small",{children:h},t)})]}),s.jsx("span",{className:d(e.display_status),children:o[e.display_status]||e.display_status})]},e.id))})]}),s.jsxs("p",{className:"ai-decision-meta",children:["생성형 AI 확률 영향 없음",r?` · 입력 컷오프 KST ${r}`:"",a.audit?.pre_registered===!1?" · 사전등록 성과 집계 제외":""]})]})}export{v as A,w as a}; +import{j as s}from"./cache-guard-BQcATxSH.js";import{A as o,c as _,g as b}from"./decision-view-model-Bfii5tWW.js";const p=a=>a!=null&&a!==""&&Number.isFinite(Number(a)),m=(a,i=1)=>p(a)?`${(Number(a)*100).toFixed(i)}%`:"–",g=a=>p(a)?`${Number(a)>=0?"+":""}${(Number(a)*100).toFixed(1)}%p`:"–",N={model_not_promoted:"미래 홀드아웃에서 아직 승격되지 않음",no_first_seen_prediction_ledger:"최초 관측 시각이 고정된 예측 원장이 없음",market_anchor_policy:"현재 운영식은 시장 기준",not_in_operating_formula:"운영 수식에 아직 포함되지 않음",context_not_probability:"확률이 아니라 확인용 문맥",diagnostic_not_promoted:"교차 마켓 진단은 연구 단계",diagnostic_only:"선택을 바꾸지 않는 진단값"},u={no_eligible_market_reference:"유효한 배당·시장확률을 갖춘 자동 비교 후보가 없습니다.",lower_odds_fallback:"최종 적중확률은 가장 높지만 1.50 미만 저배당이라 적중 시 수익폭이 작습니다.",qualified_market_reversal:"이전 역배 전환 판정입니다. 현재 정책에서는 시장 최유력으로 다시 계산합니다.",not_auto_recommendable:"시장 분석은 유지하지만 자동 투입 안전조건을 통과하지 못했습니다.",no_operating_selection:"운영 판정이 없어 선택을 보류합니다.",invalid_decision_contract:"판정 자료의 시각·식별자·스키마가 맞지 않아 보류합니다."},d=a=>`ai-status ai-status-${a||"missing"}`;function w({id:a="ai-method",showLink:i=!0}){return s.jsxs("section",{id:a,className:"ai-method","aria-labelledby":`${a}-title`,children:[s.jsxs("div",{className:"ai-method-heading",children:[s.jsxs("div",{children:[s.jsx("p",{children:"현재 AI 사용 범위"}),s.jsx("h2",{id:`${a}-title`,children:"최종 추천은 예상 적중확률로 고르고, 검증된 AI만 확률에 반영합니다"})]}),i&&s.jsx("a",{href:"research.html#ai-model",children:"검증 방법 보기"})]}),s.jsx("ol",{className:"ai-stage-grid",children:b.map((l,r)=>s.jsxs("li",{children:[s.jsxs("div",{className:"ai-stage-top",children:[s.jsx("span",{className:"tnum",children:String(r+1).padStart(2,"0")}),s.jsx("b",{children:l.label}),s.jsx("span",{className:d(l.status),children:o[l.status]})]}),s.jsx("p",{children:l.summary})]},l.id))}),s.jsx("p",{className:"ai-method-foot",children:"경기별 방향은 2.20 미만 유효 후보의 최종 적중확률로 고릅니다. 1.50 경계는 저배당 수익폭 표시이며, 다폴 조합의 배당칸은 별도로 1.50~2.20을 사용합니다. 검증된 AI 보정이 없으면 배당 기반 시장확률로 복귀합니다."})]})}function f({decision:a}){const i=a?.probability||{},l=["recalculating","closed"].includes(a?.action);return s.jsxs("div",{className:"probability-provenance",role:"group","aria-label":"최종 확률 계산 경로",children:[s.jsxs("div",{children:[s.jsx("span",{children:"배당 기반 확률"}),s.jsx("b",{children:l?"재계산 대기":m(i.market)})]}),s.jsx("span",{className:"probability-arrow","aria-hidden":"true",children:"+"}),s.jsxs("div",{children:[s.jsx("span",{children:"AI 반영"}),s.jsx("b",{children:l?"–":g(i.aiDeltaApplied)})]}),s.jsx("span",{className:"probability-arrow","aria-hidden":"true",children:"="}),s.jsxs("div",{children:[s.jsx("span",{children:"최종 확률"}),s.jsx("b",{children:l?"숨김":m(i.final)})]})]})}const x=a=>{const i=_(a).find(l=>l?.reason_code);return N[i?.reason_code]||i?.reason_code||null};function v({decision:a}){if(!a)return null;const i=a.evidenceCounts||{},l=a.asOf?new Date(a.asOf):null,r=l&&!Number.isNaN(l.getTime())?l.toLocaleString("ko-KR",{timeZone:"Asia/Seoul",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null,j=new Map((a.sources||[]).map(e=>[e.id,e])),c=(a.gateCodes||[]).find(e=>u[e]);return s.jsxs("div",{className:"ai-decision-path",children:[s.jsx(f,{decision:a}),a.action==="recalculating"&&s.jsx("p",{className:"ai-revision-warning",role:"status","aria-live":"polite",children:"실시간 배당이 바뀌어 이전 확률과 해설을 숨겼습니다. 다음 계산이 끝나면 같은 시점 값으로 다시 표시합니다."}),a.action==="closed"&&s.jsx("p",{className:"ai-revision-warning",role:"status",children:"실시간 중계에서 경기 시작을 확인해 이 판정은 더 이상 구매 후보로 사용하지 않습니다."}),a.contractReconstructed&&s.jsx("p",{className:"ai-revision-warning",role:"status",children:a.contractRecoveredErrors?.length?"저장된 선택 식별자나 당시 확률이 현재 배당과 어긋나 이전 값을 버렸습니다. 현재 유효 배당의 최종 예상 적중확률로 자동 복구했으며, 이전 픽과 미검증 AI 값은 사용하지 않았습니다.":a.policyRecalculated?a.recommendationPriority==="fallback"?"현재 배당에서 1.50 미만 시장 최유력을 경기 방향으로 다시 판정했습니다.":"현재 배당에서 1.50~2.20 후보 중 최종 예상 적중확률이 가장 높은 선택으로 다시 판정했습니다.":a.liveOddsRecalculated?"실시간 배당으로 배당 기반 시장확률과 최종 판정을 다시 계산했습니다. 구조 AI와 이전 가격의 추천값은 반영하지 않았습니다.":"판정 원장이 늦게 갱신되어 현재 배당의 시장 최유력 후보를 안전 규칙으로 다시 계산했습니다. 구조 AI와 레거시 추천값은 반영하지 않았습니다."}),a.action==="withhold"&&!!a.withholdReasons?.length&&s.jsxs("div",{className:"ai-revision-warning",role:"status",children:[s.jsx("b",{children:"이 픽을 가지 말아야 하는 이유"}),s.jsx("ul",{className:"mt-1 list-disc space-y-1 pl-4",children:a.withholdReasons.map(e=>s.jsxs("li",{children:[s.jsx("strong",{children:e.title})," · ",e.body]},`${e.title}-${e.body}`))})]}),a.action!=="withhold"&&["lower_odds_fallback","qualified_market_reversal"].includes(c)&&c&&s.jsx("p",{className:"ai-revision-warning",role:"status",children:u[c]}),s.jsx("div",{className:"ai-stage-list","aria-label":"AI 단계별 반영 상태",children:a.stages.map(e=>s.jsx("article",{children:s.jsxs("div",{children:[s.jsx("b",{children:e.label}),s.jsx("span",{className:d(e.status),children:o[e.status]||e.status})]})},e.id))}),s.jsxs("details",{className:"evidence-usage",children:[s.jsxs("summary",{children:["자료 사용 내역 · 최종 반영 ",i.used||0," · 연구 중 ",i.shadow||0," · 설명만 ",i.context_only||0," · 미반영 ",i.ignored||0," · 없음 ",i.missing||0]}),s.jsx("ul",{children:a.evidence.map(e=>s.jsxs("li",{children:[s.jsxs("span",{children:[s.jsx("b",{children:e.label}),x(e)?` · ${x(e)}`:"",(e.source_ids||[]).map(t=>{const n=j.get(t);if(!n)return null;const h=` · 출처 ${n.name||t}`;return n.url?s.jsx("a",{href:n.url,target:"_blank",rel:"noreferrer",children:h},t):s.jsx("small",{children:h},t)})]}),s.jsx("span",{className:d(e.display_status),children:o[e.display_status]||e.display_status})]},e.id))})]}),s.jsxs("p",{className:"ai-decision-meta",children:["생성형 AI 확률 영향 없음",r?` · 입력 컷오프 KST ${r}`:"",a.audit?.pre_registered===!1?" · 사전등록 성과 집계 제외":""]})]})}export{v as A,w as a}; diff --git a/docs/assets/Markets--wDDhPBF.js b/docs/assets/Markets--wDDhPBF.js deleted file mode 100644 index 8993667a..00000000 --- a/docs/assets/Markets--wDDhPBF.js +++ /dev/null @@ -1 +0,0 @@ -import{j as s,C as Pe,r as C,N as _s,O as Z,G as vs}from"./cache-guard-cBqh6aGv.js";import{d as cs,e as ds,r as ys,b as us,f as ks,a as $s,h as ws}from"./decision-view-model-Bfii5tWW.js";import{s as ae,b as De,g as J,D as Ss,r as ms,P as hs,c as Ce,a as Fs,e as Ps,k as ge,t as Cs,f as Te,h as Ms,n as As,i as xe,j as Ls,p as X,o as V,l as xs,m as ps,q as Es}from"./unified-recommendation-BCuqyY1U.js";import{e as Ds,f as Ts,u as Os}from"./bet-ledger-ES6mNno4.js";import{A as Rs}from"./AiDisclosure-BFwnE-uI.js";import{r as Is}from"./live-odds-CIQqKAce.js";import{u as Bs}from"./poll-DDJ49qcP.js";const Ks=e=>{const[t,...n]=String(e).split(" — ");return n.length?{title:t,body:n.join(" — ")}:{title:"경기력 근거",body:t}},Oe=e=>{const t=Number(e);return Number.isFinite(t)?t.toFixed(1):"-"};function Hs({forecast:e}){if(!e||e.status!=="shadow")return null;const t=e.expected_scores||{},n=Array.isArray(e.top_scorelines)?e.top_scorelines.slice(0,3):[],i=e.contract?.score_unit_label||t.unit||"득점";return s.jsxs("div",{className:"mt-3 rounded-md border border-rule2 bg-panel px-3.5 py-3","aria-label":"스코어 분포 전망",children:[s.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-2",children:[s.jsxs("div",{children:[s.jsx("b",{className:"text-[12px] text-ink",children:"스코어 흐름 전망"}),s.jsxs("p",{className:"mt-1 text-[11px] leading-5 text-ink3",children:["현재 팀 전력 분포의 평균은 홈 ",Oe(t.home)," · 원정 ",Oe(t.away)," ",i,"입니다."]})]}),s.jsx("span",{className:"rounded border border-rule2 bg-paper px-2 py-1 text-[10px] text-ink3",children:"연구값 · 추천 확률 미반영"})]}),!!n.length&&s.jsx("div",{className:"mt-2 flex flex-wrap gap-1.5",children:n.map(l=>s.jsxs("span",{className:"rounded border border-rule2 bg-paper px-2 py-1 text-[10.5px] text-ink2",children:[l.home_score,":",l.away_score," · ",Math.round(Number(l.probability||0)*100),"%"]},`${l.home_score}-${l.away_score}`))}),s.jsx("p",{className:"mt-2 text-[10px] leading-4 text-ink3",children:"선발·결장별 영향량은 사전 원장으로 충분히 검증된 뒤 시나리오에 반영합니다. 임의 난수나 ‘미래상수’는 예측 입력으로 쓰지 않습니다."})]})}function Gs({previews:e}){return e?.some(t=>t.characteristics.length||t.players.length)?s.jsxs("div",{className:"mt-3",children:[s.jsx("h4",{className:"mb-2 text-[11px] font-semibold tracking-[.04em] text-ink3",children:"팀·핵심 선수 프리뷰"}),s.jsx("div",{className:"grid gap-2 sm:grid-cols-2",children:e.map(t=>s.jsxs("article",{className:"rounded-md border border-rule2 bg-paper px-3 py-2.5",children:[s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsx("b",{className:"text-[12px] text-ink",children:t.team}),s.jsx("span",{className:"text-[9.5px] text-ink3",children:t.side==="home"?"홈":"원정"})]}),!!t.characteristics.length&&s.jsx("ul",{className:"mt-1.5 space-y-1 text-[10.5px] leading-4 text-ink2",children:t.characteristics.slice(0,3).map(n=>s.jsxs("li",{children:["· ",n]},n))}),!!t.players.length&&s.jsx("div",{className:"mt-2 border-t border-rule2 pt-2",children:t.players.map(n=>s.jsxs("div",{className:"mb-1.5 last:mb-0",children:[s.jsxs("div",{className:"flex flex-wrap items-baseline gap-1 text-[10.5px]",children:[s.jsx("b",{className:"text-ink",children:n.name}),s.jsx("span",{className:"text-ink3",children:n.characteristic})]}),!!n.facts.length&&s.jsx("p",{className:"tnum mt-0.5 text-[9.5px] text-ink3",children:n.facts.join(" · ")})]},n.name))}),!!t.unavailable.length&&s.jsxs("p",{className:"mt-2 border-t border-rule2 pt-1.5 text-[9.5px] leading-4 text-ink3",children:["출전 변수 · ",t.unavailable.map(n=>`${n.name}${n.status?`(${n.status})`:""}`).join(" · ")]})]},t.side))}),s.jsx("p",{className:"mt-1.5 text-[9.5px] text-ink3",children:"공식 시즌 기록과 최근 흐름을 요약한 설명이며, 검증 전에는 추천 확률을 직접 바꾸지 않습니다."})]}):null}function Us({analysis:e,scoreForecast:t}){if(!e)return null;const{prediction:n,reasons:i,cautions:l,signalSummary:a,decision:o,commentary:d}=e,r=n?.probability==null?null:Math.round(n.probability*100),c=cs(o),m=a?.narrative||(o?.action==="market_reference"?"추천 방향과 함께 최근 경기력·선수 정보·반대 신호를 비교해 확인하세요.":"아래 위험이 해소되지 않아 이 픽은 추천하지 않습니다.");return s.jsxs("section",{className:"prediction-card","aria-label":"경기 모델 판정과 경기력 해석",children:[s.jsxs("header",{className:"prediction-head",children:[s.jsxs("div",{children:[s.jsxs("p",{className:"prediction-label",children:[c," · 참고용"]}),s.jsx("h3",{children:n?.headline||"예측 자료 확인 중"}),s.jsx("p",{children:m})]}),r!==null&&s.jsxs("div",{className:"prediction-probability",children:[s.jsxs("b",{children:[r,"%"]}),s.jsx("span",{children:o?.model?.validatedEdge?"AI 최종확률":"배당 기반 시장확률"})]})]}),s.jsx("div",{className:"prediction-body",children:s.jsxs("div",{children:[s.jsx("h4",{children:"판정과 구분된 경기 자료"}),s.jsx("p",{className:"performance-intro",children:"설명 자료와 반대 신호를 함께 보며, AI가 사용한 값은 ‘픽 근거·수식’ 탭에서 확인합니다."}),s.jsx("ul",{className:"performance-reasons",children:i.map(x=>{const h=Ks(x);return s.jsxs("li",{children:[s.jsx("strong",{children:h.title}),s.jsx("p",{children:h.body})]},x)})})]})}),o?.action==="withhold"&&!!o.withholdReasons?.length&&s.jsxs("div",{className:"prediction-caution",children:[s.jsx("b",{children:"이 픽을 가지 말아야 하는 이유"}),s.jsx("ul",{className:"mt-1 list-disc space-y-1 pl-4",children:o.withholdReasons.map(x=>s.jsxs("li",{children:[s.jsx("strong",{children:x.title})," · ",x.body]},`${x.title}-${x.body}`))})]}),d&&s.jsxs("div",{className:"prediction-commentary",children:[s.jsx("b",{children:"해설 정리"}),s.jsx("p",{children:d}),s.jsx("small",{children:"수집된 사실을 바꾸지 않고 LLM이 문장만 다듬었습니다."})]}),s.jsx(Gs,{previews:e.teamPreviews}),s.jsx(Hs,{forecast:t}),!!l?.length&&s.jsxs("div",{className:"prediction-caution",children:[s.jsx("b",{children:"반대 근거·변수"}),s.jsx("span",{children:l.join(" ")})]}),s.jsx("footer",{children:"예상 적중확률은 실제 배당에 포함된 마진을 제거한 값을 출발점으로 삼고, 검증된 보정만 추가합니다. 구매 판단은 직접 합니다."})]})}const ce=e=>typeof e=="number"||typeof e=="string"&&e.trim()?Number(e):NaN,qs=e=>{const t=ce(e);return Number.isFinite(t)&&t>0&&t<1?t:null};function Me(e,t=null,n=Date.now()){const i=e?.prediction_record,l=ae(e),a=Date.parse(i?.captured_at||"");if(!i?.selection_id||!i.market||!i.selection||l==null||!Number.isFinite(a)||a>=l||a>n)return null;const o={selection_id:i.selection_id,offer_id:i.offer_id,market:i.market,label:i.label||"",선택:i.selection,배당:Number.isFinite(ce(i.odds))?ce(i.odds):null},d=qs(i.probability),r={option:o,openingProbability:d,capturedAt:i.captured_at,estimate:null,estimateStatus:d===null?"missing_opening":"waiting_live"};if(d===null||!t||t.status==="BEFORE")return r;if(t.finished||t.cancelled||t.postponed)return{...r,estimateStatus:"closed"};const c=Date.parse(t.observed_at||e._liveFeedAt||"");if(!Number.isFinite(c)||n-c>600*1e3||c>n+5e3)return{...r,estimateStatus:"stale_live"};if(![t.home_score,t.away_score].every(h=>Number.isInteger(ce(h))&&ce(h)>=0))return{...r,estimateStatus:"missing_score"};if(!{승패:["홈","원정","승","패"],승무패:["홈","원정","승","패","무","무승부"],언더오버:["언더","오버"]}[i.market]?.includes(i.selection)||!["sc","bs","bk"].includes(e.sport)||i.market==="언더오버"&&!/\d/.test(o.label))return{...r,estimateStatus:"unsupported_market"};const x=Ds({openingProbability:d,game:{sport:e.sport},selection:{market:o.market,label:o.label,choice:o.선택}},t);return{...r,estimate:x,estimateStatus:"available"}}const q=e=>String(e??"").trim(),z=e=>typeof e=="number"||typeof e=="string"&&e.trim()?Number(e):NaN,oe=e=>{const t=z(e);return Number.isFinite(t)&&t>0&&t<1?t:null},Re=e=>Number.isFinite(e)?new Date(e+9*36e5).toISOString().slice(0,10):null,ne=e=>JSON.stringify([q(e?.market),q(e?.market_label??e?.label),q(e?.sel??e?.selection??e?.선택)]),ue=e=>{let t=q(e);return/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?$/.test(t)&&(t=`${t.replace(" ","T")}+09:00`),Date.parse(t)},Ws=(e,t)=>{const n=ue(e?.kickoff_at);return Number.isFinite(n)?n:ae({...e,year:e?.year||t})},bs=e=>{const t=q(e?.match).split(" vs ");return[q(e?.home||t[0]),q(e?.away||t[1])]},zs=(e,t)=>JSON.stringify([t,q(e?.sport),q(e?.league),...bs(e)]),Ie=(e,t,n,i)=>{const[l,a]=bs(e);return l&&a&&l===q(t.home)&&a===q(t.away)&&q(e.league)===q(t.league)&&(!e.sport||e.sport===t.sport)&&Ws(e,i)===n},_e=(e,t,n,i)=>{const l=ue(e.recommended_at||t?.generated_at);return Number.isFinite(l)&&lds([e]).length===1,Ys={missing_opening:"사전 확률 기록이 없어 현재 추정을 제공하지 않습니다.",waiting_live:"최신 경기 중계가 확인되면 현재 추정을 표시합니다.",stale_live:"중계 갱신이 늦어 현재 추정을 보류합니다.",missing_score:"현재 점수 자료가 없어 추정을 보류합니다.",unsupported_market:"이 마켓의 현재 추정은 지원하지 않습니다.",closed:"경기 종료·중단 후에는 현재 추정을 표시하지 않습니다."};function Zs({games:e=[],today:t=null,currentToday:n=null,now:i=Date.now()}={}){i=Number(i);const l=Re(i);if(!l)return[];const a=Number(l.slice(0,4)),o=(t?.candidates||[]).filter(Boolean),d=new Set(De(o)),r=(n||t)?.candidates||[],c=new Set(De(r)),m=f=>typeof f.daily_recommendation?.recommended=="boolean"?f.daily_recommendation.recommended:d.has(f),x=f=>typeof f.daily_recommendation?.recommended=="boolean"?f.daily_recommendation.recommended:c.has(f),h=new Map;for(const f of e||[]){if(!f)continue;const p={...f,year:f.year||t?.year||a},N=ae(p);if(N==null||Re(N)!==l||!p.home||!p.away)continue;const F=p._liveState,$=J(p,F,i),w=N<=i||p._liveStarted===!0||$!=="upcoming",k=Me(p,F,i),v=o.filter(y=>Ie(y,p,N,p.year)),T=r.filter(y=>Ie(y,p,N,p.year));let g,P,D,W,S,E,H=null,L;if(k){g=k.option,P=k.openingProbability,D=z(g.배당),W=k.capturedAt;const y=v.filter(R=>ne(R)===ne(g)&&z(R.odds??R.배당)===D&&_e(R,t,N,i)),O=ue(t?.generated_at),U=Number.isFinite(O)&&Ox(R)&&ve(R)&&ne(R)===ne(g)&&z(R.odds??R.배당)===D&&_e(R,n||t,N,i)),se=U&&y.some(R=>R.daily_recommendation?.recommended===!1),le=P!=null&&P>=Ss&&ve({market:g.market,odds:D,market_prob:P,is_market_favorite:p.prediction_record.is_market_favorite,final_reversal:p.prediction_record.final_reversal});if(!Y&&!I&&(!w||se||!le))continue;S=Y?"highlight":I?"current":"recorded",E=ms(p,F),$==="live"&&k.estimateStatus==="available"&&Number.isFinite(k.estimate?.probability)&&(H=k.estimate),L=$==="finished"?"경기 종료 · 결과를 확인하세요.":Ys[k.estimateStatus]||"현재 추정 자료를 확인 중입니다."}else{if(w||p._liveOddsChanged)continue;const y=T.find(O=>x(O)&&ve(O)&&_e(O,n||t,N,i)&&oe(O.market_prob)!=null&&(p.options||[]).some(U=>ne(U)===ne(O)&&z(U.배당)===z(O.odds)&&oe(U.시장확률)!=null&&Math.abs(z(U.시장확률)-z(O.market_prob))<=50001e-9));if(!y)continue;g={market:y.market,label:y.market_label??y.label??"",선택:y.sel,배당:y.odds},P=y.decision_pipeline_applied===!0&&y.has_validated_edge===!0?oe(y.predicted_hit_prob)??oe(y.market_prob):oe(y.market_prob),D=z(y.odds),W=y.recommended_at||(n||t).generated_at,S="current",E={state:"pending",label:"경기 전",record:null},L="경기 시작 전입니다."}const G=`${zs(p,N)}|${ne(g)}`,K={key:G,game:p,kickoff:N,phase:$,source:S,option:g,openingProbability:P,originalOdds:Number.isFinite(D)&&D>1?D:null,capturedAt:W,outcome:E,estimate:H,estimateMessage:L,savedPrior:!!k},u=h.get(G),_=K.savedPrior,M=u?.savedPrior;!u||_&&!M||_===M&&ue(W)b[f.phase]-b[p.phase]||f.kickoff-p.kickoff||(f.keyp.key?1:0))}function Be(e,t,n){const i=r=>["hit","miss","void"].includes(r.outcome.state);if(i(e)&&i(t)&&e.outcome.state!==t.outcome.state)return{...e,phase:"pending",estimate:null,outcome:{...e.outcome,state:"pending",label:"정산 결과 충돌 · 확인 중"}};const l=r=>Date.parse(r.game._liveState?.observed_at||r.game._liveFeedAt||"")||0,a=i(t)&&!i(e)?t:i(e)?e:t.phase==="finished"&&e.phase!=="finished"?t:e.phase==="finished"?e:l(t)>l(e)?t:e;if(a===e)return e;const o={...e.game,status:a.game.status,score:a.game.score,_liveState:a.game._liveState,_liveFeedAt:a.game._liveFeedAt,_liveStarted:a.game._liveStarted},d=a.phase==="live"?Me(o,o._liveState,n)?.estimate:null;return{...e,game:o,phase:a.phase,estimate:d,estimateMessage:a.estimateMessage,outcome:{...a.outcome,record:e.outcome.record}}}const Ke=e=>Number.isFinite(e)?`${(e*100).toFixed(1)}%`:"기록 없음",Vs={highlight:"오늘 추천 · 사전 기록",recorded:"사전 픽 · 추천 이력 미확인",current:"오늘 추천 · 경기 전"};function Js({games:e=[],today:t=null,currentToday:n=null,now:i=Date.now()}){const l=Zs({games:e,today:t,currentToday:n,now:i}),a=l.filter(r=>r.phase!=="finished"),o=l.filter(r=>r.phase==="finished"),d=l.filter(r=>r.source!=="recorded").length;return s.jsxs("section",{className:"mb-5 min-w-0","aria-label":"오늘의 추천 픽",children:[s.jsxs("div",{className:"mb-3 flex flex-wrap items-baseline justify-between gap-2",children:[s.jsxs("h2",{className:"m-0 text-[17px] font-bold tracking-tight",children:["오늘의 추천 픽 ",s.jsx("span",{className:"tnum text-ink3",children:d})]}),s.jsx("p",{className:"m-0 text-[12px] text-ink3",children:"오늘 추천과 저장된 사전 픽을 함께 추적합니다. 추천 이력 미확인 픽은 구분해 표시합니다."})]}),!l.length&&s.jsx(Pe,{className:"p-4 text-[13px] text-ink3",children:"오늘 확인된 추천·사전 픽이 없습니다."}),s.jsx(He,{picks:a}),o.length>0&&s.jsxs("details",{className:"mt-3 rounded border border-rule p-3",children:[s.jsxs("summary",{className:"cursor-pointer text-[13px] font-semibold",children:["종료된 사전 픽 ",o.length,"개 · 적중 ",o.filter(r=>r.outcome.state==="hit").length," · 적중실패 ",o.filter(r=>r.outcome.state==="miss").length," · 결과와 당시 배당 보기"]}),s.jsx("div",{className:"mt-3",children:s.jsx(He,{picks:o})})]})]})}function He({picks:e}){return s.jsx("div",{className:"grid min-w-0 grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3",children:e.map(t=>s.jsxs(Pe,{as:"article",className:"min-w-0 p-3 sm:p-4","aria-label":`${t.game.home} 대 ${t.game.away} 사전 픽`,children:[s.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-1 text-[11px] text-ink3",children:[s.jsxs("span",{children:[t.game.league," · ",s.jsx("time",{dateTime:new Date(t.kickoff).toISOString(),children:new Date(t.kickoff+9*36e5).toISOString().slice(11,16)})]}),s.jsx("span",{className:t.phase==="live"?"live-score-badge":"",children:hs[t.phase]})]}),s.jsxs("h3",{className:"my-2 break-words text-[15px] font-semibold",children:[t.game.home," ",s.jsx("span",{className:"font-normal text-ink3",children:"vs"})," ",t.game.away]}),t.phase==="live"&&s.jsxs("p",{className:"tnum my-2 text-[16px] font-bold",children:[t.game._liveState?.home_score??"—"," : ",t.game._liveState?.away_score??"—",s.jsx("small",{className:"ml-2 text-[12px] font-normal",children:t.game._liveState?.status_text||"진행 중"})]}),s.jsx("p",{className:"m-0 text-[11px] text-ink3",children:Vs[t.source]}),s.jsxs("p",{className:"mt-1 mb-3 break-words text-[14px] font-bold",children:[t.option.market,t.option.label?` ${t.option.label}`:""," · ",t.option.선택]}),s.jsxs("dl",{className:"m-0 grid grid-cols-2 gap-3 border-t border-rule pt-3",children:[s.jsxs("div",{children:[s.jsx("dt",{className:"text-[11px] text-ink3",children:"사전 확률"}),s.jsx("dd",{className:"tnum m-0 text-[19px] font-semibold",children:Ke(t.openingProbability)})]}),s.jsxs("div",{children:[s.jsx("dt",{className:"text-[11px] text-ink3",children:"현재 추정"}),s.jsx("dd",{className:"tnum m-0 text-[19px] font-semibold",children:t.estimate?Ke(t.estimate.probability):"—"})]})]}),s.jsx("p",{className:"mt-2 mb-3 text-[11px] text-ink3",children:t.estimate?"사전 확률에 현재 점수·경기 진행을 반영한 추정":t.estimateMessage}),s.jsxs("div",{className:"border-t border-rule pt-2",children:[s.jsxs("div",{className:"flex items-center justify-between gap-2 text-[12px]",children:[s.jsx("span",{className:"text-ink3",children:"결과"}),s.jsx("b",{className:`result-badge is-${t.outcome.state}`,children:t.phase==="upcoming"?"경기 전":t.phase==="live"?"진행 중":t.outcome.label})]}),s.jsxs("div",{className:"mt-1 flex items-center justify-between gap-2 text-[12px]",children:[s.jsx("span",{className:"text-ink3",children:"당시 배당"}),s.jsx("b",{className:"tnum",children:t.originalOdds==null?"기록 없음":`${t.originalOdds.toFixed(2)}배`})]})]})]},t.key))})}function Xs({draft:e,onClose:t,onSaved:n}){const[i,l]=C.useState(1e4),[a,o]=C.useState(Number(e?.option?.배당)||0);if(!e)return null;const d=()=>{const r=Ts(e.game,e.option,{stake:i,purchaseOdds:a});Os(r),n?.(r),t()};return s.jsx("div",{className:"bet-dialog-backdrop",role:"presentation",onMouseDown:r=>{r.target===r.currentTarget&&t()},children:s.jsxs("section",{className:"bet-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"bet-dialog-title",children:[s.jsxs("div",{children:[s.jsx("small",{children:"단일 베팅 기록"}),s.jsxs("h2",{id:"bet-dialog-title",children:[e.game.home," vs ",e.game.away]})]}),s.jsxs("p",{children:[e.option.market,e.option.label?` ${e.option.label}`:""," · ",s.jsx("b",{children:e.option.선택})]}),s.jsxs("label",{children:["구매 배당",s.jsx("input",{type:"number",min:"1.01",step:"0.01",value:a,onChange:r=>o(r.target.value)})]}),s.jsxs("label",{children:["투입금",s.jsx("input",{type:"number",min:"100",step:"100",value:i,onChange:r=>l(r.target.value)})]}),s.jsxs("div",{className:"bet-dialog-actions",children:[s.jsx("button",{type:"button",onClick:t,children:"취소"}),s.jsx("button",{type:"button",className:"is-primary",disabled:!(a>1&&i>0),onClick:d,children:"내 베팅에 저장"})]})]})})}function fs(e){const t=String(e?.해설||"").trim();if(!t)return"";const n=(e?.options||[]).filter(b=>b.market==="언더오버"),i=n.find(b=>Number.isFinite(Number(b.line))),l=i?Number(i.line):null;if(l==null||!(l!==8.5&&/(?:기준선(?:인|은|\(|\s)|기준점(?:인|은|\s))[^.]{0,20}8\.5|8\.5점/.test(t)))return t;const o=t.replace(/\s*\n+\s*/g," ").split(new RegExp("(?<=[.!?])\\s+")).filter(Boolean).filter(b=>!/8\.5/.test(b)).filter(b=>!/총득점은\s*(?:오버|언더).*(?:추천|예상)/.test(b)).filter(b=>!/(?:전력|득점 차|득실 차|팀).*(?:2점\s*차|2점차)\s*이상.*(?:가능|예상|공산)/.test(b)),d=n.find(b=>b.선택==="언더"),r=n.find(b=>b.선택==="오버"),m=[d,r].filter(b=>Number.isFinite(Number(b?.모델확률))).sort((b,f)=>Number(f.모델확률)-Number(b.모델확률))[0],x=e?.sport==="sc"?"골":"점",h=m?`실제 언더오버 기준점은 ${l}${x}이다. 득점분포 모델의 ${m.선택} 확률은 ${(Number(m.모델확률)*100).toFixed(1)}%다.`:`실제 언더오버 기준점은 ${l}${x}이다. 기존 8.5${x} 비교는 잘못된 기본값이라 제외했다.`;return o.length?[o[0],h,...o.slice(1)].join(" "):h}function pe(e,t){const n=e?.선발;if(!n)return null;const i=n[`${t}_detail`];if(i)return i;const l=n[t];return l?{name:l,announced:!0}:null}function Se(e,t){return e?.선발?.teams?.[t]||null}function be(e,t){const n=e?.선발?.unavailable?.[t];return Array.isArray(n)?n.filter(i=>i?.name):[]}function Qs(e){const t=e?.선발;return t?.source?{name:t.source,url:t.source_url||null,updatedAt:t.updated_at||null}:null}function et(e,t=""){const n=pe(e,"home")||pe(e,"away"),i=e?.form_home||e?.form_away||e?.h2h||Se(e,"home")||Se(e,"away"),l=e?.라인업||{},a=!!(l.home||l.away),o=be(e,"home").length||be(e,"away").length||a||e?.선발?.lineups,d=e?.sport,r=["bs","sc","bk","vl"].includes(d),c=[];return(r||t||n||i)&&c.push({id:"summary",label:"경기 분석"}),(r||n||a||o)&&c.push({id:"players",label:"선수·출전"}),(r||e?.decision_snapshot)&&c.push({id:"evidence",label:"픽 근거·수식"}),c}function st(e){const t=e?.stats;return t?[["ERA",t.era],["WHIP",t.whip],["승-패",t.record],["이닝",t.innings_display??t.innings],["탈삼진",t.strikeouts],["K/9",t.k9],["BB/9",t.bb9],["HR/9",t.hr9],[t.fip_approx?"FIP*":"FIP",t.fip],["평균 이닝",t.avg_ip],["선발",t.games_started]].filter(([,n])=>n!=null&&n!==""):[]}const B=e=>e!=null&&e!=="",Ge=e=>{const t=Number(e);return Number.isFinite(t)?t.toFixed(3).replace(/^0/,""):null};function tt(e){if(!e)return"공식 예고 선발";const t=[];return e.period&&t.push(e.period),e.record&&t.push(e.record),B(e.era)&&t.push(`ERA ${e.era}`),B(e.whip)&&t.push(`WHIP ${e.whip}`),B(e.strikeouts)&&t.push(`${e.strikeouts}탈삼진`),t.join(" · ")||"공식 예고 선발"}function nt(e){const t=e?.stats;if(!t)return`${e.order||"-"}번 · ${e.position||"야수"}`;const n=[],i=Ge(t.avg),l=Ge(t.ops);return i&&n.push(`타율 ${i}`),B(t.home_runs)&&n.push(`${t.home_runs}홈런`),B(t.rbi)&&n.push(`${t.rbi}타점`),l&&n.push(`OPS ${l}`),`${t.season?`${t.season}시즌 · `:""}${n.join(" · ")}`}function it(e){return[...e||[]].filter(t=>t?.name&&t.position!=="투수").sort((t,n)=>{const i=t.stats||{},l=n.stats||{};return(Number(l.ops)||0)-(Number(i.ops)||0)||(Number(l.home_runs)||0)-(Number(i.home_runs)||0)||(Number(l.rbi)||0)-(Number(i.rbi)||0)||(Number(t.order)||99)-(Number(n.order)||99)})[0]||null}function lt(e,t){const n=[];return B(e.games??e.apps)&&n.push(`${e.games??e.apps}경기`),t==="sc"?(B(e.goals)&&n.push(`${e.goals}골`),B(e.assists)&&n.push(`${e.assists}도움`),B(e.xg)&&n.push(`xG ${e.xg}`)):t==="bk"?(B(e.points)&&n.push(`평균 ${e.points}점`),B(e.rebounds)&&n.push(`${e.rebounds}리바운드`),B(e.assists)&&n.push(`${e.assists}도움`)):t==="vl"&&(B(e.points)&&n.push(`${e.points}득점`),B(e.blocks)&&n.push(`${e.blocks}블로킹`),B(e.serves)&&n.push(`${e.serves}서브`)),n.join(" · ")||"공식 시즌 선수 기록"}function at(e){const t=e?.선발||{},n=[];if(e?.sport==="bs"){for(const r of["home","away"]){const c=t[`${r}_detail`]||(t[r]?{name:t[r]}:null);c?.name&&n.push({name:c.name,team:e[r],position:"선발투수",role:tt(c.stats),profileUrl:c.profile_url||null})}for(const r of["home","away"]){const c=it(t.lineups?.[r]);c&&n.push({name:c.name,team:e[r],position:`${c.order||"-"}번 타자${c.position?` · ${c.position}`:""}`,role:nt(c),profileUrl:c.profile_url||null})}const l=t.lineup_status||{},a=l.state,d=l.official_today===!0||a==="official_today"?"오늘 NPB 공식 선발 타순과 공식 시즌 선수 기록을 반영했다.":a==="mixed_official_projected"?"공개된 팀은 오늘 공식 타순, 미공개 팀은 최근 완료 경기 기반 예상 타순이다.":t.lineups?"타자는 최근 공식 경기 기반 예상 타순이며 오늘 확정 명단이 아니다.":"";return{players:n,note:d}}const i=t.key_players||{};for(const l of["home","away"]){const a=i[l]?.[0];a?.name&&n.push({name:a.name,team:e[l],position:a.position||"핵심 선수",role:lt(a,e?.sport),profileUrl:a.profile_url||null})}return{players:n,note:n.length?"공식 시즌 기록의 팀별 주요 선수를 표시한다.":""}}const A=e=>{const t=Number(e);return Number.isFinite(t)?t:null},rt=e=>{const t=A(e);return t===null?null:t.toFixed(3).replace(/^0/,"")};function ot(e,t){if(!e?.name)return null;const n=e.stats||e,i=[];let l="주요 전력";if(t==="bs")A(n.era)!==null&&i.push(`ERA ${Number(n.era).toFixed(2)}`),A(n.whip)!==null&&i.push(`WHIP ${Number(n.whip).toFixed(2)}`),A(n.ops)!==null&&i.push(`OPS ${rt(n.ops)}`),A(n.home_runs)!==null&&i.push(`${n.home_runs}홈런`),l=A(n.era)!==null?"선발 운영의 중심":"타선 생산력의 중심";else if(t==="sc"){A(n.goals)!==null&&i.push(`${n.goals}골`),A(n.assists)!==null&&i.push(`${n.assists}도움`),A(n.xg)!==null&&i.push(`xG ${n.xg}`);const a=A(n.goals)||0,o=A(n.assists)||0;l=a>o?"득점 마무리의 중심":o>a?"기회 창출의 중심":"공격 전개의 중심"}else t==="bk"?(A(n.points)!==null&&i.push(`${n.points}점`),A(n.rebounds)!==null&&i.push(`${n.rebounds}리바운드`),A(n.assists)!==null&&i.push(`${n.assists}도움`),l=(A(n.assists)||0)>=(A(n.rebounds)||0)?"볼 배급과 공격 조율":"득점·리바운드 기여"):(A(n.points)!==null&&i.push(`${n.points}득점`),A(n.blocks)!==null&&i.push(`${n.blocks}블로킹`),A(n.serves)!==null&&i.push(`${n.serves}서브`),l=(A(n.blocks)||0)>0?"공격과 네트 수비의 중심":"공격 득점의 중심");return{name:e.name,position:e.position||null,characteristic:l,facts:i,status:e.status||null}}function ct(e,t){const n=[],i=A(e?.goals_per_game??e?.points_per_game??t?.avg_scored),l=A(e?.conceded_per_game??t?.avg_conceded);return i!==null&&l!==null&&(i-l>=.35?n.push("공격 생산력이 실점 억제보다 두드러진 팀"):l-i>=.35?n.push("공수 균형 회복이 필요한 팀"):n.push("득점과 실점 흐름이 비교적 균형적인 팀")),t?.trend==="상승"&&n.push("최근 경기 내용이 상승세"),t?.trend==="하락"&&n.push("최근 경기 내용이 하락세"),t?.streak&&n.push(String(t.streak)),n}function dt(e){const t=e?.선발||{},n=t.team_profiles||{};return["home","away"].map(i=>{const l=n[i]||{},a=l.record||t.teams?.[i]||{},o=i==="home"?e?.form_home:e?.form_away;let d=l.key_players||t.key_players?.[i]||[];if(e?.sport==="bs"){const x=t[`${i}_detail`]||(t[i]?{name:t[i]}:null),h=t.lineups?.[i]||[];d=[x,...h].filter(Boolean)}const r=d.map(x=>ot(x,e?.sport)).filter(Boolean).slice(0,3),c=[...new Set([...l.characteristics||[],...ct(a,o)])].slice(0,5),m=(l.unavailable||t.unavailable?.[i]||[]).filter(x=>x?.name).slice(0,3);return{side:i,team:e?.[i],characteristics:c,players:r,unavailable:m}})}function ut(e){return(e||[]).map(t=>{const n=t.characteristics.slice(0,2).join(" · "),i=t.players.slice(0,2).map(l=>`${l.name}(${l.characteristic}${l.facts.length?` · ${l.facts.join(" · ")}`:""})`).join(", ");return[t.team,n,i].filter(Boolean).join(" — ")}).filter(t=>t.split(" — ").length>1).join(" / ")}function mt(e){return(e||[]).map(t=>{const n=(t.players||[]).slice(0,2).map(i=>i.name).filter(Boolean);return n.length&&t.team?`${t.team}: ${n.join(" · ")}`:null}).filter(Boolean).join(" / ")}const j=e=>e==null||typeof e=="string"&&!e.trim()?null:Number.isFinite(Number(e))?Number(e):null,Q=(e,t)=>{const n=String(e||"").trim().at(-1);return!n||!/[가-힣]/.test(n)?`${e}${t[1]}`:`${e}${(n.charCodeAt(0)-44032)%28?t[0]:t[1]}`};function ht(e,t=null){const n=e?.options||[],i=ys(e,n),l=t?t===i?t:null:i,a=us(e,l);if(a.action!=="market_reference"||!a.option)return{outcome:null,side:null,probability:null,marketProbability:null,shadowProbability:null,margin:0,market:null,label:"",headline:cs(a),decision:a};const o=l?.선택||null,d=l?.market||null,r=String(d||""),c=r.includes("언더오버"),m=r.includes("핸디캡"),x=["승①패","승⑤패"].includes(d),h=["전반승무패","전반승패"].includes(d),b=["승무패","승패"].includes(d),f=/무/.test(String(o||"")),p=/원정|패/.test(String(o||""))||o===e?.away,N=f?"무승부":p?e?.away:e?.home,F=b?N:null,$=a.probability.final,w=a.probability.market,k=a.recommendationPriority==="reversal"?"전환 픽 · ":"";let v;return c?v=`${k}${l?.label||"기준점"} ${o}`:m?v=`${k}${l?.label||"핸디캡"} ${o}`:x?v=`${k}${d} ${o}`:h?v=f?`${k}전반 무승부`:`${k}전반 ${N} 우세`:b?v=f?`${k}팽팽함`:`${k}${F} 우세`:v=`${k}${[d,l?.label,o].filter(Boolean).join(" ")}`,{outcome:o,side:F,probability:$,marketProbability:w,shadowProbability:a.probability.aiCandidate,margin:a.probability.aiDeltaApplied,market:d,label:l?.label||"",headline:v,decision:a}}const Ue=e=>{const t=String(e||"").match(/^(\d+)-(\d+)$/);if(!t)return null;const n=Number(t[1])+Number(t[2]);return n?Number(t[1])/n:null},qe=e=>{const t=String(e?.last10||"").match(/(\d+)승(?:\s*(\d+)무)?\s*(\d+)패/);if(!t)return null;const n=Number(t[1])+Number(t[2]||0)+Number(t[3]);return n?(Number(t[1])+Number(t[2]||0)*.5)/n:null};function me(e,t,n,i,l=0){return t===null||n===null||Math.abs(t-n)<=l?{label:e,side:null,state:"중립"}:{label:e,side:t>n?i.home:i.away,state:"우세"}}const js={"최근 10경기":"최근 성적","최근 공수":"최근 득실","홈·원정":"홈·원정 성적","시즌 순위":"시즌 성적"},ye=e=>e.map(t=>js[t.label]||t.label).join("·");function xt(e,t,n){const i=e.side,l=e?.decision?.recommendationPriority==="reversal",a=t.filter(r=>r.side===i),o=t.filter(r=>r.side&&r.side!==i),d=o[0]?.side;return n==="일치"?ye(a)+"에서 모두 "+Q(i,["이","가"])+" 앞선다. "+(l?"역배 전환 관문도 같은 방향을 가리켜 최종 픽을 교체했다.":"최근 기록도 최종 추천인 "+i+" 쪽을 뒷받침한다."):n==="엇갈림"?ye(a)+"에서는 "+Q(i,["이","가"])+" 앞서고, "+ye(o)+"에서는 "+Q(d,["이","가"])+" 낫다. "+(l?"엇갈림을 숨기지 않으며 전환 방향의 확률은 해당 역배 시장확률로 표시한다.":"엇갈리는 자료를 위험 신호로 함께 표시한다."):n==="반대"?"확인되는 최근 기록은 "+Q(d,["이","가"])+" 앞선다. "+(l?"전환 픽과 반대되는 자료이므로 위험 신호로 함께 남긴다.":"최종 추천과 반대되는 자료이므로 위험 신호로 함께 남긴다."):l?"비교할 최근 기록은 부족하다. 구조 모델 전환 관문으로 "+i+" 쪽을 최종 선택했지만 표시 확률은 해당 역배의 시장확률이다.":"비교할 최근 기록이 충분하지 않아 배당에 반영된 평가를 바탕으로 "+i+" 쪽을 비교 후보로 둔다."}function pt(e,t){if(!t?.side||t.side==="무승부"||!["승무패","승패"].includes(t.market))return null;const n=e?.form_home||{},i=e?.form_away||{},l=[],a=qe(n),o=qe(i);(a!==null||o!==null)&&l.push(me("최근 10경기",a,o,e,.05));const d=j(n.avg_scored)!==null&&j(n.avg_conceded)!==null?j(n.avg_scored)-j(n.avg_conceded):null,r=j(i.avg_scored)!==null&&j(i.avg_conceded)!==null?j(i.avg_scored)-j(i.avg_conceded):null;(d!==null||r!==null)&&l.push(me("최근 공수",d,r,e,.2));const c=Ue(n.home),m=Ue(i.away);(c!==null||m!==null)&&l.push(me("홈·원정",c,m,e,.04));const x=j(e?.선발?.teams?.home?.rank),h=j(e?.선발?.teams?.away?.rank);(x!==null||h!==null)&&l.push(me("시즌 순위",x===null?null:-x,h===null?null:-h,e));const b=l.filter($=>$.side),f=b.filter($=>$.side===t.side).length,p=b.filter($=>$.side!==t.side).length,N=p===0?f?"일치":"자료 부족":f===0?"반대":"엇갈림",F=xt(t,l,N);return{modelSide:t.side,signals:l,support:f,oppose:p,state:N,narrative:F}}function bt(e,t){const n=e?.stats||e||{},i=[];return t==="bs"?(j(n.ops)!==null&&i.push(`OPS ${j(n.ops).toFixed(3)}`),j(n.home_runs)!==null&&i.push(`${n.home_runs}홈런`),j(n.rbi)!==null&&i.push(`${n.rbi}타점`)):t==="sc"?(j(n.goals)!==null&&i.push(`${n.goals}골`),j(n.assists)!==null&&i.push(`${n.assists}도움`),j(n.key_passes)!==null&&i.push(`키패스 ${n.key_passes}`)):t==="bk"?(j(n.points)!==null&&i.push(`${n.points}점`),j(n.rebounds)!==null&&i.push(`${n.rebounds}리바운드`),j(n.assists)!==null&&i.push(`${n.assists}도움`)):(j(n.points)!==null&&i.push(`${n.points}득점`),j(n.attacks)!==null&&i.push(`공격 ${n.attacks}`),j(n.blocks)!==null&&i.push(`블로킹 ${n.blocks}`)),i.join(" · ")||e?.position||"주요 선수"}function We(e,t){const n=e?.stats||e||{};return t==="bs"?j(n.ops)??(j(n.home_runs)||0)/100:t==="sc"?(j(n.goals)||0)*4+(j(n.assists)||0)*3+(j(n.key_passes)||0)/10:t==="bk"?j(n.efficiency)??j(n.points)??0:j(n.points)??0}const ft=(e,t,n,i)=>e?.name?{team:t,name:e.stats?.name||e.name,role:e.position||"주요 선수",detail:bt(e,n)}:null;function jt(e){const t=e?.선발||{},n=at(e),i=n.players.map(d=>({team:d.team,name:d.name,role:d.position,detail:d.role,profileUrl:d.profileUrl})),l=n.note?[n.note]:[];if(e?.sport==="bs"){const d=i.filter(r=>r.role==="선발투수");d.length===2&&l.unshift(`${d[0].team} ${Q(d[0].name,["과","와"])} ${d[1].team} ${d[1].name}의 선발 맞대결이다.`)}for(const d of["home","away"]){if(i.some(b=>b.team===e?.[d]))continue;const r=t.key_players?.[d]||[],c=t.lineups?.[d]||[],x=[...r.length?r:c].sort((b,f)=>We(f,e?.sport)-We(b,e?.sport))[0],h=ft(x,e?.[d],e?.sport);h&&i.push(h)}const a=["home","away"].flatMap(d=>(t.unavailable?.[d]||[]).map(r=>({...r,team:e?.[d]})));if(a.length){const d=a.slice(0,2).map(r=>{const c=[r.reason_label||r.status,r.impact_label&&`영향 ${r.impact_label}`].filter(Boolean).join(" · ");return`${r.team} ${r.name}${c?`(${c})`:""}`}).join(", ");l.push(`${d}${a.length>2?` 외 ${a.length-2}명`:""}의 출전 여부가 변수다.`)}const o=t.availability_summary;if(o?.leans){const d=o.leans==="home"?e?.away:e?.home;l.push(`확인된 명단 기준으로는 ${d} 쪽 전력 손실 부담이 더 크다. 이 값은 과거 검증 전이라 모델 확률에는 직접 더하지 않았다.`)}return{featuredPlayers:i,playerNotes:l}}const de=e=>{const t=j(e);return t===null?null:new Intl.NumberFormat("ko-KR",{maximumFractionDigits:1}).format(t)};function ze(e,t){if(!e||!t)return null;const n=[];return t.last10?n.push(`최근 10경기 ${t.last10}`):(j(t.w)!==null||j(t.l)!==null)&&n.push(`${j(t.w)||0}승${j(t.d)?` ${j(t.d)}무`:""} ${j(t.l)||0}패`),t.streak&&n.push(`${t.streak} 흐름`),t.trend==="상승"&&n.push("경기 내용도 상승세"),t.trend==="하락"&&n.push("최근 경기 내용은 하락세"),n.length?`${Q(e,["은","는"])} ${n.join(", ")}다`:null}function Nt(e){const t=String(e?.form_home?.home||""),n=String(e?.form_away?.away||""),i=a=>{const o=a.match(/^(\d+)-(\d+)$/);return o?`${o[1]}승 ${o[2]}패`:a};if(!t&&!n)return null;const l=[];if(t&&l.push(`${e.home}의 홈 성적은 ${i(t)}`),n&&l.push(`${e.away}의 원정 성적은 ${i(n)}`),t&&n){const a=t.match(/^(\d+)-(\d+)$/),o=n.match(/^(\d+)-(\d+)$/),d=a?Number(a[1])/(Number(a[1])+Number(a[2])):null,r=o?Number(o[1])/(Number(o[1])+Number(o[2])):null,c=d!==null&&r!==null&&Math.abs(d-r)>=.08?` 이 조건에서는 ${d>r?e.home:e.away} 쪽이 더 안정적이었다.`:" 장소에 따른 뚜렷한 우위는 크지 않다.";return`${l.join(", ")}다.${c}`}return`${l[0]}다. 이번 경기 장소에서의 적응력을 함께 볼 필요가 있다.`}function gt(e){const t=j(e?.form_home?.avg_scored),n=j(e?.form_away?.avg_scored),i=j(e?.form_home?.avg_conceded),l=j(e?.form_away?.avg_conceded);if(t===null&&n===null&&i===null&&l===null)return null;const a=[];(t!==null||i!==null)&&a.push(`${e.home} ${t!==null?`${de(t)}득점`:""}${t!==null&&i!==null?"·":""}${i!==null?`${de(i)}실점`:""}`),(n!==null||l!==null)&&a.push(`${e.away} ${n!==null?`${de(n)}득점`:""}${n!==null&&l!==null?"·":""}${l!==null?`${de(l)}실점`:""}`);const o=[];t!==null&&n!==null&&(Math.abs(t-n)<.2?o.push("득점 생산력은 비슷하다"):o.push(`${t>n?e.home:e.away}의 공격 전개가 더 꾸준했다`)),i!==null&&l!==null&&(Math.abs(i-l)<.2?o.push("실점 억제력도 큰 차이가 없다"):o.push(`${i{const c=[];return j(r.rank)!==null&&c.push(`${r.rank}위`),j(r.wins)!==null&&c.push(`${r.wins}승${j(r.draws)?` ${r.draws}무`:""}${j(r.losses)!==null?` ${r.losses}패`:""}`),j(r.points)!==null&&c.push(`${r.points}점`),c.join(" · ")},o=[a(t)&&`${e.home} ${a(t)}`,a(n)&&`${e.away} ${a(n)}`].filter(Boolean),d=i!==null&&l!==null?i===l?"현재 순위상 두 팀의 간격은 없다.":`누적 성적과 시즌 안정감은 ${i`${a.team} ${a.name}(${a.role}${a.detail&&a.detail!==a.role?` · ${a.detail}`:""})`),i=e?.playerNotes?.[0];return`${n.length?`이번 경기에서 먼저 볼 선수는 ${n.join(", ")}다. 경기 운영과 승부처에서 이들의 영향력을 확인해야 한다.`:""}${i?` ${i}`:""}`.trim()}function yt(e,t){const n=String(t?.market||""),l=t?.decision?.recommendationPriority==="reversal"?"전환 최종 픽으로 정했다":"최종 예상 적중 후보로 정했다";if(n.includes("언더오버")){const p=String(t.outcome||"").includes("언더"),N=n.startsWith("전반"),F=j(e?.form_home?.avg_scored),$=j(e?.form_away?.avg_scored),w=F!==null&&$!==null?F+$:null;return`${!N&&w!==null?`두 팀의 최근 경기당 득점 합은 ${de(w)}점이다.`:`${N?"전반":"경기"} 득점 기준의 시장확률을 사용했다.`} ${t.label||"발매 기준점"}에서 ${p?"득점이 크게 벌어지지 않는":"공격 전개가 이어지는"} 쪽을 ${l}.`}if(n.includes("핸디캡"))return`${t.label||"발매 핸디캡"} 기준에서 ${t.outcome} 쪽을 ${l}. 이는 핸디캡 적용 후 적중 방향이며 실제 승리 예측과는 다르다.`;if(["승①패","승⑤패"].includes(t?.market))return`${t.market}의 ${t.outcome} 쪽을 ${l}. 이 판정은 승패만이 아니라 발매된 점수 차 조건까지 포함한다.`;if(n.startsWith("전반"))return`전반 시장에서 ${t.outcome} 쪽을 ${l}. 전반 결과와 경기 최종 결과는 별개의 마켓이다.`;if(t?.side==="무승부")return`${verdict} 양쪽의 강점이 엇갈리는 접전 시나리오를 기준으로 삼았다.`;const a=t?.side;if(!a)return t?.modelAvailable?"경기 모델 추천 기준을 통과한 선택이 없다. 경기 모델 추천에서 제외한다.":"이 경기에는 모델확률이 없다. 경기 모델 추천에서 제외한다.";const o=a===e?.home,d=o?e?.form_home:e?.form_away,r=o?d?.home:d?.away,c=[];d?.streak&&/연승/.test(d.streak)&&c.push(d.streak),d?.trend==="상승"&&c.push("최근 상승 흐름"),r&&c.push(o?"홈 경기 경쟁력":"원정 경기 경쟁력");const m=c.length?c.slice(0,2).join("과 "):"현재 확인된 경기 정보",x=o?e?.away:e?.home,h=j(d?.avg_conceded),b=j((o?e?.form_away:e?.form_home)?.avg_scored),f=h!==null&&b!==null&&b>h?` 다만 ${x}의 최근 득점 생산력은 경기를 쉽게 벌리지 못하게 할 변수다.`:" 다만 경기 초반 실점 여부와 선발 구성은 흐름을 바꿀 수 있다.";return`${Q(a,["이","가"])} ${Q(m,["을","를"])} 바탕으로 주도권을 조금 더 오래 가져갈 가능성을 높게 봤다.${f}`}function kt(e,t,n,i){const l=[],a=[ze(e?.home,e?.form_home),ze(e?.away,e?.form_away)].filter(Boolean);a.length&&l.push(`최근 분위기 — ${a.join(". ")}.`);const o=gt(e);o&&l.push(`공격·수비 균형 — ${o}`);const d=Nt(e);d&&l.push(`홈·원정 조건 — ${d}`);const r=_t(e);r&&l.push(`시즌 위치 — ${r}`);const c=vt(n);c&&l.push(`선수 변수 — ${c}`);const m=ut(i);return m&&l.push(`팀·선수 프리뷰 — ${m}`),l.push(`판정 시나리오 — ${yt(e,t)}`),[...new Set(l)].slice(0,8)}function $t(e,t=null,n=""){const i=ht(e,t),l=pt(e,i),a=jt(e),o=dt(e),d=kt(e,i,a,o),r=e?.선발?.lineup_status?.state==="announced"||e?.sport==="bs"&&e?.선발?.home,m=[...(l?.signals||[]).filter(x=>x.side&&x.side!==i.side).map(x=>`${js[x.label]||x.label}은 ${x.side} 쪽이 앞선다.`),...r?[]:["경기 직전 선발·출전 명단이 바뀌면 예상 흐름도 달라질 수 있다."]];return{prediction:i,decision:i.decision,signalSummary:l,reasons:d,commentary:String(n||"").trim(),...a,teamPreviews:o,cautions:[...new Set(m)]}}const Ye=["confidence","odds","overround","market_gap","price_distance","three_way","handicap","totals","first_half","baseball","basketball","volleyball","soccer"],ie=e=>{const t=Number(e);return Number.isFinite(t)?t:null};function wt(e){const t=`${e?.sport||""} ${e?.league||""}`.toLowerCase(),n=Number(["baseball","야구","kbo","mlb","npb"].some(a=>t.includes(a))),i=Number(["basketball","농구","nba","kbl","wnba"].some(a=>t.includes(a))),l=Number(["volleyball","배구","v리그"].some(a=>t.includes(a)));return[n,i,l,+!(n||i||l)]}function St(e,t){const n=ie(e?.market_prob),i=ie(e?.odds),l=ie(e?.overround),a=ie(t?.odds_min),o=ie(t?.odds_max),d=ie(t?.target_odds);if(!(n>0&&n<1)||!(i>1)||!(l>0)||a==null||o==null||!(d>1)||i=o)return null;const r=String(e?.market||""),[c,m,x,h]=wt(e);return[Math.log(n/(1-n)),Math.log(i),Math.max(0,l-1),Math.max(0,ie(e?.market_gap)||0),Math.abs(Math.log(i/d)),+(Number(e?.n_way||2)===3),Number(r.includes("핸디")),Number(r.includes("언더오버")),Number(r.startsWith("전반")),c,m,x,h]}function Ft(e,t,n=3){const i=t?.genome,l=(t?.genomes||[]).filter(r=>r&&typeof r=="object"),a=t?.constraints;if(!i&&!l.length||!a)return[];const o=(e||[]).flatMap(r=>{const c=St(r,a);return c?[{candidate:r,features:c}]:[]});if(!o.length)return[];let d;if(l.length){const r=o.map(({features:x})=>l.map(h=>x.reduce((b,f,p)=>b+f*Number(h[Ye[p]]||0),0))),c=l.map((x,h)=>r.reduce((b,f)=>b+f[h],0)/r.length),m=l.map((x,h)=>{const b=r.reduce((p,N)=>p+(N[h]-c[h])**2,0)/r.length,f=Math.sqrt(b);return f<1e-9?1:f});d=r.map(x=>x.reduce((h,b,f)=>h+(b-c[f])/m[f],0)/l.length)}else d=o.map(({features:r})=>r.reduce((c,m,x)=>c+m*Number(i[Ye[x]]||0),0));return o.map(({candidate:r},c)=>({...r,evolution_score:Number(d[c].toFixed(6))})).sort((r,c)=>c.evolution_score-r.evolution_score||Number(c.market_prob||0)-Number(r.market_prob||0)||Number(r.overround||99)-Number(c.overround||99)||String(r.kickoff_at||r.date||"").localeCompare(String(c.kickoff_at||c.date||""))).slice(0,Math.max(0,Number(n)||0))}function Pt(e,t){if(!e?.profiles)return e||null;const n=Object.fromEntries(Object.entries(e.profiles).map(([i,l])=>{const o=l?.historical_status==="rejected_in_historical_audit"?[]:Ft(t,l?.rule,3);return[i,{...l,selected:o[0]||null,alternatives:o.slice(1)}]}));return{...e,profiles:n}}const je=540*60*1e3,Ae=1440*60*1e3,Ct=12,Mt=/(\d{2})\.(\d{2}).*?(\d{2}):(\d{2})/,Ze=1800*1e3;function ee(e,t){const n=Date.parse(e?.kickoff_at||"");if(Number.isFinite(n))return n;const i=String(e?.date||"").match(Mt);if(!i)return Number.NaN;const[,l,a,o,d]=i.map(Number),r=Number(t)||new Date().getUTCFullYear(),c=Number(e?.round)===1&&l===12?r-1:r;return Date.UTC(c,l-1,a,o,d)-je}const At=(e,t)=>e?.event_key||`${ee(e,t)}|${e?.home||""}|${e?.away||e?.match||""}`;function Lt(e,t,n){return ee(e,n)-ee(t,n)||Number(e.overround||99)-Number(t.overround||99)||Number(t.odds||0)-Number(e.odds||0)}function Et(e){const t=[e?.solo,...(e?.plans||[]).flatMap(i=>i.picks||[])].filter(Boolean),n=new Set;return t.filter(i=>{const l=`${At(i,e?.year)}|${i.market}|${i.market_label}|${i.sel}`;return n.has(l)?!1:(n.add(l),!0)})}const he=e=>Math.floor((e+je)/Ae),Dt=e=>(he(e)+1)*Ae-je;function Ns(e,t){if(!e)return{candidates:[],window:"today"};const n=e.candidates?.length?e.candidates:Et(e),i=ds(n).filter(d=>{const r=ee(d,e.year);return Number.isFinite(r)&&r>t}),l=he(t),a=i.filter(d=>he(ee(d,e.year))===l);if(a.length)return{candidates:a,window:"today"};const o=(l+1)*Ae-je+Ct*60*60*1e3;return{candidates:i.filter(d=>{const r=ee(d,e.year);return he(r)===l+1&&r0?n:Ze;if(!e)return i;const l=Ns(e,t).candidates.map(o=>ee(o,e.year)).sort((o,d)=>o-d)[0],a=Number.isFinite(l)?l:Dt(t);return Math.min(i,Math.max(1e3,a-t+1e3))}function Ot(e,t=Date.now()){if(!e)return{plans:[],solo:null,candidates:[],next:null};const n=Ns(e,t),i=n.candidates.sort((l,a)=>Lt(l,a,e.year));return{...e,plans:[],solo:null,recommendation:{action:"disabled",recommended_target:null,why:"자동 조합 추천을 사용하지 않는다"},candidates:i,window:n.window,next:i[0]||null,evolutionary_selector:Pt(e.evolutionary_selector,i)}}const Rt=10800*1e3;function It(...e){let t=null,n=-1/0;return e.flat().forEach(i=>{const l=Date.parse(i||"");Number.isFinite(l)&&l>n&&(t=i,n=l)}),t}function Fe(e,t=Date.now()){const n=Date.parse(e||"");return!Number.isFinite(n)||t-n>=Rt}function Bt({staticGeneratedAt:e,liveGeneratedAt:t,liveChecked:n,now:i=Date.now()}){const l=It(t,e);return!n&&Fe(e,i)?"checking":Fe(l,i)?"stale":"fresh"}function Kt(e,{generatedAt:t,year:n,now:i=Date.now()}={}){if(Fe(t,i))return"데이터 갱신 지연";const l=ee(e,n);return Number.isFinite(l)&&l<=i?"상태 확인 불가":"배당 발표 전"}const ke=e=>{const t=Number(e?.시장확률);return Number.isFinite(t)&&t>0&&t<1?t:null};function Ht(e=[]){const t=e.filter(r=>{const c=Number(r?.배당);return String(r?.market||"").trim()!=="홀짝"&&Number.isFinite(c)&&c>1&&ke(r)!==null});if(!t.length)return null;const n=t.filter(r=>["승패","승무패"].includes(String(r.market).trim())),l=[...n.length?n:t].sort((r,c)=>ke(c)-ke(r)||Number(r.배당)-Number(c.배당)||String(r.selection_id||"").localeCompare(String(c.selection_id||"")))[0],a=ks(t),o=a||l,d=a?$s(a):-1;return{option:o,probability:ws(o),recommendation:a?d===1?"recommend":"weak":"watch"}}function Gt(e){if(!e)return"";if(e.tie)return`가장 나은 선택지들이 같은 ${e.g?.bin||"배당"} 구간에 있어 한쪽을 고를 실측 근거가 없다.`;const t=e.mode==="roi"?"손실 최소 기준에서 과거 수익률이 가장 덜 나쁜 조합":"적중 우선 기준에서 가장 낮은 배당 구간",n=e.exact?`${e.o.market}·${e.g.bin}의 과거 실측`:`${e.g.bin} 배당 구간의 과거 실측`,i=e.hit==null?"":` 적중률 ${(e.hit*100).toFixed(1)}%,`,l=e.roi==null?"":` 수익률 ${(e.roi*100).toFixed(1)}%`;return`${e.o.선택} ${Number(e.o.배당).toFixed(2)}를 표시한 직접 이유는 ${t}이기 때문이다. ${n}은${i}${l}였다. 선수·날씨 정보는 아직 이 선택 점수에 넣지 않았다.`}function Ut(e){return e==="llm_rewritten"||e==="llm_cache"?"LLM 문장 편집":String(e||"").startsWith("template_")?"검증 템플릿 · LLM 대체 동작":e==="disabled_no_key"?"검증 템플릿 · LLM 키 미설정":"경기 해설"}const qt=e=>String(e||"").replace(/\s+/g,"").toLowerCase(),Wt=e=>[e?.sport,e?.league,e?.date,e?.home,e?.away].map(qt).join("|"),Ve=e=>[(e?.options||[]).filter(n=>Number(n?.배당)>1).length>0?1:0,e?.status==="배당대기"?0:1,Number(e?.round)||0],zt=(e,t)=>{const n=Ve(e),i=Ve(t);for(let l=0;li[l];return!1};function Yt(e=[]){const t=new Map;return e.forEach(n=>{const i=Wt(n),l=t.get(i);(!l||zt(n,l))&&t.set(i,n)}),[...t.values()]}function Je(e,t="",n=0){const i=(e.options||[]).filter(d=>!t||d.market===t),l=e.prediction_record,a=l&&(!t||l.market===t)?l:null;if(t&&!i.length&&!a)return!1;if(!n)return!0;const o=i.map(d=>Number(d.배당));return a&&o.push(Number(a.odds)),o.some(d=>Number.isFinite(d)&&d>1&&d<=n)}const Zt="https://proto-odds-collector.fly.dev/api/live-scores",Vt="https://proto-odds-collector.fly.dev/api/live-odds",Jt="https://proto-odds-collector.fly.dev/api/today-recommendations",Xt="https://proto-odds-collector.fly.dev/api/picks",Qt="https://proto-odds-collector.fly.dev/api/loss-grades";function fe(e,t){const[n,i]=C.useState({data:null,checked:!1});return C.useEffect(()=>{let l=!1;const a=()=>fetch(`${e}?${Date.now()}`,{cache:"no-store",headers:{"Cache-Control":"no-cache"}}).then(r=>r.ok?r.json():Promise.reject(new Error(`HTTP ${r.status}`))).then(r=>{l||i({data:r,checked:!0})}).catch(()=>{l||i(r=>({...r,checked:!0}))});a();const o=setInterval(a,t),d=()=>{document.visibilityState==="visible"&&a()};return document.addEventListener("visibilitychange",d),window.addEventListener("focus",a),window.addEventListener("online",a),()=>{l=!0,clearInterval(o),document.removeEventListener("visibilitychange",d),window.removeEventListener("focus",a),window.removeEventListener("online",a)}},[e,t]),n}const en=()=>fe(Zt,15e3),sn=()=>fe(Vt,12e4),Xe={bs:"득점",sc:"골",bk:"득점",vl:"세트"};function tn(e){if(e?.score_forecast)return e.score_forecast;if(!["경기전","배당대기"].includes(e?.status)||e?._liveStarted)return null;const t=Number(e?.lam_home),n=Number(e?.lam_away);return!Number.isFinite(t)||!Number.isFinite(n)?null:{status:"shadow",affects_probability:!1,expected_scores:{home:t,away:n,total:t+n,unit:Xe[e.sport]||"득점"},contract:{score_unit_label:Xe[e.sport]||"득점"},top_scorelines:[]}}function nn(e){const t=new Map;return(e?.games||[]).forEach(n=>{const i=[n.home,...n.home_alias||[]].filter(Boolean),l=[n.away,...n.away_alias||[]].filter(Boolean);i.forEach(a=>l.forEach(o=>t.set(`${a}|${o}|${n.md}`,n)))}),t}function ln(e,t){const n=Object.values(t?.markets?.[String(e?.round)]||{}).filter(l=>l?.home===e?.home&&l?.away===e?.away&&l?.date===e?.date),i=t?.history?.[String(e?.round)]||{};return n.flatMap(l=>(i[String(l.game_no)]||[]).map(a=>({...a,gameNo:String(l.game_no)})))}function Dn(){const{data:e,at:t}=Bs({grades:Qt},3e5),{grades:n}=e,{data:i,checked:l}=fe(Xt,6e4),a=i,{data:o,checked:d}=sn(),{data:r}=fe(Jt,12e4),{data:c,checked:m}=en(),x=c,h=C.useMemo(()=>nn(x),[x]),b=C.useMemo(()=>{if(!a)return null;const N=F=>(F||[]).map($=>{const w=rn(h,$),k=Ce($),v=w||k?$:Is($,o?.odds?.[String($.round)],o?.generated_at||null,o?.markets?.[String($.round)]),T=ln($,o),g={...v,_marketHistory:T,_officialMarkets:o?.markets?.[String($.round)]},P=k?{...g,_decisionFrozen:!0,_liveOddsChanged:!1}:g;return w?{...P,_liveState:w,_liveStarted:!0,_liveFeedAt:x?.generated_at||null}:P});return{...a,live:N(a.live),past:N(a.past)}},[a,o,h]);if(l&&!b)return s.jsx($e,{children:s.jsx(gs,{children:"데이터를 불러오지 못했습니다"})});if(!b)return s.jsx($e,{children:s.jsx(an,{})});const p=Bt({staticGeneratedAt:b.generated_at,liveGeneratedAt:o?.generated_at,liveChecked:d})==="stale";return s.jsx($e,{meta:cn(a,t),children:s.jsx("section",{id:"match-list",children:s.jsx(mn,{data:b,grades:n,caps:n?.odds_caps,stale:p,today:r,liveGeneratedAt:x?.generated_at,liveChecked:m})})})}function an(){const[e,t]=C.useState(0);C.useEffect(()=>{const i=Date.now(),l=setInterval(()=>t(Math.floor((Date.now()-i)/1e3)),1e3);return()=>clearInterval(l)},[]);const n=e>=12?"경기 데이터가 많아 정리하는 중입니다. 잠시만 기다려 주세요.":e>=4?"최신 배당과 저장된 사전 예측을 맞추고 있습니다.":"오늘 경기와 추천 픽을 불러오고 있습니다.";return s.jsxs("section",{className:"match-loading",role:"status","aria-live":"polite","aria-busy":"true",children:[s.jsxs("div",{className:"match-loading-head",children:[s.jsxs("div",{children:[s.jsx("span",{className:"loading-spinner","aria-hidden":"true"}),s.jsx("b",{children:"경기 데이터 불러오는 중"})]}),s.jsx("small",{children:e?`${e}초`:"연결 중"})]}),s.jsx("p",{children:n}),s.jsx("div",{className:"match-loading-grid","aria-hidden":"true",children:[0,1,2].map(i=>s.jsxs("div",{className:"match-loading-card",children:[s.jsx("i",{className:"loading-line is-short"}),s.jsx("i",{className:"loading-line is-title"}),s.jsxs("div",{children:[s.jsx("i",{className:"loading-chip"}),s.jsx("i",{className:"loading-chip"}),s.jsx("i",{className:"loading-chip"})]}),s.jsx("i",{className:"loading-line"}),s.jsx("i",{className:"loading-line is-medium"})]},i))})]})}function rn(e,t){const n=e.get(`${t.home}|${t.away}|${String(t.date||"").slice(0,5)}`);return!n||n.status==="BEFORE"?null:n}const on=new Intl.DateTimeFormat("ko-KR",{timeZone:"Asia/Seoul",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hourCycle:"h23"});function Qe(e){const t=new Date(e);if(!Number.isFinite(t.getTime()))return String(e||"-");const n=Object.fromEntries(on.formatToParts(t).map(({type:i,value:l})=>[i,l]));return`${n.year}-${n.month}-${n.day} ${n.hour}:${n.minute}`}const cn=(e,t)=>`${(e.live||[]).length+(e.past||[]).length}경기 · 회차 ${(e.rounds||[]).join(", ")} · 승부식 · 갱신 ${Qe(e.generated_at)} KST`+(t?` · 확인 ${Qe(t)} KST`:"");function $e({children:e,meta:t}){return s.jsxs("div",{className:"mx-auto max-w-[1180px] px-5 pb-20",children:[s.jsx(_s,{current:"markets.html"}),s.jsxs("header",{className:"market-header",children:[s.jsxs("div",{children:[s.jsx("h1",{children:"오늘 경기·배당 분석"}),s.jsx("p",{children:"오늘의 판단을 먼저 보고, 필요한 경기만 열어 흐름·선수·반대 근거를 확인합니다."})]}),t&&s.jsx("div",{className:"market-meta",children:t})]}),e]})}const gs=({children:e})=>s.jsx("div",{className:"py-7 text-center text-[13px] text-ink3",children:e});function dn(e){const[t,n]=C.useState(()=>Date.now());return C.useEffect(()=>{let i;const l=()=>{const o=Date.now();n(o),clearTimeout(i),i=setTimeout(l,Tt(e,o))},a=()=>{document.visibilityState==="visible"&&l()};return l(),document.addEventListener("visibilitychange",a),window.addEventListener("focus",l),window.addEventListener("pageshow",l),window.addEventListener("online",l),()=>{clearTimeout(i),document.removeEventListener("visibilitychange",a),window.removeEventListener("focus",l),window.removeEventListener("pageshow",l),window.removeEventListener("online",l)}},[e]),t}const un=[["","전체"],["live","진행 중"],["upcoming","예정"],["finished","종료"],["pending","결과 확인 중"]];function mn({data:e,grades:t,caps:n,stale:i,today:l,liveGeneratedAt:a,liveChecked:o=!1}){const[d,r]=C.useState(null),[c,m]=C.useState({st:"",lg:"",mk:"",rd:"",q:"",dt:"today"}),[x,h]=C.useState(()=>Date.now());C.useEffect(()=>{let u;const _=()=>h(Date.now()),M=()=>{clearTimeout(u),u=setTimeout(()=>{_(),M()},As())},y=()=>{document.visibilityState==="visible"&&(_(),M())};return M(),document.addEventListener("visibilitychange",y),window.addEventListener("focus",y),()=>{clearTimeout(u),document.removeEventListener("visibilitychange",y),window.removeEventListener("focus",y)}},[]);const[b,f]=C.useState(0),p=C.useMemo(()=>Yt([...e.live||[],...e.past||[]]),[e]),N=dn(l),F=C.useMemo(()=>Fs(l,p),[l,p]);C.useMemo(()=>i?{...F,plans:[],solo:null,candidates:[]}:Ot(F,N),[F,N,i]);const $=C.useMemo(()=>Ps(F),[F]),w=c.dt==="today"?ge(0,x):c.dt==="tomorrow"?ge(1,x):"",k=u=>[...new Set(u)].filter(_=>_!=null&&_!==""),v=C.useMemo(()=>k(p.map(u=>u.league)).sort(),[p]),T=C.useMemo(()=>k(p.flatMap(u=>[...(u.options||[]).map(_=>_.market),u.prediction_record?.market])).sort(),[p]),g=C.useMemo(()=>k(p.map(u=>u.round)).sort((u,_)=>_-u),[p]),P=C.useMemo(()=>{const u=c.q.trim().toLowerCase();return p.filter(_=>(!c.st||J(_)===c.st)&&(!c.lg||_.league===c.lg)&&(!c.rd||String(_.round)===c.rd)&&(!w||String(_.date??"").slice(0,5)===w)&&(!u||[_.home,_.away,_.league].join(" ").toLowerCase().includes(u))).sort((_,M)=>{const y={live:0,pending:1,upcoming:2,finished:3};return y[J(_)]-y[J(M)]||String(_.date).localeCompare(String(M.date))})},[p,c,w,N]),D=Date.parse(a),W=o&&(!Number.isFinite(D)||N-D>600*1e3||D>N+5e3)&&p.some(u=>String(u.date||"").slice(0,5)===ge(0,N)&&ae(u)!=null&&ae(u)<=N&&J(u)!=="finished"),S=p.filter(u=>{const _=c.q.trim().toLowerCase();return(!c.lg||u.league===c.lg)&&(!c.rd||String(u.round)===c.rd)&&(!w||String(u.date??"").slice(0,5)===w)&&(!_||[u.home,u.away,u.league].join(" ").toLowerCase().includes(_))&&Je(u,c.mk,b)}).reduce((u,_)=>{const M=J(_);return u[M]=(u[M]||0)+1,u},{}),E=u=>{m(_=>({..._,st:_.st===u?"":u}))},H=[];let L=null,G=0;for(const u of P){const _=(u.options||[]).filter(I=>!c.mk||I.market===c.mk),M=u.status==="배당대기";if(!Je(u,c.mk,b))continue;const y=M||i||u._liveOddsChanged&&!Ce(u)?{option:null,membership:null}:Cs($,u.options||[],u.round),O=y.membership?.recommended===!0;G++;const U=J(u),Y=`${U} · ${u.league} · ${Te(u.date)}`;if(Y!==L){L=Y;const I=Ms(u.date);H.push(s.jsxs("div",{className:"mt-[18px] mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold tracking-[.03em] text-ink3",children:[I&&s.jsx("span",{className:`rounded-[4px] px-[5px] py-[2px] text-[10px] ${I==="오늘"?"bg-ink text-paper":"border border-rule text-ink2"}`,children:I}),s.jsx("span",{className:`match-phase-tag is-${U}`,children:hs[U]}),s.jsxs("span",{children:[u.league," · ",Te(u.date)]})]},`h${Y}${G}`))}H.push(s.jsx(bn,{g:u,opts:_,wait:M,grades:t,lv:u._liveState||null,stale:i,generatedAt:e.generated_at,year:e.year,todayMembership:y.membership,todayOption:y.option,highlightedToday:O,onSaveBet:(I,se)=>r({game:I,option:se})},`${u.league}${u.home}${u.away}${u.date}${G}`))}const K=b?(n||[]).find(u=>u.cap===b):null;return s.jsxs(s.Fragment,{children:[s.jsx(Js,{games:[...e.live||[],...e.past||[]],today:l,currentToday:F,now:N}),s.jsxs("div",{className:"match-section-title",children:[s.jsx("h2",{children:"경기 목록"}),s.jsxs("div",{className:"match-phase-counts","aria-label":"경기 상태 필터",children:[s.jsxs("button",{type:"button",className:"is-live","aria-pressed":c.st==="live",onClick:()=>E("live"),children:["진행 중 ",S.live||0]}),s.jsxs("button",{type:"button","aria-pressed":c.st==="pending",onClick:()=>E("pending"),children:["결과 확인 중 ",S.pending||0]}),s.jsxs("button",{type:"button","aria-pressed":c.st==="upcoming",onClick:()=>E("upcoming"),children:["예정 ",S.upcoming||0]}),s.jsxs("button",{type:"button","aria-pressed":c.st==="finished",onClick:()=>E("finished"),children:["종료 ",S.finished||0]}),s.jsxs("button",{type:"button","aria-pressed":!c.st,onClick:()=>E(""),children:["전체 ",Object.values(S).reduce((u,_)=>u+_,0)]})]})]}),i&&s.jsxs("div",{className:"mb-5 rounded-lg border border-amber-300 bg-amber-50 px-5 py-4 text-amber-950",children:[s.jsx("b",{className:"text-[15px]",children:"데이터 갱신이 지연되고 있습니다"}),s.jsx("p",{className:"mt-1 text-[13px] leading-6",children:"마지막 생성 이후 3시간이 지나 경기 판정을 멈췄습니다. 수집이 복구되면 경기별 픽과 추천 강도가 자동으로 다시 표시됩니다."})]}),W&&s.jsxs("div",{role:"status",className:"mb-4 rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-[13px] text-amber-950",children:[s.jsx("b",{children:"실시간 점수 갱신 지연"}),s.jsx("p",{children:"경기와 저장된 사전 픽은 유지합니다. 최신 점수가 확인될 때까지 현재 추정 확률은 표시하지 않습니다."}),Number.isFinite(D)&&s.jsxs("small",{children:["마지막 수집: ",new Date(D).toLocaleString("ko-KR",{timeZone:"Asia/Seoul"})," (KST)"]})]}),s.jsxs("div",{className:"filter-shell",children:[s.jsxs("div",{className:"filter-primary",children:[s.jsxs("div",{className:"date-switch","aria-label":"경기 날짜",children:[s.jsx("button",{type:"button","aria-pressed":c.dt==="today",onClick:()=>m({...c,dt:"today"}),children:"오늘"}),s.jsx("button",{type:"button","aria-pressed":c.dt==="tomorrow",onClick:()=>m({...c,dt:"tomorrow"}),children:"내일"}),s.jsx("button",{type:"button","aria-pressed":!c.dt,onClick:()=>m({...c,dt:""}),children:"전체"})]}),s.jsx("div",{className:"team-search",children:s.jsx("input",{type:"search",placeholder:"팀 또는 리그 검색",value:c.q,onChange:u=>m({...c,q:u.target.value})})})]}),s.jsxs("details",{className:"advanced-filters",children:[s.jsxs("summary",{children:[s.jsx("span",{children:"상세 조건"}),s.jsx("small",{children:"상태 · 리그 · 마켓 · 회차 · 배당 기준"})]}),s.jsxs("div",{className:"filter-grid",children:[s.jsxs("label",{children:["상태",s.jsx("select",{className:"filter-select",value:c.st,onChange:u=>m({...c,st:u.target.value}),children:un.map(([u,_])=>s.jsx("option",{value:u,children:_},_))})]}),s.jsx(we,{label:"리그",v:c.lg,opts:v,on:u=>m({...c,lg:u}),cls:"filter-select"}),s.jsx(we,{label:"마켓",v:c.mk,opts:T,on:u=>m({...c,mk:u}),cls:"filter-select"}),s.jsx(we,{label:"회차",v:c.rd,opts:g,on:u=>m({...c,rd:u}),cls:"filter-select",suffix:"회차"}),s.jsxs("label",{children:["최저배당",s.jsxs("select",{className:"filter-select",value:b,onChange:u=>f(Number(u.target.value)),children:[s.jsx("option",{value:0,children:"제한 없음"}),(n||[]).filter(u=>u.cap).map(u=>s.jsxs("option",{value:u.cap,children:["≤",u.cap," · 적중 ",(u.hit*100).toFixed(0),"%"]},u.cap))]})]})]}),s.jsx("div",{className:"filter-actions",children:s.jsx("button",{type:"button",onClick:()=>{m({st:"",lg:"",mk:"",rd:"",q:"",dt:"today"}),f(0)},children:"조건 초기화"})})]})]}),K&&s.jsxs("p",{className:"mt-2 text-[11.5px] leading-[1.7] text-ink3",children:["최저배당 ≤",K.cap," 인 경기만 산 과거 실측 — 적중 ",s.jsxs("b",{className:"tnum text-ink",children:[(K.hit*100).toFixed(1),"%"]})," · ROI ",s.jsxs("b",{className:"tnum",children:[(K.roi*100).toFixed(2),"%"]})," ·",s.jsxs("span",{className:"opacity-70",children:[" ","(전체 경기의 ",(K.share*100).toFixed(0),"% · n=",K.n.toLocaleString(),")"]})]}),s.jsx("div",{children:H.length?H:s.jsxs(gs,{children:["조건에 맞는 경기가 없다",c.dt&&s.jsxs(s.Fragment,{children:[" — ",s.jsx("button",{className:"underline underline-offset-2",onClick:()=>m({...c,dt:""}),children:"모든 날짜 보기"})]})]})}),d&&s.jsx(Xs,{draft:d,onClose:()=>r(null)})]})}const we=({label:e,v:t,opts:n,on:i,cls:l,suffix:a=""})=>s.jsxs("label",{className:"flex items-center gap-1.5",children:[e,s.jsxs("select",{className:l,value:t,onChange:o=>i(o.target.value),children:[s.jsx("option",{value:"",children:"전체"}),n.map(o=>s.jsxs("option",{value:o,children:[o,a]},o))]})]});function hn({live:e}){const t=e?.bases||{},n=[e?.balls,e?.strikes,e?.outs].every(l=>l!=null),i=[t.first,t.second,t.third].filter(l=>l?.occupied);return s.jsxs("div",{className:"live-baseball-situation","aria-label":"현재 야구 경기 상황",children:[s.jsx("div",{className:"base-state","aria-label":i.length?`주자 ${i.length}명`:"주자 없음",children:[["third","3루"],["second","2루"],["first","1루"]].map(([l,a])=>s.jsx("span",{className:t[l]?.occupied?"is-occupied":"",title:t[l]?.runner||`${a} 주자 없음`,children:a},l))}),s.jsxs("div",{children:[s.jsx("small",{children:"현재 타자"}),s.jsx("b",{children:e?.batter||"확인 중"}),n&&s.jsxs("span",{children:["B ",e.balls," · S ",e.strikes," · O ",e.outs]})]}),s.jsxs("div",{children:[s.jsx("small",{children:"현재 투수"}),s.jsx("b",{children:e?.pitcher||"확인 중"}),e?.next_batter&&s.jsxs("span",{children:["다음 ",e.next_batter]})]})]})}const xn=e=>{const t=new Date(e);return Number.isNaN(t.getTime())?"시각 미상":t.toLocaleString("ko-KR",{timeZone:"Asia/Seoul",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})};function pn({rows:e}){if(!e?.length)return null;const n=[...e.reduce((i,l)=>{const a=`${l.gameNo}|${l.market}`;return i.has(a)||i.set(a,[]),i.get(a).push(l),i},new Map).entries()].map(([i,l])=>({key:i,entries:l})).filter(({entries:i})=>i.length>1);return n.length?s.jsxs("details",{className:"mb-3 rounded border border-rule2 bg-panel px-3 py-2 text-[12px]",children:[s.jsx("summary",{className:"cursor-pointer font-semibold text-ink",children:"배당·기준점 변경 이력"}),s.jsxs("div",{className:"mt-2 space-y-3",children:[n.map(({key:i,entries:l})=>s.jsxs("div",{children:[s.jsxs("b",{children:[l[0].market," · 게임번호 ",l[0].gameNo]}),s.jsx("ol",{className:"mt-1 space-y-1 text-ink2",children:l.map((a,o)=>{const d=l[o-1],r=a.probabilities?.map((m,x)=>d?.probabilities?.[x]==null?null:(Number(m)-Number(d.probabilities[x]))*100),c=d?.line==null||a.line==null?null:Number(a.line)-Number(d.line);return s.jsxs("li",{className:"border-t border-rule2 pt-1 first:border-0",children:[s.jsx("span",{className:"tnum",children:xn(a.observed_at)})," · ",s.jsx("strong",{children:a.label||"기준점 없음"}),c?s.jsxs("em",{className:"ml-1 not-italic text-amber-700",children:["(기준점 ",c>0?"+":"",c,")"]}):null," · 배당 ",(a.odds||[]).map(m=>Number(m).toFixed(2)).join(" / "),a.probabilities?.length?s.jsxs("span",{children:[" · 예상 적중 ",a.probabilities.map((m,x)=>{const h=r?.[x];return`${Math.round(Number(m)*1e3)/10}%${h==null||Math.abs(h)<.01?"":` (${h>0?"+":""}${h.toFixed(1)}%p)`}`}).join(" / ")]}):null]},`${a.observed_at}-${o}`)})})]},i)),s.jsx("p",{className:"text-[10.5px] leading-5 text-ink3",children:"예상 적중은 해당 시점 배당에서 마진을 제거한 값입니다. 기준점이 바뀌면 판정 대상 자체가 달라지므로 확률 숫자만 단순 비교하지 않습니다."})]})]}):null}function bn({g:e,opts:t,wait:n,grades:i,lv:l,stale:a,generatedAt:o,year:d,todayMembership:r,todayOption:c,highlightedToday:m,onSaveBet:x}){const h=C.useMemo(()=>{const Ne={},Ee={};return t.forEach(re=>{const te=re.market+(re.label||"");(Ne[te]=Ne[te]||[]).push(xe(i,re.배당)?.grade)}),Object.entries(Ne).forEach(([re,te])=>{te.length===2&&te[0]&&te[0]===te[1]&&(Ee[re]=!0)}),Ee},[t,i]),b=e.status==="정산",f=e._liveStarted===!0,p=J(e,l),N=ae(e),F=f||p!=="upcoming"||N!=null&&N<=Date.now(),$=F||Ce(e),w=Me(e,l),k=!e.prediction_record&&(b||e.prediction_status==="prediction_ledger_required"),v=n||a||k||e._liveOddsChanged?null:Ht(t),T=$&&w?.option||(F?null:c||v?.option)||null,g=T?{o:T,g:xe(i,T.배당),tie:!1}:null,P=p==="live",D=p==="finished",W=l?.cancelled?"경기 취소":l?.postponed?"경기 연기":null,S=ms(e,l),E=!P&&!D&&!b&&e.pick_drift&&e.pick_drift.market_selection&&e.pick_drift.market_selection!==e.pick_drift.pinned_selection?e.pick_drift:null,H=n?Kt(e,{generatedAt:o,year:d}):null,L=b&&e.score||S.source==="score"&&e.sport==="sc"&&l?.regular_time_score||(l&&l.home_score!=null&&l.away_score!=null?[l.home_score,l.away_score]:null),G=n||a||k||F?null:$t(e,g?.o||null,fs(e)),K=G?.decision||us(e,g?.o||null),u=G?.prediction,_=W||(F&&!w?"사전 예측 기록 없음":e._liveOddsChanged?"배당 변경 · 재계산 대기":a?"최신 데이터 확인 필요":k?e.status==="결과확인"?"정산 결과 확인 필요":"사전 예측 기록 없음":H==="상태 확인 불가"?"경기 상태 확인 필요":n?"배당 발표 전":"분석 자료 확인 중"),M=w?.openingProbability??null,y=P?w?.estimate:null,O=mt(G?.teamPreviews),U=e._liveOddsChanged?"재계산":a?"중단":"보류",Y=n&&(f||H==="상태 확인 불가")?{...e,해설:null,해설기본:null}:e,I=S.record?`${S.record.market}${S.record.label?` ${S.record.label}`:""} ${S.record.selection}`:null,se=r?.recommended?"오늘의 추천 픽":null,le=r?.display?.text||null,R=r?.reason||null,Le=r?.counterReason||null;return s.jsxs(Pe,{as:"details",className:`match-card is-${p} result-${S.state}`,children:[s.jsxs("summary",{className:"match-row",children:[s.jsx("span",{className:"tnum text-[11.5px] text-ink3",children:Ls(e.date)}),s.jsxs("span",{className:"min-w-0 text-[13.5px] font-semibold",children:[s.jsxs("span",{children:[e.home," ",L?s.jsxs("span",{className:"tnum text-[13px]",title:e.결과||"",children:[s.jsx("b",{className:L[0]>L[1]?"":"font-normal text-ink3",children:L[0]}),s.jsx("span",{className:"px-[3px] font-normal text-ink3",children:":"}),s.jsx("b",{className:L[1]>L[0]?"":"font-normal text-ink3",children:L[1]})]}):s.jsx("span",{className:"text-[12px] font-normal text-ink3",children:"vs"})," ",e.away]}),s.jsxs("small",{className:"match-player-inline",children:[W||(P?`LIVE · ${l.status_text||"진행 중"}`:b||D?`종료 · ${S.label}`:p==="pending"?"결과 확인 중":n?H:a?"데이터 갱신 지연":"예정")," · ",e.round,"회차",e._liveLineChanged?" · 기준점 변경 반영":"",E?" · 라인 변동":"",O?` · ${O}`:""]})]}),s.jsxs("span",{className:"match-call-inline",children:[s.jsx("small",{children:p==="finished"?"예측 결과":P?"실시간 경기":p==="pending"?"사전 픽":se?"오늘 추천 픽":"경기별 픽"}),s.jsx("b",{children:p==="finished"?`${S.label}${I?` · ${I}`:""}`:g?`${g.o.market}${g.o.label?` ${g.o.label}`:""} ${g.o.선택}${se?` · ${se}`:""}`:u?.headline||_})]}),s.jsx("span",{className:"flex gap-1.5",children:P?s.jsxs(s.Fragment,{children:[s.jsxs("span",{className:"live-score-badge",children:[s.jsx("i",{}),"LIVE ",s.jsx("b",{children:l.status_text||"진행 중"})]}),Number.isFinite(M)&&s.jsx(Z,{label:"사전 확률",value:X(M),title:"경기 시작 전에 저장된 픽의 고정 확률"}),Number.isFinite(y?.probability)&&s.jsx(Z,{label:"현재 추정",value:X(y.probability),title:"사전 확률에 현재 점수와 남은 시간을 반영한 상황 추정치"})]}):W?s.jsx(Z,{label:"상태",value:W.replace("경기 ","")}):p==="finished"?s.jsxs("span",{className:"flex flex-col items-end gap-1",children:[s.jsx("span",{className:`result-badge is-${S.state}`,children:S.label}),Number(S.record?.odds)>1&&s.jsxs("small",{className:"tnum text-[11px] text-ink3",children:["당시 배당 ",V(S.record.odds),"배"]})]}):p==="pending"?s.jsx(Z,{label:"상태",value:"확인 중"}):f?s.jsx(Z,{label:"판정",value:"마감"}):n?s.jsx(Z,{label:"배당",value:a?"갱신 지연":H==="상태 확인 불가"?"확인 불가":"발표 전"}):g?s.jsx(Z,{label:g.o.선택,value:V(g.o.배당),grade:g.g?xs(g.g.grade):"U",highlighted:m,title:`${g.o.market}${g.o.label?` ${g.o.label}`:""} · ${v?.recommendation==="recommend"?"검증 보정이 실제 반영된 추천":v?.recommendation==="weak"?"방향은 제시하되 구매 우위가 약한 픽":v?.recommendation==="market"?"시장 최유력 방향이며 구매 추천은 아님":"시장 최유력 방향은 제시하되 구매 추천은 관망"}${le?` · ${le}`:" · 배당 기반 시장확률"}`}):s.jsx(Z,{label:"판정",value:U})})]}),s.jsxs("div",{className:"match-detail",children:[$&&w&&!D&&s.jsxs("div",{className:"mb-3 rounded border border-rule2 bg-panel px-3 py-2 text-[12px]","aria-label":"저장된 사전 예측",children:[s.jsxs("b",{children:["경기 전 예측 픽 · ",w.option.market," ",w.option.label," ",w.option.선택]}),s.jsxs("p",{children:["사전 확률 ",Number.isFinite(M)?X(M):"기록 없음"," · 당시 배당 ",V(w.option.배당)]}),P&&!y&&s.jsxs("small",{children:["현재 추정 확률은 ",w.estimateStatus==="unsupported_market"?"이 마켓에서 제공하지 않습니다.":"사전 확률과 최신 점수 자료가 모두 확인되어야 표시됩니다."]})]}),s.jsx(pn,{rows:e._marketHistory}),r&&R&&s.jsxs("div",{className:`today-pick-signals ${m?"is-recommended":"is-excluded"}`,role:"note",children:[s.jsx("b",{children:m?"오늘 추천한 이유":"오늘 추천하지 않은 이유"}),s.jsx("span",{children:R}),le&&s.jsx("small",{children:le}),Le&&s.jsx("small",{children:Le})]}),e._liveLineChanged&&s.jsxs("div",{className:"mb-3 rounded border border-amber-400 bg-amber-50 px-3 py-2 text-[12px] leading-6 text-amber-950",role:"status",children:[s.jsx("b",{children:"핸디캡·언더오버 기준점 변경 반영"}),s.jsx("p",{children:"이전 기준점의 예측과 구조 모델 수치는 폐기했습니다. 현재 기준점과 배당으로 확률 및 추천을 다시 계산했습니다."})]}),E&&s.jsxs("div",{className:"mb-3 rounded border border-amber-400 bg-amber-50 px-3 py-2 text-[12px] leading-6 text-amber-950",role:"status",children:[s.jsx("b",{children:"라인 변동 · 픽은 처음 게시값으로 고정"}),s.jsxs("p",{children:["이 경기의 픽은 ",s.jsxs("b",{children:[E.pinned_market," ",E.pinned_selection]}),E.pinned_odds!=null?` (배당 ${V(E.pinned_odds)})`:"","로 고정되어 킥오프까지 바뀌지 않습니다. 지금 시장 기준으로는"," ",s.jsx("b",{children:E.market_selection}),E.market_odds!=null?` (배당 ${V(E.market_odds)})`:"","가 더 유력합니다. 이미 배팅하셨다면 조건이 바뀐 점만 참고하세요."]})]}),P&&L&&s.jsxs("div",{className:"live-score-panel",role:"status","aria-live":"polite",children:[s.jsxs("div",{children:[s.jsx("span",{children:"LIVE"}),s.jsx("b",{children:l.status_text||"진행 중"})]}),s.jsxs("strong",{children:[e.home," ",s.jsx("em",{children:L[0]}),s.jsx("i",{children:":"}),s.jsx("em",{children:L[1]})," ",e.away]}),s.jsxs("small",{children:[Number.isFinite(y?.probability)?s.jsxs(s.Fragment,{children:["사전 적중 ",s.jsx("b",{children:X(M)})," → 현재 상황 추정 ",s.jsx("b",{children:X(y.probability)}),s.jsx("br",{})]}):null,"점수와 남은 시간으로 이동한 상황 추정치이며 검증된 인플레이 구매 추천은 아닙니다."]}),e.sport==="bs"&&s.jsx(hn,{live:l})]}),p==="finished"&&s.jsxs("div",{className:`settled-result-panel is-${S.state}`,children:[s.jsxs("div",{children:[s.jsxs("span",{children:["최종 결과",S.source==="score"&&e.sport==="sc"?" · 정규시간":""]}),s.jsxs("strong",{children:[e.home," ",L?`${L[0]} : ${L[1]}`:"– : –"," ",e.away]})]}),s.jsxs("div",{children:[s.jsxs("span",{children:["사전 추천 판정",S.source==="score"?" · 종료 점수 기준":""]}),s.jsx("strong",{children:S.label}),I&&s.jsxs("small",{children:[I," · 배당 ",V(S.record.odds)]})]}),S.state==="unrecorded"&&s.jsx("p",{children:"이 경기의 사전 픽이 저장되어 있지 않아 적중 여부를 판정할 수 없습니다."}),S.source==="score"&&s.jsx("p",{children:"경기 종료 점수와 저장된 픽의 기준점으로 판정했습니다. 공식 정산이 들어오면 그 결과를 우선합니다."})]}),g&&K.recommendationPriority==="reversal"&&s.jsxs("div",{className:"mb-3 rounded border border-dashed border-sev2 bg-panel px-3 py-2 text-[12px] leading-6 text-ink2",children:[s.jsxs("b",{className:"text-ink",children:["최종 픽 전환 · ",g.o.market,g.o.label?` ${g.o.label}`:""," ",g.o.선택]})," · ",s.jsx("span",{className:"tnum",children:V(g.o.배당)})," · 시장 ",s.jsx("span",{className:"tnum",children:X(g.o.시장확률)})," · 구조 모델 차이 ",s.jsxs("span",{className:"tnum",children:[ps(Number(g.o.모델확률)-Number(g.o.시장확률)),"p"]}),s.jsx("p",{className:"text-[10.5px] leading-5 text-ink3",children:"전환 관문을 통과해 기존 정배를 제거하고 이 선택 하나로 교체했습니다. 표시 확률은 해당 배당에서 마진을 제거한 시장확률입니다."})]}),n&&s.jsx("div",{className:"rounded-[2px] border border-dashed border-rule px-2.5 py-2 text-[12px] text-ink3",children:f?"실시간 중계에서 경기 시작을 확인해 사전 판정과 구매 후보를 마감했습니다.":a?"오래된 데이터로는 배당 발표 여부를 판단하지 않습니다. 최신 수집이 확인될 때까지 기다려 주세요.":H==="상태 확인 불가"?"경기 시작 시각이 지났지만 최신 상태를 확인하지 못했다.":"배당은 아직 발표되지 않았다. 경기 정보는 먼저 확인할 수 있다."}),k&&s.jsx("div",{className:"rounded-[2px] border border-dashed border-rule px-2.5 py-2 text-[12px] text-ink3",children:"경기 전에 저장된 예측 원장이 없어 현재 공식으로 과거 추천을 재구성하지 않습니다."}),s.jsx(Sn,{g:Y,analysis:G,decision:k?null:K,opts:t,grades:i,tie:h,pick:g,highlightedToday:m,recalculating:e._liveOddsChanged===!0,showPrices:!n,onSaveBet:x})]})]})}function fn({g:e,opts:t,grades:n,tie:i,pick:l,highlightedToday:a=!1,recalculating:o=!1,onSaveBet:d}){const r="border-b border-rule2 pb-[5px] pr-2 text-left text-[11px] font-medium text-ink3",c="border-b border-rule2 py-[5px] pr-2 align-baseline";return s.jsxs("table",{className:"w-full border-collapse text-[12.5px]",children:[s.jsx("caption",{className:"sr-only",children:"프로토 배당, 배당 기반 시장확률, 검증 전 구조 AI 수치와 반영 상태"}),s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{scope:"col",className:`${r} text-right`,children:"번호"}),s.jsx("th",{scope:"col",className:r,children:"마켓 / 선택"}),s.jsx("th",{scope:"col",className:`${r} text-right`,children:"배당"}),s.jsx("th",{scope:"col",className:`${r} text-right`,children:"배당구간 적중"}),s.jsx("th",{scope:"col",className:`${r} model-col text-right`,children:"배당 기반 확률"}),s.jsx("th",{scope:"col",className:`${r} model-col text-right`,children:"구조 AI"}),s.jsx("th",{scope:"col",className:`${r} model-col text-right`,children:"시장과 차이"}),s.jsx("th",{scope:"col",className:r,children:"판정"}),s.jsx("th",{scope:"col",className:`${r} text-right`,children:"내 베팅"})]})}),s.jsx("tbody",{children:t.map((m,x)=>{const h=xe(n,m.배당),b=i[m.market+(m.label||"")];return s.jsxs("tr",{className:a&&l?.o===m?"is-today-pick":"",children:[s.jsx("td",{className:`${c} tnum text-right text-ink3 whitespace-nowrap`,children:m.게임번호||"–"}),s.jsxs("td",{className:c,children:[h&&s.jsx(vs,{grade:b?"T":xs(h.grade),title:b?`양쪽이 같은 배당대(${h.bin}) — 선택 보류`:`배당 ${h.bin} 실측 ${(h.roi*100).toFixed(1)}%`}),m.market,m.label?` ${m.label}`:""," · ",m.선택,m.적중===!0?" ✔":m.적중===!1?" ✕":""]}),s.jsxs("td",{className:`${c} tnum text-right`,children:[V(m.배당),m._live&&s.jsx("span",{className:"ml-1 text-[9.5px] text-ink3","aria-label":"실시간 배당으로 확률 재계산 완료",children:"실시간"})]}),s.jsx("td",{className:`${c} tnum text-right text-ink3`,children:h?.hit!=null?`${(h.hit*100).toFixed(0)}%`:"–"}),s.jsx("td",{className:`${c} model-col tnum text-right`,children:o?"–":X(m.시장확률)}),s.jsx("td",{className:`${c} model-col tnum text-right`,title:m.모델확률==null?"이 종목·마켓은 검증된 구조 모델 확률이 아직 없습니다.":"구조 모델이 산출한 연구 확률",children:o?"–":m.모델확률==null?"미산출":X(m.모델확률)}),s.jsx("td",{className:`${c} model-col tnum text-right`,title:m.AI잔차==null?"구조 AI 확률이 없어 시장과의 차이를 계산하지 않습니다.":"구조 AI 확률 − 시장확률",children:o||m.AI잔차==null?"–":ps(m.AI잔차)}),s.jsxs("td",{className:`${c} text-[11.5px]`,children:[o&&s.jsx("span",{className:"text-ink3",children:"재계산 대기"}),l&&!l.tie&&l.o===m&&s.jsx("span",{className:"font-semibold text-signal",children:a?"★ 오늘의 베팅 추천":"경기 예측 픽"}),l&&l.tie&&xe(n,m.배당)?.grade===l.g.grade&&s.jsx("span",{className:"text-ink3",children:"동률 — 고를 근거 없음"}),!o&&(!l||!l.tie&&l.o!==m)&&s.jsx("span",{className:"text-ink3",children:m.모델확률==null?"시장 참고":"비추천"})]}),s.jsx("td",{className:`${c} text-right`,children:s.jsx("button",{type:"button",className:"bet-record-button",disabled:o,onClick:()=>d?.(e,m),children:"베팅 기록"})})]},x)})})]})}const jn="intel-tab";function Nn({source:e}){if(!e)return null;let t="";if(e.updatedAt){const i=new Date(e.updatedAt);Number.isNaN(i.getTime())||(t=i.toLocaleString("ko-KR",{timeZone:"Asia/Seoul",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}))}const n=e.url?s.jsx("a",{className:"underline decoration-rule underline-offset-2 hover:text-ink",href:e.url,target:"_blank",rel:"noreferrer",children:e.name}):e.name;return s.jsxs("div",{className:"source-stamp",children:["출처 ",n,t?` · KST ${t} 확인`:""]})}function es({team:e,pitcher:t}){const n=st(t);return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("div",{className:"text-[11px] text-ink3",children:e}),t?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"mt-0.5 flex flex-wrap items-baseline gap-1.5",children:[s.jsx("b",{className:"text-[13px] text-ink",children:t.name}),t.stats?.period&&s.jsx("span",{className:"text-[10.5px] text-ink3",children:t.stats.period}),t.stats?.low_sample&&s.jsx("span",{className:"rounded border border-dashed border-rule px-1 text-[9.5px] text-ink3",children:"표본 적음"})]}),n.length?s.jsx("div",{className:"mt-2 grid grid-cols-4 gap-x-2 gap-y-1.5 sm:grid-cols-8",children:n.map(([i,l])=>s.jsxs("div",{children:[s.jsx("div",{className:"text-[9.5px] text-ink3",children:i}),s.jsx("div",{className:"tnum text-[12px] font-semibold text-ink",children:l})]},i))}):s.jsx("p",{className:"mt-1 text-[11px] text-ink3",children:"선발 이름은 확인됐지만 개인 지표 자료는 아직 없다."}),t.stats?.fip_approx&&s.jsx("div",{className:"mt-1 text-[9.5px] text-ink3",children:"* FIP는 사구 자료가 없어 사구를 제외한 근사치다."}),t.stats_source&&s.jsx("div",{className:"mt-1.5 text-[10px] text-ink3",children:t.stats_source})]}):s.jsx("p",{className:"mt-1 text-[11.5px] text-ink3",children:"아직 선발이 발표되지 않았다."})]})}function ss({team:e,profile:t}){return t?s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 과거 라인업 성향"]}),s.jsxs("div",{className:"mt-1 text-[11.5px] leading-[1.7]",children:["주 포메이션 ",s.jsx("b",{children:t.formation||"자료 없음"}),t.churn!=null&&s.jsxs(s.Fragment,{children:[" · 직전 경기 대비 선발 교체 평균 ",s.jsxs("b",{className:"tnum",children:[t.churn,"명"]})]}),t.reserve!=null&&s.jsxs(s.Fragment,{children:[" · 비주전 선발 평균 ",s.jsxs("b",{className:"tnum",children:[t.reserve,"명"]})]})]}),s.jsxs("p",{className:"mt-1 text-[10.5px] text-ink3",children:["실제 오늘 선발 명단이 아니라 과거 ",t.n||"","경기의 팀 성향이다."]})]}):null}function ts({team:e,players:t}){return t?.length?s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 발표 라인업"]}),s.jsx("ol",{className:"mt-1 grid grid-cols-2 gap-x-2 text-[11px] leading-[1.7]",children:t.map((n,i)=>s.jsxs("li",{className:"truncate",children:[s.jsx("span",{className:"tnum mr-1 text-ink3",children:n.order||i+1}),s.jsx("b",{className:"font-medium text-ink",children:n.name}),n.position&&s.jsx("span",{className:"ml-1 text-[9.5px] text-ink3",children:n.position}),n.stats&&s.jsxs("span",{className:"ml-1 text-[9.5px] text-ink3",children:["· ",n.stats.apps??"-","경기 ",n.stats.goals??0,"골 ",n.stats.assists??0,"도움"]})]},`${n.name}-${i}`))})]}):null}function ns(e){const t=Number(e);return Number.isFinite(t)?t.toFixed(3).replace(/^0/,""):"-"}function is({team:e,players:t,referenceDate:n,lineupState:i}){if(!t?.length)return null;const l=i==="official_today",a=l?`${n?`${n.replaceAll("-",".")} · `:""}오늘 공식 선발 타순`:n?`${n.replaceAll("-",".")} 공식 선발 타순 기준 예상`:"예상 라인업";return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · ",a]}),s.jsx("ol",{className:"mt-1 space-y-1.5",children:t.map((o,d)=>{const r=o.stats||null,c=o.last_game||null;return s.jsxs("li",{className:"text-[11px] leading-[1.45]",children:[s.jsxs("div",{className:"flex items-baseline gap-1",children:[s.jsx("span",{className:"tnum w-3 text-ink3",children:o.order||d+1}),o.profile_url?s.jsx("a",{href:o.profile_url,target:"_blank",rel:"noreferrer",className:"font-medium text-ink underline decoration-rule2 underline-offset-2 hover:decoration-ink",children:o.name}):s.jsx("b",{className:"font-medium text-ink",children:o.name}),o.position&&s.jsx("span",{className:"text-[9.5px] text-ink3",children:o.position})]}),r&&s.jsxs("div",{className:"tnum ml-4 text-[9.5px] text-ink3",children:["시즌 타율 ",ns(r.avg)," · ",r.home_runs??0,"홈런 · ",r.rbi??0,"타점 · OPS ",ns(r.ops)]}),!l&&c&&s.jsxs("div",{className:"tnum ml-4 text-[9.5px] text-ink3",children:["기준 경기 ",c.at_bats??"-","타수 ",c.hits??"-","안타 · ",c.rbi??0,"타점"]})]},`${o.player_id||o.name}-${d}`)})})]})}function ls({team:e,players:t}){return t?.length?s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 현재 시즌 핵심 선수"]}),s.jsx("ul",{className:"mt-1 space-y-1.5",children:t.slice(0,5).map((n,i)=>s.jsxs("li",{className:"text-[11px]",children:[s.jsxs("div",{className:"flex items-baseline gap-1",children:[s.jsx("b",{className:"text-ink",children:n.name}),n.position&&s.jsx("span",{className:"text-[9.5px] text-ink3",children:n.position})]}),s.jsxs("div",{className:"tnum text-[10px] text-ink3",children:[n.apps!=null&&s.jsxs("span",{children:[n.apps,"경기"]}),n.starts!=null&&s.jsxs("span",{children:[" · 선발 ",n.starts]}),n.goals!=null&&s.jsxs("span",{children:[" · ",n.goals,"골"]}),n.assists!=null&&s.jsxs("span",{children:[" · ",n.assists,"도움"]}),n.xg!=null&&s.jsxs("span",{children:[" · xG ",n.xg]}),n.xa!=null&&s.jsxs("span",{children:[" · xA ",n.xa]})]})]},`${n.player_id||n.name}-${i}`))}),s.jsx("p",{className:"mt-1.5 text-[9.5px] text-ink3",children:"실제 오늘 선발이 아니라 공식 시즌 기록의 공격 기여 상위 선수다."})]}):null}function as({team:e,players:t}){return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 등록/최근 대표 명단"]}),t?.length?s.jsx("ul",{className:"mt-1 grid grid-cols-2 gap-x-2 text-[11px] leading-[1.7]",children:t.map((n,i)=>s.jsxs("li",{className:"truncate",children:[n.number!=null&&n.number!==""&&s.jsxs("span",{className:"tnum mr-1 text-ink3",children:["#",n.number]}),s.jsx("b",{className:"font-medium text-ink",children:n.name}),n.position&&s.jsx("span",{className:"ml-1 text-[9.5px] text-ink3",children:n.position}),n.club&&s.jsxs("span",{className:"ml-1 text-[9.5px] text-ink3",children:["· ",n.club]})]},`${n.player_id||n.name}-${i}`))}):s.jsx("p",{className:"mt-1 text-[11px] text-ink3",children:"이 팀의 최종 명단은 아직 공개되지 않았다."})]})}function rs({team:e,players:t,sport:n}){return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 공식 기록 상위 선수"]}),t?.length?s.jsx("ul",{className:"mt-1 space-y-1.5",children:t.slice(0,5).map((i,l)=>{const a=n==="bk"?[["경기",i.games],["득점",i.points],["리바운드",i.rebounds],["도움",i.assists],["효율",i.efficiency],["3P%",i.three_pct]]:[["순위",i.rank],["득점",i.points],["공격",i.attacks],["블로킹",i.blocks],["서브",i.serves],["공격%",i.attack_pct]];return s.jsxs("li",{className:"text-[11px]",children:[s.jsxs("div",{className:"flex items-baseline gap-1",children:[s.jsx("b",{className:"text-ink",children:i.name}),i.position&&s.jsx("span",{className:"text-[9.5px] text-ink3",children:i.position})]}),s.jsx("div",{className:"tnum text-[10px] text-ink3",children:a.filter(([,o])=>o!=null).map(([o,d],r)=>s.jsxs("span",{children:[r?" · ":"",o," ",d]},o))})]},`${i.player_id||i.name}-${l}`)})}):s.jsx("p",{className:"mt-1 text-[11px] text-ink3",children:"이 팀의 선수 기록 자료가 아직 연결되지 않았다."})]})}function gn({g:e}){const t=e.선발||{},n=t.rosters||{},i=t.key_players||{},l=!!(n.home?.length||n.away?.length),a=!!(i.home?.length||i.away?.length);return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"mb-2 rounded-[7px] border border-rule2 bg-paper2 px-2.5 py-2 text-[10.5px] leading-[1.6] text-ink3",children:[s.jsx("b",{className:"text-ink",children:t.roster_status?.label||t.coverage?.label||"공식 선수 자료원 미연결"}),t.roster_status?.label&&t.coverage?.label&&s.jsxs("span",{children:[" · ",t.coverage.label]})]}),l&&s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 text-[10.5px] font-semibold text-ink3",children:"선수 명단"}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(as,{team:e.home,players:n.home}),s.jsx(as,{team:e.away,players:n.away})]})]}),a&&s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 mt-2 text-[10.5px] font-semibold text-ink3",children:"선수 기록"}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(rs,{team:e.home,players:i.home,sport:e.sport}),s.jsx(rs,{team:e.away,players:i.away,sport:e.sport})]})]}),!l&&!a&&s.jsx("p",{className:"text-[11.5px] text-ink3",children:t.coverage?.label||"이 대회의 공식 선수·명단 자료 경로가 아직 연결되지 않았다."}),s.jsx("p",{className:"mt-2 text-[9.5px] leading-[1.6] text-ink3",children:"등록 명단과 최근 대표 명단은 실제 당일 선발·출전 확정이나 부상 보고서가 아니다."})]})}function _n({g:e}){if(e.sport==="bs"){const x=e.선발||{},h=x.lineups||{},b=x.lineup_status||{},f=b.side_states||{},p=b.official_today!==!0,N=b.reference_dates||{};return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(es,{team:e.home,pitcher:pe(e,"home")}),s.jsx(es,{team:e.away,pitcher:pe(e,"away")})]}),h.home?.length||h.away?.length?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 mt-2 text-[10.5px] font-semibold text-ink3",children:b.label||(p?"최근 공식 경기 기반 예상 타순":"오늘 공식 선발 타순")}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(is,{team:e.home,players:h.home,referenceDate:N.home,lineupState:f.home}),s.jsx(is,{team:e.away,players:h.away,referenceDate:N.away,lineupState:f.away})]}),p&&s.jsx("p",{className:"mt-1.5 text-[9.5px] leading-[1.55] text-ink3",children:b.caveat||"오늘 실제 발표 라인업이 아니며, 선발 발표 시 교체될 수 있다."})]}):s.jsx("p",{className:"mt-2 text-[10.5px] text-ink3",children:"타자 선발 명단은 아직 발표되지 않았다."})]})}if(["bk","vl"].includes(e.sport))return s.jsx(gn,{g:e});const t=e.선발||{},n=t.lineups||{},i=t.key_players||{},l=t.formations||{},a=t.lineup_status||{},o=e.라인업||{},d=a.expected_at?new Date(a.expected_at).toLocaleString("ko-KR",{timeZone:"Asia/Seoul",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null,r=!!(n.home?.length||n.away?.length),c=!!(i.home?.length||i.away?.length),m=!!(o.home||o.away);return s.jsxs(s.Fragment,{children:[r?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 text-[10.5px] font-semibold text-ink3",children:"실제 발표 선발 명단"}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(ts,{team:`${e.home}${l.home?` · ${l.home}`:""}`,players:n.home}),s.jsx(ts,{team:`${e.away}${l.away?` · ${l.away}`:""}`,players:n.away})]})]}):s.jsx("p",{className:"text-[11.5px] text-ink3",children:t.source?`실제 선발 명단 발표 전${d?` · 통상 ${d} 전후 확인`:""}`:"이 리그의 선수·라인업 자료 경로가 아직 연결되지 않았다."}),c&&s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 mt-2 text-[10.5px] font-semibold text-ink3",children:"시즌 핵심 선수 기록"}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(ls,{team:e.home,players:i.home}),s.jsx(ls,{team:e.away,players:i.away})]})]}),m&&s.jsxs("div",{className:"mt-2 grid gap-2 sm:grid-cols-2",children:[s.jsx(ss,{team:e.home,profile:o.home}),s.jsx(ss,{team:e.away,profile:o.away})]})]})}function vn({record:e,sport:t}){const n=e.rank?`${e.rank}위 · `:"",i=e.wins!=null||e.losses!=null?`${e.wins??0}승${e.draws!=null?` ${e.draws}무`:""} ${e.losses??0}패`:"경기 기록 확인 중";return s.jsxs("div",{className:"mt-1.5 border-t border-rule2 pt-1.5 text-[10.5px] leading-[1.65] text-ink3",children:["공식 현재 성적 ",s.jsxs("b",{className:"tnum text-ink",children:[n,i]}),t==="sc"&&s.jsxs(s.Fragment,{children:[e.goals_per_game!=null&&s.jsxs("span",{children:[" · 평균 ",e.goals_per_game,"득점/",e.conceded_per_game,"실점"]}),e.xg!=null&&s.jsxs("span",{children:[" · xG ",e.xg,e.xga!=null?`/xGA ${e.xga}`:""]})]}),t==="bs"&&e.pct!=null&&s.jsxs("span",{children:[" · 승률 ",(e.pct*100).toFixed(1),"%"]}),t==="bk"&&s.jsxs(s.Fragment,{children:[e.points_per_game!=null&&s.jsxs("span",{children:[" · 평균 ",e.points_per_game,"득점",e.conceded_per_game!=null?`/${e.conceded_per_game}실점`:""]}),e.rebounds_per_game!=null&&s.jsxs("span",{children:[" · 리바운드 ",e.rebounds_per_game]}),e.assists_per_game!=null&&s.jsxs("span",{children:[" · 도움 ",e.assists_per_game]}),e.fg_pct!=null&&s.jsxs("span",{children:[" · FG ",e.fg_pct,"%"]}),e.three_pct!=null&&s.jsxs("span",{children:[" · 3P ",e.three_pct,"%"]}),e.turnovers_per_game!=null&&s.jsxs("span",{children:[" · 턴오버 ",e.turnovers_per_game]})]}),t==="vl"&&s.jsxs(s.Fragment,{children:[e.table_points!=null&&s.jsxs("span",{children:[" · 승점 ",e.table_points]}),e.set_ratio!=null&&s.jsxs("span",{children:[" · 세트비 ",e.set_ratio]}),e.point_ratio!=null&&s.jsxs("span",{children:[" · 점수비 ",e.point_ratio]}),e.attack_pct!=null&&s.jsxs("span",{children:[" · 공격 ",e.attack_pct,"%"]}),e.blocks_per_set!=null&&s.jsxs("span",{children:[" · 블로킹/세트 ",e.blocks_per_set]}),e.serves_per_set!=null&&s.jsxs("span",{children:[" · 서브/세트 ",e.serves_per_set]}),e.receive_efficiency!=null&&s.jsxs("span",{children:[" · 리시브 ",e.receive_efficiency,"%"]})]})]})}function yn({team:e,form:t,status:n,record:i,sport:l}){const a={missing_db_history:"조회 시점 이전에 확인된 경기 결과가 DB에 없다.",team_unmapped:"DB 경기 기록에서 이 팀을 연결하지 못했다.",no_season_sample:"이 팀의 해당 리그·시즌 경기 기록이 없다."}[n]||"최근 경기 기록이 아직 제공되지 않았다.";return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("div",{className:"text-[11px] text-ink3",children:e}),t?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mt-0.5 text-[12px] font-semibold text-ink",children:t.last10||Es(t)}),s.jsxs("div",{className:"mt-1 text-[11px] leading-[1.7] text-ink2",children:["시즌 ",t.w,"승 ",t.d?`${t.d}무 `:"",t.l,"패",t.streak?` · ${t.streak}`:"",t.home?` · 홈 ${t.home}`:"",t.away?` · 원정 ${t.away}`:"",t.avg_scored!=null?` · 평균 ${t.avg_scored}득점/${t.avg_conceded}실점`:""]})]}):s.jsx("p",{className:"mt-1 text-[11.5px] text-ink3",children:a}),i&&Object.keys(i).length>0&&s.jsx(vn,{record:i,sport:l})]})}function kn({g:e}){const t=e.선발?.team_profiles||{},n=e.team_form_display||e;return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"grid gap-2 sm:grid-cols-2",children:[["home",e.home,n.form_home],["away",e.away,n.form_away]].map(([i,l,a])=>s.jsxs("div",{children:[s.jsx(yn,{team:l,form:a,status:n[`form_${i}_status`],record:Se(e,i),sport:e.sport}),!!t[i]?.characteristics?.length&&s.jsxs("div",{className:"mt-1 rounded-[7px] border border-rule2 bg-paper2 px-2.5 py-2 text-[10.5px] leading-[1.7] text-ink2",children:[s.jsxs("b",{className:"text-ink",children:[l," 특징"]})," · ",t[i].characteristics.join(" · ")]})]},i))}),n.h2h&&s.jsxs("div",{className:"mt-2 rounded-[7px] border border-rule2 px-2.5 py-2 text-[11.5px] text-ink2",children:[s.jsx("span",{className:"mr-1 text-[10.5px] text-ink3",children:"맞대결"}),n.h2h]})]})}function os({team:e,rows:t,connected:n,emptyText:i}){return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("div",{className:"text-[11px] text-ink3",children:e}),t.length?s.jsx("ul",{className:"mt-1 space-y-1",children:t.map((l,a)=>s.jsxs("li",{className:"text-[11.5px]",children:[s.jsx("b",{className:"text-ink",children:l.name}),s.jsxs("span",{className:"text-ink3",children:[" · ",l.reason_label||"사유 미확인"," · ",l.status||"출전 불가",l.position?` · ${l.position}`:""]}),l.impact_label&&s.jsxs("span",{className:"ml-1 rounded border border-rule px-1 text-[9.5px] text-ink3",children:["예상 영향 ",l.impact_label]})]},`${l.name}-${a}`))}):s.jsx("p",{className:"mt-1 text-[11.5px] text-ink3",children:n?i||"공식 명단에서 부상 상태로 표시된 선수가 없다.":"부상·출전 상태 자료가 아직 연결되지 않았다."})]})}function $n({g:e}){const t=e.선발||{},n=t.unavailable,i=t.lineup_status?.state,l=t.lineups||{},a=e.sport==="bs"?t.lineup_status?.official_today===!0:!!(l.home?.length||l.away?.length),o=["bk","vl"].includes(e.sport),d=t.roster_status?.state,r=["official_competition_roster","official_roster_partial","official_player_stats","recent_international_roster","season_stats"].includes(d),c=o?!0:e.sport==="sc"?i==="announced":!!n&&Object.hasOwn(n,"home")&&Object.hasOwn(n,"away"),m=o?r?"선수 자료는 연결됐지만 별도의 당일 부상·출전 확정 자료는 아니다.":t.coverage?.label||"공식 부상·출전 상태 자료원이 아직 연결되지 않았다.":e.sport==="sc"?"시즌 핵심 선수들이 모두 선발 명단에 포함됐다.":null,x=t.availability_summary,h=b=>b>=.6?"큼":b>=.25?"중간":b>0?"작음":"없음";return s.jsxs(s.Fragment,{children:[x&&(x.home_burden>0||x.away_burden>0)&&s.jsxs("div",{className:"mb-2 rounded-[7px] border border-rule2 px-2.5 py-2 text-[11.5px] leading-[1.7] text-ink2",children:["명단 변수 예상 영향 · ",s.jsxs("b",{children:[e.home," ",h(x.home_burden)]})," · ",s.jsxs("b",{children:[e.away," ",h(x.away_burden)]}),s.jsx("p",{className:"mt-1 text-[10px] text-ink3",children:"선수 비중·결장 가능성·대체 수준·출처 신뢰도를 함께 본 진단값이다. 과거 검증 전이라 승률에는 직접 더하지 않는다."})]}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(os,{team:e.home,rows:be(e,"home"),connected:c,emptyText:m}),s.jsx(os,{team:e.away,rows:be(e,"away"),connected:c,emptyText:m})]}),!a&&s.jsx("p",{className:"mt-2 text-[10.5px] leading-[1.6] text-ink3",children:o?"등록/최근 명단은 실제 당일 선발이나 부상 보고서가 아니다. 경기 직전 공식 출전 명단을 별도로 확인한다.":e.sport==="sc"?"실제 선발 명단 발표 전에는 결장·벤치 여부를 단정하지 않는다.":"실제 경기 선발 명단은 보통 시작 직전에 확정된다."})]})}function wn({g:e,pick:t}){const n=e.경기근거||null,i=Gt(t),l=[["경기 내부",n?.internal||[]],["경기 외부",n?.external||[]],["공개 픽 흐름",n?.crowd||[]]].filter(([,o])=>o.length),a=String(e.근거해설||"").trim();return!i&&!l.length&&!a&&!n?.limitations?.length?null:s.jsxs("div",{className:"mt-3 border-t border-rule2 pt-3 text-[11.5px] leading-[1.7] text-ink2",children:[i&&s.jsxs("div",{className:"rounded-[7px] border border-rule bg-panel px-3 py-2.5",children:[s.jsx("div",{className:"mb-0.5 text-[10.5px] font-semibold tracking-[.04em] text-ink3",children:"왜 이 픽인가"}),s.jsx("div",{className:"text-ink",children:i})]}),!!l.length&&s.jsx("div",{className:"mt-2 grid gap-2 [grid-template-columns:repeat(auto-fit,minmax(210px,1fr))]",children:l.map(([o,d])=>s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("div",{className:"mb-1 text-[10.5px] font-semibold tracking-[.04em] text-ink3",children:o}),d.map((r,c)=>s.jsxs("div",{className:"mb-1 last:mb-0",children:[r.text,r.source&&s.jsxs("span",{className:"ml-1 text-[10px] text-ink3",children:["(",r.source,")"]})]},`${r.kind||o}-${c}`))]},o))}),a&&s.jsxs("div",{className:"mt-2 rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("span",{className:"mr-1.5 rounded border border-rule px-1.5 py-0.5 text-[10px] text-ink3",children:Ut(e.근거해설방식)}),a]}),!!n?.limitations?.length&&s.jsxs("div",{className:"mt-1.5 text-[10.5px] text-ink3",children:["검증 메모 · ",n.limitations.join(" · ")]})]})}function Sn({g:e,analysis:t,decision:n,opts:i,grades:l,tie:a,pick:o,highlightedToday:d=!1,recalculating:r=!1,showPrices:c=!1,onSaveBet:m}){const x=fs(e),h=et(e,x);c&&!h.some(v=>v.id==="evidence")&&h.push({id:"evidence",label:"픽 근거·수식"});const[b,f]=C.useState(h[0]?.id||"summary");if(!h.length)return null;const p=h.some(v=>v.id===b)?b:h[0]?.id,N=Qs(e),F=String(e.event_id||`${e.round}-${e.home}-${e.away}`).replace(/[^a-zA-Z0-9_-]/g,"-"),$=v=>`tab-${F}-${v}`,w=`panel-${F}`,k=(v,T)=>{if(!["ArrowLeft","ArrowRight","Home","End"].includes(v.key))return;v.preventDefault();let P=T;v.key==="ArrowLeft"&&(P=(T-1+h.length)%h.length),v.key==="ArrowRight"&&(P=(T+1)%h.length),v.key==="Home"&&(P=0),v.key==="End"&&(P=h.length-1),f(h[P].id),v.currentTarget.parentElement?.querySelectorAll('[role="tab"]')?.[P]?.focus()};return s.jsxs("div",{className:"intel-panel",children:[s.jsx("div",{className:"intel-tabs",role:"tablist","aria-label":`${e.home} 대 ${e.away} 경기 정보`,children:h.map((v,T)=>s.jsx("button",{type:"button",role:"tab",id:$(v.id),"aria-controls":w,"aria-selected":p===v.id,tabIndex:p===v.id?0:-1,className:`${jn} ${p===v.id?"is-active":""}`,onKeyDown:g=>k(g,T),onClick:()=>f(v.id),children:v.label},v.id))}),s.jsxs("div",{className:"intel-content",role:"tabpanel",id:w,"aria-labelledby":$(p),tabIndex:0,children:[p==="summary"&&s.jsxs(s.Fragment,{children:[t?s.jsx(Us,{analysis:t,scoreForecast:tn(e)}):s.jsx("p",{className:"m-0 text-[11.5px] text-ink3",children:x||"경기 자료를 확인 중입니다."}),s.jsx("div",{className:"mt-3 border-t border-rule2 pt-3",children:s.jsx(kn,{g:e})})]}),p==="players"&&s.jsxs(s.Fragment,{children:[s.jsx(_n,{g:e}),s.jsx("div",{className:"mt-3 border-t border-rule2 pt-3",children:s.jsx($n,{g:e})})]}),p==="evidence"&&s.jsxs(s.Fragment,{children:[n?s.jsx(Rs,{decision:n}):s.jsx("p",{className:"m-0 text-[11.5px] text-ink3",children:"경기 전 판정 스냅샷이 아직 없습니다."}),s.jsx(wn,{g:e,pick:o}),c&&s.jsx("div",{className:"show-model mt-3 overflow-x-auto border-t border-rule2 pt-3",children:s.jsx(fn,{g:e,opts:i,grades:l,tie:a,pick:o,highlightedToday:d,recalculating:r,onSaveBet:m})})]})]}),p==="players"&&s.jsx(Nn,{source:N})]})}export{Dn as M}; diff --git a/docs/assets/Markets-BuleyXai.js b/docs/assets/Markets-BuleyXai.js new file mode 100644 index 00000000..f537f273 --- /dev/null +++ b/docs/assets/Markets-BuleyXai.js @@ -0,0 +1 @@ +import{j as s,C as Pe,r as P,N as ys,O as V,G as ks}from"./cache-guard-BQcATxSH.js";import{d as us,e as ms,r as $s,b as hs,f as ws,a as Ss,h as Fs}from"./decision-view-model-Bfii5tWW.js";import{s as le,b as Te,g as J,D as Es,r as xs,P as ps,c as Ae,a as Ps,e as As,k as ge,t as Ds,f as Oe,h as Ls,n as Ms,i as xe,j as Cs,p as X,o as Z,l as bs,m as fs,q as Ts}from"./unified-recommendation-BCuqyY1U.js";import{e as Os,f as Rs,u as Is}from"./bet-ledger-ES6mNno4.js";import{A as Bs}from"./AiDisclosure-BIoGQ_Ky.js";import{r as Gs}from"./live-odds-CIQqKAce.js";import{u as Hs}from"./poll-QCTlueAT.js";const Ks=e=>{const[t,...n]=String(e).split(" — ");return n.length?{title:t,body:n.join(" — ")}:{title:"경기력 근거",body:t}},Re=e=>{const t=Number(e);return Number.isFinite(t)?t.toFixed(1):"-"};function Us({forecast:e}){if(!e||e.status!=="shadow")return null;const t=e.expected_scores||{},n=Array.isArray(e.top_scorelines)?e.top_scorelines.slice(0,3):[],i=e.contract?.score_unit_label||t.unit||"득점";return s.jsxs("div",{className:"mt-3 rounded-md border border-rule2 bg-panel px-3.5 py-3","aria-label":"스코어 분포 전망",children:[s.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-2",children:[s.jsxs("div",{children:[s.jsx("b",{className:"text-[12px] text-ink",children:"스코어 흐름 전망"}),s.jsxs("p",{className:"mt-1 text-[11px] leading-5 text-ink3",children:["현재 팀 전력 분포의 평균은 홈 ",Re(t.home)," · 원정 ",Re(t.away)," ",i,"입니다."]})]}),s.jsx("span",{className:"rounded border border-rule2 bg-paper px-2 py-1 text-[10px] text-ink3",children:"연구값 · 추천 확률 미반영"})]}),!!n.length&&s.jsx("div",{className:"mt-2 flex flex-wrap gap-1.5",children:n.map(a=>s.jsxs("span",{className:"rounded border border-rule2 bg-paper px-2 py-1 text-[10.5px] text-ink2",children:[a.home_score,":",a.away_score," · ",Math.round(Number(a.probability||0)*100),"%"]},`${a.home_score}-${a.away_score}`))}),s.jsx("p",{className:"mt-2 text-[10px] leading-4 text-ink3",children:"선발·결장별 영향량은 사전 원장으로 충분히 검증된 뒤 시나리오에 반영합니다. 임의 난수나 ‘미래상수’는 예측 입력으로 쓰지 않습니다."})]})}function qs({previews:e}){return e?.some(t=>t.characteristics.length||t.players.length)?s.jsxs("div",{className:"mt-3",children:[s.jsx("h4",{className:"mb-2 text-[11px] font-semibold tracking-[.04em] text-ink3",children:"팀·핵심 선수 프리뷰"}),s.jsx("div",{className:"grid gap-2 sm:grid-cols-2",children:e.map(t=>s.jsxs("article",{className:"rounded-md border border-rule2 bg-paper px-3 py-2.5",children:[s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsx("b",{className:"text-[12px] text-ink",children:t.team}),s.jsx("span",{className:"text-[9.5px] text-ink3",children:t.side==="home"?"홈":"원정"})]}),!!t.characteristics.length&&s.jsx("ul",{className:"mt-1.5 space-y-1 text-[10.5px] leading-4 text-ink2",children:t.characteristics.slice(0,3).map(n=>s.jsxs("li",{children:["· ",n]},n))}),!!t.players.length&&s.jsx("div",{className:"mt-2 border-t border-rule2 pt-2",children:t.players.map(n=>s.jsxs("div",{className:"mb-1.5 last:mb-0",children:[s.jsxs("div",{className:"flex flex-wrap items-baseline gap-1 text-[10.5px]",children:[s.jsx("b",{className:"text-ink",children:n.name}),s.jsx("span",{className:"text-ink3",children:n.characteristic})]}),!!n.facts.length&&s.jsx("p",{className:"tnum mt-0.5 text-[9.5px] text-ink3",children:n.facts.join(" · ")})]},n.name))}),!!t.unavailable.length&&s.jsxs("p",{className:"mt-2 border-t border-rule2 pt-1.5 text-[9.5px] leading-4 text-ink3",children:["출전 변수 · ",t.unavailable.map(n=>`${n.name}${n.status?`(${n.status})`:""}`).join(" · ")]})]},t.side))}),s.jsx("p",{className:"mt-1.5 text-[9.5px] text-ink3",children:"공식 시즌 기록과 최근 흐름을 요약한 설명이며, 검증 전에는 추천 확률을 직접 바꾸지 않습니다."})]}):null}function Ws({analysis:e,scoreForecast:t}){if(!e)return null;const{prediction:n,reasons:i,cautions:a,signalSummary:l,decision:o,commentary:d}=e,r=n?.probability==null?null:Math.round(n.probability*100),c=us(o),u=l?.narrative||(o?.action==="market_reference"?"추천 방향과 함께 최근 경기력·선수 정보·반대 신호를 비교해 확인하세요.":"아래 위험이 해소되지 않아 이 픽은 추천하지 않습니다.");return s.jsxs("section",{className:"prediction-card","aria-label":"경기 모델 판정과 경기력 해석",children:[s.jsxs("header",{className:"prediction-head",children:[s.jsxs("div",{children:[s.jsxs("p",{className:"prediction-label",children:[c," · 참고용"]}),s.jsx("h3",{children:n?.headline||"예측 자료 확인 중"}),s.jsx("p",{children:u})]}),r!==null&&s.jsxs("div",{className:"prediction-probability",children:[s.jsxs("b",{children:[r,"%"]}),s.jsx("span",{children:o?.model?.validatedEdge?"AI 최종확률":"배당 기반 시장확률"})]})]}),s.jsx("div",{className:"prediction-body",children:s.jsxs("div",{children:[s.jsx("h4",{children:"판정과 구분된 경기 자료"}),s.jsx("p",{className:"performance-intro",children:"설명 자료와 반대 신호를 함께 보며, AI가 사용한 값은 ‘픽 근거·수식’ 탭에서 확인합니다."}),s.jsx("ul",{className:"performance-reasons",children:i.map(h=>{const x=Ks(h);return s.jsxs("li",{children:[s.jsx("strong",{children:x.title}),s.jsx("p",{children:x.body})]},h)})})]})}),o?.action==="withhold"&&!!o.withholdReasons?.length&&s.jsxs("div",{className:"prediction-caution",children:[s.jsx("b",{children:"이 픽을 가지 말아야 하는 이유"}),s.jsx("ul",{className:"mt-1 list-disc space-y-1 pl-4",children:o.withholdReasons.map(h=>s.jsxs("li",{children:[s.jsx("strong",{children:h.title})," · ",h.body]},`${h.title}-${h.body}`))})]}),d&&s.jsxs("div",{className:"prediction-commentary",children:[s.jsx("b",{children:"해설 정리"}),s.jsx("p",{children:d}),s.jsx("small",{children:"수집된 사실을 바꾸지 않고 LLM이 문장만 다듬었습니다."})]}),s.jsx(qs,{previews:e.teamPreviews}),s.jsx(Us,{forecast:t}),!!a?.length&&s.jsxs("div",{className:"prediction-caution",children:[s.jsx("b",{children:"반대 근거·변수"}),s.jsx("span",{children:a.join(" ")})]}),s.jsx("footer",{children:"예상 적중확률은 실제 배당에 포함된 마진을 제거한 값을 출발점으로 삼고, 검증된 보정만 추가합니다. 구매 판단은 직접 합니다."})]})}const ce=e=>typeof e=="number"||typeof e=="string"&&e.trim()?Number(e):NaN,zs=e=>{const t=ce(e);return Number.isFinite(t)&&t>0&&t<1?t:null};function De(e,t=null,n=Date.now()){const i=e?.prediction_record,a=le(e),l=Date.parse(i?.captured_at||"");if(!i?.selection_id||!i.market||!i.selection||a==null||!Number.isFinite(l)||l>=a||l>n)return null;const o={selection_id:i.selection_id,offer_id:i.offer_id,market:i.market,label:i.label||"",선택:i.selection,배당:Number.isFinite(ce(i.odds))?ce(i.odds):null},d=zs(i.probability),r={option:o,openingProbability:d,capturedAt:i.captured_at,estimate:null,estimateStatus:d===null?"missing_opening":"waiting_live"};if(d===null||!t||t.status==="BEFORE")return r;if(t.finished||t.cancelled||t.postponed)return{...r,estimateStatus:"closed"};const c=Date.parse(t.observed_at||e._liveFeedAt||"");if(!Number.isFinite(c)||n-c>600*1e3||c>n+5e3)return{...r,estimateStatus:"stale_live"};if(![t.home_score,t.away_score].every(x=>Number.isInteger(ce(x))&&ce(x)>=0))return{...r,estimateStatus:"missing_score"};if(!{승패:["홈","원정","승","패"],승무패:["홈","원정","승","패","무","무승부"],언더오버:["언더","오버"]}[i.market]?.includes(i.selection)||!["sc","bs","bk"].includes(e.sport)||i.market==="언더오버"&&!/\d/.test(o.label))return{...r,estimateStatus:"unsupported_market"};const h=Os({openingProbability:d,game:{sport:e.sport},selection:{market:o.market,label:o.label,choice:o.선택}},t);return{...r,estimate:h,estimateStatus:"available"}}const q=e=>String(e??"").trim(),z=e=>typeof e=="number"||typeof e=="string"&&e.trim()?Number(e):NaN,oe=e=>{const t=z(e);return Number.isFinite(t)&&t>0&&t<1?t:null},Ie=e=>Number.isFinite(e)?new Date(e+9*36e5).toISOString().slice(0,10):null,ne=e=>JSON.stringify([q(e?.market),q(e?.market_label??e?.label),q(e?.sel??e?.selection??e?.선택)]),ue=e=>{let t=q(e);return/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?$/.test(t)&&(t=`${t.replace(" ","T")}+09:00`),Date.parse(t)},Ys=(e,t)=>{const n=ue(e?.kickoff_at);return Number.isFinite(n)?n:le({...e,year:e?.year||t})},js=e=>{const t=q(e?.match).split(" vs ");return[q(e?.home||t[0]),q(e?.away||t[1])]},Vs=(e,t)=>JSON.stringify([t,q(e?.sport),q(e?.league),...js(e)]),Be=(e,t,n,i)=>{const[a,l]=js(e);return a&&l&&a===q(t.home)&&l===q(t.away)&&q(e.league)===q(t.league)&&(!e.sport||e.sport===t.sport)&&Ys(e,i)===n},_e=(e,t,n,i)=>{const a=ue(e.recommended_at||t?.generated_at);return Number.isFinite(a)&&ams([e]).length===1,Zs={missing_opening:"사전 확률 기록이 없어 현재 추정을 제공하지 않습니다.",waiting_live:"최신 경기 중계가 확인되면 현재 추정을 표시합니다.",stale_live:"중계 갱신이 늦어 현재 추정을 보류합니다.",missing_score:"현재 점수 자료가 없어 추정을 보류합니다.",unsupported_market:"이 마켓의 현재 추정은 지원하지 않습니다.",closed:"경기 종료·중단 후에는 현재 추정을 표시하지 않습니다."};function Js({games:e=[],today:t=null,currentToday:n=null,now:i=Date.now()}={}){i=Number(i);const a=Ie(i);if(!a)return[];const l=Number(a.slice(0,4)),o=(t?.candidates||[]).filter(Boolean),d=new Set(Te(o)),r=(n||t)?.candidates||[],c=new Set(Te(r)),u=f=>typeof f.daily_recommendation?.recommended=="boolean"?f.daily_recommendation.recommended:d.has(f),h=f=>typeof f.daily_recommendation?.recommended=="boolean"?f.daily_recommendation.recommended:c.has(f),x=new Map;for(const f of e||[]){if(!f)continue;const p={...f,year:f.year||t?.year||l},N=le(p);if(N==null||Ie(N)!==a||!p.home||!p.away)continue;const F=p._liveState,$=J(p,F,i),w=N<=i||p._liveStarted===!0||$!=="upcoming",k=De(p,F,i),v=o.filter(y=>Be(y,p,N,p.year)),T=r.filter(y=>Be(y,p,N,p.year));let g,E,L,W,S,C,H=null,M;if(k){g=k.option,E=k.openingProbability,L=z(g.배당),W=k.capturedAt;const y=v.filter(R=>ne(R)===ne(g)&&z(R.odds??R.배당)===L&&_e(R,t,N,i)),O=ue(t?.generated_at),U=Number.isFinite(O)&&Oh(R)&&ve(R)&&ne(R)===ne(g)&&z(R.odds??R.배당)===L&&_e(R,n||t,N,i)),se=U&&y.some(R=>R.daily_recommendation?.recommended===!1),ae=E!=null&&E>=Es&&ve({market:g.market,odds:L,market_prob:E,is_market_favorite:p.prediction_record.is_market_favorite,final_reversal:p.prediction_record.final_reversal});if(!Y&&!I&&(!w||se||!ae))continue;S=Y?"highlight":I?"current":"recorded",C=xs(p,F),$==="live"&&k.estimateStatus==="available"&&Number.isFinite(k.estimate?.probability)&&(H=k.estimate),M=$==="finished"?"경기 종료 · 결과를 확인하세요.":Zs[k.estimateStatus]||"현재 추정 자료를 확인 중입니다."}else{if(w||p._liveOddsChanged)continue;const y=T.find(O=>h(O)&&ve(O)&&_e(O,n||t,N,i)&&oe(O.market_prob)!=null&&(p.options||[]).some(U=>ne(U)===ne(O)&&z(U.배당)===z(O.odds)&&oe(U.시장확률)!=null&&Math.abs(z(U.시장확률)-z(O.market_prob))<=50001e-9));if(!y)continue;g={market:y.market,label:y.market_label??y.label??"",선택:y.sel,배당:y.odds},E=y.decision_pipeline_applied===!0&&y.has_validated_edge===!0?oe(y.predicted_hit_prob)??oe(y.market_prob):oe(y.market_prob),L=z(y.odds),W=y.recommended_at||(n||t).generated_at,S="current",C={state:"pending",label:"경기 전",record:null},M="경기 시작 전입니다."}const K=`${Vs(p,N)}|${ne(g)}`,G={key:K,game:p,kickoff:N,phase:$,source:S,option:g,openingProbability:E,originalOdds:Number.isFinite(L)&&L>1?L:null,capturedAt:W,outcome:C,estimate:H,estimateMessage:M,savedPrior:!!k},m=x.get(K),_=G.savedPrior,A=m?.savedPrior;!m||_&&!A||_===A&&ue(W)b[f.phase]-b[p.phase]||f.kickoff-p.kickoff||(f.keyp.key?1:0))}function Ge(e,t,n){const i=r=>["hit","miss","void"].includes(r.outcome.state);if(i(e)&&i(t)&&e.outcome.state!==t.outcome.state)return{...e,phase:"pending",estimate:null,outcome:{...e.outcome,state:"pending",label:"정산 결과 충돌 · 확인 중"}};const a=r=>Date.parse(r.game._liveState?.observed_at||r.game._liveFeedAt||"")||0,l=i(t)&&!i(e)?t:i(e)?e:t.phase==="finished"&&e.phase!=="finished"?t:e.phase==="finished"?e:a(t)>a(e)?t:e;if(l===e)return e;const o={...e.game,status:l.game.status,score:l.game.score,_liveState:l.game._liveState,_liveFeedAt:l.game._liveFeedAt,_liveStarted:l.game._liveStarted},d=l.phase==="live"?De(o,o._liveState,n)?.estimate:null;return{...e,game:o,phase:l.phase,estimate:d,estimateMessage:l.estimateMessage,outcome:{...l.outcome,record:e.outcome.record}}}const He=e=>typeof e=="number"||typeof e=="string"&&e.trim()?Number(e):NaN,Xs=new Set(["RESULT","END","ENDED","FINAL","FINISHED"]);function Qs(e,t=e?._liveState,n=Date.now()){if(!t)return null;const i=String(t.status||"").toUpperCase(),a=t.cancelled?"경기 취소":t.postponed?"경기 연기":String(t.status_text||"진행 중"),l=t.cancelled||t.postponed||/CANCEL|POSTPONE|SUSPEND|DELAY/.test(i)||/취소|연기|중단|우천|지연/.test(a);if(!l&&(t.finished||Xs.has(i)||e?.status==="정산"))return{state:"finished",percent:100,label:"경기 종료",basis:"종료 확인"};if(!l&&!["STARTED","IN_PROGRESS","LIVE","PLAYING"].includes(i))return null;const o=Date.parse(t.observed_at||e?._liveFeedAt||"");if(!Number.isFinite(o)||o>Number(n)+5e3)return{state:"unknown",percent:null,label:a,basis:"중계 시각 확인 중"};if(t.stale===!0||Number(n)-o>600*1e3)return{state:"stale",percent:null,label:a,basis:"중계 갱신 지연 · 마지막 진행 정보"};if(l)return{state:"interrupted",percent:null,label:a,basis:"경기 중단 · 진행률 보류"};const d=(c,u,h=a)=>({state:"live",percent:Math.min(99,Math.max(0,Math.floor(c*100))),label:h,basis:u}),r=(c="진행률 산정 정보 부족")=>({state:"unknown",percent:null,label:a,basis:c});if(e?.sport==="bs"){const c=a.match(/(\d+)\s*회\s*(초|말)/),u=c?Number(c[1]):NaN,h=c?.[2];return!Number.isInteger(u)||u<1||!h?r():u>9?{state:"live",percent:null,label:`연장 ${u}회${h}`,basis:"정규 9회 경과 · 종료 시점 미정"}:d((u-1+(h==="말"?.5:0))/9,"정규 9회 기준 · 이닝 단위 추정",`${u}회${h}`)}if(e?.sport==="sc"){if(/연장|승부차기/.test(a)||He(t.clock?.period)>2)return{state:"live",percent:null,label:a,basis:"정규시간 경과 · 종료 시점 미정"};if(t.clock?.phase==="halftime"||/하프타임|전반 종료|전반종료/.test(a))return d(.5,"정규 90분 기준 · 추가시간 제외","하프타임");const c=He(t.clock?.elapsed_minute);return!Number.isFinite(c)||c<0?r():d(c/90,c>=90?"정규시간 경과 · 종료 미확정":"정규 90분 기준 · 추가시간 제외")}if(e?.sport==="bk"){if(/연장|\bOT\b/i.test(a))return{state:"live",percent:null,label:a,basis:"연장 진행 · 종료 시점 미정"};const c=Number(a.match(/([1-4])\s*(?:쿼터|Q)/i)?.[1]);if(c)return d((c-1)/4,"정규 4쿼터 기준 · 쿼터 단위 추정")}return r(e?.sport==="vl"?"세트 경기 · 종료까지의 비율은 산정하지 않음":void 0)}function Se({game:e,live:t=e?._liveState,now:n=Date.now(),compact:i=!1}){const a=Qs(e,t,n);if(!a||a.state==="finished")return null;const l=Number.isFinite(a.percent),o=`${a.label} · ${a.basis}`;return s.jsxs("span",{className:`match-progress${i?" is-compact":""} is-${a.state}`,"aria-label":"경기 진행률",title:`${o}. 적중확률이 아닙니다.`,children:[s.jsxs("span",{className:"match-progress-caption",children:[s.jsxs("span",{children:["경기 진행",l?` 약 ${a.percent}%`:" · 비율 확인 불가"]}),s.jsx("span",{children:a.label})]}),l&&s.jsx("span",{className:"match-progress-track",role:"progressbar","aria-label":"경기 진행률","aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":a.percent,"aria-valuetext":o,children:s.jsx("span",{style:{width:`${a.percent}%`}})}),s.jsx("small",{className:"match-progress-note",children:a.basis})]})}const Ke=e=>Number.isFinite(e)?`${(e*100).toFixed(1)}%`:"기록 없음",et={highlight:"오늘 추천 · 사전 기록",recorded:"사전 픽 · 추천 이력 미확인",current:"오늘 추천 · 경기 전"};function st({games:e=[],today:t=null,currentToday:n=null,now:i=Date.now()}){const a=Js({games:e,today:t,currentToday:n,now:i}),l=a.filter(r=>r.phase!=="finished"),o=a.filter(r=>r.phase==="finished"),d=a.filter(r=>r.source!=="recorded").length;return s.jsxs("section",{className:"mb-5 min-w-0","aria-label":"오늘의 추천 픽",children:[s.jsxs("div",{className:"mb-3 flex flex-wrap items-baseline justify-between gap-2",children:[s.jsxs("h2",{className:"m-0 text-[17px] font-bold tracking-tight",children:["오늘의 추천 픽 ",s.jsx("span",{className:"tnum text-ink3",children:d})]}),s.jsx("p",{className:"m-0 text-[12px] text-ink3",children:"오늘 추천과 저장된 사전 픽을 함께 추적합니다. 추천 이력 미확인 픽은 구분해 표시합니다."})]}),!a.length&&s.jsx(Pe,{className:"p-4 text-[13px] text-ink3",children:"오늘 확인된 추천·사전 픽이 없습니다."}),s.jsx(Ue,{picks:l,now:i}),o.length>0&&s.jsxs("details",{className:"mt-3 rounded border border-rule p-3",children:[s.jsxs("summary",{className:"cursor-pointer text-[13px] font-semibold",children:["종료된 사전 픽 ",o.length,"개 · 적중 ",o.filter(r=>r.outcome.state==="hit").length," · 적중실패 ",o.filter(r=>r.outcome.state==="miss").length," · 결과와 당시 배당 보기"]}),s.jsx("div",{className:"mt-3",children:s.jsx(Ue,{picks:o,now:i})})]})]})}function Ue({picks:e,now:t}){return s.jsx("div",{className:"grid min-w-0 grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3",children:e.map(n=>s.jsxs(Pe,{as:"article",className:"min-w-0 p-3 sm:p-4","aria-label":`${n.game.home} 대 ${n.game.away} 사전 픽`,children:[s.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-1 text-[11px] text-ink3",children:[s.jsxs("span",{children:[n.game.league," · ",s.jsx("time",{dateTime:new Date(n.kickoff).toISOString(),children:new Date(n.kickoff+9*36e5).toISOString().slice(11,16)})]}),s.jsx("span",{className:n.phase==="live"?"live-score-badge":"",children:ps[n.phase]})]}),s.jsxs("h3",{className:"my-2 break-words text-[15px] font-semibold",children:[n.game.home," ",s.jsx("span",{className:"font-normal text-ink3",children:"vs"})," ",n.game.away]}),n.phase==="live"&&s.jsxs("p",{className:"tnum my-2 text-[16px] font-bold",children:[n.game._liveState?.home_score??"—"," : ",n.game._liveState?.away_score??"—",s.jsx("small",{className:"ml-2 text-[12px] font-normal",children:n.game._liveState?.status_text||"진행 중"})]}),n.phase!=="finished"&&s.jsx(Se,{game:n.game,now:t}),s.jsx("p",{className:"m-0 text-[11px] text-ink3",children:et[n.source]}),s.jsxs("p",{className:"mt-1 mb-3 break-words text-[14px] font-bold",children:[n.option.market,n.option.label?` ${n.option.label}`:""," · ",n.option.선택]}),s.jsxs("dl",{className:"m-0 grid grid-cols-2 gap-3 border-t border-rule pt-3",children:[s.jsxs("div",{children:[s.jsx("dt",{className:"text-[11px] text-ink3",children:"사전 확률"}),s.jsx("dd",{className:"tnum m-0 text-[19px] font-semibold",children:Ke(n.openingProbability)})]}),s.jsxs("div",{children:[s.jsx("dt",{className:"text-[11px] text-ink3",children:"현재 추정"}),s.jsx("dd",{className:"tnum m-0 text-[19px] font-semibold",children:n.estimate?Ke(n.estimate.probability):"—"})]})]}),s.jsx("p",{className:"mt-2 mb-3 text-[11px] text-ink3",children:n.estimate?"사전 확률에 현재 점수·경기 진행을 반영한 추정":n.estimateMessage}),s.jsxs("div",{className:"border-t border-rule pt-2",children:[s.jsxs("div",{className:"flex items-center justify-between gap-2 text-[12px]",children:[s.jsx("span",{className:"text-ink3",children:"결과"}),s.jsx("b",{className:`result-badge is-${n.outcome.state}`,children:n.phase==="upcoming"?"경기 전":n.phase==="live"?"진행 중":n.outcome.label})]}),s.jsxs("div",{className:"mt-1 flex items-center justify-between gap-2 text-[12px]",children:[s.jsx("span",{className:"text-ink3",children:"당시 배당"}),s.jsx("b",{className:"tnum",children:n.originalOdds==null?"기록 없음":`${n.originalOdds.toFixed(2)}배`})]})]})]},n.key))})}function tt({draft:e,onClose:t,onSaved:n}){const[i,a]=P.useState(1e4),[l,o]=P.useState(Number(e?.option?.배당)||0);if(!e)return null;const d=()=>{const r=Rs(e.game,e.option,{stake:i,purchaseOdds:l});Is(r),n?.(r),t()};return s.jsx("div",{className:"bet-dialog-backdrop",role:"presentation",onMouseDown:r=>{r.target===r.currentTarget&&t()},children:s.jsxs("section",{className:"bet-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"bet-dialog-title",children:[s.jsxs("div",{children:[s.jsx("small",{children:"단일 베팅 기록"}),s.jsxs("h2",{id:"bet-dialog-title",children:[e.game.home," vs ",e.game.away]})]}),s.jsxs("p",{children:[e.option.market,e.option.label?` ${e.option.label}`:""," · ",s.jsx("b",{children:e.option.선택})]}),s.jsxs("label",{children:["구매 배당",s.jsx("input",{type:"number",min:"1.01",step:"0.01",value:l,onChange:r=>o(r.target.value)})]}),s.jsxs("label",{children:["투입금",s.jsx("input",{type:"number",min:"100",step:"100",value:i,onChange:r=>a(r.target.value)})]}),s.jsxs("div",{className:"bet-dialog-actions",children:[s.jsx("button",{type:"button",onClick:t,children:"취소"}),s.jsx("button",{type:"button",className:"is-primary",disabled:!(l>1&&i>0),onClick:d,children:"내 베팅에 저장"})]})]})})}function Ns(e){const t=String(e?.해설||"").trim();if(!t)return"";const n=(e?.options||[]).filter(b=>b.market==="언더오버"),i=n.find(b=>Number.isFinite(Number(b.line))),a=i?Number(i.line):null;if(a==null||!(a!==8.5&&/(?:기준선(?:인|은|\(|\s)|기준점(?:인|은|\s))[^.]{0,20}8\.5|8\.5점/.test(t)))return t;const o=t.replace(/\s*\n+\s*/g," ").split(new RegExp("(?<=[.!?])\\s+")).filter(Boolean).filter(b=>!/8\.5/.test(b)).filter(b=>!/총득점은\s*(?:오버|언더).*(?:추천|예상)/.test(b)).filter(b=>!/(?:전력|득점 차|득실 차|팀).*(?:2점\s*차|2점차)\s*이상.*(?:가능|예상|공산)/.test(b)),d=n.find(b=>b.선택==="언더"),r=n.find(b=>b.선택==="오버"),u=[d,r].filter(b=>Number.isFinite(Number(b?.모델확률))).sort((b,f)=>Number(f.모델확률)-Number(b.모델확률))[0],h=e?.sport==="sc"?"골":"점",x=u?`실제 언더오버 기준점은 ${a}${h}이다. 득점분포 모델의 ${u.선택} 확률은 ${(Number(u.모델확률)*100).toFixed(1)}%다.`:`실제 언더오버 기준점은 ${a}${h}이다. 기존 8.5${h} 비교는 잘못된 기본값이라 제외했다.`;return o.length?[o[0],x,...o.slice(1)].join(" "):x}function pe(e,t){const n=e?.선발;if(!n)return null;const i=n[`${t}_detail`];if(i)return i;const a=n[t];return a?{name:a,announced:!0}:null}function Fe(e,t){return e?.선발?.teams?.[t]||null}function be(e,t){const n=e?.선발?.unavailable?.[t];return Array.isArray(n)?n.filter(i=>i?.name):[]}function nt(e){const t=e?.선발;return t?.source?{name:t.source,url:t.source_url||null,updatedAt:t.updated_at||null}:null}function it(e,t=""){const n=pe(e,"home")||pe(e,"away"),i=e?.form_home||e?.form_away||e?.h2h||Fe(e,"home")||Fe(e,"away"),a=e?.라인업||{},l=!!(a.home||a.away),o=be(e,"home").length||be(e,"away").length||l||e?.선발?.lineups,d=e?.sport,r=["bs","sc","bk","vl"].includes(d),c=[];return(r||t||n||i)&&c.push({id:"summary",label:"경기 분석"}),(r||n||l||o)&&c.push({id:"players",label:"선수·출전"}),(r||e?.decision_snapshot)&&c.push({id:"evidence",label:"픽 근거·수식"}),c}function at(e){const t=e?.stats;return t?[["ERA",t.era],["WHIP",t.whip],["승-패",t.record],["이닝",t.innings_display??t.innings],["탈삼진",t.strikeouts],["K/9",t.k9],["BB/9",t.bb9],["HR/9",t.hr9],[t.fip_approx?"FIP*":"FIP",t.fip],["평균 이닝",t.avg_ip],["선발",t.games_started]].filter(([,n])=>n!=null&&n!==""):[]}const B=e=>e!=null&&e!=="",qe=e=>{const t=Number(e);return Number.isFinite(t)?t.toFixed(3).replace(/^0/,""):null};function lt(e){if(!e)return"공식 예고 선발";const t=[];return e.period&&t.push(e.period),e.record&&t.push(e.record),B(e.era)&&t.push(`ERA ${e.era}`),B(e.whip)&&t.push(`WHIP ${e.whip}`),B(e.strikeouts)&&t.push(`${e.strikeouts}탈삼진`),t.join(" · ")||"공식 예고 선발"}function rt(e){const t=e?.stats;if(!t)return`${e.order||"-"}번 · ${e.position||"야수"}`;const n=[],i=qe(t.avg),a=qe(t.ops);return i&&n.push(`타율 ${i}`),B(t.home_runs)&&n.push(`${t.home_runs}홈런`),B(t.rbi)&&n.push(`${t.rbi}타점`),a&&n.push(`OPS ${a}`),`${t.season?`${t.season}시즌 · `:""}${n.join(" · ")}`}function ot(e){return[...e||[]].filter(t=>t?.name&&t.position!=="투수").sort((t,n)=>{const i=t.stats||{},a=n.stats||{};return(Number(a.ops)||0)-(Number(i.ops)||0)||(Number(a.home_runs)||0)-(Number(i.home_runs)||0)||(Number(a.rbi)||0)-(Number(i.rbi)||0)||(Number(t.order)||99)-(Number(n.order)||99)})[0]||null}function ct(e,t){const n=[];return B(e.games??e.apps)&&n.push(`${e.games??e.apps}경기`),t==="sc"?(B(e.goals)&&n.push(`${e.goals}골`),B(e.assists)&&n.push(`${e.assists}도움`),B(e.xg)&&n.push(`xG ${e.xg}`)):t==="bk"?(B(e.points)&&n.push(`평균 ${e.points}점`),B(e.rebounds)&&n.push(`${e.rebounds}리바운드`),B(e.assists)&&n.push(`${e.assists}도움`)):t==="vl"&&(B(e.points)&&n.push(`${e.points}득점`),B(e.blocks)&&n.push(`${e.blocks}블로킹`),B(e.serves)&&n.push(`${e.serves}서브`)),n.join(" · ")||"공식 시즌 선수 기록"}function dt(e){const t=e?.선발||{},n=[];if(e?.sport==="bs"){for(const r of["home","away"]){const c=t[`${r}_detail`]||(t[r]?{name:t[r]}:null);c?.name&&n.push({name:c.name,team:e[r],position:"선발투수",role:lt(c.stats),profileUrl:c.profile_url||null})}for(const r of["home","away"]){const c=ot(t.lineups?.[r]);c&&n.push({name:c.name,team:e[r],position:`${c.order||"-"}번 타자${c.position?` · ${c.position}`:""}`,role:rt(c),profileUrl:c.profile_url||null})}const a=t.lineup_status||{},l=a.state,d=a.official_today===!0||l==="official_today"?"오늘 NPB 공식 선발 타순과 공식 시즌 선수 기록을 반영했다.":l==="mixed_official_projected"?"공개된 팀은 오늘 공식 타순, 미공개 팀은 최근 완료 경기 기반 예상 타순이다.":t.lineups?"타자는 최근 공식 경기 기반 예상 타순이며 오늘 확정 명단이 아니다.":"";return{players:n,note:d}}const i=t.key_players||{};for(const a of["home","away"]){const l=i[a]?.[0];l?.name&&n.push({name:l.name,team:e[a],position:l.position||"핵심 선수",role:ct(l,e?.sport),profileUrl:l.profile_url||null})}return{players:n,note:n.length?"공식 시즌 기록의 팀별 주요 선수를 표시한다.":""}}const D=e=>{const t=Number(e);return Number.isFinite(t)?t:null},ut=e=>{const t=D(e);return t===null?null:t.toFixed(3).replace(/^0/,"")};function mt(e,t){if(!e?.name)return null;const n=e.stats||e,i=[];let a="주요 전력";if(t==="bs")D(n.era)!==null&&i.push(`ERA ${Number(n.era).toFixed(2)}`),D(n.whip)!==null&&i.push(`WHIP ${Number(n.whip).toFixed(2)}`),D(n.ops)!==null&&i.push(`OPS ${ut(n.ops)}`),D(n.home_runs)!==null&&i.push(`${n.home_runs}홈런`),a=D(n.era)!==null?"선발 운영의 중심":"타선 생산력의 중심";else if(t==="sc"){D(n.goals)!==null&&i.push(`${n.goals}골`),D(n.assists)!==null&&i.push(`${n.assists}도움`),D(n.xg)!==null&&i.push(`xG ${n.xg}`);const l=D(n.goals)||0,o=D(n.assists)||0;a=l>o?"득점 마무리의 중심":o>l?"기회 창출의 중심":"공격 전개의 중심"}else t==="bk"?(D(n.points)!==null&&i.push(`${n.points}점`),D(n.rebounds)!==null&&i.push(`${n.rebounds}리바운드`),D(n.assists)!==null&&i.push(`${n.assists}도움`),a=(D(n.assists)||0)>=(D(n.rebounds)||0)?"볼 배급과 공격 조율":"득점·리바운드 기여"):(D(n.points)!==null&&i.push(`${n.points}득점`),D(n.blocks)!==null&&i.push(`${n.blocks}블로킹`),D(n.serves)!==null&&i.push(`${n.serves}서브`),a=(D(n.blocks)||0)>0?"공격과 네트 수비의 중심":"공격 득점의 중심");return{name:e.name,position:e.position||null,characteristic:a,facts:i,status:e.status||null}}function ht(e,t){const n=[],i=D(e?.goals_per_game??e?.points_per_game??t?.avg_scored),a=D(e?.conceded_per_game??t?.avg_conceded);return i!==null&&a!==null&&(i-a>=.35?n.push("공격 생산력이 실점 억제보다 두드러진 팀"):a-i>=.35?n.push("공수 균형 회복이 필요한 팀"):n.push("득점과 실점 흐름이 비교적 균형적인 팀")),t?.trend==="상승"&&n.push("최근 경기 내용이 상승세"),t?.trend==="하락"&&n.push("최근 경기 내용이 하락세"),t?.streak&&n.push(String(t.streak)),n}function xt(e){const t=e?.선발||{},n=t.team_profiles||{};return["home","away"].map(i=>{const a=n[i]||{},l=a.record||t.teams?.[i]||{},o=i==="home"?e?.form_home:e?.form_away;let d=a.key_players||t.key_players?.[i]||[];if(e?.sport==="bs"){const h=t[`${i}_detail`]||(t[i]?{name:t[i]}:null),x=t.lineups?.[i]||[];d=[h,...x].filter(Boolean)}const r=d.map(h=>mt(h,e?.sport)).filter(Boolean).slice(0,3),c=[...new Set([...a.characteristics||[],...ht(l,o)])].slice(0,5),u=(a.unavailable||t.unavailable?.[i]||[]).filter(h=>h?.name).slice(0,3);return{side:i,team:e?.[i],characteristics:c,players:r,unavailable:u}})}function pt(e){return(e||[]).map(t=>{const n=t.characteristics.slice(0,2).join(" · "),i=t.players.slice(0,2).map(a=>`${a.name}(${a.characteristic}${a.facts.length?` · ${a.facts.join(" · ")}`:""})`).join(", ");return[t.team,n,i].filter(Boolean).join(" — ")}).filter(t=>t.split(" — ").length>1).join(" / ")}function bt(e){return(e||[]).map(t=>{const n=(t.players||[]).slice(0,2).map(i=>i.name).filter(Boolean);return n.length&&t.team?`${t.team}: ${n.join(" · ")}`:null}).filter(Boolean).join(" / ")}const j=e=>e==null||typeof e=="string"&&!e.trim()?null:Number.isFinite(Number(e))?Number(e):null,Q=(e,t)=>{const n=String(e||"").trim().at(-1);return!n||!/[가-힣]/.test(n)?`${e}${t[1]}`:`${e}${(n.charCodeAt(0)-44032)%28?t[0]:t[1]}`};function ft(e,t=null){const n=e?.options||[],i=$s(e,n),a=t?t===i?t:null:i,l=hs(e,a);if(l.action!=="market_reference"||!l.option)return{outcome:null,side:null,probability:null,marketProbability:null,shadowProbability:null,margin:0,market:null,label:"",headline:us(l),decision:l};const o=a?.선택||null,d=a?.market||null,r=String(d||""),c=r.includes("언더오버"),u=r.includes("핸디캡"),h=["승①패","승⑤패"].includes(d),x=["전반승무패","전반승패"].includes(d),b=["승무패","승패"].includes(d),f=/무/.test(String(o||"")),p=/원정|패/.test(String(o||""))||o===e?.away,N=f?"무승부":p?e?.away:e?.home,F=b?N:null,$=l.probability.final,w=l.probability.market,k=l.recommendationPriority==="reversal"?"전환 픽 · ":"";let v;return c?v=`${k}${a?.label||"기준점"} ${o}`:u?v=`${k}${a?.label||"핸디캡"} ${o}`:h?v=`${k}${d} ${o}`:x?v=f?`${k}전반 무승부`:`${k}전반 ${N} 우세`:b?v=f?`${k}팽팽함`:`${k}${F} 우세`:v=`${k}${[d,a?.label,o].filter(Boolean).join(" ")}`,{outcome:o,side:F,probability:$,marketProbability:w,shadowProbability:l.probability.aiCandidate,margin:l.probability.aiDeltaApplied,market:d,label:a?.label||"",headline:v,decision:l}}const We=e=>{const t=String(e||"").match(/^(\d+)-(\d+)$/);if(!t)return null;const n=Number(t[1])+Number(t[2]);return n?Number(t[1])/n:null},ze=e=>{const t=String(e?.last10||"").match(/(\d+)승(?:\s*(\d+)무)?\s*(\d+)패/);if(!t)return null;const n=Number(t[1])+Number(t[2]||0)+Number(t[3]);return n?(Number(t[1])+Number(t[2]||0)*.5)/n:null};function me(e,t,n,i,a=0){return t===null||n===null||Math.abs(t-n)<=a?{label:e,side:null,state:"중립"}:{label:e,side:t>n?i.home:i.away,state:"우세"}}const gs={"최근 10경기":"최근 성적","최근 공수":"최근 득실","홈·원정":"홈·원정 성적","시즌 순위":"시즌 성적"},ye=e=>e.map(t=>gs[t.label]||t.label).join("·");function jt(e,t,n){const i=e.side,a=e?.decision?.recommendationPriority==="reversal",l=t.filter(r=>r.side===i),o=t.filter(r=>r.side&&r.side!==i),d=o[0]?.side;return n==="일치"?ye(l)+"에서 모두 "+Q(i,["이","가"])+" 앞선다. "+(a?"역배 전환 관문도 같은 방향을 가리켜 최종 픽을 교체했다.":"최근 기록도 최종 추천인 "+i+" 쪽을 뒷받침한다."):n==="엇갈림"?ye(l)+"에서는 "+Q(i,["이","가"])+" 앞서고, "+ye(o)+"에서는 "+Q(d,["이","가"])+" 낫다. "+(a?"엇갈림을 숨기지 않으며 전환 방향의 확률은 해당 역배 시장확률로 표시한다.":"엇갈리는 자료를 위험 신호로 함께 표시한다."):n==="반대"?"확인되는 최근 기록은 "+Q(d,["이","가"])+" 앞선다. "+(a?"전환 픽과 반대되는 자료이므로 위험 신호로 함께 남긴다.":"최종 추천과 반대되는 자료이므로 위험 신호로 함께 남긴다."):a?"비교할 최근 기록은 부족하다. 구조 모델 전환 관문으로 "+i+" 쪽을 최종 선택했지만 표시 확률은 해당 역배의 시장확률이다.":"비교할 최근 기록이 충분하지 않아 배당에 반영된 평가를 바탕으로 "+i+" 쪽을 비교 후보로 둔다."}function Nt(e,t){if(!t?.side||t.side==="무승부"||!["승무패","승패"].includes(t.market))return null;const n=e?.form_home||{},i=e?.form_away||{},a=[],l=ze(n),o=ze(i);(l!==null||o!==null)&&a.push(me("최근 10경기",l,o,e,.05));const d=j(n.avg_scored)!==null&&j(n.avg_conceded)!==null?j(n.avg_scored)-j(n.avg_conceded):null,r=j(i.avg_scored)!==null&&j(i.avg_conceded)!==null?j(i.avg_scored)-j(i.avg_conceded):null;(d!==null||r!==null)&&a.push(me("최근 공수",d,r,e,.2));const c=We(n.home),u=We(i.away);(c!==null||u!==null)&&a.push(me("홈·원정",c,u,e,.04));const h=j(e?.선발?.teams?.home?.rank),x=j(e?.선발?.teams?.away?.rank);(h!==null||x!==null)&&a.push(me("시즌 순위",h===null?null:-h,x===null?null:-x,e));const b=a.filter($=>$.side),f=b.filter($=>$.side===t.side).length,p=b.filter($=>$.side!==t.side).length,N=p===0?f?"일치":"자료 부족":f===0?"반대":"엇갈림",F=jt(t,a,N);return{modelSide:t.side,signals:a,support:f,oppose:p,state:N,narrative:F}}function gt(e,t){const n=e?.stats||e||{},i=[];return t==="bs"?(j(n.ops)!==null&&i.push(`OPS ${j(n.ops).toFixed(3)}`),j(n.home_runs)!==null&&i.push(`${n.home_runs}홈런`),j(n.rbi)!==null&&i.push(`${n.rbi}타점`)):t==="sc"?(j(n.goals)!==null&&i.push(`${n.goals}골`),j(n.assists)!==null&&i.push(`${n.assists}도움`),j(n.key_passes)!==null&&i.push(`키패스 ${n.key_passes}`)):t==="bk"?(j(n.points)!==null&&i.push(`${n.points}점`),j(n.rebounds)!==null&&i.push(`${n.rebounds}리바운드`),j(n.assists)!==null&&i.push(`${n.assists}도움`)):(j(n.points)!==null&&i.push(`${n.points}득점`),j(n.attacks)!==null&&i.push(`공격 ${n.attacks}`),j(n.blocks)!==null&&i.push(`블로킹 ${n.blocks}`)),i.join(" · ")||e?.position||"주요 선수"}function Ye(e,t){const n=e?.stats||e||{};return t==="bs"?j(n.ops)??(j(n.home_runs)||0)/100:t==="sc"?(j(n.goals)||0)*4+(j(n.assists)||0)*3+(j(n.key_passes)||0)/10:t==="bk"?j(n.efficiency)??j(n.points)??0:j(n.points)??0}const _t=(e,t,n,i)=>e?.name?{team:t,name:e.stats?.name||e.name,role:e.position||"주요 선수",detail:gt(e,n)}:null;function vt(e){const t=e?.선발||{},n=dt(e),i=n.players.map(d=>({team:d.team,name:d.name,role:d.position,detail:d.role,profileUrl:d.profileUrl})),a=n.note?[n.note]:[];if(e?.sport==="bs"){const d=i.filter(r=>r.role==="선발투수");d.length===2&&a.unshift(`${d[0].team} ${Q(d[0].name,["과","와"])} ${d[1].team} ${d[1].name}의 선발 맞대결이다.`)}for(const d of["home","away"]){if(i.some(b=>b.team===e?.[d]))continue;const r=t.key_players?.[d]||[],c=t.lineups?.[d]||[],h=[...r.length?r:c].sort((b,f)=>Ye(f,e?.sport)-Ye(b,e?.sport))[0],x=_t(h,e?.[d],e?.sport);x&&i.push(x)}const l=["home","away"].flatMap(d=>(t.unavailable?.[d]||[]).map(r=>({...r,team:e?.[d]})));if(l.length){const d=l.slice(0,2).map(r=>{const c=[r.reason_label||r.status,r.impact_label&&`영향 ${r.impact_label}`].filter(Boolean).join(" · ");return`${r.team} ${r.name}${c?`(${c})`:""}`}).join(", ");a.push(`${d}${l.length>2?` 외 ${l.length-2}명`:""}의 출전 여부가 변수다.`)}const o=t.availability_summary;if(o?.leans){const d=o.leans==="home"?e?.away:e?.home;a.push(`확인된 명단 기준으로는 ${d} 쪽 전력 손실 부담이 더 크다. 이 값은 과거 검증 전이라 모델 확률에는 직접 더하지 않았다.`)}return{featuredPlayers:i,playerNotes:a}}const de=e=>{const t=j(e);return t===null?null:new Intl.NumberFormat("ko-KR",{maximumFractionDigits:1}).format(t)};function Ve(e,t){if(!e||!t)return null;const n=[];return t.last10?n.push(`최근 10경기 ${t.last10}`):(j(t.w)!==null||j(t.l)!==null)&&n.push(`${j(t.w)||0}승${j(t.d)?` ${j(t.d)}무`:""} ${j(t.l)||0}패`),t.streak&&n.push(`${t.streak} 흐름`),t.trend==="상승"&&n.push("경기 내용도 상승세"),t.trend==="하락"&&n.push("최근 경기 내용은 하락세"),n.length?`${Q(e,["은","는"])} ${n.join(", ")}다`:null}function yt(e){const t=String(e?.form_home?.home||""),n=String(e?.form_away?.away||""),i=l=>{const o=l.match(/^(\d+)-(\d+)$/);return o?`${o[1]}승 ${o[2]}패`:l};if(!t&&!n)return null;const a=[];if(t&&a.push(`${e.home}의 홈 성적은 ${i(t)}`),n&&a.push(`${e.away}의 원정 성적은 ${i(n)}`),t&&n){const l=t.match(/^(\d+)-(\d+)$/),o=n.match(/^(\d+)-(\d+)$/),d=l?Number(l[1])/(Number(l[1])+Number(l[2])):null,r=o?Number(o[1])/(Number(o[1])+Number(o[2])):null,c=d!==null&&r!==null&&Math.abs(d-r)>=.08?` 이 조건에서는 ${d>r?e.home:e.away} 쪽이 더 안정적이었다.`:" 장소에 따른 뚜렷한 우위는 크지 않다.";return`${a.join(", ")}다.${c}`}return`${a[0]}다. 이번 경기 장소에서의 적응력을 함께 볼 필요가 있다.`}function kt(e){const t=j(e?.form_home?.avg_scored),n=j(e?.form_away?.avg_scored),i=j(e?.form_home?.avg_conceded),a=j(e?.form_away?.avg_conceded);if(t===null&&n===null&&i===null&&a===null)return null;const l=[];(t!==null||i!==null)&&l.push(`${e.home} ${t!==null?`${de(t)}득점`:""}${t!==null&&i!==null?"·":""}${i!==null?`${de(i)}실점`:""}`),(n!==null||a!==null)&&l.push(`${e.away} ${n!==null?`${de(n)}득점`:""}${n!==null&&a!==null?"·":""}${a!==null?`${de(a)}실점`:""}`);const o=[];t!==null&&n!==null&&(Math.abs(t-n)<.2?o.push("득점 생산력은 비슷하다"):o.push(`${t>n?e.home:e.away}의 공격 전개가 더 꾸준했다`)),i!==null&&a!==null&&(Math.abs(i-a)<.2?o.push("실점 억제력도 큰 차이가 없다"):o.push(`${i{const c=[];return j(r.rank)!==null&&c.push(`${r.rank}위`),j(r.wins)!==null&&c.push(`${r.wins}승${j(r.draws)?` ${r.draws}무`:""}${j(r.losses)!==null?` ${r.losses}패`:""}`),j(r.points)!==null&&c.push(`${r.points}점`),c.join(" · ")},o=[l(t)&&`${e.home} ${l(t)}`,l(n)&&`${e.away} ${l(n)}`].filter(Boolean),d=i!==null&&a!==null?i===a?"현재 순위상 두 팀의 간격은 없다.":`누적 성적과 시즌 안정감은 ${i`${l.team} ${l.name}(${l.role}${l.detail&&l.detail!==l.role?` · ${l.detail}`:""})`),i=e?.playerNotes?.[0];return`${n.length?`이번 경기에서 먼저 볼 선수는 ${n.join(", ")}다. 경기 운영과 승부처에서 이들의 영향력을 확인해야 한다.`:""}${i?` ${i}`:""}`.trim()}function St(e,t){const n=String(t?.market||""),a=t?.decision?.recommendationPriority==="reversal"?"전환 최종 픽으로 정했다":"최종 예상 적중 후보로 정했다";if(n.includes("언더오버")){const p=String(t.outcome||"").includes("언더"),N=n.startsWith("전반"),F=j(e?.form_home?.avg_scored),$=j(e?.form_away?.avg_scored),w=F!==null&&$!==null?F+$:null;return`${!N&&w!==null?`두 팀의 최근 경기당 득점 합은 ${de(w)}점이다.`:`${N?"전반":"경기"} 득점 기준의 시장확률을 사용했다.`} ${t.label||"발매 기준점"}에서 ${p?"득점이 크게 벌어지지 않는":"공격 전개가 이어지는"} 쪽을 ${a}.`}if(n.includes("핸디캡"))return`${t.label||"발매 핸디캡"} 기준에서 ${t.outcome} 쪽을 ${a}. 이는 핸디캡 적용 후 적중 방향이며 실제 승리 예측과는 다르다.`;if(["승①패","승⑤패"].includes(t?.market))return`${t.market}의 ${t.outcome} 쪽을 ${a}. 이 판정은 승패만이 아니라 발매된 점수 차 조건까지 포함한다.`;if(n.startsWith("전반"))return`전반 시장에서 ${t.outcome} 쪽을 ${a}. 전반 결과와 경기 최종 결과는 별개의 마켓이다.`;if(t?.side==="무승부")return`${verdict} 양쪽의 강점이 엇갈리는 접전 시나리오를 기준으로 삼았다.`;const l=t?.side;if(!l)return t?.modelAvailable?"경기 모델 추천 기준을 통과한 선택이 없다. 경기 모델 추천에서 제외한다.":"이 경기에는 모델확률이 없다. 경기 모델 추천에서 제외한다.";const o=l===e?.home,d=o?e?.form_home:e?.form_away,r=o?d?.home:d?.away,c=[];d?.streak&&/연승/.test(d.streak)&&c.push(d.streak),d?.trend==="상승"&&c.push("최근 상승 흐름"),r&&c.push(o?"홈 경기 경쟁력":"원정 경기 경쟁력");const u=c.length?c.slice(0,2).join("과 "):"현재 확인된 경기 정보",h=o?e?.away:e?.home,x=j(d?.avg_conceded),b=j((o?e?.form_away:e?.form_home)?.avg_scored),f=x!==null&&b!==null&&b>x?` 다만 ${h}의 최근 득점 생산력은 경기를 쉽게 벌리지 못하게 할 변수다.`:" 다만 경기 초반 실점 여부와 선발 구성은 흐름을 바꿀 수 있다.";return`${Q(l,["이","가"])} ${Q(u,["을","를"])} 바탕으로 주도권을 조금 더 오래 가져갈 가능성을 높게 봤다.${f}`}function Ft(e,t,n,i){const a=[],l=[Ve(e?.home,e?.form_home),Ve(e?.away,e?.form_away)].filter(Boolean);l.length&&a.push(`최근 분위기 — ${l.join(". ")}.`);const o=kt(e);o&&a.push(`공격·수비 균형 — ${o}`);const d=yt(e);d&&a.push(`홈·원정 조건 — ${d}`);const r=$t(e);r&&a.push(`시즌 위치 — ${r}`);const c=wt(n);c&&a.push(`선수 변수 — ${c}`);const u=pt(i);return u&&a.push(`팀·선수 프리뷰 — ${u}`),a.push(`판정 시나리오 — ${St(e,t)}`),[...new Set(a)].slice(0,8)}function Et(e,t=null,n=""){const i=ft(e,t),a=Nt(e,i),l=vt(e),o=xt(e),d=Ft(e,i,l,o),r=e?.선발?.lineup_status?.state==="announced"||e?.sport==="bs"&&e?.선발?.home,u=[...(a?.signals||[]).filter(h=>h.side&&h.side!==i.side).map(h=>`${gs[h.label]||h.label}은 ${h.side} 쪽이 앞선다.`),...r?[]:["경기 직전 선발·출전 명단이 바뀌면 예상 흐름도 달라질 수 있다."]];return{prediction:i,decision:i.decision,signalSummary:a,reasons:d,commentary:String(n||"").trim(),...l,teamPreviews:o,cautions:[...new Set(u)]}}const Ze=["confidence","odds","overround","market_gap","price_distance","three_way","handicap","totals","first_half","baseball","basketball","volleyball","soccer"],ie=e=>{const t=Number(e);return Number.isFinite(t)?t:null};function Pt(e){const t=`${e?.sport||""} ${e?.league||""}`.toLowerCase(),n=Number(["baseball","야구","kbo","mlb","npb"].some(l=>t.includes(l))),i=Number(["basketball","농구","nba","kbl","wnba"].some(l=>t.includes(l))),a=Number(["volleyball","배구","v리그"].some(l=>t.includes(l)));return[n,i,a,+!(n||i||a)]}function At(e,t){const n=ie(e?.market_prob),i=ie(e?.odds),a=ie(e?.overround),l=ie(t?.odds_min),o=ie(t?.odds_max),d=ie(t?.target_odds);if(!(n>0&&n<1)||!(i>1)||!(a>0)||l==null||o==null||!(d>1)||i=o)return null;const r=String(e?.market||""),[c,u,h,x]=Pt(e);return[Math.log(n/(1-n)),Math.log(i),Math.max(0,a-1),Math.max(0,ie(e?.market_gap)||0),Math.abs(Math.log(i/d)),+(Number(e?.n_way||2)===3),Number(r.includes("핸디")),Number(r.includes("언더오버")),Number(r.startsWith("전반")),c,u,h,x]}function Dt(e,t,n=3){const i=t?.genome,a=(t?.genomes||[]).filter(r=>r&&typeof r=="object"),l=t?.constraints;if(!i&&!a.length||!l)return[];const o=(e||[]).flatMap(r=>{const c=At(r,l);return c?[{candidate:r,features:c}]:[]});if(!o.length)return[];let d;if(a.length){const r=o.map(({features:h})=>a.map(x=>h.reduce((b,f,p)=>b+f*Number(x[Ze[p]]||0),0))),c=a.map((h,x)=>r.reduce((b,f)=>b+f[x],0)/r.length),u=a.map((h,x)=>{const b=r.reduce((p,N)=>p+(N[x]-c[x])**2,0)/r.length,f=Math.sqrt(b);return f<1e-9?1:f});d=r.map(h=>h.reduce((x,b,f)=>x+(b-c[f])/u[f],0)/a.length)}else d=o.map(({features:r})=>r.reduce((c,u,h)=>c+u*Number(i[Ze[h]]||0),0));return o.map(({candidate:r},c)=>({...r,evolution_score:Number(d[c].toFixed(6))})).sort((r,c)=>c.evolution_score-r.evolution_score||Number(c.market_prob||0)-Number(r.market_prob||0)||Number(r.overround||99)-Number(c.overround||99)||String(r.kickoff_at||r.date||"").localeCompare(String(c.kickoff_at||c.date||""))).slice(0,Math.max(0,Number(n)||0))}function Lt(e,t){if(!e?.profiles)return e||null;const n=Object.fromEntries(Object.entries(e.profiles).map(([i,a])=>{const o=a?.historical_status==="rejected_in_historical_audit"?[]:Dt(t,a?.rule,3);return[i,{...a,selected:o[0]||null,alternatives:o.slice(1)}]}));return{...e,profiles:n}}const je=540*60*1e3,Le=1440*60*1e3,Mt=12,Ct=/(\d{2})\.(\d{2}).*?(\d{2}):(\d{2})/,Je=1800*1e3;function ee(e,t){const n=Date.parse(e?.kickoff_at||"");if(Number.isFinite(n))return n;const i=String(e?.date||"").match(Ct);if(!i)return Number.NaN;const[,a,l,o,d]=i.map(Number),r=Number(t)||new Date().getUTCFullYear(),c=Number(e?.round)===1&&a===12?r-1:r;return Date.UTC(c,a-1,l,o,d)-je}const Tt=(e,t)=>e?.event_key||`${ee(e,t)}|${e?.home||""}|${e?.away||e?.match||""}`;function Ot(e,t,n){return ee(e,n)-ee(t,n)||Number(e.overround||99)-Number(t.overround||99)||Number(t.odds||0)-Number(e.odds||0)}function Rt(e){const t=[e?.solo,...(e?.plans||[]).flatMap(i=>i.picks||[])].filter(Boolean),n=new Set;return t.filter(i=>{const a=`${Tt(i,e?.year)}|${i.market}|${i.market_label}|${i.sel}`;return n.has(a)?!1:(n.add(a),!0)})}const he=e=>Math.floor((e+je)/Le),It=e=>(he(e)+1)*Le-je;function _s(e,t){if(!e)return{candidates:[],window:"today"};const n=e.candidates?.length?e.candidates:Rt(e),i=ms(n).filter(d=>{const r=ee(d,e.year);return Number.isFinite(r)&&r>t}),a=he(t),l=i.filter(d=>he(ee(d,e.year))===a);if(l.length)return{candidates:l,window:"today"};const o=(a+1)*Le-je+Mt*60*60*1e3;return{candidates:i.filter(d=>{const r=ee(d,e.year);return he(r)===a+1&&r0?n:Je;if(!e)return i;const a=_s(e,t).candidates.map(o=>ee(o,e.year)).sort((o,d)=>o-d)[0],l=Number.isFinite(a)?a:It(t);return Math.min(i,Math.max(1e3,l-t+1e3))}function Gt(e,t=Date.now()){if(!e)return{plans:[],solo:null,candidates:[],next:null};const n=_s(e,t),i=n.candidates.sort((a,l)=>Ot(a,l,e.year));return{...e,plans:[],solo:null,recommendation:{action:"disabled",recommended_target:null,why:"자동 조합 추천을 사용하지 않는다"},candidates:i,window:n.window,next:i[0]||null,evolutionary_selector:Lt(e.evolutionary_selector,i)}}const Ht=10800*1e3;function Kt(...e){let t=null,n=-1/0;return e.flat().forEach(i=>{const a=Date.parse(i||"");Number.isFinite(a)&&a>n&&(t=i,n=a)}),t}function Ee(e,t=Date.now()){const n=Date.parse(e||"");return!Number.isFinite(n)||t-n>=Ht}function Ut({staticGeneratedAt:e,liveGeneratedAt:t,liveChecked:n,now:i=Date.now()}){const a=Kt(t,e);return!n&&Ee(e,i)?"checking":Ee(a,i)?"stale":"fresh"}function qt(e,{generatedAt:t,year:n,now:i=Date.now()}={}){if(Ee(t,i))return"데이터 갱신 지연";const a=ee(e,n);return Number.isFinite(a)&&a<=i?"상태 확인 불가":"배당 발표 전"}const ke=e=>{const t=Number(e?.시장확률);return Number.isFinite(t)&&t>0&&t<1?t:null};function Wt(e=[]){const t=e.filter(r=>{const c=Number(r?.배당);return String(r?.market||"").trim()!=="홀짝"&&Number.isFinite(c)&&c>1&&ke(r)!==null});if(!t.length)return null;const n=t.filter(r=>["승패","승무패"].includes(String(r.market).trim())),a=[...n.length?n:t].sort((r,c)=>ke(c)-ke(r)||Number(r.배당)-Number(c.배당)||String(r.selection_id||"").localeCompare(String(c.selection_id||"")))[0],l=ws(t),o=l||a,d=l?Ss(l):-1;return{option:o,probability:Fs(o),recommendation:l?d===1?"recommend":"weak":"watch"}}function zt(e){if(!e)return"";if(e.tie)return`가장 나은 선택지들이 같은 ${e.g?.bin||"배당"} 구간에 있어 한쪽을 고를 실측 근거가 없다.`;const t=e.mode==="roi"?"손실 최소 기준에서 과거 수익률이 가장 덜 나쁜 조합":"적중 우선 기준에서 가장 낮은 배당 구간",n=e.exact?`${e.o.market}·${e.g.bin}의 과거 실측`:`${e.g.bin} 배당 구간의 과거 실측`,i=e.hit==null?"":` 적중률 ${(e.hit*100).toFixed(1)}%,`,a=e.roi==null?"":` 수익률 ${(e.roi*100).toFixed(1)}%`;return`${e.o.선택} ${Number(e.o.배당).toFixed(2)}를 표시한 직접 이유는 ${t}이기 때문이다. ${n}은${i}${a}였다. 선수·날씨 정보는 아직 이 선택 점수에 넣지 않았다.`}function Yt(e){return e==="llm_rewritten"||e==="llm_cache"?"LLM 문장 편집":String(e||"").startsWith("template_")?"검증 템플릿 · LLM 대체 동작":e==="disabled_no_key"?"검증 템플릿 · LLM 키 미설정":"경기 해설"}const Vt=e=>String(e||"").replace(/\s+/g,"").toLowerCase(),Zt=e=>[e?.sport,e?.league,e?.date,e?.home,e?.away].map(Vt).join("|"),Xe=e=>[(e?.options||[]).filter(n=>Number(n?.배당)>1).length>0?1:0,e?.status==="배당대기"?0:1,Number(e?.round)||0],Jt=(e,t)=>{const n=Xe(e),i=Xe(t);for(let a=0;ai[a];return!1};function Xt(e=[]){const t=new Map;return e.forEach(n=>{const i=Zt(n),a=t.get(i);(!a||Jt(n,a))&&t.set(i,n)}),[...t.values()]}function Qe(e,t="",n=0){const i=(e.options||[]).filter(d=>!t||d.market===t),a=e.prediction_record,l=a&&(!t||a.market===t)?a:null;if(t&&!i.length&&!l)return!1;if(!n)return!0;const o=i.map(d=>Number(d.배당));return l&&o.push(Number(l.odds)),o.some(d=>Number.isFinite(d)&&d>1&&d<=n)}const Qt="https://proto-odds-collector.fly.dev/api/live-scores",en="https://proto-odds-collector.fly.dev/api/live-odds",sn="https://proto-odds-collector.fly.dev/api/today-recommendations",tn="https://proto-odds-collector.fly.dev/api/picks",nn="https://proto-odds-collector.fly.dev/api/loss-grades";function fe(e,t){const[n,i]=P.useState({data:null,checked:!1});return P.useEffect(()=>{let a=!1;const l=()=>fetch(`${e}?${Date.now()}`,{cache:"no-store",headers:{"Cache-Control":"no-cache"}}).then(r=>r.ok?r.json():Promise.reject(new Error(`HTTP ${r.status}`))).then(r=>{a||i({data:r,checked:!0})}).catch(()=>{a||i(r=>({...r,checked:!0}))});l();const o=setInterval(l,t),d=()=>{document.visibilityState==="visible"&&l()};return document.addEventListener("visibilitychange",d),window.addEventListener("focus",l),window.addEventListener("online",l),()=>{a=!0,clearInterval(o),document.removeEventListener("visibilitychange",d),window.removeEventListener("focus",l),window.removeEventListener("online",l)}},[e,t]),n}const an=()=>fe(Qt,15e3),ln=()=>fe(en,12e4),es={bs:"득점",sc:"골",bk:"득점",vl:"세트"};function rn(e){if(e?.score_forecast)return e.score_forecast;if(!["경기전","배당대기"].includes(e?.status)||e?._liveStarted)return null;const t=Number(e?.lam_home),n=Number(e?.lam_away);return!Number.isFinite(t)||!Number.isFinite(n)?null:{status:"shadow",affects_probability:!1,expected_scores:{home:t,away:n,total:t+n,unit:es[e.sport]||"득점"},contract:{score_unit_label:es[e.sport]||"득점"},top_scorelines:[]}}function on(e){const t=new Map;return(e?.games||[]).forEach(n=>{const i=[n.home,...n.home_alias||[]].filter(Boolean),a=[n.away,...n.away_alias||[]].filter(Boolean);i.forEach(l=>a.forEach(o=>t.set(`${l}|${o}|${n.md}`,n)))}),t}function cn(e,t){const n=Object.values(t?.markets?.[String(e?.round)]||{}).filter(a=>a?.home===e?.home&&a?.away===e?.away&&a?.date===e?.date),i=t?.history?.[String(e?.round)]||{};return n.flatMap(a=>(i[String(a.game_no)]||[]).map(l=>({...l,gameNo:String(a.game_no)})))}function In(){const{data:e,at:t}=Hs({grades:nn},3e5),{grades:n}=e,{data:i,checked:a}=fe(tn,6e4),l=i,{data:o,checked:d}=ln(),{data:r}=fe(sn,12e4),{data:c,checked:u}=an(),h=c,x=P.useMemo(()=>on(h),[h]),b=P.useMemo(()=>{if(!l)return null;const N=F=>(F||[]).map($=>{const w=un(x,$),k=Ae($),v=w||k?$:Gs($,o?.odds?.[String($.round)],o?.generated_at||null,o?.markets?.[String($.round)]),T=cn($,o),g={...v,_marketHistory:T,_officialMarkets:o?.markets?.[String($.round)]},E=k?{...g,_decisionFrozen:!0,_liveOddsChanged:!1}:g;return w?{...E,_liveState:w,_liveStarted:!0,_liveFeedAt:h?.generated_at||null}:E});return{...l,live:N(l.live),past:N(l.past)}},[l,o,x]);if(a&&!b)return s.jsx($e,{children:s.jsx(vs,{children:"데이터를 불러오지 못했습니다"})});if(!b)return s.jsx($e,{children:s.jsx(dn,{})});const p=Ut({staticGeneratedAt:b.generated_at,liveGeneratedAt:o?.generated_at,liveChecked:d})==="stale";return s.jsx($e,{meta:hn(l,t),children:s.jsx("section",{id:"match-list",children:s.jsx(bn,{data:b,grades:n,caps:n?.odds_caps,stale:p,today:r,liveGeneratedAt:h?.generated_at,liveChecked:u})})})}function dn(){const[e,t]=P.useState(0);P.useEffect(()=>{const i=Date.now(),a=setInterval(()=>t(Math.floor((Date.now()-i)/1e3)),1e3);return()=>clearInterval(a)},[]);const n=e>=12?"경기 데이터가 많아 정리하는 중입니다. 잠시만 기다려 주세요.":e>=4?"최신 배당과 저장된 사전 예측을 맞추고 있습니다.":"오늘 경기와 추천 픽을 불러오고 있습니다.";return s.jsxs("section",{className:"match-loading",role:"status","aria-live":"polite","aria-busy":"true",children:[s.jsxs("div",{className:"match-loading-head",children:[s.jsxs("div",{children:[s.jsx("span",{className:"loading-spinner","aria-hidden":"true"}),s.jsx("b",{children:"경기 데이터 불러오는 중"})]}),s.jsx("small",{children:e?`${e}초`:"연결 중"})]}),s.jsx("p",{children:n}),s.jsx("div",{className:"match-loading-grid","aria-hidden":"true",children:[0,1,2].map(i=>s.jsxs("div",{className:"match-loading-card",children:[s.jsx("i",{className:"loading-line is-short"}),s.jsx("i",{className:"loading-line is-title"}),s.jsxs("div",{children:[s.jsx("i",{className:"loading-chip"}),s.jsx("i",{className:"loading-chip"}),s.jsx("i",{className:"loading-chip"})]}),s.jsx("i",{className:"loading-line"}),s.jsx("i",{className:"loading-line is-medium"})]},i))})]})}function un(e,t){const n=e.get(`${t.home}|${t.away}|${String(t.date||"").slice(0,5)}`);return!n||n.status==="BEFORE"?null:n}const mn=new Intl.DateTimeFormat("ko-KR",{timeZone:"Asia/Seoul",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hourCycle:"h23"});function ss(e){const t=new Date(e);if(!Number.isFinite(t.getTime()))return String(e||"-");const n=Object.fromEntries(mn.formatToParts(t).map(({type:i,value:a})=>[i,a]));return`${n.year}-${n.month}-${n.day} ${n.hour}:${n.minute}`}const hn=(e,t)=>`${(e.live||[]).length+(e.past||[]).length}경기 · 회차 ${(e.rounds||[]).join(", ")} · 승부식 · 갱신 ${ss(e.generated_at)} KST`+(t?` · 확인 ${ss(t)} KST`:"");function $e({children:e,meta:t}){return s.jsxs("div",{className:"mx-auto max-w-[1180px] px-5 pb-20",children:[s.jsx(ys,{current:"markets.html"}),s.jsxs("header",{className:"market-header",children:[s.jsxs("div",{children:[s.jsx("h1",{children:"오늘 경기·배당 분석"}),s.jsx("p",{children:"오늘의 판단을 먼저 보고, 필요한 경기만 열어 흐름·선수·반대 근거를 확인합니다."})]}),t&&s.jsx("div",{className:"market-meta",children:t})]}),e]})}const vs=({children:e})=>s.jsx("div",{className:"py-7 text-center text-[13px] text-ink3",children:e});function xn(e){const[t,n]=P.useState(()=>Date.now());return P.useEffect(()=>{let i;const a=()=>{const o=Date.now();n(o),clearTimeout(i),i=setTimeout(a,Bt(e,o))},l=()=>{document.visibilityState==="visible"&&a()};return a(),document.addEventListener("visibilitychange",l),window.addEventListener("focus",a),window.addEventListener("pageshow",a),window.addEventListener("online",a),()=>{clearTimeout(i),document.removeEventListener("visibilitychange",l),window.removeEventListener("focus",a),window.removeEventListener("pageshow",a),window.removeEventListener("online",a)}},[e]),t}const pn=[["","전체"],["live","진행 중"],["upcoming","예정"],["finished","종료"],["pending","결과 확인 중"]];function bn({data:e,grades:t,caps:n,stale:i,today:a,liveGeneratedAt:l,liveChecked:o=!1}){const[d,r]=P.useState(null),[c,u]=P.useState({st:"",lg:"",mk:"",rd:"",q:"",dt:"today"}),[h,x]=P.useState(()=>Date.now());P.useEffect(()=>{let m;const _=()=>x(Date.now()),A=()=>{clearTimeout(m),m=setTimeout(()=>{_(),A()},Ms())},y=()=>{document.visibilityState==="visible"&&(_(),A())};return A(),document.addEventListener("visibilitychange",y),window.addEventListener("focus",y),()=>{clearTimeout(m),document.removeEventListener("visibilitychange",y),window.removeEventListener("focus",y)}},[]);const[b,f]=P.useState(0),p=P.useMemo(()=>Xt([...e.live||[],...e.past||[]]),[e]),N=xn(a),F=P.useMemo(()=>Ps(a,p),[a,p]);P.useMemo(()=>i?{...F,plans:[],solo:null,candidates:[]}:Gt(F,N),[F,N,i]);const $=P.useMemo(()=>As(F),[F]),w=c.dt==="today"?ge(0,h):c.dt==="tomorrow"?ge(1,h):"",k=m=>[...new Set(m)].filter(_=>_!=null&&_!==""),v=P.useMemo(()=>k(p.map(m=>m.league)).sort(),[p]),T=P.useMemo(()=>k(p.flatMap(m=>[...(m.options||[]).map(_=>_.market),m.prediction_record?.market])).sort(),[p]),g=P.useMemo(()=>k(p.map(m=>m.round)).sort((m,_)=>_-m),[p]),E=P.useMemo(()=>{const m=c.q.trim().toLowerCase();return p.filter(_=>(!c.st||J(_)===c.st)&&(!c.lg||_.league===c.lg)&&(!c.rd||String(_.round)===c.rd)&&(!w||String(_.date??"").slice(0,5)===w)&&(!m||[_.home,_.away,_.league].join(" ").toLowerCase().includes(m))).sort((_,A)=>{const y={live:0,pending:1,upcoming:2,finished:3};return y[J(_)]-y[J(A)]||String(_.date).localeCompare(String(A.date))})},[p,c,w,N]),L=Date.parse(l),W=o&&(!Number.isFinite(L)||N-L>600*1e3||L>N+5e3)&&p.some(m=>String(m.date||"").slice(0,5)===ge(0,N)&&le(m)!=null&&le(m)<=N&&J(m)!=="finished"),S=p.filter(m=>{const _=c.q.trim().toLowerCase();return(!c.lg||m.league===c.lg)&&(!c.rd||String(m.round)===c.rd)&&(!w||String(m.date??"").slice(0,5)===w)&&(!_||[m.home,m.away,m.league].join(" ").toLowerCase().includes(_))&&Qe(m,c.mk,b)}).reduce((m,_)=>{const A=J(_);return m[A]=(m[A]||0)+1,m},{}),C=m=>{u(_=>({..._,st:_.st===m?"":m}))},H=[];let M=null,K=0;for(const m of E){const _=(m.options||[]).filter(I=>!c.mk||I.market===c.mk),A=m.status==="배당대기";if(!Qe(m,c.mk,b))continue;const y=A||i||m._liveOddsChanged&&!Ae(m)?{option:null,membership:null}:Ds($,m.options||[],m.round),O=y.membership?.recommended===!0;K++;const U=J(m),Y=`${U} · ${m.league} · ${Oe(m.date)}`;if(Y!==M){M=Y;const I=Ls(m.date);H.push(s.jsxs("div",{className:"mt-[18px] mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold tracking-[.03em] text-ink3",children:[I&&s.jsx("span",{className:`rounded-[4px] px-[5px] py-[2px] text-[10px] ${I==="오늘"?"bg-ink text-paper":"border border-rule text-ink2"}`,children:I}),s.jsx("span",{className:`match-phase-tag is-${U}`,children:ps[U]}),s.jsxs("span",{children:[m.league," · ",Oe(m.date)]})]},`h${Y}${K}`))}H.push(s.jsx(gn,{g:m,opts:_,wait:A,grades:t,lv:m._liveState||null,stale:i,generatedAt:e.generated_at,year:e.year,todayMembership:y.membership,todayOption:y.option,highlightedToday:O,onSaveBet:(I,se)=>r({game:I,option:se})},`${m.league}${m.home}${m.away}${m.date}${K}`))}const G=b?(n||[]).find(m=>m.cap===b):null;return s.jsxs(s.Fragment,{children:[s.jsx(st,{games:[...e.live||[],...e.past||[]],today:a,currentToday:F,now:N}),s.jsxs("div",{className:"match-section-title",children:[s.jsx("h2",{children:"경기 목록"}),s.jsxs("div",{className:"match-phase-counts","aria-label":"경기 상태 필터",children:[s.jsxs("button",{type:"button",className:"is-live","aria-pressed":c.st==="live",onClick:()=>C("live"),children:["진행 중 ",S.live||0]}),s.jsxs("button",{type:"button","aria-pressed":c.st==="pending",onClick:()=>C("pending"),children:["결과 확인 중 ",S.pending||0]}),s.jsxs("button",{type:"button","aria-pressed":c.st==="upcoming",onClick:()=>C("upcoming"),children:["예정 ",S.upcoming||0]}),s.jsxs("button",{type:"button","aria-pressed":c.st==="finished",onClick:()=>C("finished"),children:["종료 ",S.finished||0]}),s.jsxs("button",{type:"button","aria-pressed":!c.st,onClick:()=>C(""),children:["전체 ",Object.values(S).reduce((m,_)=>m+_,0)]})]})]}),i&&s.jsxs("div",{className:"mb-5 rounded-lg border border-amber-300 bg-amber-50 px-5 py-4 text-amber-950",children:[s.jsx("b",{className:"text-[15px]",children:"데이터 갱신이 지연되고 있습니다"}),s.jsx("p",{className:"mt-1 text-[13px] leading-6",children:"마지막 생성 이후 3시간이 지나 경기 판정을 멈췄습니다. 수집이 복구되면 경기별 픽과 추천 강도가 자동으로 다시 표시됩니다."})]}),W&&s.jsxs("div",{role:"status",className:"mb-4 rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-[13px] text-amber-950",children:[s.jsx("b",{children:"실시간 점수 갱신 지연"}),s.jsx("p",{children:"경기와 저장된 사전 픽은 유지합니다. 최신 점수가 확인될 때까지 현재 추정 확률은 표시하지 않습니다."}),Number.isFinite(L)&&s.jsxs("small",{children:["마지막 수집: ",new Date(L).toLocaleString("ko-KR",{timeZone:"Asia/Seoul"})," (KST)"]})]}),s.jsxs("div",{className:"filter-shell",children:[s.jsxs("div",{className:"filter-primary",children:[s.jsxs("div",{className:"date-switch","aria-label":"경기 날짜",children:[s.jsx("button",{type:"button","aria-pressed":c.dt==="today",onClick:()=>u({...c,dt:"today"}),children:"오늘"}),s.jsx("button",{type:"button","aria-pressed":c.dt==="tomorrow",onClick:()=>u({...c,dt:"tomorrow"}),children:"내일"}),s.jsx("button",{type:"button","aria-pressed":!c.dt,onClick:()=>u({...c,dt:""}),children:"전체"})]}),s.jsx("div",{className:"team-search",children:s.jsx("input",{type:"search",placeholder:"팀 또는 리그 검색",value:c.q,onChange:m=>u({...c,q:m.target.value})})})]}),s.jsxs("details",{className:"advanced-filters",children:[s.jsxs("summary",{children:[s.jsx("span",{children:"상세 조건"}),s.jsx("small",{children:"상태 · 리그 · 마켓 · 회차 · 배당 기준"})]}),s.jsxs("div",{className:"filter-grid",children:[s.jsxs("label",{children:["상태",s.jsx("select",{className:"filter-select",value:c.st,onChange:m=>u({...c,st:m.target.value}),children:pn.map(([m,_])=>s.jsx("option",{value:m,children:_},_))})]}),s.jsx(we,{label:"리그",v:c.lg,opts:v,on:m=>u({...c,lg:m}),cls:"filter-select"}),s.jsx(we,{label:"마켓",v:c.mk,opts:T,on:m=>u({...c,mk:m}),cls:"filter-select"}),s.jsx(we,{label:"회차",v:c.rd,opts:g,on:m=>u({...c,rd:m}),cls:"filter-select",suffix:"회차"}),s.jsxs("label",{children:["최저배당",s.jsxs("select",{className:"filter-select",value:b,onChange:m=>f(Number(m.target.value)),children:[s.jsx("option",{value:0,children:"제한 없음"}),(n||[]).filter(m=>m.cap).map(m=>s.jsxs("option",{value:m.cap,children:["≤",m.cap," · 적중 ",(m.hit*100).toFixed(0),"%"]},m.cap))]})]})]}),s.jsx("div",{className:"filter-actions",children:s.jsx("button",{type:"button",onClick:()=>{u({st:"",lg:"",mk:"",rd:"",q:"",dt:"today"}),f(0)},children:"조건 초기화"})})]})]}),G&&s.jsxs("p",{className:"mt-2 text-[11.5px] leading-[1.7] text-ink3",children:["최저배당 ≤",G.cap," 인 경기만 산 과거 실측 — 적중 ",s.jsxs("b",{className:"tnum text-ink",children:[(G.hit*100).toFixed(1),"%"]})," · ROI ",s.jsxs("b",{className:"tnum",children:[(G.roi*100).toFixed(2),"%"]})," ·",s.jsxs("span",{className:"opacity-70",children:[" ","(전체 경기의 ",(G.share*100).toFixed(0),"% · n=",G.n.toLocaleString(),")"]})]}),s.jsx("div",{children:H.length?H:s.jsxs(vs,{children:["조건에 맞는 경기가 없다",c.dt&&s.jsxs(s.Fragment,{children:[" — ",s.jsx("button",{className:"underline underline-offset-2",onClick:()=>u({...c,dt:""}),children:"모든 날짜 보기"})]})]})}),d&&s.jsx(tt,{draft:d,onClose:()=>r(null)})]})}const we=({label:e,v:t,opts:n,on:i,cls:a,suffix:l=""})=>s.jsxs("label",{className:"flex items-center gap-1.5",children:[e,s.jsxs("select",{className:a,value:t,onChange:o=>i(o.target.value),children:[s.jsx("option",{value:"",children:"전체"}),n.map(o=>s.jsxs("option",{value:o,children:[o,l]},o))]})]});function fn({live:e}){const t=e?.bases||{},n=[e?.balls,e?.strikes,e?.outs].every(a=>a!=null),i=[t.first,t.second,t.third].filter(a=>a?.occupied);return s.jsxs("div",{className:"live-baseball-situation","aria-label":"현재 야구 경기 상황",children:[s.jsx("div",{className:"base-state","aria-label":i.length?`주자 ${i.length}명`:"주자 없음",children:[["third","3루"],["second","2루"],["first","1루"]].map(([a,l])=>s.jsx("span",{className:t[a]?.occupied?"is-occupied":"",title:t[a]?.runner||`${l} 주자 없음`,children:l},a))}),s.jsxs("div",{children:[s.jsx("small",{children:"현재 타자"}),s.jsx("b",{children:e?.batter||"확인 중"}),n&&s.jsxs("span",{children:["B ",e.balls," · S ",e.strikes," · O ",e.outs]})]}),s.jsxs("div",{children:[s.jsx("small",{children:"현재 투수"}),s.jsx("b",{children:e?.pitcher||"확인 중"}),e?.next_batter&&s.jsxs("span",{children:["다음 ",e.next_batter]})]})]})}const jn=e=>{const t=new Date(e);return Number.isNaN(t.getTime())?"시각 미상":t.toLocaleString("ko-KR",{timeZone:"Asia/Seoul",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})};function Nn({rows:e}){if(!e?.length)return null;const n=[...e.reduce((i,a)=>{const l=`${a.gameNo}|${a.market}`;return i.has(l)||i.set(l,[]),i.get(l).push(a),i},new Map).entries()].map(([i,a])=>({key:i,entries:a})).filter(({entries:i})=>i.length>1);return n.length?s.jsxs("details",{className:"mb-3 rounded border border-rule2 bg-panel px-3 py-2 text-[12px]",children:[s.jsx("summary",{className:"cursor-pointer font-semibold text-ink",children:"배당·기준점 변경 이력"}),s.jsxs("div",{className:"mt-2 space-y-3",children:[n.map(({key:i,entries:a})=>s.jsxs("div",{children:[s.jsxs("b",{children:[a[0].market," · 게임번호 ",a[0].gameNo]}),s.jsx("ol",{className:"mt-1 space-y-1 text-ink2",children:a.map((l,o)=>{const d=a[o-1],r=l.probabilities?.map((u,h)=>d?.probabilities?.[h]==null?null:(Number(u)-Number(d.probabilities[h]))*100),c=d?.line==null||l.line==null?null:Number(l.line)-Number(d.line);return s.jsxs("li",{className:"border-t border-rule2 pt-1 first:border-0",children:[s.jsx("span",{className:"tnum",children:jn(l.observed_at)})," · ",s.jsx("strong",{children:l.label||"기준점 없음"}),c?s.jsxs("em",{className:"ml-1 not-italic text-amber-700",children:["(기준점 ",c>0?"+":"",c,")"]}):null," · 배당 ",(l.odds||[]).map(u=>Number(u).toFixed(2)).join(" / "),l.probabilities?.length?s.jsxs("span",{children:[" · 예상 적중 ",l.probabilities.map((u,h)=>{const x=r?.[h];return`${Math.round(Number(u)*1e3)/10}%${x==null||Math.abs(x)<.01?"":` (${x>0?"+":""}${x.toFixed(1)}%p)`}`}).join(" / ")]}):null]},`${l.observed_at}-${o}`)})})]},i)),s.jsx("p",{className:"text-[10.5px] leading-5 text-ink3",children:"예상 적중은 해당 시점 배당에서 마진을 제거한 값입니다. 기준점이 바뀌면 판정 대상 자체가 달라지므로 확률 숫자만 단순 비교하지 않습니다."})]})]}):null}function gn({g:e,opts:t,wait:n,grades:i,lv:a,stale:l,generatedAt:o,year:d,todayMembership:r,todayOption:c,highlightedToday:u,onSaveBet:h}){const x=P.useMemo(()=>{const Ne={},Ce={};return t.forEach(re=>{const te=re.market+(re.label||"");(Ne[te]=Ne[te]||[]).push(xe(i,re.배당)?.grade)}),Object.entries(Ne).forEach(([re,te])=>{te.length===2&&te[0]&&te[0]===te[1]&&(Ce[re]=!0)}),Ce},[t,i]),b=e.status==="정산",f=e._liveStarted===!0,p=J(e,a),N=le(e),F=f||p!=="upcoming"||N!=null&&N<=Date.now(),$=F||Ae(e),w=De(e,a),k=!e.prediction_record&&(b||e.prediction_status==="prediction_ledger_required"),v=n||l||k||e._liveOddsChanged?null:Wt(t),T=$&&w?.option||(F?null:c||v?.option)||null,g=T?{o:T,g:xe(i,T.배당),tie:!1}:null,E=p==="live",L=p==="finished",W=a?.cancelled?"경기 취소":a?.postponed?"경기 연기":null,S=xs(e,a),C=!E&&!L&&!b&&e.pick_drift&&e.pick_drift.market_selection&&e.pick_drift.market_selection!==e.pick_drift.pinned_selection?e.pick_drift:null,H=n?qt(e,{generatedAt:o,year:d}):null,M=b&&e.score||S.source==="score"&&e.sport==="sc"&&a?.regular_time_score||(a&&a.home_score!=null&&a.away_score!=null?[a.home_score,a.away_score]:null),K=n||l||k||F?null:Et(e,g?.o||null,Ns(e)),G=K?.decision||hs(e,g?.o||null),m=K?.prediction,_=W||(F&&!w?"사전 예측 기록 없음":e._liveOddsChanged?"배당 변경 · 재계산 대기":l?"최신 데이터 확인 필요":k?e.status==="결과확인"?"정산 결과 확인 필요":"사전 예측 기록 없음":H==="상태 확인 불가"?"경기 상태 확인 필요":n?"배당 발표 전":"분석 자료 확인 중"),A=w?.openingProbability??null,y=E?w?.estimate:null,O=bt(K?.teamPreviews),U=e._liveOddsChanged?"재계산":l?"중단":"보류",Y=n&&(f||H==="상태 확인 불가")?{...e,해설:null,해설기본:null}:e,I=S.record?`${S.record.market}${S.record.label?` ${S.record.label}`:""} ${S.record.selection}`:null,se=r?.recommended?"오늘의 추천 픽":null,ae=r?.display?.text||null,R=r?.reason||null,Me=r?.counterReason||null;return s.jsxs(Pe,{as:"details",className:`match-card is-${p} result-${S.state}`,children:[s.jsxs("summary",{className:"match-row",children:[s.jsx("span",{className:"tnum text-[11.5px] text-ink3",children:Cs(e.date)}),s.jsxs("span",{className:"min-w-0 text-[13.5px] font-semibold",children:[s.jsxs("span",{children:[e.home," ",M?s.jsxs("span",{className:"tnum text-[13px]",title:e.결과||"",children:[s.jsx("b",{className:M[0]>M[1]?"":"font-normal text-ink3",children:M[0]}),s.jsx("span",{className:"px-[3px] font-normal text-ink3",children:":"}),s.jsx("b",{className:M[1]>M[0]?"":"font-normal text-ink3",children:M[1]})]}):s.jsx("span",{className:"text-[12px] font-normal text-ink3",children:"vs"})," ",e.away]}),s.jsxs("small",{className:"match-player-inline",children:[W||(E?`LIVE · ${a.status_text||"진행 중"}`:b||L?`종료 · ${S.label}`:p==="pending"?"결과 확인 중":n?H:l?"데이터 갱신 지연":"예정")," · ",e.round,"회차",e._liveLineChanged?" · 기준점 변경 반영":"",C?" · 라인 변동":"",O?` · ${O}`:""]}),!L&&s.jsx(Se,{game:e,live:a,compact:!0})]}),s.jsxs("span",{className:"match-call-inline",children:[s.jsx("small",{children:p==="finished"?"예측 결과":E?"실시간 경기":p==="pending"?"사전 픽":se?"오늘 추천 픽":"경기별 픽"}),s.jsx("b",{children:p==="finished"?`${S.label}${I?` · ${I}`:""}`:g?`${g.o.market}${g.o.label?` ${g.o.label}`:""} ${g.o.선택}${se?` · ${se}`:""}`:m?.headline||_})]}),s.jsx("span",{className:"flex gap-1.5",children:E?s.jsxs(s.Fragment,{children:[s.jsxs("span",{className:"live-score-badge",children:[s.jsx("i",{}),"LIVE ",s.jsx("b",{children:a.status_text||"진행 중"})]}),Number.isFinite(A)&&s.jsx(V,{label:"사전 확률",value:X(A),title:"경기 시작 전에 저장된 픽의 고정 확률"}),Number.isFinite(y?.probability)&&s.jsx(V,{label:"현재 추정",value:X(y.probability),title:"사전 확률에 현재 점수와 남은 시간을 반영한 상황 추정치"})]}):W?s.jsx(V,{label:"상태",value:W.replace("경기 ","")}):p==="finished"?s.jsxs("span",{className:"flex flex-col items-end gap-1",children:[s.jsx("span",{className:`result-badge is-${S.state}`,children:S.label}),Number(S.record?.odds)>1&&s.jsxs("small",{className:"tnum text-[11px] text-ink3",children:["당시 배당 ",Z(S.record.odds),"배"]})]}):p==="pending"?s.jsx(V,{label:"상태",value:"확인 중"}):f?s.jsx(V,{label:"판정",value:"마감"}):n?s.jsx(V,{label:"배당",value:l?"갱신 지연":H==="상태 확인 불가"?"확인 불가":"발표 전"}):g?s.jsx(V,{label:g.o.선택,value:Z(g.o.배당),grade:g.g?bs(g.g.grade):"U",highlighted:u,title:`${g.o.market}${g.o.label?` ${g.o.label}`:""} · ${v?.recommendation==="recommend"?"검증 보정이 실제 반영된 추천":v?.recommendation==="weak"?"방향은 제시하되 구매 우위가 약한 픽":v?.recommendation==="market"?"시장 최유력 방향이며 구매 추천은 아님":"시장 최유력 방향은 제시하되 구매 추천은 관망"}${ae?` · ${ae}`:" · 배당 기반 시장확률"}`}):s.jsx(V,{label:"판정",value:U})})]}),s.jsxs("div",{className:"match-detail",children:[!L&&s.jsx(Se,{game:e,live:a}),$&&w&&!L&&s.jsxs("div",{className:"mb-3 rounded border border-rule2 bg-panel px-3 py-2 text-[12px]","aria-label":"저장된 사전 예측",children:[s.jsxs("b",{children:["경기 전 예측 픽 · ",w.option.market," ",w.option.label," ",w.option.선택]}),s.jsxs("p",{children:["사전 확률 ",Number.isFinite(A)?X(A):"기록 없음"," · 당시 배당 ",Z(w.option.배당)]}),E&&!y&&s.jsxs("small",{children:["현재 추정 확률은 ",w.estimateStatus==="unsupported_market"?"이 마켓에서 제공하지 않습니다.":"사전 확률과 최신 점수 자료가 모두 확인되어야 표시됩니다."]})]}),s.jsx(Nn,{rows:e._marketHistory}),r&&R&&s.jsxs("div",{className:`today-pick-signals ${u?"is-recommended":"is-excluded"}`,role:"note",children:[s.jsx("b",{children:u?"오늘 추천한 이유":"오늘 추천하지 않은 이유"}),s.jsx("span",{children:R}),ae&&s.jsx("small",{children:ae}),Me&&s.jsx("small",{children:Me})]}),e._liveLineChanged&&s.jsxs("div",{className:"mb-3 rounded border border-amber-400 bg-amber-50 px-3 py-2 text-[12px] leading-6 text-amber-950",role:"status",children:[s.jsx("b",{children:"핸디캡·언더오버 기준점 변경 반영"}),s.jsx("p",{children:"이전 기준점의 예측과 구조 모델 수치는 폐기했습니다. 현재 기준점과 배당으로 확률 및 추천을 다시 계산했습니다."})]}),C&&s.jsxs("div",{className:"mb-3 rounded border border-amber-400 bg-amber-50 px-3 py-2 text-[12px] leading-6 text-amber-950",role:"status",children:[s.jsx("b",{children:"라인 변동 · 픽은 처음 게시값으로 고정"}),s.jsxs("p",{children:["이 경기의 픽은 ",s.jsxs("b",{children:[C.pinned_market," ",C.pinned_selection]}),C.pinned_odds!=null?` (배당 ${Z(C.pinned_odds)})`:"","로 고정되어 킥오프까지 바뀌지 않습니다. 지금 시장 기준으로는"," ",s.jsx("b",{children:C.market_selection}),C.market_odds!=null?` (배당 ${Z(C.market_odds)})`:"","가 더 유력합니다. 이미 배팅하셨다면 조건이 바뀐 점만 참고하세요."]})]}),E&&M&&s.jsxs("div",{className:"live-score-panel",role:"status","aria-live":"polite",children:[s.jsxs("div",{children:[s.jsx("span",{children:"LIVE"}),s.jsx("b",{children:a.status_text||"진행 중"})]}),s.jsxs("strong",{children:[e.home," ",s.jsx("em",{children:M[0]}),s.jsx("i",{children:":"}),s.jsx("em",{children:M[1]})," ",e.away]}),s.jsxs("small",{children:[Number.isFinite(y?.probability)?s.jsxs(s.Fragment,{children:["사전 적중 ",s.jsx("b",{children:X(A)})," → 현재 상황 추정 ",s.jsx("b",{children:X(y.probability)}),s.jsx("br",{})]}):null,"점수와 남은 시간으로 이동한 상황 추정치이며 검증된 인플레이 구매 추천은 아닙니다."]}),e.sport==="bs"&&s.jsx(fn,{live:a})]}),p==="finished"&&s.jsxs("div",{className:`settled-result-panel is-${S.state}`,children:[s.jsxs("div",{children:[s.jsxs("span",{children:["최종 결과",S.source==="score"&&e.sport==="sc"?" · 정규시간":""]}),s.jsxs("strong",{children:[e.home," ",M?`${M[0]} : ${M[1]}`:"– : –"," ",e.away]})]}),s.jsxs("div",{children:[s.jsxs("span",{children:["사전 추천 판정",S.source==="score"?" · 종료 점수 기준":""]}),s.jsx("strong",{children:S.label}),I&&s.jsxs("small",{children:[I," · 배당 ",Z(S.record.odds)]})]}),S.state==="unrecorded"&&s.jsx("p",{children:"이 경기의 사전 픽이 저장되어 있지 않아 적중 여부를 판정할 수 없습니다."}),S.source==="score"&&s.jsx("p",{children:"경기 종료 점수와 저장된 픽의 기준점으로 판정했습니다. 공식 정산이 들어오면 그 결과를 우선합니다."})]}),g&&G.recommendationPriority==="reversal"&&s.jsxs("div",{className:"mb-3 rounded border border-dashed border-sev2 bg-panel px-3 py-2 text-[12px] leading-6 text-ink2",children:[s.jsxs("b",{className:"text-ink",children:["최종 픽 전환 · ",g.o.market,g.o.label?` ${g.o.label}`:""," ",g.o.선택]})," · ",s.jsx("span",{className:"tnum",children:Z(g.o.배당)})," · 시장 ",s.jsx("span",{className:"tnum",children:X(g.o.시장확률)})," · 구조 모델 차이 ",s.jsxs("span",{className:"tnum",children:[fs(Number(g.o.모델확률)-Number(g.o.시장확률)),"p"]}),s.jsx("p",{className:"text-[10.5px] leading-5 text-ink3",children:"전환 관문을 통과해 기존 정배를 제거하고 이 선택 하나로 교체했습니다. 표시 확률은 해당 배당에서 마진을 제거한 시장확률입니다."})]}),n&&s.jsx("div",{className:"rounded-[2px] border border-dashed border-rule px-2.5 py-2 text-[12px] text-ink3",children:f?"실시간 중계에서 경기 시작을 확인해 사전 판정과 구매 후보를 마감했습니다.":l?"오래된 데이터로는 배당 발표 여부를 판단하지 않습니다. 최신 수집이 확인될 때까지 기다려 주세요.":H==="상태 확인 불가"?"경기 시작 시각이 지났지만 최신 상태를 확인하지 못했다.":"배당은 아직 발표되지 않았다. 경기 정보는 먼저 확인할 수 있다."}),k&&s.jsx("div",{className:"rounded-[2px] border border-dashed border-rule px-2.5 py-2 text-[12px] text-ink3",children:"경기 전에 저장된 예측 원장이 없어 현재 공식으로 과거 추천을 재구성하지 않습니다."}),s.jsx(An,{g:Y,analysis:K,decision:k?null:G,opts:t,grades:i,tie:x,pick:g,highlightedToday:u,recalculating:e._liveOddsChanged===!0,showPrices:!n,onSaveBet:h})]})]})}function _n({g:e,opts:t,grades:n,tie:i,pick:a,highlightedToday:l=!1,recalculating:o=!1,onSaveBet:d}){const r="border-b border-rule2 pb-[5px] pr-2 text-left text-[11px] font-medium text-ink3",c="border-b border-rule2 py-[5px] pr-2 align-baseline";return s.jsxs("table",{className:"w-full border-collapse text-[12.5px]",children:[s.jsx("caption",{className:"sr-only",children:"프로토 배당, 배당 기반 시장확률, 검증 전 구조 AI 수치와 반영 상태"}),s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{scope:"col",className:`${r} text-right`,children:"번호"}),s.jsx("th",{scope:"col",className:r,children:"마켓 / 선택"}),s.jsx("th",{scope:"col",className:`${r} text-right`,children:"배당"}),s.jsx("th",{scope:"col",className:`${r} text-right`,children:"배당구간 적중"}),s.jsx("th",{scope:"col",className:`${r} model-col text-right`,children:"배당 기반 확률"}),s.jsx("th",{scope:"col",className:`${r} model-col text-right`,children:"구조 AI"}),s.jsx("th",{scope:"col",className:`${r} model-col text-right`,children:"시장과 차이"}),s.jsx("th",{scope:"col",className:r,children:"판정"}),s.jsx("th",{scope:"col",className:`${r} text-right`,children:"내 베팅"})]})}),s.jsx("tbody",{children:t.map((u,h)=>{const x=xe(n,u.배당),b=i[u.market+(u.label||"")];return s.jsxs("tr",{className:l&&a?.o===u?"is-today-pick":"",children:[s.jsx("td",{className:`${c} tnum text-right text-ink3 whitespace-nowrap`,children:u.게임번호||"–"}),s.jsxs("td",{className:c,children:[x&&s.jsx(ks,{grade:b?"T":bs(x.grade),title:b?`양쪽이 같은 배당대(${x.bin}) — 선택 보류`:`배당 ${x.bin} 실측 ${(x.roi*100).toFixed(1)}%`}),u.market,u.label?` ${u.label}`:""," · ",u.선택,u.적중===!0?" ✔":u.적중===!1?" ✕":""]}),s.jsxs("td",{className:`${c} tnum text-right`,children:[Z(u.배당),u._live&&s.jsx("span",{className:"ml-1 text-[9.5px] text-ink3","aria-label":"실시간 배당으로 확률 재계산 완료",children:"실시간"})]}),s.jsx("td",{className:`${c} tnum text-right text-ink3`,children:x?.hit!=null?`${(x.hit*100).toFixed(0)}%`:"–"}),s.jsx("td",{className:`${c} model-col tnum text-right`,children:o?"–":X(u.시장확률)}),s.jsx("td",{className:`${c} model-col tnum text-right`,title:u.모델확률==null?"이 종목·마켓은 검증된 구조 모델 확률이 아직 없습니다.":"구조 모델이 산출한 연구 확률",children:o?"–":u.모델확률==null?"미산출":X(u.모델확률)}),s.jsx("td",{className:`${c} model-col tnum text-right`,title:u.AI잔차==null?"구조 AI 확률이 없어 시장과의 차이를 계산하지 않습니다.":"구조 AI 확률 − 시장확률",children:o||u.AI잔차==null?"–":fs(u.AI잔차)}),s.jsxs("td",{className:`${c} text-[11.5px]`,children:[o&&s.jsx("span",{className:"text-ink3",children:"재계산 대기"}),a&&!a.tie&&a.o===u&&s.jsx("span",{className:"font-semibold text-signal",children:l?"★ 오늘의 베팅 추천":"경기 예측 픽"}),a&&a.tie&&xe(n,u.배당)?.grade===a.g.grade&&s.jsx("span",{className:"text-ink3",children:"동률 — 고를 근거 없음"}),!o&&(!a||!a.tie&&a.o!==u)&&s.jsx("span",{className:"text-ink3",children:u.모델확률==null?"시장 참고":"비추천"})]}),s.jsx("td",{className:`${c} text-right`,children:s.jsx("button",{type:"button",className:"bet-record-button",disabled:o,onClick:()=>d?.(e,u),children:"베팅 기록"})})]},h)})})]})}const vn="intel-tab";function yn({source:e}){if(!e)return null;let t="";if(e.updatedAt){const i=new Date(e.updatedAt);Number.isNaN(i.getTime())||(t=i.toLocaleString("ko-KR",{timeZone:"Asia/Seoul",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}))}const n=e.url?s.jsx("a",{className:"underline decoration-rule underline-offset-2 hover:text-ink",href:e.url,target:"_blank",rel:"noreferrer",children:e.name}):e.name;return s.jsxs("div",{className:"source-stamp",children:["출처 ",n,t?` · KST ${t} 확인`:""]})}function ts({team:e,pitcher:t}){const n=at(t);return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("div",{className:"text-[11px] text-ink3",children:e}),t?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"mt-0.5 flex flex-wrap items-baseline gap-1.5",children:[s.jsx("b",{className:"text-[13px] text-ink",children:t.name}),t.stats?.period&&s.jsx("span",{className:"text-[10.5px] text-ink3",children:t.stats.period}),t.stats?.low_sample&&s.jsx("span",{className:"rounded border border-dashed border-rule px-1 text-[9.5px] text-ink3",children:"표본 적음"})]}),n.length?s.jsx("div",{className:"mt-2 grid grid-cols-4 gap-x-2 gap-y-1.5 sm:grid-cols-8",children:n.map(([i,a])=>s.jsxs("div",{children:[s.jsx("div",{className:"text-[9.5px] text-ink3",children:i}),s.jsx("div",{className:"tnum text-[12px] font-semibold text-ink",children:a})]},i))}):s.jsx("p",{className:"mt-1 text-[11px] text-ink3",children:"선발 이름은 확인됐지만 개인 지표 자료는 아직 없다."}),t.stats?.fip_approx&&s.jsx("div",{className:"mt-1 text-[9.5px] text-ink3",children:"* FIP는 사구 자료가 없어 사구를 제외한 근사치다."}),t.stats_source&&s.jsx("div",{className:"mt-1.5 text-[10px] text-ink3",children:t.stats_source})]}):s.jsx("p",{className:"mt-1 text-[11.5px] text-ink3",children:"아직 선발이 발표되지 않았다."})]})}function ns({team:e,profile:t}){return t?s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 과거 라인업 성향"]}),s.jsxs("div",{className:"mt-1 text-[11.5px] leading-[1.7]",children:["주 포메이션 ",s.jsx("b",{children:t.formation||"자료 없음"}),t.churn!=null&&s.jsxs(s.Fragment,{children:[" · 직전 경기 대비 선발 교체 평균 ",s.jsxs("b",{className:"tnum",children:[t.churn,"명"]})]}),t.reserve!=null&&s.jsxs(s.Fragment,{children:[" · 비주전 선발 평균 ",s.jsxs("b",{className:"tnum",children:[t.reserve,"명"]})]})]}),s.jsxs("p",{className:"mt-1 text-[10.5px] text-ink3",children:["실제 오늘 선발 명단이 아니라 과거 ",t.n||"","경기의 팀 성향이다."]})]}):null}function is({team:e,players:t}){return t?.length?s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 발표 라인업"]}),s.jsx("ol",{className:"mt-1 grid grid-cols-2 gap-x-2 text-[11px] leading-[1.7]",children:t.map((n,i)=>s.jsxs("li",{className:"truncate",children:[s.jsx("span",{className:"tnum mr-1 text-ink3",children:n.order||i+1}),s.jsx("b",{className:"font-medium text-ink",children:n.name}),n.position&&s.jsx("span",{className:"ml-1 text-[9.5px] text-ink3",children:n.position}),n.stats&&s.jsxs("span",{className:"ml-1 text-[9.5px] text-ink3",children:["· ",n.stats.apps??"-","경기 ",n.stats.goals??0,"골 ",n.stats.assists??0,"도움"]})]},`${n.name}-${i}`))})]}):null}function as(e){const t=Number(e);return Number.isFinite(t)?t.toFixed(3).replace(/^0/,""):"-"}function ls({team:e,players:t,referenceDate:n,lineupState:i}){if(!t?.length)return null;const a=i==="official_today",l=a?`${n?`${n.replaceAll("-",".")} · `:""}오늘 공식 선발 타순`:n?`${n.replaceAll("-",".")} 공식 선발 타순 기준 예상`:"예상 라인업";return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · ",l]}),s.jsx("ol",{className:"mt-1 space-y-1.5",children:t.map((o,d)=>{const r=o.stats||null,c=o.last_game||null;return s.jsxs("li",{className:"text-[11px] leading-[1.45]",children:[s.jsxs("div",{className:"flex items-baseline gap-1",children:[s.jsx("span",{className:"tnum w-3 text-ink3",children:o.order||d+1}),o.profile_url?s.jsx("a",{href:o.profile_url,target:"_blank",rel:"noreferrer",className:"font-medium text-ink underline decoration-rule2 underline-offset-2 hover:decoration-ink",children:o.name}):s.jsx("b",{className:"font-medium text-ink",children:o.name}),o.position&&s.jsx("span",{className:"text-[9.5px] text-ink3",children:o.position})]}),r&&s.jsxs("div",{className:"tnum ml-4 text-[9.5px] text-ink3",children:["시즌 타율 ",as(r.avg)," · ",r.home_runs??0,"홈런 · ",r.rbi??0,"타점 · OPS ",as(r.ops)]}),!a&&c&&s.jsxs("div",{className:"tnum ml-4 text-[9.5px] text-ink3",children:["기준 경기 ",c.at_bats??"-","타수 ",c.hits??"-","안타 · ",c.rbi??0,"타점"]})]},`${o.player_id||o.name}-${d}`)})})]})}function rs({team:e,players:t}){return t?.length?s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 현재 시즌 핵심 선수"]}),s.jsx("ul",{className:"mt-1 space-y-1.5",children:t.slice(0,5).map((n,i)=>s.jsxs("li",{className:"text-[11px]",children:[s.jsxs("div",{className:"flex items-baseline gap-1",children:[s.jsx("b",{className:"text-ink",children:n.name}),n.position&&s.jsx("span",{className:"text-[9.5px] text-ink3",children:n.position})]}),s.jsxs("div",{className:"tnum text-[10px] text-ink3",children:[n.apps!=null&&s.jsxs("span",{children:[n.apps,"경기"]}),n.starts!=null&&s.jsxs("span",{children:[" · 선발 ",n.starts]}),n.goals!=null&&s.jsxs("span",{children:[" · ",n.goals,"골"]}),n.assists!=null&&s.jsxs("span",{children:[" · ",n.assists,"도움"]}),n.xg!=null&&s.jsxs("span",{children:[" · xG ",n.xg]}),n.xa!=null&&s.jsxs("span",{children:[" · xA ",n.xa]})]})]},`${n.player_id||n.name}-${i}`))}),s.jsx("p",{className:"mt-1.5 text-[9.5px] text-ink3",children:"실제 오늘 선발이 아니라 공식 시즌 기록의 공격 기여 상위 선수다."})]}):null}function os({team:e,players:t}){return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 등록/최근 대표 명단"]}),t?.length?s.jsx("ul",{className:"mt-1 grid grid-cols-2 gap-x-2 text-[11px] leading-[1.7]",children:t.map((n,i)=>s.jsxs("li",{className:"truncate",children:[n.number!=null&&n.number!==""&&s.jsxs("span",{className:"tnum mr-1 text-ink3",children:["#",n.number]}),s.jsx("b",{className:"font-medium text-ink",children:n.name}),n.position&&s.jsx("span",{className:"ml-1 text-[9.5px] text-ink3",children:n.position}),n.club&&s.jsxs("span",{className:"ml-1 text-[9.5px] text-ink3",children:["· ",n.club]})]},`${n.player_id||n.name}-${i}`))}):s.jsx("p",{className:"mt-1 text-[11px] text-ink3",children:"이 팀의 최종 명단은 아직 공개되지 않았다."})]})}function cs({team:e,players:t,sport:n}){return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsxs("div",{className:"text-[11px] text-ink3",children:[e," · 공식 기록 상위 선수"]}),t?.length?s.jsx("ul",{className:"mt-1 space-y-1.5",children:t.slice(0,5).map((i,a)=>{const l=n==="bk"?[["경기",i.games],["득점",i.points],["리바운드",i.rebounds],["도움",i.assists],["효율",i.efficiency],["3P%",i.three_pct]]:[["순위",i.rank],["득점",i.points],["공격",i.attacks],["블로킹",i.blocks],["서브",i.serves],["공격%",i.attack_pct]];return s.jsxs("li",{className:"text-[11px]",children:[s.jsxs("div",{className:"flex items-baseline gap-1",children:[s.jsx("b",{className:"text-ink",children:i.name}),i.position&&s.jsx("span",{className:"text-[9.5px] text-ink3",children:i.position})]}),s.jsx("div",{className:"tnum text-[10px] text-ink3",children:l.filter(([,o])=>o!=null).map(([o,d],r)=>s.jsxs("span",{children:[r?" · ":"",o," ",d]},o))})]},`${i.player_id||i.name}-${a}`)})}):s.jsx("p",{className:"mt-1 text-[11px] text-ink3",children:"이 팀의 선수 기록 자료가 아직 연결되지 않았다."})]})}function kn({g:e}){const t=e.선발||{},n=t.rosters||{},i=t.key_players||{},a=!!(n.home?.length||n.away?.length),l=!!(i.home?.length||i.away?.length);return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"mb-2 rounded-[7px] border border-rule2 bg-paper2 px-2.5 py-2 text-[10.5px] leading-[1.6] text-ink3",children:[s.jsx("b",{className:"text-ink",children:t.roster_status?.label||t.coverage?.label||"공식 선수 자료원 미연결"}),t.roster_status?.label&&t.coverage?.label&&s.jsxs("span",{children:[" · ",t.coverage.label]})]}),a&&s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 text-[10.5px] font-semibold text-ink3",children:"선수 명단"}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(os,{team:e.home,players:n.home}),s.jsx(os,{team:e.away,players:n.away})]})]}),l&&s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 mt-2 text-[10.5px] font-semibold text-ink3",children:"선수 기록"}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(cs,{team:e.home,players:i.home,sport:e.sport}),s.jsx(cs,{team:e.away,players:i.away,sport:e.sport})]})]}),!a&&!l&&s.jsx("p",{className:"text-[11.5px] text-ink3",children:t.coverage?.label||"이 대회의 공식 선수·명단 자료 경로가 아직 연결되지 않았다."}),s.jsx("p",{className:"mt-2 text-[9.5px] leading-[1.6] text-ink3",children:"등록 명단과 최근 대표 명단은 실제 당일 선발·출전 확정이나 부상 보고서가 아니다."})]})}function $n({g:e}){if(e.sport==="bs"){const h=e.선발||{},x=h.lineups||{},b=h.lineup_status||{},f=b.side_states||{},p=b.official_today!==!0,N=b.reference_dates||{};return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(ts,{team:e.home,pitcher:pe(e,"home")}),s.jsx(ts,{team:e.away,pitcher:pe(e,"away")})]}),x.home?.length||x.away?.length?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 mt-2 text-[10.5px] font-semibold text-ink3",children:b.label||(p?"최근 공식 경기 기반 예상 타순":"오늘 공식 선발 타순")}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(ls,{team:e.home,players:x.home,referenceDate:N.home,lineupState:f.home}),s.jsx(ls,{team:e.away,players:x.away,referenceDate:N.away,lineupState:f.away})]}),p&&s.jsx("p",{className:"mt-1.5 text-[9.5px] leading-[1.55] text-ink3",children:b.caveat||"오늘 실제 발표 라인업이 아니며, 선발 발표 시 교체될 수 있다."})]}):s.jsx("p",{className:"mt-2 text-[10.5px] text-ink3",children:"타자 선발 명단은 아직 발표되지 않았다."})]})}if(["bk","vl"].includes(e.sport))return s.jsx(kn,{g:e});const t=e.선발||{},n=t.lineups||{},i=t.key_players||{},a=t.formations||{},l=t.lineup_status||{},o=e.라인업||{},d=l.expected_at?new Date(l.expected_at).toLocaleString("ko-KR",{timeZone:"Asia/Seoul",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}):null,r=!!(n.home?.length||n.away?.length),c=!!(i.home?.length||i.away?.length),u=!!(o.home||o.away);return s.jsxs(s.Fragment,{children:[r?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 text-[10.5px] font-semibold text-ink3",children:"실제 발표 선발 명단"}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(is,{team:`${e.home}${a.home?` · ${a.home}`:""}`,players:n.home}),s.jsx(is,{team:`${e.away}${a.away?` · ${a.away}`:""}`,players:n.away})]})]}):s.jsx("p",{className:"text-[11.5px] text-ink3",children:t.source?`실제 선발 명단 발표 전${d?` · 통상 ${d} 전후 확인`:""}`:"이 리그의 선수·라인업 자료 경로가 아직 연결되지 않았다."}),c&&s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mb-1 mt-2 text-[10.5px] font-semibold text-ink3",children:"시즌 핵심 선수 기록"}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(rs,{team:e.home,players:i.home}),s.jsx(rs,{team:e.away,players:i.away})]})]}),u&&s.jsxs("div",{className:"mt-2 grid gap-2 sm:grid-cols-2",children:[s.jsx(ns,{team:e.home,profile:o.home}),s.jsx(ns,{team:e.away,profile:o.away})]})]})}function wn({record:e,sport:t}){const n=e.rank?`${e.rank}위 · `:"",i=e.wins!=null||e.losses!=null?`${e.wins??0}승${e.draws!=null?` ${e.draws}무`:""} ${e.losses??0}패`:"경기 기록 확인 중";return s.jsxs("div",{className:"mt-1.5 border-t border-rule2 pt-1.5 text-[10.5px] leading-[1.65] text-ink3",children:["공식 현재 성적 ",s.jsxs("b",{className:"tnum text-ink",children:[n,i]}),t==="sc"&&s.jsxs(s.Fragment,{children:[e.goals_per_game!=null&&s.jsxs("span",{children:[" · 평균 ",e.goals_per_game,"득점/",e.conceded_per_game,"실점"]}),e.xg!=null&&s.jsxs("span",{children:[" · xG ",e.xg,e.xga!=null?`/xGA ${e.xga}`:""]})]}),t==="bs"&&e.pct!=null&&s.jsxs("span",{children:[" · 승률 ",(e.pct*100).toFixed(1),"%"]}),t==="bk"&&s.jsxs(s.Fragment,{children:[e.points_per_game!=null&&s.jsxs("span",{children:[" · 평균 ",e.points_per_game,"득점",e.conceded_per_game!=null?`/${e.conceded_per_game}실점`:""]}),e.rebounds_per_game!=null&&s.jsxs("span",{children:[" · 리바운드 ",e.rebounds_per_game]}),e.assists_per_game!=null&&s.jsxs("span",{children:[" · 도움 ",e.assists_per_game]}),e.fg_pct!=null&&s.jsxs("span",{children:[" · FG ",e.fg_pct,"%"]}),e.three_pct!=null&&s.jsxs("span",{children:[" · 3P ",e.three_pct,"%"]}),e.turnovers_per_game!=null&&s.jsxs("span",{children:[" · 턴오버 ",e.turnovers_per_game]})]}),t==="vl"&&s.jsxs(s.Fragment,{children:[e.table_points!=null&&s.jsxs("span",{children:[" · 승점 ",e.table_points]}),e.set_ratio!=null&&s.jsxs("span",{children:[" · 세트비 ",e.set_ratio]}),e.point_ratio!=null&&s.jsxs("span",{children:[" · 점수비 ",e.point_ratio]}),e.attack_pct!=null&&s.jsxs("span",{children:[" · 공격 ",e.attack_pct,"%"]}),e.blocks_per_set!=null&&s.jsxs("span",{children:[" · 블로킹/세트 ",e.blocks_per_set]}),e.serves_per_set!=null&&s.jsxs("span",{children:[" · 서브/세트 ",e.serves_per_set]}),e.receive_efficiency!=null&&s.jsxs("span",{children:[" · 리시브 ",e.receive_efficiency,"%"]})]})]})}function Sn({team:e,form:t,status:n,record:i,sport:a}){const l={missing_db_history:"조회 시점 이전에 확인된 경기 결과가 DB에 없다.",team_unmapped:"DB 경기 기록에서 이 팀을 연결하지 못했다.",no_season_sample:"이 팀의 해당 리그·시즌 경기 기록이 없다."}[n]||"최근 경기 기록이 아직 제공되지 않았다.";return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("div",{className:"text-[11px] text-ink3",children:e}),t?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mt-0.5 text-[12px] font-semibold text-ink",children:t.last10||Ts(t)}),s.jsxs("div",{className:"mt-1 text-[11px] leading-[1.7] text-ink2",children:["시즌 ",t.w,"승 ",t.d?`${t.d}무 `:"",t.l,"패",t.streak?` · ${t.streak}`:"",t.home?` · 홈 ${t.home}`:"",t.away?` · 원정 ${t.away}`:"",t.avg_scored!=null?` · 평균 ${t.avg_scored}득점/${t.avg_conceded}실점`:""]})]}):s.jsx("p",{className:"mt-1 text-[11.5px] text-ink3",children:l}),i&&Object.keys(i).length>0&&s.jsx(wn,{record:i,sport:a})]})}function Fn({g:e}){const t=e.선발?.team_profiles||{},n=e.team_form_display||e;return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"grid gap-2 sm:grid-cols-2",children:[["home",e.home,n.form_home],["away",e.away,n.form_away]].map(([i,a,l])=>s.jsxs("div",{children:[s.jsx(Sn,{team:a,form:l,status:n[`form_${i}_status`],record:Fe(e,i),sport:e.sport}),!!t[i]?.characteristics?.length&&s.jsxs("div",{className:"mt-1 rounded-[7px] border border-rule2 bg-paper2 px-2.5 py-2 text-[10.5px] leading-[1.7] text-ink2",children:[s.jsxs("b",{className:"text-ink",children:[a," 특징"]})," · ",t[i].characteristics.join(" · ")]})]},i))}),n.h2h&&s.jsxs("div",{className:"mt-2 rounded-[7px] border border-rule2 px-2.5 py-2 text-[11.5px] text-ink2",children:[s.jsx("span",{className:"mr-1 text-[10.5px] text-ink3",children:"맞대결"}),n.h2h]})]})}function ds({team:e,rows:t,connected:n,emptyText:i}){return s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("div",{className:"text-[11px] text-ink3",children:e}),t.length?s.jsx("ul",{className:"mt-1 space-y-1",children:t.map((a,l)=>s.jsxs("li",{className:"text-[11.5px]",children:[s.jsx("b",{className:"text-ink",children:a.name}),s.jsxs("span",{className:"text-ink3",children:[" · ",a.reason_label||"사유 미확인"," · ",a.status||"출전 불가",a.position?` · ${a.position}`:""]}),a.impact_label&&s.jsxs("span",{className:"ml-1 rounded border border-rule px-1 text-[9.5px] text-ink3",children:["예상 영향 ",a.impact_label]})]},`${a.name}-${l}`))}):s.jsx("p",{className:"mt-1 text-[11.5px] text-ink3",children:n?i||"공식 명단에서 부상 상태로 표시된 선수가 없다.":"부상·출전 상태 자료가 아직 연결되지 않았다."})]})}function En({g:e}){const t=e.선발||{},n=t.unavailable,i=t.lineup_status?.state,a=t.lineups||{},l=e.sport==="bs"?t.lineup_status?.official_today===!0:!!(a.home?.length||a.away?.length),o=["bk","vl"].includes(e.sport),d=t.roster_status?.state,r=["official_competition_roster","official_roster_partial","official_player_stats","recent_international_roster","season_stats"].includes(d),c=o?!0:e.sport==="sc"?i==="announced":!!n&&Object.hasOwn(n,"home")&&Object.hasOwn(n,"away"),u=o?r?"선수 자료는 연결됐지만 별도의 당일 부상·출전 확정 자료는 아니다.":t.coverage?.label||"공식 부상·출전 상태 자료원이 아직 연결되지 않았다.":e.sport==="sc"?"시즌 핵심 선수들이 모두 선발 명단에 포함됐다.":null,h=t.availability_summary,x=b=>b>=.6?"큼":b>=.25?"중간":b>0?"작음":"없음";return s.jsxs(s.Fragment,{children:[h&&(h.home_burden>0||h.away_burden>0)&&s.jsxs("div",{className:"mb-2 rounded-[7px] border border-rule2 px-2.5 py-2 text-[11.5px] leading-[1.7] text-ink2",children:["명단 변수 예상 영향 · ",s.jsxs("b",{children:[e.home," ",x(h.home_burden)]})," · ",s.jsxs("b",{children:[e.away," ",x(h.away_burden)]}),s.jsx("p",{className:"mt-1 text-[10px] text-ink3",children:"선수 비중·결장 가능성·대체 수준·출처 신뢰도를 함께 본 진단값이다. 과거 검증 전이라 승률에는 직접 더하지 않는다."})]}),s.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[s.jsx(ds,{team:e.home,rows:be(e,"home"),connected:c,emptyText:u}),s.jsx(ds,{team:e.away,rows:be(e,"away"),connected:c,emptyText:u})]}),!l&&s.jsx("p",{className:"mt-2 text-[10.5px] leading-[1.6] text-ink3",children:o?"등록/최근 명단은 실제 당일 선발이나 부상 보고서가 아니다. 경기 직전 공식 출전 명단을 별도로 확인한다.":e.sport==="sc"?"실제 선발 명단 발표 전에는 결장·벤치 여부를 단정하지 않는다.":"실제 경기 선발 명단은 보통 시작 직전에 확정된다."})]})}function Pn({g:e,pick:t}){const n=e.경기근거||null,i=zt(t),a=[["경기 내부",n?.internal||[]],["경기 외부",n?.external||[]],["공개 픽 흐름",n?.crowd||[]]].filter(([,o])=>o.length),l=String(e.근거해설||"").trim();return!i&&!a.length&&!l&&!n?.limitations?.length?null:s.jsxs("div",{className:"mt-3 border-t border-rule2 pt-3 text-[11.5px] leading-[1.7] text-ink2",children:[i&&s.jsxs("div",{className:"rounded-[7px] border border-rule bg-panel px-3 py-2.5",children:[s.jsx("div",{className:"mb-0.5 text-[10.5px] font-semibold tracking-[.04em] text-ink3",children:"왜 이 픽인가"}),s.jsx("div",{className:"text-ink",children:i})]}),!!a.length&&s.jsx("div",{className:"mt-2 grid gap-2 [grid-template-columns:repeat(auto-fit,minmax(210px,1fr))]",children:a.map(([o,d])=>s.jsxs("div",{className:"rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("div",{className:"mb-1 text-[10.5px] font-semibold tracking-[.04em] text-ink3",children:o}),d.map((r,c)=>s.jsxs("div",{className:"mb-1 last:mb-0",children:[r.text,r.source&&s.jsxs("span",{className:"ml-1 text-[10px] text-ink3",children:["(",r.source,")"]})]},`${r.kind||o}-${c}`))]},o))}),l&&s.jsxs("div",{className:"mt-2 rounded-[7px] border border-rule2 px-2.5 py-2",children:[s.jsx("span",{className:"mr-1.5 rounded border border-rule px-1.5 py-0.5 text-[10px] text-ink3",children:Yt(e.근거해설방식)}),l]}),!!n?.limitations?.length&&s.jsxs("div",{className:"mt-1.5 text-[10.5px] text-ink3",children:["검증 메모 · ",n.limitations.join(" · ")]})]})}function An({g:e,analysis:t,decision:n,opts:i,grades:a,tie:l,pick:o,highlightedToday:d=!1,recalculating:r=!1,showPrices:c=!1,onSaveBet:u}){const h=Ns(e),x=it(e,h);c&&!x.some(v=>v.id==="evidence")&&x.push({id:"evidence",label:"픽 근거·수식"});const[b,f]=P.useState(x[0]?.id||"summary");if(!x.length)return null;const p=x.some(v=>v.id===b)?b:x[0]?.id,N=nt(e),F=String(e.event_id||`${e.round}-${e.home}-${e.away}`).replace(/[^a-zA-Z0-9_-]/g,"-"),$=v=>`tab-${F}-${v}`,w=`panel-${F}`,k=(v,T)=>{if(!["ArrowLeft","ArrowRight","Home","End"].includes(v.key))return;v.preventDefault();let E=T;v.key==="ArrowLeft"&&(E=(T-1+x.length)%x.length),v.key==="ArrowRight"&&(E=(T+1)%x.length),v.key==="Home"&&(E=0),v.key==="End"&&(E=x.length-1),f(x[E].id),v.currentTarget.parentElement?.querySelectorAll('[role="tab"]')?.[E]?.focus()};return s.jsxs("div",{className:"intel-panel",children:[s.jsx("div",{className:"intel-tabs",role:"tablist","aria-label":`${e.home} 대 ${e.away} 경기 정보`,children:x.map((v,T)=>s.jsx("button",{type:"button",role:"tab",id:$(v.id),"aria-controls":w,"aria-selected":p===v.id,tabIndex:p===v.id?0:-1,className:`${vn} ${p===v.id?"is-active":""}`,onKeyDown:g=>k(g,T),onClick:()=>f(v.id),children:v.label},v.id))}),s.jsxs("div",{className:"intel-content",role:"tabpanel",id:w,"aria-labelledby":$(p),tabIndex:0,children:[p==="summary"&&s.jsxs(s.Fragment,{children:[t?s.jsx(Ws,{analysis:t,scoreForecast:rn(e)}):s.jsx("p",{className:"m-0 text-[11.5px] text-ink3",children:h||"경기 자료를 확인 중입니다."}),s.jsx("div",{className:"mt-3 border-t border-rule2 pt-3",children:s.jsx(Fn,{g:e})})]}),p==="players"&&s.jsxs(s.Fragment,{children:[s.jsx($n,{g:e}),s.jsx("div",{className:"mt-3 border-t border-rule2 pt-3",children:s.jsx(En,{g:e})})]}),p==="evidence"&&s.jsxs(s.Fragment,{children:[n?s.jsx(Bs,{decision:n}):s.jsx("p",{className:"m-0 text-[11.5px] text-ink3",children:"경기 전 판정 스냅샷이 아직 없습니다."}),s.jsx(Pn,{g:e,pick:o}),c&&s.jsx("div",{className:"show-model mt-3 overflow-x-auto border-t border-rule2 pt-3",children:s.jsx(_n,{g:e,opts:i,grades:a,tie:l,pick:o,highlightedToday:d,recalculating:r,onSaveBet:u})})]})]}),p==="players"&&s.jsx(yn,{source:N})]})}export{In as M}; diff --git a/docs/assets/cache-guard-cBqh6aGv.js b/docs/assets/cache-guard-BQcATxSH.js similarity index 100% rename from docs/assets/cache-guard-cBqh6aGv.js rename to docs/assets/cache-guard-BQcATxSH.js diff --git a/docs/assets/cache-guard-C67GmSQX.css b/docs/assets/cache-guard-C5lG5IJ8.css similarity index 60% rename from docs/assets/cache-guard-C67GmSQX.css rename to docs/assets/cache-guard-C5lG5IJ8.css index 5349b456..6947cb1b 100644 --- a/docs/assets/cache-guard-C67GmSQX.css +++ b/docs/assets/cache-guard-C5lG5IJ8.css @@ -1 +1 @@ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:"Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", sans-serif;--font-mono:"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;--color-amber-50:oklch(98.7% .022 95.277);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-950:oklch(27.9% .077 45.635);--color-white:#fff;--spacing:.25rem;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--leading-snug:1.375;--radius-md:.375rem;--radius-lg:.5rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-paper:#faf9f6;--color-panel:#fff;--color-rule:#e3e2dd;--color-rule2:#efeeea;--color-ink:#1b1b1f;--color-ink2:#55555f;--color-ink3:#8b8b92;--color-sev0:#3f4a5a;--color-sev1:#6b6480;--color-sev2:#9a6a3c;--color-sev3:#a8322a;--color-signal:#2f4f8f;--radius-card:10px}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.fixed{position:fixed}.static{position:static}.right-5{right:calc(var(--spacing) * 5)}.bottom-5{bottom:calc(var(--spacing) * 5)}.z-20{z-index:20}.m-0{margin:0}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-0{margin-top:0}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-9{margin-top:calc(var(--spacing) * 9)}.mt-\[18px\]{margin-top:18px}.mt-\[22px\]{margin-top:22px}.mr-1{margin-right:var(--spacing)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.mr-\[7px\]{margin-right:7px}.mb-0{margin-bottom:0}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-2\.5{margin-bottom:calc(var(--spacing) * 2.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-4{margin-left:calc(var(--spacing) * 4)}.block{display:block}.flex{display:flex}.grid{display:grid}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-4{height:calc(var(--spacing) * 4)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.w-3{width:calc(var(--spacing) * 3)}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-\[104px\]{width:104px}.w-auto{width:auto}.w-full{width:100%}.max-w-\[860px\]{max-width:860px}.max-w-\[900px\]{max-width:900px}.max-w-\[1180px\]{max-width:1180px}.min-w-0{min-width:0}.min-w-14{min-width:calc(var(--spacing) * 14)}.min-w-\[92px\]{min-width:92px}.min-w-\[200px\]{min-width:200px}.min-w-\[220px\]{min-width:220px}.min-w-\[340px\]{min-width:340px}.min-w-\[380px\]{min-width:380px}.min-w-\[520px\]{min-width:520px}.min-w-\[760px\]{min-width:760px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.scroll-mt-4{scroll-margin-top:calc(var(--spacing) * 4)}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.\[grid-template-columns\:repeat\(auto-fit\,minmax\(210px\,1fr\)\)\]{grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}.\[grid-template-columns\:repeat\(auto-fit\,minmax\(280px\,1fr\)\)\]{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-5{column-gap:calc(var(--spacing) * 5)}.gap-x-10{column-gap:calc(var(--spacing) * 10)}.gap-y-1\.5{row-gap:calc(var(--spacing) * 1.5)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}.gap-y-3{row-gap:calc(var(--spacing) * 3)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[3px\]{border-radius:3px}.rounded-\[4px\]{border-radius:4px}.rounded-\[6px\]{border-radius:6px}.rounded-\[7px\]{border-radius:7px}.rounded-card{border-radius:var(--radius-card)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-\[3px\]{border-left-style:var(--tw-border-style);border-left-width:3px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400{border-color:var(--color-amber-400)}.border-ink{border-color:var(--color-ink)}.border-ink3{border-color:var(--color-ink3)}.border-rule{border-color:var(--color-rule)}.border-rule2{border-color:var(--color-rule2)}.border-sev0{border-color:var(--color-sev0)}.border-sev1{border-color:var(--color-sev1)}.border-sev2{border-color:var(--color-sev2)}.border-sev3{border-color:var(--color-sev3)}.border-signal{border-color:var(--color-signal)}.border-l-rule{border-left-color:var(--color-rule)}.border-l-sev0{border-left-color:var(--color-sev0)}.border-l-sev2{border-left-color:var(--color-sev2)}.border-l-sev3{border-left-color:var(--color-sev3)}.border-l-signal{border-left-color:var(--color-signal)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-ink{background-color:var(--color-ink)}.bg-ink3{background-color:var(--color-ink3)}.bg-panel{background-color:var(--color-panel)}.bg-paper{background-color:var(--color-paper)}.bg-sev0{background-color:var(--color-sev0)}.bg-sev1{background-color:var(--color-sev1)}.bg-sev2{background-color:var(--color-sev2)}.bg-sev3{background-color:var(--color-sev3)}.bg-signal{background-color:var(--color-signal)}.bg-signal\/5{background-color:#2f4f8f0d}@supports (color:color-mix(in lab,red,red)){.bg-signal\/5{background-color:color-mix(in oklab,var(--color-signal) 5%,transparent)}}.p-0{padding:0}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-\[3px\]{padding-inline:3px}.px-\[5px\]{padding-inline:5px}.px-\[13px\]{padding-inline:13px}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-7{padding-block:calc(var(--spacing) * 7)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-\[2px\]{padding-block:2px}.py-\[5px\]{padding-block:5px}.pt-1{padding-top:var(--spacing)}.pt-1\.5{padding-top:calc(var(--spacing) * 1.5)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-1{padding-bottom:var(--spacing)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pb-20{padding-bottom:calc(var(--spacing) * 20)}.pb-\[5px\]{padding-bottom:5px}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-3\.5{padding-left:calc(var(--spacing) * 3.5)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-baseline{vertical-align:baseline}.text-\[9\.5px\]{font-size:9.5px}.text-\[10\.5px\]{font-size:10.5px}.text-\[10px\]{font-size:10px}.text-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13\.5px\]{font-size:13.5px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[17px\]{font-size:17px}.text-\[19px\]{font-size:19px}.text-\[20px\]{font-size:20px}.text-\[22px\]{font-size:22px}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.leading-\[1\.5\]{--tw-leading:1.5;line-height:1.5}.leading-\[1\.6\]{--tw-leading:1.6;line-height:1.6}.leading-\[1\.7\]{--tw-leading:1.7;line-height:1.7}.leading-\[1\.8\]{--tw-leading:1.8;line-height:1.8}.leading-\[1\.35\]{--tw-leading:1.35;line-height:1.35}.leading-\[1\.45\]{--tw-leading:1.45;line-height:1.45}.leading-\[1\.55\]{--tw-leading:1.55;line-height:1.55}.leading-\[1\.65\]{--tw-leading:1.65;line-height:1.65}.leading-\[1\.75\]{--tw-leading:1.75;line-height:1.75}.leading-\[1\.85\]{--tw-leading:1.85;line-height:1.85}.leading-none{--tw-leading:1;line-height:1}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-\.01em\]{--tw-tracking:-.01em;letter-spacing:-.01em}.tracking-\[\.03em\]{--tw-tracking:.03em;letter-spacing:.03em}.tracking-\[\.04em\]{--tw-tracking:.04em;letter-spacing:.04em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.text-balance{text-wrap:balance}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.text-amber-700{color:var(--color-amber-700)}.text-amber-950{color:var(--color-amber-950)}.text-ink{color:var(--color-ink)}.text-ink2{color:var(--color-ink2)}.text-ink3{color:var(--color-ink3)}.text-paper{color:var(--color-paper)}.text-sev2{color:var(--color-sev2)}.text-sev3{color:var(--color-sev3)}.text-signal{color:var(--color-signal)}.text-white{color:var(--color-white)}.not-italic{font-style:normal}.underline{text-decoration-line:underline}.decoration-rule{-webkit-text-decoration-color:var(--color-rule);text-decoration-color:var(--color-rule)}.decoration-rule2{-webkit-text-decoration-color:var(--color-rule2);text-decoration-color:var(--color-rule2)}.underline-offset-2{text-underline-offset:2px}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-signal\/20{--tw-ring-color:#2f4f8f33}@supports (color:color-mix(in lab,red,red)){.ring-signal\/20{--tw-ring-color:color-mix(in oklab, var(--color-signal) 20%, transparent)}}.first\:border-0:first-child{border-style:var(--tw-border-style);border-width:0}.last\:mb-0:last-child{margin-bottom:0}@media(hover:hover){.hover\:border-ink3:hover{border-color:var(--color-ink3)}.hover\:text-ink:hover{color:var(--color-ink)}.hover\:decoration-ink:hover{-webkit-text-decoration-color:var(--color-ink);text-decoration-color:var(--color-ink)}}@media(min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.sm\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.sm\:grid-cols-\[1fr_auto\]{grid-template-columns:1fr auto}.sm\:grid-cols-\[150px_1fr\]{grid-template-columns:150px 1fr}.sm\:p-4{padding:calc(var(--spacing) * 4)}}@media(min-width:64rem){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:80rem){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media print{.print\:hidden{display:none}}}@media(prefers-color-scheme:dark){:root{--color-paper:#17171a;--color-panel:#1e1e22;--color-rule:#33333a;--color-rule2:#27272c;--color-ink:#ececed;--color-ink2:#a4a4ad;--color-ink3:#74747d;--color-sev0:#8ea3c4;--color-sev1:#a99cc4;--color-sev2:#d6a55f;--color-sev3:#ef8279;--color-signal:#8fb0e8}}:root[data-theme=dark]{--color-paper:#17171a;--color-panel:#1e1e22;--color-rule:#33333a;--color-rule2:#27272c;--color-ink:#ececed;--color-ink2:#a4a4ad;--color-ink3:#74747d;--color-sev0:#8ea3c4;--color-sev1:#a99cc4;--color-sev2:#d6a55f;--color-sev3:#ef8279;--color-signal:#8fb0e8}:root[data-theme=light]{--color-paper:#faf9f6;--color-panel:#fff;--color-rule:#e3e2dd;--color-rule2:#efeeea;--color-ink:#1b1b1f;--color-ink2:#55555f;--color-ink3:#8b8b92;--color-sev0:#3f4a5a;--color-sev1:#6b6480;--color-sev2:#9a6a3c;--color-sev3:#a8322a;--color-signal:#2f4f8f}html{background:var(--color-paper);color-scheme:light dark}body{background:var(--color-paper);color:var(--color-ink);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;margin:0;font-size:15px;line-height:1.6}.tnum{font-family:var(--font-mono);font-variant-numeric:tabular-nums;letter-spacing:-.01em}.tick{letter-spacing:.06em;color:var(--color-ink3);font-size:10.5px}summary::-webkit-details-marker{display:none}summary{list-style:none}details[open]>summary{border-bottom:1px solid var(--color-rule2)}details.card[open]{border-color:var(--color-ink3)}:where(a,button,select,input,summary):focus-visible{outline:2px solid var(--color-signal);outline-offset:2px;border-radius:4px}.model-col{display:none}.show-model .model-col{display:table-cell}@media(prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}body{word-break:keep-all;overflow-wrap:break-word;font-family:Pretendard Variable,Pretendard,-apple-system,BlinkMacSystemFont,Apple SD Gothic Neo,Noto Sans KR,sans-serif}.site-nav{min-height:64px;display:flex;align-items:stretch;justify-content:space-between;border-bottom:1px solid var(--color-ink)}.site-brand{display:flex;align-items:center;color:var(--color-ink);font-size:17px;font-weight:760;letter-spacing:-.035em;text-decoration:none}.site-nav-links{display:flex;align-items:stretch}.site-nav-link{min-width:82px;padding:0 14px;display:flex;align-items:center;justify-content:center;border-bottom:3px solid transparent;color:var(--color-ink3);font-size:13px;font-weight:620;text-decoration:none}.site-nav-link:hover{color:var(--color-ink)}.site-nav-link[aria-current=page]{border-bottom-color:var(--color-ink);color:var(--color-ink)}.market-header{padding:38px 0 23px;display:grid;grid-template-columns:1fr auto;gap:24px;align-items:end;border-bottom:1px solid var(--color-rule)}.market-header h1{margin:0;font-size:clamp(27px,4vw,38px);line-height:1.2;letter-spacing:-.055em}.market-header p{margin:10px 0 0;color:var(--color-ink2);font-size:14px;line-height:1.7}.market-meta{max-width:380px;color:var(--color-ink3);font-size:10.5px;line-height:1.65;text-align:right}.section-nav{position:sticky;top:0;z-index:12;display:flex;gap:24px;overflow-x:auto;border-bottom:1px solid var(--color-rule);background:color-mix(in srgb,var(--color-paper) 96%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);scrollbar-width:thin}.section-nav a{min-width:max-content;padding:13px 0 11px;color:var(--color-ink3);font-size:12px;font-weight:650;text-decoration:none}.section-nav a:hover{color:var(--color-ink)}.budget-simulator{margin-top:12px;border-top:1px solid var(--color-rule2)}.budget-simulator>summary{padding:11px 0;color:var(--color-ink2);font-size:11px;cursor:pointer}.ai-method{margin-top:18px;padding:20px 22px;border:1px solid var(--color-rule);border-radius:5px;background:var(--color-panel)}.ai-method-heading{display:flex;align-items:end;justify-content:space-between;gap:20px}.ai-method-heading p{margin:0 0 3px;color:var(--color-ink3);font-size:10px;font-weight:680}.ai-method-heading h2{margin:0;font-size:17px;letter-spacing:-.035em}.ai-method-heading a{min-width:max-content;color:var(--color-signal);font-size:11px}.ai-stage-grid{margin:15px 0 0;padding:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));border-top:1px solid var(--color-rule);border-bottom:1px solid var(--color-rule);list-style:none}.ai-stage-grid li{min-width:0;padding:12px 12px 13px;border-right:1px solid var(--color-rule2)}.ai-stage-grid li:first-child{padding-left:0}.ai-stage-grid li:last-child{padding-right:0;border-right:0}.ai-stage-top{display:flex;flex-wrap:wrap;align-items:center;gap:5px}.ai-stage-top>.tnum{color:var(--color-ink3);font-size:9px}.ai-stage-top b{margin-right:auto;font-size:11px}.ai-stage-grid p{margin:7px 0 0;color:var(--color-ink3);font-size:10.5px;line-height:1.62}.ai-method-foot{margin:10px 0 0;color:var(--color-ink2);font-size:10.5px}.ai-status{display:inline-flex;min-height:22px;align-items:center;padding:0 7px;border:1px solid var(--color-rule);border-radius:999px;color:var(--color-ink3);font-size:10px;font-weight:650;line-height:1;white-space:nowrap}.ai-status-used,.ai-status-operational{border-color:var(--color-ink);color:var(--color-ink)}.ai-status-shadow{border-style:dashed;border-color:var(--color-sev2);color:var(--color-sev2)}.ai-status-context_only,.ai-status-wording_only,.ai-status-template{border-color:var(--color-ink3)}.ai-status-ignored,.ai-status-missing,.ai-status-unavailable{opacity:.75;border-style:dotted}.match-section-title{margin:42px 0 12px;display:flex;align-items:end;justify-content:space-between}.match-section-title h2{margin:0;font-size:20px;letter-spacing:-.04em}.match-section-title b{color:var(--color-ink3);font-size:11px}.match-loading{margin:42px 0 60px}.match-loading-head{display:flex;align-items:center;justify-content:space-between;padding-bottom:12px;border-bottom:1px solid var(--color-ink)}.match-loading-head>div{display:flex;align-items:center;gap:9px}.match-loading-head b{font-size:17px;letter-spacing:-.025em}.match-loading-head small,.match-loading>p{color:var(--color-ink3);font-size:11px}.match-loading>p{margin:10px 0 18px}.loading-spinner{width:16px;height:16px;border:2px solid var(--color-rule2);border-top-color:var(--color-signal);border-radius:50%;animation:loading-spin .8s linear infinite}.match-loading-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.match-loading-card{min-height:170px;padding:18px;border:1px solid var(--color-rule2);border-radius:7px;background:var(--color-panel)}.match-loading-card>div{display:flex;gap:7px;margin:24px 0}.loading-line,.loading-chip{display:block;border-radius:4px;background:linear-gradient(90deg,var(--color-rule2) 25%,color-mix(in srgb,var(--color-paper) 75%,white) 50%,var(--color-rule2) 75%);background-size:220% 100%;animation:loading-shimmer 1.35s ease-in-out infinite}.loading-line{width:100%;height:10px;margin-top:11px}.loading-line.is-short{width:28%;height:8px;margin-top:0}.loading-line.is-title{width:72%;height:18px;margin-top:12px}.loading-line.is-medium{width:62%}.loading-chip{width:64px;height:27px}@keyframes loading-spin{to{transform:rotate(360deg)}}@keyframes loading-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(max-width:720px){.match-loading-grid{grid-template-columns:1fr}.match-loading-card:nth-child(n+3){display:none}}@media(prefers-reduced-motion:reduce){.loading-spinner,.loading-line,.loading-chip{animation:none}}.match-phase-counts{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.match-phase-counts button{padding:4px 7px;border:1px solid var(--color-rule2);border-radius:999px;font-weight:650}.match-phase-counts .is-live{border-color:var(--color-sev2);color:var(--color-sev2)}.match-phase-counts button{cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}.match-phase-counts button:hover{border-color:var(--color-ink3)}.match-phase-counts button[aria-pressed=true]{border-color:var(--color-ink);background:var(--color-ink);color:var(--color-paper)}.match-phase-counts button.is-live[aria-pressed=true]{border-color:var(--color-sev2);background:var(--color-sev2);color:var(--color-paper)}.match-phase-tag{padding:3px 6px;border:1px solid var(--color-rule2);border-radius:999px;color:var(--color-ink2);font-size:9.5px}.match-phase-tag.is-live{border-color:var(--color-sev2);background:color-mix(in srgb,var(--color-sev2) 8%,transparent);color:var(--color-sev2)}.match-phase-tag.is-finished{background:var(--color-panel);color:var(--color-ink3)}.filter-shell{margin-bottom:22px;border-top:1px solid var(--color-ink);border-bottom:1px solid var(--color-rule)}.filter-primary{padding:12px 0;display:grid;grid-template-columns:auto minmax(220px,1fr);gap:10px}.date-switch{display:flex;border:1px solid var(--color-rule)}.date-switch button{min-width:66px;padding:9px 13px;border:0;border-right:1px solid var(--color-rule);background:var(--color-panel);color:var(--color-ink3);font:600 12px/1 var(--font-sans);cursor:pointer}.date-switch button:last-child{border-right:0}.date-switch button[aria-pressed=true]{background:var(--color-ink);color:var(--color-paper)}.team-search input{width:100%;height:36px;padding:0 12px;border:1px solid var(--color-rule);border-radius:0;background:var(--color-panel);color:var(--color-ink);font:500 12px/1 var(--font-sans)}.advanced-filters{border-top:1px solid var(--color-rule2)}.advanced-filters>summary{padding:10px 0;display:flex;align-items:center;justify-content:space-between;color:var(--color-ink2);font-size:11.5px;cursor:pointer}.advanced-filters>summary small{color:var(--color-ink3)}.filter-grid{padding:14px 0;display:grid;grid-template-columns:repeat(3,1fr);gap:12px;border-top:1px solid var(--color-rule2)}.filter-grid label{display:grid;gap:4px;color:var(--color-ink3);font-size:10px}.filter-select{width:100%;min-height:36px;padding:0 9px;border:1px solid var(--color-rule);border-radius:0;background:var(--color-panel);color:var(--color-ink);font:500 11.5px/1 var(--font-sans)}.filter-actions{padding:10px 0;display:flex;justify-content:space-between;border-top:1px solid var(--color-rule2);color:var(--color-ink3);font-size:10.5px}.filter-actions label{display:flex;gap:6px;align-items:center}.filter-actions button{border:0;background:none;color:var(--color-ink2);text-decoration:underline;cursor:pointer}.match-card.card{margin:0;border-width:0 0 1px;border-radius:0;transition:margin .16s ease}.match-card.card[open]{margin:14px 0 20px;border:1px solid var(--color-rule);border-radius:6px;background:var(--color-panel)}.match-card.card[open]>.match-row{padding:16px 18px!important;border-bottom:1px solid var(--color-rule);background:color-mix(in srgb,var(--color-paper) 72%,var(--color-panel))}.match-card.card[open]>.match-row:hover{background:color-mix(in srgb,var(--color-paper) 72%,var(--color-panel))}.odds-chip.is-today-highlighted{border-color:var(--color-signal);background:var(--color-signal);color:#fff;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-signal) 20%,transparent)}.odds-chip.is-today-highlighted>span{color:#fff}.show-model tr.is-today-pick>td{background:color-mix(in srgb,var(--color-signal) 10%,var(--color-paper));border-color:var(--color-signal)}.show-model tr.is-today-pick>td:first-child{box-shadow:inset 4px 0 0 var(--color-signal)}.today-pick-signals{margin:0 0 12px;padding:9px 11px;display:grid;grid-template-columns:max-content 1fr;gap:3px 10px;border-left:3px solid var(--color-rule);background:var(--color-panel);font-size:11px;line-height:1.6}.today-pick-signals.is-recommended{border-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 8%,transparent)}.today-pick-signals.is-excluded{border-color:var(--color-ink3)}.today-pick-signals b{color:var(--color-signal)}.today-pick-signals span{color:var(--color-ink2)}.today-pick-signals small{grid-column:2;color:var(--color-ink3)}.dashboard-recommendations{margin:24px 0 30px}.dashboard-recommendations>header{margin-bottom:12px;display:flex;align-items:end;justify-content:space-between;gap:16px}.dashboard-recommendations>header h2{font-size:18px;font-weight:750}.dashboard-recommendations>header p,.dashboard-recommendations>header strong{color:var(--color-ink3);font-size:11px}.dashboard-recommendation-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.dashboard-recommendation-card{padding:13px 14px;border:1px solid var(--color-rule);border-left:3px solid var(--color-ink3);background:var(--color-panel)}.dashboard-recommendation-card.is-recommended{border-left-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 6%,var(--color-panel))}.dashboard-recommendation-card header,.dashboard-recommendation-pick{display:flex;align-items:center;justify-content:space-between;gap:10px}.dashboard-recommendation-card h3{margin-top:2px;font-size:13px;font-weight:700}.dashboard-recommendation-card header small,.dashboard-recommendation-card>small{color:var(--color-ink3);font-size:10px}.dashboard-recommendation-card header strong{color:var(--color-signal);font-size:10px}.dashboard-recommendation-card.is-excluded header strong{color:var(--color-ink3)}.dashboard-recommendation-pick{margin:9px 0;padding:7px 0;border-block:1px solid var(--color-rule2);font-size:12px}.dashboard-recommendation-card p{margin-top:6px;color:var(--color-ink2);font-size:11px;line-height:1.65}.dashboard-recommendation-card p b{margin-right:7px;color:var(--color-ink)}.dashboard-recommendation-card p.is-counter{color:var(--color-ink3)}.dashboard-excluded-list{margin-top:12px}.dashboard-excluded-list>summary{padding:10px 0;color:var(--color-ink2);font-size:11px;cursor:pointer}.dashboard-excluded-list[open]>summary{margin-bottom:8px}@media(max-width:760px){.dashboard-recommendation-grid{grid-template-columns:1fr}.dashboard-recommendations>header{align-items:start;flex-direction:column}}.match-card.is-live.card{border-left:3px solid var(--color-sev2)}.match-card.result-hit.card{border-left:3px solid var(--color-signal)}.match-card.result-miss.card{border-left:3px solid var(--color-sev2)}.match-row{padding:15px 0!important;display:grid!important;grid-template-columns:52px minmax(210px,1.1fr) minmax(150px,.8fr) auto!important;gap:14px!important;align-items:center}.match-row:hover{background:color-mix(in srgb,var(--color-panel) 58%,transparent)}.match-call-inline{display:flex;flex-direction:column;min-width:0;padding-left:13px;border-left:1px solid var(--color-rule2)}.match-call-inline small{color:var(--color-ink3);font-size:9.5px}.match-call-inline b{overflow:hidden;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.match-player-inline{display:block;overflow:hidden;margin-top:3px;color:var(--color-ink3);font-size:10px;font-weight:450;text-overflow:ellipsis;white-space:nowrap}.live-score-badge,.result-badge{min-width:max-content;padding:7px 9px;display:inline-flex;align-items:center;gap:5px;border:1px solid var(--color-rule);border-radius:999px;font-size:10px;font-weight:750}.live-score-badge{border-color:var(--color-sev2);color:var(--color-sev2)}.live-score-badge i{width:6px;height:6px;border-radius:50%;background:currentColor;animation:live-pulse 1.4s infinite}.live-score-badge b{color:var(--color-ink);font-size:10px}.result-badge.is-hit{border-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 9%,transparent);color:var(--color-signal)}.result-badge.is-miss{border-color:var(--color-sev2);background:color-mix(in srgb,var(--color-sev2) 8%,transparent);color:var(--color-sev2)}.result-badge.is-unrecorded,.result-badge.is-pending,.result-badge.is-void{color:var(--color-ink3)}.live-score-panel{padding:14px 16px;display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:18px;border:1px solid var(--color-sev2);border-radius:6px;background:color-mix(in srgb,var(--color-sev2) 6%,var(--color-paper))}.live-score-panel>div{display:grid;gap:3px;color:var(--color-sev2);font-size:10px}.live-score-panel>strong{font-size:17px;text-align:center}.live-score-panel em{padding:0 5px;font-size:24px;font-style:normal}.live-score-panel i{color:var(--color-ink3);font-style:normal}.live-score-panel small{color:var(--color-ink3);font-size:9.5px}.live-score-panel small b{color:var(--color-ink);font-size:11px}.settled-result-panel{padding:14px 16px;display:grid;grid-template-columns:1fr 1fr;gap:16px;border:1px solid var(--color-rule);border-radius:6px}.settled-result-panel.is-hit{border-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 7%,var(--color-paper))}.settled-result-panel.is-miss{border-color:var(--color-sev2);background:color-mix(in srgb,var(--color-sev2) 6%,var(--color-paper))}.settled-result-panel>div{display:grid;gap:3px}.settled-result-panel span,.settled-result-panel small{color:var(--color-ink3);font-size:9.5px}.settled-result-panel strong{font-size:14px}.settled-result-panel.is-hit>div:last-of-type strong{color:var(--color-signal)}.settled-result-panel.is-miss>div:last-of-type strong{color:var(--color-sev2)}.settled-result-panel p{grid-column:1 / -1;margin:0;color:var(--color-ink3);font-size:10.5px}@keyframes live-pulse{0%,to{opacity:1}50%{opacity:.3}}.match-detail{padding:22px 18px 24px!important;background:var(--color-panel)}.match-detail>*+*{margin-top:16px}.prediction-card{padding:0 18px;border:1px solid var(--color-rule);border-top:2px solid var(--color-ink);border-radius:5px;background:var(--color-paper)}.prediction-head{padding:18px 0;display:flex;align-items:center;justify-content:space-between;gap:20px;border-bottom:1px solid var(--color-rule2)}.prediction-label{margin:0 0 4px!important;color:var(--color-ink3)!important;font-size:10px!important;font-weight:680}.prediction-head h3{margin:0;font-size:23px;letter-spacing:-.045em}.prediction-head>div>p:last-child{margin:5px 0 0;color:var(--color-ink3);font-size:10.5px}.prediction-probability{min-width:70px;padding-left:18px;border-left:1px solid var(--color-rule);text-align:right}.prediction-probability b,.prediction-probability span{display:block}.prediction-probability b{font-size:18px}.prediction-probability span{color:var(--color-ink3);font-size:9px}.prediction-body{padding:20px 0 22px}.prediction-body.has-players{display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,.8fr);gap:34px}.prediction-body h4{margin:0 0 9px;color:var(--color-ink3);font-size:10px}.performance-intro{margin:-3px 0 12px;color:var(--color-ink3);font-size:10.5px;line-height:1.6}.performance-reasons{margin:0;padding:0;border-top:1px solid var(--color-rule);list-style:none}.performance-reasons li{padding:13px 0;display:grid;grid-template-columns:104px 1fr;gap:18px;border-bottom:1px solid var(--color-rule2)}.performance-reasons strong{color:var(--color-ink);font-size:11px;line-height:1.65}.performance-reasons p{margin:0;color:var(--color-ink2);font-size:12px;line-height:1.72}.player-watch-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));border-top:1px solid var(--color-rule2);border-bottom:1px solid var(--color-rule2)}.player-watch article{min-width:0;padding:10px 9px;border-right:1px solid var(--color-rule2)}.player-watch article:last-child{border-right:0}.player-watch small,.player-watch b,.player-watch a,.player-watch article p{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.player-watch small{color:var(--color-ink3);font-size:9px}.player-watch b,.player-watch a{margin-top:3px;color:var(--color-ink);font-size:13px;font-weight:700}.player-watch a{text-decoration-color:var(--color-rule);text-underline-offset:2px}.player-watch article p{margin:2px 0 0;color:var(--color-ink2);font-size:10px}.player-notes{margin:9px 0 0;padding-left:15px;color:var(--color-ink3);font-size:10px}.prediction-commentary{padding:14px 0;display:grid;grid-template-columns:92px 1fr;gap:4px 16px;border-top:1px solid var(--color-rule2)}.prediction-commentary b{color:var(--color-ink);font-size:11px}.prediction-commentary p{margin:0;color:var(--color-ink2);font-size:12px;line-height:1.75}.prediction-commentary small{grid-column:2;color:var(--color-ink3);font-size:9.5px}.prediction-caution{padding:10px 0;display:grid;grid-template-columns:92px 1fr;gap:16px;border-top:1px solid var(--color-rule2);color:var(--color-ink2);font-size:10.5px}.prediction-caution b{color:var(--color-sev2)}.prediction-card>footer{padding:8px 0;border-top:1px solid var(--color-rule2);color:var(--color-ink3);font-size:9.5px;text-align:right}.intel-panel{margin-top:16px;border:1px solid var(--color-rule);border-radius:5px;background:var(--color-paper);color:var(--color-ink2);font-size:12px;line-height:1.7}.intel-tabs{padding:0 16px;display:flex;gap:18px;overflow-x:auto;border-bottom:1px solid var(--color-rule2)}.intel-tab{min-width:max-content;min-height:44px;padding:11px 0 9px;border:0;border-bottom:2px solid transparent;background:none;color:var(--color-ink3);font:620 11px/1 var(--font-sans);cursor:pointer}.intel-tab.is-active{border-bottom-color:var(--color-ink);color:var(--color-ink)}.intel-content{padding:18px 16px}.intel-content>.prediction-card{padding:0;border:0;border-radius:0;background:transparent}.intel-content>.prediction-card .prediction-head{padding-top:0}.ai-decision-path{color:var(--color-ink2)}.probability-provenance{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr) auto minmax(0,1fr);align-items:center;border-top:1px solid var(--color-rule);border-bottom:1px solid var(--color-rule)}.probability-provenance>div{padding:12px 10px}.probability-provenance>div:first-child{padding-left:0}.probability-provenance>div:last-child{padding-right:0}.probability-provenance span,.probability-provenance b{display:block}.probability-provenance div>span{color:var(--color-ink3);font-size:9.5px}.probability-provenance div>b{margin-top:2px;color:var(--color-ink);font-size:16px}.probability-provenance .probability-arrow{color:var(--color-ink3);font-size:12px}.ai-revision-warning{margin:10px 0 0;padding:9px 10px;border-left:2px solid var(--color-sev2);background:var(--color-panel);color:var(--color-sev3);font-size:10.5px}.ai-stage-list{margin-top:14px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));border-top:1px solid var(--color-rule2)}.ai-stage-list article{padding:11px 12px 11px 0;border-bottom:1px solid var(--color-rule2)}.ai-stage-list article:nth-child(2n){padding-left:12px;border-left:1px solid var(--color-rule2)}.ai-stage-list article>div{display:flex;align-items:center;justify-content:space-between;gap:8px}.ai-stage-list article b{color:var(--color-ink);font-size:11px}.ai-stage-list article p{margin:5px 0 0;color:var(--color-ink3);font-size:10.5px;line-height:1.55}.evidence-usage{margin-top:13px;border-top:1px solid var(--color-rule);border-bottom:1px solid var(--color-rule)}.evidence-usage>summary{min-height:44px;padding:9px 0;display:flex;align-items:center;color:var(--color-ink2);font-size:10.5px;cursor:pointer}.evidence-usage ul{margin:0;padding:0 0 8px;list-style:none}.evidence-usage li{padding:7px 0;display:flex;align-items:center;justify-content:space-between;gap:10px;border-top:1px solid var(--color-rule2);font-size:10.5px}.evidence-usage li b{color:var(--color-ink)}.ai-decision-meta{margin:9px 0 0;color:var(--color-ink3);font-size:9.5px;text-align:right}.source-stamp{padding:0 16px 12px}.price-sheet{margin-top:16px;border:1px solid var(--color-rule);border-radius:5px;background:var(--color-paper)}.price-sheet>summary{padding:12px 14px;display:flex;justify-content:space-between;color:var(--color-ink2);font-size:11px;cursor:pointer}.price-sheet[open]>summary{border-bottom:1px solid var(--color-rule2)}.price-sheet>div{padding:12px 14px!important}@media(max-width:720px){.site-nav{display:block;padding-top:13px}.site-brand{height:34px}.site-nav-links{width:100%;overflow-x:auto}.site-nav-link{min-width:76px;padding:10px 8px 9px}.market-header{padding-top:28px;grid-template-columns:1fr;gap:9px}.market-meta{text-align:left}.section-nav{gap:18px}.ai-method{padding:17px 15px}.ai-method-heading{align-items:start;flex-direction:column;gap:6px}.ai-stage-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.ai-stage-grid li,.ai-stage-grid li:first-child,.ai-stage-grid li:last-child{padding:11px 9px;border-right:1px solid var(--color-rule2)}.ai-stage-grid li:nth-child(2n){border-right:0}.ai-stage-grid li:nth-child(-n+2){border-bottom:1px solid var(--color-rule2)}.filter-primary,.prediction-body.has-players{grid-template-columns:1fr}.filter-grid{grid-template-columns:repeat(2,1fr)}.match-row{grid-template-columns:46px minmax(0,1fr)!important}.match-call-inline{grid-column:2;padding:8px 0 0;border-top:1px solid var(--color-rule2);border-left:0}.match-call-inline b{overflow:visible;text-overflow:clip;white-space:normal}.match-card.card[open]{margin:10px -8px 16px}.match-card.card[open]>.match-row{padding:14px!important}.match-detail{padding:14px!important}.match-section-title{align-items:start;gap:10px}.match-phase-counts{max-width:230px}.live-score-panel{grid-template-columns:1fr;gap:8px;text-align:center}.live-score-panel>div{display:flex;justify-content:center;gap:6px}.settled-result-panel{grid-template-columns:1fr}.settled-result-panel p{grid-column:1}.prediction-card{padding:0 14px}.prediction-head h3{font-size:20px}.prediction-body{padding:17px 0 18px}.performance-reasons li{grid-template-columns:1fr;gap:4px;padding:12px 0}.prediction-commentary,.prediction-caution{grid-template-columns:1fr;gap:4px}.prediction-commentary small{grid-column:1}.intel-tabs{padding:0 12px}.intel-content{padding:14px 12px}.probability-provenance{grid-template-columns:1fr}.probability-provenance>div,.probability-provenance>div:first-child,.probability-provenance>div:last-child{padding:8px 0;display:flex;align-items:center;justify-content:space-between}.probability-provenance .probability-arrow{display:none}.ai-stage-list{grid-template-columns:1fr}.ai-stage-list article:nth-child(2n){padding-left:0;border-left:0}.source-stamp{padding:0 12px 10px}}.today-pick-compact{display:flex;align-items:center;flex-wrap:wrap;gap:18px;margin:0 0 12px;padding:12px 14px;border:1px solid var(--color-rule);border-radius:8px;background:var(--color-paper)}.today-pick-compact>div{display:grid;gap:2px}.today-pick-compact small{color:var(--color-ink3);font-size:10px}.today-pick-compact b{color:var(--color-ink);font-size:13px}.today-pick-compact>span{margin-left:auto;color:var(--color-ink3);font-size:11px}.today-betting-recommendation{margin-bottom:22px}.today-betting-recommendation.is-buy{border-top:3px solid var(--color-signal)}.today-betting-recommendation.is-challenge{border-top:3px solid #b9e600}.today-bet-verdict{display:grid;grid-template-columns:repeat(auto-fit,minmax(135px,1fr));gap:9px;margin-top:13px}.today-bet-verdict>div{border:1px solid var(--color-rule2);border-radius:7px;padding:9px 11px;background:var(--color-paper)}.today-bet-verdict small{display:block;margin-bottom:2px;color:var(--color-ink3);font-size:10px}.today-bet-verdict b{font-size:12.5px}.today-bet-leg{display:flex;flex-wrap:wrap;align-items:baseline;gap:9px;border-top:1px solid var(--color-rule2);padding:8px 7px}.today-bet-leg:first-child{border-top:0}.today-bet-leg.is-highlighted{border-left:3px solid #b9e600;background:color-mix(in srgb,#dfff00 12%,transparent)}.bet-cart{position:sticky;top:8px;z-index:12;margin-bottom:14px;border:1px solid var(--color-ink);border-radius:10px;background:var(--color-paper);box-shadow:0 5px 18px #1a181414}.bet-cart-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 14px}.bet-cart-head small{display:block;color:var(--color-ink3);font-size:10px}.bet-cart-head h3{margin:2px 0 0;font-size:14px}.bet-cart-head button{color:var(--color-ink3);font-size:10.5px;text-decoration:underline;text-underline-offset:2px}.bet-cart-items{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:1px;border-top:1px solid var(--color-rule2);background:var(--color-rule2)}.bet-cart-items>div{display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:9px;padding:9px 12px;background:var(--color-paper)}.bet-cart-items span{min-width:0}.bet-cart-items span b,.bet-cart-items span small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bet-cart-items span b{font-size:11.5px}.bet-cart-items span small{margin-top:2px;color:var(--color-ink3);font-size:10px}.bet-cart-items strong{font-size:12px;font-variant-numeric:tabular-nums}.bet-cart-items button{color:var(--color-ink3);font-size:17px;line-height:1}.bet-cart-summary{display:grid;grid-template-columns:minmax(150px,1.25fr) repeat(4,minmax(90px,1fr));gap:1px;border-top:1px solid var(--color-rule2);background:var(--color-rule2)}.bet-cart-summary>*{min-width:0;padding:10px 12px;background:var(--color-panel)}.bet-cart-summary small,.bet-cart-summary label{color:var(--color-ink3);font-size:10px}.bet-cart-summary b{display:block;margin-top:2px;color:var(--color-ink);font-size:13px;font-variant-numeric:tabular-nums}.bet-cart-summary label span{display:flex;align-items:center;gap:4px;margin-top:2px;color:var(--color-ink);font-size:12px}.bet-cart-summary input{width:105px;border-bottom:1px solid var(--color-rule);background:transparent;color:var(--color-ink);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}.cart-add-button{min-width:42px;border:1px solid var(--color-rule);border-radius:5px;padding:4px 7px;color:var(--color-ink2);font-size:10.5px}.cart-add-button:hover{border-color:var(--color-ink)}.cart-add-button.is-active{border-color:var(--color-ink);background:var(--color-ink);color:var(--color-paper)}.cart-add-button:disabled{cursor:not-allowed;opacity:.35}@media(max-width:700px){.today-pick-compact{gap:10px 18px}.today-pick-compact>span{flex-basis:100%;margin-left:0}.bet-cart{position:static}.bet-cart-summary{grid-template-columns:repeat(2,1fr)}.bet-cart-summary>label{grid-column:1 / -1}}.live-baseball-situation{grid-column:1 / -1;display:grid;grid-template-columns:auto minmax(120px,1fr) minmax(120px,1fr);align-items:center;gap:14px;padding-top:12px;border-top:1px solid color-mix(in srgb,var(--color-sev2) 28%,transparent)}.live-baseball-situation>div:not(.base-state){display:grid;gap:2px}.live-baseball-situation small{color:var(--color-ink3);font-size:9.5px}.live-baseball-situation b{color:var(--color-ink);font-size:13px}.live-baseball-situation div>span{color:var(--color-ink3);font-size:10px}.base-state{display:flex;gap:7px}.base-state span{display:grid;place-items:center;width:32px;height:32px;border:1px solid var(--color-rule);border-radius:5px;background:var(--color-paper);color:var(--color-ink3);font-size:9px}.base-state span.is-occupied{border-color:var(--color-sev2);background:var(--color-sev2);color:var(--color-paper);font-weight:750}@media(max-width:700px){.live-baseball-situation{grid-template-columns:1fr 1fr;text-align:left}.base-state{grid-column:1 / -1;justify-content:center}}.bet-record-button{min-width:58px;border:1px solid var(--color-rule);border-radius:5px;padding:4px 7px;color:var(--color-ink2);font-size:10.5px;white-space:nowrap}.bet-record-button:hover{border-color:var(--color-ink);color:var(--color-ink)}.bet-record-button:disabled{cursor:not-allowed;opacity:.35}.bet-dialog-backdrop{position:fixed;inset:0;z-index:50;display:grid;place-items:center;padding:20px;background:#0f0e0c8c;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.bet-dialog{width:min(430px,100%);border:1px solid var(--color-ink);border-radius:12px;padding:20px;background:var(--color-paper);box-shadow:0 18px 60px #00000038}.bet-dialog small{color:var(--color-ink3);font-size:10px}.bet-dialog h2{margin:3px 0 14px;font-size:18px}.bet-dialog p{margin:0 0 16px;border:1px solid var(--color-rule2);border-radius:7px;padding:10px;background:var(--color-panel);font-size:13px}.bet-dialog label{display:grid;grid-template-columns:1fr 150px;align-items:center;gap:12px;margin-top:9px;color:var(--color-ink2);font-size:12px}.bet-dialog input{width:100%;border:1px solid var(--color-rule);border-radius:6px;padding:8px 9px;background:var(--color-panel);color:var(--color-ink);font:600 13px IBM Plex Mono,monospace}.bet-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.bet-dialog-actions button{border:1px solid var(--color-rule);border-radius:6px;padding:8px 12px;font-size:12px}.bet-dialog-actions .is-primary{border-color:var(--color-ink);background:var(--color-ink);color:var(--color-paper)}.dashboard-summary{display:grid;grid-template-columns:repeat(4,1fr);margin:4px 0 14px;border:1px solid var(--color-rule);border-radius:9px;overflow:hidden;background:var(--color-rule2);gap:1px}.dashboard-summary>div{padding:14px 16px;background:var(--color-panel)}.dashboard-summary small,.dashboard-summary b{display:block}.dashboard-summary small{color:var(--color-ink3);font-size:10px}.dashboard-summary b{margin-top:3px;font-size:17px;font-variant-numeric:tabular-nums}.dashboard-notice{margin-bottom:16px;border-left:3px solid var(--color-ink3);padding:8px 12px;color:var(--color-ink3);font-size:11.5px;line-height:1.7}.dashboard-bet-list{display:grid;gap:12px}.receipt-ocr{margin-bottom:14px;border:1px solid var(--color-rule);border-radius:10px;padding:16px;background:var(--color-panel)}.receipt-ocr-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.receipt-ocr-head small,.receipt-ocr-privacy{color:var(--color-ink3);font-size:10px}.receipt-ocr-head h2{margin:2px 0 0;font-size:16px}.receipt-ocr-head label,.receipt-ocr-results>button{cursor:pointer;border:1px solid var(--color-ink);border-radius:6px;padding:8px 11px;background:var(--color-ink);color:var(--color-paper);font-size:11px}.receipt-ocr-head label.is-disabled{cursor:wait;opacity:.5}.receipt-ocr-head input[type=file]{position:absolute;width:1px;height:1px;overflow:hidden;opacity:0}.receipt-ocr>p{margin:10px 0;color:var(--color-ink2);font-size:11.5px;line-height:1.6}.receipt-ocr-progress{position:relative;height:28px;overflow:hidden;border-radius:5px;background:var(--color-rule2)}.receipt-ocr-progress i{position:absolute;inset:0 auto 0 0;background:#c9f400;transition:width .2s}.receipt-ocr-progress span{position:relative;display:grid;place-items:center;height:100%;font-size:10px}.receipt-ocr-error{margin:9px 0;border-left:3px solid var(--color-sev3);padding:7px 10px;color:var(--color-sev3);font-size:11px}.receipt-ocr-results{display:grid;gap:7px;margin:12px 0}.receipt-ocr-results>label{display:grid;grid-template-columns:auto minmax(180px,1fr) 90px 110px;align-items:center;gap:9px;border-top:1px solid var(--color-rule2);padding-top:7px}.receipt-ocr-results span b,.receipt-ocr-results span small{display:block}.receipt-ocr-results span b{font-size:11.5px}.receipt-ocr-results span small{color:var(--color-ink3);font-size:9.5px}.receipt-ocr-results input[type=number]{min-width:0;border:1px solid var(--color-rule);border-radius:5px;padding:6px;background:var(--color-paper);color:var(--color-ink);font-size:11px}.receipt-ocr-results>button{justify-self:end}.receipt-ocr-results>button:disabled{cursor:not-allowed;opacity:.35}.receipt-leg-odds{color:var(--color-ink3);font-size:10px;text-align:right}.receipt-ticket-fields{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;border-top:1px solid var(--color-rule);padding-top:10px}.receipt-ticket-fields label{display:grid;gap:4px;color:var(--color-ink3);font-size:9.5px}.receipt-ticket-fields input{min-width:0;border:1px solid var(--color-rule);border-radius:5px;padding:7px;background:var(--color-paper);color:var(--color-ink);font-size:12px;font-variant-numeric:tabular-nums}@media(max-width:700px){.receipt-ocr-head{align-items:flex-start}.receipt-ocr-results>label{grid-template-columns:auto 1fr 78px}.receipt-ocr-results>label>input:last-child{grid-column:2 / -1;width:100%}}.dashboard-bet-card{border:1px solid var(--color-rule);border-left:4px solid var(--color-rule);border-radius:10px;padding:15px 16px;background:var(--color-panel)}.dashboard-bet-card.is-live{border-left-color:#c9f400}.dashboard-bet-card>header{display:flex;align-items:center;justify-content:space-between;gap:12px}.dashboard-bet-card>header span{color:var(--color-ink3);font-size:10px}.dashboard-bet-card>header h2{margin:2px 0 0;font-size:15px}.dashboard-bet-card>header>strong{border:1px solid var(--color-rule);border-radius:999px;padding:3px 8px;font-size:10px}.dashboard-bet-card.is-live>header>strong{border-color:#a3c900;background:#c9f400;color:#172000}.dashboard-bet-pick{display:grid;grid-template-columns:2fr 1fr 1fr;gap:12px;margin-top:13px;border-top:1px solid var(--color-rule2);border-bottom:1px solid var(--color-rule2);padding:11px 0}.dashboard-bet-pick small,.dashboard-bet-pick b{display:block}.dashboard-bet-pick small{color:var(--color-ink3);font-size:9.5px}.dashboard-bet-pick b{margin-top:2px;font-size:12px}.dashboard-live-state{display:flex;align-items:baseline;gap:9px;margin-top:12px}.dashboard-live-state strong{font-size:22px;font-variant-numeric:tabular-nums}.dashboard-live-state span{color:var(--color-ink3);font-size:11px}.dashboard-probability{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:12px}.dashboard-probability small,.dashboard-probability b{display:block}.dashboard-probability small{color:var(--color-ink3);font-size:9.5px}.dashboard-probability b{margin-top:2px;font-size:17px;font-variant-numeric:tabular-nums}.probability-track{margin:15px 8px 4px}.probability-track-line{position:relative;height:5px;border-radius:999px;background:var(--color-rule2)}.probability-track-line:before,.probability-track-line:after{position:absolute;top:-3px;width:1px;height:11px;background:var(--color-rule);content:""}.probability-track-line:before{left:0}.probability-track-line:after{right:0}.probability-track-line i{position:absolute;top:0;height:5px;border-radius:999px;background:var(--color-ink)}.probability-track>div:last-child{position:relative;height:20px}.probability-track span,.probability-track b{position:absolute;transform:translate(-50%);padding-top:4px;color:var(--color-ink3);font-size:8.5px;white-space:nowrap}.probability-track b{color:var(--color-ink)}.dashboard-bet-card footer{display:flex;justify-content:space-between;gap:12px;margin-top:8px;color:var(--color-ink3);font-size:9.5px}.dashboard-bet-card footer button{text-decoration:underline;text-underline-offset:2px}.dashboard-ticket-card{border:1px solid var(--color-ink);border-radius:11px;overflow:hidden;background:var(--color-panel)}.dashboard-ticket-card>header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:var(--color-ink);color:var(--color-paper)}.dashboard-ticket-card>header small{font-size:9.5px;opacity:.68}.dashboard-ticket-card>header h2{margin:2px 0 0;font-size:15px}.dashboard-ticket-card>header>strong{border:1px solid currentColor;border-radius:999px;padding:3px 8px;font-size:10px}.dashboard-ticket-card.is-live>header>strong{background:#c9f400;color:#172000}.dashboard-ticket-summary{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--color-rule2)}.dashboard-ticket-summary>div{padding:11px 13px;background:var(--color-panel)}.dashboard-ticket-summary small,.dashboard-ticket-summary b{display:block}.dashboard-ticket-summary small{color:var(--color-ink3);font-size:9.5px}.dashboard-ticket-summary b{margin-top:2px;font-size:14px;font-variant-numeric:tabular-nums}.dashboard-ticket-legs>div{display:grid;grid-template-columns:26px minmax(170px,1fr) 92px 78px;align-items:center;gap:10px;border-top:1px solid var(--color-rule2);padding:11px 13px}.dashboard-ticket-legs span b,.dashboard-ticket-legs span small{display:block}.dashboard-ticket-legs span b{font-size:12px}.dashboard-ticket-legs span small{margin-top:2px;color:var(--color-ink3);font-size:9.5px}.ticket-leg-number{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--color-rule2);font:600 10px IBM Plex Mono,monospace}.ticket-leg-live,.ticket-leg-probability{text-align:right}.dashboard-ticket-card>footer{display:flex;justify-content:space-between;gap:10px;border-top:1px solid var(--color-rule2);padding:9px 13px;color:var(--color-ink3);font-size:9.5px}.dashboard-ticket-card>footer button{text-decoration:underline;text-underline-offset:2px}.dashboard-empty{border:1px dashed var(--color-rule);border-radius:10px;padding:44px 20px;text-align:center}.dashboard-empty b{font-size:15px}.dashboard-empty p{margin:5px 0 14px;color:var(--color-ink3);font-size:12px}.dashboard-empty a{border-bottom:1px solid var(--color-ink);font-size:12px}@media(max-width:700px){.dashboard-summary{grid-template-columns:repeat(2,1fr)}.dashboard-bet-pick{grid-template-columns:1fr 1fr}.dashboard-bet-pick>div:first-child{grid-column:1 / -1}.dashboard-bet-card{padding:13px 12px}.receipt-ticket-fields,.dashboard-ticket-summary{grid-template-columns:repeat(2,1fr)}.dashboard-ticket-legs>div{grid-template-columns:24px minmax(0,1fr) 68px}.ticket-leg-live{display:none}} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:"Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", sans-serif;--font-mono:"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;--color-amber-50:oklch(98.7% .022 95.277);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-950:oklch(27.9% .077 45.635);--color-white:#fff;--spacing:.25rem;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--leading-snug:1.375;--radius-md:.375rem;--radius-lg:.5rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-paper:#faf9f6;--color-panel:#fff;--color-rule:#e3e2dd;--color-rule2:#efeeea;--color-ink:#1b1b1f;--color-ink2:#55555f;--color-ink3:#8b8b92;--color-sev0:#3f4a5a;--color-sev1:#6b6480;--color-sev2:#9a6a3c;--color-sev3:#a8322a;--color-signal:#2f4f8f;--radius-card:10px}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.fixed{position:fixed}.static{position:static}.right-5{right:calc(var(--spacing) * 5)}.bottom-5{bottom:calc(var(--spacing) * 5)}.z-20{z-index:20}.m-0{margin:0}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-0{margin-top:0}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-9{margin-top:calc(var(--spacing) * 9)}.mt-\[18px\]{margin-top:18px}.mt-\[22px\]{margin-top:22px}.mr-1{margin-right:var(--spacing)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.mr-\[7px\]{margin-right:7px}.mb-0{margin-bottom:0}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-2\.5{margin-bottom:calc(var(--spacing) * 2.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-4{margin-left:calc(var(--spacing) * 4)}.block{display:block}.flex{display:flex}.grid{display:grid}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-4{height:calc(var(--spacing) * 4)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.w-3{width:calc(var(--spacing) * 3)}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-\[104px\]{width:104px}.w-auto{width:auto}.w-full{width:100%}.max-w-\[860px\]{max-width:860px}.max-w-\[900px\]{max-width:900px}.max-w-\[1180px\]{max-width:1180px}.min-w-0{min-width:0}.min-w-14{min-width:calc(var(--spacing) * 14)}.min-w-\[92px\]{min-width:92px}.min-w-\[200px\]{min-width:200px}.min-w-\[220px\]{min-width:220px}.min-w-\[340px\]{min-width:340px}.min-w-\[380px\]{min-width:380px}.min-w-\[520px\]{min-width:520px}.min-w-\[760px\]{min-width:760px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.scroll-mt-4{scroll-margin-top:calc(var(--spacing) * 4)}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.\[grid-template-columns\:repeat\(auto-fit\,minmax\(210px\,1fr\)\)\]{grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}.\[grid-template-columns\:repeat\(auto-fit\,minmax\(280px\,1fr\)\)\]{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-5{column-gap:calc(var(--spacing) * 5)}.gap-x-10{column-gap:calc(var(--spacing) * 10)}.gap-y-1\.5{row-gap:calc(var(--spacing) * 1.5)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}.gap-y-3{row-gap:calc(var(--spacing) * 3)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[3px\]{border-radius:3px}.rounded-\[4px\]{border-radius:4px}.rounded-\[6px\]{border-radius:6px}.rounded-\[7px\]{border-radius:7px}.rounded-card{border-radius:var(--radius-card)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-\[3px\]{border-left-style:var(--tw-border-style);border-left-width:3px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400{border-color:var(--color-amber-400)}.border-ink{border-color:var(--color-ink)}.border-ink3{border-color:var(--color-ink3)}.border-rule{border-color:var(--color-rule)}.border-rule2{border-color:var(--color-rule2)}.border-sev0{border-color:var(--color-sev0)}.border-sev1{border-color:var(--color-sev1)}.border-sev2{border-color:var(--color-sev2)}.border-sev3{border-color:var(--color-sev3)}.border-signal{border-color:var(--color-signal)}.border-l-rule{border-left-color:var(--color-rule)}.border-l-sev0{border-left-color:var(--color-sev0)}.border-l-sev2{border-left-color:var(--color-sev2)}.border-l-sev3{border-left-color:var(--color-sev3)}.border-l-signal{border-left-color:var(--color-signal)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-ink{background-color:var(--color-ink)}.bg-ink3{background-color:var(--color-ink3)}.bg-panel{background-color:var(--color-panel)}.bg-paper{background-color:var(--color-paper)}.bg-sev0{background-color:var(--color-sev0)}.bg-sev1{background-color:var(--color-sev1)}.bg-sev2{background-color:var(--color-sev2)}.bg-sev3{background-color:var(--color-sev3)}.bg-signal{background-color:var(--color-signal)}.bg-signal\/5{background-color:#2f4f8f0d}@supports (color:color-mix(in lab,red,red)){.bg-signal\/5{background-color:color-mix(in oklab,var(--color-signal) 5%,transparent)}}.p-0{padding:0}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-\[3px\]{padding-inline:3px}.px-\[5px\]{padding-inline:5px}.px-\[13px\]{padding-inline:13px}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-7{padding-block:calc(var(--spacing) * 7)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-\[2px\]{padding-block:2px}.py-\[5px\]{padding-block:5px}.pt-1{padding-top:var(--spacing)}.pt-1\.5{padding-top:calc(var(--spacing) * 1.5)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-1{padding-bottom:var(--spacing)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pb-20{padding-bottom:calc(var(--spacing) * 20)}.pb-\[5px\]{padding-bottom:5px}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-3\.5{padding-left:calc(var(--spacing) * 3.5)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-baseline{vertical-align:baseline}.text-\[9\.5px\]{font-size:9.5px}.text-\[10\.5px\]{font-size:10.5px}.text-\[10px\]{font-size:10px}.text-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13\.5px\]{font-size:13.5px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[17px\]{font-size:17px}.text-\[19px\]{font-size:19px}.text-\[20px\]{font-size:20px}.text-\[22px\]{font-size:22px}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.leading-\[1\.5\]{--tw-leading:1.5;line-height:1.5}.leading-\[1\.6\]{--tw-leading:1.6;line-height:1.6}.leading-\[1\.7\]{--tw-leading:1.7;line-height:1.7}.leading-\[1\.8\]{--tw-leading:1.8;line-height:1.8}.leading-\[1\.35\]{--tw-leading:1.35;line-height:1.35}.leading-\[1\.45\]{--tw-leading:1.45;line-height:1.45}.leading-\[1\.55\]{--tw-leading:1.55;line-height:1.55}.leading-\[1\.65\]{--tw-leading:1.65;line-height:1.65}.leading-\[1\.75\]{--tw-leading:1.75;line-height:1.75}.leading-\[1\.85\]{--tw-leading:1.85;line-height:1.85}.leading-none{--tw-leading:1;line-height:1}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-\.01em\]{--tw-tracking:-.01em;letter-spacing:-.01em}.tracking-\[\.03em\]{--tw-tracking:.03em;letter-spacing:.03em}.tracking-\[\.04em\]{--tw-tracking:.04em;letter-spacing:.04em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.text-balance{text-wrap:balance}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.text-amber-700{color:var(--color-amber-700)}.text-amber-950{color:var(--color-amber-950)}.text-ink{color:var(--color-ink)}.text-ink2{color:var(--color-ink2)}.text-ink3{color:var(--color-ink3)}.text-paper{color:var(--color-paper)}.text-sev2{color:var(--color-sev2)}.text-sev3{color:var(--color-sev3)}.text-signal{color:var(--color-signal)}.text-white{color:var(--color-white)}.not-italic{font-style:normal}.underline{text-decoration-line:underline}.decoration-rule{-webkit-text-decoration-color:var(--color-rule);text-decoration-color:var(--color-rule)}.decoration-rule2{-webkit-text-decoration-color:var(--color-rule2);text-decoration-color:var(--color-rule2)}.underline-offset-2{text-underline-offset:2px}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-signal\/20{--tw-ring-color:#2f4f8f33}@supports (color:color-mix(in lab,red,red)){.ring-signal\/20{--tw-ring-color:color-mix(in oklab, var(--color-signal) 20%, transparent)}}.first\:border-0:first-child{border-style:var(--tw-border-style);border-width:0}.last\:mb-0:last-child{margin-bottom:0}@media(hover:hover){.hover\:border-ink3:hover{border-color:var(--color-ink3)}.hover\:text-ink:hover{color:var(--color-ink)}.hover\:decoration-ink:hover{-webkit-text-decoration-color:var(--color-ink);text-decoration-color:var(--color-ink)}}@media(min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.sm\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.sm\:grid-cols-\[1fr_auto\]{grid-template-columns:1fr auto}.sm\:grid-cols-\[150px_1fr\]{grid-template-columns:150px 1fr}.sm\:p-4{padding:calc(var(--spacing) * 4)}}@media(min-width:64rem){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:80rem){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media print{.print\:hidden{display:none}}}@media(prefers-color-scheme:dark){:root{--color-paper:#17171a;--color-panel:#1e1e22;--color-rule:#33333a;--color-rule2:#27272c;--color-ink:#ececed;--color-ink2:#a4a4ad;--color-ink3:#74747d;--color-sev0:#8ea3c4;--color-sev1:#a99cc4;--color-sev2:#d6a55f;--color-sev3:#ef8279;--color-signal:#8fb0e8}}:root[data-theme=dark]{--color-paper:#17171a;--color-panel:#1e1e22;--color-rule:#33333a;--color-rule2:#27272c;--color-ink:#ececed;--color-ink2:#a4a4ad;--color-ink3:#74747d;--color-sev0:#8ea3c4;--color-sev1:#a99cc4;--color-sev2:#d6a55f;--color-sev3:#ef8279;--color-signal:#8fb0e8}:root[data-theme=light]{--color-paper:#faf9f6;--color-panel:#fff;--color-rule:#e3e2dd;--color-rule2:#efeeea;--color-ink:#1b1b1f;--color-ink2:#55555f;--color-ink3:#8b8b92;--color-sev0:#3f4a5a;--color-sev1:#6b6480;--color-sev2:#9a6a3c;--color-sev3:#a8322a;--color-signal:#2f4f8f}html{background:var(--color-paper);color-scheme:light dark}body{background:var(--color-paper);color:var(--color-ink);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;margin:0;font-size:15px;line-height:1.6}.tnum{font-family:var(--font-mono);font-variant-numeric:tabular-nums;letter-spacing:-.01em}.tick{letter-spacing:.06em;color:var(--color-ink3);font-size:10.5px}summary::-webkit-details-marker{display:none}summary{list-style:none}details[open]>summary{border-bottom:1px solid var(--color-rule2)}details.card[open]{border-color:var(--color-ink3)}:where(a,button,select,input,summary):focus-visible{outline:2px solid var(--color-signal);outline-offset:2px;border-radius:4px}.model-col{display:none}.show-model .model-col{display:table-cell}@media(prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}body{word-break:keep-all;overflow-wrap:break-word;font-family:Pretendard Variable,Pretendard,-apple-system,BlinkMacSystemFont,Apple SD Gothic Neo,Noto Sans KR,sans-serif}.site-nav{min-height:64px;display:flex;align-items:stretch;justify-content:space-between;border-bottom:1px solid var(--color-ink)}.site-brand{display:flex;align-items:center;color:var(--color-ink);font-size:17px;font-weight:760;letter-spacing:-.035em;text-decoration:none}.site-nav-links{display:flex;align-items:stretch}.site-nav-link{min-width:82px;padding:0 14px;display:flex;align-items:center;justify-content:center;border-bottom:3px solid transparent;color:var(--color-ink3);font-size:13px;font-weight:620;text-decoration:none}.site-nav-link:hover{color:var(--color-ink)}.site-nav-link[aria-current=page]{border-bottom-color:var(--color-ink);color:var(--color-ink)}.market-header{padding:38px 0 23px;display:grid;grid-template-columns:1fr auto;gap:24px;align-items:end;border-bottom:1px solid var(--color-rule)}.market-header h1{margin:0;font-size:clamp(27px,4vw,38px);line-height:1.2;letter-spacing:-.055em}.market-header p{margin:10px 0 0;color:var(--color-ink2);font-size:14px;line-height:1.7}.market-meta{max-width:380px;color:var(--color-ink3);font-size:10.5px;line-height:1.65;text-align:right}.section-nav{position:sticky;top:0;z-index:12;display:flex;gap:24px;overflow-x:auto;border-bottom:1px solid var(--color-rule);background:color-mix(in srgb,var(--color-paper) 96%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);scrollbar-width:thin}.section-nav a{min-width:max-content;padding:13px 0 11px;color:var(--color-ink3);font-size:12px;font-weight:650;text-decoration:none}.section-nav a:hover{color:var(--color-ink)}.budget-simulator{margin-top:12px;border-top:1px solid var(--color-rule2)}.budget-simulator>summary{padding:11px 0;color:var(--color-ink2);font-size:11px;cursor:pointer}.ai-method{margin-top:18px;padding:20px 22px;border:1px solid var(--color-rule);border-radius:5px;background:var(--color-panel)}.ai-method-heading{display:flex;align-items:end;justify-content:space-between;gap:20px}.ai-method-heading p{margin:0 0 3px;color:var(--color-ink3);font-size:10px;font-weight:680}.ai-method-heading h2{margin:0;font-size:17px;letter-spacing:-.035em}.ai-method-heading a{min-width:max-content;color:var(--color-signal);font-size:11px}.ai-stage-grid{margin:15px 0 0;padding:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));border-top:1px solid var(--color-rule);border-bottom:1px solid var(--color-rule);list-style:none}.ai-stage-grid li{min-width:0;padding:12px 12px 13px;border-right:1px solid var(--color-rule2)}.ai-stage-grid li:first-child{padding-left:0}.ai-stage-grid li:last-child{padding-right:0;border-right:0}.ai-stage-top{display:flex;flex-wrap:wrap;align-items:center;gap:5px}.ai-stage-top>.tnum{color:var(--color-ink3);font-size:9px}.ai-stage-top b{margin-right:auto;font-size:11px}.ai-stage-grid p{margin:7px 0 0;color:var(--color-ink3);font-size:10.5px;line-height:1.62}.ai-method-foot{margin:10px 0 0;color:var(--color-ink2);font-size:10.5px}.ai-status{display:inline-flex;min-height:22px;align-items:center;padding:0 7px;border:1px solid var(--color-rule);border-radius:999px;color:var(--color-ink3);font-size:10px;font-weight:650;line-height:1;white-space:nowrap}.ai-status-used,.ai-status-operational{border-color:var(--color-ink);color:var(--color-ink)}.ai-status-shadow{border-style:dashed;border-color:var(--color-sev2);color:var(--color-sev2)}.ai-status-context_only,.ai-status-wording_only,.ai-status-template{border-color:var(--color-ink3)}.ai-status-ignored,.ai-status-missing,.ai-status-unavailable{opacity:.75;border-style:dotted}.match-section-title{margin:42px 0 12px;display:flex;align-items:end;justify-content:space-between}.match-section-title h2{margin:0;font-size:20px;letter-spacing:-.04em}.match-section-title b{color:var(--color-ink3);font-size:11px}.match-loading{margin:42px 0 60px}.match-loading-head{display:flex;align-items:center;justify-content:space-between;padding-bottom:12px;border-bottom:1px solid var(--color-ink)}.match-loading-head>div{display:flex;align-items:center;gap:9px}.match-loading-head b{font-size:17px;letter-spacing:-.025em}.match-loading-head small,.match-loading>p{color:var(--color-ink3);font-size:11px}.match-loading>p{margin:10px 0 18px}.loading-spinner{width:16px;height:16px;border:2px solid var(--color-rule2);border-top-color:var(--color-signal);border-radius:50%;animation:loading-spin .8s linear infinite}.match-loading-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.match-loading-card{min-height:170px;padding:18px;border:1px solid var(--color-rule2);border-radius:7px;background:var(--color-panel)}.match-loading-card>div{display:flex;gap:7px;margin:24px 0}.loading-line,.loading-chip{display:block;border-radius:4px;background:linear-gradient(90deg,var(--color-rule2) 25%,color-mix(in srgb,var(--color-paper) 75%,white) 50%,var(--color-rule2) 75%);background-size:220% 100%;animation:loading-shimmer 1.35s ease-in-out infinite}.loading-line{width:100%;height:10px;margin-top:11px}.loading-line.is-short{width:28%;height:8px;margin-top:0}.loading-line.is-title{width:72%;height:18px;margin-top:12px}.loading-line.is-medium{width:62%}.loading-chip{width:64px;height:27px}@keyframes loading-spin{to{transform:rotate(360deg)}}@keyframes loading-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(max-width:720px){.match-loading-grid{grid-template-columns:1fr}.match-loading-card:nth-child(n+3){display:none}}@media(prefers-reduced-motion:reduce){.loading-spinner,.loading-line,.loading-chip{animation:none}}.match-phase-counts{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.match-phase-counts button{padding:4px 7px;border:1px solid var(--color-rule2);border-radius:999px;font-weight:650}.match-phase-counts .is-live{border-color:var(--color-sev2);color:var(--color-sev2)}.match-phase-counts button{cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}.match-phase-counts button:hover{border-color:var(--color-ink3)}.match-phase-counts button[aria-pressed=true]{border-color:var(--color-ink);background:var(--color-ink);color:var(--color-paper)}.match-phase-counts button.is-live[aria-pressed=true]{border-color:var(--color-sev2);background:var(--color-sev2);color:var(--color-paper)}.match-phase-tag{padding:3px 6px;border:1px solid var(--color-rule2);border-radius:999px;color:var(--color-ink2);font-size:9.5px}.match-phase-tag.is-live{border-color:var(--color-sev2);background:color-mix(in srgb,var(--color-sev2) 8%,transparent);color:var(--color-sev2)}.match-phase-tag.is-finished{background:var(--color-panel);color:var(--color-ink3)}.filter-shell{margin-bottom:22px;border-top:1px solid var(--color-ink);border-bottom:1px solid var(--color-rule)}.filter-primary{padding:12px 0;display:grid;grid-template-columns:auto minmax(220px,1fr);gap:10px}.date-switch{display:flex;border:1px solid var(--color-rule)}.date-switch button{min-width:66px;padding:9px 13px;border:0;border-right:1px solid var(--color-rule);background:var(--color-panel);color:var(--color-ink3);font:600 12px/1 var(--font-sans);cursor:pointer}.date-switch button:last-child{border-right:0}.date-switch button[aria-pressed=true]{background:var(--color-ink);color:var(--color-paper)}.team-search input{width:100%;height:36px;padding:0 12px;border:1px solid var(--color-rule);border-radius:0;background:var(--color-panel);color:var(--color-ink);font:500 12px/1 var(--font-sans)}.advanced-filters{border-top:1px solid var(--color-rule2)}.advanced-filters>summary{padding:10px 0;display:flex;align-items:center;justify-content:space-between;color:var(--color-ink2);font-size:11.5px;cursor:pointer}.advanced-filters>summary small{color:var(--color-ink3)}.filter-grid{padding:14px 0;display:grid;grid-template-columns:repeat(3,1fr);gap:12px;border-top:1px solid var(--color-rule2)}.filter-grid label{display:grid;gap:4px;color:var(--color-ink3);font-size:10px}.filter-select{width:100%;min-height:36px;padding:0 9px;border:1px solid var(--color-rule);border-radius:0;background:var(--color-panel);color:var(--color-ink);font:500 11.5px/1 var(--font-sans)}.filter-actions{padding:10px 0;display:flex;justify-content:space-between;border-top:1px solid var(--color-rule2);color:var(--color-ink3);font-size:10.5px}.filter-actions label{display:flex;gap:6px;align-items:center}.filter-actions button{border:0;background:none;color:var(--color-ink2);text-decoration:underline;cursor:pointer}.match-card.card{margin:0;border-width:0 0 1px;border-radius:0;transition:margin .16s ease}.match-card.card[open]{margin:14px 0 20px;border:1px solid var(--color-rule);border-radius:6px;background:var(--color-panel)}.match-card.card[open]>.match-row{padding:16px 18px!important;border-bottom:1px solid var(--color-rule);background:color-mix(in srgb,var(--color-paper) 72%,var(--color-panel))}.match-card.card[open]>.match-row:hover{background:color-mix(in srgb,var(--color-paper) 72%,var(--color-panel))}.odds-chip.is-today-highlighted{border-color:var(--color-signal);background:var(--color-signal);color:#fff;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-signal) 20%,transparent)}.odds-chip.is-today-highlighted>span{color:#fff}.show-model tr.is-today-pick>td{background:color-mix(in srgb,var(--color-signal) 10%,var(--color-paper));border-color:var(--color-signal)}.show-model tr.is-today-pick>td:first-child{box-shadow:inset 4px 0 0 var(--color-signal)}.today-pick-signals{margin:0 0 12px;padding:9px 11px;display:grid;grid-template-columns:max-content 1fr;gap:3px 10px;border-left:3px solid var(--color-rule);background:var(--color-panel);font-size:11px;line-height:1.6}.today-pick-signals.is-recommended{border-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 8%,transparent)}.today-pick-signals.is-excluded{border-color:var(--color-ink3)}.today-pick-signals b{color:var(--color-signal)}.today-pick-signals span{color:var(--color-ink2)}.today-pick-signals small{grid-column:2;color:var(--color-ink3)}.dashboard-recommendations{margin:24px 0 30px}.dashboard-recommendations>header{margin-bottom:12px;display:flex;align-items:end;justify-content:space-between;gap:16px}.dashboard-recommendations>header h2{font-size:18px;font-weight:750}.dashboard-recommendations>header p,.dashboard-recommendations>header strong{color:var(--color-ink3);font-size:11px}.dashboard-recommendation-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.dashboard-recommendation-card{padding:13px 14px;border:1px solid var(--color-rule);border-left:3px solid var(--color-ink3);background:var(--color-panel)}.dashboard-recommendation-card.is-recommended{border-left-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 6%,var(--color-panel))}.dashboard-recommendation-card header,.dashboard-recommendation-pick{display:flex;align-items:center;justify-content:space-between;gap:10px}.dashboard-recommendation-card h3{margin-top:2px;font-size:13px;font-weight:700}.dashboard-recommendation-card header small,.dashboard-recommendation-card>small{color:var(--color-ink3);font-size:10px}.dashboard-recommendation-card header strong{color:var(--color-signal);font-size:10px}.dashboard-recommendation-card.is-excluded header strong{color:var(--color-ink3)}.dashboard-recommendation-pick{margin:9px 0;padding:7px 0;border-block:1px solid var(--color-rule2);font-size:12px}.dashboard-recommendation-card p{margin-top:6px;color:var(--color-ink2);font-size:11px;line-height:1.65}.dashboard-recommendation-card p b{margin-right:7px;color:var(--color-ink)}.dashboard-recommendation-card p.is-counter{color:var(--color-ink3)}.dashboard-excluded-list{margin-top:12px}.dashboard-excluded-list>summary{padding:10px 0;color:var(--color-ink2);font-size:11px;cursor:pointer}.dashboard-excluded-list[open]>summary{margin-bottom:8px}@media(max-width:760px){.dashboard-recommendation-grid{grid-template-columns:1fr}.dashboard-recommendations>header{align-items:start;flex-direction:column}}.match-card.is-live.card{border-left:3px solid var(--color-sev2)}.match-card.result-hit.card{border-left:3px solid var(--color-signal)}.match-card.result-miss.card{border-left:3px solid var(--color-sev2)}.match-row{padding:15px 0!important;display:grid!important;grid-template-columns:52px minmax(210px,1.1fr) minmax(150px,.8fr) auto!important;gap:14px!important;align-items:center}.match-row:hover{background:color-mix(in srgb,var(--color-panel) 58%,transparent)}.match-call-inline{display:flex;flex-direction:column;min-width:0;padding-left:13px;border-left:1px solid var(--color-rule2)}.match-call-inline small{color:var(--color-ink3);font-size:9.5px}.match-call-inline b{overflow:hidden;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.match-player-inline{display:block;overflow:hidden;margin-top:3px;color:var(--color-ink3);font-size:10px;font-weight:450;text-overflow:ellipsis;white-space:nowrap}.live-score-badge,.result-badge{min-width:max-content;padding:7px 9px;display:inline-flex;align-items:center;gap:5px;border:1px solid var(--color-rule);border-radius:999px;font-size:10px;font-weight:750}.live-score-badge{border-color:var(--color-sev2);color:var(--color-sev2)}.live-score-badge i{width:6px;height:6px;border-radius:50%;background:currentColor;animation:live-pulse 1.4s infinite}.live-score-badge b{color:var(--color-ink);font-size:10px}.result-badge.is-hit{border-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 9%,transparent);color:var(--color-signal)}.result-badge.is-miss{border-color:var(--color-sev2);background:color-mix(in srgb,var(--color-sev2) 8%,transparent);color:var(--color-sev2)}.result-badge.is-unrecorded,.result-badge.is-pending,.result-badge.is-void{color:var(--color-ink3)}.live-score-panel{padding:14px 16px;display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:18px;border:1px solid var(--color-sev2);border-radius:6px;background:color-mix(in srgb,var(--color-sev2) 6%,var(--color-paper))}.live-score-panel>div{display:grid;gap:3px;color:var(--color-sev2);font-size:10px}.live-score-panel>strong{font-size:17px;text-align:center}.live-score-panel em{padding:0 5px;font-size:24px;font-style:normal}.live-score-panel i{color:var(--color-ink3);font-style:normal}.live-score-panel small{color:var(--color-ink3);font-size:9.5px}.live-score-panel small b{color:var(--color-ink);font-size:11px}.settled-result-panel{padding:14px 16px;display:grid;grid-template-columns:1fr 1fr;gap:16px;border:1px solid var(--color-rule);border-radius:6px}.settled-result-panel.is-hit{border-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 7%,var(--color-paper))}.settled-result-panel.is-miss{border-color:var(--color-sev2);background:color-mix(in srgb,var(--color-sev2) 6%,var(--color-paper))}.settled-result-panel>div{display:grid;gap:3px}.settled-result-panel span,.settled-result-panel small{color:var(--color-ink3);font-size:9.5px}.settled-result-panel strong{font-size:14px}.settled-result-panel.is-hit>div:last-of-type strong{color:var(--color-signal)}.settled-result-panel.is-miss>div:last-of-type strong{color:var(--color-sev2)}.settled-result-panel p{grid-column:1 / -1;margin:0;color:var(--color-ink3);font-size:10.5px}@keyframes live-pulse{0%,to{opacity:1}50%{opacity:.3}}.match-progress{display:block;min-width:0;margin:8px 0 12px;color:var(--color-ink2);font-size:11px;font-weight:500}.match-progress-caption{display:flex;flex-wrap:wrap;justify-content:space-between;gap:3px 10px}.match-progress-track{display:block;height:5px;margin-top:5px;overflow:hidden;border-radius:3px;background:var(--color-rule2)}.match-progress-track>span{display:block;height:100%;background:var(--color-ink);transition:width .3s ease}.match-progress-note{display:block;margin-top:4px;color:var(--color-ink3);font-size:10px;font-weight:400}.match-progress.is-compact{max-width:300px;margin:6px 0 0;font-size:10px}.match-progress.is-compact .match-progress-track{height:4px}.match-progress.is-stale,.match-progress.is-interrupted{color:var(--color-sev2)}@media(prefers-reduced-motion:reduce){.match-progress-track>span{transition:none}}.match-detail{padding:22px 18px 24px!important;background:var(--color-panel)}.match-detail>*+*{margin-top:16px}.prediction-card{padding:0 18px;border:1px solid var(--color-rule);border-top:2px solid var(--color-ink);border-radius:5px;background:var(--color-paper)}.prediction-head{padding:18px 0;display:flex;align-items:center;justify-content:space-between;gap:20px;border-bottom:1px solid var(--color-rule2)}.prediction-label{margin:0 0 4px!important;color:var(--color-ink3)!important;font-size:10px!important;font-weight:680}.prediction-head h3{margin:0;font-size:23px;letter-spacing:-.045em}.prediction-head>div>p:last-child{margin:5px 0 0;color:var(--color-ink3);font-size:10.5px}.prediction-probability{min-width:70px;padding-left:18px;border-left:1px solid var(--color-rule);text-align:right}.prediction-probability b,.prediction-probability span{display:block}.prediction-probability b{font-size:18px}.prediction-probability span{color:var(--color-ink3);font-size:9px}.prediction-body{padding:20px 0 22px}.prediction-body.has-players{display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,.8fr);gap:34px}.prediction-body h4{margin:0 0 9px;color:var(--color-ink3);font-size:10px}.performance-intro{margin:-3px 0 12px;color:var(--color-ink3);font-size:10.5px;line-height:1.6}.performance-reasons{margin:0;padding:0;border-top:1px solid var(--color-rule);list-style:none}.performance-reasons li{padding:13px 0;display:grid;grid-template-columns:104px 1fr;gap:18px;border-bottom:1px solid var(--color-rule2)}.performance-reasons strong{color:var(--color-ink);font-size:11px;line-height:1.65}.performance-reasons p{margin:0;color:var(--color-ink2);font-size:12px;line-height:1.72}.player-watch-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));border-top:1px solid var(--color-rule2);border-bottom:1px solid var(--color-rule2)}.player-watch article{min-width:0;padding:10px 9px;border-right:1px solid var(--color-rule2)}.player-watch article:last-child{border-right:0}.player-watch small,.player-watch b,.player-watch a,.player-watch article p{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.player-watch small{color:var(--color-ink3);font-size:9px}.player-watch b,.player-watch a{margin-top:3px;color:var(--color-ink);font-size:13px;font-weight:700}.player-watch a{text-decoration-color:var(--color-rule);text-underline-offset:2px}.player-watch article p{margin:2px 0 0;color:var(--color-ink2);font-size:10px}.player-notes{margin:9px 0 0;padding-left:15px;color:var(--color-ink3);font-size:10px}.prediction-commentary{padding:14px 0;display:grid;grid-template-columns:92px 1fr;gap:4px 16px;border-top:1px solid var(--color-rule2)}.prediction-commentary b{color:var(--color-ink);font-size:11px}.prediction-commentary p{margin:0;color:var(--color-ink2);font-size:12px;line-height:1.75}.prediction-commentary small{grid-column:2;color:var(--color-ink3);font-size:9.5px}.prediction-caution{padding:10px 0;display:grid;grid-template-columns:92px 1fr;gap:16px;border-top:1px solid var(--color-rule2);color:var(--color-ink2);font-size:10.5px}.prediction-caution b{color:var(--color-sev2)}.prediction-card>footer{padding:8px 0;border-top:1px solid var(--color-rule2);color:var(--color-ink3);font-size:9.5px;text-align:right}.intel-panel{margin-top:16px;border:1px solid var(--color-rule);border-radius:5px;background:var(--color-paper);color:var(--color-ink2);font-size:12px;line-height:1.7}.intel-tabs{padding:0 16px;display:flex;gap:18px;overflow-x:auto;border-bottom:1px solid var(--color-rule2)}.intel-tab{min-width:max-content;min-height:44px;padding:11px 0 9px;border:0;border-bottom:2px solid transparent;background:none;color:var(--color-ink3);font:620 11px/1 var(--font-sans);cursor:pointer}.intel-tab.is-active{border-bottom-color:var(--color-ink);color:var(--color-ink)}.intel-content{padding:18px 16px}.intel-content>.prediction-card{padding:0;border:0;border-radius:0;background:transparent}.intel-content>.prediction-card .prediction-head{padding-top:0}.ai-decision-path{color:var(--color-ink2)}.probability-provenance{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr) auto minmax(0,1fr);align-items:center;border-top:1px solid var(--color-rule);border-bottom:1px solid var(--color-rule)}.probability-provenance>div{padding:12px 10px}.probability-provenance>div:first-child{padding-left:0}.probability-provenance>div:last-child{padding-right:0}.probability-provenance span,.probability-provenance b{display:block}.probability-provenance div>span{color:var(--color-ink3);font-size:9.5px}.probability-provenance div>b{margin-top:2px;color:var(--color-ink);font-size:16px}.probability-provenance .probability-arrow{color:var(--color-ink3);font-size:12px}.ai-revision-warning{margin:10px 0 0;padding:9px 10px;border-left:2px solid var(--color-sev2);background:var(--color-panel);color:var(--color-sev3);font-size:10.5px}.ai-stage-list{margin-top:14px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));border-top:1px solid var(--color-rule2)}.ai-stage-list article{padding:11px 12px 11px 0;border-bottom:1px solid var(--color-rule2)}.ai-stage-list article:nth-child(2n){padding-left:12px;border-left:1px solid var(--color-rule2)}.ai-stage-list article>div{display:flex;align-items:center;justify-content:space-between;gap:8px}.ai-stage-list article b{color:var(--color-ink);font-size:11px}.ai-stage-list article p{margin:5px 0 0;color:var(--color-ink3);font-size:10.5px;line-height:1.55}.evidence-usage{margin-top:13px;border-top:1px solid var(--color-rule);border-bottom:1px solid var(--color-rule)}.evidence-usage>summary{min-height:44px;padding:9px 0;display:flex;align-items:center;color:var(--color-ink2);font-size:10.5px;cursor:pointer}.evidence-usage ul{margin:0;padding:0 0 8px;list-style:none}.evidence-usage li{padding:7px 0;display:flex;align-items:center;justify-content:space-between;gap:10px;border-top:1px solid var(--color-rule2);font-size:10.5px}.evidence-usage li b{color:var(--color-ink)}.ai-decision-meta{margin:9px 0 0;color:var(--color-ink3);font-size:9.5px;text-align:right}.source-stamp{padding:0 16px 12px}.price-sheet{margin-top:16px;border:1px solid var(--color-rule);border-radius:5px;background:var(--color-paper)}.price-sheet>summary{padding:12px 14px;display:flex;justify-content:space-between;color:var(--color-ink2);font-size:11px;cursor:pointer}.price-sheet[open]>summary{border-bottom:1px solid var(--color-rule2)}.price-sheet>div{padding:12px 14px!important}@media(max-width:720px){.site-nav{display:block;padding-top:13px}.site-brand{height:34px}.site-nav-links{width:100%;overflow-x:auto}.site-nav-link{min-width:76px;padding:10px 8px 9px}.market-header{padding-top:28px;grid-template-columns:1fr;gap:9px}.market-meta{text-align:left}.section-nav{gap:18px}.ai-method{padding:17px 15px}.ai-method-heading{align-items:start;flex-direction:column;gap:6px}.ai-stage-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.ai-stage-grid li,.ai-stage-grid li:first-child,.ai-stage-grid li:last-child{padding:11px 9px;border-right:1px solid var(--color-rule2)}.ai-stage-grid li:nth-child(2n){border-right:0}.ai-stage-grid li:nth-child(-n+2){border-bottom:1px solid var(--color-rule2)}.filter-primary,.prediction-body.has-players{grid-template-columns:1fr}.filter-grid{grid-template-columns:repeat(2,1fr)}.match-row{grid-template-columns:46px minmax(0,1fr)!important}.match-call-inline{grid-column:2;padding:8px 0 0;border-top:1px solid var(--color-rule2);border-left:0}.match-call-inline b{overflow:visible;text-overflow:clip;white-space:normal}.match-card.card[open]{margin:10px -8px 16px}.match-card.card[open]>.match-row{padding:14px!important}.match-detail{padding:14px!important}.match-section-title{align-items:start;gap:10px}.match-phase-counts{max-width:230px}.live-score-panel{grid-template-columns:1fr;gap:8px;text-align:center}.live-score-panel>div{display:flex;justify-content:center;gap:6px}.settled-result-panel{grid-template-columns:1fr}.settled-result-panel p{grid-column:1}.prediction-card{padding:0 14px}.prediction-head h3{font-size:20px}.prediction-body{padding:17px 0 18px}.performance-reasons li{grid-template-columns:1fr;gap:4px;padding:12px 0}.prediction-commentary,.prediction-caution{grid-template-columns:1fr;gap:4px}.prediction-commentary small{grid-column:1}.intel-tabs{padding:0 12px}.intel-content{padding:14px 12px}.probability-provenance{grid-template-columns:1fr}.probability-provenance>div,.probability-provenance>div:first-child,.probability-provenance>div:last-child{padding:8px 0;display:flex;align-items:center;justify-content:space-between}.probability-provenance .probability-arrow{display:none}.ai-stage-list{grid-template-columns:1fr}.ai-stage-list article:nth-child(2n){padding-left:0;border-left:0}.source-stamp{padding:0 12px 10px}}.today-pick-compact{display:flex;align-items:center;flex-wrap:wrap;gap:18px;margin:0 0 12px;padding:12px 14px;border:1px solid var(--color-rule);border-radius:8px;background:var(--color-paper)}.today-pick-compact>div{display:grid;gap:2px}.today-pick-compact small{color:var(--color-ink3);font-size:10px}.today-pick-compact b{color:var(--color-ink);font-size:13px}.today-pick-compact>span{margin-left:auto;color:var(--color-ink3);font-size:11px}.today-betting-recommendation{margin-bottom:22px}.today-betting-recommendation.is-buy{border-top:3px solid var(--color-signal)}.today-betting-recommendation.is-challenge{border-top:3px solid #b9e600}.today-bet-verdict{display:grid;grid-template-columns:repeat(auto-fit,minmax(135px,1fr));gap:9px;margin-top:13px}.today-bet-verdict>div{border:1px solid var(--color-rule2);border-radius:7px;padding:9px 11px;background:var(--color-paper)}.today-bet-verdict small{display:block;margin-bottom:2px;color:var(--color-ink3);font-size:10px}.today-bet-verdict b{font-size:12.5px}.today-bet-leg{display:flex;flex-wrap:wrap;align-items:baseline;gap:9px;border-top:1px solid var(--color-rule2);padding:8px 7px}.today-bet-leg:first-child{border-top:0}.today-bet-leg.is-highlighted{border-left:3px solid #b9e600;background:color-mix(in srgb,#dfff00 12%,transparent)}.bet-cart{position:sticky;top:8px;z-index:12;margin-bottom:14px;border:1px solid var(--color-ink);border-radius:10px;background:var(--color-paper);box-shadow:0 5px 18px #1a181414}.bet-cart-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 14px}.bet-cart-head small{display:block;color:var(--color-ink3);font-size:10px}.bet-cart-head h3{margin:2px 0 0;font-size:14px}.bet-cart-head button{color:var(--color-ink3);font-size:10.5px;text-decoration:underline;text-underline-offset:2px}.bet-cart-items{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:1px;border-top:1px solid var(--color-rule2);background:var(--color-rule2)}.bet-cart-items>div{display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:9px;padding:9px 12px;background:var(--color-paper)}.bet-cart-items span{min-width:0}.bet-cart-items span b,.bet-cart-items span small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bet-cart-items span b{font-size:11.5px}.bet-cart-items span small{margin-top:2px;color:var(--color-ink3);font-size:10px}.bet-cart-items strong{font-size:12px;font-variant-numeric:tabular-nums}.bet-cart-items button{color:var(--color-ink3);font-size:17px;line-height:1}.bet-cart-summary{display:grid;grid-template-columns:minmax(150px,1.25fr) repeat(4,minmax(90px,1fr));gap:1px;border-top:1px solid var(--color-rule2);background:var(--color-rule2)}.bet-cart-summary>*{min-width:0;padding:10px 12px;background:var(--color-panel)}.bet-cart-summary small,.bet-cart-summary label{color:var(--color-ink3);font-size:10px}.bet-cart-summary b{display:block;margin-top:2px;color:var(--color-ink);font-size:13px;font-variant-numeric:tabular-nums}.bet-cart-summary label span{display:flex;align-items:center;gap:4px;margin-top:2px;color:var(--color-ink);font-size:12px}.bet-cart-summary input{width:105px;border-bottom:1px solid var(--color-rule);background:transparent;color:var(--color-ink);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}.cart-add-button{min-width:42px;border:1px solid var(--color-rule);border-radius:5px;padding:4px 7px;color:var(--color-ink2);font-size:10.5px}.cart-add-button:hover{border-color:var(--color-ink)}.cart-add-button.is-active{border-color:var(--color-ink);background:var(--color-ink);color:var(--color-paper)}.cart-add-button:disabled{cursor:not-allowed;opacity:.35}@media(max-width:700px){.today-pick-compact{gap:10px 18px}.today-pick-compact>span{flex-basis:100%;margin-left:0}.bet-cart{position:static}.bet-cart-summary{grid-template-columns:repeat(2,1fr)}.bet-cart-summary>label{grid-column:1 / -1}}.live-baseball-situation{grid-column:1 / -1;display:grid;grid-template-columns:auto minmax(120px,1fr) minmax(120px,1fr);align-items:center;gap:14px;padding-top:12px;border-top:1px solid color-mix(in srgb,var(--color-sev2) 28%,transparent)}.live-baseball-situation>div:not(.base-state){display:grid;gap:2px}.live-baseball-situation small{color:var(--color-ink3);font-size:9.5px}.live-baseball-situation b{color:var(--color-ink);font-size:13px}.live-baseball-situation div>span{color:var(--color-ink3);font-size:10px}.base-state{display:flex;gap:7px}.base-state span{display:grid;place-items:center;width:32px;height:32px;border:1px solid var(--color-rule);border-radius:5px;background:var(--color-paper);color:var(--color-ink3);font-size:9px}.base-state span.is-occupied{border-color:var(--color-sev2);background:var(--color-sev2);color:var(--color-paper);font-weight:750}@media(max-width:700px){.live-baseball-situation{grid-template-columns:1fr 1fr;text-align:left}.base-state{grid-column:1 / -1;justify-content:center}}.bet-record-button{min-width:58px;border:1px solid var(--color-rule);border-radius:5px;padding:4px 7px;color:var(--color-ink2);font-size:10.5px;white-space:nowrap}.bet-record-button:hover{border-color:var(--color-ink);color:var(--color-ink)}.bet-record-button:disabled{cursor:not-allowed;opacity:.35}.bet-dialog-backdrop{position:fixed;inset:0;z-index:50;display:grid;place-items:center;padding:20px;background:#0f0e0c8c;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.bet-dialog{width:min(430px,100%);border:1px solid var(--color-ink);border-radius:12px;padding:20px;background:var(--color-paper);box-shadow:0 18px 60px #00000038}.bet-dialog small{color:var(--color-ink3);font-size:10px}.bet-dialog h2{margin:3px 0 14px;font-size:18px}.bet-dialog p{margin:0 0 16px;border:1px solid var(--color-rule2);border-radius:7px;padding:10px;background:var(--color-panel);font-size:13px}.bet-dialog label{display:grid;grid-template-columns:1fr 150px;align-items:center;gap:12px;margin-top:9px;color:var(--color-ink2);font-size:12px}.bet-dialog input{width:100%;border:1px solid var(--color-rule);border-radius:6px;padding:8px 9px;background:var(--color-panel);color:var(--color-ink);font:600 13px IBM Plex Mono,monospace}.bet-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.bet-dialog-actions button{border:1px solid var(--color-rule);border-radius:6px;padding:8px 12px;font-size:12px}.bet-dialog-actions .is-primary{border-color:var(--color-ink);background:var(--color-ink);color:var(--color-paper)}.dashboard-summary{display:grid;grid-template-columns:repeat(4,1fr);margin:4px 0 14px;border:1px solid var(--color-rule);border-radius:9px;overflow:hidden;background:var(--color-rule2);gap:1px}.dashboard-summary>div{padding:14px 16px;background:var(--color-panel)}.dashboard-summary small,.dashboard-summary b{display:block}.dashboard-summary small{color:var(--color-ink3);font-size:10px}.dashboard-summary b{margin-top:3px;font-size:17px;font-variant-numeric:tabular-nums}.dashboard-notice{margin-bottom:16px;border-left:3px solid var(--color-ink3);padding:8px 12px;color:var(--color-ink3);font-size:11.5px;line-height:1.7}.dashboard-bet-list{display:grid;gap:12px}.receipt-ocr{margin-bottom:14px;border:1px solid var(--color-rule);border-radius:10px;padding:16px;background:var(--color-panel)}.receipt-ocr-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.receipt-ocr-head small,.receipt-ocr-privacy{color:var(--color-ink3);font-size:10px}.receipt-ocr-head h2{margin:2px 0 0;font-size:16px}.receipt-ocr-head label,.receipt-ocr-results>button{cursor:pointer;border:1px solid var(--color-ink);border-radius:6px;padding:8px 11px;background:var(--color-ink);color:var(--color-paper);font-size:11px}.receipt-ocr-head label.is-disabled{cursor:wait;opacity:.5}.receipt-ocr-head input[type=file]{position:absolute;width:1px;height:1px;overflow:hidden;opacity:0}.receipt-ocr>p{margin:10px 0;color:var(--color-ink2);font-size:11.5px;line-height:1.6}.receipt-ocr-progress{position:relative;height:28px;overflow:hidden;border-radius:5px;background:var(--color-rule2)}.receipt-ocr-progress i{position:absolute;inset:0 auto 0 0;background:#c9f400;transition:width .2s}.receipt-ocr-progress span{position:relative;display:grid;place-items:center;height:100%;font-size:10px}.receipt-ocr-error{margin:9px 0;border-left:3px solid var(--color-sev3);padding:7px 10px;color:var(--color-sev3);font-size:11px}.receipt-ocr-results{display:grid;gap:7px;margin:12px 0}.receipt-ocr-results>label{display:grid;grid-template-columns:auto minmax(180px,1fr) 90px 110px;align-items:center;gap:9px;border-top:1px solid var(--color-rule2);padding-top:7px}.receipt-ocr-results span b,.receipt-ocr-results span small{display:block}.receipt-ocr-results span b{font-size:11.5px}.receipt-ocr-results span small{color:var(--color-ink3);font-size:9.5px}.receipt-ocr-results input[type=number]{min-width:0;border:1px solid var(--color-rule);border-radius:5px;padding:6px;background:var(--color-paper);color:var(--color-ink);font-size:11px}.receipt-ocr-results>button{justify-self:end}.receipt-ocr-results>button:disabled{cursor:not-allowed;opacity:.35}.receipt-leg-odds{color:var(--color-ink3);font-size:10px;text-align:right}.receipt-ticket-fields{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;border-top:1px solid var(--color-rule);padding-top:10px}.receipt-ticket-fields label{display:grid;gap:4px;color:var(--color-ink3);font-size:9.5px}.receipt-ticket-fields input{min-width:0;border:1px solid var(--color-rule);border-radius:5px;padding:7px;background:var(--color-paper);color:var(--color-ink);font-size:12px;font-variant-numeric:tabular-nums}@media(max-width:700px){.receipt-ocr-head{align-items:flex-start}.receipt-ocr-results>label{grid-template-columns:auto 1fr 78px}.receipt-ocr-results>label>input:last-child{grid-column:2 / -1;width:100%}}.dashboard-bet-card{border:1px solid var(--color-rule);border-left:4px solid var(--color-rule);border-radius:10px;padding:15px 16px;background:var(--color-panel)}.dashboard-bet-card.is-live{border-left-color:#c9f400}.dashboard-bet-card>header{display:flex;align-items:center;justify-content:space-between;gap:12px}.dashboard-bet-card>header span{color:var(--color-ink3);font-size:10px}.dashboard-bet-card>header h2{margin:2px 0 0;font-size:15px}.dashboard-bet-card>header>strong{border:1px solid var(--color-rule);border-radius:999px;padding:3px 8px;font-size:10px}.dashboard-bet-card.is-live>header>strong{border-color:#a3c900;background:#c9f400;color:#172000}.dashboard-bet-pick{display:grid;grid-template-columns:2fr 1fr 1fr;gap:12px;margin-top:13px;border-top:1px solid var(--color-rule2);border-bottom:1px solid var(--color-rule2);padding:11px 0}.dashboard-bet-pick small,.dashboard-bet-pick b{display:block}.dashboard-bet-pick small{color:var(--color-ink3);font-size:9.5px}.dashboard-bet-pick b{margin-top:2px;font-size:12px}.dashboard-live-state{display:flex;align-items:baseline;gap:9px;margin-top:12px}.dashboard-live-state strong{font-size:22px;font-variant-numeric:tabular-nums}.dashboard-live-state span{color:var(--color-ink3);font-size:11px}.dashboard-probability{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:12px}.dashboard-probability small,.dashboard-probability b{display:block}.dashboard-probability small{color:var(--color-ink3);font-size:9.5px}.dashboard-probability b{margin-top:2px;font-size:17px;font-variant-numeric:tabular-nums}.probability-track{margin:15px 8px 4px}.probability-track-line{position:relative;height:5px;border-radius:999px;background:var(--color-rule2)}.probability-track-line:before,.probability-track-line:after{position:absolute;top:-3px;width:1px;height:11px;background:var(--color-rule);content:""}.probability-track-line:before{left:0}.probability-track-line:after{right:0}.probability-track-line i{position:absolute;top:0;height:5px;border-radius:999px;background:var(--color-ink)}.probability-track>div:last-child{position:relative;height:20px}.probability-track span,.probability-track b{position:absolute;transform:translate(-50%);padding-top:4px;color:var(--color-ink3);font-size:8.5px;white-space:nowrap}.probability-track b{color:var(--color-ink)}.dashboard-bet-card footer{display:flex;justify-content:space-between;gap:12px;margin-top:8px;color:var(--color-ink3);font-size:9.5px}.dashboard-bet-card footer button{text-decoration:underline;text-underline-offset:2px}.dashboard-ticket-card{border:1px solid var(--color-ink);border-radius:11px;overflow:hidden;background:var(--color-panel)}.dashboard-ticket-card>header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:var(--color-ink);color:var(--color-paper)}.dashboard-ticket-card>header small{font-size:9.5px;opacity:.68}.dashboard-ticket-card>header h2{margin:2px 0 0;font-size:15px}.dashboard-ticket-card>header>strong{border:1px solid currentColor;border-radius:999px;padding:3px 8px;font-size:10px}.dashboard-ticket-card.is-live>header>strong{background:#c9f400;color:#172000}.dashboard-ticket-summary{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--color-rule2)}.dashboard-ticket-summary>div{padding:11px 13px;background:var(--color-panel)}.dashboard-ticket-summary small,.dashboard-ticket-summary b{display:block}.dashboard-ticket-summary small{color:var(--color-ink3);font-size:9.5px}.dashboard-ticket-summary b{margin-top:2px;font-size:14px;font-variant-numeric:tabular-nums}.dashboard-ticket-legs>div{display:grid;grid-template-columns:26px minmax(170px,1fr) 92px 78px;align-items:center;gap:10px;border-top:1px solid var(--color-rule2);padding:11px 13px}.dashboard-ticket-legs span b,.dashboard-ticket-legs span small{display:block}.dashboard-ticket-legs span b{font-size:12px}.dashboard-ticket-legs span small{margin-top:2px;color:var(--color-ink3);font-size:9.5px}.ticket-leg-number{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--color-rule2);font:600 10px IBM Plex Mono,monospace}.ticket-leg-live,.ticket-leg-probability{text-align:right}.dashboard-ticket-card>footer{display:flex;justify-content:space-between;gap:10px;border-top:1px solid var(--color-rule2);padding:9px 13px;color:var(--color-ink3);font-size:9.5px}.dashboard-ticket-card>footer button{text-decoration:underline;text-underline-offset:2px}.dashboard-empty{border:1px dashed var(--color-rule);border-radius:10px;padding:44px 20px;text-align:center}.dashboard-empty b{font-size:15px}.dashboard-empty p{margin:5px 0 14px;color:var(--color-ink3);font-size:12px}.dashboard-empty a{border-bottom:1px solid var(--color-ink);font-size:12px}@media(max-width:700px){.dashboard-summary{grid-template-columns:repeat(2,1fr)}.dashboard-bet-pick{grid-template-columns:1fr 1fr}.dashboard-bet-pick>div:first-child{grid-column:1 / -1}.dashboard-bet-card{padding:13px 12px}.receipt-ticket-fields,.dashboard-ticket-summary{grid-template-columns:repeat(2,1fr)}.dashboard-ticket-legs>div{grid-template-columns:24px minmax(0,1fr) 68px}.ticket-leg-live{display:none}} diff --git a/docs/assets/dashboard-NufLhl-X.js b/docs/assets/dashboard-Do6yPHeZ.js similarity index 99% rename from docs/assets/dashboard-NufLhl-X.js rename to docs/assets/dashboard-Do6yPHeZ.js index 2ff478ee..da9346bd 100644 --- a/docs/assets/dashboard-NufLhl-X.js +++ b/docs/assets/dashboard-Do6yPHeZ.js @@ -1 +1 @@ -import{r as S,j as e,N as se,i as ne,c as ie,T as oe}from"./cache-guard-cBqh6aGv.js";import{c as re,u as ae,r as B,g as ce,l as A,a as le,e as U,w as de,s as V,b as he,d as ue}from"./bet-ledger-ES6mNno4.js";import{u as me}from"./poll-DDJ49qcP.js";import{a as pe,d as xe}from"./unified-recommendation-BCuqyY1U.js";import"./decision-view-model-Bfii5tWW.js";const fe="modulepreload",be=function(t,s){return new URL(t,s).href},K={},ge=function(s,o,n){let l=Promise.resolve();if(o&&o.length>0){let j=function(i){return Promise.all(i.map(d=>Promise.resolve(d).then(x=>({status:"fulfilled",value:x}),x=>({status:"rejected",reason:x}))))};const c=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),u=r?.nonce||r?.getAttribute("nonce");l=j(o.map(i=>{if(i=be(i,n),i in K)return;K[i]=!0;const d=i.endsWith(".css"),x=d?'[rel="stylesheet"]':"";if(n)for(let p=c.length-1;p>=0;p--){const f=c[p];if(f.href===i&&(!d||f.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${x}`))return;const a=document.createElement("link");if(a.rel=d?"stylesheet":fe,d||(a.as="script"),a.crossOrigin="",a.href=i,u&&a.setAttribute("nonce",u),document.head.appendChild(a),d)return new Promise((p,f)=>{a.addEventListener("load",p),a.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${i}`)))})}))}function h(c){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=c,window.dispatchEvent(r),!r.defaultPrevented)throw c}return l.then(c=>{for(const r of c||[])r.status==="rejected"&&h(r.reason);return s().catch(h)})},Q=t=>String(t||"").replace(/\s+/g,"").toLowerCase(),je=t=>{const s=Q(t),o=new Set([s]);return s==="홈"&&o.add("홈승"),s==="원정"&&["원정승","홈패"].forEach(n=>o.add(n)),s==="무"&&o.add("무승부"),s.includes("핸디홈")&&["핸디승","핸디캡홈"].forEach(n=>o.add(n)),s.includes("핸디원정")&&["핸디패","핸디캡원정"].forEach(n=>o.add(n)),[...o].filter(Boolean).sort((n,l)=>l.length-n.length)},Y=t=>[...String(t||"").matchAll(/\d[\d,]*(?:\.\d+)?/g)].map(s=>({raw:s[0],value:Number(s[0].replaceAll(",",""))})).filter(s=>Number.isFinite(s.value)),X=t=>String(t).split("").map(s=>`[${s}${s==="0"?"oO":s==="1"?"iIlL":""}]`).join("[^0-9a-zA-Z]{0,2}");function ye(t,s=[]){const o=String(t||"").split(/\r?\n/),n=new Set((s||[]).flatMap(h=>(h.options||[]).map(c=>String(c?.게임번호||"").trim())).filter(Boolean)),l=[];for(const h of o)if(!/(사업자|등록번호|\d{2}\s*-\s*\d{2,3}\s*-\s*\d{4,})/i.test(h))for(const c of n)l.includes(c)||new RegExp(`(^|[^0-9])${X(c)}(?=[^0-9]|$)`,"i").test(h)&&l.push(c);return l}function ve(t,s=[]){const o=String(t||"").split(/\r?\n/).map(r=>r.trim()).filter(Boolean),n=(s||[]).flatMap(r=>(r.options||[]).map(u=>({game:r,option:u}))),l=[],h=new Set,c=new Set;for(let r=0;rString(d.option?.게임번호||"").trim()))].filter(d=>d&&!c.has(d)&&new RegExp(`(^|[^0-9])${X(d)}(?=[^0-9]|$)`,"i").test(o[r]));for(const d of i){const x=n.filter(g=>String(g.option?.게임번호||"").trim()===d),a=Y(u).filter(g=>g.raw.replaceAll(",","")!==d&&g.value>1&&g.value<100),p=x.map(g=>{const C=Number(g.option?.배당),m=a.sort((N,R)=>Math.abs(N.value-C)-Math.abs(R.value-C))[0],b=je(g.option?.선택).some(N=>j.includes(N));return{candidate:g,oddsToken:m,distance:m?Math.abs(m.value-C):1/0,hasChoice:b}}).filter(g=>g.oddsToken&&g.distance<=.03).sort((g,C)=>g.distance-C.distance||Number(C.hasChoice)-Number(g.hasChoice))[0];if(!p)continue;const f=p.candidate,y=`${f.game.round}|${d}|${f.option.market}|${f.option.label||""}|${f.option.선택}`;if(h.has(y))continue;h.add(y),c.add(d);const E=Y(u).filter(g=>g.value>=100&&g.value!==Number(d)).sort((g,C)=>C.value-g.value)[0];l.push({key:y,game:f.game,option:f.option,purchaseOdds:p.oddsToken.value,stake:E?.value||1e4,sourceText:u})}}return l}function H(t,s=[]){const o=ve(t,s),n=new Map(o.map(l=>[String(l.option?.게임번호),l]));for(const l of ye(t,s)){if(n.has(l))continue;const h=(s||[]).find(r=>(r.options||[]).some(u=>String(u?.게임번호||"")===l));if(!h)continue;const c=(h.options||[]).filter(r=>String(r?.게임번호||"")===l);n.set(l,{key:`${h.round}|${l}|unresolved`,game:h,option:null,optionChoices:c,purchaseOdds:"",stake:1e4,sourceText:l,needsConfirmation:!0})}return[...n.values()]}function ke(t,s,o=[]){const n=H(t,o),l=H(s,o),h=new Map(n.map(i=>[String(i.option?.게임번호||i.sourceText),i])),c=[],r=new Set;for(const i of l){const d=String(i.option?.게임번호||i.sourceText);r.has(d)||(c.push(h.get(d)||i),r.add(d))}for(const i of n){const d=String(i.option?.게임번호||i.sourceText);r.has(d)||(c.push(i),r.add(d))}const u=[];for(const i of o||[])for(const d of i.options||[]){const x=String(d?.게임번호||"").trim();if(!x||r.has(x))continue;const a=String(t||"").search(new RegExp(`(^|[^0-9])${x}(?=[^0-9]|$)`));if(a<0)continue;const p=(i.options||[]).filter(f=>String(f?.게임번호||"")===x);u.push({index:a,row:{key:`${i.round}|${x}|unresolved`,game:i,option:null,optionChoices:p,purchaseOdds:"",stake:1e4,sourceText:x,needsConfirmation:!0}}),r.add(x)}u.sort((i,d)=>i.index-d.index).forEach(({row:i})=>c.push(i));const j=[];for(const[i,d]of String(t||"").split(/\r?\n/).entries()){const x=(o||[]).flatMap(y=>(y.options||[]).map(E=>({game:y,option:E}))).filter(({option:y})=>{const E=String(y?.게임번호||""),g=Number(y?.line),C=Number(y?.배당);return E&&!r.has(E)&&Number.isFinite(g)&&Number.isFinite(C)&&d.includes(String(Math.abs(g)))&&new RegExp(`(^|[^0-9])${C.toFixed(2).replace(".","\\.")}(?=[^0-9]|$)`).test(d)}),a=[...new Set(x.map(({option:y})=>String(y.게임번호)))];if(a.length!==1)continue;const p=x.find(({option:y})=>String(y.게임번호)===a[0]),f=(p.game.options||[]).filter(y=>String(y?.게임번호||"")===a[0]);j.push({index:i,row:{key:`${p.game.round}|${a[0]}|${p.option.market}|${p.option.label||""}|${p.option.선택}`,game:p.game,option:p.option,optionChoices:f,purchaseOdds:p.option.배당,stake:1e4,sourceText:a[0],needsConfirmation:!1}}),r.add(a[0])}return j.sort((i,d)=>i.index-d.index).forEach(({row:i})=>c.push(i)),c}const z=(t,s)=>{const n=String(t||"").replace(/\s+/g," ").match(new RegExp(`(?:${s.join("|")})[^0-9]{0,16}([0-9][0-9,.]*)`,"i"));return n?Number(n[1].replaceAll(",","")):null};function Ne(t,s=[]){const o=s.reduce((c,r)=>c*Number(r.purchaseOdds||1),1),n=z(t,["개별투표금액","총투표금액","투표금액","구매금액"])||s.find(c=>Number(c.stake)>=100)?.stake||1e4,l=z(t,["예상배당률","조합배당","배당률"])||Number(o.toFixed(2)),h=z(t,["예상적중금액","예상환급금","적중금액"])||Math.round(n*l);return{stake:n,combinedOdds:l,expectedPayout:h,legCount:s.length}}const $e=(t,s)=>{const o=t[s],n=t[s+1],l=t[s+2];return l>135&&l-o>22&&l-n>4};function Se(t,s,o){const n=[],l=s*.07;for(let c=0;c=l&&u-r>=l&&u-r<=s*.62&&n.push({y:c,left:r,right:u})}const h=[];for(let c=0;ci.y-r.y>=o*.018&&i.y-r.y<=o*.09&&Math.abs(i.left-r.left)Math.abs(i.top-j.top)c.top-r.top).reduce((c,r)=>{const u=c.at(-1);return u&&Math.abs(u.left-r.left){const c=String(h||"");return/오버/.test(c)?["오버","over"]:/언더/.test(c)?["언더","under"]:/무/.test(c)?["무","draw"]:/원정|패/.test(c)?["패","원정","away"]:/홈|승/.test(c)?["승","홈","home"]:[c]},l=s.map((h,c)=>({index:c,hit:n(h?.선택).some(r=>r&&o.includes(r.toLowerCase()))})).filter(h=>h.hit);return l.length===1?l[0].index:null}const Me="https://proto-odds-collector.fly.dev/anonymous-bets";function Oe(t){const s=Math.max(0,Number(t)||0);return s<5e3?"under_5000":s<1e4?"5000_9999":s<5e4?"10000_49999":s<1e5?"50000_99999":"100000_plus"}function _e(t,s={}){const o=(t||[]).filter(n=>n?.option).map(n=>({game_no:String(n.option.게임번호||""),sport:String(n.game?.sport||"").slice(0,12),league:String(n.game?.league||"").slice(0,40),market:String(n.option.market||"").slice(0,30),label:String(n.option.label||"").slice(0,30),choice:String(n.option.선택||"").slice(0,30),purchase_odds:Number(n.purchaseOdds||n.option.배당)}));return{schema_version:1,source:"receipt_ocr",round:Number(t?.[0]?.game?.round)||null,combo_size:o.length,combined_odds:Number(s.combinedOdds)||null,stake_band:Oe(s.stake),legs:o}}async function Re(t,s,o=globalThis.fetch){const n=_e(t,s);return!n.legs.length||!o?!1:(await o(Me,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})).ok}function Pe({games:t=[],onImported:s}){const[o,n]=S.useState(!1),[l,h]=S.useState(0),[c,r]=S.useState(""),[u,j]=S.useState([]),[i,d]=S.useState({stake:1e4,combinedOdds:0,expectedPayout:0}),[x,a]=S.useState(""),[p,f]=S.useState(""),y=async m=>{if(!m)return;n(!0),h(0),a(""),j([]);let b;try{const{createWorker:N}=await ge(async()=>{const{createWorker:v}=await import("./index-DkgChCA8.js").then(T=>T.i);return{createWorker:v}},[],import.meta.url);b=await N("kor+eng",1,{logger:v=>{v.status==="recognizing text"&&h(Math.round((v.progress||0)*100))}});const[R,F]=await Promise.all([b.recognize(m,{rotateAuto:!0}),Ce(m).catch(()=>({rects:[],width:0}))]),I=String(R?.data?.text||""),q=I;r(q);let M=ke(I,"",t);M.length||(M=(t||[]).flatMap(v=>[...new Set((v.options||[]).map($=>String($?.게임번호||"")).filter(Boolean))].filter($=>new RegExp(`(^|[^0-9])${$}(?=[^0-9]|$)`).test(I)).map($=>({key:`${v.round}|${$}|unresolved`,game:v,option:null,optionChoices:(v.options||[]).filter(w=>String(w?.게임번호||"")===$),purchaseOdds:"",stake:1e4,sourceText:$,needsConfirmation:!0,textIndex:I.indexOf($)}))).sort((v,T)=>v.textIndex-T.textIndex));const G=new Set(M.map(v=>String(v.option?.게임번호||v.sourceText)));for(const v of I.split(/\r?\n/)){const T=[...v.matchAll(/\d+(?:\.\d+)?/g)].map(k=>Number(k[0])),$=(t||[]).flatMap(k=>(k.options||[]).map(P=>({game:k,option:P}))).filter(({option:k})=>{const P=String(k?.게임번호||""),W=Number(k?.line),J=Number(k?.배당);return P&&!G.has(P)&&Number.isFinite(W)&&Number.isFinite(J)&&T.some(te=>Math.abs(te-Math.abs(W))<=2)&&v.includes(J.toFixed(2))}),w=[...new Set($.map(({option:k})=>String(k.게임번호)))];if(w.length!==1)continue;const O=$.find(({option:k})=>String(k.게임번호)===w[0]);M.push({key:`${O.game.round}|${w[0]}|inferred`,game:O.game,option:O.option,optionChoices:(O.game.options||[]).filter(k=>String(k?.게임번호||"")===w[0]),purchaseOdds:O.option.배당,stake:1e4,sourceText:w[0],needsConfirmation:!1}),G.add(w[0])}const ee=F.rects.length>=M.length?F.rects.slice(0,M.length):[],D=[];for(let v=0;v{const m=u.filter(b=>b.selected&&b.option);re(m,i).forEach(b=>ae(b)),f("전송 중"),Re(m,i).then(b=>f(b?"익명 통계 반영됨":"익명 통계 전송 실패")).catch(()=>f("익명 통계 전송 실패")),s?.(m.length),j([]),r("")},g=(m,b)=>j(N=>N.map(R=>R.key===m?{...R,...b}:R)),C=u.filter(m=>m.selected&&m.option);return e.jsxs("section",{className:"receipt-ocr",children:[e.jsxs("div",{className:"receipt-ocr-head",children:[e.jsxs("div",{children:[e.jsx("small",{children:"사진 자동 입력"}),e.jsx("h2",{children:"프로토 영수증 OCR"})]}),e.jsxs("label",{className:o?"is-disabled":"",children:["사진 선택·촬영",e.jsx("input",{type:"file",accept:"image/*",capture:"environment",disabled:o,onChange:m=>y(m.target.files?.[0])})]})]}),e.jsx("p",{children:"한 사진의 선택 경기를 하나의 티켓으로 묶습니다. 조합배당·공통 투입금·예상적중금을 확인한 뒤 저장하세요."}),o&&e.jsxs("div",{className:"receipt-ocr-progress",children:[e.jsx("i",{style:{width:`${l}%`}}),e.jsxs("span",{children:["문자 인식 ",l,"%"]})]}),x&&e.jsx("div",{className:"receipt-ocr-error",children:x}),!o&&c&&!u.length&&e.jsx("div",{className:"receipt-ocr-error",children:"일치하는 발매 선택지를 찾지 못했습니다. 사진의 게임번호와 선택 영역을 더 크게 촬영해 주세요."}),!!u.length&&e.jsxs("div",{className:"receipt-ocr-results",children:[u.map(m=>e.jsxs("label",{children:[e.jsx("input",{type:"checkbox",checked:m.selected,onChange:b=>g(m.key,{selected:b.target.checked})}),e.jsxs("span",{children:[e.jsxs("b",{children:[m.game.home," vs ",m.game.away]}),e.jsxs("small",{children:[m.option?.게임번호||m.sourceText,"번 · ",m.option?`${m.option.market} ${m.option.label||""} · ${m.option.선택}`:"번호 인식됨 · 선택 확인 필요"]})]}),m.needsConfirmation&&e.jsxs("select",{"aria-label":"선택 확인",value:m.option?m.optionChoices.indexOf(m.option):"",onChange:b=>{const N=b.target.value===""?null:m.optionChoices[Number(b.target.value)];g(m.key,{option:N,purchaseOdds:N?.배당||"",needsConfirmation:!N})},children:[e.jsx("option",{value:"",children:"픽 선택"}),m.optionChoices.map((b,N)=>e.jsxs("option",{value:N,children:[b.market," ",b.label||""," · ",b.선택]},`${b.market}-${b.선택}`))]}),e.jsx("input",{"aria-label":"구매 배당",type:"number",min:"1.01",step:"0.01",value:m.purchaseOdds,placeholder:"구매 배당",onChange:b=>g(m.key,{purchaseOdds:b.target.value})}),e.jsx("span",{className:"receipt-leg-odds",children:m.purchaseOdds?`개별 ${Number(m.purchaseOdds).toFixed(2)}배`:"배당 확인"})]},m.key)),e.jsxs("div",{className:"receipt-ticket-fields",children:[e.jsxs("label",{children:["조합배당",e.jsx("input",{"aria-label":"조합배당",type:"number",min:"1.01",step:"0.01",value:i.combinedOdds,onChange:m=>d({...i,combinedOdds:m.target.value})})]}),e.jsxs("label",{children:["총 투입금",e.jsx("input",{"aria-label":"총 투입금",type:"number",min:"100",step:"100",value:i.stake,onChange:m=>d({...i,stake:m.target.value})})]}),e.jsxs("label",{children:["예상적중금",e.jsx("input",{"aria-label":"예상적중금",type:"number",min:"0",step:"100",value:i.expectedPayout,onChange:m=>d({...i,expectedPayout:m.target.value})})]})]}),e.jsx("button",{type:"button",disabled:!C.length||u.some(m=>m.selected&&(!m.option||!Number(m.purchaseOdds))),onClick:E,children:C.length>1?`${C.length}폴더 조합으로 저장`:"단폴로 저장"})]}),e.jsx("small",{className:"receipt-ocr-privacy",children:"사진은 이 브라우저에서 문자 인식하며 서버에 저장하지 않습니다. 최초 실행 시 한국어 OCR 모델을 내려받습니다."}),e.jsxs("small",{className:"receipt-ocr-privacy",children:["저장 시 경기번호·픽·배당·금액구간·폴더 수만 익명 통계로 전송합니다. 이미지·구매번호·IP·쿠키는 통계에 저장하지 않습니다.",p?` · ${p}`:""]})]})}const Le="https://proto-odds-collector.fly.dev/api/live-scores",Ie="https://proto-odds-collector.fly.dev/api/picks",Be="https://proto-odds-collector.fly.dev/api/today-recommendations",L=t=>Number.isFinite(t)?`${(t*100).toFixed(1)}%`:"–",_=t=>`${Math.round(t).toLocaleString("ko-KR")}원`;function Fe(){const[t,s]=S.useState(null);return S.useEffect(()=>{let o=!1;const n=()=>fetch(`${Le}?${Date.now()}`,{cache:"no-store",headers:{"Cache-Control":"no-cache"}}).then(h=>h.ok?h.json():null).then(h=>{!o&&h&&s(h)}).catch(()=>{});n();const l=setInterval(n,15e3);return()=>{o=!0,clearInterval(l)}},[]),t}function De(t){const s=new Map;return(t?.games||[]).forEach(o=>{const n=[o.home,...o.home_alias||[]].filter(Boolean),l=[o.away,...o.away_alias||[]].filter(Boolean);n.forEach(h=>l.forEach(c=>s.set(`${h}|${c}|${o.md}`,o)))}),s}function Ae({bet:t,estimate:s}){const o=Number(t.openingProbability),n=Number(s.probability),l=Math.min(o,n)*100,h=Math.max(1.5,Math.abs(n-o)*100);return e.jsxs("div",{className:"probability-track","aria-label":`구매 당시 ${L(o)}, 현재 ${L(n)}`,children:[e.jsx("div",{className:"probability-track-line",children:e.jsx("i",{style:{left:`${l}%`,width:`${h}%`}})}),e.jsxs("div",{children:[e.jsx("span",{style:{left:`${o*100}%`},children:"구매"}),e.jsx("b",{style:{left:`${n*100}%`},children:"현재"})]})]})}function ze({bet:t,live:s,onRemove:o}){const n=U(t,s),l=Number.isFinite(n.probability)&&Number.isFinite(t.openingProbability)?n.probability-t.openingProbability:null,h=s?.finished,c=V(t,s),r=h?c==="hit"?t.stake*(t.purchaseOdds-1):c==="void"?0:c==="miss"?-t.stake:null:null,u=s?.cancelled?"취소":s?.postponed?"연기":h?"종료":s?.status==="STARTED"?"LIVE":"경기 전";return e.jsxs("article",{className:`dashboard-bet-card is-${u==="LIVE"?"live":h?"finished":"upcoming"}`,children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsxs("span",{children:[t.game.league," · ",t.game.date]}),e.jsxs("h2",{children:[t.game.home," vs ",t.game.away]})]}),e.jsx("strong",{children:u})]}),e.jsxs("div",{className:"dashboard-bet-pick",children:[e.jsxs("div",{children:[e.jsx("small",{children:"내 픽"}),e.jsxs("b",{children:[t.selection.market,t.selection.label?` ${t.selection.label}`:""," · ",t.selection.choice]})]}),e.jsxs("div",{children:[e.jsx("small",{children:"구매"}),e.jsxs("b",{children:[Number(t.purchaseOdds).toFixed(2),"배 · ",_(t.stake)]})]}),e.jsxs("div",{children:[e.jsx("small",{children:h?"확정 손익":"적중 시 순이익"}),e.jsx("b",{className:r<0?"text-sev3":"",children:h?r==null?"판정 확인":`${r>=0?"+":""}${_(r)}`:`+${_(t.stake*(t.purchaseOdds-1))}`})]})]}),s&&e.jsxs("div",{className:"dashboard-live-state",children:[e.jsxs("strong",{children:[s.home_score??"–"," : ",s.away_score??"–"]}),e.jsx("span",{children:s.status_text||u})]}),e.jsxs("div",{className:"dashboard-probability",children:[e.jsxs("div",{children:[e.jsx("small",{children:"구매 당시"}),e.jsx("b",{children:L(t.openingProbability)})]}),e.jsxs("div",{children:[e.jsx("small",{children:"현재 상황 추정"}),e.jsx("b",{children:L(n.probability)})]}),e.jsxs("div",{children:[e.jsx("small",{children:"변화"}),e.jsx("b",{className:l<0?"text-sev3":"",children:l==null?"–":`${l>=0?"+":""}${(l*100).toFixed(1)}%p`})]})]}),Number.isFinite(n.probability)&&e.jsx(Ae,{bet:t,estimate:n}),e.jsxs("footer",{children:[e.jsx("span",{children:n.basis==="score_time_estimate"?"점수·남은 시간 기반 상황 추정치":n.basis==="final_score"?"최종 점수 기준":n.basis==="live_score_missing"?"진행 시간 부족 · 구매 당시 확률 유지":"구매 당시 시장확률"}),e.jsx("button",{type:"button",onClick:()=>o(t.id),children:"기록 삭제"})]})]})}function Ue({group:t,index:s,onRemove:o}){const n=t.bets.map(i=>{const d=s.get(A(i.game));return{bet:i,live:d,estimate:U(i,d),outcome:V(i,d)}}),l=n.every(i=>Number.isFinite(i.estimate.probability))?n.reduce((i,d)=>i*d.estimate.probability,1):null,h=n.every(i=>Number.isFinite(i.bet.openingProbability))?n.reduce((i,d)=>i*d.bet.openingProbability,1):null,c=n.map(i=>i.outcome),r=c.includes("miss")?"miss":c.length&&c.every(i=>i==="hit"||i==="void")?"hit":null,u=n.some(i=>i.live?.status==="STARTED"&&!i.live?.finished),j=r==="hit"?Number(t.ticket.expectedPayout)-Number(t.ticket.stake):r==="miss"?-Number(t.ticket.stake):null;return e.jsxs("article",{className:`dashboard-ticket-card ${u?"is-live":r?"is-finished":""}`,children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsxs("small",{children:[n.length,"폴더 조합"]}),e.jsx("h2",{children:"조합 베팅 티켓"})]}),e.jsx("strong",{children:u?"LIVE":r==="hit"?"적중":r==="miss"?"실패":"진행 대기"})]}),e.jsxs("div",{className:"dashboard-ticket-summary",children:[e.jsxs("div",{children:[e.jsx("small",{children:"조합배당"}),e.jsxs("b",{children:[Number(t.ticket.combinedOdds).toFixed(2),"배"]})]}),e.jsxs("div",{children:[e.jsx("small",{children:"투입금"}),e.jsx("b",{children:_(t.ticket.stake)})]}),e.jsxs("div",{children:[e.jsx("small",{children:"예상적중금"}),e.jsx("b",{children:_(t.ticket.expectedPayout)})]}),e.jsxs("div",{children:[e.jsx("small",{children:r?"확정 손익":"현재 티켓 확률"}),e.jsx("b",{children:r?`${j>=0?"+":""}${_(j)}`:L(l)})]})]}),e.jsx("div",{className:"dashboard-ticket-legs",children:n.map(({bet:i,live:d,estimate:x,outcome:a},p)=>e.jsxs("div",{children:[e.jsx("span",{className:"ticket-leg-number",children:p+1}),e.jsxs("span",{children:[e.jsxs("b",{children:[i.game.home," vs ",i.game.away]}),e.jsxs("small",{children:[i.selection.market,i.selection.label?` ${i.selection.label}`:""," · ",i.selection.choice," · ",Number(i.purchaseOdds).toFixed(2),"배"]})]}),e.jsxs("span",{className:"ticket-leg-live",children:[e.jsx("b",{children:d?`${d.home_score??"–"}:${d.away_score??"–"}`:"경기 전"}),e.jsx("small",{children:d?.status_text||""})]}),e.jsxs("span",{className:"ticket-leg-probability",children:[e.jsx("b",{children:L(x.probability)}),e.jsx("small",{children:a==="hit"?"적중":a==="miss"?"실패":"현재 추정"})]})]},i.id))}),e.jsxs("footer",{children:[e.jsxs("span",{children:["구매 당시 ",L(h)," · 현재 확률은 개별 상황 추정치의 단순 곱"]}),e.jsx("button",{type:"button",onClick:()=>o(t.id),children:"티켓 삭제"})]})]})}function Z({decision:t}){const s=t.selection,o=`${s.home||""} vs ${s.away||""}`,n=`${s.market||""}${s.market_label?` ${s.market_label}`:""} · ${s.sel||""}`;return e.jsxs("article",{className:`dashboard-recommendation-card ${t.recommended?"is-recommended":"is-excluded"}`,children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsxs("small",{children:[s.league," · ",s.date]}),e.jsx("h3",{children:o})]}),e.jsx("strong",{children:t.recommended?"오늘의 추천":"추천 제외"})]}),e.jsxs("div",{className:"dashboard-recommendation-pick",children:[e.jsx("b",{children:n}),e.jsxs("span",{children:[Number(s.odds).toFixed(2),"배"]})]}),e.jsxs("p",{children:[e.jsx("b",{children:t.recommended?"추천한 이유":"추천하지 않은 이유"}),t.reason]}),t.display?.text&&e.jsx("small",{children:t.display.text}),e.jsxs("p",{className:"is-counter",children:[e.jsx("b",{children:t.recommended?"추천해도 주의할 점":"그래도 남기는 정보"}),t.counterReason]})]})}function Ve(){const[t,s]=S.useState(()=>B()),o=Fe(),{data:n}=me({picks:Ie,today:Be},6e4),l=n.picks,h=n.today,c=S.useMemo(()=>{const a=new Set;return[n.picks].flatMap(p=>p?.live||[]).filter(p=>{const f=`${p.round}|${p.home}|${p.away}|${p.date}`;return a.has(f)?!1:(a.add(f),!0)})},[n.picks]),r=S.useMemo(()=>De(o),[o]),u=S.useMemo(()=>ce(t),[t]),j=S.useMemo(()=>{const a=[...l?.live||[],...l?.past||[]],p=pe(h,a);return xe(p?.candidates||[])},[l,h]),i=j.filter(a=>a.recommended),d=j.filter(a=>!a.recommended);S.useEffect(()=>{const a=()=>s(B());return window.addEventListener("storage",a),window.addEventListener("proodd:bet-ledger",a),()=>{window.removeEventListener("storage",a),window.removeEventListener("proodd:bet-ledger",a)}},[]),S.useEffect(()=>{if(!o||!t.length)return;const a=t.map(p=>{const f=r.get(A(p.game));return le(p,U(p,f),f)});JSON.stringify(a)!==JSON.stringify(t)&&(de(a),s(a))},[o,r]);const x=u.reduce((a,p)=>{a.stake+=Number(p.ticket.stake)||0;const f=p.bets.map(E=>V(E,r.get(A(E.game)))),y=f.includes("miss")?"miss":f.length&&f.every(E=>E==="hit"||E==="void")?"hit":null;return y&&(a.settled+=1),y==="hit"&&(a.profit+=Number(p.ticket.expectedPayout)-Number(p.ticket.stake)),y==="miss"&&(a.profit-=Number(p.ticket.stake)),a},{stake:0,settled:0,profit:0});return e.jsxs("main",{className:"mx-auto max-w-[1180px] px-5 pb-20",children:[e.jsx(se,{current:"dashboard.html"}),e.jsxs("header",{className:"market-header",children:[e.jsxs("div",{children:[e.jsx("h1",{children:"내 베팅 대시보드"}),e.jsx("p",{children:"내가 구매한 단일 경기의 확률 변화와 실시간 진행, 확정 손익을 추적합니다."})]}),e.jsx("div",{className:"market-meta",children:"15초마다 실시간 점수 확인 · 이 브라우저에 저장"})]}),e.jsx(Pe,{games:c,onImported:()=>s(B())}),e.jsxs("section",{className:"dashboard-summary",children:[e.jsxs("div",{children:[e.jsx("small",{children:"저장한 티켓"}),e.jsxs("b",{children:[u.length,"장 · ",t.length,"픽"]})]}),e.jsxs("div",{children:[e.jsx("small",{children:"총 투입금"}),e.jsx("b",{children:_(x.stake)})]}),e.jsxs("div",{children:[e.jsx("small",{children:"정산 완료"}),e.jsxs("b",{children:[x.settled,"건"]})]}),e.jsxs("div",{children:[e.jsx("small",{children:"확정 손익"}),e.jsxs("b",{className:x.profit<0?"text-sev3":"",children:[x.profit>=0?"+":"",_(x.profit)]})]})]}),e.jsxs("div",{className:"dashboard-notice",children:["현재 확률은 구매 당시 시장확률을 실시간 점수와 남은 시간으로 이동시킨 ",e.jsx("b",{children:"상황 추정치"}),"입니다. 검증된 인플레이 베팅 모델이나 실시간 구매 추천이 아닙니다."]}),e.jsxs("section",{className:"dashboard-recommendations",children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsx("h2",{children:"오늘의 추천 판정"}),e.jsx("p",{children:"추천된 이유와 같은 후보가 제외된 이유를 동일한 기준으로 설명합니다."})]}),e.jsxs("strong",{children:[i.length,"개 추천 · ",d.length,"개 제외"]})]}),e.jsx("div",{className:"dashboard-recommendation-grid",children:i.map(a=>e.jsx(Z,{decision:a},`yes-${a.selection.event_key}-${a.selection.market}-${a.selection.sel||a.selection.선택}`))}),!!d.length&&e.jsxs("details",{className:"dashboard-excluded-list",children:[e.jsxs("summary",{children:["추천하지 않은 후보 ",d.length,"개와 이유 보기"]}),e.jsx("div",{className:"dashboard-recommendation-grid",children:d.map(a=>e.jsx(Z,{decision:a},`no-${a.selection.event_key}-${a.selection.market}-${a.selection.sel||a.selection.선택}`))})]}),!j.length&&e.jsxs("div",{className:"dashboard-empty",children:[e.jsx("b",{children:"현재 설명할 오늘 후보가 없습니다"}),e.jsx("p",{children:"새 배당과 판정 원장이 들어오면 추천·제외 이유가 함께 표시됩니다."})]})]}),e.jsxs("section",{className:"dashboard-bet-list",children:[u.map(a=>a.bets.length>1?e.jsx(Ue,{group:a,index:r,onRemove:p=>{he(p),s(B())}},a.id):e.jsx(ze,{bet:a.bets[0],live:r.get(A(a.bets[0].game)),onRemove:p=>{ue(p),s(B())}},a.id)),!t.length&&e.jsxs("div",{className:"dashboard-empty",children:[e.jsx("b",{children:"저장한 베팅이 없습니다"}),e.jsx("p",{children:"경기 분석에서 실제로 구매한 선택지의 ‘베팅 기록’ 버튼을 눌러 추가하세요."}),e.jsx("a",{href:"markets.html",children:"경기 분석으로 이동"})]})]})]})}ne();ie.createRoot(document.getElementById("root")).render(e.jsxs(e.Fragment,{children:[e.jsx(Ve,{}),e.jsx(oe,{})]})); +import{r as S,j as e,N as se,i as ne,c as ie,T as oe}from"./cache-guard-BQcATxSH.js";import{c as re,u as ae,r as B,g as ce,l as A,a as le,e as U,w as de,s as V,b as he,d as ue}from"./bet-ledger-ES6mNno4.js";import{u as me}from"./poll-QCTlueAT.js";import{a as pe,d as xe}from"./unified-recommendation-BCuqyY1U.js";import"./decision-view-model-Bfii5tWW.js";const fe="modulepreload",be=function(t,s){return new URL(t,s).href},K={},ge=function(s,o,n){let l=Promise.resolve();if(o&&o.length>0){let j=function(i){return Promise.all(i.map(d=>Promise.resolve(d).then(x=>({status:"fulfilled",value:x}),x=>({status:"rejected",reason:x}))))};const c=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),u=r?.nonce||r?.getAttribute("nonce");l=j(o.map(i=>{if(i=be(i,n),i in K)return;K[i]=!0;const d=i.endsWith(".css"),x=d?'[rel="stylesheet"]':"";if(n)for(let p=c.length-1;p>=0;p--){const f=c[p];if(f.href===i&&(!d||f.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${x}`))return;const a=document.createElement("link");if(a.rel=d?"stylesheet":fe,d||(a.as="script"),a.crossOrigin="",a.href=i,u&&a.setAttribute("nonce",u),document.head.appendChild(a),d)return new Promise((p,f)=>{a.addEventListener("load",p),a.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${i}`)))})}))}function h(c){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=c,window.dispatchEvent(r),!r.defaultPrevented)throw c}return l.then(c=>{for(const r of c||[])r.status==="rejected"&&h(r.reason);return s().catch(h)})},Q=t=>String(t||"").replace(/\s+/g,"").toLowerCase(),je=t=>{const s=Q(t),o=new Set([s]);return s==="홈"&&o.add("홈승"),s==="원정"&&["원정승","홈패"].forEach(n=>o.add(n)),s==="무"&&o.add("무승부"),s.includes("핸디홈")&&["핸디승","핸디캡홈"].forEach(n=>o.add(n)),s.includes("핸디원정")&&["핸디패","핸디캡원정"].forEach(n=>o.add(n)),[...o].filter(Boolean).sort((n,l)=>l.length-n.length)},Y=t=>[...String(t||"").matchAll(/\d[\d,]*(?:\.\d+)?/g)].map(s=>({raw:s[0],value:Number(s[0].replaceAll(",",""))})).filter(s=>Number.isFinite(s.value)),X=t=>String(t).split("").map(s=>`[${s}${s==="0"?"oO":s==="1"?"iIlL":""}]`).join("[^0-9a-zA-Z]{0,2}");function ye(t,s=[]){const o=String(t||"").split(/\r?\n/),n=new Set((s||[]).flatMap(h=>(h.options||[]).map(c=>String(c?.게임번호||"").trim())).filter(Boolean)),l=[];for(const h of o)if(!/(사업자|등록번호|\d{2}\s*-\s*\d{2,3}\s*-\s*\d{4,})/i.test(h))for(const c of n)l.includes(c)||new RegExp(`(^|[^0-9])${X(c)}(?=[^0-9]|$)`,"i").test(h)&&l.push(c);return l}function ve(t,s=[]){const o=String(t||"").split(/\r?\n/).map(r=>r.trim()).filter(Boolean),n=(s||[]).flatMap(r=>(r.options||[]).map(u=>({game:r,option:u}))),l=[],h=new Set,c=new Set;for(let r=0;rString(d.option?.게임번호||"").trim()))].filter(d=>d&&!c.has(d)&&new RegExp(`(^|[^0-9])${X(d)}(?=[^0-9]|$)`,"i").test(o[r]));for(const d of i){const x=n.filter(g=>String(g.option?.게임번호||"").trim()===d),a=Y(u).filter(g=>g.raw.replaceAll(",","")!==d&&g.value>1&&g.value<100),p=x.map(g=>{const C=Number(g.option?.배당),m=a.sort((N,R)=>Math.abs(N.value-C)-Math.abs(R.value-C))[0],b=je(g.option?.선택).some(N=>j.includes(N));return{candidate:g,oddsToken:m,distance:m?Math.abs(m.value-C):1/0,hasChoice:b}}).filter(g=>g.oddsToken&&g.distance<=.03).sort((g,C)=>g.distance-C.distance||Number(C.hasChoice)-Number(g.hasChoice))[0];if(!p)continue;const f=p.candidate,y=`${f.game.round}|${d}|${f.option.market}|${f.option.label||""}|${f.option.선택}`;if(h.has(y))continue;h.add(y),c.add(d);const E=Y(u).filter(g=>g.value>=100&&g.value!==Number(d)).sort((g,C)=>C.value-g.value)[0];l.push({key:y,game:f.game,option:f.option,purchaseOdds:p.oddsToken.value,stake:E?.value||1e4,sourceText:u})}}return l}function H(t,s=[]){const o=ve(t,s),n=new Map(o.map(l=>[String(l.option?.게임번호),l]));for(const l of ye(t,s)){if(n.has(l))continue;const h=(s||[]).find(r=>(r.options||[]).some(u=>String(u?.게임번호||"")===l));if(!h)continue;const c=(h.options||[]).filter(r=>String(r?.게임번호||"")===l);n.set(l,{key:`${h.round}|${l}|unresolved`,game:h,option:null,optionChoices:c,purchaseOdds:"",stake:1e4,sourceText:l,needsConfirmation:!0})}return[...n.values()]}function ke(t,s,o=[]){const n=H(t,o),l=H(s,o),h=new Map(n.map(i=>[String(i.option?.게임번호||i.sourceText),i])),c=[],r=new Set;for(const i of l){const d=String(i.option?.게임번호||i.sourceText);r.has(d)||(c.push(h.get(d)||i),r.add(d))}for(const i of n){const d=String(i.option?.게임번호||i.sourceText);r.has(d)||(c.push(i),r.add(d))}const u=[];for(const i of o||[])for(const d of i.options||[]){const x=String(d?.게임번호||"").trim();if(!x||r.has(x))continue;const a=String(t||"").search(new RegExp(`(^|[^0-9])${x}(?=[^0-9]|$)`));if(a<0)continue;const p=(i.options||[]).filter(f=>String(f?.게임번호||"")===x);u.push({index:a,row:{key:`${i.round}|${x}|unresolved`,game:i,option:null,optionChoices:p,purchaseOdds:"",stake:1e4,sourceText:x,needsConfirmation:!0}}),r.add(x)}u.sort((i,d)=>i.index-d.index).forEach(({row:i})=>c.push(i));const j=[];for(const[i,d]of String(t||"").split(/\r?\n/).entries()){const x=(o||[]).flatMap(y=>(y.options||[]).map(E=>({game:y,option:E}))).filter(({option:y})=>{const E=String(y?.게임번호||""),g=Number(y?.line),C=Number(y?.배당);return E&&!r.has(E)&&Number.isFinite(g)&&Number.isFinite(C)&&d.includes(String(Math.abs(g)))&&new RegExp(`(^|[^0-9])${C.toFixed(2).replace(".","\\.")}(?=[^0-9]|$)`).test(d)}),a=[...new Set(x.map(({option:y})=>String(y.게임번호)))];if(a.length!==1)continue;const p=x.find(({option:y})=>String(y.게임번호)===a[0]),f=(p.game.options||[]).filter(y=>String(y?.게임번호||"")===a[0]);j.push({index:i,row:{key:`${p.game.round}|${a[0]}|${p.option.market}|${p.option.label||""}|${p.option.선택}`,game:p.game,option:p.option,optionChoices:f,purchaseOdds:p.option.배당,stake:1e4,sourceText:a[0],needsConfirmation:!1}}),r.add(a[0])}return j.sort((i,d)=>i.index-d.index).forEach(({row:i})=>c.push(i)),c}const z=(t,s)=>{const n=String(t||"").replace(/\s+/g," ").match(new RegExp(`(?:${s.join("|")})[^0-9]{0,16}([0-9][0-9,.]*)`,"i"));return n?Number(n[1].replaceAll(",","")):null};function Ne(t,s=[]){const o=s.reduce((c,r)=>c*Number(r.purchaseOdds||1),1),n=z(t,["개별투표금액","총투표금액","투표금액","구매금액"])||s.find(c=>Number(c.stake)>=100)?.stake||1e4,l=z(t,["예상배당률","조합배당","배당률"])||Number(o.toFixed(2)),h=z(t,["예상적중금액","예상환급금","적중금액"])||Math.round(n*l);return{stake:n,combinedOdds:l,expectedPayout:h,legCount:s.length}}const $e=(t,s)=>{const o=t[s],n=t[s+1],l=t[s+2];return l>135&&l-o>22&&l-n>4};function Se(t,s,o){const n=[],l=s*.07;for(let c=0;c=l&&u-r>=l&&u-r<=s*.62&&n.push({y:c,left:r,right:u})}const h=[];for(let c=0;ci.y-r.y>=o*.018&&i.y-r.y<=o*.09&&Math.abs(i.left-r.left)Math.abs(i.top-j.top)c.top-r.top).reduce((c,r)=>{const u=c.at(-1);return u&&Math.abs(u.left-r.left){const c=String(h||"");return/오버/.test(c)?["오버","over"]:/언더/.test(c)?["언더","under"]:/무/.test(c)?["무","draw"]:/원정|패/.test(c)?["패","원정","away"]:/홈|승/.test(c)?["승","홈","home"]:[c]},l=s.map((h,c)=>({index:c,hit:n(h?.선택).some(r=>r&&o.includes(r.toLowerCase()))})).filter(h=>h.hit);return l.length===1?l[0].index:null}const Me="https://proto-odds-collector.fly.dev/anonymous-bets";function Oe(t){const s=Math.max(0,Number(t)||0);return s<5e3?"under_5000":s<1e4?"5000_9999":s<5e4?"10000_49999":s<1e5?"50000_99999":"100000_plus"}function _e(t,s={}){const o=(t||[]).filter(n=>n?.option).map(n=>({game_no:String(n.option.게임번호||""),sport:String(n.game?.sport||"").slice(0,12),league:String(n.game?.league||"").slice(0,40),market:String(n.option.market||"").slice(0,30),label:String(n.option.label||"").slice(0,30),choice:String(n.option.선택||"").slice(0,30),purchase_odds:Number(n.purchaseOdds||n.option.배당)}));return{schema_version:1,source:"receipt_ocr",round:Number(t?.[0]?.game?.round)||null,combo_size:o.length,combined_odds:Number(s.combinedOdds)||null,stake_band:Oe(s.stake),legs:o}}async function Re(t,s,o=globalThis.fetch){const n=_e(t,s);return!n.legs.length||!o?!1:(await o(Me,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})).ok}function Pe({games:t=[],onImported:s}){const[o,n]=S.useState(!1),[l,h]=S.useState(0),[c,r]=S.useState(""),[u,j]=S.useState([]),[i,d]=S.useState({stake:1e4,combinedOdds:0,expectedPayout:0}),[x,a]=S.useState(""),[p,f]=S.useState(""),y=async m=>{if(!m)return;n(!0),h(0),a(""),j([]);let b;try{const{createWorker:N}=await ge(async()=>{const{createWorker:v}=await import("./index-DkgChCA8.js").then(T=>T.i);return{createWorker:v}},[],import.meta.url);b=await N("kor+eng",1,{logger:v=>{v.status==="recognizing text"&&h(Math.round((v.progress||0)*100))}});const[R,F]=await Promise.all([b.recognize(m,{rotateAuto:!0}),Ce(m).catch(()=>({rects:[],width:0}))]),I=String(R?.data?.text||""),q=I;r(q);let M=ke(I,"",t);M.length||(M=(t||[]).flatMap(v=>[...new Set((v.options||[]).map($=>String($?.게임번호||"")).filter(Boolean))].filter($=>new RegExp(`(^|[^0-9])${$}(?=[^0-9]|$)`).test(I)).map($=>({key:`${v.round}|${$}|unresolved`,game:v,option:null,optionChoices:(v.options||[]).filter(w=>String(w?.게임번호||"")===$),purchaseOdds:"",stake:1e4,sourceText:$,needsConfirmation:!0,textIndex:I.indexOf($)}))).sort((v,T)=>v.textIndex-T.textIndex));const G=new Set(M.map(v=>String(v.option?.게임번호||v.sourceText)));for(const v of I.split(/\r?\n/)){const T=[...v.matchAll(/\d+(?:\.\d+)?/g)].map(k=>Number(k[0])),$=(t||[]).flatMap(k=>(k.options||[]).map(P=>({game:k,option:P}))).filter(({option:k})=>{const P=String(k?.게임번호||""),W=Number(k?.line),J=Number(k?.배당);return P&&!G.has(P)&&Number.isFinite(W)&&Number.isFinite(J)&&T.some(te=>Math.abs(te-Math.abs(W))<=2)&&v.includes(J.toFixed(2))}),w=[...new Set($.map(({option:k})=>String(k.게임번호)))];if(w.length!==1)continue;const O=$.find(({option:k})=>String(k.게임번호)===w[0]);M.push({key:`${O.game.round}|${w[0]}|inferred`,game:O.game,option:O.option,optionChoices:(O.game.options||[]).filter(k=>String(k?.게임번호||"")===w[0]),purchaseOdds:O.option.배당,stake:1e4,sourceText:w[0],needsConfirmation:!1}),G.add(w[0])}const ee=F.rects.length>=M.length?F.rects.slice(0,M.length):[],D=[];for(let v=0;v{const m=u.filter(b=>b.selected&&b.option);re(m,i).forEach(b=>ae(b)),f("전송 중"),Re(m,i).then(b=>f(b?"익명 통계 반영됨":"익명 통계 전송 실패")).catch(()=>f("익명 통계 전송 실패")),s?.(m.length),j([]),r("")},g=(m,b)=>j(N=>N.map(R=>R.key===m?{...R,...b}:R)),C=u.filter(m=>m.selected&&m.option);return e.jsxs("section",{className:"receipt-ocr",children:[e.jsxs("div",{className:"receipt-ocr-head",children:[e.jsxs("div",{children:[e.jsx("small",{children:"사진 자동 입력"}),e.jsx("h2",{children:"프로토 영수증 OCR"})]}),e.jsxs("label",{className:o?"is-disabled":"",children:["사진 선택·촬영",e.jsx("input",{type:"file",accept:"image/*",capture:"environment",disabled:o,onChange:m=>y(m.target.files?.[0])})]})]}),e.jsx("p",{children:"한 사진의 선택 경기를 하나의 티켓으로 묶습니다. 조합배당·공통 투입금·예상적중금을 확인한 뒤 저장하세요."}),o&&e.jsxs("div",{className:"receipt-ocr-progress",children:[e.jsx("i",{style:{width:`${l}%`}}),e.jsxs("span",{children:["문자 인식 ",l,"%"]})]}),x&&e.jsx("div",{className:"receipt-ocr-error",children:x}),!o&&c&&!u.length&&e.jsx("div",{className:"receipt-ocr-error",children:"일치하는 발매 선택지를 찾지 못했습니다. 사진의 게임번호와 선택 영역을 더 크게 촬영해 주세요."}),!!u.length&&e.jsxs("div",{className:"receipt-ocr-results",children:[u.map(m=>e.jsxs("label",{children:[e.jsx("input",{type:"checkbox",checked:m.selected,onChange:b=>g(m.key,{selected:b.target.checked})}),e.jsxs("span",{children:[e.jsxs("b",{children:[m.game.home," vs ",m.game.away]}),e.jsxs("small",{children:[m.option?.게임번호||m.sourceText,"번 · ",m.option?`${m.option.market} ${m.option.label||""} · ${m.option.선택}`:"번호 인식됨 · 선택 확인 필요"]})]}),m.needsConfirmation&&e.jsxs("select",{"aria-label":"선택 확인",value:m.option?m.optionChoices.indexOf(m.option):"",onChange:b=>{const N=b.target.value===""?null:m.optionChoices[Number(b.target.value)];g(m.key,{option:N,purchaseOdds:N?.배당||"",needsConfirmation:!N})},children:[e.jsx("option",{value:"",children:"픽 선택"}),m.optionChoices.map((b,N)=>e.jsxs("option",{value:N,children:[b.market," ",b.label||""," · ",b.선택]},`${b.market}-${b.선택}`))]}),e.jsx("input",{"aria-label":"구매 배당",type:"number",min:"1.01",step:"0.01",value:m.purchaseOdds,placeholder:"구매 배당",onChange:b=>g(m.key,{purchaseOdds:b.target.value})}),e.jsx("span",{className:"receipt-leg-odds",children:m.purchaseOdds?`개별 ${Number(m.purchaseOdds).toFixed(2)}배`:"배당 확인"})]},m.key)),e.jsxs("div",{className:"receipt-ticket-fields",children:[e.jsxs("label",{children:["조합배당",e.jsx("input",{"aria-label":"조합배당",type:"number",min:"1.01",step:"0.01",value:i.combinedOdds,onChange:m=>d({...i,combinedOdds:m.target.value})})]}),e.jsxs("label",{children:["총 투입금",e.jsx("input",{"aria-label":"총 투입금",type:"number",min:"100",step:"100",value:i.stake,onChange:m=>d({...i,stake:m.target.value})})]}),e.jsxs("label",{children:["예상적중금",e.jsx("input",{"aria-label":"예상적중금",type:"number",min:"0",step:"100",value:i.expectedPayout,onChange:m=>d({...i,expectedPayout:m.target.value})})]})]}),e.jsx("button",{type:"button",disabled:!C.length||u.some(m=>m.selected&&(!m.option||!Number(m.purchaseOdds))),onClick:E,children:C.length>1?`${C.length}폴더 조합으로 저장`:"단폴로 저장"})]}),e.jsx("small",{className:"receipt-ocr-privacy",children:"사진은 이 브라우저에서 문자 인식하며 서버에 저장하지 않습니다. 최초 실행 시 한국어 OCR 모델을 내려받습니다."}),e.jsxs("small",{className:"receipt-ocr-privacy",children:["저장 시 경기번호·픽·배당·금액구간·폴더 수만 익명 통계로 전송합니다. 이미지·구매번호·IP·쿠키는 통계에 저장하지 않습니다.",p?` · ${p}`:""]})]})}const Le="https://proto-odds-collector.fly.dev/api/live-scores",Ie="https://proto-odds-collector.fly.dev/api/picks",Be="https://proto-odds-collector.fly.dev/api/today-recommendations",L=t=>Number.isFinite(t)?`${(t*100).toFixed(1)}%`:"–",_=t=>`${Math.round(t).toLocaleString("ko-KR")}원`;function Fe(){const[t,s]=S.useState(null);return S.useEffect(()=>{let o=!1;const n=()=>fetch(`${Le}?${Date.now()}`,{cache:"no-store",headers:{"Cache-Control":"no-cache"}}).then(h=>h.ok?h.json():null).then(h=>{!o&&h&&s(h)}).catch(()=>{});n();const l=setInterval(n,15e3);return()=>{o=!0,clearInterval(l)}},[]),t}function De(t){const s=new Map;return(t?.games||[]).forEach(o=>{const n=[o.home,...o.home_alias||[]].filter(Boolean),l=[o.away,...o.away_alias||[]].filter(Boolean);n.forEach(h=>l.forEach(c=>s.set(`${h}|${c}|${o.md}`,o)))}),s}function Ae({bet:t,estimate:s}){const o=Number(t.openingProbability),n=Number(s.probability),l=Math.min(o,n)*100,h=Math.max(1.5,Math.abs(n-o)*100);return e.jsxs("div",{className:"probability-track","aria-label":`구매 당시 ${L(o)}, 현재 ${L(n)}`,children:[e.jsx("div",{className:"probability-track-line",children:e.jsx("i",{style:{left:`${l}%`,width:`${h}%`}})}),e.jsxs("div",{children:[e.jsx("span",{style:{left:`${o*100}%`},children:"구매"}),e.jsx("b",{style:{left:`${n*100}%`},children:"현재"})]})]})}function ze({bet:t,live:s,onRemove:o}){const n=U(t,s),l=Number.isFinite(n.probability)&&Number.isFinite(t.openingProbability)?n.probability-t.openingProbability:null,h=s?.finished,c=V(t,s),r=h?c==="hit"?t.stake*(t.purchaseOdds-1):c==="void"?0:c==="miss"?-t.stake:null:null,u=s?.cancelled?"취소":s?.postponed?"연기":h?"종료":s?.status==="STARTED"?"LIVE":"경기 전";return e.jsxs("article",{className:`dashboard-bet-card is-${u==="LIVE"?"live":h?"finished":"upcoming"}`,children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsxs("span",{children:[t.game.league," · ",t.game.date]}),e.jsxs("h2",{children:[t.game.home," vs ",t.game.away]})]}),e.jsx("strong",{children:u})]}),e.jsxs("div",{className:"dashboard-bet-pick",children:[e.jsxs("div",{children:[e.jsx("small",{children:"내 픽"}),e.jsxs("b",{children:[t.selection.market,t.selection.label?` ${t.selection.label}`:""," · ",t.selection.choice]})]}),e.jsxs("div",{children:[e.jsx("small",{children:"구매"}),e.jsxs("b",{children:[Number(t.purchaseOdds).toFixed(2),"배 · ",_(t.stake)]})]}),e.jsxs("div",{children:[e.jsx("small",{children:h?"확정 손익":"적중 시 순이익"}),e.jsx("b",{className:r<0?"text-sev3":"",children:h?r==null?"판정 확인":`${r>=0?"+":""}${_(r)}`:`+${_(t.stake*(t.purchaseOdds-1))}`})]})]}),s&&e.jsxs("div",{className:"dashboard-live-state",children:[e.jsxs("strong",{children:[s.home_score??"–"," : ",s.away_score??"–"]}),e.jsx("span",{children:s.status_text||u})]}),e.jsxs("div",{className:"dashboard-probability",children:[e.jsxs("div",{children:[e.jsx("small",{children:"구매 당시"}),e.jsx("b",{children:L(t.openingProbability)})]}),e.jsxs("div",{children:[e.jsx("small",{children:"현재 상황 추정"}),e.jsx("b",{children:L(n.probability)})]}),e.jsxs("div",{children:[e.jsx("small",{children:"변화"}),e.jsx("b",{className:l<0?"text-sev3":"",children:l==null?"–":`${l>=0?"+":""}${(l*100).toFixed(1)}%p`})]})]}),Number.isFinite(n.probability)&&e.jsx(Ae,{bet:t,estimate:n}),e.jsxs("footer",{children:[e.jsx("span",{children:n.basis==="score_time_estimate"?"점수·남은 시간 기반 상황 추정치":n.basis==="final_score"?"최종 점수 기준":n.basis==="live_score_missing"?"진행 시간 부족 · 구매 당시 확률 유지":"구매 당시 시장확률"}),e.jsx("button",{type:"button",onClick:()=>o(t.id),children:"기록 삭제"})]})]})}function Ue({group:t,index:s,onRemove:o}){const n=t.bets.map(i=>{const d=s.get(A(i.game));return{bet:i,live:d,estimate:U(i,d),outcome:V(i,d)}}),l=n.every(i=>Number.isFinite(i.estimate.probability))?n.reduce((i,d)=>i*d.estimate.probability,1):null,h=n.every(i=>Number.isFinite(i.bet.openingProbability))?n.reduce((i,d)=>i*d.bet.openingProbability,1):null,c=n.map(i=>i.outcome),r=c.includes("miss")?"miss":c.length&&c.every(i=>i==="hit"||i==="void")?"hit":null,u=n.some(i=>i.live?.status==="STARTED"&&!i.live?.finished),j=r==="hit"?Number(t.ticket.expectedPayout)-Number(t.ticket.stake):r==="miss"?-Number(t.ticket.stake):null;return e.jsxs("article",{className:`dashboard-ticket-card ${u?"is-live":r?"is-finished":""}`,children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsxs("small",{children:[n.length,"폴더 조합"]}),e.jsx("h2",{children:"조합 베팅 티켓"})]}),e.jsx("strong",{children:u?"LIVE":r==="hit"?"적중":r==="miss"?"실패":"진행 대기"})]}),e.jsxs("div",{className:"dashboard-ticket-summary",children:[e.jsxs("div",{children:[e.jsx("small",{children:"조합배당"}),e.jsxs("b",{children:[Number(t.ticket.combinedOdds).toFixed(2),"배"]})]}),e.jsxs("div",{children:[e.jsx("small",{children:"투입금"}),e.jsx("b",{children:_(t.ticket.stake)})]}),e.jsxs("div",{children:[e.jsx("small",{children:"예상적중금"}),e.jsx("b",{children:_(t.ticket.expectedPayout)})]}),e.jsxs("div",{children:[e.jsx("small",{children:r?"확정 손익":"현재 티켓 확률"}),e.jsx("b",{children:r?`${j>=0?"+":""}${_(j)}`:L(l)})]})]}),e.jsx("div",{className:"dashboard-ticket-legs",children:n.map(({bet:i,live:d,estimate:x,outcome:a},p)=>e.jsxs("div",{children:[e.jsx("span",{className:"ticket-leg-number",children:p+1}),e.jsxs("span",{children:[e.jsxs("b",{children:[i.game.home," vs ",i.game.away]}),e.jsxs("small",{children:[i.selection.market,i.selection.label?` ${i.selection.label}`:""," · ",i.selection.choice," · ",Number(i.purchaseOdds).toFixed(2),"배"]})]}),e.jsxs("span",{className:"ticket-leg-live",children:[e.jsx("b",{children:d?`${d.home_score??"–"}:${d.away_score??"–"}`:"경기 전"}),e.jsx("small",{children:d?.status_text||""})]}),e.jsxs("span",{className:"ticket-leg-probability",children:[e.jsx("b",{children:L(x.probability)}),e.jsx("small",{children:a==="hit"?"적중":a==="miss"?"실패":"현재 추정"})]})]},i.id))}),e.jsxs("footer",{children:[e.jsxs("span",{children:["구매 당시 ",L(h)," · 현재 확률은 개별 상황 추정치의 단순 곱"]}),e.jsx("button",{type:"button",onClick:()=>o(t.id),children:"티켓 삭제"})]})]})}function Z({decision:t}){const s=t.selection,o=`${s.home||""} vs ${s.away||""}`,n=`${s.market||""}${s.market_label?` ${s.market_label}`:""} · ${s.sel||""}`;return e.jsxs("article",{className:`dashboard-recommendation-card ${t.recommended?"is-recommended":"is-excluded"}`,children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsxs("small",{children:[s.league," · ",s.date]}),e.jsx("h3",{children:o})]}),e.jsx("strong",{children:t.recommended?"오늘의 추천":"추천 제외"})]}),e.jsxs("div",{className:"dashboard-recommendation-pick",children:[e.jsx("b",{children:n}),e.jsxs("span",{children:[Number(s.odds).toFixed(2),"배"]})]}),e.jsxs("p",{children:[e.jsx("b",{children:t.recommended?"추천한 이유":"추천하지 않은 이유"}),t.reason]}),t.display?.text&&e.jsx("small",{children:t.display.text}),e.jsxs("p",{className:"is-counter",children:[e.jsx("b",{children:t.recommended?"추천해도 주의할 점":"그래도 남기는 정보"}),t.counterReason]})]})}function Ve(){const[t,s]=S.useState(()=>B()),o=Fe(),{data:n}=me({picks:Ie,today:Be},6e4),l=n.picks,h=n.today,c=S.useMemo(()=>{const a=new Set;return[n.picks].flatMap(p=>p?.live||[]).filter(p=>{const f=`${p.round}|${p.home}|${p.away}|${p.date}`;return a.has(f)?!1:(a.add(f),!0)})},[n.picks]),r=S.useMemo(()=>De(o),[o]),u=S.useMemo(()=>ce(t),[t]),j=S.useMemo(()=>{const a=[...l?.live||[],...l?.past||[]],p=pe(h,a);return xe(p?.candidates||[])},[l,h]),i=j.filter(a=>a.recommended),d=j.filter(a=>!a.recommended);S.useEffect(()=>{const a=()=>s(B());return window.addEventListener("storage",a),window.addEventListener("proodd:bet-ledger",a),()=>{window.removeEventListener("storage",a),window.removeEventListener("proodd:bet-ledger",a)}},[]),S.useEffect(()=>{if(!o||!t.length)return;const a=t.map(p=>{const f=r.get(A(p.game));return le(p,U(p,f),f)});JSON.stringify(a)!==JSON.stringify(t)&&(de(a),s(a))},[o,r]);const x=u.reduce((a,p)=>{a.stake+=Number(p.ticket.stake)||0;const f=p.bets.map(E=>V(E,r.get(A(E.game)))),y=f.includes("miss")?"miss":f.length&&f.every(E=>E==="hit"||E==="void")?"hit":null;return y&&(a.settled+=1),y==="hit"&&(a.profit+=Number(p.ticket.expectedPayout)-Number(p.ticket.stake)),y==="miss"&&(a.profit-=Number(p.ticket.stake)),a},{stake:0,settled:0,profit:0});return e.jsxs("main",{className:"mx-auto max-w-[1180px] px-5 pb-20",children:[e.jsx(se,{current:"dashboard.html"}),e.jsxs("header",{className:"market-header",children:[e.jsxs("div",{children:[e.jsx("h1",{children:"내 베팅 대시보드"}),e.jsx("p",{children:"내가 구매한 단일 경기의 확률 변화와 실시간 진행, 확정 손익을 추적합니다."})]}),e.jsx("div",{className:"market-meta",children:"15초마다 실시간 점수 확인 · 이 브라우저에 저장"})]}),e.jsx(Pe,{games:c,onImported:()=>s(B())}),e.jsxs("section",{className:"dashboard-summary",children:[e.jsxs("div",{children:[e.jsx("small",{children:"저장한 티켓"}),e.jsxs("b",{children:[u.length,"장 · ",t.length,"픽"]})]}),e.jsxs("div",{children:[e.jsx("small",{children:"총 투입금"}),e.jsx("b",{children:_(x.stake)})]}),e.jsxs("div",{children:[e.jsx("small",{children:"정산 완료"}),e.jsxs("b",{children:[x.settled,"건"]})]}),e.jsxs("div",{children:[e.jsx("small",{children:"확정 손익"}),e.jsxs("b",{className:x.profit<0?"text-sev3":"",children:[x.profit>=0?"+":"",_(x.profit)]})]})]}),e.jsxs("div",{className:"dashboard-notice",children:["현재 확률은 구매 당시 시장확률을 실시간 점수와 남은 시간으로 이동시킨 ",e.jsx("b",{children:"상황 추정치"}),"입니다. 검증된 인플레이 베팅 모델이나 실시간 구매 추천이 아닙니다."]}),e.jsxs("section",{className:"dashboard-recommendations",children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsx("h2",{children:"오늘의 추천 판정"}),e.jsx("p",{children:"추천된 이유와 같은 후보가 제외된 이유를 동일한 기준으로 설명합니다."})]}),e.jsxs("strong",{children:[i.length,"개 추천 · ",d.length,"개 제외"]})]}),e.jsx("div",{className:"dashboard-recommendation-grid",children:i.map(a=>e.jsx(Z,{decision:a},`yes-${a.selection.event_key}-${a.selection.market}-${a.selection.sel||a.selection.선택}`))}),!!d.length&&e.jsxs("details",{className:"dashboard-excluded-list",children:[e.jsxs("summary",{children:["추천하지 않은 후보 ",d.length,"개와 이유 보기"]}),e.jsx("div",{className:"dashboard-recommendation-grid",children:d.map(a=>e.jsx(Z,{decision:a},`no-${a.selection.event_key}-${a.selection.market}-${a.selection.sel||a.selection.선택}`))})]}),!j.length&&e.jsxs("div",{className:"dashboard-empty",children:[e.jsx("b",{children:"현재 설명할 오늘 후보가 없습니다"}),e.jsx("p",{children:"새 배당과 판정 원장이 들어오면 추천·제외 이유가 함께 표시됩니다."})]})]}),e.jsxs("section",{className:"dashboard-bet-list",children:[u.map(a=>a.bets.length>1?e.jsx(Ue,{group:a,index:r,onRemove:p=>{he(p),s(B())}},a.id):e.jsx(ze,{bet:a.bets[0],live:r.get(A(a.bets[0].game)),onRemove:p=>{ue(p),s(B())}},a.id)),!t.length&&e.jsxs("div",{className:"dashboard-empty",children:[e.jsx("b",{children:"저장한 베팅이 없습니다"}),e.jsx("p",{children:"경기 분석에서 실제로 구매한 선택지의 ‘베팅 기록’ 버튼을 눌러 추가하세요."}),e.jsx("a",{href:"markets.html",children:"경기 분석으로 이동"})]})]})]})}ne();ie.createRoot(document.getElementById("root")).render(e.jsxs(e.Fragment,{children:[e.jsx(Ve,{}),e.jsx(oe,{})]})); diff --git a/docs/assets/index-BVDvkSYt.js b/docs/assets/index-BVDvkSYt.js deleted file mode 100644 index 0ba32d1b..00000000 --- a/docs/assets/index-BVDvkSYt.js +++ /dev/null @@ -1 +0,0 @@ -import{i as r,c as o,j as t,r as e}from"./cache-guard-cBqh6aGv.js";import{M as i}from"./Markets--wDDhPBF.js";import"./decision-view-model-Bfii5tWW.js";import"./unified-recommendation-BCuqyY1U.js";import"./bet-ledger-ES6mNno4.js";import"./AiDisclosure-BFwnE-uI.js";import"./live-odds-CIQqKAce.js";import"./poll-DDJ49qcP.js";r();o.createRoot(document.getElementById("root")).render(t.jsx(e.StrictMode,{children:t.jsx(i,{})})); diff --git a/docs/assets/index-CEFn64q7.js b/docs/assets/index-CEFn64q7.js new file mode 100644 index 00000000..2aef9c8e --- /dev/null +++ b/docs/assets/index-CEFn64q7.js @@ -0,0 +1 @@ +import{i as r,c as o,j as t,r as e}from"./cache-guard-BQcATxSH.js";import{M as i}from"./Markets-BuleyXai.js";import"./decision-view-model-Bfii5tWW.js";import"./unified-recommendation-BCuqyY1U.js";import"./bet-ledger-ES6mNno4.js";import"./AiDisclosure-BIoGQ_Ky.js";import"./live-odds-CIQqKAce.js";import"./poll-QCTlueAT.js";r();o.createRoot(document.getElementById("root")).render(t.jsx(e.StrictMode,{children:t.jsx(i,{})})); diff --git a/docs/assets/lotto-BE56CrLU.js b/docs/assets/lotto-B-TzHTxd.js similarity index 98% rename from docs/assets/lotto-BE56CrLU.js rename to docs/assets/lotto-B-TzHTxd.js index 76b97de5..e6808b1e 100644 --- a/docs/assets/lotto-BE56CrLU.js +++ b/docs/assets/lotto-B-TzHTxd.js @@ -1,2 +1,2 @@ -import{r as N,j as e,C as d,S as c,N as T,T as R,i as L,c as K}from"./cache-guard-cBqh6aGv.js";import{u as M}from"./poll-DDJ49qcP.js";const j=(s,r=5)=>s==null?"—":`${Number(s)>=0?"+":""}${Number(s).toFixed(r)}`,z=s=>s==null?"—":Number(s).toFixed(3);function B(){const{data:s,at:r}=M({lotto:"data/lotto_latest.json"},3e5),t=s.lotto,[m,y]=N.useState(10),[w,f]=N.useState(!1);if(r&&!t)return e.jsx(b,{children:e.jsx("p",{className:"py-8 text-sev3",children:"로또 검증 데이터를 불러오지 못했습니다."})});if(!t)return e.jsx(b,{children:e.jsx("p",{className:"py-8 text-ink3",children:"검증 결과를 불러오는 중…"})});const l=t.portfolio,n=t.audit,h=t.backtest,u=l.combinations?.length||0,x=Math.max(1,Math.min(m,u)),v=(l.combinations||[]).slice(0,x),S=[...new Set([1,3,5,10,20,30,50,100,u])].filter(a=>a>0&&a<=u).sort((a,i)=>a-i),$=l.model_status==="validated_weighting"?"검증 가중치 사용":"균등 복귀",C=async()=>{const a=v.map((i,F)=>`${String.fromCharCode(65+F)}. ${i.numbers.join(" ")}`).join(` +import{r as N,j as e,C as d,S as c,N as T,T as R,i as L,c as K}from"./cache-guard-BQcATxSH.js";import{u as M}from"./poll-QCTlueAT.js";const j=(s,r=5)=>s==null?"—":`${Number(s)>=0?"+":""}${Number(s).toFixed(r)}`,z=s=>s==null?"—":Number(s).toFixed(3);function B(){const{data:s,at:r}=M({lotto:"data/lotto_latest.json"},3e5),t=s.lotto,[m,y]=N.useState(10),[w,f]=N.useState(!1);if(r&&!t)return e.jsx(b,{children:e.jsx("p",{className:"py-8 text-sev3",children:"로또 검증 데이터를 불러오지 못했습니다."})});if(!t)return e.jsx(b,{children:e.jsx("p",{className:"py-8 text-ink3",children:"검증 결과를 불러오는 중…"})});const l=t.portfolio,n=t.audit,h=t.backtest,u=l.combinations?.length||0,x=Math.max(1,Math.min(m,u)),v=(l.combinations||[]).slice(0,x),S=[...new Set([1,3,5,10,20,30,50,100,u])].filter(a=>a>0&&a<=u).sort((a,i)=>a-i),$=l.model_status==="validated_weighting"?"검증 가중치 사용":"균등 복귀",C=async()=>{const a=v.map((i,F)=>`${String.fromCharCode(65+F)}. ${i.numbers.join(" ")}`).join(` `);await navigator.clipboard.writeText(a),f(!0),setTimeout(()=>f(!1),1500)};return e.jsxs(b,{children:[e.jsxs("header",{children:[e.jsx("h1",{className:"mt-[22px] mb-1 text-[22px] leading-snug tracking-[-.01em]",children:"로또 6/45 검증실"}),e.jsx("p",{className:"m-0 text-[13.5px] text-ink2",children:"패턴을 믿기 전에 균등모델과 미공개 회차에서 먼저 겨룬다."}),e.jsxs("div",{className:"mt-1.5 text-[11.5px] text-ink3",children:["공식 ",t.draw_count.toLocaleString("ko-KR"),"회 · 데이터 마감 ",t.data_cutoff_draw_no,"회 · ",t.data_cutoff_draw_date]})]}),e.jsxs(d,{className:`mt-4 border-l-[3px] ${l.model_status==="validated_weighting"?"border-l-sev2":"border-l-sev0"} px-4 py-4`,children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"tick",children:[l.target_draw_no,"회 운영 판정"]}),e.jsx("div",{className:"mt-0.5 text-[20px] font-semibold",children:$})]}),e.jsxs("div",{className:"tnum rounded-md border border-rule px-3 py-1.5 text-[12px] text-ink2",children:["edge weight ",Number(l.operational_edge_weight).toFixed(1)]})]}),e.jsx("p",{className:"mt-2.5 mb-0 text-[13px] leading-[1.75] text-ink2",children:l.model_status==="validated_weighting"?"베이지안 축소 모델이 Brier score와 조합 로그점수 관문을 모두 통과해 번호 가중치를 사용한다.":"번호 빈도와 미래상수 후보가 워크포워드에서 균등 기준을 이기지 못했다. 번호별 가중치는 정확히 같게 되돌리고, 고유 조합·인기 패턴 회피·조합 간 중첩만 최적화했다."})]}),e.jsxs("div",{className:"mt-3 grid gap-2.5 sm:grid-cols-4",children:[e.jsx(p,{label:"무작위성 감사",value:n.uniform_compatible?"균등과 구별 안 됨":"탐색 신호"}),e.jsx(p,{label:"워크포워드 선택",value:k(h.chosen_model)}),e.jsx(p,{label:"지속상태 ρ",value:Number(l.rho_after_noise_shrinkage).toFixed(4)}),e.jsx(p,{label:"1등 확률 · 공정 가정",value:`1 / ${Math.round(1/(x/8145060)).toLocaleString("ko-KR")}`})]}),e.jsx(c,{note:"1장 1,000원 · 실제 구매 기능 없음",children:"예산 안에서 볼 조합"}),e.jsxs(d,{className:"flex flex-wrap items-center justify-between gap-3 px-3.5 py-3",children:[e.jsxs("label",{className:"text-[13px] text-ink2",children:["표시 예산 ",e.jsx("select",{className:"rounded-md border border-rule bg-panel px-2 py-1 text-ink",value:x,onChange:a=>y(Number(a.target.value)),children:S.map(a=>e.jsxs("option",{value:a,children:[(a*1e3).toLocaleString("ko-KR"),"원 · ",a,"장"]},a))})]}),e.jsx("button",{onClick:C,className:"rounded-md border border-rule bg-panel px-3 py-1 text-[12px] text-ink2 hover:text-ink",children:w?"복사됨":"번호 복사"})]}),e.jsx("div",{className:"mt-2.5 grid gap-2.5 sm:grid-cols-2",children:v.map((a,i)=>e.jsx(E,{row:a,index:i},a.numbers.join("-")))}),e.jsxs("p",{className:"mt-2 text-[11.5px] leading-[1.7] text-ink3",children:["고유 ",x,"조합의 공정 추첨 1등 확률은 ",x.toLocaleString("ko-KR")," / 8,145,060이다. 조합 간 거리는 하위등수 결과의 동조를 줄이지만 1등 확률을 추가로 높이지 않는다."]}),e.jsx(c,{note:`${n.simulations}개 전체 원장 모의실험`,children:"균등 비복원 추첨 감사"}),e.jsxs(d,{className:"px-4 py-3.5",children:[e.jsx("p",{className:"mt-0 mb-3 text-[13px] leading-[1.75] text-ink2",children:"45개 번호와 990개 번호쌍을 훑은 뒤 가장 튀는 값만 고르는 효과까지 모의실험으로 보정했다. p값이 작을수록 균등 추첨에서 보기 드문 결과다."}),e.jsxs("div",{className:"grid gap-3 sm:grid-cols-3",children:[e.jsx(g,{label:"번호 빈도 최대편차",stat:n.observed.max_abs_frequency_z,p:n.familywise_monte_carlo_p.max_abs_frequency_z}),e.jsx(g,{label:"번호쌍 최대편차",stat:n.observed.max_abs_pair_z,p:n.familywise_monte_carlo_p.max_abs_pair_z}),e.jsx(g,{label:"회차간 자기상관",stat:n.observed.max_abs_lag1,p:n.familywise_monte_carlo_p.max_abs_lag1})]})]}),e.jsx(c,{note:`${h.evaluation_draw_range.join("–")}회 완전 미공개 평가`,children:"모델이 실제로 균등을 이겼나"}),e.jsxs(d,{className:"overflow-x-auto px-4 py-3.5",children:[e.jsxs("table",{className:"w-full min-w-[520px] border-collapse text-[12.5px]",children:[e.jsx("thead",{children:e.jsx("tr",{children:["후보","Brier 개선","조합 로그점수 개선","후반 재현","판정"].map(a=>e.jsx("th",{className:"border-b border-rule2 pb-1.5 pr-3 text-left text-[11px] font-medium text-ink3",children:a},a))})}),e.jsx("tbody",{children:Object.entries(h.models).map(([a,i])=>e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-rule2 py-2 pr-3 font-medium",children:k(a)}),e.jsx("td",{className:`tnum border-b border-rule2 py-2 pr-3 ${i.brier_gain.mean<0?"text-sev3":""}`,children:j(i.brier_gain.mean)}),e.jsx("td",{className:`tnum border-b border-rule2 py-2 pr-3 ${i.joint_log_gain.mean<0?"text-sev3":""}`,children:j(i.joint_log_gain.mean,4)}),e.jsx("td",{className:"tnum border-b border-rule2 py-2 pr-3",children:j(i.recent_half_log_gain,4)}),e.jsx("td",{className:`border-b border-rule2 py-2 font-semibold ${i.gate_passed?"text-sev2":"text-ink3"}`,children:i.gate_passed?"통과":"탈락"})]},a))})]}),e.jsx("p",{className:"mb-0 mt-2.5 text-[11.5px] leading-[1.7] text-ink3",children:"양수면 균등보다 낫다. 두 지표의 95% 신뢰하한과 평가 후반부가 모두 양수일 때만 번호 가중치를 켠다."})]}),e.jsx(c,{children:"미래상수는 어디에 썼나"}),e.jsx(d,{className:"px-4 py-3.5",children:e.jsxs("div",{className:"grid gap-3 sm:grid-cols-3",children:[e.jsx(_,{symbol:"C",title:"지속 가능 상태",text:`pooled ρ ${Number(l.rho_raw).toFixed(4)} → 잡음 축소 후 ${Number(l.rho_after_noise_shrinkage).toFixed(4)}`}),e.jsx(_,{symbol:"R",title:"다음 회차 순수 충격",text:"평균 0. 예측 점수에는 넣지 않고 민감도 시뮬레이션에만 사용"}),e.jsx(_,{symbol:"U",title:"미관측 장비·환경",text:"공식 확인값이 없으면 임의 보너스 금지. 불확실성으로만 유지"})]})}),e.jsx(c,{children:"사전등록"}),e.jsxs(d,{className:"px-4 py-3.5 text-[12.5px] leading-[1.8]",children:[e.jsx(o,{k:"대상 / 데이터 마감",v:`${t.preregistration.target_draw_no}회 / ${t.preregistration.data_cutoff_draw_no}회`}),e.jsx(o,{k:"모델 버전",v:t.preregistration.model_version}),e.jsx(o,{k:"생성 시각",v:t.preregistration.generated_at}),e.jsx(o,{k:"데이터 SHA-256",v:t.preregistration.data_hash,mono:!0}),e.jsx(o,{k:"예측 SHA-256",v:t.preregistration.prediction_hash,mono:!0}),e.jsx("p",{className:"mb-0 mt-2 text-[11.5px] text-ink3",children:"결과가 나온 뒤 시드나 실패 조합을 바꾸지 않는다. 같은 대상 회차의 기존 등록은 덮어쓰지 않는다."})]}),e.jsxs("details",{className:"card mt-3 rounded-card border border-rule bg-panel",children:[e.jsx("summary",{className:"cursor-pointer px-4 py-3 text-[13px] font-semibold",children:"확률 수식과 해석 보기"}),e.jsxs("div",{className:"px-4 py-3 text-[12.5px] leading-[1.85] text-ink2",children:[e.jsxs("p",{className:"mt-0",children:[e.jsx("b",{className:"text-ink",children:"균등 포함확률"})," P(i 포함)=6/45. ",e.jsx("b",{className:"text-ink",children:"고유 조합"})," P(S)=1/C(45,6)=1/8,145,060."]}),e.jsxs("p",{children:[e.jsx("b",{className:"text-ink",children:"가중 부분집합"})," P(S|η)=exp(Σᵢ∈S ηᵢ) / e₆(exp η₁,…,exp η₄₅). 여섯 번의 독립 추출로 잘못 계산하지 않는다."]}),e.jsxs("p",{className:"mb-0",children:[e.jsx("b",{className:"text-ink",children:"운영 관문"})," 미공개 회차 Brier·조합 로그점수를 동시에 개선하지 못하면 η=0으로 되돌린다. 인기 회피는 공동당첨 위험용 별도 점수다."]})]})]}),e.jsxs("footer",{className:"mt-8 border-t border-rule pt-4 text-[11.5px] leading-[1.85] text-ink3",children:[e.jsxs("p",{className:"m-0",children:["출처 ",e.jsx("a",{className:"text-signal",href:t.official_source,children:"동행복권 공식 회차 결과"})," · 연구/검증 목적 · 구매·당첨 보장 기능 없음"]}),e.jsx("p",{className:"m-0",children:"19세 미만은 복권을 구매할 수 없다. 구매 예산과 손실 한도는 모델 밖에서 먼저 정한다."})]})]})}function b({children:s}){return e.jsxs("div",{className:"mx-auto max-w-[900px] px-5 pb-20",children:[e.jsx(T,{current:"lotto.html"}),s,e.jsx(R,{})]})}function p({label:s,value:r}){return e.jsxs(d,{className:"px-3.5 py-3",children:[e.jsx("div",{className:"tick",children:s}),e.jsx("div",{className:"mt-0.5 text-[14px] font-semibold leading-snug",children:r})]})}function E({row:s,index:r}){const t=s.popularity_risk?.reasons||[];return e.jsxs(d,{className:"px-3.5 py-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"tnum text-[11px] text-ink3",children:String.fromCharCode(65+r)}),e.jsxs("span",{className:"text-[10.5px] text-ink3",children:["중복거리 ",s.min_distance_from_previous,"/6"]})]}),e.jsx("div",{className:"mt-2 flex flex-wrap gap-1.5",children:s.numbers.map(m=>e.jsx("span",{className:"tnum inline-flex h-8 w-8 items-center justify-center rounded-full border border-rule bg-paper text-[12px] font-semibold",children:m},m))}),e.jsxs("div",{className:"mt-2 text-[10.5px] text-ink3",children:["인기위험 ",Number(s.popularity_risk?.score||0).toFixed(1)," · ",t.length?t.join(" · "):"탐지된 전형 패턴 없음"]})]})}function g({label:s,stat:r,p:t}){return e.jsxs("div",{children:[e.jsx("div",{className:"tick",children:s}),e.jsx("div",{className:"tnum mt-0.5 text-[16px] font-semibold",children:Number(r).toFixed(3)}),e.jsxs("div",{className:"tnum text-[11px] text-ink3",children:["보정 p=",z(t)]})]})}function _({symbol:s,title:r,text:t}){return e.jsxs("div",{className:"border-l-2 border-rule pl-3",children:[e.jsx("div",{className:"tnum text-[16px] font-semibold",children:s}),e.jsx("div",{className:"text-[12.5px] font-semibold",children:r}),e.jsx("div",{className:"mt-1 text-[11.5px] leading-[1.65] text-ink3",children:t})]})}function o({k:s,v:r,mono:t}){return e.jsxs("div",{className:"grid gap-1 border-b border-rule2 py-1.5 sm:grid-cols-[150px_1fr]",children:[e.jsx("span",{className:"text-ink3",children:s}),e.jsx("span",{className:`${t?"tnum break-all text-[10.5px]":""}`,children:r||"—"})]})}function k(s){return{uniform:"균등",bayesian_bias:"베이지안 축소",future_constant:"미래상수 통합"}[s]||s}L();K.createRoot(document.getElementById("root")).render(e.jsx(N.StrictMode,{children:e.jsx(B,{})})); diff --git a/docs/assets/markets-BVDvkSYt.js b/docs/assets/markets-BVDvkSYt.js deleted file mode 100644 index 0ba32d1b..00000000 --- a/docs/assets/markets-BVDvkSYt.js +++ /dev/null @@ -1 +0,0 @@ -import{i as r,c as o,j as t,r as e}from"./cache-guard-cBqh6aGv.js";import{M as i}from"./Markets--wDDhPBF.js";import"./decision-view-model-Bfii5tWW.js";import"./unified-recommendation-BCuqyY1U.js";import"./bet-ledger-ES6mNno4.js";import"./AiDisclosure-BFwnE-uI.js";import"./live-odds-CIQqKAce.js";import"./poll-DDJ49qcP.js";r();o.createRoot(document.getElementById("root")).render(t.jsx(e.StrictMode,{children:t.jsx(i,{})})); diff --git a/docs/assets/markets-CEFn64q7.js b/docs/assets/markets-CEFn64q7.js new file mode 100644 index 00000000..2aef9c8e --- /dev/null +++ b/docs/assets/markets-CEFn64q7.js @@ -0,0 +1 @@ +import{i as r,c as o,j as t,r as e}from"./cache-guard-BQcATxSH.js";import{M as i}from"./Markets-BuleyXai.js";import"./decision-view-model-Bfii5tWW.js";import"./unified-recommendation-BCuqyY1U.js";import"./bet-ledger-ES6mNno4.js";import"./AiDisclosure-BIoGQ_Ky.js";import"./live-odds-CIQqKAce.js";import"./poll-QCTlueAT.js";r();o.createRoot(document.getElementById("root")).render(t.jsx(e.StrictMode,{children:t.jsx(i,{})})); diff --git a/docs/assets/poll-DDJ49qcP.js b/docs/assets/poll-QCTlueAT.js similarity index 93% rename from docs/assets/poll-DDJ49qcP.js rename to docs/assets/poll-QCTlueAT.js index 3e631b98..5d72dbb9 100644 --- a/docs/assets/poll-DDJ49qcP.js +++ b/docs/assets/poll-QCTlueAT.js @@ -1 +1 @@ -import{r as c}from"./cache-guard-cBqh6aGv.js";const y=e=>`${e}${e.includes("?")?"&":"?"}_=${Date.now()}`;function S(e,l=3e5){const[u,m]=c.useState({data:{},at:null}),s=c.useRef({}),d=Object.keys(e).join("|"),f=Object.values(e).join("|");return c.useEffect(()=>{let v=!1;const h=d.split("|").map((a,i)=>[a,f.split("|")[i]]);async function n(){const a={};let i=!1;await Promise.all(h.map(async([t,b])=>{try{const r=await fetch(y(b),{cache:"no-store",headers:{"Cache-Control":"no-cache"}});if(!r.ok)throw new Error(String(r.status));const o=await r.text();s.current[t]!==o&&(s.current[t]=o,i=!0),a[t]=JSON.parse(o)}catch{a[t]=s.current[t]?JSON.parse(s.current[t]):null}})),!v&&(i||u.at===null)&&m({data:a,at:Date.now()})}n();const p=setInterval(n,l),w=()=>{document.visibilityState==="visible"&&n()};return document.addEventListener("visibilitychange",w),window.addEventListener("online",n),()=>{v=!0,clearInterval(p),document.removeEventListener("visibilitychange",w),window.removeEventListener("online",n)}},[d,f,l]),u}export{S as u}; +import{r as c}from"./cache-guard-BQcATxSH.js";const y=e=>`${e}${e.includes("?")?"&":"?"}_=${Date.now()}`;function S(e,l=3e5){const[u,m]=c.useState({data:{},at:null}),s=c.useRef({}),d=Object.keys(e).join("|"),f=Object.values(e).join("|");return c.useEffect(()=>{let v=!1;const h=d.split("|").map((a,i)=>[a,f.split("|")[i]]);async function n(){const a={};let i=!1;await Promise.all(h.map(async([t,b])=>{try{const r=await fetch(y(b),{cache:"no-store",headers:{"Cache-Control":"no-cache"}});if(!r.ok)throw new Error(String(r.status));const o=await r.text();s.current[t]!==o&&(s.current[t]=o,i=!0),a[t]=JSON.parse(o)}catch{a[t]=s.current[t]?JSON.parse(s.current[t]):null}})),!v&&(i||u.at===null)&&m({data:a,at:Date.now()})}n();const p=setInterval(n,l),w=()=>{document.visibilityState==="visible"&&n()};return document.addEventListener("visibilitychange",w),window.addEventListener("online",n),()=>{v=!0,clearInterval(p),document.removeEventListener("visibilitychange",w),window.removeEventListener("online",n)}},[d,f,l]),u}export{S as u}; diff --git a/docs/assets/research-CmucxqVN.js b/docs/assets/research-ni-EZSkE.js similarity index 99% rename from docs/assets/research-CmucxqVN.js rename to docs/assets/research-ni-EZSkE.js index 2a7918da..807f6047 100644 --- a/docs/assets/research-CmucxqVN.js +++ b/docs/assets/research-ni-EZSkE.js @@ -1 +1 @@ -import{j as e,N as p,C as x,T as h,i as o,c as j,r as b}from"./cache-guard-cBqh6aGv.js";import{a as N}from"./AiDisclosure-BFwnE-uI.js";import"./decision-view-model-Bfii5tWW.js";const g=[["배당 구조만으로 +EV 구간이 있는가","배당대·종목·상품·리그로 134개 구간을 쪼개 검정","0개","neg"],["팀 단위 변수가 시장을 넘는가","폼·득실마진·홈원정·백투백·휴식·상대전적 9종","시장 우위","neg"],["모델을 정교화하면 좁혀지는가","점수차 기반 pi-ratings 도입","15%만 축소","warn"],["선발 정보 시차가 우위인가","KBO·MLB·NPB 선발 예고 시점 대조","재현 실패","neg"],["축구 라인업이 우위인가","라인업은 경기 1시간 전 공개 = 배당보다 확실히 늦음","표본 부족","warn"],["해외 배당과의 괴리에 기회가 있는가","509경기·1,120선택지 대조. 괴리는 있으나 마감배당(미래정보)이었다","0.9%","neg"],["구장 파크팩터가 우위인가","walk-forward 구장 효과는 진짜(우위확률 97.9%). 그런데 시장 확률 위에 얹으니 오히려 나빠졌다","시장이 안다","neg"],["타선을 선수 단위로 재면 달라지는가","2,896경기 선수별 재수집. 방향은 일관(계수 +8~9)이나 크기가 1.7%p","44시즌 필요","neg"],["프로토가 약한 리그가 있는가","리그 × 마켓 74조합. 커버가 얇은 리그일수록 오히려 더 진다","0개","neg"],["얇은 시장이 기회인가","초대면 −1.89% → 16회+ +0.10%. 단조인데 방향이 반대","기각","neg"],["배당이 얼어 있으니 정보 시차가 있지 않은가","프로토 라인의 86.2%가 33시간 동안 안 움직인다 — 전제는 참. 그런데 샤프 마켓조차 24시간 스윙이 중앙 2.4%p","정보가 작다","neg"],["구조적 선택으로 마진을 낮출 수 있는가","규칙 7개를 전부 AND. 표본은 5.5배 줄었는데 얻은 건 2.4%p","−13.9%→−11.5%","warn"]],k=[["언더오버 배당 순서를 반대로 읽음","같은 구간의 오버·언더가 둘 다 +ROI 로 나오는 물리적 모순","전체의 26%(46,088행) 오염. ROI +54% 라는 가짜 결과"],["같은 경기가 여러 회차에 중복 발매되는 걸 놓침","2026 LG 시즌 성적이 196경기(실제 144)로 집계","Elo 누수. 픽 ROI 가 −8.6% 로 좋아 보였으나 실제 −15.1%"],["pi-ratings 파라미터를 감으로 지정","농구 Brier 가 Elo 보다 0.026 악화","프로젝트 원칙 위반. 학습 구간 격자탐색으로 재설정"],["3-way 를 2-way 로직으로 계산","마진 12% 시장에서 ROI +64% 라는 불가능한 값","+EV 비율 21.6% → 실제 0.9%"]];function u(){return e.jsxs("div",{className:"mx-auto max-w-[860px] px-5 pb-20",children:[e.jsx(p,{current:"research.html"}),e.jsxs("header",{children:[e.jsx("h1",{className:"mt-[22px] mb-1 text-[22px] leading-snug tracking-[-.01em] text-balance",children:"한국 프로토 고정배당 시장은 효율적인가"}),e.jsx("p",{className:"m-0 text-[13.5px] text-ink2",children:"553회차·353,047건을 실측해 답을 찾는 공개 연구. 결과가 어느 쪽이든 그대로 적는다."}),e.jsxs("div",{className:"mt-1.5 text-[11.5px] text-ink3",children:["최종 갱신 2026-07-29 · 실측 경기 43,509 ·"," ",e.jsx("a",{className:"text-signal",href:"https://github.com/choigod1023/proto-odds-research/tree/main/findings",children:"전체 문서"})]})]}),e.jsx(x,{className:"mt-4 flex flex-wrap gap-x-10 gap-y-3 px-4 py-3.5",children:[["수집 회차","553","2023–2026"],["베팅 레코드","353,047","게임행 177,549"],["프로토 환급률","88.0%","2-way 기준"],["해외 환급률","95.3%","7.3%p 차이"]].map(([s,l,t])=>e.jsxs("div",{children:[e.jsx("div",{className:"tick",children:s}),e.jsx("div",{className:"tnum text-[19px] font-semibold leading-[1.35]",children:l}),e.jsx("div",{className:"text-[11px] text-ink3",children:t})]},s))}),e.jsxs(x,{className:"mt-4 border-l-[3px] border-l-sev3 px-4 py-4",children:[e.jsx("b",{className:"text-[13.5px] text-sev3",children:"현재 운영 결론 — 기존 신호는 승격하지 않는다"}),e.jsxs("p",{className:"mt-1.5 mb-2.5 text-[13px] leading-[1.75] text-ink2",children:[e.jsx("b",{className:"text-ink",children:"현재 검증한 신호로는 시장을 이기지 못했다."})," 새 신호도 미래 구간에서 입증되기 전에는 운영에 넣지 않는다."]}),e.jsx("table",{className:"w-auto border-collapse text-[13px]",children:e.jsx("tbody",{children:[["필요한 우위","6.8%p (2-way 마진 12%)"],["정보의 크기","2.4%p — 샤프 마켓 자신의 24h 스윙"],["구조 선택의 이득","2.4%p — 규칙 7개 전부 누적"],["합쳐도","4.8%p < 6.8%p"]].map(([s,l],t)=>e.jsxs("tr",{children:[e.jsx("td",{className:"py-0.5 pr-4 text-ink3",children:s}),e.jsx("td",{className:`tnum py-0.5 ${t===3?"font-semibold text-ink":""}`,children:l})]},s))})}),e.jsxs("p",{className:"mt-2.5 mb-0 text-[13px] leading-[1.75] text-ink2",children:["문헌이 이걸 확증한다 — 장기 수익 베터는 약 3%, 프로가 효율 시장에서 목표하는 CLV 는"," ",e.jsx("b",{className:"text-ink",children:"+2~3%"}),", 라인이 무른 틈새시장에서도 ",e.jsx("b",{className:"text-ink",children:"+5%"})," 다."," ",e.jsx("b",{className:"text-ink",children:"프로토 마진 12% 는 세계 최고의 엣지를 그대로 가져와도 −7% 가 되는 크기다."})," ",'Aoki et al.(KDD 2017)도 "운이 상당해서 정교한 모델이 단순 모델을 거의 못 이긴다"고 한다.']})]}),e.jsx(N,{id:"ai-model",showLink:!1}),e.jsxs(x,{className:"mt-3 border-l-[3px] border-l-signal px-4 py-4",children:[e.jsx("b",{className:"text-[13.5px]",children:"AI가 확률을 바꿀 수 있는 유일한 경로"}),e.jsxs("p",{className:"mt-1.5 mb-2 text-[13px] leading-[1.75] text-ink2",children:["운영식은 ",e.jsx("b",{className:"tnum text-ink",children:"logit(p최종) = logit(p시장) + AI 잔차"}),"다. AI는 시장이 이미 아는 팀 전력 전체를 다시 예측하지 않고, 같은 시각 이후 새로 확인된 선발·결장·라인업 변화만 잔차로 학습한다. 현재 잔차는 검증 전이라 ",e.jsx("b",{className:"text-ink",children:"0%p"}),"다."]}),e.jsxs("ol",{className:"m-0 grid gap-2 p-0 text-[12px] leading-[1.65] text-ink2 sm:grid-cols-2",children:[e.jsxs("li",{className:"list-none border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:"1. 시각 고정"}),e.jsx("br",{}),"예측 시점에 실제로 보인 배당·자료만 저장한다."]}),e.jsxs("li",{className:"list-none border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:"2. 워크포워드"}),e.jsx("br",{}),"과거로 학습하고 그다음 경기만 예측한다."]}),e.jsxs("li",{className:"list-none border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:"3. 확률 검정"}),e.jsx("br",{}),"Brier·로그손실·보정도를 시장 기준선과 비교한다."]}),e.jsxs("li",{className:"list-none border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:"4. 승격 또는 0"}),e.jsx("br",{}),"미공개 기간 개선의 신뢰구간이 0을 넘을 때만 반영한다."]})]}),e.jsxs("p",{className:"mt-2.5 mb-0 text-[11px] leading-[1.7] text-ink3",children:["생성형 AI는 기사·공식 발표를 구조화하고 문장을 다듬을 뿐, 직접 확률을 쓰지 않는다. 선택적 예측으로 자료가 낡았거나 분포가 바뀐 경기는 보류하고, 고정된 커버리지에서 성능을 비교한다."," ",e.jsx("a",{className:"text-signal",href:"https://proceedings.mlr.press/v70/guo17a.html",children:"확률 보정"})," ·"," ",e.jsx("a",{className:"text-signal",href:"https://proceedings.mlr.press/v97/geifman19a.html",children:"선택적 예측"})," ·"," ",e.jsx("a",{className:"text-signal",href:"https://journals.sagepub.com/doi/10.1177/1471082X20929881",children:"선수·팀 정보 결합"})]})]}),e.jsxs(x,{id:"evolutionary-selector",className:"mt-3 scroll-mt-4 border-l-[3px] border-l-rule px-4 py-4",children:[e.jsx("b",{className:"text-[13.5px]",children:"자연선택 추천기 — 확률을 바꾸지 않고 한 픽을 고르는 AI"}),e.jsx("p",{className:"mt-1.5 mb-2 text-[13px] leading-[1.75] text-ink2",children:"전략 56개를 한 세대로 두고 24세대 동안 교배·돌연변이시킨다. 적중률만 좇아 초저배당으로 퇴화하지 않도록 유형별 최소배당·추천 빈도·종목 쏠림을 함께 생존 조건으로 둔다. 2023–2024에서 진화하고 2025에서 생존 전략을 고른 뒤 2026의 하루 한 픽으로 역사 감사했다."}),e.jsx("ol",{className:"mb-3 grid list-none gap-2 p-0 text-[11px] leading-[1.6] text-ink2 sm:grid-cols-4",children:[["01","후보 생성","서로 다른 가중치 전략 56개"],["02","진화","상위 전략 교배·돌연변이 24세대"],["03","생존 선택","2025 성능으로 최종 전략 선택"],["04","감사","2026 하루 한 픽으로 재검사"]].map(([s,l,t])=>e.jsxs("li",{className:"rounded border border-rule2 bg-panel px-2.5 py-2",children:[e.jsx("span",{className:"tnum text-ink3",children:s})," ",e.jsx("b",{className:"text-ink",children:l}),e.jsx("br",{}),t]},s))}),e.jsx("div",{className:"grid gap-2 text-[12px] sm:grid-cols-3",children:[["안정형","77.9% · 평균 1.21배","2025 +0.6 → 2026 −0.9%p","−6.1~+4.3%p"],["균형형","62.8% · 평균 1.45배","2025 −0.6 → 2026 +6.1%p","−1.7~+13.9%p"],["도전형","48.5% · 평균 1.75배","2025 +1.7 → 2026 −6.1%p","−14.3~+2.2%p"]].map(([s,l,t,r])=>e.jsxs("div",{className:"border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:s}),e.jsx("br",{}),e.jsx("span",{className:"tnum text-ink2",children:l}),e.jsx("br",{}),e.jsxs("span",{className:"tnum text-sev3",children:["동일 배당범위 기준선 대비 ",t]}),e.jsx("br",{}),e.jsxs("span",{className:"tnum text-[10.5px] text-ink3",children:["95% CI ",r]})]},s))}),e.jsxs("div",{className:"mt-3 rounded border border-sev2 bg-panel px-3 py-2.5 text-[11.5px] leading-[1.7] text-ink2",children:[e.jsx("b",{className:"text-sev3",children:"운영 결정 · 세 유형 모두 추천 중단"}),e.jsx("br",{}),"2025와 2026의 개선 방향이 모두 뒤집혔다. 균형형의 2026 +6.1%p만 떼어 쓰지 않으며, 오늘 화면에서도 탈락 유형은 실제 후보를 내지 않는다."]}),e.jsxs("p",{className:"mt-2.5 mb-0 text-[11px] leading-[1.7] text-ink3",children:["초기 설계를 2026 감사 후 수정했으므로 이 수치는 독립 홀드아웃이 아니다. 사전등록 미래 300픽 이상에서 동일 날짜·동일 배당 범위 시장확률 1순위보다 적중률 차이의 95% 신뢰구간 하한이 0을 넘을 때만 다시 연다."," ",e.jsx("a",{className:"text-signal",href:"https://github.com/choigod1023/proto-odds-research/blob/main/findings/%EC%9E%90%EC%97%B0%EC%84%A0%ED%83%9D_%EC%B6%94%EC%B2%9C%EA%B8%B0_%EA%B2%80%EC%A6%9D.md",children:"재현 문서 보기 →"})]})]}),e.jsxs(x,{className:"mt-3 border-l-[3px] border-l-sev2 px-4 py-4",children:[e.jsx("b",{className:"text-[13.5px] text-sev2",children:"2026-07-29 정정 — 실제로 살 수 있는 값"}),e.jsxs("p",{className:"mt-1.5 mb-2.5 text-[13px] leading-[1.75] text-ink2",children:['이전까지 "아무거나 −13.7% → 등급 A 만 −9.2%, 4.5%p 절감" 이라고 적었다. 그런데 그건 ',e.jsx("b",{className:"text-ink",children:"선택지 1개 기준"}),"이고, 단폴(한경기구매)은 '한경기' 로 지정된 경기만 살 수 있다. 배당을 올리려면 조합해야 하고,",e.jsx("b",{className:"text-ink",children:" 조합하면 다리마다 마진이 한 번씩 더 물린다."})]}),e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full min-w-[380px] border-collapse text-[13px]",children:[e.jsx("thead",{children:e.jsx("tr",{children:["","1폴(지정 경기만)","2폴","3폴"].map(s=>e.jsx("th",{className:"border-b border-rule2 pb-1 pr-3 text-left text-[11px] font-medium text-ink3",children:s},s))})}),e.jsx("tbody",{children:[["아무거나","−13.6%","−25.3%","−35.5%"],["배당 1.0–1.3 · 2-way","−9.8%","−18.7%","−26.7%"],["절약폭","3.8%p","6.6%p","8.8%p"]].map(([s,l,t,r])=>e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-rule2 py-1 pr-3 text-ink2",children:s}),e.jsx("td",{className:"tnum border-b border-rule2 py-1 pr-3",children:l}),e.jsx("td",{className:"tnum border-b border-rule2 py-1 pr-3 font-semibold",children:t}),e.jsx("td",{className:"tnum border-b border-rule2 py-1 pr-3",children:r})]},s))})]})}),e.jsxs("p",{className:"mt-2.5 mb-0 text-[13px] leading-[1.75] text-ink2",children:["절약폭 자체는 오히려 커진다(4.5%p → 6.6%p). 절대 손실이 두 배가 될 뿐이다. 그리고 ",e.jsx("b",{className:"text-ink",children:"저배당 우선 규칙은 조합에서 뒤집힌다"})," — 배당 1.0–1.3 은 다리 하나로는 최선이지만 배당을 만드는 효율은 최악이라, 목표 배당이 있으면 다리당 배당을 올리는 쪽이 덜 잃는다."]})]}),e.jsx(n,{n:"01",children:"지금까지 만난 열두 개의 벽"}),e.jsxs(d,{children:["각 검증의 판정 기준은 ",e.jsx("b",{children:"데이터를 보기 전에"})," 정해뒀다. 나중에 정하면 어떤 결과든 성공으로 해석되기 때문이다."]}),e.jsx("ol",{className:"m-0 list-none p-0",children:g.map(([s,l,t,r],i)=>e.jsx("li",{children:e.jsxs(x,{className:"mt-2 flex flex-wrap items-center gap-3 px-3.5 py-3",children:[e.jsx("span",{className:"tnum w-6 shrink-0 text-[12px] text-ink3",children:i+1}),e.jsxs("span",{className:"min-w-[200px] flex-1",children:[e.jsx("span",{className:"block text-[13.5px] font-semibold",children:s}),e.jsx("span",{className:"block text-[12px] leading-[1.7] text-ink3",children:l})]}),e.jsx("span",{className:`tnum shrink-0 text-[12.5px] font-semibold ${r==="neg"?"text-sev3":"text-sev2"}`,children:t})]})},s))}),e.jsx(n,{n:"02",children:"확정된 사실"}),e.jsx(d,{children:"재현 가능하고, 코드가 공개돼 있다."}),e.jsxs(c,{children:["booking 은 상품이 아니라 ",e.jsx("b",{children:"구조"}),"로 결정된다"]}),e.jsx(m,{head:["구조","포함 상품","환급률","회차 내부 SD"],rows:[["2-way","승패·언더오버·2way핸디캡","88.00%","0.0006"],["3-way","승무패·승①패","87.00%","0.0007"],["3-way 핸디캡","—","85.99%","0.0008"]]}),e.jsxs(a,{children:["회차 ",e.jsx("b",{children:"내부"})," SD 는 0.0007 인데 회차 ",e.jsx("b",{children:"간"})," SD 는 0.0090 으로 ",e.jsx("b",{children:"13.5배"})," 다. 회차마다 목표 환급률이 86/87/88/89% 중 하나로 달라진다."]}),e.jsx(c,{children:"favorite-longshot 편향이 교과서적으로 존재한다"}),e.jsx(m,{head:["배당 구간","실측 ROI","기준선 대비"],rows:[["1.0–1.5 (강팀)","−9.90%","+2.50%p"],["1.8–2.2 (박빙)","−12.57%","−0.16%p"],["5.0+ (극단 역배)","−33.19%","−20.04%p"]]}),e.jsxs(a,{children:["미국 경마(Snowberg & Wolfers)와 ",e.jsx("b",{children:"같은 방향"}),'이다. "한국은 인기구단 쏠림으로 반대일 것"이라는 가설은 기각 방향.']}),e.jsx(c,{children:"승패보다 득실 마진이 정보량이 많다"}),e.jsxs(a,{children:["축구·농구·배구 ",e.jsx("b",{children:"세 종목 모두"})," '최근 득실 마진'이 '최근 승률'을 앞섰다. 같은 5승 5패라도 한 점 차로 이기고 크게 지는 팀은 다르다. 문헌(pi-ratings)을 읽고 넣은 게 아니라 ",e.jsx("b",{children:"측정에서 먼저 나왔다."})]}),e.jsx(c,{children:"선발투수는 지표를 바꾸자 결과가 뒤집혔다"}),e.jsx(m,{head:["지표","최대 z","최대 Brier 개선"],rows:[["팀 실점 (불펜 섞임)","2.58","+0.00204"],["투수 개인 자책점·이닝","4.53","+0.00613"]]}),e.jsxs(a,{children:[e.jsx("b",{children:'"선발투수는 소용없다"가 아니라 "지표가 거칠어서 안 잡혔다"'}),"였다. 흥미롭게도 자책률보다 ",e.jsx("b",{children:"평균 이닝"}),"이 더 강하다 — 얼마나 적게 내주느냐보다"," ",e.jsx("b",{children:"얼마나 길게 끌어주느냐"}),"가 승패에 직결된다."]}),e.jsx(n,{n:"03",children:"스스로 잡은 오류"}),e.jsx(d,{children:"연구 과정에서 낸 실수와 발견 경위. 같은 실수를 반복하지 않기 위해 남긴다."}),e.jsx("div",{className:"grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(280px,1fr))]",children:k.map(([s,l,t],r)=>e.jsxs(x,{className:"border-l-[3px] border-l-sev3 px-3.5 py-3",children:[e.jsxs("div",{className:"text-[13px] font-semibold",children:[r+1,". ",s]}),e.jsxs("p",{className:"mt-1.5 mb-1 text-[12.5px] leading-[1.7] text-ink2",children:[e.jsx("span",{className:"text-ink3",children:"발견"})," ",l]}),e.jsxs("p",{className:"m-0 text-[12.5px] leading-[1.7] text-ink2",children:[e.jsx("span",{className:"text-ink3",children:"영향"})," ",t]})]},s))}),e.jsxs("blockquote",{className:"mt-3 border-l-[3px] border-rule pl-3.5 text-[13px] leading-[1.8] text-ink2",children:[e.jsx("b",{className:"text-ink",children:'교훈: 결과가 "너무 좋으면" 버그다.'})," 물리적으로 불가능한 값 (양쪽 다 +EV, ROI +54%, +64%)이 매번 오류를 잡아냈다. 검증 장치를 먼저 만들어 둔 것이 실제로 작동했다. 지금은 자기검사 9종이 매 실행마다 돈다."]}),e.jsx(n,{n:"04",children:"지금 돌아가는 것"}),e.jsxs(d,{children:["남은 질문은 하나다 — ",e.jsx("b",{children:"배당이 굳은 뒤 공개되는 정보에 기회가 있는가."}),"과거 데이터로는 답할 수 없어 실시간으로 쌓는 중이다."]}),e.jsx(m,{head:["수집기","주기","무엇을"],rows:[["프로토 배당 스냅샷","15분","발매 중 배당이 실제로 변하는가"],["해외 배당 스냅샷","15분","같은 시점 해외 배당 (동시점 대조)"],["선발 정보 관측기","30분","KBO·MLB·NPB 선발 예고 시각"]]}),e.jsxs(x,{className:"mt-3 border-l-[3px] border-l-sev2 px-3.5 py-3",children:[e.jsx("div",{className:"text-[13px] font-semibold",children:"왜 실시간이어야 하나"}),e.jsxs("p",{className:"mt-1.5 mb-0 text-[12.5px] leading-[1.75] text-ink2",children:["과거 해외 배당은 ",e.jsx("b",{className:"text-ink",children:"마감 배당"}),"이다. 경기 상세의 기록 시각으로 확인했다 — 경기 약 7시간 전 값이다. 프로토는 최대 60시간 전에 굳으므로"," ",e.jsx("b",{className:"text-ink",children:"베팅 시점에 알 수 없는 값"}),"이고, 그걸로 계산한 +EV 에는 낙관 편향이 섞인다."]})]}),e.jsx(n,{n:"05",children:"현실 점검"}),e.jsxs(a,{children:["구매 한도가 ",e.jsx("b",{children:"회차당 10만원"}),"이다. 엣지 3%를 확보해도 회차당 3,000원, 연 150회차면 45만원이 구조적 천장이다. 그리고 그 3%를 실제로 확보하는 것 자체가 대부분 실패한다."]}),e.jsxs(a,{children:["열두 번 검증했고 ",e.jsx("b",{children:"실전에 쓸 수 있는 +EV 는 확인되지 않았다."}),'결과가 "시장은 효율적이다"로 나와도 그건 실패가 아니라 결론이다.']}),e.jsxs("footer",{className:"mt-9 border-t border-rule pt-4 text-[11.5px] leading-[1.85] text-ink3",children:[e.jsx("p",{className:"m-0",children:"데이터 출처 와이즈토토 회차 아카이브 · 네이버 스포츠 · BetExplorer · 비상업 연구 목적"}),e.jsx("p",{className:"m-0",children:"합법 발매처는 오프라인 판매점과 betman.co.kr 뿐이며, 해외 북메이커 이용은 국민체육진흥법 위반이다."}),e.jsx("p",{className:"m-0 mt-2",children:e.jsx("a",{className:"text-signal",href:"https://github.com/choigod1023/proto-odds-research",children:"소스·방법론·재현 코드"})})]}),e.jsx(h,{})]})}const n=({n:s,children:l})=>e.jsxs("h2",{className:"mt-9 mb-2 flex items-baseline gap-2.5 text-[16px] tracking-[-.01em]",children:[e.jsx("span",{className:"tnum text-[12px] font-normal text-ink3",children:s}),l]}),c=({children:s})=>e.jsx("h3",{className:"mt-6 mb-1.5 text-[13.5px] font-semibold",children:s}),d=({children:s})=>e.jsx("p",{className:"mt-0 mb-3 text-[13px] leading-[1.8] text-ink2",children:s}),a=({children:s})=>e.jsx("p",{className:"mt-2 mb-0 text-[13px] leading-[1.8] text-ink2",children:s});function m({head:s,rows:l}){return e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"mt-1.5 w-full min-w-[340px] border-collapse text-[12.5px]",children:[e.jsx("thead",{children:e.jsx("tr",{children:s.map((t,r)=>e.jsx("th",{className:`border-b border-rule pb-1.5 pr-3 text-[11px] font-medium text-ink3 ${r?"text-right":"text-left"}`,children:t},t))})}),e.jsx("tbody",{children:l.map(t=>e.jsx("tr",{children:t.map((r,i)=>e.jsx("td",{className:`border-b border-rule2 py-1.5 pr-3 ${i?"tnum text-right":""}`,children:r},i))},t[0]))})]})})}o();j.createRoot(document.getElementById("root")).render(e.jsx(b.StrictMode,{children:e.jsx(u,{})})); +import{j as e,N as p,C as x,T as h,i as o,c as j,r as b}from"./cache-guard-BQcATxSH.js";import{a as N}from"./AiDisclosure-BIoGQ_Ky.js";import"./decision-view-model-Bfii5tWW.js";const g=[["배당 구조만으로 +EV 구간이 있는가","배당대·종목·상품·리그로 134개 구간을 쪼개 검정","0개","neg"],["팀 단위 변수가 시장을 넘는가","폼·득실마진·홈원정·백투백·휴식·상대전적 9종","시장 우위","neg"],["모델을 정교화하면 좁혀지는가","점수차 기반 pi-ratings 도입","15%만 축소","warn"],["선발 정보 시차가 우위인가","KBO·MLB·NPB 선발 예고 시점 대조","재현 실패","neg"],["축구 라인업이 우위인가","라인업은 경기 1시간 전 공개 = 배당보다 확실히 늦음","표본 부족","warn"],["해외 배당과의 괴리에 기회가 있는가","509경기·1,120선택지 대조. 괴리는 있으나 마감배당(미래정보)이었다","0.9%","neg"],["구장 파크팩터가 우위인가","walk-forward 구장 효과는 진짜(우위확률 97.9%). 그런데 시장 확률 위에 얹으니 오히려 나빠졌다","시장이 안다","neg"],["타선을 선수 단위로 재면 달라지는가","2,896경기 선수별 재수집. 방향은 일관(계수 +8~9)이나 크기가 1.7%p","44시즌 필요","neg"],["프로토가 약한 리그가 있는가","리그 × 마켓 74조합. 커버가 얇은 리그일수록 오히려 더 진다","0개","neg"],["얇은 시장이 기회인가","초대면 −1.89% → 16회+ +0.10%. 단조인데 방향이 반대","기각","neg"],["배당이 얼어 있으니 정보 시차가 있지 않은가","프로토 라인의 86.2%가 33시간 동안 안 움직인다 — 전제는 참. 그런데 샤프 마켓조차 24시간 스윙이 중앙 2.4%p","정보가 작다","neg"],["구조적 선택으로 마진을 낮출 수 있는가","규칙 7개를 전부 AND. 표본은 5.5배 줄었는데 얻은 건 2.4%p","−13.9%→−11.5%","warn"]],k=[["언더오버 배당 순서를 반대로 읽음","같은 구간의 오버·언더가 둘 다 +ROI 로 나오는 물리적 모순","전체의 26%(46,088행) 오염. ROI +54% 라는 가짜 결과"],["같은 경기가 여러 회차에 중복 발매되는 걸 놓침","2026 LG 시즌 성적이 196경기(실제 144)로 집계","Elo 누수. 픽 ROI 가 −8.6% 로 좋아 보였으나 실제 −15.1%"],["pi-ratings 파라미터를 감으로 지정","농구 Brier 가 Elo 보다 0.026 악화","프로젝트 원칙 위반. 학습 구간 격자탐색으로 재설정"],["3-way 를 2-way 로직으로 계산","마진 12% 시장에서 ROI +64% 라는 불가능한 값","+EV 비율 21.6% → 실제 0.9%"]];function u(){return e.jsxs("div",{className:"mx-auto max-w-[860px] px-5 pb-20",children:[e.jsx(p,{current:"research.html"}),e.jsxs("header",{children:[e.jsx("h1",{className:"mt-[22px] mb-1 text-[22px] leading-snug tracking-[-.01em] text-balance",children:"한국 프로토 고정배당 시장은 효율적인가"}),e.jsx("p",{className:"m-0 text-[13.5px] text-ink2",children:"553회차·353,047건을 실측해 답을 찾는 공개 연구. 결과가 어느 쪽이든 그대로 적는다."}),e.jsxs("div",{className:"mt-1.5 text-[11.5px] text-ink3",children:["최종 갱신 2026-07-29 · 실측 경기 43,509 ·"," ",e.jsx("a",{className:"text-signal",href:"https://github.com/choigod1023/proto-odds-research/tree/main/findings",children:"전체 문서"})]})]}),e.jsx(x,{className:"mt-4 flex flex-wrap gap-x-10 gap-y-3 px-4 py-3.5",children:[["수집 회차","553","2023–2026"],["베팅 레코드","353,047","게임행 177,549"],["프로토 환급률","88.0%","2-way 기준"],["해외 환급률","95.3%","7.3%p 차이"]].map(([s,l,t])=>e.jsxs("div",{children:[e.jsx("div",{className:"tick",children:s}),e.jsx("div",{className:"tnum text-[19px] font-semibold leading-[1.35]",children:l}),e.jsx("div",{className:"text-[11px] text-ink3",children:t})]},s))}),e.jsxs(x,{className:"mt-4 border-l-[3px] border-l-sev3 px-4 py-4",children:[e.jsx("b",{className:"text-[13.5px] text-sev3",children:"현재 운영 결론 — 기존 신호는 승격하지 않는다"}),e.jsxs("p",{className:"mt-1.5 mb-2.5 text-[13px] leading-[1.75] text-ink2",children:[e.jsx("b",{className:"text-ink",children:"현재 검증한 신호로는 시장을 이기지 못했다."})," 새 신호도 미래 구간에서 입증되기 전에는 운영에 넣지 않는다."]}),e.jsx("table",{className:"w-auto border-collapse text-[13px]",children:e.jsx("tbody",{children:[["필요한 우위","6.8%p (2-way 마진 12%)"],["정보의 크기","2.4%p — 샤프 마켓 자신의 24h 스윙"],["구조 선택의 이득","2.4%p — 규칙 7개 전부 누적"],["합쳐도","4.8%p < 6.8%p"]].map(([s,l],t)=>e.jsxs("tr",{children:[e.jsx("td",{className:"py-0.5 pr-4 text-ink3",children:s}),e.jsx("td",{className:`tnum py-0.5 ${t===3?"font-semibold text-ink":""}`,children:l})]},s))})}),e.jsxs("p",{className:"mt-2.5 mb-0 text-[13px] leading-[1.75] text-ink2",children:["문헌이 이걸 확증한다 — 장기 수익 베터는 약 3%, 프로가 효율 시장에서 목표하는 CLV 는"," ",e.jsx("b",{className:"text-ink",children:"+2~3%"}),", 라인이 무른 틈새시장에서도 ",e.jsx("b",{className:"text-ink",children:"+5%"})," 다."," ",e.jsx("b",{className:"text-ink",children:"프로토 마진 12% 는 세계 최고의 엣지를 그대로 가져와도 −7% 가 되는 크기다."})," ",'Aoki et al.(KDD 2017)도 "운이 상당해서 정교한 모델이 단순 모델을 거의 못 이긴다"고 한다.']})]}),e.jsx(N,{id:"ai-model",showLink:!1}),e.jsxs(x,{className:"mt-3 border-l-[3px] border-l-signal px-4 py-4",children:[e.jsx("b",{className:"text-[13.5px]",children:"AI가 확률을 바꿀 수 있는 유일한 경로"}),e.jsxs("p",{className:"mt-1.5 mb-2 text-[13px] leading-[1.75] text-ink2",children:["운영식은 ",e.jsx("b",{className:"tnum text-ink",children:"logit(p최종) = logit(p시장) + AI 잔차"}),"다. AI는 시장이 이미 아는 팀 전력 전체를 다시 예측하지 않고, 같은 시각 이후 새로 확인된 선발·결장·라인업 변화만 잔차로 학습한다. 현재 잔차는 검증 전이라 ",e.jsx("b",{className:"text-ink",children:"0%p"}),"다."]}),e.jsxs("ol",{className:"m-0 grid gap-2 p-0 text-[12px] leading-[1.65] text-ink2 sm:grid-cols-2",children:[e.jsxs("li",{className:"list-none border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:"1. 시각 고정"}),e.jsx("br",{}),"예측 시점에 실제로 보인 배당·자료만 저장한다."]}),e.jsxs("li",{className:"list-none border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:"2. 워크포워드"}),e.jsx("br",{}),"과거로 학습하고 그다음 경기만 예측한다."]}),e.jsxs("li",{className:"list-none border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:"3. 확률 검정"}),e.jsx("br",{}),"Brier·로그손실·보정도를 시장 기준선과 비교한다."]}),e.jsxs("li",{className:"list-none border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:"4. 승격 또는 0"}),e.jsx("br",{}),"미공개 기간 개선의 신뢰구간이 0을 넘을 때만 반영한다."]})]}),e.jsxs("p",{className:"mt-2.5 mb-0 text-[11px] leading-[1.7] text-ink3",children:["생성형 AI는 기사·공식 발표를 구조화하고 문장을 다듬을 뿐, 직접 확률을 쓰지 않는다. 선택적 예측으로 자료가 낡았거나 분포가 바뀐 경기는 보류하고, 고정된 커버리지에서 성능을 비교한다."," ",e.jsx("a",{className:"text-signal",href:"https://proceedings.mlr.press/v70/guo17a.html",children:"확률 보정"})," ·"," ",e.jsx("a",{className:"text-signal",href:"https://proceedings.mlr.press/v97/geifman19a.html",children:"선택적 예측"})," ·"," ",e.jsx("a",{className:"text-signal",href:"https://journals.sagepub.com/doi/10.1177/1471082X20929881",children:"선수·팀 정보 결합"})]})]}),e.jsxs(x,{id:"evolutionary-selector",className:"mt-3 scroll-mt-4 border-l-[3px] border-l-rule px-4 py-4",children:[e.jsx("b",{className:"text-[13.5px]",children:"자연선택 추천기 — 확률을 바꾸지 않고 한 픽을 고르는 AI"}),e.jsx("p",{className:"mt-1.5 mb-2 text-[13px] leading-[1.75] text-ink2",children:"전략 56개를 한 세대로 두고 24세대 동안 교배·돌연변이시킨다. 적중률만 좇아 초저배당으로 퇴화하지 않도록 유형별 최소배당·추천 빈도·종목 쏠림을 함께 생존 조건으로 둔다. 2023–2024에서 진화하고 2025에서 생존 전략을 고른 뒤 2026의 하루 한 픽으로 역사 감사했다."}),e.jsx("ol",{className:"mb-3 grid list-none gap-2 p-0 text-[11px] leading-[1.6] text-ink2 sm:grid-cols-4",children:[["01","후보 생성","서로 다른 가중치 전략 56개"],["02","진화","상위 전략 교배·돌연변이 24세대"],["03","생존 선택","2025 성능으로 최종 전략 선택"],["04","감사","2026 하루 한 픽으로 재검사"]].map(([s,l,t])=>e.jsxs("li",{className:"rounded border border-rule2 bg-panel px-2.5 py-2",children:[e.jsx("span",{className:"tnum text-ink3",children:s})," ",e.jsx("b",{className:"text-ink",children:l}),e.jsx("br",{}),t]},s))}),e.jsx("div",{className:"grid gap-2 text-[12px] sm:grid-cols-3",children:[["안정형","77.9% · 평균 1.21배","2025 +0.6 → 2026 −0.9%p","−6.1~+4.3%p"],["균형형","62.8% · 평균 1.45배","2025 −0.6 → 2026 +6.1%p","−1.7~+13.9%p"],["도전형","48.5% · 평균 1.75배","2025 +1.7 → 2026 −6.1%p","−14.3~+2.2%p"]].map(([s,l,t,r])=>e.jsxs("div",{className:"border-t border-rule2 pt-2",children:[e.jsx("b",{className:"text-ink",children:s}),e.jsx("br",{}),e.jsx("span",{className:"tnum text-ink2",children:l}),e.jsx("br",{}),e.jsxs("span",{className:"tnum text-sev3",children:["동일 배당범위 기준선 대비 ",t]}),e.jsx("br",{}),e.jsxs("span",{className:"tnum text-[10.5px] text-ink3",children:["95% CI ",r]})]},s))}),e.jsxs("div",{className:"mt-3 rounded border border-sev2 bg-panel px-3 py-2.5 text-[11.5px] leading-[1.7] text-ink2",children:[e.jsx("b",{className:"text-sev3",children:"운영 결정 · 세 유형 모두 추천 중단"}),e.jsx("br",{}),"2025와 2026의 개선 방향이 모두 뒤집혔다. 균형형의 2026 +6.1%p만 떼어 쓰지 않으며, 오늘 화면에서도 탈락 유형은 실제 후보를 내지 않는다."]}),e.jsxs("p",{className:"mt-2.5 mb-0 text-[11px] leading-[1.7] text-ink3",children:["초기 설계를 2026 감사 후 수정했으므로 이 수치는 독립 홀드아웃이 아니다. 사전등록 미래 300픽 이상에서 동일 날짜·동일 배당 범위 시장확률 1순위보다 적중률 차이의 95% 신뢰구간 하한이 0을 넘을 때만 다시 연다."," ",e.jsx("a",{className:"text-signal",href:"https://github.com/choigod1023/proto-odds-research/blob/main/findings/%EC%9E%90%EC%97%B0%EC%84%A0%ED%83%9D_%EC%B6%94%EC%B2%9C%EA%B8%B0_%EA%B2%80%EC%A6%9D.md",children:"재현 문서 보기 →"})]})]}),e.jsxs(x,{className:"mt-3 border-l-[3px] border-l-sev2 px-4 py-4",children:[e.jsx("b",{className:"text-[13.5px] text-sev2",children:"2026-07-29 정정 — 실제로 살 수 있는 값"}),e.jsxs("p",{className:"mt-1.5 mb-2.5 text-[13px] leading-[1.75] text-ink2",children:['이전까지 "아무거나 −13.7% → 등급 A 만 −9.2%, 4.5%p 절감" 이라고 적었다. 그런데 그건 ',e.jsx("b",{className:"text-ink",children:"선택지 1개 기준"}),"이고, 단폴(한경기구매)은 '한경기' 로 지정된 경기만 살 수 있다. 배당을 올리려면 조합해야 하고,",e.jsx("b",{className:"text-ink",children:" 조합하면 다리마다 마진이 한 번씩 더 물린다."})]}),e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full min-w-[380px] border-collapse text-[13px]",children:[e.jsx("thead",{children:e.jsx("tr",{children:["","1폴(지정 경기만)","2폴","3폴"].map(s=>e.jsx("th",{className:"border-b border-rule2 pb-1 pr-3 text-left text-[11px] font-medium text-ink3",children:s},s))})}),e.jsx("tbody",{children:[["아무거나","−13.6%","−25.3%","−35.5%"],["배당 1.0–1.3 · 2-way","−9.8%","−18.7%","−26.7%"],["절약폭","3.8%p","6.6%p","8.8%p"]].map(([s,l,t,r])=>e.jsxs("tr",{children:[e.jsx("td",{className:"border-b border-rule2 py-1 pr-3 text-ink2",children:s}),e.jsx("td",{className:"tnum border-b border-rule2 py-1 pr-3",children:l}),e.jsx("td",{className:"tnum border-b border-rule2 py-1 pr-3 font-semibold",children:t}),e.jsx("td",{className:"tnum border-b border-rule2 py-1 pr-3",children:r})]},s))})]})}),e.jsxs("p",{className:"mt-2.5 mb-0 text-[13px] leading-[1.75] text-ink2",children:["절약폭 자체는 오히려 커진다(4.5%p → 6.6%p). 절대 손실이 두 배가 될 뿐이다. 그리고 ",e.jsx("b",{className:"text-ink",children:"저배당 우선 규칙은 조합에서 뒤집힌다"})," — 배당 1.0–1.3 은 다리 하나로는 최선이지만 배당을 만드는 효율은 최악이라, 목표 배당이 있으면 다리당 배당을 올리는 쪽이 덜 잃는다."]})]}),e.jsx(n,{n:"01",children:"지금까지 만난 열두 개의 벽"}),e.jsxs(d,{children:["각 검증의 판정 기준은 ",e.jsx("b",{children:"데이터를 보기 전에"})," 정해뒀다. 나중에 정하면 어떤 결과든 성공으로 해석되기 때문이다."]}),e.jsx("ol",{className:"m-0 list-none p-0",children:g.map(([s,l,t,r],i)=>e.jsx("li",{children:e.jsxs(x,{className:"mt-2 flex flex-wrap items-center gap-3 px-3.5 py-3",children:[e.jsx("span",{className:"tnum w-6 shrink-0 text-[12px] text-ink3",children:i+1}),e.jsxs("span",{className:"min-w-[200px] flex-1",children:[e.jsx("span",{className:"block text-[13.5px] font-semibold",children:s}),e.jsx("span",{className:"block text-[12px] leading-[1.7] text-ink3",children:l})]}),e.jsx("span",{className:`tnum shrink-0 text-[12.5px] font-semibold ${r==="neg"?"text-sev3":"text-sev2"}`,children:t})]})},s))}),e.jsx(n,{n:"02",children:"확정된 사실"}),e.jsx(d,{children:"재현 가능하고, 코드가 공개돼 있다."}),e.jsxs(c,{children:["booking 은 상품이 아니라 ",e.jsx("b",{children:"구조"}),"로 결정된다"]}),e.jsx(m,{head:["구조","포함 상품","환급률","회차 내부 SD"],rows:[["2-way","승패·언더오버·2way핸디캡","88.00%","0.0006"],["3-way","승무패·승①패","87.00%","0.0007"],["3-way 핸디캡","—","85.99%","0.0008"]]}),e.jsxs(a,{children:["회차 ",e.jsx("b",{children:"내부"})," SD 는 0.0007 인데 회차 ",e.jsx("b",{children:"간"})," SD 는 0.0090 으로 ",e.jsx("b",{children:"13.5배"})," 다. 회차마다 목표 환급률이 86/87/88/89% 중 하나로 달라진다."]}),e.jsx(c,{children:"favorite-longshot 편향이 교과서적으로 존재한다"}),e.jsx(m,{head:["배당 구간","실측 ROI","기준선 대비"],rows:[["1.0–1.5 (강팀)","−9.90%","+2.50%p"],["1.8–2.2 (박빙)","−12.57%","−0.16%p"],["5.0+ (극단 역배)","−33.19%","−20.04%p"]]}),e.jsxs(a,{children:["미국 경마(Snowberg & Wolfers)와 ",e.jsx("b",{children:"같은 방향"}),'이다. "한국은 인기구단 쏠림으로 반대일 것"이라는 가설은 기각 방향.']}),e.jsx(c,{children:"승패보다 득실 마진이 정보량이 많다"}),e.jsxs(a,{children:["축구·농구·배구 ",e.jsx("b",{children:"세 종목 모두"})," '최근 득실 마진'이 '최근 승률'을 앞섰다. 같은 5승 5패라도 한 점 차로 이기고 크게 지는 팀은 다르다. 문헌(pi-ratings)을 읽고 넣은 게 아니라 ",e.jsx("b",{children:"측정에서 먼저 나왔다."})]}),e.jsx(c,{children:"선발투수는 지표를 바꾸자 결과가 뒤집혔다"}),e.jsx(m,{head:["지표","최대 z","최대 Brier 개선"],rows:[["팀 실점 (불펜 섞임)","2.58","+0.00204"],["투수 개인 자책점·이닝","4.53","+0.00613"]]}),e.jsxs(a,{children:[e.jsx("b",{children:'"선발투수는 소용없다"가 아니라 "지표가 거칠어서 안 잡혔다"'}),"였다. 흥미롭게도 자책률보다 ",e.jsx("b",{children:"평균 이닝"}),"이 더 강하다 — 얼마나 적게 내주느냐보다"," ",e.jsx("b",{children:"얼마나 길게 끌어주느냐"}),"가 승패에 직결된다."]}),e.jsx(n,{n:"03",children:"스스로 잡은 오류"}),e.jsx(d,{children:"연구 과정에서 낸 실수와 발견 경위. 같은 실수를 반복하지 않기 위해 남긴다."}),e.jsx("div",{className:"grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(280px,1fr))]",children:k.map(([s,l,t],r)=>e.jsxs(x,{className:"border-l-[3px] border-l-sev3 px-3.5 py-3",children:[e.jsxs("div",{className:"text-[13px] font-semibold",children:[r+1,". ",s]}),e.jsxs("p",{className:"mt-1.5 mb-1 text-[12.5px] leading-[1.7] text-ink2",children:[e.jsx("span",{className:"text-ink3",children:"발견"})," ",l]}),e.jsxs("p",{className:"m-0 text-[12.5px] leading-[1.7] text-ink2",children:[e.jsx("span",{className:"text-ink3",children:"영향"})," ",t]})]},s))}),e.jsxs("blockquote",{className:"mt-3 border-l-[3px] border-rule pl-3.5 text-[13px] leading-[1.8] text-ink2",children:[e.jsx("b",{className:"text-ink",children:'교훈: 결과가 "너무 좋으면" 버그다.'})," 물리적으로 불가능한 값 (양쪽 다 +EV, ROI +54%, +64%)이 매번 오류를 잡아냈다. 검증 장치를 먼저 만들어 둔 것이 실제로 작동했다. 지금은 자기검사 9종이 매 실행마다 돈다."]}),e.jsx(n,{n:"04",children:"지금 돌아가는 것"}),e.jsxs(d,{children:["남은 질문은 하나다 — ",e.jsx("b",{children:"배당이 굳은 뒤 공개되는 정보에 기회가 있는가."}),"과거 데이터로는 답할 수 없어 실시간으로 쌓는 중이다."]}),e.jsx(m,{head:["수집기","주기","무엇을"],rows:[["프로토 배당 스냅샷","15분","발매 중 배당이 실제로 변하는가"],["해외 배당 스냅샷","15분","같은 시점 해외 배당 (동시점 대조)"],["선발 정보 관측기","30분","KBO·MLB·NPB 선발 예고 시각"]]}),e.jsxs(x,{className:"mt-3 border-l-[3px] border-l-sev2 px-3.5 py-3",children:[e.jsx("div",{className:"text-[13px] font-semibold",children:"왜 실시간이어야 하나"}),e.jsxs("p",{className:"mt-1.5 mb-0 text-[12.5px] leading-[1.75] text-ink2",children:["과거 해외 배당은 ",e.jsx("b",{className:"text-ink",children:"마감 배당"}),"이다. 경기 상세의 기록 시각으로 확인했다 — 경기 약 7시간 전 값이다. 프로토는 최대 60시간 전에 굳으므로"," ",e.jsx("b",{className:"text-ink",children:"베팅 시점에 알 수 없는 값"}),"이고, 그걸로 계산한 +EV 에는 낙관 편향이 섞인다."]})]}),e.jsx(n,{n:"05",children:"현실 점검"}),e.jsxs(a,{children:["구매 한도가 ",e.jsx("b",{children:"회차당 10만원"}),"이다. 엣지 3%를 확보해도 회차당 3,000원, 연 150회차면 45만원이 구조적 천장이다. 그리고 그 3%를 실제로 확보하는 것 자체가 대부분 실패한다."]}),e.jsxs(a,{children:["열두 번 검증했고 ",e.jsx("b",{children:"실전에 쓸 수 있는 +EV 는 확인되지 않았다."}),'결과가 "시장은 효율적이다"로 나와도 그건 실패가 아니라 결론이다.']}),e.jsxs("footer",{className:"mt-9 border-t border-rule pt-4 text-[11.5px] leading-[1.85] text-ink3",children:[e.jsx("p",{className:"m-0",children:"데이터 출처 와이즈토토 회차 아카이브 · 네이버 스포츠 · BetExplorer · 비상업 연구 목적"}),e.jsx("p",{className:"m-0",children:"합법 발매처는 오프라인 판매점과 betman.co.kr 뿐이며, 해외 북메이커 이용은 국민체육진흥법 위반이다."}),e.jsx("p",{className:"m-0 mt-2",children:e.jsx("a",{className:"text-signal",href:"https://github.com/choigod1023/proto-odds-research",children:"소스·방법론·재현 코드"})})]}),e.jsx(h,{})]})}const n=({n:s,children:l})=>e.jsxs("h2",{className:"mt-9 mb-2 flex items-baseline gap-2.5 text-[16px] tracking-[-.01em]",children:[e.jsx("span",{className:"tnum text-[12px] font-normal text-ink3",children:s}),l]}),c=({children:s})=>e.jsx("h3",{className:"mt-6 mb-1.5 text-[13.5px] font-semibold",children:s}),d=({children:s})=>e.jsx("p",{className:"mt-0 mb-3 text-[13px] leading-[1.8] text-ink2",children:s}),a=({children:s})=>e.jsx("p",{className:"mt-2 mb-0 text-[13px] leading-[1.8] text-ink2",children:s});function m({head:s,rows:l}){return e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"mt-1.5 w-full min-w-[340px] border-collapse text-[12.5px]",children:[e.jsx("thead",{children:e.jsx("tr",{children:s.map((t,r)=>e.jsx("th",{className:`border-b border-rule pb-1.5 pr-3 text-[11px] font-medium text-ink3 ${r?"text-right":"text-left"}`,children:t},t))})}),e.jsx("tbody",{children:l.map(t=>e.jsx("tr",{children:t.map((r,i)=>e.jsx("td",{className:`border-b border-rule2 py-1.5 pr-3 ${i?"tnum text-right":""}`,children:r},i))},t[0]))})]})})}o();j.createRoot(document.getElementById("root")).render(e.jsx(b.StrictMode,{children:e.jsx(u,{})})); diff --git a/docs/assets/slip-lxUWQQD-.js b/docs/assets/slip-Defj7D6w.js similarity index 98% rename from docs/assets/slip-lxUWQQD-.js rename to docs/assets/slip-Defj7D6w.js index 4c2e3a89..2921be3d 100644 --- a/docs/assets/slip-lxUWQQD-.js +++ b/docs/assets/slip-Defj7D6w.js @@ -1 +1 @@ -import{r as p,j as e,N as j,C as h,T as N,i as g,c as y}from"./cache-guard-cBqh6aGv.js";import{b as k,o as S}from"./unified-recommendation-BCuqyY1U.js";import{u as f}from"./poll-DDJ49qcP.js";import{r as v}from"./live-odds-CIQqKAce.js";import"./decision-view-model-Bfii5tWW.js";const w=new Intl.DateTimeFormat("en-CA",{timeZone:"Asia/Seoul",year:"numeric",month:"2-digit",day:"2-digit"});function _(s,n=new Date){const o=String(s||"").match(/^(\d{2})\.(\d{2})/);if(!o)return!0;const[u,i,x]=w.format(n).split("-");let l=Number(u);const a=Number(o[1]),d=Number(i);ad+6&&(l-=1);const b=l*1e4+a*100+Number(o[2]),t=Number(u)*1e4+d*100+Number(x);return b>=t}const T=(s,n,o)=>!!s&&!!n&&String(s.round??o)===String(n.round??o)&&String(s.game_no??s.게임번호??"")===String(n.game_no??n.게임번호??"")&&s.market===n.market&&String(s.market_label??s.label??"")===String(n.market_label??n.label??"")&&String(s.sel??s.선택??"")===String(n.sel??n.선택??"");function C(s){return k(s?.candidates||[])}function D(s,n,o=new Date,u=null){const i=new Map,x=new Set((n?.rounds||[]).map(String));return(s||[]).forEach(l=>{if(x.size&&!x.has(String(l.round))||!_(l.date,o))return;const a=v(l,n?.odds?.[String(l.round)],n?.generated_at||null,n?.markets?.[String(l.round)]),d=a.추천||l.추천||null,b=t=>Array.isArray(u)?u.some(c=>T(c,t,a.round)):!!d&&(d.selection_id&&t.selection_id===d.selection_id||d.게임번호&&String(t.게임번호)===String(d.게임번호)&&t.market===d.market&&(t.label||"")===(d.label||"")&&t.선택===d.선택);(a.options||[]).forEach(t=>{const c=String(t.게임번호||"").trim();if(!c)return;const r=`${a.round}|${c}|${t.market}|${t.label||""}`;i.has(r)||i.set(r,{key:r,round:a.round,number:c,date:a.date,home:a.home,away:a.away,market:t.market,label:t.label||"",selectionsByName:new Map}),i.get(r).selectionsByName.set(t.선택,{name:t.선택,value:Number(t.배당),live:t._live===!0,recommended:b(t)})})}),[...i.values()].map(({selectionsByName:l,...a})=>({...a,selections:[...l.values()]})).sort((l,a)=>Number(l.round)-Number(a.round)||Number(l.number)-Number(a.number))}const R="https://proto-odds-collector.fly.dev/api/live-odds",$="https://proto-odds-collector.fly.dev/api/picks",E="https://proto-odds-collector.fly.dev/api/today-recommendations",A=s=>{const n=new Date(s);return Number.isFinite(n.getTime())?new Intl.DateTimeFormat("ko-KR",{timeZone:"Asia/Seoul",dateStyle:"short",timeStyle:"short"}).format(n):"-"};function K(){const{data:s,at:n}=f({picks:$,liveOdds:R,today:E},6e4),[o,u]=p.useState("all"),[i,x]=p.useState(""),l=s.picks,a=p.useMemo(()=>C(s.today),[s.today]),d=p.useMemo(()=>D(l?.live,s.liveOdds,void 0,a),[l,s.liveOdds,a]),b=[...new Set(d.map(r=>String(r.round)))],t=i.trim().toLowerCase(),c=d.filter(r=>(o==="all"||String(r.round)===o)&&(!t||`${r.number} ${r.home} ${r.away} ${r.market} ${r.label}`.toLowerCase().includes(t)));return e.jsxs("div",{className:"mx-auto max-w-[1180px] px-5 pb-20",children:[e.jsx(j,{current:"slip.html"}),e.jsxs("header",{className:"market-header",children:[e.jsxs("div",{children:[e.jsx("h1",{children:"프로토 번호·배당표"}),e.jsx("p",{children:"승부식 용지에 옮길 게임번호와 현재 배당, 추천 선택을 한 표에서 대조합니다."})]}),e.jsxs("div",{className:"market-meta",children:[d.length,"개 게임번호 · 배당 갱신 ",A(s.liveOdds?.generated_at)," KST"]})]}),e.jsxs(h,{className:"mb-3 flex flex-wrap items-end gap-3 p-4 print:hidden",children:[e.jsxs("label",{className:"text-[12px] text-ink3",children:["회차",e.jsxs("select",{className:"mt-1 block rounded-md border border-rule bg-panel px-3 py-2 text-ink",value:o,onChange:r=>u(r.target.value),children:[e.jsx("option",{value:"all",children:"전체 회차"}),b.map(r=>e.jsxs("option",{value:r,children:[r,"회"]},r))]})]}),e.jsxs("label",{className:"min-w-[220px] flex-1 text-[12px] text-ink3",children:["번호·팀·마켓 검색",e.jsx("input",{className:"mt-1 block w-full rounded-md border border-rule bg-panel px-3 py-2 text-ink",value:i,onChange:r=>x(r.target.value),placeholder:"예: 102, 두산, 승무패"})]}),e.jsx("button",{type:"button",className:"rounded-md border border-rule bg-panel px-4 py-2 text-[13px] text-ink",onClick:()=>window.print(),children:"인쇄"})]}),e.jsx(h,{className:"overflow-x-auto p-4",children:n?c.length===0?e.jsx("p",{className:"py-7 text-center text-ink3",children:"조건에 맞는 발매 경기 배당이 없습니다."}):e.jsxs("table",{className:"w-full min-w-[760px] border-collapse text-[12.5px]",children:[e.jsx("caption",{className:"sr-only",children:"프로토 승부식 게임번호와 최신 배당"}),e.jsx("thead",{children:e.jsxs("tr",{className:"text-left text-[11px] text-ink3",children:[e.jsx("th",{className:"border-b border-rule2 p-2 text-right",children:"번호"}),e.jsx("th",{className:"border-b border-rule2 p-2",children:"회차·시간"}),e.jsx("th",{className:"border-b border-rule2 p-2",children:"경기"}),e.jsx("th",{className:"border-b border-rule2 p-2",children:"마켓"}),e.jsx("th",{className:"border-b border-rule2 p-2",children:"선택·배당"})]})}),e.jsx("tbody",{children:c.map(r=>e.jsxs("tr",{className:r.selections.some(m=>m.recommended)?"bg-signal/5":"",children:[e.jsx("td",{className:"tnum border-b border-rule2 p-2 text-right text-[16px] font-semibold",children:r.number}),e.jsxs("td",{className:"border-b border-rule2 p-2 whitespace-nowrap",children:[e.jsxs("b",{children:[r.round,"회"]}),e.jsx("br",{}),e.jsx("span",{className:"text-ink3",children:r.date})]}),e.jsxs("td",{className:"border-b border-rule2 p-2",children:[e.jsx("b",{children:r.home}),e.jsx("span",{className:"mx-1.5 text-ink3",children:"vs"}),e.jsx("b",{children:r.away})]}),e.jsxs("td",{className:"border-b border-rule2 p-2 whitespace-nowrap",children:[r.market,r.label?e.jsxs(e.Fragment,{children:[e.jsx("br",{}),e.jsx("span",{className:"text-ink3",children:r.label})]}):null]}),e.jsx("td",{className:"border-b border-rule2 p-2",children:e.jsx("div",{className:"flex flex-wrap gap-2",children:r.selections.map(m=>e.jsxs("span",{className:`inline-flex min-w-[92px] justify-between gap-3 rounded-md border px-2.5 py-1.5 ${m.recommended?"border-signal bg-signal text-white ring-2 ring-signal/20":"border-rule"}`,title:m.recommended?"오늘의 베팅 추천 선택":void 0,children:[e.jsxs("span",{children:[m.recommended?"★ ":"",m.name]}),e.jsx("b",{className:"tnum",children:S(m.value)})]},m.name))})})]},r.key))})]}):e.jsx("p",{className:"py-7 text-center text-ink3",children:"불러오는 중…"})}),e.jsx("p",{className:"mt-3 text-[11px] text-ink3",children:"승부식 전용 · 실제 구매 전 지류와 판매점 단말의 번호·배당을 마지막으로 대조하세요."}),e.jsx(N,{})]})}g();y.createRoot(document.getElementById("root")).render(e.jsx(p.StrictMode,{children:e.jsx(K,{})})); +import{r as p,j as e,N as j,C as h,T as N,i as g,c as y}from"./cache-guard-BQcATxSH.js";import{b as k,o as S}from"./unified-recommendation-BCuqyY1U.js";import{u as f}from"./poll-QCTlueAT.js";import{r as v}from"./live-odds-CIQqKAce.js";import"./decision-view-model-Bfii5tWW.js";const w=new Intl.DateTimeFormat("en-CA",{timeZone:"Asia/Seoul",year:"numeric",month:"2-digit",day:"2-digit"});function _(s,n=new Date){const o=String(s||"").match(/^(\d{2})\.(\d{2})/);if(!o)return!0;const[u,i,x]=w.format(n).split("-");let l=Number(u);const a=Number(o[1]),d=Number(i);ad+6&&(l-=1);const b=l*1e4+a*100+Number(o[2]),t=Number(u)*1e4+d*100+Number(x);return b>=t}const T=(s,n,o)=>!!s&&!!n&&String(s.round??o)===String(n.round??o)&&String(s.game_no??s.게임번호??"")===String(n.game_no??n.게임번호??"")&&s.market===n.market&&String(s.market_label??s.label??"")===String(n.market_label??n.label??"")&&String(s.sel??s.선택??"")===String(n.sel??n.선택??"");function C(s){return k(s?.candidates||[])}function D(s,n,o=new Date,u=null){const i=new Map,x=new Set((n?.rounds||[]).map(String));return(s||[]).forEach(l=>{if(x.size&&!x.has(String(l.round))||!_(l.date,o))return;const a=v(l,n?.odds?.[String(l.round)],n?.generated_at||null,n?.markets?.[String(l.round)]),d=a.추천||l.추천||null,b=t=>Array.isArray(u)?u.some(c=>T(c,t,a.round)):!!d&&(d.selection_id&&t.selection_id===d.selection_id||d.게임번호&&String(t.게임번호)===String(d.게임번호)&&t.market===d.market&&(t.label||"")===(d.label||"")&&t.선택===d.선택);(a.options||[]).forEach(t=>{const c=String(t.게임번호||"").trim();if(!c)return;const r=`${a.round}|${c}|${t.market}|${t.label||""}`;i.has(r)||i.set(r,{key:r,round:a.round,number:c,date:a.date,home:a.home,away:a.away,market:t.market,label:t.label||"",selectionsByName:new Map}),i.get(r).selectionsByName.set(t.선택,{name:t.선택,value:Number(t.배당),live:t._live===!0,recommended:b(t)})})}),[...i.values()].map(({selectionsByName:l,...a})=>({...a,selections:[...l.values()]})).sort((l,a)=>Number(l.round)-Number(a.round)||Number(l.number)-Number(a.number))}const R="https://proto-odds-collector.fly.dev/api/live-odds",$="https://proto-odds-collector.fly.dev/api/picks",E="https://proto-odds-collector.fly.dev/api/today-recommendations",A=s=>{const n=new Date(s);return Number.isFinite(n.getTime())?new Intl.DateTimeFormat("ko-KR",{timeZone:"Asia/Seoul",dateStyle:"short",timeStyle:"short"}).format(n):"-"};function K(){const{data:s,at:n}=f({picks:$,liveOdds:R,today:E},6e4),[o,u]=p.useState("all"),[i,x]=p.useState(""),l=s.picks,a=p.useMemo(()=>C(s.today),[s.today]),d=p.useMemo(()=>D(l?.live,s.liveOdds,void 0,a),[l,s.liveOdds,a]),b=[...new Set(d.map(r=>String(r.round)))],t=i.trim().toLowerCase(),c=d.filter(r=>(o==="all"||String(r.round)===o)&&(!t||`${r.number} ${r.home} ${r.away} ${r.market} ${r.label}`.toLowerCase().includes(t)));return e.jsxs("div",{className:"mx-auto max-w-[1180px] px-5 pb-20",children:[e.jsx(j,{current:"slip.html"}),e.jsxs("header",{className:"market-header",children:[e.jsxs("div",{children:[e.jsx("h1",{children:"프로토 번호·배당표"}),e.jsx("p",{children:"승부식 용지에 옮길 게임번호와 현재 배당, 추천 선택을 한 표에서 대조합니다."})]}),e.jsxs("div",{className:"market-meta",children:[d.length,"개 게임번호 · 배당 갱신 ",A(s.liveOdds?.generated_at)," KST"]})]}),e.jsxs(h,{className:"mb-3 flex flex-wrap items-end gap-3 p-4 print:hidden",children:[e.jsxs("label",{className:"text-[12px] text-ink3",children:["회차",e.jsxs("select",{className:"mt-1 block rounded-md border border-rule bg-panel px-3 py-2 text-ink",value:o,onChange:r=>u(r.target.value),children:[e.jsx("option",{value:"all",children:"전체 회차"}),b.map(r=>e.jsxs("option",{value:r,children:[r,"회"]},r))]})]}),e.jsxs("label",{className:"min-w-[220px] flex-1 text-[12px] text-ink3",children:["번호·팀·마켓 검색",e.jsx("input",{className:"mt-1 block w-full rounded-md border border-rule bg-panel px-3 py-2 text-ink",value:i,onChange:r=>x(r.target.value),placeholder:"예: 102, 두산, 승무패"})]}),e.jsx("button",{type:"button",className:"rounded-md border border-rule bg-panel px-4 py-2 text-[13px] text-ink",onClick:()=>window.print(),children:"인쇄"})]}),e.jsx(h,{className:"overflow-x-auto p-4",children:n?c.length===0?e.jsx("p",{className:"py-7 text-center text-ink3",children:"조건에 맞는 발매 경기 배당이 없습니다."}):e.jsxs("table",{className:"w-full min-w-[760px] border-collapse text-[12.5px]",children:[e.jsx("caption",{className:"sr-only",children:"프로토 승부식 게임번호와 최신 배당"}),e.jsx("thead",{children:e.jsxs("tr",{className:"text-left text-[11px] text-ink3",children:[e.jsx("th",{className:"border-b border-rule2 p-2 text-right",children:"번호"}),e.jsx("th",{className:"border-b border-rule2 p-2",children:"회차·시간"}),e.jsx("th",{className:"border-b border-rule2 p-2",children:"경기"}),e.jsx("th",{className:"border-b border-rule2 p-2",children:"마켓"}),e.jsx("th",{className:"border-b border-rule2 p-2",children:"선택·배당"})]})}),e.jsx("tbody",{children:c.map(r=>e.jsxs("tr",{className:r.selections.some(m=>m.recommended)?"bg-signal/5":"",children:[e.jsx("td",{className:"tnum border-b border-rule2 p-2 text-right text-[16px] font-semibold",children:r.number}),e.jsxs("td",{className:"border-b border-rule2 p-2 whitespace-nowrap",children:[e.jsxs("b",{children:[r.round,"회"]}),e.jsx("br",{}),e.jsx("span",{className:"text-ink3",children:r.date})]}),e.jsxs("td",{className:"border-b border-rule2 p-2",children:[e.jsx("b",{children:r.home}),e.jsx("span",{className:"mx-1.5 text-ink3",children:"vs"}),e.jsx("b",{children:r.away})]}),e.jsxs("td",{className:"border-b border-rule2 p-2 whitespace-nowrap",children:[r.market,r.label?e.jsxs(e.Fragment,{children:[e.jsx("br",{}),e.jsx("span",{className:"text-ink3",children:r.label})]}):null]}),e.jsx("td",{className:"border-b border-rule2 p-2",children:e.jsx("div",{className:"flex flex-wrap gap-2",children:r.selections.map(m=>e.jsxs("span",{className:`inline-flex min-w-[92px] justify-between gap-3 rounded-md border px-2.5 py-1.5 ${m.recommended?"border-signal bg-signal text-white ring-2 ring-signal/20":"border-rule"}`,title:m.recommended?"오늘의 베팅 추천 선택":void 0,children:[e.jsxs("span",{children:[m.recommended?"★ ":"",m.name]}),e.jsx("b",{className:"tnum",children:S(m.value)})]},m.name))})})]},r.key))})]}):e.jsx("p",{className:"py-7 text-center text-ink3",children:"불러오는 중…"})}),e.jsx("p",{className:"mt-3 text-[11px] text-ink3",children:"승부식 전용 · 실제 구매 전 지류와 판매점 단말의 번호·배당을 마지막으로 대조하세요."}),e.jsx(N,{})]})}g();y.createRoot(document.getElementById("root")).render(e.jsx(p.StrictMode,{children:e.jsx(K,{})})); diff --git a/docs/dashboard.html b/docs/dashboard.html index af4678dc..83dbd937 100644 --- a/docs/dashboard.html +++ b/docs/dashboard.html @@ -9,13 +9,13 @@ - - + + - + - +
diff --git a/docs/index.html b/docs/index.html index dfc4e1c2..99694526 100644 --- a/docs/index.html +++ b/docs/index.html @@ -9,16 +9,16 @@ - - + + - + - - - + + +
diff --git a/docs/lotto.html b/docs/lotto.html index 5d9f966c..5ff63eb3 100644 --- a/docs/lotto.html +++ b/docs/lotto.html @@ -9,10 +9,10 @@ - - - - + + + +
diff --git a/docs/markets.html b/docs/markets.html index 04c9046c..cbd71da4 100644 --- a/docs/markets.html +++ b/docs/markets.html @@ -11,16 +11,16 @@ - - + + - + - - - + + +
diff --git a/docs/research.html b/docs/research.html index 071f65f1..55001a0a 100644 --- a/docs/research.html +++ b/docs/research.html @@ -9,11 +9,11 @@ - - + + - - + +
diff --git a/docs/slip.html b/docs/slip.html index 0104268c..b63fc849 100644 --- a/docs/slip.html +++ b/docs/slip.html @@ -9,13 +9,13 @@ - - + + - + - +
diff --git a/web/src/components/LiveProgress.jsx b/web/src/components/LiveProgress.jsx new file mode 100644 index 00000000..615786f2 --- /dev/null +++ b/web/src/components/LiveProgress.jsx @@ -0,0 +1,20 @@ +import { liveMatchProgress } from "../lib/live-progress.js"; + +export function LiveProgress({ game, live = game?._liveState, now = Date.now(), compact = false }) { + const progress = liveMatchProgress(game, live, now); + if (!progress || progress.state === "finished") return null; + const known = Number.isFinite(progress.percent); + const description = `${progress.label} · ${progress.basis}`; + return + + 경기 진행{known ? ` 약 ${progress.percent}%` : " · 비율 확인 불가"} + {progress.label} + + {known && + + } + {progress.basis} + ; +} diff --git a/web/src/components/TodayPicksBoard.jsx b/web/src/components/TodayPicksBoard.jsx index 4110aa57..5feebbda 100644 --- a/web/src/components/TodayPicksBoard.jsx +++ b/web/src/components/TodayPicksBoard.jsx @@ -1,6 +1,7 @@ import { Card } from "./ui.jsx"; import { PHASE_LABEL } from "../lib/match-status.js"; import { trackTodayPicks } from "../lib/today-pick-tracking.js"; +import { LiveProgress } from "./LiveProgress.jsx"; const percent = (value) => Number.isFinite(value) ? `${(value * 100).toFixed(1)}%` : "기록 없음"; const SOURCE_LABEL = { highlight: "오늘 추천 · 사전 기록", recorded: "사전 픽 · 추천 이력 미확인", @@ -18,16 +19,16 @@ export function TodayPicksBoard({ games = [], today = null, currentToday = null,

오늘 추천과 저장된 사전 픽을 함께 추적합니다. 추천 이력 미확인 픽은 구분해 표시합니다.

{!picks.length && 오늘 확인된 추천·사전 픽이 없습니다.} - + {finished.length > 0 &&
종료된 사전 픽 {finished.length}개 · 적중 {finished.filter((pick) => pick.outcome.state === "hit").length} · 적중실패 {finished.filter((pick) => pick.outcome.state === "miss").length} · 결과와 당시 배당 보기 -
+
} ); } -function PickCards({ picks }) { +function PickCards({ picks, now }) { return
{picks.map((pick) => ( @@ -40,6 +41,7 @@ function PickCards({ picks }) { {pick.game._liveState?.home_score ?? "—"} : {pick.game._liveState?.away_score ?? "—"} {pick.game._liveState?.status_text || "진행 중"}

} + {pick.phase !== "finished" && }

{SOURCE_LABEL[pick.source]}

{pick.option.market}{pick.option.label ? ` ${pick.option.label}` : ""} · {pick.option.선택}

diff --git a/web/src/components/TodayPicksBoard.test.mjs b/web/src/components/TodayPicksBoard.test.mjs index 63e69007..5964dd8b 100644 --- a/web/src/components/TodayPicksBoard.test.mjs +++ b/web/src/components/TodayPicksBoard.test.mjs @@ -10,7 +10,7 @@ test("board SSR shows original NPB picks, evolving estimate and final result abo const server = await createServer({ configFile: false, root: fileURLToPath(new URL("../../", import.meta.url)), esbuild: { jsx: "automatic" }, optimizeDeps: { noDiscovery: true, include: [] }, - server: { middlewareMode: true, watch: null }, appType: "custom" }); + server: { middlewareMode: true, watch: null, hmr: false }, appType: "custom" }); try { const { TodayPicksBoard } = await server.ssrLoadModule("/src/components/TodayPicksBoard.jsx"); const base = { year: 2026, date: "09.05(토) 14:00", round: 105, @@ -32,6 +32,8 @@ test("board SSR shows original NPB picks, evolving estimate and final result abo assert.match(html, /1.52배/); assert.match(html, /사전 픽 · 추천 이력 미확인/); assert.match(html, /현재 추정/); + assert.match(html, /경기 진행 약 55%/); + assert.equal((html.match(/role="progressbar"/g) || []).length, 2); assert.match(html, /grid-cols-1.*sm:grid-cols-2.*xl:grid-cols-3/); assert.doesNotMatch(html, /NaN|55% 기준|60% 기준/); const ended = render([{ ...base, _liveState: { ...base._liveState, finished: true } }]); @@ -39,13 +41,17 @@ test("board SSR shows original NPB picks, evolving estimate and final result abo assert.match(ended, /56.1%/); assert.ok(ended.indexOf("적중") < ended.lastIndexOf("당시 배당")); assert.match(ended, /1.59배/); + assert.doesNotMatch(ended, /role="progressbar"/); const stale = render([{ ...base, _liveFeedAt: "2026-09-05T05:00:00Z" }]); assert.match(stale, /중계 갱신이 늦어/); assert.match(stale, /56.1%/); + assert.match(stale, /중계 갱신 지연/); + assert.doesNotMatch(stale, /role="progressbar"/); const unsupported = render([{ ...base, prediction_record: { ...base.prediction_record, market: "핸디캡", label: "H -1.5", selection: "핸디홈" } }]); assert.match(unsupported, /지원하지 않습니다/); assert.match(unsupported, /핸디홈/); + assert.match(unsupported, /경기 진행 약 55%/); const missing = render([{ ...base, prediction_record: null }]); assert.match(missing, /오늘 확인된 추천·사전 픽이 없습니다/); assert.doesNotMatch(missing, /56.1%|1.59배/); diff --git a/web/src/editorial.css b/web/src/editorial.css index d556c82a..448d6a7e 100644 --- a/web/src/editorial.css +++ b/web/src/editorial.css @@ -156,6 +156,15 @@ body { word-break: keep-all; overflow-wrap: break-word; font-family: "Pretendard .settled-result-panel.is-miss > div:last-of-type strong { color: var(--color-sev2); } .settled-result-panel p { grid-column: 1 / -1; margin: 0; color: var(--color-ink3); font-size: 10.5px; } @keyframes live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } } +.match-progress { display: block; min-width: 0; margin: 8px 0 12px; color: var(--color-ink2); font-size: 11px; font-weight: 500; } +.match-progress-caption { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 3px 10px; } +.match-progress-track { display: block; height: 5px; margin-top: 5px; overflow: hidden; border-radius: 3px; background: var(--color-rule2); } +.match-progress-track > span { display: block; height: 100%; background: var(--color-ink); transition: width .3s ease; } +.match-progress-note { display: block; margin-top: 4px; color: var(--color-ink3); font-size: 10px; font-weight: 400; } +.match-progress.is-compact { max-width: 300px; margin: 6px 0 0; font-size: 10px; } +.match-progress.is-compact .match-progress-track { height: 4px; } +.match-progress.is-stale, .match-progress.is-interrupted { color: var(--color-sev2); } +@media (prefers-reduced-motion: reduce) { .match-progress-track > span { transition: none; } } .match-detail { padding: 22px 18px 24px !important; background: var(--color-panel); } .match-detail > * + * { margin-top: 16px; } diff --git a/web/src/lib/live-progress.js b/web/src/lib/live-progress.js new file mode 100644 index 00000000..24c48a0b --- /dev/null +++ b/web/src/lib/live-progress.js @@ -0,0 +1,57 @@ +const numeric = (value) => typeof value === "number" || (typeof value === "string" && value.trim()) + ? Number(value) : NaN; +const TERMINAL = new Set(["RESULT", "END", "ENDED", "FINAL", "FINISHED"]); + +/** Display-only match progress. Never infer it from a pick, score, or wall-clock since kickoff. */ +export function liveMatchProgress(game, live = game?._liveState, now = Date.now()) { + if (!live) return null; + const status = String(live.status || "").toUpperCase(); + const label = live.cancelled ? "경기 취소" : live.postponed ? "경기 연기" : String(live.status_text || "진행 중"); + const interrupted = live.cancelled || live.postponed || /CANCEL|POSTPONE|SUSPEND|DELAY/.test(status) + || /취소|연기|중단|우천|지연/.test(label); + if (!interrupted && (live.finished || TERMINAL.has(status) || game?.status === "정산")) { + return { state: "finished", percent: 100, label: "경기 종료", basis: "종료 확인" }; + } + if (!interrupted && !["STARTED", "IN_PROGRESS", "LIVE", "PLAYING"].includes(status)) return null; + const observed = Date.parse(live.observed_at || game?._liveFeedAt || ""); + if (!Number.isFinite(observed) || observed > Number(now) + 5000) { + return { state: "unknown", percent: null, label, basis: "중계 시각 확인 중" }; + } + if (live.stale === true || Number(now) - observed > 10 * 60 * 1000) { + return { state: "stale", percent: null, label, basis: "중계 갱신 지연 · 마지막 진행 정보" }; + } + if (interrupted) return { state: "interrupted", percent: null, label, basis: "경기 중단 · 진행률 보류" }; + const result = (fraction, basis, stage = label) => ({ state: "live", + percent: Math.min(99, Math.max(0, Math.floor(fraction * 100))), label: stage, basis }); + const unknown = (basis = "진행률 산정 정보 부족") => ({ state: "unknown", percent: null, label, basis }); + if (game?.sport === "bs") { + // Both collectors normalize inning/half together in this label. Independently + // merged inning/batting_side/outs fields may describe different observations. + const match = label.match(/(\d+)\s*회\s*(초|말)/); + const inning = match ? Number(match[1]) : NaN; + const half = match?.[2]; + if (!Number.isInteger(inning) || inning < 1 || !half) return unknown(); + if (inning > 9) return { state: "live", percent: null, label: `연장 ${inning}회${half}`, + basis: "정규 9회 경과 · 종료 시점 미정" }; + return result((inning - 1 + (half === "말" ? .5 : 0)) / 9, + "정규 9회 기준 · 이닝 단위 추정", `${inning}회${half}`); + } + if (game?.sport === "sc") { + if (/연장|승부차기/.test(label) || numeric(live.clock?.period) > 2) { + return { state: "live", percent: null, label, basis: "정규시간 경과 · 종료 시점 미정" }; + } + if (live.clock?.phase === "halftime" || /하프타임|전반 종료|전반종료/.test(label)) { + return result(.5, "정규 90분 기준 · 추가시간 제외", "하프타임"); + } + const minute = numeric(live.clock?.elapsed_minute); + if (!Number.isFinite(minute) || minute < 0) return unknown(); + return result(minute / 90, minute >= 90 ? "정규시간 경과 · 종료 미확정" : "정규 90분 기준 · 추가시간 제외"); + } + if (game?.sport === "bk") { + if (/연장|\bOT\b/i.test(label)) return { state: "live", percent: null, label, + basis: "연장 진행 · 종료 시점 미정" }; + const quarter = Number(label.match(/([1-4])\s*(?:쿼터|Q)/i)?.[1]); + if (quarter) return result((quarter - 1) / 4, "정규 4쿼터 기준 · 쿼터 단위 추정"); + } + return unknown(game?.sport === "vl" ? "세트 경기 · 종료까지의 비율은 산정하지 않음" : undefined); +} diff --git a/web/src/lib/live-progress.test.mjs b/web/src/lib/live-progress.test.mjs new file mode 100644 index 00000000..1ca0deec --- /dev/null +++ b/web/src/lib/live-progress.test.mjs @@ -0,0 +1,70 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { liveMatchProgress } from "./live-progress.js"; + +const now = Date.parse("2026-09-05T08:00:00Z"); +const live = { status: "STARTED", observed_at: new Date(now).toISOString() }; +const progress = (sport, extra = {}, game = {}) => liveMatchProgress({ sport, ...game }, { ...live, ...extra }, now); + +test("baseball progress uses completed half innings, independent of picks and scores", () => { + for (const [status_text, percent] of [["1회초", 0], ["1회말", 5], ["6회초", 55], + ["6회말", 61], ["9회초", 88], ["9회말", 94]]) { + assert.equal(progress("bs", { status_text }).percent, percent); + } + assert.equal(progress("bs", { inning: 6, batting_side: "away" }).percent, null); + const mixed = progress("bs", { status_text: "7회", inning: 6, batting_side: "home", + situation_observed_at: "2026-09-05T07:00:00Z" }); + assert.equal(mixed.percent, null); + assert.equal(mixed.label, "7회"); + assert.equal(progress("bs", { status_text: "6회초", inning: 8, batting_side: "home", outs: 2 }).percent, 55); + for (const status_text of ["진행 중", "6회", "0회초"]) assert.equal(progress("bs", { status_text }).percent, null); + assert.match(progress("bs", { status_text: "10회초" }).label, /연장/); + assert.equal(progress("bs", { status_text: "10회초" }).percent, null); +}); + +test("soccer clock is cumulative; unknown, halftime and extra time stay distinct", () => { + for (const [minute, percent] of [[0, 0], [45, 50], [83, 92], [90, 99], [95, 99]]) { + assert.equal(progress("sc", { clock: { elapsed_minute: minute } }).percent, percent); + } + for (const minute of [null, undefined, "", " ", true, -1, NaN, Infinity]) { + assert.equal(progress("sc", { clock: { elapsed_minute: minute } }).percent, null); + } + assert.equal(progress("sc", { status_text: "하프타임" }).percent, 50); + assert.equal(progress("sc", { clock: { period: 3, elapsed_minute: 100 }, status_text: "연장 전반" }).percent, null); +}); + +test("quarters provide a coarse stage only; variable-length sets and unknown phases never imply 50%", () => { + assert.equal(progress("bk", { status_text: "1쿼터" }).percent, 0); + assert.equal(progress("bk", { status_text: "3Q 05:00" }).percent, 50); + assert.equal(progress("bk", { status_text: "연장" }).percent, null); + assert.equal(progress("vl", { status_text: "3세트" }).percent, null); + assert.equal(progress("bk", { status_text: "진행 중" }).percent, null); +}); + +test("finished and disrupted matches override retained clocks and innings", () => { + for (const status of ["RESULT", "END", "ENDED", "FINAL", "FINISHED"]) { + assert.equal(progress("bs", { status, status_text: "8회초" }).percent, 100); + } + assert.equal(progress("sc", { finished: true, clock: { elapsed_minute: 83 } }).percent, 100); + assert.equal(progress("bs", { status: "BEFORE", status_text: "1회초" }), null); + assert.equal(progress("bs", { status: "UNKNOWN" }), null); + for (const extra of [{ cancelled: true }, { postponed: true }, { status_text: "우천 중단" }]) { + assert.equal(progress("bs", { status_text: "6회초", ...extra }).state, "interrupted"); + assert.equal(progress("bs", { status_text: "6회초", ...extra }).percent, null); + } +}); + +test("freshness uses each row, not a newly published document or wall-clock since kickoff", () => { + const status_text = "6회초"; + const at = (age) => new Date(now - age).toISOString(); + assert.equal(progress("bs", { status_text, observed_at: at(600000) }).percent, 55); + assert.equal(progress("bs", { status_text, observed_at: at(600001) }, { _liveFeedAt: at(0) }).state, "stale"); + assert.equal(progress("bs", { status_text, stale: true }).percent, null); + assert.equal(progress("bs", { status_text, observed_at: null }).percent, null); + assert.equal(progress("bs", { status_text, observed_at: "invalid" }).percent, null); + assert.equal(progress("bs", { status_text, observed_at: at(-5001) }).percent, null); + assert.equal(progress("bs", { status_text, observed_at: null }, { _liveFeedAt: at(0) }).percent, 55); + assert.equal(progress("bs", { status_text: "우천 중단", observed_at: at(600001) }).state, "stale"); + assert.equal(progress("bs", { status_text: "우천 중단", observed_at: "invalid" }).state, "unknown"); + assert.equal(progress("bs", { status_text: "우천 중단", stale: true }).state, "stale"); +}); diff --git a/web/src/pages/Markets.jsx b/web/src/pages/Markets.jsx index a9d4c6a6..9663f344 100644 --- a/web/src/pages/Markets.jsx +++ b/web/src/pages/Markets.jsx @@ -24,6 +24,7 @@ import { commentaryMethod, directPickReason } from "../lib/recommendation.js"; import { compactTeamPlayerLine } from "../lib/team-preview.js"; import { deduplicateGameCards } from "../lib/game-dedup.js"; import { matchesGameMarketFilters } from "../lib/game-list-filter.js"; +import { LiveProgress } from "../components/LiveProgress.jsx"; // 모든 동적 상태는 수집 머신의 SQLite-backed API에서 직접 받는다. const LIVE_URL = "https://proto-odds-collector.fly.dev/api/live-scores"; @@ -726,6 +727,7 @@ export function Game({ g, opts, wait, grades, lv, stale, generatedAt, year, toda ? ` · ${compactPlayers}` : ""} + {!finished && } {phase === "finished" ? "예측 결과" : playing ? "실시간 경기" : phase === "pending" ? "사전 픽" : todayLabel ? "오늘 추천 픽" : "경기별 픽"} @@ -765,6 +767,7 @@ export function Game({ g, opts, wait, grades, lv, stale, generatedAt, year, toda
+ {!finished && } {locked && saved && !finished && (
경기 전 예측 픽 · {saved.option.market} {saved.option.label} {saved.option.선택} diff --git a/web/src/pages/live-pick-display.test.mjs b/web/src/pages/live-pick-display.test.mjs index 3127eace..9aed410c 100644 --- a/web/src/pages/live-pick-display.test.mjs +++ b/web/src/pages/live-pick-display.test.mjs @@ -12,7 +12,7 @@ test("live card renders the saved pick and prior even with no current options", const server = await createServer({ configFile: false, root: fileURLToPath(new URL("../../", import.meta.url)), esbuild: { jsx: "automatic" }, optimizeDeps: { noDiscovery: true, include: [] }, - server: { middlewareMode: true, watch: null }, appType: "custom" }); + server: { middlewareMode: true, watch: null, hmr: false }, appType: "custom" }); try { const { Game, GameList } = await server.ssrLoadModule("/src/pages/Markets.jsx"); const lv = { status: "STARTED", status_text: "6회초", home_score: 4, away_score: 1 }; @@ -27,6 +27,9 @@ test("live card renders the saved pick and prior even with no current options", assert.match(html, /57.0%/); assert.match(html, /현재 추정/); assert.match(html, /사전 확률/); + assert.match(html.split("")[0], /경기 진행 약 55%/); + assert.match(html.split("")[1], /경기 진행 약 55%/); + assert.match(html, /role="progressbar"/); assert.doesNotMatch(html, /NaN/); const missingProbability = renderToStaticMarkup(createElement(Game, { ...props, g: { ...g, prediction_record: { ...g.prediction_record, probability: null } } })); @@ -38,10 +41,17 @@ test("live card renders the saved pick and prior even with no current options", todayOption: { market: "승패", 선택: "원정", 시장확률: .9 } })); assert.match(unrecorded, /사전 예측 기록 없음/); assert.doesNotMatch(unrecorded, /경기 전 예측 픽|90.0%/); + assert.match(unrecorded.split("")[0], /경기 진행 약 55%/); + const noScore = renderToStaticMarkup(createElement(Game, { ...props, + lv: { ...lv, home_score: null, away_score: null } })); + assert.match(noScore.split("")[0], /경기 진행 약 55%/); + assert.match(noScore.split("")[1], /경기 진행 약 55%/); const stale = renderToStaticMarkup(createElement(Game, { ...props, g: { ...g, _liveFeedAt: "2026-09-05T10:00:00Z" } })); assert.match(stale, /57.0%/); assert.doesNotMatch(stale, /현재 추정/); + assert.match(stale, /중계 갱신 지연/); + assert.doesNotMatch(stale, /role="progressbar"/); const list = renderToStaticMarkup(createElement(GameList, { data: { live: [{ ...g, league: "NPB", _liveState: lv }], past: [], year: 2026 }, grades: { odds_bins: [] }, today: null, @@ -59,6 +69,7 @@ test("live card renders the saved pick and prior even with no current options", assert.match(summary, new RegExp(result)); assert.match(summary, /당시 배당 1.80배/); assert.doesNotMatch(summary, /9.90/); + assert.doesNotMatch(finished, /role="progressbar"/); } } finally { await server.close();