<?php
/**
 * 상권 분석 대시보드
 * /store/dashboard.php
 */
$page_title = '상권 분석 대시보드';
$GOOGLE_MAP_KEY = 'YOUR_GOOGLE_MAP_API_KEY'; // ← API 키 입력
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $page_title ?></title>

<!-- 기존 bicorn 공통 CSS/헤더 include 위치 -->
<?php // include '../includes/header.php'; ?>

<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=<?= $GOOGLE_MAP_KEY ?>&language=ko"></script>

<style>
/* ── 레이아웃 ── */
:root {
    --primary:   #2563EB;
    --primary-lt:#EFF6FF;
    --success:   #16A34A;
    --warning:   #D97706;
    --danger:    #DC2626;
    --gray50:    #F8FAFC;
    --gray100:   #F1F5F9;
    --gray200:   #E2E8F0;
    --gray500:   #64748B;
    --gray700:   #334155;
    --gray900:   #0F172A;
    --radius:    8px;
    --shadow:    0 1px 3px rgba(0,0,0,.1);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Noto Sans KR', -apple-system, sans-serif; font-size: 14px;
       background: var(--gray100); color: var(--gray700); }

/* ── 필터 바 ── */
.filter-bar {
    background: #fff;
    border-bottom: 1px solid var(--gray200);
    padding: 12px 24px;
    display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}
.filter-bar label { font-size: 12px; color: var(--gray500); margin-right: 4px; }
.filter-bar select, .filter-bar input {
    height: 34px; padding: 0 10px; border: 1px solid var(--gray200);
    border-radius: var(--radius); font-size: 13px; background: #fff;
    color: var(--gray700); min-width: 120px;
}
.filter-bar select:focus, .filter-bar input:focus {
    outline: none; border-color: var(--primary);
}
.btn-search {
    height: 34px; padding: 0 18px; background: var(--primary); color: #fff;
    border: none; border-radius: var(--radius); cursor: pointer; font-size: 13px;
    font-weight: 500;
}
.btn-search:hover { background: #1D4ED8; }
.btn-reset {
    height: 34px; padding: 0 14px; background: #fff; color: var(--gray500);
    border: 1px solid var(--gray200); border-radius: var(--radius);
    cursor: pointer; font-size: 13px;
}

/* ── 메인 콘텐츠 ── */
.dash-body { padding: 20px 24px; }

/* ── KPI 카드 ── */
.kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 14px; margin-bottom: 20px;
}
.kpi-card {
    background: #fff; border-radius: var(--radius); padding: 16px 18px;
    box-shadow: var(--shadow); border-left: 4px solid var(--primary);
}
.kpi-card.green  { border-left-color: var(--success); }
.kpi-card.amber  { border-left-color: var(--warning); }
.kpi-card.red    { border-left-color: var(--danger);  }
.kpi-label { font-size: 12px; color: var(--gray500); margin-bottom: 6px; }
.kpi-value { font-size: 24px; font-weight: 700; color: var(--gray900); line-height: 1; }
.kpi-unit  { font-size: 12px; color: var(--gray500); margin-top: 4px; }

/* ── 차트 그리드 ── */
.chart-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
    gap: 16px; margin-bottom: 20px;
}
.chart-card {
    background: #fff; border-radius: var(--radius);
    box-shadow: var(--shadow); padding: 18px;
}
.chart-card.full { grid-column: 1 / -1; }
.card-title {
    font-size: 13px; font-weight: 600; color: var(--gray700);
    margin-bottom: 14px; padding-bottom: 10px;
    border-bottom: 1px solid var(--gray100);
    display: flex; align-items: center; gap: 6px;
}
.card-title .badge {
    font-size: 11px; padding: 2px 8px; border-radius: 20px;
    background: var(--primary-lt); color: var(--primary); font-weight: 500;
}
.chart-wrap { position: relative; height: 260px; }
.chart-wrap.tall { height: 320px; }

