<!DOCTYPE html>

<html lang="zh">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title data-lang-key="title">在线成绩分析平台</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>

    <!-- PDF.js Library -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <link rel="preconnect" href="https://fonts.googleapis.com">

    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">

    <style>

        body { font-family: 'Inter', sans-serif; }

        /* Lock scroll when not logged in */

        body.locked { overflow: hidden; height: 100vh; }

        

        .nav-link.active {

            background-color: #eff6ff;

            color: #1d4ed8;

            font-weight: 600;

        }

        ::-webkit-scrollbar { width: 8px; }

        ::-webkit-scrollbar-track { background: #f1f5f9; }

        ::-webkit-scrollbar-thumb { background: #94a3b8; border-radius: 4px; }

        ::-webkit-scrollbar-thumb:hover { background: #64748b; }

        

        .ai-loader {

            width: 24px;

            height: 24px;

            border: 3px solid #3B82F6;

            border-bottom-color: transparent;

            border-radius: 50%;

            display: inline-block;

            box-sizing: border-box;

            animation: rotation 1s linear infinite;

        }

        @keyframes rotation {

            0% { transform: rotate(0deg); }

            100% { transform: rotate(360deg); }

        }

        

        .file-drop-area {

            border: 2px dashed #d1d5db;

            border-radius: 0.5rem;

            padding: 1.5rem;

            text-align: center;

            background-color: #f9fafb;

            transition: background-color 0.2s, border-color 0.2s;

        }

        .file-drop-area.dragover {

            background-color: #eff6ff;

            border-color: #3b82f6;

        }

        

        .report-section ul {

            list-style-type: disc;

            padding-left: 1.5rem;

            margin-top: 0.5rem;

        }

        

        /* Ensure login covers everything */

        #login-page {

            position: fixed;

            top: 0;

            left: 0;

            width: 100vw;

            height: 100vh;

            z-index: 9999;

            overflow-y: auto;

            background-color: #e2e8f0; /* bg-slate-200 */

        }

    </style>

</head>

<body class="bg-slate-100 locked">


    <!-- Login Page -->

    <div id="login-page" class="flex items-center justify-center">

        <div class="w-full max-w-md p-8 space-y-6 bg-white rounded-xl shadow-lg m-4">

            <div class="text-center">

                <h1 data-lang-key="login_title" class="text-3xl font-bold text-slate-800">在线成绩分析平台</h1>

                <p data-lang-key="login_subtitle" class="mt-2 text-slate-600">请输入访问密码</p>

                <p class="text-xs text-slate-500 mt-1">验证将在开始分析时进行</p>

            </div>

            <form id="login-form" class="space-y-6">

                <div>

                    <input type="password" id="password" required class="w-full px-4 py-2 text-lg text-center bg-slate-100 border-2 border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition" placeholder="••••••••">

                </div>

                <button type="submit" data-lang-key="login_button" class="w-full px-4 py-3 font-semibold text-white bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition">进入系统</button>

            </form>

        </div>

    </div>


    <!-- Main Application -->

    <div id="app" class="hidden h-screen w-full lg:grid lg:grid-cols-[280px_1fr]">

        <!-- Sidebar Navigation -->

        <div class="hidden border-r bg-white lg:block">

            <div class="flex h-full max-h-screen flex-col gap-2">

                <div class="flex h-14 items-center border-b px-6">

                    <a href="#" class="flex items-center gap-2 font-semibold">

                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-blue-600"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>

                        <span data-lang-key="app_title" class="text-lg">成绩分析</span>

                    </a>

                </div>

                <div class="flex-1 overflow-auto py-2">

                    <nav class="grid items-start px-4 text-sm font-medium">

                        <a href="#grade-entry" class="nav-link flex items-center gap-3 rounded-lg px-3 py-3 text-slate-600 transition-all hover:bg-slate-100 active">

                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5"><path d="M12 22h6a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v5"></path><path d="M14 2v4a2 2 0 0 0 2 2h4"></path><path d="M3 15h6"></path><path d="M6 12v6"></path></svg>

                            <span data-lang-key="nav_grade_entry">成绩录入 (简易)</span>

                        </a>

                        <a href="#test-paper-analysis" class="nav-link flex items-center gap-3 rounded-lg px-3 py-3 text-slate-600 transition-all hover:bg-slate-100">

                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5"><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/></svg>

                            <span data-lang-key="nav_test_paper_analysis">试卷分析 (综合)</span>

                        </a>

                    </nav>

                </div>

            </div>

        </div>

        <!-- Main Content -->

        <div class="flex flex-col">

            <header class="flex h-14 items-center gap-4 border-b bg-white px-6">

                <h1 id="page-title" data-lang-key="page_title_grade_entry" class="text-xl font-semibold">成绩录入 (简易)</h1>

                <div class="ml-auto flex items-center gap-2">

                    <div class="flex items-center text-sm font-medium bg-slate-200 rounded-lg p-0.5">

                        <button id="lang-en" class="px-3 py-1 rounded-md text-slate-600 transition-all">EN</button>

                        <button id="lang-zh" class="px-3 py-1 rounded-md text-slate-600 transition-all bg-white text-blue-600 shadow-sm">ZH</button>

                    </div>

                </div>

            </header>

            <main class="flex-1 overflow-y-auto p-6 space-y-6">

                <!-- Dashboard Section -->

                <div id="dashboard-section" class="page-content space-y-6">

                    <div id="welcome-message" class="text-center py-16 px-6 bg-white rounded-xl shadow">

                        <h2 data-lang-key="welcome_title" class="text-2xl font-bold text-slate-800">欢迎使用成绩分析平台</h2>

                        <p data-lang-key="welcome_text" class="mt-2 text-slate-600 max-w-2xl mx-auto">请在下方“成绩录入”板块输入或上传学生分数以生成简易报告。</p>

                        <p data-lang-key="welcome_text_2" class="mt-1 text-slate-600 max-w-2xl mx-auto">如需多文件综合分析,请使用“试卷分析”功能。</p>

                    </div>

                    <div id="report-container" class="hidden space-y-6">

                        <!-- Stat Cards & Charts -->

                        <div class="grid gap-6 md:grid-cols-2 lg:grid-cols-5">

                            <div class="p-5 bg-white rounded-xl shadow"><h3 data-lang-key="stat_students" class="text-sm font-medium text-slate-500">学生人数</h3><p id="stat-students" class="text-3xl font-bold text-slate-800">0</p></div>

                            <div class="p-5 bg-white rounded-xl shadow"><h3 data-lang-key="stat_avg" class="text-sm font-medium text-slate-500">平均分</h3><p id="stat-avg" class="text-3xl font-bold text-slate-800">0.00</p></div>

                            <div class="p-5 bg-white rounded-xl shadow"><h3 data-lang-key="stat_stddev" class="text-sm font-medium text-slate-500">标准差</h3><p id="stat-stddev" class="text-3xl font-bold text-slate-800">0.00</p></div>

                            <div class="p-5 bg-white rounded-xl shadow"><h3 data-lang-key="stat_highest" class="text-sm font-medium text-slate-500">最高分</h3><p id="stat-highest" class="text-3xl font-bold text-slate-800">0</p></div>

                            <div class="p-5 bg-white rounded-xl shadow"><h3 data-lang-key="stat_lowest" class="text-sm font-medium text-slate-500">最低分</h3><p id="stat-lowest" class="text-3xl font-bold text-slate-800">0</p></div>

                        </div>

                        <div class="grid gap-6 lg:grid-cols-2">

                             <div class="p-6 bg-white rounded-xl shadow"><h3 data-lang-key="chart_distribution_title" class="text-lg font-semibold mb-4">分数段分布</h3><div class="relative h-80"><canvas id="scoreDistributionChart"></canvas></div></div>

                             <div class="p-6 bg-white rounded-xl shadow"><h3 data-lang-key="chart_performance_title" class="text-lg font-semibold mb-4">整体表现概览</h3><div class="relative h-80"><canvas id="performanceRateChart"></canvas></div></div>

                        </div>

                        <div class="grid gap-6 lg:grid-cols-3">

                            <div class="lg:col-span-2 p-6 bg-white rounded-xl shadow">

                                <div class="flex justify-between items-center mb-4"><h3 data-lang-key="table_title" class="text-lg font-semibold">详细分析报告</h3><button id="export-btn" data-lang-key="export_button" class="px-4 py-2 text-sm font-medium text-blue-700 bg-blue-100 rounded-lg hover:bg-blue-200">导出数据 (CSV)</button></div>

                                <div class="overflow-x-auto"><table class="w-full text-sm text-left text-slate-600"><thead class="text-xs text-slate-700 uppercase bg-slate-100"><tr><th data-lang-key="table_header_class" class="px-4 py-3">班级</th><th data-lang-key="table_header_students" class="px-4 py-3">人数</th><th data-lang-key="table_header_avg" class="px-4 py-3">平均分</th><th data-lang-key="table_header_stddev" class="px-4 py-3">标准差</th><th data-lang-key="table_header_exceeds" class="px-4 py-3">&gt;85% (优秀)</th><th data-lang-key="table_header_meets_plus" class="px-4 py-3">&gt;75% (良好)</th><th data-config-key="table_header_meets" class="px-4 py-3">&gt;60% (及格)</th><th data-lang-key="table_header_working" class="px-4 py-3">&lt;60% (待提高)</th><th data-lang-key="table_header_try_again" class="px-4 py-3">&lt;35% (需努力)</th></tr></thead><tbody id="report-table-body"></tbody></table></div>

                            </div>

                            <div class="p-6 bg-white rounded-xl shadow"><h3 data-lang-key="needing_attention_title" class="text-lg font-semibold mb-4">待提高学生名单 (分数低于60)</h3><ul id="needing-attention-list" class="space-y-2 max-h-60 overflow-y-auto"></ul></div>

                        </div>

                    </div>

                </div>


                <!-- Grade Entry Section -->

                <div id="grade-entry-section" class="page-content">

                    <div class="grid lg:grid-cols-2 gap-6">

                        <div class="space-y-6">

                             <div class="p-6 bg-white rounded-xl shadow">

                                <h3 data-lang-key="entry_manual_title" class="text-lg font-semibold">手动录入成绩</h3>

                                <p data-lang-key="entry_manual_subtitle" class="text-sm text-slate-500 mb-4">逐条添加学生分数。</p>

                                <form id="add-score-form" class="flex gap-4">

                                    <input type="text" id="student-name" required class="flex-grow px-3 py-2 bg-slate-50 border border-slate-300 rounded-md focus:ring-1 focus:ring-blue-500" data-lang-placeholder="entry_student_name" placeholder="学生姓名">

                                    <input type="number" id="student-score" min="0" step="0.5" required class="w-28 px-3 py-2 bg-slate-50 border border-slate-300 rounded-md focus:ring-1 focus:ring-blue-500" data-lang-placeholder="entry_student_score" placeholder="分数">

                                    <button type="submit" data-lang-key="entry_add_button" class="px-5 py-2 font-semibold text-white bg-blue-600 rounded-lg hover:bg-blue-700">添加分数</button>

                                </form>

                            </div>

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 data-lang-key="entry_import_title" class="text-lg font-semibold">从Excel批量导入</h3>

                                <p data-lang-key="entry_import_subtitle" class="text-sm text-slate-500 mb-4">上传包含 '姓名' 和 '分数' 列的 .xlsx 或 .csv 文件。</p>

                                <div id="drop-area" class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-slate-300 border-dashed rounded-md">

                                    <div class="space-y-1 text-center">

                                        <svg class="mx-auto h-12 w-12 text-slate-400" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true"><path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>

                                        <div class="flex text-sm text-slate-600">

                                            <label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500">

                                                <span data-lang-key="entry_upload_button">上传文件</span>

                                                <input id="file-upload" name="file-upload" type="file" class="sr-only" accept=".xlsx, .xls, .csv">

                                            </label>

                                            <p data-lang-key="entry_drag_drop" class="pl-1">或拖拽文件</p>

                                        </div>

                                        <p id="file-name-display" class="text-xs text-slate-500"></p>

                                        <p id="file-feedback" class="text-sm font-medium mt-2"></p>

                                    </div>

                                </div>

                            </div>

                        </div>

                        <div class="p-6 bg-white rounded-xl shadow">

                             <div class="flex justify-between items-center mb-4">

                                <h3 data-lang-key="entry_table_title" class="text-lg font-semibold">当前分数列表</h3>

                                <div class="flex gap-3">

                                    <button id="clear-btn" data-lang-key="entry_clear_button" class="px-4 py-2 text-sm font-medium text-red-700 bg-red-100 rounded-lg hover:bg-red-200 disabled:opacity-50 disabled:cursor-not-allowed">全部清除</button>

                                    <button id="analyze-btn" data-lang-key="entry_analyze_button" class="px-4 py-2 text-sm font-semibold text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed">生成简易报告</button>

                                </div>

                            </div>

                            <div class="max-h-96 overflow-y-auto border rounded-lg">

                                <table class="w-full text-sm text-left"><thead class="text-xs text-slate-700 uppercase bg-slate-100 sticky top-0"><tr><th class="p-3 w-16" data-lang-key="entry_table_header_no">序号</th><th class="p-3" data-lang-key="entry_table_header_name">学生姓名</th><th class="p-3" data-lang-key="entry_table_header_score">分数</th><th class="p-3 text-center w-24" data-lang-key="entry_table_header_action">操作</th></tr></thead><tbody id="scores-table-body"></tbody></table>

                            </div>

                        </div>

                    </div>

                </div>

                 

                <!-- Test Paper Analysis Section -->

                <div id="test-paper-analysis-section" class="page-content hidden">

                    <div class="space-y-8">

                        <div id="analysis-upload-step" class="p-6 bg-white rounded-xl shadow">

                            <h3 class="text-xl font-semibold text-slate-900" data-lang-key="ta_upload_title">上传分析所需文件</h3>

                            <p class="text-sm text-slate-500" data-lang-key="ta_upload_subtitle">请上传所需文件。AI将分析数据并生成报告。</p>

                            

                            <div id="analysis-error-message" class="hidden mt-4 p-4 bg-red-50 text-red-700 rounded-lg">

                                <h4 class="font-bold" data-lang-key="ta_error_title">分析出错</h4>

                                <p data-lang-key="ta_error_message">无法完成分析。请检查文件格式是否正确,或稍后重试。</p>

                                <pre id="analysis-error-details" class="mt-2 text-xs whitespace-pre-wrap"></pre>

                            </div>


                            <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-6">

                                <div>

                                    <h4 class="text-base font-medium text-slate-800" data-lang-key="ta_upload_file1_title">*1. 学生总体成绩 (Excel格式)</h4>

                                    <label for="overall-grades-upload" class="file-drop-area mt-2" id="overall-grades-drop-area">

                                        <svg class="mx-auto h-10 w-10 text-slate-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125V6a3.375 3.375 0 0 0-3.375-3.375H8.25m.75 12 3 3m0 0 3-3m-3 3v-6m-1.5-9H5.625a3.375 3.375 0 0 0-3.375 3.375v11.25a3.375 3.375 0 0 0 3.375 3.375h12.75a3.375 3.375 0 0 0 3.375-3.375v-6.375" /></svg>

                                        <p id="overall-grades-feedback" class="mt-2 text-sm text-slate-600" data-lang-key="ta_upload_placeholder">上传或拖拽文件</p>

                                        <input id="overall-grades-upload" type="file" class="sr-only" accept=".xlsx, .xls, .csv">

                                    </label>

                                </div>

                                <div>

                                    <h4 class="text-base font-medium text-slate-800" data-lang-key="ta_upload_file2_title">*2. 每道题目具体得分 (Excel格式)</h4>

                                    <label for="item-scores-upload" class="file-drop-area mt-2" id="item-scores-drop-area">

                                        <svg class="mx-auto h-10 w-10 text-slate-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125V6a3.375 3.375 0 0 0-3.375-3.375H8.25m.75 12 3 3m0 0 3-3m-3 3v-6m-1.5-9H5.625a3.375 3.375 0 0 0-3.375 3.375v11.25a3.375 3.375 0 0 0 3.375 3.375h12.75a3.375 3.375 0 0 0 3.375-3.375v-6.375" /></svg>

                                        <p id="item-scores-feedback" class="mt-2 text-sm text-slate-600" data-lang-key="ta_upload_placeholder">上传或拖拽文件</p>

                                        <input id="item-scores-upload" type="file" class="sr-only" accept=".xlsx, .xls, .csv">

                                    </label>

                                </div>

                                <div>

                                    <h4 class="text-base font-medium text-slate-800" data-lang-key="ta_upload_file3_title">*3. 试卷原题 (PDF格式)</h4>

                                    <p class="text-xs text-slate-500" data-lang-key="ta_upload_file3_subtitle">(必须,用于AI提取题目内容)</p>

                                    <label for="paper-pdf-upload" class="file-drop-area mt-2" id="paper-pdf-drop-area">

                                        <svg class="mx-auto h-10 w-10 text-slate-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125V6a3.375 3.375 0 0 0-3.375-3.375H8.25m.75 12 3 3m0 0 3-3m-3 3v-6m-1.5-9H5.625a3.375 3.375 0 0 0-3.375 3.375v11.25a3.375 3.375 0 0 0 3.375 3.375h12.75a3.375 3.375 0 0 0 3.375-3.375v-6.375" /></svg>

                                        <p id="paper-pdf-feedback" class="mt-2 text-sm text-slate-600" data-lang-key="ta_upload_placeholder">上传或拖拽文件</p>

                                        <input id="paper-pdf-upload" type="file" class="sr-only" accept=".pdf">

                                    </label>

                                </div>

                            </div>

                            

                            <!-- Low Score Boundary Input -->

                            <div class="mt-6 p-4 bg-slate-50 rounded-lg border border-slate-200">

                                <label for="low-score-boundary" class="block text-sm font-medium text-slate-700 mb-2">

                                    <span data-lang-key="ta_low_score_boundary_label">低得分率阈值 (%)</span>

                                    <span class="text-xs text-slate-500 ml-2" data-lang-key="ta_low_score_boundary_hint">低于此比例的题目将被分析 (默认 60%)</span>

                                </label>

                                <input type="number" id="low-score-boundary" min="0" max="100" value="60" class="w-32 px-3 py-2 bg-white border border-slate-300 rounded-md focus:ring-1 focus:ring-blue-500">

                            </div>


                            <div id="analysis-loading" class="mt-8 text-center hidden flex-col items-center justify-center">

                                <div class="ai-loader"></div>

                                <p class="mt-4 text-lg font-semibold text-blue-600" data-lang-key="ta_loading_text">AI分析中,请稍候...</p>

                                <p class="text-slate-500" data-lang-key="ta_loading_subtext">正在解析Excel和PDF数据并调用AI生成报告...</p>

                            </div>

                            <div class="mt-8 text-center" id="analysis-button-container">

                                <button id="start-analysis-btn" class="px-8 py-3 text-base font-semibold text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed" disabled data-lang-key="ta_start_analysis_btn">开始分析</button>

                            </div>

                        </div>


                        <!-- 2. ANALYSIS REPORT SECTION (Initially Hidden) -->

                        <div id="analysis-report-section" class="hidden space-y-8 report-section">

                            <!-- Report Header -->

                             <div class="p-6 bg-white rounded-xl shadow flex justify-between items-center">

                                <div>

                                    <h2 id="ta-report-title" class="text-2xl font-bold text-slate-800" data-lang-key="ta_report_main_title">试卷分析报告</h2>

                                    <p class="text-slate-500 mt-1" data-lang-key="ta_report_main_subtitle">基于AI的深度教学分析</p>

                                </div>

                                <div class="flex">

                                    <button id="download-report-btn" class="px-5 py-2 font-semibold text-white bg-green-600 rounded-lg hover:bg-green-700 flex items-center gap-2" data-lang-key="ta_download_report_btn">

                                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>

                                        下载报告 (HTML)

                                    </button>

                                    <button id="download-word-btn" class="ml-2 px-5 py-2 font-semibold text-white bg-blue-600 rounded-lg hover:bg-blue-700 flex items-center gap-2" data-lang-key="ta_download_word_btn">

                                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>

                                        下载报告 (Word)

                                    </button>

                                </div>

                             </div>


                            <!-- Upload Status -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_status_title">① 上传状态</h3>

                                <ul id="upload-status-list" class="mt-4 space-y-2 text-slate-700 text-base grid grid-cols-1 md:grid-cols-3 gap-4"></ul>

                            </div>


                            <!-- Basic Exam Situation -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_basic_title">② 考试基本情况</h3>

                                <div class="grid gap-6 md:grid-cols-2 lg:grid-cols-6 mt-4">

                                    <div class="p-4 bg-slate-50 rounded-lg"><h4 data-lang-key="ta_basic_students" class="text-sm font-medium text-slate-500">参考人数</h4><p id="ta-stat-students" class="text-3xl font-bold text-slate-800">0</p></div>

                                    <div class="p-4 bg-slate-50 rounded-lg"><h4 data-lang-key="ta_basic_avg" class="text-sm font-medium text-slate-500">平均分</h4><p id="ta-stat-avg" class="text-3xl font-bold text-slate-800">0.00</p></div>

                                    <div class="p-4 bg-slate-50 rounded-lg"><h4 data-lang-key="ta_basic_highest" class="text-sm font-medium text-slate-500">最高分</h4><p id="ta-stat-highest" class="text-3xl font-bold text-slate-800">0</p></div>

                                    <div class="p-4 bg-slate-50 rounded-lg"><h4 data-lang-key="ta_basic_lowest" class="text-sm font-medium text-slate-500">最低分</h4><p id="ta-stat-lowest" class="text-3xl font-bold text-slate-800">0</p></div>

                                    <div class="p-4 bg-slate-50 rounded-lg"><h4 data-lang-key="ta_basic_stddev" class="text-sm font-medium text-slate-500">标准差</h4><p id="ta-stat-stddev" class="text-3xl font-bold text-slate-800">0.00</p></div>

                                    <div class="p-4 bg-slate-50 rounded-lg"><h4 data-lang-key="ta_basic_pass_rate" class="text-sm font-medium text-slate-500">及格率</h4><p id="ta-stat-pass-rate" class="text-3xl font-bold text-slate-800">0.0%</p></div>

                                </div>

                            </div>

                            

                            <!-- Score Distribution Stats -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                 <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_distribution_title">③ 成绩分布统计</h3>

                                 <div class="overflow-x-auto mt-4">

                                    <table class="w-full text-sm text-left text-slate-600">

                                        <thead class="text-xs text-slate-700 uppercase bg-slate-100">

                                           <tr>

                                                <th class="p-3" data-lang-key="ta_dist_exceeds">优秀率 (≥85%)</th>

                                                <th class="p-3" data-lang-key="ta_dist_meets_plus">良好率 (≥75%)</th>

                                                <th class="p-3" data-lang-key="ta_dist_meets">及格率 (≥60%)</th>

                                                <th class="p-3" data-lang-key="ta_dist_working">不及格率 (&lt;60%)</th>

                                                <th class="p-3" data-lang-key="ta_dist_try_again">极差率 (&lt;35%)</th>

                                           </tr>

                                        </thead>

                                        <tbody id="distribution-stats-table-body" class="text-base"></tbody>

                                    </table>

                                 </div>

                            </div>


                            <!-- Low Score Rate Question Analysis -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_low_score_title">④ 低得分率题目分析</h3>

                                <div id="low-score-card-container" class="mt-4 grid grid-cols-1 lg:grid-cols-2 gap-6"></div>

                            </div>

                            

                            <!-- Knowledge Point Mastery -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_kp_mastery_title">⑤ 知识点掌握情况</h3>

                                <div id="kp-mastery-container" class="mt-4 grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6"></div>

                            </div>

                            

                            <!-- Summary Teaching Remarks -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_summary_title">⑥ 总结教学备注</h3>

                                <div id="summary-remarks-content" class="mt-4 text-slate-700 prose max-w-none"></div>

                            </div>


                            <!-- Student Stratification Analysis -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_stratification_title">⑦ 学生分层分析</h3>

                                <div id="stratification-container" class="mt-4 grid grid-cols-1 md:grid-cols-3 gap-6"></div>

                            </div>


                            <!-- Case Study: Bottom Students -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_case_study_title">⑧ 个案分析: 待提高学生</h3>

                                <div id="case-study-container" class="mt-4 space-y-6"></div>

                            </div>

                            

                            <!-- Case Study Summary -->

                            <div class="p-6 bg-white rounded-xl shadow">

                                <h3 class="text-xl font-semibold text-slate-800" data-lang-key="ta_report_case_summary_title">⑨ 个案分析总结备注</h3>

                                <div id="case-summary-content" class="mt-4 text-slate-700 prose max-w-none"></div>

                            </div>


                        </div>

                    </div>

                </div>

            </main>

        </div>

    </div>


    <!-- Confirmation Modal -->

    <div id="confirmation-modal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden items-center justify-center">

        <div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-sm">

            <h3 id="modal-title" class="text-lg font-bold text-slate-800">确认操作</h3>

            <p id="modal-text" class="mt-2 text-sm text-slate-600">您确定要继续吗?</p>

            <div class="mt-6 flex justify-end gap-3">

                <button id="modal-cancel-btn" class="px-4 py-2 text-sm font-medium text-slate-700 bg-slate-100 rounded-lg hover:bg-slate-200">取消</button>

                <button id="modal-confirm-btn" class="px-4 py-2 text-sm font-medium text-white bg-red-600 rounded-lg hover:bg-red-700">确认</button>

            </div>

        </div>

    </div>


    <script>

    document.addEventListener('DOMContentLoaded', () => {

        // --- STATE MANAGEMENT ---

        let studentData = [];

        let currentLang = 'en';

        let currentClassName = '';

        let scoreDistributionChart, performanceRateChart;

        // Full analysis files

        let overallGradesFile = null;

        let itemScoresFile = null;

        let paperPdfFile = null;



        // --- DOM ELEMENTS ---

        const loginPage = document.getElementById('login-page');

        const app = document.getElementById('app');

        const loginForm = document.getElementById('login-form');

        const passwordInput = document.getElementById('password');

        const errorMessage = document.getElementById('error-message');

        const navLinks = document.querySelectorAll('.nav-link');

        const pageContents = document.querySelectorAll('.page-content');

        const pageTitle = document.getElementById('page-title');

        

        // Grade Entry (Simple)

        const dashboardSection = document.getElementById('dashboard-section');

        const gradeEntrySection = document.getElementById('grade-entry-section');

        const welcomeMessage = document.getElementById('welcome-message');

        const reportContainer = document.getElementById('report-container');

        const statStudents = document.getElementById('stat-students');

        const statAvg = document.getElementById('stat-avg');

        const statStdDev = document.getElementById('stat-stddev');

        const statHighest = document.getElementById('stat-highest');

        const statLowest = document.getElementById('stat-lowest');

        const needingAttentionList = document.getElementById('needing-attention-list');

        const addScoreForm = document.getElementById('add-score-form');

        const studentNameInput = document.getElementById('student-name');

        const studentScoreInput = document.getElementById('student-score');

        const scoresTableBody = document.getElementById('scores-table-body');

        const fileUpload = document.getElementById('file-upload');

        const fileNameDisplay = document.getElementById('file-name-display');

        const fileFeedback = document.getElementById('file-feedback');

        const dropArea = document.getElementById('drop-area');

        const analyzeBtn = document.getElementById('analyze-btn');

        const clearBtn = document.getElementById('clear-btn');

        const exportBtn = document.getElementById('export-btn');

        

        // Language & Modal

        const langEnBtn = document.getElementById('lang-en');

        const langZhBtn = document.getElementById('lang-zh');

        const confirmationModal = document.getElementById('confirmation-modal');

        const modalTitle = document.getElementById('modal-title');

        const modalText = document.getElementById('modal-text');

        const modalConfirmBtn = document.getElementById('modal-confirm-btn');

        const modalCancelBtn = document.getElementById('modal-cancel-btn');


        // Test Paper Analysis (Full)

        const testPaperAnalysisSection = document.getElementById('test-paper-analysis-section');

        const analysisUploadStep = document.getElementById('analysis-upload-step');

        const analysisReportSection = document.getElementById('analysis-report-section');

        const analysisLoading = document.getElementById('analysis-loading');

        const analysisButtonContainer = document.getElementById('analysis-button-container');

        const analysisErrorMessage = document.getElementById('analysis-error-message');

        const analysisErrorDetails = document.getElementById('analysis-error-details');

        const startAnalysisBtn = document.getElementById('start-analysis-btn');

        const lowScoreBoundaryInput = document.getElementById('low-score-boundary');

        

        // File Drop Areas & Uploads (Standard ID mapping)

        const overallGradesDropArea = document.getElementById('overall-grades-drop-area');

        const itemScoresDropArea = document.getElementById('item-scores-drop-area');

        const paperPdfDropArea = document.getElementById('paper-pdf-drop-area');

        const overallGradesUpload = document.getElementById('overall-grades-upload');

        const itemScoresUpload = document.getElementById('item-scores-upload');

        const paperPdfUpload = document.getElementById('paper-pdf-upload');

        const overallGradesFeedback = document.getElementById('overall-grades-feedback');

        const itemScoresFeedback = document.getElementById('item-scores-feedback');

        const paperPdfFeedback = document.getElementById('paper-pdf-feedback');

        

        // Report Sections

        const taReportTitle = document.getElementById('ta-report-title');

        const uploadStatusList = document.getElementById('upload-status-list');

        const taStatStudents = document.getElementById('ta-stat-students');

        const taStatAvg = document.getElementById('ta-stat-avg');

        const taStatHighest = document.getElementById('ta-stat-highest');

        const taStatLowest = document.getElementById('ta-stat-lowest');

        const taStatStddev = document.getElementById('ta-stat-stddev');

        const taStatPassRate = document.getElementById('ta-stat-pass-rate');

        const distributionStatsTableBody = document.getElementById('distribution-stats-table-body');

        const lowScoreCardContainer = document.getElementById('low-score-card-container');

        const kpMasteryContainer = document.getElementById('kp-mastery-container');

        const summaryRemarksContent = document.getElementById('summary-remarks-content');

        const stratificationContainer = document.getElementById('stratification-container');

        const caseStudyContainer = document.getElementById('case-study-container');

        const caseSummaryContent = document.getElementById('case-summary-content');

        const downloadReportBtn = document.getElementById('download-report-btn');

        const downloadWordBtn = document.getElementById('download-word-btn');


        const translations = {

            en: {

                title: "Online Grade Analysis Platform", login_title: "Online Grade Analysis Platform", login_subtitle: "Please enter access password", login_error: "Invalid password.", login_button: "Enter System", app_title: "Grade Analysis", 

                nav_grade_entry: "Grade Entry (Simple)", nav_test_paper_analysis: "Test Analysis (Full)", 

                page_title_grade_entry: "Grade Entry (Simple)", page_title_test_paper_analysis: "Test Paper Analysis (Full)", 

                welcome_title: "Welcome to the Grade Analysis Platform", welcome_text: "To get started, please input or upload student scores in the \"Grade Entry\" section below for a simple report.", welcome_text_2: "For multi-file comprehensive analysis, please use the \"Test Analysis\" feature.",

                stat_students: "Number of Students", stat_avg: "Average Score", stat_stddev: "Standard Deviation", stat_highest: "Highest Score", stat_lowest: "Lowest Score", chart_distribution_title: "Score Distribution", chart_performance_title: "Performance Overview", table_title: "Detailed Analysis Report", export_button: "Export Data (CSV)", table_header_class: "Class", table_header_students: "Students", table_header_avg: "Avg Score", table_header_stddev: "Std Dev", table_header_exceeds: ">85% (Exceeds)", table_header_meets_plus: ">75% (Meets +)", table_header_meets: ">60% (Meets)", table_header_working: "<60% (Working Toward)", table_header_try_again: "<35% (Try Again)", entry_manual_title: "Manual Grade Entry", entry_manual_subtitle: "Add student scores one by one.", entry_student_name: "Student Name", entry_student_score: "Score", entry_add_button: "Add Score", entry_import_title: "Batch Import from Excel", entry_import_subtitle: "Upload an .xlsx or .csv file with 'Name' and 'Score' columns.", entry_upload_button: "Upload a file", entry_drag_drop: "or drag and drop", entry_table_title: "Current Scores", entry_analyze_button: "Generate Simple Report", entry_clear_button: "Clear All", entry_table_header_no: "#", entry_table_header_name: "Student Name", entry_table_header_score: "Score", entry_table_header_action: "Action", entry_table_no_data: "No data yet. Add scores above or import a file.", 

                needing_attention_title: "Students Needing Attention (Score < 60)",

                all_students_passed: "Excellent work! All students have passed.",

                chart_label_exceeds: 'Exceeds (>85%)', chart_label_meets_plus: 'Meets+ (75-85%)', chart_label_meets: 'Meets (60-75%)', chart_label_working_toward: 'Working Toward (<60%)', confirm_clear_title: 'Confirm Deletion', confirm_clear_text: 'Are you sure you want to clear all student data? This action cannot be undone.', file_error_missing_cols: 'Import failed. File must contain "Name" and "Score" columns.', file_error_missing_name: 'Import failed. Could not find a "Name" column.', file_error_missing_score: 'Import failed. Could not find a "Score" column.', file_success_replace: (count) => `Previous data cleared. Successfully imported ${count} new records.`, manual_entry_class_name: "Manual Entry Class",

                ta_upload_title: "Upload Files for Analysis", ta_upload_subtitle: "Please upload the required files. The AI will analyze the data and generate a report.", ta_upload_file1_title: "*1. Overall Student Grades (Excel)", ta_upload_file2_title: "*2. Itemized Student Scores (Excel)", ta_upload_file3_title: "*3. Original Test Paper (PDF)", ta_upload_file3_subtitle: "(Required, for AI to extract question text)", ta_upload_placeholder: "Upload or drag file", ta_start_analysis_btn: "Start Analysis", ta_loading_text: "AI Analysis in progress, please wait...", ta_loading_subtext: "Parsing Excel & PDF data and calling AI...", ta_report_main_title: "Test Paper Analysis Report", ta_report_main_subtitle: "In-depth Teaching Analysis Powered by AI", ta_download_report_btn: "Download Report (HTML)", ta_download_word_btn: "Download Report (Word)", ta_report_status_title: "① Upload Status", ta_report_basic_title: "② Basic Exam Situation", ta_basic_students: "Participants", ta_basic_avg: "Average", ta_basic_highest: "Highest", ta_basic_lowest: "Lowest", ta_basic_stddev: "Std. Dev.", ta_basic_pass_rate: "Pass Rate (≥60%)", ta_report_distribution_title: "③ Score Distribution Statistics", ta_dist_exceeds: "Exceeds (≥85%)", ta_dist_meets_plus: "Meets+ (≥75%)", ta_dist_meets: "Meets (≥60%)", ta_dist_working: "Working Toward (<60%)", ta_dist_try_again: "Try Again (<35%)", ta_report_low_score_title: "④ Low Scoring Rate Question Analysis", ta_low_q_header_no: "Q#", ta_low_q_header_rate: "Score Rate", ta_low_q_header_kp: "Knowledge Point & Question Text", ta_low_q_header_error: "Main Error Type", ta_low_q_header_solution: "Teaching Strategy Solution", ta_report_kp_mastery_title: "⑤ Knowledge Point Mastery", ta_report_summary_title: "⑥ Summary Teaching Remarks", ta_report_stratification_title: "⑦ Student Stratification Analysis", ta_report_case_study_title: "⑧ Case Study: Students Working Toward", ta_report_case_summary_title: "⑨ Case Study Summary Remarks", file_upload_success: (name) => `File uploaded: ${name}`, file_upload_ready: "Ready", ta_error_title: "Analysis Error", ta_error_message: "Could not complete analysis. Please check file formats or try again.", ta_error_details_prefix: "Details:", ta_error_check_format: "Error: Please check the 'Overall Grades' file format. Could not find student names or scores.", ta_error_check_format_item: "Error: Please check the 'Itemized Scores' file. Could not find the '得分率' (Score Rate) row at the end of the file.", ta_error_api_failed: "Error: AI analysis failed. The API may be unavailable.", ta_error_pdf_parse: "Error: Could not parse PDF file.", ta_case_study_issues: "Main Issues", ta_case_study_strategies: "Improvement Strategies", ta_low_score_boundary_label: "Low Score Threshold (%)", ta_low_score_boundary_hint: "Questions below this rate will be analyzed (Default 60%)"

            },

            zh: {

                title: "在线成绩分析平台", login_title: "在线成绩分析平台", login_subtitle: "请输入访问密码", login_error: "密码无效,请重试。", login_button: "进入系统", app_title: "成绩分析", 

                nav_grade_entry: "成绩录入 (简易)", nav_test_paper_analysis: "试卷分析 (综合)", 

                page_title_grade_entry: "成绩录入 (简易)", page_title_test_paper_analysis: "试卷分析 (综合)", 

                welcome_title: "欢迎使用成绩分析平台", welcome_text: "请在下方“成绩录入”板块输入或上传学生分数以生成简易报告。", welcome_text_2: "如需多文件综合分析,请使用“试卷分析”功能。",

                stat_students: "学生人数", stat_avg: "平均分", stat_stddev: "标准差", stat_highest: "最高分", stat_lowest: "最低分", chart_distribution_title: "分数段分布", chart_performance_title: "整体表现概览", table_title: "详细分析报告", export_button: "导出数据 (CSV)", table_header_class: "班级", table_header_students: "人数", table_header_avg: "平均分", table_header_stddev: "标准差", table_header_exceeds: ">85% (优秀)", table_header_meets_plus: ">75% (良好)", table_header_meets: ">60% (及格)", table_header_working: "<60% (待提高)", table_header_try_again: "<35% (需努力)", entry_manual_title: "手动录入成绩", entry_manual_subtitle: "逐条添加学生分数。", entry_student_name: "学生姓名", entry_student_score: "分数", entry_add_button: "添加分数", entry_import_title: "从Excel批量导入", entry_import_subtitle: "上传包含 '姓名' 和 '分数' 列的 .xlsx 或 .csv 文件。", entry_upload_button: "上传文件", entry_drag_drop: "或拖拽文件", entry_table_title: "当前分数列表", entry_analyze_button: "生成简易报告", entry_clear_button: "全部清除", entry_table_header_no: "序号", entry_table_header_name: "学生姓名", entry_table_header_score: "分数", entry_table_header_action: "操作", entry_table_no_data: "暂无数据。请在上方添加分数或导入文件。",

                needing_attention_title: "待提高学生名单 (分数低于60)",

                all_students_passed: "表现优异,所有学生均已及格!",

                chart_label_exceeds: '优秀 (>85%)', chart_label_meets_plus: '良好 (75-85%)', chart_label_meets: '及格 (60-75%)', chart_label_working_toward: '待提高 (<60%)', confirm_clear_title: '确认清除', confirm_clear_text: '您确定要清除所有学生数据吗?此操作无法撤销。', file_error_missing_cols: '导入失败。文件必须包含“姓名”和“分数”列。', file_error_missing_name: '导入失败。文件中未找到“姓名”或类似列。', file_error_missing_score: '导入失败。文件中未找到“分数”或类似列。', file_success_replace: (count) => `已清除旧数据。成功导入 ${count} 条新记录。`, manual_entry_class_name: "手动录入班级",

                ta_upload_title: "上传分析所需文件", ta_upload_subtitle: "请上传所需文件。AI将分析数据并生成报告。", ta_upload_file1_title: "*1. 学生总体成绩 (Excel格式)", ta_upload_file2_title: "*2. 每道题目具体得分 (Excel格式)", ta_upload_file3_title: "*3. 试卷原题 (PDF格式)", ta_upload_file3_subtitle: "(必须,用于AI提取题目内容)", ta_upload_placeholder: "上传或拖拽文件", ta_start_analysis_btn: "开始分析", ta_loading_text: "AI分析中,请稍候...", ta_loading_subtext: "正在解析Excel和PDF数据并调用AI生成报告...", ta_report_main_title: "试卷分析报告", ta_report_main_subtitle: "基于AI的深度教学分析", ta_download_report_btn: "下载报告 (HTML)", ta_download_word_btn: "下载报告 (Word)", ta_report_status_title: "① 上传状态", ta_report_basic_title: "② 考试基本情况", ta_basic_students: "参考人数", ta_basic_avg: "平均分", ta_basic_highest: "最高分", ta_basic_lowest: "最低分", ta_basic_stddev: "标准差", ta_basic_pass_rate: "及格率 (≥60%)", ta_report_distribution_title: "③ 成绩分布统计", ta_dist_exceeds: "优秀率 (≥85%)", ta_dist_meets_plus: "良好率 (≥75%)", ta_dist_meets: "及格率 (≥60%)", ta_dist_working: "不及格率 (<60%)", ta_dist_try_again: "极差率 (&lt;35%)", ta_report_low_score_title: "④ 低得分率题目分析", ta_low_q_header_no: "题号", ta_low_q_header_rate: "得分率", ta_low_q_header_kp: "知识点 & 题目原文", ta_low_q_header_error: "主要错误类型", ta_low_q_header_solution: "教学策略解决方案", ta_report_kp_mastery_title: "⑤ 知识点掌握情况", ta_report_summary_title: "⑥ 总结教学备注", ta_report_stratification_title: "⑦ 学生分层分析", ta_report_case_study_title: "⑧ 个案分析: 待提高学生", ta_report_case_summary_title: "⑨ 个案分析总结备注", file_upload_success: (name) => `文件已上传: ${name}`, file_upload_ready: "准备就绪", ta_error_title: "分析出错", ta_error_message: "无法完成分析。请检查文件格式是否正确,或稍后重试。", ta_error_details_prefix: "错误详情:", ta_error_check_format: "错误:请检查'学生总体成绩'文件格式。未能在文件中找到学生姓名或分数。", ta_error_check_format_item: "错误:请检查'每道题目具体得分'文件。未能在文件末尾找到'得分率'行。", ta_error_api_failed: "错误:AI分析失败。API可能无法访问或返回错误。", ta_error_pdf_parse: "错误:无法解析PDF文件。", ta_case_study_issues: "主要问题", ta_case_study_strategies: "改进策略", ta_low_score_boundary_label: "低得分率阈值 (%)", ta_low_score_boundary_hint: "低于此比例的题目将被分析 (默认 60%)"

            }

        };


        // --- FUNCTIONS ---


        function setLanguage(lang) {

            currentLang = lang;

            document.documentElement.lang = lang;

            

            document.querySelectorAll('[data-lang-key]').forEach(el => {

                const key = el.getAttribute('data-lang-key');

                const translation = translations[lang][key];

                if (translation && typeof translation !== 'function') {

                    el.textContent = translation;

                }

            });


             document.querySelectorAll('[data-lang-placeholder]').forEach(el => {

                const key = el.getAttribute('data-lang-placeholder');

                if (translations[lang][key]) {

                    el.placeholder = translations[lang][key];

                }

            });


            if (lang === 'en') {

                langEnBtn.classList.add('bg-white', 'text-blue-600', 'shadow-sm');

                langEnBtn.classList.remove('text-slate-600');

                langZhBtn.classList.remove('bg-white', 'text-blue-600', 'shadow-sm');

                langZhBtn.classList.add('text-slate-600');

            } else {

                langZhBtn.classList.add('bg-white', 'text-blue-600', 'shadow-sm');

                langZhBtn.classList.remove('text-slate-600');

                langEnBtn.classList.remove('bg-white', 'text-blue-600', 'shadow-sm');

                langEnBtn.classList.add('text-slate-600');

            }

            

            if (currentClassName === translations.en.manual_entry_class_name || currentClassName === translations.zh.manual_entry_class_name) {

                currentClassName = translations[lang].manual_entry_class_name;

            }


            if (reportContainer.style.display !== 'none' && studentData.length > 0) {

                generateReport();

            }

            

            // Update file feedback text on lang change

            updateFileFeedback(overallGradesFile, overallGradesFeedback);

            updateFileFeedback(itemScoresFile, itemScoresFeedback);

            updateFileFeedback(paperPdfFile, paperPdfFeedback);

        }

        

        function handleLogin(e) {

            e.preventDefault();

            const inputPwd = passwordInput.value.trim();

            if (inputPwd) { 

                // Store password locally to send with API request later

                localStorage.setItem('app_password', inputPwd); 

                loginPage.classList.add('hidden');

                app.classList.remove('hidden');

                document.body.classList.remove('locked'); 

            } else {

                // simple visual feedback

                passwordInput.classList.add('border-red-500');

            }

        }


        // ... (Navigation, Table Update, File Handling functions remain the same) ...

        function handleNavigation(e) {

            e.preventDefault();

            const targetId = e.currentTarget.getAttribute('href').substring(1);

            navLinks.forEach(link => link.classList.remove('active'));

            e.currentTarget.classList.add('active');

            pageContents.forEach(content => content.classList.add('hidden'));

            if (targetId === 'grade-entry') {

                dashboardSection.classList.remove('hidden');

                gradeEntrySection.classList.remove('hidden');

            } else {

                document.getElementById(`${targetId}-section`).classList.remove('hidden');

            }

            const titleKey = `page_title_${targetId.replace('-', '_')}`;

            pageTitle.textContent = translations[currentLang][titleKey] || 'Page';

            pageTitle.setAttribute('data-lang-key', titleKey);

        }


        function updateTable() {

            scoresTableBody.innerHTML = '';

            if (studentData.length === 0) {

                scoresTableBody.innerHTML = `<tr><td colspan="4" class="text-center p-4 text-slate-500" data-lang-key="entry_table_no_data">${translations[currentLang].entry_table_no_data}</td></tr>`;

                analyzeBtn.disabled = true;

                clearBtn.disabled = true;

                return;

            }

            studentData.forEach((student, index) => {

                const tr = document.createElement('tr');

                tr.className = 'border-b';

                tr.innerHTML = `<td class="p-3">${index + 1}</td><td class="p-3">${student.name}</td><td class="p-3">${student.score}</td><td class="p-3 text-center"><button class="text-red-500 hover:text-red-700 remove-btn" data-index="${index}"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg></button></td>`;

                scoresTableBody.appendChild(tr);

            });

            analyzeBtn.disabled = false;

            clearBtn.disabled = false;

        }


        function addScore(name, score) {

            if (studentData.length === 0) { currentClassName = translations[currentLang].manual_entry_class_name; }

            studentData.push({ name, score: parseFloat(score) });

            updateTable();

        }

        function removeScore(index) { studentData.splice(index, 1); updateTable(); }

        function handleFile(file) {

            if (!file) return;

            fileNameDisplay.textContent = file.name;

            fileFeedback.textContent = '';

            fileFeedback.classList.remove('text-red-500', 'text-green-600');

            const reader = new FileReader();

            reader.onload = (e) => {

                try {

                    const data = new Uint8Array(e.target.result);

                    const workbook = XLSX.read(data, {type: 'array'});

                    const firstSheetName = workbook.SheetNames[0];

                    const worksheet = workbook.Sheets[firstSheetName];

                    const rows = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: "" });

                    if (rows.length === 0) { fileFeedback.textContent = '文件为空或格式不正确。'; fileFeedback.classList.add('text-red-500'); return; }

                    let headerRow = rows[0].map(h => String(h).toLowerCase().trim());

                    let dataRows = rows.slice(1);

                    const nameSynonyms = ['name', 'student name', 'student', '姓名'];

                    const scoreSynonyms = ['score', 'grade', '分数', '成绩'];

                    let nameIndex = headerRow.findIndex(h => nameSynonyms.includes(h));

                    let scoreIndex = headerRow.findIndex(h => scoreSynonyms.includes(h));

                    if (nameIndex === -1 || scoreIndex === -1) { dataRows = rows; nameIndex = 0; scoreIndex = 1; }

                    const importedData = dataRows.map(row => ({ name: row[nameIndex], score: parseFloat(row[scoreIndex]) })).filter(d => d.name && !isNaN(d.score) && d.score >= 0);

                    if(importedData.length > 0) { studentData = importedData; currentClassName = file.name.replace(/\.(xlsx|xls|csv)$/i, ''); updateTable(); fileFeedback.textContent = translations[currentLang].file_success_replace(importedData.length); fileFeedback.classList.add('text-green-600'); } else { fileFeedback.textContent = '在文件中未找到有效的数据。'; fileFeedback.classList.add('text-red-500'); }

                } catch (error) { console.error("File parsing error:", error); fileFeedback.textContent = "读取文件时发生错误。"; fileFeedback.classList.add('text-red-500'); } finally { fileUpload.value = null; }

            };

            reader.readAsArrayBuffer(file);

        }

        function calculateStats() {

            const scores = studentData.map(s => s.score);

            const count = scores.length;

            if (count === 0) return { count: 0, avg: 0, stdDev: 0, highest: 0, lowest: 0, distribution: [0,0,0,0,0], performance: {exceeds: 0, meets_plus: 0, meets: 0, working_toward: 0} };

            const sum = scores.reduce((a, b) => a + b, 0);

            const avg = sum / count;

            const stdDev = Math.sqrt(scores.map(x => Math.pow(x - avg, 2)).reduce((a, b) => a + b, 0) / count);

            const highest = Math.max(...scores);

            const lowest = Math.min(...scores);

            const distribution = [0, 0, 0, 0, 0];

            const performance = {exceeds: 0, meets_plus: 0, meets: 0, working_toward: 0, try_again: 0};

            scores.forEach(score => {

                if (score < 60) distribution[0]++; else if (score < 70) distribution[1]++; else if (score < 80) distribution[2]++; else if (score < 90) distribution[3]++; else distribution[4]++;

                if(score >= 85) performance.exceeds++; if(score >= 75) performance.meets_plus++; if(score >= 60) performance.meets++; if(score < 60) performance.working_toward++; if(score < 35) performance.try_again++;

            });

            return { count, avg: avg.toFixed(2), stdDev: stdDev.toFixed(2), highest, lowest, distribution, performance };

        }

        function generateReport() {

            if (studentData.length === 0) return;

            const stats = calculateStats();

            welcomeMessage.classList.add('hidden');

            reportContainer.classList.remove('hidden');

            statStudents.textContent = stats.count; statAvg.textContent = stats.avg; statStdDev.textContent = stats.stdDev; statHighest.textContent = stats.highest; statLowest.textContent = stats.lowest;

            const chartCtx1 = document.getElementById('scoreDistributionChart').getContext('2d');

            if (scoreDistributionChart) scoreDistributionChart.destroy();

            scoreDistributionChart = new Chart(chartCtx1, { type: 'bar', data: { labels: ['0-59', '60-69', '70-79', '80-89', '90-100'], datasets: [{ data: stats.distribution, backgroundColor: 'rgba(59, 130, 246, 0.5)', borderColor: 'rgba(59, 130, 246, 1)', borderWidth: 1, borderRadius: 4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { stepSize: 1 } } } } });

            const chartCtx2 = document.getElementById('performanceRateChart').getContext('2d');

            if (performanceRateChart) performanceRateChart.destroy();

            performanceRateChart = new Chart(chartCtx2, { type: 'doughnut', data: { labels: [translations[currentLang].chart_label_exceeds, translations[currentLang].chart_label_meets_plus, translations[currentLang].chart_label_meets, translations[currentLang].chart_label_working_toward], datasets: [{ data: [stats.performance.exceeds, stats.performance.meets_plus - stats.performance.exceeds, stats.performance.meets - stats.performance.meets_plus, stats.performance.working_toward], backgroundColor: ['#10B981', '#3B82F6', '#F59E0B', '#EF4444'], }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' } } } });

            document.getElementById('report-table-body').innerHTML = `<tr class="bg-white border-b"><td class="p-4 font-medium">${currentClassName}</td><td class="p-4">${stats.count}</td><td class="p-4">${stats.avg}</td><td class="p-4">${stats.stdDev}</td><td class="p-4">${stats.performance.exceeds} (${(stats.performance.exceeds/stats.count*100).toFixed(1)}%)</td><td class="p-4">${stats.performance.meets_plus} (${(stats.performance.meets_plus/stats.count*100).toFixed(1)}%)</td><td class="p-4">${stats.performance.meets} (${(stats.performance.meets/stats.count*100).toFixed(1)}%)</td><td class="p-4">${stats.performance.working_toward} (${(stats.performance.working_toward/stats.count*100).toFixed(1)}%)</td><td class="p-4">${stats.performance.try_again} (${(stats.performance.try_again/stats.count*100).toFixed(1)}%)</td></tr>`;

            const needingAttentionStudents = studentData.filter(s => s.score < 60).sort((a,b) => b.score - a.score);

            needingAttentionList.innerHTML = '';

            if(needingAttentionStudents.length > 0) { needingAttentionStudents.forEach(student => { const li = document.createElement('li'); li.className = "flex justify-between items-center py-2 px-3 border-b border-slate-100 last:border-b-0"; li.innerHTML = `<span class="text-slate-700">${student.name}</span><span class="font-bold text-red-600">${student.score}</span>`; needingAttentionList.appendChild(li); }); } else { needingAttentionList.innerHTML = `<p class="text-center text-slate-500 py-4" data-lang-key="all_students_passed">${translations[currentLang].all_students_passed}</p>`; }

        }

        

        function exportToCsv() {

            const stats = calculateStats();

            let csvContent = "data:text/csv;charset=utf-8,\uFEFF"; // Add BOM for Excel UTF-8 support

            

            const headers = [

                translations[currentLang].table_header_class,

                translations[currentLang].table_header_students,

                translations[currentLang].table_header_avg,

                translations[currentLang].table_header_stddev,

                translations[currentLang].table_header_exceeds,

                translations[currentLang].table_header_meets_plus,

                translations[currentLang].table_header_meets,

                translations[currentLang].table_header_working,

                translations[currentLang].table_header_try_again,

            ];

            

            // Fix: ensure proper string concatenation and quoting

            const dataRow = [

                currentClassName,

                stats.count,

                stats.avg,

                stats.stdDev,

                `${stats.performance.exceeds} (${(stats.performance.exceeds/stats.count*100).toFixed(1)}%)`,

                `${stats.performance.meets_plus} (${(stats.performance.meets_plus/stats.count*100).toFixed(1)}%)`,

                `${stats.performance.meets} (${(stats.performance.meets/stats.count*100).toFixed(1)}%)`,

                `${stats.performance.working_toward} (${(stats.performance.working_toward/stats.count*100).toFixed(1)}%)`,

                `${stats.performance.try_again} (${(stats.performance.try_again/stats.count*100).toFixed(1)}%)`,

            ];


            csvContent += headers.map(h => `"${h}"`).join(",") + "\n";

            csvContent += dataRow.map(d => `"${d}"`).join(",") + "\n";


            const needingAttentionStudents = studentData

                .filter(s => s.score < 60)

                .sort((a, b) => b.score - a.score);


            if (needingAttentionStudents.length > 0) {

                csvContent += "\n\n"; 

                csvContent += `"${translations[currentLang].needing_attention_title}"\n`;

                csvContent += `"${translations[currentLang].entry_table_header_name}","${translations[currentLang].entry_table_header_score}"\n`;

                needingAttentionStudents.forEach(student => {

                    csvContent += `"${student.name}",${student.score}\n`;

                });

            }


            const encodedUri = encodeURI(csvContent);

            const link = document.createElement("a");

            link.setAttribute("href", encodedUri);

            link.setAttribute("download", `${currentClassName || 'report'}_analysis_report.csv`);

            document.body.appendChild(link);

            link.click();

            document.body.removeChild(link);

        }


        function showConfirmationModal(titleKey, textKey, onConfirm) { modalTitle.textContent = translations[currentLang][titleKey]; modalText.textContent = translations[currentLang][textKey]; confirmationModal.classList.remove('hidden'); confirmationModal.classList.add('flex'); const newConfirmBtn = modalConfirmBtn.cloneNode(true); modalConfirmBtn.parentNode.replaceChild(newConfirmBtn, modalConfirmBtn); newConfirmBtn.addEventListener('click', () => { onConfirm(); hideConfirmationModal(); }, { once: true }); }

        function hideConfirmationModal() { confirmationModal.classList.add('hidden'); confirmationModal.classList.remove('flex'); }


        // --- Test Paper Analysis Functions ---

        function setupFileUploader(dropArea, uploadInput, feedbackEl, fileStateSetter) {

            const dragEvents = ['dragenter', 'dragover', 'dragleave', 'drop'];

            dragEvents.forEach(eventName => { dropArea.addEventListener(eventName, (e) => { e.preventDefault(); e.stopPropagation(); if (eventName === 'dragenter' || eventName === 'dragover') { dropArea.classList.add('dragover'); } else { dropArea.classList.remove('dragover'); } if (eventName === 'drop') { const file = e.dataTransfer.files[0]; uploadInput.files = e.dataTransfer.files; handleFileChange(file, feedbackEl, fileStateSetter); } }, false); });

            uploadInput.addEventListener('change', (e) => { const file = e.target.files[0]; handleFileChange(file, feedbackEl, fileStateSetter); });

        }

        function handleFileChange(file, feedbackEl, fileStateSetter) { if (file) { fileStateSetter(file); updateFileFeedback(file, feedbackEl); } checkAllFilesUploaded(); }

        function updateFileFeedback(file, feedbackEl) { if (file) { feedbackEl.textContent = translations[currentLang].file_upload_success(file.name); feedbackEl.classList.add('text-green-600', 'font-semibold'); feedbackEl.classList.remove('text-slate-600'); } else { feedbackEl.textContent = translations[currentLang].ta_upload_placeholder; feedbackEl.classList.remove('text-green-600', 'font-semibold'); feedbackEl.classList.add('text-slate-600'); } }

        function checkAllFilesUploaded() { if (overallGradesFile && itemScoresFile && paperPdfFile) { startAnalysisBtn.disabled = false; } else { startAnalysisBtn.disabled = true; } }

        function parseExcel(file) { return new Promise((resolve, reject) => { if (!file) { return reject(new Error("No file provided")); } const reader = new FileReader(); reader.onload = (e) => { try { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; const json = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); resolve({data: json, name: file.name}); } catch (error) { reject(error); } }; reader.onerror = (error) => reject(error); reader.readAsArrayBuffer(file); }); }


        // --- UPDATED PARSE PDF FOR TEXT ---

        async function parsePdfForText(file) {

            if (!file) return null;

            try {

                const arrayBuffer = await file.arrayBuffer();

                const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;

                let allText = '';

                

                for (let i = 1; i <= pdf.numPages; i++) {

                    const page = await pdf.getPage(i);

                    const textContent = await page.getTextContent();

                    

                    // Add X/Y coordinates to items to allow spatial sorting

                    const items = textContent.items.map(item => {

                        const tx = item.transform; // [scaleX, skewY, skewX, scaleY, translateX, translateY]

                        return {

                            str: item.str,

                            x: tx[4],

                            y: tx[5], // Higher Y is higher on page (bottom-left origin)

                            hasEOL: item.hasEOL,

                            height: item.height

                        };

                    });


                    // Sort items: Top-to-Bottom (Y Desc), then Left-to-Right (X Asc)

                    // We allow a small 'row tolerance' so slightly misaligned items are treated as same line

                    items.sort((a, b) => {

                        const yDiff = b.y - a.y;

                        if (Math.abs(yDiff) > 8) { // Tolerance of 8 units for a "line"

                            return yDiff; 

                        }

                        return a.x - b.x;

                    });


                    allText += `\n--- Page ${i} ---\n`;

                    

                    // Reconstruct text with newlines

                    let lastY = items[0]?.y || 0;

                    items.forEach(item => {

                        // If Y drops significantly, insert a newline

                        if (lastY - item.y > 10) {

                            allText += '\n';

                        }

                        allText += item.str + ' '; // Add space between words

                        lastY = item.y;

                    });

                    allText += '\n';

                }

                return allText;

            } catch (error) {

                console.error("PDF parsing error:", error);

                throw new Error(translations[currentLang].ta_error_pdf_parse);

            }

        }

        

        function processStudentData(overallGrades, itemScores) { /* ... existing logic ... */ let overallData = []; let nameCol = '姓名'; let scoreCol = '分数'; let headerRow = overallGrades.data[0].map(h => String(h).toLowerCase().trim()); let dataRows; let nameIndex = headerRow.findIndex(h => h.includes('name') || h.includes('姓名')); let scoreIndex = headerRow.findIndex(h => h.includes('score') || h.includes('分数') || h.includes('成绩') || h.includes('总分')); if (nameIndex !== -1 && scoreIndex !== -1) { dataRows = overallGrades.data.slice(1); nameCol = overallGrades.data[0][nameIndex]; scoreCol = overallGrades.data[0][scoreIndex]; } else { nameIndex = 0; scoreIndex = 1; dataRows = overallGrades.data; if (dataRows.length > 0 && isNaN(parseFloat(dataRows[0][scoreIndex]))) { dataRows = dataRows.slice(1); } } overallData = dataRows.map(row => ({ name: row[nameIndex], score: parseFloat(row[scoreIndex]) })).filter(d => d.name && !isNaN(d.score)); if (overallData.length === 0) { throw new Error(translations[currentLang].ta_error_check_format); } const scores = overallData.map(s => s.score); const count = scores.length; const sum = scores.reduce((a, b) => a + b, 0); const avg = sum / count; const stdDev = Math.sqrt(scores.map(x => Math.pow(x - avg, 2)).reduce((a, b) => a + b, 0) / count); const highest = Math.max(...scores); const lowest = Math.min(...scores); let exceeds = 0, meets_plus = 0, meets = 0, working_toward = 0, try_again = 0; scores.forEach(score => { if(score >= 85) exceeds++; if(score >= 75) meets_plus++; if(score >= 60) meets++; if(score < 60) working_toward++; if(score < 35) try_again++; }); const stats = { count, avg: avg.toFixed(1), highest, lowest, stdDev: stdDev.toFixed(1), passRate: (meets / count * 100).toFixed(1) + '%' }; const distribution = { exceeds: (exceeds / count * 100).toFixed(1) + '%', meets_plus: (meets_plus / count * 100).toFixed(1) + '%', meets: (meets / count * 100).toFixed(1) + '%', working_toward: (working_toward / count * 100).toFixed(1) + '%', try_again: (try_again / count * 100).toFixed(1) + '%' }; let itemHeaderRow = itemScores.data[0].map(h => String(h).trim()); let itemNameCol = itemHeaderRow[0]; let questionCols = itemHeaderRow.slice(1); let scoreRateRowIndex = -1; for(let i = itemScores.data.length - 1; i >= 0; i--) { if (String(itemScores.data[i][0]).trim().includes('得分率')) { scoreRateRowIndex = i; break; } } if (scoreRateRowIndex === -1) { throw new Error(translations[currentLang].ta_error_check_format_item); } let scoreRateRow = itemScores.data[scoreRateRowIndex]; let scoreRates = scoreRateRow.slice(1); let studentItemDataRows = itemScores.data.slice(1, scoreRateRowIndex); let itemAnalysis = []; for(let i = 0; i < questionCols.length; i++) { if (i >= scoreRates.length) break; const qName = questionCols[i]; const qScoreRate = String(scoreRates[i]); let qTotalScore = 0; let validStudents = 0; studentItemDataRows.forEach(row => { let score = parseFloat(row[i+1]); if (isNaN(score)) { score = 0; } qTotalScore += score; validStudents++; }); const qAvg = validStudents > 0 ? qTotalScore / validStudents : 0; itemAnalysis.push({ question: qName, scoreRate: qScoreRate, avgScore: qAvg.toFixed(2) }); } const bottomStudents = overallData.filter(s => s.score < 60).sort((a,b) => a.score - b.score).slice(0, 5); return { stats, distribution, itemAnalysis, bottomStudents, className: overallGrades.name.replace(/\.(xlsx|xls|csv)$/i, '') }; }


        // --- UPDATED AI PROMPT ---

        function createAIPrompt(analysisData, pdfText, threshold) {

            const lang = currentLang === 'zh' ? 'Chinese' : 'English';

            const fullPdfText = pdfText || "Not provided"; 

            const userThreshold = threshold || 60;


            const prompt = `

                You are an expert teaching analyst. I provide you with test statistics and the original PDF text content.

                Your task is to analyze this data and provide pedagogical feedback in ${lang}.

                

                **Item Analysis (Questions & Score Rates):**

                ${JSON.stringify(analysisData.itemAnalysis, null, 2)}

                

                **Original Paper Text (Extracted from PDF):**

                ${fullPdfText}

                

                **Bottom 5 Performing Students (Name, Score):**

                ${JSON.stringify(analysisData.bottomStudents, null, 2)}


                **CRITICAL INSTRUCTION FOR "lowScoreAnalysis":**

                1. FILTER: Only analyze questions where 'scoreRate' is LESS THAN ${userThreshold}%.

                2. LOCATE TEXT: For each filtered question (e.g., "Q17"), you MUST find the exact text in the provided PDF content.

                   - Look for the number at the START of a line (e.g., "\n17", "\n17)", "\nQ17").

                   - DO NOT confuse Question 17 with the number "17" appearing inside the text of Question 16.

                   - **ANTI-DRIFT CHECK:** The text for Question X MUST appear after Question X-1 and before Question X+1.

                   - If you see "16 ... [text] ... 18", and no "17" starting a line in between, then Q17 text is missing. State "Text not found".

                   - **Do NOT hallucinate.** If you cannot find the exact text starting with "17", do not invent it.

                

                - **STRICT INSTRUCTION FOR "caseStudies":**

                  1. You must ONLY generate case studies for the specific students listed in the "**Bottom 5 Performing Students**" JSON data provided above.

                  2. **COPY the "name" and "score" EXACTLY** from that list. Do not invent names or change scores.

                  3. If the list is empty (no students < 60), return an empty array [].

                  4. For "problems" and "strategies", infer likely weaknesses based on the *general* low-scoring questions found in the paper, tailored to how low their specific score is.


                Return a JSON object matching this schema:

                {

                  "lowScoreAnalysis": [

                    {

                      "question": "Q17",

                      "scoreRate": "45%",

                      "questionText": "exact text found in PDF...", 

                      "knowledgePoint": "Topic...", 

                      "errorType": "Reason...", 

                      "solution": ["Strategy 1", "Strategy 2"] 

                    }

                  ],

                  "kpMastery": [ { "name": "Topic", "rate": 0.85, "errors": ["..."] } ],

                  "summaryRemarks": ["..."],

                  "stratification": { "high": [], "medium": [], "low": [] },

                  "caseStudies": [],

                  "caseSummary": []

                }

            `;

            return prompt;

        }

        

        // ... (AI Schema, Call, Retry functions remain the same) ...

        const aiResponseSchema = { "type": "OBJECT", "required": ["lowScoreAnalysis", "kpMastery", "summaryRemarks", "stratification", "caseStudies", "caseSummary"], "properties": { "lowScoreAnalysis": { "type": "ARRAY", "items": { "type": "OBJECT", "required": ["question", "scoreRate", "questionText", "knowledgePoint", "errorType", "solution"], "properties": { "question": { "type": "STRING" }, "scoreRate": { "type": "STRING" }, "questionText": { "type": "STRING" }, "knowledgePoint": { "type": "STRING" }, "errorType": { "type": "STRING" }, "solution": { "type": "ARRAY", "items": { "type": "STRING" } } } } }, "kpMastery": { "type": "ARRAY", "items": { "type": "OBJECT", "properties": { "name": { "type": "STRING" }, "rate": { "type": "NUMBER" }, "errors": { "type": "ARRAY", "items": { "type": "STRING" } } } } }, "summaryRemarks": { "type": "ARRAY", "items": { "type": "STRING" } }, "stratification": { "type": "OBJECT", "properties": { "high": { "type": "ARRAY", "items": { "type": "STRING" } }, "medium": { "type": "ARRAY", "items": { "type": "STRING" } }, "low": { "type": "ARRAY", "items": { "type": "STRING" } } } }, "caseStudies": { "type": "ARRAY", "items": { "type": "OBJECT", "properties": { "name": { "type": "STRING" }, "score": { "type": "NUMBER" }, "problems": { "type": "ARRAY", "items": { "type": "STRING" } }, "strategies": { "type": "ARRAY", "items": { "type": "STRING" } } } } }, "caseSummary": { "type": "ARRAY", "items": { "type": "STRING" } } } };

        async function callGeminiAPI(prompt) { 

            // ⚠️请在此处替换为你的Cloudflare Worker URL ⚠️

            const workerUrl = "https://orange-frog-89ba.linxiaochun168.workers.dev/"; 

            

            const userPassword = localStorage.getItem('app_password') || "";

            if (!userPassword) { throw new Error("请先登录。"); }


            const payload = { contents: [{ parts: [{ text: prompt }] }], generationConfig: { responseMimeType: "application/json", responseSchema: aiResponseSchema, temperature: 0.2 } }; 

            

            let response; 

            try { 

                response = await fetch(workerUrl, { 

                    method: 'POST', 

                    headers: { 'Content-Type': 'application/json', 'Authorization': userPassword }, 

                    body: JSON.stringify(payload) 

                }); 

                

                if (response.status === 401) { throw new Error("密码错误或已过期,请刷新页面重新登录。"); }

                if (!response.ok) { throw new Error(`Server Error: ${response.status} ${response.statusText}`); }

                

                const result = await response.json(); 

                if (result.candidates && result.candidates.length > 0 && result.candidates[0].content) { 

                    const text = result.candidates[0].content.parts[0].text; 

                    return JSON.parse(text); 

                } else { 

                    throw new Error("Invalid API response structure."); 

                } 

            } catch (error) { 

                console.error("Gemini API call failed:", error); 

                throw error; 

            } 

        }

        

        async function callGeminiWithRetry(prompt, retries = 3, delay = 1000) { for (let i = 0; i < retries; i++) { try { return await callGeminiAPI(prompt); } catch (error) { if (i === retries - 1) throw error; await new Promise(res => setTimeout(res, delay * (i + 1))); } } }


        async function startFullAnalysis() {

            analysisButtonContainer.classList.add('hidden');

            analysisErrorMessage.classList.add('hidden');

            analysisLoading.classList.remove('hidden');

            try {

                let file1Promise = parseExcel(overallGradesFile);

                let file2Promise = parseExcel(itemScoresFile);

                let pdfTextPromise = parsePdfForText(paperPdfFile);

                let [file1Data, file2Data, pdfText] = await Promise.all([file1Promise, file2Promise, pdfTextPromise]);

                const isFile1ItemScore = file1Data.data.some(row => String(row[0]).trim().includes('得分率'));

                const isFile2ItemScore = file2Data.data.some(row => String(row[0]).trim().includes('得分率'));

                let overallGrades, itemScores;

                if (isFile1ItemScore && !isFile2ItemScore) { overallGrades = file2Data; itemScores = file1Data; } else { overallGrades = file1Data; itemScores = file2Data; }

                const analysisData = processStudentData(overallGrades, itemScores);

                const threshold = parseInt(lowScoreBoundaryInput.value) || 60;

                const aiPrompt = createAIPrompt(analysisData, pdfText, threshold);

                const aiJson = await callGeminiWithRetry(aiPrompt);

                populateReport(analysisData, aiJson);

                analysisUploadStep.classList.add('hidden');

                analysisLoading.classList.add('hidden');

                analysisReportSection.classList.remove('hidden');

            } catch (error) {

                console.error("Analysis failed:", error);

                const errorMessage = error instanceof Error ? error.message : String(error);

                analysisErrorMessage.classList.remove('hidden');

                analysisErrorDetails.textContent = `${translations[currentLang].ta_error_details_prefix} ${errorMessage}`;

                analysisLoading.classList.add('hidden');

                analysisButtonContainer.classList.remove('hidden');

            }

        }

        

        function populateReport(analysisData, aiJson) {

            const { stats, distribution, className } = analysisData;

            const { lowScoreAnalysis, kpMastery, summaryRemarks, stratification, caseStudies, caseSummary } = aiJson;

            taReportTitle.textContent = `${className} ${translations[currentLang].ta_report_main_title}`;

            uploadStatusList.innerHTML = `<li class="flex items-center gap-2"><svg class="h-5 w-5 text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.707-9.293a1 1 0 0 0-1.414-1.414L9 10.586 7.707 9.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4Z" clip-rule="evenodd" /></svg> <span>${overallGradesFile.name}</span></li><li class="flex items-center gap-2"><svg class="h-5 w-5 text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.707-9.293a1 1 0 0 0-1.414-1.414L9 10.586 7.707 9.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4Z" clip-rule="evenodd" /></svg> <span>${itemScoresFile.name}</span></li><li class="flex items-center gap-2"><svg class="h-5 w-5 text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.707-9.293a1 1 0 0 0-1.414-1.414L9 10.586 7.707 9.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4Z" clip-rule="evenodd" /></svg> <span>${paperPdfFile.name}</span></li>`;

            taStatStudents.textContent = stats.count; taStatAvg.textContent = stats.avg; taStatHighest.textContent = stats.highest; taStatLowest.textContent = stats.lowest; taStatStddev.textContent = stats.stdDev; taStatPassRate.textContent = stats.passRate;

            distributionStatsTableBody.innerHTML = `<tr class="bg-white border-b"><td class="p-3 font-medium text-green-600">${distribution.exceeds}</td><td class="p-3 font-medium text-blue-600">${distribution.meets_plus}</td><td class="p-3 font-medium text-yellow-600">${distribution.meets}</td><td class="p-3 font-medium text-red-600">${distribution.working_toward}</td><td class="p-3 font-medium text-red-700">${distribution.try_again}</td></tr>`;

            lowScoreCardContainer.innerHTML = lowScoreAnalysis.map(item => { const score = parseFloat(item.scoreRate); const bgColorClass = score < 35 ? 'bg-red-50' : 'bg-yellow-50'; const textColorClass = score < 35 ? 'text-red-600' : 'text-yellow-700'; return `<div class="border rounded-lg shadow-sm bg-white overflow-hidden"><div class="p-4 flex justify-between items-center border-b ${bgColorClass}"><div><span class="text-lg font-bold text-slate-800">${item.question || 'Unknown Q'}</span><span class="block text-sm font-semibold ${textColorClass}">${item.scoreRate || 'N/A'} <span data-lang-key="ta_low_q_header_rate">${translations[currentLang].ta_low_q_header_rate}</span></span></div><span class="font-semibold text-slate-700 text-right">${item.knowledgePoint || 'N/A'}</span></div><div class="p-4 space-y-3"><div><h5 class="font-medium text-slate-600" data-lang-key="ta_low_q_header_kp">${translations[currentLang].ta_low_q_header_kp}</h5><p class="text-sm text-slate-500 italic" title="${item.questionText || ''}">${item.questionText || 'N/A'}</p></div><div><h5 class="font-medium text-slate-600" data-lang-key="ta_low_q_header_error">${translations[currentLang].ta_low_q_header_error}</h5><p class="text-sm text-slate-700">${item.errorType || 'N/A'}</p></div><div><h5 class="font-medium text-slate-600" data-lang-key="ta_low_q_header_solution">${translations[currentLang].ta_low_q_header_solution}</h5><ul class="list-decimal pl-5 text-sm text-slate-700 space-y-1">${(item.solution || []).map(s => `<li>${s}</li>`).join('')}</ul></div></div></div>`; }).join('');

            kpMasteryContainer.innerHTML = kpMastery.map(kp => { let ratePercent = parseFloat(kp.rate); if (ratePercent >= 0 && ratePercent <= 1) { ratePercent = ratePercent * 100; } const rateColor = ratePercent < 60 ? 'bg-red-500' : (ratePercent < 75 ? 'bg-yellow-500' : 'bg-green-500'); const errorList = (kp.errors || []).map(err => `<li class="text-sm">${err}</li>`).join(''); return `<div class="border rounded-lg p-4"><div class="flex justify-between items-center mb-2"><span class="font-semibold">${kp.name || 'N/A'}</span><span class="font-bold text-lg ${rateColor.replace('bg-', 'text-')}">${ratePercent.toFixed(1)}%</span></div><div class="w-full bg-slate-200 rounded-full h-2.5"><div class="${rateColor} h-2.5 rounded-full" style="width: ${ratePercent}%"></div></div><div class="mt-3"><h5 class="font-medium text-slate-600" data-lang-key="ta_low_q_header_error">${translations[currentLang].ta_low_q_header_error}:</h5><ul class="list-disc pl-5 mt-1 text-slate-500">${errorList}</ul></div></div>`; }).join('');

            summaryRemarksContent.innerHTML = `<ul>${(summaryRemarks || []).map(s => `<li>${s}</li>`).join('')}</ul>`;

            stratificationContainer.innerHTML = `<div class="border-l-4 border-green-500 bg-white p-4 rounded-r-lg shadow"><h4 class="text-lg font-bold text-green-600" data-lang-key="ta_dist_exceeds">${translations[currentLang].ta_dist_exceeds}</h4><ul>${(stratification.high || []).map(s => `<li>${s}</li>`).join('')}</ul></div><div class="border-l-4 border-yellow-500 bg-white p-4 rounded-r-lg shadow"><h4 class="text-lg font-bold text-yellow-600" data-lang-key="ta_dist_meets">${translations[currentLang].ta_dist_meets}</h4><ul>${(stratification.medium || []).map(s => `<li>${s}</li>`).join('')}</ul></div><div class="border-l-4 border-red-500 bg-white p-4 rounded-r-lg shadow"><h4 class="text-lg font-bold text-red-600" data-lang-key="ta_dist_working">${translations[currentLang].ta_dist_working}</h4><ul>${(stratification.low || []).map(s => `<li>${s}</li>`).join('')}</ul></div>`;

            caseStudyContainer.innerHTML = caseStudies.map((student, index) => `<div class="border rounded-lg p-4"><div class="flex justify-between items-center border-b pb-2"><h4 class="text-lg font-semibold">${index + 1}. ${student.name}</h4><span class="text-2xl font-bold text-red-600">${student.score} <span data-lang-key="entry_student_score">${translations[currentLang].entry_student_score}</span></span></div><div class="mt-3 grid grid-cols-1 md:grid-cols-2 gap-4"><div><h5 class="font-medium" data-lang-key="ta_case_study_issues">${translations[currentLang].ta_case_study_issues}:</h5><ul>${(student.problems || []).map(s => `<li>${s}</li>`).join('')}</ul></div><div><h5 class="font-medium" data-lang-key="ta_case_study_strategies">${translations[currentLang].ta_case_study_strategies}:</h5><ul>${(student.strategies || []).map(s => `<li>${s}</li>`).join('')}</ul></div></div></div>`).join('');

            caseSummaryContent.innerHTML = `<ul>${(caseSummary || []).map(s => `<li>${s}</li>`).join('')}</ul>`;

        }

        

        // --- ADDED MISSING FUNCTIONS ---

        function downloadReport() {

            const reportHtml = analysisReportSection.innerHTML;

            const styles = Array.from(document.styleSheets)

                .map(styleSheet => {

                    try {

                        return Array.from(styleSheet.cssRules)

                            .map(rule => rule.cssText)

                            .join('\n');

                    } catch (e) {

                        return '';

                    }

                })

                .join('\n');

            

            const tailwind = document.querySelector('script[src*="tailwindcss.com"]').outerHTML;

            const fullHtml = `

                <!DOCTYPE html>

                <html lang="${currentLang}">

                <head>

                    <meta charset="UTF-8">

                    <title>${taReportTitle.textContent}</title>

                    ${tailwind}

                    <style>

                        body { font-family: 'Inter', sans-serif; padding: 2rem; background-color: #f1f5f9; }

                        .report-section { display: block !important; }

                        /* Re-add prose styles for lists */

                        .prose ul { list-style-position: outside; padding-left: 1.25rem; }

                        .prose li { margin-top: 0.5em; }

                        .prose h5 { font-size: 1.1em; font-weight: 600; margin-top: 1em; }

                        .report-section ul {

                            list-style-type: disc;

                            padding-left: 1.5rem;

                            margin-top: 0.5rem;

                        }

                    </style>

                </head>

                <body class="bg-slate-100">

                    <main class="space-y-8 report-section">${reportHtml}</main>

                </body>

                </html>

            `;

            

            const blob = new Blob([fullHtml], { type: 'text/html' });

            const href = URL.createObjectURL(blob);

            const a = document.createElement('a');

            a.href = href;

            a.download = `${taReportTitle.textContent}.html`;

            document.body.appendChild(a);

            a.click();

            document.body.removeChild(a);

            URL.revokeObjectURL(href);

        }


        function downloadWordReport() {

            const title = translations[currentLang].ta_report_main_title;

            const tailwind = document.querySelector('script[src*="tailwindcss.com"]').outerHTML;

            

            // Helper function to get outerHTML of a section by its title's data-lang-key

            const getSectionHtml = (key) => {

                const el = document.querySelector(`[data-lang-key="${key}"]`);

                if (el) {

                    // Find the closest parent container of the section

                    return el.closest('.p-6').outerHTML;

                }

                return '';

            };


            const fullHtml = `

                <!DOCTYPE html>

                <html lang="${currentLang}">

                <head>

                    <meta charset="utf-8">

                    <!-- Specific settings for Word -->

                    <meta name=ProgId content=Word.Document>

                    <meta name=Generator content="Microsoft Word 15">

                    <meta name=Originator content="Microsoft Word 15">

                    <style>

                        body { font-family: 'Inter', sans-serif; padding: 2rem; background-color: #ffffff; }

                        .report-section { display: block !important; }

                        /* Page break avoidance */

                        .p-6, .border-l-4, .border, .border-l-4, .bg-white, .shadow { page-break-inside: avoid; }

                        

                        /* Re-add prose styles */

                        .prose ul { list-style-position: outside; padding-left: 1.25rem; margin-top: 0.5em; }

                        .prose li { margin-top: 0.5em; }

                        .prose h5 { font-size: 1.1em; font-weight: 600; margin-top: 1em; }

                        .report-section ul {

                            list-style-type: disc;

                            padding-left: 1.5rem;

                            margin-top: 0.5rem;

                        }

                    </style>

                    ${tailwind}

                </head>

                <body class="bg-white">

                    <!-- Add the new title -->

                    <h1 class="text-2xl font-bold text-slate-800">${title}</h1>

                    

                    <!-- Add all sections *except* status -->

                    <main class="space-y-8 report-section">

                        ${getSectionHtml('ta_report_basic_title')}

                        ${getSectionHtml('ta_report_distribution_title')}

                        ${getSectionHtml('ta_report_low_score_title')}

                        ${getSectionHtml('ta_report_kp_mastery_title')}

                        ${getSectionHtml('ta_report_summary_title')}

                        ${getSectionHtml('ta_report_stratification_title')}

                        ${getSectionHtml('ta_report_case_study_title')}

                        ${getSectionHtml('ta_report_case_summary_title')}

                    </main>

                </body>

                </html>

            `;

            

            // Add BOM for UTF-8

            const blob = new Blob(['\ufeff', fullHtml], {

                type: 'application/msword;charset=utf-8'

            });

            const href = URL.createObjectURL(blob);

            const a = document.createElement('a');

            a.href = href;

            a.download = `${title}.doc`; // Use .doc extension

            document.body.appendChild(a);

            a.click();

            document.body.removeChild(a);

            URL.revokeObjectURL(href);

        }

        

        // --- EVENT LISTENERS ---

        loginForm.addEventListener('submit', handleLogin);

        navLinks.forEach(link => link.addEventListener('click', handleNavigation));

        addScoreForm.addEventListener('submit', (e) => { e.preventDefault(); addScore(studentNameInput.value, studentScoreInput.value); addScoreForm.reset(); studentNameInput.focus(); });

        scoresTableBody.addEventListener('click', (e) => { const removeBtn = e.target.closest('.remove-btn'); if (removeBtn) { removeScore(removeBtn.dataset.index); } });

        fileUpload.addEventListener('change', (e) => handleFile(e.target.files[0]));

        dropArea.addEventListener('dragover', (e) => { e.preventDefault(); dropArea.classList.add('border-blue-500', 'bg-blue-50'); });

        dropArea.addEventListener('dragleave', () => dropArea.classList.remove('border-blue-500', 'bg-blue-50'));

        dropArea.addEventListener('drop', (e) => { e.preventDefault(); dropArea.classList.remove('border-blue-500', 'bg-blue-50'); handleFile(e.dataTransfer.files[0]); });

        analyzeBtn.addEventListener('click', () => { generateReport(); reportContainer.scrollIntoView({ behavior: 'smooth' }); });

        clearBtn.addEventListener('click', () => showConfirmationModal('confirm_clear_title', 'confirm_clear_text', () => { studentData = []; currentClassName = ''; updateTable(); reportContainer.classList.add('hidden'); welcomeMessage.classList.remove('hidden'); }));

        exportBtn.addEventListener('click', exportToCsv);

        langEnBtn.addEventListener('click', () => setLanguage('en'));

        langZhBtn.addEventListener('click', () => setLanguage('zh'));

        modalCancelBtn.addEventListener('click', hideConfirmationModal);

        

        setupFileUploader(overallGradesDropArea, overallGradesUpload, overallGradesFeedback, (file) => { overallGradesFile = file; });

        setupFileUploader(itemScoresDropArea, itemScoresUpload, itemScoresFeedback, (file) => { itemScoresFile = file; });

        setupFileUploader(paperPdfDropArea, paperPdfUpload, paperPdfFeedback, (file) => { paperPdfFile = file; });

        startAnalysisBtn.addEventListener('click', startFullAnalysis);

        downloadReportBtn.addEventListener('click', downloadReport);

        downloadWordBtn.addEventListener('click', downloadWordReport);


        updateTable();

        setLanguage('zh');

        document.querySelector('.nav-link[href="#grade-entry"]').classList.add('active');

        dashboardSection.classList.remove('hidden');

        gradeEntrySection.classList.remove('hidden');

    });

    </script>

</body>

</html>