drone-env / server /static /index.html
manikandan-n-07's picture
Final: inclusive logs for Neural Console in HF
02fbe0a
Raw
History Blame
41.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>๐Ÿš Drone Delivery โ€” OpenEnv</title>
<link
href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=JetBrains+Mono:wght@300;400;500;700&display=swap"
rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script>
<style>
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
TOKENS
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
:root {
--bg0: #06080f;
--bg1: #0c1120;
--glass: rgba(255, 255, 255, 0.04);
--glass-b: rgba(255, 255, 255, 0.09);
--glass-hover: rgba(255, 255, 255, 0.07);
--border: rgba(255, 255, 255, 0.08);
--border-bright: rgba(255, 255, 255, 0.18);
--cyan: #00e5ff;
--green: #00ffb2;
--amber: #ffb300;
--red: #ff4d6d;
--purple: #b388ff;
--text: #e8f4ff;
--dim: rgba(232, 244, 255, 0.45);
--dimmer: rgba(232, 244, 255, 0.22);
--glow-cyan: 0 0 24px rgba(0, 229, 255, 0.35);
--glow-green: 0 0 20px rgba(0, 255, 178, 0.35);
--glow-amber: 0 0 20px rgba(255, 179, 0, 0.35);
--glow-red: 0 0 20px rgba(255, 77, 109, 0.3);
--f-display: 'Syne', sans-serif;
--f-mono: 'JetBrains Mono', monospace;
--cell: 34px;
--r: 16px;
--r-sm: 10px;
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
ADVANCED ANIMATIONS
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
@keyframes glowPulse {
0% {
box-shadow: 0 0 5px rgba(0, 229, 255, 0.1);
}
100% {
box-shadow: 0 0 20px rgba(0, 229, 255, 0.4);
}
}
.glow-on-hover:hover {
animation: glowPulse 1.5s infinite alternate;
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
NEURAL CONSOLE (TERMINAL)
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.console {
background: rgba(0, 0, 0, 0.4);
border: 1px solid var(--border);
border-radius: var(--r-sm);
padding: 12px;
font-family: var(--f-mono);
font-size: 0.65rem;
color: var(--green);
height: 140px;
overflow-y: auto;
line-height: 1.5;
margin-top: 8px;
position: relative;
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.5);
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
.console::-webkit-scrollbar {
width: 4px;
}
.console::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
.console::before {
content: 'NEURAL_LINK_STABLE';
position: absolute;
top: 4px;
right: 8px;
font-size: 0.5rem;
opacity: 0.4;
letter-spacing: 2px;
}
.console-line {
margin-bottom: 4px;
border-left: 2px solid transparent;
padding-left: 6px;
}
.console-line.info {
border-left-color: var(--cyan);
color: var(--cyan);
}
.console-line.success {
border-left-color: var(--green);
color: var(--green);
}
.console-line.warn {
border-left-color: var(--amber);
color: var(--amber);
}
/* Split Console Layout */
.console-row {
display: flex;
gap: 12px;
width: 100%;
}
.console-wrapper {
flex: 1;
display: flex;
flex-direction: column;
}
.console-header {
font-size: 0.55rem;
color: var(--cyan);
margin-bottom: 6px;
letter-spacing: 1px;
opacity: 0.8;
display: flex;
justify-content: space-between;
}
.console-header span {
opacity: 0.5;
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
MISSION CONTROL BAR
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.mission-control {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
background: var(--bg1);
border-top: 1px solid var(--border-bright);
border-radius: 0 0 var(--r) var(--r);
}
.control-group {
display: flex;
gap: 8px;
align-items: center;
}
.status-indicator {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.6rem;
color: var(--dim);
}
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--dim);
}
.status-dot.active {
background: var(--green);
box-shadow: var(--glow-green);
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
RESET + BASE
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
RESET + BASE
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
background: var(--bg0);
color: var(--text);
font-family: var(--f-mono);
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
/* โ”€โ”€ Animated background โ”€โ”€ */
body::before {
content: '';
position: fixed;
inset: 0;
z-index: 0;
background:
radial-gradient(ellipse 60% 40% at 20% 30%, rgba(0, 229, 255, 0.05) 0%, transparent 60%),
radial-gradient(ellipse 50% 40% at 80% 70%, rgba(0, 255, 178, 0.04) 0%, transparent 60%),
radial-gradient(ellipse 40% 30% at 50% 10%, rgba(179, 136, 255, 0.04) 0%, transparent 60%);
animation: bgPulse 12s ease-in-out infinite alternate;
pointer-events: none;
}
body::after {
content: '';
position: fixed;
inset: 0;
z-index: 0;
background-image:
linear-gradient(rgba(0, 229, 255, 0.025) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 229, 255, 0.025) 1px, transparent 1px);
background-size: 48px 48px;
pointer-events: none;
}
@keyframes bgPulse {
0% {
opacity: 0.6;
}
100% {
opacity: 1;
}
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
PROCESSING OVERLAY
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
#processing {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(6, 8, 15, 0.85);
backdrop-filter: blur(12px);
padding: 30px 50px;
border-radius: 16px;
border: 1px solid var(--border-bright);
z-index: 10000;
display: none;
flex-direction: column;
align-items: center;
gap: 15px;
box-shadow: 0 0 60px rgba(0, 229, 255, 0.25);
}
.spinner {
width: 44px;
height: 44px;
border: 3px solid var(--glass-b);
border-top-color: var(--cyan);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.processing-text {
font-family: var(--f-mono);
font-size: 0.85rem;
color: var(--cyan);
text-transform: uppercase;
letter-spacing: 3px;
font-weight: 700;
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
LAYOUT
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
#app {
position: relative;
z-index: 1;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* โ”€โ”€ Header โ”€โ”€ */
header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 28px;
background: rgba(12, 17, 32, 0.7);
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(24px) saturate(180%);
border-bottom: 1px solid var(--border);
position: sticky;
top: 0;
z-index: 100;
}
.logo {
font-family: var(--f-display);
font-size: 1.25rem;
font-weight: 800;
letter-spacing: .05em;
color: var(--cyan);
text-shadow: var(--glow-cyan);
display: flex;
align-items: center;
gap: 10px;
}
.critical-flash {
color: #ff3d00 !important;
text-shadow: 0 0 15px rgba(255, 61, 0, 0.8);
animation: pulse-red .8s infinite;
}
@keyframes pulse-red {
0% {
opacity: 1;
}
50% {
opacity: 0.4;
}
100% {
opacity: 1;
}
}
.title-icon {
font-size: 1.5rem;
animation: rotorSpin 3s linear infinite;
display: inline-block;
}
@keyframes rotorSpin {
0%,
100% {
transform: rotate(0deg) scale(1);
}
50% {
transform: rotate(5deg) scale(1.05);
}
}
.header-right {
display: flex;
gap: 10px;
align-items: center;
}
.hbadge {
font-size: .65rem;
font-family: var(--f-mono);
letter-spacing: .1em;
padding: 4px 12px;
border-radius: 100px;
background: var(--glass);
border: 1px solid var(--border);
color: var(--dim);
}
.hbadge.live {
border-color: var(--green);
color: var(--green);
text-shadow: var(--glow-green);
}
/* โ”€โ”€ Main grid โ”€โ”€ */
.main {
display: grid;
grid-template-columns: 260px 1fr 280px;
gap: 14px;
padding: 14px 18px 20px;
flex: 1;
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
GLASS PANEL
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.panel {
background: var(--glass);
border: 1px solid var(--border);
border-radius: var(--r);
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
overflow: hidden;
position: relative;
transition: border-color .25s, box-shadow .25s;
}
.panel:hover {
border-color: var(--border-bright);
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.4);
}
/* shimmer top line */
.panel::before {
content: '';
position: absolute;
top: 0;
left: 10%;
right: 10%;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.15), transparent);
border-radius: 2px;
}
.panel-inner {
padding: 16px;
}
.panel-title {
font-family: var(--f-display);
font-size: .65rem;
font-weight: 700;
letter-spacing: .18em;
text-transform: uppercase;
color: var(--cyan);
margin-bottom: 14px;
display: flex;
align-items: center;
gap: 10px;
}
.panel-title svg {
opacity: .7;
flex-shrink: 0;
}
.panel-title::after {
content: '';
flex: 1;
height: 1px;
background: linear-gradient(90deg, var(--border-bright), transparent);
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
LEFT COLUMN
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.left-col {
display: flex;
flex-direction: column;
gap: 12px;
}
/* Task buttons */
.task-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.task-btn {
width: 100%;
font-family: var(--f-display);
font-size: .72rem;
font-weight: 700;
letter-spacing: .06em;
padding: 12px 14px;
border: 1px solid var(--border);
border-radius: var(--r-sm);
background: var(--glass);
color: var(--dim);
cursor: pointer;
text-align: left;
transition: all .2s;
display: flex;
align-items: center;
gap: 8px;
}
.task-btn:hover {
background: var(--glass-hover);
border-color: rgba(0, 229, 255, 0.3);
color: var(--text);
}
.task-btn.active {
background: rgba(0, 229, 255, 0.08);
border-color: var(--cyan);
color: var(--cyan);
box-shadow: var(--glow-cyan), inset 0 0 20px rgba(0, 229, 255, 0.05);
}
.task-btn .dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: currentColor;
box-shadow: 0 0 8px currentColor;
flex-shrink: 0;
}
.task-tag {
margin-left: auto;
font-size: .55rem;
opacity: .6;
padding: 2px 7px;
border: 1px solid currentColor;
border-radius: 100px;
}
/* Stat cards */
.stat-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.stat-card {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border);
border-radius: var(--r-sm);
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 3px;
}
.stat-label {
font-size: .58rem;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--dim);
}
.stat-value {
font-size: 1.1rem;
font-weight: 700;
color: var(--text);
font-family: var(--f-display);
}
.stat-value.green {
color: var(--green);
text-shadow: var(--glow-green);
}
.stat-value.cyan {
color: var(--cyan);
text-shadow: var(--glow-cyan);
}
.stat-value.amber {
color: var(--amber);
text-shadow: var(--glow-amber);
}
.stat-value.red {
color: var(--red);
text-shadow: var(--glow-red);
}
/* Battery bar */
.battery-wrap {
margin-top: 2px;
}
.battery-label {
display: flex;
justify-content: space-between;
font-size: .6rem;
color: var(--dim);
margin-bottom: 5px;
letter-spacing: .08em;
}
.battery-track {
height: 6px;
border-radius: 100px;
background: rgba(255, 255, 255, 0.06);
overflow: hidden;
}
.battery-fill {
height: 100%;
border-radius: 100px;
transition: width .4s ease, background .4s ease;
background: linear-gradient(90deg, var(--cyan), var(--green));
box-shadow: 0 0 10px rgba(0, 229, 255, 0.5);
}
.battery-fill.low {
background: linear-gradient(90deg, var(--amber), var(--red));
}
/* Control buttons */
.btn-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 6px;
margin-top: 4px;
}
.ctrl-btn {
font-family: var(--f-display);
font-size: .65rem;
font-weight: 700;
letter-spacing: .06em;
padding: 9px 6px;
border: 1px solid var(--border);
border-radius: var(--r-sm);
background: var(--glass);
color: var(--dim);
cursor: pointer;
text-align: center;
transition: all .18s;
user-select: none;
}
.ctrl-btn:hover {
border-color: rgba(0, 229, 255, 0.4);
color: var(--cyan);
background: rgba(0, 229, 255, 0.06);
}
.ctrl-btn:active {
transform: scale(.94);
}
.ctrl-btn.primary {
border-color: var(--cyan);
color: var(--cyan);
background: rgba(0, 229, 255, 0.08);
}
.ctrl-btn.primary:hover {
box-shadow: var(--glow-cyan);
}
.ctrl-btn.success {
border-color: var(--green);
color: var(--green);
background: rgba(0, 255, 178, 0.07);
}
.ctrl-btn.danger {
border-color: var(--red);
color: var(--red);
background: rgba(255, 77, 109, 0.07);
}
.ctrl-btn.dir {
font-size: .85rem;
}
.btn-spacer {
visibility: hidden;
}
/* Auto-play */
.auto-row {
display: flex;
gap: 8px;
align-items: center;
margin-top: 6px;
}
.toggle-label {
font-size: .65rem;
color: var(--dim);
letter-spacing: .06em;
}
.toggle {
position: relative;
width: 36px;
height: 20px;
cursor: pointer;
}
.toggle input {
display: none;
}
.toggle-track {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.08);
border-radius: 100px;
transition: background .2s;
border: 1px solid var(--border);
}
.toggle input:checked~.toggle-track {
background: rgba(0, 229, 255, 0.25);
border-color: var(--cyan);
}
.toggle-thumb {
position: absolute;
top: 3px;
left: 3px;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--dim);
transition: all .2s;
}
.toggle input:checked~.toggle-thumb {
left: 19px;
background: var(--cyan);
box-shadow: var(--glow-cyan);
}
.speed-label {
font-size: .6rem;
color: var(--dimmer);
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
CENTER โ€” GRID VIEWER
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.center-col {
display: flex;
flex-direction: column;
gap: 12px;
}
.grid-panel {
flex: 1;
}
.grid-outer {
display: flex;
justify-content: center;
align-items: center;
padding: 16px;
min-height: 380px;
position: relative;
}
.grid-wrap {
display: inline-block;
border: 1px solid rgba(0, 229, 255, 0.12);
border-radius: 10px;
background: rgba(6, 8, 15, 0.6);
box-shadow: 0 0 60px rgba(0, 229, 255, 0.06), inset 0 0 30px rgba(0, 0, 0, 0.5);
overflow: hidden;
position: relative;
}
.grid-row {
display: flex;
line-height: 1;
}
.grid-cell {
width: var(--cell);
height: var(--cell);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3rem;
position: relative;
transition: all .2s cubic-bezier(.34, 1.56, .64, 1);
user-select: none;
}
.grid-cell:hover {
transform: scale(1.1);
z-index: 10;
filter: brightness(1.2);
}
.grid-cell.road {
background: rgba(0, 229, 255, 0.02);
}
.grid-cell.building {
background: rgba(255, 255, 255, 0.04);
}
.grid-cell.tree {
background: rgba(0, 255, 178, 0.03);
}
.grid-cell.obstacle {
background: rgba(255, 179, 0, 0.06);
}
.grid-cell.delivery {
background: rgba(255, 179, 0, 0.08);
}
.grid-cell.drone {
background: rgba(0, 229, 255, 0.12);
box-shadow: inset 0 0 12px rgba(0, 229, 255, 0.3);
}
/* Path trail */
.trail-dot {
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(0, 229, 255, 0.35);
pointer-events: none;
animation: trailFade 1.5s ease-out forwards;
}
@keyframes trailFade {
to {
opacity: 0;
transform: scale(0.4);
}
}
/* Message bar */
.msg-bar {
font-size: .75rem;
padding: 9px 14px;
background: rgba(255, 255, 255, 0.03);
border-top: 1px solid var(--border);
color: var(--dim);
letter-spacing: .04em;
min-height: 34px;
display: flex;
align-items: center;
gap: 8px;
}
.msg-bar.good {
color: var(--green);
}
.msg-bar.warn {
color: var(--amber);
}
.msg-bar.bad {
color: var(--red);
}
/* Progress bar (deliveries) */
.delivery-bar-wrap {
padding: 0 16px 14px;
}
.delivery-bar-label {
display: flex;
justify-content: space-between;
font-size: .6rem;
color: var(--dim);
margin-bottom: 5px;
letter-spacing: .08em;
}
.delivery-bar-track {
height: 4px;
border-radius: 100px;
background: rgba(255, 255, 255, 0.06);
overflow: hidden;
}
.delivery-bar-fill {
height: 100%;
border-radius: 100px;
background: linear-gradient(90deg, var(--amber), var(--green));
transition: width .5s cubic-bezier(.34, 1.56, .64, 1);
box-shadow: 0 0 10px rgba(0, 255, 178, 0.4);
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
RIGHT COLUMN
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.right-col {
display: flex;
flex-direction: column;
gap: 12px;
}
/* Reward chart */
.chart-wrap {
position: relative;
height: 160px;
}
/* Episode log */
.log-list {
display: flex;
flex-direction: column;
gap: 5px;
max-height: 200px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
}
.log-item {
display: flex;
gap: 8px;
align-items: flex-start;
font-size: .65rem;
color: var(--dim);
padding: 5px 8px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.02);
border: 1px solid transparent;
animation: logSlideIn .2s ease;
}
@keyframes logSlideIn {
from {
opacity: 0;
transform: translateX(6px);
}
}
.log-item.good {
color: var(--green);
border-color: rgba(0, 255, 178, 0.1);
}
.log-item.warn {
color: var(--amber);
border-color: rgba(255, 179, 0, 0.1);
}
.log-item.bad {
color: var(--red);
border-color: rgba(255, 77, 109, 0.1);
}
.log-step {
opacity: .5;
flex-shrink: 0;
}
/* Analytics panel */
.analytics-grid {
display: flex;
flex-direction: column;
gap: 8px;
}
.analytics-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: .68rem;
padding: 6px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.analytics-key {
color: var(--dim);
letter-spacing: .06em;
}
.analytics-val {
font-weight: 500;
color: var(--text);
font-family: var(--f-display);
}
.analytics-val.cyan {
color: var(--cyan);
}
.analytics-val.green {
color: var(--green);
}
/* Action distribution mini chart */
.action-dist {
display: flex;
gap: 4px;
align-items: flex-end;
height: 40px;
}
.action-bar-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
flex: 1;
}
.action-bar {
width: 100%;
border-radius: 3px 3px 0 0;
background: rgba(0, 229, 255, 0.3);
transition: height .4s cubic-bezier(.34, 1.56, .64, 1);
min-height: 2px;
}
.action-bar-lbl {
font-size: .5rem;
color: var(--dimmer);
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
LEGEND
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.legend-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5px;
}
.legend-item {
display: flex;
align-items: center;
gap: 7px;
font-size: .63rem;
color: var(--dim);
}
.legend-emoji {
font-size: 1rem;
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
SCROLLBAR
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
::-webkit-scrollbar {
width: 5px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.08);
border-radius: 10px;
}
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
RESPONSIVE
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
RESPONSIVE DESIGN SYSTEM
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.main {
display: grid;
grid-template-columns: 260px 1fr 280px;
gap: 16px;
padding: 16px;
width: 100%;
max-width: 1600px;
margin: 0 auto;
flex: 1;
}
/* Column containment */
.left-col, .right-col {
display: flex;
flex-direction: column;
gap: 14px;
min-width: 0; /* Prevents flex overflow */
}
/* Mission Control Refactor */
.mission-control {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
padding: 12px 16px;
background: var(--bg1);
border-top: 1px solid var(--border-bright);
border-radius: 0 0 var(--r) var(--r);
}
.control-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
/* โ”€โ”€โ”€โ”€ BREAKPOINTS โ”€โ”€โ”€โ”€ */
/* TABLET / SMALL LAPTOP */
@media (max-width: 1200px) {
.main {
grid-template-columns: 240px 1fr;
}
.right-col {
grid-column: 1 / -1;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
}
/* MOBILE (STACKED) */
@media (max-width: 860px) {
header {
padding: 12px 16px;
flex-direction: column;
gap: 10px;
}
.main {
grid-template-columns: 1fr;
padding: 10px;
}
.right-col {
grid-template-columns: 1fr;
}
.stat-grid {
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
}
.mission-control {
justify-content: center;
}
.control-group {
justify-content: center;
width: 100%;
}
.ctrl-btn {
flex: 1;
min-width: 120px;
}
.console-row {
flex-direction: column;
}
:root {
--cell: 28px;
}
}
/* SMALL MOBILE (TOUCH OPTIMIZED) */
@media (max-width: 480px) {
:root {
--cell: 24px;
}
.logo { font-size: 1.1rem; }
.panel-inner { padding: 12px; }
.ctrl-btn.dir { font-size: 1.1rem; padding: 12px 8px; }
.stat-value { font-size: 0.95rem; }
}
</style>
</head>
<body>
<div id="processing">
<div class="spinner"></div>
<div class="processing-text">Processing...</div>
</div>
<div id="loading">
<div class="spinner"></div>
<div class="loading-text">Initialising Drone Grid</div>
</div>
<div id="app">
<!-- โ”€โ”€ HEADER โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -->
<header>
<div class="logo">
<span class="logo-icon">๐Ÿš</span>
DRONE<span style="color:var(--green)">ENV</span>
</div>
<div class="header-right">
<span class="hbadge">OpenEnv v0.1</span>
<span class="hbadge live" id="hStatus">โ— LIVE</span>
</div>
</header>
<!-- โ”€โ”€ MAIN โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -->
<div class="main">
<!-- โ•โ• LEFT โ•โ• -->
<div class="left-col">
<!-- Task selector -->
<div class="panel">
<div class="panel-inner">
<div class="panel-title">Mission Select</div>
<div class="task-group" id="taskGroup">
<button class="task-btn active" data-task="easy_delivery">
<span class="dot"></span>
Easy Delivery
<span class="task-tag">10ร—10</span>
</button>
<button class="task-btn" data-task="medium_delivery">
<span class="dot"></span>
Medium Delivery
<span class="task-tag">14ร—14</span>
</button>
<button class="task-btn" data-task="hard_delivery">
<span class="dot"></span>
Hard Delivery
<span class="task-tag">18ร—18</span>
</button>
</div>
</div>
</div>
<!-- Stats -->
<div class="panel">
<div class="panel-inner">
<div class="panel-title">Episode Stats</div>
<div class="stat-grid">
<div class="stat-card">
<div class="stat-label">Step</div>
<div class="stat-value cyan" id="statStep">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Reward</div>
<div class="stat-value" id="statReward">0.000</div>
</div>
<div class="stat-card">
<div class="stat-label">Deliveries</div>
<div class="stat-value green" id="statDel">0/0</div>
</div>
<div class="stat-card">
<div class="stat-label">Score</div>
<div class="stat-value amber" id="statScore">โ€”</div>
</div>
</div>
<!-- Battery -->
<div class="battery-wrap" style="margin-top:12px;">
<div class="battery-label">
<span>BATTERY</span>
<span id="batPct">100%</span>
</div>
<div class="battery-track">
<div class="battery-fill" id="batFill" style="width:100%"></div>
</div>
</div>
</div>
</div>
<!-- Controls -->
<div class="panel">
<div class="panel-inner">
<div class="panel-title">Controls</div>
<!-- DPAD -->
<div class="btn-grid">
<div class="btn-spacer"></div>
<button class="ctrl-btn dir" id="btnUp" onclick="doStep('UP')">โ–ฒ</button>
<div class="btn-spacer"></div>
<button class="ctrl-btn dir" id="btnLeft" onclick="doStep('LEFT')">โ—€</button>
<div class="btn-spacer"></div>
<button class="ctrl-btn dir" id="btnRight" onclick="doStep('RIGHT')">โ–ถ</button>
<div class="btn-spacer"></div>
<button class="ctrl-btn dir" id="btnDown" onclick="doStep('DOWN')">โ–ผ</button>
<div class="btn-spacer"></div>
</div>
<div style="display:flex; gap:8px; margin-top:10px;">
<button class="ctrl-btn success" style="flex:1" onclick="doReset()">โŸณ Reset</button>
<button class="ctrl-btn danger" style="flex:1" onclick="doAnalyse()">โฌก Analyse</button>
</div>
<div class="auto-row" style="margin-top:10px;">
<label class="toggle">
<input type="checkbox" id="autoToggle" onchange="toggleAuto()" />
<div class="toggle-track"></div>
<div class="toggle-thumb"></div>
</label>
<span class="toggle-label">Autonomous Mode</span>
<!-- CSV button moved to Mission Control bar -->
</div>
</div>
</div>
<!-- Legend -->
<div class="panel">
<div class="panel-inner">
<div class="panel-title">Legend</div>
<div class="legend-grid">
<div class="legend-item"><span class="legend-emoji">๐Ÿš</span> Drone</div>
<div class="legend-item"><span class="legend-emoji">๐Ÿ›ฃ๏ธ</span> Road</div>
<div class="legend-item"><span class="legend-emoji">๐Ÿ“ฆ</span> Package</div>
<div class="legend-item"><span class="legend-emoji">โœ…</span> Delivered</div>
<div class="legend-item"><span class="legend-emoji">๐Ÿข</span> Building</div>
<div class="legend-item"><span class="legend-emoji">๐ŸŒณ</span> Tree</div>
<div class="legend-item"><span class="legend-emoji">๐Ÿšง</span> Obstacle</div>
</div>
</div>
</div>
</div><!-- /left -->
<!-- โ•โ• CENTER โ•โ• -->
<div class="center-col">
<div class="panel grid-panel">
<div class="panel-inner" style="padding-bottom:6px;">
<div class="panel-title">
Mission Grid
<span id="gridInfo"
style="font-size:.6rem;color:var(--dim);margin-left:auto;font-family:var(--f-mono);letter-spacing:.05em;"></span>
</div>
</div>
<div class="grid-outer" id="gridOuter">
<div class="grid-wrap" id="gridWrap">
<div style="padding:40px;color:var(--dim);font-size:.8rem;">Loading neural pathwaysโ€ฆ</div>
</div>
</div>
<!-- Mission Control Bar (No Overlap) -->
<div class="mission-control">
<div class="status-indicator">
<div class="status-dot active"></div>
<span>NEURAL_ENGINE: READY</span>
</div>
<div style="flex: 1"></div>
<div class="control-group">
<button class="ctrl-btn success glow-on-hover" id="btnStart"
style="min-width:110px; height:36px; font-weight:800;" onclick="startAuto()">START ENGINE</button>
<button class="ctrl-btn danger" id="btnStop" style="min-width:110px; height:36px; font-weight:800;"
onclick="stopAuto()">STOP ENGINE</button>
<div style="width:1px; height:24px; background:var(--border); margin: 0 4px;"></div>
<button class="ctrl-btn" id="btnExport" style="padding: 0 14px; height:36px; font-size:0.75rem;"
onclick="downloadCSV()">๐Ÿ“ฅ EXPORT AS CSV</button>
<button class="ctrl-btn" id="btnDownloadGraph" style="padding: 0 14px; height:36px; font-size:0.75rem;"
onclick="downloadGraph()">๐Ÿ“ˆ DOWNLOAD GRAPH</button>
</div>
</div>
<!-- Delivery progress -->
<div class="delivery-bar-wrap">
<div class="delivery-bar-label">
<span>PACKAGE DELIVERY PROGRESS</span>
<span id="delBarlabel">0 / 0</span>
</div>
<div class="delivery-bar-track">
<div class="delivery-bar-fill" id="delBarFill" style="width:0%"></div>
</div>
</div>
<!-- Antigravity Terminal Logs (Below progress bar) -->
<div class="panel" style="margin-top:0; border-top:1px solid var(--border);">
<div class="panel-inner" style="padding-top:10px;">
<div class="console-row">
<div class="console-wrapper">
<div class="console-header">TERMINAL_LOGS<span>[SYSTEM]</span></div>
<div class="console" id="terminalConsole" style="height:140px; color: var(--text);">
<div class="console-line info">> Awaiting system telemetry...</div>
</div>
</div>
<div class="console-wrapper">
<div class="console-header">NEURAL_TELEMETRY_JSON_STREAM <span>[LIVE]</span></div>
<div class="console" id="memoryConsole" style="height:140px; color: var(--cyan); font-size: 0.6rem;">
<div class="console-line info">> Awaiting telemetric JSON stream...</div>
</div>
</div>
</div>
</div>
</div>
<!-- Message -->
<div class="msg-bar" id="msgBar">Ready to fly.</div>
</div>
</div><!-- /center -->
<!-- โ•โ• RIGHT โ•โ• -->
<div class="right-col">
<!-- Reward trend chart -->
<div class="panel">
<div class="panel-inner">
<div class="panel-title">Reward Trend</div>
<div class="chart-wrap">
<canvas id="rewardChart"></canvas>
</div>
</div>
</div>
<!-- Neural Telemetry Console (Moved from center) -->
<div class="panel">
<div class="panel-inner">
<div class="panel-title">NEURAL_ENGINE_SYSTEM</div>
<div class="console" id="neuralConsole" style="height:160px;">
<div class="console-line info">> Neural link active.</div>
</div>
</div>
</div>
<!-- Episode log -->
<div class="panel">
<div class="panel-inner">
<div class="panel-title">In-Flight Events</div>
<div class="log-list" id="logList"></div>
</div>
</div>
<!-- Analytics -->
<div class="panel">
<div class="panel-inner">
<div class="panel-title">RL Analytics</div>
<div class="analytics-grid" id="analyticsGrid">
<div class="analytics-row">
<span class="analytics-key">STATUS</span>
<span class="analytics-val" style="color:var(--dim)">Click Analyse</span>
</div>
</div>
<!-- Action distribution -->
<div style="margin-top:12px;">
<div class="panel-title" style="margin-bottom:8px;font-size:.58rem;">Action Distribution</div>
<div class="action-dist" id="actionDist">
<div class="action-bar-wrap">
<div class="action-bar" style="height:0px" id="ab-UP"></div>
<div class="action-bar-lbl">UP</div>
</div>
<div class="action-bar-wrap">
<div class="action-bar" style="height:0px" id="ab-DOWN"></div>
<div class="action-bar-lbl">DN</div>
</div>
<div class="action-bar-wrap">
<div class="action-bar" style="height:0px" id="ab-LEFT"></div>
<div class="action-bar-lbl">LT</div>
</div>
<div class="action-bar-wrap">
<div class="action-bar" style="height:0px" id="ab-RIGHT"></div>
<div class="action-bar-lbl">RT</div>
</div>
<div class="action-bar-wrap">
<div class="action-bar" style="height:0px" id="ab-WAIT"></div>
<div class="action-bar-lbl">WT</div>
</div>
</div>
</div>
</div>
</div>
</div><!-- /right -->
</div><!-- /main -->
</div><!-- /app -->
<script src="/static/script.js?v=1.0.1"></script>
</body>
</html>