/* ============================================================================ flight-sim — HUD + UI stylesheet (integrator-owned). ---------------------------------------------------------------------------- CONTRACT for src/instruments.js (see README / INTEGRATOR_NOTES): createInstruments(rootEl) creates ALL overlay DOM inside #hud using exactly the class names below. No inline styles except dynamic transforms set per frame (pitch/roll of the horizon, tape translations, bar widths, lamp glow). Conventions: .hud-debug — hidden by default (display:none); add .visible to show it. instruments.debug(lines|null): null hides, array shows. .hud-message — hidden by default; message(text, kind) sets text + kind class (.info | .warn | .crash) + visible; clearMessage() removes visible. autoHideMessageMs is applied by main.js. .lamp — off-state lamp; add .on to light it. .hud-stall — warning card; add .on (flashes) while data.stall is true. ========================================================================== */ :root { color-scheme: dark; --hud-bg: rgba(8, 14, 22, 0.55); --hud-edge: rgba(120, 190, 255, 0.18); --hud-text: #cfe6ff; --hud-dim: #7fa6cf; --hud-amber: #ffb347; --hud-green: #7dff9e; --hud-red: #ff5a5a; --hud-cyan: #86f4ff; --font-mono: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Courier New', monospace; } * { margin: 0; padding: 0; box-sizing: border-box; } html, body { width: 100%; height: 100%; overflow: hidden; background: #05070c; } body { font-family: var(--font-mono); color: var(--hud-text); -webkit-user-select: none; user-select: none; } /* ---- 3D canvas ----------------------------------------------------------- */ #sim { position: fixed; inset: 0; display: block; width: 100vw; height: 100vh; cursor: crosshair; outline: none; touch-action: none; } /* ---- HUD root ------------------------------------------------------------- instruments.js mounts every panel into #hud. pointer-events:none so clicks reach the canvas (orbit-camera drag). */ #hud { position: fixed; inset: 0; z-index: 10; pointer-events: none; font-family: var(--font-mono); font-size: 13px; color: var(--hud-text); letter-spacing: 0.5px; } /* Generic glassy panel look used by every instrument block. */ .hud-attitude, .hud-airspeed, .hud-altitude, .hud-heading, .hud-engine, .hud-lamps, .hud-debug, .hud-message, .hud-stall { position: absolute; background: var(--hud-bg); border: 1px solid var(--hud-edge); border-radius: 8px; backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); text-shadow: 0 0 6px rgba(90, 160, 255, 0.45); } /* Big numeric readouts (airspeed / altitude current value). */ .hud-num { font-size: 26px; font-weight: 700; color: #fff; text-align: center; line-height: 1.05; } .hud-num small { display: block; font-size: 10px; font-weight: 400; color: var(--hud-dim); letter-spacing: 1px; } /* Generic tape (moving scale strip). The module translates it per frame. */ .tape { position: relative; overflow: hidden; background: rgba(6, 10, 16, 0.55); border: 1px solid rgba(120, 190, 255, 0.14); border-radius: 4px; } .tape .tick { position: absolute; color: var(--hud-dim); font-size: 10px; white-space: nowrap; } /* ---- attitude indicator (left column, above the airspeed tape) ---------- */ /* The AI and airspeed panel share one flex column, vertically centered on the left edge, so the middle of the screen stays clear for flying. */ .hud-left-col { position: absolute; left: 18px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; align-items: center; gap: 14px; } .hud-attitude { position: relative; width: 210px; height: 210px; border-radius: 50%; overflow: hidden; background: rgba(6, 10, 16, 0.45); } .ai-horizon { position: absolute; left: 50%; top: 50%; width: 340px; height: 340px; margin: -170px 0 0 -170px; /* instrument sets: translateY(pitchPx) rotate(-bankDeg) */ will-change: transform; } .ai-sky { position: absolute; inset: 0 0 50% 0; background: linear-gradient(180deg, #2b6fb3, #7fb2d8); } .ai-ground { position: absolute; inset: 50% 0 0 0; background: linear-gradient(180deg, #8a6a3c, #b08a52); } .ai-line { position: absolute; left: 8px; right: 8px; height: 2px; background: rgba(255, 255, 255, 0.75); } .ai-fixed { position: absolute; left: 50%; top: 50%; width: 120px; height: 90px; transform: translate(-50%, -50%); pointer-events: none; } .ai-symbol { position: absolute; left: 50%; top: 50%; width: 86px; height: 14px; transform: translate(-50%, -50%); border: 2px solid var(--hud-amber); border-radius: 3px; } .ai-symbol::after { content: ''; position: absolute; left: 50%; top: 50%; width: 10px; height: 10px; transform: translate(-50%, -50%); background: var(--hud-amber); border-radius: 50%; } /* ---- airspeed tape (left) ------------------------------------------------ */ .hud-airspeed { position: relative; width: 92px; padding: 10px; display: flex; flex-direction: column; align-items: center; gap: 6px; } .hud-airspeed .tape { width: 64px; height: 220px; } .hud-airspeed .tick { left: 0; right: 0; text-align: center; } /* ---- altitude + vertical speed (right) ----------------------------------- */ .hud-altitude { right: 18px; top: 50%; transform: translateY(-50%); width: 116px; padding: 10px; display: flex; flex-direction: column; align-items: center; gap: 6px; } .hud-altitude .tape { width: 84px; height: 220px; } .hud-altitude .tick { left: 0; right: 0; text-align: center; } .hud-vsi { position: absolute; right: -34px; top: 8px; bottom: 8px; width: 26px; border: 1px solid rgba(120, 190, 255, 0.25); border-radius: 4px; background: rgba(6, 10, 16, 0.5); overflow: hidden; } .hud-vsi-fill { position: absolute; left: 0; right: 0; height: 2px; background: var(--hud-cyan); /* module sets top/bottom of a zero-bar span; simplest: a centered bar that the module scales: transform: translateY via per-frame inline */ box-shadow: 0 0 8px var(--hud-cyan); } /* ---- heading tape (bottom center) ---------------------------------------- */ .hud-heading { bottom: 64px; left: 50%; transform: translateX(-50%); width: 330px; padding: 8px 10px; } .hud-heading .tape { width: 100%; height: 34px; } .hud-heading .tick { bottom: 2px; text-align: center; } .hud-heading .tick.major { color: #fff; font-weight: 700; } .hud-heading .bug { position: absolute; left: 50%; top: -4px; width: 0; height: 0; border-left: 7px solid transparent; border-right: 7px solid transparent; border-top: 10px solid var(--hud-amber); transform: translateX(-50%); } /* ---- engine panel (bottom left) ------------------------------------------ */ .hud-engine { left: 18px; bottom: 18px; width: 190px; padding: 10px 12px; } .hud-engine .bar-row { display: flex; align-items: center; gap: 8px; margin: 5px 0; font-size: 11px; color: var(--hud-dim); } .hud-engine .bar { position: relative; flex: 1; height: 10px; background: rgba(6, 10, 16, 0.7); border: 1px solid rgba(120, 190, 255, 0.18); border-radius: 3px; overflow: hidden; } .hud-engine .bar-fill { position: absolute; inset: 0 auto 0 0; width: 0%; background: linear-gradient(90deg, var(--hud-green), var(--hud-amber)); } .hud-engine .bar-fill.throttle { background: linear-gradient(90deg, var(--hud-cyan), #4aa8ff); } /* ---- lamps (bottom right) ------------------------------------------------- */ .hud-lamps { right: 18px; bottom: 18px; display: flex; gap: 8px; padding: 8px 10px; } .lamp { padding: 4px 9px; border-radius: 4px; border: 1px solid rgba(120, 190, 255, 0.22); background: rgba(6, 10, 16, 0.6); color: var(--hud-dim); font-size: 11px; letter-spacing: 1px; text-transform: uppercase; transition: background 0.15s, color 0.15s, box-shadow 0.15s; } .lamp.on { color: #06121c; background: var(--hud-green); box-shadow: 0 0 10px rgba(125, 255, 158, 0.8); } .lamp--gear.on { background: var(--hud-amber); color: #241500; box-shadow: 0 0 10px rgba(255, 179, 71, 0.8); } .lamp--brakes.on { background: var(--hud-red); color: #2a0606; box-shadow: 0 0 10px rgba(255, 90, 90, 0.8); } .lamp--stall.on { background: var(--hud-red); color: #2a0606; animation: stall-flash 0.45s steps(2, jump-none) infinite; } /* ---- stall warning (center, flashing) ------------------------------------- */ .hud-stall { display: none; left: 50%; top: 26%; transform: translateX(-50%); padding: 10px 26px; font-size: 22px; font-weight: 700; letter-spacing: 4px; color: var(--hud-red); border-color: var(--hud-red); text-shadow: 0 0 12px rgba(255, 90, 90, 0.9); } .hud-stall.on { display: block; animation: stall-flash 0.45s steps(2, jump-none) infinite; } /* ---- message banner (top center) ----------------------------------------- */ .hud-message { display: none; top: 26px; left: 50%; transform: translateX(-50%); padding: 12px 30px; font-size: 24px; font-weight: 700; letter-spacing: 3px; border-width: 2px; background: rgba(8, 14, 22, 0.72); animation: msg-in 0.25s ease-out; } .hud-message.visible { display: block; } .hud-message.info { color: var(--hud-green); border-color: rgba(125, 255, 158, 0.6); text-shadow: 0 0 14px rgba(125, 255, 158, 0.7); } .hud-message.warn { color: var(--hud-amber); border-color: rgba(255, 179, 71, 0.6); text-shadow: 0 0 14px rgba(255, 179, 71, 0.7); } .hud-message.crash { color: #fff; border-color: var(--hud-red); background: rgba(120, 12, 12, 0.62); text-shadow: 0 0 16px rgba(255, 90, 90, 0.95); animation: msg-in 0.25s ease-out, crash-pulse 0.6s ease-in-out 2; } /* ---- debug panel (top left) ---------------------------------------------- */ .hud-debug { display: none; top: 18px; left: 18px; min-width: 210px; padding: 10px 12px; font-size: 11px; line-height: 1.7; color: var(--hud-cyan); } .hud-debug.visible { display: block; } .hud-debug .line b { color: #fff; font-weight: 700; } .hud-debug .line.pair b::after { content: ' '; } /* ---- start overlay --------------------------------------------------------- */ #start-overlay { position: fixed; inset: 0; z-index: 20; display: flex; align-items: center; justify-content: center; cursor: pointer; background: radial-gradient(1200px 700px at 50% 30%, rgba(30, 60, 110, 0.35), transparent 70%), rgba(3, 6, 12, 0.66); } #start-overlay.hidden { display: none; } .start-card { width: min(620px, 92vw); max-height: 92vh; overflow-y: auto; padding: 34px 42px 30px; text-align: center; background: rgba(9, 15, 24, 0.85); border: 1px solid rgba(140, 200, 255, 0.25); border-radius: 14px; box-shadow: 0 18px 60px rgba(0, 0, 0, 0.6); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); } .start-title { font-size: 34px; font-weight: 800; letter-spacing: 12px; color: #eaf4ff; text-shadow: 0 0 24px rgba(90, 170, 255, 0.6); margin-bottom: 8px; } .start-sub { font-size: 12px; color: var(--hud-dim); line-height: 1.6; margin-bottom: 18px; } .start-controls { width: 100%; border-collapse: collapse; margin: 0 auto 22px; font-size: 12px; } .start-controls th { color: var(--hud-cyan); font-weight: 700; text-align: left; padding: 5px 12px; border-bottom: 1px solid rgba(140, 200, 255, 0.3); letter-spacing: 2px; } .start-controls td { padding: 4px 12px; text-align: left; border-bottom: 1px solid rgba(140, 200, 255, 0.09); } .start-controls td:first-child { color: #fff; font-weight: 700; white-space: nowrap; width: 34%; } #start-btn { display: inline-block; padding: 14px 46px; font-family: var(--font-mono); font-size: 17px; font-weight: 800; letter-spacing: 4px; color: #04101c; background: linear-gradient(180deg, #9fe0ff, #4aa8ff); border: none; border-radius: 8px; cursor: pointer; box-shadow: 0 0 24px rgba(74, 168, 255, 0.55); transition: transform 0.12s ease, box-shadow 0.12s ease; } #start-btn:hover { transform: translateY(-2px); box-shadow: 0 0 34px rgba(74, 168, 255, 0.85); } #start-btn:active { transform: translateY(0); } /* ---- animations ------------------------------------------------------------ */ @keyframes stall-flash { 0%, 100% { opacity: 1; } 50% { opacity: 0.12; } } @keyframes msg-in { from { transform: translate(-50%, -14px); opacity: 0; } to { transform: translate(-50%, 0); opacity: 1; } } @keyframes crash-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(255, 90, 90, 0); } 50% { box-shadow: 0 0 26px 4px rgba(255, 90, 90, 0.55); } }