Files
hope-voice-api/web/style.css
T
snowspeeder 436b3fc8b1
Rebuild & Deploy / rebuild (push) Successful in 2m7s
refactor: split music streaming into Hope Music
Move the media server, downloader, library volume, and browser player to the
standalone hope-media-music product. Keep Hope's music tools as a thin HTTP
client of the LAN service on port 8094.

Use a stable voice-room player scope instead of ephemeral LiveKit room names.
The agent now owns the conversation handshake: fresh reported playback mutes
its input, joining pauses music, and the last participant leaving resumes only
music it paused. Peer failures remain non-fatal.

Also move the voice deploy workflow onto the active Unraid Gitea runner and
remove yt-dlp/ffmpeg/media health and volume dependencies from the image.
2026-09-12 06:35:15 -04:00

1338 lines
27 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:root {
--canvas: #08111f;
--canvas-deep: #050b14;
--surface: #0d1929;
--surface-raised: #132238;
--surface-soft: #172941;
--line: rgba(183, 204, 235, 0.13);
--line-strong: rgba(183, 204, 235, 0.22);
--ink: #f4f7fb;
--ink-soft: #c5d1e2;
--ink-muted: #8395ad;
--iris: #8ba7ff;
--iris-strong: #6f8ff7;
--iris-deep: #3c5fc9;
--warm: #ffb38e;
--mint: #76d9b2;
--danger: #ff806f;
--user-bubble: #1b3150;
--agent-bubble: #14243a;
--focus: #b5c5ff;
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
--z-panel: 40;
--z-backdrop: 50;
--z-sheet: 60;
color-scheme: dark;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
min-height: 100%;
background: var(--canvas-deep);
}
body {
min-height: 100vh;
min-height: 100dvh;
padding: calc(1.25rem + env(safe-area-inset-top))
calc(1.25rem + env(safe-area-inset-right))
calc(1.25rem + env(safe-area-inset-bottom))
calc(1.25rem + env(safe-area-inset-left));
overflow-x: hidden;
color: var(--ink);
background:
radial-gradient(circle at 16% 8%, rgba(86, 115, 207, 0.16), transparent 34rem),
radial-gradient(circle at 88% 92%, rgba(255, 179, 142, 0.07), transparent 30rem),
var(--canvas);
font-family: "Avenir Next", "Segoe UI Variable", "Segoe UI", system-ui, -apple-system, sans-serif;
-webkit-font-smoothing: antialiased;
}
button,
input,
select {
font: inherit;
}
button {
color: inherit;
}
[hidden] {
display: none !important;
}
.skip-link {
position: fixed;
top: 0.75rem;
left: 0.75rem;
z-index: 100;
padding: 0.7rem 1rem;
border-radius: 8px;
color: var(--canvas);
background: var(--ink);
font-size: 0.85rem;
font-weight: 700;
transform: translateY(-160%);
transition: transform 180ms var(--ease-out);
}
.skip-link:focus {
transform: translateY(0);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.app-shell {
width: min(1180px, 100%);
margin: 0 auto;
}
/* ── Header ─────────────────────────────────────────────────────────────── */
.topbar {
min-height: 62px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1.25rem;
margin-bottom: 1.25rem;
padding: 0.25rem 0.15rem;
}
.brand,
.topbar-actions,
.identity-chip,
.tasks-trigger {
display: flex;
align-items: center;
}
.brand {
gap: 0.8rem;
}
.brand-glyph {
position: relative;
width: 38px;
height: 38px;
flex: 0 0 38px;
border: 1px solid rgba(139, 167, 255, 0.55);
border-radius: 50%;
}
.brand-glyph::before,
.brand-glyph::after,
.brand-glyph i {
content: "";
position: absolute;
border-radius: 50%;
}
.brand-glyph::before {
inset: 7px;
border: 1px solid rgba(139, 167, 255, 0.35);
}
.brand-glyph::after {
width: 8px;
height: 8px;
right: 4px;
top: 5px;
background: var(--warm);
box-shadow: 0 0 12px rgba(255, 179, 142, 0.52);
}
.brand-glyph i {
width: 6px;
height: 6px;
inset: 0;
margin: auto;
background: var(--iris);
}
.brand h1 {
color: var(--ink);
font-size: 1.18rem;
font-weight: 680;
letter-spacing: -0.02em;
}
.brand p {
margin-top: 0.1rem;
color: var(--ink-muted);
font-size: 0.74rem;
}
.topbar-actions {
justify-content: flex-end;
gap: 0.65rem;
}
.mode-switch {
height: 38px;
display: flex;
align-items: center;
gap: 2px;
padding: 3px;
border: 1px solid var(--line);
border-radius: 10px;
background: rgba(5, 11, 20, 0.55);
}
.mode-option {
height: 30px;
display: inline-flex;
align-items: center;
gap: 0.38rem;
padding: 0 0.65rem;
border: 0;
border-radius: 7px;
cursor: pointer;
color: var(--ink-muted);
background: transparent;
font-size: 0.72rem;
font-weight: 650;
transition: color 160ms ease, background 160ms ease, box-shadow 160ms ease;
}
.mode-option:hover {
color: var(--ink-soft);
}
.mode-option.active {
color: var(--ink);
background: var(--surface-soft);
box-shadow: 0 1px 5px rgba(2, 7, 14, 0.3);
}
.mode-option:focus-visible,
.chat-composer textarea:focus-visible,
.send-chat:focus-visible {
outline: 2px solid var(--focus);
outline-offset: 2px;
}
.identity-chip {
min-height: 38px;
gap: 0.55rem;
padding: 0 0.85rem;
border: 1px solid var(--line);
border-radius: 999px;
color: var(--ink-soft);
background: rgba(13, 25, 41, 0.72);
font-size: 0.78rem;
}
.identity-chip strong {
color: var(--ink);
font-weight: 650;
}
.presence-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--mint);
box-shadow: 0 0 0 4px rgba(118, 217, 178, 0.1);
}
/* ── Main workspace ─────────────────────────────────────────────────────── */
.workspace {
display: grid;
grid-template-columns: minmax(390px, 0.92fr) minmax(420px, 1.08fr);
grid-template-rows: minmax(0, 1fr);
height: min(700px, calc(100dvh - 108px));
min-height: 610px;
border: 1px solid var(--line);
border-radius: 16px;
overflow: hidden;
background: var(--surface);
box-shadow: 0 8px 8px rgba(2, 7, 14, 0.18);
}
.voice-console {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
min-width: 0;
min-height: 0;
padding: clamp(2rem, 4vw, 3.5rem) clamp(1.5rem, 4vw, 3.25rem) 1.6rem;
overflow: hidden;
background:
radial-gradient(circle at 50% 38%, rgba(90, 121, 215, 0.14), transparent 18rem),
var(--surface);
}
.voice-console::after {
content: "";
position: absolute;
left: 10%;
right: 10%;
bottom: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(139, 167, 255, 0.34), transparent);
}
.console-intro {
position: relative;
z-index: 2;
width: 100%;
text-align: center;
}
.console-intro h2 {
color: var(--ink);
font-size: 2rem;
font-weight: 620;
letter-spacing: -0.035em;
text-wrap: balance;
}
.console-intro p {
max-width: 38ch;
margin: 0.55rem auto 0;
color: var(--ink-muted);
font-size: 0.91rem;
line-height: 1.55;
text-wrap: pretty;
}
/* ── Voice presence ─────────────────────────────────────────────────────── */
.orb-stage {
position: relative;
z-index: 1;
flex: 1;
width: 100%;
min-height: 310px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 0.75rem;
}
/* ── LED wall avatar ────────────────────────────────────────────────────── */
.led-wall {
position: relative;
z-index: 1;
width: 320px;
height: 320px;
max-width: 100%;
display: grid;
grid-template-columns: repeat(21, 1fr);
grid-template-rows: repeat(21, 1fr);
gap: 2px;
place-items: center;
padding: 12px;
border-radius: 26px;
isolation: isolate;
transition: filter 220ms ease;
}
/* A soft glass panel that holds the matrix up off the page. */
.led-wall::before {
content: "";
position: absolute;
inset: 0;
border-radius: 26px;
border: 1px solid rgba(139, 167, 255, 0.14);
background:
radial-gradient(circle at 50% 50%, rgba(111, 143, 247, 0.1), transparent 62%),
rgba(6, 12, 24, 0.4);
z-index: -1;
}
.led-dot {
--sz: 5px;
width: var(--sz);
height: var(--sz);
min-width: 3px;
min-height: 3px;
border: 0;
border-radius: 50%;
background: currentColor;
opacity: 0.16;
transition: opacity 90ms linear, width 90ms linear, height 90ms linear;
}
/* Palette shifts by state — a cool blue while it listens, warm toward the
center as the agent speaks, amber while thinking, dim when idle. */
.led-wall[data-state="idle"] {
color: #56739e;
}
.led-wall[data-state="connecting"],
.led-wall[data-state="thinking"] {
color: #d99a6c;
}
.led-wall[data-state="listening"] {
color: #6f8ff7;
}
.led-wall[data-state="speaking"] {
color: #9dc2ff;
filter: drop-shadow(0 0 14px rgba(111, 143, 247, 0.35));
}
.orb-labels {
min-height: 46px;
margin-top: 0.2rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.35rem;
}
.state-label {
color: var(--ink-soft);
font-size: 0.83rem;
font-weight: 600;
}
.thinking-chip,
.barge-hint {
color: var(--ink-muted);
font-size: 0.73rem;
}
.thinking-chip::before {
content: "";
display: inline-block;
width: 5px;
height: 5px;
margin: 0 0.42rem 0.1rem 0;
border-radius: 50%;
background: var(--warm);
vertical-align: middle;
animation: think-pulse 1.2s ease-in-out infinite;
}
@keyframes think-pulse {
50% { opacity: 0.28; transform: scale(0.75); }
}
/* ── Buttons and controls ───────────────────────────────────────────────── */
.controls {
position: relative;
z-index: 2;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 0.6rem;
width: 100%;
}
.btn {
min-height: 42px;
border: 0;
border-radius: 9px;
padding: 0.68rem 1rem;
cursor: pointer;
font-size: 0.84rem;
font-weight: 650;
line-height: 1;
transition: color 180ms ease, background 180ms ease, border-color 180ms ease, transform 180ms var(--ease-out), opacity 180ms ease;
}
.btn:hover:not(:disabled) {
transform: translateY(-1px);
}
.btn:active:not(:disabled) {
transform: translateY(0);
}
.btn:focus-visible,
.voice-card:focus-visible {
outline: 2px solid var(--focus);
outline-offset: 3px;
}
.btn:disabled {
opacity: 0.38;
cursor: not-allowed;
}
.btn-primary {
position: relative;
min-width: 180px;
color: #07101d;
background: var(--ink);
}
.btn-primary:hover:not(:disabled) {
background: #dfe7f4;
}
.button-spark {
display: inline-block;
width: 7px;
height: 7px;
margin-right: 0.5rem;
border-radius: 50%;
background: var(--iris-deep);
box-shadow: 0 0 0 4px rgba(60, 95, 201, 0.12);
}
.btn-primary:disabled,
.btn-danger:disabled {
display: none;
}
.btn-danger {
color: #fff4f1;
background: #8f403a;
}
.btn-danger:hover:not(:disabled) {
background: #a84b43;
}
.btn-secondary {
color: var(--ink-soft);
background: var(--surface-raised);
border: 1px solid var(--line);
}
.btn-secondary:hover:not(:disabled) {
color: var(--ink);
border-color: var(--line-strong);
background: var(--surface-soft);
}
.btn-icon {
width: 42px;
min-width: 42px;
height: 42px;
padding: 0;
display: inline-grid;
place-items: center;
color: var(--ink-muted);
background: var(--surface-raised);
border: 1px solid var(--line);
}
.btn-icon:hover:not(:disabled) {
color: var(--ink);
border-color: var(--line-strong);
background: var(--surface-soft);
}
.btn-icon.muted {
color: #fff;
background: var(--danger);
border-color: var(--danger);
}
.btn-icon.muted:hover:not(:disabled) {
color: #fff;
background: var(--danger);
border-color: var(--danger);
}
.btn-ghost {
min-height: 34px;
padding: 0.45rem 0.65rem;
color: var(--ink-muted);
background: transparent;
}
.btn-ghost:hover:not(:disabled) {
color: var(--ink);
background: var(--surface-raised);
}
.tasks-trigger {
position: relative;
min-height: 38px;
gap: 0.45rem;
padding: 0 0.8rem;
font-size: 0.78rem;
}
.tasks-badge {
min-width: 17px;
height: 17px;
display: inline-grid;
place-items: center;
padding: 0 4px;
border-radius: 999px;
color: #07101d;
background: var(--warm);
font-size: 0.64rem;
font-weight: 750;
}
.connection-strip {
width: min(340px, 100%);
flex-shrink: 0;
margin-top: 1rem;
}
.status {
min-height: 20px;
color: var(--ink-muted);
font-size: 0.72rem;
text-align: center;
}
.mic-meter {
height: 3px;
margin-top: 0.55rem;
overflow: hidden;
border-radius: 999px;
background: rgba(139, 167, 255, 0.1);
}
.mic-meter-fill {
display: block;
width: 0;
height: 100%;
border-radius: inherit;
background: var(--iris);
transition: width 80ms linear;
}
.privacy-note {
display: flex;
flex-shrink: 0;
align-items: center;
gap: 0.4rem;
margin-top: 1.15rem;
color: var(--ink-muted);
font-size: 0.7rem;
}
.privacy-note svg {
color: var(--mint);
}
/* ── Conversation ───────────────────────────────────────────────────────── */
.transcript {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
padding: clamp(1.4rem, 3vw, 2.1rem);
border-left: 1px solid var(--line);
background: #0a1524;
}
.transcript-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding-bottom: 1.1rem;
border-bottom: 1px solid var(--line);
}
.transcript-header h2 {
color: var(--ink);
font-size: 1rem;
font-weight: 650;
letter-spacing: -0.01em;
}
.transcript-header p {
margin-top: 0.22rem;
color: var(--ink-muted);
font-size: 0.7rem;
}
#messages {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 1.25rem 0.2rem 1rem 0;
scroll-behavior: smooth;
}
#messages:empty {
display: grid;
place-items: center;
}
#messages:empty::before {
content: "Your conversation will appear here.\A Start when you’re ready—there’s nothing to configure.";
max-width: 30ch;
color: var(--ink-muted);
font-size: 0.84rem;
line-height: 1.65;
text-align: center;
white-space: pre-wrap;
}
.message-row {
position: relative;
display: flex;
align-items: flex-end;
gap: 0.55rem;
margin-bottom: 0.85rem;
animation: message-in 180ms var(--ease-out);
}
.message-row.user {
justify-content: flex-end;
}
.message-row.system {
justify-content: center;
}
.message-row.system .bubble {
padding: 0.35rem 0.65rem;
color: var(--ink-muted);
background: transparent;
font-size: 0.7rem;
}
@keyframes message-in {
from { opacity: 0; transform: translateY(5px); }
to { opacity: 1; transform: translateY(0); }
}
.avatar-dot {
position: relative;
width: 27px;
height: 27px;
flex: 0 0 27px;
margin-bottom: 2px;
border: 1px solid rgba(139, 167, 255, 0.38);
border-radius: 50%;
}
.avatar-dot::after {
content: "";
position: absolute;
inset: 8px;
border-radius: 50%;
background: var(--iris);
}
.bubble {
max-width: min(82%, 58ch);
padding: 0.72rem 0.9rem;
border-radius: 12px;
color: var(--ink-soft);
font-size: 0.9rem;
line-height: 1.55;
overflow-wrap: anywhere;
}
.message-row.user .bubble {
color: #e8efff;
background: var(--user-bubble);
border-bottom-right-radius: 3px;
}
.message-row.agent .bubble {
background: var(--agent-bubble);
border-bottom-left-radius: 3px;
}
.message-row.partial,
.bubble.partial {
opacity: 0.58;
}
.message-row::after {
content: attr(data-ts);
position: absolute;
bottom: -0.9rem;
color: var(--ink-muted);
font-size: 0.62rem;
opacity: 0;
pointer-events: none;
transition: opacity 150ms ease;
}
.message-row:hover::after {
opacity: 0.75;
}
.message-row.user::after { right: 0; }
.message-row.agent::after { left: 2rem; }
.audio-viz {
height: 13px;
margin-left: 0.5rem;
display: inline-flex;
align-items: flex-end;
gap: 2px;
vertical-align: middle;
}
.audio-viz i {
width: 2px;
height: 15%;
border-radius: 2px;
background: var(--iris);
opacity: 0.78;
transition: height 70ms linear;
}
/* ── Text composer ──────────────────────────────────────────────────────── */
.chat-composer {
position: relative;
display: none;
align-items: flex-end;
gap: 0.55rem;
padding: 0.75rem 0 1.25rem;
border-top: 1px solid var(--line);
}
body[data-mode="text"] .chat-composer {
display: flex;
}
.chat-composer textarea {
width: 100%;
min-height: 48px;
max-height: 144px;
resize: none;
padding: 0.8rem 3.2rem 0.8rem 0.9rem;
border: 1px solid var(--line-strong);
border-radius: 12px;
color: var(--ink);
background: var(--surface-raised);
font: inherit;
font-size: 0.9rem;
line-height: 1.4;
caret-color: var(--iris);
transition: border-color 160ms ease, background 160ms ease;
}
.chat-composer textarea::placeholder {
color: var(--ink-muted);
}
.chat-composer textarea:focus {
border-color: rgba(139, 167, 255, 0.56);
background: var(--surface-soft);
}
.send-chat {
position: absolute;
right: 0.55rem;
bottom: 2.05rem;
width: 34px;
height: 34px;
display: grid;
place-items: center;
border: 0;
border-radius: 9px;
cursor: pointer;
color: #08111f;
background: var(--iris);
transition: opacity 150ms ease, transform 150ms var(--ease-out), background 150ms ease;
}
.send-chat:hover:not(:disabled) {
background: #a9bbff;
transform: translateY(-1px);
}
.send-chat:disabled {
cursor: default;
opacity: 0.25;
}
.composer-hint {
position: absolute;
left: 0.2rem;
bottom: 0.25rem;
color: var(--ink-muted);
font-size: 0.62rem;
}
/* Text mode makes the transcript the primary surface while preserving Hope's
live presence and the same underlying conversation. */
body[data-mode="text"] .workspace {
grid-template-columns: minmax(240px, 0.62fr) minmax(480px, 1.38fr);
}
body[data-mode="text"] .voice-console {
padding: 2rem 1.5rem 1.4rem;
}
body[data-mode="text"] .console-intro h2 {
font-size: 1.55rem;
}
body[data-mode="text"] .orb-stage {
min-height: 220px;
}
body[data-mode="text"] .led-wall {
width: 260px;
height: 260px;
}
body[data-mode="text"] .controls {
max-width: 220px;
}
body[data-mode="text"] #muteBtn {
display: none;
}
body[data-mode="text"] .mic-meter {
visibility: hidden;
}
/* ── Voice settings ─────────────────────────────────────────────────────── */
.sheet-backdrop {
position: fixed;
inset: 0;
z-index: var(--z-backdrop);
background: rgba(2, 7, 14, 0.68);
opacity: 0;
transition: opacity 200ms ease;
}
.sheet-backdrop:not([hidden]) {
opacity: 1;
}
.settings-sheet {
position: fixed;
top: 0;
left: 50%;
z-index: var(--z-sheet);
width: min(660px, 100%);
height: 100dvh;
padding: 0.75rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom));
display: flex;
flex-direction: column;
border: 1px solid var(--line-strong);
border-radius: 16px;
background: #101e31;
transform: translate(-50%, 102%);
transition: transform 240ms var(--ease-out);
}
.settings-sheet.open {
transform: translate(-50%, 0);
}
.sheet-handle {
width: 36px;
height: 3px;
margin: 0 auto 1rem;
border-radius: 999px;
background: var(--line-strong);
}
.sheet-header,
.tasks-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.sheet-header {
padding-bottom: 1rem;
}
.sheet-header h2,
.tasks-header h2 {
color: var(--ink);
font-size: 1rem;
font-weight: 650;
}
.sheet-header p,
.tasks-header p {
margin-top: 0.25rem;
color: var(--ink-muted);
font-size: 0.72rem;
}
.sheet-section-title {
margin: 0.4rem 0 0.55rem;
padding: 0 0.2rem;
color: var(--ink-muted);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.sheet-note {
margin: 0.5rem 0 0;
color: var(--ink-muted);
font-size: 0.72rem;
}
.voice-list {
min-height: 0;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.4rem;
padding: 0.15rem 0.2rem 0 0;
}
/* Two pickers (voice + brain) share the settings sheet height. */
.settings-sheet .voice-list {
flex: 1 1 0;
}
.voice-card {
min-height: 54px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
padding: 0.65rem 0.75rem 0.65rem 1rem;
border: 1px solid transparent;
border-radius: 10px;
cursor: pointer;
color: var(--ink-soft);
background: rgba(23, 41, 65, 0.55);
transition: color 160ms ease, background 160ms ease, border-color 160ms ease;
}
.voice-card:hover {
color: var(--ink);
background: var(--surface-soft);
}
.voice-card.active {
color: var(--ink);
border-color: rgba(139, 167, 255, 0.42);
background: rgba(60, 95, 201, 0.18);
}
.voice-name {
font-size: 0.86rem;
font-weight: 600;
}
.voice-card.active .voice-name::after {
content: "Selected";
display: inline-block;
margin-left: 0.55rem;
color: var(--iris);
font-size: 0.66rem;
font-weight: 550;
}
.voice-preview {
min-height: 32px;
color: var(--ink-muted);
}
.voice-preview::before {
content: "Listen";
}
/* ── Tasks ──────────────────────────────────────────────────────────────── */
.tasks-panel {
position: fixed;
top: 0;
right: 0;
z-index: var(--z-panel);
width: min(380px, 92vw);
height: 100dvh;
display: flex;
flex-direction: column;
border-left: 1px solid var(--line-strong);
background: #0d1929;
transform: translateX(102%);
transition: transform 240ms var(--ease-out);
}
.tasks-panel.open {
transform: translateX(0);
}
.tasks-header {
padding: calc(1.1rem + env(safe-area-inset-top)) 1.1rem 1rem;
border-bottom: 1px solid var(--line);
}
.tasks-list {
flex: 1;
overflow-y: auto;
padding: 0.9rem;
display: flex;
flex-direction: column;
gap: 0.65rem;
}
.tasks-empty {
min-height: 180px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.35rem;
color: var(--ink-muted);
text-align: center;
font-size: 0.78rem;
}
.tasks-empty strong {
color: var(--ink-soft);
font-size: 0.86rem;
}
.task-card {
padding: 0.85rem;
border-radius: 10px;
color: var(--ink-soft);
background: var(--surface-raised);
font-size: 0.8rem;
}
.task-card-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.45rem;
}
.task-status-dot {
width: 7px;
height: 7px;
flex: 0 0 7px;
border-radius: 50%;
background: var(--ink-muted);
}
.task-status-dot.running {
background: var(--iris);
animation: status-pulse 1.4s ease-in-out infinite;
}
.task-status-dot.completed { background: var(--mint); }
.task-status-dot.failed { background: var(--danger); }
@keyframes status-pulse {
50% { opacity: 0.3; }
}
.task-desc {
flex: 1;
overflow: hidden;
color: var(--ink);
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-steps {
max-height: 200px;
margin-top: 0.5rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.3rem;
list-style: none;
}
.task-step,
.task-result,
.task-error {
padding: 0.42rem 0.5rem;
border-radius: 6px;
font-size: 0.72rem;
}
.task-step {
color: var(--ink-muted);
background: rgba(5, 11, 20, 0.3);
}
.task-step.tool_call { color: #a9bbff; }
.task-step.tool_result { color: var(--ink-muted); }
.task-step.text { color: var(--ink-soft); }
.task-result { margin-top: 0.5rem; color: #bcebd8; background: rgba(118, 217, 178, 0.1); }
.task-error { margin-top: 0.5rem; color: #ffc2ba; background: rgba(255, 128, 111, 0.1); }
/* ── Responsive ─────────────────────────────────────────────────────────── */
@media (max-height: 760px) and (min-width: 881px) {
.workspace {
min-height: 540px;
}
.voice-console {
padding-top: 1.5rem;
padding-bottom: 1rem;
}
.console-intro h2 {
font-size: 1.65rem;
}
.console-intro p {
margin-top: 0.35rem;
}
.orb-stage {
min-height: 220px;
margin: -1rem 0;
}
.led-wall {
width: 260px;
height: 260px;
}
.connection-strip,
.privacy-note {
margin-top: 0.65rem;
}
}
@media (max-width: 880px) {
body {
padding: calc(0.85rem + env(safe-area-inset-top))
calc(0.85rem + env(safe-area-inset-right))
calc(0.85rem + env(safe-area-inset-bottom))
calc(0.85rem + env(safe-area-inset-left));
}
.workspace,
body[data-mode="text"] .workspace {
grid-template-columns: 1fr;
height: auto;
min-height: auto;
}
.voice-console {
min-height: 620px;
}
.transcript {
min-height: 430px;
max-height: 62dvh;
border-top: 1px solid var(--line);
border-left: 0;
}
}
@media (max-width: 560px) {
body {
padding-right: env(safe-area-inset-right);
padding-left: env(safe-area-inset-left);
}
.topbar {
padding-right: 0.85rem;
padding-left: 0.85rem;
margin-bottom: 0.65rem;
}
.brand p,
.identity-chip,
.mode-option svg {
display: none;
}
.mode-option {
padding: 0 0.55rem;
}
.workspace {
border-right: 0;
border-left: 0;
border-radius: 0;
}
.voice-console {
min-height: calc(100dvh - 82px);
padding: 2.1rem 1.15rem 1.25rem;
}
body[data-mode="text"] .voice-console {
min-height: 270px;
padding: 1.35rem 1rem 1rem;
}
body[data-mode="text"] .orb-stage {
min-height: 120px;
margin: -0.75rem 0;
}
body[data-mode="text"] .led-wall {
width: 210px;
height: 210px;
}
.console-intro h2 {
font-size: 1.7rem;
}
.orb-stage {
min-height: 300px;
}
.led-wall {
width: 270px;
height: 270px;
padding: 9px;
}
.controls {
max-width: 330px;
}
.btn-primary,
.btn-danger {
flex: 1 1 180px;
}
.privacy-note {
margin-top: 1rem;
}
.transcript {
min-height: 480px;
max-height: none;
padding: 1.25rem 1rem;
}
.tasks-panel {
width: 100vw;
}
.bubble {
max-width: 86%;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}