diff --git a/portal/static/chat.css b/portal/static/chat.css new file mode 100644 index 0000000..625fbee --- /dev/null +++ b/portal/static/chat.css @@ -0,0 +1,227 @@ +/* Aria chat widget */ +#aria-launcher { + position: fixed; + bottom: 28px; + right: 28px; + z-index: 9999; + display: flex; + align-items: center; + gap: 10px; +} + +#aria-bubble { + background: #c9a84c; + color: #0a1628; + font-family: 'Inter', sans-serif; + font-size: 13px; + font-weight: 600; + padding: 8px 14px; + border-radius: 20px; + box-shadow: 0 4px 16px rgba(0,0,0,0.3); + white-space: nowrap; + opacity: 0; + transform: translateX(8px); + transition: opacity 0.3s, transform 0.3s; + pointer-events: none; +} + +#aria-launcher:hover #aria-bubble { + opacity: 1; + transform: translateX(0); +} + +#aria-btn { + width: 52px; + height: 52px; + border-radius: 50%; + background: #c9a84c; + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4px 20px rgba(201,168,76,0.4); + transition: transform 0.2s, box-shadow 0.2s; + flex-shrink: 0; +} + +#aria-btn:hover { + transform: scale(1.08); + box-shadow: 0 6px 24px rgba(201,168,76,0.5); +} + +#aria-btn svg { width: 24px; height: 24px; } + +#aria-window { + position: fixed; + bottom: 92px; + right: 28px; + width: 360px; + max-height: 520px; + background: #0d1f3c; + border: 1px solid rgba(255,255,255,0.1); + border-radius: 16px; + box-shadow: 0 16px 48px rgba(0,0,0,0.5); + display: flex; + flex-direction: column; + z-index: 9998; + overflow: hidden; + font-family: 'Inter', sans-serif; + transform: scale(0.95) translateY(12px); + opacity: 0; + pointer-events: none; + transition: transform 0.2s, opacity 0.2s; +} + +#aria-window.open { + transform: scale(1) translateY(0); + opacity: 1; + pointer-events: all; +} + +#aria-header { + background: #0a1628; + border-bottom: 1px solid rgba(255,255,255,0.08); + padding: 14px 16px; + display: flex; + align-items: center; + gap: 10px; +} + +#aria-avatar { + width: 32px; + height: 32px; + border-radius: 50%; + background: rgba(201,168,76,0.2); + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + flex-shrink: 0; +} + +#aria-header-text { flex: 1; } +#aria-header-name { color: #fff; font-size: 14px; font-weight: 600; } +#aria-header-status { color: #4ade80; font-size: 11px; } + +#aria-close { + background: none; + border: none; + color: #94a3b8; + font-size: 20px; + cursor: pointer; + padding: 0 4px; + line-height: 1; +} +#aria-close:hover { color: #fff; } + +#aria-messages { + flex: 1; + overflow-y: auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 10px; + min-height: 200px; +} + +#aria-messages::-webkit-scrollbar { width: 4px; } +#aria-messages::-webkit-scrollbar-track { background: transparent; } +#aria-messages::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; } + +.aria-msg { + max-width: 85%; + padding: 10px 14px; + border-radius: 12px; + font-size: 13px; + line-height: 1.5; +} + +.aria-msg.bot { + background: rgba(255,255,255,0.06); + color: #e2e8f0; + align-self: flex-start; + border-bottom-left-radius: 4px; +} + +.aria-msg.user { + background: #c9a84c; + color: #0a1628; + align-self: flex-end; + border-bottom-right-radius: 4px; + font-weight: 500; +} + +.aria-typing { + display: flex; + gap: 4px; + align-items: center; + padding: 12px 14px; + background: rgba(255,255,255,0.06); + border-radius: 12px; + border-bottom-left-radius: 4px; + align-self: flex-start; +} + +.aria-typing span { + width: 6px; + height: 6px; + border-radius: 50%; + background: #94a3b8; + animation: aria-bounce 1.2s infinite; +} +.aria-typing span:nth-child(2) { animation-delay: 0.2s; } +.aria-typing span:nth-child(3) { animation-delay: 0.4s; } + +@keyframes aria-bounce { + 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } + 30% { transform: translateY(-4px); opacity: 1; } +} + +#aria-compose { + border-top: 1px solid rgba(255,255,255,0.08); + padding: 12px; + display: flex; + gap: 8px; + align-items: flex-end; +} + +#aria-input { + flex: 1; + background: rgba(255,255,255,0.06); + border: 1px solid rgba(255,255,255,0.1); + border-radius: 10px; + padding: 8px 12px; + color: #fff; + font-size: 13px; + font-family: 'Inter', sans-serif; + resize: none; + outline: none; + max-height: 100px; + line-height: 1.4; +} + +#aria-input:focus { border-color: rgba(201,168,76,0.5); } +#aria-input::placeholder { color: #475569; } + +#aria-send { + width: 36px; + height: 36px; + border-radius: 9px; + background: #c9a84c; + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + transition: background 0.2s; +} +#aria-send:hover { background: #b8973f; } +#aria-send:disabled { background: rgba(201,168,76,0.3); cursor: not-allowed; } +#aria-send svg { width: 16px; height: 16px; color: #0a1628; } + +@media (max-width: 420px) { + #aria-window { right: 12px; left: 12px; width: auto; } + #aria-launcher { right: 12px; bottom: 16px; } +} diff --git a/portal/static/chat.js b/portal/static/chat.js new file mode 100644 index 0000000..c13e7f0 --- /dev/null +++ b/portal/static/chat.js @@ -0,0 +1,157 @@ +(function () { + 'use strict'; + + const SESSION_KEY = 'aria_session_id'; + let sessionId = localStorage.getItem(SESSION_KEY) || ''; + let history = []; + let isOpen = false; + let isTyping = false; + + const GREETING = "Hi! I'm Aria, the Dealspace assistant. Ask me anything about how Dealspace works, pricing, or security."; + + function init() { + // Launcher + const launcher = document.createElement('div'); + launcher.id = 'aria-launcher'; + launcher.innerHTML = ` +