/* ── 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; } /* A longer translated title in a collapsed rail launcher tightens its tracking rather than pushing the disclosure button out of the card. */ html:not([lang='en']) #right-context-rail .panel-collapsible.collapsed .panel-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; letter-spacing: 1.5px; } .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; cursor: pointer; transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast); } /* A long translated title must not squeeze the toggle into a sliver. */ html:not([lang='en']) .panel-collapse-btn { flex-shrink: 0; } .panel-collapse-btn:hover { border-color: var(--glass-border-hover); color: var(--text-primary); background: rgba(255, 255, 255, 0.07); } /* ── 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 { font-family: var(--font-mono); font-size: 8px; letter-spacing: 1.4px; color: var(--text-dim); } #pp-toggles > .pp-header-row .pp-collapse-btn { width: 20px; height: 20px; border-radius: 6px; font-size: 12px; padding: 0; } #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); } /* ── 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; }