mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
567 lines
16 KiB
CSS
567 lines
16 KiB
CSS
/* ------------------------------------------------------------------ *
|
|
* Voice cost control — model-tier toggle + running session estimate.
|
|
* Appended (not woven into the dock passes above) so the three existing
|
|
* #gev-voice-control cascades stay untouched; these rules only add the
|
|
* new .gev-voice-cost cluster inside the existing .gev-voice-heading row.
|
|
* ------------------------------------------------------------------ */
|
|
.gev-voice-cost {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.22rem;
|
|
margin-left: auto; /* pin right; kicker + status keep their left cluster */
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.gev-voice-tier-btn {
|
|
padding: 0 0.24rem;
|
|
height: 0.72rem;
|
|
min-width: 1.55rem;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: rgba(0, 212, 255, 0.06);
|
|
border: 1px solid rgba(0, 212, 255, 0.34);
|
|
border-radius: 0.18rem;
|
|
color: var(--text-secondary);
|
|
font-family: var(--font-mono);
|
|
font-size: 0.4rem;
|
|
letter-spacing: 0.045rem;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
transition:
|
|
color var(--transition-fast),
|
|
border-color var(--transition-fast),
|
|
background var(--transition-fast);
|
|
}
|
|
|
|
.gev-voice-tier-btn:hover {
|
|
color: var(--text-primary);
|
|
border-color: rgba(0, 212, 255, 0.6);
|
|
background: rgba(0, 212, 255, 0.12);
|
|
}
|
|
|
|
/* MINI engaged — amber, distinct from the cyan default so the cheaper tier
|
|
is legible at a glance without reading the label. */
|
|
.gev-voice-tier-btn[aria-pressed='true'] {
|
|
color: #ffc46b;
|
|
border-color: rgba(255, 196, 107, 0.55);
|
|
background: rgba(255, 196, 107, 0.12);
|
|
}
|
|
|
|
.gev-voice-cost-value {
|
|
color: var(--text-dim);
|
|
font-family: var(--font-mono);
|
|
font-size: 0.4rem;
|
|
letter-spacing: 0.02rem;
|
|
line-height: 1;
|
|
font-variant-numeric: tabular-nums;
|
|
transition: color var(--transition-fast);
|
|
}
|
|
|
|
.gev-voice-cost-value[data-level='warn'] {
|
|
color: #ffc46b;
|
|
}
|
|
|
|
.gev-voice-cost-warning {
|
|
margin-left: auto;
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.gev-voice-cost-warning[data-level='unknown'] {
|
|
color: #ffc46b;
|
|
}
|
|
|
|
.gev-voice-cost-value[data-level='cap'] {
|
|
color: #ff6b6b;
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* The dock heading row is 0.8rem tall; keep the cluster inside it. */
|
|
#command-dock .gev-voice-cost {
|
|
max-height: 0.8rem;
|
|
}
|
|
|
|
/* The dock collapses the kicker/readout when a tray is open — the cost
|
|
cluster follows the same rule so the row never overflows. */
|
|
#command-dock.tray-open .gev-voice-cost {
|
|
display: none;
|
|
}
|
|
|
|
.gev-voice-tier-btn[hidden],
|
|
.gev-voice-cost-value[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
/* The heading row is only as wide as the voice column: the cost cluster
|
|
keeps its size and a long state (ПОДКЛЮЧЕНИЕ, CONECTANDO) ends in an
|
|
ellipsis. Dropping the kicker returns its gap to the state label; the
|
|
negative margin reaches over the gutter beside the mic ring, which has
|
|
curved away at heading height. */
|
|
html:not([lang='en']) #command-dock .gev-voice-heading {
|
|
gap: 0.2rem;
|
|
margin-left: -0.6rem;
|
|
}
|
|
|
|
html:not([lang='en']) #command-dock .gev-voice-heading > .gev-voice-kicker {
|
|
display: none;
|
|
}
|
|
|
|
html:not([lang='en']) #command-dock .gev-voice-heading > #gev-voice-status {
|
|
flex: 0 1 auto;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
letter-spacing: 0;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
html:not([lang='en']) #command-dock .gev-voice-cost {
|
|
gap: 0.16rem;
|
|
padding-left: 0.1rem;
|
|
}
|
|
|
|
html:not([lang='en']) #command-dock .gev-voice-heading .gev-voice-tier-btn {
|
|
min-width: 0;
|
|
padding: 0 0.2rem;
|
|
}
|
|
|
|
html:not([lang='en']) #command-dock .gev-voice-cost-value {
|
|
letter-spacing: 0;
|
|
}
|
|
|
|
/* Live hints and the tier switch ("СТАНДАРТ — со следующей сессии") run
|
|
longer: a second line from the spare space under the readout, with no
|
|
tracking so the tier line does not need a third. */
|
|
html:not([lang='en'])
|
|
#command-dock
|
|
#gev-voice-control:is(
|
|
[data-status='connecting'],
|
|
[data-status='listening'],
|
|
[data-status='executing']
|
|
)
|
|
#gev-voice-detail {
|
|
display: -webkit-box;
|
|
-webkit-box-orient: vertical;
|
|
-webkit-line-clamp: 2;
|
|
line-clamp: 2;
|
|
line-height: 1.15;
|
|
letter-spacing: 0;
|
|
white-space: normal;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
#gev-voice-control[data-provider-choice='true'] {
|
|
--gev-provider-lift: 0px;
|
|
}
|
|
|
|
/* Provider choice belongs to the existing voice control, not a separate panel. */
|
|
.gev-voice-provider-field {
|
|
position: absolute;
|
|
bottom: calc(100% + var(--gev-provider-lift, 0px));
|
|
z-index: 9;
|
|
left: 0;
|
|
right: 0;
|
|
display: grid;
|
|
gap: 4px;
|
|
padding: 8px 6px;
|
|
border: 1px solid var(--glass-border);
|
|
border-radius: 8px;
|
|
background: var(--bg-panel, #09121b);
|
|
color: var(--text-secondary);
|
|
font-family: var(--font-mono);
|
|
font-size: 0.5rem;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
.gev-voice-provider-field[hidden] {
|
|
display: none;
|
|
}
|
|
.gev-voice-provider-field select {
|
|
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-voice-control:hover .gev-voice-provider-field,
|
|
#gev-voice-control:focus-within .gev-voice-provider-field {
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
#command-dock
|
|
#gev-voice-control[data-provider-choice='true']
|
|
.gev-voice-help-tray,
|
|
#command-dock #gev-voice-control[data-provider-choice='true'] .gev-voice-card,
|
|
#command-dock
|
|
#gev-voice-control[data-provider-choice='true']
|
|
.gev-voice-error-tray {
|
|
bottom: calc(100% + 4.8rem + var(--gev-provider-lift));
|
|
}
|
|
#command-dock.dock-has-pinned-tray
|
|
#gev-voice-control[data-provider-choice='true'] {
|
|
--gev-provider-lift: calc(1.7rem + var(--dock-pinned-stack-height));
|
|
}
|
|
/* Pinned trays separate the chooser from its hover owner. Keep it reachable
|
|
across that gap without requiring keyboard focus to hold it open. */
|
|
#command-dock.dock-has-pinned-tray .gev-voice-provider-field {
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
@media (max-width: 900px) {
|
|
/* The attribution clearance also creates a gap for mouse users in a
|
|
narrow window, so the raised chooser must not depend on root hover. */
|
|
.gev-voice-provider-field {
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
#command-dock #gev-voice-control[data-provider-choice='true'] {
|
|
--gev-provider-lift: 3rem;
|
|
}
|
|
#command-dock.dock-has-pinned-tray
|
|
#gev-voice-control[data-provider-choice='true'] {
|
|
--gev-provider-lift: calc(3.5rem + var(--dock-pinned-stack-height));
|
|
}
|
|
}
|
|
@media (max-width: 720px) {
|
|
#command-dock #gev-voice-control[data-provider-choice='true'] {
|
|
--gev-provider-lift: calc(2vh + 2.5rem);
|
|
}
|
|
#command-dock.dock-has-pinned-tray
|
|
#gev-voice-control[data-provider-choice='true'] {
|
|
--gev-provider-lift: calc(2vh + 3rem + var(--dock-pinned-stack-height));
|
|
}
|
|
}
|
|
@media (hover: none) {
|
|
.gev-voice-provider-field {
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
}
|
|
|
|
/* The opt-in provider and gear share a discoverable row at the existing anchor. */
|
|
#gev-voice-control:has(> .gev-voice-settings-button) {
|
|
--gev-voice-settings-gap: 0.75rem;
|
|
--gev-voice-tray-clearance: 0px;
|
|
}
|
|
#gev-voice-control:has(> .gev-voice-settings-button)
|
|
.gev-voice-provider-field:not([hidden]) {
|
|
bottom: calc(
|
|
100% + var(--gev-provider-lift, 0px) + var(--gev-voice-settings-gap)
|
|
);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 6px 46px 6px 6px;
|
|
border-color: rgba(123, 189, 211, 0.28);
|
|
background: rgba(9, 18, 27, 0.88);
|
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.46);
|
|
color-scheme: dark;
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
#gev-voice-control:has(> .gev-voice-settings-button)
|
|
.gev-voice-provider-field
|
|
> span {
|
|
position: absolute;
|
|
width: 1px;
|
|
height: 1px;
|
|
overflow: hidden;
|
|
clip-path: inset(50%);
|
|
white-space: nowrap;
|
|
}
|
|
#gev-voice-control:has(> .gev-voice-settings-button)
|
|
.gev-voice-provider-field
|
|
select {
|
|
min-height: 34px;
|
|
font-size: 0.7rem;
|
|
}
|
|
#gev-voice-control .gev-voice-settings-button {
|
|
position: absolute;
|
|
top: 1rem;
|
|
right: 0.25rem;
|
|
z-index: 11;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 34px;
|
|
height: 34px;
|
|
min-width: 34px;
|
|
min-height: 34px;
|
|
padding: 0;
|
|
color: rgba(190, 221, 230, 0.72);
|
|
background: rgba(9, 18, 27, 0.92);
|
|
border: 1px solid var(--glass-border);
|
|
border-radius: 0.3rem;
|
|
font-size: 18px;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
}
|
|
#gev-voice-control[data-provider-choice='true']:has(
|
|
.gev-voice-provider-field:not([hidden])
|
|
)
|
|
.gev-voice-settings-button {
|
|
top: auto;
|
|
right: 7px;
|
|
bottom: calc(
|
|
100% + var(--gev-provider-lift, 0px) + var(--gev-voice-settings-gap) + 7px
|
|
);
|
|
}
|
|
#gev-voice-control
|
|
.gev-voice-settings-button:is(:hover, [aria-expanded='true']) {
|
|
color: var(--voice-panel-accent, var(--accent));
|
|
border-color: currentColor;
|
|
background: rgba(0, 212, 255, 0.12);
|
|
}
|
|
#gev-voice-control .gev-voice-settings-button:focus-visible {
|
|
outline: 2px solid var(--voice-panel-accent, var(--accent));
|
|
outline-offset: 2px;
|
|
}
|
|
:root[data-ui-theme='cyber']
|
|
#gev-voice-control:has(> .gev-voice-settings-button) {
|
|
--voice-panel-accent: var(--cyber-red-bright);
|
|
}
|
|
:root[data-ui-theme='cyber']
|
|
#gev-voice-control:has(> .gev-voice-settings-button)
|
|
.gev-voice-provider-field {
|
|
background: var(--cyber-panel-surface);
|
|
border-color: var(--glass-border);
|
|
}
|
|
@media (pointer: coarse) {
|
|
#gev-voice-control .gev-voice-settings-button {
|
|
width: 44px;
|
|
height: 44px;
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
}
|
|
#gev-voice-control:has(> .gev-voice-settings-button)
|
|
.gev-voice-provider-field:not([hidden]) {
|
|
padding-right: 56px;
|
|
}
|
|
#gev-voice-control:has(> .gev-voice-settings-button)
|
|
.gev-voice-provider-field
|
|
select {
|
|
min-height: 44px;
|
|
}
|
|
}
|
|
|
|
/* Keep the row and popup on the same lift, with a small gap above pinned
|
|
trays. On very short screens the shared lift still yields to the viewport. */
|
|
#command-dock.dock-has-pinned-tray
|
|
#gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
) {
|
|
--gev-voice-settings-gap: 0.375rem;
|
|
--gev-provider-lift: min(
|
|
calc(var(--dock-pinned-stack-height) + var(--gev-voice-tray-clearance)),
|
|
max(0px, calc(100dvh - 20rem))
|
|
);
|
|
}
|
|
|
|
/* Match the attribution-aware tray offsets without moving the trays. The
|
|
popup can scroll before the shared viewport budget caps its lift. */
|
|
@media (max-width: 900px) {
|
|
#gev-voice-control:has(> .gev-voice-settings-button) {
|
|
--gev-voice-tray-clearance: calc(3rem - 1.2rem);
|
|
}
|
|
}
|
|
@media (max-width: 720px) {
|
|
#gev-voice-control:has(> .gev-voice-settings-button) {
|
|
--gev-voice-tray-clearance: calc(2vh + 2.5rem - 1.2rem);
|
|
}
|
|
}
|
|
|
|
/* Short touch landscapes have room beside the dock, not above its pinned
|
|
stack. Keep the same chooser and gear together in that empty lower gutter;
|
|
the open popup remains a bounded, scrollable overlay above the microphone. */
|
|
@media (pointer: coarse) and (orientation: landscape) and (min-width: 480px) and (max-height: 500px) {
|
|
#command-dock
|
|
> #gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
),
|
|
#command-dock.dock-has-pinned-tray
|
|
> #gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
) {
|
|
--gev-provider-lift: 0px;
|
|
--gev-landscape-row-width: clamp(
|
|
108px,
|
|
calc((100vw - var(--dock-unified-width)) / 2 - 0.875rem),
|
|
calc(100% - 2px)
|
|
);
|
|
}
|
|
#command-dock
|
|
> #gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
)
|
|
> .gev-voice-provider-field:not([hidden]) {
|
|
left: calc(50% - 50vw + 0.5rem);
|
|
right: auto;
|
|
bottom: 0.375rem;
|
|
width: var(--gev-landscape-row-width);
|
|
box-sizing: border-box;
|
|
}
|
|
#command-dock
|
|
> #gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
)
|
|
> .gev-voice-settings-button {
|
|
left: calc(50% - 50vw + 0.5rem + var(--gev-landscape-row-width) - 51px);
|
|
right: auto;
|
|
top: auto;
|
|
bottom: calc(0.375rem + 7px);
|
|
}
|
|
}
|
|
|
|
/* The revealed row clears pinned wings without making the Voice column's
|
|
noninteractive gaps cover their controls. Keep hit surfaces stable so hover
|
|
promotion cannot oscillate when the pointer crosses root padding. */
|
|
#command-dock
|
|
> #gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
) {
|
|
pointer-events: none;
|
|
}
|
|
#command-dock
|
|
> #gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
)
|
|
> :is(
|
|
#gev-voice-button,
|
|
.gev-voice-heading,
|
|
.gev-voice-readout,
|
|
.gev-voice-provider-field,
|
|
.gev-voice-settings-button,
|
|
.gev-voice-panel,
|
|
.gev-voice-error-tray
|
|
) {
|
|
pointer-events: auto;
|
|
}
|
|
@media (hover: hover) and (pointer: fine) {
|
|
#command-dock.dock-has-pinned-tray
|
|
> #gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
):is(
|
|
:hover,
|
|
[data-row-grace],
|
|
:has(
|
|
:focus-visible,
|
|
#gev-voice-provider:focus,
|
|
.gev-voice-panel[data-view='settings']:not([hidden])
|
|
)
|
|
) {
|
|
z-index: 101;
|
|
}
|
|
}
|
|
@media (hover: none), (pointer: coarse) {
|
|
#command-dock.dock-has-pinned-tray
|
|
> #gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
) {
|
|
z-index: 101;
|
|
}
|
|
}
|
|
|
|
/* Pointer grace crosses pinned trays without covering their controls. Keyboard
|
|
focus, the provider select and an open panel hold the shared row visible. */
|
|
@media (hover: hover) and (pointer: fine) {
|
|
#command-dock
|
|
#gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
)
|
|
:is(.gev-voice-provider-field, .gev-voice-settings-button) {
|
|
transition:
|
|
opacity 160ms ease,
|
|
transform 180ms ease;
|
|
}
|
|
#command-dock
|
|
#gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
):not(:hover):not([data-row-grace]):not(
|
|
:has(
|
|
:focus-visible,
|
|
#gev-voice-provider:focus,
|
|
.gev-voice-panel[data-view='settings']:not([hidden])
|
|
)
|
|
)
|
|
:is(.gev-voice-provider-field, .gev-voice-settings-button) {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transform: translateY(8px);
|
|
}
|
|
#command-dock:not(.dock-has-pinned-tray)
|
|
#gev-voice-control[data-provider-choice='true']:has(
|
|
> .gev-voice-settings-button
|
|
)
|
|
.gev-voice-provider-field::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 100%;
|
|
right: 0;
|
|
left: 0;
|
|
height: calc(var(--gev-voice-settings-gap) + 8px);
|
|
}
|
|
}
|
|
|
|
/* Existing tier and estimate controls are readable Settings content in panel mode. */
|
|
.gev-voice-cost-settings {
|
|
display: grid;
|
|
gap: 0.55rem;
|
|
margin-top: 0.75rem;
|
|
padding-top: 0.7rem;
|
|
border-top: 1px solid var(--glass-border);
|
|
}
|
|
.gev-voice-cost-settings:has(#gev-voice-tier[hidden]):has(
|
|
#gev-voice-cost-value[hidden]
|
|
),
|
|
.gev-voice-cost-setting-row:has(> [hidden]) {
|
|
display: none;
|
|
}
|
|
.gev-voice-cost-setting-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
min-height: 2rem;
|
|
color: var(--text-primary);
|
|
font-size: 0.68rem;
|
|
}
|
|
.gev-voice-cost-setting-row > span {
|
|
min-width: 0;
|
|
}
|
|
#gev-voice-control .gev-voice-cost-settings .gev-voice-tier-btn {
|
|
flex: 0 0 auto;
|
|
min-height: 2rem;
|
|
min-width: 3.25rem;
|
|
padding: 0.3rem 0.55rem;
|
|
font-size: 0.67rem;
|
|
}
|
|
.gev-voice-cost-settings .gev-voice-cost-value {
|
|
flex: 0 0 auto;
|
|
font-size: 0.75rem;
|
|
}
|
|
.gev-voice-cost-settings .gev-voice-cost-value[data-level='ok'] {
|
|
color: var(--voice-panel-accent, var(--accent));
|
|
}
|
|
#gev-voice-control .gev-voice-settings-icon {
|
|
font-size: 18px;
|
|
line-height: 1;
|
|
}
|
|
@media (pointer: coarse) {
|
|
#gev-voice-control .gev-voice-cost-settings .gev-voice-tier-btn {
|
|
min-height: 44px;
|
|
min-width: 44px;
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
#command-dock
|
|
#gev-voice-control:has(> .gev-voice-settings-button)
|
|
:is(.gev-voice-provider-field, .gev-voice-settings-button) {
|
|
transition: none;
|
|
}
|
|
}
|