Files
gods-eye-view/src/ui/styles/controls.css
T
Said Turksever ab1239d3ed Cap rail title size and keep compact type to launchers
The rail titles took the dock's rem type inside fixed 176px launchers, so a
larger browser font cut collapsed titles such as Recent Imagery short. Cap
the rail type at its 16px-root size.

The compact type for translated titles now skips Chinese and Japanese,
which fit at full size and keep their 9px floor, and reaches only the rail's
own launchers, so Radio inside Context keeps its type.

Tests now check the launcher divider, gap, toggle margin and flex-shrink
rules by their declarations, not only their selectors.
2026-10-30 15:26:21 +00:00

1272 lines
29 KiB
CSS

/* ── Control Panel ────────────────────────────── */
#control-panel {
position: fixed;
bottom: 36px;
left: 50%;
transform: translateX(-50%);
z-index: 100;
width: auto;
max-width: 680px;
}
#control-panel .panel-glow {
position: absolute;
inset: -30px;
background: radial-gradient(
ellipse at center bottom,
var(--accent-dim) 0%,
transparent 60%
);
filter: blur(30px);
opacity: 0.4;
pointer-events: none;
}
.panel-inner {
position: relative;
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: var(--panel-radius);
padding: 16px 20px 18px;
backdrop-filter: blur(24px) saturate(1.4);
-webkit-backdrop-filter: blur(24px) saturate(1.4);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.5),
0 0 0 1px rgba(255, 255, 255, 0.03) inset,
0 1px 0 rgba(255, 255, 255, 0.06) inset;
}
.panel-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
padding: 0 4px;
}
.panel-mini-status {
display: none;
align-items: center;
gap: 8px;
padding: 0 6px 2px;
}
#control-panel.collapsed .panel-mini-status {
display: flex;
}
.panel-mini-label {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 1.4px;
color: var(--text-dim);
text-transform: uppercase;
}
.panel-mini-value {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 1.8px;
color: var(--accent);
text-transform: uppercase;
text-shadow: 0 0 8px rgba(0, 212, 255, 0.3);
}
.panel-title {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 3px;
color: var(--text-dim);
text-transform: uppercase;
white-space: nowrap;
}
/* Side panel titles read like the command dock's labels: one type for every
panel, collapsed or open, and bright enough to read over the globe. The
dock's type is in rem, but a collapsed launcher is a fixed 176px, so the
rail caps it at its 16px-root size: a larger browser font must not cut
Recent Imagery short. */
:is(#left-panel-stack, #right-context-rail)
:is(.panel-header > .panel-title, .pp-header-label) {
font-family: var(--font-mono);
font-size: min(var(--dock-label-font-size), 9.28px);
font-weight: var(--dock-label-font-weight);
letter-spacing: min(var(--dock-label-letter-spacing), 2.08px);
text-transform: uppercase;
}
/* A hovered panel sets --rail-label-hover on itself (see below); its own
header picks it up. Radio sits inside Context and keeps its own colour. */
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
:is(
.panel-header > .panel-title,
.pp-header-label,
:is(.panel-header, .pp-header-row) > .panel-collapse-btn
) {
color: var(--rail-label-hover, var(--dock-label-color));
}
#radio-panel {
--rail-label-hover: initial;
}
/* A title never pushes its toggle out of the card: one that does not fit
ends in an ellipsis. Cyber draws a marker left of the title, which a clip
would cut, and keeps its own header layout. */
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
:is(.panel-header > .panel-title, .pp-header-label) {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
/* A collapsed launcher is 176px wide and its divider has no room to draw,
so the header drops it and packs closer. English titles such as Recent
Imagery then fit whole. Only the rail's own launchers: Radio, nested in
Context, keeps its divider. */
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
> .panel-collapsible.collapsed
> [class$='-panel-inner']
> .panel-header,
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
> #pp-toggles.collapsed
> .pp-header-row {
gap: 8px;
}
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
> .panel-collapsible.collapsed
> [class$='-panel-inner']
> .panel-header
> .panel-divider,
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
> #pp-toggles.collapsed
> .pp-header-row
> .panel-divider {
display: none;
}
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
> .panel-collapsible.collapsed
> [class$='-panel-inner']
> .panel-header
> .panel-collapse-btn,
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
> #pp-toggles.collapsed
> .pp-header-row
> .panel-collapse-btn {
margin-left: auto;
}
/* Translated titles run longer (Aktuelle Bilddaten), so a collapsed rail
launcher sets them in compact type rather than pushing the disclosure
button out of the card or cutting the word short. Chinese and Japanese
titles fit at full size and stay at or above their 9px floor (cjk.css);
Radio, nested in Context, is not a launcher. */
html:not([lang='en']):not(:lang(zh)):not(:lang(ja))
:is(#left-panel-stack, #right-context-rail)
> .panel-collapsible.collapsed
> [class$='-panel-inner']
> .panel-header
> .panel-title {
font-size: 8px;
letter-spacing: 1px;
}
.panel-divider {
flex: 1;
height: 1px;
background: linear-gradient(to right, var(--glass-border), transparent);
}
.panel-collapse-btn {
width: 22px;
height: 22px;
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(255, 255, 255, 0.03);
color: var(--text-dim);
font-family: var(--font-mono);
font-size: 13px;
line-height: 1;
/* A long title or a header action must not squeeze the toggle. */
flex-shrink: 0;
cursor: pointer;
transition:
background-color var(--transition-fast),
border-color var(--transition-fast),
color var(--transition-fast);
}
.panel-collapse-btn:hover {
border-color: var(--glass-border-hover);
color: var(--text-primary);
background: rgba(255, 255, 255, 0.07);
}
/* A side panel answers the pointer as the command dock's trays do: a
brighter edge and an accent title and toggle. Touch screens have no hover,
so a tap must not leave a panel lit. */
@media (hover: hover) and (pointer: fine) {
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
:where(
.data-panel-inner,
.scene-panel-inner,
.cctv-panel-inner,
.weather-panel-inner,
.recent-imagery-panel-inner,
.street-level-panel-inner,
.global-context-panel-inner,
#pp-toggles:not(.collapsed),
#pp-toggles.collapsed > .pp-header-row
) {
transition: border-color var(--transition-fast);
}
:root:not([data-ui-theme='cyber'])
:is(#left-panel-stack, #right-context-rail)
:where(
.data-panel-inner,
.scene-panel-inner,
.cctv-panel-inner,
.weather-panel-inner,
.recent-imagery-panel-inner,
.street-level-panel-inner,
.global-context-panel-inner,
#pp-toggles:not(.collapsed),
#pp-toggles.collapsed > .pp-header-row
):hover {
--rail-label-hover: var(--accent);
border-color: var(--glass-border-hover);
}
}
/* ── Button Grid ──────────────────────────────── */
.button-grid {
display: flex;
gap: 8px;
overflow-x: auto;
scroll-behavior: smooth;
scrollbar-width: none;
-ms-overflow-style: none;
}
.button-grid::-webkit-scrollbar {
display: none;
}
.style-btn {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 14px 16px 10px;
min-width: 78px;
flex-shrink: 0;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: var(--btn-radius);
cursor: pointer;
/* Keep the keyboard focus outline immediate. */
transition:
background-color var(--transition-fast),
border-color var(--transition-fast),
box-shadow var(--transition-fast),
color var(--transition-fast),
transform var(--transition-fast);
overflow: hidden;
outline: none;
}
/* Keep keyboard focus visible inside the scrolling preset row. */
.style-btn:focus-visible {
outline: 2px solid var(--text-primary);
outline-offset: -3px;
}
.style-btn::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.04) 0%,
transparent 100%
);
opacity: 0;
transition: opacity var(--transition-fast);
}
.style-btn:hover {
border-color: var(--glass-border-hover);
background: rgba(255, 255, 255, 0.06);
transform: translateY(-1px);
}
.style-btn:hover::before {
opacity: 1;
}
.style-btn:active {
transform: translateY(0);
}
.style-btn.active {
border-color: var(--accent);
background: var(--accent-dim);
box-shadow:
0 0 20px rgba(0, 212, 255, 0.15),
0 0 0 1px rgba(0, 212, 255, 0.1) inset;
}
.style-btn.active .btn-icon {
color: var(--accent);
text-shadow: 0 0 12px var(--accent-glow);
}
.style-btn.active .btn-label {
color: var(--text-primary);
}
.btn-icon {
font-size: 20px;
line-height: 1;
color: var(--text-secondary);
transition:
color var(--transition-fast),
text-shadow var(--transition-fast);
}
.btn-label {
font-family: var(--font-sans);
font-size: 11px;
font-weight: 500;
color: var(--text-secondary);
letter-spacing: 0.3px;
transition: color var(--transition-fast);
}
.btn-key {
position: absolute;
top: 6px;
right: 6px;
font-family: var(--font-mono);
font-size: 8px;
color: var(--text-dim);
opacity: 0;
transition: opacity var(--transition-fast);
}
.style-btn:hover .btn-key,
.style-btn.active .btn-key {
opacity: 1;
}
/* ── Loading Screen ───────────────────────────── */
#loading-screen {
position: fixed;
inset: 0;
z-index: 1000;
background: var(--bg-dark);
display: flex;
align-items: center;
justify-content: center;
transition:
opacity 0.8s ease,
visibility 0.8s ease;
}
#loading-screen.hidden {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.loader-content {
text-align: center;
}
.loader-logo {
display: block;
width: 128px;
height: auto;
margin: 0 auto 24px;
filter: drop-shadow(0 0 24px rgba(0, 246, 255, 0.42));
animation: logo-pulse 2s ease-in-out infinite;
}
@keyframes logo-pulse {
0%,
100% {
opacity: 0.72;
transform: scale(0.98);
}
50% {
opacity: 1;
transform: scale(1.02);
}
}
.loader-content h2 {
font-family: var(--font-mono);
font-size: 18px;
font-weight: 600;
letter-spacing: 6px;
color: var(--text-primary);
margin-bottom: 12px;
}
.loader-status {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 2px;
color: var(--text-dim);
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 0.4;
}
50% {
opacity: 1;
}
}
/* ── Post-Processing Toggles ─────────────────── */
#pp-toggles {
--pp-expanded-width: var(--display-panel-expanded-width);
position: relative;
z-index: 110;
width: var(--pp-expanded-width);
max-width: 100%;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 7px;
}
#pp-toggles:not(.collapsed) {
box-sizing: border-box;
padding: 8px;
border: 1px solid var(--glass-border);
border-radius: var(--panel-radius);
background: var(--glass-bg);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.42),
inset 0 1px 0 rgba(255, 255, 255, 0.04);
backdrop-filter: blur(24px) saturate(1.4);
-webkit-backdrop-filter: blur(24px) saturate(1.4);
}
#pp-toggles:not(.collapsed) > .pp-header-row,
#pp-toggles:not(.collapsed) > .pp-panel-body > .pp-toggle-group,
#pp-toggles:not(.collapsed) > .pp-panel-body > #clean-view-toggle {
width: 100%;
}
.pp-panel-body,
.cyber-panel-body {
display: contents;
}
.panel-collapsible.collapsed > .global-context-panel-inner > .cyber-panel-body {
display: none;
}
.pp-toggle-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 7px 12px;
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: 8px;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
cursor: pointer;
transition:
background-color var(--transition-fast),
border-color var(--transition-fast),
box-shadow var(--transition-fast),
color var(--transition-fast),
border-radius var(--transition-fast);
outline: none;
}
.pp-toggle-group {
width: var(--pp-expanded-width);
max-width: 100%;
}
#pp-toggles .pp-toggle-btn {
width: 100%;
min-height: 38px;
box-sizing: border-box;
}
#pp-toggles .pp-header-row {
width: var(--pp-expanded-width);
max-width: 100%;
min-height: 42px;
box-sizing: border-box;
margin-left: auto;
margin-bottom: 0;
padding: 8px 10px 8px 14px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
border: 1px solid var(--glass-border);
border-radius: var(--panel-radius);
background: var(--glass-bg);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.42),
inset 0 1px 0 rgba(255, 255, 255, 0.04);
backdrop-filter: blur(24px) saturate(1.4);
-webkit-backdrop-filter: blur(24px) saturate(1.4);
cursor: default;
}
/* Expanded Display already owns the glass shell. Keep its header visually
* integrated with that container, matching the other expanded panel headers;
* the collapsed launcher retains the standalone card treatment above. */
#pp-toggles:not(.collapsed) > .pp-header-row {
min-height: 32px;
padding: 0 4px;
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
#pp-toggles .pp-header-label {
color: var(--text-dim);
}
/* Display's title and toggle sit where the other panels' do: 14px of panel
padding plus a 4px header inset. The cyber theme sets its own insets. */
:root:not([data-ui-theme='cyber']) #pp-toggles.collapsed > .pp-header-row {
padding: 8px 18px;
}
:root:not([data-ui-theme='cyber'])
#pp-toggles:not(.collapsed)
> .pp-header-row {
padding: 0 10px;
}
#pp-toggles > .pp-header-row .pp-collapse-btn {
width: 20px;
height: 20px;
border-radius: 6px;
font-size: 12px;
padding: 0;
}
/* In the default theme Display's toggle matches the other panels' 22px. */
:root:not([data-ui-theme='cyber'])
#pp-toggles
> .pp-header-row
.pp-collapse-btn {
width: 22px;
height: 22px;
font-size: 13px;
}
#pp-toggles.collapsed {
gap: 0;
}
#pp-toggles.collapsed .pp-header-row {
width: var(--right-collapsed-width, 132px);
}
#pp-toggles.collapsed .pp-toggle-group,
#pp-toggles.collapsed #clean-view-toggle,
#pp-toggles.collapsed #param-slider-panel {
display: none;
}
.pp-toggle-btn:hover {
border-color: var(--glass-border-hover);
background: rgba(255, 255, 255, 0.06);
}
.pp-toggle-btn.active {
border-color: var(--accent);
background: var(--accent-dim);
box-shadow: 0 0 12px rgba(0, 212, 255, 0.15);
}
.pp-icon {
font-size: 14px;
line-height: 1;
}
.pp-icon.material-symbols-outlined {
width: 14px;
overflow: hidden;
font-family: 'Material Symbols Outlined';
font-size: 14px;
font-weight: 400;
font-style: normal;
font-variation-settings:
'FILL' 0,
'wght' 400,
'GRAD' 0,
'opsz' 20;
}
.pp-label {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 1.5px;
color: var(--text-secondary);
text-transform: uppercase;
}
.pp-toggle-btn.active .pp-label {
color: var(--accent);
}
.pp-scope-toggle input {
width: 14px;
height: 14px;
margin: 0 0 0 auto;
accent-color: var(--accent);
cursor: pointer;
}
.pp-scope-toggle:has(input:checked) .pp-label {
color: var(--accent);
}
.pp-scope-toggle:has(input:focus-visible) {
outline: 1px solid var(--accent);
outline-offset: 2px;
}
#scope-slider-row:has(input:disabled) {
opacity: 0.45;
}
/* ── Bloom Slider (inline with toggle) ───────── */
.pp-toggle-group {
display: flex;
flex-direction: column;
gap: 0;
}
.pp-slider-row {
display: none;
align-items: center;
gap: 6px;
padding: 5px 10px 6px;
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-top: none;
border-radius: 0 0 8px 8px;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
}
.pp-slider-row.visible {
display: flex;
}
.pp-toggle-group .pp-slider-row.visible ~ .pp-toggle-btn,
.pp-toggle-group:has(.pp-slider-row.visible) .pp-toggle-btn {
border-radius: 8px 8px 0 0;
}
.pp-slider {
flex: 1;
min-width: 0;
}
.pp-slider-mini-label {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 1px;
color: var(--text-dim);
text-transform: uppercase;
white-space: nowrap;
}
/* One label column across the Display rows: --pp-label-chars is the longest
label of the active language (set in applicationShell.js); the labels are
monospace. 0.125em is the 1px letter-spacing at 8px. */
html:not([lang='en']) .pp-slider-row > .pp-slider-mini-label {
flex: 0 0 auto;
min-width: calc(var(--pp-label-chars, 0) * (1ch + 0.125em));
}
.detection-slider-row {
min-width: 170px;
padding: 5px 8px 6px;
}
#detection-slider-row.visible,
#detection-allocation-row.visible,
#detection-fade-row.visible {
border-radius: 0;
}
.detection-allocation-row {
min-width: 170px;
padding: 5px 8px 6px;
}
.detection-fade-row,
.detection-opacity-row {
min-width: 170px;
padding: 5px 8px 6px;
}
#hud-layout-row {
min-width: 200px;
}
.cyber-sonar-control {
display: none;
}
:root[data-ui-theme='cyber'] .cyber-sonar-control {
display: flex;
}
.cyber-sonar-toggle-row,
.cyber-sonar-setting {
min-width: 200px;
}
.cyber-sonar-toggle-row .pp-mode-btn:not(.gev-segment) {
margin-left: auto;
min-width: 48px;
}
:root[data-cyber-sonar='off'] .cyber-sonar-setting {
display: none;
}
/* Map Source tiles live with Visual Presets in the bottom command tray. */
.map-source-section {
display: grid;
gap: 6px;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid rgba(123, 189, 211, 0.18);
}
.map-source-heading {
display: flex;
align-items: center;
justify-content: space-between;
color: rgba(190, 221, 230, 0.62);
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 1.35px;
}
.map-stack-chip-row {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 4px;
}
.map-stack-chip {
display: inline-flex;
align-items: baseline;
justify-content: center;
gap: 4px;
padding: 4px 7px;
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.6px;
text-transform: uppercase;
white-space: nowrap;
color: var(--text-dim);
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 4px;
cursor: pointer;
/* Explicit property list, NOT `transition: all`: `all` animates
`outline-width` off the `outline: none` base, and Chrome leaves that
transition parked at 0px for chips on a wrapped line — the keyboard ring
silently never appeared on rows 2 and 3. A focus indicator should be
instant anyway. */
transition:
color var(--transition-fast),
background-color var(--transition-fast),
border-color var(--transition-fast),
box-shadow var(--transition-fast);
outline: none;
}
#command-dock #control-panel.collapsed .map-source-section {
display: none;
}
@media (max-width: 620px) {
.map-stack-chip-row {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
/* Order matters, and matches `.data-toggle-btn`: hover/focus, then active, then
disabled — all at the same specificity, so the later rule wins. Hovering the
active chip must not wash the cyan out, and a disabled chip must not light up
under the cursor. Do not add `:not(...)` here: it would raise specificity and
let hover beat `.active` again. */
.map-stack-chip:hover,
.map-stack-chip:focus-visible {
border-color: var(--glass-border-hover);
background: rgba(255, 255, 255, 0.06);
color: var(--text-primary);
}
/* The keyboard ring is an `outline`, a property NO other chip-state rule sets,
so it survives on the active chip — whose `.active` rule legitimately wins
the color/border/background/box-shadow it shares with the rule above. Without
this, tabbing onto the active chip produced no visible change at all. */
.map-stack-chip:focus-visible {
outline: 1px solid color-mix(in srgb, var(--accent) 80%, transparent);
outline-offset: 2px;
}
.map-stack-chip.active {
color: var(--accent);
border-color: var(--accent);
background: var(--accent-dim);
box-shadow: 0 0 10px rgba(0, 212, 255, 0.15);
}
.map-stack-chip.unavailable,
.map-stack-chip[aria-disabled='true'] {
color: rgba(232, 234, 237, 0.28);
border-color: rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.02);
cursor: not-allowed;
}
.map-stack-chip-req {
font-size: 7px;
letter-spacing: 0.3px;
color: rgba(232, 234, 237, 0.28);
}
.pp-stack-status {
min-width: 42px;
max-width: 54px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.8px;
color: var(--accent);
text-align: center;
text-transform: uppercase;
}
.pp-stack-status.warn {
color: #ffb84d;
}
.pp-select {
flex: 1;
min-width: 0;
}
/* DISPLAY ▸ Draw: shape segment, label field, colour, and the one-line hint */
#draw-mode-row {
min-width: 184px;
}
#draw-label-row {
flex-wrap: wrap;
row-gap: 6px;
}
.pp-text-input {
flex: 1 1 110px;
min-width: 0;
height: 24px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 6px;
color: var(--text-primary);
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.6px;
padding: 2px 6px;
outline: none;
}
.pp-text-input:focus {
border-color: var(--accent);
}
.draw-color-select {
flex: 0 0 74px;
}
/* Sized to its longest colour name so a translated one is never clipped. */
html:not([lang='en']) .draw-color-select {
flex: 0 0 auto;
min-width: 74px;
}
.draw-clear-btn {
flex: 0 0 auto;
padding: 0 8px;
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 6px;
}
.draw-hint {
flex-basis: 100%;
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.6px;
color: var(--text-dim);
line-height: 1.4;
}
body.gev-drawing #cesiumContainer canvas {
cursor: crosshair;
}
/* DISPLAY ▸ Import: one row per imported file, and the drop-target outline */
.geo-import-list {
display: none;
flex-direction: column;
gap: 4px;
margin: 6px 0 0;
padding: 0;
list-style: none;
}
.geo-import-list.visible {
display: flex;
}
.geo-import-row {
display: flex;
align-items: center;
gap: 6px;
min-width: 0;
}
.geo-import-swatch {
flex: 0 0 8px;
height: 8px;
border-radius: 2px;
}
/* KML/KMZ draw with the styles written in the file, so the swatch shows a
hatch instead of a color the globe does not use. */
.geo-import-swatch.own-styles {
background: repeating-linear-gradient(
45deg,
var(--text-dim) 0 1px,
transparent 1px 3px
);
box-shadow: inset 0 0 0 1px var(--text-dim);
}
.geo-import-name {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.6px;
color: var(--text-primary);
}
.geo-import-action {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
padding: 0;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 6px;
color: var(--text-dim);
cursor: pointer;
}
.geo-import-action:hover,
.geo-import-action:focus-visible {
color: var(--text-primary);
border-color: var(--accent);
}
.geo-import-action .material-symbols-outlined {
font-size: 14px;
}
.geo-import-hint:empty {
display: none;
}
body.gev-geo-import-drag #cesiumContainer {
outline: 2px dashed var(--accent);
outline-offset: -6px;
}
/* Proximity / All segmented control under the 3D toggle */
#models3d-mode-row {
min-width: 184px;
}
.pp-mode-seg {
display: flex;
flex: 1;
}
.pp-mode-btn:not(.gev-segment) {
flex: 1;
height: 22px;
background: rgba(255, 255, 255, 0.04);
border: none;
color: var(--text-dim);
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.6px;
text-transform: uppercase;
cursor: pointer;
transition:
background-color var(--transition-fast),
color var(--transition-fast);
}
.pp-mode-btn:not(.gev-segment):hover {
background: rgba(255, 255, 255, 0.08);
color: var(--text-primary);
}
.pp-mode-btn:not(.gev-segment).active {
background: var(--accent);
color: #000;
}
/* These controls also move into Cockpit Display; keep focus on the control
itself and inside clipped button groups, independent of selection color. */
.pp-toggle-btn:focus-visible,
.pp-select:focus-visible,
.pp-mode-btn:focus-visible {
outline: 2px solid var(--text-primary);
outline-offset: -3px;
}
.pp-mode-btn:focus-visible {
box-shadow: inset 0 0 0 4px #061014;
}
#clean-view-toggle {
width: var(--pp-expanded-width);
}
#clean-view-exit {
position: fixed;
top: 18px;
right: 50%;
transform: translateX(50%);
z-index: 300;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(8, 10, 14, 0.88);
color: #d9f7ff;
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 1.8px;
padding: 8px 14px;
cursor: pointer;
display: none;
}
body.ui-clean-view #clean-view-exit {
display: inline-flex;
}
body.ui-clean-view #title-bar,
body.ui-clean-view #style-indicator,
body.ui-clean-view #top-center-actions,
body.ui-clean-view #global-loading-status,
body.ui-clean-view #traffic-sync-chip,
body.ui-clean-view #cctv-sync-chip,
body.ui-clean-view #pp-toggles,
body.ui-clean-view #param-slider-panel,
body.ui-clean-view #control-panel,
body.ui-clean-view #location-bar,
body.ui-clean-view #data-panel,
body.ui-clean-view #cctv-panel,
body.ui-clean-view #radio-panel,
body.ui-clean-view #global-context-panel,
body.ui-clean-view #scene-panel {
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
body.ui-clean-view #scene-runtime.active {
opacity: 1;
visibility: visible;
}
/* ── Detection Toggle States ─────────────────── */
#detection-toggle.active {
border-color: rgba(0, 255, 80, 0.6);
background: rgba(0, 255, 80, 0.1);
box-shadow: 0 0 12px rgba(0, 255, 80, 0.15);
}
#detection-toggle.active .pp-label {
color: rgba(0, 255, 80, 0.9);
}
#detection-toggle.active .pp-icon {
color: rgba(0, 255, 80, 0.9);
text-shadow: 0 0 6px rgba(0, 255, 80, 0.5);
}
#detection-toggle.panoptic,
#detection-toggle.god {
border-color: rgba(0, 255, 80, 0.8);
background: rgba(0, 255, 80, 0.18);
box-shadow: 0 0 16px rgba(0, 255, 80, 0.25);
animation: detection-pulse 2s ease-in-out infinite;
}
@keyframes detection-pulse {
0%,
100% {
box-shadow: 0 0 12px rgba(0, 255, 80, 0.15);
}
50% {
box-shadow: 0 0 22px rgba(0, 255, 80, 0.35);
}
}
/* ── Parameter Slider Panel ──────────────────── */
#param-slider-panel {
display: none;
position: relative;
top: auto;
right: auto;
left: auto;
bottom: auto;
z-index: 95;
width: 100%;
max-height: min(38vh, 360px);
box-sizing: border-box;
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: 12px;
padding: 14px 16px;
backdrop-filter: blur(24px) saturate(1.4);
-webkit-backdrop-filter: blur(24px) saturate(1.4);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
opacity: 0;
visibility: hidden;
transform: translateY(6px);
transition:
opacity var(--transition-smooth),
visibility var(--transition-smooth),
transform var(--transition-smooth);
}
#param-slider-panel::-webkit-scrollbar {
display: none;
}
#param-slider-panel.active {
display: block;
opacity: 1;
visibility: visible;
transform: translateY(0);
}
/* Display owns the constrained desktop scroll surface. Keeping Parameters as
* a second scroll container traps wheel/touch input at its boundary and lets
* the flex column compress newly added preset rows below the visible panel. */
#pp-toggles:not(.collapsed) > .pp-panel-body > #param-slider-panel.active {
flex: 0 0 auto;
max-height: none;
overflow-y: visible;
}
.param-panel-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
}
.param-panel-title {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 2.5px;
color: var(--text-dim);
text-transform: uppercase;
white-space: nowrap;
}
.param-panel-divider {
flex: 1;
height: 1px;
background: linear-gradient(to right, var(--glass-border), transparent);
}
#param-slider-panel:not(.collapsed) .param-panel-divider {
background: linear-gradient(
90deg,
rgb(0 212 255 / 28%),
rgba(0, 212, 255, 0.18) 58%,
transparent
);
}
.param-slider-row {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.param-slider-row:last-child {
margin-bottom: 0;
}
/* One label column for every parameter, as wide as the longest translated
label, so all slider tracks start and end at the same x. */
html:not([lang='en']) #param-sliders {
display: grid;
grid-template-columns: max-content minmax(0, 1fr) max-content;
align-items: center;
gap: 10px;
}
html:not([lang='en']) .param-slider-row {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
margin-bottom: 0;
}
.param-label {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 1px;
color: var(--text-secondary);
min-width: 72px;
flex-shrink: 0;
}
.param-slider {
flex: 1;
min-width: 0;
}