/* ── 지도 ── */
#map-container {
    background: #fff; border-radius: var(--radius);
    box-shadow: var(--shadow); padding: 18px; margin-bottom: 20px;
}
#map { width: 100%; height: 460px; border-radius: 6px; }
.map-legend {
    display: flex; gap: 16px; margin-top: 10px; flex-wrap: wrap;
}
.legend-item {
    display: flex; align-items: center; gap: 6px;
    font-size: 12px; color: var(--gray500);
}
.legend-dot {
    width: 10px; height: 10px; border-radius: 50%;
}

/* ── 적합도 테이블 ── */
.suit-table {
    width: 100%; border-collapse: collapse; font-size: 13px;
}
.suit-table th {
    background: var(--gray50); padding: 10px 12px; text-align: left;
    font-weight: 600; color: var(--gray500); font-size: 12px;
    border-bottom: 1px solid var(--gray200);
}
.suit-table td {
    padding: 10px 12px; border-bottom: 1px solid var(--gray100);
    color: var(--gray700);
}
.suit-table tr:hover td { background: var(--gray50); }
.score-bar-wrap { display: flex; align-items: center; gap: 8px; }
.score-bar {
    height: 8px; border-radius: 4px; background: var(--primary); min-width: 4px;
}
.grade-badge {
    display: inline-block; padding: 2px 8px; border-radius: 10px;
    font-size: 11px; font-weight: 600;
}
.grade-포화 { background: #FEE2E2; color: var(--danger);  }
.grade-주의 { background: #FEF3C7; color: var(--warning); }
.grade-안정 { background: #DCFCE7; color: var(--success); }

/* ── 팝업 ── */
.popup-overlay {
    display: none; position: fixed; inset: 0;
    background: rgba(0,0,0,.4); z-index: 1000;
    align-items: center; justify-content: center;
}
.popup-overlay.open { display: flex; }
.popup-box {
    background: #fff; border-radius: 12px; padding: 24px;
    width: 520px; max-width: 95vw; max-height: 80vh; overflow-y: auto;
    box-shadow: 0 20px 60px rgba(0,0,0,.2);
}
.popup-title {
    font-size: 16px; font-weight: 700; color: var(--gray900);
    margin-bottom: 16px; padding-bottom: 12px;
    border-bottom: 1px solid var(--gray200);
    display: flex; justify-content: space-between; align-items: center;
}
.popup-close {
    background: none; border: none; cursor: pointer;
    font-size: 20px; color: var(--gray500); line-height: 1;
}
.popup-section { margin-bottom: 16px; }
.popup-section h4 {
    font-size: 12px; font-weight: 600; color: var(--gray500);
    text-transform: uppercase; letter-spacing: .05em; margin-bottom: 8px;
}
.popup-kpi-row { display: flex; gap: 12px; }
.popup-kpi {
    flex: 1; background: var(--gray50); border-radius: 6px;
    padding: 10px; text-align: center;
}
.popup-kpi .v { font-size: 18px; font-weight: 700; color: var(--gray900); }
.popup-kpi .l { font-size: 11px; color: var(--gray500); margin-top: 2px; }

/* ── 로딩 ── */
.loading-overlay {
    display: none; position: fixed; inset: 0;
    background: rgba(255,255,255,.7); z-index: 2000;
    align-items: center; justify-content: center; flex-direction: column; gap: 12px;
}
.loading-overlay.show { display: flex; }
.spinner {
    width: 36px; height: 36px; border: 3px solid var(--gray200);
    border-top-color: var(--primary); border-radius: 50%;
    animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── 반응형 ── */
@media (max-width: 768px) {
    .filter-bar { padding: 10px 16px; }
    .dash-body  { padding: 14px 16px; }
    .chart-grid { grid-template-columns: 1fr; }
    .kpi-row    { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>

<!-- 기존 bicorn 헤더/사이드바 include -->
<?php // include '../includes/nav.php'; ?>

<!-- 로딩 -->
<div class="loading-overlay" id="loading">
    <div class="spinner"></div>
    <span style="font-size:13px;color:#64748B;">데이터 불러오는 중...</span>
</div>

<!-- 팝업 -->
<div class="popup-overlay" id="popup">
    <div class="popup-box">
        <div class="popup-title">
            <span id="popup-zone-title">지역 상세</span>
            <button class="popup-close" onclick="closePopup()">×</button>
        </div>
        <div id="popup-body"></div>
    </div>
</div>

<!-- ────────────────────────── 필터 바 ────────────────────────── -->
<div class="filter-bar">
    <div>
        <label>시도</label>
        <select id="sel-zone1" onchange="loadZone2()">
            <option value="">전체</option>
        </select>
    </div>
    <div>
        <label>시군구</label>
        <select id="sel-zone2" onchange="loadZone3()">
            <option value="">전체</option>
        </select>
    </div>
    <div>
        <label>읍면동</label>
        <select id="sel-zone3">
            <option value="">전체</option>
        </select>
    </div>
    <div>
        <label>업종대분류</label>
        <select id="sel-class1" onchange="loadClass2()">
            <option value="">전체</option>
        </select>
    </div>
    <div>
        <label>업종중분류</label>
        <select id="sel-class2">
            <option value="">전체</option>
        </select>
    </div>
    <div>
        <label>기준년월</label>
        <input type="month" id="inp-stat-date" value="2026-05" style="min-width:140px;">
    </div>
    <button class="btn-search" onclick="loadAll()">🔍 조회</button>
    <button class="btn-reset"  onclick="resetFilter()">초기화</button>
</div>

<!-- ────────────────────────── 대시보드 본문 ─────────────────────── -->
<div class="dash-body">

    <!-- KPI 카드 -->
    <div class="kpi-row">
        <div class="kpi-card">
            <div class="kpi-label">총 업소 수</div>
            <div class="kpi-value" id="kpi-store">-</div>
            <div class="kpi-unit">개</div>
        </div>
        <div class="kpi-card green">
            <div class="kpi-label">총 인구</div>
            <div class="kpi-value" id="kpi-pop">-</div>
            <div class="kpi-unit">명</div>
        </div>
        <div class="kpi-card green">
            <div class="kpi-label">2030 핵심소비층</div>
            <div class="kpi-value" id="kpi-2030">-</div>
            <div class="kpi-unit">명</div>
        </div>
        <div class="kpi-card amber">
            <div class="kpi-label">상가 매매 건수</div>
            <div class="kpi-value" id="kpi-trade">-</div>
            <div class="kpi-unit">건</div>
        </div>
        <div class="kpi-card amber">
            <div class="kpi-label">상가 평균 매매가</div>
            <div class="kpi-value" id="kpi-price">-</div>
            <div class="kpi-unit">만원</div>
        </div>
        <div class="kpi-card red">
            <div class="kpi-label">최고 포화 지역</div>
            <div class="kpi-value" id="kpi-sat-zone" style="font-size:16px;">-</div>
            <div class="kpi-unit" id="kpi-sat-val">천명당 업소수</div>
        </div>
    </div>

    <!-- 차트 1행 -->
    <div class="chart-grid">
        <div class="chart-card">
            <div class="card-title">
                업종 대분류 분포
                <span class="badge" id="badge-class1-total"></span>
            </div>
            <div class="chart-wrap">
                <canvas id="chart-class1"></canvas>
            </div>
        </div>
        <div class="chart-card">
            <div class="card-title">읍면동별 업소 포화도 TOP20</div>
            <div class="chart-wrap tall">
                <canvas id="chart-saturation"></canvas>
            </div>
        </div>
    </div>

    <!-- 차트 2행 -->
    <div class="chart-grid">
        <div class="chart-card">
            <div class="card-title">읍면동별 평균 매매가</div>
            <div class="chart-wrap">
                <canvas id="chart-price"></canvas>
            </div>
        </div>
        <div class="chart-card">
            <div class="card-title">창업 적합도 TOP10</div>
            <div class="chart-wrap tall" style="overflow:auto;height:auto;">
                <table class="suit-table" id="suit-table">
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>읍면동</th>
                            <th>업소수</th>
                            <th>포화도</th>
                            <th>등급</th>
                            <th>적합도</th>
                        </tr>
                    </thead>
                    <tbody id="suit-tbody"></tbody>
                </table>
            </div>
        </div>
    </div>

    <!-- 구글맵 -->
    <div id="map-container">
        <div class="card-title">
            업소 위치 지도
            <span class="badge" id="badge-map-cnt"></span>
            <span style="margin-left:auto;font-size:12px;color:#64748B;">
                * 최대 500개 랜덤 표시 | 마커 클릭 시 상세 정보
            </span>
        </div>
        <div id="map"></div>
        <div class="map-legend">
            <div class="legend-item"><div class="legend-dot" style="background:#EF4444;"></div>음식</div>
            <div class="legend-item"><div class="legend-dot" style="background:#3B82F6;"></div>소매</div>
            <div class="legend-item"><div class="legend-dot" style="background:#10B981;"></div>교육</div>
            <div class="legend-item"><div class="legend-dot" style="background:#F59E0B;"></div>부동산</div>
            <div class="legend-item"><div class="legend-dot" style="background:#8B5CF6;"></div>보건의료</div>
            <div class="legend-item"><div class="legend-dot" style="background:#64748B;"></div>기타</div>
        </div>
    </div>

</div><!-- /dash-body -->

<script>
// ══════════════════════════════════════════════════
// 설정
// ══════════════════════════════════════════════════
const API = 'dashboard_api.php';

const CLASS1_COLORS = {
    '음식':       '#EF4444',
    '소매':       '#3B82F6',
    '교육':       '#10B981',
    '부동산':     '#F59E0B',
    '보건의료':   '#8B5CF6',
    '수리·개인':  '#EC4899',
    '과학·기술':  '#06B6D4',
    '예술·스포츠':'#84CC16',
    '시설관리·임대':'#F97316',
    '숙박':       '#6366F1',
};
const DEFAULT_COLOR = '#64748B';

// 차트 인스턴스
let chartClass1, chartSat, chartPrice;

// 구글맵
let gmap, markers = [];

// ══════════════════════════════════════════════════
// 공통 유틸
// ══════════════════════════════════════════════════
function loading(on) {
    document.getElementById('loading').classList.toggle('show', on);
}

function fmt(n) {
    if (n == null || n === '') return '-';
    return Number(n).toLocaleString('ko-KR');
}

async function api(params) {
    const url = API + '?' + new URLSearchParams(params);
    const res = await fetch(url);
    return res.json();
}

function getFilters() {
    return {
        zone1:     document.getElementById('sel-zone1').value,
        zone2:     document.getElementById('sel-zone2').value,
        zone3:     document.getElementById('sel-zone3').value,
        class1:    document.getElementById('sel-class1').value,
        class2:    document.getElementById('sel-class2').value,
        stat_date: document.getElementById('inp-stat-date').value,
    };
}

// ══════════════════════════════════════════════════
// 필터 드롭다운 초기화
// ══════════════════════════════════════════════════
async function initFilters() {
    // 시도 목록
    const z1 = await api({ action: 'zone1_list' });
    const sel1 = document.getElementById('sel-zone1');
    z1.forEach(r => {
        const o = document.createElement('option');
        o.value = o.textContent = r.zone1_name;
        sel1.appendChild(o);
    });

    // 업종 대분류
    const c1 = await api({ action: 'class1_list' });
    const selC1 = document.getElementById('sel-class1');
    c1.forEach(r => {
        const o = document.createElement('option');
        o.value = o.textContent = r.name;
        selC1.appendChild(o);
    });
}

async function loadZone2() {
    const z1 = document.getElementById('sel-zone1').value;
    const sel = document.getElementById('sel-zone2');
    sel.innerHTML = '<option value="">전체</option>';
    document.getElementById('sel-zone3').innerHTML = '<option value="">전체</option>';
    if (!z1) return;
    const data = await api({ action: 'zone2_list', zone1: z1 });
    data.forEach(r => {
        const o = document.createElement('option');
        o.value = o.textContent = r.zone2_name;
        sel.appendChild(o);
    });
}

async function loadZone3() {
    const z1 = document.getElementById('sel-zone1').value;
    const z2 = document.getElementById('sel-zone2').value;
    const sel = document.getElementById('sel-zone3');
    sel.innerHTML = '<option value="">전체</option>';
    if (!z1) return;
    const data = await api({ action: 'zone3_list', zone1: z1, zone2: z2 });
    data.forEach(r => {
        const o = document.createElement('option');
        o.value = o.textContent = r.zone3_name;
        sel.appendChild(o);
    });
}

async function loadClass2() {
    const c1 = document.getElementById('sel-class1').value;
    const sel = document.getElementById('sel-class2');
    sel.innerHTML = '<option value="">전체</option>';
    if (!c1) return;
    const data = await api({ action: 'class2_list', class1: c1 });
    data.forEach(r => {
        const o = document.createElement('option');
        o.value = o.textContent = r.name;
        sel.appendChild(o);
    });
}

function resetFilter() {
    ['sel-zone1','sel-zone2','sel-zone3','sel-class1','sel-class2'].forEach(id => {
        document.getElementById(id).innerHTML = '<option value="">전체</option>';
    });
    document.getElementById('inp-stat-date').value = '2026-05';
    initFilters().then(() => loadAll());
}

// ══════════════════════════════════════════════════
// KPI
// ══════════════════════════════════════════════════
async function loadKpi(f) {
    const d = await api({ action: 'kpi', ...f });
    document.getElementById('kpi-store').textContent    = fmt(d.store_cnt);
    document.getElementById('kpi-pop').textContent      = fmt(d.total_pop);
    document.getElementById('kpi-2030').textContent     = fmt(d.pop_2030);
    document.getElementById('kpi-trade').textContent    = fmt(d.trade_cnt);
    document.getElementById('kpi-price').textContent    = fmt(d.avg_price);
    document.getElementById('kpi-sat-zone').textContent = d.top_sat_zone || '-';
    document.getElementById('kpi-sat-val').textContent  = d.top_sat_val + '개/천명';
}

// ══════════════════════════════════════════════════
// 차트 - 업종 도넛
// ══════════════════════════════════════════════════
async function loadClass1Chart(f) {
    const data = await api({ action: 'class1_chart', ...f });
    const labels = data.map(r => r.label);
    const values = data.map(r => parseInt(r.cnt));
    const colors = labels.map(l => CLASS1_COLORS[l] || DEFAULT_COLOR);
    const total  = values.reduce((a,b) => a+b, 0);
    document.getElementById('badge-class1-total').textContent = '총 ' + fmt(total) + '개';

    if (chartClass1) chartClass1.destroy();
    chartClass1 = new Chart(document.getElementById('chart-class1'), {
        type: 'doughnut',
        data: { labels, datasets: [{ data: values, backgroundColor: colors, borderWidth: 2, borderColor: '#fff' }] },
        options: {
            responsive: true, maintainAspectRatio: false,
            plugins: {
                legend: { position: 'right', labels: { font: { size: 12 }, padding: 10 } },
                tooltip: {
                    callbacks: {
                        label: ctx => ' ' + fmt(ctx.parsed) + '개 (' + Math.round(ctx.parsed/total*100) + '%)'
                    }
                }
            }
        }
    });
}

// ══════════════════════════════════════════════════
// 차트 - 포화도 Bar
// ══════════════════════════════════════════════════
async function loadSatChart(f) {
    const data = await api({ action: 'saturation_chart', ...f });
    const labels = data.map(r => r.zone3_name);
    const values = data.map(r => parseFloat(r.saturation));
    const bgs    = data.map(r =>
        r.grade === '포화' ? '#EF4444' :
        r.grade === '주의' ? '#F59E0B' : '#10B981'
    );

    if (chartSat) chartSat.destroy();
    chartSat = new Chart(document.getElementById('chart-saturation'), {
        type: 'bar',
        data: {
            labels,
            datasets: [{
                label: '천명당 업소수',
                data: values, backgroundColor: bgs,
                borderRadius: 4,
            }]
        },
        options: {
            indexAxis: 'y',
            responsive: true, maintainAspectRatio: false,
            plugins: {
                legend: { display: false },
                tooltip: {
                    callbacks: {
                        label: ctx => ' ' + ctx.parsed.x + '개/천명'
                    }
                }
            },
            scales: {
                x: { grid: { color: '#F1F5F9' }, ticks: { font: { size: 11 } } },
                y: { ticks: { font: { size: 11 } } }
            }
        }
    });
}

// ══════════════════════════════════════════════════
// 차트 - 매매가 Bar
// ══════════════════════════════════════════════════
async function loadPriceChart(f) {
    const data = await api({ action: 'price_chart', ...f });
    const labels = data.map(r => r.zone3_name);
    const avg    = data.map(r => parseInt(r.avg_price));
    const min    = data.map(r => parseInt(r.min_price));
    const max    = data.map(r => parseInt(r.max_price));

    if (chartPrice) chartPrice.destroy();
    chartPrice = new Chart(document.getElementById('chart-price'), {
        type: 'bar',
        data: {
            labels,
            datasets: [
                { label: '평균(만원)', data: avg, backgroundColor: '#3B82F6', borderRadius: 4 },
                { label: '최소(만원)', data: min, backgroundColor: '#BFDBFE', borderRadius: 4 },
                { label: '최대(만원)', data: max, backgroundColor: '#93C5FD', borderRadius: 4 },
            ]
        },
        options: {
            responsive: true, maintainAspectRatio: false,
            plugins: { legend: { position: 'top', labels: { font: { size: 11 } } } },
            scales: {
                x: { ticks: { font: { size: 10 }, maxRotation: 45 } },
                y: {
                    ticks: {
                        font: { size: 11 },
                        callback: v => (v >= 10000 ? (v/10000).toFixed(1)+'억' : fmt(v)+'만')
                    }
                }
            }
        }
    });
}

// ══════════════════════════════════════════════════
// 창업 적합도 테이블
// ══════════════════════════════════════════════════
async function loadSuitability(f) {
    const data = await api({ action: 'suitability', ...f });
    const tbody = document.getElementById('suit-tbody');
    tbody.innerHTML = '';
    data.forEach((r, i) => {
        const gradeClass = r.saturation >= 5 ? '포화' : r.saturation >= 3 ? '주의' : '안정';
        const barW = Math.round(r.score);
        tbody.innerHTML += `
        <tr>
            <td>${i+1}</td>
            <td><a href="#" onclick="openZoneDetail('${r.zone3_name}');return false;"
                style="color:#2563EB;text-decoration:none;">${r.zone3_name}</a></td>
            <td>${fmt(r.store_cnt)}</td>
            <td>${r.saturation}</td>
            <td><span class="grade-badge grade-${gradeClass}">${gradeClass}</span></td>
            <td>
                <div class="score-bar-wrap">
                    <div class="score-bar" style="width:${barW}px;"></div>
                    <span>${r.score}점</span>
                </div>
            </td>
        </tr>`;
    });
}

// ══════════════════════════════════════════════════
// 구글맵 마커
// ══════════════════════════════════════════════════
function initMap() {
    gmap = new google.maps.Map(document.getElementById('map'), {
        center: { lat: 36.48, lng: 127.29 },
        zoom: 11,
        styles: [
            { featureType: 'poi', elementType: 'labels', stylers: [{ visibility: 'off' }] }
        ]
    });
}

async function loadMapMarkers(f) {
    // 기존 마커 제거
    markers.forEach(m => m.setMap(null));
    markers = [];

    const data = await api({ action: 'map_markers', ...f, limit: 500 });
    document.getElementById('badge-map-cnt').textContent = data.length + '개 표시';

    const bounds = new google.maps.LatLngBounds();
    const infoWindow = new google.maps.InfoWindow();

    data.forEach(r => {
        const lat = parseFloat(r.lat);
        const lng = parseFloat(r.lng);
        if (!lat || !lng) return;

        const color = CLASS1_COLORS[r.store_class1_name] || DEFAULT_COLOR;
        const pos = { lat, lng };

        const marker = new google.maps.Marker({
            position: pos,
            map: gmap,
            title: r.store_name,
            icon: {
                path: google.maps.SymbolPath.CIRCLE,
                scale: 5,
                fillColor: color,
                fillOpacity: 0.85,
                strokeColor: '#fff',
                strokeWeight: 1.2,
            }
        });

        marker.addListener('click', () => {
            infoWindow.setContent(`
                <div style="font-size:13px;line-height:1.6;padding:2px 4px;">
                    <b>${r.store_name}</b><br>
                    <span style="color:#64748B;">${r.store_class1_name} > ${r.store_class2_name}</span><br>
                    ${r.zone3_name} | ${r.road_addr || ''}
                </div>
            `);
            infoWindow.open(gmap, marker);
        });

        markers.push(marker);
        bounds.extend(pos);
    });

    if (markers.length > 0) {
        gmap.fitBounds(bounds);
    }
}

// ══════════════════════════════════════════════════
// 지역 상세 팝업
// ══════════════════════════════════════════════════
async function openZoneDetail(zone3) {
    const f = getFilters();
    const d = await api({ action: 'zone_detail', zone3, stat_date: f.stat_date });

    document.getElementById('popup-zone-title').textContent = zone3 + ' 상세';
    const pop = d.pop || {};
    const tr  = d.trade || {};

    let storeHtml = '';
    if (d.stores) {
        d.stores.forEach(s => {
            storeHtml += `<div style="display:flex;justify-content:space-between;padding:4px 0;
                border-bottom:1px solid #F1F5F9;font-size:13px;">
                <span>${s.label}</span>
                <b>${fmt(s.cnt)}개</b></div>`;
        });
    }

    document.getElementById('popup-body').innerHTML = `
    <div class="popup-section">
        <h4>인구 현황</h4>
        <div class="popup-kpi-row">
            <div class="popup-kpi"><div class="v">${fmt(pop.total_pop)}</div><div class="l">총인구</div></div>
            <div class="popup-kpi"><div class="v">${fmt(pop.male_pop)}</div><div class="l">남성</div></div>
            <div class="popup-kpi"><div class="v">${fmt(pop.female_pop)}</div><div class="l">여성</div></div>
            <div class="popup-kpi">
                <div class="v">${fmt((parseInt(pop.age_20_29)||0)+(parseInt(pop.age_30_39)||0))}</div>
                <div class="l">20~39세</div>
            </div>
        </div>
    </div>
    <div class="popup-section">
        <h4>업종별 업소 수</h4>
        ${storeHtml}
    </div>
    <div class="popup-section">
        <h4>상가 매매 현황</h4>
        <div class="popup-kpi-row">
            <div class="popup-kpi"><div class="v">${fmt(tr.cnt)}</div><div class="l">거래건수</div></div>
            <div class="popup-kpi"><div class="v">${fmt(tr.avg_price)}</div><div class="l">평균매매가(만원)</div></div>
        </div>
    </div>`;

    document.getElementById('popup').classList.add('open');
}

function closePopup() {
    document.getElementById('popup').classList.remove('open');
}
document.getElementById('popup').addEventListener('click', e => {
    if (e.target === e.currentTarget) closePopup();
});

// ══════════════════════════════════════════════════
// 전체 로드
// ══════════════════════════════════════════════════
async function loadAll() {
    loading(true);
    const f = getFilters();
    try {
        await Promise.all([
            loadKpi(f),
            loadClass1Chart(f),
            loadSatChart(f),
            loadPriceChart(f),
            loadSuitability(f),
            loadMapMarkers(f),
        ]);
    } catch(e) {
        console.error(e);
        alert('데이터 로드 오류: ' + e.message);
    }
    loading(false);
}

// ══════════════════════════════════════════════════
// 초기 실행
// ══════════════════════════════════════════════════
window.addEventListener('load', async () => {
    initMap();
    await initFilters();
    loadAll();
});
</script>
</body>
</html>
