/* gamepad_panel.css — Saltybot Gamepad Teleop (Issue #598) */ *, *::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; } body { font-family: 'Courier New', Courier, monospace; font-size: 12px; background: var(--bg0); color: var(--base); height: 100dvh; display: flex; flex-direction: column; overflow: hidden; } /* ── Header ── */ #header { display: flex; align-items: center; padding: 5px 12px; background: var(--bg1); border-bottom: 1px solid var(--bd); flex-shrink: 0; gap: 8px; flex-wrap: wrap; } .logo { color: #f97316; font-weight: bold; letter-spacing: .15em; font-size: 13px; flex-shrink: 0; } #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; } #gp-indicator { display: flex; align-items: center; gap: 5px; font-size: 10px; color: var(--mid); } #gp-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dim); flex-shrink: 0; transition: background .3s; } #gp-dot.active { background: var(--amber); } @keyframes blink { 0%,100%{opacity:1} 50%{opacity:.3} } #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: 185px; } #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; } .estop-btn { border-color: #7f1d1d; background: #1c0505; color: #fca5a5; padding: 3px 12px; font-size: 11px; } .estop-btn:hover { background: #3b0606; } .estop-btn.active { background: #7f1d1d; border-color: var(--red); color: #fff; animation: blink .8s infinite; } .resume-btn { border-color: #14532d; background: #052010; color: #86efac; padding: 3px 12px; font-size: 11px; } .resume-btn:hover { background: #0a3a1a; } /* ── Toolbar ── */ #toolbar { display: flex; align-items: center; gap: 8px; padding: 5px 12px; background: var(--bg1); border-bottom: 1px solid var(--bd); flex-shrink: 0; flex-wrap: wrap; font-size: 10px; } .tsep { width: 1px; height: 16px; background: var(--bd2); } .tlbl { color: var(--mid); letter-spacing: .08em; } .tval { color: #67e8f9; min-width: 70px; font-family: monospace; } .tslider { -webkit-appearance: none; width: 100px; height: 4px; border-radius: 2px; background: var(--bd2); outline: none; cursor: pointer; } .tslider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%; background: var(--cyan); cursor: pointer; } .tslider::-moz-range-thumb { width: 12px; height: 12px; border-radius: 50%; background: var(--cyan); cursor: pointer; border: none; } /* ── Main ── */ #main { flex: 1; display: grid; grid-template-columns: 1fr 220px; min-height: 0; overflow: hidden; } /* ── Joystick area ── */ #joystick-area { display: flex; align-items: center; justify-content: space-evenly; gap: 12px; padding: 16px; overflow: hidden; } .stick-wrap { display: flex; flex-direction: column; align-items: center; gap: 8px; flex-shrink: 0; } .stick-label { font-size: 9px; letter-spacing: .12em; color: var(--mid); text-transform: uppercase; } .stick-vals { font-size: 10px; color: #67e8f9; font-family: monospace; min-width: 100px; text-align: center; } canvas { display: block; border-radius: 50%; border: 1px solid var(--bd2); touch-action: none; cursor: grab; } canvas.active { cursor: grabbing; } /* ── Center panel ── */ #center-panel { flex: 1; display: flex; flex-direction: column; gap: 8px; max-width: 260px; position: relative; } .cp-block { background: var(--bg2); border: 1px solid var(--bd2); border-radius: 6px; padding: 8px; } .cp-title { font-size: 9px; font-weight: bold; letter-spacing: .12em; color: #0891b2; text-transform: uppercase; margin-bottom: 5px; } .cp-row { display: flex; justify-content: space-between; padding: 2px 0; border-bottom: 1px solid var(--bd); font-size: 10px; } .cp-row:last-child { border-bottom: none; } .cp-lbl { color: var(--mid); } .cp-val { color: var(--hi); font-family: monospace; } /* Keyboard diagram */ .key-grid { display: grid; grid-template-columns: repeat(3, 28px); grid-template-rows: repeat(3, 22px); gap: 3px; justify-content: center; margin: 4px 0; } .key { background: var(--bg1); border: 1px solid var(--bd2); border-radius: 3px; display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: bold; color: var(--mid); transition: background .1s, color .1s, border-color .1s; } .key.pressed { background: #0e4f69; border-color: var(--cyan); color: #fff; } .key-wide { grid-column: 1 / 4; width: 100%; height: 22px; } /* E-stop overlay */ #estop-panel { position: absolute; inset: 0; background: rgba(127,0,0,0.9); border: 2px solid var(--red); border-radius: 8px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; animation: blink .8s infinite; } .estop-text { font-size: 28px; font-weight: bold; color: #fca5a5; letter-spacing: .1em; } .estop-sub { font-size: 11px; color: #fca5a5; letter-spacing: .2em; } /* ── Sidebar ── */ #sidebar { background: var(--bg1); border-left: 1px solid var(--bd); display: flex; flex-direction: column; overflow-y: auto; padding: 8px; gap: 8px; font-size: 11px; } .sb-card { background: var(--bg2); border: 1px solid var(--bd2); border-radius: 6px; padding: 8px; } .sb-title { font-size: 9px; font-weight: bold; letter-spacing: .12em; color: #0891b2; text-transform: uppercase; margin-bottom: 6px; } .sb-row { display: flex; justify-content: space-between; padding: 2px 0; border-bottom: 1px solid var(--bd); font-size: 10px; } .sb-row:last-child { border-bottom: none; } .sb-lbl { color: var(--mid); } .sb-val { color: var(--hi); font-family: monospace; } /* Gamepad axis bars */ .gp-axis-row { display: flex; align-items: center; gap: 4px; font-size: 9px; color: var(--mid); margin-bottom: 3px; } .gp-axis-label { width: 30px; flex-shrink: 0; } .gp-bar-track { flex: 1; height: 6px; background: var(--bg0); border: 1px solid var(--bd); border-radius: 3px; overflow: hidden; position: relative; } .gp-bar-center { position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--dim); } .gp-bar-fill { position: absolute; top: 0; bottom: 0; background: var(--cyan); transition: none; } .gp-axis-val { width: 32px; text-align: right; color: var(--hi); flex-shrink: 0; } .gp-btn-row { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 6px; } .gp-btn-chip { font-size: 8px; padding: 1px 5px; border-radius: 2px; border: 1px solid var(--bd); background: var(--bg0); color: var(--dim); transition: background .1s, color .1s; } .gp-btn-chip.pressed { background: var(--bd2); color: var(--hi); border-color: var(--cyan); } /* ── Footer ── */ #footer { background: var(--bg1); border-top: 1px solid var(--bd); padding: 3px 12px; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; font-size: 10px; color: var(--dim); } /* ── Responsive ── */ @media (max-width: 800px) { #main { grid-template-columns: 1fr; } #sidebar { display: none; } } @media (max-width: 560px) { #right-wrap { display: none; } #center-panel { max-width: 100%; } .tslider { width: 70px; } }