<?php
declare(strict_types=1);
require __DIR__ . '/lib.php';
require_once __DIR__ . '/golden-onboarding-admin-lib.php';
start_admin_session();
$cfg = config();

function h($v): string { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); }
function go(string $message = '', string $page = ''): void {
    if ($message !== '') $_SESSION['flash'] = $message;
    $allowed = ['accounts','golden','golden_monitor','tribonacci','mean_reversion','logs','overview','expiry','downloads'];
    header('Location: /admin' . (in_array($page, $allowed, true) ? '?page=' . rawurlencode($page) : '')); exit;
}
function audit(string $account, string $action, string $detail = ''): void {
    db()->prepare('INSERT INTO audit_logs(account,action,detail,ip,created_at) VALUES(?,?,?,?,?)')
        ->execute([$account, $action, $detail, client_ip(), gmdate('c')]);
}
function expiry_meta(string $expires, int $now): array {
    if ($expires === '') return ['class'=>'','label'=>'永久授权','level'=>'permanent'];
    $end = strtotime($expires.' 23:59:59 UTC');
    if ($end < $now) return ['class'=>'expiry-expired','label'=>'已到期','level'=>'expired'];
    $days = max(1, (int)ceil(($end-$now)/86400));
    if ($expires === gmdate('Y-m-d',$now)) return ['class'=>'expiry-urgent','label'=>'今日到期','level'=>'urgent'];
    if ($days <= 3) return ['class'=>'expiry-urgent','label'=>'紧急 · 剩'.$days.'天','level'=>'urgent'];
    if ($days <= 7) return ['class'=>'expiry-warning','label'=>'即将到期 · 剩'.$days.'天','level'=>'warning'];
    return ['class'=>'','label'=>'剩余 '.$days.' 天','level'=>'normal'];
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'logout') {
    if (!empty($_SESSION['admin_ok'])) verify_csrf();
    $_SESSION = [];
    session_destroy();
    header('Location: /admin'); exit;
}
$loginError = '';
if (empty($_SESSION['admin_ok']) && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $userOk = hash_equals((string)$cfg['admin_user'], (string)($_POST['username'] ?? ''));
    $passOk = password_verify((string)($_POST['password'] ?? ''), (string)$cfg['admin_password_hash']);
    if ($userOk && $passOk) {
        session_regenerate_id(true); $_SESSION['admin_ok'] = true; go();
    }
    usleep(500000); $loginError = '账号或密码不正确';
}
if (empty($_SESSION['admin_ok'])) {
?><!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>登录 · HS License</title>
<style>
*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;font:14px/1.5 Inter,"PingFang SC","Microsoft YaHei",sans-serif;color:#eaf8f1;background:#06110c;overflow:hidden}.orb{position:fixed;border-radius:50%;filter:blur(90px);opacity:.24}.o1{width:520px;height:520px;background:#13a968;left:-180px;top:-220px}.o2{width:420px;height:420px;background:#087c52;right:-140px;bottom:-190px}.login{position:relative;width:min(420px,calc(100vw - 32px));padding:38px;background:rgba(8,25,18,.9);border:1px solid #245b43;border-radius:24px;box-shadow:0 30px 100px #000b;backdrop-filter:blur(22px)}.brand{display:flex;align-items:center;gap:13px;margin-bottom:34px}.logo{width:44px;height:44px;display:grid;place-items:center;border-radius:13px;color:#042116;background:linear-gradient(135deg,#67e7ad,#20a86f);box-shadow:0 10px 28px #20b67838;font-weight:800;font-size:18px}.brand b{font-size:18px}.brand small{display:block;color:#78a590;margin-top:2px}.login h1{font-size:28px;margin:0 0 8px}.sub{color:#7eaa95;margin:0 0 28px}.field{margin:15px 0}.field label{display:block;color:#a9caba;font-size:12px;margin:0 0 7px}.field input{width:100%;height:48px;border:1px solid #28543f;border-radius:11px;background:#06130d;color:#fff;padding:0 14px;outline:none;transition:.2s}.field input:focus{border-color:#36c98a;box-shadow:0 0 0 3px #24b97b20}.submit{width:100%;height:49px;margin-top:10px;border:0;border-radius:11px;color:#042116;font-weight:800;background:linear-gradient(135deg,#65e3aa,#24aa72);cursor:pointer;box-shadow:0 12px 28px #1e9e6938}.error{padding:10px 12px;border-radius:9px;background:#ff53651c;color:#ff8994;margin-bottom:15px}.secure{text-align:center;color:#5f9279;font-size:12px;margin-top:22px}
</style></head><body><div class="orb o1"></div><div class="orb o2"></div><form class="login" method="post"><div class="brand"><div class="logo">HS</div><div><b>HS License</b><small>EA 授权管理平台</small></div></div><h1>欢迎回来</h1><p class="sub">登录以管理交易账户授权与设备状态</p><?php if($loginError):?><div class="error"><?=h($loginError)?></div><?php endif;?><div class="field"><label>管理员账号</label><input name="username" autocomplete="username" placeholder="请输入账号" required autofocus></div><div class="field"><label>登录密码</label><input name="password" type="password" autocomplete="current-password" placeholder="请输入密码" required></div><button class="submit">安全登录</button><div class="secure">HTTPS 加密连接 · 登录会话受保护</div></form></body></html><?php exit; }

