/* Discord-ish dark theme (POC) */ :root { --bg-0: #1e1f22; /* user panel / topbar */ --bg-1: #2b2d31; /* sidebar */ --bg-2: #313338; /* main */ --bg-3: #383a40; /* hover */ --bg-4: #404249; --text-0: #f2f3f5; --text-1: #b5bac1; --text-2: #80848e; --brand: #5865f2; --green: #23a55a; --red: #f23f43; --yellow: #f0b232; --radius: 8px; } * { box-sizing: border-box; margin: 0; } html, body { height: 100%; } body { font: 14px/1.4 "gg sans", "Segoe UI", system-ui, -apple-system, sans-serif; background: var(--bg-2); color: var(--text-0); overflow: hidden; } /* Material Symbols font configuration */ .material-symbols-outlined { font-family: 'Material Symbols Outlined'; font-weight: normal; font-style: normal; font-size: 20px; line-height: 1; letter-spacing: normal; text-transform: none; display: inline-block; white-space: nowrap; word-wrap: normal; direction: ltr; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; -moz-osx-font-smoothing: grayscale; font-feature-settings: 'liga'; user-select: none; vertical-align: middle; } .inline-icon { font-size: 16px; vertical-align: -2px; } .channel-icon { font-size: 18px; color: var(--text-2); flex-shrink: 0; } .channel:hover .channel-icon, .channel.active .channel-icon { color: var(--text-0); } #app { display: flex; height: 100vh; } /* sidebar */ #sidebar { width: 250px; background: var(--bg-1); display: flex; flex-direction: column; flex-shrink: 0; position: relative; z-index: 10; } #server-name { padding: 14px 10px 14px 16px; font-weight: 700; font-size: 15px; border-bottom: 1px solid rgba(0,0,0,.25); box-shadow: 0 1px 0 rgba(0,0,0,.06); display: flex; align-items: center; gap: 8px; } #server-logo, #mobile-logo { border-radius: 6px; flex-shrink: 0; } .server-name-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .icon-btn { background: transparent; border: none; color: var(--text-2); cursor: pointer; width: 26px; height: 26px; border-radius: 6px; display: grid; place-items: center; flex-shrink: 0; padding: 0; transition: background .12s, color .12s; } .icon-btn .material-symbols-outlined { font-size: 18px; } .icon-btn:hover { background: var(--bg-3); color: var(--text-0); } /* sidebar drag-to-resize handle */ #sidebar-resizer { position: absolute; top: 0; right: -3px; width: 6px; height: 100%; cursor: col-resize; z-index: 15; background: transparent; } #sidebar-resizer:hover, #sidebar-resizer.dragging { background: var(--brand); opacity: .5; } body.sidebar-collapsed #sidebar { display: none; } /* floating button to bring the sidebar back once collapsed */ #btn-sidebar-expand { display: none; position: absolute; top: 12px; left: 12px; z-index: 20; background: var(--bg-1); border: 1px solid var(--bg-4); color: var(--text-1); width: 32px; height: 32px; border-radius: 6px; cursor: pointer; place-items: center; box-shadow: 0 2px 6px rgba(0,0,0,.35); } #btn-sidebar-expand .material-symbols-outlined { font-size: 18px; } #btn-sidebar-expand:hover { background: var(--bg-3); color: var(--text-0); } body.sidebar-collapsed #btn-sidebar-expand { display: grid; } .poc-tag { font-size: 10px; background: var(--yellow); color: #000; border-radius: 4px; padding: 1px 5px; vertical-align: 2px; margin-left: 6px; font-weight: 800; } .channel-group-label { padding: 18px 8px 4px 16px; font-size: 11px; font-weight: 700; letter-spacing: .4px; color: var(--text-2); text-transform: uppercase; } .channel { margin: 1px 8px; padding: 7px 8px; border-radius: 6px; color: var(--text-1); cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; gap: 6px; } .channel:hover { background: var(--bg-3); color: var(--text-0); } .channel.text { color: var(--text-2); } .channel.text.active { background: rgba(88,101,242,.25); color: var(--text-0); } .channel.text .hint { font-size: 10px; margin-left: 4px; } .channel.voice.active { background: rgba(88,101,242,.25); color: var(--text-0); } .channel .count { margin-left: auto; color: var(--text-2); font-size: 12px; } #user-panel { margin-top: auto; background: var(--bg-0); padding: 8px; display: flex; flex-direction: column; gap: 6px; } /* row 1: available tools */ .user-buttons { display: flex; align-items: center; justify-content: center; gap: 4px; padding-bottom: 6px; border-bottom: 1px solid rgba(255,255,255,.06); } .ctl { background: transparent; border: none; color: var(--text-1); font-size: 15px; width: 34px; height: 30px; border-radius: 6px; cursor: pointer; position: relative; display: grid; place-items: center; padding: 0; transition: background .12s, color .12s; } .ctl .material-symbols-outlined { font-size: 19px; } .ctl:hover { background: var(--bg-3); color: var(--text-0); } .ctl.off { color: var(--red); } .ctl:disabled { opacity: .35; cursor: not-allowed; } .ctl.active { color: var(--green); } /* row 2: user info + help */ .me { display: flex; align-items: center; gap: 8px; min-width: 0; } .me-meta { display: flex; flex-direction: column; min-width: 0; flex: 1; overflow: hidden; } #nick { background: transparent; border: none; color: var(--text-0); font: inherit; font-weight: 600; font-size: 13px; width: 100%; padding: 0; outline: none; border-bottom: 1px dashed transparent; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #nick:hover, #nick:focus { border-bottom-color: var(--text-2); } .status-row { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2; } .status-indicator { width: 7px; height: 7px; border-radius: 50%; background: var(--text-2); flex-shrink: 0; } .status-indicator.online { background: var(--green); } .status-indicator.connecting { background: var(--yellow); } .status-indicator.error { background: var(--red); } #voice-status { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .help-ctl { flex-shrink: 0; color: var(--text-2); } /* main */ #main { flex: 1; display: flex; flex-direction: column; min-width: 0; position: relative; height: 100%; } #voice-view { flex: 1; display: flex; flex-direction: column; min-width: 0; height: 100%; } #voice-view.hidden { display: none; } #stage { flex: 1; display: flex; flex-direction: column; padding: 16px; gap: 16px; overflow: auto; position: relative; } #stage-empty { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; gap: 8px; color: var(--text-1); padding: 24px; isolation: isolate; overflow: hidden; } #stage-empty::before, #stage-empty::after { content: ""; position: absolute; left: 50%; top: 50%; z-index: -2; width: min(48vw, 440px); aspect-ratio: 1; border-radius: 50%; transform: translate(-50%, -54%); pointer-events: none; } #stage-empty::before { background: radial-gradient(circle, rgba(88,101,242,.13), rgba(88,101,242,0) 68%); } #stage-empty::after { width: min(31vw, 280px); border: 1px solid rgba(88,101,242,.14); box-shadow: 0 0 0 24px rgba(88,101,242,.035), 0 0 0 48px rgba(88,101,242,.025); } #stage-empty .big-icon { position: absolute; left: 50%; top: 50%; z-index: -1; color: rgba(139,148,255,.11); transform: translate(-50%, -57%) rotate(-8deg); pointer-events: none; } #stage-empty .big-icon .material-symbols-outlined { font-size: clamp(180px, 25vw, 330px); font-variation-settings: 'wght' 300; } #stage-empty h2 { font-size: 20px; color: var(--text-0); } #stage-empty p { max-width: 420px; color: var(--text-2); } #stage.empty #stage-empty { display: grid; } #stage:not(.empty) #stage-empty { display: none; } #screens { display: flex; gap: 12px; flex-wrap: wrap; flex: 1; min-height: 0; align-items: stretch; } #screens.multi { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); align-content: stretch; } #screens.multi .screen-card { flex: none; } .screen-card.popped { min-height: 120px; display: grid; place-items: center; background: var(--bg-0); box-shadow: none; border: 1px dashed var(--bg-4); } .pop-btn { position: absolute; right: 8px; top: 8px; z-index: 2; background: rgba(0,0,0,.65); color: var(--text-0); border: none; cursor: pointer; border-radius: 5px; padding: 4px 8px; font-size: 12px; font-weight: 600; display: inline-flex; align-items: center; gap: 4px; } .pop-btn .material-symbols-outlined { font-size: 16px; } .pop-btn:hover { background: var(--brand); } .screen-card .stop-share-btn { right: 96px; } .screen-card { position: relative; background: #000; border-radius: var(--radius); overflow: hidden; flex: 1 1 480px; max-width: 100%; box-shadow: 0 8px 24px rgba(0,0,0,.4); } .screen-card video { width: 100%; height: 100%; display: block; max-height: 100%; object-fit: contain; } .screen-card .label { position: absolute; left: 8px; bottom: 8px; background: rgba(0,0,0,.65); padding: 3px 8px; border-radius: 5px; font-size: 12px; font-weight: 600; display: inline-flex; align-items: center; } .screen-card .label .live { color: var(--red); font-weight: 800; margin-right: 5px; } .screen-card.speaking { box-shadow: 0 0 0 3px var(--green), 0 8px 24px rgba(0,0,0,.4); } .tile-mic-badge { margin-left: 6px; display: inline-flex; align-items: center; } .tile-mic-badge .material-symbols-outlined { font-size: 15px; color: var(--red); } /* channel user lines */ .channel-users { margin: 2px 8px 8px 16px; display: flex; flex-direction: column; gap: 2px; } .channel-user-item { position: relative; display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 4px; color: var(--text-1); font-size: 13px; cursor: default; } .channel-user-item:hover { background: var(--bg-3); color: var(--text-0); } .channel-user-item.speaking .user-line-avatar { box-shadow: 0 0 0 2px var(--green); } .user-line-avatar { width: 22px; height: 22px; font-size: 11px; } .user-line-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .user-line-badges { display: flex; align-items: center; gap: 4px; font-size: 12px; } .user-line-badges .material-symbols-outlined { font-size: 15px; } .badge-conn { color: var(--yellow); font-weight: bold; } .badge-mute { color: var(--red); display: inline-flex; align-items: center; } .badge-deafen { color: var(--red); display: inline-flex; align-items: center; } .badge-share { color: var(--brand); display: inline-flex; align-items: center; } /* hover volume popup */ .volume-popup { position: absolute; left: calc(100% + 6px); top: 50%; transform: translateY(-50%); background: var(--bg-0); border: 1px solid var(--bg-4); border-radius: var(--radius); padding: 8px 12px; width: 150px; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45); display: none; z-index: 100; pointer-events: auto; } .channel-user-item:hover .volume-popup, .channel-user-item.show-vol .volume-popup { display: flex; flex-direction: column; gap: 6px; } .volume-popup .vol-title { font-size: 11px; font-weight: 600; color: var(--text-1); white-space: nowrap; } .volume-popup input[type=range] { width: 100%; accent-color: var(--brand); } .avatar { width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; font-weight: 700; color: #fff; flex-shrink: 0; user-select: none; } .channel-user-item div.user-line-avatar { width: 24px; height: 24px; } /* spatial plane */ #spatial-resizer { height: 6px; flex-shrink: 0; cursor: row-resize; background: transparent; position: relative; } #spatial-resizer::after { content: ''; position: absolute; left: 0; right: 0; top: 2px; height: 2px; } #spatial-resizer:hover, #spatial-resizer.dragging { background: var(--brand); opacity: .5; } #spatial-wrap { height: 34vh; min-height: 220px; background: var(--bg-1); border-top: 1px solid rgba(0,0,0,.35); display: flex; flex-direction: column; } .spatial-label { padding: 8px 16px; font-size: 11px; font-weight: 700; color: var(--text-2); letter-spacing: .5px; display: flex; align-items: center; gap: 16px; } .spatial-label .hint { font-weight: 400; letter-spacing: 0; text-transform: none; flex: 1; } .spatial-action-btn { background: var(--bg-2); border: 1px solid var(--bg-4); color: var(--text-1); padding: 3px 8px; border-radius: 4px; font-size: 11px; cursor: pointer; display: inline-flex; align-items: center; gap: 4px; transition: background 0.15s, color 0.15s; } .spatial-action-btn:hover { background: var(--bg-3); color: var(--text-0); } #spatial { flex: 1; width: 100%; height: 100%; background: radial-gradient(circle, rgba(255, 255, 255, 0.03) 1px, transparent 1px); background-size: 24px 24px; position: relative; overflow: hidden; } /* modal */ #modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.7); display: grid; place-items: center; z-index: 50; } #modal-backdrop.hidden { display: none; } #settings-modal { width: min(620px, 92vw); max-height: 90vh; overflow: auto; background: var(--bg-1); border-radius: 8px; padding: 20px; box-shadow: 0 12px 40px rgba(0,0,0,.6); } #settings-modal h2 { font-size: 18px; margin-bottom: 6px; } #settings-modal h3 { font-size: 13px; margin: 16px 0 4px; color: var(--text-1); } #settings-modal p { margin-bottom: 10px; } .settings-select { width: 100%; background: var(--bg-0); color: var(--text-0); border: 1px solid var(--bg-4); border-radius: 6px; padding: 9px 10px; margin-bottom: 4px; font: inherit; } .settings-select:focus { outline: none; border-color: var(--brand); } #ice-json { width: 100%; background: var(--bg-0); color: var(--text-0); border: 1px solid var(--bg-4); border-radius: 6px; padding: 10px; font: 12px/1.5 ui-monospace, "Cascadia Code", monospace; resize: vertical; } #ice-json:focus { outline: none; border-color: var(--brand); } #ice-server-default { background: var(--bg-0); padding: 8px 10px; border-radius: 6px; font: 11px/1.5 ui-monospace, monospace; white-space: pre-wrap; } .toggle-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; cursor: pointer; } .toggle-row input { accent-color: var(--brand); width: 16px; height: 16px; } .modal-actions { display: flex; gap: 8px; margin-top: 12px; align-items: center; } .modal-actions .spacer { flex: 1; } .modal-actions button { padding: 8px 16px; border-radius: 6px; border: none; cursor: pointer; font-weight: 600; font-size: 13px; } .modal-actions button:not(.ghost) { background: var(--brand); color: #fff; } .modal-actions button.ghost { background: transparent; color: var(--text-1); } .modal-actions button.ghost:hover { text-decoration: underline; } /* welcome modal */ #welcome-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.7); display: grid; place-items: center; z-index: 60; } #welcome-backdrop.hidden { display: none; } #welcome-modal { width: min(480px, 92vw); max-height: 90vh; overflow: auto; background: var(--bg-1); border-radius: 8px; padding: 20px; box-shadow: 0 12px 40px rgba(0,0,0,.6); } #welcome-modal h2 { font-size: 18px; margin-bottom: 6px; } #welcome-modal h3 { font-size: 13px; margin: 18px 0 8px; color: var(--text-1); } .field-label { display: block; font-size: 12px; color: var(--text-1); margin-bottom: 4px; } #welcome-nick { width: 100%; background: var(--bg-0); color: var(--text-0); border: 1px solid var(--bg-4); border-radius: 6px; padding: 8px 10px; font-size: 14px; } #welcome-nick:focus { outline: none; border-color: var(--brand); } #welcome-nick.invalid { border-color: var(--red); } .field-error { color: var(--red); font-size: 12px; margin-top: 4px; } .field-error.hidden { display: none; } .howto-list { display: flex; flex-direction: column; gap: 8px; padding-left: 18px; font-size: 13px; color: var(--text-1); } .howto-list li { line-height: 1.4; } /* matrix text chat panel (main view) */ #mx-panel { flex: 1; width: 100%; height: 100%; background: var(--bg-2); display: flex; flex-direction: column; overflow: hidden; } #mx-panel.hidden { display: none; } .mx-header { padding: 14px 16px; font-weight: 700; font-size: 15px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid rgba(0,0,0,.25); box-shadow: 0 1px 0 rgba(0,0,0,.06); flex-shrink: 0; background: var(--bg-2); } .mx-header-info { display: flex; align-items: center; gap: 8px; } .mx-header-title { color: var(--text-0); font-weight: 700; } .mx-header button.ghost { background: transparent; border: none; color: var(--text-1); display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 6px; cursor: pointer; padding: 0; } .mx-header button.ghost:hover { background: var(--bg-3); color: var(--text-0); } #mx-messages { flex: 1; overflow-y: auto; padding: 16px 20px; display: flex; flex-direction: column; gap: 10px; } .mx-msg { font-size: 14px; line-height: 1.45; } .mx-sender { font-weight: 700; color: var(--text-0); margin-right: 8px; } .mx-body { color: var(--text-1); white-space: pre-wrap; overflow-wrap: anywhere; } .mx-composer { display: flex; gap: 10px; padding: 14px 20px; border-top: 1px solid rgba(0,0,0,.2); background: var(--bg-2); flex-shrink: 0; } .mx-composer textarea { flex: 1; resize: none; background: var(--bg-0); border: 1px solid var(--bg-4); border-radius: 8px; color: var(--text-0); padding: 10px 14px; font: inherit; max-height: 120px; } .mx-composer textarea:focus { outline: none; border-color: var(--brand); } .mx-composer button { background: var(--brand); border: none; color: #fff; border-radius: 8px; padding: 0 18px; font-weight: 600; cursor: pointer; } /* reconnect banner */ #reconnect-banner { position: fixed; top: 16px; left: 50%; transform: translateX(-50%); z-index: 70; display: flex; align-items: center; gap: 12px; background: var(--bg-0); border: 1px solid var(--red); box-shadow: 0 8px 28px rgba(0,0,0,.55), 0 0 0 1px rgba(242,63,67,.15); border-radius: 10px; padding: 10px 14px; font-size: 13px; animation: banner-in .2s ease-out; } #reconnect-banner.hidden { display: none; } #reconnect-banner #reconnect-text { color: var(--text-0); font-weight: 600; white-space: nowrap; } .reconnect-spinner { width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0; border: 2px solid rgba(242,63,67,.25); border-top-color: var(--red); animation: spin .8s linear infinite; } .reconnect-actions { display: flex; gap: 6px; margin-left: 4px; } .reconnect-actions button { padding: 5px 10px; border-radius: 6px; border: none; cursor: pointer; font-weight: 600; font-size: 12px; background: var(--red); color: #fff; } .reconnect-actions button.ghost { background: transparent; color: var(--text-1); } .reconnect-actions button.ghost:hover { color: var(--text-0); text-decoration: underline; } @keyframes spin { to { transform: rotate(360deg); } } @keyframes banner-in { from { transform: translate(-50%, -12px); opacity: 0; } } /* toasts */ #toasts { position: fixed; bottom: 16px; right: 16px; display: flex; flex-direction: column; gap: 8px; z-index: 60; max-width: 380px; } .toast { background: var(--bg-0); border-left: 3px solid var(--brand); border-radius: 6px; padding: 10px 14px; box-shadow: 0 8px 20px rgba(0,0,0,.5); font-size: 13px; animation: slide-in .18s ease-out; } .toast.error { border-left-color: var(--red); } .toast.warn { border-left-color: var(--yellow); } .toast.ok { border-left-color: var(--green); } @keyframes slide-in { from { transform: translateX(30px); opacity: 0; } } .hint { color: var(--text-2); font-size: 12px; } code { font-family: ui-monospace, monospace; font-size: 12px; } /* mobile chrome — hidden on desktop, shown/positioned in the breakpoint below */ #mobile-topbar, #sidebar-backdrop, #mobile-controls { display: none; } /* --------------------------------------------------------------------------- mobile layout - sidebar becomes an off-canvas drawer opened from #mobile-topbar - mic/deafen/share get an always-visible mirror bar pinned to the bottom --------------------------------------------------------------------------- */ @media (max-width: 860px) { #app { padding-top: 48px; } /* room for the fixed topbar */ #mobile-topbar { display: flex; align-items: center; gap: 10px; position: fixed; top: 0; left: 0; right: 0; height: 48px; z-index: 55; background: var(--bg-0); padding: 0 12px; box-shadow: 0 1px 0 rgba(0,0,0,.3); } #mobile-topbar button { background: transparent; border: none; color: var(--text-0); font-size: 20px; width: 24px; height: 24px; border-radius: 6px; cursor: pointer; } #mobile-topbar button:active { background: var(--bg-3); } #mobile-title { font-weight: 700; font-size: 14px; } #mobile-status-row { display: flex; align-items: center; gap: 5px; margin-left: auto; font-size: 12px; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #sidebar-backdrop.show { display: block; position: fixed; inset: 0; top: 48px; z-index: 65; background: rgba(0,0,0,.55); } #sidebar { position: fixed; top: 48px; bottom: 0; left: 0; z-index: 66; width: 82vw; max-width: 320px; transform: translateX(-100%); transition: transform .2s ease; box-shadow: 8px 0 24px rgba(0,0,0,.5); } #sidebar.open { transform: translateX(0); } /* desktop sidebar collapse/resize is irrelevant here — the drawer always exists, it's just toggled on/off screen via .open */ body.sidebar-collapsed #sidebar { display: flex; } #sidebar-resizer, #spatial-resizer, #btn-sidebar-expand, #btn-sidebar-collapse { display: none !important; } #main { width: 100%; padding-bottom: 64px; } /* room for the fixed bottom controls */ #stage { padding: 10px; gap: 10px; } #screens { flex-direction: column; } #screens .screen-card, #screens.multi .screen-card { flex: 1 1 auto; width: 100%; min-height: 220px; } #spatial-wrap { height: 26vh; min-height: 160px; } .spatial-label { flex-wrap: wrap; gap: 6px; padding: 6px 10px; } .spatial-label .hint { display: none; } /* the drag/zoom hint doesn't fit — controls are still discoverable by touch */ /* bigger touch targets everywhere controls are tapped directly */ .ctl { width: 40px; height: 40px; font-size: 18px; } #mobile-controls { display: flex; justify-content: center; gap: 10px; position: fixed; bottom: 0; left: 0; right: 0; z-index: 55; background: var(--bg-0); padding: 8px; box-shadow: 0 -1px 0 rgba(0,0,0,.3); } #mobile-controls .ctl { width: 48px; height: 48px; font-size: 20px; background: var(--bg-2); } /* volume popup: side placement would run off a narrow drawer */ .volume-popup { left: 0; right: 0; top: 100%; transform: none; width: auto; margin-top: 4px; } /* modals already clamp to 92vw; just tighten padding a bit */ #settings-modal, #welcome-modal { padding: 16px; } #toasts { left: 12px; right: 12px; max-width: none; } }