Files
hope-voice-api/web/style.css
T
snowspeeder 68f4c461c8
Rebuild & Deploy / rebuild (push) Successful in 37s
feat: replace orb avatar with a livekit-style LED wall driven by agent audio
- Swap the CSS orb for a 21x21 LED dot matrix on a glass panel, matching
  the livekit.com LED-array hero visual.
- Pulse driven by the agent's rendered audio: an expanding radial bloom
  brightens and widens with remote-audio level, dimmer when listening on
  the mic, faint static matrix when idle, and a subtle warm breathing
  state while thinking.
- State-tinted palettes: cool azure when speaking, blue when listening,
  amber when thinking, dim slate when idle.
- Drop the unused orb CSS-variable pulse plumbing and the presence-breathe
  / orbit-turn keyframes; the wall renders per-frame from the analyser.
2026-08-29 17:05:57 -04:00

1608 lines
32 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); }
/* ── Media player ───────────────────────────────────────────────────────── */
/* Reuses .tasks-panel positioning (fixed right, slides in via .open); the
content scrolls under a sticky header. */
.media-panel {
overflow-y: auto;
padding-bottom: 1.2rem;
}
.media-panel .tasks-header {
position: sticky;
top: 0;
z-index: 1;
background: #0d1929;
}
.media-now {
display: flex;
align-items: center;
gap: 0.75rem;
margin: 1rem 1.1rem 0;
padding: 0.75rem;
border-radius: 12px;
background: var(--surface-raised);
}
.media-art,
.media-art-placeholder {
width: 56px;
height: 56px;
flex: 0 0 56px;
border-radius: 8px;
object-fit: cover;
background: rgba(23, 41, 65, 0.7);
}
.media-art-placeholder {
display: flex;
align-items: center;
justify-content: center;
color: var(--ink-muted);
}
.media-now-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.media-now-info strong {
color: var(--ink);
font-size: 0.88rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.media-now-info span {
color: var(--ink-muted);
font-size: 0.72rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.media-progress {
position: relative;
margin-top: 0.35rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
/* Track bar behind the fill (fill width is driven inline by progress). */
.media-progress::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 90px;
height: 4px;
border-radius: 2px;
background: rgba(139, 167, 255, 0.18);
}
.media-progress-fill {
height: 4px;
flex: 0 0 auto;
width: 0;
border-radius: 2px;
background: var(--iris);
transition: width 400ms ease;
}
.media-progress-label {
color: var(--ink-muted);
font-size: 0.66rem;
white-space: nowrap;
}
.media-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 0.9rem;
margin: 0.9rem 1.1rem 0;
}
.media-controls .btn-icon {
color: var(--ink-soft);
}
.media-play {
width: 52px;
height: 52px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
color: #0b1523 !important;
background: var(--iris) !important;
border: none;
}
.media-play .play-icon,
.media-play .pause-icon {
display: none;
}
.media-play:not(.playing) .play-icon {
display: inline-block;
}
.media-play.playing .pause-icon {
display: inline-block;
}
.media-play:hover:not(:disabled) {
filter: brightness(1.1);
}
.media-seek {
display: flex;
align-items: center;
gap: 0.55rem;
margin: 0.75rem 1.1rem 0;
}
.media-time {
color: var(--ink-muted);
font-size: 0.68rem;
font-variant-numeric: tabular-nums;
min-width: 2.6rem;
text-align: center;
}
.media-seek input[type="range"],
.media-volume input[type="range"] {
flex: 1;
accent-color: var(--iris);
cursor: pointer;
}
.media-volume {
display: flex;
align-items: center;
gap: 0.55rem;
margin: 0.55rem 1.1rem 0;
color: var(--ink-muted);
}
.media-search {
display: flex;
gap: 0.45rem;
margin: 0 1.1rem;
}
.media-search input {
flex: 1;
min-width: 0;
padding: 0.5rem 0.7rem;
border: 1px solid var(--line-strong);
border-radius: 9px;
color: var(--ink);
background: rgba(5, 11, 20, 0.45);
font-size: 0.8rem;
}
.media-search input::placeholder {
color: var(--ink-muted);
}
.media-stream-form input:first-child {
flex: 0 0 38%;
}
.media-list {
display: flex;
flex-direction: column;
gap: 0.4rem;
margin: 0.65rem 1.1rem 1rem;
max-height: 240px;
overflow-y: auto;
}
.media-row {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.55rem 0.65rem;
border-radius: 9px;
background: var(--surface-raised);
}
.media-row-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.media-row-info strong {
color: var(--ink);
font-size: 0.78rem;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.media-row-info span {
color: var(--ink-muted);
font-size: 0.68rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.media-row-play {
flex: 0 0 auto;
padding: 0.3rem 0.7rem;
font-size: 0.7rem;
}
.media-row-delete {
flex: 0 0 auto;
color: var(--ink-muted);
}
.media-row-delete:hover {
color: #ffc2ba;
}
.media-empty {
padding: 0.8rem;
border-radius: 9px;
color: var(--ink-muted);
background: var(--surface-raised);
font-size: 0.74rem;
text-align: center;
}
.media-choice {
border: 1px solid rgba(139, 167, 255, 0.35);
background: rgba(60, 95, 201, 0.12);
}
/* ── 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,
.media-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;
}
}