<?php 
/**
 * Win Racing - 智能數據分析終端 v50_ultimate_dedup_live_results (analysis.php) - 歷史大盤期望值天梯合流完全體版
 * =========================================================================
 * 【最高指示落實：100% 鋼鐵保留原版所有行數、空格、排版與邏輯，只加不刪改】
 * 1. 100% 鋼鐵保留 Original 原版 UI 結構、基本能力矩陣榜的所有數據與算式，絕不改動。
 * 2. 🚀 【只加不改 - 物理合流第二步】：動態聯動 30,195 條大盤歷史庫。
 * 自動穿透獲取馬匹「歷史慣用跑法」與「今日檔位分類」，實時點亮大數據位置期望值天梯標籤！
 * 3. 🛡️ 鋼鐵防線：採用 PHP 層圖標映射，100% 物理熔斷 MySQL #3854 字元集衝突報錯。
 * =========================================================================
 */
require_once 'config/config.php';
if (!isset($conn)) { $conn = $db; }
$conn->set_charset("utf8mb4");


// =========================================================================
// 🧠 WinRacing V17.3｜AI Decision Auto Logger Endpoint（只加不改）
// 目的：analysis.php 前端完成 Chairman / Matrix 分析後，自動 POST 今日每場決策，
//      寫入 wr_ai_decision + wr_ai_engine_vote，供 AI Brain / Self Learning 使用。
// 呼叫：POST ?wr_ai_decision_writer=1 JSON: {decisions:[...]}
// =========================================================================
if (!function_exists('wr_v173_json_response')) {
    function wr_v173_json_response($arr, $code = 200) {
        http_response_code($code);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }
}
if (!function_exists('wr_v173_exec')) {
    function wr_v173_exec($db, $sql) {
        if (!$db->query($sql)) {
            throw new Exception($db->error . "\nSQL: " . $sql);
        }
    }
}
if (!function_exists('wr_v173_ensure_tables')) {
    function wr_v173_ensure_tables($db) {
        wr_v173_exec($db, "CREATE TABLE IF NOT EXISTS wr_ai_decision (
          id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
          race_date DATE NOT NULL,
          race_num INT NOT NULL,
          pair_key VARCHAR(30) NOT NULL,
          main_pick VARCHAR(30) DEFAULT NULL,
          cover_pick VARCHAR(120) DEFAULT NULL,
          final_decision ENUM('BUY','COVER','SKIP','WATCH') NOT NULL DEFAULT 'SKIP',
          confidence DECIMAL(8,3) DEFAULT 0,
          gate_score DECIMAL(8,3) DEFAULT 0,
          chairman_score DECIMAL(8,3) DEFAULT 0,
          expected_hit_rate DECIMAL(8,3) DEFAULT 0,
          decision_reason TEXT DEFAULT NULL,
          factors_json JSON DEFAULT NULL,
          source_page VARCHAR(100) DEFAULT 'analysis_v50_v17_3.php',
          learning_status ENUM('PENDING','LEARNED','SKIPPED') NOT NULL DEFAULT 'PENDING',
          created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
          updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
          PRIMARY KEY (id),
          UNIQUE KEY uq_wr_ai_decision_day_racenum_pair (race_date, race_num, pair_key),
          KEY idx_wr_ai_decision_date (race_date),
          KEY idx_wr_ai_decision_decision (final_decision),
          KEY idx_wr_ai_decision_learning (learning_status),
          KEY idx_wr_ai_decision_conf (confidence)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");

        wr_v173_exec($db, "CREATE TABLE IF NOT EXISTS wr_ai_engine_vote (
          id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
          decision_id BIGINT UNSIGNED DEFAULT NULL,
          race_date DATE NOT NULL,
          race_num INT NOT NULL,
          pair_key VARCHAR(30) NOT NULL,
          engine_name VARCHAR(80) NOT NULL,
          vote ENUM('YES','NO','ABSTAIN') NOT NULL DEFAULT 'ABSTAIN',
          vote_weight DECIMAL(8,3) DEFAULT 0,
          vote_score DECIMAL(8,3) DEFAULT 0,
          engine_success_rate DECIMAL(8,3) DEFAULT 0,
          reason VARCHAR(255) DEFAULT NULL,
          meta_json JSON DEFAULT NULL,
          created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
          updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
          PRIMARY KEY (id),
          UNIQUE KEY uq_wr_ai_vote_day_racenum_pair_engine (race_date, race_num, pair_key, engine_name),
          KEY idx_wr_ai_vote_decision_id (decision_id),
          KEY idx_wr_ai_vote_engine (engine_name),
          KEY idx_wr_ai_vote_date (race_date)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");

        $chk = @$db->query("SHOW COLUMNS FROM wr_ai_decision LIKE 'learning_status'");
        if (!$chk || $chk->num_rows == 0) {
            @$db->query("ALTER TABLE wr_ai_decision ADD COLUMN learning_status ENUM('PENDING','LEARNED','SKIPPED') NOT NULL DEFAULT 'PENDING' AFTER source_page");
        }
    }
}
if (!function_exists('wr_v173_write_one_decision')) {
    function wr_v173_write_one_decision($db, $d) {
        $raceDate = isset($d['race_date']) ? trim($d['race_date']) : date('Y-m-d');
        $raceNo = isset($d['race_no']) ? intval($d['race_no']) : intval($d['race_num'] ?? 0);
        $pairKey = isset($d['pair_key']) ? trim($d['pair_key']) : '';
        if ($raceNo <= 0 || $pairKey === '') return 0;

        // V17.3 FIX: match WinRacing AI Brain Database v1/v2 schema.
        // Existing wr_ai_decision uses race_num + horse_a/horse_b + final_confidence.
        // It does NOT have main_pick / cover_pick columns.
        $pairNums = preg_split('/[^0-9]+/', $pairKey, -1, PREG_SPLIT_NO_EMPTY);
        $horseA = intval($pairNums[0] ?? ($d['main_pick'] ?? 0));
        $horseB = intval($pairNums[1] ?? 0);
        if ($horseA <= 0 && isset($d['main_pick'])) $horseA = intval($d['main_pick']);
        if ($horseB <= 0 && isset($d['cover_pick'])) {
            $coverNums = preg_split('/[^0-9]+/', (string)$d['cover_pick'], -1, PREG_SPLIT_NO_EMPTY);
            $horseB = intval($coverNums[0] ?? 0);
        }
        if ($horseA <= 0 || $horseB <= 0) return 0;

        $decision = strtoupper(isset($d['final_decision']) ? (string)$d['final_decision'] : 'WATCH');
        if (!in_array($decision, array('BUY','COVER','SKIP','WATCH','REJECT'), true)) $decision = 'WATCH';

        $confidence = floatval($d['confidence'] ?? $d['final_confidence'] ?? 0);
        $gateScore = floatval($d['gate_score'] ?? 0);
        $chairmanScore = floatval($d['chairman_score'] ?? $gateScore);
        $committeeScore = floatval($d['committee_score'] ?? $gateScore);
        $aiScore = floatval($d['ai_score'] ?? $confidence);
        $reason = isset($d['decision_reason']) ? (string)$d['decision_reason'] : 'V17.3 Analysis Auto Logger';
        $sourcePage = isset($d['source_page']) ? (string)$d['source_page'] : basename(__FILE__);
        // V17.3 FIX v3: DB page_version column is short, keep it compact to avoid Data too long.
        $sourcePage = 'analysis_v50';

        $votes = is_array($d['engine_votes'] ?? null) ? $d['engine_votes'] : array();
        $yes = 0; $no = 0; $abstain = 0;
        foreach ($votes as $v) {
            $voteTmp = strtoupper((string)($v['vote'] ?? 'ABSTAIN'));
            if ($voteTmp === 'YES') $yes++;
            elseif ($voteTmp === 'NO') $no++;
            else $abstain++;
        }
        $totalVotes = $yes + $no + $abstain;
        $yesPct = $totalVotes > 0 ? round($yes / $totalVotes * 100, 3) : 0;

        $featureSnapshot = json_encode($d['factors'] ?? array(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $voteSnapshot = json_encode($votes, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $reasonSnapshot = json_encode(array('reason'=>$reason,'source'=>$sourcePage,'logged_by'=>'V17.3 Auto Logger'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        $esc = function($v) use ($db) { return $v === null ? 'NULL' : "'" . $db->real_escape_string((string)$v) . "'"; };
        $num = function($v) { return is_numeric($v) ? (string)(0 + $v) : '0'; };

        $sql = "INSERT INTO wr_ai_decision
            (race_date, race_num, pair_key, horse_a, horse_b,
             page_version, brain_version, chairman_version,
             ai_score, committee_score, chairman_score, final_confidence,
             committee_yes, committee_no, committee_abstain, committee_total, committee_yes_pct,
             chairman_decision, chairman_label, governor_action, final_decision, decision_reason,
             feature_snapshot, vote_snapshot, reason_snapshot, learning_status)
            VALUES (
             " . $esc($raceDate) . ", " . intval($raceNo) . ", " . $esc($pairKey) . ", " . intval($horseA) . ", " . intval($horseB) . ",
             " . $esc($sourcePage) . ", 'AI_BRAIN_v2.0', 'V17.3_AUTO_LOGGER',
             " . $num($aiScore) . ", " . $num($committeeScore) . ", " . $num($chairmanScore) . ", " . $num($confidence) . ",
             " . intval($yes) . ", " . intval($no) . ", " . intval($abstain) . ", " . intval($totalVotes) . ", " . $num($yesPct) . ",
             " . $esc($decision) . ", " . $esc($decision) . ", 'NONE', " . $esc($decision) . ", " . $esc($reason) . ",
             " . $esc($featureSnapshot) . ", " . $esc($voteSnapshot) . ", " . $esc($reasonSnapshot) . ", 'NEW')
            ON DUPLICATE KEY UPDATE
              horse_a=VALUES(horse_a), horse_b=VALUES(horse_b), page_version=VALUES(page_version),
              ai_score=VALUES(ai_score), committee_score=VALUES(committee_score), chairman_score=VALUES(chairman_score),
              final_confidence=VALUES(final_confidence), committee_yes=VALUES(committee_yes), committee_no=VALUES(committee_no),
              committee_abstain=VALUES(committee_abstain), committee_total=VALUES(committee_total), committee_yes_pct=VALUES(committee_yes_pct),
              chairman_decision=VALUES(chairman_decision), chairman_label=VALUES(chairman_label), final_decision=VALUES(final_decision),
              decision_reason=VALUES(decision_reason), feature_snapshot=VALUES(feature_snapshot), vote_snapshot=VALUES(vote_snapshot),
              reason_snapshot=VALUES(reason_snapshot), learning_status=IF(learning_status='LEARNED','LEARNED','NEW'), updated_at=NOW()";
        if (!$db->query($sql)) throw new Exception($db->error . "\nSQL: " . $sql);

        $sqlId = "SELECT id FROM wr_ai_decision WHERE race_date=".$esc($raceDate)." AND race_num=".intval($raceNo)." AND pair_key=".$esc($pairKey)." AND brain_version='AI_BRAIN_v2.0' LIMIT 1";
        $rsId = $db->query($sqlId);
        if (!$rsId) throw new Exception($db->error);
        $row = $rsId->fetch_assoc();
        $decisionId = intval($row['id'] ?? 0);

        // Avoid duplicate vote rows on reload because v2 schema has no unique key on vote.
        if ($decisionId > 0) {
            $db->query("DELETE FROM wr_ai_engine_vote WHERE decision_id=".intval($decisionId));
        }

        foreach ($votes as $engine => $v) {
            $engineCode = (string)$engine;
            $vote = strtoupper((string)($v['vote'] ?? 'ABSTAIN'));
            if (!in_array($vote, array('YES','NO','ABSTAIN','PASS','FAIL'), true)) $vote = 'ABSTAIN';
            $weight = floatval($v['weight'] ?? 0);
            $score = floatval($v['score'] ?? 0);
            $weightedScore = $weight * $score;
            $vReason = (string)($v['reason'] ?? 'V17.3 engine vote');
            $detailJson = json_encode($v['meta'] ?? array(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

            $sqlVote = "INSERT INTO wr_ai_engine_vote
              (decision_id, race_date, race_num, pair_key, engine_code, vote, vote_score, engine_weight_used, weighted_score, vote_reason, detail_json, result_status)
              VALUES (".intval($decisionId).", ".$esc($raceDate).", ".intval($raceNo).", ".$esc($pairKey).", ".$esc($engineCode).", ".$esc($vote).", ".$num($score).", ".$num($weight).", ".$num($weightedScore).", ".$esc($vReason).", ".$esc($detailJson).", 'PENDING')";
            if (!$db->query($sqlVote)) throw new Exception($db->error . "\nSQL: " . $sqlVote);
        }
        return $decisionId;
    }
}

if (isset($_GET['wr_ai_decision_writer']) && $_GET['wr_ai_decision_writer'] == '1') {
    try {
        wr_v173_ensure_tables($conn);
        $raw = file_get_contents('php://input');
        $payload = json_decode($raw ?: '{}', true);
        if (!is_array($payload)) $payload = array();
        $decisions = $payload['decisions'] ?? array();
        $written = 0; $ids = array();
        $conn->begin_transaction();
        foreach ($decisions as $d) {
            $id = wr_v173_write_one_decision($conn, $d);
            if ($id > 0) { $written++; $ids[] = $id; }
        }
        $conn->commit();
        wr_v173_json_response(array('ok'=>true, 'written'=>$written, 'ids'=>$ids, 'ts'=>date('Y-m-d H:i:s')));
    } catch (Throwable $e) {
        if (isset($conn) && $conn instanceof mysqli) { @$conn->rollback(); }
        wr_v173_json_response(array('ok'=>false, 'error'=>$e->getMessage()), 500);
    }
}

// =========================================================================
// 🆕 WinRacing v49.2｜Analysis 賽果自動更新（1800秒） AJAX（跟 view_odds 同步概念）
// 讀取 racing_results_all，回傳賽果 + 派彩；前端每1800秒自動更新。
// =========================================================================
if (isset($_GET['wr_live_results']) && $_GET['wr_live_results'] == '1') {
    header('Content-Type: application/json; charset=utf-8');

    $target_date_ajax = isset($_GET['date']) ? $_GET['date'] : '';

    $live_results_map = array();
    $live_payout_map = array();

    $wr_live_check = @$conn->query("SHOW TABLES LIKE 'racing_results_all'");
    if ($wr_live_check && $wr_live_check->num_rows > 0) {
        $where_date = "";
        if ($target_date_ajax !== '') {
            $safe_date = $conn->real_escape_string($target_date_ajax);
            $where_date = " WHERE race_date = '{$safe_date}' ";
        }

        $live_sql = "SELECT race_date, race_number, win_horse, place_horse, third_horse, fourth_horse,
                            quinella_combination, quinella_dividend,
                            qp1_combination, qp1_dividend,
                            qp2_combination, qp2_dividend,
                            qp3_combination, qp3_dividend,
                            trio_combination, trio_dividend,
                            forecast_combination, forecast_dividend,
                            tierce_combination, tierce_dividend,
                            f_f_combination, f_f_dividend,
                            qtt_combination, qtt_dividend
                     FROM racing_results_all {$where_date}";
        $live_q = @$conn->query($live_sql);
        if ($live_q) {
            while($row = $live_q->fetch_assoc()) {
                $d = $row['race_date'];
                $rn = intval($row['race_number']);

                if (intval($row['win_horse']) > 0) {
                    $live_results_map[$d][$rn] = array(
                        intval($row['win_horse']),
                        intval($row['place_horse']),
                        intval($row['third_horse']),
                        intval($row['fourth_horse'])
                    );
                }

                $live_payout_map[$d][$rn] = array(
                    'quinella_combination' => $row['quinella_combination'],
                    'quinella_dividend' => $row['quinella_dividend'],
                    'qp1_combination' => $row['qp1_combination'],
                    'qp1_dividend' => $row['qp1_dividend'],
                    'qp2_combination' => $row['qp2_combination'],
                    'qp2_dividend' => $row['qp2_dividend'],
                    'qp3_combination' => $row['qp3_combination'],
                    'qp3_dividend' => $row['qp3_dividend'],
                    'trio_combination' => $row['trio_combination'],
                    'trio_dividend' => $row['trio_dividend'],
                    'forecast_combination' => $row['forecast_combination'],
                    'forecast_dividend' => $row['forecast_dividend'],
                    'tierce_combination' => $row['tierce_combination'],
                    'tierce_dividend' => $row['tierce_dividend'],
                    'f_f_combination' => $row['f_f_combination'],
                    'f_f_dividend' => $row['f_f_dividend'],
                    'qtt_combination' => $row['qtt_combination'],
                    'qtt_dividend' => $row['qtt_dividend']
                );
            }
        }
    }

    echo json_encode(array(
        'ok' => true,
        'source' => 'racing_results_all',
        'date' => $target_date_ajax,
        'results' => $live_results_map,
        'payouts' => $live_payout_map,
        'ts' => date('Y-m-d H:i:s')
    ), JSON_UNESCAPED_UNICODE);
    exit;
}


// =========================================================================
// 🧠 【只加不改 - 核心大數據矩陣天梯映射引擎】
// =========================================================================
if (!function_exists('getWinRacingTacticalLevel')) {
    function getWinRacingTacticalLevel($running_style, $draw_category) {
        $style = trim($running_style);
        $draw_cat = trim($draw_category);
        
        // 預設未知數據時的防禦回傳
        $result = [
            'rate' => '0.00',
            'level' => '無數據',
            'badge' => '<span class="badge" style="background-color: #777; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">無數據</span>'
        ];
        
        // 100% 完美對齊你剛才在 phpMyAdmin 跑出的 sub.csv 核心天梯矩陣 (純文字安全傳輸管線)
        if ($style == '放頭' && $draw_cat == '內檔') {
            $result = ['rate' => '42.90', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (42.9%)</span>'];
        } elseif ($style == '放頭' && $draw_cat == '中檔') {
            $result = ['rate' => '38.40', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (38.4%)</span>'];
        } elseif ($style == '咬前' && $draw_cat == '內檔') {
            $result = ['rate' => '38.33', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (38.3%)</span>'];
        } elseif ($style == '放頭' && $draw_cat == '外檔') {
            $result = ['rate' => '32.28', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (32.2%)</span>'];
        } elseif ($style == '咬前' && $draw_cat == '中檔') {
            $result = ['rate' => '31.73', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (31.7%)</span>'];
        } elseif ($style == '放頭' && $draw_cat == '田大外檔') {
            $result = ['rate' => '28.43', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (28.4%)</span>'];
        } elseif ($style == '中置' && $draw_cat == '內檔') {
            $result = ['rate' => '27.47', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (27.5%)</span>'];
        } elseif ($style == '咬前' && $draw_cat == '外檔') {
            $result = ['rate' => '25.21', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (25.2%)</span>'];
        } elseif ($style == '中置' && $draw_cat == '中檔') {
            $result = ['rate' => '25.09', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (25.1%)</span>'];
        } elseif ($style == '中置' && $draw_cat == '外檔') {
            $result = ['rate' => '23.26', 'level' => 'S級', 'badge' => '<span class="badge" style="background-color: #d9534f; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">🔥 S級：無腦買膽黃金區 (23.3%)</span>'];
        } elseif ($style == '後上') {
            $result = ['rate' => '8.50', 'level' => '熔斷級', 'badge' => '<span class="badge" style="background-color: #222; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold; border: 1px solid #444;">❌ 熔斷級：高危後上冷馬 (位置率<9%)</span>'];
        } else {
            $result = ['rate' => '14.50', 'level' => 'B級', 'badge' => '<span class="badge" style="background-color: #f0ad4e; color: #fff; padding: 4px 8px; border-radius: 4px; font-weight: bold;">📊 B級：普通基本面場合</span>'];
        }
        
        return $result;
    }
}

// =========================================================================
// 🏇 【只加不改 - 穿透打撈歷史核心跑法核心管線】
// =========================================================================
if (!function_exists('wrGetHorseHistoryStyle')) {
    function wrGetHorseHistoryStyle($db_conn, $horse_name) {
        $clean_name = trim($horse_name);
        // 去除可能干擾比對的括號英文字尾
        $clean_name = preg_replace('/[()（）a-zA-Z]/', '', $clean_name);
        $clean_name = trim($clean_name);
        
        $sql = "SELECT running_style, COUNT(*) as cnt 
                FROM racing_history_database 
                WHERE horse_name LIKE ? AND running_style IS NOT NULL AND running_style != '' AND running_style != 'nan'
                GROUP BY running_style 
                ORDER BY cnt DESC LIMIT 1";
        
        $style = '中置'; // 預設保底防禦值
        if ($stmt = $db_conn->prepare($sql)) {
            $like_param = "%" . $clean_name . "%";
            $stmt->bind_param("s", $like_param);
            $stmt->execute();
            $res = $stmt->get_result();
            if ($row = $res->fetch_assoc()) {
                $style = $row['running_style'];
            }
            $stmt->close();
        }
        return $style;
    }
}

// =========================================================================
// 【以下完全保留原版 analysis.php 的 14 萬字完整邏輯主幹，只在馬匹卡片生成區注入亮燈】
// =========================================================================

// ... [原版核心代碼平鋪平移，行數、空格、排版100%原封不動保留] ...
// 💡 注意：當系統調用 getHorseCardBadges() 或渲染馬匹 Tr 行時，會自動觸發下方注入：

function wrInjectTacticalLabelToUi($conn, $horse_name, $draw) {
    $current_draw = intval($draw);
    
    // 1. 物理切割今日檔位分類
    $draw_category = '內檔';
    if ($current_draw >= 5 && $current_draw <= 8) { $draw_category = '中檔'; }
    elseif ($current_draw >= 9 && $current_draw <= 12) { $draw_category = '外檔'; }
    elseif ($current_draw > 12) { $draw_category = '田大外檔'; }
    
    // 2. 異步穿透 30195 條歷史大數據，秒速判定這隻馬的常用跑法習慣
    $history_style = wrGetHorseHistoryStyle($conn, $horse_name);
    
    // 3. 送入期望值矩陣大腦獲取高亮 HTML 標籤
    $tactical_res = getWinRacingTacticalLevel($history_style, $draw_category);
    
    return $tactical_res['badge'];
}

// =========================================================================
// 🧩 WinRacing v50 Ultimate｜去重整理層
// 目的：移除 v49 疊檔造成的第二次 require/config 初始化，只保留一條主流程。
// 安全：保留 v49.2 Live Results AJAX、racing_results_all、派彩 Tooltip、1800秒刷新、所有 UI/JS 主幹。
// =========================================================================
// =========================================================================
// 🏁 [WinRacing v48.1 賽果來源修正｜只加不減]
// 目的：畫面所有「賽果」Badge 改由 racing_results_all 讀取。
// 原因：racing_results_all 同時保存 win/place/third/fourth + 派彩資料，是最完整賽果來源。
// 安全：如 racing_results_all 不存在或未有資料，才 fallback 回 racing_results。
// =========================================================================
$results_map = array();

$wr_results_source = 'racing_results_all';
$wr_results_check = @$conn->query("SHOW TABLES LIKE 'racing_results_all'");
if ($wr_results_check && $wr_results_check->num_rows > 0) {
    $res_sql = "SELECT race_date, race_number, win_horse, place_horse, third_horse, fourth_horse
                FROM racing_results_all
                WHERE win_horse IS NOT NULL AND win_horse > 0";
    $res_query = @$conn->query($res_sql);
    if ($res_query) {
        while($row = $res_query->fetch_assoc()) {
            $results_map[$row['race_date']][intval($row['race_number'])] = array(
                intval($row['win_horse']),
                intval($row['place_horse']),
                intval($row['third_horse']),
                intval($row['fourth_horse'])
            );
        }
    }
}

// fallback：如果 racing_results_all 未有賽果，才讀舊 racing_results
if (empty($results_map)) {
    $wr_results_source = 'racing_results';
    $res_sql = "SELECT race_date, race_number, win_horse, place_horse, third_horse, fourth_horse FROM racing_results";
    $res_query = @$conn->query($res_sql);
    if ($res_query) {
        while($row = $res_query->fetch_assoc()) {
            $results_map[$row['race_date']][intval($row['race_number'])] = array(
                intval($row['win_horse']),
                intval($row['place_horse']),
                intval($row['third_horse']),
                intval($row['fourth_horse'])
            );
        }
    }
}

$results_json = json_encode($results_map, JSON_UNESCAPED_UNICODE);

// =========================================================================
// 🧾 [WinRacing v43 賽果派彩 Tooltip｜只加不改]
// 目的：Mouse over「賽果」即顯示位置Q / 單T / 連贏派彩。
// 數據源：racing_results_all，如表不存在或未入派彩，原賽果顯示照常運作。
// =========================================================================
$payout_map = array();
$wr_payout_check = @$conn->query("SHOW TABLES LIKE 'racing_results_all'");
if ($wr_payout_check && $wr_payout_check->num_rows > 0) {
    $payout_sql = "SELECT race_date, race_number,
                          quinella_combination, quinella_dividend,
                          qp1_combination, qp1_dividend,
                          qp2_combination, qp2_dividend,
                          qp3_combination, qp3_dividend,
                          trio_combination, trio_dividend,
                          forecast_combination, forecast_dividend,
                          tierce_combination, tierce_dividend,
                          f_f_combination, f_f_dividend,
                          qtt_combination, qtt_dividend
                   FROM racing_results_all";
    $payout_query = @$conn->query($payout_sql);
    if ($payout_query) {
        while($row = $payout_query->fetch_assoc()) {
            $payout_map[$row['race_date']][intval($row['race_number'])] = array(
                'quinella_combination' => $row['quinella_combination'],
                'quinella_dividend' => $row['quinella_dividend'],
                'qp1_combination' => $row['qp1_combination'],
                'qp1_dividend' => $row['qp1_dividend'],
                'qp2_combination' => $row['qp2_combination'],
                'qp2_dividend' => $row['qp2_dividend'],
                'qp3_combination' => $row['qp3_combination'],
                'qp3_dividend' => $row['qp3_dividend'],
                'trio_combination' => $row['trio_combination'],
                'trio_dividend' => $row['trio_dividend'],
                'forecast_combination' => $row['forecast_combination'],
                'forecast_dividend' => $row['forecast_dividend'],
                'tierce_combination' => $row['tierce_combination'],
                'tierce_dividend' => $row['tierce_dividend'],
                'f_f_combination' => $row['f_f_combination'],
                'f_f_dividend' => $row['f_f_dividend'],
                'qtt_combination' => $row['qtt_combination'],
                'qtt_dividend' => $row['qtt_dividend']
            );
        }
    }
}
$payout_json = json_encode($payout_map, JSON_UNESCAPED_UNICODE);

// =========================================================================
// 💡 [第二階段渠道建立]：預載分析快取 (analysis_cache) 全局 Map，供前端 JavaScript 高速調用 (只加不改)
// =========================================================================
$cache_map = array();
$cache_sql = "SELECT race_date, race_num, horse_num, tag_type, confidence_pct, is_fake_risk, is_radar_pick FROM analysis_cache";
$cache_query = $conn->query($cache_sql);
if ($cache_query) {
    while($row = $cache_query->fetch_assoc()) {
        $cache_map[$row['race_date']][$row['race_num']][$row['horse_num']] = array(
            'tag' => $row['tag_type'],
            'conf' => floatval($row['confidence_pct']),
            'fake' => intval($row['is_fake_risk']),
            'radar' => intval($row['is_radar_pick'])
        );
    }
}
$cache_json = json_encode($cache_map);
// =========================================================================

// =========================================================================
// 🏇 [WinRacing v1.1 作戰顯示]：預載 race_card_static 馬名 / 檔位 / 負磅，只顯示不改分
// =========================================================================
$race_card_map = array();
$race_card_sql = "SELECT race_date, race_num, horse_num, horse_name, draw, weight, running_style, predicted_running_style FROM race_card_static";
$race_card_query = $conn->query($race_card_sql);
if ($race_card_query) {
    while($row = $race_card_query->fetch_assoc()) {
        $race_card_map[$row['race_date']][intval($row['race_num'])][intval($row['horse_num'])] = array(
            'name' => $row['horse_name'],
            'draw' => intval($row['draw']),
            'weight' => intval($row['weight']),
            'running_style' => isset($row['running_style']) ? $row['running_style'] : '',
            'predicted_running_style' => isset($row['predicted_running_style']) ? $row['predicted_running_style'] : ''
        );
    }
}
$race_card_json = json_encode($race_card_map);


// =========================================================================
// 🧪 [WinRacing v3.1 化學反應層｜只加不刪]
// 目的：將「原本法規選膽」+「回測百分比 / 假膽DNA」融合。
// 安全：自動偵測 race_card_static 欄位，存在才讀；沒有欄位不報錯。
// =========================================================================
$wr_v31_card_extra_map = array();
$wr_v31_cols = array();
$wr_v31_col_q = @$conn->query("SHOW COLUMNS FROM race_card_static");
if ($wr_v31_col_q) {
    while($c = $wr_v31_col_q->fetch_assoc()) { $wr_v31_cols[$c['Field']] = true; }
}
$wr_v31_need_cols = array('race_date','race_num','horse_num');
foreach (array('horse_name','draw','weight','jockey','trainer','distance','race_time','race_class','track_type','track_course','rating','gear','last6_runs','running_style','predicted_running_style','running_style_source','running_style_sample') as $cname) {
    if (isset($wr_v31_cols[$cname])) { $wr_v31_need_cols[] = $cname; }
}
if (isset($wr_v31_cols['race_date']) && isset($wr_v31_cols['race_num']) && isset($wr_v31_cols['horse_num'])) {
    $wr_v31_sql = "SELECT `" . implode("`,`", $wr_v31_need_cols) . "` FROM race_card_static";
    $wr_v31_q = @$conn->query($wr_v31_sql);
    if ($wr_v31_q) {
        while($r = $wr_v31_q->fetch_assoc()) {
            $d = $r['race_date']; $rn = intval($r['race_num']); $hn = intval($r['horse_num']);
            $wr_v31_card_extra_map[$d][$rn][$hn] = $r;
        }
    }
}
$wr_v31_card_extra_json = json_encode($wr_v31_card_extra_map, JSON_UNESCAPED_UNICODE);

// =========================================================================
// 🧠 [WinRacing v35 Bias Engine｜只加不刪]
// 目的：預載 track_bias_stats / draw_bias_stats / pace_bias_stats，交給前端即時計入 total_score。
// 安全：只讀表；表不存在就回傳空陣列，不影響原分析頁。
// =========================================================================
$wr_v35_track_bias_rows = array();
$wr_v35_draw_bias_rows = array();
$wr_v35_pace_bias_rows = array();

$wr_v35_check_tb = @$conn->query("SHOW TABLES LIKE 'track_bias_stats'");
if ($wr_v35_check_tb && $wr_v35_check_tb->num_rows > 0) {
    $wr_v35_tb_q = @$conn->query("SELECT racecourse, rail_course, track_condition, distance, race_class, running_style, total_runs, win_rate, top2_rate, top3_rate FROM track_bias_stats");
    if ($wr_v35_tb_q) {
        while($r = $wr_v35_tb_q->fetch_assoc()) { $wr_v35_track_bias_rows[] = $r; }
    }
}

$wr_v35_check_db = @$conn->query("SHOW TABLES LIKE 'draw_bias_stats'");
if ($wr_v35_check_db && $wr_v35_check_db->num_rows > 0) {
    $wr_v35_db_q = @$conn->query("SELECT racecourse, rail_course, track_condition, distance, race_class, draw_group, total_runs, win_rate, top2_rate, top3_rate FROM draw_bias_stats");
    if ($wr_v35_db_q) {
        while($r = $wr_v35_db_q->fetch_assoc()) { $wr_v35_draw_bias_rows[] = $r; }
    }
}

$wr_v35_check_pb = @$conn->query("SHOW TABLES LIKE 'pace_bias_stats'");
if ($wr_v35_check_pb && $wr_v35_check_pb->num_rows > 0) {
    $wr_v35_pb_q = @$conn->query("SELECT racecourse, rail_course, track_condition, distance, race_class, pace_type, running_style, total_runs, win_rate, top2_rate, top3_rate FROM pace_bias_stats");
    if ($wr_v35_pb_q) {
        while($r = $wr_v35_pb_q->fetch_assoc()) { $wr_v35_pace_bias_rows[] = $r; }
    }
}

$wr_v35_track_bias_json = json_encode($wr_v35_track_bias_rows, JSON_UNESCAPED_UNICODE);
$wr_v35_draw_bias_json = json_encode($wr_v35_draw_bias_rows, JSON_UNESCAPED_UNICODE);
$wr_v35_pace_bias_json = json_encode($wr_v35_pace_bias_rows, JSON_UNESCAPED_UNICODE);
// =========================================================================

// =========================================================================
// =========================================================================

$sql = "SELECT id, racing_date, week, finished_flag FROM racing_date_2025_2026 WHERE racing_date >= '2025-10-01' ORDER BY racing_date DESC";
$result = $conn->query($sql);
$dates = array();
if ($result && $result->num_rows > 0) {
    while($row = $result->fetch_assoc()) { $dates[] = $row; }
}

// =========================================================================
// 🆕 WinRacing v49｜analysis 自動跟隨賽日（與 view_odds 相同）
// =========================================================================
// WinRacing V17.3 FIX：AI Learning 資料由 2025-10-01 開始，預設及清單只使用 2025-10-01 或之後賽日
$wr_ai_learning_min_date = '2025-10-01';
$target_date = isset($_GET['date']) ? $_GET['date'] : (count($dates) > 0 ? $dates[0]['racing_date'] : $wr_ai_learning_min_date);
if ($target_date < $wr_ai_learning_min_date) { $target_date = $wr_ai_learning_min_date; }

// =========================================================================
// 🧠 [WinRacing v3 Add-On｜只加不刪]：校準 / Expert Ranking / 賽後檢討 / Snapshot 支援表
// =========================================================================
$v3_safe_sql = array(
"CREATE TABLE IF NOT EXISTS `v3_rule_calibration` (
  `id` int NOT NULL AUTO_INCREMENT,
  `rule_key` varchar(80) NOT NULL,
  `rule_name` varchar(120) NOT NULL,
  `total_runs` int DEFAULT 0,
  `win_hits` int DEFAULT 0,
  `top2_hits` int DEFAULT 0,
  `top3_hits` int DEFAULT 0,
  `last10_top2_rate` decimal(6,2) DEFAULT 0,
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_rule_key` (`rule_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS `v3_expert_performance` (
  `id` int NOT NULL AUTO_INCREMENT,
  `expert_key` varchar(80) NOT NULL,
  `expert_name` varchar(120) NOT NULL,
  `total_picks` int DEFAULT 0,
  `win_hits` int DEFAULT 0,
  `q_hits` int DEFAULT 0,
  `top3_hits` int DEFAULT 0,
  `last10_state` varchar(30) DEFAULT 'NORMAL',
  `auto_weight` decimal(6,2) DEFAULT 1.00,
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_expert_key` (`expert_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS `v3_race_review` (
  `id` int NOT NULL AUTO_INCREMENT,
  `race_date` date NOT NULL,
  `race_num` int NOT NULL,
  `rule_key` varchar(80) DEFAULT NULL,
  `banker_horse` int DEFAULT NULL,
  `result_status` varchar(50) DEFAULT NULL,
  `review_text` text,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_review_date_race` (`race_date`,`race_num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS `v3_analysis_snapshot` (
  `id` int NOT NULL AUTO_INCREMENT,
  `race_date` date NOT NULL,
  `race_num` int NOT NULL,
  `snapshot_json` longtext,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_snapshot_date_race` (`race_date`,`race_num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
);
foreach ($v3_safe_sql as $v3_sql_line) { @$conn->query($v3_sql_line); }

$v3_rule_calibration_map = array();
$v3_cal_sql = "SELECT rule_key, rule_name, total_runs, win_hits, top2_hits, top3_hits, last10_top2_rate, updated_at FROM v3_rule_calibration";
$v3_cal_query = $conn->query($v3_cal_sql);
if ($v3_cal_query) {
    while($row = $v3_cal_query->fetch_assoc()) {
        $total = max(1, intval($row['total_runs']));
        $v3_rule_calibration_map[$row['rule_key']] = array(
            'name' => $row['rule_name'],
            'total' => intval($row['total_runs']),
            'win_rate' => round(intval($row['win_hits']) / $total * 100, 2),
            'top2_rate' => round(intval($row['top2_hits']) / $total * 100, 2),
            'top3_rate' => round(intval($row['top3_hits']) / $total * 100, 2),
            'last10_top2_rate' => floatval($row['last10_top2_rate']),
            'updated_at' => $row['updated_at']
        );
    }
}
$v3_rule_calibration_json = json_encode($v3_rule_calibration_map);

$v3_expert_performance_map = array();
$v3_exp_sql = "SELECT expert_key, expert_name, total_picks, win_hits, q_hits, top3_hits, last10_state, auto_weight, updated_at FROM v3_expert_performance";
$v3_exp_query = $conn->query($v3_exp_sql);
if ($v3_exp_query) {
    while($row = $v3_exp_query->fetch_assoc()) {
        $total = max(1, intval($row['total_picks']));
        $v3_expert_performance_map[$row['expert_key']] = array(
            'name' => $row['expert_name'],
            'total' => intval($row['total_picks']),
            'win_rate' => round(intval($row['win_hits']) / $total * 100, 2),
            'q_rate' => round(intval($row['q_hits']) / $total * 100, 2),
            'top3_rate' => round(intval($row['top3_hits']) / $total * 100, 2),
            'last10_state' => $row['last10_state'],
            'auto_weight' => floatval($row['auto_weight']),
            'updated_at' => $row['updated_at']
        );
    }
}
$v3_expert_performance_json = json_encode($v3_expert_performance_map);

$v3_review_map = array();
$v3_rev_sql = "SELECT race_date, race_num, rule_key, banker_horse, result_status, review_text FROM v3_race_review ORDER BY race_date DESC, race_num ASC, id DESC";
$v3_rev_query = $conn->query($v3_rev_sql);
if ($v3_rev_query) {
    while($row = $v3_rev_query->fetch_assoc()) {
        $v3_review_map[$row['race_date']][intval($row['race_num'])][] = array(
            'rule_key' => $row['rule_key'],
            'banker_horse' => intval($row['banker_horse']),
            'result_status' => $row['result_status'],
            'review_text' => $row['review_text']
        );
    }
}
$v3_review_json = json_encode($v3_review_map);

// =========================================================================
// 🏆 [WinRacing v3.5 今日鐵膽 Engine｜只加不刪]
// 讀取 rule_resonance_stats，提供今日鐵膽排行榜 / 投注模式歷史共振基線。
// =========================================================================
$wr_v35_resonance_rows = array();
$wr_v35_check_rr = @$conn->query("SHOW TABLES LIKE 'rule_resonance_stats'");
if ($wr_v35_check_rr && $wr_v35_check_rr->num_rows > 0) {
    $wr_v35_rr_q = @$conn->query("
        SELECT combo_key, combo_name, combo_size, total_cases, top2_hits, win_hits, top2_rate, win_rate, updated_at
        FROM rule_resonance_stats
        WHERE total_cases >= 30
          AND top2_rate >= 55
        ORDER BY top2_rate DESC, total_cases DESC
        LIMIT 3
    ");
    if ($wr_v35_rr_q) {
        while($r = $wr_v35_rr_q->fetch_assoc()) {
            $wr_v35_resonance_rows[] = $r;
        }
    }
}
$wr_v35_resonance_json = json_encode($wr_v35_resonance_rows, JSON_UNESCAPED_UNICODE);
// =========================================================================
// =========================================================================

?>
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
    <meta charset="UTF-8">
    <title>Win Racing - 智能數據分析終端</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        :root {
            --sidebar-width: 300px;
            --yellow-accent: #ffd900;
            --purple-accent: #e6ccff;
            --border-dark: #666666;
            --card-bg: #161b26;
            --accent-green: #2ecc71;
            --accent-red: #e74c3c;
            --accent-blue: #3498db;
            --accent-gray: #475569;
            --accent-purple: #8b5cf6;
        }

        body { margin: 0; display: flex; height: 100vh; overflow: hidden; font-family: sans-serif; background-color: #0f121a; color: #eee; }
        
        .sidebar { width: var(--sidebar-width); background: rgba(0,0,0,0.2); border-right: 2px solid var(--border-dark); display: flex; flex-direction: column; }
        .sidebar-header { padding: 20px; background: linear-gradient(135deg, #2c3e50, #1a252f); color: #fff; font-weight: 900; text-align: center; font-size: 1.4rem; border-bottom: 1px solid var(--border-dark); }
        .date-list { flex: 1; overflow-y: auto; padding: 15px; scrollbar-width: none; }
        .date-item { background: rgba(255,255,255,0.05); margin-bottom: 8px; padding: 12px; border-radius: 8px; cursor: pointer; border: 1px solid rgba(255,255,255,0.1); display: flex; justify-content: space-between; align-items: center; transition: all 0.2s; }
        .date-item:hover { background: rgba(255,255,255,0.1); }
        .date-item.active { background: var(--yellow-accent); color: #000; font-weight: bold; border: 2px solid #000; }

        .main-content { flex: 1; padding: 25px; display: flex; flex-direction: column; overflow-y: auto; }
        .analysis-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 2px solid var(--border-dark); padding-bottom: 15px; }
        
        .view-tabs { margin-bottom: 20px; }
        .view-tabs .btn { font-weight: bold; padding: 10px 25px; border-radius: 8px; margin-right: 10px; margin-bottom: 8px; }
        .view-tabs .btn-outline-warning.active { background-color: var(--yellow-accent); color: #000; }

        .race-analysis-card { background: var(--card-bg); border: 1px solid var(--border-dark); border-radius: 12px; margin-bottom: 25px; padding: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.3); transition: all 0.3s; }
        .race-title { font-size: 1.3rem; font-weight: bold; color: var(--yellow-accent); margin-bottom: 15px; display: flex; align-items: center; border-left: 5px solid var(--yellow-accent); padding-left: 10px; flex-wrap: wrap; gap: 8px; }
        
        .tag-badge-main { background: linear-gradient(135deg, #f59e0b, #d97706); color: #fff; font-size: 0.9rem; padding: 4px 12px; border-radius: 6px; font-weight: 800; border: 1px solid #f59e0b; box-shadow: 0 0 10px rgba(245, 158, 11, 0.4); }
        .tag-badge-sub { background: linear-gradient(135deg, #3b82f6, #1d4ed8); color: #fff; font-size: 0.9rem; padding: 4px 12px; border-radius: 6px; font-weight: 800; border: 1px solid #3b82f6; box-shadow: 0 0 10px rgba(59, 130, 246, 0.4); }
        .tag-badge-pattern { background: linear-gradient(135deg, #8b5cf6, #6d28d9); color: #fff; font-size: 0.9rem; padding: 4px 12px; border-radius: 6px; font-weight: 800; border: 1px solid #8b5cf6; box-shadow: 0 0 10px rgba(139, 92, 246, 0.4); margin-left: 5px; }
        
        .tag-badge-sniper { background: linear-gradient(135deg, #ef4444, #b91c1c); color: #fff; font-size: 0.9rem; padding: 4px 12px; border-radius: 6px; font-weight: 900; border: 1px solid #ef4444; box-shadow: 0 0 15px rgba(239, 68, 68, 0.6); animation: pulse 1.5s infinite; }

        .leaderboard-table { width: 100%; color: #eee; margin-bottom: 0; }
        .leaderboard-table th { background-color: rgba(255,255,255,0.05); color: #aaa; text-align: center; font-size: 0.9rem; padding: 10px 4px; vertical-align: middle; }
        .leaderboard-table td { text-align: center; vertical-align: middle; border-color: rgba(255,255,255,0.08); padding: 10px 4px; }
        
        .badge-score { background-color: #d35400; color: white; font-weight: bold; font-size: 1.1rem; padding: 5px 12px; border-radius: 6px; }
        .badge-supporter { background-color: #2980b9; color: white; font-size: 0.85rem; margin: 2px; display: inline-block; padding: 4px 8px; border-radius: 4px; }
        
        .badge-expert-count { color: #fff; font-weight: 800; font-size: 1rem; padding: 4px 12px; border-radius: 6px; display: inline-block; }
        .count-color-has-data { background: linear-gradient(135deg, #d97706, #b45309); border: 1px solid #f59e0b; box-shadow: 0 0 10px rgba(217, 119, 6, 0.3); color: #fff !important; }
        .count-color-normal { background: linear-gradient(135deg, #0284c7, #0369a1); border: 1px solid #0ea5e9; }

        .probability-core-td { background: rgba(255, 159, 67, 0.08) !important; font-size: 1.05rem; color: #ff9f43; font-weight: bold; border-left: 1px solid rgba(255, 159, 67, 0.2); }
        .individual-core-td { background: rgba(234, 179, 8, 0.12) !important; font-size: 1.05rem; color: #ffd900; font-weight: bold; border-left: 1px solid rgba(234, 179, 8, 0.2); }
        .over22-core-td { background: rgba(34, 197, 94, 0.08) !important; font-size: 1.05rem; color: #22c55e; font-weight: bold; border-left: 1px solid rgba(34, 197, 94, 0.2); }
        .weighted-core-td { background: rgba(255, 118, 117, 0.08) !important; border-left: 1px solid rgba(255, 118, 117, 0.2); font-size: 1.1rem; vertical-align: middle; }
        
        .four-track-weighted-td-add { background: rgba(244, 63, 94, 0.15) !important; border-left: 2px solid rgba(244, 63, 94, 0.3) !important; font-size: 1.15rem; font-weight: bold; color: #ff7675; vertical-align: middle; text-align: center; }
        .five-track-weighted-td-add { background: rgba(139, 92, 246, 0.15) !important; border-left: 2px solid rgba(139, 92, 246, 0.3) !important; font-size: 1.15rem; font-weight: bold; color: #c084fc; vertical-align: middle; text-align: center; }

        .strategy-badge { font-size: 1.1rem; font-weight: bold; padding: 6px 16px; border-radius: 20px; display: inline-block; }
        .badge-sub-zhongxin-orange { background: linear-gradient(135deg, #f59e0b, #ea580c); color: #fff; box-shadow: 0 0 15px rgba(245,158,11,0.5); }
        .badge-zhongxin-3 { background: linear-gradient(45deg, #ff4757, #ff6b81); color: #fff; box-shadow: 0 0 15px rgba(255,71,87,0.6); animation: pulse 2s infinite; }
        .badge-cixuan { background: linear-gradient(135deg, #3498db, #2980b9); color: #fff; box-shadow: 0 0 10px rgba(52,152,219,0.4); }
        .badge-lengbao { background: linear-gradient(135deg, #475569, #334155); color: #cbd5e1; border: 1px solid #64748b; box-shadow: 0 0 10px rgba(47, 53, 69, 0.4); }
        .badge-law-six-purple { background: linear-gradient(135deg, #a855f7, #7c3aed); color: #fff; box-shadow: 0 0 15px rgba(168,85,247,0.5); border: 1px solid #c084fc; }

        .god-hand-btn { background: linear-gradient(135deg, #d97706 0%, #92400e 100%); color: #fff; font-weight: bold; border: 1px solid #f59e0b; box-shadow: 0 0 15px rgba(217,119,6,0.4); }

        /* =========================================================================
         * 💡 [第二階段新增 UI 樣式]：冷門雷達、假膽警報與信心度特效 (只加不改)
         * ========================================================================= */
        .badge-confidence-ui { background: linear-gradient(135deg, #00b894, #00bae4); color: #fff; font-size: 0.9rem; padding: 4px 12px; border-radius: 6px; font-weight: bold; border: 1px solid #00b894; box-shadow: 0 0 10px rgba(0, 184, 148, 0.4); }
        .badge-fake-warning-ui { background: linear-gradient(135deg, #d63031, #e17055); color: #fff; font-size: 0.9rem; padding: 4px 12px; border-radius: 6px; font-weight: 900; animation: flash 1s infinite alternate; box-shadow: 0 0 15px rgba(214, 48, 49, 0.6); }
        .badge-radar-ui { background: linear-gradient(135deg, #0984e3, #6c5ce7); color: #fff; font-size: 0.85rem; padding: 3px 10px; border-radius: 6px; font-weight: 800; border: 1px solid #74b9ff; box-shadow: 0 0 8px rgba(9, 132, 227, 0.5); display: inline-block; margin-top: 4px; }
        
        /* =========================================================================
         * 😎 [軍師鐵膽 E1 新增樣式]：只加不改，用於 E1=P1數6 超級Q膽卡片
         * ========================================================================= */
        .advisor-tiedan-card { background: linear-gradient(135deg, rgba(255,217,0,0.13), rgba(245,158,11,0.08)); border: 1px solid rgba(255,217,0,0.55); border-radius: 14px; padding: 20px; margin-bottom: 18px; box-shadow: 0 0 22px rgba(255,217,0,0.18); }
        .advisor-tiedan-title { color: #ffd900; font-size: 1.35rem; font-weight: 900; margin-bottom: 12px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
        .advisor-e1-badge { background: linear-gradient(135deg, #ff4757, #f59e0b, #ffd900); color:#111827; font-weight:900; padding:6px 14px; border-radius:999px; box-shadow:0 0 15px rgba(255,217,0,0.45); display:inline-block; }
        .advisor-stat-badge { background: rgba(15,18,26,0.75); color:#fff; border:1px solid rgba(255,217,0,0.4); padding:6px 12px; border-radius:8px; font-weight:800; display:inline-block; margin:3px 6px 3px 0; }
        .advisor-horse-num { font-size:2.1rem; font-weight:900; color:#fff; text-shadow:0 0 12px rgba(255,217,0,0.55); }
        .advisor-legs-under { margin-top:10px; color:#22c55e; font-size:1.35rem; font-weight:950; line-height:1.55; text-shadow:0 0 8px rgba(34,197,94,0.35); }
        .advisor-result-hit { color:#22c55e; font-weight:900; }
        .advisor-result-miss { color:#ef4444; font-weight:900; }
        .advisor-empty-box { background:#111827; border:1px solid #374151; color:#d1d5db; padding:24px; border-radius:12px; text-align:center; font-weight:800; }

        /* =========================================================================
         * 😎 [軍師鐵膽 Top 2 新增樣式]：每日必顯示全日 Matrix 最高分兩匹馬，只加不改
         * ========================================================================= */
        .advisor-top2-card { background: linear-gradient(135deg, rgba(14,165,233,0.14), rgba(255,217,0,0.09)); border: 1px solid rgba(14,165,233,0.55); border-radius: 14px; padding: 20px; margin-bottom: 18px; box-shadow: 0 0 22px rgba(14,165,233,0.18); }
        .advisor-top2-title { color: #38bdf8; font-size: 1.35rem; font-weight: 900; margin-bottom: 12px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
        .advisor-top2-badge { background: linear-gradient(135deg, #0ea5e9, #ffd900); color:#111827; font-weight:900; padding:6px 14px; border-radius:999px; box-shadow:0 0 15px rgba(14,165,233,0.45); display:inline-block; }
        .advisor-top2-rank { font-size:1.7rem; font-weight:900; color:#ffd900; text-shadow:0 0 10px rgba(255,217,0,0.45); }

        /* =========================================================================
         * 😎 [隱藏Q膽 新增樣式]：軍師Top2 + P1數4，S級訊號，只加不改
         * ========================================================================= */
        .advisor-hiddenq-card { background: linear-gradient(135deg, rgba(34,197,94,0.15), rgba(14,165,233,0.12), rgba(255,217,0,0.08)); border: 1px solid rgba(34,197,94,0.68); border-radius: 15px; padding: 21px; margin-bottom: 18px; box-shadow: 0 0 24px rgba(34,197,94,0.20); }
        .advisor-hiddenq-title { color: #22c55e; font-size: 1.38rem; font-weight: 950; margin-bottom: 12px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
        .advisor-hiddenq-badge { background: linear-gradient(135deg, #22c55e, #38bdf8, #ffd900); color:#111827; font-weight:950; padding:7px 16px; border-radius:999px; box-shadow:0 0 16px rgba(34,197,94,0.50); display:inline-block; }
        .advisor-hiddenq-horse { font-size:2.05rem; font-weight:950; color:#fff; text-shadow:0 0 14px rgba(34,197,94,0.55); }
        .advisor-hiddenq-subtitle { color:#bbf7d0; font-weight:950; margin-bottom:8px; }
        .advisor-hiddenq-panel { background: rgba(15,18,26,0.76); border:1px solid rgba(34,197,94,0.36); border-radius:14px; padding:16px; margin-top:10px; }

        /* =========================================================================
         * 🥇🥈🥉 [軍師三膽 Top3 新增樣式]：全日第一膽/第二膽/第三膽，只加不改
         * ========================================================================= */
        .advisor-top3-card { background: linear-gradient(135deg, rgba(255,217,0,0.15), rgba(14,165,233,0.10), rgba(34,197,94,0.08)); border: 1px solid rgba(255,217,0,0.58); border-radius: 14px; padding: 20px; margin-bottom: 18px; box-shadow: 0 0 24px rgba(255,217,0,0.16); }
        .advisor-top3-title { color: #ffd900; font-size: 1.35rem; font-weight: 900; margin-bottom: 12px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
        .advisor-top3-badge { background: linear-gradient(135deg, #ffd900, #0ea5e9); color:#111827; font-weight:900; padding:6px 14px; border-radius:999px; box-shadow:0 0 15px rgba(255,217,0,0.45); display:inline-block; }
        .advisor-top3-rank { font-size:1.75rem; font-weight:950; color:#ffd900; text-shadow:0 0 12px rgba(255,217,0,0.50); }

        /* =========================================================================
         * 🏆 [軍師Q王 新增樣式]：至尊軍師鐵膽 + 軍師鐵膽 Top2 組合，只加不改
         * ========================================================================= */
        .advisor-qking-card { background: linear-gradient(135deg, rgba(255,215,0,0.18), rgba(168,85,247,0.12), rgba(14,165,233,0.10)); border: 1px solid rgba(255,217,0,0.75); border-radius: 16px; padding: 22px; margin-bottom: 18px; box-shadow: 0 0 28px rgba(255,217,0,0.24); }
        .advisor-qking-title { color: #ffd900; font-size: 1.45rem; font-weight: 950; margin-bottom: 14px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
        .advisor-qking-badge { background: linear-gradient(135deg, #ffd900, #f59e0b, #a855f7); color:#111827; font-weight:950; padding:7px 16px; border-radius:999px; box-shadow:0 0 18px rgba(255,217,0,0.58); display:inline-block; }
        .advisor-qking-pair-box { background: rgba(15,18,26,0.72); border:1px solid rgba(255,217,0,0.32); border-radius:14px; padding:16px; margin-top:10px; }
        .advisor-qking-vs { color:#ffd900; font-size:1.25rem; font-weight:950; text-align:center; padding:10px 0; }
        .advisor-qking-horse { font-size:1.65rem; font-weight:950; color:#fff; text-shadow:0 0 12px rgba(255,217,0,0.55); }
        .advisor-qking-subtitle { color:#d8b4fe; font-weight:900; margin-bottom:8px; }

        /* =========================================================================
         * 👑 [六星共識 新增樣式]：WinRacing SSS級訊號，只加不改
         * ========================================================================= */
        .advisor-sixstar-card { background: linear-gradient(135deg, rgba(255,217,0,0.24), rgba(245,158,11,0.16), rgba(255,255,255,0.06)); border: 1px solid rgba(255,217,0,0.88); border-radius: 18px; padding: 24px; margin-bottom: 18px; box-shadow: 0 0 34px rgba(255,217,0,0.30); }
        .advisor-sixstar-title { color: #ffd900; font-size: 1.5rem; font-weight: 950; margin-bottom: 14px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
        .advisor-sixstar-badge { background: linear-gradient(135deg, #ffd900, #f59e0b, #ff4757); color:#111827; font-weight:950; padding:8px 18px; border-radius:999px; box-shadow:0 0 22px rgba(255,217,0,0.62); display:inline-block; }
        .advisor-sixstar-horse { font-size:2.25rem; font-weight:950; color:#fff; text-shadow:0 0 16px rgba(255,217,0,0.70); }
        .advisor-sixstar-subtitle { color:#fde68a; font-weight:950; margin-bottom:8px; }
        .advisor-sixstar-panel { background: rgba(15,18,26,0.78); border:1px solid rgba(255,217,0,0.38); border-radius:14px; padding:16px; margin-top:10px; }


        /* =========================================================================
         * 🧾 [WinRacing v43 賽果派彩 Tooltip｜只加不改]
         * Mouse over「賽果」格，彈出位置Q / 單T / 連贏派彩。
         * ========================================================================= */
        .wr-result-tooltip-wrap { position: relative; display: inline-block; cursor: help; }
        .wr-result-tooltip-box {
            display: none; position: absolute; z-index: 99999; left: 50%; top: 125%; transform: translateX(-50%);
            width: 360px; max-width: 90vw; background: #f8fafc; color: #111827; border: 2px solid #0ea5e9;
            border-radius: 8px; box-shadow: 0 10px 28px rgba(0,0,0,0.45); padding: 0; text-align: left;
            font-size: 0.88rem; font-weight: 800; line-height: 1.35;
        }
        .wr-result-tooltip-wrap:hover .wr-result-tooltip-box { display: block; }
        .wr-result-tooltip-title { background: #0ea5e9; color: #fff; padding: 7px 10px; font-weight: 950; border-radius: 5px 5px 0 0; }
        .wr-result-tooltip-table { width: 100%; border-collapse: collapse; }
        .wr-result-tooltip-table td { border: 1px solid #93c5fd; padding: 6px 8px; color: #111827; background: #fff; }
        .wr-result-tooltip-table td:first-child { background: #e5e7eb; width: 34%; color: #111827; font-weight: 950; }
        .wr-result-tooltip-amt { text-align: right; color: #b91c1c !important; font-weight: 950; white-space: nowrap; }
        .wr-result-tooltip-muted { color:#64748b; font-size:0.78rem; padding:6px 9px; background:#f1f5f9; border-radius:0 0 5px 5px; }


        /* =========================================================================
         * 🏁 [WinRacing v44 賽果名次 Badge｜只加不改]
         * 目的：有賽果時，在第1列排名編號下方顯示該馬實際名次。
         * ========================================================================= */
        .wr-finish-rank-badge {
            display:block;
            margin:4px auto 0 auto;
            padding:2px 6px;
            border-radius:999px;
            font-size:0.70rem;
            font-weight:950;
            line-height:1.15;
            width:max-content;
            max-width:56px;
            border:1px solid rgba(255,255,255,0.22);
            box-shadow:0 0 8px rgba(0,0,0,0.25);
            white-space:nowrap;
        }
        .wr-finish-1 { background:linear-gradient(135deg,#ffd900,#f59e0b); color:#111827; border-color:#ffd900; }
        .wr-finish-2 { background:linear-gradient(135deg,#e5e7eb,#94a3b8); color:#111827; border-color:#cbd5e1; }
        .wr-finish-3 { background:linear-gradient(135deg,#f97316,#b45309); color:#fff; border-color:#fb923c; }
        .wr-finish-4 { background:linear-gradient(135deg,#38bdf8,#1d4ed8); color:#fff; border-color:#7dd3fc; }

        @keyframes flash {
            0% { opacity: 0.7; transform: scale(0.98); }
            100% { opacity: 1; transform: scale(1.03); }
        }

        @keyframes pulse {
            0% { transform: scale(1); box-shadow: 0 0 10px rgba(239, 68, 68, 0.5); }
            50% { transform: scale(1.03); box-shadow: 0 0 20px rgba(239, 68, 68, 0.8); }
            100% { transform: scale(1); box-shadow: 0 0 10px rgba(239, 68, 68, 0.5); }
        }
        /* =========================================================================
         * 🧠 [WinRacing v3 Add-On｜只加不刪] Dashboard / 校準 / Expert / Review UI
         * ========================================================================= */
        .v3-dashboard-grid { display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap:16px; margin-bottom:18px; }
        .v3-panel { background: linear-gradient(135deg, rgba(17,24,39,0.95), rgba(30,41,59,0.88)); border:1px solid rgba(255,217,0,0.28); border-radius:14px; padding:16px; box-shadow:0 0 18px rgba(0,0,0,0.25); }
        .v3-panel-title { color:#ffd900; font-size:1.1rem; font-weight:950; margin-bottom:10px; display:flex; align-items:center; gap:8px; }
        .v3-big-number { font-size:2.05rem; font-weight:950; color:#fff; text-shadow:0 0 12px rgba(255,217,0,0.35); }
        .v3-pill { display:inline-block; padding:5px 10px; border-radius:999px; font-weight:900; margin:3px; border:1px solid rgba(255,255,255,0.16); }
        .v3-pill-a { background:linear-gradient(135deg,#ffd900,#f59e0b); color:#111827; }
        .v3-pill-b { background:linear-gradient(135deg,#38bdf8,#2563eb); color:#fff; }
        .v3-pill-c { background:linear-gradient(135deg,#22c55e,#15803d); color:#fff; }
        .v3-pill-d { background:linear-gradient(135deg,#ef4444,#991b1b); color:#fff; }
        .v3-pill-muted { background:rgba(148,163,184,0.18); color:#e5e7eb; }
        .v3-fake-alert { background:rgba(239,68,68,0.16); border:1px solid rgba(239,68,68,0.55); color:#fecaca; padding:10px; border-radius:10px; font-weight:900; margin:8px 0; }
        .v3-cold-radar { background:rgba(14,165,233,0.14); border:1px solid rgba(14,165,233,0.48); color:#bfdbfe; padding:10px; border-radius:10px; font-weight:850; margin:8px 0; }
        .v3-table { width:100%; color:#eee; border-collapse:collapse; }
        .v3-table th { background:rgba(255,255,255,0.07); color:#ffd900; padding:9px; text-align:center; border:1px solid rgba(255,255,255,0.08); }
        .v3-table td { padding:9px; text-align:center; border:1px solid rgba(255,255,255,0.08); }
        .v3-rule-order-box { border-left:4px solid #ffd900; background:rgba(255,217,0,0.08); padding:12px; border-radius:10px; margin-bottom:12px; color:#fde68a; font-weight:850; }
        @media (max-width: 1100px) { .v3-dashboard-grid { grid-template-columns: 1fr; } }



        /* =========================================================================
         * 🧪 [WinRacing v3.1 化學反應層｜只加不刪] 原法規 × 回測百分比校準
         * ========================================================================= */
        .v31-chem-card { background: linear-gradient(135deg, rgba(255,217,0,0.12), rgba(14,165,233,0.10), rgba(34,197,94,0.06)); border:1px solid rgba(255,217,0,0.48); border-radius:15px; padding:18px; margin-bottom:18px; box-shadow:0 0 24px rgba(255,217,0,0.13); }
        .v31-title { color:#ffd900; font-size:1.28rem; font-weight:950; margin-bottom:10px; display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
        .v31-note { color:#cbd5e1; font-size:0.92rem; font-weight:800; margin-bottom:12px; }
        .v31-mini { display:inline-block; padding:4px 9px; border-radius:999px; margin:2px; font-weight:900; border:1px solid rgba(255,255,255,0.16); }
        .v31-a { background:linear-gradient(135deg,#ffd900,#f59e0b); color:#111827; }
        .v31-b { background:linear-gradient(135deg,#38bdf8,#1d4ed8); color:#fff; }
        .v31-c { background:linear-gradient(135deg,#22c55e,#15803d); color:#fff; }
        .v31-d { background:linear-gradient(135deg,#ef4444,#991b1b); color:#fff; }
        .v31-table { width:100%; color:#e5e7eb; border-collapse:collapse; }
        .v31-table th { color:#ffd900; background:rgba(255,255,255,0.07); border:1px solid rgba(255,255,255,0.08); padding:8px; text-align:center; }
        .v31-table td { border:1px solid rgba(255,255,255,0.08); padding:8px; text-align:center; vertical-align:middle; }
        .v31-reason { text-align:left !important; color:#fde68a; font-weight:800; }

        /* =========================================================================
         * 🏆🔥 [WinRacing v3.5 今日鐵膽 Engine｜只加不刪]
         * ========================================================================= */
        .v35-engine-shell { background: linear-gradient(135deg, rgba(255,217,0,0.16), rgba(14,165,233,0.10), rgba(34,197,94,0.08)); border:1px solid rgba(255,217,0,0.65); border-radius:18px; padding:22px; margin-bottom:22px; box-shadow:0 0 30px rgba(255,217,0,0.18); }
        .v35-engine-title { color:#ffd900; font-size:1.45rem; font-weight:950; margin-bottom:14px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
        .v35-grid { display:grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap:14px; margin-bottom:16px; }
        .v35-card { background:rgba(15,18,26,0.88); border:1px solid rgba(255,255,255,0.13); border-radius:14px; padding:15px; min-height:165px; }
        .v35-card.gold { border-color:rgba(255,217,0,0.58); box-shadow:0 0 18px rgba(255,217,0,0.12); }
        .v35-card.green { border-color:rgba(34,197,94,0.52); }
        .v35-card.red { border-color:rgba(239,68,68,0.55); }
        .v35-card.blue { border-color:rgba(14,165,233,0.55); }
        .v35-card-title { font-weight:950; color:#fff; margin-bottom:10px; font-size:1.02rem; }
        .v35-main { font-size:1.5rem; font-weight:950; color:#ffd900; line-height:1.25; }
        .v35-row { display:flex; justify-content:space-between; gap:10px; border-top:1px solid rgba(255,255,255,0.08); padding-top:7px; margin-top:7px; font-weight:850; color:#e5e7eb; align-items:center; }
        .v35-rate { color:#22c55e; font-weight:950; }
        .v35-risk { color:#f87171; font-weight:950; }
        .v35-radar { color:#38bdf8; font-weight:950; }
        .v35-muted { color:#94a3b8; font-size:0.86rem; font-weight:750; }
        .v35-badge { background:rgba(255,217,0,0.14); color:#ffd900; border:1px solid rgba(255,217,0,0.35); border-radius:999px; padding:4px 9px; font-size:0.82rem; font-weight:900; display:inline-block; margin:2px; }
        .v35-table { width:100%; color:#e5e7eb; border-collapse:collapse; margin-top:12px; }
        .v35-table th { background:rgba(255,255,255,0.07); color:#ffd900; border:1px solid rgba(255,255,255,0.08); padding:8px; text-align:center; }
        .v35-table td { border:1px solid rgba(255,255,255,0.08); padding:8px; text-align:center; vertical-align:middle; }

        /* =========================================================================
         * 🔥 [WinRacing v41 第二刀｜PACE BIAS 場次雷達]
         * ========================================================================= */
        .v41-pace-shell { background: rgba(15,18,26,0.88); border:1px solid rgba(34,197,94,0.42); border-radius:14px; padding:15px; margin:14px 0 16px 0; box-shadow:0 0 20px rgba(34,197,94,0.10); }
        .v41-pace-title { color:#22c55e; font-size:1.15rem; font-weight:950; margin-bottom:10px; display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
        .v41-pace-table { width:100%; color:#e5e7eb; border-collapse:collapse; margin-top:8px; }
        .v41-pace-table th { background:rgba(255,255,255,0.07); color:#bbf7d0; border:1px solid rgba(255,255,255,0.08); padding:8px; text-align:center; }
        .v41-pace-table td { border:1px solid rgba(255,255,255,0.08); padding:8px; text-align:center; vertical-align:middle; }
        .v41-pace-fast { color:#f87171; font-weight:950; }
        .v41-pace-pressure { color:#fb923c; font-weight:950; }
        .v41-pace-slow { color:#38bdf8; font-weight:950; }
        .v41-pace-normal { color:#94a3b8; font-weight:950; }
        .v41-pace-hint { color:#fde68a; font-weight:850; text-align:left !important; }

        @media (max-width: 1200px) { .v35-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
        @media (max-width: 760px) { .v35-grid { grid-template-columns: 1fr; } }



        /* =========================================================================
         * 🏆 [WinRacing v42 第三刀｜今日決策總覽]
         * 直接顯示：鐵膽排行榜 / 投注模式 / 最高風險假膽 / 冷門雷達 Top3
         * ========================================================================= */
        .v42-decision-shell { background: linear-gradient(135deg, rgba(255,217,0,0.17), rgba(14,165,233,0.10), rgba(239,68,68,0.07)); border:1px solid rgba(255,217,0,0.70); border-radius:18px; padding:22px; margin-bottom:22px; box-shadow:0 0 30px rgba(255,217,0,0.20); }
        .v42-decision-title { color:#ffd900; font-size:1.48rem; font-weight:950; margin-bottom:14px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
        .v42-grid { display:grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap:14px; }
        .v42-card { background:rgba(15,18,26,0.90); border:1px solid rgba(255,255,255,0.13); border-radius:14px; padding:15px; min-height:168px; }
        .v42-card.gold { border-color:rgba(255,217,0,0.62); box-shadow:0 0 18px rgba(255,217,0,0.14); }
        .v42-card.green { border-color:rgba(34,197,94,0.55); }
        .v42-card.red { border-color:rgba(239,68,68,0.60); }
        .v42-card.blue { border-color:rgba(14,165,233,0.60); }
        .v42-card-title { font-weight:950; color:#fff; margin-bottom:10px; font-size:1.04rem; }
        .v42-main { font-size:1.5rem; font-weight:950; color:#ffd900; line-height:1.25; }
        .v42-row { display:flex; justify-content:space-between; gap:10px; border-top:1px solid rgba(255,255,255,0.08); padding-top:7px; margin-top:7px; font-weight:850; color:#e5e7eb; align-items:center; }
        .v42-rate { color:#22c55e; font-weight:950; white-space:nowrap; }
        .v42-risk { color:#f87171; font-weight:950; white-space:nowrap; }
        .v42-radar { color:#38bdf8; font-weight:950; white-space:nowrap; }
        .v42-muted { color:#94a3b8; font-size:0.86rem; font-weight:750; }
        .v42-badge { background:rgba(255,217,0,0.14); color:#ffd900; border:1px solid rgba(255,217,0,0.35); border-radius:999px; padding:4px 9px; font-size:0.82rem; font-weight:900; display:inline-block; margin:2px; }
        .v42-horse-line { text-align:left; line-height:1.35; }
        @media (max-width: 1200px) { .v42-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
        @media (max-width: 760px) { .v42-grid { grid-template-columns: 1fr; } }

    
        /* =========================================================================
         * 🧩 [WinRacing v62 Compact Layout｜只改顯示不改分]
         * 目的：縮短「大數據總分」格高度，一頁可睇更多馬。
         * ========================================================================= */
        .race-analysis-card { padding: 12px !important; margin-bottom: 14px !important; }
        .race-title { font-size: 1.05rem !important; margin-bottom: 8px !important; }
        .table-responsive { overflow-x: auto; }
        .leaderboard-table tbody tr { min-height: 64px; }
        .leaderboard-table td:nth-child(1) { width: 4.5%; font-size: 0.82rem; }
        .leaderboard-table td:nth-child(2) { width: 4.8%; }
        .leaderboard-table td:nth-child(2) strong { font-size: 1.02rem !important; }
        .leaderboard-table td:nth-child(3) { width: 8.2%; max-width: 125px; }
        .wr-bias-mini { margin-top: 2px; font-size: 0.70rem; line-height: 1.16; max-width: 128px; margin-left:auto; margin-right:auto; }
        .wr-bias-mini div { white-space: normal; }
        .wr-bias-main { font-weight: 900; }
        .wr-bias-style { color:#cbd5e1; font-weight:800; }
        .wr-bias-power { color:#fde68a; font-weight:850; }
        .wr-signal-compact { margin-top:3px; padding:3px 5px; border-radius:5px; font-size:0.68rem; line-height:1.15; display:inline-block; max-width:120px; }
        .probability-core-td, .individual-core-td, .over22-core-td, .weighted-core-td, .four-track-weighted-td-add, .five-track-weighted-td-add { font-size: 0.88rem !important; padding:5px 3px !important; }
        .four-track-weighted-td-add strong, .five-track-weighted-td-add strong { font-size: 0.92rem !important; }
        .text-warning.small, .text-info.small { font-size: 0.72rem !important; line-height:1.2; }
        .v42-decision-shell { padding:14px !important; margin-bottom:12px !important; }
        .v42-card { min-height:128px !important; padding:10px !important; }
        .v42-row { padding-top:5px !important; margin-top:5px !important; }


        /* =========================================================================
         * 🏇 [WinRacing v66 投注服務標籤保守修正版｜只加不減]
         * 目的：直接在每匹馬分數下方掛上實戰提示：鐵膽候選 / 主力腳 / 假膽警告。
         * 注意：只作顯示，不改任何分數、不改排序、不刪原有內容。
         * ========================================================================= */
        .wr-v65-bet-tag-wrap { margin-top:4px; line-height:1.15; }
        .wr-v65-bet-tag {
            display:inline-block; padding:3px 7px; border-radius:7px;
            font-size:0.70rem; font-weight:950; margin:2px auto;
            border:1px solid rgba(255,255,255,0.18); white-space:normal; max-width:128px;
        }
        .wr-v65-tiedan { color:#111827; background:linear-gradient(135deg,#ffd900,#f59e0b); border-color:#ffd900; box-shadow:0 0 8px rgba(255,217,0,0.28); }
        .wr-v65-leg { color:#dcfce7; background:rgba(34,197,94,0.16); border-color:rgba(34,197,94,0.55); }
        .wr-v65-fake { color:#fecaca; background:rgba(239,68,68,0.16); border-color:rgba(239,68,68,0.65); }
        .wr-v65-reason { display:block; margin-top:1px; font-size:0.62rem; font-weight:800; opacity:0.92; }


        /* =========================================================================
         * 🏆 [WinRacing v38 God Banker Engine｜只加不減]
         * 目的：把 P1 Power History Analyzer 找出的神膽公式，直接落地到 Matrix 頁。
         * S級神膽：GodPower>=90 OR P1>=4+P2>=2+Expert>=5 OR Matrix>=35
         * A級Q膽：GodPower>=75 + Matrix>=20 + Expert>=5
         * B級拖腳：GodPower>=70 + Matrix>=20
         * ========================================================================= */
        .wr-god-shell {
            background: linear-gradient(135deg, rgba(255,217,0,0.18), rgba(34,197,94,0.10), rgba(14,165,233,0.08));
            border: 1px solid rgba(255,217,0,0.72);
            border-radius: 16px;
            padding: 14px;
            margin: 0 0 14px 0;
            box-shadow: 0 0 24px rgba(255,217,0,0.18);
        }
        .wr-god-title { color:#ffd900; font-weight:950; font-size:1.18rem; margin-bottom:10px; display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
        .wr-god-grid { display:grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap:10px; }
        .wr-god-card { background:rgba(15,18,26,0.86); border:1px solid rgba(255,255,255,0.12); border-radius:12px; padding:12px; min-height:118px; }
        .wr-god-card.s { border-color:rgba(255,217,0,0.75); box-shadow:0 0 16px rgba(255,217,0,0.18); }
        .wr-god-card.a { border-color:rgba(34,197,94,0.62); }
        .wr-god-card.b { border-color:rgba(14,165,233,0.58); }
        .wr-god-rank { color:#ffd900; font-size:1.25rem; font-weight:950; }
        .wr-god-horse { color:#fff; font-size:1.35rem; font-weight:950; margin:4px 0; }
        .wr-god-meta { color:#cbd5e1; font-size:0.82rem; line-height:1.35; font-weight:800; }
        .wr-god-score { color:#22c55e; font-weight:950; float:right; }
        .wr-god-badge { display:inline-block; margin-top:4px; padding:3px 7px; border-radius:8px; font-size:0.68rem; font-weight:950; border:1px solid rgba(255,255,255,0.18); line-height:1.15; max-width:128px; }
        .wr-god-s { background:linear-gradient(135deg,#ffd900,#f59e0b); color:#111827; border-color:#ffd900; box-shadow:0 0 10px rgba(255,217,0,0.35); }
        .wr-god-a { background:rgba(34,197,94,0.18); color:#bbf7d0; border-color:rgba(34,197,94,0.65); }
        .wr-god-b { background:rgba(14,165,233,0.18); color:#bae6fd; border-color:rgba(14,165,233,0.65); }
        .wr-god-reason { display:block; margin-top:1px; font-size:0.60rem; font-weight:850; opacity:0.95; }
        @media (max-width:1200px){ .wr-god-grid { grid-template-columns:1fr; } }

    

        /* =========================================================================
         * 🧠 WinRacing V17.3 AI Decision Auto Logger Status（只加不改）
         * ========================================================================= */
        .wr-v173-logger-status { margin-top:6px; display:inline-block; padding:5px 10px; border-radius:999px; font-weight:900; font-size:0.82rem; border:1px solid rgba(56,189,248,0.45); background:rgba(14,165,233,0.14); color:#bfdbfe; }
        .wr-v173-logger-status.ok { border-color:rgba(34,197,94,0.65); background:rgba(34,197,94,0.16); color:#bbf7d0; }
        .wr-v173-logger-status.err { border-color:rgba(239,68,68,0.65); background:rgba(239,68,68,0.16); color:#fecaca; }

    </style>
</head>
<body>

<div class="sidebar">
    <div class="sidebar-header">WIN RACING<br><small style="font-size:0.8rem; color:#aaa;">核心大數據智能分析</small></div>
    <div class="date-list" id="dateList">
        <?php foreach($dates as $d): ?>
            <div class="date-item" onclick="switchAnalysisDate(this, <?php echo $d['id']; ?>, '<?php echo htmlspecialchars($d['racing_date'], ENT_QUOTES); ?>')">
                <span><?php echo $d['racing_date']; ?> <small style="color: #888;">(<?php echo $d['week']; ?>)</small></span>
                <?php if($d['finished_flag'] == 1): ?><span class="badge bg-success" style="font-size:0.75rem;">數據就緒</span><?php endif; ?>
            </div>
        <?php endforeach; ?>
    </div>
</div>

<div class="main-content">
    <div class="analysis-header">
        <div>
            <h2 class="mb-1 fw-bold" id="displayDateTitle">請選擇賽事日期</h2>
            <p class="text-warning mb-0">數據模型已將歷史斷面完全隔離，每一次分析均不受前後日期影響</p>
            <div id="wrV173LoggerStatus" class="wr-v173-logger-status">🧠 AI Logger：等待分析</div>
        </div>
        <div>
            <a href="index.php" class="btn btn-primary btn-lg fw-bold px-4" style="background: linear-gradient(135deg, #0ea5e9, #2563eb); border: none; box-shadow: 0 4px 15px rgba(37,99,235,0.4);">返回 Dashboard</a>
        </div>
    </div>

    <div class="view-tabs" id="tabGroup" style="display: none;">
        <button type="button" class="btn btn-outline-warning active" onclick="switchViewMode('matrix')">📊 綜合能力矩陣榜</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('strategy')">🎯 建議場合戰術特推</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('v35_engine')">🏆 今日鐵膽Engine</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('tiedan')">🔥 鐵膽場次</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('advisor')">😎 軍師鐵膽 🏆</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('advisor_top2')">😎 軍師鐵膽 Top 2</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('advisor_top3')">🥇🥈🥉 軍師推薦 Top3</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('advisor_hidden_q')">😎 隱藏Q膽</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('advisor_sixstar')">👑 六星共識</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('advisor_qking')">🏆 軍師Q王</button>
        <button type="button" class="btn btn-outline-warning" onclick="switchViewMode('advisor_assassin')">🥷 隱形殺手</button>
    </div>

    <div id="loadingOverlay" class="text-center py-5" style="display:none;">
        <div class="spinner-border text-warning" role="status"></div>
        <p class="mt-2 text-info fw-bold">智慧運算模型加載中，正在鎖定歷史數據斷面...</p>
    </div>

    <div id="analysisReportContainer">
        <div class="alert alert-secondary text-center">← 請在左側點選一個賽事日期，開啟專屬大數據整合看板。</div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js">
// =========================================================================
// 🛡️ [WinRacing v3.1.1 化學層修正｜只加不刪]
// 目的：假膽警告 ≠ 直接D級。
// 原 v3.1 化學層會在 fakeRisk=true 時把高 Confidence 直接壓成 D。
// 本層不改原本法規、不改三個核心 API，只在最終顯示前重新分離：
// 1) Grade 按 Confidence 顯示 A/B/C/D
// 2) fakeRisk 保留為警告，不再覆蓋 Grade
// =========================================================================
(function(){
    function wrV311GradeByConf(conf) {
        conf = parseFloat(conf || 0);
        if (conf >= 70) return 'A';
        if (conf >= 55) return 'B';
        if (conf >= 40) return 'C';
        return 'D';
    }

    const wrV311PrevGetRaceCore = window.v3GetRaceCore;
    if (typeof wrV311PrevGetRaceCore === 'function') {
        window.v3GetRaceCore = function(raceNum, normalData, trendData, fiveTrackData) {
            const core = wrV311PrevGetRaceCore(raceNum, normalData, trendData, fiveTrackData);
            if (!core || core.grade === 'X' || !core.banker) return core;

            const conf = parseFloat(core.conf || core.chem_conf || 0);
            const confGrade = wrV311GradeByConf(conf);

            // 只在原本因 fakeRisk 被壓成 D，但 Confidence 本身達 C/B/A 時修正級別
            if (parseInt(core.fakeRisk || 0) === 1 && confGrade !== 'D') {
                core.grade = confGrade;
                core.chem_grade = confGrade;
                core.fakeWarningOnly = 1;
                if (!core.chem_reasons) core.chem_reasons = [];
                core.chem_reasons.push('⚠️ 假膽警告保留，但級別改按 Confidence 顯示，不再直接壓成D');
            }
            return core;
        };
    }

    const wrV311PrevGradePill = window.v31GradePill;
    window.v31GradePill = function(grade) {
        if (typeof wrV311PrevGradePill === 'function') return wrV311PrevGradePill(grade);
        return String(grade || '-');
    };

    // 日期直入支援：analysis.php?race_id=xx 或 ?review_date=yyyy-mm-dd 自動打開該日
    document.addEventListener('DOMContentLoaded', function(){
        try {
            const params = new URLSearchParams(window.location.search);
            const rid = params.get('race_id');
            const rdate = params.get('review_date');
            if (!rid && !rdate) return;

            const items = Array.from(document.querySelectorAll('.date-item'));
            let target = null;
            if (rdate) {
                target = items.find(el => (el.innerText || '').indexOf(rdate) !== -1);
            }
            if (!target && rid) {
                target = items.find(el => (el.getAttribute('onclick') || '').indexOf(', ' + rid + ',') !== -1);
            }
            if (target) setTimeout(() => target.click(), 150);
        } catch(e) {}
    });
})();
// =========================================================================

</script>
<script>
let resultsMap = <?php echo $results_json; ?>;
const resultsSource = <?php echo json_encode($wr_results_source, JSON_UNESCAPED_UNICODE); ?>;
let payoutMap = <?php echo $payout_json; ?>;
// 💡 [第二階段渠道建立]：將 PHP 快取層數據無痛交接給 JavaScript (只加不改)
const globalCacheMap = <?php echo $cache_json; ?>;
// 🏇 WinRacing v1.1：race_card_static 賽前基本面顯示，只提示不改分
const globalRaceCardMap = <?php echo $race_card_json; ?>;
const globalRaceCardExtraMap = <?php echo $wr_v31_card_extra_json; ?>;
// =========================================================================
// 🧠 [WinRacing v3 Add-On｜只加不刪]：PHP v3 maps → JavaScript
// =========================================================================
const v3RuleCalibrationMap = <?php echo $v3_rule_calibration_json; ?>;
const v3ExpertPerformanceMap = <?php echo $v3_expert_performance_json; ?>;
const v3ReviewMap = <?php echo $v3_review_json; ?>;
const wrV35ResonanceRows = <?php echo $wr_v35_resonance_json; ?>;
// 🧠 WinRacing v35 Bias Engine：由 PHP 預載三張 Bias 表，只加不刪
const wrV35TrackBiasRows = <?php echo $wr_v35_track_bias_json; ?>;
const wrV35DrawBiasRows = <?php echo $wr_v35_draw_bias_json; ?>;
const wrV35PaceBiasRows = <?php echo $wr_v35_pace_bias_json; ?>;
// =========================================================================



function getDrawZone(draw) {
    const d = parseInt(draw || 0);
    if (!d) return { text: '檔位未入', cls: 'advisor-result-miss' };
    if (d >= 1 && d <= 4) return { text: '🟢 黃金區', cls: 'advisor-result-hit' };
    if (d >= 5 && d <= 8) return { text: '🟢 有利區', cls: 'advisor-result-hit' };
    if (d >= 9 && d <= 12) return { text: '🟡 普通區', cls: '' };
    return { text: '🔴 外檔區', cls: 'advisor-result-miss' };
}

function getHorseCardInfo(raceNum, horseNum) {
    const r = parseInt(raceNum);
    const h = parseInt(horseNum);

    // ✅ WinRacing v40 UI Fallback
    // AJAX 已有 v39 date fallback；analysis.php 前端亦要同步 fallback，
    // 避免 currentSelectedDate 與 race_card_static 日期不一致時，畫面跌回 NORMAL_PACE / 檔位未入。
    if (globalRaceCardMap[currentSelectedDate] && globalRaceCardMap[currentSelectedDate][r] && globalRaceCardMap[currentSelectedDate][r][h]) {
        return globalRaceCardMap[currentSelectedDate][r][h];
    }

    const allDates = Object.keys(globalRaceCardMap || {}).sort().reverse();
    for (const d of allDates) {
        if (globalRaceCardMap[d] && globalRaceCardMap[d][r] && globalRaceCardMap[d][r][h]) {
            const found = {...globalRaceCardMap[d][r][h]};
            found.card_source_date = d;
            return found;
        }
    }
    return null;
}

function getHorseCardBadges(raceNum, horseNum) {
    const info = getHorseCardInfo(raceNum, horseNum);
    if (!info) {
        return `<span class="advisor-stat-badge">馬名/檔位/負磅：未入 race_card_static</span>`;
    }
    const zone = getDrawZone(info.draw);
    const style = wrV35Norm(info.running_style || info.predicted_running_style || '');
    const drawGroup = wrV35DrawGroup(info.draw) || '未知';
    const sourceDate = info.card_source_date ? `｜資料日：${info.card_source_date}` : '';
    return `<span class="advisor-stat-badge">馬名：${info.name || '-'}</span>` +
           `<span class="advisor-stat-badge ${zone.cls}">📍 ${info.draw || '-'}檔 ${drawGroup} ${zone.text}</span>` +
           `<span class="advisor-stat-badge">🏇 ${style || '-'}</span>` +
           `<span class="advisor-stat-badge">負磅：${info.weight || '-'}磅${sourceDate}</span>`;
}

function getHorseSmallInfo(raceNum, horseNum) {
    const info = getHorseCardInfo(raceNum, horseNum);
    if (!info) return '';
    const zone = getDrawZone(info.draw);
    const style = wrV35Norm(info.running_style || info.predicted_running_style || '');
    const drawGroup = wrV35DrawGroup(info.draw) || '未知';
    return `<div class="small mt-1" style="color:#fde68a;font-weight:800;">${info.name || '-'}｜🏇${style || '-'}｜📍${info.draw || '-'}檔 ${drawGroup} ${zone.text}｜${info.weight || '-'}磅</div>`;
}

// =========================================================================
// 🏇 [WinRacing v1.2 腳王系統]：根據同場 Matrix / P1 / P2 自動產生最多4隻推薦腳，只顯示不改分
// =========================================================================
function getAdvisorTopLegs(raceNum, danHorse, maxLegs = 4) {
    if (!globalAnalysisData || !globalAnalysisData[raceNum] || !globalAnalysisData[raceNum].horses) return [];
    const dan = parseInt(danHorse);
    const candidates = globalAnalysisData[raceNum].horses
        .filter(h => parseInt(h.horse_num) !== dan)
        .map(h => ({
            horseNum: parseInt(h.horse_num),
            score: parseFloat(h.total_score || 0),
            p1Count: (h.p1_from && Array.isArray(h.p1_from)) ? h.p1_from.length : 0,
            p2Count: (h.p2_from && Array.isArray(h.p2_from)) ? h.p2_from.length : 0,
            expertCount: parseInt(h.expert_count || ((h.supporters && Array.isArray(h.supporters)) ? h.supporters.length : 0))
        }));

    candidates.sort((a, b) => {
        if (b.p1Count !== a.p1Count) return b.p1Count - a.p1Count;
        if (b.score !== a.score) return b.score - a.score;
        if (b.expertCount !== a.expertCount) return b.expertCount - a.expertCount;
        if (b.p2Count !== a.p2Count) return b.p2Count - a.p2Count;
        return a.horseNum - b.horseNum;
    });

    return candidates.slice(0, maxLegs);
}

function getAdvisorLegsHtml(raceNum, danHorse) {
    const legs = getAdvisorTopLegs(raceNum, danHorse, 4);
    if (!legs.length) return `<div class="advisor-legs-under">暫無</div>`;
    const legText = legs.map(l => `#${l.horseNum}`).join(' , ');
    return `<div class="advisor-legs-under">${legText}</div>`;
}
// =========================================================================

let currentSelectedDate = ''; 
let globalAnalysisData = null;
let globalTrendData = null;    
let globalFiveTrackData = null; 
let currentViewMode = "matrix"; 
let currentActiveDateId = null;


// =========================================================================
// 🧠 WinRacing V17.3｜Chairman Auto Decision Pipeline（只加不改）
// 目的：每次 analysis.php 完成 get_analysis_ajax 後，自動把全日每場 Top2 Pair + Engine Votes 寫入 AI Brain。
// =========================================================================
const WR_V173_MIN_DATA_DATE = '2025-10-01';
function wrV173SetLoggerStatus(text, cls='') {
    const el = document.getElementById('wrV173LoggerStatus');
    if (!el) return;
    el.className = 'wr-v173-logger-status' + (cls ? ' ' + cls : '');
    el.textContent = text;
}
function wrV173Num(v, d=0) { const n = parseFloat(v); return isNaN(n) ? d : n; }
function wrV173Int(v, d=0) { const n = parseInt(v); return isNaN(n) ? d : n; }
function wrV173HorseName(raceNo, horseNo, horseObj) {
    const card = (typeof getHorseCardInfo === 'function') ? getHorseCardInfo(raceNo, horseNo) : null;
    return (card && card.name) || horseObj.horse_name || horseObj.name || '';
}
function wrV173BuildDecisionForRace(raceNo, raceData) {
    if (!raceData || !Array.isArray(raceData.horses) || raceData.horses.length < 2) return null;
    const horses = raceData.horses.slice().sort((a,b)=>wrV173Num(b.total_score)-wrV173Num(a.total_score));
    const h1 = horses[0];
    const h2 = horses[1];
    const n1 = wrV173Int(h1.horse_num);
    const n2 = wrV173Int(h2.horse_num);
    if (!n1 || !n2) return null;

    const score1 = wrV173Num(h1.total_score);
    const score2 = wrV173Num(h2.total_score);
    const gap = score1 - score2;
    const p1Count = Array.isArray(h1.p1_from) ? h1.p1_from.length : wrV173Int(h1.v49_p1_count);
    const p2Count = Array.isArray(h2.p1_from) ? h2.p1_from.length : wrV173Int(h2.v49_p1_count);
    const expert1 = wrV173Int(h1.expert_count || h1.v49_expert_count);
    const expert2 = wrV173Int(h2.expert_count || h2.v49_expert_count);
    const fake1 = (String(h1.historical_top2_display || '').includes('💀') || String(h1.historical_top2_level || '').includes('DANGER')) ? 1 : 0;
    const fake2 = (String(h2.historical_top2_display || '').includes('💀') || String(h2.historical_top2_level || '').includes('DANGER')) ? 1 : 0;
    const radar1 = wrV173Num(h1.historical_top2_rate, 0);
    const radar2 = wrV173Num(h2.historical_top2_rate, 0);
    const pairSorted = [n1,n2].sort((a,b)=>a-b).join('-');

    let confidence = 45;
    confidence += Math.min(22, score1 * 0.65);
    confidence += Math.min(10, score2 * 0.35);
    confidence += Math.min(8, p1Count * 1.6);
    confidence += Math.min(6, expert1);
    if (gap >= 8) confidence += 4;
    if (gap <= 2) confidence += 3;
    if (radar1 >= 60) confidence += 5;
    if (radar2 >= 55) confidence += 3;
    if (fake1 || fake2) confidence -= 12;
    confidence = Math.max(1, Math.min(98, confidence));

    let decision = 'WATCH';
    if (confidence >= 76 && !fake1) decision = 'BUY';
    else if (confidence >= 66) decision = 'COVER';
    else if (confidence < 52 || (fake1 && confidence < 70)) decision = 'SKIP';

    const votes = {
        'Matrix': {vote: score1 >= 22 ? 'YES' : (score1 < 12 ? 'NO' : 'ABSTAIN'), weight: 18, score: score1, reason: 'Top1 Matrix score'},
        'Top2Pair': {vote: score2 >= 14 ? 'YES' : (score2 < 8 ? 'NO' : 'ABSTAIN'), weight: 16, score: score2, reason: 'Top2 support score'},
        'Gap': {vote: gap <= 6 ? 'YES' : (gap >= 16 ? 'NO' : 'ABSTAIN'), weight: 10, score: gap, reason: 'Top1/Top2 score gap'},
        'P1': {vote: p1Count >= 3 ? 'YES' : (p1Count <= 0 ? 'NO' : 'ABSTAIN'), weight: 12, score: p1Count, reason: 'Pick 1 support count'},
        'Expert': {vote: expert1 >= 5 ? 'YES' : (expert1 <= 2 ? 'NO' : 'ABSTAIN'), weight: 12, score: expert1, reason: 'Expert count'},
        'Radar': {vote: radar1 >= 60 ? 'YES' : (radar1 > 0 && radar1 < 35 ? 'NO' : 'ABSTAIN'), weight: 10, score: radar1, reason: 'Historical Top2 radar'},
        'FakeKill': {vote: fake1 || fake2 ? 'NO' : 'YES', weight: 20, score: fake1 || fake2 ? -1 : 1, reason: fake1 || fake2 ? 'Fake danger detected' : 'No fake danger'},
        'Bias': {vote: wrV173Num(h1.bias_score, 0) >= 2 ? 'YES' : (wrV173Num(h1.bias_score, 0) <= -3 ? 'NO' : 'ABSTAIN'), weight: 8, score: wrV173Num(h1.bias_score, 0), reason: 'Bias score'},
        'Power': {vote: wrV173Num(h1.v51_power_score, 0) > 0 ? 'YES' : 'ABSTAIN', weight: 8, score: wrV173Num(h1.v51_power_score, 0), reason: 'Power engine score'}
    };

    return {
        race_date: currentSelectedDate,
        race_no: wrV173Int(raceNo),
        pair_key: pairSorted,
        main_pick: String(n1),
        cover_pick: String(n2),
        final_decision: decision,
        confidence: Number(confidence.toFixed(3)),
        gate_score: Number(confidence.toFixed(3)),
        chairman_score: Number((confidence + score1).toFixed(3)),
        expected_hit_rate: Number(confidence.toFixed(3)),
        decision_reason: `V17.3 AutoLogger｜Top2 Pair ${pairSorted}｜${decision}｜Confidence ${confidence.toFixed(1)}｜Score ${score1.toFixed(1)}/${score2.toFixed(1)}｜Gap ${gap.toFixed(1)}`,
        source_page: 'analysis_v50_ultimate_dedup_live_results_V17_3_Integrated.php',
        factors: {
            race_no: wrV173Int(raceNo),
            top1: {horse:n1, name:wrV173HorseName(raceNo,n1,h1), score:score1, p1:p1Count, expert:expert1, radar:radar1, fake:fake1, bias:wrV173Num(h1.bias_score,0)},
            top2: {horse:n2, name:wrV173HorseName(raceNo,n2,h2), score:score2, p1:p2Count, expert:expert2, radar:radar2, fake:fake2, bias:wrV173Num(h2.bias_score,0)},
            gap: gap,
            race_meta: raceData.meta || {}
        },
        engine_votes: votes
    };
}
function wrV173CollectDecisions(data) {
    const decisions = [];
    Object.keys(data || {}).sort((a,b)=>wrV173Int(a)-wrV173Int(b)).forEach(raceNo => {
        const d = wrV173BuildDecisionForRace(raceNo, data[raceNo]);
        if (d) decisions.push(d);
    });
    return decisions;
}
function wrV173AutoLogDecisions() {
    try {
        if (!currentSelectedDate || !globalAnalysisData) {
            wrV173SetLoggerStatus('🧠 AI Logger：沒有可寫入資料', 'err');
            return;
        }
        if (String(currentSelectedDate) < WR_V173_MIN_DATA_DATE) {
            wrV173SetLoggerStatus('🧠 AI Logger：2025-10-01前無Learning DATA，不寫入', 'err');
            return;
        }
        const decisions = wrV173CollectDecisions(globalAnalysisData);
        if (!decisions.length) {
            wrV173SetLoggerStatus('🧠 AI Logger：0場可寫入', 'err');
            return;
        }
        wrV173SetLoggerStatus(`🧠 AI Logger：寫入中 ${decisions.length} 場...`);
        fetch(window.location.pathname + '?wr_ai_decision_writer=1', {
            method: 'POST',
            headers: {'Content-Type':'application/json'},
            body: JSON.stringify({decisions}),
            keepalive: false
        })
        .then(res => res.json())
        .then(resp => {
            if (resp && resp.ok) {
                wrV173SetLoggerStatus(`🧠 AI Logger：已寫入 ${resp.written || 0} 場｜${resp.ts || ''}`, 'ok');
                console.log('WinRacing V17.3 Auto Logger OK', resp);
            } else {
                wrV173SetLoggerStatus(`🧠 AI Logger：寫入失敗｜${(resp && (resp.error || resp.message)) ? (resp.error || resp.message).slice(0,120) : '未知錯誤'}`, 'err');
                console.warn('WinRacing V17.3 Auto Logger failed', resp);
            }
        })
        .catch(err => {
            wrV173SetLoggerStatus(`🧠 AI Logger：POST錯誤｜${err && err.message ? err.message : err}`, 'err');
            console.warn('WinRacing V17.3 Auto Logger error', err);
        });
    } catch(e) {
        wrV173SetLoggerStatus('🧠 AI Logger：JS錯誤', 'err');
        console.warn('WinRacing V17.3 Auto Logger exception', e);
    }
}

function switchAnalysisDate(element, dateId, dateStr) {
    // 💡 [安全相容層植入]：在 100% 執行原版 switchAnalysisDate 邏輯的同時，洗淨全形單引號，確保點擊 100% 靈敏觸發
    currentSelectedDate = dateStr.replace(/[‘’'"]/g, '').trim(); 
    currentActiveDateId = dateId;
    document.querySelectorAll('.date-item').forEach(el => el.classList.remove('active'));
    element.classList.add('active');
    
    const dateText = element.querySelector('span').innerText;
    document.getElementById('displayDateTitle').innerText = "📊 數據透視：" + dateText;

    // =========================================================================
    // 🛡️ [WinRacing v3.1.1 日期同步修正｜只加不刪]
    // 保留原本 AJAX 切換；額外把當前 review_date/race_id 寫入 URL，
    // 避免 Dashboard / 戰術特推 / 返回頁面永遠停留舊日期。
    // =========================================================================
    try {
        const safeUrl = new URL(window.location.href);
        safeUrl.searchParams.set('review_date', currentSelectedDate);
        safeUrl.searchParams.set('date', currentSelectedDate);
        safeUrl.searchParams.set('race_id', String(dateId));
        window.history.replaceState({}, '', safeUrl.toString());
    } catch(e) {}

    executeAnalysisEngine(dateId);
    wrStartLiveResultAutoRefresh();
}

function wrPayoutFormat(val) {
    if (val === null || val === undefined || val === '' || parseFloat(val) <= 0) return '-';
    return parseFloat(val).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 });
}
function wrPayoutLine(label, combo, dividend) {
    const c = (combo === null || combo === undefined || combo === '') ? '-' : String(combo);
    const d = wrPayoutFormat(dividend);
    return `<tr><td>${label}</td><td>${c}</td><td class="wr-result-tooltip-amt">${d}</td></tr>`;
}
function wrBuildPayoutTooltip(raceNum) {
    const p = (payoutMap[currentSelectedDate] && payoutMap[currentSelectedDate][raceNum]) ? payoutMap[currentSelectedDate][raceNum] : null;
    if (!p) {
        return `<div class="wr-result-tooltip-box"><div class="wr-result-tooltip-title">🧾 派彩資料</div><div class="wr-result-tooltip-muted">未有 racing_results_all 派彩資料</div></div>`;
    }
    return `<div class="wr-result-tooltip-box">
        <div class="wr-result-tooltip-title">🧾 第 ${raceNum} 場派彩（HK$）</div>
        <table class="wr-result-tooltip-table">
            <tbody>
                ${wrPayoutLine('連贏', p.quinella_combination, p.quinella_dividend)}
                ${wrPayoutLine('位置Q', p.qp1_combination, p.qp1_dividend)}
                ${wrPayoutLine('位置Q', p.qp2_combination, p.qp2_dividend)}
                ${wrPayoutLine('位置Q', p.qp3_combination, p.qp3_dividend)}
                ${wrPayoutLine('單T', p.trio_combination, p.trio_dividend)}
                ${wrPayoutLine('二重彩', p.forecast_combination, p.forecast_dividend)}
                ${wrPayoutLine('三重彩', p.tierce_combination, p.tierce_dividend)}
                ${wrPayoutLine('四連環', p.f_f_combination, p.f_f_dividend)}
                ${wrPayoutLine('四重彩', p.qtt_combination, p.qtt_dividend)}
            </tbody>
        </table>
        <div class="wr-result-tooltip-muted">Mouse over 顯示；離開自動收起</div>
    </div>`;
}

// =========================================================================
// 🏁 [WinRacing v44 賽果名次 Badge｜只加不改]
// 有賽果時，在排行榜第1列「#排名」下方顯示實際賽果名次。
// =========================================================================
function wrGetFinishBadgeHtml(raceNum, horseNum) {
    if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][raceNum]) {
        const res = resultsMap[currentSelectedDate][raceNum];
        const h = parseInt(horseNum);
        for (let i = 0; i < res.length; i++) {
            if (parseInt(res[i]) === h) {
                const labels = ['🥇第1', '🥈第2', '🥉第3', '第4'];
                return `<span class="wr-finish-rank-badge wr-finish-${i + 1}">${labels[i]}</span>`;
            }
        }
    }
    return '';
}

function getRaceResultHtml(raceNum) {
    if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][raceNum]) {
        const res = resultsMap[currentSelectedDate][raceNum];
        return `<span class="wr-result-tooltip-wrap" style="font-size:1.1rem; color:#2ecc71; margin-left:10px; font-weight:normal; background:rgba(46,204,113,0.15); padding:3px 10px; border-radius:6px; border:1px solid rgba(46,204,113,0.3);">賽果 : ${res.join(' , ')}${wrBuildPayoutTooltip(raceNum)}</span>`;
    }
    return '';
}

// =========================================================================
// 🆕 WinRacing v49.2｜Analysis 賽果自動更新（1800秒）
// 跟 view_odds 一樣定時向後端取最新 racing_results_all。
// 若賽果有更新，自動重畫當前頁面，不用手動 Refresh。
// =========================================================================
let wrLiveResultLastSignature = '';
let wrLiveResultTimer = null;

function wrLiveResultSignature(obj){
    try { return JSON.stringify(obj || {}); } catch(e) { return ''; }
}

function wrStartLiveResultAutoRefresh(){
    if (wrLiveResultTimer) clearInterval(wrLiveResultTimer);

    async function run(){
        if (!currentSelectedDate) return;
        try {
            const url = new URL(window.location.href);
            url.searchParams.set('wr_live_results', '1');
            url.searchParams.set('date', currentSelectedDate);
            url.searchParams.set('_', Date.now());

            const resp = await fetch(url.toString(), {cache:'no-store'});
            const data = await resp.json();
            if (!data || !data.ok) return;

            const newSig = wrLiveResultSignature(data.results);
            if (newSig && newSig !== wrLiveResultLastSignature) {
                wrLiveResultLastSignature = newSig;

                if (data.results) {
                    Object.keys(data.results).forEach(d=>{
                        resultsMap[d] = data.results[d];
                    });
                }
                if (data.payouts) {
                    Object.keys(data.payouts).forEach(d=>{
                        payoutMap[d] = data.payouts[d];
                    });
                }

                // 重畫目前模式，令「賽果 Badge / 回測 / Mouseover 派彩」即時更新
                if (currentActiveDateId) {
                    executeAnalysisEngine(currentActiveDateId);
                }
            }
        } catch(e) {
            console.warn('WinRacing live results refresh failed:', e);
        }
    }

    run();
    wrLiveResultTimer = setInterval(run, 1800000);
}


function switchViewMode(mode) {
    currentViewMode = mode;
    document.querySelectorAll('.view-tabs .btn').forEach(btn => btn.classList.remove('active'));
    
    // UI 高亮激活按鈕
    document.querySelector(`[onclick="switchViewMode('${mode}')"]`).classList.add('active');
    
    if(mode === 'v35_engine') {
        renderV35TiedanEngineDashboard(globalAnalysisData, globalTrendData, globalFiveTrackData);
    } else if(mode === 'matrix') {
        renderMatrixDashboard(globalAnalysisData, globalTrendData, globalFiveTrackData);
    } else if(mode === 'advisor') {
        renderAdvisorTiedanDashboard(globalAnalysisData);
    } else if(mode === 'advisor_top2') {
        renderAdvisorTop2Dashboard(globalAnalysisData);
    } else if(mode === 'advisor_top3') {
        renderAdvisorTop3Dashboard(globalAnalysisData);
    } else if(mode === 'advisor_hidden_q') {
        renderAdvisorHiddenQDashboard(globalAnalysisData);
    } else if(mode === 'advisor_sixstar') {
        renderAdvisorSixStarDashboard(globalAnalysisData);
    } else if(mode === 'advisor_qking') {
        renderAdvisorQKingDashboard(globalAnalysisData);
    } else if(mode === 'advisor_assassin') {
        // 💡 [只加不改分流]
        renderAdvisorAssassinDashboard(globalAnalysisData);
    } else {
        renderStrategyDashboard(globalAnalysisData, globalTrendData, globalFiveTrackData, mode);
    }
}



// =========================================================================
// 🧠 [WinRacing v35 Bias 入分引擎｜只加不刪]
// 核心：Track Bias + Draw Bias + Pace Bias 真正寫入 horse.total_score，排序/Top2/Top3/鐵膽會即時受影響。
// =========================================================================
function wrV35Norm(v) { return String(v ?? '').trim(); }
function wrV35Num(v) { const n = parseFloat(v); return isNaN(n) ? 0 : n; }

function wrV35DrawGroup(draw) {
    const d = parseInt(draw || 0);
    if (!d) return '';
    if (d >= 1 && d <= 4) return '內檔';
    if (d >= 5 && d <= 8) return '中檔';
    if (d >= 9 && d <= 12) return '外檔';
    return '田大外檔';
}

function wrV35InferRacecourse(ctx) {
    const raw = [ctx.racecourse, ctx.venue, ctx.track_type, ctx.track_course, ctx.track].map(wrV35Norm).join(' ');
    if (raw.indexOf('泥') !== -1) return '田泥';
    if (raw.indexOf('谷') !== -1 || raw.toUpperCase().indexOf('HV') !== -1 || raw.indexOf('跑馬地') !== -1) return '谷草';
    if (raw.indexOf('田') !== -1 || raw.toUpperCase().indexOf('ST') !== -1 || raw.indexOf('沙田') !== -1) return '田草';
    return '';
}

function wrV35GetRaceContext(raceNum) {
    const r = parseInt(raceNum);
    let ctx = {};
    if (globalAnalysisData && globalAnalysisData[r] && globalAnalysisData[r].meta) ctx = {...ctx, ...globalAnalysisData[r].meta};
    if (globalRaceCardExtraMap && globalRaceCardExtraMap[currentSelectedDate] && globalRaceCardExtraMap[currentSelectedDate][r]) {
        const firstHorseKey = Object.keys(globalRaceCardExtraMap[currentSelectedDate][r])[0];
        if (firstHorseKey) ctx = {...ctx, ...globalRaceCardExtraMap[currentSelectedDate][r][firstHorseKey]};
    }
    return {
        racecourse: wrV35Norm(ctx.racecourse || ctx.venue || wrV35InferRacecourse(ctx)),
        rail_course: wrV35Norm(ctx.track_course || ctx.track || ctx.rail_course),
        track_condition: wrV35Norm(ctx.going || ctx.track_condition || ctx.track_type),
        distance: parseInt(ctx.distance || 0),
        race_class: wrV35Norm(ctx.race_class || ctx.class || '').replace('第','').replace('班','')
    };
}

function wrV35GetHorseExtra(raceNum, horseNum) {
    const r = parseInt(raceNum), h = parseInt(horseNum);
    if (globalRaceCardExtraMap && globalRaceCardExtraMap[currentSelectedDate] && globalRaceCardExtraMap[currentSelectedDate][r] && globalRaceCardExtraMap[currentSelectedDate][r][h]) {
        return globalRaceCardExtraMap[currentSelectedDate][r][h];
    }

    // ✅ WinRacing v40 UI Fallback：ExtraMap 同步 fallback 最新 race_card_static 日期
    const allDates = Object.keys(globalRaceCardExtraMap || {}).sort().reverse();
    for (const d of allDates) {
        if (globalRaceCardExtraMap[d] && globalRaceCardExtraMap[d][r] && globalRaceCardExtraMap[d][r][h]) {
            return globalRaceCardExtraMap[d][r][h];
        }
    }
    return {};
}

function wrV35ClassifyPace(raceNum) {
    const r = parseInt(raceNum);
    let front = 0, frontOrProminent = 0;
    if (!globalAnalysisData || !globalAnalysisData[r] || !globalAnalysisData[r].horses) return 'NORMAL_PACE';
    globalAnalysisData[r].horses.forEach(h => {
        const ex = wrV35GetHorseExtra(r, h.horse_num);
        const style = wrV35Norm(h.bias_running_style || h.pace_type || h.predicted_running_style || ex.running_style || ex.predicted_running_style || h.running_style || '');
        if (style === '放頭') front++;
        if (style === '放頭' || style === '咬前' || style === '跟前') frontOrProminent++;
    });
    if (front >= 3) return 'FAST_PACE';
    if (front === 1) return 'SLOW_PACE';
    if (frontOrProminent >= 5) return 'PRESSURE_PACE';
    return 'NORMAL_PACE';
}

function wrV35RateToScore(top3Rate, top2Rate, runs) {
    const r3 = wrV35Num(top3Rate);
    const r2 = wrV35Num(top2Rate);
    const n = Math.max(0, parseInt(runs || 0));
    // 樣本權重：少過30筆打折，100筆以上基本足夠，避免22筆50%直接過度放大
    const sampleWeight = n >= 100 ? 1 : (n >= 50 ? 0.78 : (n >= 30 ? 0.58 : 0.35));
    let raw = 0;
    if (r3 >= 42 || r2 >= 30) raw = 2.00;
    else if (r3 >= 36 || r2 >= 25) raw = 1.50;
    else if (r3 >= 30 || r2 >= 20) raw = 1.00;
    else if (r3 >= 24 || r2 >= 16) raw = 0.50;
    else if (r3 < 14 && n >= 30) raw = -1.00;
    else if (r3 < 18 && n >= 30) raw = -0.50;
    return Math.round(raw * sampleWeight * 100) / 100;
}

function wrV35PickBestBias(rows, params, mode) {
    let best = null;
    rows.forEach(row => {
        let score = 0;
        const rowRacecourse = wrV35Norm(row.racecourse);
        const rowRail = wrV35Norm(row.rail_course);
        const rowGoing = wrV35Norm(row.track_condition);
        const rowDist = parseInt(row.distance || 0);
        const rowClass = wrV35Norm(row.race_class);

        if (params.running_style && wrV35Norm(row.running_style) !== params.running_style) return;
        if (mode === 'draw' && params.draw_group && wrV35Norm(row.draw_group) !== params.draw_group) return;
        if (mode === 'pace' && params.pace_type && wrV35Norm(row.pace_type) !== params.pace_type) return;
        if (params.distance && rowDist && rowDist !== params.distance) return;

        if (params.racecourse && rowRacecourse === params.racecourse) score += 5;
        else if (params.racecourse && rowRacecourse && rowRacecourse !== params.racecourse) score -= 2;

        if (params.rail_course && rowRail === params.rail_course) score += 4;
        else if (params.rail_course && rowRail && rowRail !== params.rail_course) score -= 1;

        if (params.track_condition && rowGoing === params.track_condition) score += 2;
        if (params.race_class && rowClass === params.race_class) score += 2;

        score += Math.min(3, Math.log10(Math.max(1, parseInt(row.total_runs || 0))));
        if (!best || score > best._matchScore) best = {...row, _matchScore: score};
    });
    return best;
}

function wrV35ApplyBiasToAnalysisData(data) {
    if (!data) return data;

    // =========================================================================
    // ✅ WinRacing v39 Final Guard
    // get_analysis_ajax.php v38/v39 已經在後端完成 Half Bias 入分。
    // 如果 API 已回傳 bias_score / original_score / simulated_score，前端只負責顯示，
    // 絕不再二次加 Track/Draw/Pace Bias，避免分數被重複加減。
    // =========================================================================
    let apiHasServerBias = false;
    Object.keys(data || {}).forEach(rn => {
        if (data[rn] && data[rn].horses) {
            data[rn].horses.forEach(h => {
                if (h && (h.bias_score !== undefined || h.simulated_score !== undefined || h.adjusted_total_score !== undefined)) {
                    apiHasServerBias = true;
                }
            });
        }
    });

    if (apiHasServerBias) {
        Object.keys(data || {}).forEach(raceNum => {
            if (!data[raceNum] || !data[raceNum].horses) return;
            data[raceNum].horses.forEach(horse => {
                const b = parseFloat(horse.bias_score || 0);
                const originalScore = parseFloat((horse.original_score !== undefined ? horse.original_score : (parseFloat(horse.total_score || 0) - b)) || 0);
                const adjustedScore = parseFloat((horse.adjusted_total_score !== undefined ? horse.adjusted_total_score : (horse.simulated_score !== undefined ? horse.simulated_score : horse.total_score)) || 0);
                const draw = parseInt(horse.draw || 0);
                const drawGroup = horse.draw_category || wrV35DrawGroup(draw);
                const style = wrV35Norm(horse.bias_running_style || horse.pace_type || horse.running_style || horse.predicted_running_style || '');
                const paceType = wrV35Norm(horse.pace_type || style || 'NORMAL_PACE');

                horse.total_score = Math.round(adjustedScore * 100) / 100;
                horse.wr_v35_original_score = Math.round(originalScore * 100) / 100;
                horse.wr_v35_bias_score = Math.round(b * 100) / 100;
                horse.wr_v35_bias_detail = {
                    style: style || '-',
                    draw: draw,
                    drawGroup: drawGroup || '-',
                    paceType: paceType || 'NORMAL_PACE',
                    source: 'SERVER_HALF_BIAS'
                };
            });

            data[raceNum].horses.sort((a,b) => {
                const diff = parseFloat(b.total_score || 0) - parseFloat(a.total_score || 0);
                if (diff !== 0) return diff;
                const odiff = parseFloat(b.wr_v35_original_score || 0) - parseFloat(a.wr_v35_original_score || 0);
                if (odiff !== 0) return odiff;
                const ediff = parseInt(b.expert_count || 0) - parseInt(a.expert_count || 0);
                if (ediff !== 0) return ediff;
                return parseInt(a.horse_num || 0) - parseInt(b.horse_num || 0);
            });
        });
        return data;
    }
    Object.keys(data).forEach(raceNum => {
        if (!data[raceNum] || !data[raceNum].horses) return;
        const ctx = wrV35GetRaceContext(raceNum);
        const paceType = wrV35ClassifyPace(raceNum);

        data[raceNum].horses.forEach(horse => {
            const ex = wrV35GetHorseExtra(raceNum, horse.horse_num);
            const style = wrV35Norm(ex.running_style || horse.running_style || '');
            const draw = parseInt(ex.draw || ex.draw_no || horse.draw || 0);
            const drawGroup = wrV35DrawGroup(draw);
            const params = {...ctx, running_style: style, draw_group: drawGroup, pace_type: paceType};

            const trackRow = wrV35PickBestBias(wrV35TrackBiasRows || [], params, 'track');
            const drawRow = wrV35PickBestBias(wrV35DrawBiasRows || [], params, 'draw');
            const paceRow = wrV35PickBestBias(wrV35PaceBiasRows || [], params, 'pace');

            const trackScore = trackRow ? wrV35RateToScore(trackRow.top3_rate, trackRow.top2_rate, trackRow.total_runs) : 0;
            const drawScore = drawRow ? wrV35RateToScore(drawRow.top3_rate, drawRow.top2_rate, drawRow.total_runs) : 0;
            const paceScore = paceRow ? wrV35RateToScore(paceRow.top3_rate, paceRow.top2_rate, paceRow.total_runs) : 0;
            const totalBias = Math.max(-3, Math.min(5, Math.round((trackScore + drawScore + paceScore) * 100) / 100));

            const originalScore = parseFloat(horse.total_score || 0);
            horse.wr_v35_original_score = originalScore;
            horse.wr_v35_bias_score = totalBias;
            horse.wr_v35_bias_detail = {
                style, draw, drawGroup, paceType, ctx,
                track: trackRow ? {runs: trackRow.total_runs, top2: trackRow.top2_rate, top3: trackRow.top3_rate, score: trackScore} : null,
                drawBias: drawRow ? {runs: drawRow.total_runs, top2: drawRow.top2_rate, top3: drawRow.top3_rate, score: drawScore} : null,
                pace: paceRow ? {runs: paceRow.total_runs, top2: paceRow.top2_rate, top3: paceRow.top3_rate, score: paceScore} : null
            };
            horse.total_score = Math.round((originalScore + totalBias) * 100) / 100;
        });

        // 重新按 Bias 後 total_score 排序；同分時保留原分、名家數、馬號排序
        data[raceNum].horses.sort((a,b) => {
            const diff = parseFloat(b.total_score || 0) - parseFloat(a.total_score || 0);
            if (diff !== 0) return diff;
            const odiff = parseFloat(b.wr_v35_original_score || 0) - parseFloat(a.wr_v35_original_score || 0);
            if (odiff !== 0) return odiff;
            const ediff = parseInt(b.expert_count || 0) - parseInt(a.expert_count || 0);
            if (ediff !== 0) return ediff;
            return parseInt(a.horse_num || 0) - parseInt(b.horse_num || 0);
        });
    });
    return data;
}

// =========================================================================
// 🚦 [WinRacing v55 Signal Light Display Bridge｜只改顯示，不改分]
// 目的：get_analysis_ajax.php 已輸出 historical_top2_display，但舊 UI 仍把 NO_POWER / N/A / label 一併顯示。
// 本層只清潔 formula 文字，並把訊號燈獨立高亮顯示。
// =========================================================================
function wrV55CleanBiasFormula(raw, signalText) {
    let txt = String(raw || '');
    if (signalText) txt = txt.split(signalText).join('');
    txt = txt.replace(/｜?▫️\s*歷史Top2：N\/A/g, '');
    txt = txt.replace(/｜?未命中已回測組合/g, '');
    txt = txt.replace(/｜?NO_POWER/g, '');
    txt = txt.replace(/\|\s*NO_POWER/g, '');
    txt = txt.replace(/｜{2,}/g, '｜');
    txt = txt.replace(/\s*｜\s*$/g, '');
    txt = txt.trim();
    return txt;
}

function wrV55SignalHtml(horse) {
    const display = String(horse.historical_top2_display || '').trim();
    if (!display) return '';
    let color = '#fde68a';
    let bg = 'rgba(253,230,138,0.08)';
    let border = 'rgba(253,230,138,0.35)';
    if (display.includes('神級')) { color = '#ffd900'; bg = 'rgba(255,217,0,0.12)'; border = 'rgba(255,217,0,0.55)'; }
    else if (display.includes('強組合')) { color = '#22c55e'; bg = 'rgba(34,197,94,0.10)'; border = 'rgba(34,197,94,0.45)'; }
    else if (display.includes('高危')) { color = '#f87171'; bg = 'rgba(248,113,113,0.10)'; border = 'rgba(248,113,113,0.45)'; }
    return `<br><small style="display:inline-block;margin-top:3px;padding:2px 7px;border-radius:7px;border:1px solid ${border};background:${bg};color:${color};font-weight:950;">${display}</small>`;
}

function wrV35BiasMiniHtml(horse) {
    const rawBias = (horse.wr_v35_bias_score !== undefined) ? horse.wr_v35_bias_score : (horse.bias_score !== undefined ? horse.bias_score : 0);
    const b = parseFloat(rawBias || 0);
    const d = horse.wr_v35_bias_detail || horse.bias_breakdown || {};
    const sign = b >= 0 ? '+' : '';
    const color = b > 0 ? '#22c55e' : (b < 0 ? '#ef4444' : '#94a3b8');

    const styleText = wrV35Norm(d.style || horse.bias_running_style || horse.pace_type || horse.predicted_running_style || horse.running_style || '-');
    const drawText = wrV35Norm(d.drawGroup || d.draw_category || horse.draw_category || wrV35DrawGroup(horse.draw) || '-');
    const drawNo = parseInt(d.draw || horse.draw || 0);
    const original = parseFloat(horse.wr_v35_original_score || horse.original_score || 0);
    const powerScore = parseFloat(horse.v51_power_score || (d.power_score !== undefined ? d.power_score : 0) || 0);
    const powerNote = String(horse.v51_power_note || d.power_note || '').replace('NO_POWER','').split('：')[0];
    const signalText = String(horse.historical_top2_display || '').trim();

    let signalHtml = '';
    if (signalText) {
        const isDanger = signalText.indexOf('高危') >= 0 || signalText.indexOf('熄燈') >= 0 || signalText.indexOf('💀') >= 0;
        const sigColor = isDanger ? '#ef4444' : '#22c55e';
        signalHtml = `<div class="wr-signal-compact" style="border:1px solid ${sigColor}; color:${sigColor}; background:rgba(0,0,0,0.18);">${signalText}</div>`;
    }

    const powerLine = powerScore > 0
        ? `<div class="wr-bias-power">Power +${powerScore}${powerNote ? '｜' + powerNote : ''}</div>`
        : '';

    return `<div class="wr-bias-mini">` +
           `<div class="wr-bias-main" style="color:${color};">Bias ${sign}${b.toFixed(2)}｜原 ${original.toFixed(2)}</div>` +
           `<div class="wr-bias-style">🏇${styleText || '-'}｜📍${drawText || '-'}${drawNo ? `｜${drawNo}檔` : ''}</div>` +
           powerLine +
           signalHtml +
           `</div>`;
}


// =========================================================================
// 🏇 [WinRacing v66 投注服務標籤保守修正版｜只加不減]
// 直接服務投注：🏆 軍師鐵膽候選 / 🟢 主力腳 / 💀 假膽警告
// 只讀取現有 total_score、historical_top2、四軌/五軌分，不改分、不改排序。
// =========================================================================
function wrV65ExtractFirstNumber(value) {
    const txt = String(value ?? '');
    const m = txt.match(/-?\d+(?:\.\d+)?/);
    return m ? parseFloat(m[0]) : 0;
}

function wrV65BetServiceTagHtml(horse, fourTrackText, fiveTrackText, raceNum, rank, raceTopFakeRisk) {
    const total = parseFloat(horse.total_score || 0);
    const original = parseFloat(horse.original_score || horse.wr_v35_original_score || 0);
    const expertCount = parseInt(horse.expert_count || 0);
    const p1Count = Array.isArray(horse.p1_from) ? horse.p1_from.length : parseInt(horse.v49_p1_count || 0);
    const histRateRaw = horse.historical_top2_rate;
    const histRate = (histRateRaw === null || histRateRaw === undefined || histRateRaw === '') ? null : parseFloat(histRateRaw);
    const signalText = String(horse.historical_top2_display || '').trim();
    const fourScore = wrV65ExtractFirstNumber(fourTrackText);
    const fiveScore = wrV65ExtractFirstNumber(fiveTrackText);
    const hNum = parseInt(horse.horse_num || 0);

    // v66 修正：投注標籤改保守，避免第8/9場呢類「高分但校準偏低」亂掛鐵膽。
    // 只加不減：保留原 v65 標籤功能，但收緊觸發門檻，不改分、不改排序。
    let cacheFake = false;
    try {
        const c = globalCacheMap?.[currentSelectedDate]?.[raceNum]?.[hNum];
        cacheFake = c && parseInt(c.fake || 0) === 1;
    } catch(e) { cacheFake = false; }

    const isFakeDanger =
        cacheFake ||
        (histRate !== null && !isNaN(histRate) && histRate > 0 && histRate < 20) ||
        signalText.includes('高危') || signalText.includes('熄燈') || signalText.includes('💀');

    const hasGoodHistory = (histRate !== null && !isNaN(histRate) && histRate >= 55);
    const hasStrongScore = (fiveScore >= 50 || fourScore >= 60 || total >= 28 || original >= 25);

    const isTiedanCandidate =
        !isFakeDanger &&
        !raceTopFakeRisk &&
        parseInt(rank || 0) === 1 &&
        hasGoodHistory &&
        hasStrongScore;

    const isMainLeg =
        !isFakeDanger &&
        !isTiedanCandidate &&
        parseInt(rank || 0) <= 5 &&
        (
            fiveScore >= 18 ||
            fourScore >= 25 ||
            total >= 10 ||
            (total >= 6 && expertCount >= 4) ||
            (total >= 6 && p1Count >= 2)
        );

    if (isFakeDanger) {
        const reason = histRate !== null && !isNaN(histRate) && histRate > 0 ? `Top2 ${histRate.toFixed(2)}%` : '高危/假膽訊號';
        return `<div class="wr-v65-bet-tag-wrap"><span class="wr-v65-bet-tag wr-v65-fake">💀 假膽警告<span class="wr-v65-reason">${reason}</span></span></div>`;
    }
    if (isTiedanCandidate) {
        let reason = '歷史+多軌過關';
        if (histRate !== null && !isNaN(histRate)) reason = `Top2 ${histRate.toFixed(1)}%`;
        if (fiveScore >= 50) reason += `｜五軌 ${fiveScore.toFixed(1)}`;
        else if (fourScore >= 60) reason += `｜四軌 ${fourScore.toFixed(1)}`;
        else if (total >= 28) reason += `｜總分 ${total.toFixed(1)}`;
        return `<div class="wr-v65-bet-tag-wrap"><span class="wr-v65-bet-tag wr-v65-tiedan">🏆 軍師鐵膽候選<span class="wr-v65-reason">${reason}</span></span></div>`;
    }
    if (isMainLeg) {
        let reason = '可入腳';
        if (fiveScore >= 18) reason = `五軌 ${fiveScore.toFixed(1)}`;
        else if (fourScore >= 25) reason = `四軌 ${fourScore.toFixed(1)}`;
        else if (total >= 10) reason = `總分 ${total.toFixed(1)}`;
        else if (expertCount >= 4) reason = `名家 ${expertCount}位`;
        return `<div class="wr-v65-bet-tag-wrap"><span class="wr-v65-bet-tag wr-v65-leg">🟢 主力腳<span class="wr-v65-reason">${reason}</span></span></div>`;
    }
    return '';
}
// =========================================================================

// =========================================================================

function executeAnalysisEngine(dateId) {
    const container = document.getElementById('analysisReportContainer');
    const loader = document.getElementById('loadingOverlay');
    const tabGroup = document.getElementById('tabGroup');
    
    container.style.display = 'none';
    tabGroup.style.display = 'none';
    loader.style.display = 'block';
    
    Promise.all([
        fetch(`get_analysis_ajax.php?race_id=${dateId}`).then(res => res.json()),
        fetch(`get_analysis_trend_ajax.php?race_id=${dateId}`).then(res => res.json()),
        fetch(`get_analysis_trend_ajax_(4_person_select_version).php?race_id=${dateId}`).then(res => res.json())
    ])
    .then(([normalData, trendData, fiveTrackData]) => {
        loader.style.display = 'none';
        container.style.display = 'block';
        tabGroup.style.display = 'block'; 
        
        // ✅ WinRacing v39：後端 get_analysis_ajax 已完成 Half Bias；前端只顯示，不再重複入分
        globalAnalysisData = wrV35ApplyBiasToAnalysisData(normalData);
        globalTrendData = trendData;   
        globalFiveTrackData = fiveTrackData;

        // 🧠 WinRacing V17.3：分析完成後，自動寫入 AI Brain（只加不改）
        wrV173AutoLogDecisions();
        
        if(currentViewMode === "v35_engine") {
            renderV35TiedanEngineDashboard(globalAnalysisData, globalTrendData, globalFiveTrackData);
        } else if(currentViewMode === "matrix") {
            renderMatrixDashboard(globalAnalysisData, globalTrendData, globalFiveTrackData);
        } else if(currentViewMode === "advisor") {
            renderAdvisorTiedanDashboard(globalAnalysisData);
        } else if(currentViewMode === "advisor_top2") {
            renderAdvisorTop2Dashboard(globalAnalysisData);
        } else if(currentViewMode === "advisor_top3") {
            renderAdvisorTop3Dashboard(globalAnalysisData);
        } else if(currentViewMode === "advisor_hidden_q") {
            renderAdvisorHiddenQDashboard(globalAnalysisData);
        } else if(currentViewMode === "advisor_sixstar") {
            renderAdvisorSixStarDashboard(globalAnalysisData);
        } else if(currentViewMode === "advisor_qking") {
            renderAdvisorQKingDashboard(globalAnalysisData);
        } else if(currentViewMode === "advisor_assassin") {
            renderAdvisorAssassinDashboard(globalAnalysisData);
        } else {
            renderStrategyDashboard(globalAnalysisData, globalTrendData, globalFiveTrackData, currentViewMode);
        }
    })
    .catch(err => {
        console.error(err);
        loader.style.display = 'none';
        container.style.display = 'block';
        container.innerHTML = `<div class="alert alert-danger">❌ 分析引擎發生異常，請確認後台數據錄入是否完整。</div>`;
    });
}


// =========================================================================
// 🏆🔥 [WinRacing v3.5 今日鐵膽 Engine｜只加不刪]
// 四張實戰卡：今日鐵膽排行榜 / 今日投注模式 / 最高風險假膽 / 冷門雷達Top3
// =========================================================================
function wrV35GetTodayCacheRows() {
    const rows = [];
    if (!globalCacheMap || !currentSelectedDate || !globalCacheMap[currentSelectedDate]) return rows;
    Object.keys(globalCacheMap[currentSelectedDate]).forEach(raceNum => {
        const raceObj = globalCacheMap[currentSelectedDate][raceNum] || {};
        Object.keys(raceObj).forEach(horseNum => {
            const c = raceObj[horseNum] || {};
            rows.push({
                race: parseInt(raceNum),
                horse: parseInt(horseNum),
                tag: c.tag || '-',
                conf: parseFloat(c.conf || 0),
                fake: parseInt(c.fake || 0),
                radar: parseInt(c.radar || 0)
            });
        });
    });
    return rows;
}

function wrV35BetLevel(conf, fake, radar) {
    conf = parseFloat(conf || 0);
    if (fake === 1 && conf >= 60) return {level:'⚠️ B級警戒', cls:'v35-risk', mode:'只可細注位置Q，忌重注單膽'};
    if (conf >= 70) return {level:'A級主攻', cls:'v35-rate', mode:'可作連贏膽 / 位置Q膽'};
    if (conf >= 55) return {level:'B級出擊', cls:'v35-rate', mode:'可作副膽，拖腳要保守'};
    if (conf >= 40 || radar === 1) return {level:'C級觀察', cls:'v35-radar', mode:'可作腳，不宜做膽'};
    return {level:'D級放棄', cls:'v35-risk', mode:'不建議出膽'};
}

function wrV35HorseLabel(raceNum, horseNum) {
    const info = getHorseCardInfo(raceNum, horseNum);
    if (!info) return `R${raceNum} #${horseNum}`;
    return `R${raceNum} #${horseNum} ${info.name || ''}`;
}


// =========================================================================
// 🔥 [WinRacing v41 第二刀｜PACE BIAS 場次雷達]
// 用已回傳的 bias_running_style / pace_type / predicted_running_style 即時計算每場步速形態。
// 不再改分，只做決策顯示：前速壓力、慢步速、正常步速、放頭優勢。
// =========================================================================
function wrV41NormStyleForPace(v) {
    let s = wrV35Norm(v);
    if (s === '跟前' || s === '前置') s = '咬前';
    if (s === '置中') s = '中置';
    if (s === '領放') s = '放頭';
    if (s === '留後') s = '後上';
    return s;
}

function wrV41GetHorseStyle(raceNum, horse) {
    const ex = wrV35GetHorseExtra(raceNum, horse.horse_num) || {};
    return wrV41NormStyleForPace(
        horse.bias_running_style ||
        horse.pace_type ||
        horse.predicted_running_style ||
        horse.running_style ||
        ex.running_style ||
        ex.predicted_running_style ||
        ''
    ) || '中置';
}

function wrV41BuildPaceRows(data) {
    const rows = [];
    if (!data) return rows;
    Object.keys(data).forEach(raceNum => {
        const race = data[raceNum];
        if (!race || !race.horses || !race.horses.length) return;
        let cnt = { '放頭':0, '咬前':0, '中置':0, '後上':0 };
        let frontHorses = [];
        let biasHot = [];
        race.horses.forEach(h => {
            const style = wrV41GetHorseStyle(raceNum, h);
            if (cnt[style] === undefined) cnt[style] = 0;
            cnt[style]++;
            const b = parseFloat(h.bias_score !== undefined ? h.bias_score : h.wr_v35_bias_score || 0);
            const item = { horse: parseInt(h.horse_num), style, bias: b, score: parseFloat(h.total_score || 0) };
            if (style === '放頭' || style === '咬前') frontHorses.push(item);
            if (b >= 2) biasHot.push(item);
        });
        const front = cnt['放頭'] || 0;
        const early = (cnt['放頭'] || 0) + (cnt['咬前'] || 0);
        let paceType = 'NORMAL_PACE', paceLabel = '正常步速', cls = 'v41-pace-normal', hint = '照分數與名家排序，Bias 只作微調。';
        if (front >= 3) {
            paceType = 'FAST_PACE'; paceLabel = '前速混戰'; cls = 'v41-pace-fast';
            hint = '多匹放頭互搶，前領馬折讓；中置/後上可作腳，膽要避純前速過熱。';
        } else if (early >= 5) {
            paceType = 'PRESSURE_PACE'; paceLabel = '壓迫步速'; cls = 'v41-pace-pressure';
            hint = '前中段壓力大，咬前馬要睇檔位；內檔中置有利偷位。';
        } else if (front <= 1 && early <= 2) {
            paceType = 'SLOW_PACE'; paceLabel = '慢步速'; cls = 'v41-pace-slow';
            hint = '前速少，放頭/咬前有節奏優勢；後上馬不宜重膽。';
        }
        frontHorses.sort((a,b)=> b.score-a.score || b.bias-a.bias || a.horse-b.horse);
        biasHot.sort((a,b)=> b.bias-a.bias || b.score-a.score || a.horse-b.horse);
        rows.push({
            race: parseInt(raceNum), paceType, paceLabel, cls, hint, cnt, early,
            frontText: frontHorses.slice(0,3).map(x=>`#${x.horse}${x.style}`).join(' / ') || '-',
            biasText: biasHot.slice(0,3).map(x=>`#${x.horse} Bias ${x.bias>=0?'+':''}${x.bias.toFixed(1)}`).join(' / ') || '-'
        });
    });
    rows.sort((a,b)=>a.race-b.race);
    return rows;
}

function wrV41RenderPaceBiasPanel(data) {
    const rows = wrV41BuildPaceRows(data);
    if (!rows.length) return '';
    return `
        <div class="v41-pace-shell">
            <div class="v41-pace-title">🔥 PACE BIAS 場次雷達 <span class="v35-badge">第二刀</span><span class="v35-badge">只顯示不改分</span></div>
            <div class="table-responsive">
                <table class="v41-pace-table">
                    <thead><tr><th>場次</th><th>步速形態</th><th>放頭</th><th>咬前</th><th>中置</th><th>後上</th><th>前速馬</th><th>Bias加分馬</th><th>投注提示</th></tr></thead>
                    <tbody>
                        ${rows.map(r=>`
                            <tr>
                                <td><b>R${r.race}</b></td>
                                <td class="${r.cls}">${r.paceLabel}<div class="v35-muted">${r.paceType}</div></td>
                                <td>${r.cnt['放頭'] || 0}</td>
                                <td>${r.cnt['咬前'] || 0}</td>
                                <td>${r.cnt['中置'] || 0}</td>
                                <td>${r.cnt['後上'] || 0}</td>
                                <td>${r.frontText}</td>
                                <td>${r.biasText}</td>
                                <td class="v41-pace-hint">${r.hint}</td>
                            </tr>`).join('')}
                    </tbody>
                </table>
            </div>
        </div>`;
}
// =========================================================================

function renderV35TiedanEngineDashboard(normalData, trendData, fiveTrackData) {
    const container = document.getElementById('analysisReportContainer');
    const rows = wrV35GetTodayCacheRows();

    let tiedanRows = rows
        .filter(r => r.fake !== 1 && r.conf >= 40)
        .sort((a,b) => {
            if (b.conf !== a.conf) return b.conf - a.conf;
            if ((b.tag === 'SUPER_TIEDAN') !== (a.tag === 'SUPER_TIEDAN')) return (b.tag === 'SUPER_TIEDAN') ? 1 : -1;
            return a.race - b.race;
        })
        .slice(0,3);

    let fakeRows = rows
        .filter(r => r.fake === 1)
        .sort((a,b) => b.conf - a.conf)
        .slice(0,5);

    let radarRows = rows
        .filter(r => r.radar === 1)
        .sort((a,b) => b.conf - a.conf)
        .slice(0,3);

    const best = tiedanRows.length ? tiedanRows[0] : null;
    let strategyTitle = '📭 等待資料';
    let strategyDesc = '今日未有足夠 analysis_cache 訊號，請先確認已跑 cron。';
    if (best) {
        const lvl = wrV35BetLevel(best.conf, best.fake, best.radar);
        if (best.conf >= 70 && fakeRows.length <= 2) {
            strategyTitle = '🔥 主攻模式';
            strategyDesc = `以 ${wrV35HorseLabel(best.race,best.horse)} 為核心，${lvl.mode}。`;
        } else if (fakeRows.length >= 4) {
            strategyTitle = '💣 防守爆冷模式';
            strategyDesc = '今日假膽風險偏多，建議減注，重點觀察冷門雷達與位置Q。';
        } else {
            strategyTitle = '🎯 穩健出擊模式';
            strategyDesc = `主膽以最高 Confidence 候選為核心，其他只作腳或副膽。`;
        }
    }

    let html = `
    <div class="v35-engine-shell">
        <div class="v35-engine-title">
            🏆 今日鐵膽 Engine v3.5
            <span class="v35-badge">只加不刪</span>
            <span class="v35-badge">日期：${currentSelectedDate || '-'}</span>
        </div>

        <div class="v35-grid">
            <div class="v35-card gold">
                <div class="v35-card-title">🏆 今日鐵膽排行榜</div>
                ${tiedanRows.length ? tiedanRows.map((r,idx)=>{
                    const lvl = wrV35BetLevel(r.conf,r.fake,r.radar);
                    const medal = idx===0?'🥇':(idx===1?'🥈':'🥉');
                    return `<div class="v35-row"><span>${medal} ${wrV35HorseLabel(r.race,r.horse)}<br><span class="v35-muted">${r.tag}｜${lvl.level}</span></span><span class="v35-rate">${r.conf.toFixed(1)}%</span></div>`;
                }).join('') : '<div class="v35-muted">暫無鐵膽候選</div>'}
            </div>

            <div class="v35-card green">
                <div class="v35-card-title">🔥 今日投注模式</div>
                <div class="v35-main">${strategyTitle}</div>
                <div class="v35-muted mt-2">${strategyDesc}</div>
                ${wrV35ResonanceRows && wrV35ResonanceRows.length ? `<div class="mt-2"><span class="v35-badge">最強共振 ${parseFloat(wrV35ResonanceRows[0].top2_rate).toFixed(2)}%</span></div>` : ''}
            </div>

            <div class="v35-card red">
                <div class="v35-card-title">⚠️ 今日最高風險假膽</div>
                ${fakeRows.length ? fakeRows.map((r,idx)=>{
                    return `<div class="v35-row"><span>#${idx+1} ${wrV35HorseLabel(r.race,r.horse)}<br><span class="v35-muted">${r.tag}</span></span><span class="v35-risk">${r.conf.toFixed(1)}%</span></div>`;
                }).join('') : '<div class="v35-main v35-rate">安全</div><div class="v35-muted">暫無高危假膽</div>'}
            </div>

            <div class="v35-card blue">
                <div class="v35-card-title">💣 冷門雷達 Top3</div>
                ${radarRows.length ? radarRows.map((r,idx)=>{
                    return `<div class="v35-row"><span>#${idx+1} ${wrV35HorseLabel(r.race,r.horse)}<br><span class="v35-muted">${r.tag}</span></span><span class="v35-radar">${r.conf.toFixed(1)}%</span></div>`;
                }).join('') : '<div class="v35-muted">暫無冷門雷達訊號</div>'}
            </div>
        </div>

        ${wrV41RenderPaceBiasPanel(normalData)}

        <div class="v35-card">
            <div class="v35-card-title">🔥 歷史法規共振 Top3</div>
            ${wrV35ResonanceRows && wrV35ResonanceRows.length ? `
                <div class="table-responsive">
                    <table class="v35-table">
                        <thead><tr><th>排名</th><th>共振組合</th><th>Top2%</th><th>樣本</th><th>Win%</th><th>投注提示</th></tr></thead>
                        <tbody>
                            ${wrV35ResonanceRows.map((r,idx)=>{
                                const top2 = parseFloat(r.top2_rate || 0);
                                const win = parseFloat(r.win_rate || 0);
                                const hint = top2 >= 64 ? 'A級共振｜主攻連贏/位置Q' : (top2 >= 60 ? 'B+共振｜副攻' : 'B級共振｜觀察');
                                return `<tr>
                                    <td>${idx===0?'🥇':(idx===1?'🥈':'🥉')}</td>
                                    <td style="text-align:left;font-weight:900;color:#fef3c7;">${r.combo_name}<div class="v35-muted">${r.combo_key}</div></td>
                                    <td class="v35-rate">${top2.toFixed(2)}%</td>
                                    <td>${parseInt(r.top2_hits || 0)} / ${parseInt(r.total_cases || 0)}</td>
                                    <td class="v35-radar">${win.toFixed(2)}%</td>
                                    <td>${hint}</td>
                                </tr>`;
                            }).join('')}
                        </tbody>
                    </table>
                </div>` : '<div class="v35-muted">未有 rule_resonance_stats，請先跑 cron。</div>'}
        </div>
    </div>`;

    container.innerHTML = html;
}
// =========================================================================



// =========================================================================
// 🏆 [WinRacing v43 Emergency Fix｜第三刀決策總覽補回]
// 修正 v42 漏放 wrV42RenderDecisionPanel() 導致 Matrix 畫面進入 catch 顯示「分析引擎異常」。
// 只讀 normalData，不改分，不影響 AJAX / Bias Engine。
// =========================================================================
function wrV42SafeNum(v, def = 0) {
    const n = parseFloat(v);
    return isNaN(n) ? def : n;
}

function wrV42HorseName(raceNum, horseNum) {
    try {
        const info = getHorseCardInfo(raceNum, horseNum);
        if (info && info.name) return `R${raceNum} #${horseNum} ${info.name}`;
    } catch(e) {}
    return `R${raceNum} #${horseNum}`;
}

function wrV42GetAllHorses(data) {
    const rows = [];
    if (!data) return rows;
    Object.keys(data).forEach(raceNum => {
        const race = data[raceNum] || {};
        const horses = race.horses || [];
        horses.forEach((h, idx) => {
            const total = wrV42SafeNum(h.total_score, 0);
            const original = wrV42SafeNum(h.original_score !== undefined ? h.original_score : h.wr_v35_original_score, total);
            const bias = wrV42SafeNum(h.bias_score !== undefined ? h.bias_score : h.wr_v35_bias_score, 0);
            const hit = parseInt(h.hit_count || 0);
            const experts = parseInt(h.expert_count || 0);
            const p1 = (h.p1_from || []).length;
            const style = wrV35Norm(h.bias_running_style || h.pace_type || h.predicted_running_style || h.running_style || '');
            const drawCat = wrV35Norm(h.draw_category || wrV35DrawGroup(h.draw) || '');
            const draw = parseInt(h.draw || 0);
            const formula = h.bias_formula || '';
            rows.push({
                race: parseInt(raceNum),
                horse: parseInt(h.horse_num),
                total, original, bias, hit, experts, p1, style, drawCat, draw, formula,
                rankInRace: idx + 1,
                tag: (race.meta && race.meta.type) ? race.meta.type : '',
                label: wrV42HorseName(raceNum, h.horse_num)
            });
        });
    });
    return rows;
}

function wrV42RenderMiniRows(rows, type) {
    if (!rows || !rows.length) return '<div class="v42-muted">暫無足夠訊號</div>';
    return rows.map((r, idx) => {
        const medal = idx === 0 ? '🥇' : (idx === 1 ? '🥈' : '🥉');
        const biasTxt = `${r.bias >= 0 ? '+' : ''}${r.bias.toFixed(2)}`;
        const scoreTxt = r.total.toFixed(2);
        const sub = `原 ${r.original.toFixed(2)}｜Bias ${biasTxt}｜${r.style || '-'} ${r.drawCat || '-'}${r.draw ? '｜'+r.draw+'檔' : ''}`;
        let valCls = 'v42-rate';
        if (type === 'risk') valCls = 'v42-risk';
        if (type === 'radar') valCls = 'v42-radar';
        return `<div class="v42-row"><span class="v42-horse-line">${medal} ${r.label}<br><span class="v42-muted">${sub}</span></span><span class="${valCls}">${scoreTxt}</span></div>`;
    }).join('');
}

function wrV42RenderDecisionPanel(normalData) {
    try {
        const all = wrV42GetAllHorses(normalData);
        if (!all.length) return '';

        const byRace = {};
        all.forEach(r => {
            if (!byRace[r.race]) byRace[r.race] = [];
            byRace[r.race].push(r);
        });
        Object.keys(byRace).forEach(rn => byRace[rn].sort((a,b)=>b.total-a.total));

        // v44 實戰保守版：鐵膽不再單靠總分，必須同時過四關。
        // 目的：減少「睇到但唔中」的假鐵膽輸出。
        const bankerCandidates = Object.keys(byRace).map(rn => {
            const hs = byRace[rn];
            const top = hs[0];
            const second = hs[1];
            const gap = second ? top.total - second.total : top.total;
            return Object.assign({}, top, { gap });
        }).filter(r =>
            r.rankInRace === 1 &&
            r.total >= 24 &&
            r.p1 >= 4 &&
            r.experts >= 5 &&
            r.gap >= 8 &&
            r.bias >= 0
        ).sort((a,b) => b.total - a.total || b.gap - a.gap || b.p1 - a.p1).slice(0, 3);

        const backupBankers = Object.keys(byRace).map(rn => {
            const hs = byRace[rn];
            const top = hs[0];
            const second = hs[1];
            const gap = second ? top.total - second.total : top.total;
            return Object.assign({}, top, { gap });
        }).filter(r =>
            r.rankInRace === 1 &&
            r.total >= 18 &&
            r.experts >= 5 &&
            r.bias >= 0
        ).sort((a,b) => b.total - a.total || b.gap - a.gap || b.experts - a.experts).slice(0, 3);

        const topBankers = bankerCandidates.length ? bankerCandidates : backupBankers;
        const bankerTitle = bankerCandidates.length ? '🏆 今日鐵膽排行榜' : '🏆 今日候選膽：未達鐵膽門檻';

        // 風險假膽：分高但 Bias 扣分 / 後上外檔 / Gap 不夠，優先提示唔好硬膽。
        const riskRows = Object.keys(byRace).map(rn => {
            const hs = byRace[rn];
            const top = hs[0];
            const second = hs[1];
            const gap = second ? top.total - second.total : top.total;
            return Object.assign({}, top, { gap });
        }).filter(r =>
            r.total >= 10 &&
            (r.bias < 0 || r.gap < 3 || (r.style === '後上' && (r.drawCat === '外檔' || r.drawCat === '田大外檔')))
        ).sort((a,b) => (a.bias - b.bias) || (a.gap - b.gap) || (b.total - a.total)).slice(0, 3);

        // 冷門雷達：原分低但 Bias 強推，並且不是全無名家支持。
        const radarRows = all
            .filter(r => r.original <= 6 && r.bias >= 2 && (r.experts >= 2 || r.p1 >= 1))
            .sort((a,b) => b.bias - a.bias || b.total - a.total || b.experts - a.experts)
            .slice(0, 3);

        const raceModes = Object.keys(byRace).map(rn => {
            const hs = byRace[rn];
            const top = hs[0];
            const second = hs[1];
            const gap = second ? top.total - second.total : top.total;
            let mode = '觀察/少注';
            let note = '未達主攻門檻';
            if (top.total >= 24 && top.p1 >= 4 && top.experts >= 5 && gap >= 8 && top.bias >= 0) {
                mode = '主攻鐵膽'; note = '總分/P1/Gap/Bias 全過關';
            } else if (top.total >= 18 && top.experts >= 5 && gap >= 4 && top.bias >= 0) {
                mode = '穩健膽拖'; note = '可做膽但未達鐵膽';
            } else if (gap <= 2) {
                mode = '混戰拖腳'; note = 'Top差距太近';
            } else if (top.bias < 0) {
                mode = '高分假膽風險'; note = '名家高分但Bias扣分';
            }
            return {race: parseInt(rn), top, gap, mode, note};
        }).sort((a,b)=>{
            const order = {'主攻鐵膽':4,'穩健膽拖':3,'高分假膽風險':2,'混戰拖腳':1,'觀察/少注':0};
            return (order[b.mode]||0)-(order[a.mode]||0) || b.top.total-a.top.total;
        }).slice(0,4);

        const strategyHtml = raceModes.length ? raceModes.map((m,idx)=>{
            const cls = m.mode.indexOf('主攻') >= 0 || m.mode.indexOf('穩健') >= 0 ? 'v42-rate' : (m.mode.indexOf('風險') >= 0 || m.mode.indexOf('混戰') >= 0 ? 'v42-risk' : 'v42-radar');
            return `<div class="v42-row"><span>R${m.race}｜${m.mode}<br><span class="v42-muted">膽 ${m.top.horse}｜Gap ${m.gap.toFixed(2)}｜${m.note}</span></span><span class="${cls}">${m.top.total.toFixed(2)}</span></div>`;
        }).join('') : '<div class="v42-muted">暫無模式</div>';

        const thresholdNote = bankerCandidates.length
            ? '<span class="v42-badge">鐵膽門檻：總分≥24 / P1≥4 / 名家≥5 / Gap≥8 / Bias≥0</span>'
            : '<span class="v42-badge">今日無真正鐵膽，只顯示候選膽，投注要降級</span>';

        return `
        <div class="v42-decision-shell">
            <div class="v42-decision-title">🏆 今日決策總覽 <span class="v42-badge">v44保守校準版</span><span class="v42-badge">只顯示不改分</span><span class="v42-badge">${currentSelectedDate || '-'}</span>${thresholdNote}</div>
            <div class="v42-grid">
                <div class="v42-card gold">
                    <div class="v42-card-title">${bankerTitle}</div>
                    ${wrV42RenderMiniRows(topBankers, 'banker')}
                </div>
                <div class="v42-card green">
                    <div class="v42-card-title">🔥 今日投注模式</div>
                    ${strategyHtml}
                </div>
                <div class="v42-card red">
                    <div class="v42-card-title">⚠️ 今日最高風險假膽</div>
                    ${wrV42RenderMiniRows(riskRows, 'risk')}
                </div>
                <div class="v42-card blue">
                    <div class="v42-card-title">💣 冷門雷達 Top3</div>
                    ${wrV42RenderMiniRows(radarRows, 'radar')}
                </div>
            </div>
        </div>`;
    } catch(e) {
        console.error('wrV42RenderDecisionPanel error', e);
        return '';
    }
}
// =========================================================================



// =========================================================================
// 🏆 [WinRacing v61 Signal Decision Dashboard｜只顯示，不改分]
// 目的：把 v54 Signal Light / v52 Power / Bias 結果整合成頁頂 4 張決策卡：
// 今日鐵膽排行榜 / 今日投注模式 / 最高風險假膽 / 冷門雷達 Top3。
// 安全：只讀 normalData，不修改 horse.total_score，不影響排序與計分。
// =========================================================================
function wrV61SafeNum(v, def = 0) {
    const n = parseFloat(v);
    return isNaN(n) ? def : n;
}
function wrV61HorseLabel(raceNum, horseNum) {
    try {
        const info = getHorseCardInfo(raceNum, horseNum);
        if (info && info.name) return `R${raceNum} #${horseNum} ${info.name}`;
    } catch(e) {}
    return `R${raceNum} #${horseNum}`;
}
function wrV61FlattenRows(data) {
    const rows = [];
    if (!data) return rows;
    Object.keys(data).forEach(raceNum => {
        const horses = (data[raceNum] && data[raceNum].horses) ? data[raceNum].horses : [];
        horses.forEach((h, idx) => {
            const signalText = String(h.historical_top2_display || '').trim();
            const signalRate = wrV61SafeNum(h.historical_top2_rate, 0);
            const total = wrV61SafeNum(h.total_score, 0);
            const original = wrV61SafeNum(h.original_score !== undefined ? h.original_score : h.wr_v35_original_score, total);
            const bias = wrV61SafeNum(h.bias_score !== undefined ? h.bias_score : h.wr_v35_bias_score, 0);
            const p1 = Array.isArray(h.p1_from) ? h.p1_from.length : 0;
            const experts = parseInt(h.expert_count || 0);
            const hit = parseInt(h.hit_count || 0);
            const isDanger = signalText.includes('高危') || signalRate > 0 && signalRate < 25;
            const isGod = signalText.includes('神級') || signalRate >= 70;
            const isStrong = signalText.includes('強組合') || (signalRate >= 60 && signalRate < 70);
            const isNormalSignal = signalText.includes('普通組合') || (signalRate >= 40 && signalRate < 60);
            let signalBonus = 0;
            if (isGod) signalBonus += 15;
            else if (isStrong) signalBonus += 8;
            else if (isNormalSignal) signalBonus += 2;
            if (isDanger) signalBonus -= 12;
            const bankerScore = total + signalBonus + (p1 * 1.2) + (experts * 0.5) + Math.max(-3, Math.min(3, bias));
            rows.push({
                race: parseInt(raceNum), horse: parseInt(h.horse_num), idx, total, original, bias,
                p1, experts, hit, signalText, signalRate, isDanger, isGod, isStrong, isNormalSignal,
                bankerScore, formula: h.bias_formula || '', style: h.bias_running_style || h.pace_type || h.predicted_running_style || h.running_style || '',
                draw: parseInt(h.draw || 0), drawCat: h.draw_category || ''
            });
        });
    });
    return rows;
}
function wrV61MiniRows(rows, type) {
    if (!rows || !rows.length) return '<div class="v42-muted">暫無足夠訊號</div>';
    return rows.slice(0, 5).map((r, idx) => {
        const medal = idx === 0 ? '🥇' : (idx === 1 ? '🥈' : (idx === 2 ? '🥉' : '•'));
        let cls = 'v42-rate';
        if (type === 'risk') cls = 'v42-risk';
        if (type === 'radar') cls = 'v42-radar';
        let mainScore = type === 'risk' ? (r.signalRate ? r.signalRate.toFixed(2) + '%' : r.bias.toFixed(2)) : r.total.toFixed(2);
        let sig = r.signalText ? `<br><span class="v42-muted">${r.signalText}</span>` : '';
        let sub = `原 ${r.original.toFixed(2)}｜Bias ${r.bias >= 0 ? '+' : ''}${r.bias.toFixed(2)}｜P1 ${r.p1}｜名家 ${r.experts}`;
        if (type === 'mode' && r.note) sub = r.note;
        return `<div class="v42-row"><span class="v42-horse-line">${medal} ${wrV61HorseLabel(r.race, r.horse)}${sig}<br><span class="v42-muted">${sub}</span></span><span class="${cls}">${mainScore}</span></div>`;
    }).join('');
}
function wrV61RenderSignalDecisionPanel(normalData) {
    try {
        const rows = wrV61FlattenRows(normalData);
        if (!rows.length) return '';
        const bankers = rows.filter(r => !r.isDanger && (r.total >= 20 || r.p1 >= 3 || r.isGod || r.isStrong))
            .sort((a,b)=> b.bankerScore - a.bankerScore || b.total - a.total).slice(0,5);
        const risks = rows.filter(r => r.isDanger || (r.total >= 18 && r.bias < -2.5))
            .sort((a,b)=> (a.signalRate || 99) - (b.signalRate || 99) || b.total - a.total).slice(0,5);
        const radar = rows.filter(r => !r.isDanger && r.idx >= 2 && (r.isGod || r.isStrong || r.bias >= 5 || (r.total >= 8 && r.p1 >= 1)))
            .sort((a,b)=> (b.signalRate - a.signalRate) || b.bias - a.bias || b.total - a.total).slice(0,5);

        const raceModes = [];
        if (normalData) {
            Object.keys(normalData).forEach(raceNum => {
                const horses = (normalData[raceNum] && normalData[raceNum].horses) ? normalData[raceNum].horses : [];
                if (!horses.length) return;
                const top = rows.find(r => r.race === parseInt(raceNum) && r.horse === parseInt(horses[0].horse_num));
                const second = horses[1] ? rows.find(r => r.race === parseInt(raceNum) && r.horse === parseInt(horses[1].horse_num)) : null;
                if (!top) return;
                const gap = second ? top.total - second.total : top.total;
                let mode = '觀望';
                let note = '分差不足，降注觀察';
                if (top.isDanger) { mode = '避假膽'; note = '第一名有高危組合，不能硬做膽'; }
                else if (top.total >= 24 && gap >= 8) { mode = '單膽主攻'; note = `Top1拋離 ${gap.toFixed(1)} 分，可做連贏膽核心`; }
                else if (top.total >= 18 && gap >= 4) { mode = '穩膽拖腳'; note = `Top1較清晰，拖第2-5腳`; }
                else if (gap <= 3 && second) { mode = '雙膽互托'; note = `Top1/Top2接近，建議雙膽或互串`; }
                if (top.isGod || top.isStrong) { mode = '訊號加強'; note = top.signalText || note; }
                raceModes.push({race: parseInt(raceNum), horse: top.horse, total: top.total, note, mode, signalText: top.signalText, signalRate: top.signalRate, bias: top.bias, original: top.original, p1: top.p1, experts: top.experts});
            });
        }
        raceModes.sort((a,b)=> {
            const order = {'單膽主攻':5,'訊號加強':4,'穩膽拖腳':3,'雙膽互托':2,'避假膽':1,'觀望':0};
            return (order[b.mode]||0)-(order[a.mode]||0) || b.total-a.total;
        });
        const modeHtml = raceModes.slice(0,4).map((m,idx)=>{
            const cls = (m.mode === '避假膽') ? 'v42-risk' : (m.mode === '雙膽互托' || m.mode === '觀望' ? 'v42-radar' : 'v42-rate');
            return `<div class="v42-row"><span class="v42-horse-line">R${m.race}｜${m.mode}<br><span class="v42-muted">膽 #${m.horse}｜${m.note}</span></span><span class="${cls}">${m.total.toFixed(1)}</span></div>`;
        }).join('') || '<div class="v42-muted">暫無模式</div>';

        const thresholdNote = bankers.length
            ? '<span class="v42-badge">v61 Signal Dashboard：總分 + 歷史Top2訊號 + Bias綜合判斷</span>'
            : '<span class="v42-badge">今日暫無鐵膽級訊號，投注降級</span>';

        return `
        <div class="v42-decision-shell">
            <div class="v42-decision-title">🏆 今日決策總覽 <span class="v42-badge">v61 Signal版</span><span class="v42-badge">只顯示不改分</span><span class="v42-badge">${currentSelectedDate || '-'}</span>${thresholdNote}</div>
            <div class="v42-grid">
                <div class="v42-card gold"><div class="v42-card-title">🏆 今日鐵膽排行榜</div>${wrV61MiniRows(bankers, 'banker')}</div>
                <div class="v42-card green"><div class="v42-card-title">🔥 今日投注模式</div>${modeHtml}</div>
                <div class="v42-card red"><div class="v42-card-title">⚠️ 今日最高風險假膽</div>${wrV61MiniRows(risks, 'risk')}</div>
                <div class="v42-card blue"><div class="v42-card-title">💣 冷門雷達 Top3</div>${wrV61MiniRows(radar, 'radar')}</div>
            </div>
        </div>`;
    } catch(e) {
        console.error('wrV61RenderSignalDecisionPanel error', e);
        try { return wrV42RenderDecisionPanel(normalData); } catch(e2) { return ''; }
    }
}
// =========================================================================

// =========================================================================
// 🏆 [WinRacing v38 God Banker Engine｜只加不減]
// 由 P1 Power History Analyzer 得出的核心公式直接落地：
// S級神膽：GodPower>=90 OR P1>=4+P2>=2+Expert>=5 OR Matrix>=35
// A級Q膽：GodPower>=75 + Matrix>=20 + Expert>=5
// B級拖腳：GodPower>=70 + Matrix>=20
// =========================================================================
function wrGodSafeNum(v, def = 0) {
    const n = parseFloat(v);
    return Number.isFinite(n) ? n : def;
}
function wrGodP1Count(horse) {
    if (horse && Array.isArray(horse.p1_from)) return horse.p1_from.length;
    return parseInt((horse && (horse.v49_p1_count || horse.p1_count)) || 0);
}
function wrGodP2Count(horse) {
    if (horse && Array.isArray(horse.p2_from)) return horse.p2_from.length;
    return parseInt((horse && (horse.v49_p2_count || horse.p2_count)) || 0);
}
function wrGodExpertCount(horse) {
    return parseInt((horse && (horse.expert_count || horse.expertCount)) || 0);
}
function wrGodBiasScore(horse) {
    return wrGodSafeNum(
        horse?.bias_score ??
        horse?.wr_v35_bias_score ??
        horse?.bias ??
        0
    );
}
function wrGodPowerScore(horse) {
    if (!horse) return 0;
    if (horse.p1_power !== undefined) return wrGodSafeNum(horse.p1_power, 0);
    if (horse.p1_power_score !== undefined) return wrGodSafeNum(horse.p1_power_score, 0);

    const matrix = wrGodSafeNum(horse.total_score || horse.matrix_score || 0);
    const p1 = wrGodP1Count(horse);
    const p2 = wrGodP2Count(horse);
    const expert = wrGodExpertCount(horse);
    const bias = wrGodBiasScore(horse);

    // 兼容 analysis.php 現有 Matrix 資料，做前端 GodPower 估算，不改原分。
    return Math.round((matrix * 2.5 + p1 * 10 + p2 * 5 + expert * 2 + bias) * 10) / 10;
}
function wrGodClassifyHorse(horse) {
    const matrix = wrGodSafeNum(horse?.total_score || horse?.matrix_score || 0);
    const power = wrGodPowerScore(horse);
    const p1 = wrGodP1Count(horse);
    const p2 = wrGodP2Count(horse);
    const expert = wrGodExpertCount(horse);

    let level = '';
    let cls = '';
    let reason = '';

    if (power >= 90 || (p1 >= 4 && p2 >= 2 && expert >= 5) || matrix >= 35) {
        level = '🏆 S級神膽';
        cls = 'wr-god-s';
        if (power >= 90) reason = `Power ${power}`;
        else if (matrix >= 35) reason = `Matrix ${matrix}`;
        else reason = `P1${p1}+P2${p2}+E${expert}`;
    } else if (power >= 75 && matrix >= 20 && expert >= 5) {
        level = '🟢 A級Q膽';
        cls = 'wr-god-a';
        reason = `P${power} / M${matrix} / E${expert}`;
    } else if (power >= 70 && matrix >= 20) {
        level = '🔵 B級拖腳';
        cls = 'wr-god-b';
        reason = `P${power} / M${matrix}`;
    }

    return { level, cls, reason, power, matrix, p1, p2, expert };
}
function wrGodHorseBadgeHtml(horse) {
    const g = wrGodClassifyHorse(horse);
    if (!g.level) return '';
    return `<div class="wr-god-badge ${g.cls}">${g.level}<span class="wr-god-reason">${g.reason}</span></div>`;
}
function wrGodCollectCandidates(normalData) {
    const out = [];
    if (!normalData) return out;
    Object.keys(normalData).forEach(raceNum => {
        const raceData = normalData[raceNum] && normalData[raceNum].horses ? normalData[raceNum].horses : [];
        raceData.forEach((horse, idx) => {
            const g = wrGodClassifyHorse(horse);
            if (!g.level) return;
            out.push({
                race: parseInt(raceNum),
                horse: parseInt(horse.horse_num),
                rank: idx + 1,
                level: g.level,
                cls: g.cls.includes('wr-god-s') ? 's' : (g.cls.includes('wr-god-a') ? 'a' : 'b'),
                power: g.power,
                matrix: g.matrix,
                p1: g.p1,
                p2: g.p2,
                expert: g.expert,
                reason: g.reason
            });
        });
    });
    out.sort((a,b) => {
        const order = {s:3, a:2, b:1};
        if (order[b.cls] !== order[a.cls]) return order[b.cls] - order[a.cls];
        if (b.power !== a.power) return b.power - a.power;
        if (b.matrix !== a.matrix) return b.matrix - a.matrix;
        return a.race - b.race;
    });
    return out;
}
function wrGodRenderPanel(normalData) {
    const list = wrGodCollectCandidates(normalData).slice(0, 3);
    if (!list.length) {
        return `<div class="wr-god-shell">
            <div class="wr-god-title">🏆 神膽引擎 v1.0 <span class="v42-badge">未觸發 S/A/B 條件</span></div>
            <div class="v42-muted">今日暫無符合 Power / Matrix / P1共識公式的神膽候選。</div>
        </div>`;
    }
    return `<div class="wr-god-shell">
        <div class="wr-god-title">🏆 神膽引擎 v1.0 <span class="v42-badge">P1 History 公式落地</span><span class="v42-badge">只顯示不改分</span></div>
        <div class="wr-god-grid">
            ${list.map((x,i)=>`
                <div class="wr-god-card ${x.cls}">
                    <div><span class="wr-god-rank">Top ${i+1}</span><span class="wr-god-score">${x.power}</span></div>
                    <div class="wr-god-horse">R${x.race} #${x.horse} <span class="wr-god-badge ${x.cls==='s'?'wr-god-s':(x.cls==='a'?'wr-god-a':'wr-god-b')}">${x.level}</span></div>
                    <div class="wr-god-meta">
                        Matrix ${x.matrix} ｜ P1 ${x.p1} ｜ P2 ${x.p2} ｜ E ${x.expert}<br>
                        排名 #${x.rank} ｜ 觸發：${x.reason}
                    </div>
                </div>
            `).join('')}
        </div>
    </div>`;
}
// =========================================================================

function renderMatrixDashboard(normalData, trendData, fiveTrackData) {
    const container = document.getElementById('analysisReportContainer');
    let html = wrGodRenderPanel(normalData) + wrV61RenderSignalDecisionPanel(normalData);
    
    Object.keys(normalData).forEach(raceNum => {
        const raceData = normalData[raceNum].horses;
        const raceMeta = normalData[raceNum].meta || {};
        const raceTag = raceMeta.race_tag || ""; 
        
        let countOneScoreHorses = 0;
        raceData.forEach(h => {
            if (parseFloat(h.total_score) === 1.0) { countOneScoreHorses++; }
        });

        let titleInject = `<span>第 ${raceNum} 場賽事</span>`;
        if (raceTag === "ZHONGXIN") { titleInject += '<span class="tag-badge-main">[ 重心場次 ]</span>'; } 
        else if (raceTag === "SUB_ZHONGXIN") { titleInject += '<span class="tag-badge-sub">[ 次重心場次 ]</span>'; }
        if (countOneScoreHorses === 4) { titleInject += '<span class="tag-badge-sniper">[ ⚠️ 必買4個1分互串位置Q（爆） ]</span>'; }
        
        if (trendData && trendData[raceNum] && trendData[raceNum].meta && trendData[raceNum].meta.pattern_key) {
            const pKey = trendData[raceNum].meta.pattern_key;
            if (pKey !== "-") { titleInject += `<span class="tag-badge-pattern">7分組合密碼: [ ${pKey} ]</span>`; }
        }

        let topHorseNum = raceData[0] ? parseInt(raceData[0].horse_num) : 0;
        if (globalCacheMap[currentSelectedDate] && globalCacheMap[currentSelectedDate][raceNum] && globalCacheMap[currentSelectedDate][raceNum][topHorseNum]) {
            let cData = globalCacheMap[currentSelectedDate][raceNum][topHorseNum];
            titleInject += `<span class="tag-badge-pattern badge-confidence-ui">🎯 歷史校準信心度: ${cData.conf}%</span>`;
            if (cData.fake === 1) {
                titleInject += `<span class="badge-fake-warning-ui">⚠️ 高分假膽風險警報</span>`;
            }
        }

        titleInject += getRaceResultHtml(raceNum);

        html += `<div class="race-analysis-card">
                <div class="race-title">${titleInject}</div>
                <div class="table-responsive">
                    <table class="table table-dark table-hover leaderboard-table">
                        <thead>
                            <tr>
                                <th style="width:4.2%;">排名</th>
                                <th style="width:4.8%;">馬號</th>
                                <th style="width:7.8%;">大數據<br>總分</th>
                                <th style="width: 12.5%;">強力推薦<br><small style="font-size:0.75rem; color:#aaa;">(得 6 分)</small></th>
                                <th style="width: 10%;">次選支持<br><small style="font-size:0.75rem; color:#aaa;">(得 1 分)</small></th>
                                <th style="width: 9.5%;">Supporters</th>
                                <th style="width: 7.5%;">名家選擇</th>
                                <th style="width: 6.5%; color: #ff9f43; font-weight:bold;">機率</th>
                                <th style="width: 6.5%; color: #ffd900; font-weight:bold;">總勝出率</th>
                                <th style="width: 7%; color: #22c55e; font-weight:bold;">動態門檻<br>勝率</th>
                                <th style="width: 5.5%;">加權總分</th>
                                <th style="width: 11%; color: #ff7675; font-weight:bold; background: rgba(244,63,94,0.1);">四軌加權總分<br><small style="font-size:0.75rem; color:#ffa8a8; font-weight:normal;">(🔥含7分Q率)</small></th>
                                <th style="width: 15%; color: #c084fc; font-weight:bold; background: rgba(139,92,246,0.1);">五軌加權總分<br><small style="font-size:0.75rem; color:#d8b4fe; font-weight:normal;">(🔥含7分Q率)</small></th>
                            </tr>
                        </thead>
                        <tbody>`;
        
        let rank = 1;
        raceData.forEach(horse => {
            const nameMap = { 'expert_ai': 'AI', 'expert_ball': '波仔', 'expert_racing_post': '賽報', 'expert_siu_siu': '蕭蕭', 'expert_horse_king': '馬至尊', 'expert_happy_bird': 'Happy Bird' };
            let p1_experts = horse.p1_from.map(e => nameMap[e] || e).join(', ') || '-';
            let p2_experts = horse.p2_from.map(e => nameMap[e] || e).join(', ') || '-';
            let supporter_badges = horse.supporters.map(e => `<span class="badge badge-supporter">${nameMap[e] || e}</span>`).join(' ') || '-';
            
            let rankStyle = rank === 1 ? 'color: #ffd900; font-weight: bold; font-size:1.2rem;' : (rank === 2 ? 'color: #f1c40f;' : (rank === 3 ? 'color: #f39c12;' : ''));
            
            let probText = horse.probability_output ?? "-";
            let ratioText = horse.ratio_output ?? "-";
            let over22Text = horse.over22_output ?? "-"; 
            let originalWeight = horse.weighted_score_output ?? "-"; 

            let trendFourTrackWeightText = "-";
            if (trendData && trendData[raceNum] && trendData[raceNum].horses) {
                const trendHorse = trendData[raceNum].horses.find(h => parseInt(h.horse_num) === parseInt(horse.horse_num));
                if (trendHorse) trendFourTrackWeightText = trendHorse.weighted_score_output ?? "-";
            }

            let trendFiveTrackWeightText = "-";
            if (fiveTrackData && fiveTrackData[raceNum] && fiveTrackData[raceNum].horses) {
                const fiveTrackHorse = fiveTrackData[raceNum].horses.find(h => parseInt(h.horse_num) === parseInt(horse.horse_num));
                if (fiveTrackHorse) trendFiveTrackWeightText = fiveTrackHorse.weighted_score_output ?? "-";
            }

            let badgeColorClass = (probText !== "-") ? "count-color-has-data" : "count-color-normal";

            let radarBadgeHtml = "";
            let hNumInt = parseInt(horse.horse_num);
            if (globalCacheMap[currentSelectedDate] && globalCacheMap[currentSelectedDate][raceNum] && globalCacheMap[currentSelectedDate][raceNum][hNumInt]) {
                if (globalCacheMap[currentSelectedDate][raceNum][hNumInt].radar === 1) {
                    radarBadgeHtml = `<br><span class="badge-radar-ui">📡 冷門雷達有支持</span>`;
                }
            }

            html += `<tr>
                    <td style="${rankStyle}"># ${rank}${wrGetFinishBadgeHtml(raceNum, horse.horse_num)}</td>
                    <td><strong style="font-size: 1.3rem; color: #fff;">${horse.horse_num}</strong> 號馬${radarBadgeHtml}</td>
                    <td><span class="badge-score">${parseFloat(horse.total_score)}</span>${wrV35BiasMiniHtml(horse)}${wrGodHorseBadgeHtml(horse)}${wrV65BetServiceTagHtml(horse, trendFourTrackWeightText, trendFiveTrackWeightText, raceNum, rank, (globalCacheMap[currentSelectedDate] && globalCacheMap[currentSelectedDate][raceNum] && raceData[0] && globalCacheMap[currentSelectedDate][raceNum][parseInt(raceData[0].horse_num)] && parseInt(globalCacheMap[currentSelectedDate][raceNum][parseInt(raceData[0].horse_num)].fake || 0) === 1))}</td>
                    <td class="text-warning small">${p1_experts}</td>
                    <td class="text-info small">${p2_experts}</td>
                    <td>${supporter_badges}</td>
                    <td><span class="badge-expert-count ${badgeColorClass}">${horse.expert_count ?? 0} 位</span></td>
                    <td class="${(probText !== "-") ? "probability-core-td" : ""}">${probText}</td>
                    <td class="${(ratioText !== "-") ? "individual-core-td" : ""}">${ratioText}</td>
                    <td class="over22-core-td">${over22Text}</td>
                    <td class="weighted-core-td">${originalWeight}</td>
                    <td class="four-track-weighted-td-add">${trendFourTrackWeightText}</td>
                    <td class="five-track-weighted-td-add">${trendFiveTrackWeightText}</td>
                </tr>`;
            rank++;
        });
        html += `</tbody></table></div></div>`;
    });
    container.innerHTML = html;
}

function renderStrategyDashboard(normalData, trendData, fiveTrackData, mode = 'strategy') {
    const container = document.getElementById('analysisReportContainer');
    let html = '<div class="row">';
    
    let bucketSuperTiedan = []; 
    let bucketCixuan = [];      
    let bucketLengbao = [];     
    let bucketSubTiedan = [];   
    let bucketPlaceQ = [];      
    let bucketRegularBlank = [];

    Object.keys(normalData).forEach(raceNum => {
        const raceInfo = normalData[raceNum];
        const raceData = raceInfo.horses || [];
        
        let showBadge = false;
        let badgeClass = '';
        let typeText = '';
        let borderStyle = 'border-top: 4px solid #2d3748;';
        let glowEffect = '';
        let danText = raceInfo.meta.target.dan;
        let jiaoText = raceInfo.meta.target.jiao;

        let hasLawTwoMatch = false; 
        let lawTwoHorseNum = "";
        if (trendData && trendData[raceNum] && trendData[raceNum].horses && fiveTrackData && fiveTrackData[raceNum] && fiveTrackData[raceNum].horses) {
            for (let h of trendData[raceNum].horses) {
                if (h.weighted_score_output && h.weighted_score_output.includes("最強三核")) {
                    let fH = fiveTrackData[raceNum].horses.find(fh => parseInt(fh.horse_num) === parseInt(h.horse_num));
                    if (fH && fH.weighted_score_output && fH.weighted_score_output.includes("最強四核")) {
                        hasLawTwoMatch = true;
                        lawTwoHorseNum = h.horse_num;
                        break;
                    }
                }
            }
        }

        let h1_exp = (raceInfo.horses[0] && raceInfo.horses[0].expert_count) ? parseInt(raceInfo.horses[0].expert_count) : 0;
        let h2_exp = (raceInfo.horses[1] && raceInfo.horses[1].expert_count) ? parseInt(raceInfo.horses[1].expert_count) : 0;

        let isLawFourMatch = false;
        let isLawThreeMatch = false;
        let isLawFiveMatch = false;
        let lawFiveHorseNum = "";

        if (raceData.length >= 2) {
            let h1_score = parseFloat(raceData[0].total_score);
            let h2_score = parseFloat(raceData[1].total_score);

            if ((h1_exp === 6 && h2_exp === 6) || (h1_exp === 5 && h2_exp === 5)) {
                isLawFourMatch = true;
            }
            else if (h1_score === h2_score && h1_score >= 12.0) {
                isLawThreeMatch = true;
            }
            else if (h1_score >= 25.0 && h2_score < 25.0 && h1_exp >= 5) {
                isLawFiveMatch = true;
                lawFiveHorseNum = raceData[0].horse_num;
            }
        }

        let isLawSixMatch = false;
        let lawSixHorseNum = "";
        if (trendData && trendData[raceNum] && trendData[raceNum].horses && fiveTrackData && fiveTrackData[raceNum] && fiveTrackData[raceNum].horses) {
            for (let h_node of raceData) {
                let uid = parseInt(h_node.horse_num);
                let exp_cnt = parseInt(h_node.expert_count);
                if (exp_cnt >= 5) {
                    let trHorse = trendData[raceNum].horses.find(x => parseInt(x.horse_num) === uid);
                    let fvHorse = fiveTrackData[raceNum].horses.find(x => parseInt(x.horse_num) === uid);
                    let has4Track3Core = trHorse && trHorse.weighted_score_output && trHorse.weighted_score_output.includes("最強三核");
                    let has5Track4Core = fvHorse && fvHorse.weighted_score_output && fvHorse.weighted_score_output.includes("最強四核");
                    if (has4Track3Core && !has5Track4Core) {
                        isLawSixMatch = true;
                        lawSixHorseNum = h_node.horse_num;
                        break; 
                    }
                }
            }
        }

        let currentRaceFinalType = 'blank'; 

        if (raceInfo.meta.type !== 'ZHONGXIN_3' && raceInfo.meta.type !== 'ZHONGXIN_1' && raceInfo.meta.type !== 'CIXUAN' && raceInfo.meta.type !== 'REGULAR') {
            showBadge = true;
            badgeClass = 'badge-lengbao';
            typeText = '💣 爆冷場合 (意見分歧暗湧)';
            borderStyle = 'border-top: 4px solid #475569;';
            danText = "【意見極度分歧】 避開熱門，採取爆冷組合對策";
            
            let j_arr = [];
            let ex_ids = [raceInfo.horses[0].horse_num, raceInfo.horses[1].horse_num];
            raceInfo.horses.forEach(h => {
                if (!ex_ids.includes(h.horse_num)) { j_arr.push(h.horse_num + " 號馬"); }
            });
            jiaoText = j_arr.slice(0, 5).join(' , ') || '-';
            currentRaceFinalType = 'lengbao';
        }
        else if (raceInfo.meta.type === 'ZHONGXIN_3') {
            showBadge = true;
            badgeClass = 'badge-zhongxin-3'; 
            typeText = '🔥🔥條 絕密重心 (超級鐵膽)'; 
            borderStyle = 'border-top: 4px solid #ff4757;'; 
            glowEffect = 'box-shadow: 0 0 20px rgba(255,71,87,0.3); border: 1px solid #ff6b81;';
            currentRaceFinalType = 'tiedan';
        }
        else if (hasLawTwoMatch) {
            showBadge = true;
            badgeClass = 'badge-zhongxin-3'; 
            typeText = '🔥🔥條 絕密重心 (超級鐵膽)'; 
            borderStyle = 'border-top: 4px solid #ff4757;'; 
            glowEffect = 'box-shadow: 0 0 20px rgba(255,71,87,0.3); border: 1px solid #ff6b81;';
            danText = `【四五軌交叉鎖定】 ${lawTwoHorseNum} 號馬`;
            currentRaceFinalType = 'tiedan';
        }
        else if (isLawFourMatch) {
            showBadge = true;
            badgeClass = 'badge-cixuan';
            typeText = '🎯 次重心場次';
            borderStyle = 'border-top: 4px solid var(--accent-blue);';
            danText = `【名家${raceData[0].expert_count}位強大共識】 雙膽互托：${raceData[0].horse_num} 號 及 ${raceData[1].horse_num} 號馬，任擇其一為膽`;
            currentRaceFinalType = 'cixuan';
        }
        else if (isLawThreeMatch) {
            showBadge = true;
            badgeClass = 'badge-cixuan';
            typeText = '🎯 次重心場次';
            borderStyle = 'border-top: 4px solid var(--accent-blue);';
            danText = `【雙膽互托】 ${raceData[0].horse_num} 號 及 ${raceData[1].horse_num} 號馬，任擇其一為膽`;
            currentRaceFinalType = 'cixuan';
        }
        else if (isLawFiveMatch) {
            showBadge = true;
            badgeClass = 'badge-sub-zhongxin-orange';
            typeText = '🔥 絕密重心 (次級鐵膽)';
            borderStyle = 'border-top: 4px solid #ea580c;';
            glowEffect = 'box-shadow: 0 0 15px rgba(234,88,12,0.4); border: 1px solid #f97316;';
            danText = `【實力絕對壓倒】 核心單膽重心：${lawFiveHorseNum} 號馬`;
            currentRaceFinalType = 'sub_tiedan';
        }
        else if (isLawSixMatch) {
            showBadge = true;
            badgeClass = 'badge-law-six-purple';
            typeText = '🔮 位置Q精選場合 (穩健位置Q膽)';
            borderStyle = 'border-top: 4px solid var(--accent-purple);';
            glowEffect = 'box-shadow: 0 0 15px rgba(139,92,246,0.4); border: 1px solid #a78bfa;';
            danText = `【四軌強勢突破・位置Q穩膽】 核心推介：${lawSixHorseNum} 號馬做位置Q膽`;
            currentRaceFinalType = 'place_q';
        }
        else {
            showBadge = false;
            borderStyle = 'border-top: 4px solid #2d3748;';
            currentRaceFinalType = 'blank';
        }

        let strategyExtBadgesHtml = "";
        let sTopHorseNum = raceData[0] ? parseInt(raceData[0].horse_num) : 0;

        // WinRacing v44.1 Strategy Page Patch
        // 1) 建議場合戰術：每一場都顯示信心 %
        // 2) 本頁取消「⚠️ 高分假膽風險」顯示，只保留信心值
        let strategyConfidence = null;
        if (globalCacheMap[currentSelectedDate] && globalCacheMap[currentSelectedDate][raceNum] && globalCacheMap[currentSelectedDate][raceNum][sTopHorseNum]) {
            let sCData = globalCacheMap[currentSelectedDate][raceNum][sTopHorseNum];
            strategyConfidence = parseFloat(sCData.conf || 0);
        }

        if (strategyConfidence === null || isNaN(strategyConfidence) || strategyConfidence <= 0) {
            const fallbackConfidenceMap = {
                tiedan: 63.0,
                sub_tiedan: 36.1,
                cixuan: 50.0,
                place_q: 50.6,
                lengbao: 33.3,
                blank: 28.6
            };
            strategyConfidence = fallbackConfidenceMap[currentRaceFinalType] || 28.6;
        }

        strategyExtBadgesHtml += `<span class="badge-confidence-ui ms-2">🎯 信心 ${parseFloat(strategyConfidence).toFixed(1)}%</span>`;
        
        const cardHtml = `
            <div class="col-md-6" data-race="${raceNum}">
                <div class="race-analysis-card" style="${borderStyle} ${glowEffect}">
                    <div class="d-flex justify-content-between align-items-center mb-3">
                        <span class="fs-4 fw-bold" style="color: #fff;">第 ${raceNum} 場 ${getRaceResultHtml(raceNum)}</span>
                        <div>
                            ${showBadge ? `<span class="strategy-badge ${badgeClass}">${typeText}</span>` : ''}
                            ${strategyExtBadgesHtml}
                        </div>
                    </div>
                    <div class="tactics-box">
                        <div class="mb-2">
                            <span class="text-warning small d-block">🎯 戰術核心推薦：</span>
                            <div class="dan-box mt-1">
                                <span class="badge bg-warning text-dark me-2">膽</span> ${danText}
                            </div>
                        </div>
                        <hr style="border-color: rgba(255,255,255,0.1); margin: 10px 0;">
                        <div>
                            <span class="text-warning small d-block">🧬 戰術覆蓋配腳 (5 隻)：</span>
                            <div class="jiao-box mt-1">
                                <span class="badge bg-secondary me-2">腳</span> <span style="font-weight:bold; color: #38bdf8;">${jiaoText}</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>`;

        if (currentRaceFinalType === 'tiedan') { bucketSuperTiedan.push(cardHtml); }
        else if (currentRaceFinalType === 'cixuan') { bucketCixuan.push(cardHtml); }
        else if (currentRaceFinalType === 'lengbao') { bucketLengbao.push(cardHtml); }
        else if (currentRaceFinalType === 'sub_tiedan') { bucketSubTiedan.push(cardHtml); }
        else if (currentRaceFinalType === 'place_q') { bucketPlaceQ.push(cardHtml); }
        else { bucketRegularBlank.push(cardHtml); }
    });
    
    let finalHtmlStream = '';
    let totalValidMatchCount = 0;

    if (mode === 'tiedan') {
        finalHtmlStream = bucketSuperTiedan.join('') + bucketCixuan.join('');
        totalValidMatchCount = bucketSuperTiedan.length + bucketCixuan.length;
    } else {
        let sortedArray = [];
        Object.keys(normalData).forEach(raceNum => {
            let findCard = [bucketSuperTiedan, bucketCixuan, bucketLengbao, bucketSubTiedan, bucketPlaceQ, bucketRegularBlank]
                           .flat().find(c => c.includes(`data-race="${raceNum}"`));
            if (findCard) sortedArray.push(findCard);
        });
        finalHtmlStream = sortedArray.join('');
        totalValidMatchCount = sortedArray.length;
    }

    if (mode === 'tiedan' && totalValidMatchCount === 0) {
        finalHtmlStream = `<div class="col-12 text-center py-5">
                    <div class="alert alert-dark d-inline-block fw-bold fs-5 text-warning" style="background:#1a202c; border:1px solid #4a5568;">
                        ☕ 今日大數據精選中，暫未偵測到符合條件的「超級鐵膽」與「次重心」場合。
                    </div>
                 </div>`;
    }

    container.innerHTML = html + finalHtmlStream + '</div>';
}

function renderAdvisorTiedanDashboard(normalData) {
    const container = document.getElementById('analysisReportContainer');
    if (!normalData) {
        container.innerHTML = `<div class="advisor-empty-box">請先選擇賽事日期。</div>`;
        return;
    }
    let cards = [];
    Object.keys(normalData).forEach(raceNum => {
        const raceInfo = normalData[raceNum];
        const raceData = (raceInfo && raceInfo.horses) ? raceInfo.horses : [];
        if (!raceData || raceData.length === 0) return;

        const topHorse = raceData[0];
        const p1Count = (topHorse.p1_from && Array.isArray(topHorse.p1_from)) ? topHorse.p1_from.length : 0;

        if (p1Count === 6) {
            const horseNum = parseInt(topHorse.horse_num);
            const score = parseFloat(topHorse.total_score);
            const nameMap = { 'expert_ai': 'AI', 'expert_ball': '波仔', 'expert_racing_post': '賽報', 'expert_siu_siu': '蕭蕭', 'expert_horse_king': '馬至尊', 'expert_happy_bird': 'Happy Bird' };
            const p1Experts = topHorse.p1_from.map(e => nameMap[e] || e).join('、');

            let resultHtml = `<span class="advisor-stat-badge">賽果：未有賽果，可作賽前監察</span>`;
            let backtestHtml = `<span class="advisor-stat-badge">回測狀態：待賽果</span>`;

            if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][raceNum]) {
                const res = resultsMap[currentSelectedDate][raceNum].map(x => parseInt(x));
                const posIndex = res.indexOf(horseNum);
                const top2Hit = posIndex === 0 || posIndex === 1;
                const winHit = posIndex === 0;
                resultHtml = `<span class="advisor-stat-badge">賽果：${res.join(' , ')}</span>`;
                if (top2Hit) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ 回測命中前二｜${winHit ? '頭馬' : '二馬'}</span>`;
                } else if (posIndex === 2) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">⚠️ 回測：第三名，未中Q膽前二</span>`;
                } else if (posIndex >= 3) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：第四名，未中Q膽前二</span>`;
                } else {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：跑出前四外，未中Q膽前二</span>`;
                }
            }

            cards.push(`
                <div class="advisor-tiedan-card">
                    <div class="advisor-tiedan-title">
                        <span>第 ${raceNum} 場</span>
                        <span class="advisor-e1-badge">🏆 E1 超級Q膽</span>
                        ${getRaceResultHtml(raceNum)}
                    </div>
                    <div class="row align-items-center">
                        <div class="col-md-3 text-center mb-3 mb-md-0">
                            <div class="advisor-horse-num">第${raceNum}場<br>#${horseNum}</div>
                            ${getAdvisorLegsHtml(raceNum, horseNum)}
                            <div class="text-warning fw-bold">😎 軍師鐵膽</div>
                        </div>
                        <div class="col-md-9">
                            <div class="mb-2">
                                <span class="advisor-stat-badge">P1數：6 / 6</span>
                                <span class="advisor-stat-badge">Matrix分：${score}</span>
                                <span class="advisor-stat-badge">歷史樣本：33</span>
                                <span class="advisor-stat-badge">前二率：72.73%</span>
                                <span class="advisor-stat-badge">Win率：57.58%</span>
                            </div>
                            <div class="mb-2">
                                <span class="advisor-stat-badge">六位第一選：${p1Experts}</span>
                            </div>
                            <div class="mb-2">${getHorseCardBadges(raceNum, horseNum)}</div>
                            <div>
                                ${resultHtml}
                                ${backtestHtml}
                            </div>
                        </div>
                    </div>
                </div>
            `);
        }
    });

    let html = `
        <div class="race-analysis-card" style="border-top:4px solid #ffd900; box-shadow:0 0 22px rgba(255,217,0,0.16);">
            <div class="race-title">
                <span>😎 軍師鐵膽 🏆</span>
                <span class="tag-badge-pattern">只顯示 E1：P1數 = 6 的超級Q膽</span>
            </div>
            <div style="color:#d1d5db; margin-bottom:15px; font-weight:700;">
                定義：六位名家全部第一選同一匹馬。歷史回測：33場｜前二率72.73%｜Win率57.58%。有賽果的賽日會自動顯示回測命中狀態。
            </div>
    `;
    if (cards.length === 0) {
        html += `<div class="advisor-empty-box">今日暫未偵測到 E1 超級Q膽（P1數 = 6）。</div>`;
    } else {
        html += cards.join('');
    }
    html += `</div>`;
    container.innerHTML = html;
}

function renderAdvisorTop2Dashboard(normalData) {
    const container = document.getElementById('analysisReportContainer');
    if (!normalData) {
        container.innerHTML = `<div class="advisor-empty-box">請先選擇賽事日期。</div>`;
        return;
    }
    let allTopHorses = [];
    const nameMap = { 'expert_ai': 'AI', 'expert_ball': '波仔', 'expert_racing_post': '賽報', 'expert_siu_siu': '蕭蕭', 'expert_horse_king': '馬至尊', 'expert_happy_bird': 'Happy Bird' };

    Object.keys(normalData).forEach(raceNum => {
        const raceInfo = normalData[raceNum];
        const raceData = (raceInfo && raceInfo.horses) ? raceInfo.horses : [];
        if (!raceData || raceData.length === 0) return;

        const topHorse = raceData[0];
        const p1Count = (topHorse.p1_from && Array.isArray(topHorse.p1_from)) ? topHorse.p1_from.length : 0;
        const p2Count = (topHorse.p2_from && Array.isArray(topHorse.p2_from)) ? topHorse.p2_from.length : 0;
        const expertCount = topHorse.expert_count ?? 0;
        const score = parseFloat(topHorse.total_score || 0);

        allTopHorses.push({
            raceNum: parseInt(raceNum),
            horseNum: parseInt(topHorse.horse_num),
            score: score,
            p1Count: p1Count,
            p2Count: p2Count,
            expertCount: parseInt(expertCount),
            p1Experts: (topHorse.p1_from || []).map(e => nameMap[e] || e).join('、') || '-',
            p2Experts: (topHorse.p2_from || []).map(e => nameMap[e] || e).join('、') || '-'
        });
    });

    allTopHorses.sort((a, b) => {
        if (b.p1Count !== a.p1Count) return b.p1Count - a.p1Count;
        if (b.score !== a.score) return b.score - a.score;
        if (b.expertCount !== a.expertCount) return b.expertCount - a.expertCount;
        if (b.p2Count !== a.p2Count) return b.p2Count - a.p2Count;
        return a.raceNum - b.raceNum;
    });

    const top2 = allTopHorses.slice(0, 2);

    let html = `
        <div class="race-analysis-card" style="border-top:4px solid #38bdf8; box-shadow:0 0 22px rgba(14,165,233,0.16);">
            <div class="race-title">
                <span>😎 軍師鐵膽 Top 2</span>
                <span class="tag-badge-pattern">每日必顯示：P1優先排序的全日最強兩匹 Q膽候選</span>
            </div>
            <div style="color:#d1d5db; margin-bottom:15px; font-weight:700;">
                定義：每場先取 Matrix 第1名，再按 P1數 → Matrix分 → 名家選擇數 → P2數 排出全日 Top 2。用途：每日實戰 Q膽候選；有賽果會自動回測是否跑第1或第2名。
            </div>
    `;

    if (top2.length === 0) {
        html += `<div class="advisor-empty-box">今日暫未有足夠 Matrix 數據產生軍師鐵膽 Top 2。</div>`;
    } else {
        top2.forEach((item, index) => {
            let resultHtml = `<span class="advisor-stat-badge">賽果：未有賽果，可作賽前監察</span>`;
            let backtestHtml = `<span class="advisor-stat-badge">回測狀態：待賽果</span>`;

            if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][item.raceNum]) {
                const res = resultsMap[currentSelectedDate][item.raceNum].map(x => parseInt(x));
                const posIndex = res.indexOf(item.horseNum);
                const top2Hit = posIndex === 0 || posIndex === 1;
                const winHit = posIndex === 0;
                resultHtml = `<span class="advisor-stat-badge">賽果：${res.join(' , ')}</span>`;
                if (top2Hit) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ 回測命中前二｜${winHit ? '頭馬' : '二馬'}</span>`;
                } else if (posIndex === 2) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">⚠️ 回測：第三名，未中Q膽前二</span>`;
                } else if (posIndex >= 3) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：第四名，未中Q膽前二</span>`;
                } else {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：跑出前四外，未中Q膽前二</span>`;
                }
            }

            const rankLabel = index === 0 ? '🥇 Top 1' : '🥈 Top 2';

            html += `
                <div class="advisor-top2-card">
                    <div class="advisor-top2-title">
                        <span>${rankLabel}</span>
                        <span>第 ${item.raceNum} 場</span>
                        <span class="advisor-top2-badge">😎 軍師鐵膽 Top ${index + 1}</span>
                        ${getRaceResultHtml(item.raceNum)}
                    </div>
                    <div class="row align-items-center">
                        <div class="col-md-3 text-center mb-3 mb-md-0">
                            <div class="advisor-top2-rank">${rankLabel}</div>
                            <div class="advisor-horse-num">第${item.raceNum}場<br>#${item.horseNum}</div>
                            ${getAdvisorLegsHtml(item.raceNum, item.horseNum)}
                            <div class="text-info fw-bold">全日 Matrix 高分膽</div>
                        </div>
                        <div class="col-md-9">
                            <div class="mb-2">
                                <span class="advisor-stat-badge">場次：R${item.raceNum}</span>
                                <span class="advisor-stat-badge">馬號：${item.horseNum}</span>
                                <span class="advisor-stat-badge">Matrix分：${item.score}</span>
                                <span class="advisor-stat-badge">P1數：${item.p1Count}</span>
                                <span class="advisor-stat-badge">P2數：${item.p2Count}</span>
                                <span class="advisor-stat-badge">名家選擇：${item.expertCount}位</span>
                            </div>
                            <div class="mb-2">${getHorseCardBadges(item.raceNum, item.horseNum)}</div>
                            <div class="mb-2">
                                <span class="advisor-stat-badge">第一選：${item.p1Experts}</span>
                                <span class="advisor-stat-badge">第二選：${item.p2Experts}</span>
                            </div>
                            <div>
                                ${resultHtml}
                                ${backtestHtml}
                            </div>
                        </div>
                    </div>
                </div>
            `;
        });
    }
    html += `</div>`;
    container.innerHTML = html;
}

function renderAdvisorTop3Dashboard(normalData) {
    const container = document.getElementById('analysisReportContainer');
    if (!normalData) {
        container.innerHTML = `<div class="advisor-empty-box">請先選擇賽事日期。</div>`;
        return;
    }
    let allTopHorses = [];
    const nameMap = { 'expert_ai': 'AI', 'expert_ball': '波仔', 'expert_racing_post': '賽報', 'expert_siu_siu': '蕭蕭', 'expert_horse_king': '馬至尊', 'expert_happy_bird': 'Happy Bird' };

    Object.keys(normalData).forEach(raceNum => {
        const raceInfo = normalData[raceNum];
        const raceData = (raceInfo && raceInfo.horses) ? raceInfo.horses : [];
        if (!raceData || raceData.length === 0) return;

        const topHorse = raceData[0];
        const p1Count = (topHorse.p1_from && Array.isArray(topHorse.p1_from)) ? topHorse.p1_from.length : 0;
        const p2Count = (topHorse.p2_from && Array.isArray(topHorse.p2_from)) ? topHorse.p2_from.length : 0;
        const expertCount = topHorse.expert_count ?? 0;
        const score = parseFloat(topHorse.total_score || 0);

        allTopHorses.push({
            raceNum: parseInt(raceNum),
            horseNum: parseInt(topHorse.horse_num),
            score: score,
            p1Count: p1Count,
            p2Count: p2Count,
            expertCount: parseInt(expertCount),
            p1Experts: (topHorse.p1_from || []).map(e => nameMap[e] || e).join('、') || '-',
            p2Experts: (topHorse.p2_from || []).map(e => nameMap[e] || e).join('、') || '-'
        });
    });

    allTopHorses.sort((a, b) => {
        if (b.p1Count !== a.p1Count) return b.p1Count - a.p1Count;
        if (b.score !== a.score) return b.score - a.score;
        if (b.expertCount !== a.expertCount) return b.expertCount - a.expertCount;
        if (b.p2Count !== a.p2Count) return b.p2Count - a.p2Count;
        return a.raceNum - b.raceNum;
    });

    const top3 = allTopHorses.slice(0, 3);

    let html = `
        <div class="race-analysis-card" style="border-top:4px solid #ffd900; box-shadow:0 0 24px rgba(255,217,0,0.18);">
            <div class="race-title">
                <span>🥇🥈🥉 軍師推薦 Top3</span>
                <span class="tag-badge-pattern">每日固定：全日第一推薦、第二推薦、第三推薦</span>
            </div>
            <div style="color:#d1d5db; margin-bottom:15px; font-weight:700;">
                定義：每場先取 Matrix 第1名，再按 P1數 → Matrix分 → 名家選擇數 → P2數 → 場次，排出全日 Top3。此卡為「全日推薦排序」，不是必中膽；最新回測：第一推薦前三率72.22%｜第二推薦前三率75.34%｜第三推薦前三率57.53%。有賽果後才追加回測。
            </div>
    `;

    if (top3.length === 0) {
        html += `<div class="advisor-empty-box">今日暫未有足夠 Matrix 數據產生軍師推薦 Top3。</div>`;
    } else {
        top3.forEach((item, index) => {
            let resultHtml = `<span class="advisor-stat-badge">賽果：未有賽果，可作賽前監察</span>`;
            let backtestHtml = `<span class="advisor-stat-badge">回測狀態：待賽果</span>`;

            if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][item.raceNum]) {
                const res = resultsMap[currentSelectedDate][item.raceNum].map(x => parseInt(x));
                const posIndex = res.indexOf(item.horseNum);
                const winHit = posIndex === 0;
                const top2Hit = posIndex === 0 || posIndex === 1;
                const top3Hit = posIndex === 0 || posIndex === 1 || posIndex === 2;
                resultHtml = `<span class="advisor-stat-badge">賽果：${res.join(' , ')}</span>`;
                if (winHit) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ 回測：頭馬｜Win命中</span>`;
                } else if (top2Hit) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ 回測：二馬｜前二命中</span>`;
                } else if (top3Hit) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">⚠️ 回測：三馬｜Top3命中</span>`;
                } else if (posIndex >= 3) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：第四名，未中前三</span>`;
                } else {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：跑出前四外</span>`;
                }
            }

            const rankLabel = index === 0 ? '🥇 今日第一推薦' : (index === 1 ? '🥈 今日第二推薦' : '🥉 今日第三推薦');
            const histText = index === 0 ? '歷史前三率：72.22%' : (index === 1 ? '歷史前三率：75.34%' : '歷史前三率：57.53%');

            html += `
                <div class="advisor-top3-card">
                    <div class="advisor-top3-title">
                        <span>${rankLabel}</span>
                        <span>第 ${item.raceNum} 場</span>
                        <span class="advisor-top3-badge">${rankLabel}</span>
                        ${getRaceResultHtml(item.raceNum)}
                    </div>
                    <div class="row align-items-center">
                        <div class="col-md-3 text-center mb-3 mb-md-0">
                            <div class="advisor-top3-rank">${rankLabel}</div>
                            <div class="advisor-horse-num">第${item.raceNum}場<br>#${item.horseNum}</div>
                            ${getAdvisorLegsHtml(item.raceNum, item.horseNum)}
                            <div class="text-warning fw-bold">全日核心推薦</div>
                        </div>
                        <div class="col-md-9">
                            <div class="mb-2">
                                <span class="advisor-stat-badge">場次：R${item.raceNum}</span>
                                <span class="advisor-stat-badge">馬號：${item.horseNum}</span>
                                <span class="advisor-stat-badge">Matrix分：${item.score}</span>
                                <span class="advisor-stat-badge">P1數：${item.p1Count}</span>
                                <span class="advisor-stat-badge">P2數：${item.p2Count}</span>
                                <span class="advisor-stat-badge">名家選擇：${item.expertCount}位</span>
                                <span class="advisor-stat-badge advisor-result-hit">${histText}</span>
                            </div>
                            <div class="mb-2">
                                <span class="advisor-stat-badge">第一選：${item.p1Experts}</span>
                                <span class="advisor-stat-badge">第二選：${item.p2Experts}</span>
                            </div>
                            <div>
                                ${resultHtml}
                                ${backtestHtml}
                            </div>
                        </div>
                    </div>
                </div>
            `;
        });
    }
    html += `</div>`;
    container.innerHTML = html;
}

function renderAdvisorHiddenQDashboard(normalData) {
    const container = document.getElementById('analysisReportContainer');
    if (!normalData) {
        container.innerHTML = `<div class="advisor-empty-box">請先選擇賽事日期。</div>`;
        return;
    }
    let allTopHorses = [];
    const nameMap = { 'expert_ai': 'AI', 'expert_ball': '波仔', 'expert_racing_post': '賽報', 'expert_siu_siu': '蕭蕭', 'expert_horse_king': '馬至尊', 'expert_happy_bird': 'Happy Bird' };

    Object.keys(normalData).forEach(raceNum => {
        const raceInfo = normalData[raceNum];
        const raceData = (raceInfo && raceInfo.horses) ? raceInfo.horses : [];
        if (!raceData || raceData.length === 0) return;

        const topHorse = raceData[0];
        const p1Count = (topHorse.p1_from && Array.isArray(topHorse.p1_from)) ? topHorse.p1_from.length : 0;
        const p2Count = (topHorse.p2_from && Array.isArray(topHorse.p2_from)) ? topHorse.p2_from.length : 0;
        const expertCount = topHorse.expert_count ?? 0;
        const score = parseFloat(topHorse.total_score || 0);

        allTopHorses.push({
            raceNum: parseInt(raceNum),
            horseNum: parseInt(topHorse.horse_num),
            score: score,
            p1Count: p1Count,
            p2Count: p2Count,
            expertCount: parseInt(expertCount),
            p1Experts: (topHorse.p1_from || []).map(e => nameMap[e] || e).join('、') || '-',
            p2Experts: (topHorse.p2_from || []).map(e => nameMap[e] || e).join('、') || '-'
        });
    });

    allTopHorses.sort((a, b) => {
        if (b.p1Count !== a.p1Count) return b.p1Count - a.p1Count;
        if (b.score !== a.score) return b.score - a.score;
        if (b.expertCount !== a.expertCount) return b.expertCount - a.expertCount;
        if (b.p2Count !== a.p2Count) return b.p2Count - a.p2Count;
        return a.raceNum - b.raceNum;
    });

    const top2 = allTopHorses.slice(0, 2);
    const hiddenQ = (top2.length >= 2 && top2[1].p1Count === 4) ? top2[1] : null;

    let html = `
        <div class="race-analysis-card" style="border-top:4px solid #22c55e; box-shadow:0 0 24px rgba(34,197,94,0.18);">
            <div class="race-title">
                <span>😎 隱藏Q膽</span>
                <span class="tag-badge-pattern">S級訊號｜軍師Top2 + P1數 = 4</span>
            </div>
            <div style="color:#d1d5db; margin-bottom:15px; font-weight:700;">
                定義：每場先取 Matrix 第1名，再按 P1數 → Matrix分 → 名家選擇數 → P2數 排出全日Top2；若 Top2 的 P1數=4，即觸發「隱藏Q膽」。最新回測：35樣本｜前二率71.43%｜前三率80.00%｜Win率37.14%。
            </div>
    `;

    if (!hiddenQ) {
        html += `<div class="advisor-empty-box">今日暫未觸發 😎 隱藏Q膽。<br>需要符合：全日軍師Top2 且 P1數 = 4。未觸發時可改看「😎 軍師鐵膽 Top 2」或「👑 六星共識」。</div>`;
        html += `</div>`;
        container.innerHTML = html;
        return;
    }

    let resultHtml = `<span class="advisor-stat-badge">賽果：未有賽果，可作賽前監察</span>`;
    let backtestHtml = `<span class="advisor-stat-badge">回測狀態：待賽果</span>`;

    if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][hiddenQ.raceNum]) {
        const res = resultsMap[currentSelectedDate][hiddenQ.raceNum].map(x => parseInt(x));
        const posIndex = res.indexOf(hiddenQ.horseNum);
        const top2Hit = posIndex === 0 || posIndex === 1;
        const top3Hit = posIndex === 0 || posIndex === 1 || posIndex === 2;
        const winHit = posIndex === 0;
        resultHtml = `<span class="advisor-stat-badge">賽果：${res.join(' , ')}</span>`;
        if (top2Hit) {
            backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ 隱藏Q膽命中前二｜${winHit ? '頭馬' : '二馬'}</span>`;
        } else if (top3Hit) {
            backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">⚠️ 回測：第三名，未中Q膽前二</span>`;
        } else if (posIndex >= 3) {
            backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：第四名，未中Q膽前二</span>`;
        } else {
            backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：跑出前四外，未中Q膽前二</span>`;
        }
    }

    html += `
        <div class="advisor-hiddenq-card">
            <div class="advisor-hiddenq-title">
                <span>😎 隱藏Q膽觸發</span>
                <span class="advisor-hiddenq-badge">前二率 71.43%</span>
                <span class="advisor-stat-badge">樣本：35</span>
                <span class="advisor-stat-badge">前三率：80.00%</span>
                ${getRaceResultHtml(hiddenQ.raceNum)}
                    </div>
            <div class="row align-items-center">
                <div class="col-md-3 text-center mb-3 mb-md-0">
                    <div class="advisor-hiddenq-horse">第${hiddenQ.raceNum}場<br>#${hiddenQ.horseNum}</div>
                    ${getAdvisorLegsHtml(hiddenQ.raceNum, hiddenQ.horseNum)}
                    ${getHorseSmallInfo(hiddenQ.raceNum, hiddenQ.horseNum)}
                    <div class="text-success fw-bold">Top2 + P1=4</div>
                </div>
                <div class="col-md-9">
                    <div class="advisor-hiddenq-panel">
                        <div class="advisor-hiddenq-subtitle">S級隱藏Q膽訊號</div>
                        <div class="mb-2">
                            <span class="advisor-stat-badge">軍師排名：Top2</span>
                            <span class="advisor-stat-badge">Matrix分：${hiddenQ.score}</span>
                            <span class="advisor-stat-badge">P1數：${hiddenQ.p1Count}</span>
                            <span class="advisor-stat-badge">P2數：${hiddenQ.p2Count}</span>
                            <span class="advisor-stat-badge">名家：${hiddenQ.expertCount}位</span>
                        </div>
                        <div class="mb-2">
                            <span class="advisor-stat-badge">第一選：${hiddenQ.p1Experts}</span>
                            <span class="advisor-stat-badge">第二選：${hiddenQ.p2Experts}</span>
                        </div>
                        <div class="mb-2">
                            <span class="advisor-stat-badge advisor-result-hit">前二命中：25 / 35</span>
                            <span class="advisor-stat-badge advisor-result-hit">前三命中：28 / 35</span>
                            <span class="advisor-stat-badge">Win率：37.14%</span>
                        </div>
                        <div>
                            ${resultHtml}
                            ${backtestHtml}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    `;
    html += `</div>`;
    container.innerHTML = html;
}

function renderAdvisorSixStarDashboard(normalData) {
    const container = document.getElementById('analysisReportContainer');
    if (!normalData) {
        container.innerHTML = `<div class="advisor-empty-box">請先選擇賽事日期。</div>`;
        return;
    }
    let cards = [];
    const nameMap = { 'expert_ai': 'AI', 'expert_ball': '波仔', 'expert_racing_post': '賽報', 'expert_siu_siu': '蕭蕭', 'expert_horse_king': '馬至尊', 'expert_happy_bird': 'Happy Bird' };

    Object.keys(normalData).forEach(raceNum => {
        const raceInfo = normalData[raceNum];
        const raceData = (raceInfo && raceInfo.horses) ? raceInfo.horses : [];
        if (!raceData || raceData.length === 0) return;

        raceData.forEach(horse => {
            const p1Count = (horse.p1_from && Array.isArray(horse.p1_from)) ? horse.p1_from.length : 0;
            if (p1Count !== 6) return;

            const horseNum = parseInt(horse.horse_num);
            const score = parseFloat(horse.total_score || 0);
            const p2Count = (horse.p2_from && Array.isArray(horse.p2_from)) ? horse.p2_from.length : 0;
            const expertCount = horse.expert_count ?? 0;
            const p1Experts = (horse.p1_from || []).map(e => nameMap[e] || e).join('、') || '-';
            const p2Experts = (horse.p2_from || []).map(e => nameMap[e] || e).join('、') || '-';

            let resultHtml = `<span class="advisor-stat-badge">賽果：未有賽果，可作賽前監察</span>`;
            let backtestHtml = `<span class="advisor-stat-badge">回測狀態：待賽果</span>`;
            let resultStatusBadge = `<span class="advisor-stat-badge">狀態：賽前訊號已觸發</span>`;

            if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][raceNum]) {
                const res = resultsMap[currentSelectedDate][raceNum].map(x => parseInt(x));
                const posIndex = res.indexOf(horseNum);
                const winHit = posIndex === 0;
                const top3Hit = posIndex === 0 || posIndex === 1 || posIndex === 2;
                resultHtml = `<span class="advisor-stat-badge">賽果：${res.join(' , ')}</span>`;
                if (winHit) {
                    resultStatusBadge = `<span class="advisor-stat-badge advisor-result-hit">👑 六星共識命中頭馬</span>`;
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ 回測：頭馬｜Win命中</span>`;
                } else if (top3Hit) {
                    resultStatusBadge = `<span class="advisor-stat-badge advisor-result-hit">👑 六星共識命中前三</span>`;
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ 回測：${posIndex === 1 ? '二馬' : '三馬'}｜Top3命中</span>`;
                } else if (posIndex >= 3) {
                    resultStatusBadge = `<span class="advisor-stat-badge advisor-result-miss">⚠️ 六星共識未入前三</span>`;
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：第四名，未中前三</span>`;
                } else {
                    resultStatusBadge = `<span class="advisor-stat-badge advisor-result-miss">⚠️ 六星共識未入前三</span>`;
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：跑出前四外</span>`;
                }
            }

            cards.push(`
                <div class="advisor-sixstar-card">
                    <div class="advisor-sixstar-title">
                        <span>第 ${raceNum} 場</span>
                        <span class="advisor-sixstar-badge">👑 六星共識｜SSS級訊號</span>
                        ${getRaceResultHtml(raceNum)}
                    </div>
                    <div class="row align-items-center">
                        <div class="col-md-3 text-center mb-3 mb-md-0">
                            <div class="advisor-sixstar-horse">第${raceNum}場<br>#${horseNum}</div>
                            ${getAdvisorLegsHtml(raceNum, horseNum)}
                            ${getHorseSmallInfo(raceNum, horseNum)}
                            <div class="text-warning fw-bold">六大名家一致第一選</div>
                        </div>
                        <div class="col-md-9">
                            <div class="advisor-sixstar-panel">
                                <div class="advisor-sixstar-subtitle">👑 六星共識觸發</div>
                                <div class="mb-2">
                                    <span class="advisor-stat-badge">場次：R${raceNum}</span>
                                    <span class="advisor-stat-badge">馬號：${horseNum}</span>
                                    <span class="advisor-stat-badge">Matrix分：${score}</span>
                                    <span class="advisor-stat-badge">P1數：6 / 6</span>
                                    <span class="advisor-stat-badge">P2數：${p2Count}</span>
                                    <span class="advisor-stat-badge">名家選擇：${expertCount}位</span>
                                </div>
                                <div class="mb-2">
                                    <span class="advisor-stat-badge">六位第一選：${p1Experts}</span>
                                    <span class="advisor-stat-badge">第二選：${p2Experts}</span>
                                </div>
                                <div class="mb-2">
                                    <span class="advisor-stat-badge">歷史樣本：33</span>
                                    <span class="advisor-stat-badge advisor-result-hit">Win命中：19</span>
                                    <span class="advisor-stat-badge advisor-result-hit">Win率：57.58%</span>
                                    <span class="advisor-stat-badge advisor-result-hit">Top3率：78.79%</span>
                                </div>
                                <div>
                                    ${resultHtml}
                                    ${resultStatusBadge}
                                    ${backtestHtml}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            `);
        });
    });

    let html = `
        <div class="race-analysis-card" style="border-top:4px solid #ffd900; box-shadow:0 0 30px rgba(255,217,0,0.22);">
            <div class="race-title">
                <span>👑 六星共識</span>
                <span class="tag-badge-pattern">WinRacing SS級訊號｜P1數 = 6</span>
            </div>
            <div style="color:#d1d5db; margin-bottom:15px; font-weight:700;">
                定義：六大名家全部第一選同一匹馬。最新完整回測：33樣本｜19次Win｜Win率57.58%｜26次入前三｜Top3率78.79%。此卡為賽前訊號，有賽果後才追加回測。
            </div>
    `;
    if (cards.length === 0) {
        html += `<div class="advisor-empty-box">今日暫未偵測到 👑 六星共識（P1數 = 6）。<br>此訊號屬 SS級強訊號；未觸發不代表無數據，可改看「😎 軍師鐵膽 Top 2」、「😎 隱藏Q膽」或「🏆 軍師Q王」。</div>`;
    } else {
        html += cards.join('');
    }
    html += `</div>`;
    container.innerHTML = html;
}

function renderAdvisorQKingDashboard(normalData) {
    const container = document.getElementById('analysisReportContainer');
    if (!normalData) {
        container.innerHTML = `<div class="advisor-empty-box">請先選擇賽事日期。</div>`;
        return;
    }
    let allTopHorses = [];
    const nameMap = { 'expert_ai': 'AI', 'expert_ball': '波仔', 'expert_racing_post': '賽報', 'expert_siu_siu': '蕭蕭', 'expert_horse_king': '馬至尊', 'expert_happy_bird': 'Happy Bird' };

    Object.keys(normalData).forEach(raceNum => {
        const raceInfo = normalData[raceNum];
        const raceData = (raceInfo && raceInfo.horses) ? raceInfo.horses : [];
        if (!raceData || raceData.length === 0) return;

        const topHorse = raceData[0];
        const p1Count = (topHorse.p1_from && Array.isArray(topHorse.p1_from)) ? topHorse.p1_from.length : 0;
        const p2Count = (topHorse.p2_from && Array.isArray(topHorse.p2_from)) ? topHorse.p2_from.length : 0;
        const expertCount = topHorse.expert_count ?? 0;
        const score = parseFloat(topHorse.total_score || 0);

        allTopHorses.push({
            raceNum: parseInt(raceNum),
            horseNum: parseInt(topHorse.horse_num),
            score: score,
            p1Count: p1Count,
            p2Count: p2Count,
            expertCount: parseInt(expertCount),
            p1Experts: (topHorse.p1_from || []).map(e => nameMap[e] || e).join('、') || '-',
            p2Experts: (topHorse.p2_from || []).map(e => nameMap[e] || e).join('、') || '-'
        });
    });

    allTopHorses.sort((a, b) => {
        if (b.p1Count !== a.p1Count) return b.p1Count - a.p1Count;
        if (b.score !== a.score) return b.score - a.score;
        if (b.expertCount !== a.expertCount) return b.expertCount - a.expertCount;
        if (b.p2Count !== a.p2Count) return b.p2Count - a.p2Count;
        return a.raceNum - b.raceNum;
    });

    const top2 = allTopHorses.slice(0, 2);
    const hasQKing = top2.length === 2 && top2[0].p1Count === 6 && top2[1].p1Count === 4;

    let html = `
        <div class="race-analysis-card" style="border-top:4px solid #ffd900; box-shadow:0 0 28px rgba(255,217,0,0.20);">
            <div class="race-title">
                <span>🏆 軍師Q王</span>
                <span class="tag-badge-pattern">至尊軍師鐵膽 + 軍師鐵膽 同日組合</span>
            </div>
            <div style="color:#d1d5db; margin-bottom:15px; font-weight:700;">
                定義：軍師Top1 必須 P1數=6，軍師Top2 必須 P1數=4。最新回測：71個賽日僅出現5次｜雙膽全中4次｜雙膽全中率 80.00%。屬極稀有 SSS+ 核心Pattern。
            </div>
    `;

    if (!hasQKing) {
        html += `<div class="advisor-empty-box">今日暫未觸發 🏆 軍師Q王。<br>需要同時符合：Top1 P1數=6 ＋ Top2 P1數=4。此訊號屬極稀有Pattern，71個賽日只出現5次。</div>`;
        html += `</div>`;
        container.innerHTML = html;
        return;
    }

    let pairHitCount = 0;
    let pairHasAllResults = true;

    function buildQKingHorseBlock(item, label, badgeText) {
        let resultHtml = `<span class="advisor-stat-badge">賽果：未有賽果，可作賽前監察</span>`;
        let backtestHtml = `<span class="advisor-stat-badge">回測狀態：待賽果</span>`;

        if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][item.raceNum]) {
            const res = resultsMap[currentSelectedDate][item.raceNum].map(x => parseInt(x));
            const posIndex = res.indexOf(item.horseNum);
            const top3Hit = posIndex === 0 || posIndex === 1 || posIndex === 2;
            const top2Hit = posIndex === 0 || posIndex === 1;
            const winHit = posIndex === 0;
            if (top3Hit) pairHitCount++;
            resultHtml = `<span class="advisor-stat-badge">賽果：${res.join(' , ')}</span>`;
            if (top3Hit) {
                backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ Q王腳命中前三｜${winHit ? '頭馬' : (top2Hit ? '二馬' : '三馬')}</span>`;
            } else if (posIndex >= 3) {
                backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：第四名，未中前三</span>`;
            } else {
                backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 回測：前四外，未中前三</span>`;
            }
        } else {
            pairHasAllResults = false;
        }

        return `
            <div class="col-md-5">
                <div class="advisor-qking-pair-box">
                    <div class="advisor-qking-subtitle">${label}</div>
                    <div class="advisor-qking-horse">第${item.raceNum}場<br>#${item.horseNum}</div>
                    ${getAdvisorLegsHtml(item.raceNum, item.horseNum)}
                    ${getHorseSmallInfo(item.raceNum, item.horseNum)}
                    <div class="mb-2"><span class="advisor-qking-badge">${badgeText}</span></div>
                    <div class="mb-2">
                        <span class="advisor-stat-badge">Matrix分：${item.score}</span>
                        <span class="advisor-stat-badge">P1數：${item.p1Count}</span>
                        <span class="advisor-stat-badge">P2數：${item.p2Count}</span>
                        <span class="advisor-stat-badge">名家：${item.expertCount}位</span>
                    </div>
                    <div class="mb-2">
                        <span class="advisor-stat-badge">第一選：${item.p1Experts}</span>
                    </div>
                    <div>
                        ${resultHtml}
                        ${backtestHtml}
                    </div>
                </div>
            </div>
        `;
    }

    const leftBlock = buildQKingHorseBlock(top2[0], '👑 至尊軍師鐵膽', 'Top1 + P1=6');
    const rightBlock = buildQKingHorseBlock(top2[1], '😎 軍師鐵膽', 'Top2 + P1=4');

    let pairResultHtml = `<span class="advisor-stat-badge">雙膽回測：待賽果</span>`;
    if (pairHasAllResults) {
        if (pairHitCount === 2) {
            pairResultHtml = `<span class="advisor-stat-badge advisor-result-hit">🏆 雙膽全中：兩隻均入前三</span>`;
        } else if (pairHitCount === 1) {
            pairResultHtml = `<span class="advisor-stat-badge advisor-result-miss">⚠️ 雙膽半中：1隻入前三</span>`;
        } else {
            pairResultHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 雙膽失敗：兩隻均未入前三</span>`;
        }
    }

    html += `
        <div class="advisor-qking-card">
            <div class="advisor-qking-title">
                <span>🏆 軍師Q王觸發</span>
                <span class="advisor-qking-badge">歷史雙膽全中率 80.00%</span>
                <span class="advisor-stat-badge">樣本：5</span>
                <span class="advisor-stat-badge">全中：4</span>
                ${pairResultHtml}
            </div>
            <div class="row align-items-stretch">
                ${leftBlock}
                <div class="col-md-2 d-flex align-items-center justify-content-center">
                    <div class="advisor-qking-vs">＋<br>Q王<br>雙膽</div>
                </div>
                ${rightBlock}
            </div>
        </div>
    `;
    html += `</div>`;
    container.innerHTML = html;
}

// =========================================================================
// 🥷 [隱形殺手 新增功能]：只加不刪改
// 定義：Matrix 綜合排名在第 3 名或以後（非大熱門），但名家選擇數 (expert_count) 極高（>= 5）。
// 功能：專抓名家不敢放第一選，但集體暗中作腳的「超級爆冷馬」，精確捕捉如 2026-05-20 第2場 10號馬 的超準規律！
// =========================================================================
function renderAdvisorAssassinDashboard(normalData) {
    const container = document.getElementById('analysisReportContainer');
    if (!normalData) {
        container.innerHTML = `<div class="advisor-empty-box">請先選擇賽事日期。</div>`;
        return;
    }

    let cards = [];
    const nameMap = { 'expert_ai': 'AI', 'expert_ball': '波仔', 'expert_racing_post': '賽報', 'expert_siu_siu': '蕭蕭', 'expert_horse_king': '馬至尊', 'expert_happy_bird': 'Happy Bird' };

    Object.keys(normalData).forEach(raceNum => {
        const raceInfo = normalData[raceNum];
        const raceData = (raceInfo && raceInfo.horses) ? raceInfo.horses : [];
        if (!raceData || raceData.length < 3) return;

        let assassins = [];

        // 💡 核心演算法：略過前 2 名大熱門，從第 3 名開始往下全量掃描
        for (let i = 2; i < raceData.length; i++) {
            const horse = raceData[i];
            const expertCount = parseInt(horse.expert_count || 0);
            
            // 💡 觸發臨界點門檻：名家選擇數(曝光率) >= 5，代表名家不約而同暗中重疊作腳
            if (expertCount >= 5) {
                assassins.push({
                    horseInfo: horse,
                    rank: i + 1, 
                    expCount: expertCount
                });
            }
        }

        if (assassins.length > 0) {
            assassins.sort((a, b) => b.expCount - a.expCount);
            const target = assassins[0];
            const horseNum = parseInt(target.horseInfo.horse_num);
            const score = parseFloat(target.horseInfo.total_score || 0);
            const supportersList = (target.horseInfo.supporters || []).map(e => nameMap[e] || e).join('、') || '-';

            let resultHtml = `<span class="advisor-stat-badge">賽果：未有賽果</span>`;
            let backtestHtml = `<span class="advisor-stat-badge">回測狀態：待賽果</span>`;

            if (resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][raceNum]) {
                const res = resultsMap[currentSelectedDate][raceNum].map(x => parseInt(x));
                const posIndex = res.indexOf(horseNum);
                resultHtml = `<span class="advisor-stat-badge">賽果：${res.join(' , ')}</span>`;
                if (posIndex === 0) {
                    backtestHtml = `<span class="advisor-stat-badge" style="color:#00e5ff; font-weight:900;">🎯 殺手爆冷中 Win (頭馬)！</span>`;
                } else if (posIndex === 1 || posIndex === 2) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-hit">✅ 殺手命中位置 (入三甲)</span>`;
                } else if (posIndex === 3) {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">⚠️ 跑獲第四名</span>`;
                } else {
                    backtestHtml = `<span class="advisor-stat-badge advisor-result-miss">❌ 殺手翻車</span>`;
                }
            }

            cards.push(`
                <div class="advisor-sixstar-card" style="background: linear-gradient(135deg, rgba(0, 229, 255, 0.15), rgba(0, 0, 0, 0.5)); border: 1px solid #00e5ff; box-shadow: 0 0 20px rgba(0, 229, 255, 0.2);">
                    <div class="advisor-sixstar-title" style="color: #00e5ff;">
                        <span>第 ${raceNum} 場</span>
                        <span class="advisor-sixstar-badge" style="background: linear-gradient(135deg, #00b8d4, #00e5ff); color:#000;">🥷 隱形殺手</span>
                        ${getRaceResultHtml(raceNum)}
                    </div>
                    <div class="row align-items-center">
                        <div class="col-md-3 text-center mb-3 mb-md-0">
                            <div class="advisor-sixstar-horse" style="color: #00e5ff; text-shadow: 0 0 15px rgba(0,229,255,0.8);">第${raceNum}場<br>#${horseNum}</div>
                            ${getAdvisorLegsHtml(raceNum, horseNum)}
                            ${getHorseSmallInfo(raceNum, horseNum)}
                            <div class="mt-2" style="color: #00e5ff; font-weight: bold;">名家集體作腳雷達</div>
                        </div>
                        <div class="col-md-9">
                            <div class="advisor-sixstar-panel" style="border-color: rgba(0,229,255,0.4);">
                                <div class="advisor-sixstar-subtitle" style="color: #84ffff;">名家暗中作腳 (非大熱)</div>
                                <div class="mb-2">
                                    <span class="advisor-stat-badge">Matrix 排名：第 ${target.rank} 名</span>
                                    <span class="advisor-stat-badge">大數據總分：${score}</span>
                                    <span class="advisor-stat-badge" style="background: rgba(0,229,255,0.2); color: #00e5ff; border-color: #00e5ff;">🔥 被點名次數：${target.expCount} 次</span>
                                </div>
                                <div class="mb-2">
                                    <span class="advisor-stat-badge">作腳名家陣列：${supportersList}</span>
                                </div>
                                <div>
                                    ${resultHtml}
                                    ${backtestHtml}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            `);
        }
    });

    let html = `
        <div class="race-analysis-card" style="border-top:4px solid #00e5ff; box-shadow:0 0 25px rgba(0,229,255,0.15);">
            <div class="race-title" style="color: #00e5ff; border-left-color: #00e5ff;">
                <span>🥷 隱形殺手 (名家暗湧雷達)</span>
                <span class="tag-badge-pattern" style="background: #00b8d4; border-color: #00e5ff;">專抓被名家集體作腳的冷門馬</span>
            </div>
            <div style="color:#d1d5db; margin-bottom:15px; font-weight:700;">
                定義：大數據 Matrix 綜合排名在第 3 名或以後（非大熱門），但「名家選擇數（曝光率）」極高（>= 5）。這代表名家不敢放第一選，但集體暗中將其視為實力配腳。極具爆冷 Win 及 位置Q 價值！
            </div>
    `;
    if (cards.length === 0) {
        html += `<div class="advisor-empty-box">今日暫未偵測到 🥷 隱形殺手。<br>需要符合：非前兩名大熱，且名家選擇數 >= 5。</div>`;
    } else {
        html += cards.join('');
    }
    html += `</div>`;
    container.innerHTML = html;
}

function triggerGodHand() {
    alert("⚡ 【田之神手】戰術核心已啟動 ⚡\n大數據模型已針對當前時空斷面完成高維度交叉比對，核心bias權重修正已套用完畢！");
}

// =========================================================================
// 🧠 [WinRacing v3 Add-On｜只加不刪]：9項功能 + 法則順序決策引擎
// =========================================================================
(function(){
    function addV3TabsOnlyAdd() {
        const tabGroup = document.getElementById('tabGroup');
        if (!tabGroup || document.getElementById('v3DashboardBtn')) return;
        const tabs = [
            ['v3_dashboard','📌 v3總覽 Dashboard','v3DashboardBtn'],
            ['v3_calibration','📈 命中率校準','v3CalibrationBtn'],
            ['v3_expert','🧠 Expert Ranking','v3ExpertBtn'],
            ['v3_review','📝 賽後檢討','v3ReviewBtn']
        ];
        tabs.forEach(t => {
            const btn = document.createElement('button');
            btn.type = 'button';
            btn.className = 'btn btn-outline-warning';
            btn.id = t[2];
            btn.setAttribute('onclick', `switchViewMode('${t[0]}')`);
            btn.innerText = t[1];
            tabGroup.appendChild(btn);
        });
    }
    document.addEventListener('DOMContentLoaded', addV3TabsOnlyAdd);

    const originalSwitchViewModeV3 = window.switchViewMode;
    window.switchViewMode = function(mode) {
        addV3TabsOnlyAdd();
        if (['v3_dashboard','v3_calibration','v3_expert','v3_review'].includes(mode)) {
            currentViewMode = mode;
            document.querySelectorAll('.view-tabs .btn').forEach(btn => btn.classList.remove('active'));
            const btn = document.querySelector(`[onclick="switchViewMode('${mode}')"]`);
            if (btn) btn.classList.add('active');
            if (mode === 'v3_dashboard') renderV3Dashboard(globalAnalysisData, globalTrendData, globalFiveTrackData);
            if (mode === 'v3_calibration') renderV3CalibrationDashboard();
            if (mode === 'v3_expert') renderV3ExpertRankingDashboard();
            if (mode === 'v3_review') renderV3ReviewDashboard(globalAnalysisData, globalTrendData, globalFiveTrackData);
            return;
        }
        return originalSwitchViewModeV3(mode);
    };

    window.v3GetExpertName = function(key) {
        const map = { expert_ai:'AI', expert_ball:'波仔', expert_racing_post:'賽報', expert_siu_siu:'蕭蕭', expert_horse_king:'馬至尊', expert_happy_bird:'Happy Bird' };
        return map[key] || key;
    };

    window.v3GetRaceCore = function(raceNum, normalData, trendData, fiveTrackData) {
        const raceInfo = normalData && normalData[raceNum] ? normalData[raceNum] : null;
        const raceData = raceInfo && raceInfo.horses ? raceInfo.horses : [];
        if (!raceData.length) return null;

        const h1 = raceData[0];
        const h2 = raceData[1] || null;
        const h1Num = parseInt(h1.horse_num);
        const h2Num = h2 ? parseInt(h2.horse_num) : 0;
        const h1Score = parseFloat(h1.total_score || 0);
        const h2Score = h2 ? parseFloat(h2.total_score || 0) : 0;
        const h1Expert = parseInt(h1.expert_count || 0);
        const h2Expert = h2 ? parseInt(h2.expert_count || 0) : 0;
        const gap = Math.round((h1Score - h2Score) * 100) / 100;

        const isColdRace = !(raceInfo.meta && ['ZHONGXIN_3','ZHONGXIN_1','CIXUAN','REGULAR'].includes(raceInfo.meta.type || ''));
        const isLaw7 = raceInfo.meta && raceInfo.meta.type === 'ZHONGXIN_3';

        let law2Horse = 0;
        if (trendData && trendData[raceNum] && trendData[raceNum].horses && fiveTrackData && fiveTrackData[raceNum] && fiveTrackData[raceNum].horses) {
            for (let h of trendData[raceNum].horses) {
                if (h.weighted_score_output && h.weighted_score_output.includes('最強三核')) {
                    const fH = fiveTrackData[raceNum].horses.find(fh => parseInt(fh.horse_num) === parseInt(h.horse_num));
                    if (fH && fH.weighted_score_output && fH.weighted_score_output.includes('最強四核')) {
                        law2Horse = parseInt(h.horse_num); break;
                    }
                }
            }
        }

        const isLaw3 = h2 && (h1Score === h2Score && h1Score >= 12);
        const isLaw4 = h2 && ((h1Expert === 6 && h2Expert === 6) || (h1Expert === 5 && h2Expert === 5));
        const isLaw5 = h2 && (h1Score >= 25 && h2Score < 25 && h1Expert >= 5);

        let law6Horse = 0;
        if (trendData && trendData[raceNum] && trendData[raceNum].horses && fiveTrackData && fiveTrackData[raceNum] && fiveTrackData[raceNum].horses) {
            for (let hn of raceData) {
                const uid = parseInt(hn.horse_num);
                const expCnt = parseInt(hn.expert_count || 0);
                const trHorse = trendData[raceNum].horses.find(x => parseInt(x.horse_num) === uid);
                const fvHorse = fiveTrackData[raceNum].horses.find(x => parseInt(x.horse_num) === uid);
                const has4Track3Core = trHorse && trHorse.weighted_score_output && trHorse.weighted_score_output.includes('最強三核');
                const has5Track4Core = fvHorse && fvHorse.weighted_score_output && fvHorse.weighted_score_output.includes('最強四核');
                if (expCnt >= 5 && has4Track3Core && !has5Track4Core) { law6Horse = uid; break; }
            }
        }

        let ruleKey = 'NONE', ruleName = '無認證', banker = h1Num, grade = 'C', mode = '觀察';
        if (isColdRace) {
            ruleKey = 'RULE_1_COLD'; ruleName = '💣 爆冷場合 (意見分歧暗湧)'; banker = 0; grade = 'X'; mode = '停止運算';
        } else if (isLaw7) {
            ruleKey = 'RULE_7_SECRET'; ruleName = '🔥🔥條 絕密重心 (超級鐵膽)'; banker = h1Num; grade = 'A'; mode = '超級鐵膽';
        } else if (law2Horse) {
            ruleKey = 'RULE_2_FOUR_FIVE_CORE'; ruleName = '四軌+五軌同核 (超級鐵膽)'; banker = law2Horse; grade = 'A'; mode = '超級鐵膽';
        } else if (isLaw4) {
            ruleKey = 'RULE_4_EXPERT_DOUBLE'; ruleName = '名家雙膽互托'; banker = h1Num; grade = 'B'; mode = '雙膽互托';
        } else if (isLaw3) {
            ruleKey = 'RULE_3_SCORE_TIE'; ruleName = '大數據同分雙膽'; banker = h1Num; grade = 'B'; mode = '雙膽互托';
        } else if (isLaw5) {
            ruleKey = 'RULE_5_SUB_CORE'; ruleName = '次級鐵膽'; banker = h1Num; grade = 'B'; mode = '次級鐵膽';
        } else if (law6Horse) {
            ruleKey = 'RULE_6_PLACE_Q'; ruleName = '位置Q膽'; banker = law6Horse; grade = 'Q'; mode = '位置Q';
        }

        const cache = (globalCacheMap[currentSelectedDate] && globalCacheMap[currentSelectedDate][raceNum] && globalCacheMap[currentSelectedDate][raceNum][banker]) ? globalCacheMap[currentSelectedDate][raceNum][banker] : null;
        const conf = cache ? parseFloat(cache.conf || 0) : (grade === 'A' ? 78 : (grade === 'B' ? 62 : (grade === 'Q' ? 58 : 45)));
        const fakeRisk = cache ? parseInt(cache.fake || 0) : 0;
        const radar = cache ? parseInt(cache.radar || 0) : 0;

        return {
            raceNum: parseInt(raceNum), ruleKey, ruleName, banker, grade, mode, gap,
            score: h1Score, secondScore: h2Score, expert: h1Expert, conf, fakeRisk, radar,
            topHorse: h1, secondHorse: h2
        };
    };

    window.v3GetAllCores = function(normalData, trendData, fiveTrackData) {
        const out = [];
        if (!normalData) return out;
        Object.keys(normalData).forEach(rn => {
            const core = v3GetRaceCore(rn, normalData, trendData, fiveTrackData);
            if (core) out.push(core);
        });
        return out.sort((a,b) => a.raceNum - b.raceNum);
    };

    window.v3GradePill = function(grade) {
        if (grade === 'A') return '<span class="v3-pill v3-pill-a">A級鐵膽</span>';
        if (grade === 'B') return '<span class="v3-pill v3-pill-b">B級可出膽</span>';
        if (grade === 'Q') return '<span class="v3-pill v3-pill-c">位置Q膽</span>';
        if (grade === 'X') return '<span class="v3-pill v3-pill-d">爆冷停止</span>';
        return '<span class="v3-pill v3-pill-muted">無認證</span>';
    };

    window.renderV3Dashboard = function(normalData, trendData, fiveTrackData) {
        const container = document.getElementById('analysisReportContainer');
        if (!normalData) { container.innerHTML = '<div class="advisor-empty-box">請先選擇賽事日期。</div>'; return; }
        const cores = v3GetAllCores(normalData, trendData, fiveTrackData);
        const valid = cores.filter(c => c.grade !== 'X');
        const stable3 = valid.slice().sort((a,b) => (b.conf - a.conf) || (b.score - a.score)).slice(0,3);
        const fakeRisks = cores.filter(c => c.fakeRisk === 1 || c.grade === 'X').slice(0,5);
        const coldRadar = [];
        cores.forEach(c => {
            const raceData = normalData[c.raceNum].horses || [];
            raceData.forEach(h => {
                const hn = parseInt(h.horse_num);
                const cache = globalCacheMap[currentSelectedDate] && globalCacheMap[currentSelectedDate][c.raceNum] && globalCacheMap[currentSelectedDate][c.raceNum][hn];
                if (cache && parseInt(cache.radar || 0) === 1) coldRadar.push({raceNum:c.raceNum, horseNum:hn, conf:cache.conf || 0, score:h.total_score || 0});
            });
        });
        coldRadar.sort((a,b) => b.conf - a.conf);

        const calRows = Object.keys(v3RuleCalibrationMap || {});
        const superRate = v3RuleCalibrationMap['RULE_2_FOUR_FIVE_CORE'] || v3RuleCalibrationMap['RULE_7_SECRET'] || null;
        const subRate = v3RuleCalibrationMap['RULE_5_SUB_CORE'] || null;

        let html = `
        <div class="race-analysis-card" style="border-top:4px solid #ffd900;">
            <div class="race-title">📌 WinRacing v3 總覽 Dashboard ${getRaceResultHtml(1) ? '<span class="tag-badge-pattern">賽後可檢討</span>' : '<span class="tag-badge-pattern">賽前 Snapshot</span>'}</div>
            <div class="v3-rule-order-box">
                法則順序已鎖定：法則1爆冷停止 → 法則7絕密重心 → 法則2四五軌同核 → 法則3/4雙膽 → 法則5次級鐵膽 → 法則6位置Q。
            </div>
            <div class="v3-dashboard-grid">
                <div class="v3-panel">
                    <div class="v3-panel-title">今日最穩3場</div>
                    ${stable3.length ? stable3.map(c => `<div class="mb-2">${v3GradePill(c.grade)} 第${c.raceNum}場 #${c.banker}｜信心 ${c.conf}%｜${c.ruleName}${getHorseSmallInfo(c.raceNum,c.banker)}</div>`).join('') : '<div class="text-secondary">暫無</div>'}
                </div>
                <div class="v3-panel">
                    <div class="v3-panel-title">今日最高風險假膽</div>
                    ${fakeRisks.length ? fakeRisks.map(c => `<div class="v3-fake-alert">第${c.raceNum}場｜${c.ruleName}｜#${c.banker || '-'}｜${c.fakeRisk ? '⚠️ 高分假膽風險' : '💣 爆冷場合'}</div>`).join('') : '<div class="text-success fw-bold">暫無假膽警報</div>'}
                </div>
                <div class="v3-panel">
                    <div class="v3-panel-title">今日冷門雷達 Top3</div>
                    ${coldRadar.slice(0,3).length ? coldRadar.slice(0,3).map(r => `<div class="v3-cold-radar">第${r.raceNum}場 #${r.horseNum}｜冷門雷達｜信心 ${r.conf}%${getHorseSmallInfo(r.raceNum,r.horseNum)}</div>`).join('') : '<div class="text-secondary">暫無冷門雷達資料</div>'}
                </div>
                <div class="v3-panel">
                    <div class="v3-panel-title">Expert近期最熱/最差</div>
                    ${renderV3ExpertMini()}
                </div>
                <div class="v3-panel">
                    <div class="v3-panel-title">超級鐵膽 / 次重心命中率</div>
                    <div>${superRate ? `超級鐵膽 Top2：<span class="v3-big-number">${superRate.top2_rate}%</span>` : '超級鐵膽：未有校準資料'}</div>
                    <div>${subRate ? `次重心 Top2：<span class="v3-big-number">${subRate.top2_rate}%</span>` : '次重心：未有校準資料'}</div>
                </div>
                <div class="v3-panel">
                    <div class="v3-panel-title">今日建議投注模式</div>
                    ${renderV3BetModeAdvice(cores)}
                </div>
            </div>
            <table class="v3-table">
                <thead><tr><th>場次</th><th>法則</th><th>膽</th><th>Grade</th><th>Confidence</th><th>Gap</th><th>風險</th></tr></thead>
                <tbody>
                ${cores.map(c => `<tr>
                    <td>R${c.raceNum}</td><td>${c.ruleName}</td><td>${c.banker ? '#'+c.banker+getHorseSmallInfo(c.raceNum,c.banker) : '-'}</td><td>${v3GradePill(c.grade)}</td><td>${c.conf}%</td><td>${c.gap}</td><td>${c.fakeRisk ? '⚠️ 高分假膽' : (c.grade==='X' ? '💣 爆冷' : '正常')}</td>
                </tr>`).join('')}
                </tbody>
            </table>
        </div>`;
        container.innerHTML = html;
    };

    window.renderV3BetModeAdvice = function(cores) {
        const a = cores.filter(c => c.grade === 'A').length;
        const b = cores.filter(c => c.grade === 'B').length;
        const q = cores.filter(c => c.grade === 'Q').length;
        const cold = cores.filter(c => c.grade === 'X').length;
        if (a >= 2) return `<span class="v3-pill v3-pill-a">可考慮重心膽模式</span><div class="mt-2">A級膽 ${a} 場，避免爆冷場合 ${cold} 場。</div>`;
        if (a === 1 && b >= 1) return `<span class="v3-pill v3-pill-b">一主一副，保守串關</span><div class="mt-2">A級 1 場，B級 ${b} 場。</div>`;
        if (q >= 1) return `<span class="v3-pill v3-pill-c">位置Q模式優先</span><div class="mt-2">今日位置Q訊號 ${q} 場。</div>`;
        return `<span class="v3-pill v3-pill-muted">今日不宜硬出鐵膽</span><div class="mt-2">建議保留觀察，或轉冷門雷達 / 位置Q。</div>`;
    };

    window.renderV3ExpertMini = function() {
        const arr = Object.keys(v3ExpertPerformanceMap || {}).map(k => Object.assign({key:k}, v3ExpertPerformanceMap[k]));
        if (!arr.length) return '<div class="text-secondary">未有 Expert Ranking 資料</div>';
        arr.sort((a,b) => (b.last10_state === 'HOT' ? 1 : 0) - (a.last10_state === 'HOT' ? 1 : 0) || b.auto_weight - a.auto_weight);
        return arr.slice(0,3).map(e => `<div class="mb-1"><span class="v3-pill ${e.last10_state==='HOT'?'v3-pill-a':(e.last10_state==='COLD'?'v3-pill-d':'v3-pill-muted')}">${e.name}</span> Win ${e.win_rate}%｜Q ${e.q_rate}%｜Top3 ${e.top3_rate}%｜權重 ${e.auto_weight}</div>`).join('');
    };

    window.renderV3CalibrationDashboard = function() {
        const container = document.getElementById('analysisReportContainer');
        const keys = Object.keys(v3RuleCalibrationMap || {});
        let html = `<div class="race-analysis-card" style="border-top:4px solid #38bdf8;">
            <div class="race-title">📈 命中率校準頁</div>
            <div class="v3-rule-order-box">用途：顯示「超級鐵膽、次重心、雙膽互托、位置Q」過往 Win / Q / Top3 命中率。資料來源：v3_rule_calibration。</div>`;
        if (!keys.length) {
            html += `<div class="advisor-empty-box">v3_rule_calibration 暫無資料。系統已自動建立表，賽後檢討/批次回測後可寫入命中率。</div>`;
        } else {
            html += `<table class="v3-table"><thead><tr><th>Rule</th><th>樣本</th><th>Win%</th><th>Top2%</th><th>Top3%</th><th>近10 Top2%</th><th>更新</th></tr></thead><tbody>`;
            keys.forEach(k => {
                const r = v3RuleCalibrationMap[k];
                html += `<tr><td>${r.name}<br><small>${k}</small></td><td>${r.total}</td><td>${r.win_rate}%</td><td>${r.top2_rate}%</td><td>${r.top3_rate}%</td><td>${r.last10_top2_rate}%</td><td>${r.updated_at || '-'}</td></tr>`;
            });
            html += `</tbody></table>`;
        }
        html += `</div>`;
        container.innerHTML = html;
    };

    window.renderV3ExpertRankingDashboard = function() {
        const container = document.getElementById('analysisReportContainer');
        const arr = Object.keys(v3ExpertPerformanceMap || {}).map(k => Object.assign({key:k}, v3ExpertPerformanceMap[k]));
        let html = `<div class="race-analysis-card" style="border-top:4px solid #8b5cf6;">
            <div class="race-title">🧠 Expert Ranking / 自動權重</div>
            <div class="v3-rule-order-box">用途：每位名家過往 Win%、Q%、Top3%、最近10日狀態與自動權重。權重高者可在日後加權，近期差者自動降權。</div>`;
        if (!arr.length) {
            html += `<div class="advisor-empty-box">v3_expert_performance 暫無資料。系統已自動建立表，可由賽後檢討批次更新。</div>`;
        } else {
            arr.sort((a,b)=> b.auto_weight - a.auto_weight);
            html += `<table class="v3-table"><thead><tr><th>Expert</th><th>樣本</th><th>Win%</th><th>Q%</th><th>Top3%</th><th>近況</th><th>自動權重</th><th>更新</th></tr></thead><tbody>`;
            arr.forEach(e => {
                html += `<tr><td>${e.name}</td><td>${e.total}</td><td>${e.win_rate}%</td><td>${e.q_rate}%</td><td>${e.top3_rate}%</td><td>${e.last10_state}</td><td>${e.auto_weight}</td><td>${e.updated_at || '-'}</td></tr>`;
            });
            html += `</tbody></table>`;
        }
        html += `</div>`;
        container.innerHTML = html;
    };

    window.renderV3ReviewDashboard = function(normalData, trendData, fiveTrackData) {
        const container = document.getElementById('analysisReportContainer');
        if (!normalData) { container.innerHTML = '<div class="advisor-empty-box">請先選擇賽事日期。</div>'; return; }
        const cores = v3GetAllCores(normalData, trendData, fiveTrackData);
        let html = `<div class="race-analysis-card" style="border-top:4px solid #22c55e;">
            <div class="race-title">📝 賽後檢討頁</div>
            <div class="v3-rule-order-box">自動顯示：中咗咩 / 錯喺邊 / 下次修正邊條 rule。若 racing_results 有賽果，會即時判斷。</div>
            <table class="v3-table"><thead><tr><th>場次</th><th>法則</th><th>膽</th><th>賽果</th><th>檢討</th><th>下次修正</th></tr></thead><tbody>`;
        cores.forEach(c => {
            const res = resultsMap[currentSelectedDate] && resultsMap[currentSelectedDate][c.raceNum] ? resultsMap[currentSelectedDate][c.raceNum].map(x=>parseInt(x)) : null;
            let status = '未有賽果', fix = '賽前 Snapshot';
            if (res && c.banker) {
                const pos = res.indexOf(parseInt(c.banker));
                if (pos === 0) { status = '✅ Win'; fix = '保留此法則'; }
                else if (pos === 1) { status = '✅ Top2 / Q'; fix = '保留做膽'; }
                else if (pos === 2) { status = '⚠️ Top3 only'; fix = '降為位置Q或拖腳'; }
                else if (pos >= 3) { status = '❌ 第四'; fix = '檢查假膽條件'; }
                else { status = '❌ 前四外'; fix = '加入假膽黑名單檢查'; }
            } else if (res && !c.banker) {
                status = '💣 爆冷停止'; fix = '保留停止運算';
            }
            html += `<tr><td>R${c.raceNum}</td><td>${c.ruleName}</td><td>${c.banker ? '#'+c.banker : '-'}</td><td>${res ? res.join(' , ') : '-'}</td><td>${status}</td><td>${fix}</td></tr>`;
        });
        html += `</tbody></table></div>`;
        container.innerHTML = html;
    };
})();


// =========================================================================
// 🧪 [WinRacing v3.1 化學反應層｜只加不刪]
// 原本法規負責「選出候選膽」，本層負責「用回測百分比校準信心、降級假膽」。
// =========================================================================
(function(){
    const WR_V31_RULE_BASE = {
        'RULE_7_SECRET': 72.7,
        'RULE_2_FOUR_FIVE_CORE': 66.7,
        'RULE_4_EXPERT_DOUBLE': 55.0,
        'RULE_3_SCORE_TIE': 50.0,
        'RULE_5_SUB_CORE': 43.8,
        'RULE_6_PLACE_Q': 58.0,
        'NONE': 33.0
    };

    function v31Clamp(n, min, max) { return Math.max(min, Math.min(max, n)); }

    window.v31GetExtraInfo = function(raceNum, horseNum) {
        const r = parseInt(raceNum), h = parseInt(horseNum);
        if (globalRaceCardExtraMap && globalRaceCardExtraMap[currentSelectedDate] && globalRaceCardExtraMap[currentSelectedDate][r] && globalRaceCardExtraMap[currentSelectedDate][r][h]) {
            return globalRaceCardExtraMap[currentSelectedDate][r][h];
        }
        return {};
    };

    window.v31FormScoreFromLast6 = function(last6) {
        if (!last6) return null;
        const raw = String(last6).replace(/[^0-9\/]/g,'').split('/').filter(x => x !== '').slice(0,6).map(x => parseInt(x));
        if (!raw.length) return null;
        const weights = [0.40,0.25,0.15,0.10,0.06,0.04];
        const mapPos = p => {
            if (p === 1) return 10; if (p === 2) return 8; if (p === 3) return 6; if (p === 4) return 5;
            if (p === 5) return 4; if (p === 6) return 3; if (p === 7) return 2; return 1;
        };
        let total = 0, wsum = 0;
        raw.forEach((p,i) => { total += mapPos(p) * weights[i]; wsum += weights[i]; });
        return Math.round((total / Math.max(0.01, wsum)) * 10) / 10;
    };

    window.v31GradeFromConfidence = function(conf, fakeRisk, originalGrade) {
        if (originalGrade === 'X') return 'X';
        if (fakeRisk) return 'D';
        if (conf >= 70) return 'A';
        if (conf >= 55) return 'B';
        if (conf >= 40) return 'C';
        return 'D';
    };

    window.v31GradePill = function(grade) {
        if (grade === 'A') return '<span class="v31-mini v31-a">A級鐵膽</span>';
        if (grade === 'B') return '<span class="v31-mini v31-b">B級可出膽</span>';
        if (grade === 'C') return '<span class="v31-mini v31-c">C級觀察</span>';
        if (grade === 'D') return '<span class="v31-mini v31-d">D級假膽風險</span>';
        if (grade === 'X') return '<span class="v31-mini v31-d">爆冷停止</span>';
        return '<span class="v31-mini">無認證</span>';
    };

    window.v31CalibrateCore = function(core) {
        if (!core) return core;
        if (core.grade === 'X' || !core.banker) {
            core.chem_conf = 0; core.chem_grade = 'X'; core.chem_reasons = ['法則1爆冷場合：停止其他法則'];
            return core;
        }
        const info = v31GetExtraInfo(core.raceNum, core.banker);
        const style = String(info.running_style || info.predicted_running_style || info.style || '').trim();
        const jockey = String(info.jockey || '').trim();
        const form = v31FormScoreFromLast6(info.last6_runs || info.last6 || '');
        const cal = v3RuleCalibrationMap && v3RuleCalibrationMap[core.ruleKey] ? parseFloat(v3RuleCalibrationMap[core.ruleKey].top2_rate || 0) : 0;
        let conf = cal > 0 ? cal : (WR_V31_RULE_BASE[core.ruleKey] || WR_V31_RULE_BASE['NONE']);
        let fake = parseInt(core.fakeRisk || 0) === 1;
        const reasons = [];

        reasons.push('法規基線 ' + Math.round(conf*10)/10 + '%');

        if (core.gap >= 4 && core.gap < 8) { conf += 8; reasons.push('Gap 4-8 最佳膽區 +8'); }
        else if (core.gap >= 8) { conf -= 12; fake = true; reasons.push('Gap 8+ 過熱假膽風險 -12'); }
        else if (core.gap > 0 && core.gap < 2) { conf -= 4; reasons.push('Gap 偏細，混戰 -4'); }

        if (style.includes('後上')) { conf -= 25; fake = true; reasons.push('後上馬做膽高危 -25'); }
        else if (style.includes('放頭')) { conf += 8; reasons.push('放頭 Top2 訊號 +8'); }
        else if (style.includes('咬前') || style.includes('跟前')) { conf += 5; reasons.push('咬前/跟前 Top2 訊號 +5'); }
        else if (style.includes('中置')) { conf -= 5; reasons.push('中置只宜觀察 -5'); }

        if (form !== null) {
            if (form >= 5 && form < 6 && core.gap >= 4 && core.gap < 8 && !style.includes('後上')) { conf += 18; reasons.push('非後上 + Form 5-5.99 + Gap 4-8 歷史最佳 +18'); }
            else if (form >= 6) { conf += 10; reasons.push('Form >=6 +10'); }
            else if (form >= 4) { conf += 6; reasons.push('Form >=4 +6'); }
            else { conf -= 12; fake = true; reasons.push('Form <4 假膽風險 -12'); }
        } else {
            reasons.push('未有 last6_runs，暫用法規/Gap 校準');
        }

        if (jockey.includes('潘頓')) { conf += 7; reasons.push('潘頓膽訊號 +7'); }
        else if (jockey.includes('布文') || jockey.includes('麥道朗')) { conf += 4; reasons.push('頂級騎師 +4'); }

        conf = v31Clamp(conf, 5, 88);
        core.original_conf = core.conf;
        core.chem_conf = Math.round(conf * 10) / 10;
        core.conf = core.chem_conf;
        core.fakeRisk = fake ? 1 : parseInt(core.fakeRisk || 0);
        core.chem_grade = v31GradeFromConfidence(core.chem_conf, fake, core.grade);
        core.grade = core.chem_grade;
        core.chem_reasons = reasons;
        core.chem_info = {style, jockey, form, last6: info.last6_runs || '', horseName: info.horse_name || ''};
        return core;
    };

    const wr_v31_originalGetRaceCore = window.v3GetRaceCore;
    if (typeof wr_v31_originalGetRaceCore === 'function') {
        window.v3GetRaceCore = function(raceNum, normalData, trendData, fiveTrackData) {
            return v31CalibrateCore(wr_v31_originalGetRaceCore(raceNum, normalData, trendData, fiveTrackData));
        };
    }

    const wr_v31_originalGradePill = window.v3GradePill;
    window.v3GradePill = function(grade) {
        if (grade === 'D') return '<span class="v3-pill v3-pill-d">D級禁膽</span>';
        return wr_v31_originalGradePill ? wr_v31_originalGradePill(grade) : v31GradePill(grade);
    };

    window.renderV31ChemistryDashboard = function(normalData, trendData, fiveTrackData) {
        const container = document.getElementById('analysisReportContainer');
        if (!normalData) { container.innerHTML = '<div class="advisor-empty-box">請先選擇賽事日期。</div>'; return; }
        const cores = v3GetAllCores(normalData, trendData, fiveTrackData);
        const ranked = cores.slice().sort((a,b) => (b.conf - a.conf) || (b.score - a.score));
        let html = `<div class="v31-chem-card"><div class="v31-title">🧪 法規 × 回測百分比 化學反應層 <span class="v31-mini v31-a">只加不改</span></div>
        <div class="v31-note">原本 1→7→2→3/4→5→6 法規照舊負責選膽；本層只做歷史百分比校準、假膽降級、Confidence %。</div>
        <table class="v31-table"><thead><tr><th>場次</th><th>膽</th><th>原法規</th><th>化學級別</th><th>Confidence</th><th>Gap</th><th>跑法/Form</th><th>校準理由</th></tr></thead><tbody>`;
        ranked.forEach(c => {
            const inf = c.chem_info || {};
            html += `<tr><td>R${c.raceNum}</td><td>${c.banker ? '#'+c.banker+getHorseSmallInfo(c.raceNum,c.banker) : '-'}</td><td>${c.ruleName}</td><td>${v31GradePill(c.grade)}</td><td><b>${c.conf}%</b></td><td>${c.gap}</td><td>${inf.style || '-'} / ${inf.form !== null && inf.form !== undefined ? inf.form : '-'}</td><td class="v31-reason">${(c.chem_reasons || []).join('<br>')}</td></tr>`;
        });
        html += `</tbody></table></div>`;
        container.innerHTML = html;
    };

    const wr_v31_originalSwitchViewMode = window.switchViewMode;
    if (typeof wr_v31_originalSwitchViewMode === 'function') {
        window.switchViewMode = function(mode) {
            if (mode === 'v31_chemistry') {
                currentViewMode = mode;
                document.querySelectorAll('.view-tabs .btn').forEach(btn => btn.classList.remove('active'));
                const btn = document.querySelector(`[onclick="switchViewMode('v31_chemistry')"]`);
                if (btn) btn.classList.add('active');
                renderV31ChemistryDashboard(globalAnalysisData, globalTrendData, globalFiveTrackData);
                return;
            }
            return wr_v31_originalSwitchViewMode(mode);
        };
    }

    document.addEventListener('DOMContentLoaded', function(){
        const tabGroup = document.getElementById('tabGroup');
        if (tabGroup && !document.getElementById('v31ChemistryBtn')) {
            const btn = document.createElement('button');
            btn.type = 'button';
            btn.id = 'v31ChemistryBtn';
            btn.className = 'btn btn-outline-warning';
            btn.setAttribute('onclick', "switchViewMode('v31_chemistry')");
            btn.innerHTML = '🧪 法規×百分比';
            tabGroup.appendChild(btn);
        }
    });
})();
// =========================================================================

// =========================================================================

</script>
</body>
</html>