Files
hope-voice-api/web/style.css
T
snowspeeder 436a26a0fc feat: show album art in the avatar square while music is loaded
The LED wall square switches to the track's cover art whenever a song is
loaded outside a conversation, and returns to the pulsing LED wall while
speaking to the agent (music and conversation are mutually exclusive, so
starting a talk hides the art and ending it restores it).
2026-08-29 17:37:39 -04:00

1637 lines
33 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;
}
/* Album art fills the square while a track is loaded outside a conversation. */
.led-art {
position: absolute;
inset: 12px;
width: calc(100% - 24px);
height: calc(100% - 24px);
object-fit: cover;
border-radius: 18px;
z-index: 1;
opacity: 0;
transform: scale(0.96);
transition: opacity 240ms ease, transform 240ms ease;
pointer-events: none;
}
.led-wall.show-art .led-art {
opacity: 1;
transform: scale(1);
}
.led-wall.show-art .led-dot {
opacity: 0 !important;
transition: none;
}
.led-wall.show-art {
filter: none;
}
/* 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;
}
}