if (isset($_GET['export']) && $_GET['export'] === 'csv') {
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="ea-licenses-'.gmdate('Ymd-His').'.csv"');
    echo "\xEF\xBB\xBF";
    $out=fopen('php://output','w');
    fputcsv($out,['MT5账户','客户名称','平台账户名称','交易服务器','IP地址','余额','净值','当日盈亏','保存计划','状态','到期日期','备注','最后在线','终端公司','更新时间']);
    foreach(db()->query('SELECT * FROM licenses ORDER BY updated_at DESC') as $r)
        fputcsv($out,[$r['account'],$r['customer_name'],$r['account_name'],$r['trade_server'],$r['last_ip'],$r['balance'],$r['equity'],$r['profit'],$r['plan_direction'],$r['status'],$r['expires_at'],$r['note'],$r['last_seen_at'],$r['terminal_company'],$r['updated_at']]);
    fclose($out); exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    verify_csrf();
    $action = (string)($_POST['action'] ?? 'save');
    if ((string)($_POST['product'] ?? '') === 'mean_reversion') {
        $account = trim((string)($_POST['account'] ?? ''));
        if (!valid_account($account)) exit('Invalid account');
        if ($action === 'delete') {
            db()->prepare('DELETE FROM mean_reversion_licenses WHERE account=?')->execute([$account]);
            audit($account, 'mean_reversion_delete', '删除均值回归对冲授权');
            go('均值回归对冲账户 '.$account.' 已删除');
        }
        $status = (string)($_POST['status'] ?? 'pending');
        $expires = trim((string)($_POST['expires_at'] ?? ''));
        $customerName = mb_substr(trim((string)($_POST['customer_name'] ?? '')),0,80);
        $note = mb_substr(trim((string)($_POST['note'] ?? '')),0,500);
        if (!in_array($status,['active','close_only','disabled','pending'],true)) exit('Invalid status');
        if ($expires !== '' && !valid_iso_date($expires)) exit('Invalid date');
        db()->prepare('INSERT INTO mean_reversion_licenses(account,customer_name,status,expires_at,note,updated_at) VALUES(?,?,?,?,?,?) ON CONFLICT(account) DO UPDATE SET customer_name=excluded.customer_name,status=excluded.status,expires_at=excluded.expires_at,note=excluded.note,updated_at=excluded.updated_at')
            ->execute([$account,$customerName,$status,$expires?:null,$note,gmdate('c')]);
        audit($account, 'mean_reversion_save', '弘盛量化均值回归对冲 / '.$status.' / '.($expires?:'永久'));
        go('均值回归对冲账户 '.$account.' 已保存');
    }
    if ((string)($_POST['product'] ?? '') === 'tribonacci_god') {
        $account = trim((string)($_POST['account'] ?? ''));
        if (!valid_account($account)) exit('Invalid account');
        if ($action === 'delete') {
            db()->prepare('DELETE FROM tribonacci_god_licenses WHERE account=?')->execute([$account]);
            audit($account, 'tribonacci_delete', '删除三阶战神授权');
            go('三阶战神账户 '.$account.' 已删除');
        }
        $status = (string)($_POST['status'] ?? 'pending');
        $expires = trim((string)($_POST['expires_at'] ?? ''));
        $customerName = mb_substr(trim((string)($_POST['customer_name'] ?? '')),0,80);
        $note = mb_substr(trim((string)($_POST['note'] ?? '')),0,500);
        if (!in_array($status,['active','close_only','disabled','pending'],true)) exit('Invalid status');
        if ($expires !== '' && !valid_iso_date($expires)) exit('Invalid date');
        db()->prepare('INSERT INTO tribonacci_god_licenses(account,customer_name,status,expires_at,note,updated_at) VALUES(?,?,?,?,?,?) ON CONFLICT(account) DO UPDATE SET customer_name=excluded.customer_name,status=excluded.status,expires_at=excluded.expires_at,note=excluded.note,updated_at=excluded.updated_at')
            ->execute([$account,$customerName,$status,$expires?:null,$note,gmdate('c')]);
        audit($account, 'tribonacci_save', '弘盛量化三阶战神 / '.$status.' / '.($expires?:'永久'));
        go('三阶战神账户 '.$account.' 已保存');
    }
    if ((string)($_POST['product'] ?? '') === 'golden_god') {
        if ($action === 'risk_toggle_enforcement') {
            $enabled = (string)($_POST['enabled'] ?? '') === '1' ? 1 : 0;
            $agreement = current_risk_agreement(db(), 'golden_god');
            if (!is_array($agreement) || empty($agreement['_integrity_ok'])) {
                go('协议正文完整性未通过，不能变更强制门禁', 'golden');
            }
            $update = db()->prepare('UPDATE risk_agreement_versions SET enforcement_enabled=? WHERE product_id=? AND version=? AND is_current=1');
            $update->execute([$enabled, 'golden_god', (string)$agreement['version']]);
            if ($update->rowCount() !== 1) go('风险协议强制门禁更新失败，请重试', 'golden');
            audit('SYSTEM', 'golden_risk_enforcement', ($enabled ? '启用' : '暂停') . ' / ' . $agreement['version']);
            go('黄金战神风险协议强制门禁已'.($enabled ? '启用；未签客户将禁止首单和加仓' : '暂停；签署记录仍完整保留'), 'golden');
        }
        $account = trim((string)($_POST['account'] ?? ''));
        if (!valid_account($account)) exit('Invalid account');
        if ($action === 'contract8_unbind_device') {
            $bindingId = trim((string)($_POST['binding_id'] ?? ''));
            $reason = mb_substr(trim((string)($_POST['unbind_reason'] ?? '管理员解绑设备')), 0, 200);
            try {
                if (!golden_contract8_unbind_device(db(), $account, $bindingId, (string)$cfg['admin_user'], $reason)) {
                    go('设备不存在、已解绑或状态已变化', 'golden');
                }
                go('账户 '.$account.' 的设备已解绑；旧设备不能自动重新占席', 'golden');
            } catch (Throwable $error) {
                error_log('Contract 8 device unbind failed');
                go('设备解绑失败，请重试', 'golden');
            }
        }
        if ($action === 'risk_create_code') {
            $pdo = db();
            $lookup = $pdo->prepare('SELECT * FROM golden_god_licenses WHERE account=?');
            $lookup->execute([$account]);
            $license = $lookup->fetch();
            if (!is_array($license)) go('没有找到该黄金战神授权账户', 'golden');
            $expired = !empty($license['expires_at']) && strtotime((string)$license['expires_at'].' 23:59:59 UTC') < time();
            if ((string)$license['status'] !== 'active' || $expired) {
                go('请先把账户设为正常使用且有效期未过期，再生成签署启动码', 'golden');
            }
            $riskState = golden_risk_state($pdo, $license);
            if (($riskState['status'] ?? '') === 'accepted') {
                go('该账户已经签署当前协议；如需重新确认，请先点击“要求重新签署”', 'golden');
            }
            try {
                $startCode = create_risk_start_code($pdo, $license, $riskState, (string)$cfg['admin_user'], client_ip(), 86400);
                $_SESSION['risk_start_code'] = [
                    'account' => $account,
                    'code' => (string)$startCode['code'],
                    'portal_url' => (string)$startCode['portal_url'],
                    'expires_at' => (string)$startCode['expires_at'],
                ];
                audit($account, 'golden_risk_code', '生成一次性签署启动码 / 到期=' . $startCode['expires_at']);
                go('账户 '.$account.' 的一次性启动码已生成；原码只显示本次，请立即安全发送给客户', 'golden');
            } catch (Throwable $error) {
                error_log('admin risk start code failed: ' . $error->getMessage());
                go('签署启动码生成失败，请检查协议完整性和服务器密钥', 'golden');
            }
        }
        if ($action === 'risk_create_invite') {
            $pdo = db();
            $lookup = $pdo->prepare('SELECT * FROM golden_god_licenses WHERE account=?');
            $lookup->execute([$account]);
            $license = $lookup->fetch();
            if (!is_array($license)) go('没有找到该黄金战神授权账户', 'golden');
            $expired = !empty($license['expires_at']) && strtotime((string)$license['expires_at'].' 23:59:59 UTC') < time();
            if ((string)$license['status'] !== 'active' || $expired) {
                go('请先把账户设为正常使用且有效期未过期，再生成签署邀请', 'golden');
            }
            $riskState = golden_risk_state($pdo, $license);
            if (($riskState['status'] ?? '') === 'accepted') {
                go('该账户已经签署当前协议；如需重新确认，请先点击“要求重新签署”', 'golden');
            }
            try {
                $invite = create_risk_signing_invite($pdo, $license, $riskState, (string)$cfg['admin_user'], client_ip(), 86400);
                $_SESSION['risk_invite'] = [
                    'account' => $account,
                    'url' => (string)$invite['url'],
                    'expires_at' => (string)$invite['expires_at'],
                ];
                audit($account, 'golden_risk_invite', '生成一次性签署邀请 / 到期=' . $invite['expires_at']);
                go('账户 '.$account.' 的一次性签署链接已生成，请复制后通过可信渠道发给客户', 'golden');
            } catch (Throwable $error) {
                error_log('admin risk invite failed: ' . $error->getMessage());
                go('签署邀请生成失败，请检查协议完整性和服务器密钥', 'golden');
            }
        }
        if ($action === 'risk_require_resign') {
            $pdo = db();
            $transactionStarted = false;
            try {
                db_begin_immediate($pdo);
                $transactionStarted = true;
                $lookup = $pdo->prepare('SELECT * FROM golden_god_licenses WHERE account=?');
                $lookup->execute([$account]);
                $license = $lookup->fetch();
                if (!is_array($license)) throw new RuntimeException('License not found');
                $subjectId = ensure_golden_risk_subject($pdo, $license);
                if ($subjectId === '') throw new RuntimeException('Risk subject unavailable');
                $now = gmdate('c');
                $reason = mb_substr(trim((string)($_POST['risk_reason'] ?? '管理员要求重新签署')), 0, 200);
                if ($reason === '') $reason = '管理员要求重新签署';
                $revoke = $pdo->prepare('UPDATE risk_acceptances SET revoked_at=?,revoked_ip=?,revoked_reason=?
                    WHERE account=? AND product_id=? AND license_subject_id=? AND revoked_at IS NULL');
                $revoke->execute([$now, client_ip(), $reason, $account, 'golden_god', $subjectId]);
                $revokeTerminalAcceptances = $pdo->prepare('UPDATE risk_terminal_acceptances SET revoked_at=?,revoked_ip=?,revoked_reason=?
                    WHERE account=? AND product_id=? AND license_subject_id=? AND revoked_at IS NULL');
                $revokeTerminalAcceptances->execute([$now, client_ip(), $reason, $account, 'golden_god', $subjectId]);
                $revokeTerminalChallenges = $pdo->prepare('UPDATE risk_terminal_challenges SET revoked_at=?,revoked_reason=?
                    WHERE account=? AND product_id=? AND license_subject_id=? AND used_at IS NULL AND revoked_at IS NULL');
                $revokeTerminalChallenges->execute([$now, $reason, $account, 'golden_god', $subjectId]);
                $revokeInvites = $pdo->prepare('UPDATE risk_signing_invites SET revoked_at=?,revoked_reason=?
                    WHERE account=? AND product_id=? AND license_subject_id=? AND used_at IS NULL AND revoked_at IS NULL');
                $revokeSessions = $pdo->prepare('UPDATE risk_signing_sessions SET revoked_at=?,revoked_reason=?
                    WHERE account=? AND product_id=? AND license_subject_id=? AND used_at IS NULL AND revoked_at IS NULL');
                $revokeSessions->execute([$now, $reason, $account, 'golden_god', $subjectId]);
                $revokeInvites->execute([$now, $reason, $account, 'golden_god', $subjectId]);
                $generation = max(1, (int)($license['risk_generation'] ?? 1)) + 1;
                $update = $pdo->prepare('UPDATE golden_god_licenses SET risk_generation=?,updated_at=? WHERE account=? AND risk_subject_id=?');
                $update->execute([$generation, $now, $account, $subjectId]);
                if ($update->rowCount() !== 1) throw new RuntimeException('License changed while requiring re-sign');
                audit($account, 'golden_risk_resign', $reason . ' / generation=' . $generation);
                db_commit_immediate($pdo);
                $transactionStarted = false;
                go('账户 '.$account.' 已要求重新签署风险协议；旧电子确认挑战、启动码、链接和浏览器会话立即失效', 'golden');
            } catch (Throwable $error) {
                if ($transactionStarted) {
                    try { db_rollback_immediate($pdo); }
                    catch (Throwable $rollbackError) { error_log('admin risk resign rollback failed: ' . $rollbackError->getMessage()); }
                }
                error_log('admin risk resign failed: ' . $error->getMessage());
                go('风险协议重签操作失败，请重试', 'golden');
            }
        }
        if ($action === 'delete') {
            $deletePdo = db();
            $transactionStarted = false;
            try {
                db_begin_immediate($deletePdo);
                $transactionStarted = true;
                $deletedAt = gmdate('c');
                $deletePdo->prepare('UPDATE risk_signing_sessions SET revoked_at=?,revoked_reason=? WHERE account=? AND product_id=? AND used_at IS NULL AND revoked_at IS NULL')
                    ->execute([$deletedAt, '对应授权记录已删除', $account, 'golden_god']);
                $deletePdo->prepare('UPDATE risk_signing_invites SET revoked_at=?,revoked_reason=? WHERE account=? AND product_id=? AND used_at IS NULL AND revoked_at IS NULL')
                    ->execute([$deletedAt, '对应授权记录已删除', $account, 'golden_god']);
                $deletePdo->prepare('UPDATE risk_terminal_challenges SET revoked_at=?,revoked_reason=? WHERE account=? AND product_id=? AND used_at IS NULL AND revoked_at IS NULL')
                    ->execute([$deletedAt, '对应授权记录已删除', $account, 'golden_god']);
                $deletePdo->prepare('UPDATE risk_terminal_acceptances SET revoked_at=?,revoked_ip=?,revoked_reason=? WHERE account=? AND product_id=? AND revoked_at IS NULL')
                    ->execute([$deletedAt, client_ip(), '对应授权记录已删除', $account, 'golden_god']);
                $deletePdo->prepare('UPDATE risk_acceptances SET revoked_at=?,revoked_ip=?,revoked_reason=? WHERE account=? AND product_id=? AND revoked_at IS NULL')
                    ->execute([$deletedAt, client_ip(), '对应授权记录已删除', $account, 'golden_god']);
                $deletePdo->prepare('DELETE FROM golden_god_licenses WHERE account=?')->execute([$account]);
                audit($account, 'golden_delete', '删除黄金战神授权；历史风险签署证据保留');
                db_commit_immediate($deletePdo);
                $transactionStarted = false;
                go('黄金战神账户 '.$account.' 已删除；历史签署凭证未删除', 'golden');
            } catch (Throwable $error) {
                if ($transactionStarted) {
                    try { db_rollback_immediate($deletePdo); }
                    catch (Throwable $rollbackError) { error_log('golden license delete rollback failed: ' . $rollbackError->getMessage()); }
                }
                error_log('golden license delete failed: ' . $error->getMessage());
                go('黄金战神授权删除失败，请重试', 'golden');
            }
        }
        try {
            if($action==='golden_authorization'&&($_POST['intent']??'')==='authorization') {
                $permission=$_POST;unset($permission['product'],$permission['intent']);
                $permission['action']=$permission['authorization_action']??'';unset($permission['authorization_action']);
                if(!in_array($permission['action'],['formal','permanent','extend','extra_trial','restrict','approve_switch'],true))throw new InvalidArgumentException('请选择明确的授权操作');
                golden_oa_action(db(),$permission,(string)$cfg['admin_user']);
                go('账户 '.$account.' 的授权操作已完成；两页共用同一权限，不需要再次批准','golden');
            }
            if($action!=='golden_metadata_save')throw new InvalidArgumentException('请刷新页面，分别使用资料保存或授权操作');
            $saved=golden_oa_save_metadata(db(),$_POST,(string)$cfg['admin_user']);
            audit($account,'golden_metadata_save','metadata_only；仅保存客户资料'.($saved['policy_changed']?'及设备策略':'').'；授权期限与试用窗口不变');
            go('账户 '.$account.' 资料已保存；授权状态及有效期未修改','golden');
        }catch(InvalidArgumentException | DomainException $error){go($error->getMessage(),'golden');}
        catch(Throwable $error){error_log('Golden administrator update failed: '.get_class($error));go('操作未完成，请刷新核对当前状态后再处理','golden');}
    }
    if ($action === 'delete') {
        $account = trim((string)($_POST['account'] ?? ''));
        if (!valid_account($account)) exit('Invalid account');
        db()->prepare('DELETE FROM licenses WHERE account=?')->execute([$account]);
        audit($account, 'delete', '删除授权账户'); go('账户 '.$account.' 已删除');
    }
    if ($action === 'toggle_status') {
        $account = trim((string)($_POST['account'] ?? ''));
        $targetStatus = (string)($_POST['target_status'] ?? '');
        if (!valid_account($account)) exit('Invalid account');
        if (!in_array($targetStatus,['active','disabled'],true)) exit('Invalid status');
        db()->prepare('UPDATE licenses SET status=?,updated_at=? WHERE account=?')->execute([$targetStatus,gmdate('c'),$account]);
        audit($account, 'update', $targetStatus==='active'?'管理员快捷启用账户':'管理员快捷禁用账户');
        go('账户 '.$account.($targetStatus==='active'?' 已启用':' 已禁用'));
    }
    if ($action === 'bulk_status') {
        $accounts = array_values(array_filter((array)($_POST['accounts'] ?? []), fn($a)=>valid_account((string)$a)));
        $status = (string)($_POST['bulk_status'] ?? '');
        if (!$accounts || !in_array($status,['active','close_only','disabled'],true)) go('请选择账户和目标状态');
        $stmt = db()->prepare('UPDATE licenses SET status=?,updated_at=? WHERE account=?');
        foreach ($accounts as $a) { $stmt->execute([$status,gmdate('c'),$a]); audit((string)$a,'bulk_status',$status); }
        go('已批量更新 '.count($accounts).' 个账户');
    }
    $account = trim((string)($_POST['account'] ?? ''));
    $status = (string)($_POST['status'] ?? 'active');
    $expires = trim((string)($_POST['expires_at'] ?? ''));
    $customerName = mb_substr(trim((string)($_POST['customer_name'] ?? '')),0,80);
    $note = mb_substr(trim((string)($_POST['note'] ?? '')),0,500);
    if (!valid_account($account)) exit('Invalid account');
    if (!in_array($status,['active','close_only','disabled','pending'],true)) exit('Invalid status');
    if ($expires !== '' && !valid_iso_date($expires)) exit('Invalid date');
    $exists = db()->prepare('SELECT 1 FROM licenses WHERE account=?'); $exists->execute([$account]);
    db()->prepare('INSERT INTO licenses(account,customer_name,status,expires_at,note,updated_at) VALUES(?,?,?,?,?,?) ON CONFLICT(account) DO UPDATE SET customer_name=excluded.customer_name,status=excluded.status,expires_at=excluded.expires_at,note=excluded.note,updated_at=excluded.updated_at')
        ->execute([$account,$customerName,$status,$expires?:null,$note,gmdate('c')]);
    audit($account,$exists->fetchColumn()?'update':'create',($customerName?:'未备注客户').' / '.$status.' / '.($expires?:'永久'));
    go('账户 '.$account.' 已保存');
}

$pdo = db();
$allRows = $pdo->query('SELECT * FROM licenses ORDER BY updated_at DESC')->fetchAll();
$goldenRows = $pdo->query('SELECT * FROM golden_god_licenses ORDER BY updated_at DESC')->fetchAll();
$goldenDevices = golden_contract8_device_list($pdo);
$goldenRiskRows = [];
$goldenRiskStats = ['accepted'=>0,'attention'=>0];
$goldenAuthorizationAt=time();
foreach ($goldenRows as &$goldenRow) {
    if(($_GET['page']??'')==='golden')$goldenRow['authorization']=golden_oa_authorization_view($pdo,$goldenRow,$goldenAuthorizationAt);
    $riskState = golden_risk_state($pdo, $goldenRow);
    $goldenRiskRows[(string)$goldenRow['account']] = $riskState;
    if ($riskState['status'] === 'accepted') $goldenRiskStats['accepted']++;
    elseif ($riskState['required']) $goldenRiskStats['attention']++;
}
unset($goldenRow);
$currentGoldenRiskAgreement = current_risk_agreement($pdo, 'golden_god');
$goldenRiskEnforced = !empty($currentGoldenRiskAgreement['enforcement_enabled']);
$goldenRiskToggleConfirm = $goldenRiskEnforced
    ? '确定暂停风险协议强制门禁吗？签署记录会保留。'
    : '确定现在启用强制门禁吗？所有尚未签署当前协议的黄金战神账户将立即禁止首单和加仓，已有仓位仍继续管理和平仓。';
$meanReversionRows = $pdo->query('SELECT * FROM mean_reversion_licenses ORDER BY updated_at DESC')->fetchAll();
$tribonacciRows = $pdo->query('SELECT * FROM tribonacci_god_licenses ORDER BY updated_at DESC')->fetchAll();
$downloadCounts=[];
foreach($pdo->query('SELECT product_id,download_count FROM download_stats') as $downloadRow)$downloadCounts[$downloadRow['product_id']]=(int)$downloadRow['download_count'];
$downloadCatalog=require __DIR__.'/download_catalog.php';
foreach ($allRows as &$displayRow) {
    $rawIp = (string)($displayRow['last_ip'] ?? '');
    $location = (string)($displayRow['ip_location'] ?? '');
    $displayRow['terminal_company'] = $rawIp !== '' ? $rawIp : 'IP 待上报';
    $displayRow['last_ip'] = $location !== '' ? $location : '地区待检测';
}
unset($displayRow);
$logs = $pdo->query('SELECT * FROM audit_logs ORDER BY id DESC LIMIT 30')->fetchAll();
$now = time(); $stats=['total'=>count($allRows),'active'=>0,'close_only'=>0,'disabled'=>0,'pending'=>0,'online'=>0,'warning'=>0,'expired'=>0,'permanent'=>0];
foreach($allRows as $r){
    if($r['status']==='active')$stats['active']++;
    if($r['status']==='close_only')$stats['close_only']++;
    if($r['status']==='disabled')$stats['disabled']++;
    if($r['status']==='pending')$stats['pending']++;
    if(!empty($r['last_seen_at']) && strtotime($r['last_seen_at']) >= $now-600)$stats['online']++;
    if(!empty($r['expires_at']) && strtotime($r['expires_at'].' 23:59:59 UTC') <= $now+7*86400)$stats['warning']++;
    if(!empty($r['expires_at']) && strtotime($r['expires_at'].' 23:59:59 UTC') < $now)$stats['expired']++;
    if(empty($r['expires_at']))$stats['permanent']++;
}
$riskRows=array_values(array_filter($allRows,fn($r)=>$r['status']!=='active'||(!empty($r['expires_at'])&&strtotime($r['expires_at'].' 23:59:59 UTC')<=$now+7*86400)));
$flash=(string)($_SESSION['flash']??''); unset($_SESSION['flash']);
$riskInvite=is_array($_SESSION['risk_invite']??null)?$_SESSION['risk_invite']:null; unset($_SESSION['risk_invite']);
$riskStartCode=is_array($_SESSION['risk_start_code']??null)?$_SESSION['risk_start_code']:null; unset($_SESSION['risk_start_code']);
$csrf=csrf_token();
$labels=['active'=>'正常使用','close_only'=>'仅管理仓位','disabled'=>'已停用','pending'=>'等待授权'];
$logLabels=['create'=>'新增账户','update'=>'更新账户','delete'=>'删除账户','bulk_status'=>'批量调整','auto_register'=>'自动登记','auto_register_pending'=>'自动登记待授权','auto_register_active'=>'自动登记并启用','golden_save'=>'黄金战神授权变更','golden_delete'=>'删除黄金战神授权','golden_risk_signed'=>'黄金战神风险协议已签署','golden_risk_code'=>'黄金战神签署启动码','golden_risk_invite'=>'黄金战神签署邀请','golden_risk_resign'=>'黄金战神要求重新签署','golden_risk_enforcement'=>'黄金战神协议强制门禁','golden_share_create'=>'黄金战神绩效二维码已生成','golden_share_rotate'=>'黄金战神绩效二维码已重新生成','golden_share_disable'=>'黄金战神绩效报告已停用','golden_share_enable'=>'黄金战神绩效报告已重新启用','mean_reversion_save'=>'均值回归对冲授权变更','mean_reversion_delete'=>'删除均值回归对冲授权','tribonacci_save'=>'三阶战神授权变更','tribonacci_delete'=>'删除三阶战神授权'];
$page=(string)($_GET['page']??'accounts');
if(!in_array($page,['accounts','golden','golden_monitor','tribonacci','mean_reversion','logs','overview','expiry','downloads'],true))$page='accounts';
?><!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>HS License · 授权控制台</title>
<style>
:root{--bg:#080c15;--side:#0c111d;--card:#111827;--card2:#151e30;--line:#222d42;--text:#e9eef8;--muted:#8490a8;--primary:#6c6ff7;--green:#22c98e;--orange:#f3a83b;--red:#f26070}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 Inter,"PingFang SC","Microsoft YaHei",sans-serif}button,input,select,textarea{font:inherit}.app{min-height:100vh;display:grid;grid-template-columns:238px 1fr}.sidebar{position:fixed;inset:0 auto 0 0;width:238px;padding:25px 18px;background:var(--side);border-right:1px solid #ffffff0a}.brand{display:flex;align-items:center;gap:12px;padding:0 9px 28px}.logo{width:39px;height:39px;display:grid;place-items:center;border-radius:12px;background:linear-gradient(135deg,#7477ff,#4945d4);font-weight:800;box-shadow:0 10px 25px #5250db44}.brand b{font-size:16px}.brand small{display:block;color:#68748d}.nav-label{font-size:10px;color:#59657b;letter-spacing:1.5px;margin:15px 12px 8px}.nav a{display:flex;gap:12px;align-items:center;color:#8895ad;text-decoration:none;padding:11px 13px;border-radius:10px;margin:3px 0}.nav a.active{color:#fff;background:#6b6ef719}.nav a.active:before{content:"";width:3px;height:20px;background:#7477ff;border-radius:5px;margin-left:-13px}.dot{width:8px;height:8px;border-radius:50%;background:#4d596e}.nav .active .dot{background:#777aff;box-shadow:0 0 12px #777aff}.server{position:absolute;left:18px;right:18px;bottom:25px;padding:13px;background:#121a29;border:1px solid #202b40;border-radius:12px}.server span{display:flex;align-items:center;gap:8px}.pulse{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 0 5px #22c98e16}.server small{display:block;color:#68758d;margin-top:5px}.main{grid-column:2;padding:0 32px 40px;min-width:0}.topbar{height:82px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #ffffff0b}.topbar h1{font-size:20px;margin:0}.topbar p{margin:3px 0 0;color:var(--muted);font-size:12px}.top-actions{display:flex;align-items:center;gap:12px}.logout{color:#9aa6bc;text-decoration:none;padding:8px 12px;border:1px solid var(--line);border-radius:9px}.btn{border:0;border-radius:10px;padding:10px 15px;color:#fff;cursor:pointer;font-weight:650}.btn-primary{background:linear-gradient(135deg,#7477ff,#5752de);box-shadow:0 8px 22px #5a55df32}.btn-ghost{background:#182237;color:#b6c0d2}.btn-danger{background:#f260701b;color:#ff8490}.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin:24px 0}.stat{position:relative;padding:20px;background:linear-gradient(145deg,#121a2a,#0f1624);border:1px solid #202a3e;border-radius:15px;overflow:hidden}.stat:after{content:"";position:absolute;width:90px;height:90px;border-radius:50%;right:-35px;top:-35px;background:var(--c);opacity:.08}.stat .label{color:#8490a6;font-size:12px}.stat strong{display:block;font-size:28px;margin:7px 0 4px}.stat small{color:#65718a}.stat i{position:absolute;right:18px;bottom:18px;width:9px;height:9px;border-radius:50%;background:var(--c);box-shadow:0 0 13px var(--c)}.panel{background:var(--card);border:1px solid #202a3d;border-radius:16px;margin:18px 0;box-shadow:0 12px 40px #0002}.panel-head{display:flex;align-items:center;justify-content:space-between;padding:18px 20px;border-bottom:1px solid #202a3d}.panel-head h2{font-size:15px;margin:0}.panel-head small{color:var(--muted)}.tools{display:flex;gap:10px;align-items:center}.search{position:relative}.search input{width:240px;height:38px;padding:0 13px 0 34px;background:#0c1320;color:#dfe5f0;border:1px solid #263148;border-radius:9px;outline:none}.search:before{content:"⌕";position:absolute;left:12px;top:7px;color:#748098;font-size:18px}.filter{height:38px;background:#0c1320;color:#bbc5d7;border:1px solid #263148;border-radius:9px;padding:0 10px}.table-wrap{overflow:auto}table{width:100%;border-collapse:collapse;min-width:960px}th{padding:12px 16px;color:#66738a;font-size:11px;text-transform:uppercase;letter-spacing:.6px;text-align:left;background:#0e1624}td{padding:14px 16px;border-top:1px solid #1c2638;color:#cbd3e1}tbody tr{transition:.15s}tbody tr:hover{background:#151f30}.account{font-weight:700;color:#f2f5fb}.company{font-size:11px;color:#66738a;margin-top:2px;max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.badge{display:inline-flex;align-items:center;gap:6px;padding:5px 9px;border-radius:20px;font-size:11px;font-weight:650}.badge:before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}.active{background:#1bc88b16;color:#35d99e}.close_only{background:#f1a73b17;color:#f5b552}.disabled{background:#ef607019;color:#ff7886}.online{color:#39d59e}.offline{color:#637087}.expiry-warn{color:#ffb454}.note{max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#8f9bb0}.actions{display:flex;gap:6px}.icon-btn{padding:7px 9px;border:1px solid #28344a;border-radius:8px;background:#151e2e;color:#aab5c8;cursor:pointer}.icon-btn:hover{border-color:#6065e8;color:#fff}.bulk{display:none;align-items:center;gap:10px;padding:12px 20px;background:#121c2d;border-top:1px solid var(--line)}.bulk.show{display:flex}.bulk span{color:#8e9ab0;margin-right:auto}.empty{text-align:center!important;padding:48px!important;color:#68758d}.lower{display:grid;grid-template-columns:1.3fr .7fr;gap:18px}.log{display:flex;gap:12px;padding:13px 20px;border-top:1px solid #1d2739}.log-mark{width:30px;height:30px;display:grid;place-items:center;border-radius:9px;background:#6b6ef716;color:#8d90ff}.log-main{flex:1}.log-main b{font-size:12px}.log-main p{margin:2px 0;color:#7d899f;font-size:11px}.log time{font-size:10px;color:#59667c}.guide{padding:20px}.guide-item{display:flex;gap:12px;margin:15px 0}.guide-num{flex:0 0 27px;height:27px;display:grid;place-items:center;border-radius:8px;background:#6b6ef71c;color:#9799ff;font-weight:700}.guide b{font-size:12px}.guide p{margin:2px 0;color:#738097;font-size:11px}.modal-back{display:none;position:fixed;inset:0;background:#02050bb8;backdrop-filter:blur(6px);z-index:20;place-items:center}.modal-back.show{display:grid}.modal{width:min(500px,calc(100vw - 28px));background:#111a29;border:1px solid #29344a;border-radius:18px;box-shadow:0 30px 100px #000b}.modal-head{display:flex;justify-content:space-between;padding:20px 22px;border-bottom:1px solid var(--line)}.modal-head h3{margin:0;font-size:16px}.close{background:none;border:0;color:#7f8ba2;font-size:22px;cursor:pointer}.modal-body{padding:20px 22px}.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}.field{margin-bottom:14px}.field label{display:block;color:#8c98ae;font-size:11px;margin-bottom:6px}.field input,.field select,.field textarea{width:100%;background:#0b1220;color:#e5eaf3;border:1px solid #29354a;border-radius:9px;padding:10px;outline:none}.field textarea{height:82px;resize:vertical}.modal-foot{display:flex;justify-content:flex-end;gap:10px;padding:0 22px 21px}.toast{position:fixed;right:24px;top:22px;z-index:30;background:#152338;border:1px solid #2d3e59;color:#dce5f2;padding:11px 16px;border-radius:10px;box-shadow:0 12px 40px #0007;animation:hide .4s 3.2s forwards}@keyframes hide{to{opacity:0;transform:translateY(-8px);visibility:hidden}}@media(max-width:1050px){.stats{grid-template-columns:1fr 1fr}.lower{grid-template-columns:1fr}.sidebar{width:76px}.app{grid-template-columns:76px 1fr}.brand div:not(.logo),.nav span,.nav-label,.server{display:none}.nav a{justify-content:center}.main{grid-column:2;padding:0 18px 35px}}@media(max-width:650px){.app{display:block}.sidebar{display:none}.main{padding:0 12px 30px}.topbar{height:70px}.topbar p{display:none}.stats{gap:10px;margin:15px 0}.stat{padding:15px}.stat strong{font-size:22px}.panel-head{align-items:flex-start;gap:12px;flex-direction:column}.tools{width:100%}.search{flex:1}.search input{width:100%}.top-actions .logout{display:none}.grid2{grid-template-columns:1fr}}
</style><style>
/* Black Gold Edition */
:root{--bg:#050505;--side:#090909;--card:#0e0e0f;--card2:#15130f;--line:#2c281f;--text:#f4f0e7;--muted:#948b79;--primary:#d6b66d;--green:#55c995;--orange:#d9a94f;--red:#db6871}body{background:radial-gradient(circle at 70% -20%,#33270d55,transparent 35%),#050505}.sidebar{background:linear-gradient(180deg,#0c0b09,#070707);border-right-color:#332d20}.logo{color:#171109;background:linear-gradient(135deg,#f5dda0,#a87b28);box-shadow:0 10px 28px #c49a3b2b}.brand small,.topbar p{color:#8d8067}.nav a.active{background:linear-gradient(90deg,#d2ad5823,#d2ad5808);color:#f2ddb0}.nav a.active:before,.nav .active .dot{background:#d8b763;box-shadow:0 0 14px #d8b763}.server{background:#100f0c;border-color:#39301e}.topbar{border-color:#2c261a}.btn-primary{color:#171109;background:linear-gradient(135deg,#efd78f,#b88931);box-shadow:0 10px 28px #bf963a2b}.logout,.icon-btn,.filter,.search input{border-color:#3a3222;background:#0a0a0a}.panel,.stat{background:linear-gradient(145deg,#11110f,#0b0b0b);border-color:#2f291e}.panel{box-shadow:0 18px 60px #0008}.panel-head,td,.bulk{border-color:#2b271e}th{background:#0a0907;color:#897d67}.stat:before{content:"";position:absolute;left:0;top:0;right:0;height:1px;background:linear-gradient(90deg,transparent,#d8b65e77,transparent)}tbody tr:hover{background:#17140d}.badge.active{background:#c8aa5420;color:#e3c46f}.badge.close_only{background:#b97c251c;color:#d8a34d}.badge.disabled{background:#9e39391c;color:#df7070}.modal{background:#11100d;border-color:#403723}.modal-head{border-color:#332c1e}.field input,.field select,.field textarea{background:#090909;border-color:#3a3222}.guide-num,.log-mark{background:#d1ad5320;color:#e5c570}.toast{background:#17130b;border-color:#5a4722}.insights{display:grid;grid-template-columns:1.05fr 1.15fr .9fr;gap:16px;margin:0 0 18px}.insight{min-height:176px;padding:20px;background:linear-gradient(145deg,#11100d,#0b0b0a);border:1px solid #30291d;border-radius:15px}.insight h3{margin:0;font-size:14px}.insight .hint{color:#807663;font-size:11px;margin-top:3px}.dist{display:flex;align-items:center;gap:22px;margin-top:18px}.donut{width:88px;height:88px;border-radius:50%;display:grid;place-items:center;background:conic-gradient(#d7b45f 0 var(--a),#9a6a28 var(--a) var(--b),#49332d var(--b));position:relative}.donut:after{content:"";position:absolute;inset:13px;border-radius:50%;background:#0e0d0a}.donut b{z-index:1;font-size:20px}.legend{display:grid;gap:7px;font-size:11px;color:#9a907d}.legend span:before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--lc);margin-right:7px}.risk-list{margin-top:13px}.risk{display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-top:1px solid #282319}.risk:first-child{border:0}.risk b{font-size:12px}.risk small{display:block;color:#766d5d}.risk em{font-style:normal;font-size:10px;color:#d7ae55}.api-box{margin-top:15px;padding:11px;border:1px solid #332c20;border-radius:9px;background:#080807;color:#a8997c;font:11px ui-monospace,monospace;word-break:break-all}.api-status{display:flex;align-items:center;gap:8px;color:#57c891;margin-top:15px}.copy{width:100%;margin-top:13px}.quick-days{display:flex;gap:7px;flex-wrap:wrap;margin-top:8px}.quick-days button{border:1px solid #3c3321;background:#14120d;color:#cdb576;padding:6px 9px;border-radius:7px;cursor:pointer}.quick-days button:hover{border-color:#d1ad55}.gold-link{color:#d9b961;text-decoration:none;border:1px solid #413720;padding:9px 12px;border-radius:9px}@media(max-width:1150px){.insights{grid-template-columns:1fr 1fr}.insight:last-child{grid-column:1/-1}}@media(max-width:650px){.insights{grid-template-columns:1fr}.insight:last-child{grid-column:auto}.gold-link{display:none}}
</style><style>
/* Emerald Green Edition */
:root{--bg:#07100c;--side:#09140f;--card:#0d1913;--card2:#112219;--line:#203b2d;--text:#e9f7ef;--muted:#80a18f;--primary:#2fc383;--green:#38d894;--orange:#d5a64b;--red:#df6b75}body{background:radial-gradient(circle at 72% -18%,#14583b66,transparent 38%),#07100c}.sidebar{background:linear-gradient(180deg,#0b1912,#07100c);border-right-color:#1d3d2d}.logo{color:#032116;background:linear-gradient(135deg,#6be6ae,#21a86f);box-shadow:0 10px 28px #1da66c38}.brand small,.topbar p{color:#75a18b}.nav a.active{background:linear-gradient(90deg,#2fc38325,#2fc38308);color:#c9f3dd}.nav a.active:before,.nav .active .dot{background:#42d594;box-shadow:0 0 14px #42d594}.server{background:#0b1d14;border-color:#22503a}.topbar{border-color:#1b382a}.btn-primary{color:#032117;background:linear-gradient(135deg,#62dda7,#27ad74);box-shadow:0 10px 28px #1f9f6938}.btn-ghost{background:#10261b;color:#b7dec9;border:1px solid #28513e}.logout,.icon-btn,.filter,.search input,.gold-link{border-color:#28513e;background:#091710}.gold-link{color:#64d9a5}.panel,.stat{background:linear-gradient(145deg,#0f1d16,#0a1510);border-color:#213d30}.panel{box-shadow:0 18px 60px #0007}.panel-head,td,.bulk{border-color:#1d382b}th{background:#09140f;color:#779786}.stat:before{background:linear-gradient(90deg,transparent,#3fd49277,transparent)}tbody tr:hover{background:#10241a}.badge.active{background:#2fc3831c;color:#54dda0}.badge.close_only{background:#d19b3618;color:#ddb05a}.badge.disabled{background:#b74a551c;color:#e37780}.modal{background:#0c1b13;border-color:#28513e}.modal-head{border-color:#204431}.field input,.field select,.field textarea{background:#07130d;border-color:#28513e}.guide-num,.log-mark{background:#2fc3831d;color:#66dda9}.toast{background:#0f291c;border-color:#2c664a}.insight{background:linear-gradient(145deg,#0f1d16,#09140f);border-color:#213d30}.donut{background:conic-gradient(#43d194 0 var(--a),#d1a23d var(--a) var(--b),#61383a var(--b))}.donut:after{background:#0d1913}.legend{color:#8cac9a}.risk{border-color:#1d382b}.risk em{color:#58d69d}.api-box{border-color:#28513e;background:#07130d;color:#8eb9a2}.quick-days button{border-color:#28513e;background:#0d2117;color:#81d8b0}.quick-days button:hover{border-color:#44ce91}.accounts-head .tools{flex-wrap:wrap}.add-account{white-space:nowrap}@media(max-width:760px){.accounts-head .tools{display:grid;grid-template-columns:1fr 120px;width:100%}.accounts-head .add-account{grid-column:1/-1;width:100%}.accounts-head .search input{width:100%}}
</style><style>
.customer-name{font-weight:750;color:#eaf8f1;margin-bottom:2px}.customer-name.empty-name{color:#668576;font-weight:600}.account-sub{font:11px ui-monospace,SFMono-Regular,Consolas,monospace;color:#8eb3a0}.ip-value{font:12px ui-monospace,SFMono-Regular,Consolas,monospace;color:#b7d8c7}.plan-badge{display:inline-flex;min-width:48px;justify-content:center;padding:4px 8px;border-radius:7px;font-size:10px;font-weight:850;letter-spacing:.4px}.plan-buy{color:#bdf6d8;background:#258d5d;border:1px solid #41c985}.plan-sell{color:#ffd5d5;background:#9d383e;border:1px solid #dc656b}.plan-none{color:#8ba496;background:#18271f;border:1px solid #2b4336}.expiry-box{display:flex;flex-direction:column;align-items:flex-start;gap:5px;white-space:nowrap}.expiry-date{font:12px ui-monospace,SFMono-Regular,Consolas,monospace}.expiry-pill{display:inline-flex;align-items:center;gap:5px;padding:3px 8px;border-radius:999px;font-size:10px;font-weight:800}.expiry-pill:before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}.expiry-warning .expiry-pill{color:#f2c35f;background:#d5a64b20;border:1px solid #d5a64b55}.expiry-urgent .expiry-pill{color:#ff938c;background:#df6b7524;border:1px solid #df6b7566;animation:expiryPulse 1.8s ease-in-out infinite}.expiry-expired .expiry-pill{color:#ff6f78;background:#a82d382d;border:1px solid #df536066}.expiry-warning td:first-child{box-shadow:inset 3px 0 #d5a64b}.expiry-urgent td:first-child,.expiry-expired td:first-child{box-shadow:inset 3px 0 #df6b75}.expiry-warning{background:#d5a64b08}.expiry-urgent,.expiry-expired{background:#df6b750c}@keyframes expiryPulse{50%{box-shadow:0 0 0 5px #df6b7515}}.risk-expired em,.risk-urgent em{color:#ff7d84!important;font-weight:800}.risk-warning em{color:#edbd59!important;font-weight:800}#accountRows td:nth-child(3){display:flex;flex-direction:column;align-items:flex-start}#accountRows td:nth-child(3)>div:first-child{order:1;font-weight:700;color:#e8eef7}#accountRows td:nth-child(3)>.company{order:2;font:12px ui-monospace,SFMono-Regular,Consolas,monospace;color:#b7d8c7}#accountRows td:nth-child(3)>.ip-value{order:3;margin-top:3px;color:#789688}.plan-summary{min-width:142px}.plan-top{display:flex;align-items:center;gap:7px;white-space:nowrap}.plan-param{font-size:11px;color:#c9d5ce;font-weight:650}.plan-time{margin-top:6px;color:#85998e;font:11px ui-monospace,SFMono-Regular,Consolas,monospace;white-space:nowrap}.plan-empty{color:#718078;font-size:11px}
.version-badge{display:inline-flex;margin-top:6px;padding:3px 7px;border:1px solid #315344;border-radius:6px;background:#14261e;color:#75cfa3;font:10px ui-monospace,SFMono-Regular,Consolas,monospace;font-weight:750}.version-unknown{color:#78867e;border-color:#2a3831;background:#141b17}.actions{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:92px}.action-tools{display:flex;gap:5px}.action-edit,.action-delete{display:grid;place-items:center;width:34px;height:28px;padding:0;border-radius:7px}.action-edit{color:#a9d7c0;border-color:#315744;background:#10231a}.action-delete{color:#9ca9a2;border-color:#34453c;background:#101b16}.action-edit:hover{color:#d9f5e7!important;border-color:#4f8d6f!important;background:#173326}.action-delete:hover{color:#ff8b91!important;border-color:#85454b!important;background:#32191c}.action-edit svg,.action-delete svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.icon-btn{white-space:nowrap;font-weight:650}.status-switch{display:inline-flex;align-items:center;gap:7px;height:28px;padding:2px 9px 2px 3px;border:0;background:transparent!important;cursor:pointer;font-size:11px;font-weight:750;white-space:nowrap}.status-switch i{position:relative;width:42px;height:24px;border-radius:999px;background:#26322c;box-shadow:inset 0 0 0 1px #405149;transition:.18s}.status-switch i:after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:#d7dfdb;box-shadow:0 2px 5px #0008;transition:.18s}.status-switch:hover i{box-shadow:inset 0 0 0 1px #668174}.status-switch.switch-on{color:#55d99d}.status-switch.switch-on i{background:#20ad70;box-shadow:inset 0 0 0 1px #55d99d66,0 0 12px #20ad7026}.status-switch.switch-on i:after{left:21px;background:#f3fff9}.status-switch.switch-off{color:#84928b}.status-switch.switch-off i{background:#2b3430}.status-switch:focus-visible{outline:2px solid #4acb91;outline-offset:2px}
.account-summary{display:flex;align-items:center;justify-content:flex-end;gap:0;color:#7f9c8d}.account-summary span{padding:0 18px;border-left:1px solid #254233;font-size:12px}.account-summary span:first-child{border-left:0}.account-summary b{margin-left:7px;color:#58d99f;font-size:18px}.account-summary .warn b{color:#e4b453}.accounts-compact{margin-top:16px}.page-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin-top:22px}.page-grid .insight{min-height:210px}.risk-panel{margin-top:20px}.risk-row{display:grid;grid-template-columns:minmax(180px,1.2fr) minmax(150px,1fr) 130px 120px;align-items:center;gap:14px;padding:15px 20px;border-top:1px solid #1d382b}.risk-row:first-child{border-top:0}.risk-row .identity b{display:block}.risk-row .identity small,.risk-row .server-meta{color:#789485;font-size:11px}.risk-row .expires{font:12px ui-monospace,SFMono-Regular,Consolas,monospace}.download-list{display:grid;gap:12px;padding:18px}.download-row{display:grid;grid-template-columns:220px minmax(260px,1fr) 132px 120px;align-items:center;gap:12px;padding:15px 16px;border:1px solid #234433;border-radius:12px;background:#0a1710}.download-name{display:flex;align-items:center;gap:12px}.download-icon{width:36px;height:36px;display:grid;place-items:center;border-radius:10px;background:#2fc3831c;color:#57d99f;font-size:18px}.download-name b{display:block}.download-name small{color:#759383}.download-url{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#8cb29e;font:11px ui-monospace,SFMono-Regular,Consolas,monospace}.download-row .btn{width:100%;text-align:center;text-decoration:none}.service-strip{display:flex;align-items:center;gap:12px;margin:20px 0 0;padding:14px 17px;border:1px solid #234433;border-radius:12px;background:#0b1912;color:#84a795}.service-strip .api-status{margin:0}.service-strip code{flex:1;color:#87b49d;font-size:11px}@media(max-width:1050px){.account-summary span{padding:0 10px}.download-row{grid-template-columns:1fr 1fr}.download-url{grid-column:1/-1;grid-row:2}.risk-row{grid-template-columns:1fr 1fr}}@media(max-width:700px){.account-summary{display:none}.page-grid{grid-template-columns:1fr}.download-row{grid-template-columns:1fr}.download-url{grid-column:auto;grid-row:auto}.risk-row{grid-template-columns:1fr}}
</style></head><body>
<?php if($flash):?><div class="toast">✓ <?=h($flash)?></div><?php endif;?>
<div class="app"><aside class="sidebar"><div class="brand"><div class="logo">HS</div><div><b>HS License</b><small>Control Center</small></div></div><div class="nav-label">工作台</div><nav class="nav"><a class="<?=$page==='accounts'?'active':''?>" href="?page=accounts"><i class="dot"></i><span>对冲授权账户</span></a><a class="<?=$page==='golden'?'active':''?>" href="?page=golden"><i class="dot"></i><span>黄金战神授权</span></a><a href="/golden-onboarding-admin.php"><i class="dot"></i><span>黄金战神客户服务</span></a><a class="<?=$page==='golden_monitor'?'active':''?>" href="?page=golden_monitor"><i class="dot"></i><span>黄金战神监控</span></a><a class="<?=$page==='tribonacci'?'active':''?>" href="?page=tribonacci"><i class="dot"></i><span>三阶战神授权</span></a><a class="<?=$page==='mean_reversion'?'active':''?>" href="?page=mean_reversion"><i class="dot"></i><span>均值回归对冲授权</span></a><a class="<?=$page==='logs'?'active':''?>" href="?page=logs"><i class="dot"></i><span>操作记录</span></a><a class="<?=$page==='overview'?'active':''?>" href="?page=overview"><i class="dot"></i><span>数据概览</span></a><a class="<?=$page==='expiry'?'active':''?>" href="?page=expiry"><i class="dot"></i><span>到期管理</span></a><a class="<?=$page==='downloads'?'active':''?>" href="?page=downloads"><i class="dot"></i><span>下载管理</span></a><a href="/api/license" target="_blank"><i class="dot"></i><span>API 状态</span></a></nav><div class="server"><span><i class="pulse"></i><b>服务运行正常</b></span><small>API · HTTPS · HMAC</small></div></aside>
<?php $pageMeta=['accounts'=>['对冲授权账户','管理对冲 EA 客户授权与连接状态'],'golden'=>['黄金战神授权','单独管理黄金战神客户、授权状态和有效期'],'golden_monitor'=>['黄金战神实时交易指挥台','只读查看客户 EA 自报的持仓与盈利曲线（非券商审计记录）'],'tribonacci'=>['三阶战神授权','独立管理弘盛量化三阶战神 V1.0 账户授权'],'mean_reversion'=>['均值回归对冲授权','独立管理弘盛量化均值回归对冲 V1.0 账户授权'],'logs'=>['操作记录','查看后台授权变更与安全审计记录'],'overview'=>['数据概览','查看账户授权状态与在线运行概况'],'expiry'=>['到期管理','集中处理即将到期、已到期和异常授权'],'downloads'=>['下载管理','统一管理 EA 安装包及公网下载链接']];$meta=$pageMeta[$page];?>
  <main class="main<?=$page==='golden_monitor'?' main-monitor':''?>"><header class="topbar"><div><h1><?=h($meta[0])?></h1><p><?=h($meta[1])?></p></div><div class="top-actions"><?php if($page==='accounts'):?><div class="account-summary"><span>全部<b><?=$stats['total']?></b></span><span>已启用<b><?=$stats['active']?></b></span><span class="warn">待授权<b><?=$stats['pending']?></b></span><span>已禁用<b><?=$stats['disabled']?></b></span></div><a class="gold-link" href="?export=csv">导出 CSV</a><?php endif;?><form method="post" style="margin:0"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="action" value="logout"><button class="logout" type="submit">退出</button></form></div></header>
<?php if($page==='accounts'):?>
<section class="panel accounts-compact" id="accounts"><div class="panel-head accounts-head"><div><h2>授权账户</h2><small>EA 首次挂载自动登记，管理员确认后启用</small></div><div class="tools"><div class="search"><input id="search" placeholder="搜索账户、名称、服务器或IP"></div><select class="filter" id="statusFilter"><option value="">全部状态</option><option value="pending">等待授权</option><option value="active">正常使用</option><option value="close_only">仅管理</option><option value="disabled">已停用</option></select><button class="btn btn-primary add-account" type="button" onclick="openModal()">＋ 手动新增</button></div></div>
<form method="post" id="bulkForm"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="action" value="bulk_status"><div class="table-wrap"><table><thead><tr><th><input type="checkbox" id="checkAll"></th><th>客户名称 / 账户</th><th>服务器 / IP / 地区</th><th>余额 / 净值</th><th>当日盈亏</th><th>保存计划</th><th>授权状态</th><th>授权期限</th><th>连接</th><th>操作</th></tr></thead><tbody id="accountRows">
<?php foreach($allRows as $r):$seen=!empty($r['last_seen_at'])?strtotime($r['last_seen_at']):0;$isOnline=$seen>=$now-600;$exp=(string)($r['expires_at']??'');$em=expiry_meta($exp,$now);$plan=(string)($r['plan_direction']??'none');$planClass=$plan==='buy'?'plan-buy':($plan==='sell'?'plan-sell':'plan-none');$planText=$plan==='buy'?'多单':($plan==='sell'?'空单':'未保存');?><tr class="<?=h($em['class'])?>" data-status="<?=h($r['status'])?>" data-search="<?=h(strtolower(($r['customer_name']??'').' '.$r['account'].' '.$r['account_name'].' '.$r['trade_server'].' '.$r['note'].' '.$r['last_ip'].' '.$r['terminal_company'].' '.$planText))?>"><td><input class="row-check" type="checkbox" name="accounts[]" value="<?=h($r['account'])?>"></td><td><div class="customer-name <?=$r['customer_name']?'':'empty-name'?>"><?=h($r['customer_name']?:'未备注客户')?></div><div class="account-sub"><?=h($r['account'])?></div><div class="company"><?=h($r['account_name']?:'未提供平台名称')?></div></td><td><div><?=h($r['trade_server']?:'—')?></div><div class="ip-value"><?=h($r['last_ip']?:'IP —')?></div><div class="company"><?=h($r['terminal_company']?:'—')?></div></td><td><div><?=number_format((float)$r['balance'],2)?></div><div class="company">净值 <?=number_format((float)$r['equity'],2)?></div></td><td class="<?=((float)$r['profit']>=0)?'online':'expiry-warn'?>"><?=number_format((float)$r['profit'],2)?></td><td><span class="plan-badge <?=h($planClass)?>"><?=h($planText)?></span></td><td><span class="badge <?=h($r['status'])?>"><?=h($labels[$r['status']]??$r['status'])?></span></td><td><div class="expiry-box"><div class="expiry-date"><?=$exp?h($exp):'永久授权'?></div><span class="expiry-pill"><?=h($em['label'])?></span></div></td><td><span class="<?=$isOnline?'online':'offline'?>">● <?=$isOnline?'在线':'离线'?></span><div class="company"><?=$seen?h(gmdate('m-d H:i',$seen)).' UTC':'从未连接'?></div></td><td><div class="actions"><button type="button" class="icon-btn" onclick='editAccount(<?=json_encode($r,JSON_UNESCAPED_UNICODE|JSON_HEX_APOS|JSON_HEX_QUOT)?>)'>编辑资料</button><button type="button" class="icon-btn" onclick="removeAccount('<?=h($r['account'])?>')">删除</button></div></td></tr><?php endforeach;?>
<?php if(!$allRows):?><tr><td colspan="10" class="empty">等待客户挂载 EA，账户将自动出现在这里</td></tr><?php endif;?></tbody></table></div><div class="bulk" id="bulkBar"><span>已选择 <b id="selectedCount">0</b> 个账户</span><select class="filter" name="bulk_status" required><option value="">批量设置状态</option><option value="active">允许使用</option><option value="close_only">设为仅管理</option><option value="disabled">停用</option></select><button class="btn btn-primary">应用</button></div></form></section>
<?php elseif($page==='golden'):?><div class="risk-policy-card"><div><b>黄金战神风险协议门禁</b><small>当前版本 <?=h($currentGoldenRiskAgreement['version']??'未发布')?> · <?=$goldenRiskEnforced?'已强制确认':'灰度准备中（尚未强制）'?> · EA内实名电子确认；网页手写签署作为备用方式</small></div><div class="risk-policy-stats"><span>已确认 <b><?=$goldenRiskStats['accepted']?></b></span><span class="warn">待处理 <b><?=$goldenRiskStats['attention']?></b></span><form method="post" onsubmit="return confirm(<?=h(json_encode($goldenRiskToggleConfirm,JSON_UNESCAPED_UNICODE|JSON_HEX_APOS|JSON_HEX_QUOT))?>)"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="golden_god"><input type="hidden" name="action" value="risk_toggle_enforcement"><input type="hidden" name="enabled" value="<?=$goldenRiskEnforced?'0':'1'?>"><button class="btn <?=$goldenRiskEnforced?'btn-ghost':'btn-primary'?>" type="submit"><?=$goldenRiskEnforced?'暂停强制':'启用强制门禁'?></button></form></div></div><?php if(is_array($riskStartCode)):?><div class="risk-invite-card risk-code-card"><div><b>账户 <?=h($riskStartCode['account']??'')?> 的一次性签署启动码</b><small>原码仅显示这一次，24小时内可兑换一次；客户打开固定门户后输入账号和启动码。</small><code id="freshRiskCode"><?=h($riskStartCode['code']??'')?></code><small>固定门户：<?=h($riskStartCode['portal_url']??risk_portal_url())?></small></div><button class="btn btn-primary" type="button" onclick="copyText('freshRiskCode',this)">复制启动码</button></div><?php endif;?><?php if(is_array($riskInvite)):?><div class="risk-invite-card"><div><b>账户 <?=h($riskInvite['account']??'')?> 的一次性签署长链接</b><small>24小时内有效；客户签署一次后立即失效。请通过可信渠道单独发送。</small><code id="freshRiskInvite"><?=h($riskInvite['url']??'')?></code></div><button class="btn btn-primary" type="button" onclick="copyText('freshRiskInvite',this)">复制一次性链接</button></div><?php endif;?><section class="panel" id="golden-accounts"><div class="panel-head"><div><h2>黄金战神授权账户</h2><small>模拟自动30天、实盘批准一次；本页与客户服务共用有效授权，邮箱和本次挂载确认另行核验。</small></div><button class="btn btn-primary" type="button" onclick="document.getElementById('goldenAdd').classList.toggle('show')">＋ 登记客户资料</button></div>
<form method="post" id="goldenAdd" style="display:none;padding:18px 20px;gap:10px;grid-template-columns:1.1fr 1.1fr 1fr 1fr 1.5fr auto" class="golden-form"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="golden_god"><input type="hidden" name="action" value="golden_metadata_save"><input type="hidden" name="intent" value="metadata"><input name="account" placeholder="MT5账户" required><input name="customer_name" placeholder="客户名称" maxlength="80"><input name="max_devices" type="number" min="1" max="32" value="1" title="最大设备数"><input name="min_ea_version" value="<?=h(GOLDEN_CONTRACT8_DEFAULT_MIN_EA_VERSION)?>" placeholder="最低EA版本"><input name="note" placeholder="备注（不改变授权）" maxlength="500"><button class="btn btn-primary">登记资料</button><small style="grid-column:1/-1">这里只登记客户资料，不授予永久或正式授权。模拟账户完成有效挂载后自动获得固定30天；实盘请登记后使用“授权操作”明确批准一次。</small></form>
<?php $goldenRiskLabels=['accepted'=>'已签署','unsigned'=>'未签署','outdated'=>'协议已更新','revoked'=>'需要重签','unavailable'=>'证据或协议异常','not_required'=>'暂不强制']; ?>
<div class="table-wrap"><table><thead><tr><th>账户 / 客户</th><th>服务器</th><th>EA版本</th><th>有效授权 / 来源</th><th>风险协议 / 本次挂载</th><th>V8策略</th><th>有效期</th><th>最后在线</th><th>操作</th></tr></thead><tbody><?php foreach($goldenRows as $g):$ga=$g['authorization'];$gs=!empty($g['last_seen_at'])?strtotime($g['last_seen_at']):0;$gr=$goldenRiskRows[(string)$g['account']]??['status'=>'unavailable','version'=>'','signed_at'=>'','signer_name'=>'','receipt_no'=>''];$grClass=$gr['status']==='accepted'?'active':($gr['status']==='not_required'?'close_only':'disabled');$grMethod=($gr['acceptance_method']??'')==='terminal_electronic'?'EA内实名电子确认':((($gr['acceptance_method']??'')==='web_handwritten')?'网页手写签署':'');?><tr><td><b><?=h($g['account'])?></b><div class="company"><?=h($g['customer_name']?:'未备注客户')?></div></td><td><?=h($g['trade_server']?:'—')?><div class="company"><?=h($g['account_name']?:'—')?></div></td><td><?=h($g['ea_version']?:'—')?></td><td><span class="badge <?=h($ga['status'])?>"><?=h($ga['status_label'])?></span><div class="company"><?=h($ga['source_label'])?></div><div class="company"><?=h($ga['message'])?></div></td><td><div class="company"><?=h($ga['mount_label'])?></div><span class="badge <?=h($grClass)?>"><?=h($goldenRiskLabels[$gr['status']]??$gr['status'])?></span><div class="company">当前 <?=h($gr['version']?:'无协议')?><?php if(!empty($gr['signed_at'])):?> · 已确认 <?=h($gr['accepted_version']??$gr['version'])?> · <?=h($grMethod)?> · <?=h($gr['signer_name'])?> · <?=h(gmdate('m-d H:i',strtotime($gr['signed_at'])))?> UTC<?php endif;?></div></td><td><b><?=h((string)($g['max_devices']??1))?> 台</b><div class="company">最低 <?=h($g['min_ea_version']??GOLDEN_CONTRACT8_DEFAULT_MIN_EA_VERSION)?> · epoch <?=h((string)($g['license_epoch']??1))?><?=$g['force_upgrade']?' · 强制升级':''?></div><div class="company">主体 <?=h(substr((string)($g['license_subject_id']??''),0,8))?>…</div></td><td><?=h($ga['expiry_label'])?><div class="company"><?=h($ga['source']==='automatic_trial'?'自动30天 · 无需人工批准':'资料编辑不会更改此期限')?></div></td><td><?=$gs?h(gmdate('m-d H:i',$gs)).' UTC':'从未连接'?></td><td><div class="risk-actions"><button class="icon-btn" type="button" onclick='editGolden(<?=json_encode($g,JSON_UNESCAPED_UNICODE|JSON_HEX_APOS|JSON_HEX_QUOT)?>)'>资料 / 设备</button><button class="icon-btn" type="button" onclick='authorizeGolden(<?=json_encode(['account'=>$g['account'],'authorization'=>$ga],JSON_UNESCAPED_UNICODE|JSON_HEX_APOS|JSON_HEX_QUOT)?>)'>授权操作</button><?php if(($gr['status']??'')!=='accepted'):?><form method="post" class="inline-form"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="golden_god"><input type="hidden" name="action" value="risk_create_code"><input type="hidden" name="account" value="<?=h($g['account'])?>"><button class="icon-btn risk-code" type="submit">生成启动码</button></form><form method="post" class="inline-form"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="golden_god"><input type="hidden" name="action" value="risk_create_invite"><input type="hidden" name="account" value="<?=h($g['account'])?>"><button class="icon-btn risk-copy" type="submit">生成备用长链接</button></form><?php endif;?><?php if(!empty($gr['receipt_no'])):?><a class="icon-btn proof-link" href="/risk-proof?receipt=<?=rawurlencode($gr['receipt_no'])?>" target="_blank" rel="noopener">查看确认凭证</a><?php endif;?><button class="icon-btn risk-reset" type="button" onclick="requireGoldenRiskResign('<?=h($g['account'])?>')">要求重新确认</button></div></td></tr><?php endforeach;?><?php if(!$goldenRows):?><tr><td colspan="9" class="empty">等待客户挂载黄金战神 EA，账户会自动出现在此板块</td></tr><?php endif;?></tbody></table></div></section>
<section class="panel"><div class="panel-head"><div><h2>Contract 8 设备席位</h2><small>数据库仅保存外部 pepper 的设备哈希；解绑会递增授权 epoch，已解绑设备不能自动重新占席。</small></div></div><div class="table-wrap"><table><thead><tr><th>账户</th><th>绑定编号</th><th>服务器</th><th>状态</th><th>版本</th><th>首次绑定</th><th>最后在线</th><th>操作</th></tr></thead><tbody><?php foreach($goldenDevices as $device):?><tr><td class="account"><?=h($device['account'])?></td><td><code><?=h($device['binding_id'])?></code></td><td><?=h($device['trade_server'])?></td><td><span class="badge <?=$device['state']==='active'?'active':'disabled'?>"><?=h($device['state']==='active'?'已绑定':'已解绑')?></span><?php if(!empty($device['unbound_reason'])):?><div class="company"><?=h($device['unbound_reason'])?></div><?php endif;?></td><td><?=h($device['last_ea_version']?:'—')?> / <?=h((string)$device['last_mt5_build'])?></td><td><?=h($device['first_bound_at'])?></td><td><?=h($device['last_seen_at'])?></td><td><?php if($device['state']==='active'):?><form method="post" class="inline-form" onsubmit="return confirm('确定解绑此设备吗？旧设备将不能自动重新占席。')"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="golden_god"><input type="hidden" name="action" value="contract8_unbind_device"><input type="hidden" name="account" value="<?=h($device['account'])?>"><input type="hidden" name="binding_id" value="<?=h($device['binding_id'])?>"><input type="hidden" name="unbind_reason" value="管理员从授权后台解绑"><button class="icon-btn risk-reset" type="submit">解绑设备</button></form><?php else:?>—<?php endif;?></td></tr><?php endforeach;?><?php if(!$goldenDevices):?><tr><td colspan="8" class="empty">尚无 Contract 8 设备绑定</td></tr><?php endif;?></tbody></table></div></section>
<div class="modal-back" id="goldenModal"><form class="modal" method="post"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="golden_god"><input type="hidden" name="action" value="golden_metadata_save"><input type="hidden" name="intent" value="metadata"><div class="modal-head"><h3>编辑客户资料 / 设备</h3><button type="button" class="close" onclick="goldenModal.classList.remove('show')">×</button></div><div class="modal-body"><div class="grid2"><div class="field"><label>MT5账户</label><input id="gAccount" name="account" readonly></div><div class="field"><label>客户名称</label><input id="gName" name="customer_name"></div></div><p id="gAuthorizationInfo"></p><p class="company">此处仅保存资料和设备策略，不修改授权、不重置30天试用。修改有效期请关闭后选择“授权操作”。</p><div class="grid2"><div class="field"><label>V8 最大设备数（1-32）</label><input id="gMaxDevices" name="max_devices" type="number" min="1" max="32" value="1"></div><div class="field"><label>V8 最低 EA 版本</label><input id="gMinEaVersion" name="min_ea_version" value="<?=h(GOLDEN_CONTRACT8_DEFAULT_MIN_EA_VERSION)?>"></div></div><div class="field"><label><input id="gForceUpgrade" name="force_upgrade" type="checkbox" value="1" style="width:auto;margin-right:8px">强制升级（不满足策略时仅允许管理和平仓）</label></div><div class="field"><label>备注</label><textarea id="gNote" name="note"></textarea></div></div><div class="modal-foot"><button type="button" class="btn btn-ghost" onclick="goldenModal.classList.remove('show')">取消</button><button class="btn btn-primary">仅保存资料 / 设备</button></div></form></div>
<div class="modal-back" id="goldenAuthModal"><form class="modal" method="post" onsubmit="return confirm('确定执行这一次授权操作吗？它不会重置原自动试用窗口；无需到另一个页面再批准。')"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="golden_god"><input type="hidden" name="action" value="golden_authorization"><input type="hidden" name="intent" value="authorization"><div class="modal-head"><h3>黄金战神授权操作</h3><button type="button" class="close" onclick="goldenAuthModal.classList.remove('show')">×</button></div><div class="modal-body"><div class="field"><label>MT5账户</label><input id="gAuthAccount" name="account" readonly></div><p id="gAuthCurrent"></p><p class="company">模拟自动30天无需批准；实盘在此批准一次即可，两页立即共用同一授权。邮箱与本次风险确认仍须完成。</p><div class="field"><label>明确选择操作</label><select id="gAuthAction" name="authorization_action" required onchange="syncGoldenAuthorization()"><option value="">请选择，不默认授予权限</option><option value="formal">批准正式授权（指定天数 / 日期）</option><option value="extend">延长有效授权</option><option value="permanent">明确授予永久授权</option><option value="extra_trial">人工批准额外试用</option><option value="restrict">限制交易权限</option></select></div><div class="grid2" data-golden-permission="term" hidden><div class="field"><label>天数（与日期二选一，UTC日末到期）</label><input name="days" type="number" min="1" max="3650" disabled></div><div class="field"><label>固定到期日（UTC，与天数二选一）</label><input name="expires_at" type="date" disabled></div></div><div class="field" data-golden-permission="restrict" hidden><label>限制状态</label><select name="status" disabled><option value="pending">等待审核</option><option value="close_only">仅管理仓位</option><option value="disabled">已停用</option></select></div><div class="field"><label>操作原因（必填，保留审计）</label><textarea name="reason" maxlength="500" required></textarea></div></div><div class="modal-foot"><button type="button" class="btn btn-ghost" onclick="goldenAuthModal.classList.remove('show')">取消</button><button class="btn btn-primary">确认本次授权操作</button></div></form></div>
<style>#goldenAuthModal [hidden]{display:none!important}#goldenAuthModal .modal{max-height:90vh;overflow:auto}#goldenAuthModal .company,#goldenModal .company{white-space:normal;overflow:visible;text-overflow:clip;line-height:1.65}</style>
<form method="post" id="goldenRiskResignForm"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="golden_god"><input type="hidden" name="action" value="risk_require_resign"><input type="hidden" name="account" id="goldenRiskResignAccount"><input type="hidden" name="risk_reason" value="管理员要求客户重新确认当前风险协议"></form>
<?php elseif($page==='golden_monitor'):?>
<div class="gm-console" id="goldenMonitor" data-endpoint="/golden-monitor-data.php" data-share-endpoint="/golden-performance-admin.php" data-share-qr="/golden-performance-qr.php" data-csrf="<?=h($csrf)?>" aria-busy="true">
  <div class="gm-overviewbar" aria-label="监控服务状态">
    <div class="gm-overview-item"><i class="gm-signal gm-signal-ok" id="gmServiceSignal"></i><span id="gmServiceStatus">正在连接监控服务</span></div>
    <div class="gm-overview-item"><span>筛选内在线客户</span><b id="gmOnlineCount">—</b></div>
    <div class="gm-overview-item"><span>延迟/离线条目</span><b class="gm-warn" id="gmDelayedCount">—</b></div>
        <div class="gm-overview-item"><span>风险账户</span><b class="gm-danger" id="gmRiskCount">—</b></div>
    <div class="gm-overview-item gm-overview-time"><span>数据时间</span><b id="gmGlobalTime">等待首次同步</b></div>
    <button class="gm-icon-control" id="gmRefresh" type="button" title="刷新监控数据" aria-label="刷新监控数据">↻</button>
  </div>

  <div class="gm-notice" id="gmGlobalNotice" role="status" hidden></div>

  <div class="gm-layout">
    <aside class="gm-account-pane gm-card" aria-label="客户账户列表">
      <div class="gm-card-title">
        <div><h2>客户账户</h2><small id="gmAccountTotal">正在读取…</small></div>
        <span class="gm-readonly">只读监控</span>
      </div>
      <div class="gm-select-filters">
        <label for="gmOnlineFilter">在线状态<select id="gmOnlineFilter"><option value="all">全部状态</option><option value="online">仅在线</option><option value="delayed">数据延迟</option><option value="offline">离线</option></select></label>
        <label for="gmEmployeeFilter">所属员工<select id="gmEmployeeFilter"><option value="all">全部员工</option><option value="unassigned">未分配</option></select></label>
      </div>
      <label class="gm-search" for="gmAccountSearch"><span aria-hidden="true">⌕</span><input id="gmAccountSearch" type="search" autocomplete="off" maxlength="64" placeholder="搜索客户 / 账号 / 服务器"></label>
      <div class="gm-filters gm-combined-filters" role="group" aria-label="可组合的持仓与风险筛选">
        <button id="gmHoldingFilter" type="button" aria-pressed="false">有持仓 <b id="gmFilterPositions">0</b></button>
        <button id="gmRiskFilter" type="button" aria-pressed="false">风险关注 <b id="gmFilterRisk">0</b></button>
        <button id="gmResetFilters" class="gm-filter-reset" type="button" title="重置在线、员工、持仓、风险与搜索">重置</button>
      </div>
      <div class="gm-account-list" id="gmAccountList" aria-live="polite">
        <div class="gm-placeholder"><i class="gm-spinner"></i><b>正在载入客户账户</b><span>监控数据接入后会自动显示</span></div>
      </div>
      <div class="gm-list-foot"><span>按实例列示 · 账号脱敏</span><span id="gmListResult">—</span></div>
    </aside>

    <section class="gm-workspace" aria-label="账户交易数据">
      <article class="gm-card gm-account-head">
        <div class="gm-selected-identity">
          <div class="gm-avatar" aria-hidden="true">客</div>
          <div><div class="gm-selected-title"><h2 id="gmSelectedName">请选择客户账户</h2><span class="gm-version" id="gmSelectedVersion">—</span><span class="gm-live-badge" id="gmSelectedState">等待选择</span></div><p id="gmSelectedMeta">从左侧选择客户后查看黄金战神策略数据</p><p class="gm-selected-employee" id="gmSelectedEmployee">所属员工：—</p><p class="gm-instance-detail" id="gmSelectedInstance">尚未选择终端实例</p></div>
        </div>
        <div class="gm-head-actions"><button class="gm-share-button" id="gmShareButton" type="button" disabled>▦ 客户二维码</button><div class="gm-synced" id="gmSelectedSync">尚未同步</div></div>
      </article>

      <section class="gm-metrics" aria-label="账户核心指标">
        <article class="gm-metric"><span>账户净值</span><strong id="gmEquity">—</strong><small id="gmBalanceHint">余额 —</small></article>
        <article class="gm-metric"><span>EA 浮盈亏</span><strong id="gmFloating">—</strong><small id="gmRealizedHint">累计已实现 —</small></article>
        <article class="gm-metric"><span>保证金率</span><strong id="gmMargin">—</strong><small id="gmMarginHint">等待终端上报</small></article>
        <article class="gm-metric"><span>账户净值回撤</span><strong id="gmDrawdown">—</strong><small id="gmDrawdownHint">按账户净值峰值计算</small></article>
      </section>

      <article class="gm-card gm-chart-card">
        <div class="gm-card-title gm-chart-head">
          <div><h2>策略权益曲线</h2><small>黄金战神专属 Magic · 已实现净利润＋当前浮盈亏</small></div>
          <div class="gm-range" role="group" aria-label="曲线时间范围">
            <button type="button" data-range="1d" class="is-active">日内</button><button type="button" data-range="7d">7天</button><button type="button" data-range="30d">30天</button><button type="button" data-range="90d">90天</button>
          </div>
        </div>
        <div class="gm-chart-legend"><span class="gm-legend-realized">EA 累计已实现净收益</span><span class="gm-legend-strategy">EA 策略权益</span><em id="gmCurveUpdated">等待曲线数据</em></div>
        <div class="gm-chart-wrap"><canvas id="gmEquityChart" role="img" aria-label="黄金战神累计已实现净收益与策略权益曲线"></canvas><div class="gm-chart-empty" id="gmChartEmpty">选择账户后显示曲线</div></div>
      </article>

      <article class="gm-card gm-positions-card">
        <div class="gm-card-title"><div><h2>当前持仓 <span id="gmPositionHeading">(0)</span></h2><small id="gmPositionSummary">仅展示黄金战神自身订单，不包含手动单和其他 EA</small></div><div class="gm-card-pl">持仓浮盈亏 <b id="gmPositionProfit">—</b></div></div>
        <div class="gm-table-wrap"><table class="gm-table"><thead><tr><th>方向</th><th>层级</th><th>手数</th><th>开仓价格</th><th>当前价格</th><th>浮盈亏</th><th>持仓时长</th><th>开仓时间</th></tr></thead><tbody id="gmPositionRows"><tr><td colspan="8" class="gm-empty-cell">请选择客户账户</td></tr></tbody></table></div>
      </article>
    </section>

    <aside class="gm-side-stack" aria-label="运行状态与事件">
      <article class="gm-card gm-status-card">
        <div class="gm-card-title"><div><h2>运行状态</h2><small id="gmRuntimeSubtitle">等待选择账户</small></div></div>
        <div class="gm-status-list" id="gmStatusList"><div class="gm-placeholder gm-placeholder-compact"><b>暂无运行状态</b><span>选择客户后显示</span></div></div>
      </article>

      <article class="gm-card gm-orders-card">
        <div class="gm-card-title"><div><h2>活动挂单 <span id="gmOrderHeading">(0)</span></h2><small>服务器确认属于本 EA 的当前挂单</small></div></div>
        <div class="gm-table-wrap"><table class="gm-table gm-orders-table"><thead><tr><th>类型 / 方向</th><th>价格</th><th>手数</th><th>状态</th></tr></thead><tbody id="gmOrderRows"><tr><td colspan="4" class="gm-empty-cell">暂无挂单数据</td></tr></tbody></table></div>
      </article>

      <article class="gm-card gm-events-card">
        <div class="gm-card-title"><div><h2>最近事件</h2><small>交易、风控与终端运行记录</small></div><span class="gm-event-count" id="gmEventCount">0</span></div>
        <div class="gm-event-list" id="gmEventList"><div class="gm-placeholder gm-placeholder-compact"><b>暂无事件</b><span>选择客户后显示</span></div></div>
      </article>
    </aside>
  </div>
  <div class="gm-share-modal" id="gmShareModal" role="dialog" aria-modal="true" aria-labelledby="gmShareTitle" aria-describedby="gmShareHelp" hidden>
    <div class="gm-share-dialog" tabindex="-1">
      <div class="gm-share-head"><div><h2 id="gmShareTitle">客户专属绩效二维码</h2><p id="gmShareAccount">尚未选择账户</p></div><button type="button" id="gmShareClose" aria-label="关闭">×</button></div>
      <div class="gm-share-body"><div class="gm-share-qr"><img id="gmShareQr" alt="客户专属绩效二维码" hidden><span id="gmShareQrEmpty">首次生成后显示二维码</span></div><div class="gm-share-info"><span class="gm-share-state" id="gmShareState">未生成</span><dl><div><dt>生成时间</dt><dd id="gmShareCreated">—</dd></div><div><dt>有效期</dt><dd id="gmShareExpires">跟随黄金战神授权</dd></div><div><dt>访问次数</dt><dd id="gmShareVisits">0 次</dd></div><div><dt>最后访问</dt><dd id="gmShareLastVisit">尚未访问</dd></div></dl><label for="gmShareUrl">HTTPS 分享链接</label><div class="gm-share-link"><input id="gmShareUrl" readonly value="" aria-label="客户绩效分享链接"><button id="gmShareCopy" type="button" disabled>复制链接</button></div><p class="gm-share-help" id="gmShareHelp">公开页显示完整账号、实盘/模拟标识、EA绩效和已上报成交；不显示姓名、账户余额或后台信息。请仅向可信接收方分享。</p></div></div>
      <div class="gm-share-actions"><button class="gm-share-primary" id="gmShareCreate" type="button">首次生成</button><a id="gmSharePreview" href="#" target="_blank" rel="noopener noreferrer" hidden>预览报告</a><button id="gmShareRotate" type="button" hidden>重新生成</button><button id="gmShareToggle" type="button" hidden>停用</button></div>
      <div class="gm-share-message" id="gmShareMessage" role="status" hidden></div>
    </div>
  </div>
</div>
<?php elseif($page==='tribonacci'):?><section class="panel" id="tribonacci-accounts"><div class="panel-head"><div><h2>弘盛量化｜三阶战神授权账户</h2><small>V1.0 · 首次挂载自动登记为待授权；与黄金战神及其他产品授权完全隔离</small></div><button class="btn btn-primary" type="button" onclick="document.getElementById('tribonacciAdd').classList.toggle('show')">＋ 新增客户授权</button></div>
<form method="post" id="tribonacciAdd" style="display:none;padding:18px 20px;gap:10px;grid-template-columns:1.1fr 1.1fr 1fr 1fr 1.5fr auto" class="golden-form"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="tribonacci_god"><input name="account" inputmode="numeric" pattern="[0-9]{1,20}" placeholder="MT5账户" required><input name="customer_name" maxlength="80" placeholder="客户名称"><select name="status"><option value="pending">等待授权</option><option value="active">正常使用</option><option value="close_only">仅管理仓位</option><option value="disabled">已停用</option></select><input type="date" name="expires_at"><input name="note" maxlength="500" placeholder="备注"><button class="btn btn-primary">保存</button></form>
<div class="table-wrap"><table><thead><tr><th>账户 / 客户</th><th>服务器</th><th>EA版本</th><th>授权状态</th><th>有效期</th><th>最后在线</th><th>操作</th></tr></thead><tbody><?php foreach($tribonacciRows as $t):$ts=!empty($t['last_seen_at'])?strtotime($t['last_seen_at']):0;?><tr><td><b><?=h($t['account'])?></b><div class="company"><?=h($t['customer_name']?:'未备注客户')?></div></td><td><?=h($t['trade_server']?:'—')?><div class="company"><?=h($t['account_name']?:'—')?></div></td><td><?=h($t['ea_version']?:'—')?></td><td><span class="badge <?=h($t['status'])?>"><?=h($labels[$t['status']]??$t['status'])?></span></td><td><?=h($t['expires_at']?:'永久授权')?></td><td><?=$ts?h(gmdate('m-d H:i',$ts)).' UTC':'从未连接'?></td><td><div class="actions"><button class="icon-btn" type="button" onclick='editTribonacci(<?=json_encode($t,JSON_UNESCAPED_UNICODE|JSON_HEX_APOS|JSON_HEX_QUOT)?>)'>编辑</button><button class="icon-btn" type="button" onclick="deleteTribonacci('<?=h($t['account'])?>')">删除</button></div></td></tr><?php endforeach;?><?php if(!$tribonacciRows):?><tr><td colspan="7" class="empty">等待客户挂载三阶战神 EA，账户会自动出现在此板块</td></tr><?php endif;?></tbody></table></div></section>
<div class="modal-back" id="tribonacciModal"><form class="modal" method="post"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="tribonacci_god"><div class="modal-head"><h3>编辑三阶战神授权</h3><button type="button" class="close" onclick="tribonacciModal.classList.remove('show')">×</button></div><div class="modal-body"><div class="grid2"><div class="field"><label>MT5账户</label><input id="tgAccount" name="account" readonly></div><div class="field"><label>客户名称</label><input id="tgName" name="customer_name" maxlength="80"></div></div><div class="grid2"><div class="field"><label>授权状态</label><select id="tgStatus" name="status"><option value="pending">等待授权</option><option value="active">正常使用</option><option value="close_only">仅管理仓位</option><option value="disabled">已停用</option></select></div><div class="field"><label>到期日期（留空永久）</label><input id="tgExpires" name="expires_at" type="date"></div></div><div class="field"><label>备注</label><textarea id="tgNote" name="note" maxlength="500"></textarea></div></div><div class="modal-foot"><button type="button" class="btn btn-ghost" onclick="tribonacciModal.classList.remove('show')">取消</button><button class="btn btn-primary">保存</button></div></form></div>
<form method="post" id="tribonacciDeleteForm"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="tribonacci_god"><input type="hidden" name="action" value="delete"><input type="hidden" name="account" id="tribonacciDeleteAccount"></form>
<?php elseif($page==='mean_reversion'):?><section class="panel" id="mean-reversion-accounts"><div class="panel-head"><div><h2>弘盛量化均值回归对冲授权账户</h2><small>V1.0 · 首次挂载自动登记为待授权；与其他产品授权完全隔离</small></div><button class="btn btn-primary" type="button" onclick="document.getElementById('meanReversionAdd').classList.toggle('show')">＋ 新增客户授权</button></div>
<form method="post" id="meanReversionAdd" style="display:none;padding:18px 20px;gap:10px;grid-template-columns:1.1fr 1.1fr 1fr 1fr 1.5fr auto" class="golden-form"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="mean_reversion"><input name="account" placeholder="MT5账户" required><input name="customer_name" placeholder="客户名称"><select name="status"><option value="pending">等待授权</option><option value="active">正常使用</option><option value="close_only">仅管理仓位</option><option value="disabled">已停用</option></select><input type="date" name="expires_at"><input name="note" placeholder="备注"><button class="btn btn-primary">保存</button></form>
<div class="table-wrap"><table><thead><tr><th>账户 / 客户</th><th>服务器</th><th>EA版本</th><th>授权状态</th><th>有效期</th><th>最后在线</th><th>操作</th></tr></thead><tbody><?php foreach($meanReversionRows as $m):$ms=!empty($m['last_seen_at'])?strtotime($m['last_seen_at']):0;?><tr><td><b><?=h($m['account'])?></b><div class="company"><?=h($m['customer_name']?:'未备注客户')?></div></td><td><?=h($m['trade_server']?:'—')?><div class="company"><?=h($m['account_name']?:'—')?></div></td><td><?=h($m['ea_version']?:'—')?></td><td><span class="badge <?=h($m['status'])?>"><?=h($labels[$m['status']]??$m['status'])?></span></td><td><?=h($m['expires_at']?:'永久授权')?></td><td><?=$ms?h(gmdate('m-d H:i',$ms)).' UTC':'从未连接'?></td><td><div class="actions"><button class="icon-btn" type="button" onclick='editMeanReversion(<?=json_encode($m,JSON_UNESCAPED_UNICODE|JSON_HEX_APOS|JSON_HEX_QUOT)?>)'>编辑</button><button class="icon-btn" type="button" onclick="deleteMeanReversion('<?=h($m['account'])?>')">删除</button></div></td></tr><?php endforeach;?><?php if(!$meanReversionRows):?><tr><td colspan="7" class="empty">等待客户挂载均值回归对冲 EA，账户会自动出现在此板块</td></tr><?php endif;?></tbody></table></div></section>
<div class="modal-back" id="meanReversionModal"><form class="modal" method="post"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="mean_reversion"><div class="modal-head"><h3>编辑均值回归对冲授权</h3><button type="button" class="close" onclick="meanReversionModal.classList.remove('show')">×</button></div><div class="modal-body"><div class="grid2"><div class="field"><label>MT5账户</label><input id="mrAccount" name="account" readonly></div><div class="field"><label>客户名称</label><input id="mrName" name="customer_name"></div></div><div class="grid2"><div class="field"><label>授权状态</label><select id="mrStatus" name="status"><option value="pending">等待授权</option><option value="active">正常使用</option><option value="close_only">仅管理仓位</option><option value="disabled">已停用</option></select></div><div class="field"><label>到期日期（留空永久）</label><input id="mrExpires" name="expires_at" type="date"></div></div><div class="field"><label>备注</label><textarea id="mrNote" name="note"></textarea></div></div><div class="modal-foot"><button type="button" class="btn btn-ghost" onclick="meanReversionModal.classList.remove('show')">取消</button><button class="btn btn-primary">保存</button></div></form></div>
<form method="post" id="meanReversionDeleteForm"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="product" value="mean_reversion"><input type="hidden" name="action" value="delete"><input type="hidden" name="account" id="meanReversionDeleteAccount"></form>
<?php elseif($page==='logs'):?><section class="panel" id="activity"><div class="panel-head"><div><h2>操作记录</h2><small>后台授权变更、安全操作和自动登记审计记录</small></div><a class="btn btn-ghost" href="?page=logs">刷新记录</a></div><?php foreach($logs as $l):?><div class="log"><div class="log-mark">↗</div><div class="log-main"><b><?=h($logLabels[$l['action']]??$l['action'])?> · <?=h($l['account'])?></b><p><?=h($l['detail'])?>　操作 IP <?=h($l['ip'])?></p></div><time><?=h(gmdate('Y-m-d H:i',strtotime($l['created_at'])))?> UTC</time></div><?php endforeach;?><?php if(!$logs):?><div class="empty">暂无操作记录</div><?php endif;?></section>
<?php elseif($page==='overview'):?><?php $p1=$stats['total']?round($stats['active']/$stats['total']*100):0;$p2=$stats['total']?round(($stats['active']+$stats['close_only'])/$stats['total']*100):0;?><section class="stats"><div class="stat" style="--c:#3ed392"><div class="label">授权账户总数</div><strong><?=$stats['total']?></strong><small>全部登记账户</small><i></i></div><div class="stat" style="--c:#64e0a9"><div class="label">正常授权</div><strong><?=$stats['active']?></strong><small>允许完整交易</small><i></i></div><div class="stat" style="--c:#25b97a"><div class="label">当前在线</div><strong><?=$stats['online']?></strong><small>10 分钟内有心跳</small><i></i></div><div class="stat" style="--c:#d2a246"><div class="label">即将到期</div><strong><?=$stats['warning']?></strong><small>7 天内到期或已过期</small><i></i></div></section><section class="page-grid"><div class="insight"><h3>授权状态分布</h3><div class="hint">所有账户实时占比</div><div class="dist"><div class="donut" style="--a:<?=$p1?>%;--b:<?=$p2?>%"><b><?=$stats['total']?></b></div><div class="legend"><span style="--lc:#43d194">正常使用 <?=$stats['active']?></span><span style="--lc:#d1a23d">仅管理 <?=$stats['close_only']?></span><span style="--lc:#df6b75">已停用 <?=$stats['disabled']?></span><span style="--lc:#e0b14d">等待授权 <?=$stats['pending']?></span><span style="--lc:#648574">永久授权 <?=$stats['permanent']?></span></div></div></div><div class="insight"><h3>连接与风险概况</h3><div class="hint">需要关注的实时指标</div><div class="risk-list"><div class="risk"><div><b>当前在线账户</b><small>10 分钟内正常上报</small></div><em><?=$stats['online']?> 个</em></div><div class="risk"><div><b>等待人工授权</b><small>EA 已登记但尚未允许使用</small></div><em><?=$stats['pending']?> 个</em></div><div class="risk"><div><b>7 天内到期</b><small>包括已经到期的账户</small></div><em><?=$stats['warning']?> 个</em></div><div class="risk"><div><b>已到期账户</b><small>授权已自动停止</small></div><em><?=$stats['expired']?> 个</em></div></div></div></section>
<?php elseif($page==='expiry'):?><section class="panel risk-panel"><div class="panel-head"><div><h2>风险与到期账户</h2><small>优先显示异常状态、7 天内到期及已到期账户</small></div><a class="btn btn-ghost" href="?page=expiry">刷新状态</a></div><?php foreach($riskRows as $r):$rm=expiry_meta((string)($r['expires_at']??''),$now);?><div class="risk-row risk-<?=h($rm['level'])?>"><div class="identity"><b><?=h($r['customer_name']?:'未备注客户')?> · <?=h($r['account'])?></b><small><?=h($r['account_name']?:'未提供平台名称')?></small></div><div class="server-meta"><?=h($r['trade_server']?:'服务器待上报')?><br><?=h($r['terminal_company']?:'IP 待上报')?></div><div><span class="badge <?=h($r['status'])?>"><?=h($labels[$r['status']]??$r['status'])?></span></div><div class="expires"><?=h($r['expires_at']?:'永久')?><br><span class="expiry-pill"><?=h($rm['level']==='permanent'?($labels[$r['status']]??'永久授权'):$rm['label'])?></span></div></div><?php endforeach;?><?php if(!$riskRows):?><div class="empty">当前没有需要处理的风险或到期账户</div><?php endif;?></section>
<?php elseif($page==='downloads'):?>
<div class="service-strip"><div class="api-status"><i class="pulse"></i>授权 API 运行正常</div><code id="apiUrl">https://dc.hongshenglh.com/api/license</code><button class="btn btn-ghost" type="button" onclick="copyText('apiUrl',this)">复制接口地址</button></div>
<section class="panel"><div class="panel-head"><div><h2>EA 下载文件</h2><small>每次有效下载自动累计；复制链接不会增加次数</small></div></div><div class="download-list">
<?php foreach($downloadCatalog as $productId=>$download):$urlId='downloadUrl_'.$productId;$interfaceId='interfaceUrl_'.$productId;?>
<div class="download-row" data-product="<?=h($productId)?>">
    <div class="download-name"><span class="download-icon">↓</span><div><b><?=h($download['title'])?></b><small><?=h($download['subtitle'])?></small></div></div>
    <div class="download-url" id="<?=h($urlId)?>"><?=h($download['url'])?></div>
    <div class="download-copy">
        <button class="btn btn-primary" type="button" onclick="copyText('<?=h($urlId)?>',this)">复制链接</button>
        <span class="download-count" title="该下载项的累计下载开始次数"><i></i>累计下载 <b><?=number_format($downloadCounts[$productId]??0)?></b> 次</span>
        <?php if(!empty($download['interface_url'])):?><span class="copy-source" id="<?=h($interfaceId)?>"><?=h($download['interface_url'])?></span><button class="btn btn-interface" type="button" title="<?=h($download['interface_url'])?>" onclick="copyText('<?=h($interfaceId)?>',this)">复制接口地址</button><?php endif;?>
    </div>
</div>
<?php endforeach;?>
</div></section><?php endif;?></main></div>
<?php if($page==='accounts'):?>
<div class="modal-back" id="modal"><form class="modal" method="post"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="action" value="save"><div class="modal-head"><h3 id="modalTitle">新增授权账户</h3><button type="button" class="close" onclick="closeModal()">×</button></div><div class="modal-body"><div class="grid2"><div class="field"><label>MT5 交易账户</label><input id="mAccount" name="account" inputmode="numeric" pattern="[0-9]{1,20}" placeholder="例如 12345678" required></div><div class="field"><label>客户名称</label><input id="mCustomerName" name="customer_name" maxlength="80" placeholder="例如：广州李先生"></div></div><div class="grid2"><div class="field"><label>授权状态</label><select id="mStatus" name="status"><option value="pending">等待授权</option><option value="active">正常使用</option><option value="close_only">仅管理仓位</option><option value="disabled">停用</option></select></div><div class="field"><label>到期日期（留空为永久）</label><input id="mExpires" name="expires_at" type="date"></div></div><div class="quick-days"><button type="button" onclick="setDays(30)">＋30天</button><button type="button" onclick="setDays(90)">＋90天</button><button type="button" onclick="setDays(180)">＋180天</button><button type="button" onclick="setDays(365)">＋1年</button><button type="button" onclick="setDays(0)">永久</button></div><div class="field"><label>内部备注</label><textarea id="mNote" name="note" placeholder="联系方式、渠道或授权说明（仅后台可见）"></textarea></div></div><div class="modal-foot"><button type="button" class="btn btn-ghost" onclick="closeModal()">取消</button><button class="btn btn-primary">保存资料与授权</button></div></form></div>
<form method="post" id="deleteForm"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="action" value="delete"><input type="hidden" name="account" id="deleteAccount"></form>
<form method="post" id="toggleForm"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="action" value="toggle_status"><input type="hidden" name="account" id="toggleAccountValue"><input type="hidden" name="target_status" id="toggleTargetStatus"></form>
<script>
const modal=document.getElementById('modal'),search=document.getElementById('search'),filter=document.getElementById('statusFilter'),rows=[...document.querySelectorAll('#accountRows tr[data-status]')];
const planDetails=<?=json_encode(array_column($allRows,null,'account'),JSON_UNESCAPED_UNICODE|JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT)?>;
rows.forEach(row=>{const account=row.querySelector('.row-check')?.value,data=planDetails[account],cell=row.querySelector('td:nth-child(6)');if(!data||!cell)return;cell.textContent='';const box=document.createElement('div');box.className='plan-summary';if(data.plan_direction!=='buy'&&data.plan_direction!=='sell'){const empty=document.createElement('span');empty.className='plan-empty';empty.textContent='未保存计划';box.appendChild(empty)}else{const top=document.createElement('div');top.className='plan-top';const badge=document.createElement('span');badge.className='plan-badge '+(data.plan_direction==='buy'?'plan-buy':'plan-sell');badge.textContent=data.plan_direction==='buy'?'多单':'空单';const param=document.createElement('span');param.className='plan-param';param.textContent=Number(data.plan_full_mode)?'满仓 · 缓冲 '+Math.round(Number(data.plan_buffer)*100)+'%':(data.plan_lot||'—')+' 手';top.append(badge,param);const time=document.createElement('div');time.className='plan-time';time.textContent=data.plan_time||'时间待上报';box.append(top,time)}cell.appendChild(box)});
rows.forEach(row=>{const account=row.querySelector('.row-check')?.value,data=planDetails[account],cell=row.querySelector('td:nth-child(9)');if(!data||!cell)return;const badge=document.createElement('span');badge.className='version-badge'+(data.ea_version?'':' version-unknown');badge.textContent=data.ea_version?'EA V'+data.ea_version:'版本未知';cell.appendChild(badge)});
rows.forEach(row=>{const account=row.querySelector('.row-check')?.value,data=planDetails[account],box=row.querySelector('.actions');if(!account||!data||!box)return;const buttons=[...box.querySelectorAll('button')],edit=buttons[0],remove=buttons[1];if(edit){edit.classList.add('action-edit');edit.title='编辑资料';edit.setAttribute('aria-label','编辑资料');edit.innerHTML='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L8 18l-4 1 1-4Z"/></svg>'}if(remove){remove.classList.add('action-delete');remove.title='删除账户';remove.setAttribute('aria-label','删除账户');remove.innerHTML='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6M10 10v6M14 10v6"/></svg>'}const tools=document.createElement('div');tools.className='action-tools';if(edit)tools.appendChild(edit);if(remove)tools.appendChild(remove);const enabled=data.status!=='disabled';const toggle=document.createElement('button');toggle.type='button';toggle.className='status-switch '+(enabled?'switch-on':'switch-off');toggle.title=enabled?'点击禁用账户':'点击启用账户';toggle.setAttribute('aria-label',toggle.title);toggle.innerHTML='<i aria-hidden="true"></i><span>'+(enabled?'已启用':'已禁用')+'</span>';toggle.addEventListener('click',()=>toggleAccount(account,enabled?'disabled':'active'));box.append(toggle,tools)});
function openModal(){document.getElementById('modalTitle').textContent='新增授权账户';['mAccount','mCustomerName','mExpires','mNote'].forEach(id=>document.getElementById(id).value='');document.getElementById('mAccount').readOnly=false;document.getElementById('mStatus').value='active';modal.classList.add('show')}
function editAccount(r){document.getElementById('modalTitle').textContent='编辑客户资料 · '+r.account;document.getElementById('mAccount').value=r.account;document.getElementById('mAccount').readOnly=true;document.getElementById('mCustomerName').value=r.customer_name||'';document.getElementById('mStatus').value=r.status;document.getElementById('mExpires').value=r.expires_at||'';document.getElementById('mNote').value=r.note||'';modal.classList.add('show')}
function closeModal(){modal.classList.remove('show')} modal.addEventListener('click',e=>{if(e.target===modal)closeModal()});document.addEventListener('keydown',e=>{if(e.key==='Escape')closeModal()});
function applyFilter(){const q=search.value.trim().toLowerCase(),s=filter.value;rows.forEach(r=>r.style.display=(!q||r.dataset.search.includes(q))&&(!s||r.dataset.status===s)?'':'none')};search.addEventListener('input',applyFilter);filter.addEventListener('change',applyFilter);
const checks=[...document.querySelectorAll('.row-check')],all=document.getElementById('checkAll'),bar=document.getElementById('bulkBar'),count=document.getElementById('selectedCount');function sync(){const n=checks.filter(c=>c.checked).length;count.textContent=n;bar.classList.toggle('show',n>0);all.checked=n===checks.length&&n>0;all.indeterminate=n>0&&n<checks.length}checks.forEach(c=>c.addEventListener('change',sync));all.addEventListener('change',()=>{checks.forEach(c=>{if(c.closest('tr').style.display!=='none')c.checked=all.checked});sync()});
function removeAccount(a){if(confirm('确定删除账户 '+a+' 的授权记录吗？\n该账户下一次验证将被停用。')){document.getElementById('deleteAccount').value=a;document.getElementById('deleteForm').submit()}}
function toggleAccount(a,target){const enabling=target==='active';if(confirm('确定'+(enabling?'启用':'禁用')+'账户 '+a+' 吗？'+(enabling?'':'\n禁用后该客户将不能继续使用 EA。'))){document.getElementById('toggleAccountValue').value=a;document.getElementById('toggleTargetStatus').value=target;document.getElementById('toggleForm').submit()}}
function setDays(n){const e=document.getElementById('mExpires');if(!n){e.value='';return}const d=new Date();d.setDate(d.getDate()+n);e.value=d.toISOString().slice(0,10)}
async function copyText(id,btn){await navigator.clipboard.writeText(document.getElementById(id).textContent);const old=btn.textContent;btn.textContent='已复制';setTimeout(()=>btn.textContent=old,1400)}
</script><?php endif;?>
<?php if($page!=='accounts'):?><script>async function copyText(id,btn){try{await navigator.clipboard.writeText(document.getElementById(id).textContent);const old=btn.textContent;btn.textContent='已复制';setTimeout(()=>btn.textContent=old,1400)}catch(e){alert('复制失败，请手动选择链接复制')}}</script><?php endif;?>
<style>
.golden-form.show{display:grid!important}.golden-form input,.golden-form select{min-width:0;background:#0b1220;color:#e5eaf3;border:1px solid #29354a;border-radius:9px;padding:10px}
.badge.pending{background:#727b8b20;color:#a9b2c2}
.risk-policy-card{display:flex;align-items:center;justify-content:space-between;gap:18px;margin:20px 0 0;padding:17px 20px;border:1px solid #315b46;border-radius:14px;background:linear-gradient(135deg,#10261b,#0b1b13)}.risk-policy-card b{display:block;color:#dff7ea}.risk-policy-card small{display:block;color:#80a890;margin-top:3px}.risk-policy-stats{display:flex;align-items:center;gap:18px;white-space:nowrap}.risk-policy-stats span{color:#84a894}.risk-policy-stats b{display:inline;margin-left:5px;color:#5bdca1;font-size:18px}.risk-policy-stats .warn b{color:#efb85b}.risk-invite-card{display:flex;align-items:center;justify-content:space-between;gap:18px;margin:14px 0 0;padding:16px 20px;border:1px solid #496733;border-radius:14px;background:#1b2811}.risk-invite-card b,.risk-invite-card small{display:block}.risk-invite-card small{color:#9db184;margin:4px 0 9px}.risk-invite-card code{display:block;max-width:780px;color:#ccefae;word-break:break-all}.risk-code-card{border-color:#7c6730;background:#29220f}.risk-code-card code{font-size:25px;font-weight:900;letter-spacing:3px;color:#ffe197}.risk-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:180px}.risk-actions .icon-btn{display:inline-flex;align-items:center;justify-content:center;text-decoration:none;padding:7px 9px}.inline-form{display:inline-flex;margin:0}.risk-code{color:#f0ce76!important;border-color:#705d2d!important}.risk-copy{color:#74dfae!important;border-color:#316c4e!important}.proof-link{color:#efd078!important;border-color:#705d2d!important}.risk-reset{color:#ef9b9f!important;border-color:#684046!important}
.download-copy{display:flex;align-items:center;gap:8px;flex:0 0 auto;min-width:0;flex-wrap:wrap}.download-copy .btn{width:auto!important;min-width:92px;white-space:nowrap}.download-count{height:34px;display:inline-flex;align-items:center;gap:5px;padding:0 11px;border:1px solid #2a4a3a;border-radius:9px;background:#0d2017;color:#8eaa9b;font-size:11px;white-space:nowrap;font-variant-numeric:tabular-nums;box-shadow:inset 0 1px 0 #ffffff08}.download-count i{width:6px;height:6px;border-radius:50%;background:#45d494;box-shadow:0 0 0 3px #45d49417}.download-count b{color:#68dfa9;font:700 12px ui-monospace,SFMono-Regular,Consolas,monospace}.copy-source{display:none}.btn-interface{background:#e9b44c!important;color:#241a05!important;border:1px solid #f1c862!important;box-shadow:0 7px 18px #e9b44c24}.btn-interface:hover{background:#f0c35e!important;border-color:#f7d37b!important;color:#171005!important}
@media(max-width:650px){.top-actions .logout{display:inline-block}}
@media(min-width:901px){.download-row{display:flex!important;align-items:center;gap:10px}.download-name{flex:0 0 250px;min-width:0}.download-url{flex:1 1 280px;min-width:120px}.download-row .btn{width:auto!important;flex:0 0 auto;min-width:92px;white-space:nowrap;padding-left:14px;padding-right:14px}}
@media(max-width:900px){.golden-form.show{grid-template-columns:1fr 1fr!important}.download-row{grid-template-columns:1fr 1fr!important}.download-url{grid-column:1/-1!important}.download-row>.btn{width:100%}.download-copy{width:100%}.download-copy .btn{flex:1}}
@media(max-width:650px){.download-row{grid-template-columns:1fr!important}.download-url{grid-column:auto!important;grid-row:auto!important}.download-copy{justify-content:flex-start}.download-copy .btn{flex:1 1 120px}.download-count{flex:0 0 auto}.risk-policy-card{align-items:flex-start;flex-direction:column}.risk-policy-stats{width:100%;justify-content:space-between}}
</style>
<?php if($page==='golden_monitor'):?>
<style>
/* 黄金战神 V1.42｜方案 B：实时交易指挥台（管理员只读） */
.main-monitor{padding-left:20px;padding-right:20px}.gm-console{--gm-panel:#0b1912;--gm-panel-2:#0d2017;--gm-border:#1f4935;--gm-line:#183a2a;--gm-bright:#51dda0;--gm-soft:#7ea38f;--gm-red:#ff655f;--gm-amber:#e7b64f;margin:18px 0 0;min-width:0}.gm-console button,.gm-console input{font:inherit}.gm-overviewbar{min-height:48px;display:flex;align-items:center;justify-content:flex-end;gap:0;padding:6px 10px;border:1px solid var(--gm-border);border-radius:12px;background:linear-gradient(180deg,#0c1c14,#08140e);box-shadow:0 14px 40px #0004}.gm-overview-item{display:flex;align-items:center;gap:7px;padding:2px 16px;border-right:1px solid #1d3d2d;color:#7ea18e;font-size:11px;white-space:nowrap}.gm-overview-item b{color:#e6f4ec;font:700 12px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-overview-item .gm-warn{color:var(--gm-amber)}.gm-overview-item .gm-danger{color:var(--gm-red)}.gm-overview-time{margin-left:auto}.gm-signal{width:8px;height:8px;border-radius:50%;display:inline-block;background:#76867d;box-shadow:0 0 0 4px #76867d17}.gm-signal-ok{background:var(--gm-bright);box-shadow:0 0 0 4px #51dda018,0 0 12px #51dda055}.gm-signal-warn{background:var(--gm-amber);box-shadow:0 0 0 4px #e7b64f19}.gm-signal-error{background:var(--gm-red);box-shadow:0 0 0 4px #ff655f19}.gm-icon-control{width:32px;height:32px;border:1px solid #28533e;border-radius:8px;background:#0b2117;color:#79dbae;cursor:pointer;font-size:18px;line-height:1;transition:.18s}.gm-icon-control:hover{border-color:#4bd294;background:#103021}.gm-icon-control:disabled{opacity:.5;cursor:wait}.gm-icon-control.is-loading{animation:gmRotate .9s linear infinite}.gm-notice{margin-top:12px;padding:11px 14px;border:1px solid #7b5e24;border-radius:10px;background:#2b210d;color:#e9c874;font-size:12px}.gm-notice.is-error{border-color:#723d41;background:#2d1518;color:#ff999d}.gm-layout{display:grid;grid-template-columns:minmax(245px,292px) minmax(540px,1fr) minmax(280px,340px);gap:12px;align-items:start;margin-top:12px}.gm-card{min-width:0;background:linear-gradient(150deg,#0d1d15,#08140e);border:1px solid var(--gm-border);border-radius:12px;box-shadow:0 16px 42px #0004;overflow:hidden}.gm-card-title{min-height:58px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:13px 15px;border-bottom:1px solid var(--gm-line)}.gm-card-title h2{margin:0;color:#eaf8f0;font-size:14px;font-weight:750}.gm-card-title h2 span{color:#6f9581;font-size:11px}.gm-card-title small{display:block;margin-top:2px;color:#6f907e;font-size:10px}.gm-readonly{padding:4px 8px;border:1px solid #315441;border-radius:999px;background:#10271b;color:#77bb98;font-size:9px;white-space:nowrap}.gm-account-pane{position:sticky;top:12px;height:calc(100vh - 126px);min-height:620px;display:flex;flex-direction:column}.gm-search{position:relative;display:block;margin:12px 12px 9px}.gm-search span{position:absolute;left:11px;top:7px;color:#6c9180;font-size:17px}.gm-search input{width:100%;height:37px;padding:0 10px 0 33px;border:1px solid #28503b;border-radius:8px;outline:0;background:#07130d;color:#e4f2ea;font-size:11px}.gm-search input:focus{border-color:#42bf84;box-shadow:0 0 0 3px #42bf8414}.gm-search input::placeholder{color:#587264}.gm-filters{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;padding:0 12px 10px}.gm-filters button,.gm-range button{border:1px solid #264837;border-radius:7px;background:#0a1710;color:#779786;cursor:pointer;font-size:10px;transition:.15s}.gm-filters button{height:31px}.gm-filters button b{margin-left:3px;color:#a5c4b3}.gm-filters button:hover,.gm-filters button.is-active,.gm-range button:hover,.gm-range button.is-active{border-color:#2e9d69;background:#103121;color:#62dda7}.gm-account-list{flex:1;min-height:0;padding:0 8px 8px;overflow:auto;scrollbar-width:thin;scrollbar-color:#28533d transparent}.gm-account-row{width:100%;display:block;margin:0 0 7px;padding:11px;border:1px solid #1e422f;border-radius:9px;background:#09170f;color:inherit;text-align:left;cursor:pointer;transition:.16s}.gm-account-row:hover{border-color:#337050;background:#0d2117}.gm-account-row.is-selected{border-color:#34c281;background:linear-gradient(135deg,#102b1d,#0a1a12);box-shadow:inset 3px 0 #43d795,0 0 0 1px #2fc38322}.gm-account-row.is-risk{border-color:#73413f}.gm-account-row.is-selected.is-risk{box-shadow:inset 3px 0 var(--gm-red),0 0 0 1px #ff655f1c}.gm-account-top,.gm-account-bottom{display:flex;align-items:center;justify-content:space-between;gap:8px}.gm-account-person{min-width:0;display:flex;align-items:center;gap:8px}.gm-account-person i{width:26px;height:26px;flex:0 0 26px;display:grid;place-items:center;border-radius:50%;background:#123523;color:#59d99e;font-style:normal;font-size:11px}.gm-account-person div{min-width:0}.gm-account-person b{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#dceee4;font-size:11px}.gm-account-person small{display:block;color:#678875;font:9px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-account-badge{flex:0 0 auto;padding:3px 6px;border:1px solid #2e6b4c;border-radius:5px;background:#10291c;color:#60dca4;font-size:8px}.gm-account-badge.is-delayed{border-color:#80621f;background:#2b210b;color:#eec35a}.gm-account-badge.is-offline,.gm-account-badge.is-risk{border-color:#6b393d;background:#271316;color:#f08084}.gm-account-badge.is-unsupported{border-color:#45564d;background:#172019;color:#95a79d}.gm-account-bottom{margin-top:8px;padding-top:7px;border-top:1px solid #163725;color:#71907e;font-size:9px}.gm-account-bottom b{font:700 10px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-account-bottom .is-positive{color:#55dca0}.gm-account-bottom .is-negative{color:#ff716a}.gm-load-more{width:100%;height:34px;margin:2px 0 7px;border:1px dashed #315c45;border-radius:8px;background:#0b1c13;color:#77b696;cursor:pointer;font-size:10px}.gm-load-more:hover{border-color:#43c387;color:#59dda0}.gm-load-more:disabled{opacity:.55;cursor:wait}.gm-list-foot{display:flex;justify-content:space-between;padding:9px 12px;border-top:1px solid var(--gm-line);color:#587665;font-size:9px}.gm-placeholder{min-height:180px;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:6px;padding:22px;color:#61816f;text-align:center}.gm-placeholder b{color:#8bab99;font-size:11px}.gm-placeholder span{font-size:9px}.gm-placeholder-compact{min-height:110px}.gm-spinner{width:20px;height:20px;border:2px solid #244a36;border-top-color:#50d99d;border-radius:50%;animation:gmRotate .8s linear infinite}.gm-workspace,.gm-side-stack{display:grid;gap:12px;min-width:0}.gm-account-head{min-height:64px;display:flex;align-items:center;justify-content:space-between;padding:10px 15px}.gm-selected-identity{display:flex;align-items:center;gap:10px;min-width:0}.gm-avatar{width:34px;height:34px;flex:0 0 34px;display:grid;place-items:center;border-radius:9px;background:linear-gradient(135deg,#1f9a63,#12492f);color:#d7fae7;font-weight:850}.gm-selected-identity>div:last-child{min-width:0}.gm-selected-title{display:flex;align-items:center;gap:7px;min-width:0}.gm-selected-title h2{max-width:330px;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#f0faf5;font-size:15px}.gm-selected-identity p{margin:2px 0 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#6f9380;font-size:10px}.gm-version,.gm-live-badge{padding:3px 6px;border-radius:5px;font:800 8px ui-monospace,SFMono-Regular,Consolas,monospace;white-space:nowrap}.gm-version{border:1px solid #30543f;background:#11251a;color:#90c7a9}.gm-live-badge{border:1px solid #2d774f;background:#0e2b1c;color:#55dda0}.gm-live-badge.is-delayed{border-color:#7a5d25;background:#2a200c;color:#e8bd55}.gm-live-badge.is-offline,.gm-live-badge.is-risk{border-color:#6a3a3d;background:#2b1417;color:#f17d81}.gm-live-badge.is-unsupported{border-color:#45544c;background:#172019;color:#a1aaa5}.gm-synced{flex:0 0 auto;color:#658573;font:9px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.gm-metric{position:relative;min-height:106px;padding:15px;border:1px solid var(--gm-border);border-radius:11px;background:linear-gradient(145deg,#0d2217,#09150f);overflow:hidden}.gm-metric:after{content:"";position:absolute;right:-24px;bottom:-30px;width:80px;height:80px;border-radius:50%;background:#31bd7c0a}.gm-metric span{color:#7b9a89;font-size:10px}.gm-metric strong{display:block;margin:5px 0 4px;color:#e9f7ef;font:750 22px ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:-.8px}.gm-metric strong.is-positive{color:#45db96}.gm-metric strong.is-negative{color:var(--gm-red)}.gm-metric strong.is-warning{color:var(--gm-amber)}.gm-metric small{color:#5e7d6c;font-size:9px}.gm-chart-card{height:342px}.gm-chart-head{min-height:60px}.gm-range{display:flex;gap:4px}.gm-range button{height:28px;padding:0 9px}.gm-chart-legend{display:flex;align-items:center;gap:18px;padding:9px 15px 2px;color:#7e9b8a;font-size:9px}.gm-chart-legend span:before{content:"";display:inline-block;width:14px;height:2px;margin-right:6px;vertical-align:middle;background:currentColor}.gm-legend-strategy{color:#4cd99a}.gm-legend-account{color:#e1b447}.gm-chart-legend em{margin-left:auto;color:#5c7869;font-style:normal}.gm-chart-wrap{position:relative;height:245px;margin:0 8px 8px}.gm-chart-wrap canvas{display:block;width:100%;height:100%}.gm-chart-empty{position:absolute;inset:0;display:grid;place-items:center;color:#5f7e6d;font-size:10px;pointer-events:none}.gm-chart-empty[hidden]{display:none}.gm-positions-card{min-height:252px}.gm-card-pl{color:#7d9b8a;font-size:10px;white-space:nowrap}.gm-card-pl b{margin-left:7px;color:#e8f6ee;font:750 13px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-card-pl b.is-positive{color:#4bdda0}.gm-card-pl b.is-negative{color:var(--gm-red)}.gm-table-wrap{width:100%;overflow:auto}.gm-table{width:100%;min-width:0;border-collapse:collapse;font-size:9px}.gm-table th{padding:8px 10px;background:#08130d;color:#5e806d;font-size:8px;letter-spacing:.25px;text-transform:none;white-space:nowrap}.gm-table td{padding:9px 10px;border-top:1px solid #163625;color:#b7ccbf;white-space:nowrap}.gm-table tbody tr:hover{background:#0d2016}.gm-side-stack .gm-card{margin:0}.gm-status-card{min-height:258px}.gm-status-list{padding:5px 14px 11px}.gm-status-item{position:relative;display:grid;grid-template-columns:22px 1fr auto;gap:7px;min-height:45px;padding:9px 0;border-bottom:1px solid #173626}.gm-status-item:last-child{border-bottom:0}.gm-status-icon{width:17px;height:17px;display:grid;place-items:center;border-radius:50%;background:#3bd492;color:#062015;font-size:10px;font-weight:900}.gm-status-icon.is-warning{background:#dbaa46;color:#271b05}.gm-status-icon.is-error{background:#e8686e;color:#2e090c}.gm-status-main b{display:block;color:#d5e9de;font-size:10px}.gm-status-main small{display:block;margin-top:1px;color:#668674;font-size:8px}.gm-status-item time{color:#597565;font:8px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-orders-card{min-height:180px}.gm-orders-table th,.gm-orders-table td{padding-left:9px;padding-right:9px}.gm-side-label{display:flex;align-items:center;gap:5px}.gm-side-arrow{font-size:12px}.gm-buy{color:#56dba0!important}.gm-sell{color:#ff716a!important}.gm-order-state{padding:3px 5px;border:1px solid #315740;border-radius:5px;background:#10251a;color:#8cc4a7;font-size:8px}.gm-empty-cell{text-align:center!important;padding:30px 10px!important;color:#607d6c!important}.gm-events-card{min-height:280px}.gm-event-count{min-width:23px;height:23px;display:grid;place-items:center;border:1px solid #2b5941;border-radius:7px;background:#0e2519;color:#6fdba9;font:750 9px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-event-list{position:relative;padding:5px 14px 12px}.gm-event-list:before{content:"";position:absolute;top:19px;bottom:25px;left:70px;width:1px;background:#25543d}.gm-event{position:relative;display:grid;grid-template-columns:48px 12px 1fr;gap:7px;min-height:39px;padding:9px 0;border-bottom:1px solid #173626}.gm-event:last-child{border-bottom:0}.gm-event time{color:#6e8d7c;font:8px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-event-dot{z-index:1;width:8px;height:8px;margin-top:2px;border-radius:50%;background:#45d897;box-shadow:0 0 0 3px #0c1912}.gm-event.is-warning .gm-event-dot{background:var(--gm-amber)}.gm-event.is-critical .gm-event-dot{background:var(--gm-red)}.gm-event p{margin:0;color:#a9c1b4;font-size:9px;line-height:1.5}.gm-unsupported{margin:0 15px 12px;padding:9px 10px;border:1px solid #5d5534;border-radius:8px;background:#24200f;color:#d4bd70;font-size:9px}.is-positive{color:#4bdda0!important}.is-negative{color:var(--gm-red)!important}.gm-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}@keyframes gmRotate{to{transform:rotate(360deg)}}
.gm-legend-realized{color:#e1b447}.gm-account-row.is-warning{border-color:#6b5726}.gm-account-row.is-selected.is-warning{box-shadow:inset 3px 0 var(--gm-amber),0 0 0 1px #e7b64f1c}
.gm-account-person .gm-account-instance{max-width:190px;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#769986}.gm-account-person .gm-account-instance.is-duplicate{color:var(--gm-amber)}.gm-account-head{min-height:74px}.gm-selected-identity .gm-instance-detail{color:#88aa98;font:9px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-selected-identity .gm-instance-detail.is-duplicate{color:var(--gm-amber)}
.gm-select-filters{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:12px 12px 0}.gm-select-filters label{min-width:0;color:#8faa9a;font-size:10px}.gm-select-filters select{display:block;width:100%;min-width:0;height:34px;margin-top:5px;padding:0 7px;border:1px solid #385541;border-radius:7px;background:#08180f;color:#e2cb89;font:inherit;outline:0;text-overflow:ellipsis}.gm-select-filters select:focus{border-color:#c8a550;box-shadow:0 0 0 2px #c8a55020}.gm-select-filters option{background:#0b1912;color:#e7eee9}.gm-combined-filters{grid-template-columns:minmax(0,1fr) minmax(0,1fr) 40px}.gm-combined-filters .gm-filter-reset{border-style:dashed;color:#c0a667}.gm-account-person .gm-account-employee{max-width:185px;margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#c7b67d;font:10px inherit}.gm-selected-identity .gm-selected-employee{color:#c7b67d}.gm-account-list[aria-busy="true"]{opacity:.65}
.gm-account-entry{display:grid;grid-template-columns:minmax(0,1fr) 34px;gap:5px;margin:0 0 7px;min-width:0}.gm-account-entry .gm-account-row{min-width:0;margin:0}.gm-share-shortcut{position:relative;width:34px;min-height:100%;display:grid;place-items:center;border:1px solid #28513d;border-radius:9px;background:#0a1b13;color:#75a68d;cursor:pointer;transition:.16s}.gm-share-shortcut:hover,.gm-share-shortcut:focus-visible{border-color:#45ce91;background:#103020;color:#6fe0ad;outline:0;box-shadow:0 0 0 3px #3cc98b17}.gm-share-shortcut:after{content:"";position:absolute;right:4px;top:4px;width:6px;height:6px;border-radius:50%;background:#526a5d}.gm-share-shortcut[data-share-state="active"]:after{background:var(--gm-bright);box-shadow:0 0 7px #51dda0}.gm-share-shortcut[data-share-state="disabled"]:after,.gm-share-shortcut[data-share-state="invalid"]:after{background:var(--gm-amber)}.gm-share-shortcut[data-share-state="none"]:after{background:#60766a}.gm-share-shortcut span[aria-hidden]{font-size:17px;line-height:1}.gm-head-actions{flex:0 0 auto;display:flex;align-items:center;gap:11px}.gm-share-button{min-height:36px;padding:0 12px;border:1px solid #347252;border-radius:9px;background:#10281c;color:#6edda9;font-size:10px;font-weight:750;white-space:nowrap;cursor:pointer;transition:.16s}.gm-share-button:hover,.gm-share-button:focus-visible{border-color:#4bd496;background:#153625;color:#9bf0c8;outline:0;box-shadow:0 0 0 3px #3cc98b17}.gm-share-button[data-share-state="active"]{border-color:#3fc98b;background:#123523;color:#82e8ba}.gm-share-button[data-share-state="disabled"],.gm-share-button[data-share-state="invalid"]{border-color:#715b2a;background:#2a210d;color:#e8bd5b}.gm-share-button:disabled{opacity:.48;cursor:not-allowed;box-shadow:none}.gm-share-modal[hidden]{display:none}.gm-share-modal{position:fixed;z-index:60;inset:0;display:grid;place-items:center;padding:18px;background:#020805d9;backdrop-filter:blur(7px)}body.gm-share-open{overflow:hidden}.gm-share-dialog{width:min(760px,100%);max-height:calc(100dvh - 36px);overflow:auto;border:1px solid #2d6248;border-radius:17px;background:linear-gradient(150deg,#0d2117,#07130d);box-shadow:0 28px 90px #000c}.gm-share-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:18px 20px;border-bottom:1px solid #1c402e}.gm-share-head h2{margin:0;color:#eaf8f0;font-size:16px}.gm-share-head p{margin:3px 0 0;color:#779986;font-size:10px}.gm-share-head>button{width:34px;height:34px;border:1px solid #2a513d;border-radius:9px;background:#0b1912;color:#83a794;cursor:pointer;font-size:20px;line-height:1}.gm-share-head>button:hover,.gm-share-head>button:focus-visible{border-color:#4acb90;color:#dff7eb;outline:0}.gm-share-body{display:grid;grid-template-columns:250px minmax(0,1fr);gap:20px;padding:20px}.gm-share-qr{min-height:250px;display:grid;place-items:center;padding:14px;border:1px solid #28543f;border-radius:14px;background:#f7fbf8;color:#5d7467;text-align:center}.gm-share-qr img{display:block;width:218px;height:218px;max-width:100%;object-fit:contain;image-rendering:pixelated}.gm-share-qr img[hidden]{display:none}.gm-share-qr span{max-width:170px;color:#6d8075;font-size:11px;line-height:1.6}.gm-share-info{min-width:0}.gm-share-state{display:inline-flex;align-items:center;gap:7px;margin-bottom:13px;padding:5px 9px;border:1px solid #3c5547;border-radius:999px;background:#15251c;color:#9bb2a5;font-size:10px;font-weight:800}.gm-share-state:before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor}.gm-share-state.is-active{border-color:#337552;background:#103020;color:#5fdca5}.gm-share-state.is-disabled,.gm-share-state.is-warning{border-color:#735b28;background:#2b220d;color:#e5b84f}.gm-share-state.is-error{border-color:#6d3b3f;background:#2a1517;color:#ef7b80}.gm-share-info dl{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:0 0 14px}.gm-share-info dl div{min-width:0;padding:9px 10px;border:1px solid #1e412f;border-radius:9px;background:#091810}.gm-share-info dt{color:#668674;font-size:9px}.gm-share-info dd{margin:2px 0 0;overflow:hidden;text-overflow:ellipsis;color:#cfe5d8;font:700 10px ui-monospace,SFMono-Regular,Consolas,monospace}.gm-share-info>label{display:block;margin-bottom:5px;color:#789988;font-size:9px}.gm-share-link{display:flex;gap:6px}.gm-share-link input{min-width:0;flex:1;height:38px;padding:0 10px;border:1px solid #28513d;border-radius:8px;background:#06110c;color:#b9d7c7;font:9px ui-monospace,SFMono-Regular,Consolas,monospace;outline:0}.gm-share-link input:focus{border-color:#3cc487}.gm-share-link button,.gm-share-actions button,.gm-share-actions a{min-height:38px;display:inline-flex;align-items:center;justify-content:center;padding:0 12px;border:1px solid #2d5c44;border-radius:8px;background:#10251a;color:#8bc6a7;text-decoration:none;font-size:10px;font-weight:750;cursor:pointer}.gm-share-actions [hidden]{display:none!important}.gm-share-link button:hover,.gm-share-actions button:hover,.gm-share-actions a:hover,.gm-share-link button:focus-visible,.gm-share-actions button:focus-visible,.gm-share-actions a:focus-visible{border-color:#49cd92;color:#dff8eb;outline:0}.gm-share-link button:disabled,.gm-share-actions button:disabled{opacity:.45;cursor:not-allowed}.gm-share-help{margin:10px 0 0;color:#658473;font-size:9px;line-height:1.6}.gm-share-actions{display:flex;justify-content:flex-end;gap:7px;padding:0 20px 16px}.gm-share-actions .gm-share-primary{border-color:#42c98b;background:linear-gradient(135deg,#4ed49a,#239c67);color:#052016}.gm-share-actions #gmShareToggle{border-color:#6c3d42;background:#291517;color:#eb8c91}.gm-share-actions #gmShareToggle[data-action="enable"]{border-color:#3d7456;background:#102c1e;color:#72dcaa}.gm-share-message{margin:0 20px 18px;padding:9px 11px;border:1px solid #2f6248;border-radius:9px;background:#10291c;color:#71dda9;font-size:10px}.gm-share-message.is-error{border-color:#713d42;background:#2b1518;color:#f18a8f}.gm-share-message.is-warning{border-color:#765d29;background:#2a210d;color:#e8bd56}.gm-share-dialog.is-loading{cursor:wait}.gm-share-dialog.is-loading .gm-share-body,.gm-share-dialog.is-loading .gm-share-actions{opacity:.68}.gm-share-dialog.is-loading button:not(#gmShareClose){pointer-events:none}
@media(max-width:1550px){.gm-layout{grid-template-columns:250px minmax(500px,1fr) 285px}.gm-overview-item{padding-left:10px;padding-right:10px}.gm-metric strong{font-size:19px}.gm-table th,.gm-table td{padding-left:7px;padding-right:7px}}
@media(max-width:1380px){.gm-layout{grid-template-columns:270px minmax(0,1fr)}.gm-account-pane{grid-row:1/3}.gm-side-stack{grid-column:2;grid-template-columns:repeat(2,minmax(0,1fr))}.gm-events-card{grid-column:1/-1}.gm-status-card,.gm-orders-card{min-height:220px}.gm-overview-time{display:none}}
@media(max-width:1050px){.main-monitor{padding-left:14px;padding-right:14px}.gm-layout{grid-template-columns:245px minmax(0,1fr)}.gm-account-pane{height:calc(100vh - 118px)}.gm-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.gm-overview-item:nth-child(4){display:none}}
@media(max-width:820px){.gm-overviewbar{justify-content:flex-start;overflow:auto}.gm-overview-item{flex:0 0 auto}.gm-layout{display:block}.gm-account-pane{position:relative;top:auto;height:auto;min-height:0;margin-bottom:12px}.gm-account-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));max-height:340px}.gm-workspace{margin-bottom:12px}.gm-side-stack{grid-template-columns:1fr}.gm-events-card{grid-column:auto}}
@media(max-width:600px){.gm-overview-item:nth-child(3),.gm-overview-item:nth-child(4){display:none}.gm-account-list{grid-template-columns:1fr}.gm-selected-title{flex-wrap:wrap}.gm-selected-title h2{max-width:100%;width:100%}.gm-account-head{align-items:flex-start;gap:10px}.gm-head-actions{align-items:flex-end;flex-direction:column}.gm-synced{display:none}.gm-share-button{min-height:38px}.gm-metrics{grid-template-columns:1fr 1fr}.gm-metric{min-height:92px;padding:12px}.gm-metric strong{font-size:17px}.gm-chart-card{height:330px}.gm-chart-head{align-items:flex-start;flex-direction:column}.gm-range{width:100%}.gm-range button{flex:1}.gm-chart-wrap{height:222px}.gm-card-title{align-items:flex-start}.gm-card-pl{margin-top:2px}.gm-table{min-width:650px}.gm-share-modal{align-items:end;padding:8px}.gm-share-dialog{max-height:calc(100dvh - 8px);border-radius:15px 15px 10px 10px}.gm-share-head{padding:15px}.gm-share-body{grid-template-columns:1fr;gap:13px;padding:15px}.gm-share-qr{width:min(230px,100%);min-height:230px;margin:auto}.gm-share-qr img{width:200px;height:200px}.gm-share-info dl{grid-template-columns:1fr 1fr}.gm-share-link{align-items:stretch}.gm-share-link button{flex:0 0 auto}.gm-share-actions{display:grid;grid-template-columns:1fr 1fr;padding:0 15px 14px}.gm-share-actions>*{width:100%}.gm-share-actions .gm-share-primary{grid-column:1/-1}.gm-share-message{margin:0 15px 15px}}
</style>
<?php endif;?>
<?php if($page==='golden_monitor'):?>
<script>
(()=>{
'use strict';
const root=document.getElementById('goldenMonitor');
if(!root)return;
const endpoint=root.dataset.endpoint||'/golden-monitor-data.php';
const shareEndpoint=root.dataset.shareEndpoint||'/golden-performance-admin.php';
const shareQrEndpoint=root.dataset.shareQr||'/golden-performance-qr.php';
const shareCsrf=String(root.dataset.csrf||'');
const byId=id=>document.getElementById(id);
const el={
 serviceSignal:byId('gmServiceSignal'),serviceStatus:byId('gmServiceStatus'),onlineCount:byId('gmOnlineCount'),delayedCount:byId('gmDelayedCount'),riskCount:byId('gmRiskCount'),globalTime:byId('gmGlobalTime'),refresh:byId('gmRefresh'),notice:byId('gmGlobalNotice'),
 accountTotal:byId('gmAccountTotal'),search:byId('gmAccountSearch'),accountList:byId('gmAccountList'),listResult:byId('gmListResult'),filterPositions:byId('gmFilterPositions'),filterRisk:byId('gmFilterRisk'),onlineFilter:byId('gmOnlineFilter'),employeeFilter:byId('gmEmployeeFilter'),holdingFilter:byId('gmHoldingFilter'),riskFilter:byId('gmRiskFilter'),resetFilters:byId('gmResetFilters'),
 selectedName:byId('gmSelectedName'),selectedVersion:byId('gmSelectedVersion'),selectedState:byId('gmSelectedState'),selectedMeta:byId('gmSelectedMeta'),selectedEmployee:byId('gmSelectedEmployee'),selectedInstance:byId('gmSelectedInstance'),selectedSync:byId('gmSelectedSync'),shareButton:byId('gmShareButton'),
 equity:byId('gmEquity'),balanceHint:byId('gmBalanceHint'),floating:byId('gmFloating'),realizedHint:byId('gmRealizedHint'),margin:byId('gmMargin'),marginHint:byId('gmMarginHint'),drawdown:byId('gmDrawdown'),drawdownHint:byId('gmDrawdownHint'),
 chart:byId('gmEquityChart'),chartEmpty:byId('gmChartEmpty'),curveUpdated:byId('gmCurveUpdated'),positionHeading:byId('gmPositionHeading'),positionSummary:byId('gmPositionSummary'),positionProfit:byId('gmPositionProfit'),positionRows:byId('gmPositionRows'),runtimeSubtitle:byId('gmRuntimeSubtitle'),statusList:byId('gmStatusList'),orderHeading:byId('gmOrderHeading'),orderRows:byId('gmOrderRows'),eventCount:byId('gmEventCount'),eventList:byId('gmEventList'),
 shareModal:byId('gmShareModal'),shareDialog:document.querySelector('#gmShareModal .gm-share-dialog'),shareClose:byId('gmShareClose'),shareAccount:byId('gmShareAccount'),shareQr:byId('gmShareQr'),shareQrEmpty:byId('gmShareQrEmpty'),shareState:byId('gmShareState'),shareCreated:byId('gmShareCreated'),shareExpires:byId('gmShareExpires'),shareVisits:byId('gmShareVisits'),shareLastVisit:byId('gmShareLastVisit'),shareUrl:byId('gmShareUrl'),shareCopy:byId('gmShareCopy'),shareCreate:byId('gmShareCreate'),sharePreview:byId('gmSharePreview'),shareRotate:byId('gmShareRotate'),shareToggle:byId('gmShareToggle'),shareMessage:byId('gmShareMessage')
};
const state={accounts:[],selected:null,filter:'all',range:'1d',curve:[],selectionToken:0,curveToken:0,loadingAccounts:false,loadingMore:false,lastServerTime:null,loadedPage:1,accountPages:1,accountTotalCount:0,summary:null,shareCache:new Map(),shareFetchToken:0,shareAccount:'',shareBusy:false,shareLastFocus:null,curveCache:new Map(),curvePending:new Map(),curveProofs:new Map(),curveEpoch:0,apiSequence:0,apiControllers:new Set(),curveDisplayKey:'',curveSignature:'',curveDrawKey:'',curveEmptyMessage:'',curveDisplayEntry:null};
// Memory only: never persist customer curves or authority proofs in browser storage.
const CURVE_POLICY=Object.freeze({entries:24,points:1200,pending:8,proofs:128,refreshMs:60000,proofMs:60000,expireMs:300000});
const FILTER_STORAGE_KEY='golden_monitor_filters_v1';
state.filters={state:'all',employee_id:'all',positions:'all',risk:'all',q:''};state.filterEpoch=0;state.accountsRun=0;state.moreRun=0;state.employees=[];state.employeeDirectoryReady=false;state.curveRemovedOwners=new Map();state.searchTimer=0;
const nf=new Intl.NumberFormat('zh-CN',{maximumFractionDigits:2,minimumFractionDigits:2});
const intf=new Intl.NumberFormat('zh-CN',{maximumFractionDigits:0});

function make(tag,className,text){const n=document.createElement(tag);if(className)n.className=className;if(text!==undefined&&text!==null)n.textContent=String(text);return n}
function finite(value){if(value===null||value===undefined||value==='')return null;const n=Number(value);return Number.isFinite(n)?n:null}
function valueOf(obj,names,fallback=null){for(const name of names){if(obj&&obj[name]!==undefined&&obj[name]!==null&&obj[name]!=='')return obj[name]}return fallback}
function currencyPrefix(account){const code=String(valueOf(account,['currency'],'USD')).toUpperCase();return code==='USD'?'$':code+' '}
function money(value,account,signed=false){const n=finite(value);if(n===null)return '—';const sign=signed&&n>0?'+':'';return sign+currencyPrefix(account)+nf.format(n)}
function pct(value){const n=finite(value);return n===null?'—':nf.format(n)+'%'}
function lots(value){const n=finite(value);return n===null?'—':n.toFixed(Math.abs(n)<1?2:Math.min(2,(String(n).split('.')[1]||'').length))}
function price(value){const n=finite(value);return n===null?'—':n.toLocaleString('zh-CN',{minimumFractionDigits:2,maximumFractionDigits:5})}
function maskAccount(raw){const s=String(raw||'').replace(/\s+/g,'');if(!s)return '账户待上报';if(s.length<=4)return '*'.repeat(Math.max(2,s.length));if(s.length<=7)return s.slice(0,1)+'*'.repeat(s.length-3)+s.slice(-2);return s.slice(0,2)+'*'.repeat(Math.min(5,s.length-4))+s.slice(-2)}
function masked(account){return String(valueOf(account,['masked_account','account_masked'],''))||maskAccount(account&&account.account)}
function safeIdentifierPrefix(value){const s=String(value||'').trim();if(!s)return '—';return s.length>8?s.slice(0,8)+'…':s}
function instanceInfo(account){const snapshot=hasTelemetrySnapshot(account),total=Math.max(0,Math.trunc(finite(account&&account.instance_count)??(snapshot?1:0))),active=Math.max(0,Math.min(total,Math.trunc(finite(account&&account.active_instance_count)??(snapshot&&connectionState(account)!=='offline'?1:0)))),install=String(valueOf(account,['install_id_prefix'],'')||safeIdentifierPrefix(account&&account.install_id)),session=String(valueOf(account,['session_id_prefix'],'')||safeIdentifierPrefix(account&&account.session_id));return{total,active,duplicate:Boolean(account&&account.duplicate_instance)||total>1,install,session}}
function parseTime(value){if(value===null||value===undefined||value==='')return null;const raw=String(value).trim();if(/^\d{10,16}$/.test(raw)){const numeric=Number(raw),milliseconds=numeric<1e12?numeric*1000:numeric;return Number.isFinite(milliseconds)?new Date(milliseconds):null}let ms=Date.parse(raw);if(Number.isNaN(ms)&&/^\d{4}-\d{2}-\d{2} /.test(raw))ms=Date.parse(raw.replace(' ','T')+'Z');return Number.isNaN(ms)?null:new Date(ms)}
function timeText(value,seconds=false){const d=parseTime(value);if(!d)return '时间未上报';const o={month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',hour12:false};if(seconds)o.second='2-digit';return new Intl.DateTimeFormat('zh-CN',o).format(d).replace(/\//g,'-')}
function fullTime(value){const d=parseTime(value);if(!d)return '等待首次同步';return new Intl.DateTimeFormat('zh-CN',{year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false,timeZoneName:'short'}).format(d)}
function ageSeconds(value){const d=parseTime(value);return d?Math.max(0,Math.floor((Date.now()-d.getTime())/1000)):Infinity}
function relativeTime(value){const seconds=ageSeconds(value);if(!Number.isFinite(seconds))return '从未上报';if(seconds<8)return '刚刚';if(seconds<60)return seconds+'秒前';if(seconds<3600)return Math.floor(seconds/60)+'分钟前';if(seconds<86400)return Math.floor(seconds/3600)+'小时前';return Math.floor(seconds/86400)+'天前'}
function versionSupportsTelemetry(value){const m=String(value||'').match(/(?:^|[^0-9])(\d+)\.(\d+)(?:\.(\d+))?/);if(!m)return null;const parts=[Number(m[1]),Number(m[2]),Number(m[3]||0)],minimum=[1,42,0];for(let i=0;i<parts.length;i++){if(parts[i]>minimum[i])return true;if(parts[i]<minimum[i])return false}return true}
function supportsTelemetry(account){if(account&&account.telemetry_supported===false)return false;if(account&&account.telemetry_supported===true)return true;const supported=versionSupportsTelemetry(account&&account.ea_version);return supported===null?true:supported}
function monitoringMessage(account){return String(valueOf(account,['monitoring_message'],'')||'').trim()}
function hasTelemetrySnapshot(account){if(account&&(account.telemetry_ready===false||account.telemetry_available===false||account.monitoring_ready===false))return false;return Boolean(valueOf(account,['instance_id','last_received_at','last_reported_at'],''))}
function connectionState(account){if(!supportsTelemetry(account))return 'unsupported';const explicit=String(valueOf(account,['online_state','connection_state'],'')).toLowerCase();if(['online','delayed','offline'].includes(explicit))return explicit;const age=ageSeconds(valueOf(account,['last_received_at','last_reported_at','updated_at'],''));if(age<=90)return 'online';if(age<=180)return 'delayed';return 'offline'}
function riskLevel(account){const explicit=String(valueOf(account,['risk_level','alert_level'],'')).toLowerCase();if(['normal','warning','high','critical'].includes(explicit))return explicit;const dd=finite(account&&account.drawdown_pct)||0,margin=finite(account&&account.margin_level)||0,layers=finite(account&&account.layer_count)||0;if(dd>=80||(margin>0&&margin<120))return'critical';if(dd>=60||(margin>0&&margin<150)||layers>=10)return'high';if(dd>=30||(margin>0&&margin<250)||layers>=7)return'warning';return'normal'}
function isRisk(account){return riskLevel(account)!=='normal'}
function riskLabel(level){return level==='critical'?'严重风险':level==='high'?'高风险':level==='warning'?'风险预警':''}
function connectionLabel(connection){return connection==='online'?'在线':connection==='delayed'?'数据延迟':connection==='offline'?'离线':'版本不支持'}
function stateLabel(value){const v=String(value||'').trim().toLowerCase();const labels={managing:'持仓管理中',waiting:'等待首单',closing:'正在组合平仓',ambiguous:'交易证据待核验',deal_reconcile:'成交对账中',oco_unwind:'OCO 双成交处置中',first_pending:'双边首单已生效',first_preparing:'空仓准备首单',submitting:'正在提交挂单',reconciling:'成交对账中',close_only:'只平不加',oco_recovery:'OCO 异常恢复中',oco_cancel:'一侧成交撤销对侧',waiting_market:'等待开盘',waiting_quote:'等待有效报价',waiting_spread:'等待点差恢复',idle:'空仓待命',stopped:'EA 已停止'};return labels[v]||String(value||'状态待上报')}
function sideLabel(value){const v=String(value||'').toLowerCase();return ['buy','long','多','多单'].includes(v)?'多单':['sell','short','空','空单'].includes(v)?'空单':['none','flat','unknown',''].includes(v)?'空仓':['hedged','both'].includes(v)?'双向持仓':String(value||'—')}
function sideClass(value){return sideLabel(value)==='多单'?'gm-buy':sideLabel(value)==='空单'?'gm-sell':''}
function orderTypeLabel(value){const v=String(value||'').toLowerCase();const labels={buy_stop:'买入停止',sell_stop:'卖出停止',buy_limit:'买入限价',sell_limit:'卖出限价',buy_stop_limit:'买入停止限价',sell_stop_limit:'卖出停止限价'};return labels[v]||String(value||'挂单').replace(/_/g,' ')}
function orderStateLabel(value){const v=String(value||'').toLowerCase();const labels={active:'活动中',placed:'已生效',pending:'等待触发',partial:'部分成交',cancel_pending:'撤销确认中'};return labels[v]||String(value||'活动中')}
function setTone(node,value,warningMode=false){node.classList.remove('is-positive','is-negative','is-warning');const n=finite(value);if(n===null)return;if(warningMode){if(n>=50)node.classList.add('is-negative');else if(n>=25)node.classList.add('is-warning')}else if(n>0)node.classList.add('is-positive');else if(n<0)node.classList.add('is-negative')}
function showNotice(message,error=false){el.notice.textContent=message;el.notice.classList.toggle('is-error',error);el.notice.hidden=!message}
function markService(ok,message,serverTime){el.serviceSignal.className='gm-signal '+(ok?'gm-signal-ok':'gm-signal-error');el.serviceStatus.textContent=message;if(serverTime){state.lastServerTime=serverTime;el.globalTime.textContent=fullTime(serverTime);el.globalTime.title='服务器时间：'+String(serverTime)}}

async function api(action,params={},requestController=null){
 const url=new URL(endpoint,window.location.origin);url.searchParams.set('action',action);Object.entries(params).forEach(([key,value])=>{if(value!==undefined&&value!==null&&value!=='')url.searchParams.set(key,String(value))});
 const controller=requestController||new AbortController(),timer=setTimeout(()=>controller.abort(),12000),requestId=++state.apiSequence,epoch=state.curveEpoch,filterEpoch=state.filterEpoch;state.apiControllers.add(controller);
 try{
  if(document.hidden)throw Object.assign(new Error('页面已暂停刷新'),{paused:true});
  const response=await fetch(url.toString(),{method:'GET',headers:{Accept:'application/json'},credentials:'same-origin',cache:'no-store',signal:controller.signal});
  let json;try{json=await response.json()}catch(error){throw Object.assign(new Error('服务器返回了无法识别的数据'),{httpStatus:response.status})}
  if(!response.ok||json.ok===false)throw Object.assign(new Error(String(json.error||json.message||('请求失败（HTTP '+response.status+'）'))),{httpStatus:response.status});
  const data=json.data!==undefined?json.data:json;if(epoch===state.curveEpoch&&(action!=='accounts'||filterEpoch===state.filterEpoch))observeCurveResponse(action,data,requestId);
  markService(true,'监控服务正常',json.server_time||new Date().toISOString());
  return data;
 }catch(error){if([401,403,404].includes(error&&error.httpStatus))invalidateCurveAccess(error.message);if(!document.hidden&&!error.paused)markService(false,'监控服务异常',null);if(error&&error.name==='AbortError')throw Object.assign(new Error(document.hidden?'页面已暂停刷新':'监控请求超时'),{paused:document.hidden});throw error}finally{clearTimeout(timer);state.apiControllers.delete(controller)}
}
function shareAccountNumber(account){return String(account&&account.account||account||'').trim()}
function validShareAccount(account){return /^[0-9]{1,20}$/.test(shareAccountNumber(account))}
function shareVisualState(share){
 if(!share)return{key:'unknown',label:'客户二维码',detail:'读取二维码状态'};
 const exists=share.exists===true,licenseValid=share.license_valid===true,current=String(share.state||'none');
 if(!exists)return licenseValid?{key:'none',label:'生成客户二维码',detail:'尚未生成客户绩效链接'}:{key:'invalid',label:'二维码暂不可用',detail:'授权正常后才可生成'};
 if(current==='disabled')return{key:'disabled',label:'二维码已停用',detail:'分享链接目前不可访问'};
 if(current==='active'&&share.effective===true)return{key:'active',label:'二维码已启用',detail:'客户绩效链接正常'};
 return{key:'invalid',label:'二维码暂不可用',detail:'链接已因授权状态暂停'};
}
function shareErrorMessage(code,status){
 const messages={admin_required:'后台登录已失效，请刷新页面重新登录',invalid_account:'账户编号无效',not_found:'没有找到该黄金战神授权账户',invalid_parameter:'请求参数不正确',invalid_action:'不支持该操作',license_inactive:'账户必须处于正常授权且未到期状态',risk_not_accepted:'当前强制风险协议尚未有效签署，已按只平不开处理，不能生成或启用绩效二维码',share_not_found:'该账户尚未生成二维码',method_not_allowed:'服务器不允许该请求',operation_failed:'服务器暂时无法完成操作，请稍后重试',csrf_invalid:'页面安全令牌已过期，请刷新后台后重试',network_timeout:'请求超时，请检查网络后重试',invalid_response:'服务器响应异常，请稍后重试'};
 if(status===401)return messages.admin_required;if(status===403)return messages.csrf_invalid;return messages[code]||'二维码管理暂时不可用，请稍后重试';
}
async function shareApi(account,action=''){
 const url=new URL(shareEndpoint,window.location.origin);if(url.origin!==window.location.origin)throw Object.assign(new Error('invalid share endpoint'),{shareCode:'invalid_response'});
 const method=action?'POST':'GET',controller=new AbortController(),timer=setTimeout(()=>controller.abort(),12000),options={method,headers:{Accept:'application/json'},credentials:'same-origin',cache:'no-store',signal:controller.signal};
 if(action){const body=new URLSearchParams({csrf:shareCsrf,account:shareAccountNumber(account),action});options.headers['Content-Type']='application/x-www-form-urlencoded;charset=UTF-8';options.body=body.toString()}else url.searchParams.set('account',shareAccountNumber(account));
 try{
  const response=await fetch(url.toString(),options);let json=null;try{json=await response.json()}catch(_){const error=new Error('invalid_response');error.shareCode=response.status===403?'csrf_invalid':'invalid_response';error.shareStatus=response.status;throw error}
  if(!response.ok||!json||json.ok!==true||!json.share||typeof json.share!=='object'){const error=new Error(String(json&&json.error||'operation_failed'));error.shareCode=String(json&&json.error||'operation_failed');error.shareStatus=response.status;throw error}
  return json.share;
 }catch(error){if(error&&error.name==='AbortError'){const timeout=new Error('network_timeout');timeout.shareCode='network_timeout';throw timeout}throw error}finally{clearTimeout(timer)}
}
function shareTime(value,empty='—'){const d=parseTime(value);if(!d)return empty;return new Intl.DateTimeFormat('zh-CN',{year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false}).format(d).replace(/\//g,'-')}
function setShareMessage(message,tone=''){el.shareMessage.textContent=message;el.shareMessage.className='gm-share-message'+(tone?' is-'+tone:'');el.shareMessage.hidden=!message}
function refreshShareButton(account){
 const number=shareAccountNumber(account),valid=validShareAccount(number),visual=shareVisualState(valid?state.shareCache.get(number):null);el.shareButton.disabled=!valid;el.shareButton.dataset.shareState=visual.key;el.shareButton.textContent='▦ '+visual.label;el.shareButton.title=visual.detail;
}
function rememberShare(account,share,refreshList=true){const number=shareAccountNumber(account);if(!validShareAccount(number))return;state.shareCache.set(number,share);if(state.selected&&shareAccountNumber(state.selected)===number)refreshShareButton(state.selected);if(refreshList)renderAccountList()}
function resetSharePanel(){
 el.shareState.textContent='正在读取';el.shareState.className='gm-share-state';el.shareCreated.textContent='—';el.shareExpires.textContent='跟随黄金战神授权';el.shareVisits.textContent='—';el.shareLastVisit.textContent='—';el.shareUrl.value='';el.shareCopy.disabled=true;el.shareQr.hidden=true;el.shareQr.removeAttribute('src');el.shareQrEmpty.hidden=false;el.shareQrEmpty.textContent='正在读取二维码状态…';el.shareCreate.hidden=false;el.shareCreate.disabled=true;el.sharePreview.hidden=true;el.sharePreview.removeAttribute('href');el.shareRotate.hidden=true;el.shareToggle.hidden=true;
}
function renderShare(share){
 const exists=share&&share.exists===true,licenseValid=share&&share.license_valid===true,shareState=String(share&&share.state||'none'),effective=share&&share.effective===true,url=exists?String(share.url||''):'',visual=shareVisualState(share),busy=state.shareBusy;
 el.shareState.textContent=visual.key==='active'?'已启用':visual.key==='disabled'?'已停用':visual.key==='invalid'?'授权暂停':'未生成';el.shareState.className='gm-share-state '+(visual.key==='active'?'is-active':visual.key==='disabled'?'is-disabled':visual.key==='invalid'?'is-warning':'');
 el.shareCreated.textContent=exists?shareTime(share.created_at,'时间未记录'):'—';el.shareExpires.textContent=share&&share.expires_at?String(share.expires_at)+'（跟随授权）':'永久（跟随授权）';el.shareVisits.textContent=intf.format(Math.max(0,Number(share&&share.access_count)||0))+' 次';el.shareLastVisit.textContent=exists?shareTime(share.last_access_at,'尚未访问'):'尚未访问';el.shareUrl.value=url;el.shareCopy.disabled=busy||!url;
 el.shareCreate.hidden=exists;el.shareCreate.disabled=busy||!licenseValid;el.shareCreate.textContent=licenseValid?'首次生成':'授权正常后可生成';el.shareRotate.hidden=!exists;el.shareRotate.disabled=busy||!licenseValid;el.sharePreview.hidden=!exists||!url;el.sharePreview.href=url||'#';el.shareToggle.hidden=!exists;el.shareToggle.dataset.action=shareState==='disabled'?'enable':'disable';el.shareToggle.textContent=shareState==='disabled'?'重新启用':'停用';el.shareToggle.disabled=busy||(shareState==='disabled'&&!licenseValid);
 if(exists&&validShareAccount(state.shareAccount)){
  const qrUrl=new URL(shareQrEndpoint,window.location.origin);qrUrl.searchParams.set('account',state.shareAccount);const qrGeneration=String(share.generation||share.selector_prefix||'1');
  el.shareQr.alt=maskAccount(state.shareAccount)+' 客户专属绩效二维码';el.shareQr.onload=()=>{if(state.shareAccount&&el.shareQr.src===qrUrl.toString()){el.shareQr.hidden=false;el.shareQrEmpty.hidden=true}};el.shareQr.onerror=()=>{el.shareQr.hidden=true;el.shareQrEmpty.hidden=false;el.shareQrEmpty.textContent='二维码读取失败，请稍后重试'};if(el.shareQr.dataset.generation!==qrGeneration||el.shareQr.src!==qrUrl.toString()){el.shareQr.hidden=true;el.shareQrEmpty.hidden=false;el.shareQrEmpty.textContent='正在生成二维码预览…';el.shareQr.dataset.generation=qrGeneration;el.shareQr.removeAttribute('src');el.shareQr.src=qrUrl.toString()}
 }else{el.shareQr.hidden=true;el.shareQr.removeAttribute('src');el.shareQrEmpty.hidden=false;el.shareQrEmpty.textContent=licenseValid?'首次生成后显示二维码':'授权恢复正常后可生成二维码'}
 if(!licenseValid){const reason=String(share&&share.license_reason||'');setShareMessage(reason==='license_expired'?'授权已经到期，绩效链接已自动失效':reason==='license_inactive'?'账户当前不是“正常使用”，绩效链接已自动暂停':reason.startsWith('risk_')?'当前强制风险协议尚未有效签署，绩效链接已按只平不开门禁自动停用':'授权状态不满足分享条件，暂时不能生成或启用二维码','warning')}
 if(exists&&!effective&&licenseValid&&shareState!=='disabled')setShareMessage('该分享记录当前不可用，请重新生成后再发送给客户','warning');
}
async function loadShare(account,force=true){
 const number=shareAccountNumber(account);if(!validShareAccount(number))return;const token=++state.shareFetchToken,cached=state.shareCache.get(number);if(cached)renderShare(cached);else resetSharePanel();state.shareBusy=true;el.shareDialog.classList.add('is-loading');setShareMessage('正在读取二维码状态…');
 try{const share=force||!cached?await shareApi(number):cached;if(token!==state.shareFetchToken||state.shareAccount!==number)return;rememberShare(number,share,false);setShareMessage('');renderShare(share)}catch(error){if(token!==state.shareFetchToken||state.shareAccount!==number)return;setShareMessage(shareErrorMessage(error&&error.shareCode,error&&error.shareStatus),'error');if(cached)renderShare(cached);else{resetSharePanel();el.shareState.textContent='读取失败';el.shareState.className='gm-share-state is-error'}}finally{if(token===state.shareFetchToken&&state.shareAccount===number){state.shareBusy=false;el.shareDialog.classList.remove('is-loading');const current=state.shareCache.get(number);if(current)renderShare(current)}}
}
async function primeShare(account){const number=shareAccountNumber(account);if(!validShareAccount(number)||state.shareCache.has(number))return;try{const share=await shareApi(number);rememberShare(number,share,true)}catch(_){/* 快捷状态读取失败不影响实时监控。 */}}
function openShare(account){
 const number=shareAccountNumber(account);if(!validShareAccount(number))return;state.shareLastFocus=document.activeElement;state.shareAccount=number;el.shareAccount.textContent=masked(account)+' · '+String(account&&account.customer_name||'未备注客户');el.shareModal.hidden=false;document.body.classList.add('gm-share-open');setShareMessage('');const cached=state.shareCache.get(number);if(cached)renderShare(cached);else resetSharePanel();requestAnimationFrame(()=>el.shareClose.focus());loadShare(number,true);
}
function closeShare(){if(el.shareModal.hidden)return;state.shareFetchToken++;state.shareAccount='';state.shareBusy=false;el.shareDialog.classList.remove('is-loading');el.shareModal.hidden=true;document.body.classList.remove('gm-share-open');el.shareQr.onload=null;el.shareQr.onerror=null;el.shareQr.removeAttribute('src');const last=state.shareLastFocus;state.shareLastFocus=null;if(last&&typeof last.focus==='function')last.focus()}
async function mutateShare(action){
 const number=state.shareAccount,current=state.shareCache.get(number);if(!validShareAccount(number)||state.shareBusy)return;if(action==='rotate'&&!confirm('重新生成后，当前二维码和旧链接会立即失效。\n确定为账户 '+number+' 生成新二维码吗？'))return;if(action==='disable'&&!confirm('停用后，客户打开现有二维码将立即显示“报告已停用”。\n确定停用账户 '+number+' 的绩效报告吗？'))return;if(action==='enable'&&!confirm('确定重新启用账户 '+number+' 的现有绩效链接吗？'))return;
 const pending={create:'正在生成客户专属二维码…',rotate:'正在重新生成，旧链接将立即失效…',disable:'正在停用绩效报告…',enable:'正在重新启用绩效报告…'},success={create:'客户专属二维码已生成，可以复制链接或扫码预览。',rotate:'新二维码已生成，旧二维码和旧链接已经失效。',disable:'绩效报告已停用，现有链接已立即失效。',enable:'绩效报告已重新启用，原链接可以继续使用。'};state.shareBusy=true;el.shareDialog.classList.add('is-loading');setShareMessage(pending[action]||'正在处理…');if(current)renderShare(current);
 try{const share=await shareApi(number,action);if(state.shareAccount!==number)return;rememberShare(number,share,true);setShareMessage(success[action]||'操作已完成');renderShare(share)}catch(error){if(state.shareAccount===number){setShareMessage(shareErrorMessage(error&&error.shareCode,error&&error.shareStatus),'error');if(current)renderShare(current)}}finally{if(state.shareAccount===number){state.shareBusy=false;el.shareDialog.classList.remove('is-loading');const fresh=state.shareCache.get(number);if(fresh)renderShare(fresh)}}
}
async function copyShareLink(){const value=el.shareUrl.value.trim();if(!value)return;let copied=false;try{await navigator.clipboard.writeText(value);copied=true}catch(_){el.shareUrl.focus();el.shareUrl.select();try{copied=document.execCommand('copy')}catch(__){copied=false}el.shareUrl.setSelectionRange(0,0)}setShareMessage(copied?'绩效链接已复制，可以直接发给客户。':'复制失败，请手动选中链接复制',copied?'':'error')}
function identityParams(account){if(account&&account.instance_id)return{instance_id:account.instance_id};return{account:account&&account.account||'',trade_server:account&&account.trade_server||'',install_id:account&&account.install_id||''}}
function accountKey(account){return String(valueOf(account,['instance_id'],'')||[account&&account.account||'',account&&account.trade_server||'',account&&account.install_id||''].join('|'))}
function normalizeItems(data,names){if(Array.isArray(data))return data;for(const name of names){if(data&&Array.isArray(data[name]))return data[name]}return[]}

function validEmployeeId(value){return typeof value==='string'&&value.length>0&&[...value].length<=96&&!/[\u0000-\u001f\u007f]/.test(value)}
function employeeLabel(account){if(account&&account.employee_label)return String(account.employee_label);return account&&account.employee_id?String(account.employee_name||'已分配员工'):'未分配'}
function instanceFinanceNote(account){if(account&&account.is_primary===false)return'只读/历史实例 · 不计汇总';return account&&account.financial_representative===false?'非唯一有效代表 · 不计汇总':''}
function filterParams(page=1){return Object.assign({},state.filters,{page,per_page:100})}
function filtersAreAll(){return state.filters.state==='all'&&state.filters.employee_id==='all'&&state.filters.positions==='all'&&state.filters.risk==='all'&&!state.filters.q}
function restoreMonitorFilters(){
 try{const saved=JSON.parse(window.localStorage.getItem(FILTER_STORAGE_KEY)||'null');if(!saved||typeof saved!=='object')return;if(['all','online','delayed','offline'].includes(saved.state))state.filters.state=saved.state;if(validEmployeeId(saved.employee_id))state.filters.employee_id=saved.employee_id;if(['all','holding'].includes(saved.positions))state.filters.positions=saved.positions;if(['all','attention'].includes(saved.risk))state.filters.risk=saved.risk}catch(_){}
}
function saveMonitorFilters(){
 // Only non-sensitive preferences. Never write search text, accounts, employee names or curves.
 const employee=state.filters.employee_id;if(!['all','unassigned'].includes(employee)&&!state.employees.some(item=>String(item.employee_id)===employee))return;
 try{window.localStorage.setItem(FILTER_STORAGE_KEY,JSON.stringify({state:state.filters.state,employee_id:employee,positions:state.filters.positions,risk:state.filters.risk}))}catch(_){}
}
function syncFilterControls(){el.onlineFilter.value=state.filters.state;el.employeeFilter.value=state.filters.employee_id;el.holdingFilter.classList.toggle('is-active',state.filters.positions==='holding');el.holdingFilter.setAttribute('aria-pressed',String(state.filters.positions==='holding'));el.riskFilter.classList.toggle('is-active',state.filters.risk==='attention');el.riskFilter.setAttribute('aria-pressed',String(state.filters.risk==='attention'));el.search.value=state.filters.q}
function renderEmployeeOptions(employees){
 const unique=new Map();normalizeItems(employees,['items']).forEach(item=>{const id=String(item&&item.employee_id||'');if(validEmployeeId(id)&&!['all','unassigned'].includes(id))unique.set(id,item)});state.employees=Array.from(unique.values());state.employeeDirectoryReady=true;el.employeeFilter.replaceChildren();[['all','全部员工'],['unassigned','未分配']].forEach(([id,label])=>{const option=make('option','',label);option.value=id;el.employeeFilter.append(option)});
 const names=new Map(),suffixes=new Map();state.employees.forEach(item=>{const name=String(item.employee_name||item.employee_label||'员工'),key=JSON.stringify([name,String(item.employee_id).slice(-8)]);names.set(name,(names.get(name)||0)+1);suffixes.set(key,(suffixes.get(key)||0)+1)});state.employees.forEach(item=>{const id=String(item.employee_id),name=String(item.employee_name||item.employee_label||'员工'),base=String(item.employee_label||item.employee_name||'员工'),suffix=suffixes.get(JSON.stringify([name,id.slice(-8)]))>1?id:id.slice(-8),option=make('option','',base+(names.get(name)>1?' · #'+suffix:''));option.value=id;el.employeeFilter.append(option)});
 if(!['all','unassigned'].includes(state.filters.employee_id)&&!unique.has(state.filters.employee_id)){changeMonitorFilters({employee_id:'all'});return false}syncFilterControls();return true;
}
function matchesMonitorFilters(account){
 if(!account)return false;const f=state.filters,employee=String(account.employee_id||''),connection=connectionState(account);if(f.state!=='all'&&(connection==='unsupported'?'offline':connection)!==f.state)return false;if(f.employee_id==='unassigned'&&employee)return false;if(!['all','unassigned'].includes(f.employee_id)&&employee!==f.employee_id)return false;if(f.positions==='holding'&&!(Number(account.position_count)>0))return false;if(f.risk==='attention'&&!isRisk(account))return false;
 if(!f.q)return true;return [account.customer_name,account.account,account.trade_server,account.symbol,account.ea_version,account.employee_name].join(' ').toLowerCase().includes(f.q.toLowerCase());
}
function changeMonitorFilters(changes,debounce=false){
 const next=Object.assign({},state.filters,changes);next.q=normalizeMonitorSearch(next.q);if(JSON.stringify(next)===JSON.stringify(state.filters))return;state.filters=next;state.filterEpoch++;state.accountsRun++;state.moreRun++;if(state.accountsController)state.accountsController.abort();if(state.moreController)state.moreController.abort();state.loadingAccounts=false;state.loadingMore=false;state.loadedPage=1;state.accountPages=1;state.accountTotalCount=0;state.summary=null;clearTimeout(state.searchTimer);saveMonitorFilters();syncFilterControls();
 state.selectionToken++;state.curveToken++;if(state.selected&&!matchesMonitorFilters(state.selected)){state.selected=null;closeShare();renderEmptyWorkspace()}state.accounts=state.accounts.filter(matchesMonitorFilters);renderAccountList();el.accountTotal.textContent='正在按组合条件筛选…';el.listResult.textContent='正在读取';[el.onlineCount,el.delayedCount,el.riskCount,el.filterPositions,el.filterRisk].forEach(node=>{node.textContent='—'});el.accountList.setAttribute('aria-busy','true');
 if(debounce)state.searchTimer=setTimeout(()=>loadAccounts(true),300);else loadAccounts(true);
}
function normalizeMonitorSearch(value){let result='',bytes=0;for(const char of String(value||'').trim()){const cp=char.codePointAt(0),size=cp>65535?4:cp>2047?3:cp>127?2:1;if(bytes+size>64)break;bytes+=size;result+=char}return result}

function renderAccountCounts(){
 const accounts=state.accounts,summary=state.summary||{},online=finite(summary.online_count)??accounts.filter(a=>connectionState(a)==='online').length,delayed=(finite(summary.delayed_count)??accounts.filter(a=>connectionState(a)==='delayed').length)+(finite(summary.offline_count)??accounts.filter(a=>['offline','unsupported'].includes(connectionState(a))).length),riskRows=finite(summary.risk_count)??accounts.filter(isRisk).length,risks=finite(summary.risk_accounts)??new Set(accounts.filter(isRisk).map(a=>a.account)).size,positions=finite(summary.holding_count)??accounts.filter(a=>(finite(a.position_count)||0)>0).length,total=state.accountTotalCount,unique=finite(summary.account_count)??new Set(accounts.map(a=>a.account)).size,instances=finite(summary.instance_count)??accounts.filter(a=>a.instance_id).length,pending=Math.max(0,(finite(summary.items_total)??total)-instances);
 el.onlineCount.textContent=intf.format(finite(summary.online_accounts)??new Set(accounts.filter(a=>connectionState(a)==='online'&&a.is_primary!==false&&!a.history_server).map(a=>a.account+'|'+a.trade_server)).size);el.onlineCount.title='当前筛选在线连接实例 '+intf.format(online)+' 个；上方数值按当前有效实例账户去重，同一账户只计一次';el.delayedCount.textContent=intf.format(delayed);el.riskCount.textContent=intf.format(risks);el.accountTotal.textContent='筛选结果 '+intf.format(unique)+' 账户 · '+intf.format(instances)+' 实例'+(pending?' · '+intf.format(pending)+' 待上报':'');el.filterPositions.textContent=positions;el.filterRisk.textContent=riskRows;
}
function filteredAccounts(){return state.accounts}
function renderAccountList(){
 const items=filteredAccounts();el.accountList.replaceChildren();el.listResult.textContent='已载入 '+items.length+' / '+state.accountTotalCount+' 条';
 if(!items.length){const empty=make('div','gm-placeholder');empty.append(make('b','',filtersAreAll()?'暂无黄金战神监控数据':'没有符合组合条件的客户'),make('span','',filtersAreAll()?'客户成功上报后会自动出现':'可调整在线、员工、持仓、风险条件，或点击重置'));el.accountList.append(empty)}
 const selectedKey=state.selected?accountKey(state.selected):'';
  items.forEach(account=>{
  const conn=connectionState(account),level=riskLevel(account),risk=level!=='normal',entry=make('div','gm-account-entry'),row=make('button','gm-account-row'+(accountKey(account)===selectedKey?' is-selected':'')+(level==='warning'?' is-warning':risk?' is-risk':''));row.type='button';row.dataset.key=accountKey(account);row.setAttribute('aria-pressed',accountKey(account)===selectedKey?'true':'false');
  const instance=instanceInfo(account),top=make('div','gm-account-top'),person=make('div','gm-account-person'),avatar=make('i','',String(account.customer_name||'客').slice(0,1)),identity=make('div'),name=make('b','',masked(account)+' · '+String(account.customer_name||'未备注客户')),meta=make('small','',[account.trade_server||'服务器待上报',account.ea_version?'V'+String(account.ea_version).replace(/^V/i,''):'版本未知'].join(' · ')),instanceMeta=make('small','gm-account-instance'+(instance.duplicate?' is-duplicate':''),instance.total?'检测到'+instance.total+'个实例（'+instance.active+'个活跃） · 安装 '+instance.install+' · 会话 '+instance.session:'尚未检测到终端实例'),employeeMeta=make('small','gm-account-employee','员工：'+employeeLabel(account)+(instanceFinanceNote(account)?' · '+instanceFinanceNote(account):''));employeeMeta.title=employeeMeta.textContent;identity.append(name,meta,employeeMeta,instanceMeta);person.append(avatar,identity);
  const badge=make('span','gm-account-badge is-'+(level==='warning'?'delayed':risk?'risk':conn),risk?riskLabel(level):connectionLabel(conn));top.append(person,badge);
  const bottom=make('div','gm-account-bottom'),left=make('span','',((account.direction?sideLabel(account.direction):'空仓')+' · '+(finite(account.layer_count)||0)+'层 · '+(finite(account.position_count)||0)+'仓')),profit=finite(account.strategy_floating),right=make('b',profit===null?'':(profit>=0?'is-positive':'is-negative'),money(profit,account,true));bottom.append(left,right);row.append(top,bottom);row.addEventListener('click',()=>selectAccount(account));
  const cached=state.shareCache.get(shareAccountNumber(account)),shareView=shareVisualState(cached),shortcut=make('button','gm-share-shortcut');shortcut.type='button';shortcut.dataset.shareState=shareView.key;shortcut.title=shareView.detail+' · 点击管理';shortcut.setAttribute('aria-label',masked(account)+' '+shareView.detail);shortcut.append(make('span','', '▦'));shortcut.lastChild.setAttribute('aria-hidden','true');shortcut.addEventListener('click',()=>{selectAccount(account);openShare(account)});entry.append(row,shortcut);el.accountList.append(entry);
  });
  if(state.loadedPage<state.accountPages){const more=make('button','gm-load-more','加载更多（已载入 '+state.accounts.length+' / '+state.accountTotalCount+'）');more.type='button';more.disabled=state.loadingMore;more.addEventListener('click',loadMoreAccounts);el.accountList.append(more)}
}
function renderSelectedHeader(account){
 el.selectedEmployee.textContent='所属员工：'+employeeLabel(account)+(instanceFinanceNote(account)?' · '+instanceFinanceNote(account):'');el.selectedEmployee.title=el.selectedEmployee.textContent;
 const conn=connectionState(account),risk=riskLevel(account),version=String(account.ea_version||'').replace(/^V/i,''),mode=String(account.account_mode||'').toLowerCase(),modeText=['real','live'].includes(mode)?'实盘账户':mode==='demo'?'模拟账户':account.account_mode||'账户类型待上报',instance=instanceInfo(account);el.selectedName.textContent=masked(account)+' · '+String(account.customer_name||'未备注客户');el.selectedVersion.textContent=version?'V'+version:'版本未知';el.selectedState.textContent=risk!=='normal'?riskLabel(risk):connectionLabel(conn);el.selectedState.className='gm-live-badge '+(risk==='critical'||risk==='high'?'is-risk':risk==='warning'?'is-delayed':'is-'+conn);el.selectedMeta.textContent=[account.trade_server||'交易服务器待上报',account.symbol||'品种待上报',modeText].join(' · ');el.selectedInstance.textContent=instance.total?'检测到'+instance.total+'个实例（'+instance.active+'个活跃） · 当前安装 '+instance.install+' · 会话 '+instance.session:'尚未检测到终端实例';el.selectedInstance.classList.toggle('is-duplicate',instance.duplicate);el.selectedInstance.title=el.selectedInstance.textContent;const seen=valueOf(account,['last_received_at','last_reported_at'],'');el.selectedSync.textContent='数据 '+relativeTime(seen);el.selectedSync.title=seen?'最后接收：'+fullTime(seen):'尚未收到监控快照';refreshShareButton(account);
}
function clearMetric(node,hint,text='—'){node.textContent=text;node.classList.remove('is-positive','is-negative','is-warning');if(hint)hint.textContent='等待终端上报'}
function renderMetrics(account){
 if(!supportsTelemetry(account)||!hasTelemetrySnapshot(account)){[el.equity,el.floating,el.margin,el.drawdown].forEach(node=>clearMetric(node));el.balanceHint.textContent='等待监控快照';el.realizedHint.textContent='等待监控快照';el.marginHint.textContent='等待监控快照';el.drawdownHint.textContent='等待监控快照';return}
 const equity=finite(account.equity),balance=finite(account.balance),floating=finite(account.strategy_floating),realized=finite(account.strategy_realized),margin=finite(account.margin_level),drawdown=finite(account.drawdown_pct),realizedReady=account.realized_ready!==false&&account.backfill_complete!==false;
 el.equity.textContent=money(equity,account);el.balanceHint.textContent='账户余额 '+money(balance,account);el.floating.textContent=money(floating,account,true);setTone(el.floating,floating);el.realizedHint.textContent=realizedReady?'累计已实现 '+money(realized,account,true):'累计收益正在回补';el.margin.textContent=margin===null?'—':pct(margin);el.marginHint.textContent=margin===null?'等待终端上报':margin<=150?'低于 150% 安全参考线':'安全参考线 150%';el.margin.classList.remove('is-positive','is-negative','is-warning');if(margin!==null)el.margin.classList.add(margin<=150?'is-negative':margin<=250?'is-warning':'is-positive');el.drawdown.textContent=drawdown===null?'—':pct(drawdown);setTone(el.drawdown,drawdown,true);el.drawdownHint.textContent=drawdown===null?'等待终端上报':'按账户净值峰值计算';
}
function durationSince(value){const seconds=ageSeconds(value);if(!Number.isFinite(seconds))return '—';const days=Math.floor(seconds/86400),hours=Math.floor(seconds%86400/3600),minutes=Math.floor(seconds%3600/60);return days?days+'天 '+hours+'小时':hours?hours+'小时 '+minutes+'分':minutes+'分钟'}
function appendCell(row,text,className=''){const td=make('td',className,text);row.append(td);return td}
function renderPositions(positions,account){
 const items=Array.isArray(positions)?positions:[];el.positionRows.replaceChildren();el.positionHeading.textContent='('+items.length+')';let totalLots=0,totalProfit=0;items.forEach(position=>{const rowNet=(finite(position.profit)||0)+(finite(position.swap)||0)+(finite(position.commission)||0);totalLots+=finite(position.volume)||0;totalProfit+=rowNet;const row=make('tr'),side=sideLabel(position.side||position.direction),first=appendCell(row,(side==='多单'?'↑ ':'↓ ')+side,sideClass(side));appendCell(row,(finite(position.layer)||0)+'层');appendCell(row,lots(position.volume));appendCell(row,price(position.open_price));appendCell(row,price(position.current_price));const profitCell=appendCell(row,money(rowNet,account,true),rowNet>=0?'is-positive':'is-negative');profitCell.title='持仓盈亏 '+money(position.profit,account,true)+' · 库存费 '+money(position.swap,account,true)+' · 手续费 '+money(position.commission,account,true);appendCell(row,durationSince(position.opened_at));const opened=appendCell(row,timeText(position.opened_at,true));opened.title=position.opened_at?fullTime(position.opened_at):'时间未上报';el.positionRows.append(row)});
 if(!items.length){const row=make('tr');const cell=appendCell(row,'当前没有黄金战神持仓','gm-empty-cell');cell.colSpan=8;el.positionRows.append(row)}el.positionSummary.textContent=items.length?'共 '+lots(totalLots)+' 手 · 仅统计黄金战神专属 Magic':'仅展示黄金战神自身订单，不包含手动单和其他 EA';el.positionProfit.textContent=money(totalProfit,account,true);setTone(el.positionProfit,totalProfit);
}
function renderOrders(orders){
 const items=Array.isArray(orders)?orders:[];el.orderRows.replaceChildren();el.orderHeading.textContent='('+items.length+')';items.forEach(order=>{const row=make('tr'),side=sideLabel(order.side),type=orderTypeLabel(order.type),first=appendCell(row,type+' · '+side,sideClass(side));first.title=order.symbol||'';appendCell(row,price(order.price));appendCell(row,lots(valueOf(order,['volume_current','volume_initial','volume'],null)));appendCell(row,orderStateLabel(order.status),'gm-order-state');el.orderRows.append(row)});if(!items.length){const row=make('tr');const cell=appendCell(row,'当前没有本 EA 活动挂单','gm-empty-cell');cell.colSpan=4;el.orderRows.append(row)}
}
function addStatus(title,description,level,time){const item=make('div','gm-status-item'),icon=make('span','gm-status-icon'+(level==='warning'?' is-warning':level==='error'?' is-error':''),level==='error'?'!':level==='warning'?'!':'✓'),main=make('div','gm-status-main');main.append(make('b','',title),make('small','',description));const clock=make('time','',time||'');item.append(icon,main,clock);el.statusList.append(item)}
function renderStatus(account){
 el.statusList.replaceChildren();const conn=connectionState(account),stamp=timeText(valueOf(account,['last_reported_at','last_received_at'],''),true),stateText=String(account.strategy_state_text||'').trim();el.runtimeSubtitle.textContent=stateText||stateLabel(account.strategy_state);
 const monitorMessage=monitoringMessage(account);if(monitorMessage)el.statusList.append(make('div','gm-unsupported',monitorMessage));
 const license=String(account.effective_license_status||account.license_status||'').toLowerCase();addStatus(license==='active'?'授权有效':license==='close_only'?'授权仅管理仓位':license==='expired'?'授权已到期':license==='disabled'?'授权已停用':license==='pending'?'等待后台授权':'授权状态待确认',account.expires_at?'有效期至 '+account.expires_at:'永久授权或有效期未上报',license==='active'?'ok':license==='close_only'||license==='pending'?'warning':'error',stamp);
 if(!supportsTelemetry(account)){addStatus('等待升级 V1.42',monitorMessage||'旧版本不会上报持仓、挂单和策略曲线','warning',stamp);return}
 if(!hasTelemetrySnapshot(account)){addStatus('尚未收到监控快照',monitorMessage||'请确认 V1.42 已运行并允许 WebRequest','warning',stamp);return}
 const instance=instanceInfo(account);addStatus('检测到'+instance.total+'个实例','同账户及交易服务器 · '+instance.active+'个活跃 · 当前安装 '+instance.install+' · 会话 '+instance.session,instance.duplicate?'warning':'ok',stamp);
 const consent=String(account.monitoring_consent_status||'required').toLowerCase(),consentAt=timeText(account.monitoring_consent_accepted_at,true),consentVersion=String(account.monitoring_consent_version||'');addStatus(consent==='accepted'?'监控告知已确认':consent==='invalid'?'监控告知证据异常':'监控告知未确认',consent==='accepted'?(consentVersion+' · 终端确认 '+consentAt):consent==='invalid'?'告知版本、摘要或来源与当前版本不一致':'请客户在 MT5 图表内确认只读监控数据告知',consent==='accepted'?'ok':consent==='invalid'?'error':'warning',consentAt||stamp);
 const strategy=stateText||stateLabel(account.strategy_state),strategyRaw=String(account.strategy_state||'').toLowerCase(),strategyLevel=['ambiguous'].includes(strategyRaw)?'error':(!account.strategy_state||['close_only','oco_recovery','reconciling','deal_reconcile','oco_unwind','closing'].includes(strategyRaw)?'warning':'ok');addStatus(strategy,account.strategy_state?'EA 当前交易状态':'等待下一次完整状态快照',strategyLevel,stamp);
 if(account.realized_ready===false||account.backfill_complete===false)addStatus('历史收益回补中','累计收益与策略权益曲线暂不完整，完成后会自动显示','warning',stamp);
 const reconcileActive=valueOf(account,['reconcile_active'],null),reconcValue=valueOf(account,['reconciliation_state','reconcile_state'],null),reconc=String(reconcValue||'').toLowerCase(),reconcOk=reconcileActive===false||reconc==='normal'||reconc==='ok',reconcBusy=reconcileActive===true||(!reconcOk&&reconcValue!==null);addStatus(reconcBusy?'成交对账中':reconcOk?'成交对账正常':'成交对账状态未上报',reconcBusy?'正在等待服务器成交终态':reconcOk?'实时持仓与成交账本一致':'等待下一次完整状态快照',reconcBusy||!reconcOk?'warning':'ok',stamp);
 const terminal=account.terminal_connected;addStatus(terminal===false?'MT5 终端未连接':terminal===true?'MT5 终端连接':'终端连接状态未上报',terminal===false?'终端报告已断开交易服务器':terminal===true?'交易服务器连接状态正常':'等待下一次完整状态快照',terminal===false?'error':terminal===true?'ok':'warning',stamp);
 const algo=account.algo_trading;addStatus(algo===false?'自动交易已关闭':algo===true?'自动交易已开启':'自动交易状态未上报',algo===false?'EA 不能提交新交易请求':algo===true?'终端允许 EA 执行交易':'等待下一次完整状态快照',algo===false?'error':algo===true?'ok':'warning',stamp);
 addStatus(conn==='online'?'数据更新正常':conn==='delayed'?'数据更新延迟':'数据已离线',conn==='online'?'最近 90 秒内收到终端快照':conn==='delayed'?'已超过 90 秒未收到快照':'已超过 3 分钟未收到快照',conn==='online'?'ok':conn==='delayed'?'warning':'error',stamp);
}
function retainFilteredSelection(instance){if(matchesMonitorFilters(instance))return true;state.accounts=state.accounts.filter(item=>accountKey(item)!==accountKey(instance));state.selected=null;state.selectionToken++;state.curveToken++;closeShare();renderAccountList();renderEmptyWorkspace();state.resumeRefresh=true;resumeMonitorRefresh();return false}
function renderDetail(data,fallback){const instance=Object.assign({},fallback||{},data&&data.instance||data&&data.account||{}),proof=state.curveProofs.get(accountKey(instance));if(proof&&proof.scope!==curveScope(instance))return;if(!retainFilteredSelection(instance))return;state.selected=Object.assign({},state.selected||{},instance);const index=state.accounts.findIndex(item=>accountKey(item)===accountKey(instance));if(index>=0)state.accounts[index]=Object.assign({},state.accounts[index],instance);renderSelectedHeader(state.selected);renderMetrics(state.selected);renderPositions(normalizeItems(data,['positions','position_items']),state.selected);renderOrders(normalizeItems(data,['orders','pending_orders','order_items']));renderStatus(state.selected);renderAccountList()}
function renderEvents(data){
 const events=normalizeItems(data,['items','events']);el.eventList.replaceChildren();el.eventCount.textContent=events.length;if(!events.length){const empty=make('div','gm-placeholder gm-placeholder-compact');empty.append(make('b','','暂无最近事件'),make('span','','交易或运行状态变化后会在这里显示'));el.eventList.append(empty);return}
 events.slice(0,12).forEach(event=>{const level=String(event.level||'info').toLowerCase(),item=make('div','gm-event '+(level==='critical'||level==='error'?'is-critical':level==='warning'?'is-warning':'')),time=make('time','',timeText(event.occurred_at,true)),dot=make('i','gm-event-dot'),message=make('p','',event.message||event.event_type||event.category||'运行事件');time.title=event.occurred_at?fullTime(event.occurred_at):'';item.append(time,dot,message);el.eventList.append(item)});
}
function clearEvents(message){el.eventList.replaceChildren();const empty=make('div','gm-placeholder gm-placeholder-compact');empty.append(make('b','',message),make('span','','稍后将自动重试'));el.eventList.append(empty);el.eventCount.textContent='—'}

function curveScope(account){const scope=String(account&&account.curve_cache_scope||'');return /^[a-f0-9]{64}$/i.test(scope)?scope.toLowerCase():''}
function curveAllowed(account){return Boolean(account&&supportsTelemetry(account)&&hasTelemetrySnapshot(account)&&account.curve_available!==false&&account.realized_ready!==false&&account.backfill_complete!==false)}
function curveUnavailable(account){return String(account&&account.curve_unavailable_reason||monitoringMessage(account)||(account&&(account.realized_ready===false||account.backfill_complete===false)?'历史收益正在回补，完成前不生成正式曲线':'当前实例尚不具备有效的曲线监控条件'))}
function curveKey(account,range=state.range){return JSON.stringify([accountKey(account),String(account&&account.trade_server||''),curveScope(account),range])}
function curveVerified(account){const proof=state.curveProofs.get(accountKey(account));return Boolean(proof&&proof.epoch===state.curveEpoch&&proof.scope&&proof.scope===curveScope(account)&&proof.allowed&&curveAllowed(account)&&Date.now()-proof.checkedAt<CURVE_POLICY.proofMs)}
function observeCurveIdentity(account,requestId){
 if(!account||!account.instance_id)return;const owner=accountKey(account),scope=curveScope(account),previous=state.curveProofs.get(owner),removedAt=state.curveRemovedOwners.get(owner);if((previous&&previous.requestId>requestId)||(removedAt!==undefined&&requestId<=removedAt))return;state.curveRemovedOwners.delete(owner);
 const proof={scope,allowed:curveAllowed(account),checkedAt:Date.now(),requestId,epoch:state.curveEpoch};state.curveProofs.delete(owner);state.curveProofs.set(owner,proof);while(state.curveProofs.size>CURVE_POLICY.proofs)state.curveProofs.delete(state.curveProofs.keys().next().value);
 for(const [key,entry]of state.curveCache)if(entry.owner===owner&&(!proof.allowed||entry.scope!==scope))state.curveCache.delete(key);
 if(state.curveDisplayEntry&&state.curveDisplayEntry.owner===owner&&(!proof.allowed||state.curveDisplayEntry.scope!==scope))clearCurve(proof.allowed?'实例身份已更新，正在重新核验曲线':curveUnavailable(account));
}
function observeCurveResponse(action,data,requestId){if(action==='accounts')normalizeItems(data,['items','accounts']).forEach(account=>observeCurveIdentity(account,requestId));else if(action==='account'||action==='curve')observeCurveIdentity(data&&data.instance,requestId)}
function invalidateCurveAccess(message){state.curveEpoch++;state.curveToken++;state.selectionToken++;state.curveCache.clear();state.curveProofs.clear();for(const entry of state.curvePending.values())entry.controller.abort();state.curvePending.clear();clearCurve(message||'监控访问已失效，请重新验证')}
function pruneCurveCache(){const now=Date.now();for(const [key,entry]of state.curveCache)if(now-entry.fetchedAt>=CURVE_POLICY.expireMs)state.curveCache.delete(key);while(state.curveCache.size>CURVE_POLICY.entries)state.curveCache.delete(state.curveCache.keys().next().value)}
function cachedCurve(account,range=state.range){pruneCurveCache();if(!curveVerified(account))return null;const key=curveKey(account,range),entry=state.curveCache.get(key);if(!entry)return null;state.curveCache.delete(key);state.curveCache.set(key,entry);return entry}
function expireCurveDisplay(){const entry=state.curveDisplayEntry;if(entry&&(!state.selected||!curveVerified(state.selected)||entry.key!==curveKey(state.selected)||Date.now()-entry.fetchedAt>=CURVE_POLICY.expireMs))clearCurve('正在重新核验曲线数据')}
function showCurveEntry(entry,cached=false,refreshing=false,warning=''){
 const changed=state.curveDisplayKey!==entry.key||state.curveSignature!==entry.signature;state.curve=entry.points;state.curveDisplayKey=entry.key;state.curveSignature=entry.signature;state.curveDisplayEntry=entry;state.curveEmptyMessage='';
 const sampled=entry.points.length?'采样 '+timeText(entry.points[entry.points.length-1].time,true):'暂无采样';el.curveUpdated.textContent=(cached?'缓存 ':'获取 ')+timeText(entry.fetchedAt,true)+' · '+sampled+(refreshing?' · 更新中':warning?' · 更新暂缓':'');el.curveUpdated.title=warning||('获取时间：'+fullTime(entry.fetchedAt)+(cached?'；仅复用已通过当前实例权限核验的内存数据':''));if(changed)drawCurve();
}
async function requestCurve(account,range,force=false){
 const key=curveKey(account,range),pending=state.curvePending.get(key),waitingEpoch=state.curveEpoch;if(pending){if(force&&!pending.force){try{await pending.promise}catch(_){}if(document.hidden||waitingEpoch!==state.curveEpoch)return null;return requestCurve(account,range,true)}return pending.promise}
 if(document.hidden)return null;const epoch=state.curveEpoch,controller=new AbortController(),entry={controller,force,owner:accountKey(account),promise:null};
 entry.promise=(async()=>{
  const data=await api('curve',Object.assign(identityParams(account),{range},force?{force:1}:{}),controller);if(epoch!==state.curveEpoch)return null;
  if(!data||!data.instance||typeof data.instance!=='object')throw new Error('曲线缺少实例身份，等待重新核验');const instance=Object.assign({},account,data.instance);if(accountKey(instance)!==accountKey(account)||String(instance.trade_server||'')!==String(account.trade_server||''))throw new Error('曲线实例身份不一致，等待重新核验');
  if(!curveAllowed(instance))return{unavailable:true,instance};const scope=curveScope(instance);if(scope&&!curveVerified(instance))return null;
  const raw=normalizeItems(data,['points','items']);if(raw.length>CURVE_POLICY.points)throw new Error('曲线点数超过安全上限，请稍后重试');const points=curvePoints(data),result={key:curveKey(instance,range),owner:accountKey(instance),scope,points,signature:JSON.stringify(points),fetchedAt:Date.now(),instance};
  if(scope&&instance.curve_available===true&&curveVerified(instance)){state.curveCache.delete(result.key);state.curveCache.set(result.key,result);pruneCurveCache()}return result;
 })();state.curvePending.set(key,entry);while(state.curvePending.size>CURVE_POLICY.pending){const oldestKey=state.curvePending.keys().next().value,oldest=state.curvePending.get(oldestKey);state.curvePending.delete(oldestKey);oldest.controller.abort()}
 try{return await entry.promise}finally{if(state.curvePending.get(key)===entry)state.curvePending.delete(key)}
}
function curvePoints(data){const points=normalizeItems(data,['points','items']).map(point=>({time:valueOf(point,['sampled_at','timestamp','time','at'],''),strategy:finite(valueOf(point,['strategy_equity','strategy_value','value'],null)),realized:finite(valueOf(point,['strategy_realized','realized_net','realized'],null))})).filter(point=>point.strategy!==null||point.realized!==null);points.sort((a,b)=>(parseTime(a.time)?.getTime()||0)-(parseTime(b.time)?.getTime()||0));return points}
function compactValue(value){const n=finite(value);if(n===null)return '—';const a=Math.abs(n);if(a>=1000000)return (n/1000000).toFixed(1)+'m';if(a>=1000)return (n/1000).toFixed(a>=100000?0:1)+'k';return Math.round(n).toString()}
function drawCurve(){
 if(document.hidden)return;const canvas=el.chart,wrap=canvas.parentElement,points=state.curve,dpr=Math.min(window.devicePixelRatio||1,2),width=Math.max(320,Math.floor(wrap.clientWidth)),height=Math.max(180,Math.floor(wrap.clientHeight)),drawKey=JSON.stringify([state.curveDisplayKey,state.curveSignature,state.curveEmptyMessage,width,height,dpr]);if(drawKey===state.curveDrawKey)return;state.curveDrawKey=drawKey;canvas.width=Math.floor(width*dpr);canvas.height=Math.floor(height*dpr);canvas.style.width=width+'px';canvas.style.height=height+'px';const ctx=canvas.getContext('2d');ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,width,height);
 if(points.length<2){el.chartEmpty.hidden=false;el.chartEmpty.textContent=state.curveEmptyMessage||(points.length?'曲线采样不足，收到更多数据后自动绘制':'该时间范围暂无曲线数据');return}el.chartEmpty.hidden=true;
 const pad={left:54,right:12,top:15,bottom:27},plotW=width-pad.left-pad.right,plotH=height-pad.top-pad.bottom,values=[];points.forEach(p=>{if(p.strategy!==null)values.push(p.strategy);if(p.realized!==null)values.push(p.realized)});let min=Math.min(...values),max=Math.max(...values);if(min===max){const spread=Math.max(1,Math.abs(min)*.02);min-=spread;max+=spread}else{const spread=(max-min)*.1;min-=spread;max+=spread}const x=i=>pad.left+(points.length===1?0:i/(points.length-1)*plotW),y=v=>pad.top+(max-v)/(max-min)*plotH;
 ctx.font='9px ui-monospace,Consolas,monospace';ctx.textAlign='right';ctx.textBaseline='middle';for(let i=0;i<=4;i++){const py=pad.top+i/4*plotH,value=max-i/4*(max-min);ctx.strokeStyle='#1c3b2b';ctx.lineWidth=1;ctx.setLineDash([3,3]);ctx.beginPath();ctx.moveTo(pad.left,py);ctx.lineTo(width-pad.right,py);ctx.stroke();ctx.setLineDash([]);ctx.fillStyle='#658171';ctx.fillText(compactValue(value),pad.left-7,py)}
 const line=(field,color,widthPx)=>{let started=false;ctx.beginPath();points.forEach((point,index)=>{const v=point[field];if(v===null){started=false;return}if(!started){ctx.moveTo(x(index),y(v));started=true}else ctx.lineTo(x(index),y(v))});ctx.strokeStyle=color;ctx.lineWidth=widthPx;ctx.lineJoin='round';ctx.lineCap='round';ctx.stroke()};
 line('realized','#dcb447',1.35);line('strategy','#45dc9b',1.8);
 ctx.textAlign='center';ctx.textBaseline='top';ctx.fillStyle='#607d6d';[0,Math.floor((points.length-1)/2),points.length-1].forEach(index=>{const d=parseTime(points[index].time);const label=d?new Intl.DateTimeFormat('zh-CN',state.range==='1d'?{hour:'2-digit',minute:'2-digit',hour12:false}:{month:'2-digit',day:'2-digit'}).format(d).replace('/','-'):'—';ctx.fillText(label,x(index),height-pad.bottom+8)});
}
function clearCurve(message){state.curve=[];state.curveDisplayKey='';state.curveSignature='';state.curveDisplayEntry=null;state.curveEmptyMessage=message;el.curveUpdated.textContent=message;el.curveUpdated.title='';el.chartEmpty.textContent=message;el.chartEmpty.hidden=false;drawCurve()}

async function loadCurve(account,token,options={}){
 if(document.hidden)return;const curveToken=++state.curveToken,range=state.range,epoch=state.curveEpoch,current=()=>!document.hidden&&token===state.selectionToken&&curveToken===state.curveToken&&epoch===state.curveEpoch&&range===state.range&&state.selected&&accountKey(account)===accountKey(state.selected);
 if(!curveAllowed(account)){if(current())clearCurve(curveUnavailable(account));return}const cached=cachedCurve(account,range),refresh=Boolean(options.force)||!cached||Date.now()-cached.fetchedAt>=CURVE_POLICY.refreshMs;
 if(cached)showCurveEntry(cached,true,refresh);else if(state.curveDisplayKey!==curveKey(account,range)||!curveVerified(account))clearCurve('正在读取策略曲线');if(!refresh)return;
 try{const result=await requestCurve(account,range,Boolean(options.force));if(!result||!current())return;if(!retainFilteredSelection(result.instance))return;if(result.unavailable){clearCurve(curveUnavailable(result.instance));return}if(result.scope&&!curveVerified(result.instance))return;state.selected=Object.assign({},state.selected,result.instance);const index=state.accounts.findIndex(item=>accountKey(item)===accountKey(result.instance));if(index>=0)state.accounts[index]=Object.assign({},state.accounts[index],result.instance);showCurveEntry(result)}
 catch(error){if(!current())return;const fallback=cachedCurve(state.selected,range);if(fallback)showCurveEntry(fallback,true,false,error.message);else clearCurve(error.message)}
}
async function loadSelection(account,options={}){
 if(document.hidden)return;const token=++state.selectionToken,monitorMessage=monitoringMessage(account);renderSelectedHeader(account);renderMetrics(account);renderStatus(account);if(!options.preserve){renderPositions([],account);renderOrders([]);clearEvents(monitorMessage||'正在读取最近事件')}
 if(!supportsTelemetry(account)){clearEvents(monitorMessage||'旧版本不支持事件监控');clearCurve(monitorMessage||'终端需升级至 V1.42 才能生成策略曲线');return}
 if(!hasTelemetrySnapshot(account)){clearEvents(monitorMessage||'尚未收到监控快照');clearCurve(monitorMessage||'尚未收到监控快照，请检查 WebRequest 设置');return}
 const params=identityParams(account);
 const detailPromise=api('account',params).then(data=>{if(token===state.selectionToken&&!document.hidden){renderDetail(data,account);expireCurveDisplay()}}).catch(error=>{if(token!==state.selectionToken||document.hidden)return;showNotice('账户详情暂时无法读取：'+error.message+'。页面会自动重试，不影响客户 EA 交易。',true)});
 const curvePromise=loadCurve(account,token,{force:Boolean(options.forceCurve)});
 const eventPromise=api('events',Object.assign({},params,{level:'all',page:1,per_page:20})).then(data=>{if(token===state.selectionToken)renderEvents(data)}).catch(error=>{if(token===state.selectionToken)clearEvents(error.message)});
 await Promise.allSettled([detailPromise,curvePromise,eventPromise]);
}
function selectAccount(account){state.selected=account;renderAccountList();loadSelection(account);primeShare(account)}
function accountPagination(data){const p=data&&data.pagination||{};return{page:Math.max(1,Number(p.page)||1),pages:Math.max(1,Number(p.pages)||1),total:Math.max(0,Number(p.total)||0)}}
function mergeAccountPages(pages){const merged=new Map();pages.flat().forEach(account=>merged.set(accountKey(account),account));return Array.from(merged.values())}
async function loadMoreAccounts(){
 if(document.hidden||state.loadingAccounts||state.loadingMore||state.loadedPage>=state.accountPages)return;const epoch=state.curveEpoch,filterEpoch=state.filterEpoch,run=++state.moreRun,controller=new AbortController();state.moreController=controller;state.loadingMore=true;renderAccountList();
 try{const next=state.loadedPage+1,data=await api('accounts',filterParams(next),controller);if(epoch!==state.curveEpoch||filterEpoch!==state.filterEpoch||run!==state.moreRun||document.hidden)return;if(Array.isArray(data.employees)&&!renderEmployeeOptions(data.employees))return;const items=normalizeItems(data,['items','accounts']),pagination=accountPagination(data);state.accounts=mergeAccountPages([state.accounts,items]);state.loadedPage=pagination.page;state.accountPages=pagination.pages;state.accountTotalCount=pagination.total;state.summary=data.summary||state.summary;const selectedKey=state.selected?accountKey(state.selected):'',match=state.accounts.find(item=>accountKey(item)===selectedKey);if(match)state.selected=match;pruneRemovedCurveOwners();renderAccountCounts();renderAccountList();expireCurveDisplay()}
 catch(error){if(!document.hidden&&filterEpoch===state.filterEpoch&&run===state.moreRun)showNotice('更多客户暂时无法载入：'+error.message,true)}finally{if(run===state.moreRun){state.loadingMore=false;if(!document.hidden)renderAccountList();resumeMonitorRefresh()}}
}
function pruneRemovedCurveOwners(){
 // Only a complete, unfiltered catalogue proves removal. A filtered page is not a deletion.
 if(!filtersAreAll()||state.loadedPage<state.accountPages)return;const present=new Set(state.accounts.map(accountKey)),owners=new Set([...state.curveProofs.keys(),...Array.from(state.curveCache.values(),entry=>entry.owner),...Array.from(state.curvePending.values(),entry=>entry.owner)]);for(const owner of owners)if(!present.has(owner)){state.curveProofs.delete(owner);state.curveRemovedOwners.delete(owner);state.curveRemovedOwners.set(owner,state.apiSequence)}for(const [key,entry]of state.curveCache)if(!present.has(entry.owner))state.curveCache.delete(key);while(state.curveRemovedOwners.size>256)state.curveRemovedOwners.delete(state.curveRemovedOwners.keys().next().value);
}
async function loadAccounts(silent=false,options={}){
 if(document.hidden||state.loadingAccounts||state.loadingMore)return;const epoch=state.curveEpoch,filterEpoch=state.filterEpoch,run=++state.accountsRun,controller=new AbortController(),params=Object.assign({},state.filters);state.accountsController=controller;expireCurveDisplay();state.loadingAccounts=true;el.refresh.disabled=true;el.refresh.classList.add('is-loading');el.accountList.setAttribute('aria-busy','true');if(!silent)showNotice('');
 try{
  const requestedPages=Math.max(1,state.loadedPage),first=await api('accounts',Object.assign({},params,{page:1,per_page:100}),controller);if(epoch!==state.curveEpoch||filterEpoch!==state.filterEpoch||run!==state.accountsRun||document.hidden)return;if(Array.isArray(first.employees)&&!renderEmployeeOptions(first.employees))return;const pagination=accountPagination(first),pageItems=[normalizeItems(first,['items','accounts'])],targetPages=Math.min(requestedPages,pagination.pages);for(let page=2;page<=targetPages;page++){const data=await api('accounts',Object.assign({},params,{page,per_page:100}),controller);if(epoch!==state.curveEpoch||filterEpoch!==state.filterEpoch||run!==state.accountsRun||document.hidden)return;pageItems.push(normalizeItems(data,['items','accounts']))}const items=mergeAccountPages(pageItems);state.accounts=items;state.loadedPage=targetPages;state.accountPages=pagination.pages;state.accountTotalCount=pagination.total;state.summary=first.summary||{};pruneRemovedCurveOwners();renderAccountCounts();
  if(items.length){const old=state.selected,oldKey=old?accountKey(old):'',match=items.find(item=>accountKey(item)===oldKey),next=match||(old&&matchesMonitorFilters(old)&&pagination.pages>1?old:items[0]),preserve=Boolean(old&&accountKey(old)===accountKey(next)&&curveScope(old)===curveScope(next));state.selected=next;renderAccountList();primeShare(next);await loadSelection(next,{preserve,forceCurve:Boolean(options.forceCurve)})}else{state.selected=null;state.selectionToken++;state.curveToken++;closeShare();renderAccountList();renderEmptyWorkspace()}
  if(!silent)showNotice('');root.setAttribute('aria-busy','false');
 }catch(error){if(document.hidden||filterEpoch!==state.filterEpoch||run!==state.accountsRun)return;expireCurveDisplay();if(!state.accounts.length){el.accountList.replaceChildren();const empty=make('div','gm-placeholder');empty.append(make('b','','监控接口暂时不可用'),make('span','',error.message));el.accountList.append(empty);renderEmptyWorkspace()}showNotice('监控数据暂时无法读取：'+error.message+'。该故障不会影响授权接口或客户 EA 交易。',true)}finally{if(run===state.accountsRun){state.loadingAccounts=false;el.refresh.disabled=false;el.refresh.classList.remove('is-loading');root.setAttribute('aria-busy','false');el.accountList.setAttribute('aria-busy','false');resumeMonitorRefresh()}}
}
function renderEmptyWorkspace(){
 const empty={customer_name:'请选择客户账户',account:'',ea_version:'',trade_server:'从左侧选择账户后查看黄金战神策略数据',symbol:'',account_mode:'',last_received_at:''};
 [el.selectedName,el.selectedVersion,el.selectedState,el.selectedMeta,el.selectedEmployee,el.selectedInstance,el.selectedSync,el.runtimeSubtitle].forEach(node=>{node.title=''});
 el.selectedName.textContent='请选择客户账户';el.selectedVersion.textContent='—';el.selectedState.textContent='等待选择';el.selectedState.className='gm-live-badge';el.selectedMeta.textContent='客户运行 V1.42 并成功上报后，这里会显示交易数据';el.selectedEmployee.textContent='所属员工：—';el.selectedInstance.textContent='尚未选择终端实例';el.selectedInstance.classList.remove('is-duplicate');el.selectedSync.textContent='尚未同步';el.runtimeSubtitle.textContent='等待选择账户';
 el.shareButton.disabled=true;el.shareButton.dataset.shareState='unknown';el.shareButton.textContent='▦ 客户二维码';el.shareButton.title='请先选择客户账户';[el.equity,el.floating,el.margin,el.drawdown].forEach(node=>{node.textContent='—';node.classList.remove('is-positive','is-negative','is-warning')});el.balanceHint.textContent='余额 —';el.realizedHint.textContent='累计已实现 —';el.marginHint.textContent='等待终端上报';el.drawdownHint.textContent='按账户净值峰值计算';renderPositions([],empty);renderOrders([]);el.statusList.replaceChildren();const statusEmpty=make('div','gm-placeholder gm-placeholder-compact');statusEmpty.append(make('b','','暂无运行状态'),make('span','','选择客户后显示'));el.statusList.append(statusEmpty);clearEvents('暂无最近事件');clearCurve('选择账户后显示曲线');
}

el.onlineFilter.addEventListener('change',()=>changeMonitorFilters({state:el.onlineFilter.value}));el.employeeFilter.addEventListener('change',()=>changeMonitorFilters({employee_id:el.employeeFilter.value}));el.holdingFilter.addEventListener('click',()=>changeMonitorFilters({positions:state.filters.positions==='holding'?'all':'holding'}));el.riskFilter.addEventListener('click',()=>changeMonitorFilters({risk:state.filters.risk==='attention'?'all':'attention'}));el.resetFilters.addEventListener('click',()=>changeMonitorFilters({state:'all',employee_id:'all',positions:'all',risk:'all',q:''}));
el.search.addEventListener('input',()=>changeMonitorFilters({q:el.search.value},true));document.querySelectorAll('.gm-range button').forEach(button=>button.addEventListener('click',()=>{document.querySelectorAll('.gm-range button').forEach(item=>item.classList.toggle('is-active',item===button));state.range=button.dataset.range||'1d';if(state.selected)loadCurve(state.selected,state.selectionToken)}));
el.refresh.addEventListener('click',()=>loadAccounts(false,{forceCurve:true}));
el.shareButton.addEventListener('click',()=>{if(state.selected)openShare(state.selected)});el.shareClose.addEventListener('click',closeShare);el.shareModal.addEventListener('click',event=>{if(event.target===el.shareModal)closeShare()});el.shareCreate.addEventListener('click',()=>mutateShare('create'));el.shareRotate.addEventListener('click',()=>mutateShare('rotate'));el.shareToggle.addEventListener('click',()=>mutateShare(el.shareToggle.dataset.action||'disable'));el.shareCopy.addEventListener('click',copyShareLink);
document.addEventListener('keydown',event=>{if(el.shareModal.hidden)return;if(event.key==='Escape'){event.preventDefault();closeShare();return}if(event.key!=='Tab')return;const focusable=[...el.shareDialog.querySelectorAll('a[href],button:not([disabled]),input:not([disabled])')].filter(node=>!node.hidden&&node.offsetParent!==null);if(!focusable.length){event.preventDefault();el.shareDialog.focus();return}const first=focusable[0],last=focusable[focusable.length-1];if(event.shiftKey&&document.activeElement===first){event.preventDefault();last.focus()}else if(!event.shiftKey&&document.activeElement===last){event.preventDefault();first.focus()}});
let resizeFrame=0;const resizeChart=()=>{cancelAnimationFrame(resizeFrame);resizeFrame=requestAnimationFrame(drawCurve)};if('ResizeObserver'in window)new ResizeObserver(resizeChart).observe(el.chart.parentElement);else window.addEventListener('resize',resizeChart);
function resumeMonitorRefresh(){if(state.resumeRefresh&&!document.hidden&&!state.loadingAccounts&&!state.loadingMore){state.resumeRefresh=false;loadAccounts(true)}}
setInterval(()=>{if(!document.hidden)loadAccounts(true)},30000);document.addEventListener('visibilitychange',()=>{if(document.hidden){state.curveEpoch++;state.curveToken++;state.selectionToken++;state.curveProofs.clear();for(const controller of state.apiControllers)controller.abort();state.curvePending.clear();clearCurve('页面已暂停刷新')}else{state.resumeRefresh=true;resumeMonitorRefresh()}});
restoreMonitorFilters();syncFilterControls();renderEmptyWorkspace();loadAccounts(false);
})();
</script>
<?php endif;?>
<script>function editGolden(r){gAccount.value=r.account;gName.value=r.customer_name||'';gAuthorizationInfo.textContent=goldenAuthorizationSummary(r.authorization);gMaxDevices.value=r.max_devices||1;gMinEaVersion.value=r.min_ea_version||'<?=h(GOLDEN_CONTRACT8_DEFAULT_MIN_EA_VERSION)?>';gForceUpgrade.checked=Number(r.force_upgrade||0)===1;gNote.value=r.note||'';goldenModal.classList.add('show')}function goldenAuthorizationSummary(a){return a?('当前有效授权：'+a.status_label+' · '+a.source_label+' · '+a.expiry_label):'授权状态待刷新核对'}function authorizeGolden(r){const f=goldenAuthModal.querySelector('form');f.reset();gAuthAccount.value=r.account;gAuthCurrent.textContent=goldenAuthorizationSummary(r.authorization);syncGoldenAuthorization();goldenAuthModal.classList.add('show')}function syncGoldenAuthorization(){const a=gAuthAction.value;goldenAuthModal.querySelectorAll('[data-golden-permission]').forEach(group=>{const shown=group.dataset.goldenPermission==='term'?['formal','extend','extra_trial'].includes(a):a==='restrict';group.hidden=!shown;group.querySelectorAll('input,select').forEach(field=>field.disabled=!shown)})}function requireGoldenRiskResign(account){if(confirm('确定要求账户 '+account+' 重新签署风险协议吗？\n现有签署和旧链接将立即失效，EA只允许管理和平仓，直到客户重新签署。')){document.getElementById('goldenRiskResignAccount').value=account;document.getElementById('goldenRiskResignForm').submit()}}</script>
<script>
function editTribonacci(r){
    document.getElementById('tgAccount').value=r.account;
    document.getElementById('tgName').value=r.customer_name||'';
    document.getElementById('tgStatus').value=r.status;
    document.getElementById('tgExpires').value=r.expires_at||'';
    document.getElementById('tgNote').value=r.note||'';
    document.getElementById('tribonacciModal').classList.add('show');
}
function deleteTribonacci(account){
    if(confirm('确定删除账户 '+account+' 的三阶战神授权记录吗？\n下次校验将重新登记为“等待授权”。')){
        document.getElementById('tribonacciDeleteAccount').value=account;
        document.getElementById('tribonacciDeleteForm').submit();
    }
}
function editMeanReversion(r){
    document.getElementById('mrAccount').value=r.account;
    document.getElementById('mrName').value=r.customer_name||'';
    document.getElementById('mrStatus').value=r.status;
    document.getElementById('mrExpires').value=r.expires_at||'';
    document.getElementById('mrNote').value=r.note||'';
    document.getElementById('meanReversionModal').classList.add('show');
}
function deleteMeanReversion(account){
    if(confirm('确定删除账户 '+account+' 的均值回归对冲授权记录吗？\n下次校验将重新登记为“等待授权”。')){
        document.getElementById('meanReversionDeleteAccount').value=account;
        document.getElementById('meanReversionDeleteForm').submit();
    }
}
</script>
</body></html>
