MarketLens / fe /styles.css
royzhou01's picture
Fixed header layout and price chart resizing
87e19e9
/* ============================================
MarketLens - Design System
Direction: Data-dense, professional, terminal-inspired
Base unit: 16px | Depth: Subtle shadows
============================================ */
/* CSS Custom Properties */
:root {
/* Neutrals - Slate palette */
--color-bg: #f8fafc;
--color-bg-elevated: #ffffff;
--color-bg-subtle: #f1f5f9;
--color-bg-muted: #e2e8f0;
--color-border: #e2e8f0;
--color-border-subtle: #f1f5f9;
--color-text: #0f172a;
--color-text-secondary: #475569;
--color-text-muted: #94a3b8;
/* Accent - Indigo */
--color-accent: #4f46e5;
--color-accent-hover: #4338ca;
--color-accent-subtle: #eef2ff;
/* Semantic */
--color-positive: #059669;
--color-positive-bg: #ecfdf5;
--color-negative: #dc2626;
--color-negative-bg: #fef2f2;
--color-neutral-sentiment: #94a3b8;
--color-neutral-sentiment-bg: rgba(148, 163, 184, 0.15);
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Typography */
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', system-ui, sans-serif;
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
--text-2xs: 0.625rem;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 2rem;
/* Line heights */
--line-height-dense: 1.3;
--line-height-tight: 1.15;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04);
--shadow-md: 0 2px 8px rgba(15, 23, 42, 0.06);
--shadow-lg: 0 4px 16px rgba(15, 23, 42, 0.08);
/* Radius */
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--radius-xl: 12px;
/* Layout */
--header-height: 60px;
--chat-width: 340px;
}
/* Dark Mode */
[data-theme="dark"] {
--color-bg: #0f172a;
--color-bg-elevated: #1e293b;
--color-bg-subtle: #1e293b;
--color-bg-muted: #334155;
--color-border: #334155;
--color-border-subtle: #1e293b;
--color-text: #f1f5f9;
--color-text-secondary: #cbd5e1;
--color-text-muted: #64748b;
--color-accent: #818cf8;
--color-accent-hover: #a5b4fc;
--color-accent-subtle: rgba(129, 140, 248, 0.15);
--color-positive: #34d399;
--color-positive-bg: rgba(52, 211, 153, 0.15);
--color-negative: #f87171;
--color-negative-bg: rgba(248, 113, 113, 0.15);
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
--shadow-md: 0 2px 8px rgba(0, 0, 0, 0.25);
--shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.3);
}
/* Reset & Base */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
}
body {
font-family: var(--font-sans);
background-color: var(--color-bg);
color: var(--color-text);
line-height: 1.5;
font-size: var(--text-sm);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden;
}
/* ============================================
App Layout - Persistent Chat Panel
============================================ */
.global-header {
height: var(--header-height);
background: var(--color-bg-elevated);
border-bottom: 1px solid var(--color-border);
padding: 0 var(--space-5);
display: flex;
align-items: center;
box-shadow: var(--shadow-sm);
position: relative;
z-index: 100;
}
.global-header .header-content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.global-header h1 {
font-size: var(--text-lg);
font-weight: 700;
color: var(--color-text);
letter-spacing: -0.03em;
}
.header-actions {
display: flex;
align-items: center;
gap: var(--space-4);
}
.market-status {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--text-xs);
}
.status-label {
color: var(--color-text-muted);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.status-text {
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-sm);
font-weight: 600;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.status-open {
background: var(--color-positive-bg);
color: var(--color-positive);
}
.status-closed {
background: var(--color-negative-bg);
color: var(--color-negative);
}
.theme-toggle {
background: var(--color-bg-subtle);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
padding: var(--space-2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
transition: background 0.15s, border-color 0.15s;
}
.theme-toggle:hover {
background: var(--color-bg-muted);
}
.theme-toggle:active {
transform: scale(0.96);
}
.theme-toggle svg {
width: 16px;
height: 16px;
color: var(--color-text-secondary);
}
.theme-toggle .icon-moon {
display: block;
}
.theme-toggle .icon-sun {
display: none;
}
[data-theme="dark"] .theme-toggle .icon-moon {
display: none;
}
[data-theme="dark"] .theme-toggle .icon-sun {
display: block;
}
/* App Layout Grid */
.app-layout {
display: grid;
grid-template-columns: 1fr var(--chat-width);
height: calc(100vh - var(--header-height));
}
/* Main Content Area */
.main-content {
overflow-y: auto;
min-width: 0;
padding: var(--space-4) var(--space-5);
background: var(--color-bg);
scrollbar-width: thin;
scrollbar-color: var(--color-border) transparent;
}
.main-content::-webkit-scrollbar {
width: 6px;
}
.main-content::-webkit-scrollbar-track {
background: transparent;
}
.main-content::-webkit-scrollbar-thumb {
background: var(--color-border);
border-radius: 3px;
}
.main-content::-webkit-scrollbar-thumb:hover {
background: var(--color-text-muted);
}
/* ============================================
Chat Panel - Persistent Right Side
============================================ */
.chat-panel {
background: var(--color-bg-elevated);
border-left: 1px solid var(--color-border);
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
}
.chat-panel-content {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
transition: opacity 0.2s;
}
.chat-header {
padding: var(--space-3) var(--space-4);
background: var(--color-bg-elevated);
border-bottom: 1px solid var(--color-border);
display: flex;
justify-content: space-between;
align-items: center;
}
.chat-header h3 {
margin: 0;
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-text);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.close-mobile-chat {
display: none;
background: none;
border: none;
color: var(--color-text-muted);
cursor: pointer;
padding: var(--space-1);
border-radius: var(--radius-sm);
transition: background 0.15s, color 0.15s;
}
.close-mobile-chat:hover {
background: var(--color-bg-subtle);
color: var(--color-text);
}
.chat-ticker-context {
padding: var(--space-2) var(--space-4);
background: var(--color-bg-subtle);
border-bottom: 1px solid var(--color-border-subtle);
font-size: 10px;
font-weight: 600;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-3);
background: var(--color-bg);
}
.message {
max-width: 90%;
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-md);
line-height: var(--line-height-dense);
word-wrap: break-word;
font-size: var(--text-sm);
animation: messageIn 0.2s ease-out;
}
@keyframes messageIn {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message.user {
align-self: flex-end;
background: var(--color-accent);
color: white;
border-bottom-right-radius: var(--radius-sm);
}
.message.assistant {
align-self: flex-start;
background: var(--color-bg-elevated);
color: var(--color-text);
border: 1px solid var(--color-border);
border-bottom-left-radius: var(--radius-sm);
}
.message.error {
align-self: flex-start;
background: var(--color-negative-bg);
color: var(--color-negative);
border: 1px solid var(--color-negative);
}
.message.loading {
align-self: flex-start;
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
padding: var(--space-3);
}
.typing-indicator {
display: flex;
gap: 4px;
align-items: center;
}
.typing-indicator span {
width: 5px;
height: 5px;
background: var(--color-text-muted);
border-radius: 50%;
animation: typing 1.2s infinite;
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.15s;
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.3s;
}
@keyframes typing {
0%, 60%, 100% {
transform: translateY(0);
opacity: 0.4;
}
30% {
transform: translateY(-4px);
opacity: 1;
}
}
/* Agent tool call status indicators */
.message.tool-status {
align-self: flex-start;
background: transparent;
padding: var(--space-1) var(--space-2);
max-width: 100%;
border: none;
}
.tool-status-item {
display: flex;
align-items: center;
gap: 6px;
padding: 2px 0;
font-size: var(--text-xs);
color: var(--color-text-muted);
}
.tool-status-item.complete {
color: var(--color-positive);
}
.tool-status-item.error {
color: var(--color-negative);
}
.tool-status-item.calling {
color: var(--color-accent);
}
.tool-spinner {
display: inline-block;
width: 10px;
height: 10px;
border: 2px solid var(--color-border);
border-top-color: var(--color-accent);
border-radius: 50%;
animation: tool-spin 0.6s linear infinite;
}
@keyframes tool-spin {
to { transform: rotate(360deg); }
}
.tool-status-complete {
opacity: 0.5;
max-height: 80px;
overflow: hidden;
transition: opacity 0.3s ease, max-height 0.3s ease;
}
.chat-input-container {
padding: var(--space-3);
border-top: 1px solid var(--color-border);
background: var(--color-bg-elevated);
display: flex;
gap: var(--space-2);
}
#chatInput {
flex: 1;
padding: var(--space-2) var(--space-3);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-family: var(--font-sans);
font-size: var(--text-sm);
resize: none;
transition: border-color 0.15s, box-shadow 0.15s;
background: var(--color-bg-elevated);
color: var(--color-text);
}
#chatInput::placeholder {
color: var(--color-text-muted);
}
#chatInput:focus {
outline: none;
border-color: var(--color-accent);
box-shadow: 0 0 0 2px var(--color-accent-subtle);
}
.send-chat-btn {
background: var(--color-accent);
color: white;
border: none;
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-sm);
cursor: pointer;
font-weight: 600;
font-size: var(--text-xs);
text-transform: uppercase;
letter-spacing: 0.05em;
transition: background 0.15s, transform 0.1s;
font-family: var(--font-sans);
}
.send-chat-btn:hover {
background: var(--color-accent-hover);
}
.send-chat-btn:active {
transform: scale(0.96);
}
.send-chat-btn:disabled {
background: var(--color-bg-muted);
color: var(--color-text-muted);
cursor: not-allowed;
}
/* ============================================
Ticker Selector - Compact
============================================ */
.ticker-selector {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-md);
margin-bottom: var(--space-4);
box-shadow: var(--shadow-sm);
}
.ticker-selector label {
display: block;
margin-bottom: var(--space-1);
font-weight: 600;
font-size: 10px;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.dropdown-container {
position: relative;
}
#tickerSearch {
width: 100%;
padding: var(--space-2) var(--space-3);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-size: var(--text-sm);
font-family: var(--font-sans);
background: var(--color-bg-elevated);
color: var(--color-text);
transition: border-color 0.15s, box-shadow 0.15s;
}
#tickerSearch::placeholder {
color: var(--color-text-muted);
}
#tickerSearch:focus {
outline: none;
border-color: var(--color-accent);
box-shadow: 0 0 0 2px var(--color-accent-subtle);
}
.dropdown-list {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
max-height: 320px;
overflow-y: auto;
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
z-index: 1000;
}
.dropdown-section-header {
padding: var(--space-1) var(--space-3);
font-size: 10px;
font-weight: 700;
color: var(--color-text-muted);
background: var(--color-bg-subtle);
text-transform: uppercase;
letter-spacing: 0.08em;
border-bottom: 1px solid var(--color-border-subtle);
position: sticky;
top: 0;
}
.dropdown-item {
padding: var(--space-2) var(--space-3);
cursor: pointer;
transition: background 0.1s;
border-bottom: 1px solid var(--color-border-subtle);
font-size: var(--text-sm);
}
.dropdown-item:hover,
.dropdown-item.highlighted {
background: var(--color-bg-subtle);
}
.dropdown-item:last-child {
border-bottom: none;
}
.dropdown-no-results {
padding: var(--space-4);
text-align: center;
color: var(--color-text-muted);
font-size: var(--text-sm);
}
/* ============================================
Stock Data Card - Compact
============================================ */
.stock-data {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
padding: var(--space-4);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
}
.stock-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: var(--space-4);
padding-bottom: var(--space-3);
border-bottom: 1px solid var(--color-border);
}
.stock-header h2 {
font-size: var(--text-xl);
font-weight: 700;
color: var(--color-text);
letter-spacing: -0.025em;
}
.stock-price {
font-size: var(--text-2xl);
font-weight: 700;
font-family: var(--font-mono);
letter-spacing: -0.02em;
}
.stock-price.positive {
color: var(--color-positive);
}
.stock-price.negative {
color: var(--color-negative);
}
/* ============================================
Tabs - Terminal Style
============================================ */
.tabs {
display: flex;
gap: 0;
margin-bottom: var(--space-4);
border-bottom: 1px solid var(--color-border);
background: var(--color-bg-subtle);
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
overflow-x: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
.tabs::-webkit-scrollbar {
display: none;
}
.tab-button {
padding: var(--space-2) var(--space-4);
background: none;
border: none;
font-size: 11px;
font-weight: 600;
cursor: pointer;
color: var(--color-text-muted);
transition: color 0.15s, background 0.15s;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
white-space: nowrap;
font-family: var(--font-sans);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.tab-button:hover {
color: var(--color-text-secondary);
background: var(--color-bg-muted);
}
.tab-button.active {
color: var(--color-accent);
background: var(--color-bg-elevated);
border-bottom-color: var(--color-accent);
}
.beta-tag {
display: inline-block;
padding: 1px 4px;
margin-left: var(--space-1);
font-size: 9px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
background: var(--color-accent-subtle);
color: var(--color-accent);
border-radius: 2px;
vertical-align: middle;
}
.tab-content {
position: relative;
}
.tab-pane {
display: none;
opacity: 0;
transform: translateY(6px);
}
.tab-pane.active {
display: block;
animation: fadeInUp 0.2s ease forwards;
}
@keyframes fadeInUp {
to {
opacity: 1;
transform: translateY(0);
}
}
/* ============================================
Metrics Grid - Dense
============================================ */
.metrics-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: var(--space-2);
margin-bottom: var(--space-4);
}
.metric-card {
background: var(--color-bg-subtle);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-sm);
border: 1px solid var(--color-border-subtle);
}
.metric-label {
font-size: 10px;
font-weight: 600;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 2px;
}
.metric-value {
font-size: var(--text-base);
font-weight: 600;
color: var(--color-text);
font-family: var(--font-mono);
line-height: var(--line-height-tight);
}
/* ============================================
Chart Section - Compact
============================================ */
.chart-section {
margin-bottom: var(--space-4);
padding: var(--space-3);
background: var(--color-bg-subtle);
border-radius: var(--radius-md);
border: 1px solid var(--color-border-subtle);
}
.chart-section h3 {
font-size: 10px;
font-weight: 700;
color: var(--color-text-muted);
margin-bottom: var(--space-3);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.chart-controls {
display: flex;
gap: var(--space-1);
margin-bottom: var(--space-3);
}
.chart-range-btn {
padding: var(--space-1) var(--space-2);
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 10px;
font-weight: 600;
color: var(--color-text-muted);
transition: all 0.15s;
font-family: var(--font-mono);
}
.chart-range-btn:hover {
background: var(--color-bg-muted);
color: var(--color-text-secondary);
}
.chart-range-btn:active {
transform: scale(0.96);
}
.chart-range-btn.active {
background: var(--color-accent);
color: white;
border-color: var(--color-accent);
}
.chart-controls-spacer {
flex: 1;
}
.chart-view-toggle {
display: flex;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
overflow: hidden;
}
.chart-view-btn {
padding: var(--space-1) var(--space-2);
background: var(--color-bg-elevated);
border: none;
border-right: 1px solid var(--color-border);
cursor: pointer;
font-size: 10px;
font-weight: 600;
color: var(--color-text-muted);
transition: all 0.15s;
font-family: var(--font-sans);
}
.chart-view-btn:last-child {
border-right: none;
}
.chart-view-btn:hover {
background: var(--color-bg-muted);
}
.chart-view-btn:active {
transform: scale(0.96);
}
.chart-view-btn.active {
background: var(--color-accent);
color: white;
}
.chart-canvas-wrapper {
position: relative;
}
.chart-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#priceChart {
width: 100%;
height: 280px;
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
cursor: crosshair;
}
/* ============================================
Company Description - Compact
============================================ */
.company-description {
padding: var(--space-3);
background: var(--color-bg-subtle);
border-radius: var(--radius-md);
border: 1px solid var(--color-border-subtle);
}
.company-description h3 {
font-size: 10px;
font-weight: 700;
color: var(--color-text-muted);
margin-bottom: var(--space-2);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.company-description p {
font-size: var(--text-sm);
line-height: 1.6;
color: var(--color-text-secondary);
}
/* ============================================
Financials - Dense
============================================ */
#financialsData {
padding: var(--space-2);
}
.financial-period {
margin-bottom: var(--space-4);
padding: var(--space-3);
background: var(--color-bg-subtle);
border-radius: var(--radius-md);
border: 1px solid var(--color-border-subtle);
}
.financial-period h4 {
margin-bottom: var(--space-3);
color: var(--color-text);
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.financial-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: var(--space-2);
}
.financial-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-2) var(--space-3);
background: var(--color-bg-elevated);
border-radius: var(--radius-sm);
border: 1px solid var(--color-border-subtle);
}
.financial-item-label {
color: var(--color-text-secondary);
font-size: var(--text-xs);
}
.financial-item-value {
font-weight: 600;
color: var(--color-text);
font-family: var(--font-mono);
font-size: var(--text-xs);
}
/* ============================================
News - Dense
============================================ */
#newsContainer {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.news-article {
padding: var(--space-3);
background: var(--color-bg-subtle);
border-radius: var(--radius-md);
border: 1px solid var(--color-border-subtle);
transition: border-color 0.15s;
}
.news-article:hover {
border-color: var(--color-border);
}
.news-article h4 {
margin-bottom: var(--space-1);
font-size: var(--text-sm);
font-weight: 600;
line-height: var(--line-height-dense);
}
.news-article h4 a {
color: var(--color-text);
text-decoration: none;
transition: color 0.15s;
}
.news-article h4 a:hover {
color: var(--color-accent);
}
.news-meta {
font-size: 10px;
color: var(--color-text-muted);
margin-bottom: var(--space-2);
text-transform: uppercase;
letter-spacing: 0.03em;
}
.news-description {
color: var(--color-text-secondary);
font-size: var(--text-xs);
line-height: 1.5;
}
/* ============================================
Data Tables - Dense with Sticky Headers
============================================ */
.data-table-container {
max-height: 400px;
overflow-y: auto;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: var(--text-xs);
}
.data-table thead {
position: sticky;
top: 0;
z-index: 10;
background: var(--color-bg-subtle);
}
.data-table th {
padding: var(--space-2) var(--space-3);
text-align: left;
font-weight: 700;
color: var(--color-text-muted);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.06em;
border-bottom: 2px solid var(--color-border);
white-space: nowrap;
}
.data-table td {
padding: var(--space-2) var(--space-3);
border-bottom: 1px solid var(--color-border-subtle);
color: var(--color-text);
font-family: var(--font-mono);
font-size: 12px;
}
.data-table td[data-type="number"] {
text-align: right;
}
.data-table tbody tr:nth-child(even) {
background: var(--color-bg-subtle);
}
.data-table tbody tr:hover {
background: var(--color-accent-subtle);
}
.data-table tbody tr:last-child td {
border-bottom: none;
}
/* ============================================
Loading States & Skeletons
============================================ */
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.spinner {
width: 24px;
height: 24px;
border: 2px solid var(--color-border);
border-top-color: var(--color-accent);
border-radius: 50%;
animation: spin 0.7s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.hidden {
display: none;
}
/* Skeleton Loading */
.skeleton {
background: linear-gradient(
90deg,
var(--color-bg-subtle) 25%,
var(--color-bg-muted) 50%,
var(--color-bg-subtle) 75%
);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: var(--radius-sm);
}
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.skeleton-metric {
height: 48px;
}
.skeleton-chart {
height: 280px;
}
.skeleton-text {
height: 14px;
margin-bottom: 8px;
}
.skeleton-text:last-child {
width: 60%;
}
.skeleton-text-sm {
height: 12px;
margin-bottom: 6px;
}
/* ============================================
Empty States
============================================ */
.empty-state {
text-align: center;
padding: var(--space-8) var(--space-4);
color: var(--color-text-muted);
}
.empty-state-icon {
width: 40px;
height: 40px;
margin: 0 auto var(--space-3);
opacity: 0.5;
color: var(--color-text-muted);
}
.empty-state-title {
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-text-secondary);
margin-bottom: var(--space-1);
}
.empty-state-description {
font-size: var(--text-xs);
max-width: 240px;
margin: 0 auto;
line-height: 1.5;
}
/* ============================================
Micro-animations
============================================ */
/* Button press feedback */
button:active:not(:disabled) {
transform: scale(0.98);
}
/* Data update flash */
.data-updated {
animation: dataFlash 0.3s ease;
}
@keyframes dataFlash {
0%, 100% { background: transparent; }
50% { background: var(--color-accent-subtle); }
}
/* Focus visible states */
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
/* ============================================
Sentiment Analysis Tab
============================================ */
.sentiment-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: var(--space-4);
margin-bottom: var(--space-4);
padding: var(--space-3);
background: var(--color-bg-elevated);
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
}
.sentiment-aggregate {
display: flex;
gap: var(--space-6);
align-items: center;
}
.sentiment-score-container {
text-align: center;
}
/* Sentiment Gauge */
.sentiment-gauge {
width: 120px;
height: 60px;
position: relative;
overflow: hidden;
margin-bottom: var(--space-2);
}
.gauge-background {
position: absolute;
bottom: 0;
left: 0;
width: 120px;
height: 60px;
border-radius: 60px 60px 0 0;
background: linear-gradient(90deg,
var(--color-negative) 0%,
var(--color-negative) 20%,
var(--color-neutral-sentiment) 40%,
var(--color-neutral-sentiment) 60%,
var(--color-positive) 80%,
var(--color-positive) 100%
);
opacity: 0.15;
}
.gauge-fill {
position: absolute;
bottom: 0;
left: 50%;
width: 3px;
height: 50px;
background: var(--color-text-muted);
transform-origin: bottom center;
transform: rotate(0deg);
transition: transform 0.5s ease-out, background 0.3s;
border-radius: 2px;
z-index: 2;
}
.gauge-fill.bullish {
background: var(--color-positive);
}
.gauge-fill.bearish {
background: var(--color-negative);
}
.gauge-fill.neutral {
background: var(--color-neutral-sentiment);
}
.gauge-needle {
position: absolute;
bottom: 0;
left: 50%;
width: 2px;
height: 45px;
background: var(--color-text);
transform-origin: bottom center;
transform: rotate(0deg);
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 2px;
z-index: 3;
margin-left: -1px;
}
.gauge-center {
position: absolute;
bottom: -6px;
left: 50%;
transform: translateX(-50%);
width: 12px;
height: 12px;
background: var(--color-bg-elevated);
border: 2px solid var(--color-text);
border-radius: 50%;
z-index: 4;
}
.sentiment-label {
font-size: var(--text-base);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.sentiment-label.bullish {
color: var(--color-positive);
}
.sentiment-label.bearish {
color: var(--color-negative);
}
.sentiment-label.neutral {
color: var(--color-neutral-sentiment);
}
.sentiment-stats {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.sentiment-stats .stat-item {
display: flex;
flex-direction: column;
gap: 2px;
}
.sentiment-stats .stat-label {
font-size: 10px;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.sentiment-stats .stat-value {
font-size: var(--text-base);
font-weight: 600;
color: var(--color-text);
font-family: var(--font-mono);
}
.refresh-btn {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
background: var(--color-bg-subtle);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 10px;
font-weight: 600;
color: var(--color-text-secondary);
transition: all 0.15s;
font-family: var(--font-sans);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.refresh-btn:hover {
background: var(--color-bg-muted);
}
.refresh-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.refresh-btn.loading svg {
animation: spin 1s linear infinite;
}
/* Source Breakdown */
.sentiment-sources {
margin-bottom: var(--space-4);
}
.sentiment-sources h4 {
font-size: 10px;
font-weight: 700;
color: var(--color-text-muted);
margin-bottom: var(--space-2);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.source-breakdown {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-2);
}
.source-item {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
background: var(--color-bg-elevated);
border-radius: var(--radius-sm);
border: 1px solid var(--color-border);
}
.source-icon {
font-size: var(--text-sm);
}
.source-name {
font-size: var(--text-xs);
color: var(--color-text-secondary);
flex: 1;
}
.source-count {
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-text);
font-family: var(--font-mono);
}
/* Sentiment Posts Section */
.sentiment-posts-section {
background: var(--color-bg-elevated);
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
padding: var(--space-3);
}
.posts-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-3);
}
.posts-header h4 {
font-size: 10px;
font-weight: 700;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.posts-filter {
display: flex;
gap: var(--space-1);
}
.filter-btn {
padding: var(--space-1) var(--space-2);
background: var(--color-bg-subtle);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 10px;
font-weight: 600;
color: var(--color-text-muted);
transition: all 0.15s;
font-family: var(--font-sans);
}
.filter-btn:hover {
background: var(--color-bg-muted);
}
.filter-btn.active {
background: var(--color-accent);
color: white;
border-color: var(--color-accent);
}
#sentimentPostsContainer {
max-height: 400px;
overflow-y: auto;
}
.sentiment-post {
padding: var(--space-3);
background: var(--color-bg-subtle);
border-radius: var(--radius-sm);
border-left: 3px solid var(--color-border);
margin-bottom: var(--space-2);
transition: border-color 0.15s;
}
.sentiment-post:last-child {
margin-bottom: 0;
}
.sentiment-post.positive {
border-left-color: var(--color-positive);
}
.sentiment-post.negative {
border-left-color: var(--color-negative);
}
.sentiment-post.neutral {
border-left-color: var(--color-neutral-sentiment);
}
.sentiment-post .post-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-1);
}
.post-platform {
font-size: 10px;
font-weight: 700;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.02em;
}
.post-sentiment {
font-size: 10px;
font-weight: 600;
padding: 2px var(--space-2);
border-radius: 2px;
}
.post-sentiment.positive {
background: var(--color-positive-bg);
color: var(--color-positive);
}
.post-sentiment.negative {
background: var(--color-negative-bg);
color: var(--color-negative);
}
.post-sentiment.neutral {
background: var(--color-neutral-sentiment-bg);
color: var(--color-neutral-sentiment);
}
.post-content {
font-size: var(--text-xs);
line-height: 1.5;
color: var(--color-text);
margin-bottom: var(--space-2);
word-break: break-word;
}
.post-meta {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
font-size: 10px;
color: var(--color-text-muted);
}
.post-link {
display: inline-block;
margin-top: var(--space-1);
font-size: 10px;
color: var(--color-accent);
text-decoration: none;
}
.post-link:hover {
text-decoration: underline;
}
.loading-text,
.error-text,
.no-data-text {
text-align: center;
padding: var(--space-6);
color: var(--color-text-muted);
font-size: var(--text-sm);
}
.error-text {
color: var(--color-negative);
}
/* ============================================
Forecast Tab
============================================ */
.forecast-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: var(--space-4);
margin-bottom: var(--space-4);
padding: var(--space-3);
background: var(--color-bg-elevated);
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
flex-wrap: wrap;
}
.forecast-info h4 {
font-size: var(--text-base);
font-weight: 600;
color: var(--color-text);
margin-bottom: var(--space-1);
}
.forecast-description {
font-size: var(--text-xs);
color: var(--color-text-secondary);
}
.forecast-controls {
display: flex;
flex-direction: column;
gap: var(--space-3);
align-items: flex-end;
}
.forecast-status {
display: flex;
gap: var(--space-4);
}
.forecast-status .status-item {
display: flex;
flex-direction: column;
gap: 2px;
text-align: right;
}
.forecast-status .status-label {
font-size: 10px;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.forecast-status .status-value {
font-size: var(--text-xs);
font-weight: 600;
color: var(--color-text);
font-family: var(--font-mono);
}
.forecast-actions {
display: flex;
gap: var(--space-2);
}
.forecast-btn {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
background: var(--color-accent);
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 10px;
font-weight: 600;
color: white;
transition: background 0.15s;
font-family: var(--font-sans);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.forecast-btn:hover:not(:disabled) {
background: var(--color-accent-hover);
}
.forecast-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.forecast-btn.loading svg {
animation: spin 1s linear infinite;
}
.forecast-chart-section {
margin-bottom: var(--space-4);
padding: var(--space-3);
background: var(--color-bg-elevated);
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
}
.chart-legend {
display: flex;
gap: var(--space-4);
margin-bottom: var(--space-3);
flex-wrap: wrap;
}
.legend-item {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: 10px;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.03em;
}
.legend-color {
width: 16px;
height: 3px;
border-radius: 2px;
}
.legend-item.historical .legend-color {
background: var(--color-accent);
}
.legend-item.predicted .legend-color {
background: var(--color-positive);
background-image: linear-gradient(90deg, var(--color-positive) 60%, transparent 60%);
background-size: 6px 3px;
}
.legend-item.confidence .legend-color {
background: rgba(16, 185, 129, 0.25);
height: 10px;
width: 20px;
}
#forecastChart {
width: 100%;
height: 300px;
background: var(--color-bg-subtle);
border-radius: var(--radius-sm);
}
.forecast-disclaimer {
padding: var(--space-3);
background: var(--color-negative-bg);
border-radius: var(--radius-sm);
border: 1px solid var(--color-negative);
border-left: 3px solid var(--color-negative);
}
.forecast-disclaimer p {
font-size: var(--text-xs);
color: var(--color-negative);
line-height: 1.5;
margin: 0;
}
.forecast-disclaimer strong {
font-weight: 700;
}
/* ============================================
Responsive Design
============================================ */
@media (max-width: 1024px) {
.metrics-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 768px) {
:root {
--chat-width: 100%;
}
.app-layout {
grid-template-columns: 1fr;
}
.chat-panel {
position: fixed;
right: 0;
top: var(--header-height);
height: calc(100vh - var(--header-height));
width: 100%;
transform: translateX(100%);
transition: transform 0.25s ease;
z-index: 1000;
}
.chat-panel.open {
transform: translateX(0);
}
.close-mobile-chat {
display: block;
}
/* Mobile chat toggle button */
.mobile-chat-toggle {
position: fixed;
right: var(--space-4);
bottom: var(--space-4);
width: 48px;
height: 48px;
background: var(--color-accent);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
box-shadow: var(--shadow-lg);
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
font-size: var(--text-lg);
}
.main-content {
padding: var(--space-3);
}
.global-header {
padding: 0 var(--space-4);
}
.global-header h1 {
font-size: var(--text-base);
}
.stock-header {
flex-direction: column;
align-items: flex-start;
gap: var(--space-2);
}
.stock-header h2 {
font-size: var(--text-lg);
}
.stock-price {
font-size: var(--text-xl);
}
.metrics-grid {
grid-template-columns: repeat(2, 1fr);
}
.tabs {
-webkit-overflow-scrolling: touch;
}
.tab-button {
padding: var(--space-2) var(--space-3);
font-size: 10px;
}
#priceChart {
height: 200px;
}
.source-breakdown {
grid-template-columns: 1fr;
}
.financial-grid {
grid-template-columns: 1fr;
}
.sentiment-aggregate {
flex-direction: column;
gap: var(--space-3);
}
.forecast-header {
flex-direction: column;
}
.forecast-controls {
width: 100%;
align-items: flex-start;
}
}
@media (max-width: 480px) {
.metrics-grid {
grid-template-columns: 1fr;
}
.chart-controls {
flex-wrap: wrap;
}
.header-actions {
gap: var(--space-2);
}
.market-status {
display: none;
}
}