Cosmic Command Center
:root {
–bg-void: #08070a;
–bg-nebula-a: #2a1f0d;
–bg-nebula-b: #1a1006;
–gold: #c9a05c;
–gold-bright: #e6c98f;
–ink: #e8e6f0;
–ink-dim: #9b98ab;
–ink-faint: #5c5a6e;
–sapphire: #2f9fa4;
–sapphire-dim: #12474b;
–opal-a: #f5c6d8;
–opal-b: #c6e8d8;
–opal-c: #c6d8f0;
–amber: #f5a623;
–amber-dim: #c97a2b;
–jade: #1f6b4b;
–jade-dim: #0d3b29;
–onyx: #8a8a9e;
–onyx-dim: #2a2a2e;
–lapis: #3d4a6b;
–lapis-gold: #d4af37;
–krystal: #f3a98a;
–krystal-dim: #b06a4e;
–font-display: ‘Playfair Display’, serif;
–font-body: ‘EB Garamond’, serif;
–font-mono: ‘Cormorant Garamond’, serif;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: var(–bg-void);
color: var(–ink);
font-family: var(–font-body);
overflow: hidden;
}
/* ———- Starfield ———- */
#starfield {
position: fixed;
inset: 0;
z-index: 0;
background:
radial-gradient(ellipse at 20% 30%, rgba(180,140,45,0.16), transparent 55%),
radial-gradient(ellipse at 80% 70%, rgba(120,85,20,0.22), transparent 55%),
var(–bg-void);
}
.star-layer {
position: absolute;
inset: -10%;
background-repeat: repeat;
}
.star-layer.a { animation: drift-a 240s linear infinite; opacity: 0.8; }
.star-layer.b { animation: drift-b 340s linear infinite; opacity: 0.5; }
.star-layer.c { animation: drift-a 180s linear infinite reverse; opacity: 0.35; }
@keyframes drift-a { from { transform: translate(0,0); } to { transform: translate(-6%, -4%); } }
@keyframes drift-b { from { transform: translate(0,0); } to { transform: translate(5%, -6%); } }
@media (prefers-reduced-motion: reduce) {
.star-layer { animation: none !important; }
}
/* ———- Layout shell ———- */
#stage {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.header-block {
position: absolute;
top: 24px;
left: 50%;
transform: translateX(-50%);
text-align: center;
}
.eyebrow-title {
font-family: var(–font-display);
font-size: 32px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(–gold);
white-space: nowrap;
}
.eyebrow-subtitle {
margin-top: 4px;
font-family: var(–font-mono);
font-size: 15px;
font-weight: 600;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(–gold);
opacity: 0.7;
white-space: nowrap;
}
/* ———- Table ———- */
#table-wrap {
position: relative;
width: min(70vw, 760px);
height: min(44vw, 440px);
margin-top: 220px;
}
#table-surface {
position: absolute;
inset: 0;
border-radius: 50% / 50%;
background:
radial-gradient(ellipse at 50% 40%, rgba(180,140,45,0.14), transparent 62%),
linear-gradient(160deg, rgba(40,30,15,0.55), rgba(10,8,6,0.75));
border: 1px solid rgba(212,175,55,0.22);
box-shadow:
inset 0 0 80px rgba(150,110,30,0.12),
0 40px 90px rgba(0,0,0,0.55);
}
.seat {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
width: 170px;
text-align: center;
}
.seat .gem {
width: 72px;
height: 72px;
clip-path: polygon(50% 2%, 90% 25%, 90% 75%, 50% 98%, 10% 75%, 10% 25%);
position: relative;
animation: ambient-float 6s ease-in-out infinite;
}
.seat .gem::before {
content: ”;
position: absolute;
inset: 0;
clip-path: polygon(50% 2%, 90% 25%, 50% 50%, 10% 25%);
background: rgba(255,255,255,0.22);
mix-blend-mode: overlay;
}
.seat .gem::after {
content: ”;
position: absolute;
inset: -18px;
border-radius: 50%;
filter: blur(13px);
opacity: 0.55;
background: inherit;
z-index: -1;
}
@keyframes ambient-float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.seat .name {
font-family: var(–font-display);
font-size: 21px;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.seat .role {
font-family: var(–font-mono);
font-size: 13px;
font-weight: 600;
color: var(–ink-dim);
text-transform: uppercase;
letter-spacing: 0.06em;
line-height: 1.4;
}
.planet-title {
display: block;
font-size: 12px;
font-weight: 500;
letter-spacing: 0.02em;
text-transform: none;
color: var(–ink-dim);
opacity: 1;
white-space: nowrap;
margin: 3px auto 0;
}
.seat.active .gem {
box-shadow: 0 0 0 2px rgba(255,255,255,0.55), 0 0 34px 6px currentColor;
animation: ambient-float 6s ease-in-out infinite, active-pulse 2.4s ease-in-out infinite;
}
@keyframes active-pulse {
0%, 100% { filter: brightness(1); }
50% { filter: brightness(1.35); }
}
/* per-agent gem colors */
.gem.sapphire { background:
repeating-conic-gradient(from 210deg, rgba(0,0,0,0.32) 0deg 1.5deg, transparent 1.5deg 44deg),
linear-gradient(125deg, rgba(255,255,255,0.5) 0%, transparent 38%),
conic-gradient(from 205deg, #d4f5f0 0deg 26deg, #6fd0c0 26deg 70deg, var(–sapphire) 70deg 118deg, var(–sapphire-dim) 118deg 165deg, #051f22 165deg 210deg, var(–sapphire-dim) 210deg 255deg, var(–sapphire) 255deg 300deg, #7fd8ca 300deg 340deg, #d4f5f0 340deg 360deg);
box-shadow: inset -5px -7px 9px rgba(0,0,0,0.4), inset 3px 3px 6px rgba(255,255,255,0.25);
color: var(–sapphire); }
.gem.opal { background:
repeating-conic-gradient(from 200deg, rgba(0,0,0,0.18) 0deg 1.5deg, transparent 1.5deg 44deg),
linear-gradient(125deg, rgba(255,255,255,0.45) 0%, transparent 38%),
conic-gradient(from 195deg, var(–opal-a) 0deg 45deg, var(–opal-c) 45deg 90deg, var(–opal-b) 90deg 135deg, #efe4f7 135deg 180deg, var(–opal-a) 180deg 225deg, var(–opal-c) 225deg 270deg, var(–opal-b) 270deg 315deg, #efe4f7 315deg 360deg);
box-shadow: inset -5px -7px 9px rgba(120,90,110,0.3), inset 3px 3px 6px rgba(255,255,255,0.35);
color: var(–opal-c); }
.gem.amber { background:
repeating-conic-gradient(from 210deg, rgba(0,0,0,0.32) 0deg 1.5deg, transparent 1.5deg 44deg),
linear-gradient(125deg, rgba(255,255,255,0.5) 0%, transparent 38%),
conic-gradient(from 205deg, #fff3d6 0deg 26deg, #ffcf7a 26deg 70deg, var(–amber) 70deg 118deg, var(–amber-dim) 118deg 165deg, #5c3608 165deg 210deg, var(–amber-dim) 210deg 255deg, var(–amber) 255deg 300deg, #ffdb96 300deg 340deg, #fff3d6 340deg 360deg);
box-shadow: inset -5px -7px 9px rgba(0,0,0,0.4), inset 3px 3px 6px rgba(255,255,255,0.3);
color: var(–amber); }
.gem.jade { background:
repeating-conic-gradient(from 210deg, rgba(0,0,0,0.32) 0deg 1.5deg, transparent 1.5deg 44deg),
linear-gradient(125deg, rgba(255,255,255,0.45) 0%, transparent 38%),
conic-gradient(from 205deg, #d4ffe9 0deg 26deg, #6fdfae 26deg 70deg, var(–jade) 70deg 118deg, var(–jade-dim) 118deg 165deg, #041b11 165deg 210deg, var(–jade-dim) 210deg 255deg, var(–jade) 255deg 300deg, #85e8bb 300deg 340deg, #d4ffe9 340deg 360deg);
box-shadow: inset -5px -7px 9px rgba(0,0,0,0.45), inset 3px 3px 6px rgba(255,255,255,0.2);
color: var(–jade); }
.gem.onyx { background:
repeating-conic-gradient(from 210deg, rgba(255,255,255,0.08) 0deg 1.5deg, transparent 1.5deg 44deg),
linear-gradient(125deg, rgba(255,255,255,0.35) 0%, transparent 38%),
conic-gradient(from 205deg, #c4c4d4 0deg 26deg, #6a6a7a 26deg 70deg, #3a3a42 70deg 118deg, #101014 118deg 165deg, #000000 165deg 210deg, #101014 210deg 255deg, #3a3a42 255deg 300deg, #77778a 300deg 340deg, #c4c4d4 340deg 360deg);
box-shadow: inset -5px -7px 9px rgba(0,0,0,0.55), inset 3px 3px 6px rgba(255,255,255,0.2);
color: var(–onyx); }
.gem.lapis { background:
radial-gradient(circle at 28% 22%, var(–lapis-gold) 3%, transparent 4%),
radial-gradient(circle at 68% 58%, var(–lapis-gold) 2.5%, transparent 3.5%),
radial-gradient(circle at 45% 75%, var(–lapis-gold) 2%, transparent 3%),
repeating-conic-gradient(from 210deg, rgba(0,0,0,0.32) 0deg 1.5deg, transparent 1.5deg 44deg),
linear-gradient(125deg, rgba(255,255,255,0.45) 0%, transparent 38%),
conic-gradient(from 205deg, #cdd2e2 0deg 26deg, #8891ac 26deg 70deg, var(–lapis) 70deg 118deg, #1c2233 118deg 165deg, #06070c 165deg 210deg, #1c2233 210deg 255deg, var(–lapis) 255deg 300deg, #a3aac2 300deg 340deg, #cdd2e2 340deg 360deg);
box-shadow: inset -5px -7px 9px rgba(0,0,0,0.45), inset 3px 3px 6px rgba(255,255,255,0.2);
color: var(–lapis); }
.gem.krystal { background:
repeating-conic-gradient(from 210deg, rgba(0,0,0,0.2) 0deg 1.5deg, transparent 1.5deg 44deg),
linear-gradient(125deg, rgba(255,255,255,0.55) 0%, transparent 38%),
conic-gradient(from 205deg, #fff2ea 0deg 26deg, #f8c4a8 26deg 70deg, var(–krystal) 70deg 118deg, var(–krystal-dim) 118deg 165deg, #5c3320 165deg 210deg, var(–krystal-dim) 210deg 255deg, var(–krystal) 255deg 300deg, #fad3bd 300deg 340deg, #fff2ea 340deg 360deg);
box-shadow: inset -5px -7px 9px rgba(140,80,50,0.35), inset 3px 3px 6px rgba(255,255,255,0.4);
color: var(–krystal); }
/* Seat positions around the arc — head (Sapphire) at top-center */
.seat.sapphire-seat { top: -6%; left: 50%; transform: translateX(-50%); }
.seat.opal-seat { top: 14%; left: 4%; }
.seat.jade-seat { top: 52%; left: -2%; }
.seat.krystal-seat { top: 88%; left: 14%; }
.seat.amber-seat { top: 14%; right: 4%; }
.seat.lapis-seat { top: 52%; right: -2%; }
.seat.onyx-seat { top: 88%; right: 14%; }
/* ———- Planets (downstream specialists) ———- */
.planet {
position: absolute;
top: -58px;
left: 50%;
transform: translateX(-50%);
width: 30px;
height: 30px;
border-radius: 50%;
opacity: 0.85;
}
.planet:not(.label) {
animation: planet-orbit 10s ease-in-out infinite;
}
/* Organic scattered positions and true-to-scale sizes (Luna as the baseline smallest body) */
.opal-seat .planet.sun { width: 102px; height: 102px; top: -118px; left: 16%; animation-duration: 15s; }
.opal-seat .planet.gaia { width: 60px; height: 60px; top: -96px; left: 98%; animation-duration: 11s; animation-delay: -3s; }
.amber-seat .planet.mars { width: 44px; height: 44px; top: -92px; left: 2%; animation-duration: 9s; animation-delay: -1s; }
.amber-seat .planet.vena { width: 62px; height: 62px; top: -50px; left: 90%; animation-duration: 12s; animation-delay: -5s; }
.jade-seat .planet.luna { width: 34px; height: 34px; top: 26px; left: -34%; animation-duration: 8s; animation-delay: -2s; }
@keyframes planet-orbit {
0%, 100% { transform: translateX(-50%) translateY(0) rotate(0deg); }
50% { transform: translateX(-50%) translateY(-6px) rotate(6deg); }
}
.planet.sun { background: radial-gradient(circle at 35% 30%, #fff3c4, #ffb347 60%, #b5651d); box-shadow: 0 0 18px 3px rgba(255,179,71,0.5); }
.planet.luna { background: radial-gradient(circle at 32% 28%, #ffffff, #d4d4dc 45%, #8d8d98 80%); box-shadow: 0 0 14px 2px rgba(216,216,224,0.45); }
.planet.gaia {
background:
radial-gradient(circle at 28% 68%, rgba(58,138,88,0.65) 12%, transparent 32%),
radial-gradient(circle at 62% 32%, rgba(58,138,88,0.55) 10%, transparent 28%),
radial-gradient(circle at 35% 30%, #bfe8ff, #2e6fb8 55%, #12305e 85%);
box-shadow: 0 0 16px 3px rgba(60,140,200,0.4);
}
.planet.vena { background: radial-gradient(circle at 35% 30%, #fff3ea, #f3a98a 55%, #b06a4e 85%); box-shadow: 0 0 14px 2px rgba(243,169,138,0.45); }
.planet.mars { background: radial-gradient(circle at 35% 30%, #ffb199, #c1440e 60%, #6e260a); box-shadow: 0 0 14px 2px rgba(193,68,14,0.45); }
.planet.luna { background: radial-gradient(circle at 35% 30%, #f4f4f8, #b8b8c4 60%, #7c7c88); box-shadow: 0 0 12px 2px rgba(200,200,210,0.4); }
.planet.label {
position: absolute;
top: -74px;
left: 50%;
transform: translateX(-50%);
font-family: var(–font-mono);
font-size: 15px;
font-weight: 600;
color: var(–ink);
white-space: nowrap;
letter-spacing: 0.06em;
text-transform: uppercase;
z-index: 3;
}
.opal-seat .planet.label.sun-label { top: -192px; left: 16%; }
.opal-seat .planet.label.gaia-label { top: -154px; left: 98%; }
.amber-seat .planet.label.mars-label { top: -168px; left: 2%; }
.amber-seat .planet.label.vena-label { top: -102px; left: 90%; }
.jade-seat .planet.label.luna-label { top: -30px; left: -34%; }
/* ———- Center console ———- */
#console {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 320px;
text-align: center;
}
#active-name {
font-family: var(–font-display);
font-size: 34px;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(–gold);
}
#active-status {
font-family: var(–font-mono);
font-size: 15px;
font-weight: 600;
color: var(–gold);
opacity: 0.7;
text-transform: uppercase;
letter-spacing: 0.1em;
margin-top: 4px;
min-height: 14px;
}
/* ———- Bottom control bar ———- */
#control-bar {
position: absolute;
bottom: 36px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;
z-index: 2;
}
#mic-btn {
width: 64px;
height: 64px;
border-radius: 50%;
border: 1px solid rgba(212,175,55,0.4);
background: radial-gradient(circle at 35% 30%, rgba(150,110,30,0.5), rgba(18,14,10,0.9));
color: var(–ink);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: box-shadow 0.25s ease, transform 0.15s ease;
}
#mic-btn:hover { transform: translateY(-2px); }
#mic-btn.listening {
box-shadow: 0 0 0 2px rgba(255,255,255,0.4), 0 0 36px 10px rgba(120,110,200,0.55);
animation: mic-pulse 1.6s ease-in-out infinite;
}
@keyframes mic-pulse {
0%, 100% { box-shadow: 0 0 0 2px rgba(255,255,255,0.4), 0 0 24px 6px rgba(120,110,200,0.4); }
50% { box-shadow: 0 0 0 2px rgba(255,255,255,0.6), 0 0 44px 14px rgba(120,110,200,0.65); }
}
#mic-btn svg { width: 24px; height: 24px; }
#transcript {
font-family: var(–font-mono);
font-size: 18px;
font-weight: 500;
font-style: italic;
color: var(–ink-dim);
max-width: 480px;
text-align: center;
min-height: 16px;
}
audio#voice-out { display: none; }
#brand-strip {
position: fixed;
top: 108px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 34px;
z-index: 2;
opacity: 0.95;
}
#brand-strip img {
height: 140px;
width: 140px;
filter: drop-shadow(0 0 10px rgba(212,175,55,0.35));
}
#brand-strip .divider {
width: 1px;
height: 40px;
background: rgba(212,175,55,0.3);
}
Sapphire
Chief Intelligence Officer
SolaraCapital Systems Analyst
GaiaResource Systems Analyst
Opal
Chief Financial Officer
LunaInstitutional Systems Analyst
Jade
Chief Administrative Officer
Krystal
Chief Experience Officer
MarsCustomer & Community Relationship Analyst
VenaCreative Experience Analyst
Amber
Chief Marketing Officer
Lapis
Chief Information Officer
Onyx
Chief Technology Officer
/* ============================================================
CONFIG — fill in each agent’s real webhook URL from n8n
(Webhook node → Production URL, set to return the ElevenLabs
audio binary as the response)
============================================================ */
const AGENT_WEBHOOKS = {
Sapphire: “
https://n8n.sapphirestardust.com/webhook/sapphire-voice”,
Opal: “
https://n8n.sapphirestardust.com/webhook/opal-voice”,
Amber: “
https://n8n.sapphirestardust.com/webhook/amber-voice”,
Jade: “
https://n8n.sapphirestardust.com/webhook/jade-voice”,
Onyx: “
https://n8n.sapphirestardust.com/webhook/onyx-voice”,
Lapis: “
https://n8n.sapphirestardust.com/webhook/lapis-voice”,
Krystal: “
https://n8n.sapphirestardust.com/webhook/krystal-voice”,
};
const AGENT_NAMES = Object.keys(AGENT_WEBHOOKS);
/* ============================================================
Starfield generation (simple twinkling dot layers)
============================================================ */
function makeStarLayer(el, count, size, color) {
let dots = “”;
for (let i = 0; i < count; i++) {
const x = Math.random() * 100;
const y = Math.random() * 100;
const r = Math.random() * size + 0.4;
dots += “;
}
el.style.backgroundImage = `url(“data:image/svg+xml,${encodeURIComponent(
`${dots}`
)}”)`;
el.style.backgroundSize = “700px 700px”;
}
makeStarLayer(document.getElementById(‘stars-a’), 60, 0.5, ‘#f3e6c8’);
makeStarLayer(document.getElementById(‘stars-b’), 40, 0.35, ‘#d4af37’);
makeStarLayer(document.getElementById(‘stars-c’), 30, 0.3, ‘#fff6df’);
/* ============================================================
State
============================================================ */
let currentAgent = “Sapphire”;
const activeNameEl = document.getElementById(‘active-name’);
const activeStatusEl = document.getElementById(‘active-status’);
const transcriptEl = document.getElementById(‘transcript’);
const micBtn = document.getElementById(‘mic-btn’);
const audioOut = document.getElementById(‘voice-out’);
function setActiveAgent(name) {
currentAgent = name;
activeNameEl.textContent = name;
document.querySelectorAll(‘.seat’).forEach(seat => {
seat.classList.toggle(‘active’, seat.dataset.agent === name);
});
}
function setStatus(text) {
activeStatusEl.textContent = text;
}
/* ============================================================
Hand-off phrase detection
Recognized patterns (case-insensitive):
– “bring forward ” / “call forward” / “speak to ” / “let me talk to ”
-> switches active agent to
– “sapphire, let’s continue” / “thank you , sapphire” / “back to sapphire”
-> switches active agent back to Sapphire
============================================================ */
function detectHandoff(text) {
const lower = text.toLowerCase();
// returning to Sapphire
if (/back to sapphire|sapphire.*(let’?s continue|continue the conversation)|thank you.*sapphire/.test(lower)) {
return “Sapphire”;
}
// bringing someone forward
const bringForward = /(bring forward|call forward|speak to|talk to|let me speak with)\s+([a-z]+)/.exec(lower);
if (bringForward) {
const requested = bringForward[2];
const match = AGENT_NAMES.find(n => n.toLowerCase() === requested);
if (match) return match;
}
return null; // no hand-off detected — message goes to current agent as-is
}
/* ============================================================
Send transcribed text to the current agent’s webhook,
play back the returned audio
============================================================ */
async function sendToAgent(agentName, text) {
const url = AGENT_WEBHOOKS[agentName];
if (!url || url.startsWith(“PASTE_”)) {
setStatus(`no webhook configured for ${agentName} yet`);
return;
}
setStatus(`${agentName} is responding…`);
try {
const res = await fetch(url, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({ chatInput: text })
});
if (!res.ok) {
setStatus(`${agentName} could not be reached (${res.status})`);
return;
}
const blob = await res.blob();
const audioUrl = URL.createObjectURL(blob);
audioOut.src = audioUrl;
await audioOut.play();
setStatus(`${agentName} responding`);
audioOut.onended = () => setStatus(“standing by”);
} catch (err) {
setStatus(`connection error — ${err.message}`);
}
}
/* ============================================================
Speech recognition (browser built-in, no cost)
============================================================ */
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
let recognizer = null;
let listening = false;
if (SpeechRecognition) {
recognizer = new SpeechRecognition();
recognizer.continuous = false;
recognizer.interimResults = false;
recognizer.lang = “en-US”;
recognizer.onstart = () => {
listening = true;
micBtn.classList.add(‘listening’);
setStatus(“listening…”);
};
recognizer.onend = () => {
listening = false;
micBtn.classList.remove(‘listening’);
};
recognizer.onerror = (e) => {
listening = false;
micBtn.classList.remove(‘listening’);
setStatus(`mic error — ${e.error}`);
};
recognizer.onresult = (event) => {
const text = event.results[0][0].transcript;
transcriptEl.textContent = `”${text}”`;
const handoff = detectHandoff(text);
if (handoff && handoff !== currentAgent) {
setActiveAgent(handoff);
setStatus(handoff === “Sapphire” ? “returning to Sapphire” : `bringing ${handoff} forward`);
// Announce the hand-off itself as a message to the newly active agent
// so they greet the user naturally.
sendToAgent(handoff, `[hand-off] Dr. Drysdale would like to speak with you now.`);
return;
}
sendToAgent(currentAgent, text);
};
} else {
setStatus(“speech recognition not supported in this browser”);
}
micBtn.addEventListener(‘click’, () => {
if (!recognizer) return;
if (listening) {
recognizer.stop();
} else {
recognizer.start();
}
});
setActiveAgent(“Sapphire”);