/* ═══════════════════════════════════════════════════════════════════════════ Portfolio Assistant - Dark Theme with Animated Stars ═══════════════════════════════════════════════════════════════════════════ */ :root { --bg-primary: #0a0a0a; --bg-secondary: #111111; --bg-tertiary: #1a1a1a; --text-primary: #ffffff; --text-secondary: #a0a0a0; --text-muted: #666666; --accent: #6366f1; --accent-light: #818cf8; --success: #22c55e; --warning: #f59e0b; --error: #ef4444; --border: #2a2a2a; --glass: rgba(255, 255, 255, 0.05); --glass-border: rgba(255, 255, 255, 0.1); } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: var(--bg-primary); color: var(--text-primary); min-height: 100vh; overflow-x: hidden; } /* ═══════════════════════════════════════════════════════════════════════════ Animated Star Background ═══════════════════════════════════════════════════════════════════════════ */ .star-field { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; overflow: hidden; } .star { position: absolute; background: white; border-radius: 50%; animation: twinkle var(--duration, 3s) ease-in-out infinite; animation-delay: var(--delay, 0s); opacity: 0; width: var(--size, 2px); height: var(--size, 2px); } @keyframes twinkle { 0%, 100% { opacity: 0; transform: scale(0.5); } 50% { opacity: var(--opacity, 0.8); transform: scale(1); } } /* Generate 100 stars with random positions using nth-child */ .star:nth-child(1) { left: 2%; top: 5%; --duration: 3s; --opacity: 0.9; --delay: 0.1s; --size: 2px; } .star:nth-child(2) { left: 8%; top: 15%; --duration: 4s; --opacity: 0.7; --delay: 0.5s; --size: 1px; } .star:nth-child(3) { left: 12%; top: 8%; --duration: 3.5s; --opacity: 0.8; --delay: 1s; --size: 3px; } .star:nth-child(4) { left: 18%; top: 25%; --duration: 5s; --opacity: 0.6; --delay: 0.3s; --size: 2px; } .star:nth-child(5) { left: 25%; top: 12%; --duration: 4.5s; --opacity: 0.9; --delay: 1.5s; --size: 1px; } .star:nth-child(6) { left: 32%; top: 35%; --duration: 3s; --opacity: 0.7; --delay: 2s; --size: 2px; } .star:nth-child(7) { left: 38%; top: 18%; --duration: 4s; --opacity: 0.8; --delay: 0.8s; --size: 3px; } .star:nth-child(8) { left: 45%; top: 42%; --duration: 5s; --opacity: 0.6; --delay: 1.2s; --size: 1px; } .star:nth-child(9) { left: 52%; top: 28%; --duration: 3.5s; --opacity: 0.9; --delay: 0.1s; --size: 2px; } .star:nth-child(10) { left: 58%; top: 8%; --duration: 4.5s; --opacity: 0.7; --delay: 2.5s; --size: 2px; } .star:nth-child(11) { left: 65%; top: 48%; --duration: 3s; --opacity: 0.8; --delay: 0.4s; --size: 1px; } .star:nth-child(12) { left: 72%; top: 22%; --duration: 4s; --opacity: 0.6; --delay: 1.8s; --size: 3px; } .star:nth-child(13) { left: 78%; top: 55%; --duration: 5s; --opacity: 0.9; --delay: 0.9s; --size: 2px; } .star:nth-child(14) { left: 85%; top: 32%; --duration: 3.5s; --opacity: 0.7; --delay: 2.2s; --size: 1px; } .star:nth-child(15) { left: 92%; top: 15%; --duration: 4.5s; --opacity: 0.8; --delay: 0.6s; --size: 2px; } .star:nth-child(16) { left: 5%; top: 62%; --duration: 3s; --opacity: 0.6; --delay: 1.4s; --size: 3px; } .star:nth-child(17) { left: 15%; top: 45%; --duration: 4s; --opacity: 0.9; --delay: 0.2s; --size: 1px; } .star:nth-child(18) { left: 22%; top: 72%; --duration: 5s; --opacity: 0.7; --delay: 1.6s; --size: 2px; } .star:nth-child(19) { left: 28%; top: 58%; --duration: 3.5s; --opacity: 0.8; --delay: 2.8s; --size: 2px; } .star:nth-child(20) { left: 35%; top: 82%; --duration: 4.5s; --opacity: 0.6; --delay: 0.7s; --size: 1px; } .star:nth-child(21) { left: 42%; top: 65%; --duration: 3s; --opacity: 0.9; --delay: 1.1s; --size: 3px; } .star:nth-child(22) { left: 48%; top: 78%; --duration: 4s; --opacity: 0.7; --delay: 2.3s; --size: 2px; } .star:nth-child(23) { left: 55%; top: 52%; --duration: 5s; --opacity: 0.8; --delay: 0.05s; --size: 1px; } .star:nth-child(24) { left: 62%; top: 88%; --duration: 3.5s; --opacity: 0.6; --delay: 1.9s; --size: 2px; } .star:nth-child(25) { left: 68%; top: 68%; --duration: 4.5s; --opacity: 0.9; --delay: 2.6s; --size: 3px; } .star:nth-child(26) { left: 75%; top: 75%; --duration: 3s; --opacity: 0.7; --delay: 0.15s; --size: 1px; } .star:nth-child(27) { left: 82%; top: 62%; --duration: 4s; --opacity: 0.8; --delay: 1.3s; --size: 2px; } .star:nth-child(28) { left: 88%; top: 85%; --duration: 5s; --opacity: 0.6; --delay: 2.1s; --size: 2px; } .star:nth-child(29) { left: 95%; top: 72%; --duration: 3.5s; --opacity: 0.9; --delay: 0.85s; --size: 1px; } .star:nth-child(30) { left: 3%; top: 92%; --duration: 4.5s; --opacity: 0.7; --delay: 1.7s; --size: 3px; } .star:nth-child(31) { left: 10%; top: 38%; --duration: 3s; --opacity: 0.8; --delay: 0.3s; --size: 2px; } .star:nth-child(32) { left: 17%; top: 85%; --duration: 4s; --opacity: 0.6; --delay: 2.4s; --size: 1px; } .star:nth-child(33) { left: 24%; top: 52%; --duration: 5s; --opacity: 0.9; --delay: 0.55s; --size: 2px; } .star:nth-child(34) { left: 31%; top: 92%; --duration: 3.5s; --opacity: 0.7; --delay: 1.25s; --size: 3px; } .star:nth-child(35) { left: 37%; top: 28%; --duration: 4.5s; --opacity: 0.8; --delay: 2.7s; --size: 1px; } .star:nth-child(36) { left: 44%; top: 95%; --duration: 3s; --opacity: 0.6; --delay: 0.45s; --size: 2px; } .star:nth-child(37) { left: 51%; top: 38%; --duration: 4s; --opacity: 0.9; --delay: 1.55s; --size: 2px; } .star:nth-child(38) { left: 57%; top: 72%; --duration: 5s; --opacity: 0.7; --delay: 2.15s; --size: 1px; } .star:nth-child(39) { left: 64%; top: 5%; --duration: 3.5s; --opacity: 0.8; --delay: 0.35s; --size: 3px; } .star:nth-child(40) { left: 71%; top: 95%; --duration: 4.5s; --opacity: 0.6; --delay: 1.85s; --size: 2px; } .star:nth-child(n+41) { left: calc(2% + (var(--n, 0) * 2.3%)); top: calc(5% + (var(--n, 0) * 1.8%)); --duration: 3.5s; --opacity: 0.7; } .star:nth-child(41) { --n: 1; left: 4%; top: 18%; } .star:nth-child(42) { --n: 2; left: 9%; top: 32%; } .star:nth-child(43) { --n: 3; left: 14%; top: 48%; } .star:nth-child(44) { --n: 4; left: 19%; top: 62%; } .star:nth-child(45) { --n: 5; left: 24%; top: 78%; } .star:nth-child(46) { --n: 6; left: 29%; top: 12%; } .star:nth-child(47) { --n: 7; left: 34%; top: 28%; } .star:nth-child(48) { --n: 8; left: 39%; top: 42%; } .star:nth-child(49) { --n: 9; left: 44%; top: 58%; } .star:nth-child(50) { --n: 10; left: 49%; top: 72%; } .star:nth-child(51) { left: 54%; top: 88%; --duration: 4s; --opacity: 0.8; --delay: 0.5s; } .star:nth-child(52) { left: 59%; top: 22%; --duration: 3s; --opacity: 0.6; --delay: 1.2s; } .star:nth-child(53) { left: 64%; top: 38%; --duration: 5s; --opacity: 0.9; --delay: 2s; } .star:nth-child(54) { left: 69%; top: 52%; --duration: 3.5s; --opacity: 0.7; --delay: 0.8s; } .star:nth-child(55) { left: 74%; top: 68%; --duration: 4.5s; --opacity: 0.8; --delay: 1.5s; } .star:nth-child(56) { left: 79%; top: 82%; --duration: 3s; --opacity: 0.6; --delay: 2.3s; } .star:nth-child(57) { left: 84%; top: 18%; --duration: 4s; --opacity: 0.9; --delay: 0.3s; } .star:nth-child(58) { left: 89%; top: 32%; --duration: 5s; --opacity: 0.7; --delay: 1.8s; } .star:nth-child(59) { left: 94%; top: 48%; --duration: 3.5s; --opacity: 0.8; --delay: 2.6s; } .star:nth-child(60) { left: 99%; top: 62%; --duration: 4.5s; --opacity: 0.6; --delay: 0.9s; } .star:nth-child(61) { left: 6%; top: 75%; --duration: 3s; --opacity: 0.9; --delay: 1.6s; --size: 3px; } .star:nth-child(62) { left: 11%; top: 88%; --duration: 4s; --opacity: 0.7; --delay: 2.4s; } .star:nth-child(63) { left: 16%; top: 2%; --duration: 5s; --opacity: 0.8; --delay: 0.6s; } .star:nth-child(64) { left: 21%; top: 15%; --duration: 3.5s; --opacity: 0.6; --delay: 1.3s; } .star:nth-child(65) { left: 26%; top: 28%; --duration: 4.5s; --opacity: 0.9; --delay: 2.1s; } .star:nth-child(66) { left: 30%; top: 42%; --duration: 3s; --opacity: 0.7; --delay: 0.4s; --size: 3px; } .star:nth-child(67) { left: 36%; top: 55%; --duration: 4s; --opacity: 0.8; --delay: 1.1s; } .star:nth-child(68) { left: 41%; top: 68%; --duration: 5s; --opacity: 0.6; --delay: 1.9s; } .star:nth-child(69) { left: 46%; top: 82%; --duration: 3.5s; --opacity: 0.9; --delay: 2.7s; } .star:nth-child(70) { left: 50%; top: 95%; --duration: 4.5s; --opacity: 0.7; --delay: 0.2s; } .star:nth-child(71) { left: 56%; top: 8%; --duration: 3s; --opacity: 0.8; --delay: 0.95s; --size: 3px; } .star:nth-child(72) { left: 60%; top: 22%; --duration: 4s; --opacity: 0.6; --delay: 1.65s; } .star:nth-child(73) { left: 66%; top: 35%; --duration: 5s; --opacity: 0.9; --delay: 2.35s; } .star:nth-child(74) { left: 70%; top: 48%; --duration: 3.5s; --opacity: 0.7; --delay: 0.55s; } .star:nth-child(75) { left: 76%; top: 62%; --duration: 4.5s; --opacity: 0.8; --delay: 1.25s; } .star:nth-child(76) { left: 80%; top: 75%; --duration: 3s; --opacity: 0.6; --delay: 1.95s; --size: 3px; } .star:nth-child(77) { left: 86%; top: 88%; --duration: 4s; --opacity: 0.9; --delay: 2.65s; } .star:nth-child(78) { left: 90%; top: 2%; --duration: 5s; --opacity: 0.7; --delay: 0.35s; } .star:nth-child(79) { left: 96%; top: 15%; --duration: 3.5s; --opacity: 0.8; --delay: 1.05s; } .star:nth-child(80) { left: 1%; top: 28%; --duration: 4.5s; --opacity: 0.6; --delay: 1.75s; } .star:nth-child(81) { left: 7%; top: 42%; --duration: 3s; --opacity: 0.9; --delay: 2.45s; --size: 3px; } .star:nth-child(82) { left: 13%; top: 55%; --duration: 4s; --opacity: 0.7; --delay: 0.15s; } .star:nth-child(83) { left: 18%; top: 68%; --duration: 5s; --opacity: 0.8; --delay: 0.85s; } .star:nth-child(84) { left: 23%; top: 82%; --duration: 3.5s; --opacity: 0.6; --delay: 1.55s; } .star:nth-child(85) { left: 27%; top: 95%; --duration: 4.5s; --opacity: 0.9; --delay: 2.25s; } .star:nth-child(86) { left: 33%; top: 8%; --duration: 3s; --opacity: 0.7; --delay: 0.45s; --size: 3px; } .star:nth-child(87) { left: 38%; top: 22%; --duration: 4s; --opacity: 0.8; --delay: 1.15s; } .star:nth-child(88) { left: 43%; top: 35%; --duration: 5s; --opacity: 0.6; --delay: 1.85s; } .star:nth-child(89) { left: 47%; top: 48%; --duration: 3.5s; --opacity: 0.9; --delay: 2.55s; } .star:nth-child(90) { left: 53%; top: 62%; --duration: 4.5s; --opacity: 0.7; --delay: 0.25s; } .star:nth-child(91) { left: 58%; top: 75%; --duration: 3s; --opacity: 0.8; --delay: 0.95s; --size: 3px; } .star:nth-child(92) { left: 63%; top: 88%; --duration: 4s; --opacity: 0.6; --delay: 1.65s; } .star:nth-child(93) { left: 67%; top: 2%; --duration: 5s; --opacity: 0.9; --delay: 2.35s; } .star:nth-child(94) { left: 73%; top: 15%; --duration: 3.5s; --opacity: 0.7; --delay: 0.55s; } .star:nth-child(95) { left: 77%; top: 28%; --duration: 4.5s; --opacity: 0.8; --delay: 1.25s; } .star:nth-child(96) { left: 83%; top: 42%; --duration: 3s; --opacity: 0.6; --delay: 1.95s; --size: 3px; } .star:nth-child(97) { left: 87%; top: 55%; --duration: 4s; --opacity: 0.9; --delay: 2.65s; } .star:nth-child(98) { left: 93%; top: 68%; --duration: 5s; --opacity: 0.7; --delay: 0.35s; } .star:nth-child(99) { left: 97%; top: 82%; --duration: 3.5s; --opacity: 0.8; --delay: 1.05s; } .star:nth-child(100) { left: 4%; top: 95%; --duration: 4.5s; --opacity: 0.6; --delay: 1.75s; --size: 3px; } /* ═══════════════════════════════════════════════════════════════════════════ Layout ═══════════════════════════════════════════════════════════════════════════ */ .app-container { position: relative; z-index: 1; min-height: 100vh; display: flex; flex-direction: column; max-width: 900px; margin: 0 auto; padding: 24px; } .header { text-align: center; margin-bottom: 32px; } .header h1 { font-size: 2rem; font-weight: 700; background: linear-gradient(135deg, var(--accent-light), var(--accent)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 8px; } .header p { color: var(--text-secondary); font-size: 0.95rem; } /* ═══════════════════════════════════════════════════════════════════════════ File Upload ═══════════════════════════════════════════════════════════════════════════ */ .upload-zone { border: 2px dashed var(--border); border-radius: 16px; padding: 32px; text-align: center; cursor: pointer; transition: all 0.3s ease; background: var(--glass); backdrop-filter: blur(10px); margin-bottom: 24px; } .upload-zone:hover, .upload-zone.dragover { border-color: var(--accent); background: rgba(99, 102, 241, 0.1); } .upload-zone svg { color: var(--text-muted); margin-bottom: 12px; } .upload-zone p { color: var(--text-secondary); font-size: 0.9rem; } .upload-zone .formats { color: var(--text-muted); font-size: 0.8rem; margin-top: 8px; } /* Document List */ .documents { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 24px; } .doc-chip { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 20px; font-size: 0.85rem; } .doc-chip .type { color: var(--accent); font-weight: 600; text-transform: uppercase; font-size: 0.7rem; } .doc-chip button { background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 0; display: flex; } .doc-chip button:hover { color: var(--error); } /* ═══════════════════════════════════════════════════════════════════════════ Chat Interface ═══════════════════════════════════════════════════════════════════════════ */ .chat-container { flex: 1; display: flex; flex-direction: column; background: var(--glass); backdrop-filter: blur(10px); border: 1px solid var(--glass-border); border-radius: 16px; overflow: hidden; } .messages { flex: 1; overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 16px; min-height: 400px; max-height: 500px; } .message { max-width: 80%; padding: 12px 16px; border-radius: 16px; font-size: 0.95rem; line-height: 1.5; } .message.user { align-self: flex-end; background: linear-gradient(135deg, var(--accent), var(--accent-light)); color: white; border-bottom-right-radius: 4px; } .message.assistant { align-self: flex-start; background: var(--bg-tertiary); border: 1px solid var(--border); border-bottom-left-radius: 4px; } .message.assistant .sources { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); font-size: 0.8rem; color: var(--text-muted); } .message.assistant .source-chip { display: inline-block; background: var(--bg-secondary); padding: 4px 8px; border-radius: 4px; margin: 4px 4px 0 0; font-size: 0.75rem; } .welcome-message { text-align: center; color: var(--text-secondary); padding: 48px 24px; } .welcome-message svg { color: var(--accent); margin-bottom: 16px; } /* Chat Input */ .chat-input-container { display: flex; gap: 12px; padding: 16px; border-top: 1px solid var(--border); background: var(--bg-secondary); } .chat-input { flex: 1; background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 12px; padding: 12px 16px; color: var(--text-primary); font-size: 0.95rem; font-family: inherit; resize: none; outline: none; transition: border-color 0.2s; } .chat-input:focus { border-color: var(--accent); } .chat-input::placeholder { color: var(--text-muted); } .send-button { background: linear-gradient(135deg, var(--accent), var(--accent-light)); border: none; border-radius: 12px; padding: 12px 20px; color: white; cursor: pointer; display: flex; align-items: center; gap: 8px; font-weight: 500; transition: transform 0.2s, opacity 0.2s; } .send-button:hover:not(:disabled) { transform: translateY(-2px); } .send-button:disabled { opacity: 0.5; cursor: not-allowed; } /* ═══════════════════════════════════════════════════════════════════════════ Loading State ═══════════════════════════════════════════════════════════════════════════ */ .loading-dots { display: flex; gap: 4px; padding: 8px 0; } .loading-dots span { width: 8px; height: 8px; background: var(--accent); border-radius: 50%; animation: bounce 1.4s ease-in-out infinite; } .loading-dots span:nth-child(2) { animation-delay: 0.2s; } .loading-dots span:nth-child(3) { animation-delay: 0.4s; } @keyframes bounce { 0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; } 40% { transform: scale(1); opacity: 1; } } /* ═══════════════════════════════════════════════════════════════════════════ Markdown Content ═══════════════════════════════════════════════════════════════════════════ */ .message.assistant p { margin-bottom: 0.75em; } .message.assistant p:last-child { margin-bottom: 0; } .message.assistant code { background: var(--bg-primary); padding: 2px 6px; border-radius: 4px; font-family: 'JetBrains Mono', monospace; font-size: 0.85em; } .message.assistant pre { background: var(--bg-primary); padding: 12px; border-radius: 8px; overflow-x: auto; margin: 12px 0; } .message.assistant pre code { background: none; padding: 0; } /* ═══════════════════════════════════════════════════════════════════════════ Settings Panel (LLM TestLab style) ═══════════════════════════════════════════════════════════════════════════ */ .settings-panel { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 24px; } @media (max-width: 640px) { .settings-panel { grid-template-columns: 1fr; } } .settings-card { background: var(--glass); border: 1px solid var(--glass-border); border-radius: 12px; padding: 16px; backdrop-filter: blur(10px); } .card-header { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; color: var(--text-primary); font-weight: 600; font-size: 0.9rem; } .form-select, .form-input { width: 100%; background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; color: var(--text-primary); font-size: 0.9rem; font-family: inherit; outline: none; cursor: pointer; } .form-select:focus, .form-input:focus { border-color: var(--accent); } .form-input.error { border-color: var(--error); } .form-group { margin-top: 12px; } .form-label { display: flex; align-items: center; gap: 6px; font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 6px; } .model-count { color: var(--text-muted); font-weight: 400; } .spinner { animation: spin 1s linear infinite; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .status-badge { display: flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; font-size: 0.7rem; font-weight: 500; margin-left: auto; } .status-badge.success { background: rgba(34, 197, 94, 0.15); color: var(--success); } .status-text { display: flex; align-items: center; gap: 4px; font-size: 0.75rem; color: var(--text-muted); margin-top: 8px; } .status-text.success { color: var(--success); } .status-text.error { color: var(--error); } .test-connection-btn { width: 100%; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 10px 16px; margin-top: 12px; background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 8px; color: var(--text-primary); font-size: 0.85rem; font-weight: 500; cursor: pointer; transition: all 0.2s; } .test-connection-btn:hover:not(:disabled) { border-color: var(--accent); background: rgba(99, 102, 241, 0.1); } .test-connection-btn:disabled { opacity: 0.5; cursor: not-allowed; } .api-key-link { display: block; font-size: 0.75rem; color: var(--accent-light); margin-top: 12px; text-decoration: none; } .api-key-link:hover { text-decoration: underline; } .api-key-input:focus { border-color: var(--accent); } .api-key-section label { display: flex; align-items: center; gap: 8px; color: var(--text-secondary); font-size: 0.85rem; white-space: nowrap; } .provider-select { background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; color: var(--text-primary); font-size: 0.9rem; font-family: inherit; outline: none; cursor: pointer; min-width: 140px; } .provider-select:focus { border-color: var(--accent); } .provider-select option { background: var(--bg-secondary); color: var(--text-primary); } /* ═══════════════════════════════════════════════════════════════════════════ Responsive ═══════════════════════════════════════════════════════════════════════════ */ @media (max-width: 640px) { .app-container { padding: 16px; } .header h1 { font-size: 1.5rem; } .message { max-width: 90%; } .upload-zone { padding: 24px 16px; } }