mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
Merge branch 'main' into denis/hal-728-dock-icon-only-wings
This commit is contained in:
commit
2c007a4ace
782 files changed
+68624
-21392
No files matched your search
@@ -0,0 +1,25 @@
|
||||
/* ── Screen-space annotations (src/annotations/screenAnnotationRenderer.js) ── */
|
||||
.gev-screen-whiteboard { position: fixed; inset: 0; pointer-events: none; z-index: 90; }
|
||||
.gev-screen-whiteboard-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
|
||||
/* No CSS opacity transition here on purpose: group opacity is driven per-frame in JS
|
||||
(the 260ms fade-in via computeAlpha, and the tracked-entity z-order ease). A CSS
|
||||
transition fought those per-frame writes and made the tracked-fade opacity oscillate
|
||||
(flicker). Fade-OUT keeps its own transition via .gev-anno.gev-out below. */
|
||||
.gev-anno-ring { filter: drop-shadow(0 0 6px currentColor); }
|
||||
.gev-anno-dot { filter: drop-shadow(0 0 5px rgba(255,255,255,0.6)); }
|
||||
.gev-anno-area { filter: drop-shadow(0 0 5px currentColor); }
|
||||
.gev-anno-arrow { filter: drop-shadow(0 0 4px currentColor); stroke-linecap: round; }
|
||||
.gev-anno-leader { stroke-dasharray: 2 3; }
|
||||
.gev-anno-card { fill: rgba(8,18,28,0.78); stroke: rgba(255,255,255,0.14); stroke-width: 1; }
|
||||
.gev-anno-text { fill: #eaf6ff; font: 600 13px "JetBrains Mono", ui-monospace, monospace; letter-spacing: 0.02em; }
|
||||
/* draw-on: outlined shapes reveal their stroke */
|
||||
.gev-draw { stroke-dasharray: 1400; stroke-dashoffset: 1400; }
|
||||
.gev-anno.gev-in .gev-draw { transition: stroke-dashoffset 900ms ease-out; stroke-dashoffset: 0; }
|
||||
/* opacity-only fade-in: a CSS transform here would override the SVG transform
|
||||
attribute used to POSITION the callout and snap it to 0,0. */
|
||||
.gev-anno-callout { opacity: 0; }
|
||||
.gev-anno.gev-in .gev-anno-callout { animation: gev-pop 320ms ease-out both; }
|
||||
.gev-anno.gev-out { opacity: 0 !important; transition: opacity 320ms ease; }
|
||||
.gev-pulse { animation: gev-ring-pulse 1.8s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
|
||||
@keyframes gev-ring-pulse { 0%,100% { opacity: 0.9; stroke-width: 2; } 50% { opacity: 0.35; stroke-width: 3.5; } }
|
||||
@keyframes gev-pop { from { opacity: 0; } to { opacity: 1; } }
|
||||
@@ -106,8 +106,6 @@
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
background: #03080d;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(107, 232, 255, 0.36) transparent;
|
||||
}
|
||||
|
||||
.bhote-embedded-callout.is-youtube {
|
||||
@@ -518,14 +516,15 @@ html:not([lang='en']) .bhote-event-story-current strong {
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
/* Phase / time / sources run long in translation: allow a second line. */
|
||||
/* Phase / time / sources / imagery note run long in translation: allow up to
|
||||
three lines so the last status is not cut off. */
|
||||
html:not([lang='en']) .bhote-event-story-current small {
|
||||
display: -webkit-box;
|
||||
margin-top: 3px;
|
||||
text-overflow: clip;
|
||||
white-space: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
.bhote-event-actions {
|
||||
|
||||
@@ -35,8 +35,6 @@
|
||||
gap: 8px;
|
||||
max-height: calc(100vh - 24px);
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 212, 255, 0.46) rgba(255, 255, 255, 0.06);
|
||||
backdrop-filter: blur(24px) saturate(1.4);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(1.4);
|
||||
box-shadow:
|
||||
@@ -45,25 +43,6 @@
|
||||
0 1px 0 rgba(255, 255, 255, 0.06) inset;
|
||||
}
|
||||
|
||||
.cctv-panel-inner::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.cctv-panel-inner::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.cctv-panel-inner::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(0, 212, 255, 0.6),
|
||||
rgba(0, 212, 255, 0.28)
|
||||
);
|
||||
border: 1px solid rgba(0, 212, 255, 0.4);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.cctv-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
+44
-16
@@ -1,6 +1,7 @@
|
||||
/* Tight tracking keeps Chinese labels readable; preserve Latin tracking for
|
||||
intel HUD readouts and content marked translate="no", including its containers. */
|
||||
html:lang(zh)
|
||||
/* Chinese and Japanese text. Tight tracking keeps CJK labels readable;
|
||||
preserve Latin tracking for intel HUD readouts and content marked
|
||||
translate="no", including its containers. */
|
||||
html:is(:lang(zh), :lang(ja))
|
||||
body
|
||||
:not(
|
||||
#intel-hud,
|
||||
@@ -12,40 +13,67 @@ html:lang(zh)
|
||||
letter-spacing: 0.02em !important;
|
||||
}
|
||||
|
||||
/* Keep small Chinese labels legible at a minimum of 9px. */
|
||||
html:lang(zh) #command-dock .gev-mic-label,
|
||||
html:lang(zh) #command-dock #gev-voice-detail,
|
||||
html:lang(zh) .gev-voice-tier-btn,
|
||||
html:lang(zh) .panel-layer-toggle {
|
||||
/* Japanese line breaking: never start a line with ー, a small kana or closing
|
||||
punctuation (プレビュ / ー). */
|
||||
html:lang(ja) body {
|
||||
line-break: strict;
|
||||
}
|
||||
|
||||
/* Keep small CJK labels legible at a minimum of 9px. */
|
||||
html:is(:lang(zh), :lang(ja)) #command-dock .gev-mic-label,
|
||||
html:is(:lang(zh), :lang(ja)) #command-dock #gev-voice-detail,
|
||||
html:is(:lang(zh), :lang(ja)) .gev-voice-tier-btn,
|
||||
html:is(:lang(zh), :lang(ja)) .panel-layer-toggle {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
/* A two-glyph button label must not break between its glyphs. */
|
||||
html:lang(zh) .pp-mode-btn {
|
||||
html:is(:lang(zh), :lang(ja)) .pp-mode-btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Japanese segment labels are short words (すべて, 平面): keep each whole. */
|
||||
html:lang(ja) .gev-segment {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Keep unit symbols in their catalog case, including the cockpit readouts. */
|
||||
html:lang(zh) #intel-hud *,
|
||||
html:lang(zh) #cockpit-hud * {
|
||||
html:is(:lang(zh), :lang(ja)) #intel-hud *,
|
||||
html:is(:lang(zh), :lang(ja)) #cockpit-hud * {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/* Move short travel modes (步行 / 骑行) to the next line as whole words. */
|
||||
html:lang(zh) [data-layer-id='directions'] .data-toggle-meta {
|
||||
/* Move short travel modes (步行 / 骑行, 徒歩 / 自転車) to the next line as
|
||||
whole words. */
|
||||
html:is(:lang(zh), :lang(ja)) [data-layer-id='directions'] .data-toggle-meta {
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Leave room for the complete sensor name and resolution on two lines. */
|
||||
html:lang(zh) .recent-imagery-readout .ri-strip {
|
||||
html:is(:lang(zh), :lang(ja)) .recent-imagery-readout .ri-strip {
|
||||
height: 131px;
|
||||
}
|
||||
html:lang(zh) .recent-imagery-readout .ri-card {
|
||||
html:is(:lang(zh), :lang(ja)) .recent-imagery-readout .ri-card {
|
||||
height: 123px;
|
||||
grid-template-rows: 42px 13px 22px 11px 18px;
|
||||
}
|
||||
html:lang(zh) .recent-imagery-readout .ri-card-sensor {
|
||||
html:is(:lang(zh), :lang(ja)) .recent-imagery-readout .ri-card-sensor {
|
||||
white-space: normal;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
/* The Japanese imagery slot line wraps at its · separators, never inside a
|
||||
word (プレ / ビュー); a run too long for the line still breaks. */
|
||||
html:lang(ja) .recent-imagery-readout .ri-slot-value {
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Japanese layer status lines wrap at their spaces and · separators, so an
|
||||
age such as 1分以内 stays whole (更新 1 / 分以内); a run too long for the
|
||||
line still breaks. */
|
||||
html:lang(ja) .data-toggle-meta {
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
+45
-59
@@ -730,7 +730,8 @@ html:not([lang='en']) .cockpit-roll-token.is-separator {
|
||||
}
|
||||
.cockpit-vision-controls {
|
||||
display: grid;
|
||||
grid-template-columns: 48px 58px 48px;
|
||||
/* The current style grows for a long translated name (ЗВИЧАЙНИЙ). */
|
||||
grid-template-columns: 48px minmax(58px, max-content) 48px;
|
||||
align-items: stretch;
|
||||
gap: 2px;
|
||||
margin-top: 2px;
|
||||
@@ -968,23 +969,50 @@ body.cockpit-mode #view-switcher button {
|
||||
.cockpit-context-window[data-state='lost'] {
|
||||
border-top-color: rgba(213, 168, 99, 0.72);
|
||||
}
|
||||
.cockpit-context-header {
|
||||
/* The Cockpit cards' headers (Contact, the briefing) share their frame: a
|
||||
title column over a hairline, a kicker in the accent, and a row of square
|
||||
actions. Each card sets only its spacing and sizes. */
|
||||
.cockpit-card-header {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 5px 10px;
|
||||
padding: 0 5px 7px;
|
||||
border-bottom: 1px solid
|
||||
color-mix(in srgb, var(--cockpit-accent) 22%, transparent);
|
||||
}
|
||||
.cockpit-context-header > div {
|
||||
.cockpit-card-header > div {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
.cockpit-context-kicker {
|
||||
.cockpit-card-kicker {
|
||||
color: var(--cockpit-accent);
|
||||
font-size: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.cockpit-card-actions {
|
||||
display: flex;
|
||||
}
|
||||
.cockpit-card-actions button {
|
||||
display: grid;
|
||||
height: 24px;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid color-mix(in srgb, var(--cockpit-accent) 30%, transparent);
|
||||
border-radius: 5px;
|
||||
background: rgba(0, 7, 12, 0.72);
|
||||
color: color-mix(in srgb, var(--cockpit-accent) 82%, white);
|
||||
cursor: pointer;
|
||||
}
|
||||
.cockpit-card-actions button:hover,
|
||||
.cockpit-card-actions button:focus-visible {
|
||||
border-color: var(--cockpit-accent);
|
||||
color: #fff;
|
||||
outline: none;
|
||||
}
|
||||
.cockpit-context-header {
|
||||
gap: 5px 10px;
|
||||
padding: 0 5px 7px;
|
||||
}
|
||||
.cockpit-context-kicker {
|
||||
font-size: 8px;
|
||||
letter-spacing: 0.2em;
|
||||
}
|
||||
#cockpit-context-subject {
|
||||
@@ -1040,26 +1068,10 @@ body.cockpit-mode #view-switcher button {
|
||||
outline: none;
|
||||
}
|
||||
.cockpit-context-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.cockpit-context-actions button {
|
||||
display: grid;
|
||||
width: 28px;
|
||||
height: 24px;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid color-mix(in srgb, var(--cockpit-accent) 30%, transparent);
|
||||
border-radius: 5px;
|
||||
background: rgba(0, 7, 12, 0.72);
|
||||
color: color-mix(in srgb, var(--cockpit-accent) 82%, white);
|
||||
cursor: pointer;
|
||||
}
|
||||
.cockpit-context-actions button:hover,
|
||||
.cockpit-context-actions button:focus-visible {
|
||||
border-color: var(--cockpit-accent);
|
||||
color: #fff;
|
||||
outline: none;
|
||||
}
|
||||
.cockpit-context-actions button:disabled {
|
||||
border-color: rgba(244, 251, 255, 0.1);
|
||||
@@ -1081,7 +1093,7 @@ body.cockpit-mode #view-switcher button {
|
||||
gap: 3px 8px;
|
||||
min-width: 0;
|
||||
padding: 6px 9px;
|
||||
border-left: 1px solid
|
||||
border-inline-start: 1px solid
|
||||
color-mix(in srgb, var(--cockpit-accent) 40%, transparent);
|
||||
background: rgba(0, 7, 12, 0.34);
|
||||
}
|
||||
@@ -1107,7 +1119,7 @@ body.cockpit-mode #view-switcher button {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.cockpit-context-cohorts > div.unknown {
|
||||
border-left-color: rgba(213, 168, 99, 0.58);
|
||||
border-inline-start-color: rgba(213, 168, 99, 0.58);
|
||||
}
|
||||
.cockpit-context-cohorts > div.unknown strong {
|
||||
color: #d5a863;
|
||||
@@ -1499,17 +1511,8 @@ body.cockpit-mode #view-switcher button {
|
||||
width: 100%;
|
||||
}
|
||||
.cockpit-signal-header {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0 4px 7px 8px;
|
||||
border-bottom: 1px solid
|
||||
color-mix(in srgb, var(--cockpit-accent) 22%, transparent);
|
||||
}
|
||||
.cockpit-signal-header > div {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
html:not([lang='en']) .cockpit-signal-header > div {
|
||||
min-width: 0;
|
||||
@@ -1525,9 +1528,7 @@ html:not([lang='en']) .cockpit-signal-kicker {
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.cockpit-signal-kicker {
|
||||
color: var(--cockpit-accent);
|
||||
font-size: 8px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
.cockpit-signal-kicker i {
|
||||
@@ -1541,7 +1542,6 @@ html:not([lang='en']) .cockpit-signal-kicker {
|
||||
animation: cockpit-live 1.6s ease-in-out infinite;
|
||||
}
|
||||
.cockpit-brief-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
@@ -1551,22 +1551,7 @@ html:not([lang='en']) .cockpit-brief-actions > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.cockpit-brief-actions button {
|
||||
display: grid;
|
||||
width: 23px;
|
||||
height: 24px;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid color-mix(in srgb, var(--cockpit-accent) 30%, transparent);
|
||||
border-radius: 5px;
|
||||
background: rgba(0, 7, 12, 0.72);
|
||||
color: color-mix(in srgb, var(--cockpit-accent) 82%, white);
|
||||
cursor: pointer;
|
||||
}
|
||||
.cockpit-brief-actions button:hover,
|
||||
.cockpit-brief-actions button:focus-visible {
|
||||
border-color: var(--cockpit-accent);
|
||||
color: #fff;
|
||||
outline: none;
|
||||
}
|
||||
.cockpit-brief-actions .material-symbols-outlined {
|
||||
font-size: 14px;
|
||||
@@ -1626,8 +1611,9 @@ html:not([lang='en']) #cockpit-brief-auto {
|
||||
display: grid;
|
||||
grid-template-columns: 54px minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
padding: 7px 7px 7px 9px;
|
||||
border-left: 1px solid rgba(244, 251, 255, 0.2);
|
||||
padding-block: 7px;
|
||||
padding-inline: 9px 7px;
|
||||
border-inline-start: 1px solid rgba(244, 251, 255, 0.2);
|
||||
background: rgba(0, 7, 12, 0.3);
|
||||
}
|
||||
.cockpit-signal-list li.actionable {
|
||||
@@ -1637,7 +1623,7 @@ html:not([lang='en']) #cockpit-brief-auto {
|
||||
}
|
||||
.cockpit-signal-list li.actionable:hover,
|
||||
.cockpit-signal-list li.actionable:focus-within {
|
||||
border-left-color: var(--cockpit-accent);
|
||||
border-inline-start-color: var(--cockpit-accent);
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--cockpit-accent) 10%,
|
||||
@@ -1646,16 +1632,16 @@ html:not([lang='en']) #cockpit-brief-auto {
|
||||
}
|
||||
.cockpit-signal-list li.track,
|
||||
.cockpit-signal-list li.current {
|
||||
border-left-color: var(--cockpit-accent);
|
||||
border-inline-start-color: var(--cockpit-accent);
|
||||
}
|
||||
.cockpit-signal-list li.nearby {
|
||||
border-left-color: #fff;
|
||||
border-inline-start-color: #fff;
|
||||
}
|
||||
.cockpit-signal-list li.military {
|
||||
border-left-color: #d5a863;
|
||||
border-inline-start-color: #d5a863;
|
||||
}
|
||||
.cockpit-signal-list li.warning {
|
||||
border-left-color: #d5a863;
|
||||
border-inline-start-color: #d5a863;
|
||||
}
|
||||
.cockpit-signal-list time {
|
||||
color: rgba(244, 251, 255, 0.38);
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.42rem;
|
||||
color: rgba(206, 232, 239, 0.72);
|
||||
font-size: 0.58rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.13rem;
|
||||
color: var(--dock-label-color);
|
||||
font-size: var(--dock-label-font-size);
|
||||
font-weight: var(--dock-label-font-weight);
|
||||
letter-spacing: var(--dock-label-letter-spacing);
|
||||
}
|
||||
|
||||
#command-dock .dock-label-icon {
|
||||
@@ -463,17 +463,10 @@ body:not(.ui-clean-view):not(.recording-mode):has(
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='tactical'] .hud-bottom-left,
|
||||
#intel-hud[data-variant='tactical'] .hud-bottom-right,
|
||||
#intel-hud[data-variant='operator'] .hud-bottom-left,
|
||||
#intel-hud[data-variant='operator'] .hud-bottom-right {
|
||||
#intel-hud[data-variant='tactical'] .hud-bottom-right {
|
||||
bottom: calc(2vh + 6.5rem);
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='operator'] .hud-bottom-bar {
|
||||
bottom: calc(2vh + 9.8rem);
|
||||
width: min(48vw, 680px);
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='minimal'] .hud-bottom-left {
|
||||
bottom: calc(2vh + 5.4rem);
|
||||
}
|
||||
|
||||
+157
-15
@@ -81,16 +81,104 @@
|
||||
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 {
|
||||
/* 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;
|
||||
letter-spacing: 1.5px;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
@@ -109,6 +197,8 @@ html:not([lang='en'])
|
||||
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),
|
||||
@@ -116,17 +206,50 @@ html:not([lang='en'])
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
@@ -431,12 +554,21 @@ html:not([lang='en']) .panel-collapse-btn {
|
||||
}
|
||||
|
||||
#pp-toggles .pp-header-label {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 8px;
|
||||
letter-spacing: 1.4px;
|
||||
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;
|
||||
@@ -445,6 +577,16 @@ html:not([lang='en']) .panel-collapse-btn {
|
||||
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;
|
||||
}
|
||||
|
||||
+60
-29
@@ -41,8 +41,6 @@
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(165, 188, 199, 0.35) transparent;
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber'] .cctv-panel-inner > .cyber-panel-body {
|
||||
@@ -68,8 +66,6 @@
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(165, 188, 199, 0.35) transparent;
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber'] #pp-toggles > .pp-panel-body > * {
|
||||
@@ -82,6 +78,8 @@
|
||||
|
||||
:root[data-ui-theme='cyber'] {
|
||||
color-scheme: dark;
|
||||
--scroll-thumb: rgba(165, 188, 199, 0.35);
|
||||
--scroll-track: transparent;
|
||||
--cyber-red: #c45454;
|
||||
--cyber-red-bright: #e16666;
|
||||
--cyber-slate: #26343a;
|
||||
@@ -247,7 +245,7 @@
|
||||
.street-level-panel-inner,
|
||||
.global-context-panel-inner
|
||||
) {
|
||||
padding-right: 8px;
|
||||
padding-inline-end: 8px;
|
||||
}
|
||||
|
||||
/* Collapsed right-rail panels otherwise inherit different legacy gutters:
|
||||
@@ -747,12 +745,6 @@
|
||||
.btn-icon::before {
|
||||
content: '◐';
|
||||
}
|
||||
:root[data-ui-theme='cyber']
|
||||
#command-dock
|
||||
.style-btn[data-style='snow']
|
||||
.btn-icon::before {
|
||||
content: '❄︎';
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber'] :where(#bloom-toggle, #sharpen-toggle) {
|
||||
font-size: 0;
|
||||
@@ -802,6 +794,12 @@
|
||||
accent-color: var(--cyber-red);
|
||||
}
|
||||
|
||||
/* Street Level's range wears Cyber's slider accent like every other range. */
|
||||
:root[data-ui-theme='cyber'] .sl-range {
|
||||
--accent: inherit;
|
||||
--accent-glow: inherit;
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber']
|
||||
:where(button, a[href], input, select, textarea):focus-visible {
|
||||
outline-color: var(--cyber-red-bright) !important;
|
||||
@@ -1447,6 +1445,15 @@
|
||||
text-shadow: 0 0 6px var(--cyber-red);
|
||||
}
|
||||
|
||||
/* Cyber frames each mode button itself; the shared segmented track would
|
||||
draw a third, blue frame around them. */
|
||||
:root[data-ui-theme='cyber'] .global-context-modes {
|
||||
gap: 7px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber'] .dock-tray-toggle,
|
||||
:root[data-ui-theme='cyber'] .context-mode-button {
|
||||
background-color: rgba(38, 52, 58, 0.95);
|
||||
@@ -1558,10 +1565,6 @@
|
||||
clip-path: polygon(12px 0, 100% 0, calc(100% - 12px) 100%, 0 100%);
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='cyber'] .hud-top-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Cyber keeps the useful style and locality readouts, not fictional IDs. */
|
||||
#intel-hud[data-variant='cyber'] .hud-top-left .hud-classification,
|
||||
#intel-hud[data-variant='cyber'] .hud-top-left .hud-system {
|
||||
@@ -1651,12 +1654,27 @@
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber'] #left-panel-stack {
|
||||
/* The stack holds still, so its rail needs no offset (leftPanelRail.js). */
|
||||
--panel-stack-top-pinned: 1;
|
||||
top: var(--cyber-left-rail-top) !important;
|
||||
bottom: calc(2vh + 13rem);
|
||||
left: 54px;
|
||||
inset-inline-start: calc(54px + var(--panel-rail-clearance));
|
||||
width: 300px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid rgba(196, 84, 84, 0.72);
|
||||
padding-inline-start: 10px;
|
||||
border-inline-start: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* An edge line marks each side's open panel, mirrored left and right. */
|
||||
:root[data-ui-theme='cyber']
|
||||
body:not(.cockpit-mode)
|
||||
#left-panel-stack:has(> [data-panel-id]:not(.collapsed, [hidden])) {
|
||||
border-inline-start-color: rgba(196, 84, 84, 0.72);
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber']
|
||||
body:not(.cockpit-mode)
|
||||
#right-context-rail:has(> [data-panel-id]:not(.collapsed, [hidden])) {
|
||||
border-inline-end-color: rgba(196, 84, 84, 0.72);
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber']
|
||||
@@ -1675,6 +1693,13 @@
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
/* The panel rail lists the collapsed panels. */
|
||||
:root[data-ui-theme='cyber']
|
||||
#left-panel-stack.layout-focus.layout-exclusive
|
||||
> [data-panel-id].collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber']
|
||||
#left-panel-stack
|
||||
> :is(#data-panel, #scene-panel) {
|
||||
@@ -1728,8 +1753,11 @@
|
||||
:root[data-ui-theme='cyber'] #right-context-rail {
|
||||
--right-display-collapsed-width: var(--cyber-panel-collapsed-width);
|
||||
--right-context-collapsed-width: var(--cyber-panel-collapsed-width);
|
||||
right: 62px;
|
||||
width: 312px;
|
||||
/* The edge line and its gap keep the panels where they were. */
|
||||
right: calc(51px + var(--panel-rail-clearance));
|
||||
width: 323px;
|
||||
padding-inline-end: 10px;
|
||||
border-inline-end: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* The map rails now use one visible expanded width. Keeping the rail itself
|
||||
@@ -1798,8 +1826,11 @@
|
||||
isolation: isolate;
|
||||
min-height: 132px;
|
||||
flex: 1 1 auto;
|
||||
border-style: solid;
|
||||
border-color: rgba(206, 222, 226, 0.14);
|
||||
place-content: center;
|
||||
padding: 12px;
|
||||
border: 1px solid rgba(206, 222, 226, 0.14);
|
||||
border-radius: 7px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(
|
||||
@@ -1861,7 +1892,6 @@
|
||||
background-size: 78px 78px;
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='cyber'] .hud-top-bar,
|
||||
#intel-hud[data-variant='cyber'] .hud-bottom-bar {
|
||||
display: none;
|
||||
}
|
||||
@@ -2498,15 +2528,13 @@
|
||||
);
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber']
|
||||
body.cockpit-mode
|
||||
:is(.cockpit-context-header, .cockpit-signal-header) {
|
||||
:root[data-ui-theme='cyber'] body.cockpit-mode .cockpit-card-header {
|
||||
border-bottom-color: rgba(105, 203, 209, 0.25);
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber']
|
||||
body.cockpit-mode
|
||||
:is(.cockpit-context-kicker, .cockpit-signal-kicker, .cockpit-route-kicker) {
|
||||
:is(.cockpit-card-kicker, .cockpit-route-kicker) {
|
||||
color: var(--cockpit-caution);
|
||||
}
|
||||
|
||||
@@ -2721,10 +2749,13 @@
|
||||
:root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack {
|
||||
top: clamp(164px, 19vh, 204px) !important;
|
||||
bottom: auto;
|
||||
/* The cockpit keeps its physical sides in every language (rtl.css): the
|
||||
utility controls hold the right edge. */
|
||||
right: auto;
|
||||
left: 30px;
|
||||
width: var(--cyber-panel-width);
|
||||
padding-left: 0;
|
||||
border-left: 0;
|
||||
padding-inline-start: 0;
|
||||
border-inline-start: 0;
|
||||
}
|
||||
|
||||
:root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack > #data-panel {
|
||||
|
||||
@@ -128,20 +128,9 @@ test('cockpit brief controls keep their English sizes', () => {
|
||||
|
||||
test('the count-line class is set only outside English', async () => {
|
||||
const { setLocale } = await import('../../i18n/index.js');
|
||||
const { LayerPanel } = await import('../layerPanel.js');
|
||||
const classes = () => {
|
||||
const toggled = new Map();
|
||||
const node = () => ({
|
||||
textContent: '',
|
||||
classList: { toggle: (name, on) => toggled.set(name, on) },
|
||||
appendChild() {},
|
||||
});
|
||||
LayerPanel.prototype._syncCount.call(LayerPanel.prototype, node(), node(), {
|
||||
countLabel: '693 nearby · 93 on screen',
|
||||
});
|
||||
return toggled.get('data-toggle-row--count-line');
|
||||
};
|
||||
globalThis.document ??= { createElement: () => ({}) };
|
||||
const { presentLayerCount } = await import('../layerPanel.js');
|
||||
const classes = () =>
|
||||
presentLayerCount({ countLabel: '693 nearby · 93 on screen' }).countLine;
|
||||
try {
|
||||
setLocale('en');
|
||||
assert.equal(classes(), false);
|
||||
|
||||
@@ -110,20 +110,11 @@
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scroll-behavior: smooth;
|
||||
/* A visible, non-overlaying affordance that the list continues. */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(54, 220, 255, 0.45) transparent;
|
||||
/* A visible, non-overlaying affordance that the list continues; the
|
||||
scrollbar itself is the kit's .ui-scroll. */
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.first-run-choices::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.first-run-choices::-webkit-scrollbar-thumb {
|
||||
background: rgba(54, 220, 255, 0.45);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.first-run-choices button {
|
||||
display: grid;
|
||||
grid-template-columns: 1.7rem 1fr 1.3rem;
|
||||
@@ -133,7 +124,7 @@
|
||||
min-height: 3.55rem;
|
||||
padding: 0.6rem 0.7rem;
|
||||
color: rgba(225, 244, 248, 0.92);
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
background: rgba(29, 69, 81, 0.18);
|
||||
border: 1px solid rgba(100, 200, 220, 0.18);
|
||||
border-radius: 0.42rem;
|
||||
|
||||
@@ -10,6 +10,20 @@
|
||||
font-family: var(--font-sans);
|
||||
font-size: 12px;
|
||||
outline: none;
|
||||
/* Native number spinners and the open select list follow the dark skin. */
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* A select draws the field's own chevron, not the platform's arrow, so every
|
||||
dropdown in the app reads the same. The open list stays native. */
|
||||
select.gev-field {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
padding-inline-end: 22px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='rgba(255,255,255,0.62)' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 8px center;
|
||||
background-size: 10px 6px;
|
||||
}
|
||||
|
||||
.gev-field:focus-visible {
|
||||
@@ -30,6 +44,11 @@
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
select.gev-field--compact {
|
||||
padding-inline-end: 18px;
|
||||
background-position: right 6px center;
|
||||
}
|
||||
|
||||
/* Compact inline editors share density and hide native number spinners.
|
||||
Their containing feature sets width to fit the surrounding readout. */
|
||||
.gev-field--inline {
|
||||
|
||||
Vendored
+17
-6
@@ -9,6 +9,9 @@
|
||||
--glass-border: rgba(255, 255, 255, 0.08);
|
||||
--glass-border-hover: rgba(255, 255, 255, 0.15);
|
||||
--accent: #00d4ff;
|
||||
/* Every scrolling surface's scrollbar (the kit's .ui-scroll). */
|
||||
--scroll-thumb: rgba(0, 212, 255, 0.46);
|
||||
--scroll-track: rgba(255, 255, 255, 0.06);
|
||||
--accent-dim: rgba(0, 212, 255, 0.15);
|
||||
--accent-glow: rgba(0, 212, 255, 0.4);
|
||||
--text-primary: #e8eaed;
|
||||
@@ -21,9 +24,17 @@
|
||||
--panel-radius: 16px;
|
||||
--btn-radius: 10px;
|
||||
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
/* Label type shared by the command dock and the side panel titles. */
|
||||
--dock-label-color: rgba(206, 232, 239, 0.72);
|
||||
--dock-label-font-size: 0.58rem;
|
||||
--dock-label-font-weight: 500;
|
||||
--dock-label-letter-spacing: 0.13rem;
|
||||
--transition-smooth: 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--left-stack-x: 52px;
|
||||
--right-rail-x: 52px;
|
||||
/* The side stacks clear the 52px edge gutter that holds the vertical HUD
|
||||
readout, plus the floating panel rail (42px) and its 6px gap. */
|
||||
--panel-rail-clearance: 48px;
|
||||
--left-stack-x: calc(52px + var(--panel-rail-clearance));
|
||||
--right-rail-x: calc(52px + var(--panel-rail-clearance));
|
||||
--left-stack-top: 26vh;
|
||||
--left-stack-gap: 72px;
|
||||
--left-collapsed-width: 176px;
|
||||
@@ -492,7 +503,7 @@ body.cockpit-mode #cockpit-cloud-effects.active {
|
||||
#title-bar {
|
||||
position: fixed;
|
||||
top: 32px;
|
||||
left: 36px;
|
||||
inset-inline-start: 36px;
|
||||
z-index: 100;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
@@ -501,7 +512,7 @@ body.cockpit-mode #cockpit-cloud-effects.active {
|
||||
#title-bar .title-glow {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: -20px;
|
||||
inset-inline-start: -20px;
|
||||
width: 200px;
|
||||
height: 80px;
|
||||
background: radial-gradient(ellipse, var(--accent-dim) 0%, transparent 70%);
|
||||
@@ -626,11 +637,11 @@ body.cockpit-mode #cockpit-cloud-effects.active {
|
||||
#style-indicator {
|
||||
position: fixed;
|
||||
top: 32px;
|
||||
right: 36px;
|
||||
inset-inline-end: 36px;
|
||||
z-index: 100;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
text-align: right;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
#style-indicator .indicator-label {
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/* Generic components in src/ui/kit. The surfaces use this application's
|
||||
glass, accent and type tokens. */
|
||||
|
||||
/* The thin themed scrollbar every scrolling surface wears (.ui-scroll). Without it a surface draws the platform's light scrollbar,
|
||||
because the page declares no dark color-scheme. */
|
||||
/* Cesium builds its credit list and the rail card builder its lines, so
|
||||
those two surfaces are named here instead of carrying the class. */
|
||||
.ui-scroll,
|
||||
.cesium-credit-lightbox > ul,
|
||||
.weather-card [data-line-id='status'] {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--scroll-thumb) var(--scroll-track);
|
||||
}
|
||||
.ui-scroll::-webkit-scrollbar,
|
||||
.cesium-credit-lightbox > ul::-webkit-scrollbar,
|
||||
.weather-card [data-line-id='status']::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
.ui-scroll::-webkit-scrollbar-thumb,
|
||||
.cesium-credit-lightbox > ul::-webkit-scrollbar-thumb,
|
||||
.weather-card [data-line-id='status']::-webkit-scrollbar-thumb {
|
||||
border-radius: 999px;
|
||||
background: var(--scroll-thumb);
|
||||
}
|
||||
.ui-scroll::-webkit-scrollbar-track,
|
||||
.cesium-credit-lightbox > ul::-webkit-scrollbar-track,
|
||||
.weather-card [data-line-id='status']::-webkit-scrollbar-track {
|
||||
background: var(--scroll-track);
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
font-variation-settings:
|
||||
'FILL' 0,
|
||||
'wght' 400,
|
||||
'GRAD' 0,
|
||||
'opsz' 20;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ui-tooltip {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
}
|
||||
.ui-tooltip__tip {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
z-index: 1;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 6px;
|
||||
background: var(--menu-bg);
|
||||
color: var(--dock-label-color);
|
||||
font: var(--dock-label-font-weight) var(--dock-label-font-size) / 1.4
|
||||
var(--font-sans);
|
||||
letter-spacing: var(--dock-label-letter-spacing);
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
/* Sides are for left-to-right; a right-to-left page mirrors them with the
|
||||
rails, so a tip still opens toward the middle of the screen. */
|
||||
.ui-tooltip__tip--right {
|
||||
inset-inline-start: calc(100% + 10px);
|
||||
}
|
||||
.ui-tooltip__tip--left {
|
||||
inset-inline-end: calc(100% + 10px);
|
||||
}
|
||||
|
||||
.ui-rail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 4px;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: calc(var(--btn-radius) + 4px);
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
pointer-events: auto;
|
||||
}
|
||||
.ui-rail__sep {
|
||||
height: 1px;
|
||||
margin: 3px 6px;
|
||||
background: var(--glass-border);
|
||||
}
|
||||
.ui-rail__item {
|
||||
position: relative;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--btn-radius);
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
color var(--transition-fast),
|
||||
background var(--transition-fast),
|
||||
border-color var(--transition-fast);
|
||||
}
|
||||
.ui-rail__item:hover:not(:disabled) {
|
||||
color: var(--text-primary);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
.ui-rail__item--on,
|
||||
.ui-rail__item--on:hover:not(:disabled) {
|
||||
border-color: var(--accent-dim);
|
||||
background: var(--accent-dim);
|
||||
color: var(--accent);
|
||||
}
|
||||
.ui-rail__item:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* A collapsible rail shows each item as an icon and its label while no panel
|
||||
is open, and folds the labels away once one is. The collapsed item keeps
|
||||
the plain rail's 32px square. */
|
||||
.ui-rail--collapsible {
|
||||
transition: border-radius var(--transition-fast);
|
||||
}
|
||||
.ui-rail--collapsible .ui-rail__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
min-width: 32px;
|
||||
padding: 0 6px;
|
||||
transition:
|
||||
color var(--transition-fast),
|
||||
background var(--transition-fast),
|
||||
border-color var(--transition-fast),
|
||||
border-radius var(--transition-fast);
|
||||
}
|
||||
.ui-rail__label {
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font: var(--dock-label-font-weight) var(--dock-label-font-size) / 1
|
||||
var(--font-sans);
|
||||
letter-spacing: var(--dock-label-letter-spacing);
|
||||
transition:
|
||||
max-width var(--transition-fast),
|
||||
margin var(--transition-fast),
|
||||
opacity var(--transition-fast);
|
||||
}
|
||||
.ui-rail--expanded {
|
||||
border-radius: 21px;
|
||||
}
|
||||
.ui-rail--expanded .ui-rail__item {
|
||||
border-radius: 16px;
|
||||
}
|
||||
.ui-rail--expanded .ui-rail__label {
|
||||
max-width: 220px;
|
||||
margin-inline: 8px 8px;
|
||||
opacity: 1;
|
||||
}
|
||||
.ui-rail__badge {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
inset-inline-end: 1px;
|
||||
min-width: 14px;
|
||||
padding: 0 3px;
|
||||
border-radius: 7px;
|
||||
background: var(--accent);
|
||||
color: var(--bg-dark);
|
||||
font: 600 9px/14px var(--font-mono);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Application view hosts (src/ui/views): the view's own elements lay out as
|
||||
children of the page, as the static markup they replace did. */
|
||||
.gev-view {
|
||||
display: contents;
|
||||
}
|
||||
+87
-227
@@ -3,7 +3,7 @@
|
||||
position: fixed;
|
||||
top: var(--left-stack-safe-top, var(--left-stack-top));
|
||||
bottom: var(--left-stack-safe-bottom, 4vh);
|
||||
left: var(--left-stack-x);
|
||||
inset-inline-start: var(--left-stack-x);
|
||||
z-index: 100;
|
||||
width: 360px;
|
||||
display: flex;
|
||||
@@ -84,29 +84,9 @@
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
padding-right: 4px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 212, 255, 0.5) rgba(255, 255, 255, 0.04);
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.data-toggle-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.data-toggle-list::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.data-toggle-list::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(0, 212, 255, 0.65),
|
||||
rgba(0, 212, 255, 0.2)
|
||||
);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.data-layer-group-heading {
|
||||
margin: 8px 10px 2px;
|
||||
padding-top: 12px;
|
||||
@@ -197,11 +177,11 @@
|
||||
#right-context-rail #weather-panel.collapsed,
|
||||
#right-context-rail #recent-imagery-panel.collapsed {
|
||||
width: var(--right-display-collapsed-width);
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
#right-context-rail > #pp-toggles.collapsed {
|
||||
width: var(--right-display-collapsed-width);
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
#right-context-rail > #pp-toggles.collapsed .pp-header-row {
|
||||
width: 100%;
|
||||
@@ -240,7 +220,7 @@
|
||||
width: var(--right-context-collapsed-width);
|
||||
/* Keep the compact Context tab on the rail edge rather than leaving it
|
||||
stranded at the rail's expanded-width origin. */
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
#right-context-rail
|
||||
#global-context-panel.collapsed
|
||||
@@ -249,7 +229,7 @@
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
overflow: visible;
|
||||
padding: 8px 10px 8px 14px;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
#right-context-rail #global-context-panel.collapsed .panel-header {
|
||||
margin: 0;
|
||||
@@ -260,6 +240,42 @@
|
||||
#right-context-rail.layout-exclusive > [data-panel-id].collapsed {
|
||||
display: none;
|
||||
}
|
||||
#left-panel-stack.layout-exclusive > [data-panel-id].collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Panel rails (src/ui/panelNavigation.js). Each rail lives inside its stack,
|
||||
so it shows and hides with the stack, and floats in the screen-edge gutter.
|
||||
The stack's layout offsets it back to its resting top, so it holds still
|
||||
when an opened panel moves the stack. */
|
||||
.gev-panel-rail {
|
||||
position: absolute;
|
||||
top: var(--panel-rail-offset, 0px);
|
||||
z-index: 1;
|
||||
pointer-events: auto;
|
||||
transition: top var(--transition-fast);
|
||||
}
|
||||
/* Each rail keeps its outer edge at the screen-side clearance, so a rail that
|
||||
widens (labelled pills while its side has no open panel) grows toward the
|
||||
middle of the screen. Collapsed, it sits 6px outside its stack. */
|
||||
.gev-panel-rail--left {
|
||||
inset-inline-start: calc(-1 * var(--panel-rail-clearance));
|
||||
}
|
||||
.gev-panel-rail--right {
|
||||
inset-inline-end: calc(-1 * var(--panel-rail-clearance));
|
||||
}
|
||||
body.mobile-shell #left-panel-stack > .gev-panel-rail,
|
||||
body.mobile-shell #right-context-rail > .gev-panel-rail,
|
||||
body.cockpit-mode .gev-panel-rail {
|
||||
display: none;
|
||||
}
|
||||
/* Clean view and recording hide the panels one by one, not their stacks. */
|
||||
body.ui-clean-view .gev-panel-rail,
|
||||
body.recording-mode .gev-panel-rail {
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
#right-context-rail.layout-focus > [data-panel-id].collapsed {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
@@ -281,8 +297,6 @@
|
||||
#right-context-rail.layout-focus > #pp-toggles:not(.collapsed) {
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 212, 255, 0.4) transparent;
|
||||
}
|
||||
#right-context-rail.layout-focus
|
||||
> #weather-panel:not(.collapsed)
|
||||
@@ -385,7 +399,7 @@
|
||||
gap: 10px;
|
||||
padding: 1px 3px 7px;
|
||||
border-bottom: 1px solid rgba(34, 230, 230, 0.14);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
#space-mission-panel-host .space-mission-roster-header > div {
|
||||
display: grid;
|
||||
@@ -415,8 +429,6 @@
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-right: 3px;
|
||||
scrollbar-color: rgba(34, 230, 230, 0.38) transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
#space-mission-panel-host .space-mission-roster-item {
|
||||
position: relative;
|
||||
@@ -431,7 +443,7 @@
|
||||
border-radius: 4px;
|
||||
color: #d8ffff;
|
||||
background: rgba(42, 113, 171, 0.045);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-family: var(--font-mono);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
@@ -985,17 +997,9 @@
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
/* Context is read for longer than other panels; it carries no halo. */
|
||||
#global-context-panel .panel-glow {
|
||||
position: absolute;
|
||||
inset: -20px;
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(98, 181, 255, 0.2) 0%,
|
||||
transparent 62%
|
||||
);
|
||||
filter: blur(20px);
|
||||
opacity: 0.24;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
}
|
||||
.global-context-panel-inner {
|
||||
position: relative;
|
||||
@@ -1012,157 +1016,14 @@
|
||||
backdrop-filter: blur(24px) saturate(1.4);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(1.4);
|
||||
}
|
||||
.context-radio-dock {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.context-radio-toggle {
|
||||
display: grid;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid rgba(98, 181, 255, 0.22);
|
||||
border-radius: 6px;
|
||||
color: rgba(143, 207, 255, 0.58);
|
||||
background: rgba(42, 113, 171, 0.08);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.15s ease,
|
||||
color 0.15s ease,
|
||||
background 0.15s ease,
|
||||
box-shadow 0.15s ease;
|
||||
}
|
||||
.context-radio-toggle .material-symbols-outlined {
|
||||
font-size: 17px;
|
||||
}
|
||||
.context-radio-toggle:hover,
|
||||
.context-radio-toggle:focus-visible {
|
||||
border-color: #79c2ff;
|
||||
color: #fff;
|
||||
outline: none;
|
||||
}
|
||||
.context-radio-toggle.active {
|
||||
border-color: rgba(101, 247, 255, 0.82);
|
||||
color: #eaffff;
|
||||
background: rgba(0, 212, 255, 0.16);
|
||||
box-shadow:
|
||||
inset 0 0 12px rgba(0, 212, 255, 0.1),
|
||||
0 0 12px rgba(0, 212, 255, 0.2);
|
||||
}
|
||||
.context-radio-toggle:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: wait;
|
||||
}
|
||||
.context-radio-mini {
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
top: calc(100% + 20px);
|
||||
right: -38px;
|
||||
display: none;
|
||||
width: 244px;
|
||||
box-sizing: border-box;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
border: 1px solid rgba(65, 220, 231, 0.28);
|
||||
border-radius: 8px;
|
||||
background: rgba(2, 15, 20, 0.96);
|
||||
box-shadow:
|
||||
0 12px 32px rgba(0, 0, 0, 0.62),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||
backdrop-filter: blur(22px) saturate(1.35);
|
||||
-webkit-backdrop-filter: blur(22px) saturate(1.35);
|
||||
}
|
||||
.context-radio-mini[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
.context-radio-dock.disclosure-open .context-radio-mini {
|
||||
display: grid;
|
||||
}
|
||||
.context-radio-mini-heading {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
color: #65f7ff;
|
||||
font: 7px/1.2 var(--font-mono);
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
.context-radio-mini-heading button {
|
||||
display: grid;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid rgba(65, 220, 231, 0.28);
|
||||
border-radius: 5px;
|
||||
color: #8feaf2;
|
||||
background: rgba(14, 69, 78, 0.16);
|
||||
cursor: pointer;
|
||||
}
|
||||
.context-radio-mini-heading button:hover,
|
||||
.context-radio-mini-heading button:focus-visible {
|
||||
border-color: #65f7ff;
|
||||
color: #fff;
|
||||
outline: none;
|
||||
}
|
||||
.context-radio-mini-actions {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
.context-radio-mini-heading .material-symbols-outlined {
|
||||
font-size: 16px;
|
||||
}
|
||||
.context-radio-mini > .panel-layer-toggle {
|
||||
width: 100%;
|
||||
}
|
||||
.context-radio-mini-heading strong {
|
||||
overflow: hidden;
|
||||
color: #e8feff;
|
||||
font-size: 8px;
|
||||
font-weight: 500;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.context-radio-mini-transport {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 5px;
|
||||
}
|
||||
.context-radio-mini-transport button {
|
||||
min-height: 28px;
|
||||
padding: 5px;
|
||||
border: 1px solid rgba(65, 220, 231, 0.28);
|
||||
border-radius: 5px;
|
||||
color: #8feaf2;
|
||||
background: rgba(14, 69, 78, 0.16);
|
||||
font: 8px/1 var(--font-mono);
|
||||
cursor: pointer;
|
||||
}
|
||||
.context-radio-mini-transport button:hover:not(:disabled),
|
||||
.context-radio-mini-transport button:focus-visible:not(:disabled),
|
||||
.context-radio-mini-transport button.active {
|
||||
border-color: #65f7ff;
|
||||
color: #fff;
|
||||
outline: none;
|
||||
}
|
||||
.context-radio-mini-transport button:disabled {
|
||||
opacity: 0.38;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.context-radio-mini-volume {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) 30px;
|
||||
gap: 7px;
|
||||
align-items: center;
|
||||
color: var(--text-dim);
|
||||
font: 7px/1 var(--font-mono);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.context-radio-mini-volume input {
|
||||
width: 100%;
|
||||
|
||||
/* Data Layers and Context share the 14px side padding of the other default
|
||||
theme panels, so every title and toggle sits 19px from its edge. Cyber
|
||||
keeps its own gutters. */
|
||||
:root:not([data-ui-theme='cyber'])
|
||||
:is(.data-panel-inner, .global-context-panel-inner) {
|
||||
padding-right: 14px;
|
||||
padding-left: 14px;
|
||||
}
|
||||
.global-context-actions {
|
||||
display: grid;
|
||||
@@ -1229,40 +1090,44 @@
|
||||
rather than relying on the chrome alone. */
|
||||
filter: drop-shadow(0 0 5px rgba(150, 210, 255, 0.9));
|
||||
}
|
||||
/* One segmented control: the two modes read as a single choice. */
|
||||
.global-context-modes {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 7px;
|
||||
margin: 0 0 10px;
|
||||
gap: 2px;
|
||||
margin: 0 0 12px;
|
||||
padding: 2px;
|
||||
border: 1px solid rgba(98, 181, 255, 0.22);
|
||||
border-radius: 7px;
|
||||
background: rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
.context-mode-button {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
min-height: 36px;
|
||||
min-height: 34px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 7px 8px;
|
||||
border: 1px solid rgba(98, 181, 255, 0.28);
|
||||
border-radius: 6px;
|
||||
color: #8fcfff;
|
||||
background: rgba(42, 113, 171, 0.09);
|
||||
font: 8px/1.15 var(--font-mono);
|
||||
letter-spacing: 0.09em;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 5px;
|
||||
color: rgba(214, 236, 255, 0.72);
|
||||
background: transparent;
|
||||
font: 600 10px/1.2 var(--font-mono);
|
||||
letter-spacing: 0.06em;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.15s ease,
|
||||
color 0.15s ease,
|
||||
background 0.15s ease,
|
||||
box-shadow 0.15s ease;
|
||||
background 0.15s ease;
|
||||
}
|
||||
.context-mode-button .material-symbols-outlined {
|
||||
font-size: 16px;
|
||||
}
|
||||
.context-mode-button:hover,
|
||||
.context-mode-button:focus-visible {
|
||||
border-color: #79c2ff;
|
||||
color: #fff;
|
||||
background: rgba(98, 181, 255, 0.08);
|
||||
outline: none;
|
||||
}
|
||||
/* Keep focus distinct when the selected tab owns the accent colors. */
|
||||
@@ -1272,12 +1137,9 @@
|
||||
}
|
||||
|
||||
.context-mode-button.active {
|
||||
border-color: rgba(0, 212, 255, 0.75);
|
||||
color: #eaffff;
|
||||
background: rgba(0, 212, 255, 0.14);
|
||||
box-shadow:
|
||||
inset 0 0 16px rgba(0, 212, 255, 0.08),
|
||||
0 0 12px rgba(0, 212, 255, 0.12);
|
||||
border-color: rgba(0, 212, 255, 0.55);
|
||||
color: #fff;
|
||||
background: rgba(0, 212, 255, 0.16);
|
||||
}
|
||||
.context-mode-button:disabled {
|
||||
opacity: 0.55;
|
||||
@@ -1287,23 +1149,20 @@
|
||||
.context-mode-standby[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/* Plain guidance under the tabs, not a placeholder box. */
|
||||
.context-mode-standby {
|
||||
display: grid;
|
||||
min-height: 72px;
|
||||
place-content: center;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
border: 1px dashed rgba(98, 181, 255, 0.2);
|
||||
border-radius: 7px;
|
||||
color: rgba(190, 230, 230, 0.5);
|
||||
text-align: center;
|
||||
font: 8px/1.45 var(--font-mono);
|
||||
letter-spacing: 0.1em;
|
||||
padding: 2px 2px 4px;
|
||||
color: rgba(214, 236, 255, 0.72);
|
||||
font: 10px/1.6 var(--font-mono);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.context-mode-standby strong {
|
||||
color: #8fcfff;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.16em;
|
||||
color: var(--text-primary);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.context-mode-view {
|
||||
min-height: 0;
|
||||
@@ -1344,19 +1203,20 @@
|
||||
}
|
||||
.global-context-actions .panel-layer-toggle {
|
||||
width: 100%;
|
||||
min-height: 30px;
|
||||
min-height: 32px;
|
||||
padding: 6px 8px;
|
||||
font-size: 8px;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
#global-context-panel #cockpit-entry {
|
||||
position: static;
|
||||
z-index: auto;
|
||||
width: 100%;
|
||||
min-height: 30px;
|
||||
min-height: 32px;
|
||||
padding: 5px 8px;
|
||||
border-radius: 5px;
|
||||
justify-content: center;
|
||||
font-size: 8px;
|
||||
font-size: 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
#global-context-panel #cockpit-entry .material-symbols-outlined {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* ============================================================
|
||||
Bottom-right corner row: the POWER UP chip (provider-settings.css)
|
||||
and, in the very corner, the interface language picker
|
||||
(src/ui/localePicker.js). Logical properties throughout, so a
|
||||
(src/ui/views/panels/LocalePicker.vue). Logical properties throughout, so a
|
||||
right-to-left page mirrors the row and the list.
|
||||
============================================================ */
|
||||
|
||||
|
||||
@@ -84,10 +84,10 @@ body.mobile-shell[data-mobile-sheet] #mobile-sheet-backdrop:not([hidden]) {
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
/* ── Top bar: logo left, globe actions right ───────────────────────────── */
|
||||
/* ── Top bar: logo at the start, globe actions at the end ──────────────── */
|
||||
body.mobile-shell #title-bar {
|
||||
top: calc(var(--mobile-top-inset) + 10px);
|
||||
left: 14px;
|
||||
inset-inline-start: 14px;
|
||||
}
|
||||
|
||||
body.mobile-shell #title-bar .title-logo {
|
||||
@@ -101,13 +101,13 @@ body.mobile-shell #title-bar .subtitle {
|
||||
|
||||
body.mobile-shell #top-center-actions {
|
||||
top: calc(var(--mobile-top-inset) + 10px);
|
||||
right: 12px;
|
||||
inset-inline-start: auto;
|
||||
inset-inline-end: 12px;
|
||||
}
|
||||
|
||||
/* ── HUD: one compact readout under the top bar ────────────────────────── */
|
||||
body.mobile-shell #intel-hud .hud-edge,
|
||||
body.mobile-shell #intel-hud .hud-corner,
|
||||
body.mobile-shell #intel-hud .hud-top-bar,
|
||||
body.mobile-shell #intel-hud .hud-bottom-bar {
|
||||
display: none;
|
||||
}
|
||||
@@ -401,7 +401,7 @@ body.mobile-shell:not(.cockpit-mode) #view-switcher {
|
||||
|
||||
body.mobile-shell #corner-actions {
|
||||
top: calc(var(--mobile-top-inset) + 60px);
|
||||
right: 12px;
|
||||
inset-inline-end: 12px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
|
||||
+12
-80
@@ -124,7 +124,6 @@
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.hud-top-bar,
|
||||
.hud-bottom-bar {
|
||||
display: none;
|
||||
position: absolute;
|
||||
@@ -141,24 +140,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hud-top-bar {
|
||||
top: 70px;
|
||||
}
|
||||
|
||||
.hud-bottom-bar {
|
||||
bottom: 110px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hud-top-bar-center {
|
||||
opacity: 0.84;
|
||||
}
|
||||
|
||||
.hud-top-bar-left,
|
||||
.hud-top-bar-right {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Corner blocks */
|
||||
.hud-corner {
|
||||
position: absolute;
|
||||
@@ -298,54 +284,15 @@
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='tactical'] .hud-top-bar,
|
||||
#intel-hud[data-variant='tactical'] .hud-bottom-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='operator'] .hud-top-bar,
|
||||
#intel-hud[data-variant='operator'] .hud-bottom-bar {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='operator'] .hud-top-bar {
|
||||
top: max(10vh, 90px);
|
||||
left: 50%;
|
||||
right: auto;
|
||||
width: min(52vw, 720px);
|
||||
transform: translateX(-50%);
|
||||
border-color: rgba(255, 255, 255, 0.24);
|
||||
background: linear-gradient(90deg, rgba(0, 0, 0, 0.56), rgba(8, 26, 34, 0.5));
|
||||
box-shadow: 0 0 24px rgba(0, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='tactical'] .hud-top-left,
|
||||
#intel-hud[data-variant='tactical'] .hud-top-right,
|
||||
#intel-hud[data-variant='operator'] .hud-top-left,
|
||||
#intel-hud[data-variant='operator'] .hud-top-right {
|
||||
#intel-hud[data-variant='tactical'] .hud-top-right {
|
||||
top: max(14vh, 136px);
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='operator'] .hud-bottom-bar {
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 0, 0, 0.52),
|
||||
rgba(8, 22, 18, 0.52)
|
||||
);
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='operator'] .hud-corner {
|
||||
padding: 8px 10px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 0, 0, 0.36);
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='operator'] .hud-bracket {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='minimal'] .hud-top-right,
|
||||
#intel-hud[data-variant='minimal'] .hud-bottom-right,
|
||||
#intel-hud[data-variant='minimal'] .hud-left-edge,
|
||||
@@ -378,11 +325,7 @@
|
||||
}
|
||||
|
||||
:root[data-scope-mask='off']
|
||||
#intel-hud:is(
|
||||
[data-variant='minimal'],
|
||||
[data-variant='operator'],
|
||||
[data-variant='tactical']
|
||||
)
|
||||
#intel-hud:is([data-variant='minimal'], [data-variant='tactical'])
|
||||
.hud-readout,
|
||||
:root[data-scope-mask='off']:not([data-ui-theme='cyber']) .scene-text-holdout {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
@@ -399,11 +342,7 @@
|
||||
}
|
||||
|
||||
:root[data-scope-mask='off']
|
||||
#intel-hud:is(
|
||||
[data-variant='minimal'],
|
||||
[data-variant='operator'],
|
||||
[data-variant='tactical']
|
||||
)
|
||||
#intel-hud:is([data-variant='minimal'], [data-variant='tactical'])
|
||||
.hud-readout {
|
||||
display: block;
|
||||
width: max-content;
|
||||
@@ -413,11 +352,7 @@
|
||||
}
|
||||
|
||||
:root[data-scope-mask='off']
|
||||
#intel-hud:is(
|
||||
[data-variant='minimal'],
|
||||
[data-variant='operator'],
|
||||
[data-variant='tactical']
|
||||
)
|
||||
#intel-hud:is([data-variant='minimal'], [data-variant='tactical'])
|
||||
:is(.hud-top-right, .hud-bottom-right)
|
||||
.hud-readout,
|
||||
:root[data-scope-mask='off']:not([data-ui-theme='cyber'])
|
||||
@@ -427,38 +362,35 @@
|
||||
}
|
||||
|
||||
/* Preserve glyph opacity without dimming the vertical backplates. */
|
||||
:root[data-scope-mask='off']
|
||||
#intel-hud:is([data-variant='operator'], [data-variant='tactical'])
|
||||
.hud-edge {
|
||||
:root[data-scope-mask='off'] #intel-hud[data-variant='tactical'] .hud-edge {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:root[data-scope-mask='off']
|
||||
#intel-hud:is([data-variant='operator'], [data-variant='tactical'])
|
||||
#intel-hud[data-variant='tactical']
|
||||
.hud-edge
|
||||
.hud-readout
|
||||
> * {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
:root[data-scope-mask='off'] #intel-hud[data-variant='operator'] .hud-corner,
|
||||
:root[data-scope-mask='off']
|
||||
#intel-hud:is([data-variant='minimal'], [data-variant='operator'])
|
||||
:is(.hud-top-bar, .hud-bottom-bar) {
|
||||
#intel-hud[data-variant='minimal']
|
||||
.hud-bottom-bar {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:root[data-scope-mask='off']
|
||||
#intel-hud:is([data-variant='minimal'], [data-variant='operator'])
|
||||
:is(.hud-top-bar, .hud-bottom-bar) {
|
||||
#intel-hud[data-variant='minimal']
|
||||
.hud-bottom-bar {
|
||||
width: fit-content;
|
||||
max-width: calc(100vw - 120px);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
:root[data-scope-mask='off']
|
||||
#intel-hud:is([data-variant='minimal'], [data-variant='operator'])
|
||||
:is(.hud-top-bar, .hud-bottom-bar)
|
||||
#intel-hud[data-variant='minimal']
|
||||
.hud-bottom-bar
|
||||
.hud-readout {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--glass-border-hover) transparent;
|
||||
scrollbar-color: var(--scroll-thumb) var(--scroll-track);
|
||||
/* Keep an inset focus ring inside the intentional scrolling boundary. */
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@@ -184,19 +184,9 @@
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(54, 220, 255, 0.45) transparent;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.key-setup-rows::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.key-setup-rows::-webkit-scrollbar-thumb {
|
||||
background: rgba(54, 220, 255, 0.45);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.key-setup-row {
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
|
||||
+22
-13
@@ -18,8 +18,6 @@
|
||||
border-radius: 8px;
|
||||
background: rgba(2, 15, 20, 0.42);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.035);
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 212, 255, 0.46) rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
#radio-panel .panel-header {
|
||||
margin-bottom: 0;
|
||||
@@ -51,7 +49,8 @@
|
||||
#radio-filter {
|
||||
/* Radio owns its compact density and arrow space; shared fields paint the
|
||||
palette and disabled state, including this intentional theme exception. */
|
||||
padding: 7px 24px 7px 8px;
|
||||
padding-block: 7px;
|
||||
padding-inline: 8px 24px;
|
||||
border-radius: 5px;
|
||||
font: 9px/1.2 var(--font-mono);
|
||||
--gev-field-border-color: rgba(65, 220, 231, 0.28);
|
||||
@@ -461,14 +460,16 @@ html:not([lang='en']) .radio-transport button {
|
||||
border-top: 1px solid rgba(98, 181, 255, 0.16);
|
||||
}
|
||||
.military-awareness-standby strong {
|
||||
color: #79c2ff;
|
||||
color: var(--text-primary);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.1em;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.military-awareness-standby span {
|
||||
color: #8192a6;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.05em;
|
||||
color: rgba(214, 236, 255, 0.72);
|
||||
font-size: 10px;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.military-awareness-standby button {
|
||||
justify-self: start;
|
||||
@@ -688,8 +689,14 @@ html:not([lang='en']) .data-count {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/* The status column keeps its width, so a wide chip (WIRD AKTIVIERT) never
|
||||
overlaps the name; the name wraps in the room left beside it. */
|
||||
html:not([lang='en']) .data-toggle-right {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
/* Row text pieces that must not break inside: a hyphenated code (ADS-B) and
|
||||
the space before a " · " separator (see setRowText in layerPanel.js). */
|
||||
the space before a " · " separator (see rowTextRuns in layerRowText.js). */
|
||||
html:not([lang='en']) .data-keep {
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -1084,11 +1091,12 @@ html:not([lang='en']) .data-row-list-text {
|
||||
.sdr-radio-card select,
|
||||
.sdr-radio-card input[type='number'] {
|
||||
min-height: 28px;
|
||||
border: 1px solid rgba(255, 183, 82, 0.28);
|
||||
border-radius: 5px;
|
||||
color: #ffd79a;
|
||||
background: rgba(38, 25, 8, 0.52);
|
||||
font: 8px/1 var(--font-mono);
|
||||
/* The shared field paints the surface (and the select's chevron). */
|
||||
--gev-field-border-color: rgba(255, 183, 82, 0.28);
|
||||
--gev-field-color: #ffd79a;
|
||||
--gev-field-background: rgba(38, 25, 8, 0.52);
|
||||
}
|
||||
.sdr-radio-card button {
|
||||
padding: 6px 8px;
|
||||
@@ -1121,7 +1129,8 @@ html:not([lang='en']) .data-row-list-text {
|
||||
.sdr-gain-row select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 5px 7px;
|
||||
padding-block: 5px;
|
||||
padding-inline: 7px 22px;
|
||||
}
|
||||
.sdr-adsb-stats {
|
||||
display: grid;
|
||||
|
||||
@@ -46,8 +46,6 @@
|
||||
max-height: calc(var(--right-stack-max-height, 50svh) - 64px);
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 212, 255, 0.46) transparent;
|
||||
}
|
||||
#recent-imagery-panel.collapsed #recent-imagery-panel-body,
|
||||
#recent-imagery-panel.collapsed #recent-imagery-panel-count,
|
||||
@@ -56,10 +54,6 @@
|
||||
#recent-imagery-split-line[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
#recent-imagery-panel.collapsed .panel-title {
|
||||
font-size: 8px;
|
||||
letter-spacing: 1.8px;
|
||||
}
|
||||
#recent-imagery-panel.collapsed .panel-header {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
@@ -172,7 +166,6 @@
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scroll-snap-type: x proximity;
|
||||
scrollbar-width: thin;
|
||||
outline: none;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#title-bar {
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
inset-inline-start: 20px;
|
||||
}
|
||||
|
||||
#top-center-actions {
|
||||
@@ -55,7 +55,7 @@
|
||||
#title-bar .frame-rate-readout {
|
||||
position: fixed;
|
||||
top: 52px;
|
||||
left: 20px;
|
||||
inset-inline-start: 20px;
|
||||
margin: 0;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
@@ -98,25 +98,12 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hud-top-bar,
|
||||
.hud-bottom-bar {
|
||||
width: calc(100vw - 24px);
|
||||
left: 12px;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.hud-top-bar {
|
||||
top: 54px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
#intel-hud[data-variant='operator'] .hud-top-bar {
|
||||
left: 12px;
|
||||
right: auto;
|
||||
width: calc(100vw - 24px);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.hud-bottom-bar {
|
||||
bottom: 90px;
|
||||
font-size: 9px;
|
||||
@@ -205,7 +192,7 @@
|
||||
under the expanded panel plus a vertical scrollbar that scrolls nothing
|
||||
but glow. Pin the glow to the panel box here — its blur is ink overflow,
|
||||
which never scrolls — instead of clipping the rail, which would also clip
|
||||
legitimate content such as the Context radio popover. */
|
||||
legitimate content that extends past a panel's edge. */
|
||||
#left-panel-stack .panel-glow,
|
||||
#right-context-rail .panel-glow {
|
||||
inset: 0;
|
||||
@@ -228,10 +215,6 @@
|
||||
#right-context-rail #global-context-panel.collapsed {
|
||||
width: 100%;
|
||||
}
|
||||
.context-radio-mini {
|
||||
right: -38px;
|
||||
width: min(244px, calc(100vw - 32px));
|
||||
}
|
||||
|
||||
#left-panel-stack > #data-panel,
|
||||
#left-panel-stack > #cctv-panel,
|
||||
@@ -262,7 +245,7 @@
|
||||
/* 16px keeps the Radio broadcast waves (15px above the logo) on-screen. */
|
||||
#title-bar {
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
inset-inline-start: 16px;
|
||||
}
|
||||
|
||||
#title-bar h1 > span:last-child,
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/* Right-to-left languages (Arabic). The chrome mirrors through logical
|
||||
properties in the component sheets (inset-inline-*, margin-inline-*); this
|
||||
sheet holds only what logical properties cannot express. */
|
||||
|
||||
/* Arabic letters join: letter-spacing tears every word into separate glyphs.
|
||||
Latin marked translate="no" and the intel HUD's Latin telemetry keep their
|
||||
tracking; the HUD's translated lines are released below. Unlike cjk.css, a
|
||||
container that merely holds a translate="no" span is not spared: its own
|
||||
Arabic text would come apart. */
|
||||
html:lang(ar)
|
||||
body
|
||||
:not(#intel-hud, #intel-hud *, [translate='no'], [translate='no'] *) {
|
||||
letter-spacing: 0 !important;
|
||||
}
|
||||
|
||||
/* The intel HUD is an instrument overlay: corners, brackets, coordinates and
|
||||
sensor codes keep the English geometry. Its translated lines still read
|
||||
right to left inside that frame. */
|
||||
html[dir='rtl'] #intel-hud {
|
||||
direction: ltr;
|
||||
}
|
||||
html[dir='rtl'] #intel-hud :is(.hud-mode, .hud-summary-label, .hud-summary) {
|
||||
unicode-bidi: plaintext;
|
||||
text-align: left;
|
||||
letter-spacing: 0 !important;
|
||||
}
|
||||
|
||||
/* Font stacks: JetBrains Mono and Inter have no Arabic glyphs, and the
|
||||
generic monospace fallback is a fixed-pitch Arabic face that spaces every
|
||||
letter apart. Noto Sans Arabic (loaded with the other Google fonts; its
|
||||
files download only when the page shows Arabic) leads the mono stack, so
|
||||
the spaces between Arabic words come from it too and not from the
|
||||
fixed-pitch Latin face. The intel and cockpit HUDs keep JetBrains Mono for
|
||||
their Latin telemetry and column-aligned digits. */
|
||||
html:lang(ar) {
|
||||
--font-mono:
|
||||
'Noto Sans Arabic', 'JetBrains Mono', 'SF Mono', 'Fira Code', 'Geeza Pro',
|
||||
Tahoma, monospace;
|
||||
--font-sans:
|
||||
'Inter', 'Noto Sans Arabic', -apple-system, BlinkMacSystemFont, 'Geeza Pro',
|
||||
Tahoma, sans-serif;
|
||||
}
|
||||
/* The Global Context lines and their buttons set ui-monospace through the
|
||||
`font` shorthand; route them through the Arabic-aware stack too. */
|
||||
html:lang(ar)
|
||||
:is(
|
||||
#military-awareness-panel,
|
||||
.military-awareness-controls button,
|
||||
.military-awareness-standby button,
|
||||
.military-awareness-compass-label,
|
||||
.military-awareness-compass-heading,
|
||||
.military-awareness-direction-label,
|
||||
.tr3b-toggle
|
||||
) {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
html:lang(ar) :is(#intel-hud, #cockpit-hud) {
|
||||
--font-mono:
|
||||
'JetBrains Mono', 'SF Mono', 'Fira Code', 'Noto Sans Arabic', 'Geeza Pro',
|
||||
Tahoma, monospace;
|
||||
}
|
||||
|
||||
/* The Display/Context rail moves to the left edge. It keeps a physical
|
||||
`right` in layers.css because the attribution-clearance model
|
||||
(creditAttribution.test.mjs) reads that rule; the swap only applies above
|
||||
the 720px band, where the rail is not edge-to-edge. */
|
||||
@media (min-width: 721px) {
|
||||
html[dir='rtl'] #right-context-rail {
|
||||
right: auto;
|
||||
left: var(--right-rail-x);
|
||||
}
|
||||
}
|
||||
@media (min-width: 961px) and (min-height: 650px) {
|
||||
html[dir='rtl'][data-ui-theme='cyber'] #right-context-rail {
|
||||
right: auto;
|
||||
left: calc(51px + var(--panel-rail-clearance));
|
||||
}
|
||||
}
|
||||
|
||||
/* The dock's wings swap sides (Location on the right, Visual presets on the
|
||||
left), so each slide-up tray is anchored from the wing's outer edge and
|
||||
centred on the whole dock from there, as it is in left-to-right. */
|
||||
html[dir='rtl'] #command-dock #location-bar .dock-popover-content {
|
||||
right: calc((var(--dock-unified-width) - var(--dock-location-open)) / 2);
|
||||
left: auto;
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
html[dir='rtl'] #command-dock #control-panel .dock-popover-content {
|
||||
right: auto;
|
||||
left: calc((var(--dock-unified-width) - var(--dock-presets-open)) / 2);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
html[dir='rtl'] #command-dock #location-bar .dock-popover-content {
|
||||
right: calc(
|
||||
(var(--dock-unified-width) - var(--dock-popover-mobile-width)) / 2
|
||||
);
|
||||
}
|
||||
html[dir='rtl'] #command-dock #control-panel .dock-popover-content {
|
||||
left: calc(
|
||||
(var(--dock-unified-width) - var(--dock-popover-mobile-width)) / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
/* The tray pin sits on the tray's top end corner: top-left here. */
|
||||
html[dir='rtl'] #command-dock :is(#location-bar, #control-panel) .dock-pin-btn {
|
||||
right: auto;
|
||||
left: -1rem;
|
||||
}
|
||||
|
||||
/* The right rail sits on the left: its expand/collapse arrows point the
|
||||
other way, and the start window's "go" arrows point along the reading
|
||||
direction. */
|
||||
html[dir='rtl'] #right-context-rail .panel-collapse-btn,
|
||||
html[dir='rtl'] .first-run-arrow {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
html[dir='rtl'] .first-run-choices button:hover .first-run-arrow,
|
||||
html[dir='rtl'] .first-run-choices button:focus-visible .first-run-arrow {
|
||||
transform: translateX(-2px) scaleX(-1);
|
||||
}
|
||||
|
||||
/* The cockpit is an aircraft panel, not page chrome: its windows keep their
|
||||
physical sides, so the data-layer stack goes back to the left edge instead
|
||||
of landing on the utility controls. Gauges, tapes and the roll counters stay
|
||||
left to right (speed left, altitude right, -10 not 10-); the cards keep
|
||||
reading right to left. */
|
||||
html[dir='rtl'] body.cockpit-mode #left-panel-stack {
|
||||
left: var(--left-stack-x);
|
||||
right: auto;
|
||||
}
|
||||
html[dir='rtl']
|
||||
#cockpit-hud
|
||||
:is(
|
||||
.cockpit-instrument-row,
|
||||
.cockpit-compass,
|
||||
.cockpit-pitch-rail,
|
||||
.cockpit-altitude-rim,
|
||||
.cockpit-roll-arc,
|
||||
.cockpit-vision-controls
|
||||
) {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* Feed text (news headlines, station names) arrives in its own language:
|
||||
each line takes its direction from its first letter, so an English headline
|
||||
reads and aligns left to right instead of moving its punctuation. */
|
||||
html[dir='rtl']
|
||||
:is(
|
||||
.cockpit-news-list li *,
|
||||
#radio-station-name,
|
||||
#radio-station-tags,
|
||||
.cockpit-radio-station
|
||||
) {
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
|
||||
/* Clock readouts (~0:05 / 5:19) are numbers, not prose. */
|
||||
html[dir='rtl'] #bhote-koshi-event-panel [data-role='time'] {
|
||||
direction: ltr;
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
/* Physical controls keep their left-to-right sense: FM seek down/up and the
|
||||
radio dial. The worded PREV / PLAY / NEXT row follows the reading
|
||||
direction like any other button row. */
|
||||
html[dir='rtl'] :is(.sdr-seek-row, .radio-tuner) {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* Canvas labels (installations, map cards, detections) are measured and drawn
|
||||
with left-to-right geometry: a card's text starts at its left padding. A
|
||||
canvas inherits the page direction, which turned that start into the right
|
||||
edge and pushed every title out of its card. */
|
||||
html[dir='rtl'] canvas {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* Scene callouts on the screen whiteboard: the card grows to the right of the
|
||||
text's x, so right-to-left text ends there instead of starting there. The
|
||||
injected stack (JetBrains Mono) has no Arabic glyphs; use the Arabic-aware
|
||||
one. */
|
||||
html[dir='rtl'] .gev-anno-text {
|
||||
text-anchor: end;
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
/* Position counters built from bare numbers (01 / 25, 1 / 3) read as a number
|
||||
pair, left to right, as an unspaced 1/3 does; otherwise the slash swaps
|
||||
them and the current item looks like the total. Translated counters say
|
||||
«من» instead. */
|
||||
html[dir='rtl']
|
||||
:is(
|
||||
#bhote-koshi-event-panel [data-role='beat-index'],
|
||||
#cockpit-brief-position,
|
||||
[data-mission-index]
|
||||
) {
|
||||
direction: ltr;
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
/* The radio tuner header is a left-to-right instrument row; its channel
|
||||
readout («القناة 01 من 750») still reads in its own language. */
|
||||
html[dir='rtl'] #radio-tuner-value {
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
|
||||
/* The select chevron sits at the end of the field, where the text stops
|
||||
(form-field.css pads that side through padding-inline-end). */
|
||||
html[dir='rtl'] select.gev-field {
|
||||
background-position: left 8px center;
|
||||
}
|
||||
html[dir='rtl'] select.gev-field--compact {
|
||||
background-position: left 6px center;
|
||||
}
|
||||
|
||||
/* A legend's range is printed under its colour ramp, which always runs low
|
||||
to high from left to right; the numbers keep that order. */
|
||||
html[dir='rtl'] .rail-card-scale {
|
||||
direction: ltr;
|
||||
}
|
||||
+122
-41
@@ -1,6 +1,8 @@
|
||||
/* ── Scene Director Panel ───────────────────── */
|
||||
#scene-panel {
|
||||
--panel-expanded-width: 360px;
|
||||
/* Secondary text in Authoring: dimmer than labels, but legible on the globe. */
|
||||
--scene-quiet: rgba(232, 234, 237, 0.62);
|
||||
position: fixed;
|
||||
top: calc(var(--left-stack-top) + (var(--left-stack-gap) * 2));
|
||||
left: var(--left-stack-x);
|
||||
@@ -150,15 +152,15 @@
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* The scene's name takes its own row; NEW SCENE / DELETE share the next. */
|
||||
#scene-select {
|
||||
flex: 1;
|
||||
flex: 1 1 100%;
|
||||
min-width: 0;
|
||||
min-height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* A translated scene name needs the width: the select takes its own row,
|
||||
NEW / DEL share the next one. */
|
||||
html:not([lang='en']) #scene-select {
|
||||
flex: 1 1 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -168,8 +170,7 @@ html:not([lang='en']) #scene-select {
|
||||
}
|
||||
|
||||
/* A translated action longer than its button wraps inside the button instead
|
||||
of running past it. English keeps its one-line buttons ("EXPORT PRESETS"
|
||||
included). */
|
||||
of running past it. English keeps its one-line buttons. */
|
||||
html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
white-space: normal;
|
||||
overflow-wrap: break-word;
|
||||
@@ -180,6 +181,70 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
min-width: 72px;
|
||||
}
|
||||
|
||||
#scene-panel .scene-btn {
|
||||
font-size: 10px;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
/* Authoring's steps: SCENE, SHOTS, PREVIEW, SAVE & SHARE. */
|
||||
.scene-step-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 10px 0 6px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
letter-spacing: 1.6px;
|
||||
color: var(--scene-quiet);
|
||||
}
|
||||
|
||||
.scene-step-heading::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.panel-header + .scene-step-heading {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* One primary action per step: CAPTURE SHOT and PLAY. The panel id keeps
|
||||
this above Cyber's shared button surface. */
|
||||
#scene-panel .scene-btn-primary:not(:disabled) {
|
||||
color: var(--accent);
|
||||
border-color: color-mix(in srgb, var(--accent) 55%, transparent);
|
||||
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
||||
}
|
||||
|
||||
/* The file row is housekeeping; it stays quieter than the steps above. */
|
||||
#scene-panel .scene-file-controls .scene-btn {
|
||||
font-size: 9px;
|
||||
/* Room for one long translated word (IMPORTIEREN, ІМПОРТУВАТИ) per line. */
|
||||
padding: 8px 4px;
|
||||
letter-spacing: 0.6px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Keep the shared hover on the restyled actions. */
|
||||
#scene-panel .scene-btn-primary:hover:not(:disabled),
|
||||
#scene-panel .scene-file-controls .scene-btn:hover:not(:disabled) {
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
background: color-mix(in srgb, var(--accent) 16%, transparent);
|
||||
}
|
||||
|
||||
.scene-shot-hint {
|
||||
margin: 0 0 8px;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
color: var(--scene-quiet);
|
||||
}
|
||||
|
||||
#scene-panel:not(.has-shots) .scene-shot-hint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#scene-start-btn,
|
||||
#scene-stop-btn,
|
||||
#scene-next-btn {
|
||||
@@ -190,39 +255,34 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
max-height: 160px;
|
||||
min-height: 96px;
|
||||
max-height: 260px;
|
||||
/* Two shot rows stay visible; the panel scrolls when space is short. */
|
||||
min-height: 128px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 4px 2px 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.scene-shot-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.scene-shot-list::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
border-radius: 999px;
|
||||
margin-bottom: 4px;
|
||||
counter-reset: scene-shot;
|
||||
}
|
||||
|
||||
.scene-shot-empty {
|
||||
border: 1px dashed rgba(255, 255, 255, 0.14);
|
||||
border-radius: 8px;
|
||||
padding: 10px 8px;
|
||||
padding: 16px 12px;
|
||||
text-align: center;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 9px;
|
||||
letter-spacing: 1px;
|
||||
color: var(--text-dim);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.3px;
|
||||
line-height: 1.5;
|
||||
color: var(--scene-quiet);
|
||||
}
|
||||
|
||||
.scene-shot-row {
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 9px;
|
||||
padding: 7px 8px;
|
||||
padding: 8px 10px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
counter-increment: scene-shot;
|
||||
transition:
|
||||
border-color var(--transition-fast),
|
||||
background var(--transition-fast);
|
||||
@@ -243,12 +303,38 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
.scene-shot-label {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
letter-spacing: 1px;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.4px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* While renaming, the label holds only the editor at full width. */
|
||||
.scene-shot-label:has(.scene-shot-rename) {
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.scene-shot-label:has(.scene-shot-rename)::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
/* Shots are numbered 01, 02… in playback order. */
|
||||
.scene-shot-label::before {
|
||||
content: counter(scene-shot, decimal-leading-zero);
|
||||
margin-right: 8px;
|
||||
font-size: 10px;
|
||||
color: var(--scene-quiet);
|
||||
}
|
||||
|
||||
.scene-shot-row.active .scene-shot-label::before {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.scene-shot-rename {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
@@ -260,11 +346,11 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
}
|
||||
|
||||
.scene-shot-meta {
|
||||
margin-top: 4px;
|
||||
margin-top: 5px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 8px;
|
||||
letter-spacing: 0.8px;
|
||||
color: var(--text-dim);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.4px;
|
||||
color: var(--scene-quiet);
|
||||
}
|
||||
|
||||
.scene-shot-actions {
|
||||
@@ -275,7 +361,7 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
.scene-progress {
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
height: 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
@@ -305,9 +391,10 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
#scene-status {
|
||||
margin-top: 8px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
letter-spacing: 1px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.4px;
|
||||
line-height: 1.4;
|
||||
color: var(--scene-quiet);
|
||||
}
|
||||
|
||||
#scene-runtime {
|
||||
@@ -357,6 +444,8 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
#cctv-panel.collapsed .cctv-summary-label,
|
||||
#cctv-panel.collapsed #cctv-summary,
|
||||
#scene-panel.collapsed .scene-controls,
|
||||
#scene-panel.collapsed .scene-step-heading,
|
||||
#scene-panel.collapsed .scene-shot-hint,
|
||||
#scene-panel.collapsed .scene-shot-list,
|
||||
#scene-panel.collapsed .scene-progress,
|
||||
#scene-panel.collapsed #scene-status,
|
||||
@@ -378,14 +467,6 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#data-panel.collapsed .panel-title,
|
||||
#cctv-panel.collapsed .panel-title,
|
||||
#radio-panel.collapsed .panel-title,
|
||||
#scene-panel.collapsed .panel-title {
|
||||
font-size: 8px;
|
||||
letter-spacing: 1.8px;
|
||||
}
|
||||
|
||||
#control-panel.collapsed .panel-inner,
|
||||
#data-panel.collapsed .data-panel-inner,
|
||||
#cctv-panel.collapsed .cctv-panel-inner,
|
||||
|
||||
@@ -243,20 +243,20 @@ html:not([lang='en']) #global-loading-detail {
|
||||
|
||||
/* ── Split-flap chip text ─────────────────────────────────────────
|
||||
Per-character departure-board flip when a chip label changes.
|
||||
Driven entirely by src/splitFlap.js, which rebuilds the label as
|
||||
one span per character on the change and strips them again once
|
||||
the cascade lands.
|
||||
Planned by src/splitFlap.js and rendered by SplitFlapText.vue, which
|
||||
lays out one cell per character on the change and strips them again
|
||||
once the cascade lands.
|
||||
|
||||
The DOM text is the truth and its node never moves. A chip label is
|
||||
upgraded once into a permanent shell — a .gev-flap-text span holding
|
||||
one long-lived Text node, plus an aria-hidden .gev-flap-cells
|
||||
sibling — and from then on a label change is only `node.data = next`.
|
||||
a .gev-flap-text span holding one long-lived Text node, plus an
|
||||
aria-hidden .gev-flap-cells sibling, and a label change is only
|
||||
`node.data = next`.
|
||||
During a cascade the host takes .gev-flap-active, which clips the
|
||||
real text out of view and lets the cells stand in for it. The cells
|
||||
carry no text at all: both glyphs are generated content (::before =
|
||||
outgoing, ::after = incoming), and generated content never reaches
|
||||
textContent. That is what keeps QA pins and the traffic chip's own
|
||||
`textContent !==` guard honest, and it lets the cells be rebuilt
|
||||
textContent. That is what keeps QA pins honest, and it lets the
|
||||
cells be rebuilt
|
||||
freely while the aria-live region sees only one characterData
|
||||
change per label update.
|
||||
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
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;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 212, 255, 0.46) rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
/* Class-qualified, not id-qualified, so a theme's header rule still wins. */
|
||||
@@ -236,17 +234,10 @@ html:not([lang='en']) .sl-meta-row .sl-nearest-btn {
|
||||
gap: 4px 10px;
|
||||
}
|
||||
|
||||
/* The shared slider surface, in Street Level's green. */
|
||||
.sl-range {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
accent-color: var(--sl-green);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sl-range:focus-visible {
|
||||
outline: 1px solid var(--accent);
|
||||
outline-offset: 3px;
|
||||
--accent: var(--sl-green);
|
||||
--accent-glow: color-mix(in srgb, var(--sl-green) 55%, transparent);
|
||||
}
|
||||
|
||||
/* The value and its note share a line: ANY DATE (by upload date). */
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
0 0 1.2rem rgba(0, 212, 255, 0.08);
|
||||
backdrop-filter: blur(24px) saturate(1.3);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(1.3);
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
transform: translateX(-50%);
|
||||
transform-origin: bottom center;
|
||||
animation: gev-voice-card-in 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
@@ -95,7 +95,7 @@
|
||||
}
|
||||
|
||||
.gev-voice-card-dismiss {
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
padding: 0;
|
||||
@@ -288,7 +288,7 @@
|
||||
content: counter(gev-referent);
|
||||
display: inline-block;
|
||||
min-width: 0.9rem;
|
||||
margin-right: 0.3rem;
|
||||
margin-inline-end: 0.3rem;
|
||||
color: rgba(54, 220, 255, 0.72);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.52rem;
|
||||
@@ -415,7 +415,7 @@ body.recording-mode .gev-pointer-reticle {
|
||||
-webkit-backdrop-filter: blur(24px) saturate(1.3);
|
||||
color-scheme: dark;
|
||||
font: 0.75rem/1.45 var(--font-sans);
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
:root[data-ui-theme='cyber'] #gev-voice-control .gev-voice-panel {
|
||||
@@ -443,7 +443,7 @@ body.recording-mode .gev-pointer-reticle {
|
||||
letter-spacing: 0.14em;
|
||||
}
|
||||
.gev-voice-panel-tabs {
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
#gev-voice-control .gev-voice-panel-header .gev-voice-panel-tab {
|
||||
display: inline-flex;
|
||||
@@ -467,7 +467,8 @@ body.recording-mode .gev-pointer-reticle {
|
||||
min-height: 2rem;
|
||||
box-sizing: border-box;
|
||||
color: var(--text-primary);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
/* background-color, so a .gev-field select keeps its chevron. */
|
||||
background-color: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 0.3rem;
|
||||
font: inherit;
|
||||
@@ -504,7 +505,6 @@ body.recording-mode .gev-pointer-reticle {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
padding: 0.55rem 0.75rem 3px 3px;
|
||||
line-height: 1.45;
|
||||
overflow-wrap: anywhere;
|
||||
@@ -553,7 +553,8 @@ body.recording-mode .gev-pointer-reticle {
|
||||
#gev-voice-timeout-select {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
padding: 0 0.45rem;
|
||||
padding-block: 0;
|
||||
padding-inline: 0.45rem 22px;
|
||||
}
|
||||
#gev-voice-timeout-input {
|
||||
flex: 0 0 4.5rem;
|
||||
|
||||
@@ -180,12 +180,11 @@ html:not([lang='en'])
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
min-height: 30px;
|
||||
color: var(--text-primary);
|
||||
background: #09121b;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 4px;
|
||||
font: inherit;
|
||||
font-size: 0.65rem;
|
||||
--gev-field-background: #09121b;
|
||||
--gev-field-border-color: var(--glass-border);
|
||||
}
|
||||
#gev-voice-control:hover .gev-voice-provider-field,
|
||||
#gev-voice-control:focus-within .gev-voice-provider-field {
|
||||
|
||||
@@ -42,18 +42,12 @@
|
||||
max-height: calc(var(--right-stack-max-height, 50svh) - 64px);
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 212, 255, 0.46) transparent;
|
||||
}
|
||||
#weather-panel.collapsed #weather-panel-body,
|
||||
#weather-panel[hidden],
|
||||
.weather-readout[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
#weather-panel.collapsed .panel-title {
|
||||
font-size: 8px;
|
||||
letter-spacing: 1.8px;
|
||||
}
|
||||
#weather-panel.collapsed .panel-header {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user