Files
awesome-copilot/extensions/flight-map-canvas/game/styles.css
T
John Haugabook c46b4a919a Add flight-map-canvas extension (#2482)
* Add flight-map-canvas extension

A canvas port of the Flight Map VSCode extension: a first-person
satellite terrain map flown with flight simulator controls, for
session breaks while an agent works.

The simulator under game/ is copied verbatim from the source
extension's media/ folder. That page already reached its host through
one seam - an acquireVsCodeApi() object and a placeholder in its head -
so extension.mjs fills that seam for the canvas: a loopback server that
injects a policy, the render configuration, and a shim translating
Server-Sent Events into the messages the page already handles.

Two agent actions: fly_to sends the flight to a capital, a geocoded
city, or a raw lat/lng, and picks a random capital when called with no
input; report_job shows the current job step under the HUD.

Adds the vendored three.min.js to the codespell skip list, matching the
existing entry for arcade-canvas's phaser.min.js.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Apply suggestions from code review

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 13:32:55 +10:00

847 lines
18 KiB
CSS

/* ============================================================
3D Flight Simulator - Styles
============================================================ */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
background: #000;
color: #e0e0e0;
overflow: hidden;
width: 100vw;
height: 100vh;
}
/* ---- Canvas Container ---- */
#canvas-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
#canvas-container canvas {
display: block;
width: 100%;
height: 100%;
}
/* ---- Top Bar ---- */
/*
The selector panel and the location readout share one row. The bar
mirrors the selector panel's measured width as right padding so the
readout reads as centered in the panel rather than in the space left
over beside the menu. When the panel is too narrow for both, app.js
adds .stacked and the readout takes its own row underneath.
--selector-width and --top-bar-height are published by app.js.
*/
#top-bar {
position: fixed;
top: 0;
left: 0;
width: auto;
z-index: 100;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 12px;
padding: 12px;
padding-right: calc(12px + var(--selector-width, 0px));
pointer-events: none;
}
#top-bar.stacked {
padding-right: 12px;
}
/* ---- Capital Selector ---- */
#selector-panel {
flex: 0 1 auto;
min-width: 0;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
background: rgba(0, 0, 0, 0.65);
padding: 8px 14px;
border-radius: 6px;
border: 1px solid rgba(100, 200, 255, 0.2);
backdrop-filter: blur(8px);
pointer-events: auto;
}
#selector-panel label {
font-size: 0.75rem;
color: #88bbdd;
text-transform: uppercase;
letter-spacing: 1px;
}
#capital-select {
padding: 5px 10px;
font-size: 0.85rem;
font-family: inherit;
border: 1px solid rgba(100, 200, 255, 0.3);
border-radius: 3px;
background: rgba(10, 20, 40, 0.9);
color: #c0e0f0;
cursor: pointer;
flex: 1 1 auto;
min-width: 140px;
max-width: 100%;
outline: none;
}
#capital-select:focus {
border-color: rgba(100, 200, 255, 0.6);
}
#capital-select optgroup {
color: #66aacc;
font-weight: bold;
}
#capital-select option {
color: #c0e0f0;
background: #0a1428;
}
/* ---- HUD Overlay ---- */
#hud {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 50;
pointer-events: none;
}
/* Location display (lives in #top-bar; the flex basis below is mirrored
by LOCATION_MIN_WIDTH in app.js, which decides when to stack) */
#hud-location {
/* flex: 1 1 240px; */
min-width: 0;
text-align: center;
}
/* Both lines sit over bright satellite imagery, so they carry a hard
1px drop shadow rather than a blurred glow. A blur radius spreads the
dark halo through the glyphs themselves and softens the strokes, which
is what made the coordinates unreadable against a pale sky. */
#hud-capital {
font-size: calc(1rem + 2pt);
color: rgb(235, 246, 255);
font-weight: bold;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* white-space: pre keeps the two spaces that separate the latitude from
the longitude; normal whitespace handling would collapse them to one */
#hud-coords {
font-size: calc(0.72rem + 2pt);
color: rgb(214, 234, 250);
margin-top: 2px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85);
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
/* Heading: sits under the top bar at whatever height it settled on */
#hud-heading {
position: absolute;
top: calc(var(--top-bar-height, 56px) + 6px);
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.35);
padding: 3px 16px;
border-radius: 3px;
border: 1px solid rgba(100, 200, 255, 0.15);
}
#hud-heading-value {
font-size: 0.9rem;
color: rgba(100, 220, 255, 0.85);
text-shadow: 0 0 6px rgba(0, 100, 200, 0.3);
}
/* Speed (left side) */
#hud-speed {
position: absolute;
left: 30px;
top: 50%;
transform: translateY(-50%);
text-align: center;
background: rgba(0, 0, 0, 0.35);
padding: 10px 14px;
border-radius: 4px;
border: 1px solid rgba(100, 200, 255, 0.15);
}
#hud-speed .hud-label {
font-size: 0.65rem;
color: rgba(100, 200, 255, 0.5);
letter-spacing: 2px;
margin-bottom: 4px;
}
#hud-speed-value {
font-size: 1.6rem;
color: rgba(100, 255, 180, 0.9);
font-weight: bold;
text-shadow: 0 0 10px rgba(0, 200, 100, 0.3);
}
#hud-speed .hud-unit {
font-size: 0.6rem;
color: rgba(100, 200, 255, 0.4);
margin-top: 2px;
}
/* Altitude (right side) */
#hud-altitude {
position: absolute;
right: 30px;
top: 50%;
transform: translateY(-50%);
text-align: center;
background: rgba(0, 0, 0, 0.35);
padding: 10px 14px;
border-radius: 4px;
border: 1px solid rgba(100, 200, 255, 0.15);
}
#hud-altitude .hud-label {
font-size: 0.65rem;
color: rgba(100, 200, 255, 0.5);
letter-spacing: 2px;
margin-bottom: 4px;
}
#hud-altitude-value {
font-size: 1.6rem;
color: rgba(255, 200, 100, 0.9);
font-weight: bold;
text-shadow: 0 0 10px rgba(200, 150, 0, 0.3);
}
#hud-altitude .hud-unit {
font-size: 0.6rem;
color: rgba(100, 200, 255, 0.4);
margin-top: 2px;
}
/* Crosshair */
#hud-crosshair {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.5rem;
color: rgba(200, 230, 255, 0.4);
text-shadow: 0 0 4px rgba(0, 0, 0, 0.5);
line-height: 1;
}
/* Pitch indicator */
#hud-pitch {
position: absolute;
right: 30px;
top: 50%;
transform: translateY(80px);
text-align: center;
}
#hud-pitch-value {
font-size: 0.75rem;
color: rgba(180, 200, 255, 0.6);
}
/* ---- Click to Fly Prompt ---- */
#fly-prompt {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 80;
text-align: center;
pointer-events: none;
}
#fly-prompt .prompt-text {
font-size: 1.8rem;
color: rgba(180, 220, 255, 0.9);
text-shadow: 0 0 20px rgba(50, 150, 255, 0.5);
animation: pulse 2s ease-in-out infinite;
letter-spacing: 2px;
text-transform: uppercase;
}
#fly-prompt .prompt-hint {
margin-top: 8px;
font-size: 0.72rem;
letter-spacing: 1px;
color: rgba(120, 170, 210, 0.6);
}
@keyframes pulse {
0%, 100% { opacity: 0.7; }
50% { opacity: 1; }
}
/* ---- Controls Help ---- */
#controls-help {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 80;
text-align: center;
background: rgba(0, 0, 0, 0.5);
padding: 8px 20px;
border-radius: 6px;
border: 1px solid rgba(100, 200, 255, 0.15);
pointer-events: none;
font-size: 0.72rem;
color: rgba(160, 200, 230, 0.8);
line-height: 1.6;
transition: opacity 2s ease;
}
#controls-help.fade-out {
opacity: 0;
}
/* ---- Static Controls List ---- */
#controls-list {
position: fixed;
left: 12px;
bottom: 20px;
z-index: 60;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(100, 200, 255, 0.1);
border-radius: 4px;
padding: 8px 12px;
pointer-events: none;
font-size: 0.62rem;
color: rgba(150, 190, 220, 0.6);
}
#controls-list .controls-title {
font-size: 0.58rem;
color: rgba(120, 170, 210, 0.5);
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 5px;
}
#controls-list ul {
list-style: none;
}
#controls-list li {
line-height: 1.55;
white-space: nowrap;
}
#controls-list li span {
display: inline-block;
width: 52px;
color: rgba(120, 210, 255, 0.7);
}
/* ---- Pause Indicator ---- */
#pause-indicator {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 95;
background: rgba(0, 0, 0, 0.45);
padding: 12px 28px;
border-radius: 8px;
border: 1px solid rgba(255, 220, 120, 0.3);
pointer-events: none;
font-size: 1.6rem;
letter-spacing: 6px;
text-transform: uppercase;
color: rgba(255, 220, 120, 0.9);
text-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
}
/* ---- Loading Overlay ---- */
#loading-overlay {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 90;
text-align: center;
background: rgba(0, 10, 30, 0.85);
padding: 30px 50px;
border-radius: 10px;
border: 1px solid rgba(100, 200, 255, 0.2);
backdrop-filter: blur(10px);
}
#loading-text {
font-size: 1rem;
color: rgba(150, 200, 240, 0.9);
margin-bottom: 14px;
text-transform: uppercase;
letter-spacing: 2px;
}
#loading-bar-container {
width: 240px;
height: 4px;
background: rgba(50, 80, 120, 0.5);
border-radius: 2px;
overflow: hidden;
margin: 0 auto;
}
#loading-bar {
width: 0%;
height: 100%;
background: linear-gradient(90deg, #2288cc, #44ccff);
border-radius: 2px;
transition: width 0.3s ease;
}
#loading-progress {
font-size: 0.8rem;
color: rgba(100, 180, 230, 0.7);
margin-top: 8px;
}
/* ---- Welcome Screen ---- */
#welcome {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 70;
text-align: center;
pointer-events: none;
}
#welcome h1 {
font-size: 2.2rem;
color: rgba(180, 220, 255, 0.9);
font-weight: 300;
letter-spacing: 4px;
text-transform: uppercase;
text-shadow: 0 0 30px rgba(50, 150, 255, 0.3);
margin-bottom: 8px;
}
#welcome p {
font-size: 0.85rem;
color: rgba(130, 170, 210, 0.6);
letter-spacing: 1px;
}
#welcome .welcome-hint {
margin-top: 20px;
font-size: 0.75rem;
color: rgba(100, 150, 200, 0.5);
animation: pulse 3s ease-in-out infinite;
}
/* ---- Agent Status ---- */
/* Clears the controls help that sits at the bottom edge, so both can be
on screen during the five seconds before the help fades */
#agent-status {
position: fixed;
bottom: 64px;
left: 50%;
transform: translateX(-50%);
z-index: 90;
max-width: min(560px, calc(100vw - 24px));
display: flex;
align-items: center;
gap: 8px;
padding: 7px 14px;
border-radius: 20px;
background: rgba(0, 8, 24, 0.8);
border: 1px solid rgba(100, 200, 255, 0.25);
font-size: 0.75rem;
color: rgba(200, 230, 255, 0.9);
pointer-events: none;
}
#agent-status-dot {
flex: 0 0 auto;
width: 8px;
height: 8px;
border-radius: 50%;
background: #66ddaa;
animation: pulse 1.6s ease-in-out infinite;
}
/* A finished job leaves the last status readable but stops the pulse */
#agent-status.idle #agent-status-dot {
background: rgba(120, 170, 210, 0.5);
animation: none;
}
#agent-status-text {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#agent-status-tokens {
flex: 0 0 auto;
color: rgba(120, 200, 255, 0.65);
}
#agent-status-tokens:empty {
display: none;
}
/* ---- Utility ---- */
.hidden {
display: none !important;
}
/* ---- Destination Input Dialog ---- */
/* The "Input Destination" entry is the only action in the menu, so it
is set apart from the capitals listed under it. */
#opt-input-destination {
background: #e8edf1;
color: #16283f;
font-weight: bold;
}
/* Sits above the top bar so the dialog is never partly behind the menu,
and covers the canvas so a click cannot engage flight behind it. The
card hangs from just under the bar rather than centering, so it stays
next to the menu entry that opened it. */
#destination-dialog {
position: fixed;
inset: 0;
z-index: 200;
display: flex;
align-items: flex-start;
justify-content: center;
padding: 24px;
padding-top: calc(var(--top-bar-height, 56px) + 16px);
background: rgba(4, 12, 24, 0.45);
}
.destination-card {
width: 100%;
max-width: 400px;
max-height: calc(100vh - var(--top-bar-height, 56px) - 40px);
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 12px;
padding: 18px;
border-radius: 14px;
background: rgba(238, 242, 245, 0.97);
border: 1px solid rgba(120, 160, 200, 0.45);
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}
#destination-form,
#destination-results {
display: flex;
flex-direction: column;
gap: 10px;
}
.dest-row {
display: flex;
align-items: center;
gap: 10px;
}
/* Fixed basis keeps the three fields on one left edge regardless of
how wide the individual label text is */
.dest-label {
flex: 0 0 92px;
padding: 8px 10px;
border-radius: 8px;
background: #ccd4da;
color: #1c2b3d;
font-size: 0.7rem;
letter-spacing: 0.5px;
text-align: center;
}
.dest-row input,
.dest-row select {
flex: 1 1 auto;
min-width: 0;
padding: 9px 12px;
font-family: inherit;
font-size: 0.9rem;
border-radius: 8px;
border: 1px solid rgba(80, 130, 180, 0.35);
background: #16283f;
color: #dceaf6;
outline: none;
}
.dest-row input::placeholder {
color: rgba(190, 215, 235, 0.55);
}
.dest-row input:focus,
.dest-row select:focus {
border-color: rgba(120, 200, 255, 0.85);
}
.dest-row select option {
background: #16283f;
color: #dceaf6;
}
#dest-result-list {
list-style: none;
max-height: 210px;
overflow-y: auto;
padding: 6px 4px;
border-radius: 10px;
border: 1px solid rgba(80, 130, 180, 0.35);
background: #16283f;
outline: none;
}
#dest-result-list:focus {
border-color: rgba(120, 200, 255, 0.85);
}
/* The dashed rule between rows is a bottom border on every row but the
last, so the list reads as separated entries rather than a paragraph */
.dest-result {
padding: 9px 10px;
border-radius: 6px;
color: #dceaf6;
font-size: 0.85rem;
cursor: pointer;
border-bottom: 1px dashed rgba(140, 185, 225, 0.4);
}
.dest-result:last-child {
border-bottom: none;
}
.dest-result:hover {
background: rgba(90, 140, 190, 0.25);
}
.dest-result.active {
background: rgba(110, 165, 215, 0.45);
color: #ffffff;
}
.dest-message {
min-height: 1em;
font-size: 0.72rem;
color: #7a1f1f;
}
#dest-results-message {
color: #33465c;
}
.dest-actions {
display: flex;
gap: 12px;
}
.dest-actions button {
flex: 1 1 0;
padding: 10px 12px;
font-family: inherit;
font-size: 0.85rem;
border-radius: 8px;
border: 1px solid rgba(80, 130, 180, 0.35);
background: #16283f;
color: #dceaf6;
cursor: pointer;
transition: background 0.2s, border-color 0.2s;
}
.dest-actions button:hover:not(:disabled) {
background: #21395a;
border-color: rgba(120, 200, 255, 0.7);
}
.dest-actions button:disabled {
opacity: 0.5;
cursor: default;
}
/* ---- Selector Panel Buttons ---- */
#selector-panel button {
padding: 5px 12px;
font-size: 0.75rem;
font-family: inherit;
border: 1px solid rgba(100, 200, 255, 0.3);
border-radius: 3px;
background: rgba(10, 20, 40, 0.9);
color: #c0e0f0;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 1px;
transition: border-color 0.2s, background 0.2s;
}
#selector-panel button:hover {
border-color: rgba(100, 200, 255, 0.6);
background: rgba(20, 40, 70, 0.9);
}
#btn-export-json {
color: #88ddaa !important;
border-color: rgba(100, 255, 160, 0.3) !important;
}
#btn-export-json:hover {
border-color: rgba(100, 255, 160, 0.6) !important;
}
/* ---- Render Config Panel ---- */
#config-panel {
position: fixed;
top: calc(var(--top-bar-height, 56px) + 12px);
left: 12px;
z-index: 100;
width: 320px;
max-width: calc(100vw - 24px);
max-height: calc(100vh - var(--top-bar-height, 56px) - 36px);
overflow-y: auto;
background: rgba(0, 8, 24, 0.88);
padding: 14px 16px;
border-radius: 6px;
border: 1px solid rgba(100, 200, 255, 0.2);
backdrop-filter: blur(10px);
font-size: 0.72rem;
}
#config-panel h3 {
font-size: 0.85rem;
color: rgba(180, 220, 255, 0.9);
font-weight: 600;
letter-spacing: 1px;
text-transform: uppercase;
margin-bottom: 4px;
}
#config-panel .config-hint {
font-size: 0.65rem;
color: rgba(120, 170, 210, 0.6);
margin-bottom: 12px;
line-height: 1.4;
}
#config-panel fieldset {
border: 1px solid rgba(100, 200, 255, 0.12);
border-radius: 4px;
padding: 8px 10px;
margin-bottom: 10px;
}
#config-panel legend {
font-size: 0.7rem;
color: #66aacc;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
padding: 0 6px;
}
.cfg-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
}
.cfg-row label {
flex: 0 0 100px;
font-size: 0.68rem;
color: rgba(160, 200, 230, 0.8);
text-align: right;
}
.cfg-row input[type="range"] {
flex: 1;
height: 4px;
-webkit-appearance: none;
appearance: none;
background: rgba(50, 80, 120, 0.5);
border-radius: 2px;
outline: none;
}
.cfg-row input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: #44ccff;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0.3);
}
.cfg-row input[type="range"]::-moz-range-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: #44ccff;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0.3);
}
.cfg-row input[type="checkbox"] {
width: 14px;
height: 14px;
cursor: pointer;
accent-color: #44ccff;
}
.cfg-row span {
flex: 0 0 50px;
font-size: 0.65rem;
color: rgba(100, 220, 255, 0.85);
text-align: right;
font-variant-numeric: tabular-nums;
}
/* Scrollbar has styling for config panel */
#config-panel::-webkit-scrollbar {
width: 4px;
}
#config-panel::-webkit-scrollbar-track {
background: transparent;
}
#config-panel::-webkit-scrollbar-thumb {
background: rgba(100, 200, 255, 0.2);
border-radius: 2px;
}