Files
LEGSTECH Optimizer d9ed625579 docs: update repo URLs after rename to Apex_AI_MT5_EA_Optimizer
Replace all 13 occurrences of github.com/tonnylegacy/MT5_Optimizer
with github.com/tonnylegacy/Apex_AI_MT5_EA_Optimizer across:
- README.md (clone instructions)
- SUBMISSION.md (GitHub URL field + checklist)
- TODO_NEXT.md (next-session prompt)
- PROJECT_HANDOFF.md (header + status table + footer)
- docs/GETTING_STARTED.md (clone instructions, issue tracker link)
- ui/templates/dashboard.html (Source on GitHub nav link)

Also clean up the SUBMISSION.md Video/GIF block to list both the
YouTube video and the in-README demo GIF.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 08:14:24 +00:00

2401 lines
87 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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="icon" type="image/png" href="/static/img/favicon.png">
<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);
--brand-cyan: #00d4ff;
--brand-blue: #4f6cff;
--brand-purple: #9333ea;
--accent: #4f6cff;
--accent2: #8b5cf6;
--teal: #00d4aa;
--text: #e2e8f0;
--muted: #64748b;
--green: #10b981;
--red: #ef4444;
--yellow: #f59e0b;
--nav-active-bg: rgba(79,108,255,0.18);
--nav-active-border: #4f6cff;
}
/* ── 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-mark,
.cmd-mark {
display: block;
flex-shrink: 0;
filter: drop-shadow(0 0 10px rgba(79,108,255,0.3));
}
.logo-text {
font-size: 1.25rem;
font-weight: 800;
letter-spacing: 0.12em;
background: linear-gradient(180deg, #ffffff 0%, #d4dae6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.logo-tagline {
font-size: 0.58rem;
color: var(--muted);
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
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,212,255,0.55), rgba(79,108,255,0.65), rgba(147,51,234,0.55));
}
.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(180deg, #ffffff 0%, #d4dae6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.cmd-tagline {
font-size: 0.5rem;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
display: inline-flex;
gap: 0.35em;
}
.cmd-tagline .tag-cyan { color: #00d4ff; }
.cmd-tagline .tag-purple { color: #b06bff; }
.cmd-tagline .tag-white { color: #cfd5e0; }
.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, #00d4ff, #4f6cff, #9333ea);
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, #4f6cff 0%, #9333ea 100%);
color: white;
box-shadow: 0 0 0 1px rgba(147,51,234,0.4), 0 4px 14px rgba(79,108,255,0.25);
}
.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, #00d4ff, #4f6cff, #9333ea);
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">
<img src="/static/img/apex-mark.png" alt="APEX" class="logo-mark" width="38" height="42">
<div>
<span class="logo-text">APEX</span>
<span class="logo-tagline">AI Powered 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/Apex_AI_MT5_EA_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">
<img src="/static/img/apex-mark.png" alt="APEX" class="cmd-mark" width="34" height="38">
<div class="cmd-logo-text">
<span class="cmd-apex">APEX</span>
<span class="cmd-tagline">
<span class="tag-cyan">AI</span>
<span class="tag-white">POWERED</span>
<span class="tag-purple">EA</span>
<span class="tag-white">OPTIMIZER</span>
</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 / no-key state -->
<div class="ai-waiting" id="ai-waiting">
<div class="ai-pulse-dot"></div>
<span id="ai-waiting-text">Waiting for optimization to start...</span>
<a href="#" id="ai-waiting-action" onclick="openSettings(); return false;"
style="display:none;font-size:0.72rem;color:var(--accent2);text-decoration:underline;cursor:pointer;">
Configure API key in Settings →
</a>
</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>