/* dashboard.css — Saltybot Main Dashboard (Issue #630) */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } :root { --bg0: #050510; --bg1: #070712; --bg2: #0a0a1a; --bd: #0c2a3a; --bd2: #1e3a5f; --dim: #374151; --mid: #6b7280; --base: #9ca3af; --hi: #d1d5db; --cyan: #06b6d4; --green: #22c55e; --amber: #f59e0b; --red: #ef4444; } html, body { height: 100%; font-family: 'Courier New', Courier, monospace; font-size: 12px; background: var(--bg0); color: var(--base); display: flex; flex-direction: column; overflow: hidden; } /* ══════════════════ TOP BAR ══════════════════ */ #topbar { display: flex; align-items: center; gap: 10px; padding: 6px 14px; background: var(--bg1); border-bottom: 1px solid var(--bd); flex-shrink: 0; flex-wrap: wrap; } #topbar-left { display: flex; align-items: baseline; gap: 8px; flex-shrink: 0; } .logo { color: #f97316; font-weight: bold; letter-spacing: .18em; font-size: 14px; } #robot-name { color: var(--mid); font-size: 10px; letter-spacing: .12em; } #topbar-status { display: flex; align-items: center; gap: 0; flex: 1; min-width: 0; flex-wrap: wrap; gap: 2px; } .stat-block { display: flex; align-items: center; gap: 5px; padding: 2px 10px; } .stat-icon { font-size: 14px; flex-shrink: 0; } .stat-body { display: flex; flex-direction: column; gap: 1px; } .stat-label { font-size: 8px; color: var(--dim); letter-spacing: .1em; text-transform: uppercase; } .stat-val { font-size: 11px; color: var(--hi); font-family: monospace; white-space: nowrap; } .stat-sep { width: 1px; height: 28px; background: var(--bd); flex-shrink: 0; } /* Battery bar */ .batt-bar { width: 28px; height: 12px; border: 1px solid var(--bd2); border-radius: 2px; overflow: hidden; position: relative; background: var(--bg0); } .batt-bar::after { content: ''; position: absolute; right: -3px; top: 3px; width: 3px; height: 6px; background: var(--bd2); border-radius: 0 1px 1px 0; } #batt-fill { height: 100%; width: 0%; background: var(--green); transition: width .5s, background .5s; } #topbar-right { display: flex; align-items: center; gap: 6px; flex-shrink: 0; margin-left: auto; } #conn-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dim); flex-shrink: 0; transition: background .3s; } #conn-dot.connected { background: var(--green); } #conn-dot.error { background: var(--red); animation: blink 1s infinite; } #conn-label { font-size: 10px; color: var(--mid); white-space: nowrap; } #ws-input { background: var(--bg2); border: 1px solid var(--bd2); border-radius: 4px; color: #67e8f9; padding: 2px 7px; font-family: monospace; font-size: 11px; width: 175px; } #ws-input:focus { outline: none; border-color: var(--cyan); } .hbtn { padding: 2px 8px; border-radius: 4px; border: 1px solid var(--bd2); background: var(--bg2); color: #67e8f9; font-family: monospace; font-size: 10px; font-weight: bold; cursor: pointer; transition: background .15s; white-space: nowrap; } .hbtn:hover { background: #0e4f69; } @keyframes blink { 0%,100%{opacity:1} 50%{opacity:.3} } @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.5} } /* ══════════════════ MAIN / GRID ══════════════════ */ #main { flex: 1; display: flex; flex-direction: column; overflow-y: auto; padding: 16px; gap: 12px; min-height: 0; } /* Auto-detect bar */ #detect-bar { display: flex; align-items: center; gap: 10px; padding: 6px 12px; background: var(--bg2); border: 1px solid var(--bd2); border-radius: 6px; font-size: 10px; color: var(--mid); flex-shrink: 0; } #detect-bar.connected { border-color: #14532d; color: var(--green); background: #020f06; } #detect-bar.error { border-color: #7f1d1d; color: var(--red); } #detect-status { flex: 1; } #detect-dots { display: flex; gap: 4px; } .detect-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--dim); transition: background .3s; } .detect-dot.trying { background: var(--amber); animation: pulse .7s infinite; } .detect-dot.ok { background: var(--green); } .detect-dot.fail { background: var(--red); } /* Panel grid */ #grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; } /* Panel card */ .panel-card { display: flex; flex-direction: column; gap: 8px; padding: 14px; background: var(--bg2); border: 1px solid var(--bd2); border-radius: 10px; text-decoration: none; color: inherit; cursor: pointer; transition: border-color .2s, background .2s, transform .1s; position: relative; overflow: hidden; } .panel-card::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse at top left, rgba(6,182,212,.04), transparent 60%); pointer-events: none; } .panel-card:hover { border-color: var(--cyan); background: #0a0a1e; transform: translateY(-1px); } .panel-card:active { transform: translateY(0); } .card-header { display: flex; align-items: center; gap: 10px; } .card-icon { font-size: 24px; flex-shrink: 0; } .card-title { font-size: 12px; font-weight: bold; letter-spacing: .12em; color: var(--hi); text-transform: uppercase; } .card-sub { font-size: 9px; color: var(--dim); } .card-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--dim); margin-left: auto; flex-shrink: 0; transition: background .3s; } .card-dot.live { background: var(--green); } .card-dot.idle { background: var(--amber); } .card-dot.config { background: var(--mid); } .card-desc { font-size: 10px; color: var(--mid); line-height: 1.5; } .card-topics { display: flex; flex-wrap: wrap; gap: 4px; } .card-topics code { font-size: 8px; color: #4b5563; background: var(--bg0); border: 1px solid var(--bd); border-radius: 2px; padding: 1px 4px; } .card-footer { display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--bd); padding-top: 6px; margin-top: auto; } .card-status { font-size: 9px; font-weight: bold; letter-spacing: .1em; color: var(--dim); } .card-status.live { color: var(--green); } .card-status.idle { color: var(--amber); } .card-msg { font-size: 9px; color: var(--dim); font-family: monospace; } /* Estop active state on card */ .panel-card.estop-flash { border-color: var(--red) !important; animation: blink .6s infinite; } /* Info strip */ #info-strip { display: flex; align-items: center; gap: 0; padding: 6px 12px; background: var(--bg2); border: 1px solid var(--bd); border-radius: 6px; flex-shrink: 0; flex-wrap: wrap; } .info-item { display: flex; align-items: center; gap: 6px; padding: 0 14px; border-right: 1px solid var(--bd); } .info-item:first-child { padding-left: 0; } .info-item:last-child { border-right: none; } .info-lbl { font-size: 8px; color: var(--dim); letter-spacing: .1em; } .info-val { font-size: 10px; color: var(--hi); font-family: monospace; } /* ══════════════════ BOTTOM BAR ══════════════════ */ #bottombar { display: flex; align-items: center; padding: 6px 14px; background: var(--bg1); border-top: 1px solid var(--bd); flex-shrink: 0; gap: 12px; flex-wrap: wrap; } #bottombar-left { display: flex; gap: 8px; align-items: center; } .estop-btn { padding: 5px 18px; border-radius: 5px; border: 2px solid #7f1d1d; background: #1c0505; color: #fca5a5; font-family: monospace; font-size: 11px; font-weight: bold; cursor: pointer; letter-spacing: .08em; transition: background .15s, border-color .15s; } .estop-btn:hover { background: #3b0606; border-color: var(--red); } .estop-btn.active { background: #7f1d1d; border-color: var(--red); color: #fff; animation: blink .7s infinite; } .resume-btn { padding: 5px 14px; border-radius: 5px; border: 1px solid #14532d; background: #052010; color: #86efac; font-family: monospace; font-size: 11px; font-weight: bold; cursor: pointer; transition: background .15s; } .resume-btn:hover { background: #0a3a1a; } #bottombar-center { display: flex; align-items: center; gap: 8px; flex: 1; } .bb-lbl { font-size: 9px; color: var(--dim); letter-spacing: .1em; flex-shrink: 0; } #mode-btns { display: flex; gap: 4px; } .mode-btn { padding: 3px 10px; border-radius: 4px; border: 1px solid var(--bd2); background: var(--bg2); color: var(--mid); font-family: monospace; font-size: 10px; font-weight: bold; cursor: pointer; letter-spacing: .08em; transition: background .15s, color .15s, border-color .15s; } .mode-btn:hover { background: #0e4f69; color: var(--hi); } .mode-btn.active { background: #0e4f69; border-color: var(--cyan); color: #fff; } #bottombar-right { display: flex; align-items: center; gap: 10px; margin-left: auto; } #uptime-display { display: flex; align-items: center; gap: 6px; } #session-time { font-size: 11px; color: var(--mid); font-family: monospace; } /* ══════════════════ RESPONSIVE ══════════════════ */ @media (max-width: 700px) { #topbar-status { display: none; } #grid { grid-template-columns: 1fr 1fr; } } @media (max-width: 480px) { #grid { grid-template-columns: 1fr; } #mode-btns .mode-btn:nth-child(n+3) { display: none; } }