Files
Apex_AI_MT5_EA_Optimizer/ui/templates/dashboard.html
T

2411 lines
89 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APEX — AI-Powered EA Optimizer</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
/* ── CSS Variables ──────────────────────────────────────────── */
:root {
--bg: #080d1a;
--sidebar-bg: #0a0f1e;
--card-bg: #0f1629;
--card-hover: #131d35;
--border: rgba(255,255,255,0.07);
--border-hover: rgba(255,255,255,0.14);
--accent: #4f46e5;
--accent2: #7c6dfa;
--teal: #00d4aa;
--text: #e2e8f0;
--muted: #64748b;
--green: #10b981;
--red: #ef4444;
--yellow: #f59e0b;
--nav-active-bg: rgba(79,70,229,0.18);
--nav-active-border: #4f46e5;
}
/* ── Reset ──────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg);
color: var(--text);
height: 100vh;
overflow: hidden;
font-size: 14px;
line-height: 1.5;
}
/* ── App shell ──────────────────────────────────────────────── */
.app-shell {
display: flex;
height: 100vh;
}
/* ══════════════════════════════════════════════════════════════
SIDEBAR
══════════════════════════════════════════════════════════════ */
.sidebar {
width: 220px;
flex-shrink: 0;
background: var(--sidebar-bg);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
padding: 1.25rem 0;
overflow: hidden;
}
/* Logo */
.logo-wrap {
display: flex;
align-items: center;
gap: 0.65rem;
padding: 0 1.25rem 1.25rem;
border-bottom: 1px solid var(--border);
margin-bottom: 0.75rem;
}
.logo-text {
font-size: 1.25rem;
font-weight: 800;
letter-spacing: 0.12em;
background: linear-gradient(135deg, #7c6dfa, #00d4aa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.logo-tagline {
font-size: 0.58rem;
color: var(--muted);
font-weight: 400;
letter-spacing: 0.06em;
display: block;
line-height: 1;
margin-top: -2px;
-webkit-text-fill-color: var(--muted);
}
/* Nav */
.sidebar-nav {
flex: 1;
display: flex;
flex-direction: column;
gap: 1px;
padding: 0.25rem 0;
}
.nav-section-label {
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
padding: 0.75rem 1.25rem 0.35rem;
}
.nav-item {
display: flex;
align-items: center;
gap: 0.7rem;
padding: 0.6rem 1.25rem;
color: var(--muted);
text-decoration: none;
font-size: 0.83rem;
font-weight: 500;
transition: all 0.15s ease;
border-left: 2px solid transparent;
position: relative;
}
.nav-item svg { flex-shrink: 0; }
.nav-item:hover {
color: var(--text);
background: rgba(255,255,255,0.04);
}
.nav-item.active {
color: var(--text);
background: var(--nav-active-bg);
border-left-color: var(--accent);
}
.nav-item.active svg { color: var(--accent2); }
.nav-badge {
margin-left: auto;
background: var(--accent);
color: white;
font-size: 0.6rem;
font-weight: 700;
padding: 0.1rem 0.4rem;
border-radius: 999px;
min-width: 18px;
text-align: center;
}
/* Sidebar footer */
.sidebar-footer {
padding: 1rem 1.25rem 0;
border-top: 1px solid var(--border);
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.mt5-status {
display: flex;
align-items: center;
gap: 0.6rem;
}
.mt5-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--green);
box-shadow: 0 0 8px var(--green);
animation: pulse-dot 2s ease-in-out infinite;
flex-shrink: 0;
}
.mt5-dot.disconnected {
background: var(--red);
box-shadow: 0 0 8px var(--red);
animation: none;
}
@keyframes pulse-dot {
0%, 100% { opacity: 1; box-shadow: 0 0 8px var(--green); }
50% { opacity: 0.6; box-shadow: 0 0 4px var(--green); }
}
.mt5-label { font-size: 0.68rem; color: var(--muted); font-weight: 500; }
.mt5-state { font-size: 0.75rem; color: var(--text); font-weight: 600; }
.mt5-brand {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.7rem;
color: var(--muted);
}
/* ══════════════════════════════════════════════════════════════
MAIN CONTENT
══════════════════════════════════════════════════════════════ */
.main-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 1.25rem 1.5rem 1.5rem;
display: flex;
flex-direction: column;
gap: 0.85rem;
scrollbar-width: thin;
scrollbar-color: rgba(255,255,255,0.1) transparent;
}
.main-content::-webkit-scrollbar { width: 5px; }
.main-content::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 999px; }
/* ── Command Header ──────────────────────────────────────── */
.cmd-header {
display: flex;
align-items: center;
background: var(--card-bg);
border: 1px solid rgba(79,70,229,0.28);
border-radius: 14px;
padding: 0.7rem 1.25rem;
flex-shrink: 0;
position: relative;
overflow: hidden;
gap: 0;
}
.cmd-header::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0; height: 1px;
background: linear-gradient(90deg, rgba(0,229,255,0.45), rgba(79,70,229,0.55), rgba(124,58,237,0.45));
}
.cmd-logo {
display: flex;
align-items: center;
gap: 0.6rem;
flex-shrink: 0;
}
.cmd-logo-text {
display: flex;
flex-direction: column;
gap: 0.05rem;
}
.cmd-apex {
font-size: 1.05rem;
font-weight: 800;
letter-spacing: 0.14em;
background: linear-gradient(135deg, #00e5ff 0%, #4f46e5 50%, #7c3aed 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.cmd-tagline {
font-size: 0.46rem;
font-weight: 600;
letter-spacing: 0.1em;
color: var(--muted);
text-transform: uppercase;
-webkit-text-fill-color: var(--muted);
}
.cmd-sep {
width: 1px;
height: 30px;
background: var(--border);
margin: 0 1.1rem;
flex-shrink: 0;
}
.cmd-section {
display: flex;
flex-direction: column;
gap: 0.12rem;
}
.cmd-gen-section { min-width: 145px; }
.cmd-status-row { display: flex; align-items: center; gap: 0.4rem; }
.cmd-status-dot {
width: 8px; height: 8px;
border-radius: 50%;
background: var(--muted);
flex-shrink: 0;
transition: background 0.3s, box-shadow 0.3s;
}
.cmd-status-dot.running {
background: var(--green);
box-shadow: 0 0 8px rgba(16,185,129,0.6);
animation: pulse-dot 1.5s ease-in-out infinite;
}
.cmd-status-label {
font-size: 0.82rem;
font-weight: 600;
color: var(--text);
white-space: nowrap;
}
.cmd-section-sub { font-size: 0.66rem; color: var(--muted); white-space: nowrap; }
.cmd-gen-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
}
.cmd-gen-label {
font-size: 0.61rem;
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.07em;
}
.cmd-gen-val {
font-size: 0.8rem;
font-weight: 700;
color: var(--text);
font-family: 'JetBrains Mono', monospace;
}
.cmd-gen-sep { color: var(--muted); margin: 0 0.1rem; font-weight: 400; }
.cmd-prog-track {
width: 100%;
height: 3px;
background: rgba(255,255,255,0.07);
border-radius: 999px;
overflow: hidden;
margin: 0.12rem 0;
}
.cmd-prog-fill {
height: 100%;
background: linear-gradient(90deg, #00e5ff, #4f46e5);
border-radius: 999px;
transition: width 0.5s ease;
}
.cmd-pct-label { font-size: 0.61rem; color: var(--muted); }
.cmd-score-label {
font-size: 0.61rem;
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.07em;
}
.cmd-score-val {
font-size: 0.98rem;
font-weight: 700;
color: var(--teal);
font-family: 'JetBrains Mono', monospace;
}
.cmd-score-delta { font-size: 0.62rem; color: var(--muted); }
.cmd-spacer { flex: 1; }
.cmd-actions { display: flex; align-items: center; gap: 0.45rem; flex-shrink: 0; }
.cmd-btn {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.45rem 0.85rem;
border-radius: 8px;
font-size: 0.76rem;
font-weight: 600;
cursor: pointer;
border: 1px solid transparent;
font-family: inherit;
transition: all 0.15s;
text-decoration: none;
white-space: nowrap;
}
.cmd-btn-start {
background: linear-gradient(135deg, #4f46e5, #7c6dfa);
color: white;
}
.cmd-btn-start:hover { opacity: 0.88; }
.cmd-btn-pause {
background: rgba(245,158,11,0.1);
border-color: rgba(245,158,11,0.25);
color: var(--yellow);
opacity: 0.4;
pointer-events: none;
}
.cmd-btn-pause.active { opacity: 1; pointer-events: auto; }
.cmd-btn-pause.active:hover { background: rgba(245,158,11,0.18); }
.cmd-btn-stop {
background: rgba(100,116,139,0.08);
border-color: rgba(100,116,139,0.2);
color: var(--muted);
opacity: 0.4;
pointer-events: none;
}
.cmd-btn-stop.active {
background: rgba(239,68,68,0.12);
border-color: rgba(239,68,68,0.35);
color: var(--red);
opacity: 1;
pointer-events: auto;
}
.cmd-btn-stop.active:hover { background: rgba(239,68,68,0.22); }
/* keep run-indicator wiring for JS compat, always hidden */
#run-indicator { display: none !important; }
/* ── Metric cards row ────────────────────────────────────── */
.metrics-row {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 0.7rem;
flex-shrink: 0;
}
.metric-card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 12px;
padding: 0.9rem 1rem;
transition: border-color 0.2s, transform 0.15s;
position: relative;
overflow: hidden;
}
.metric-card::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 2px;
background: linear-gradient(90deg, var(--accent), var(--accent2));
opacity: 0;
transition: opacity 0.2s;
}
.metric-card:hover { border-color: var(--border-hover); }
.metric-card:hover::before { opacity: 1; }
.metric-label { font-size: 0.68rem; color: var(--muted); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; margin-bottom: 0.3rem; }
.metric-value { font-size: 1.35rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; }
.metric-value.green { color: var(--green); }
.metric-value.red { color: var(--red); }
.metric-value.gold { color: var(--yellow); }
.metric-value.teal { color: var(--teal); }
.metric-value.white { color: var(--text); }
.metric-delta {
font-size: 0.7rem;
color: var(--muted);
margin-top: 0.2rem;
display: flex;
align-items: center;
gap: 0.2rem;
}
.delta-up { color: var(--green); }
.delta-down { color: var(--red); }
.sparkline-wrap {
margin-top: 0.55rem;
}
/* AI Confidence card */
.ai-conf-card { display: flex; flex-direction: column; }
.ai-conf-value {
font-size: 0.95rem;
font-weight: 700;
color: var(--text);
margin-top: 0.15rem;
}
.ai-gauge-wrap {
margin-top: 0.45rem;
display: flex;
align-items: center;
justify-content: flex-start;
}
/* ── Charts + AI panel row ───────────────────────────────── */
.charts-ai-row {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 0.7rem;
flex-shrink: 0;
}
.charts-col {
display: flex;
flex-direction: column;
gap: 0.7rem;
}
/* Card base */
.card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1rem 1.25rem;
display: flex;
flex-direction: column;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.6rem;
flex-shrink: 0;
}
.card-title {
font-size: 0.82rem;
font-weight: 600;
color: var(--text);
display: flex;
align-items: center;
gap: 0.5rem;
}
.card-title .dot {
width: 7px; height: 7px; border-radius: 50%;
background: var(--teal);
}
.card-badge {
font-size: 0.65rem;
font-weight: 600;
padding: 0.15rem 0.5rem;
border-radius: 999px;
background: rgba(0,212,170,0.12);
color: var(--teal);
}
.chart-wrap {
position: relative;
overflow: hidden;
}
.chart-wrap canvas {
position: absolute !important;
inset: 0;
width: 100% !important;
height: 100% !important;
}
.equity-chart-wrap { height: 165px; }
.drawdown-chart-wrap { height: 100px; }
/* ── AI Summary panel ────────────────────────────────────── */
.ai-panel-col {
display: flex;
flex-direction: column;
}
.ai-panel-card {
background: var(--card-bg);
border: 1px solid rgba(79,70,229,0.3);
border-radius: 12px;
padding: 1rem 1.25rem;
flex: 1;
display: flex;
flex-direction: column;
gap: 0.75rem;
position: relative;
overflow: hidden;
}
.ai-panel-card::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0; height: 1px;
background: linear-gradient(90deg, transparent, rgba(124,109,250,0.6), transparent);
}
.ai-panel-card::after {
content: '';
position: absolute;
top: -80px; right: -80px;
width: 200px; height: 200px;
background: radial-gradient(circle, rgba(79,70,229,0.06) 0%, transparent 70%);
pointer-events: none;
}
.ai-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.ai-panel-title {
font-size: 0.82rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.4rem;
}
.ai-icon {
color: var(--accent2);
font-size: 0.9rem;
}
.view-details-link {
font-size: 0.72rem;
color: var(--accent2);
text-decoration: none;
transition: opacity 0.15s;
}
.view-details-link:hover { opacity: 0.7; }
.ai-waiting {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
gap: 0.75rem;
color: var(--muted);
font-size: 0.78rem;
}
.ai-pulse-dot {
width: 32px; height: 32px;
border-radius: 50%;
border: 2px solid rgba(124,109,250,0.4);
position: relative;
animation: ai-spin 2s linear infinite;
}
.ai-pulse-dot::before {
content: '';
position: absolute;
top: 4px; left: 4px; right: 4px; bottom: 4px;
border-radius: 50%;
background: rgba(124,109,250,0.3);
animation: ai-pulse 2s ease-in-out infinite;
}
@keyframes ai-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes ai-pulse { 0%,100% { opacity: 0.4; } 50% { opacity: 1; } }
.ai-content { display: flex; flex-direction: column; gap: 0.65rem; overflow-y: auto; flex: 1; }
.ai-content::-webkit-scrollbar { width: 3px; }
.ai-content::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 999px; }
.ai-headline {
font-size: 0.82rem;
font-weight: 600;
color: var(--text);
line-height: 1.4;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border);
}
.ai-diagnosis { font-size: 0.78rem; line-height: 1.7; color: #94a3b8; }
.ai-sub-title {
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.09em;
color: var(--muted);
margin-bottom: 0.35rem;
}
.ai-sub-title.warn { color: #f59e0b; }
.ai-check-list { list-style: none; display: flex; flex-direction: column; gap: 0.3rem; }
.ai-check-list li {
font-size: 0.76rem;
color: var(--text);
display: flex;
align-items: flex-start;
gap: 0.45rem;
line-height: 1.4;
}
.ai-check-list li::before { content: "✓"; color: var(--green); font-weight: 700; flex-shrink: 0; margin-top: 0.05em; }
.ai-warn-list { list-style: none; display: flex; flex-direction: column; gap: 0.3rem; }
.ai-warn-list li {
font-size: 0.76rem;
color: #fbbf24;
display: flex;
align-items: flex-start;
gap: 0.45rem;
line-height: 1.4;
}
.ai-warn-list li::before { content: "⚠"; flex-shrink: 0; margin-top: 0.05em; font-size: 0.7rem; }
.ai-suggestion-box {
background: rgba(79,70,229,0.1);
border: 1px solid rgba(79,70,229,0.22);
border-radius: 8px;
padding: 0.7rem 0.75rem;
position: relative;
}
.ai-suggestion-text { font-size: 0.76rem; color: var(--text); line-height: 1.55; padding-right: 1.5rem; }
.ai-badge {
position: absolute;
top: 0.45rem;
right: 0.45rem;
background: var(--accent);
color: white;
font-size: 0.58rem;
font-weight: 700;
padding: 0.1rem 0.35rem;
border-radius: 4px;
letter-spacing: 0.04em;
}
.ai-confidence-row {
display: flex;
align-items: center;
gap: 0.6rem;
margin-top: auto;
padding-top: 0.5rem;
border-top: 1px solid var(--border);
flex-shrink: 0;
}
.ai-conf-badge {
display: inline-flex;
align-items: center;
gap: 0.3rem;
padding: 0.2rem 0.6rem;
border-radius: 6px;
font-size: 0.7rem;
font-weight: 600;
text-transform: capitalize;
}
.ai-conf-badge::before { content: '●'; font-size: 0.5rem; }
.ai-conf-badge.high { background: rgba(16,185,129,0.15); color: #10b981; }
.ai-conf-badge.medium { background: rgba(245,158,11,0.15); color: #f59e0b; }
.ai-conf-badge.low { background: rgba(239,68,68,0.15); color: #ef4444; }
.ai-run-ref { font-size: 0.68rem; color: var(--muted); margin-left: auto; font-family: 'JetBrains Mono', monospace; }
/* ── Bottom row ──────────────────────────────────────────── */
.bottom-row {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0.7rem;
flex-shrink: 0;
}
.bottom-card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 12px;
padding: 0.9rem 1rem;
display: flex;
flex-direction: column;
}
.bottom-card-title {
font-size: 0.8rem;
font-weight: 600;
margin-bottom: 0.6rem;
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.view-all-link {
font-size: 0.7rem;
color: var(--accent2);
text-decoration: none;
font-weight: 500;
}
.view-all-link:hover { opacity: 0.75; }
/* Tables */
.params-table, .runs-table {
width: 100%;
border-collapse: collapse;
font-size: 0.76rem;
}
.params-table th, .runs-table th {
color: var(--muted);
font-size: 0.64rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
padding: 0.3rem 0.5rem;
border-bottom: 1px solid var(--border);
text-align: left;
}
.params-table td, .runs-table td {
padding: 0.38rem 0.5rem;
border-bottom: 1px solid rgba(255,255,255,0.03);
vertical-align: middle;
}
.params-table tr:last-child td, .runs-table tr:last-child td { border-bottom: none; }
.params-table tr:hover td, .runs-table tr:hover td { background: rgba(255,255,255,0.02); }
.param-name { font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; color: var(--text); }
.param-val { font-family: 'JetBrains Mono', monospace; font-size: 0.72rem; color: var(--teal); font-weight: 600; }
.impact-high { display: inline-block; background: rgba(16,185,129,0.15); color: var(--green); padding: 0.08rem 0.4rem; border-radius: 4px; font-size: 0.64rem; font-weight: 700; }
.impact-medium { display: inline-block; background: rgba(245,158,11,0.15); color: var(--yellow); padding: 0.08rem 0.4rem; border-radius: 4px; font-size: 0.64rem; font-weight: 700; }
.impact-low { display: inline-block; background: rgba(100,116,139,0.15); color: var(--muted); padding: 0.08rem 0.4rem; border-radius: 4px; font-size: 0.64rem; font-weight: 700; }
.verdict-recommended { display: inline-block; background: rgba(16,185,129,0.15); color: var(--green); padding: 0.08rem 0.45rem; border-radius: 4px; font-size: 0.64rem; font-weight: 700; }
.verdict-risky { display: inline-block; background: rgba(245,158,11,0.15); color: var(--yellow); padding: 0.08rem 0.45rem; border-radius: 4px; font-size: 0.64rem; font-weight: 700; }
.verdict-not-reliable { display: inline-block; background: rgba(239,68,68,0.15); color: var(--red); padding: 0.08rem 0.45rem; border-radius: 4px; font-size: 0.64rem; font-weight: 700; }
.empty-state { color: var(--muted); font-size: 0.76rem; padding: 0.65rem 0.5rem; }
.run-num { font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; color: var(--muted); }
.profit-pos { color: var(--green); font-weight: 600; }
.profit-neg { color: var(--red); font-weight: 600; }
/* Optimization progress */
.opt-gen-label {
font-size: 0.75rem;
color: var(--muted);
font-family: 'JetBrains Mono', monospace;
margin-bottom: 0.45rem;
}
.opt-progress-bar-track {
height: 5px;
background: rgba(255,255,255,0.06);
border-radius: 999px;
overflow: hidden;
margin-bottom: 0.65rem;
}
.opt-progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), var(--teal));
border-radius: 999px;
transition: width 0.6s ease;
width: 0%;
}
.opt-stats-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.5rem;
}
.opt-stat { }
.opt-stat-label { font-size: 0.63rem; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.opt-stat-val { font-size: 0.8rem; color: var(--text); font-weight: 600; margin-top: 0.15rem; }
/* Phase steps */
.phase-steps {
display: flex;
align-items: center;
margin-top: 0.6rem;
gap: 0;
}
.phase-step {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.3rem;
flex: 1;
position: relative;
}
.phase-dot {
width: 24px; height: 24px;
border-radius: 50%;
border: 2px solid rgba(255,255,255,0.1);
background: var(--bg);
display: flex; align-items: center; justify-content: center;
font-size: 0.65rem;
font-weight: 700;
color: var(--muted);
transition: all 0.35s;
z-index: 1;
}
.phase-name { font-size: 0.6rem; color: var(--muted); font-weight: 500; text-align: center; }
.phase-connector {
flex: 1;
height: 2px;
background: rgba(255,255,255,0.07);
margin-top: -14px;
}
.phase-step.active .phase-dot { border-color: var(--teal); color: var(--teal); background: rgba(0,212,170,0.1); box-shadow: 0 0 10px rgba(0,212,170,0.25); }
.phase-step.active .phase-name { color: var(--teal); }
.phase-step.done .phase-dot { border-color: var(--teal); background: var(--teal); color: #051a14; }
.phase-step.done .phase-name { color: var(--text); }
/* ── Live log ────────────────────────────────────────────── */
.log-panel {
flex-shrink: 0;
}
.log-toggle-btn {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 1rem;
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 8px;
cursor: pointer;
font-family: inherit;
font-size: 0.75rem;
color: var(--muted);
font-weight: 500;
transition: all 0.15s;
}
.log-toggle-btn:hover { color: var(--text); border-color: var(--border-hover); }
.log-toggle-icon { transition: transform 0.2s; }
.log-toggle-icon.open { transform: rotate(180deg); }
.log-body {
display: none;
margin-top: 0.35rem;
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
}
.log-body.open { display: block; }
.log-feed {
height: 120px;
overflow-y: auto;
padding: 0.6rem 0.75rem;
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
line-height: 1.7;
color: #64748b;
scrollbar-width: thin;
scrollbar-color: rgba(255,255,255,0.06) transparent;
}
.log-feed::-webkit-scrollbar { width: 3px; }
.log-line { display: flex; gap: 0.6rem; }
.log-time { color: rgba(100,116,139,0.6); flex-shrink: 0; }
.log-line.info .log-msg { color: #94a3b8; }
.log-line.success .log-msg { color: var(--green); }
.log-line.warn .log-msg { color: var(--yellow); }
.log-line.error .log-msg { color: var(--red); }
/* ── Verdict overlay ─────────────────────────────────────── */
.verdict-overlay {
position: fixed; inset: 0;
background: rgba(0,0,0,0.75);
display: flex;
align-items: center;
justify-content: center;
z-index: 200;
backdrop-filter: blur(5px);
}
.verdict-card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 20px;
padding: 2.25rem 2.5rem;
max-width: 580px;
width: 92%;
animation: popIn 0.4s cubic-bezier(0.34,1.56,0.64,1) both;
}
@keyframes popIn {
from { opacity: 0; transform: scale(0.88) translateY(20px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
.verdict-header-row {
display: flex;
align-items: center;
gap: 1.25rem;
margin-bottom: 1.5rem;
}
.verdict-icon-big { font-size: 2.5rem; line-height: 1; }
.verdict-title {
font-size: 1.5rem;
font-weight: 800;
letter-spacing: -0.02em;
}
.verdict-subtitle { font-size: 0.85rem; color: var(--muted); margin-top: 0.25rem; line-height: 1.5; }
.verdict-metrics-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
padding: 1.25rem 0;
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
margin-bottom: 1.5rem;
}
.vm { text-align: center; }
.vm-val { font-size: 1.1rem; font-weight: 700; }
.vm-lbl { font-size: 0.68rem; color: var(--muted); margin-top: 0.2rem; font-weight: 500; }
.verdict-actions-row {
display: flex;
gap: 0.65rem;
flex-wrap: wrap;
}
.verdict-btn {
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.6rem 1.1rem;
border-radius: 8px;
font-size: 0.82rem;
font-weight: 600;
cursor: pointer;
text-decoration: none;
font-family: inherit;
border: 1px solid transparent;
transition: all 0.15s;
}
.verdict-btn.primary { background: linear-gradient(135deg, var(--accent), var(--accent2)); color: white; }
.verdict-btn.primary:hover { opacity: 0.88; }
.verdict-btn.secondary { background: rgba(255,255,255,0.06); border-color: var(--border); color: var(--text); }
.verdict-btn.secondary:hover { background: rgba(255,255,255,0.1); }
.verdict-btn.ghost { background: transparent; border-color: rgba(255,255,255,0.1); color: var(--muted); }
.verdict-btn.ghost:hover { color: var(--text); }
/* ── Settings Modal ─────────────────────────────────────── */
.settings-overlay {
position: fixed; inset: 0;
background: rgba(0,0,0,0.78);
display: flex;
align-items: center;
justify-content: center;
z-index: 300;
backdrop-filter: blur(6px);
animation: fadeIn 0.2s ease both;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.settings-card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 18px;
width: 680px;
max-width: 95vw;
max-height: 90vh;
display: flex;
flex-direction: column;
overflow: hidden;
animation: popIn 0.3s cubic-bezier(0.34,1.56,0.64,1) both;
}
.settings-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.25rem 1.5rem;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.settings-title {
font-size: 1rem;
font-weight: 700;
letter-spacing: -0.01em;
display: flex;
align-items: center;
gap: 0.6rem;
}
.settings-title svg { color: var(--accent2); }
.settings-close-btn {
width: 28px; height: 28px;
border-radius: 50%;
background: rgba(255,255,255,0.06);
border: 1px solid var(--border);
color: var(--muted);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
font-size: 14px;
line-height: 1;
}
.settings-close-btn:hover { background: rgba(255,255,255,0.12); color: var(--text); }
.settings-tabs {
display: flex;
gap: 0;
padding: 0 1.5rem;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
overflow-x: auto;
}
.settings-tab {
padding: 0.65rem 1rem;
font-size: 0.78rem;
font-weight: 600;
color: var(--muted);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.15s;
white-space: nowrap;
display: flex;
align-items: center;
gap: 0.4rem;
}
.settings-tab:hover { color: var(--text); }
.settings-tab.active { color: var(--text); border-bottom-color: var(--accent); }
.settings-body {
flex: 1;
overflow-y: auto;
padding: 1.5rem;
scrollbar-width: thin;
scrollbar-color: rgba(255,255,255,0.1) transparent;
}
.settings-panel { display: none; }
.settings-panel.active { display: block; }
.settings-section-label {
font-size: 0.62rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 0.75rem;
margin-top: 1.25rem;
}
.settings-section-label:first-child { margin-top: 0; }
.settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
}
.settings-grid.full { grid-template-columns: 1fr; }
.settings-field {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.settings-field label {
font-size: 0.72rem;
font-weight: 600;
color: var(--muted);
letter-spacing: 0.02em;
}
.settings-input {
background: rgba(255,255,255,0.04);
border: 1px solid var(--border);
border-radius: 7px;
padding: 0.5rem 0.75rem;
color: var(--text);
font-size: 0.82rem;
font-family: inherit;
transition: border-color 0.15s;
width: 100%;
}
.settings-input:focus {
outline: none;
border-color: var(--accent);
background: rgba(79,70,229,0.06);
}
.settings-input.mono { font-family: 'JetBrains Mono', monospace; font-size: 0.78rem; }
.settings-select {
background: rgba(255,255,255,0.04);
border: 1px solid var(--border);
border-radius: 7px;
padding: 0.5rem 0.75rem;
color: var(--text);
font-size: 0.82rem;
font-family: inherit;
transition: border-color 0.15s;
width: 100%;
cursor: pointer;
}
.settings-select:focus { outline: none; border-color: var(--accent); }
.settings-select option { background: #0f1629; }
.settings-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(255,255,255,0.03);
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.6rem 0.85rem;
}
.settings-toggle-label { font-size: 0.82rem; font-weight: 500; }
.settings-toggle-sub { font-size: 0.7rem; color: var(--muted); margin-top: 0.15rem; }
.settings-toggle {
width: 40px; height: 22px;
background: rgba(255,255,255,0.12);
border-radius: 999px;
border: none;
cursor: pointer;
position: relative;
transition: background 0.2s;
flex-shrink: 0;
}
.settings-toggle::after {
content: '';
position: absolute;
top: 3px; left: 3px;
width: 16px; height: 16px;
border-radius: 50%;
background: white;
transition: transform 0.2s;
}
.settings-toggle.on { background: var(--accent); }
.settings-toggle.on::after { transform: translateX(18px); }
.settings-hint {
font-size: 0.68rem;
color: var(--muted);
margin-top: 0.2rem;
line-height: 1.4;
}
.settings-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.65rem;
padding: 1rem 1.5rem;
border-top: 1px solid var(--border);
flex-shrink: 0;
}
.settings-btn {
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.55rem 1.1rem;
border-radius: 8px;
font-size: 0.82rem;
font-weight: 600;
cursor: pointer;
font-family: inherit;
border: 1px solid transparent;
transition: all 0.15s;
}
.settings-btn.cancel {
background: rgba(255,255,255,0.05);
border-color: var(--border);
color: var(--muted);
}
.settings-btn.cancel:hover { background: rgba(255,255,255,0.1); color: var(--text); }
.settings-btn.save {
background: linear-gradient(135deg, var(--accent), var(--accent2));
color: white;
}
.settings-btn.save:hover { opacity: 0.88; }
.settings-btn.save:disabled { opacity: 0.5; cursor: not-allowed; }
.settings-save-msg {
font-size: 0.75rem;
font-weight: 600;
margin-right: auto;
transition: opacity 0.3s;
}
.settings-save-msg.ok { color: var(--green); }
.settings-save-msg.err { color: var(--red); }
/* ── No profit state banner ──────────────────────────────── */
.no-profit-banner {
display: none;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
background: rgba(239,68,68,0.1);
border: 1px solid rgba(239,68,68,0.25);
border-radius: 10px;
font-size: 0.8rem;
color: var(--red);
}
.no-profit-banner.active { display: flex; }
.no-profit-banner-icon { font-size: 1.1rem; flex-shrink: 0; }
/* ── Scrollbar for whole page ────────────────────────────── */
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 999px; }
/* ── AI panel expand / collapse ─────────────────────────── */
.ai-preview { display: flex; flex-direction: column; gap: 0.5rem; }
.ai-details { display: none; flex-direction: column; gap: 0.65rem; }
.ai-details.open { display: flex; animation: slideDown 0.2s ease; }
@keyframes slideDown { from { opacity:0; transform:translateY(-6px); } to { opacity:1; transform:translateY(0); } }
.ai-expand-btn {
align-self: flex-start;
background: rgba(79,70,229,0.12);
border: 1px solid rgba(79,70,229,0.28);
border-radius: 6px;
color: var(--accent2);
font-size: 0.72rem;
font-weight: 600;
padding: 0.25rem 0.65rem;
cursor: pointer;
font-family: inherit;
transition: all 0.15s;
margin-top: 0.15rem;
}
.ai-expand-btn:hover { background: rgba(79,70,229,0.22); }
/* ── Details button in runs table ───────────────────────── */
.detail-btn {
background: rgba(79,70,229,0.1);
border: 1px solid rgba(79,70,229,0.22);
border-radius: 5px;
color: var(--accent2);
font-size: 0.65rem;
font-weight: 600;
padding: 0.15rem 0.5rem;
cursor: pointer;
font-family: inherit;
transition: all 0.15s;
white-space: nowrap;
}
.detail-btn:hover { background: rgba(79,70,229,0.22); }
/* ── Run Detail Modal ────────────────────────────────────── */
.run-detail-overlay {
position: fixed; inset: 0;
background: rgba(0,0,0,0.78);
display: flex;
align-items: center;
justify-content: center;
z-index: 250;
backdrop-filter: blur(5px);
}
.run-detail-card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 18px;
width: 720px;
max-width: 95vw;
max-height: 88vh;
display: flex;
flex-direction: column;
overflow: hidden;
animation: popIn 0.3s cubic-bezier(0.34,1.56,0.64,1) both;
}
.run-detail-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 1.1rem 1.5rem;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.run-detail-title {
font-size: 0.95rem;
font-weight: 700;
color: var(--text);
font-family: 'JetBrains Mono', monospace;
letter-spacing: 0.02em;
}
.run-detail-sub { font-size: 0.72rem; color: var(--muted); margin-top: 0.2rem; }
.run-detail-body {
flex: 1;
overflow-y: auto;
padding: 1.1rem 1.5rem;
display: flex;
flex-direction: column;
gap: 1rem;
scrollbar-width: thin;
scrollbar-color: rgba(255,255,255,0.1) transparent;
}
.rd-metrics-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.6rem;
}
.rd-metric {
background: rgba(255,255,255,0.03);
border: 1px solid var(--border);
border-radius: 9px;
padding: 0.65rem 0.8rem;
}
.rd-metric-label { font-size: 0.6rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
.rd-metric-val { font-size: 1rem; font-weight: 700; margin-top: 0.15rem; }
.rd-section-title {
font-size: 0.63rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--border);
}
.rd-params-wrap { max-height: 200px; overflow-y: auto; }
.rd-ai-box {
background: rgba(79,70,229,0.07);
border: 1px solid rgba(79,70,229,0.2);
border-radius: 10px;
padding: 0.9rem 1rem;
font-size: 0.78rem;
color: var(--text);
line-height: 1.7;
}
.rd-ai-analysis { color: #94a3b8; margin-bottom: 0.65rem; }
.rd-ai-changes { display: flex; flex-direction: column; gap: 0.35rem; }
.rd-ai-change {
display: flex; align-items: center; gap: 0.5rem;
font-family: 'JetBrains Mono', monospace; font-size: 0.72rem;
}
.rd-ai-change-param { color: var(--teal); }
.rd-ai-change-arrow { color: var(--muted); }
.rd-ai-change-val { color: var(--text); font-weight: 600; }
.run-detail-footer {
padding: 0.85rem 1.5rem;
border-top: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.65rem;
flex-shrink: 0;
}
/* ── Live Intelligence (thinking / changes / validation) ──── */
.live-intel-row {
display: grid;
grid-template-columns: 1.1fr 1fr 1fr;
gap: 0.7rem;
flex-shrink: 0;
}
.live-intel-card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 12px;
padding: 0.9rem 1rem;
display: flex;
flex-direction: column;
min-height: 240px;
max-height: 280px;
position: relative;
overflow: hidden;
}
.live-intel-card.accent {
border-color: rgba(79,70,229,0.32);
}
.live-intel-card.accent::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0; height: 1px;
background: linear-gradient(90deg, transparent, rgba(124,109,250,0.7), transparent);
}
.live-intel-title {
font-size: 0.78rem;
font-weight: 600;
color: var(--text);
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.65rem;
flex-shrink: 0;
}
.live-intel-title .icon { color: var(--accent2); }
.live-intel-title .badge {
margin-left: auto;
font-size: 0.6rem;
font-weight: 700;
padding: 0.12rem 0.5rem;
border-radius: 999px;
background: rgba(100,116,139,0.15);
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.06em;
}
.live-intel-title .badge.live {
background: rgba(0,212,170,0.15);
color: var(--teal);
}
.live-intel-title .badge.live::before {
content: '●';
margin-right: 0.25rem;
animation: pulse-dot 1.8s ease-in-out infinite;
}
.live-intel-body {
flex: 1;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: rgba(255,255,255,0.1) transparent;
}
.live-intel-body::-webkit-scrollbar { width: 3px; }
.live-intel-body::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 999px; }
.live-intel-empty {
color: var(--muted);
font-size: 0.76rem;
padding: 1rem 0.5rem;
text-align: center;
}
/* ── Thinking feed entries ──────────────────────────────── */
.tf-entry {
display: flex;
gap: 0.55rem;
padding: 0.5rem 0.1rem;
border-bottom: 1px solid rgba(255,255,255,0.03);
font-size: 0.76rem;
line-height: 1.45;
animation: tf-slidein 0.25s ease both;
}
@keyframes tf-slidein {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
.tf-entry:last-child { border-bottom: none; }
.tf-marker {
flex-shrink: 0;
width: 18px;
text-align: center;
font-weight: 700;
font-size: 0.78rem;
margin-top: 0.05em;
}
.tf-entry.info .tf-marker { color: var(--muted); }
.tf-entry.reasoning .tf-marker { color: var(--accent2); }
.tf-entry.decision .tf-marker { color: var(--teal); }
.tf-entry.success .tf-marker { color: var(--green); }
.tf-entry.warning .tf-marker { color: var(--yellow); }
.tf-entry.hypothesis .tf-marker { color: #a78bfa; }
.tf-body { flex: 1; min-width: 0; }
.tf-msg { color: var(--text); word-wrap: break-word; }
.tf-entry.info .tf-msg { color: #94a3b8; }
.tf-entry.warning .tf-msg { color: #fbbf24; }
.tf-meta {
font-size: 0.62rem;
color: var(--muted);
margin-top: 0.15rem;
display: flex;
gap: 0.5rem;
font-family: 'JetBrains Mono', monospace;
}
.tf-cursor {
display: inline-block;
margin-left: 1px;
color: var(--accent2);
font-weight: 700;
animation: tf-blink 1s steps(2) infinite;
}
@keyframes tf-blink { 50% { opacity: 0; } }
.tf-chip {
display: inline-block;
padding: 0.05rem 0.35rem;
border-radius: 4px;
background: rgba(255,255,255,0.05);
color: var(--muted);
font-size: 0.6rem;
letter-spacing: 0.04em;
}
/* ── Parameter Changes list ─────────────────────────────── */
.pc-iteration {
border-bottom: 1px solid rgba(255,255,255,0.04);
padding: 0.55rem 0.1rem;
animation: tf-slidein 0.25s ease both;
}
.pc-iteration:last-child { border-bottom: none; }
.pc-iter-header {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.72rem;
color: var(--text);
font-weight: 600;
margin-bottom: 0.3rem;
}
.pc-iter-label {
background: rgba(79,70,229,0.15);
color: var(--accent2);
font-family: 'JetBrains Mono', monospace;
padding: 0.08rem 0.4rem;
border-radius: 4px;
font-size: 0.65rem;
}
.pc-iter-conf {
margin-left: auto;
font-size: 0.62rem;
color: var(--muted);
}
.pc-change {
font-size: 0.7rem;
line-height: 1.55;
padding: 0.12rem 0;
}
.pc-param { color: var(--teal); font-family: 'JetBrains Mono', monospace; }
.pc-arrow { color: var(--muted); margin: 0 0.3rem; }
.pc-oldval { color: var(--muted); font-family: 'JetBrains Mono', monospace; text-decoration: line-through; }
.pc-newval { color: var(--text); font-family: 'JetBrains Mono', monospace; font-weight: 600; }
.pc-reason { color: #94a3b8; font-size: 0.66rem; margin-left: 1.3rem; font-style: italic; }
/* ── Validation Live Panel ──────────────────────────────── */
.val-header {
font-size: 0.7rem;
color: var(--muted);
margin-bottom: 0.5rem;
}
.val-progress-row {
display: flex;
align-items: center;
gap: 0.45rem;
font-size: 0.68rem;
color: var(--muted);
margin-bottom: 0.6rem;
}
.val-progress-bar {
flex: 1;
height: 4px;
background: rgba(255,255,255,0.07);
border-radius: 999px;
overflow: hidden;
}
.val-progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), var(--teal));
width: 0%;
transition: width 0.4s ease;
}
.val-run {
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.55rem 0.7rem;
margin-bottom: 0.4rem;
background: rgba(255,255,255,0.02);
animation: tf-slidein 0.25s ease both;
}
.val-run.active { border-color: rgba(0,212,170,0.4); background: rgba(0,212,170,0.04); }
.val-run.pass { border-color: rgba(16,185,129,0.35); }
.val-run.fail { border-color: rgba(239,68,68,0.35); }
.val-run-title {
font-size: 0.72rem;
font-weight: 600;
color: var(--text);
display: flex;
align-items: center;
gap: 0.4rem;
}
.val-run-kind {
font-size: 0.58rem;
font-weight: 700;
padding: 0.08rem 0.4rem;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.06em;
background: rgba(100,116,139,0.15);
color: var(--muted);
}
.val-run-kind.oos { background: rgba(79,70,229,0.18); color: var(--accent2); }
.val-run-kind.sensitivity { background: rgba(245,158,11,0.15); color: var(--yellow); }
.val-run-desc { font-size: 0.64rem; color: #94a3b8; margin-top: 0.2rem; line-height: 1.4; }
.val-run-metrics {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.3rem;
margin-top: 0.4rem;
}
.val-metric { background: rgba(255,255,255,0.03); border-radius: 5px; padding: 0.25rem 0.4rem; }
.val-metric-lbl { font-size: 0.55rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.val-metric-val { font-size: 0.72rem; font-weight: 700; margin-top: 0.05rem; }
.val-spinner {
display: inline-block;
width: 10px; height: 10px;
border: 2px solid rgba(124,109,250,0.3);
border-top-color: var(--accent2);
border-radius: 50%;
animation: ai-spin 0.8s linear infinite;
}
/* ── Early Termination Banner ───────────────────────────── */
.early-term-banner {
display: none;
flex-direction: column;
gap: 0.35rem;
padding: 0.85rem 1.1rem;
background: rgba(245,158,11,0.08);
border: 1px solid rgba(245,158,11,0.28);
border-radius: 12px;
font-size: 0.82rem;
color: var(--yellow);
animation: tf-slidein 0.3s ease both;
}
.early-term-banner.active { display: flex; }
.early-term-banner.success { background: rgba(16,185,129,0.08); border-color: rgba(16,185,129,0.3); color: var(--green); }
.early-term-banner.warning { background: rgba(245,158,11,0.08); border-color: rgba(245,158,11,0.28); color: var(--yellow); }
.early-term-banner.critical { background: rgba(239,68,68,0.1); border-color: rgba(239,68,68,0.3); color: var(--red); }
.early-term-title { display: flex; align-items: center; gap: 0.55rem; font-weight: 700; }
.early-term-msg { font-size: 0.75rem; color: var(--text); line-height: 1.5; font-weight: 500; }
.early-term-details {
font-size: 0.7rem;
color: var(--muted);
margin-top: 0.2rem;
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1rem;
}
.early-term-dismiss {
background: transparent;
border: none;
color: var(--muted);
cursor: pointer;
margin-left: auto;
font-size: 1rem;
line-height: 1;
padding: 0;
}
.early-term-dismiss:hover { color: var(--text); }
/* ── Phase tracker (enhanced) ───────────────────────────── */
.phase-step .phase-sub {
font-size: 0.55rem;
color: var(--muted);
margin-top: 0.1rem;
text-align: center;
font-family: 'JetBrains Mono', monospace;
line-height: 1;
}
.phase-step.active .phase-sub { color: var(--teal); }
</style>
</head>
<body>
<div class="app-shell">
<!-- ══════════════════════════════════════
SIDEBAR
══════════════════════════════════════ -->
<aside class="sidebar">
<!-- Logo -->
<div class="logo-wrap">
<svg width="36" height="40" viewBox="0 0 40 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="logoGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00e5ff"/>
<stop offset="45%" stop-color="#4f46e5"/>
<stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
<clipPath id="sbTriClip">
<polygon points="20,2 39,40 1,40"/>
</clipPath>
</defs>
<polygon points="20,2 39,40 1,40" fill="rgba(79,70,229,0.08)" stroke="url(#logoGrad)" stroke-width="2" stroke-linejoin="round"/>
<line x1="10" y1="30" x2="30" y2="30" stroke="url(#logoGrad)" stroke-width="1.5"/>
<rect x="11" y="31" width="4" height="8" rx="0.8" fill="url(#logoGrad)" opacity="0.5" clip-path="url(#sbTriClip)"/>
<rect x="17" y="24" width="4" height="15" rx="0.8" fill="url(#logoGrad)" opacity="0.75" clip-path="url(#sbTriClip)"/>
<rect x="23" y="18" width="4" height="21" rx="0.8" fill="url(#logoGrad)" clip-path="url(#sbTriClip)"/>
<polyline points="8,38 17,27 22,30 33,16" stroke="#00e5ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" clip-path="url(#sbTriClip)" opacity="0.9"/>
</svg>
<div>
<span class="logo-text">APEX</span>
<span class="logo-tagline">AI Optimizer</span>
</div>
</div>
<!-- Navigation -->
<nav class="sidebar-nav">
<a href="/dashboard" class="nav-item active" data-page="dashboard">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/>
</svg>
Dashboard
</a>
<a href="/setup" class="nav-item" data-page="setup">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/>
<line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/>
<line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/>
<line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/>
</svg>
New Run
</a>
<a href="/reports" class="nav-item" data-page="reports">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/>
<line x1="6" y1="20" x2="6" y2="14"/><line x1="2" y1="20" x2="22" y2="20"/>
</svg>
Reports
</a>
<a href="https://github.com/tonnylegacy/MT5_Optimizer" target="_blank" rel="noopener" class="nav-item" data-page="github">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/>
</svg>
Source on GitHub
</a>
<a href="#" class="nav-item" data-page="set" onclick="openSettings(); return false;">
<!-- Gear icon -->
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/>
</svg>
Settings
</a>
</nav>
<!-- Footer: MT5 status -->
<div class="sidebar-footer">
<div class="mt5-status">
<div class="mt5-dot" id="mt5-dot"></div>
<div>
<div class="mt5-label">MT5 Connection</div>
<div class="mt5-state" id="mt5-state">Ready</div>
</div>
</div>
<div class="mt5-brand">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/>
</svg>
MetaTrader 5
</div>
</div>
</aside>
<!-- ══════════════════════════════════════
MAIN CONTENT
══════════════════════════════════════ -->
<div class="main-content" id="main-content">
<!-- ── Command Header ────────────────────────────────────── -->
<div class="cmd-header">
<!-- Logo -->
<div class="cmd-logo">
<svg width="32" height="36" viewBox="0 0 40 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="hdrLogoGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00e5ff"/>
<stop offset="45%" stop-color="#4f46e5"/>
<stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
<clipPath id="hdrTriClip">
<polygon points="20,2 39,40 1,40"/>
</clipPath>
</defs>
<polygon points="20,2 39,40 1,40" fill="rgba(79,70,229,0.09)" stroke="url(#hdrLogoGrad)" stroke-width="1.8" stroke-linejoin="round"/>
<line x1="10" y1="30" x2="30" y2="30" stroke="url(#hdrLogoGrad)" stroke-width="1.4"/>
<rect x="11" y="31" width="4" height="8" rx="0.8" fill="url(#hdrLogoGrad)" opacity="0.5" clip-path="url(#hdrTriClip)"/>
<rect x="17" y="24" width="4" height="15" rx="0.8" fill="url(#hdrLogoGrad)" opacity="0.75" clip-path="url(#hdrTriClip)"/>
<rect x="23" y="18" width="4" height="21" rx="0.8" fill="url(#hdrLogoGrad)" clip-path="url(#hdrTriClip)"/>
<polyline points="8,38 17,27 22,30 33,16" stroke="#00e5ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" clip-path="url(#hdrTriClip)" opacity="0.9"/>
</svg>
<div class="cmd-logo-text">
<span class="cmd-apex">APEX</span>
<span class="cmd-tagline">AI Powered EA Optimizer</span>
</div>
</div>
<div class="cmd-sep"></div>
<!-- Status -->
<div class="cmd-section">
<div class="cmd-status-row">
<div class="cmd-status-dot" id="hdr-dot"></div>
<span class="cmd-status-label" id="hdr-status">System Idle</span>
</div>
<div class="cmd-section-sub" id="hdr-status-sub">Ready to optimize</div>
</div>
<div class="cmd-sep"></div>
<!-- Generation / Progress -->
<div class="cmd-section cmd-gen-section">
<div class="cmd-gen-row">
<span class="cmd-gen-label">Generation</span>
<span class="cmd-gen-val"><span id="hdr-gen-cur">0</span><span class="cmd-gen-sep">/</span><span id="hdr-gen-total"></span></span>
</div>
<div class="cmd-prog-track">
<div class="cmd-prog-fill" id="hdr-prog-fill" style="width:0%"></div>
</div>
<div class="cmd-pct-label" id="hdr-pct">0% Complete</div>
</div>
<div class="cmd-sep"></div>
<!-- Best Score -->
<div class="cmd-section">
<div class="cmd-score-label">Best Score</div>
<div class="cmd-score-val" id="hdr-score"></div>
<div class="cmd-score-delta" id="hdr-score-delta">Waiting for results...</div>
</div>
<div class="cmd-spacer"></div>
<!-- Action buttons -->
<div class="cmd-actions">
<a href="/setup" class="cmd-btn cmd-btn-start">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Start New
</a>
<!-- Pause is intentionally not shown — pipeline doesn't currently support it
(see: optimizer/pipeline.py — only _stop_flag exists, no _pause_event). -->
<button class="cmd-btn cmd-btn-stop" id="btn-stop">
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><rect x="4" y="4" width="16" height="16" rx="2"/></svg>
Stop
</button>
</div>
<!-- Hidden run-indicator kept for JS compatibility -->
<div id="run-indicator" style="display:none!important;"><span id="run-indicator-label"></span></div>
</div>
<!-- No-profit error banner -->
<div class="no-profit-banner" id="no-profit-banner">
<span class="no-profit-banner-icon"></span>
<div>
<strong>No profitable configuration found.</strong>
<span id="no-profit-msg"> Try different parameters or date ranges.</span>
</div>
</div>
<!-- Early Termination banner (dismissible) -->
<div class="early-term-banner" id="early-term-banner">
<div class="early-term-title">
<span id="early-term-icon"></span>
<span id="early-term-heading">Optimization stopped early</span>
<button class="early-term-dismiss" onclick="dismissEarlyTerm()"></button>
</div>
<div class="early-term-msg" id="early-term-msg"></div>
<div class="early-term-details" id="early-term-details"></div>
</div>
<!-- ── Metric cards ────────────────────────────────────────── -->
<div class="metrics-row">
<!-- Net Profit (clickable → Best Result detail) -->
<div class="metric-card" id="best-card" onclick="openBestResult()" style="cursor:pointer" title="Click to see how the AI arrived at this best result">
<div class="metric-label">Best Net Profit <span style="font-size:0.55rem;color:var(--accent2);font-weight:700">▸ details</span></div>
<div class="metric-value green" id="m-net-profit">$0.00</div>
<div class="metric-delta" id="m-net-profit-delta">
<span class="delta-up"></span>
<span id="m-net-profit-delta-val">0%</span>
<span style="margin-left:2px">vs baseline</span>
</div>
<div class="sparkline-wrap">
<canvas id="spark-profit" width="80" height="28"></canvas>
</div>
</div>
<!-- Max Drawdown -->
<div class="metric-card">
<div class="metric-label">Max Drawdown</div>
<div class="metric-value red" id="m-max-dd">0.00%</div>
<div class="metric-delta" id="m-max-dd-delta">
<span class="delta-down"></span>
<span id="m-max-dd-delta-val">0%</span>
<span style="margin-left:2px">vs baseline</span>
</div>
<div class="sparkline-wrap">
<canvas id="spark-dd" width="80" height="28"></canvas>
</div>
</div>
<!-- Profit Factor -->
<div class="metric-card">
<div class="metric-label">Profit Factor</div>
<div class="metric-value gold" id="m-pf">0.00</div>
<div class="metric-delta">
<span id="m-pf-quality" style="color:var(--muted)"></span>
</div>
<div class="sparkline-wrap">
<canvas id="spark-pf" width="80" height="28"></canvas>
</div>
</div>
<!-- Total Trades -->
<div class="metric-card">
<div class="metric-label">Total Trades</div>
<div class="metric-value teal" id="m-trades">0</div>
<div class="metric-delta">
<span style="color:var(--muted)">This run</span>
</div>
<div class="sparkline-wrap">
<canvas id="spark-trades" width="80" height="28"></canvas>
</div>
</div>
<!-- Optimization Runs -->
<div class="metric-card">
<div class="metric-label">Optimization Runs</div>
<div class="metric-value white" id="m-runs">0</div>
<div class="metric-delta">
<span style="color:var(--muted)">This session</span>
</div>
<div class="sparkline-wrap">
<canvas id="spark-runs" width="80" height="28"></canvas>
</div>
</div>
<!-- AI Confidence (special) -->
<div class="metric-card ai-conf-card">
<div class="metric-label">AI Confidence</div>
<div class="ai-conf-value" id="ai-conf-label"></div>
<div class="ai-gauge-wrap">
<svg id="ai-gauge" width="60" height="60" viewBox="0 0 64 64">
<circle cx="32" cy="32" r="26" fill="none" stroke="rgba(255,255,255,0.07)" stroke-width="6"/>
<circle id="ai-gauge-arc" cx="32" cy="32" r="26" fill="none"
stroke="#00d4aa" stroke-width="6" stroke-linecap="round"
stroke-dasharray="163.4" stroke-dashoffset="163.4"
transform="rotate(-90 32 32)"
style="transition: stroke-dashoffset 1s ease, stroke 0.5s ease"/>
<text id="ai-gauge-pct" x="32" y="37" text-anchor="middle"
fill="#e2e8f0" font-size="12" font-weight="700" font-family="Inter, sans-serif">0%</text>
</svg>
</div>
</div>
</div>
<!-- ── Charts + AI panel ────────────────────────────────────── -->
<div class="charts-ai-row">
<!-- Left: charts column -->
<div class="charts-col">
<!-- Equity Curve -->
<div class="card">
<div class="card-header">
<div class="card-title">
<div class="dot"></div>
Equity Curve
</div>
<div style="display:flex;gap:0.6rem;align-items:center;">
<span style="font-size:0.68rem;color:var(--muted);display:flex;align-items:center;gap:0.3rem;">
<span style="display:inline-block;width:20px;height:2px;background:#4f46e5;border-radius:2px;"></span> Equity
</span>
<span style="font-size:0.68rem;color:var(--muted);display:flex;align-items:center;gap:0.3rem;">
<span style="display:inline-block;width:20px;height:2px;background:#00d4aa;border-radius:2px;border-bottom:2px dashed #00d4aa;height:0;"></span> Balance
</span>
<div class="card-badge" id="equity-badge">Live</div>
</div>
</div>
<div class="chart-wrap equity-chart-wrap">
<canvas id="equityChart"></canvas>
</div>
</div>
<!-- Drawdown Chart -->
<div class="card">
<div class="card-header">
<div class="card-title">
<div class="dot" style="background:var(--red);"></div>
Drawdown Analysis
</div>
<span class="card-badge" id="max-dd-badge" style="background:rgba(239,68,68,0.12);color:var(--red);">Max DD: 0%</span>
</div>
<div class="chart-wrap drawdown-chart-wrap">
<canvas id="drawdownChart"></canvas>
</div>
</div>
</div>
<!-- Right: AI panel -->
<div class="ai-panel-col">
<div class="ai-panel-card" id="ai-panel">
<div class="ai-panel-header">
<div class="ai-panel-title">
<span class="ai-icon"></span>
AI Summary
</div>
<a href="#" class="view-details-link" id="ai-view-details" onclick="toggleAIDetails(); return false;">View Details →</a>
</div>
<!-- Waiting state -->
<div class="ai-waiting" id="ai-waiting">
<div class="ai-pulse-dot"></div>
<span>Waiting for optimization to start...</span>
</div>
<!-- Active state -->
<div class="ai-content" id="ai-content" style="display:none;">
<!-- Collapsed preview (always shown when data loaded) -->
<div class="ai-preview" id="ai-preview">
<div class="ai-headline" id="ai-headline" style="display:none;"></div>
<p class="ai-diagnosis" id="ai-diagnosis-text"></p>
<button class="ai-expand-btn" id="ai-expand-btn" onclick="toggleAIDetails()">View Details →</button>
</div>
<!-- Expanded details (hidden by default) -->
<div class="ai-details" id="ai-details">
<div id="ai-strengths-section" style="display:none;">
<div class="ai-sub-title">Top Patterns</div>
<ul class="ai-check-list" id="ai-strengths-list"></ul>
</div>
<div class="ai-suggestion-box" id="ai-suggestion-box" style="display:none;">
<div class="ai-sub-title" style="margin-bottom:0.3rem;">Key Suggestion</div>
<p class="ai-suggestion-text" id="ai-suggestion-text"></p>
<div class="ai-badge">AI</div>
</div>
<div id="ai-risks-section" style="display:none;">
<div class="ai-sub-title warn">Risk Flags</div>
<ul class="ai-warn-list" id="ai-risks-list"></ul>
</div>
</div>
<div class="ai-confidence-row">
<span class="ai-conf-badge" id="ai-panel-conf-badge">analyzing</span>
<span class="ai-run-ref" id="ai-run-ref"></span>
</div>
</div>
</div>
</div>
</div>
<!-- ── Live Intelligence (thinking feed / param changes / validation) ── -->
<div class="live-intel-row">
<!-- AI Thinking Feed -->
<div class="live-intel-card accent">
<div class="live-intel-title">
<span class="icon"></span>
AI Thinking
<span class="badge" id="tf-badge">Idle</span>
</div>
<div class="live-intel-body" id="thinking-feed">
<div class="live-intel-empty">Waiting for the optimizer to start…<br><span style="font-size:0.68rem">The AI's reasoning will stream here in real time.</span></div>
</div>
</div>
<!-- Parameter Changes -->
<div class="live-intel-card">
<div class="live-intel-title">
<svg class="icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
Parameter Changes
<span class="badge" id="pc-badge"></span>
</div>
<div class="live-intel-body" id="param-changes-list">
<div class="live-intel-empty">No iterations yet.<br><span style="font-size:0.68rem">AI-driven parameter edits appear per iteration.</span></div>
</div>
</div>
<!-- Validation Activity -->
<div class="live-intel-card">
<div class="live-intel-title">
<svg class="icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
Validation
<span class="badge" id="val-badge">Pending</span>
</div>
<div class="live-intel-body" id="validation-panel">
<div class="live-intel-empty">Validation runs after optimization.<br><span style="font-size:0.68rem">Out-of-sample + sensitivity tests.</span></div>
</div>
</div>
</div>
<!-- ── Bottom row ───────────────────────────────────────────── -->
<div class="bottom-row">
<!-- Top Parameters -->
<div class="bottom-card">
<div class="bottom-card-title">
Top Parameters
<span style="font-size:0.65rem;color:var(--muted);font-weight:400;">(Best Run)</span>
</div>
<div style="overflow-x:auto;flex:1;">
<table class="params-table" id="params-table">
<thead>
<tr>
<th>Parameter</th>
<th>Value</th>
<th>Impact</th>
</tr>
</thead>
<tbody id="params-tbody">
<tr><td colspan="3" class="empty-state">Run optimizer to see parameters</td></tr>
</tbody>
</table>
</div>
</div>
<!-- Optimization Progress -->
<div class="bottom-card">
<div class="bottom-card-title">Optimization Progress</div>
<div style="display:flex;flex-direction:column;gap:0.5rem;flex:1;">
<div class="opt-gen-label" id="opt-gen">Generation — / —</div>
<div class="opt-progress-bar-track">
<div class="opt-progress-bar-fill" id="opt-progress-fill" style="width:0%"></div>
</div>
<div class="opt-stats-grid">
<div class="opt-stat">
<div class="opt-stat-label">Current Phase</div>
<div class="opt-stat-val" id="opt-phase">Waiting</div>
</div>
<div class="opt-stat">
<div class="opt-stat-label">Elapsed</div>
<div class="opt-stat-val" id="opt-elapsed"></div>
</div>
<div class="opt-stat">
<div class="opt-stat-label">Est. Remaining</div>
<div class="opt-stat-val" id="opt-remaining"></div>
</div>
</div>
<!-- Phase steps -->
<div class="phase-steps" id="phase-steps">
<div class="phase-step" id="phase-step-1">
<div class="phase-dot">1</div>
<div class="phase-name">Exploration</div>
<div class="phase-sub" id="phase-sub-1"></div>
</div>
<div class="phase-connector"></div>
<div class="phase-step" id="phase-step-2">
<div class="phase-dot">2</div>
<div class="phase-name">Iteration</div>
<div class="phase-sub" id="phase-sub-2"></div>
</div>
<div class="phase-connector"></div>
<div class="phase-step" id="phase-step-3">
<div class="phase-dot">3</div>
<div class="phase-name">Validation</div>
<div class="phase-sub" id="phase-sub-3"></div>
</div>
</div>
</div>
</div>
<!-- Recent Runs -->
<div class="bottom-card">
<div class="bottom-card-title">
Recent Runs
<a href="/reports" class="view-all-link">View All →</a>
</div>
<div style="overflow-x:auto;flex:1;">
<table class="runs-table">
<thead>
<tr>
<th>Run</th>
<th>Net Profit</th>
<th>PF</th>
<th>Max DD</th>
<th>Verdict</th>
<th></th>
</tr>
</thead>
<tbody id="recent-runs-tbody">
<tr><td colspan="6" class="empty-state">No runs yet</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- ── Live log (collapsible) ───────────────────────────────── -->
<div class="log-panel">
<button class="log-toggle-btn" id="log-toggle-btn" onclick="toggleLog()">
<span style="display:flex;align-items:center;gap:0.5rem;">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
Live Log
<span id="log-count" style="background:rgba(100,116,139,0.2);color:var(--muted);font-size:0.65rem;padding:0.05rem 0.4rem;border-radius:999px;font-weight:600;">0</span>
</span>
<svg class="log-toggle-icon" id="log-toggle-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<div class="log-body" id="log-body">
<div class="log-feed" id="log-feed"></div>
</div>
</div>
</div><!-- /.main-content -->
</div><!-- /.app-shell -->
<!-- ══════════════════════════════════════
SETTINGS MODAL
══════════════════════════════════════ -->
<div class="settings-overlay" id="settings-overlay" style="display:none;" onclick="if(event.target===this)closeSettings()">
<div class="settings-card">
<div class="settings-header">
<div class="settings-title">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/>
</svg>
Settings
</div>
<button class="settings-close-btn" onclick="closeSettings()"></button>
</div>
<div class="settings-tabs">
<div class="settings-tab active" data-tab="ai" onclick="switchSettingsTab('ai')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
AI Engine
</div>
<div class="settings-tab" data-tab="mt5" onclick="switchSettingsTab('mt5')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
MetaTrader 5
</div>
<div class="settings-tab" data-tab="broker" onclick="switchSettingsTab('broker')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6"/></svg>
Broker
</div>
<div class="settings-tab" data-tab="thresholds" onclick="switchSettingsTab('thresholds')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg>
Thresholds
</div>
</div>
<div class="settings-body">
<!-- AI Engine tab -->
<div class="settings-panel active" id="stab-ai">
<div class="settings-section-label">AI Analysis Engine</div>
<div class="settings-toggle-row" style="margin-bottom:0.75rem;">
<div>
<div class="settings-toggle-label">Enable AI Insights</div>
<div class="settings-toggle-sub">Claude analyzes each run and provides actionable suggestions</div>
</div>
<button class="settings-toggle" id="s-ai-enabled" onclick="toggleSettingsBool(this)"></button>
</div>
<div class="settings-grid full">
<div class="settings-field">
<label>Anthropic API Key</label>
<input type="password" class="settings-input mono" id="s-ai-key" placeholder="sk-ant-api03-..." autocomplete="off">
<div class="settings-hint">Your Claude API key from console.anthropic.com. Kept locally in config.yaml.</div>
</div>
</div>
<div class="settings-grid" style="margin-top:0.75rem;">
<div class="settings-field">
<label>Model</label>
<select class="settings-select" id="s-ai-model">
<option value="claude-opus-4-7">claude-opus-4-7 (Most capable)</option>
<option value="claude-sonnet-4-6">claude-sonnet-4-6 (Balanced)</option>
<option value="claude-haiku-4-5-20251001">claude-haiku-4-5 (Fast)</option>
</select>
</div>
<div class="settings-field">
<label>Timeout (seconds)</label>
<input type="number" class="settings-input" id="s-ai-timeout" min="10" max="120" step="5">
</div>
</div>
</div>
<!-- MetaTrader 5 tab -->
<div class="settings-panel" id="stab-mt5">
<div class="settings-section-label">Terminal Paths</div>
<div class="settings-grid full">
<div class="settings-field">
<label>Terminal Executable</label>
<input type="text" class="settings-input mono" id="s-mt5-exe" placeholder="C:/Program Files/MetaTrader 5/terminal64.exe">
<div class="settings-hint">Full path to terminal64.exe</div>
</div>
<div class="settings-field" style="margin-top:0.5rem;">
<label>AppData Path</label>
<input type="text" class="settings-input mono" id="s-mt5-appdata" placeholder="C:/Users/.../AppData/Roaming/MetaQuotes/Terminal/...">
</div>
<div class="settings-field" style="margin-top:0.5rem;">
<label>MQL5 Files Path</label>
<input type="text" class="settings-input mono" id="s-mt5-mql5" placeholder="C:/Users/.../MQL5/Files">
</div>
</div>
<div class="settings-section-label">Tester Settings</div>
<div class="settings-grid">
<div class="settings-field">
<label>Tester Timeout (seconds)</label>
<input type="number" class="settings-input" id="s-mt5-timeout" min="30" max="600" step="10">
</div>
<div class="settings-field">
<label>Tester Model</label>
<select class="settings-select" id="s-mt5-model">
<option value="1">1 — Every tick</option>
<option value="2">2 — Every tick based on real ticks</option>
<option value="3">3 — OHLC (M1)</option>
<option value="4">4 — Open prices only</option>
<option value="5">5 — Math calculations</option>
</select>
<div class="settings-hint">Strategy Tester modeling method</div>
</div>
</div>
</div>
<!-- Broker tab -->
<div class="settings-panel" id="stab-broker">
<div class="settings-section-label">Broker Configuration</div>
<div class="settings-grid">
<div class="settings-field">
<label>Timezone Offset (hours)</label>
<input type="number" class="settings-input" id="s-broker-tz" min="-12" max="14" step="0.5">
<div class="settings-hint">GMT offset, e.g. 3 for EET (Eastern European Time)</div>
</div>
<div class="settings-field">
<label>Account Deposit ($)</label>
<input type="number" class="settings-input" id="s-broker-deposit" min="100" step="100">
</div>
<div class="settings-field">
<label>Leverage (1:X)</label>
<input type="number" class="settings-input" id="s-broker-leverage" min="1" max="2000" step="1">
</div>
</div>
</div>
<!-- Thresholds tab -->
<div class="settings-panel" id="stab-thresholds">
<div class="settings-section-label">Quality Filters</div>
<div class="settings-grid">
<div class="settings-field">
<label>Min Trades</label>
<input type="number" class="settings-input" id="s-thr-trades" min="1" step="1">
<div class="settings-hint">Minimum trades per backtest to pass</div>
</div>
<div class="settings-field">
<label>Min Profit Factor</label>
<input type="number" class="settings-input" id="s-thr-pf" min="0" max="10" step="0.05">
</div>
<div class="settings-field">
<label>Min Calmar Ratio</label>
<input type="number" class="settings-input" id="s-thr-calmar" min="0" max="10" step="0.05">
</div>
<div class="settings-field">
<label>Max OOS Degradation</label>
<input type="number" class="settings-input" id="s-thr-oos" min="0" max="1" step="0.01">
<div class="settings-hint">Max allowed out-of-sample score drop (01)</div>
</div>
<div class="settings-field">
<label>Sensitivity Tolerance</label>
<input type="number" class="settings-input" id="s-thr-sens" min="0" max="1" step="0.01">
<div class="settings-hint">Allowed parameter sensitivity variance (01)</div>
</div>
</div>
</div>
</div><!-- /.settings-body -->
<div class="settings-footer">
<span class="settings-save-msg" id="settings-save-msg" style="opacity:0;"></span>
<button class="settings-btn cancel" onclick="closeSettings()">Cancel</button>
<button class="settings-btn save" id="settings-save-btn" onclick="saveSettings()">Save Settings</button>
</div>
</div>
</div>
<!-- ══════════════════════════════════════
RUN DETAIL MODAL
══════════════════════════════════════ -->
<div class="run-detail-overlay" id="run-detail-overlay" style="display:none;" onclick="if(event.target===this)closeRunDetail()">
<div class="run-detail-card">
<div class="run-detail-header">
<div>
<div class="run-detail-title" id="rd-run-id">Run Details</div>
<div class="run-detail-sub" id="rd-meta"></div>
</div>
<button class="settings-close-btn" onclick="closeRunDetail()"></button>
</div>
<div class="run-detail-body">
<div class="rd-metrics-grid" id="rd-metrics"></div>
<div class="rd-section-title">Parameters</div>
<div class="rd-params-wrap">
<table class="params-table" style="width:100%">
<thead><tr><th>Parameter</th><th>Value</th></tr></thead>
<tbody id="rd-params-tbody">
<tr><td colspan="2" class="empty-state">Loading...</td></tr>
</tbody>
</table>
</div>
<div id="rd-ai-section" style="display:none;">
<div class="rd-section-title">AI Reasoning</div>
<div id="rd-ai-content"></div>
</div>
</div>
<div class="run-detail-footer" id="rd-footer">
<button class="verdict-btn ghost" onclick="closeRunDetail()">Close</button>
</div>
</div>
</div>
<!-- ══════════════════════════════════════
VERDICT OVERLAY
══════════════════════════════════════ -->
<div class="verdict-overlay" id="verdict-overlay" style="display:none;">
<div class="verdict-card" id="verdict-card">
<div class="verdict-header-row">
<div class="verdict-icon-big" id="verdict-icon"></div>
<div>
<div class="verdict-title" id="verdict-title">RECOMMENDED</div>
<div class="verdict-subtitle" id="verdict-subtitle">Optimization complete.</div>
</div>
</div>
<div class="verdict-metrics-row" id="verdict-metrics"></div>
<div class="verdict-actions-row" id="verdict-actions"></div>
</div>
</div>
<!-- ══════════════════════════════════════
SCRIPTS
══════════════════════════════════════ -->
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js"></script>
<script src="/static/js/dashboard.js"></script>
</body>
</html>