/* ─── Dualbat home-sections.jsx — 홈 리디자인 전용 컴포넌트 ────────────── 원본: design_handoff_landing_redesign/components.jsx IIFE 래퍼로 기존 sections.jsx의 Header·Hero·등과 이름 충돌 차단. `window.KBHome*` prefix로 export — app.jsx에서 명시적으로 호출. */ (function(){ const { useState, useEffect, useMemo, useRef } = React; const D = window.KB_HOME_DATA; const TEAM_COLORS = window.KB_TEAM_COLORS || {}; function relDate(s){ const m = (s||"").match(/^(\d{4})-(\d{2})-(\d{2})$/); if(!m) return s; const pub = new Date(+m[1], +m[2]-1, +m[3]); const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const days = Math.round((today-pub)/86400000); if (days<=0) return "오늘"; if (days===1) return "어제"; if (days<=7) return `${days}일 전`; return `${+m[2]}/${+m[3]}`; } /* ─── Icons ─────────────────────────────────────────────────────────── */ const I = { search:(p)=>, moon:(p)=>, sun:(p)=>, star:(p)=>, arrow:(p)=>, home:(p)=>, user:(p)=>, vs:(p)=>, pin:(p)=>, menu:(p)=>, }; /* ─── Header ─────────────────────────────────────────────────────────── */ function Header(){ const [isDark, setIsDark] = useState(()=>localStorage.getItem("dualbat_theme")==="dark"); useEffect(()=>{ // .home2026 스코프 내 토글 (글 페이지의 :root 토큰 영향 X) const root = document.querySelector(".home2026") || document.documentElement; root.setAttribute("data-theme", isDark?"dark":"light"); localStorage.setItem("dualbat_theme", isDark?"dark":"light"); },[isDark]); const items = [ {id:"home",label:"홈",active:true},{id:"today",label:"오늘 경기"},{id:"mlb",label:"MLB"}, {id:"kbo",label:"KBO"},{id:"players",label:"선수"},{id:"rank",label:"랭킹"},{id:"guide",label:"가이드"}, ]; return (
DualbatMLB · KBO ANALYTICS
선수 · 팀 · 지표 검색 ⌘K
); } /* ─── Hero — single clear spotlight ──────────────────────────────────── */ function Hero(){ const h = D.hero; return (
한국인 MLB 오늘의 메이저리거
갱신 {h.updated} · MLB Stats API
{h.ko[0]}
{h.ko} {h.today.vs} · {h.today.date}
{h.today.line}
{h.role}
{h.stats.map((s,i)=>(
{s.k}
{s.v}
{s.d}
))}
📅 오늘 KBO 5경기 매치업 분석

최근 4경기 폼

{h.form.map((g,i)=>(
{g.date} {g.line}
))}

다른 한국인 선수

{h.others.map((p,i)=>(
{p.team} {p.ko} {p.line}
))}
✦ 선수 1대1 비교 도구
); } /* ─── Today's KBO games ──────────────────────────────────────────────── */ function Games(){ return (
2026-06-07 · 5경기

오늘 KBO 경기

전체 일정
{D.matchups.map((m,i)=>(
KBO{m.time}
{m.away.code} {m.away.code}선발{m.away.sp}
VS
{m.home.code} {m.home.code}선발{m.home.sp}
{m.tag}{m.h2h}
))}
); } /* ─── Analysis — lead + compact rows ─────────────────────────────────── */ function Analysis(){ const lead = D.lead; return (
오늘의 분석

한국 야구, 오늘의 시선

전체 보기
{lead.league}
{lead.stat}{lead.unit}

{lead.title}

{lead.excerpt}

{lead.author} · {relDate(lead.date)}{lead.read} 읽기
{D.stories.map((s,i)=>(
{s.stat}{s.unit}

{s.title}

{s.league} {s.sub}{relDate(s.date)}
))}
); } /* ─── Player carousel ────────────────────────────────────────────────── */ function Carousel(){ return (
인기 선수

지금 이 선수들

선수 전체
{D.players.map((p,i)=>(
=3?13:17, boxShadow:`0 0 0 4px ${p.color}22, 0 8px 20px -8px rgba(0,0,0,.35)`}}> {p.team} {p.live && }
{p.ko}
{p.team} · {p.pos}
{p.stat}
{p.league}
))}
); } /* ─── Career chart ───────────────────────────────────────────────────── */ function CareerChart(){ const c = D.career, S = c.seasons; const W=760, H=300, padL=44, padR=20, padT=24, padB=40; const xs = (i)=> padL + i*((W-padL-padR)/(S.length-1)); const lo=.60, hi=1.05; const ys = (v)=> padT + (1-(v-lo)/(hi-lo))*(H-padT-padB); const kIdx = S.map((s,i)=>({s,i})).filter(o=>o.s.lg==="kbo"); const mIdx = S.map((s,i)=>({s,i})).filter(o=>o.s.lg==="mlb"); const line = (arr)=> arr.map((o,j)=> `${j===0?"M":"L"} ${xs(o.i).toFixed(1)} ${ys(o.s.ops).toFixed(1)}`).join(" "); const area = (arr)=>{ const d = arr.map((o,j)=>`${j===0?"M":"L"} ${xs(o.i).toFixed(1)} ${ys(o.s.ops).toFixed(1)}`).join(" "); return `${d} L ${xs(arr[arr.length-1].i).toFixed(1)} ${H-padB} L ${xs(arr[0].i).toFixed(1)} ${H-padB} Z`; }; const grid = [.6,.7,.8,.9,1.0]; return (
커리어 아크

이정후 KBO → MLB 여정

KBO 2017–2022 MLB 2024–현재
{grid.map((g,i)=>( {g.toFixed(2)} ))} {S.map((s,i)=>( {(s.ops>=.95 || s.partial) && {s.ops.toFixed(3).replace(/^0/,"")}} {s.y} ))}
KBO 통산 OPS .896 · 2022 정점 .996 → MLB 적응기 거쳐 2026 .808 회복 중 · 출처 KBO 공식 · MLB Stats API
); } /* ─── Leaderboard ────────────────────────────────────────────────────── */ function Sparkbars({ data, color }){ const max = 4; return (
{data.map((v,i)=>( =3?color:(v>=1?`color-mix(in srgb, ${color} 55%, transparent)`:"var(--hairline-strong)")}}/> ))}
); } function Leaderboard(){ const [tab, setTab] = useState("kbo"); const lb = D.leaderboards[tab]; const max = Math.max(...lb.rows.map(r=>r.ops), 0.5); const LINKS = window.KB_HOME_PLAYER_LINKS || {}; return (
2026 리더보드

OPS 기준 탑 플레이어

자동 갱신 · {tab==="kbo"?"KBO 공식":"MLB Stats API"}
{lb.rows.map((r,i)=>{ const tc = TEAM_COLORS[r.sub.split(" ")[0]] || "var(--card-2)"; const accent = tab==="mlb" ? "var(--mlb)" : "var(--kbo)"; const href = LINKS[r.name]; const trProps = href ? { onClick: () => { window.location.href = href; }, onKeyDown: (e) => { if (e.key==="Enter") window.location.href = href; }, role: "link", tabIndex: 0, title: `${r.name} 페이지로 이동`, style: { cursor: "pointer" }, } : { style: { cursor: "default" } }; return ( );})}
# 선수 OPS 시즌 OPS 비교 최근 5경기 경기 홈런 타점 타율
{i+1}
=3?9:11,fontWeight:800,color:"#fff"}}>{r.sub.split(" ")[0]}
{r.name}
{r.sub}
{r.ops?r.ops.toFixed(3).replace(/^0/,""):"—"}
{r.ops?`${Math.round((r.ops/max)*100)}%`:"—"}
{r.g||"—"} {r.hr} {r.rbi||"—"} {r.avg?r.avg.toFixed(3).replace(/^0/,""):"—"}
); } /* ─── Guides ─────────────────────────────────────────────────────────── */ function Guides(){ return (
입문자 가이드

처음 보는 야구 데이터, 5분이면 이해

전체 24편
{D.guides.map((g,i)=>(
{g.n}
{g.eyebrow}

{g.title}

{g.body}

읽으러 가기 {g.time}
))}
); } /* ─── Footer ─────────────────────────────────────────────────────────── */ function Footer(){ const cols = [ { h:"선수", items:[["이정후 — SF","/players/이정후.html"],["김혜성 — LAD","/players/김혜성.html"],["김하성 — ATL","/players/김하성.html"],["오늘 KBO 경기 ✦","/today.html"]] }, { h:"레전드", items:[["박찬호 124승","/players/박찬호.html"],["류현진 78승","/players/류현진.html"],["추신수 218HR","/players/추신수.html"],["한국인 MLB 순위","/players/korean-mlb-ranking.html"]] }, { h:"도구", items:[["선수 1대1 비교 ✦","/tools/compare.html"],["드림팀 빌더","/tools/dreamteam.html"],["히트존 매핑","/tools/heatzone.html"],["KBO 10팀 시즌","/teams/"]] }, { h:"가이드", items:[["가이드 전체 24편","/guides/"],["wRC+가 뭐야?","/guides/wrc-plus.html"],["WAR 5분","/guides/war.html"],["Statcast 풀이","/guides/statcast.html"]] }, ]; return ( ); } /* ─── Mobile tab bar ─────────────────────────────────────────────────── */ function MobileTabBar(){ const tabs = [ {id:"home",label:"홈",icon:I.home,href:"#"},{id:"today",label:"오늘",icon:I.vs,href:"/today.html"}, {id:"players",label:"선수",icon:I.user,href:"#players"},{id:"rank",label:"랭킹",icon:I.pin,href:"#rank"}, {id:"guide",label:"가이드",icon:I.menu,href:"#guide"}, ]; return ( ); } /* IIFE export — 명시적 prefix로 기존 sections.jsx와 분리 */ Object.assign(window, { KBHomeHeader: Header, KBHomeHero: Hero, KBHomeGames: Games, KBHomeAnalysis: Analysis, KBHomeCarousel: Carousel, KBHomeCareerChart: CareerChart, KBHomeLeaderboard: Leaderboard, KBHomeGuides: Guides, KBHomeFooter: Footer, KBHomeMobileTabBar: MobileTabBar, }); })();