diff --git a/services/frontend/frontend/index.html b/services/frontend/frontend/index.html
index a5d4072..bc04cd6 100644
--- a/services/frontend/frontend/index.html
+++ b/services/frontend/frontend/index.html
@@ -6,7 +6,7 @@
IMPHNEN -- Discord Moderation
-
+
diff --git a/services/frontend/frontend/src/features/messages/components/message_card.rs b/services/frontend/frontend/src/features/messages/components/message_card.rs
index d6a52e0..9a77dc3 100644
--- a/services/frontend/frontend/src/features/messages/components/message_card.rs
+++ b/services/frontend/frontend/src/features/messages/components/message_card.rs
@@ -1,5 +1,5 @@
use leptos::prelude::*;
-use shared_types::message::{AiSeverity, AiStatus, AttachmentRef, MessageRecord};
+use shared_types::message::{AiSeverity, AiStatus, AttachmentRef, MessageRecord, ReferenceInfo};
use std::sync::Arc;
use super::message_actions::ReanalyzeButton;
@@ -79,6 +79,39 @@ pub fn MessageRow(
.cloned()
.unwrap_or_default();
+ // Extract reply info before view! to avoid closure capture issues
+ let reply_el = message.is_reply.unwrap_or(false).then(|| {
+ message
+ .metadata
+ .as_ref()
+ .and_then(|m| m.reference.as_ref())
+ .map(|ref_info| {
+ let r_user = ref_info.replied_username.as_deref().unwrap_or("unknown").to_string();
+ let r_content = ref_info
+ .content
+ .as_deref()
+ .unwrap_or("")
+ .to_string();
+ (r_user, r_content)
+ })
+ });
+ let reply_user = reply_el.as_ref().map(|r| r.as_ref().map(|(u, _)| u.clone()));
+ let reply_user = reply_user.flatten();
+ let reply_content = reply_el.as_ref().map(|r| r.as_ref().map(|(_, c)| c.clone()));
+ let reply_content = reply_content.flatten();
+ let reply_content_snippet = reply_content.as_ref().map(|c| {
+ if c.len() > 48 {
+ format!("{}…", &c[..48])
+ } else {
+ c.clone()
+ }
+ });
+ let reply_initial = reply_user
+ .as_ref()
+ .and_then(|u| u.chars().next())
+ .map(|c| c.to_uppercase().to_string())
+ .unwrap_or_else(|| "?".to_string());
+
view! {
{/* Header */}
@@ -104,6 +137,29 @@ pub fn MessageRow(
+ {/* Reply indicator — Discord-style reference block */}
+ {reply_user.as_ref().map(|r_user| {
+ let r_user_cloned = r_user.clone();
+ let snippet = reply_content_snippet.clone();
+ let initial = reply_initial.clone();
+ view! {
+
+
+
+ {initial}
+ "Replying to"
+ "@" {r_user_cloned}
+ {(!snippet.as_deref().unwrap_or("").is_empty()).then(|| {
+ let s = snippet.unwrap_or_default();
+ view! {
+ {s}
+ }
+ })}
+
+
+ }
+ })}
+
{/* Content */}
{show.then(|| {
let rendered = render_emojis(display);
diff --git a/services/frontend/frontend/src/styles/animations.css b/services/frontend/frontend/src/styles/animations.css
new file mode 100644
index 0000000..a33f36e
--- /dev/null
+++ b/services/frontend/frontend/src/styles/animations.css
@@ -0,0 +1,543 @@
+/* ── Animations — Live Dashboard Effects ──────────────── *
+ * Keyframes, applied animations, and utility classes. *
+ * Respects prefers-reduced-motion. *
+ * ──────────────────────────────────────────────────────── */
+
+/* ════════════════════════════════════════════════════════
+ 1. KEYFRAMES
+ ════════════════════════════════════════════════════════ */
+
+/* ── Entry / Reveal ─────────────────────────────────── */
+@keyframes slide-in-left {
+ from { opacity: 0; transform: translateX(-20px); }
+ to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes slide-in-down {
+ from { opacity: 0; transform: translateY(-12px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+@keyframes scale-bounce {
+ 0% { opacity: 0; transform: scale(0.85); }
+ 60% { opacity: 1; transform: scale(1.04); }
+ 100% { opacity: 1; transform: scale(1); }
+}
+
+@keyframes pop-in {
+ 0% { opacity: 0; transform: scale(0.8); }
+ 70% { opacity: 1; transform: scale(1.08); }
+ 100% { opacity: 1; transform: scale(1); }
+}
+
+/* ── Status / Alive ─────────────────────────────────── */
+@keyframes breathe {
+ 0%, 100% { opacity: 1; transform: scale(1); box-shadow: 0 0 4px currentColor; }
+ 50% { opacity: 0.6; transform: scale(1.2); box-shadow: 0 0 14px currentColor; }
+}
+
+@keyframes breathe-soft {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.5; }
+}
+
+@keyframes ring-pulse {
+ 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); }
+ 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
+ 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
+}
+
+@keyframes ring-pulse-success {
+ 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
+ 70% { box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }
+ 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
+}
+
+@keyframes ring-pulse-error {
+ 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
+ 70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); }
+ 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
+}
+
+@keyframes processing-sweep {
+ 0% { background-position: -200% center; }
+ 100% { background-position: 200% center; }
+}
+
+@keyframes live-indicator {
+ 0%, 100% { opacity: 1; box-shadow: 0 0 6px rgba(239, 68, 68, 0.4); }
+ 50% { opacity: 0.7; box-shadow: 0 0 16px rgba(239, 68, 68, 0.7); }
+}
+
+/* ── Ambient / Decorative ───────────────────────────── */
+/* Slow breathing for background glow layers */
+@keyframes ambient-glow {
+ 0%, 100% { opacity: 0.3; }
+ 50% { opacity: 0.7; }
+}
+
+/* Dynamic mesh drift — shifts gradient center positions */
+@keyframes bg-mesh-drift {
+ 0% { background-position: 0% 0%, 100% 100%, 50% 50%; }
+ 25% { background-position: 30% 20%, 70% 80%, 20% 60%; }
+ 50% { background-position: 60% 10%, 40% 60%, 80% 30%; }
+ 75% { background-position: 20% 60%, 80% 20%, 40% 80%; }
+ 100% { background-position: 0% 0%, 100% 100%, 50% 50%; }
+}
+
+/* Slow hue rotation for orb glow */
+@keyframes hue-rotate-slow {
+ 0% { filter: hue-rotate(0deg); }
+ 50% { filter: hue-rotate(30deg); }
+ 100% { filter: hue-rotate(0deg); }
+}
+
+/* Orb size pulsing */
+@keyframes orb-pulse {
+ 0%, 100% { transform: scale(1); opacity: 0.4; }
+ 50% { transform: scale(1.08); opacity: 0.6; }
+}
+
+/* Expanded orb drift — wider, slower, organic */
+@keyframes orb-drift-enhanced {
+ 0% { transform: translate(0, 0) scale(1); }
+ 20% { transform: translate(80px, -50px) scale(1.1); }
+ 40% { transform: translate(-50px, 70px) scale(0.9); }
+ 60% { transform: translate(100px, 30px) scale(1.05); }
+ 80% { transform: translate(-70px, -60px) scale(0.95); }
+ 100% { transform: translate(0, 0) scale(1); }
+}
+
+/* Grain/noise texture shifting */
+@keyframes grain-shift {
+ 0%, 100% { transform: translate(0, 0) rotate(0deg); }
+ 10% { transform: translate(-5%, -5%) rotate(0.5deg); }
+ 20% { transform: translate(-10%, 0%) rotate(-0.5deg); }
+ 30% { transform: translate(0%, 5%) rotate(1deg); }
+ 40% { transform: translate(5%, -3%) rotate(-0.3deg); }
+ 50% { transform: translate(-3%, -8%) rotate(0.8deg); }
+ 60% { transform: translate(8%, 3%) rotate(-0.6deg); }
+ 70% { transform: translate(-6%, 6%) rotate(0.4deg); }
+ 80% { transform: translate(4%, -6%) rotate(-0.7deg); }
+ 90% { transform: translate(-2%, 2%) rotate(0.2deg); }
+}
+
+@keyframes gradient-shimmer {
+ 0% { background-position: 0% center; }
+ 100% { background-position: 200% center; }
+}
+
+@keyframes float-variation {
+ 0%, 100% { transform: translateY(0) rotate(0deg); }
+ 33% { transform: translateY(-8px) rotate(1.5deg); }
+ 66% { transform: translateY(-4px) rotate(-1deg); }
+}
+
+@keyframes fade-in-down {
+ from { opacity: 0; transform: translateY(-10px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* ── Interactive ────────────────────────────────────── */
+@keyframes glow-border {
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
+ 50% { box-shadow: 0 0 12px 1px rgba(59, 130, 246, 0.15); }
+}
+
+@keyframes ripple {
+ 0% { transform: scale(0); opacity: 0.6; }
+ 100% { transform: scale(4); opacity: 0; }
+}
+
+/* ── Bar pulses (audio / visualizer) ────────────────── */
+@keyframes bar-pulse-1 {
+ 0%, 100% { transform: scaleY(1); }
+ 50% { transform: scaleY(0.4); }
+}
+@keyframes bar-pulse-2 {
+ 0%, 100% { transform: scaleY(0.5); }
+ 50% { transform: scaleY(1.2); }
+}
+@keyframes bar-pulse-3 {
+ 0%, 100% { transform: scaleY(0.7); }
+ 50% { transform: scaleY(1); }
+}
+
+@keyframes count-up {
+ from { opacity: 0; transform: translateY(8px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+
+/* ════════════════════════════════════════════════════════
+ 2. UTILITY ANIMATION CLASSES
+ Use these in Rust components: class="animate-breathe"
+ ════════════════════════════════════════════════════════ */
+
+.animate-breathe {
+ animation: breathe 3s ease-in-out infinite;
+}
+
+.animate-breathe-soft {
+ animation: breathe-soft 2s ease-in-out infinite;
+}
+
+.animate-ring-pulse {
+ animation: ring-pulse 2s ease-in-out infinite;
+}
+
+.animate-live-indicator {
+ animation: live-indicator 1.5s ease-in-out infinite;
+}
+
+.animate-float {
+ animation: float-variation 5s ease-in-out infinite;
+}
+
+.animate-gradient-shimmer {
+ background-size: 200% 100%;
+ animation: gradient-shimmer 4s ease-in-out infinite alternate;
+}
+
+.animate-spin-slow {
+ animation: spin 3s linear infinite;
+}
+
+.animate-pulse-connecting {
+ animation: pulse-connecting 1s ease-in-out infinite;
+}
+
+.animate-scale-bounce {
+ animation: scale-bounce 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both;
+}
+
+.animate-pop-in {
+ animation: pop-in 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) both;
+}
+
+.animate-fade-in-down {
+ animation: fade-in-down var(--transition-normal) ease-out;
+}
+
+.animate-slide-in-left {
+ animation: slide-in-left var(--transition-normal) ease-out;
+}
+
+.animate-count-up {
+ animation: count-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+.animate-shimmer-sweep {
+ background: linear-gradient(
+ 110deg,
+ transparent 30%,
+ rgba(59, 130, 246, 0.08) 50%,
+ transparent 70%
+ );
+ background-size: 200% 100%;
+ animation: processing-sweep 1.5s ease-in-out infinite;
+}
+
+
+/* ════════════════════════════════════════════════════════
+ 3. APPLIED ANIMATIONS — auto-wired to selectors
+ ════════════════════════════════════════════════════════ */
+
+/* ── Ambient animated mesh background ────────────────── */
+.app-shell::before {
+ content: '';
+ position: fixed;
+ inset: -50%;
+ pointer-events: none;
+ z-index: -1;
+ background:
+ radial-gradient(ellipse at 20% 30%, rgba(59, 130, 246, 0.06) 0%, transparent 40%),
+ radial-gradient(ellipse at 80% 70%, rgba(99, 102, 241, 0.05) 0%, transparent 40%),
+ radial-gradient(ellipse at 40% 80%, rgba(16, 185, 129, 0.03) 0%, transparent 40%),
+ radial-gradient(ellipse at 60% 20%, rgba(245, 158, 11, 0.02) 0%, transparent 40%);
+ background-size: 200% 200%, 200% 200%, 200% 200%, 200% 200%;
+ animation: bg-mesh-drift 20s ease-in-out infinite alternate;
+ will-change: background-position;
+}
+
+/* ── Subtle noise/grain texture overlay ─────────────── */
+.app-shell::after {
+ content: '';
+ position: fixed;
+ inset: -50%;
+ pointer-events: none;
+ z-index: 0;
+ opacity: 0.015;
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
+ background-repeat: repeat;
+ background-size: 256px 256px;
+ animation: grain-shift 0.5s steps(4) infinite;
+ will-change: transform;
+}
+
+/* ── Light theme mesh adjustment ────────────────────── */
+[data-theme="light"] .app-shell::before {
+ background:
+ radial-gradient(ellipse at 20% 30%, rgba(37, 99, 235, 0.04) 0%, transparent 40%),
+ radial-gradient(ellipse at 80% 70%, rgba(99, 102, 241, 0.04) 0%, transparent 40%),
+ radial-gradient(ellipse at 40% 80%, rgba(16, 185, 129, 0.03) 0%, transparent 40%),
+ radial-gradient(ellipse at 60% 20%, rgba(245, 158, 11, 0.02) 0%, transparent 40%);
+}
+[data-theme="light"] .app-shell::after {
+ opacity: 0.008;
+}
+
+/* ── Gradient text shimmer (brand elements) ─────────── */
+.sidebar-brand-name,
+.live-title,
+.auth-title {
+ background-size: 200% 100%;
+ animation: gradient-shimmer 6s ease-in-out infinite alternate;
+}
+
+/* ── Connected status dot: alive breath ─────────────── */
+.status-dot,
+.voice-connection-dot.connected {
+ animation: breathe 3s ease-in-out infinite;
+}
+
+/* Keep existing connecting animation (overrides above) */
+.status-dot.is-connecting,
+.voice-connection-dot.connecting {
+ animation: pulse-connecting 1s ease-in-out infinite !important;
+}
+
+.status-dot.disconnected,
+.voice-connection-dot.disconnected {
+ animation: none !important;
+}
+
+/* ── Live indicator (red recording dot) ─────────────── */
+.msg-card-ai-badge.flagged::before,
+.msg-analysis.flagged::before {
+ content: '';
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--color-error);
+ margin-right: 4px;
+ vertical-align: middle;
+ animation: live-indicator 1.5s ease-in-out infinite;
+}
+
+/* ── Particle orbs: ambient breathing overlay ───────── */
+.particle-bg {
+ animation: ambient-glow 6s ease-in-out infinite alternate;
+}
+
+/* ── Live bento grid: staggered entry ───────────────── */
+.live-bento > * {
+ animation: fade-in-up 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.live-bento > :nth-child(1) { animation-delay: 0.03s; }
+.live-bento > :nth-child(2) { animation-delay: 0.06s; }
+.live-bento > :nth-child(3) { animation-delay: 0.09s; }
+.live-bento > :nth-child(4) { animation-delay: 0.12s; }
+.live-bento > :nth-child(5) { animation-delay: 0.15s; }
+.live-bento > :nth-child(6) { animation-delay: 0.18s; }
+
+/* ── Dashboard metric cards: staggered entry ────────── */
+.dashboard-metric-card {
+ animation: fade-in-up 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.dashboard-metric-card:nth-child(1) { animation-delay: 0.04s; }
+.dashboard-metric-card:nth-child(2) { animation-delay: 0.08s; }
+.dashboard-metric-card:nth-child(3) { animation-delay: 0.12s; }
+.dashboard-metric-card:nth-child(4) { animation-delay: 0.16s; }
+.dashboard-metric-card:nth-child(5) { animation-delay: 0.20s; }
+.dashboard-metric-card:nth-child(6) { animation-delay: 0.24s; }
+.dashboard-metric-card:nth-child(7) { animation-delay: 0.28s; }
+.dashboard-metric-card:nth-child(8) { animation-delay: 0.32s; }
+
+/* ── Dashboard rows: staggered fade-in ──────────────── */
+.dashboard-summary-row,
+.dashboard-top-channel-row {
+ animation: fade-in 0.4s ease-out both;
+}
+.dashboard-summary-row:nth-child(1), .dashboard-top-channel-row:nth-child(1) { animation-delay: 0.02s; }
+.dashboard-summary-row:nth-child(2), .dashboard-top-channel-row:nth-child(2) { animation-delay: 0.04s; }
+.dashboard-summary-row:nth-child(3), .dashboard-top-channel-row:nth-child(3) { animation-delay: 0.06s; }
+.dashboard-summary-row:nth-child(4), .dashboard-top-channel-row:nth-child(4) { animation-delay: 0.08s; }
+.dashboard-summary-row:nth-child(5), .dashboard-top-channel-row:nth-child(5) { animation-delay: 0.10s; }
+.dashboard-summary-row:nth-child(6), .dashboard-top-channel-row:nth-child(6) { animation-delay: 0.12s; }
+.dashboard-summary-row:nth-child(7), .dashboard-top-channel-row:nth-child(7) { animation-delay: 0.14s; }
+.dashboard-summary-row:nth-child(8), .dashboard-top-channel-row:nth-child(8) { animation-delay: 0.16s; }
+.dashboard-summary-row:nth-child(9), .dashboard-top-channel-row:nth-child(9) { animation-delay: 0.18s; }
+.dashboard-summary-row:nth-child(10),.dashboard-top-channel-row:nth-child(10) { animation-delay: 0.20s; }
+
+/* ── Message cards: staggered entry ─────────────────── */
+.msg-card {
+ animation: fade-in-up 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.msg-card:nth-child(1) { animation-delay: 0.02s; }
+.msg-card:nth-child(2) { animation-delay: 0.05s; }
+.msg-card:nth-child(3) { animation-delay: 0.08s; }
+.msg-card:nth-child(4) { animation-delay: 0.11s; }
+.msg-card:nth-child(5) { animation-delay: 0.14s; }
+.msg-card:nth-child(6) { animation-delay: 0.17s; }
+.msg-card:nth-child(7) { animation-delay: 0.20s; }
+.msg-card:nth-child(8) { animation-delay: 0.23s; }
+.msg-card:nth-child(9) { animation-delay: 0.26s; }
+.msg-card:nth-child(10) { animation-delay: 0.29s; }
+
+/* ── Recording items: slide in from left ────────────── */
+.rec-item {
+ animation: slide-in-left 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.rec-item:nth-child(1) { animation-delay: 0.02s; }
+.rec-item:nth-child(2) { animation-delay: 0.06s; }
+.rec-item:nth-child(3) { animation-delay: 0.10s; }
+.rec-item:nth-child(4) { animation-delay: 0.14s; }
+.rec-item:nth-child(5) { animation-delay: 0.18s; }
+
+/* ── Speaker items: pop in ──────────────────────────── */
+.speak-item {
+ animation: fade-in 0.3s ease-out both;
+}
+.speak-item:nth-child(1) { animation-delay: 0.02s; }
+.speak-item:nth-child(2) { animation-delay: 0.05s; }
+.speak-item:nth-child(3) { animation-delay: 0.08s; }
+.speak-item:nth-child(4) { animation-delay: 0.11s; }
+.speak-item:nth-child(5) { animation-delay: 0.14s; }
+
+/* ── Audio visualizer bars: individual timing ───────── */
+.audio-bar {
+ animation: bar-pulse-2 1.2s ease-in-out infinite;
+}
+.audio-bar:nth-child(odd) {
+ animation-name: bar-pulse-1;
+ animation-duration: 0.9s;
+}
+.audio-bar:nth-child(3n) {
+ animation-name: bar-pulse-3;
+ animation-duration: 1.5s;
+}
+
+/* ── Interactive cards: hover glow effect ────────────── */
+.card-interactive:hover {
+ animation: glow-border 0.8s ease-in-out;
+ border-color: var(--color-primary);
+}
+
+/* ── Nav active indicator: continuous subtle glow ───── */
+.sidebar-nav-item.is-active::after {
+ animation: indicator-grow 250ms cubic-bezier(0.16, 1, 0.3, 1) both,
+ breathe-soft 3s ease-in-out 0.3s infinite;
+}
+
+/* ── Filter chip active: pop scale ──────────────────── */
+.filter-chip-v2.is-active {
+ animation: pop-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
+}
+
+/* ── AI badge processing: shimmer sweep ─────────────── */
+.msg-card-ai-badge.processing,
+.status-badge-processing {
+ background: linear-gradient(
+ 110deg,
+ var(--color-primary-muted) 25%,
+ rgba(59, 130, 246, 0.35) 50%,
+ var(--color-primary-muted) 75%
+ ) !important;
+ background-size: 200% 100% !important;
+ animation: processing-sweep 1.4s ease-in-out infinite;
+}
+.status-badge-processing::before {
+ animation: ring-pulse 1.2s ease-in-out infinite !important;
+}
+
+/* ── Image grid items: reveal ───────────────────────── */
+.image-grid-item {
+ animation: fade-in 0.4s ease-out both;
+}
+.image-grid-item:nth-child(1) { animation-delay: 0.02s; }
+.image-grid-item:nth-child(2) { animation-delay: 0.05s; }
+.image-grid-item:nth-child(3) { animation-delay: 0.08s; }
+.image-grid-item:nth-child(4) { animation-delay: 0.11s; }
+.image-grid-item:nth-child(5) { animation-delay: 0.14s; }
+.image-grid-item:nth-child(6) { animation-delay: 0.17s; }
+.image-grid-item:nth-child(7) { animation-delay: 0.20s; }
+.image-grid-item:nth-child(8) { animation-delay: 0.23s; }
+
+/* ── Dashboard queue value: count-up feel ───────────── */
+.dashboard-queue-value {
+ animation: count-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+/* ── Voice connection panel: entry ──────────────────── */
+.voice-connection {
+ animation: fade-in-up 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+/* ── Now playing: slide in ──────────────────────────── */
+.np-body {
+ animation: slide-in-left 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+/* ── Skeleton: enhanced shimmer ─────────────────────── */
+.skeleton,
+.msg-skel-avatar,
+.msg-skel-line,
+.msg-skel-badge,
+.dashboard-skel-avatar,
+.dashboard-skel-line {
+ background: linear-gradient(
+ 110deg,
+ var(--surface-container) 28%,
+ rgba(59, 130, 246, 0.06) 48%,
+ var(--surface-container) 68%
+ ) !important;
+ background-size: 200% 100% !important;
+}
+
+/* ── Tab content: stronger entry ────────────────────── */
+.tab-content {
+ animation: fade-in-up 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+/* ── Mobile tab active: subtle indicator ────────────── */
+.mobile-tab-item.is-active {
+ animation: pop-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
+}
+
+/* ── Mascot launcher: enhanced float ────────────────── */
+.mascot-launcher {
+ animation: float-variation 5s ease-in-out infinite;
+}
+.mascot-launcher:hover {
+ animation: none !important;
+}
+
+/* ── Empty state icons: subtle float ────────────────── */
+.empty-state-icon {
+ animation: float-variation 6s ease-in-out infinite;
+}
+
+/* ── Recent message action buttons ──────────────────── */
+.btn-icon-sm:active:not(:disabled),
+.btn-icon:active:not(:disabled) {
+ animation: ripple 0.4s ease-out;
+}
+
+/* ── Small decorative: channel list rows ────────────── */
+.dashboard-top-channel-row:hover {
+ animation: none; /* Keep the existing hover translateX */
+}
+
+
+/* ════════════════════════════════════════════════════════
+ 4. RESPECT REDUCED MOTION
+ ════════════════════════════════════════════════════════ */
+@media (prefers-reduced-motion: reduce) {
+ *, *::before, *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/services/frontend/frontend/src/styles/bundle.css b/services/frontend/frontend/src/styles/bundle.css
new file mode 100644
index 0000000..c3eb139
--- /dev/null
+++ b/services/frontend/frontend/src/styles/bundle.css
@@ -0,0 +1,3120 @@
+/* ── Design Tokens — IMPHNEN Neuform Dark ────────────── *
+ * Dark default (no attribute selector) *
+ * Light: [data-theme="light"] *
+ * ──────────────────────────────────────────────────────── */
+
+:root {
+ /* ── Surfaces ──────────────────────────────────── */
+ --surface-base: #050510;
+ --surface-raised: #0a0a1a;
+ --surface-overlay: #12122a;
+ --surface-container: #1a1a35;
+ --surface-border: rgba(255, 255, 255, 0.06);
+ --surface-hover: rgba(59, 130, 246, 0.06);
+ --surface-glass: rgba(5, 5, 16, 0.78);
+
+ /* ── Text ──────────────────────────────────────── */
+ --text-primary: #f1f1f9;
+ --text-secondary: #9d9db5;
+ --text-tertiary: #5c5c78;
+ --text-inverse: #050510;
+
+ /* ── Brand ─────────────────────────────────────── */
+ --color-primary: #3b82f6;
+ --color-primary-hover: #60a5fa;
+ --color-primary-active: #2563eb;
+ --color-primary-muted: rgba(59, 130, 246, 0.12);
+ --gradient-primary: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);
+ --gradient-brand: linear-gradient(135deg, #3b82f6 0%, #5865f2 50%, #6366f1 100%);
+
+ /* ── Semantics ─────────────────────────────────── */
+ --color-success: #10b981;
+ --color-warning: #f59e0b;
+ --color-error: #ef4444;
+ --color-info: #3b82f6;
+ --color-destructive: #ef4444;
+
+ /* ── AI Status ─────────────────────────────────── */
+ --color-ai-flagged: #ef4444;
+ --color-ai-clean: #10b981;
+ --color-ai-warn: #f59e0b;
+ --color-ai-pending: #5c5c78;
+ --color-ai-processing: #3b82f6;
+ --color-ai-error: #dc2626;
+ --color-ai-deleted: #6b7280;
+
+ /* ── Shadows ───────────────────────────────────── */
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35);
+ --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.4);
+
+ /* ── Radii ─────────────────────────────────────── */
+ --radius-sm: 8px;
+ --radius-md: 12px;
+ --radius-lg: 16px;
+ --radius-xl: 23px;
+ --radius-pill: 9999px;
+
+ /* ── Spacing ───────────────────────────────────── */
+ --space-0: 0px;
+ --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;
+ --space-16: 64px;
+
+ /* ── Layout ────────────────────────────────────── */
+ --sidebar-width: 240px;
+ --header-height: 0px;
+
+ /* ── Transitions ───────────────────────────────── */
+ --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-normal: 300ms cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-bounce: 400ms cubic-bezier(0.34, 1.56, 0.64, 1);
+
+ /* ── Z-index ───────────────────────────────────── */
+ --z-sidebar: 30;
+ --z-header: 40;
+ --z-overlay: 50;
+ --z-modal: 60;
+ --z-toast: 70;
+}
+
+/* ── Light Theme ──────────────────────────────────────── */
+[data-theme="light"] {
+ --surface-base: #f4f6fb;
+ --surface-raised: #ffffff;
+ --surface-overlay: #eeeff4;
+ --surface-container: #dde0e8;
+ --surface-border: rgba(0, 0, 0, 0.06);
+ --surface-hover: rgba(37, 99, 235, 0.05);
+ --surface-glass: rgba(255, 255, 255, 0.72);
+
+ --text-primary: #0f172a;
+ --text-secondary: #475569;
+ --text-tertiary: #94a3b8;
+ --text-inverse: #ffffff;
+
+ --color-primary: #2563eb;
+ --color-primary-hover: #3b82f6;
+ --color-primary-active: #1d4ed8;
+ --color-primary-muted: rgba(37, 99, 235, 0.1);
+ --gradient-primary: linear-gradient(135deg, #2563eb 0%, #6366f1 100%);
+ --gradient-brand: linear-gradient(135deg, #2563eb 0%, #5865f2 50%, #6366f1 100%);
+
+ --color-success: #10b981;
+ --color-warning: #f59e0b;
+ --color-error: #ef4444;
+ --color-info: #2563eb;
+ --color-destructive: #ef4444;
+
+ --color-ai-flagged: #ef4444;
+ --color-ai-clean: #10b981;
+ --color-ai-warn: #f59e0b;
+ --color-ai-pending: #94a3b8;
+ --color-ai-processing: #2563eb;
+ --color-ai-error: #dc2626;
+ --color-ai-deleted: #6b7280;
+
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04);
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.06);
+ --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.08);
+}
+/* ── Reset & Base ─────────────────────────────────────── */
+
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ font-size: 16px;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ line-height: 1.6;
+ min-height: 100vh;
+ overflow-x: hidden;
+ background: var(--surface-base);
+ color: var(--text-primary);
+}
+
+a {
+ color: var(--color-primary);
+ text-decoration: none;
+ transition: color var(--transition-fast);
+}
+a:hover { color: var(--color-primary-hover); }
+
+img { max-width: 100%; height: auto; }
+svg { display: inline-block; vertical-align: middle; }
+
+/* ── Scrollbar ────────────────────────────────────────── */
+::-webkit-scrollbar { width: 6px; height: 6px; }
+::-webkit-scrollbar-track { background: transparent; }
+::-webkit-scrollbar-thumb {
+ background: var(--color-primary);
+ border-radius: var(--radius-pill);
+}
+
+::selection {
+ background: rgba(59, 130, 246, 0.25);
+ color: var(--text-primary);
+}
+[data-theme="light"] ::selection {
+ background: rgba(37, 99, 235, 0.25);
+}
+/* ── Utility Classes ──────────────────────────────────── */
+
+/* Layout */
+.flex { display: flex; }
+.inline-flex { display: inline-flex; }
+.grid { display: grid; }
+.block { display: block; }
+.hidden { display: none; }
+.flex-col { flex-direction: column; }
+.flex-row { flex-direction: row; }
+.flex-wrap { flex-wrap: wrap; }
+.flex-1 { flex: 1 1 0%; }
+.flex-shrink-0, .shrink-0 { flex-shrink: 0; }
+.items-center { align-items: center; }
+.items-start { align-items: flex-start; }
+.items-end { align-items: flex-end; }
+.items-baseline { align-items: baseline; }
+.justify-center { justify-content: center; }
+.justify-between { justify-content: space-between; }
+.justify-end { justify-content: flex-end; }
+.gap-0 { gap: 0; }
+.gap-1 { gap: var(--space-1); }
+.gap-1\.5 { gap: 6px; }
+.gap-2 { gap: var(--space-2); }
+.gap-3 { gap: var(--space-3); }
+.gap-4 { gap: var(--space-4); }
+.gap-6 { gap: var(--space-6); }
+.gap-8 { gap: var(--space-8); }
+.grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
+.grid-cols-3 { grid-template-columns: repeat(3, 1fr); }
+
+/* Width / Height */
+.w-full { width: 100%; }
+.w-auto { width: auto; }
+.h-full { height: 100%; }
+.h-auto { height: auto; }
+.min-w-0 { min-width: 0; }
+.min-h-0 { min-height: 0; }
+
+/* Sizing helpers */
+.h-3 { height: 12px; }
+.h-4 { height: 16px; }
+.h-5 { height: 20px; }
+.h-6 { height: 24px; }
+.h-8 { height: 32px; }
+.h-10 { height: 40px; }
+.h-12 { height: 48px; }
+.h-16 { height: 64px; }
+.h-28 { height: 112px; }
+.w-3 { width: 12px; }
+.w-4 { width: 16px; }
+.w-5 { width: 20px; }
+.w-6 { width: 24px; }
+.w-8 { width: 32px; }
+.w-10 { width: 40px; }
+.w-12 { width: 48px; }
+.w-16 { width: 64px; }
+.w-48 { width: 192px; }
+
+/* Position */
+.relative { position: relative; }
+.absolute { position: absolute; }
+.fixed { position: fixed; }
+.sticky { position: sticky; }
+.inset-0 { inset: 0; }
+.top-0 { top: 0; }
+.right-0 { right: 0; }
+.bottom-0 { bottom: 0; }
+.left-0 { left: 0; }
+
+/* Overflow */
+.overflow-auto { overflow: auto; }
+.overflow-hidden { overflow: hidden; }
+.overflow-y-auto { overflow-y: auto; }
+.overflow-x-auto { overflow-x: auto; }
+
+/* Z-index */
+.z-0 { z-index: 0; }
+.z-10 { z-index: 10; }
+.z-50 { z-index: 50; }
+
+/* Margin */
+.m-0 { margin: 0; }
+.mx-auto { margin-left: auto; margin-right: auto; }
+.ml-auto { margin-left: auto; }
+.mr-auto { margin-right: auto; }
+.mt-0 { margin-top: 0; }
+.mt-1 { margin-top: var(--space-1); }
+.mt-2 { margin-top: var(--space-2); }
+.mt-3 { margin-top: var(--space-3); }
+.mt-4 { margin-top: var(--space-4); }
+.mt-6 { margin-top: var(--space-6); }
+.mb-0 { margin-bottom: 0; }
+.mb-1 { margin-bottom: var(--space-1); }
+.mb-2 { margin-bottom: var(--space-2); }
+.mb-3 { margin-bottom: var(--space-3); }
+.mb-4 { margin-bottom: var(--space-4); }
+.mb-6 { margin-bottom: var(--space-6); }
+.ml-0 { margin-left: 0; }
+.ml-1 { margin-left: var(--space-1); }
+.ml-2 { margin-left: var(--space-2); }
+.mr-1 { margin-right: var(--space-1); }
+.mr-2 { margin-right: var(--space-2); }
+.mr-1\.5 { margin-right: 6px; }
+
+/* Padding */
+.p-0 { padding: 0; }
+.p-1 { padding: var(--space-1); }
+.p-2 { padding: var(--space-2); }
+.p-3 { padding: var(--space-3); }
+.p-4 { padding: var(--space-4); }
+.p-5 { padding: var(--space-5); }
+.p-6 { padding: var(--space-6); }
+.px-1 { padding-left: var(--space-1); padding-right: var(--space-1); }
+.px-2 { padding-left: var(--space-2); padding-right: var(--space-2); }
+.px-3 { padding-left: var(--space-3); padding-right: var(--space-3); }
+.px-4 { padding-left: var(--space-4); padding-right: var(--space-4); }
+.px-6 { padding-left: var(--space-6); padding-right: var(--space-6); }
+.py-0 { padding-top: 0; padding-bottom: 0; }
+.py-1 { padding-top: var(--space-1); padding-bottom: var(--space-1); }
+.py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); }
+.py-3 { padding-top: var(--space-3); padding-bottom: var(--space-3); }
+.py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); }
+.pt-2 { padding-top: var(--space-2); }
+.pt-3 { padding-top: var(--space-3); }
+.pt-4 { padding-top: var(--space-4); }
+.pb-2 { padding-bottom: var(--space-2); }
+.pb-3 { padding-bottom: var(--space-3); }
+.pb-4 { padding-bottom: var(--space-4); }
+.pl-2 { padding-left: var(--space-2); }
+.pr-2 { padding-right: var(--space-2); }
+
+/* Border */
+.border { border: 1px solid var(--surface-border); }
+.border-0 { border: none; }
+.border-t { border-top: 1px solid var(--surface-border); }
+.border-b { border-bottom: 1px solid var(--surface-border); }
+.border-l { border-left: 1px solid var(--surface-border); }
+.border-r { border-right: 1px solid var(--surface-border); }
+.border-l-3 { border-left-width: 3px; }
+.border-destructive { border-color: var(--color-error); }
+.border-border { border-color: var(--surface-border); }
+.border-primary { border-color: var(--color-primary); }
+.rounded-sm { border-radius: var(--radius-sm); }
+.rounded { border-radius: var(--radius-md); }
+.rounded-md { border-radius: var(--radius-md); }
+.rounded-lg { border-radius: var(--radius-lg); }
+.rounded-xl { border-radius: var(--radius-xl); }
+.rounded-full { border-radius: var(--radius-pill); }
+
+/* Typography */
+.text-left { text-align: left; }
+.text-center { text-align: center; }
+.text-right { text-align: right; }
+.whitespace-nowrap { white-space: nowrap; }
+.whitespace-pre-wrap { white-space: pre-wrap; }
+.break-words { word-break: break-word; }
+.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.text-xs { font-size: 0.75rem; line-height: 1rem; }
+.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
+.text-base { font-size: 1rem; line-height: 1.5rem; }
+.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
+.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
+.text-2xl { font-size: 1.5rem; line-height: 2rem; }
+.text-3xl { font-size: 1.875rem; line-height: 2.25rem; }
+.font-normal { font-weight: 400; }
+.font-medium { font-weight: 500; }
+.font-semibold { font-weight: 600; }
+.font-bold { font-weight: 700; }
+.font-extrabold { font-weight: 800; }
+.text-primary { color: var(--text-primary); }
+.text-secondary { color: var(--text-secondary); }
+.text-tertiary { color: var(--text-tertiary); }
+.text-primary-color { color: var(--color-primary); }
+.text-error { color: var(--color-error); }
+.text-success { color: var(--color-success); }
+.text-warning { color: var(--color-warning); }
+.text-inverse { color: var(--text-inverse); }
+
+/* Background */
+.bg-surface { background: var(--surface-raised); }
+.bg-overlay { background: var(--surface-overlay); }
+.bg-base { background: var(--surface-base); }
+.bg-primary { background: var(--color-primary); }
+.bg-transparent { background: transparent; }
+
+/* Opacity / Misc */
+.opacity-0 { opacity: 0; }
+.opacity-50 { opacity: 0.5; }
+.opacity-60 { opacity: 0.6; }
+.opacity-70 { opacity: 0.7; }
+.opacity-80 { opacity: 0.8; }
+.opacity-85 { opacity: 0.85; }
+.object-contain { object-fit: contain; }
+.object-cover { object-fit: cover; }
+.cursor-pointer { cursor: pointer; }
+.cursor-default { cursor: default; }
+.cursor-not-allowed { cursor: not-allowed; }
+.select-none { user-select: none; }
+.pointer-events-none { pointer-events: none; }
+.pointer-events-auto { pointer-events: auto; }
+
+/* Transitions */
+.transition-all { transition: all var(--transition-fast); }
+.transition-transform { transition: transform var(--transition-fast); }
+.transition-opacity { transition: opacity var(--transition-fast); }
+.transition-colors { transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast); }
+.transition-shadow { transition: box-shadow var(--transition-fast); }
+.hover\:scale-105:hover { transform: scale(1.05); }
+.hover\:opacity-80:hover { opacity: 0.8; }
+
+/* Animation */
+.animate-spin { animation: spin 1s linear infinite; }
+@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
+
+/* Responsive */
+@media (max-width: 768px) {
+ .grid-cols-2 { grid-template-columns: 1fr; }
+ .grid-cols-3 { grid-template-columns: 1fr; }
+}
+
+/* ── Extra Utilities (component-used aliases) ────────── */
+.active { background: var(--gradient-primary); color: white; }
+.is-active { background: var(--gradient-primary); color: white; }
+.is-deleted { opacity: 0.6; }
+.is-connecting { animation: pulse-connecting 1s ease-in-out infinite; }
+.is-scroll { overflow-x: auto; flex-wrap: nowrap; }
+.separated > * + * { margin-top: 0.625rem; padding-top: 0.625rem; border-top: 1px solid var(--surface-border); }
+.tall { height: 112px; width: 64px; }
+.typing { animation: pulse-dot 1.5s ease-in-out infinite; }
+.mascot { font-variant-numeric: tabular-nums; }
+
+.ml-0\.5 { margin-left: 2px; }
+.space-y-2 > * + * { margin-top: var(--space-2); }
+.tracking-tight { letter-spacing: -0.025em; }
+.text-foreground { color: var(--text-primary); }
+.text-muted-foreground { color: var(--text-tertiary); }
+.text-destructive { color: var(--color-error); }
+.text-\[10px\] { font-size: 10px; }
+.h-3\.5 { height: 14px; }
+.w-3\.5 { width: 14px; }
+
+.bg-background { background: var(--surface-base); }
+.bg-card { background: var(--surface-raised); }
+.border-input { border-color: var(--surface-border); }
+
+.head-left { display: flex; align-items: center; gap: var(--space-3); }
+.head-right { display: flex; align-items: center; gap: var(--space-4); margin-left: auto; }
+.head-status { display: flex; align-items: center; gap: var(--space-1); font-size: 0.75rem; color: var(--text-secondary); }
+.head-status-dot { width: 8px; height: 8px; border-radius: 50%; transition: all var(--transition-fast); }
+
+.card-bordered { border: 1px solid var(--surface-border); }
+.card-elevated { box-shadow: var(--shadow-md); }
+
+.empty-state-icon { font-size: 3rem; margin-bottom: var(--space-4); opacity: 0.5; }
+
+.input-error { border-color: var(--color-error); }
+.input-soft { background: var(--surface-container); border: 1px solid transparent; }
+
+.theme-toggle { width: 44px; height: 44px; border: 1px solid var(--surface-border); border-radius: var(--radius-md); background: var(--surface-overlay); color: var(--text-tertiary); cursor: pointer; font-size: 1.25rem; display: flex; align-items: center; justify-content: center; transition: all var(--transition-fast); }
+.theme-toggle:hover { color: var(--color-primary); border-color: var(--color-primary); }
+
+.mascot-controls { display: flex; align-items: center; gap: var(--space-2); }
+.mascot-inline { display: flex; align-items: center; gap: var(--space-1); }
+
+.placeholder-muted-foreground::placeholder { color: var(--text-tertiary); }
+
+.md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+@media (max-width: 768px) { .md\:grid-cols-2 { grid-template-columns: 1fr; } }
+
+.bg-destructive\/15 { background: rgba(239, 68, 68, 0.15); }
+.border-border\/50 { border-color: rgba(255, 255, 255, 0.03); }
+[data-theme="light"] .border-border\/50 { border-color: rgba(0, 0, 0, 0.03); }
+/* ── App Shell & Layout ───────────────────────────────── */
+
+.app-shell {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ height: 100vh;
+ background: var(--surface-base);
+ color: var(--text-primary);
+}
+
+/* ── Sidebar ─────────────────────────────────────────── */
+.app-sidebar {
+ width: var(--sidebar-width);
+ flex-shrink: 0;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ border-right: 1px solid var(--surface-border);
+ background: var(--surface-glass);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ position: sticky;
+ top: 0;
+ z-index: var(--z-sidebar);
+}
+
+.sidebar-brand {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ height: 60px;
+ padding: 0 var(--space-4);
+ flex-shrink: 0;
+ border-bottom: 1px solid var(--surface-border);
+}
+
+.sidebar-brand-icon {
+ font-size: 1.25rem;
+ line-height: 1;
+}
+
+.sidebar-brand-text {
+ display: flex;
+ flex-direction: column;
+}
+
+.sidebar-brand-name {
+ font-weight: 800;
+ font-size: 1rem;
+ background: var(--gradient-brand);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ letter-spacing: -0.03em;
+ line-height: 1.2;
+}
+
+.sidebar-brand-subtitle {
+ color: var(--text-tertiary);
+ font-size: 0.6875rem;
+ font-weight: 500;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ line-height: 1.3;
+}
+
+/* ── Navigation ──────────────────────────────────────── */
+.sidebar-nav {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-1);
+ padding: var(--space-3);
+ overflow-y: auto;
+}
+
+.sidebar-nav-item {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ height: 40px;
+ padding: 0 var(--space-3);
+ border: none;
+ border-radius: 10px;
+ background: transparent;
+ color: var(--text-secondary);
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ cursor: pointer;
+ transition: all var(--transition-fast);
+ text-align: left;
+ width: 100%;
+ position: relative;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.sidebar-nav-item:hover {
+ background: var(--surface-hover);
+ color: var(--color-primary);
+}
+
+.sidebar-nav-item.is-active {
+ background: var(--surface-overlay);
+ color: var(--color-primary);
+ font-weight: 600;
+}
+
+.sidebar-nav-item.is-active::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 3px;
+ height: 24px;
+ border-radius: var(--radius-pill);
+ background: var(--gradient-primary);
+ box-shadow: 0 0 12px rgba(59, 130, 246, 0.4);
+ animation: indicator-grow 250ms cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+.sidebar-nav-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 20px;
+ height: 20px;
+ flex-shrink: 0;
+ font-size: 1rem;
+}
+
+/* ── Sidebar Footer ──────────────────────────────────── */
+.sidebar-footer {
+ flex-shrink: 0;
+ padding: var(--space-3);
+ border-top: 1px solid var(--surface-border);
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+}
+
+.sidebar-footer-status {
+ display: flex;
+ align-items: center;
+ gap: var(--space-1);
+ flex: 1;
+ min-width: 0;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ flex-shrink: 0;
+ transition: background var(--transition-fast);
+}
+
+.status-dot.is-connecting {
+ animation: pulse-connecting 1s ease-in-out infinite;
+}
+
+.status-text {
+ font-size: 0.75rem;
+ color: var(--text-tertiary);
+ white-space: nowrap;
+}
+
+/* ── Content Area ────────────────────────────────────── */
+.app-content {
+ flex: 1;
+ min-width: 0;
+ overflow-y: auto;
+ padding: var(--space-6);
+ max-width: 1400px;
+ animation: content-enter 500ms cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+.app-content > * {
+ animation: card-in 500ms cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.app-content > :nth-child(1) { animation-delay: 30ms; }
+.app-content > :nth-child(2) { animation-delay: 60ms; }
+.app-content > :nth-child(3) { animation-delay: 90ms; }
+
+/* ── Theme Toggle in Sidebar ──────────────────────────── */
+.sidebar-theme-btn {
+ width: 32px;
+ height: 32px;
+ border: 1px solid var(--surface-border);
+ border-radius: var(--radius-sm);
+ background: var(--surface-overlay);
+ color: var(--text-tertiary);
+ cursor: pointer;
+ font-size: 0.875rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all var(--transition-fast);
+ flex-shrink: 0;
+}
+.sidebar-theme-btn:hover {
+ color: var(--color-primary);
+ border-color: var(--color-primary);
+}
+
+/* ── Mobile Tab Bar ──────────────────────────────────── */
+.mobile-tab-bar {
+ display: none;
+}
+
+@media (max-width: 768px) {
+ .app-main { flex-direction: column; }
+ .app-sidebar { display: none; }
+ .app-content { padding: var(--space-4); }
+
+ .mobile-tab-bar {
+ display: flex;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: var(--surface-base);
+ border-top: 1px solid var(--surface-border);
+ z-index: var(--z-overlay);
+ }
+
+ .mobile-tab-item {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.25rem;
+ padding: 0.5rem;
+ background: none;
+ border: none;
+ font-family: inherit;
+ font-size: 0.625rem;
+ cursor: pointer;
+ color: var(--text-tertiary);
+ transition: color var(--transition-fast);
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ .mobile-tab-item.is-active {
+ color: var(--color-primary);
+ }
+
+ .mobile-tab-item-icon {
+ font-size: 1.25rem;
+ line-height: 1;
+ }
+}
+
+/* ── Animations ──────────────────────────────────────── */
+@keyframes content-enter {
+ from { opacity: 0; transform: translateY(16px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+@keyframes card-in {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+@keyframes indicator-grow {
+ from { height: 0; opacity: 0; }
+ to { height: 24px; opacity: 1; }
+}
+@keyframes pulse-connecting {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.4; transform: scale(0.7); }
+}
+/* ── UI Primitives ────────────────────────────────────── */
+
+/* ── Button ──────────────────────────────────────────── */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ padding: 12px 24px;
+ height: 44px;
+ border: 1px solid transparent;
+ border-radius: var(--radius-pill);
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 20px;
+ letter-spacing: 0.02em;
+ cursor: pointer;
+ transition: all var(--transition-fast);
+ white-space: nowrap;
+ user-select: none;
+ -webkit-tap-highlight-color: transparent;
+ text-decoration: none;
+}
+.btn:active:not(:disabled) { transform: scale(0.97); }
+.btn:disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
+
+.btn-primary {
+ background: var(--gradient-primary);
+ color: white;
+ border: none;
+}
+.btn-primary:hover:not(:disabled) {
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.25);
+ transform: translateY(-1px);
+}
+
+.btn-secondary {
+ background: transparent;
+ color: var(--color-primary);
+ border: 1.5px solid var(--color-primary);
+}
+.btn-secondary:hover:not(:disabled) {
+ background: var(--color-primary-muted);
+ transform: translateY(-1px);
+}
+
+.btn-destructive {
+ background: var(--color-error);
+ color: white;
+ border: none;
+}
+.btn-destructive:hover:not(:disabled) {
+ filter: brightness(1.1);
+ transform: translateY(-1px);
+}
+
+.btn-outline {
+ background: var(--surface-glass);
+ border: 1px solid var(--surface-border);
+ color: var(--text-secondary);
+}
+.btn-outline:hover:not(:disabled) {
+ background: var(--surface-raised);
+ border-color: var(--color-primary);
+ color: var(--color-primary);
+}
+
+.btn-ghost {
+ background: transparent;
+ color: var(--text-secondary);
+ border: none;
+ height: auto;
+ padding: 0.5rem 0.75rem;
+ border-radius: var(--radius-md);
+}
+.btn-ghost:hover:not(:disabled) {
+ background: var(--surface-hover);
+ color: var(--text-primary);
+}
+
+.btn-link {
+ background: transparent;
+ color: var(--color-primary);
+ border: none;
+ height: auto;
+ padding: 0;
+ font-weight: 500;
+}
+.btn-link:hover:not(:disabled) { text-decoration: underline; }
+
+/* Sizes */
+.btn-sm { padding: 8px 16px; height: 36px; font-size: 12px; border-radius: var(--radius-md); }
+.btn-lg { padding: 14px 28px; height: 48px; font-size: 16px; }
+.btn-icon { width: 44px; height: 44px; padding: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 1.25rem; border-radius: var(--radius-pill); }
+.btn-icon-sm { width: 36px; height: 36px; padding: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 1rem; border-radius: var(--radius-md); }
+
+/* ── Card ────────────────────────────────────────────── */
+.card {
+ background: var(--surface-raised);
+ border: 1px solid var(--surface-border);
+ border-radius: var(--radius-md);
+ overflow: hidden;
+ box-shadow: var(--shadow-sm);
+ transition: all var(--transition-normal);
+}
+.card:hover {
+ box-shadow: var(--shadow-md);
+}
+.card-interactive:hover {
+ border-color: var(--color-primary);
+ box-shadow: var(--shadow-md);
+ transform: translateY(-1px);
+}
+
+.card-header {
+ padding: var(--space-4) var(--space-6);
+ border-bottom: 1px solid var(--surface-border);
+}
+.card-title {
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--text-primary);
+}
+.card-description {
+ font-size: 0.875rem;
+ color: var(--text-secondary);
+ margin-top: var(--space-1);
+}
+.card-content { padding: var(--space-6); }
+.card-footer {
+ padding: var(--space-4) var(--space-6);
+ border-top: 1px solid var(--surface-border);
+}
+
+/* ── Badge ───────────────────────────────────────────── */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 8px;
+ border-radius: var(--radius-sm);
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 16px;
+ letter-spacing: 0.03em;
+ background: var(--surface-container);
+ color: var(--text-secondary);
+ border: none;
+ transition: all var(--transition-fast);
+}
+.badge-primary { background: var(--color-primary-muted); color: var(--color-primary); }
+.badge-success { background: rgba(16, 185, 129, 0.12); color: var(--color-success); }
+.badge-warning { background: rgba(245, 158, 11, 0.12); color: var(--color-warning); }
+.badge-destructive { background: rgba(239, 68, 68, 0.12); color: var(--color-error); }
+.badge-outline { background: transparent; border: 1px solid var(--surface-border); color: var(--text-tertiary); }
+.badge-info { background: var(--color-primary-muted); color: var(--color-primary); }
+
+/* ── Status Badge ────────────────────────────────────── */
+.status-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-1);
+ padding: 3px 10px;
+ border-radius: var(--radius-pill);
+ font-size: 11px;
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ transition: all var(--transition-fast);
+}
+.status-badge::before {
+ content: '';
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+.status-badge-flagged { background: rgba(239, 68, 68, 0.12); color: var(--color-ai-flagged); }
+.status-badge-flagged::before { background: var(--color-ai-flagged); box-shadow: 0 0 8px rgba(239, 68, 68, 0.6); }
+.status-badge-clean { background: rgba(16, 185, 129, 0.12); color: var(--color-ai-clean); }
+.status-badge-clean::before { background: var(--color-ai-clean); box-shadow: 0 0 8px rgba(16, 185, 129, 0.4); }
+.status-badge-warn { background: rgba(245, 158, 11, 0.12); color: var(--color-ai-warn); }
+.status-badge-warn::before { background: var(--color-ai-warn); box-shadow: 0 0 8px rgba(245, 158, 11, 0.4); }
+.status-badge-pending { background: rgba(92, 92, 120, 0.12); color: var(--color-ai-pending); }
+.status-badge-pending::before { background: var(--color-ai-pending); }
+.status-badge-processing { background: var(--color-primary-muted); color: var(--color-ai-processing); }
+.status-badge-processing::before { background: var(--color-ai-processing); animation: pulse-dot 1.5s ease-in-out infinite; }
+.status-badge-error { background: rgba(220, 38, 38, 0.12); color: var(--color-ai-error); }
+.status-badge-error::before { background: var(--color-ai-error); box-shadow: 0 0 8px rgba(220, 38, 38, 0.4); }
+
+@keyframes pulse-dot {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.3; }
+}
+
+/* ── Input ───────────────────────────────────────────── */
+.input {
+ display: block;
+ width: 100%;
+ padding: var(--space-3);
+ background: var(--surface-container);
+ border: 1.5px solid transparent;
+ border-radius: var(--radius-sm);
+ color: var(--text-primary);
+ font-family: inherit;
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 24px;
+ transition: all var(--transition-fast);
+ outline: none;
+}
+.input::placeholder { color: var(--text-tertiary); }
+.input:focus {
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 2px var(--color-primary-muted), 0 0 16px rgba(59, 130, 246, 0.08);
+ background: var(--surface-raised);
+}
+.input[aria-invalid="true"] {
+ border-color: var(--color-error);
+ box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.12);
+}
+
+/* ── Select ──────────────────────────────────────────── */
+.select {
+ display: block;
+ width: 100%;
+ padding: var(--space-3) 2rem var(--space-3) var(--space-3);
+ background: var(--surface-container);
+ border: 1.5px solid transparent;
+ border-radius: var(--radius-sm);
+ color: var(--text-primary);
+ font-family: inherit;
+ font-size: 0.875rem;
+ cursor: pointer;
+ transition: all var(--transition-fast);
+ outline: none;
+ appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235c5c78' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 0.75rem center;
+}
+.select:focus {
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 2px var(--color-primary-muted);
+ background: var(--surface-raised);
+}
+
+/* ── Skeleton ────────────────────────────────────────── */
+.skeleton {
+ background: linear-gradient(110deg, var(--surface-container) 30%, rgba(59, 130, 246, 0.04) 50%, var(--surface-container) 70%);
+ background-size: 200% 100%;
+ animation: shimmer 1.8s ease-in-out infinite;
+ border-radius: var(--radius-sm);
+}
+.skeleton-circular { border-radius: 50%; }
+@keyframes shimmer {
+ 0% { background-position: 200% 0; }
+ 100% { background-position: -200% 0; }
+}
+
+/* ── Empty State ─────────────────────────────────────── */
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: var(--space-16) var(--space-4);
+ text-align: center;
+}
+.empty-state-title {
+ font-size: 0.9375rem;
+ font-weight: 600;
+ color: var(--text-secondary);
+ margin-bottom: var(--space-2);
+}
+.empty-state-description {
+ font-size: 0.8125rem;
+ color: var(--text-tertiary);
+ max-width: 280px;
+ line-height: 1.5;
+}
+
+/* ── Tabs ────────────────────────────────────────────── */
+.tabs { display: flex; flex-direction: column; }
+.tab-list {
+ display: flex;
+ gap: var(--space-2);
+ border-bottom: 2px solid var(--surface-border);
+}
+.tab-trigger {
+ padding: 8px 16px;
+ background: transparent;
+ border: none;
+ border-bottom: 2px solid transparent;
+ margin-bottom: -2px;
+ color: var(--text-secondary);
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all var(--transition-fast);
+}
+.tab-trigger:hover { color: var(--text-primary); }
+.tab-trigger.active,
+.tab-trigger[aria-selected="true"] {
+ color: var(--color-primary);
+ font-weight: 600;
+ border-bottom-color: var(--color-primary);
+}
+.tab-content {
+ padding-top: var(--space-4);
+ animation: fade-in-up var(--transition-normal) ease-out;
+}
+
+/* ── Modal ───────────────────────────────────────────── */
+.modal-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.55);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: var(--z-modal);
+ animation: fade-in var(--transition-fast) ease-out;
+}
+.modal-content {
+ background: var(--surface-raised);
+ border-radius: var(--radius-lg);
+ border: 1px solid var(--surface-border);
+ box-shadow: var(--shadow-lg);
+ max-width: 90vw;
+ max-height: 85vh;
+ overflow: auto;
+ animation: scale-in var(--transition-bounce);
+}
+.modal-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--space-4) var(--space-6);
+ border-bottom: 1px solid var(--surface-border);
+}
+.modal-body { padding: var(--space-6); }
+.modal-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: var(--space-2);
+ padding: var(--space-4) var(--space-6);
+ border-top: 1px solid var(--surface-border);
+}
+
+/* ── Toast ───────────────────────────────────────────── */
+.toast-container {
+ position: fixed;
+ bottom: var(--space-4);
+ right: var(--space-4);
+ z-index: var(--z-toast);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+ pointer-events: none;
+}
+.toast {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ padding: 0.75rem 1rem;
+ background: var(--surface-raised);
+ border: 1px solid var(--surface-border);
+ border-radius: var(--radius-md);
+ box-shadow: var(--shadow-lg);
+ min-width: 300px;
+ max-width: 420px;
+ pointer-events: auto;
+ animation: slide-in-right var(--transition-bounce);
+}
+.toast-success { border-left: 3px solid var(--color-success); }
+.toast-error { border-left: 3px solid var(--color-error); }
+.toast-warning { border-left: 3px solid var(--color-warning); }
+.toast-info { border-left: 3px solid var(--color-primary); }
+.toast-close {
+ margin-left: auto;
+ background: none;
+ border: none;
+ color: var(--text-tertiary);
+ cursor: pointer;
+ padding: 0.25rem;
+ transition: color var(--transition-fast);
+}
+.toast-close:hover { color: var(--text-primary); }
+
+/* ── Animations ──────────────────────────────────────── */
+@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
+@keyframes fade-in-up { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
+@keyframes scale-in { from { opacity: 0; transform: scale(0.92); } to { opacity: 1; transform: scale(1); } }
+@keyframes slide-in-right { from { opacity: 0; transform: translateX(120%); } to { opacity: 1; transform: translateX(0); } }
+
+.animate-fade-in { animation: fade-in var(--transition-normal) ease-out; }
+.animate-fade-in-up { animation: fade-in-up var(--transition-normal) ease-out; }
+.animate-scale-in { animation: scale-in var(--transition-normal) ease-out; }
+.animate-slide-in-right { animation: slide-in-right var(--transition-normal) ease-out; }
+
+/* Reduced motion */
+@media (prefers-reduced-motion: reduce) {
+ *, *::before, *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
+/* ── Messages Panel ───────────────────────────────────── */
+
+.messages-panel { display: flex; flex-direction: column; gap: var(--space-5); }
+
+/* ── Filter Bar ──────────────────────────────────────── */
+.filter-bar {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--space-3);
+}
+
+.filter-bar-search {
+ position: relative;
+ flex: 1;
+ min-width: 200px;
+}
+
+.filter-bar-search-icon {
+ position: absolute;
+ left: 0.75rem;
+ top: 50%;
+ transform: translateY(-50%);
+ color: var(--text-tertiary);
+ pointer-events: none;
+}
+
+.filter-bar-input {
+ width: 100%;
+ padding: 0.5rem 0.75rem 0.5rem 2.25rem;
+ height: 36px;
+ background: var(--surface-container);
+ border: 1px solid transparent;
+ border-radius: var(--radius-pill);
+ color: var(--text-primary);
+ font-family: inherit;
+ font-size: 0.875rem;
+ transition: all var(--transition-fast);
+ outline: none;
+}
+.filter-bar-input::placeholder { color: var(--text-tertiary); }
+.filter-bar-input:focus {
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 2px var(--color-primary-muted);
+ background: var(--surface-raised);
+}
+
+.filter-bar-chips {
+ display: flex;
+ align-items: center;
+ gap: 0.375rem;
+ flex-wrap: wrap;
+}
+
+.filter-chip {
+ padding: 0.25rem 0.75rem;
+ border-radius: var(--radius-pill);
+ font-size: 0.6875rem;
+ font-weight: 500;
+ border: 1px solid transparent;
+ background: var(--surface-hover);
+ color: var(--text-tertiary);
+ cursor: pointer;
+ transition: all var(--transition-fast);
+ font-family: inherit;
+}
+.filter-chip:hover {
+ color: var(--text-secondary);
+ background: var(--surface-container);
+}
+.filter-chip.is-active {
+ background: var(--gradient-primary);
+ color: white;
+}
+
+.filter-bar-live-count {
+ font-size: 0.875rem;
+ color: var(--text-tertiary);
+ white-space: nowrap;
+}
+
+/* ── Stats Bar ───────────────────────────────────────── */
+.stats-bar {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--space-1);
+}
+
+/* ── Message Card ────────────────────────────────────── */
+.msg-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--surface-border);
+ border-radius: var(--radius-md);
+ overflow: hidden;
+ transition: all var(--transition-fast);
+ box-shadow: var(--shadow-sm);
+}
+.msg-card:hover {
+ border-color: rgba(59, 130, 246, 0.20);
+ box-shadow: var(--shadow-md);
+ transform: translateY(-1px);
+}
+.msg-card.is-deleted {
+ opacity: 0.6;
+ border-color: var(--color-error);
+}
+
+.msg-card-inner {
+ display: flex;
+ gap: var(--space-3);
+ padding: var(--space-4);
+}
+
+.msg-card-avatar {
+ width: 40px;
+ height: 40px;
+ flex-shrink: 0;
+ border-radius: 50%;
+ object-fit: cover;
+}
+
+.msg-card-body { min-width: 0; flex: 1; }
+
+.msg-card-meta {
+ display: flex;
+ align-items: baseline;
+ gap: var(--space-2);
+ margin-bottom: var(--space-2);
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.msg-card-username {
+ font-size: 0.8125rem;
+ font-weight: 600;
+ color: var(--text-primary);
+}
+
+.msg-card-channel {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-1);
+ font-size: 0.6875rem;
+ color: var(--text-tertiary);
+ background: var(--surface-container);
+ padding: 1px 8px;
+ border-radius: var(--radius-pill);
+}
+.msg-card-channel::before { content: '#'; opacity: 0.5; }
+
+.msg-card-time {
+ font-size: 0.6875rem;
+ color: var(--text-tertiary);
+ font-variant-numeric: tabular-nums;
+ margin-left: auto;
+}
+
+/* ── AI Badge ────────────────────────────────────────── */
+.msg-card-ai-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ padding: 2px 8px;
+ border-radius: 6px;
+ font-size: 0.625rem;
+ font-weight: 600;
+ margin-left: var(--space-1);
+}
+.msg-card-ai-badge.clean {
+ background: rgba(16, 185, 129, 0.10);
+ color: var(--color-ai-clean);
+}
+.msg-card-ai-badge.warn {
+ background: rgba(245, 158, 11, 0.10);
+ color: var(--color-ai-warn);
+}
+.msg-card-ai-badge.flagged {
+ background: rgba(239, 68, 68, 0.10);
+ color: var(--color-ai-flagged);
+}
+.msg-card-ai-badge.processing {
+ background: var(--color-primary-muted);
+ color: var(--color-ai-processing);
+}
+.msg-card-ai-badge.pending {
+ background: rgba(92, 92, 120, 0.10);
+ color: var(--color-ai-pending);
+}
+
+/* ── Reply indicator (Discord-style reference block) ──── */
+.msg-row-reply {
+ display: flex;
+ align-items: stretch;
+ margin: 0.375rem 0 0.625rem;
+ border-radius: 6px;
+ overflow: hidden;
+ cursor: default;
+ transition: background 150ms ease;
+}
+.msg-row-reply:hover {
+ background: rgba(59, 130, 246, 0.04);
+}
+.msg-row-reply-line {
+ width: 3px;
+ flex-shrink: 0;
+ border-radius: 2px;
+ background: linear-gradient(180deg, #3b82f6, #8b5cf6);
+ opacity: 0.6;
+}
+.msg-row-reply-main {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.375rem 0.625rem;
+ min-width: 0;
+ flex: 1;
+ font-size: 0.75rem;
+ line-height: 1.4;
+}
+.msg-row-reply-avatar {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.625rem;
+ font-weight: 700;
+ color: white;
+ background: linear-gradient(135deg, #3b82f6, #6366f1);
+ text-transform: uppercase;
+ box-shadow: 0 1px 3px rgba(59, 130, 246, 0.25);
+}
+.msg-row-reply-label {
+ flex-shrink: 0;
+ color: var(--text-tertiary);
+ font-weight: 450;
+ opacity: 0.8;
+}
+.msg-row-reply-user {
+ flex-shrink: 0;
+ font-weight: 700;
+ color: #3b82f6;
+ letter-spacing: -0.01em;
+}
+.msg-row-reply-snippet {
+ color: var(--text-tertiary);
+ font-style: italic;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ flex: 1;
+ min-width: 0;
+ opacity: 0.9;
+ display: inline-flex;
+ align-items: center;
+ gap: 0;
+}
+.msg-row-reply-snippet::before {
+ content: '"';
+ opacity: 0.5;
+ margin-right: 1px;
+}
+.msg-row-reply-snippet::after {
+ content: '"';
+ opacity: 0.5;
+ margin-left: 1px;
+}
+
+/* ── Message Content ─────────────────────────────────── */
+.msg-card-content {
+ font-size: 0.875rem;
+ line-height: 1.5;
+ white-space: pre-wrap;
+ word-break: break-word;
+ color: var(--text-primary);
+}
+.msg-card-content.is-deleted {
+ opacity: 0.6;
+ color: var(--text-secondary);
+}
+
+.msg-card-messages.separated > * + * {
+ margin-top: 0.625rem;
+ padding-top: 0.625rem;
+ border-top: 1px solid var(--surface-border);
+}
+
+/* ── Actions Bar ─────────────────────────────────────── */
+.msg-card-actions {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-3) var(--space-4);
+ border-top: 1px solid var(--surface-border);
+ background: var(--surface-base);
+}
+
+.msg-card-actions .btn-icon-sm {
+ color: var(--text-tertiary);
+}
+.msg-card-actions .btn-icon-sm:hover {
+ color: var(--color-primary);
+}
+
+/* ── Embed ───────────────────────────────────────────── */
+.msg-embed {
+ margin-top: var(--space-2);
+ padding: var(--space-3);
+ border-left: 3px solid var(--color-primary);
+ border-radius: var(--radius-sm);
+ background: var(--surface-overlay);
+}
+.msg-embed-title {
+ font-weight: 600;
+ font-size: 0.875rem;
+ color: var(--text-primary);
+ margin-bottom: var(--space-1);
+}
+.msg-embed-description {
+ font-size: 0.8125rem;
+ color: var(--text-secondary);
+ line-height: 1.5;
+}
+.msg-embed-fields {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-1);
+ margin-top: var(--space-2);
+}
+.msg-embed-field {
+ display: flex;
+ flex-direction: column;
+}
+.msg-embed-field-name {
+ font-size: 0.75rem;
+ font-weight: 600;
+ color: var(--text-secondary);
+ margin-bottom: 0.125rem;
+}
+.msg-embed-field-value {
+ font-size: 0.8125rem;
+ color: var(--text-primary);
+}
+.msg-embed-footer {
+ margin-top: var(--space-2);
+ font-size: 0.6875rem;
+ color: var(--text-tertiary);
+}
+
+/* ── Image Grid ──────────────────────────────────────── */
+.image-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: var(--space-2);
+}
+.image-grid-item {
+ aspect-ratio: 1;
+ border-radius: var(--radius-md);
+ overflow: hidden;
+ cursor: pointer;
+ transition: all var(--transition-fast);
+ border: 1px solid var(--surface-border);
+}
+.image-grid-item:hover {
+ opacity: 0.85;
+ transform: scale(1.02);
+}
+.image-grid-item img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+/* ── Message Media ───────────────────────────────────── */
+.msg-media-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ margin-top: var(--space-2);
+}
+.msg-media-row.is-scroll {
+ overflow-x: auto;
+ flex-wrap: nowrap;
+}
+.msg-thumb {
+ width: 64px;
+ height: 64px;
+ object-fit: cover;
+ border-radius: var(--radius-sm);
+ border: 1px solid var(--surface-border);
+ transition: all var(--transition-fast);
+}
+.msg-thumb:hover { transform: scale(1.08); }
+.msg-thumb-link {
+ display: block;
+ flex-shrink: 0;
+ overflow: hidden;
+ border-radius: var(--radius-sm);
+ border: 1px solid var(--surface-border);
+ transition: all var(--transition-fast);
+}
+.msg-thumb-link:hover { border-color: var(--color-primary); }
+.msg-sticker {
+ width: 48px;
+ height: 48px;
+ object-fit: contain;
+}
+.msg-video {
+ height: 112px;
+ width: 192px;
+ flex-shrink: 0;
+ border-radius: var(--radius-sm);
+ border: 1px solid var(--surface-border);
+ object-fit: cover;
+ background: #000;
+}
+.msg-media-overflow {
+ display: flex;
+ width: 64px;
+ height: 64px;
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--radius-sm);
+ border: 1px solid var(--surface-border);
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ background: var(--surface-container);
+}
+.msg-media-overflow.tall { height: 112px; width: 64px; }
+
+/* ── Message Categories ──────────────────────────────── */
+.msg-cats {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-1);
+ margin-top: var(--space-2);
+}
+
+/* ── AI Analysis ─────────────────────────────────────── */
+.msg-analysis {
+ margin-top: var(--space-2);
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-sm);
+ font-size: 0.75rem;
+ border-left: 3px solid;
+}
+.msg-analysis.flagged {
+ background: rgba(239, 68, 68, 0.06);
+ border-color: var(--color-ai-flagged);
+}
+.msg-analysis.clean {
+ background: rgba(16, 185, 129, 0.06);
+ border-color: var(--color-ai-clean);
+}
+.msg-analysis-row {
+ display: flex;
+ align-items: flex-start;
+ gap: var(--space-2);
+}
+.msg-analysis-body { min-width: 0; flex: 1; }
+.msg-analysis-summary {
+ display: block;
+ font-weight: 500;
+ margin-bottom: var(--space-1);
+}
+.msg-analysis-text {
+ font-size: 0.75rem;
+ line-height: 1.5;
+ white-space: pre-wrap;
+}
+
+/* ── Message Error ───────────────────────────────────── */
+.msg-error {
+ margin-top: var(--space-2);
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-sm);
+ font-size: 0.75rem;
+ color: var(--color-warning);
+ background: rgba(245, 158, 11, 0.06);
+}
+
+/* ── Message Skeleton ────────────────────────────────── */
+.msg-skel {
+ display: flex;
+ gap: var(--space-3);
+ padding: var(--space-4);
+}
+.msg-skel-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background: linear-gradient(90deg, var(--surface-container) 25%, rgba(59, 130, 246, 0.04) 50%, var(--surface-container) 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s ease-in-out infinite;
+ flex-shrink: 0;
+}
+.msg-skel-lines {
+ min-width: 0;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+}
+.msg-skel-line {
+ height: 20px;
+ background: linear-gradient(90deg, var(--surface-container) 25%, rgba(59, 130, 246, 0.04) 50%, var(--surface-container) 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s ease-in-out infinite;
+ border-radius: var(--radius-sm);
+}
+.msg-skel-badges {
+ display: flex;
+ gap: var(--space-2);
+}
+.msg-skel-badge {
+ height: 24px;
+ border-radius: var(--radius-pill);
+ background: linear-gradient(90deg, var(--surface-container) 25%, rgba(59, 130, 246, 0.04) 50%, var(--surface-container) 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s ease-in-out infinite;
+}
+
+/* ── Feed ────────────────────────────────────────────── */
+.feed-wrap {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-4);
+}
+.feed-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: var(--space-16) var(--space-4);
+ text-align: center;
+}
+.feed-empty-title {
+ font-size: 0.9375rem;
+ font-weight: 600;
+ color: var(--text-secondary);
+ margin-bottom: var(--space-2);
+}
+.feed-empty-desc {
+ font-size: 0.8125rem;
+ color: var(--text-tertiary);
+ max-width: 280px;
+ line-height: 1.5;
+}
+.feed-sentinel { height: var(--space-4); }
+.feed-loader { margin-top: var(--space-4); text-align: center; }
+
+/* ── Misc ────────────────────────────────────────────── */
+.search-count {
+ font-size: 0.875rem;
+ color: var(--text-secondary);
+}
+.icon-spin { animation: spin 1s linear infinite; }
+.custom-emoji {
+ display: inline-block;
+ height: 20px;
+ width: 20px;
+ vertical-align: middle;
+ object-fit: contain;
+}
+
+/* ── Legacy aliases (components still use these) ────── */
+.panel-card { background: var(--surface-raised); border: 1px solid var(--surface-border); border-radius: var(--radius-md); overflow: hidden; box-shadow: var(--shadow-sm); transition: all var(--transition-normal); }
+.panel-card-head { padding: var(--space-5) var(--space-6); border-bottom: 1px solid var(--surface-border); }
+.panel-card-title { font-size: 1rem; font-weight: 600; color: var(--text-primary); }
+.panel-card-desc { font-size: 0.875rem; color: var(--text-secondary); margin-top: var(--space-1); }
+
+.search-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
+.search-wrap { position: relative; flex: 1; min-width: 200px; }
+.search-icon { position: absolute; left: 0.75rem; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); pointer-events: none; }
+.search-input { width: 100%; padding: 0.5rem 0.75rem 0.5rem 2.25rem; height: 36px; background: var(--surface-container); border: 1px solid transparent; border-radius: var(--radius-pill); color: var(--text-primary); font-family: inherit; font-size: 0.875rem; transition: all var(--transition-fast); outline: none; }
+.search-input::placeholder { color: var(--text-tertiary); }
+.search-input:focus { border-color: var(--color-primary); box-shadow: 0 0 0 2px var(--color-primary-muted); background: var(--surface-raised); }
+
+.filter-group { display: flex; align-items: center; gap: 0.375rem; margin-left: auto; }
+.filter-chip-v2 { padding: 0.25rem 0.75rem; border-radius: var(--radius-pill); font-size: 0.6875rem; font-weight: 500; border: 1px solid transparent; background: var(--surface-hover); color: var(--text-tertiary); cursor: pointer; transition: all var(--transition-fast); font-family: inherit; }
+.filter-chip-v2:hover { color: var(--text-secondary); background: var(--surface-container); }
+.filter-chip-v2.is-active { background: var(--gradient-primary); color: white; }
+
+.msg-row { padding: var(--space-3) 0; border-bottom: 1px solid var(--surface-border); transition: all var(--transition-fast); }
+.msg-row:hover { background: var(--surface-hover); margin: 0 calc(-1 * var(--space-4)); padding-left: var(--space-4); padding-right: var(--space-4); border-radius: var(--radius-sm); }
+.msg-row:last-child { border-bottom: none; }
+.msg-row-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; }
+.msg-row-time { font-size: 0.6875rem; color: var(--text-tertiary); font-variant-numeric: tabular-nums; min-width: 48px; }
+.msg-row-badge { display: inline-flex; align-items: center; gap: 0.125rem; font-size: 0.75rem; }
+.msg-row-badge.edited { color: var(--text-secondary); }
+.msg-row-badge.deleted { color: var(--color-error); }
+.msg-row-status { margin-left: auto; display: flex; align-items: center; gap: var(--space-1); }
+.msg-row-body { font-size: 0.875rem; line-height: 1.5rem; white-space: pre-wrap; word-break: break-word; }
+.msg-row-body.is-deleted { opacity: 0.6; color: var(--text-secondary); }
+
+.msg-card-head { display: flex; align-items: baseline; gap: var(--space-2); margin-bottom: var(--space-2); }
+.msg-card-name { font-size: 0.875rem; font-weight: 600; color: var(--text-primary); }
+.msg-card-messages > * + * { margin-top: 0.625rem; padding-top: 0.625rem; border-top: 1px solid var(--surface-border); }
+.msg-card-messages.separated > * + * { margin-top: 0.625rem; padding-top: 0.625rem; border-top: 1px solid var(--surface-border); }
+
+.msg-actions { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
+.msg-retry-hint { font-size: 0.75rem; color: var(--text-tertiary); opacity: 0.7; }
+
+.msg-sticker-placeholder { display: flex; width: 48px; height: 48px; align-items: center; justify-content: center; border-radius: var(--radius-sm); border: 1px solid var(--surface-border); }
+
+.msg-analysis-icon { margin-top: 0.125rem; flex-shrink: 0; }
+
+.img-empty { display: flex; align-items: center; justify-content: center; height: 128px; color: var(--text-secondary); font-style: italic; }
+/* ── Dashboard Panel ──────────────────────────────────── */
+
+.dashboard-panel {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-6);
+}
+
+.dashboard-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: var(--space-4);
+}
+
+/* ── Stats Overview (4-column grid) ──────────────────── */
+.dashboard-stats-grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: var(--space-4);
+}
+
+.dashboard-metric-card {
+ padding: var(--space-5);
+ overflow: hidden;
+ height: 140px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+.dashboard-metric-card:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-md);
+ border-color: rgba(59, 130, 246, 0.15);
+}
+
+.dashboard-metric-content {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-3);
+}
+
+.dashboard-metric-label {
+ color: var(--text-secondary);
+ font-size: 0.75rem;
+ font-weight: 500;
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+}
+
+.dashboard-metric-value {
+ margin-top: var(--space-1);
+ font-size: 1.75rem;
+ font-weight: 800;
+ letter-spacing: -0.03em;
+ color: var(--text-primary);
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.dashboard-metric-trend {
+ font-size: 0.6875rem;
+ font-weight: 500;
+ margin-top: var(--space-1);
+}
+.dashboard-metric-trend.positive { color: var(--color-success); }
+.dashboard-metric-trend.negative { color: var(--color-error); }
+
+.dashboard-metric-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.75rem;
+ height: 2.75rem;
+ border-radius: var(--radius-lg);
+ font-size: 1.25rem;
+}
+
+/* ── Summary Lists ───────────────────────────────────── */
+.dashboard-wide-card { grid-column: span 2; }
+
+.dashboard-summary-list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.dashboard-summary-row {
+ display: flex;
+ align-items: flex-start;
+ gap: var(--space-3);
+ padding: var(--space-3);
+ border-bottom: 1px solid var(--surface-border);
+ transition: all var(--transition-fast);
+}
+.dashboard-summary-row:hover {
+ background: var(--surface-hover);
+}
+
+.dashboard-summary-avatar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: var(--radius-full);
+ background: var(--surface-container);
+ color: var(--text-secondary);
+ font-weight: 700;
+ overflow: hidden;
+}
+.dashboard-summary-avatar-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+.dashboard-channel-avatar {
+ color: var(--color-primary);
+ background: var(--color-primary-muted);
+}
+
+.dashboard-summary-main { min-width: 0; flex: 1; }
+.dashboard-summary-title {
+ color: var(--text-primary);
+ font-size: 0.875rem;
+ font-weight: 600;
+}
+.dashboard-summary-text {
+ margin-top: var(--space-1);
+ color: var(--text-secondary);
+ font-size: 0.8125rem;
+ line-height: 1.45;
+}
+.dashboard-summary-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ margin-top: var(--space-2);
+ color: var(--text-tertiary);
+ font-size: 0.75rem;
+}
+
+.dashboard-list-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-3);
+ padding: var(--space-10) var(--space-4);
+ text-align: center;
+}
+
+/* ── Skeleton cells ──────────────────────────────────── */
+.dashboard-skel-row {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ padding: var(--space-3);
+}
+.dashboard-skel-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ flex-shrink: 0;
+ background: linear-gradient(90deg, var(--surface-container) 25%, rgba(59, 130, 246, 0.04) 50%, var(--surface-container) 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s ease-in-out infinite;
+}
+.dashboard-skel-lines {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+.dashboard-skel-line {
+ height: 16px;
+ border-radius: var(--radius-sm);
+ background: linear-gradient(90deg, var(--surface-container) 25%, rgba(59, 130, 246, 0.04) 50%, var(--surface-container) 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s ease-in-out infinite;
+}
+
+/* ── Responsive ──────────────────────────────────────── */
+@media (max-width: 1024px) {
+ .dashboard-stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+}
+@media (max-width: 768px) {
+ .dashboard-stats-grid { grid-template-columns: 1fr; }
+ .dashboard-wide-card { grid-column: span 1; }
+}
+
+/* ── Legacy aliases ──────────────────────────────────── */
+.dashboard-stats { display: flex; flex-wrap: wrap; gap: var(--space-4); }
+.dashboard-top-channels, .dashboard-summary-list { display: flex; flex-direction: column; gap: var(--space-2); }
+.dashboard-top-channel-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); background: var(--surface-container); color: var(--text-secondary); font-size: 0.875rem; transition: all var(--transition-fast); }
+.dashboard-top-channel-row:hover { background: var(--surface-overlay); transform: translateX(2px); }
+.dashboard-moderation-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
+.dashboard-queue-value { font-size: 1.75rem; font-weight: 800; color: var(--text-primary); letter-spacing: -0.03em; }
+.dashboard-queue-label { margin-top: var(--space-1); color: var(--text-secondary); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; }
+.dashboard-list-card { overflow: hidden; }
+.dashboard-list-toolbar { margin-bottom: var(--space-4); }
+
+.badge-secondary { background: var(--color-primary-muted); color: var(--color-primary); }
+/* ── Live Panel (Bento Grid) ──────────────────────────── */
+
+.live-body {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-6);
+}
+
+.live-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.live-title {
+ font-size: 1.5rem;
+ font-weight: 700;
+ letter-spacing: -0.025em;
+ background: var(--gradient-brand);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+.live-desc {
+ font-size: 0.875rem;
+ color: var(--text-secondary);
+ margin-top: var(--space-1);
+}
+
+/* ── Bento Grid ──────────────────────────────────────── */
+.live-grid {
+ display: grid;
+ gap: var(--space-4);
+}
+
+/* 2-column bento layout */
+.live-bento {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--space-4);
+}
+
+.live-bento > .live-span-2 {
+ grid-column: 1 / -1;
+}
+
+@media (max-width: 768px) {
+ .live-bento { grid-template-columns: 1fr; }
+}
+
+/* ── Voice Connection Card ───────────────────────────── */
+.voice-connection {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+}
+
+.voice-connection-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.voice-connection-status {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-size: 0.875rem;
+}
+
+.voice-connection-dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+}
+.voice-connection-dot.connected { background: var(--color-success); box-shadow: 0 0 8px rgba(16, 185, 129, 0.4); }
+.voice-connection-dot.connecting { background: var(--color-warning); animation: pulse-connecting 1s ease-in-out infinite; }
+.voice-connection-dot.disconnected { background: var(--text-tertiary); }
+
+/* ── Active Speakers ─────────────────────────────────── */
+.speak-list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.speak-item {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-sm);
+ transition: background var(--transition-fast);
+}
+.speak-item:hover { background: var(--surface-hover); }
+
+.speak-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background: var(--surface-container);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+}
+
+.speak-info { flex: 1; min-width: 0; }
+.speak-name { font-size: 0.8125rem; font-weight: 500; color: var(--text-primary); }
+.speak-status { font-size: 0.6875rem; color: var(--text-tertiary); }
+
+.speak-indicator {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+}
+.speak-bar {
+ width: 3px;
+ height: 12px;
+ border-radius: 1px;
+ background: var(--color-primary);
+ animation: bar-pulse 0.8s ease-in-out infinite;
+}
+.speak-bar:nth-child(2) { animation-delay: 0.1s; }
+.speak-bar:nth-child(3) { animation-delay: 0.2s; }
+.speak-bar:nth-child(4) { animation-delay: 0.3s; }
+.speak-bar:nth-child(5) { animation-delay: 0.4s; }
+
+@keyframes bar-pulse {
+ 0%, 100% { transform: scaleY(1); }
+ 50% { transform: scaleY(0.5); }
+}
+
+/* ── Audio Visualizer ────────────────────────────────── */
+.audio-visualizer-canvas {
+ width: 100%;
+ height: 80px;
+ border-radius: var(--radius-sm);
+ background: var(--surface-container);
+}
+
+/* ── Mic Level Meter ─────────────────────────────────── */
+.mic-level {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.mic-track {
+ position: relative;
+ height: var(--space-2);
+ border-radius: var(--radius-pill);
+ overflow: hidden;
+ background: var(--surface-container);
+}
+.mic-fill {
+ height: 100%;
+ border-radius: var(--radius-pill);
+ background: var(--gradient-primary);
+ transition: width 100ms ease;
+}
+.mic-clip {
+ position: absolute;
+ height: 100%;
+ width: 2px;
+ background: white;
+ right: 0;
+}
+
+/* ── Music Player / Now Playing ──────────────────────── */
+.np-body {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.np-meta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+}
+
+.np-tags {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+}
+
+.np-sep {
+ border-top: 1px solid var(--surface-border);
+ padding-top: var(--space-3);
+}
+
+.wave-wrap {
+ border: 1px solid var(--surface-border);
+ border-radius: var(--radius-md);
+ padding: var(--space-3);
+}
+.wave-progress {
+ height: var(--space-2);
+ border-radius: var(--radius-pill);
+ overflow: hidden;
+ background: var(--surface-container);
+ margin-bottom: var(--space-2);
+}
+.wave-bar {
+ height: 100%;
+ border-radius: var(--radius-pill);
+ background: var(--gradient-primary);
+ transition: width 200ms ease;
+}
+.wave-info {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+.wave-title {
+ font-size: 0.875rem;
+ font-weight: 500;
+ max-width: 192px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.wave-time {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-size: 0.75rem;
+ font-family: monospace;
+}
+
+/* ── Screen Share Panel ──────────────────────────────── */
+.scrn-actions {
+ display: flex;
+ gap: var(--space-2);
+}
+.scrn-status {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: var(--radius-sm);
+ font-size: 0.75rem;
+ color: var(--color-success);
+}
+
+/* ── Recordings Panel ────────────────────────────────── */
+.rec-list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+.rec-item {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ padding: var(--space-3);
+ border-radius: var(--radius-md);
+ border: 1px solid var(--surface-border);
+ transition: all var(--transition-fast);
+}
+.rec-item:hover {
+ background: var(--surface-hover);
+ border-color: var(--color-primary);
+ transform: translateX(4px);
+}
+.rec-info { flex: 1; min-width: 0; }
+.rec-name { font-size: 0.875rem; font-weight: 500; }
+.rec-meta {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ margin-top: var(--space-1);
+}
+.rec-actions {
+ display: flex;
+ align-items: center;
+ gap: 0.375rem;
+ flex-shrink: 0;
+}
+.rec-footer { margin-top: var(--space-3); text-align: center; }
+.rec-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: var(--space-8);
+ gap: var(--space-2);
+}
+
+.music-body {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+/* ── Legacy aliases ──────────────────────────────────── */
+.live-panel { display: flex; flex-direction: column; gap: var(--space-6); }
+.live-grid-3 { grid-template-columns: repeat(3, 1fr); }
+
+.recordings-sub-panel { overflow: hidden; }
+
+.audio-visualizer { display: flex; align-items: flex-end; justify-content: center; height: 80px; gap: 2px; padding: var(--space-2); }
+.audio-visualizer-bars { display: flex; align-items: flex-end; gap: 2px; height: 100%; width: 100%; }
+.audio-bar { width: 8px; background: var(--gradient-primary); border-radius: var(--radius-sm) var(--radius-sm) 0 0; transition: height 100ms ease; }
+
+.mic-level-meter { display: flex; flex-direction: column; gap: var(--space-2); }
+.mic-row { display: flex; align-items: center; gap: var(--space-2); }
+
+@media (max-width: 1024px) { .live-grid-3 { grid-template-columns: repeat(2, 1fr); } }
+@media (max-width: 768px) { .live-grid-3 { grid-template-columns: 1fr; } }
+/* ── Polish: Particles, Theme Toggle, Mascot ──────────── */
+
+/* ── Particle Background ─────────────────────────────── */
+.particle-bg {
+ position: fixed;
+ inset: 0;
+ overflow: hidden;
+ pointer-events: none;
+ z-index: 0;
+}
+.particle-orb {
+ position: absolute;
+ border-radius: 50%;
+ will-change: transform, filter;
+}
+.particle-orb:nth-child(1) {
+ width: 600px; height: 600px;
+ top: -200px; right: -150px;
+ background: radial-gradient(circle, rgba(59, 130, 246, 0.5) 0%, transparent 70%);
+ filter: blur(100px);
+ animation: orb-drift-enhanced 30s ease-in-out infinite,
+ hue-rotate-slow 12s ease-in-out infinite,
+ orb-pulse 8s ease-in-out infinite;
+}
+.particle-orb:nth-child(2) {
+ width: 500px; height: 500px;
+ bottom: -150px; left: -150px;
+ background: radial-gradient(circle, rgba(99, 102, 241, 0.5) 0%, transparent 70%);
+ filter: blur(100px);
+ animation: orb-drift-enhanced 35s ease-in-out infinite reverse,
+ hue-rotate-slow 14s ease-in-out infinite reverse,
+ orb-pulse 10s ease-in-out infinite;
+ animation-delay: -5s;
+}
+.particle-orb:nth-child(3) {
+ width: 350px; height: 350px;
+ top: 40%; left: 60%;
+ background: radial-gradient(circle, rgba(99, 102, 241, 0.4) 0%, transparent 70%);
+ filter: blur(100px);
+ animation: orb-drift-enhanced 40s ease-in-out infinite,
+ hue-rotate-slow 16s ease-in-out infinite,
+ orb-pulse 6s ease-in-out infinite;
+ animation-delay: -10s;
+}
+.particle-orb:nth-child(4) {
+ width: 250px; height: 250px;
+ top: 10%; left: 20%;
+ background: radial-gradient(circle, rgba(16, 185, 129, 0.4) 0%, transparent 70%);
+ filter: blur(100px);
+ animation: orb-drift-enhanced 25s ease-in-out infinite reverse,
+ hue-rotate-slow 18s ease-in-out infinite,
+ orb-pulse 12s ease-in-out infinite;
+ animation-delay: -3s;
+}
+
+.particle-orb:nth-child(5) {
+ width: 180px; height: 180px;
+ top: 70%; left: 30%;
+ background: radial-gradient(circle, rgba(59, 130, 246, 0.35) 0%, transparent 70%);
+ filter: blur(80px);
+ animation: orb-drift-enhanced 20s ease-in-out infinite,
+ hue-rotate-slow 10s ease-in-out infinite,
+ orb-pulse 5s ease-in-out infinite;
+ animation-delay: -7s;
+}
+
+@media (max-width: 768px) {
+ .particle-bg { display: none; }
+ .app-shell::after { display: none; }
+}
+
+/* ── Theme Toggle ────────────────────────────────────── */
+.theme-toggle-btn {
+ width: 32px;
+ height: 32px;
+ border: 1px solid var(--surface-border);
+ border-radius: var(--radius-sm);
+ background: var(--surface-overlay);
+ color: var(--text-tertiary);
+ cursor: pointer;
+ font-size: 0.875rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all var(--transition-fast);
+ flex-shrink: 0;
+ -webkit-tap-highlight-color: transparent;
+}
+.theme-toggle-btn:hover {
+ color: var(--color-primary);
+ border-color: var(--color-primary);
+}
+
+/* ── Mascot Chatbot ──────────────────────────────────── */
+.mascot-widget {
+ position: fixed;
+ right: var(--space-6);
+ bottom: var(--space-6);
+ z-index: var(--z-toast);
+}
+.mascot-launcher {
+ width: 3.75rem;
+ height: 3.75rem;
+ border: none;
+ border-radius: 50%;
+ background: var(--gradient-primary);
+ color: white;
+ box-shadow: var(--shadow-lg), 0 0 24px rgba(59, 130, 246, 0.25);
+ cursor: pointer;
+ font-size: 1.5rem;
+ transition: all var(--transition-fast);
+ animation: float 4s ease-in-out infinite;
+}
+.mascot-launcher:hover {
+ transform: translateY(-3px) scale(1.05);
+ animation: none;
+}
+.mascot-panel {
+ width: min(24rem, calc(100vw - 2rem));
+ height: min(32.5rem, calc(100vh - 7rem));
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ border: 1px solid var(--surface-border);
+ border-radius: var(--radius-lg);
+ background: var(--surface-raised);
+ box-shadow: var(--shadow-lg);
+ animation: fade-in-up var(--transition-bounce);
+}
+.mascot-panel.minimized { height: 3.75rem; }
+.mascot-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-3);
+ padding: var(--space-3);
+ background: var(--gradient-primary);
+ color: white;
+ flex-shrink: 0;
+}
+.mascot-header-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2rem;
+ height: 2rem;
+ border-radius: var(--radius-sm);
+ background: rgba(255,255,255,0.15);
+}
+.mascot-title {
+ font-size: 0.875rem;
+ font-weight: 700;
+ line-height: 1.15;
+ letter-spacing: 0.02em;
+}
+.mascot-subtitle { margin-top: 0.125rem; font-size: 0.6875rem; opacity: 0.7; }
+.mascot-icon-button {
+ width: 2rem;
+ height: 2rem;
+ border: none;
+ border-radius: var(--radius-sm);
+ background: transparent;
+ color: white;
+ cursor: pointer;
+ transition: background var(--transition-fast);
+}
+.mascot-icon-button:hover { background: rgba(255,255,255,0.20); }
+.mascot-messages {
+ flex: 1;
+ min-height: 0;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+ padding: var(--space-4);
+}
+.mascot-message-row {
+ display: flex;
+ gap: var(--space-2);
+ animation: fade-in-up var(--transition-fast) both;
+}
+.mascot-message-row.user { justify-content: flex-end; }
+.mascot-avatar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ width: 1.5rem;
+ height: 1.5rem;
+ border-radius: 50%;
+ background: var(--surface-container);
+ font-size: 0.875rem;
+}
+.mascot-bubble {
+ max-width: 17.5rem;
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-lg);
+ font-size: 0.875rem;
+ line-height: 1.5;
+ overflow-wrap: anywhere;
+}
+.mascot-bubble.user {
+ color: white;
+ background: var(--gradient-primary);
+ border-bottom-right-radius: var(--radius-sm);
+}
+.mascot-bubble.mascot {
+ color: var(--text-primary);
+ background: var(--surface-container);
+ border: 1px solid var(--surface-border);
+ border-bottom-left-radius: var(--radius-sm);
+}
+.mascot-bubble.typing {
+ display: flex;
+ gap: 0.25rem;
+ padding-top: 0.7rem;
+ padding-bottom: 0.7rem;
+}
+.mascot-bubble.typing span {
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 50%;
+ background: var(--text-tertiary);
+ animation: notification-pulse 0.8s infinite;
+}
+.mascot-bubble.typing span:nth-child(2) { animation-delay: 0.1s; }
+.mascot-bubble.typing span:nth-child(3) { animation-delay: 0.2s; }
+@keyframes notification-pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.3; }
+}
+.mascot-form {
+ display: flex;
+ gap: var(--space-2);
+ padding: var(--space-3);
+ border-top: 1px solid var(--surface-border);
+}
+.mascot-input {
+ flex: 1;
+ min-width: 0;
+ border: 1.5px solid transparent;
+ border-radius: var(--radius-sm);
+ padding: var(--space-2) var(--space-3);
+ background: var(--surface-container);
+ color: var(--text-primary);
+ font-size: 0.875rem;
+ transition: all var(--transition-fast);
+}
+.mascot-input:focus {
+ outline: none;
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 2px var(--color-primary-muted);
+ background: var(--surface-raised);
+}
+.mascot-send {
+ width: 2.25rem;
+ border: none;
+ border-radius: var(--radius-sm);
+ background: var(--gradient-primary);
+ color: white;
+ cursor: pointer;
+ transition: all var(--transition-fast);
+}
+.mascot-send:hover { box-shadow: 0 0 12px rgba(59, 130, 246, 0.25); }
+.mascot-send:disabled, .mascot-input:disabled { cursor: not-allowed; opacity: 0.55; }
+
+/* ── Float animation ─────────────────────────────────── */
+@keyframes float {
+ 0%, 100% { transform: translateY(0); }
+ 50% { transform: translateY(-8px); }
+}
+
+/* ── Auth ────────────────────────────────────────────── */
+.auth-box { width: 400px; text-align: center; }
+.auth-lock { font-size: 3rem; margin-bottom: var(--space-4); }
+.auth-title {
+ font-size: 1.375rem;
+ font-weight: 700;
+ margin-bottom: var(--space-2);
+ background: var(--gradient-brand);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+.auth-desc {
+ font-size: 0.875rem;
+ color: var(--text-secondary);
+ margin-bottom: var(--space-6);
+}
+.auth-form { display: flex; flex-direction: column; gap: var(--space-3); }
+.auth-error {
+ font-size: 0.75rem;
+ color: var(--color-error);
+ padding: var(--space-2) var(--space-3);
+ background: rgba(239, 68, 68, 0.08);
+ border-radius: var(--radius-sm);
+}
+.auth-close-btn {
+ position: absolute;
+ top: 0;
+ right: 0;
+ background: none;
+ border: none;
+ color: var(--text-tertiary);
+ font-size: 1.25rem;
+ cursor: pointer;
+ padding: 0.25rem;
+ line-height: 1;
+ transition: color var(--transition-fast);
+}
+.auth-close-btn:hover { color: var(--text-primary); }
+/* ── Animations — Live Dashboard Effects ──────────────── *
+ * Keyframes, applied animations, and utility classes. *
+ * Respects prefers-reduced-motion. *
+ * ──────────────────────────────────────────────────────── */
+
+/* ════════════════════════════════════════════════════════
+ 1. KEYFRAMES
+ ════════════════════════════════════════════════════════ */
+
+/* ── Entry / Reveal ─────────────────────────────────── */
+@keyframes slide-in-left {
+ from { opacity: 0; transform: translateX(-20px); }
+ to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes slide-in-down {
+ from { opacity: 0; transform: translateY(-12px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+@keyframes scale-bounce {
+ 0% { opacity: 0; transform: scale(0.85); }
+ 60% { opacity: 1; transform: scale(1.04); }
+ 100% { opacity: 1; transform: scale(1); }
+}
+
+@keyframes pop-in {
+ 0% { opacity: 0; transform: scale(0.8); }
+ 70% { opacity: 1; transform: scale(1.08); }
+ 100% { opacity: 1; transform: scale(1); }
+}
+
+/* ── Status / Alive ─────────────────────────────────── */
+@keyframes breathe {
+ 0%, 100% { opacity: 1; transform: scale(1); box-shadow: 0 0 4px currentColor; }
+ 50% { opacity: 0.6; transform: scale(1.2); box-shadow: 0 0 14px currentColor; }
+}
+
+@keyframes breathe-soft {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.5; }
+}
+
+@keyframes ring-pulse {
+ 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); }
+ 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
+ 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
+}
+
+@keyframes ring-pulse-success {
+ 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
+ 70% { box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }
+ 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
+}
+
+@keyframes ring-pulse-error {
+ 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
+ 70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); }
+ 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
+}
+
+@keyframes processing-sweep {
+ 0% { background-position: -200% center; }
+ 100% { background-position: 200% center; }
+}
+
+@keyframes live-indicator {
+ 0%, 100% { opacity: 1; box-shadow: 0 0 6px rgba(239, 68, 68, 0.4); }
+ 50% { opacity: 0.7; box-shadow: 0 0 16px rgba(239, 68, 68, 0.7); }
+}
+
+/* ── Ambient / Decorative ───────────────────────────── */
+/* Slow breathing for background glow layers */
+@keyframes ambient-glow {
+ 0%, 100% { opacity: 0.3; }
+ 50% { opacity: 0.7; }
+}
+
+/* Dynamic mesh drift — shifts gradient center positions */
+@keyframes bg-mesh-drift {
+ 0% { background-position: 0% 0%, 100% 100%, 50% 50%; }
+ 25% { background-position: 30% 20%, 70% 80%, 20% 60%; }
+ 50% { background-position: 60% 10%, 40% 60%, 80% 30%; }
+ 75% { background-position: 20% 60%, 80% 20%, 40% 80%; }
+ 100% { background-position: 0% 0%, 100% 100%, 50% 50%; }
+}
+
+/* Slow hue rotation for orb glow */
+@keyframes hue-rotate-slow {
+ 0% { filter: hue-rotate(0deg); }
+ 50% { filter: hue-rotate(30deg); }
+ 100% { filter: hue-rotate(0deg); }
+}
+
+/* Orb size pulsing */
+@keyframes orb-pulse {
+ 0%, 100% { transform: scale(1); opacity: 0.4; }
+ 50% { transform: scale(1.08); opacity: 0.6; }
+}
+
+/* Expanded orb drift — wider, slower, organic */
+@keyframes orb-drift-enhanced {
+ 0% { transform: translate(0, 0) scale(1); }
+ 20% { transform: translate(80px, -50px) scale(1.1); }
+ 40% { transform: translate(-50px, 70px) scale(0.9); }
+ 60% { transform: translate(100px, 30px) scale(1.05); }
+ 80% { transform: translate(-70px, -60px) scale(0.95); }
+ 100% { transform: translate(0, 0) scale(1); }
+}
+
+/* Grain/noise texture shifting */
+@keyframes grain-shift {
+ 0%, 100% { transform: translate(0, 0) rotate(0deg); }
+ 10% { transform: translate(-5%, -5%) rotate(0.5deg); }
+ 20% { transform: translate(-10%, 0%) rotate(-0.5deg); }
+ 30% { transform: translate(0%, 5%) rotate(1deg); }
+ 40% { transform: translate(5%, -3%) rotate(-0.3deg); }
+ 50% { transform: translate(-3%, -8%) rotate(0.8deg); }
+ 60% { transform: translate(8%, 3%) rotate(-0.6deg); }
+ 70% { transform: translate(-6%, 6%) rotate(0.4deg); }
+ 80% { transform: translate(4%, -6%) rotate(-0.7deg); }
+ 90% { transform: translate(-2%, 2%) rotate(0.2deg); }
+}
+
+@keyframes gradient-shimmer {
+ 0% { background-position: 0% center; }
+ 100% { background-position: 200% center; }
+}
+
+@keyframes float-variation {
+ 0%, 100% { transform: translateY(0) rotate(0deg); }
+ 33% { transform: translateY(-8px) rotate(1.5deg); }
+ 66% { transform: translateY(-4px) rotate(-1deg); }
+}
+
+@keyframes fade-in-down {
+ from { opacity: 0; transform: translateY(-10px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* ── Interactive ────────────────────────────────────── */
+@keyframes glow-border {
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
+ 50% { box-shadow: 0 0 12px 1px rgba(59, 130, 246, 0.15); }
+}
+
+@keyframes ripple {
+ 0% { transform: scale(0); opacity: 0.6; }
+ 100% { transform: scale(4); opacity: 0; }
+}
+
+/* ── Bar pulses (audio / visualizer) ────────────────── */
+@keyframes bar-pulse-1 {
+ 0%, 100% { transform: scaleY(1); }
+ 50% { transform: scaleY(0.4); }
+}
+@keyframes bar-pulse-2 {
+ 0%, 100% { transform: scaleY(0.5); }
+ 50% { transform: scaleY(1.2); }
+}
+@keyframes bar-pulse-3 {
+ 0%, 100% { transform: scaleY(0.7); }
+ 50% { transform: scaleY(1); }
+}
+
+@keyframes count-up {
+ from { opacity: 0; transform: translateY(8px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+
+/* ════════════════════════════════════════════════════════
+ 2. UTILITY ANIMATION CLASSES
+ Use these in Rust components: class="animate-breathe"
+ ════════════════════════════════════════════════════════ */
+
+.animate-breathe {
+ animation: breathe 3s ease-in-out infinite;
+}
+
+.animate-breathe-soft {
+ animation: breathe-soft 2s ease-in-out infinite;
+}
+
+.animate-ring-pulse {
+ animation: ring-pulse 2s ease-in-out infinite;
+}
+
+.animate-live-indicator {
+ animation: live-indicator 1.5s ease-in-out infinite;
+}
+
+.animate-float {
+ animation: float-variation 5s ease-in-out infinite;
+}
+
+.animate-gradient-shimmer {
+ background-size: 200% 100%;
+ animation: gradient-shimmer 4s ease-in-out infinite alternate;
+}
+
+.animate-spin-slow {
+ animation: spin 3s linear infinite;
+}
+
+.animate-pulse-connecting {
+ animation: pulse-connecting 1s ease-in-out infinite;
+}
+
+.animate-scale-bounce {
+ animation: scale-bounce 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both;
+}
+
+.animate-pop-in {
+ animation: pop-in 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) both;
+}
+
+.animate-fade-in-down {
+ animation: fade-in-down var(--transition-normal) ease-out;
+}
+
+.animate-slide-in-left {
+ animation: slide-in-left var(--transition-normal) ease-out;
+}
+
+.animate-count-up {
+ animation: count-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+.animate-shimmer-sweep {
+ background: linear-gradient(
+ 110deg,
+ transparent 30%,
+ rgba(59, 130, 246, 0.08) 50%,
+ transparent 70%
+ );
+ background-size: 200% 100%;
+ animation: processing-sweep 1.5s ease-in-out infinite;
+}
+
+
+/* ════════════════════════════════════════════════════════
+ 3. APPLIED ANIMATIONS — auto-wired to selectors
+ ════════════════════════════════════════════════════════ */
+
+/* ── Ambient animated mesh background ────────────────── */
+.app-shell::before {
+ content: '';
+ position: fixed;
+ inset: -50%;
+ pointer-events: none;
+ z-index: -1;
+ background:
+ radial-gradient(ellipse at 20% 30%, rgba(59, 130, 246, 0.06) 0%, transparent 40%),
+ radial-gradient(ellipse at 80% 70%, rgba(99, 102, 241, 0.05) 0%, transparent 40%),
+ radial-gradient(ellipse at 40% 80%, rgba(16, 185, 129, 0.03) 0%, transparent 40%),
+ radial-gradient(ellipse at 60% 20%, rgba(245, 158, 11, 0.02) 0%, transparent 40%);
+ background-size: 200% 200%, 200% 200%, 200% 200%, 200% 200%;
+ animation: bg-mesh-drift 20s ease-in-out infinite alternate;
+ will-change: background-position;
+}
+
+/* ── Subtle noise/grain texture overlay ─────────────── */
+.app-shell::after {
+ content: '';
+ position: fixed;
+ inset: -50%;
+ pointer-events: none;
+ z-index: 0;
+ opacity: 0.015;
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
+ background-repeat: repeat;
+ background-size: 256px 256px;
+ animation: grain-shift 0.5s steps(4) infinite;
+ will-change: transform;
+}
+
+/* ── Light theme mesh adjustment ────────────────────── */
+[data-theme="light"] .app-shell::before {
+ background:
+ radial-gradient(ellipse at 20% 30%, rgba(37, 99, 235, 0.04) 0%, transparent 40%),
+ radial-gradient(ellipse at 80% 70%, rgba(99, 102, 241, 0.04) 0%, transparent 40%),
+ radial-gradient(ellipse at 40% 80%, rgba(16, 185, 129, 0.03) 0%, transparent 40%),
+ radial-gradient(ellipse at 60% 20%, rgba(245, 158, 11, 0.02) 0%, transparent 40%);
+}
+[data-theme="light"] .app-shell::after {
+ opacity: 0.008;
+}
+
+/* ── Gradient text shimmer (brand elements) ─────────── */
+.sidebar-brand-name,
+.live-title,
+.auth-title {
+ background-size: 200% 100%;
+ animation: gradient-shimmer 6s ease-in-out infinite alternate;
+}
+
+/* ── Connected status dot: alive breath ─────────────── */
+.status-dot,
+.voice-connection-dot.connected {
+ animation: breathe 3s ease-in-out infinite;
+}
+
+/* Keep existing connecting animation (overrides above) */
+.status-dot.is-connecting,
+.voice-connection-dot.connecting {
+ animation: pulse-connecting 1s ease-in-out infinite !important;
+}
+
+.status-dot.disconnected,
+.voice-connection-dot.disconnected {
+ animation: none !important;
+}
+
+/* ── Live indicator (red recording dot) ─────────────── */
+.msg-card-ai-badge.flagged::before,
+.msg-analysis.flagged::before {
+ content: '';
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--color-error);
+ margin-right: 4px;
+ vertical-align: middle;
+ animation: live-indicator 1.5s ease-in-out infinite;
+}
+
+/* ── Particle orbs: ambient breathing overlay ───────── */
+.particle-bg {
+ animation: ambient-glow 6s ease-in-out infinite alternate;
+}
+
+/* ── Live bento grid: staggered entry ───────────────── */
+.live-bento > * {
+ animation: fade-in-up 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.live-bento > :nth-child(1) { animation-delay: 0.03s; }
+.live-bento > :nth-child(2) { animation-delay: 0.06s; }
+.live-bento > :nth-child(3) { animation-delay: 0.09s; }
+.live-bento > :nth-child(4) { animation-delay: 0.12s; }
+.live-bento > :nth-child(5) { animation-delay: 0.15s; }
+.live-bento > :nth-child(6) { animation-delay: 0.18s; }
+
+/* ── Dashboard metric cards: staggered entry ────────── */
+.dashboard-metric-card {
+ animation: fade-in-up 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.dashboard-metric-card:nth-child(1) { animation-delay: 0.04s; }
+.dashboard-metric-card:nth-child(2) { animation-delay: 0.08s; }
+.dashboard-metric-card:nth-child(3) { animation-delay: 0.12s; }
+.dashboard-metric-card:nth-child(4) { animation-delay: 0.16s; }
+.dashboard-metric-card:nth-child(5) { animation-delay: 0.20s; }
+.dashboard-metric-card:nth-child(6) { animation-delay: 0.24s; }
+.dashboard-metric-card:nth-child(7) { animation-delay: 0.28s; }
+.dashboard-metric-card:nth-child(8) { animation-delay: 0.32s; }
+
+/* ── Dashboard rows: staggered fade-in ──────────────── */
+.dashboard-summary-row,
+.dashboard-top-channel-row {
+ animation: fade-in 0.4s ease-out both;
+}
+.dashboard-summary-row:nth-child(1), .dashboard-top-channel-row:nth-child(1) { animation-delay: 0.02s; }
+.dashboard-summary-row:nth-child(2), .dashboard-top-channel-row:nth-child(2) { animation-delay: 0.04s; }
+.dashboard-summary-row:nth-child(3), .dashboard-top-channel-row:nth-child(3) { animation-delay: 0.06s; }
+.dashboard-summary-row:nth-child(4), .dashboard-top-channel-row:nth-child(4) { animation-delay: 0.08s; }
+.dashboard-summary-row:nth-child(5), .dashboard-top-channel-row:nth-child(5) { animation-delay: 0.10s; }
+.dashboard-summary-row:nth-child(6), .dashboard-top-channel-row:nth-child(6) { animation-delay: 0.12s; }
+.dashboard-summary-row:nth-child(7), .dashboard-top-channel-row:nth-child(7) { animation-delay: 0.14s; }
+.dashboard-summary-row:nth-child(8), .dashboard-top-channel-row:nth-child(8) { animation-delay: 0.16s; }
+.dashboard-summary-row:nth-child(9), .dashboard-top-channel-row:nth-child(9) { animation-delay: 0.18s; }
+.dashboard-summary-row:nth-child(10),.dashboard-top-channel-row:nth-child(10) { animation-delay: 0.20s; }
+
+/* ── Message cards: staggered entry ─────────────────── */
+.msg-card {
+ animation: fade-in-up 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.msg-card:nth-child(1) { animation-delay: 0.02s; }
+.msg-card:nth-child(2) { animation-delay: 0.05s; }
+.msg-card:nth-child(3) { animation-delay: 0.08s; }
+.msg-card:nth-child(4) { animation-delay: 0.11s; }
+.msg-card:nth-child(5) { animation-delay: 0.14s; }
+.msg-card:nth-child(6) { animation-delay: 0.17s; }
+.msg-card:nth-child(7) { animation-delay: 0.20s; }
+.msg-card:nth-child(8) { animation-delay: 0.23s; }
+.msg-card:nth-child(9) { animation-delay: 0.26s; }
+.msg-card:nth-child(10) { animation-delay: 0.29s; }
+
+/* ── Recording items: slide in from left ────────────── */
+.rec-item {
+ animation: slide-in-left 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+.rec-item:nth-child(1) { animation-delay: 0.02s; }
+.rec-item:nth-child(2) { animation-delay: 0.06s; }
+.rec-item:nth-child(3) { animation-delay: 0.10s; }
+.rec-item:nth-child(4) { animation-delay: 0.14s; }
+.rec-item:nth-child(5) { animation-delay: 0.18s; }
+
+/* ── Speaker items: pop in ──────────────────────────── */
+.speak-item {
+ animation: fade-in 0.3s ease-out both;
+}
+.speak-item:nth-child(1) { animation-delay: 0.02s; }
+.speak-item:nth-child(2) { animation-delay: 0.05s; }
+.speak-item:nth-child(3) { animation-delay: 0.08s; }
+.speak-item:nth-child(4) { animation-delay: 0.11s; }
+.speak-item:nth-child(5) { animation-delay: 0.14s; }
+
+/* ── Audio visualizer bars: individual timing ───────── */
+.audio-bar {
+ animation: bar-pulse-2 1.2s ease-in-out infinite;
+}
+.audio-bar:nth-child(odd) {
+ animation-name: bar-pulse-1;
+ animation-duration: 0.9s;
+}
+.audio-bar:nth-child(3n) {
+ animation-name: bar-pulse-3;
+ animation-duration: 1.5s;
+}
+
+/* ── Interactive cards: hover glow effect ────────────── */
+.card-interactive:hover {
+ animation: glow-border 0.8s ease-in-out;
+ border-color: var(--color-primary);
+}
+
+/* ── Nav active indicator: continuous subtle glow ───── */
+.sidebar-nav-item.is-active::after {
+ animation: indicator-grow 250ms cubic-bezier(0.16, 1, 0.3, 1) both,
+ breathe-soft 3s ease-in-out 0.3s infinite;
+}
+
+/* ── Filter chip active: pop scale ──────────────────── */
+.filter-chip-v2.is-active {
+ animation: pop-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
+}
+
+/* ── AI badge processing: shimmer sweep ─────────────── */
+.msg-card-ai-badge.processing,
+.status-badge-processing {
+ background: linear-gradient(
+ 110deg,
+ var(--color-primary-muted) 25%,
+ rgba(59, 130, 246, 0.35) 50%,
+ var(--color-primary-muted) 75%
+ ) !important;
+ background-size: 200% 100% !important;
+ animation: processing-sweep 1.4s ease-in-out infinite;
+}
+.status-badge-processing::before {
+ animation: ring-pulse 1.2s ease-in-out infinite !important;
+}
+
+/* ── Image grid items: reveal ───────────────────────── */
+.image-grid-item {
+ animation: fade-in 0.4s ease-out both;
+}
+.image-grid-item:nth-child(1) { animation-delay: 0.02s; }
+.image-grid-item:nth-child(2) { animation-delay: 0.05s; }
+.image-grid-item:nth-child(3) { animation-delay: 0.08s; }
+.image-grid-item:nth-child(4) { animation-delay: 0.11s; }
+.image-grid-item:nth-child(5) { animation-delay: 0.14s; }
+.image-grid-item:nth-child(6) { animation-delay: 0.17s; }
+.image-grid-item:nth-child(7) { animation-delay: 0.20s; }
+.image-grid-item:nth-child(8) { animation-delay: 0.23s; }
+
+/* ── Dashboard queue value: count-up feel ───────────── */
+.dashboard-queue-value {
+ animation: count-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+/* ── Voice connection panel: entry ──────────────────── */
+.voice-connection {
+ animation: fade-in-up 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+/* ── Now playing: slide in ──────────────────────────── */
+.np-body {
+ animation: slide-in-left 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+/* ── Skeleton: enhanced shimmer ─────────────────────── */
+.skeleton,
+.msg-skel-avatar,
+.msg-skel-line,
+.msg-skel-badge,
+.dashboard-skel-avatar,
+.dashboard-skel-line {
+ background: linear-gradient(
+ 110deg,
+ var(--surface-container) 28%,
+ rgba(59, 130, 246, 0.06) 48%,
+ var(--surface-container) 68%
+ ) !important;
+ background-size: 200% 100% !important;
+}
+
+/* ── Tab content: stronger entry ────────────────────── */
+.tab-content {
+ animation: fade-in-up 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+/* ── Mobile tab active: subtle indicator ────────────── */
+.mobile-tab-item.is-active {
+ animation: pop-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
+}
+
+/* ── Mascot launcher: enhanced float ────────────────── */
+.mascot-launcher {
+ animation: float-variation 5s ease-in-out infinite;
+}
+.mascot-launcher:hover {
+ animation: none !important;
+}
+
+/* ── Empty state icons: subtle float ────────────────── */
+.empty-state-icon {
+ animation: float-variation 6s ease-in-out infinite;
+}
+
+/* ── Recent message action buttons ──────────────────── */
+.btn-icon-sm:active:not(:disabled),
+.btn-icon:active:not(:disabled) {
+ animation: ripple 0.4s ease-out;
+}
+
+/* ── Small decorative: channel list rows ────────────── */
+.dashboard-top-channel-row:hover {
+ animation: none; /* Keep the existing hover translateX */
+}
+
+
+/* ════════════════════════════════════════════════════════
+ 4. RESPECT REDUCED MOTION
+ ════════════════════════════════════════════════════════ */
+@media (prefers-reduced-motion: reduce) {
+ *, *::before, *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/services/frontend/frontend/src/styles/dashboard.css b/services/frontend/frontend/src/styles/dashboard.css
index 94b0e40..6b31224 100644
--- a/services/frontend/frontend/src/styles/dashboard.css
+++ b/services/frontend/frontend/src/styles/dashboard.css
@@ -189,3 +189,16 @@
.dashboard-stats-grid { grid-template-columns: 1fr; }
.dashboard-wide-card { grid-column: span 1; }
}
+
+/* ── Legacy aliases ──────────────────────────────────── */
+.dashboard-stats { display: flex; flex-wrap: wrap; gap: var(--space-4); }
+.dashboard-top-channels, .dashboard-summary-list { display: flex; flex-direction: column; gap: var(--space-2); }
+.dashboard-top-channel-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); background: var(--surface-container); color: var(--text-secondary); font-size: 0.875rem; transition: all var(--transition-fast); }
+.dashboard-top-channel-row:hover { background: var(--surface-overlay); transform: translateX(2px); }
+.dashboard-moderation-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
+.dashboard-queue-value { font-size: 1.75rem; font-weight: 800; color: var(--text-primary); letter-spacing: -0.03em; }
+.dashboard-queue-label { margin-top: var(--space-1); color: var(--text-secondary); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; }
+.dashboard-list-card { overflow: hidden; }
+.dashboard-list-toolbar { margin-bottom: var(--space-4); }
+
+.badge-secondary { background: var(--color-primary-muted); color: var(--color-primary); }
diff --git a/services/frontend/frontend/src/styles/live.css b/services/frontend/frontend/src/styles/live.css
index f9d3fcd..bafd3f0 100644
--- a/services/frontend/frontend/src/styles/live.css
+++ b/services/frontend/frontend/src/styles/live.css
@@ -298,3 +298,19 @@
flex-direction: column;
gap: var(--space-2);
}
+
+/* ── Legacy aliases ──────────────────────────────────── */
+.live-panel { display: flex; flex-direction: column; gap: var(--space-6); }
+.live-grid-3 { grid-template-columns: repeat(3, 1fr); }
+
+.recordings-sub-panel { overflow: hidden; }
+
+.audio-visualizer { display: flex; align-items: flex-end; justify-content: center; height: 80px; gap: 2px; padding: var(--space-2); }
+.audio-visualizer-bars { display: flex; align-items: flex-end; gap: 2px; height: 100%; width: 100%; }
+.audio-bar { width: 8px; background: var(--gradient-primary); border-radius: var(--radius-sm) var(--radius-sm) 0 0; transition: height 100ms ease; }
+
+.mic-level-meter { display: flex; flex-direction: column; gap: var(--space-2); }
+.mic-row { display: flex; align-items: center; gap: var(--space-2); }
+
+@media (max-width: 1024px) { .live-grid-3 { grid-template-columns: repeat(2, 1fr); } }
+@media (max-width: 768px) { .live-grid-3 { grid-template-columns: 1fr; } }
diff --git a/services/frontend/frontend/src/styles/messages.css b/services/frontend/frontend/src/styles/messages.css
index 6a7c4d5..fce873f 100644
--- a/services/frontend/frontend/src/styles/messages.css
+++ b/services/frontend/frontend/src/styles/messages.css
@@ -189,6 +189,87 @@
color: var(--color-ai-pending);
}
+/* ── Reply indicator (Discord-style reference block) ──── */
+.msg-row-reply {
+ display: flex;
+ align-items: stretch;
+ margin: 0.375rem 0 0.625rem;
+ border-radius: 6px;
+ overflow: hidden;
+ cursor: default;
+ transition: background 150ms ease;
+}
+.msg-row-reply:hover {
+ background: rgba(59, 130, 246, 0.04);
+}
+.msg-row-reply-line {
+ width: 3px;
+ flex-shrink: 0;
+ border-radius: 2px;
+ background: linear-gradient(180deg, #3b82f6, #8b5cf6);
+ opacity: 0.6;
+}
+.msg-row-reply-main {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.375rem 0.625rem;
+ min-width: 0;
+ flex: 1;
+ font-size: 0.75rem;
+ line-height: 1.4;
+}
+.msg-row-reply-avatar {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.625rem;
+ font-weight: 700;
+ color: white;
+ background: linear-gradient(135deg, #3b82f6, #6366f1);
+ text-transform: uppercase;
+ box-shadow: 0 1px 3px rgba(59, 130, 246, 0.25);
+}
+.msg-row-reply-label {
+ flex-shrink: 0;
+ color: var(--text-tertiary);
+ font-weight: 450;
+ opacity: 0.8;
+}
+.msg-row-reply-user {
+ flex-shrink: 0;
+ font-weight: 700;
+ color: #3b82f6;
+ letter-spacing: -0.01em;
+}
+.msg-row-reply-snippet {
+ color: var(--text-tertiary);
+ font-style: italic;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ flex: 1;
+ min-width: 0;
+ opacity: 0.9;
+ display: inline-flex;
+ align-items: center;
+ gap: 0;
+}
+.msg-row-reply-snippet::before {
+ content: '"';
+ opacity: 0.5;
+ margin-right: 1px;
+}
+.msg-row-reply-snippet::after {
+ content: '"';
+ opacity: 0.5;
+ margin-left: 1px;
+}
+
/* ── Message Content ─────────────────────────────────── */
.msg-card-content {
font-size: 0.875rem;
@@ -485,3 +566,47 @@
vertical-align: middle;
object-fit: contain;
}
+
+/* ── Legacy aliases (components still use these) ────── */
+.panel-card { background: var(--surface-raised); border: 1px solid var(--surface-border); border-radius: var(--radius-md); overflow: hidden; box-shadow: var(--shadow-sm); transition: all var(--transition-normal); }
+.panel-card-head { padding: var(--space-5) var(--space-6); border-bottom: 1px solid var(--surface-border); }
+.panel-card-title { font-size: 1rem; font-weight: 600; color: var(--text-primary); }
+.panel-card-desc { font-size: 0.875rem; color: var(--text-secondary); margin-top: var(--space-1); }
+
+.search-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
+.search-wrap { position: relative; flex: 1; min-width: 200px; }
+.search-icon { position: absolute; left: 0.75rem; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); pointer-events: none; }
+.search-input { width: 100%; padding: 0.5rem 0.75rem 0.5rem 2.25rem; height: 36px; background: var(--surface-container); border: 1px solid transparent; border-radius: var(--radius-pill); color: var(--text-primary); font-family: inherit; font-size: 0.875rem; transition: all var(--transition-fast); outline: none; }
+.search-input::placeholder { color: var(--text-tertiary); }
+.search-input:focus { border-color: var(--color-primary); box-shadow: 0 0 0 2px var(--color-primary-muted); background: var(--surface-raised); }
+
+.filter-group { display: flex; align-items: center; gap: 0.375rem; margin-left: auto; }
+.filter-chip-v2 { padding: 0.25rem 0.75rem; border-radius: var(--radius-pill); font-size: 0.6875rem; font-weight: 500; border: 1px solid transparent; background: var(--surface-hover); color: var(--text-tertiary); cursor: pointer; transition: all var(--transition-fast); font-family: inherit; }
+.filter-chip-v2:hover { color: var(--text-secondary); background: var(--surface-container); }
+.filter-chip-v2.is-active { background: var(--gradient-primary); color: white; }
+
+.msg-row { padding: var(--space-3) 0; border-bottom: 1px solid var(--surface-border); transition: all var(--transition-fast); }
+.msg-row:hover { background: var(--surface-hover); margin: 0 calc(-1 * var(--space-4)); padding-left: var(--space-4); padding-right: var(--space-4); border-radius: var(--radius-sm); }
+.msg-row:last-child { border-bottom: none; }
+.msg-row-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; }
+.msg-row-time { font-size: 0.6875rem; color: var(--text-tertiary); font-variant-numeric: tabular-nums; min-width: 48px; }
+.msg-row-badge { display: inline-flex; align-items: center; gap: 0.125rem; font-size: 0.75rem; }
+.msg-row-badge.edited { color: var(--text-secondary); }
+.msg-row-badge.deleted { color: var(--color-error); }
+.msg-row-status { margin-left: auto; display: flex; align-items: center; gap: var(--space-1); }
+.msg-row-body { font-size: 0.875rem; line-height: 1.5rem; white-space: pre-wrap; word-break: break-word; }
+.msg-row-body.is-deleted { opacity: 0.6; color: var(--text-secondary); }
+
+.msg-card-head { display: flex; align-items: baseline; gap: var(--space-2); margin-bottom: var(--space-2); }
+.msg-card-name { font-size: 0.875rem; font-weight: 600; color: var(--text-primary); }
+.msg-card-messages > * + * { margin-top: 0.625rem; padding-top: 0.625rem; border-top: 1px solid var(--surface-border); }
+.msg-card-messages.separated > * + * { margin-top: 0.625rem; padding-top: 0.625rem; border-top: 1px solid var(--surface-border); }
+
+.msg-actions { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
+.msg-retry-hint { font-size: 0.75rem; color: var(--text-tertiary); opacity: 0.7; }
+
+.msg-sticker-placeholder { display: flex; width: 48px; height: 48px; align-items: center; justify-content: center; border-radius: var(--radius-sm); border: 1px solid var(--surface-border); }
+
+.msg-analysis-icon { margin-top: 0.125rem; flex-shrink: 0; }
+
+.img-empty { display: flex; align-items: center; justify-content: center; height: 128px; color: var(--text-secondary); font-style: italic; }
diff --git a/services/frontend/frontend/src/styles/polish.css b/services/frontend/frontend/src/styles/polish.css
index d2e3416..64095fc 100644
--- a/services/frontend/frontend/src/styles/polish.css
+++ b/services/frontend/frontend/src/styles/polish.css
@@ -11,50 +11,63 @@
.particle-orb {
position: absolute;
border-radius: 50%;
- opacity: 0.5;
- will-change: transform;
+ will-change: transform, filter;
}
.particle-orb:nth-child(1) {
width: 600px; height: 600px;
top: -200px; right: -150px;
- background: radial-gradient(circle, var(--color-primary) 0%, transparent 70%);
+ background: radial-gradient(circle, rgba(59, 130, 246, 0.5) 0%, transparent 70%);
filter: blur(100px);
- animation: orb-drift 25s ease-in-out infinite;
+ animation: orb-drift-enhanced 30s ease-in-out infinite,
+ hue-rotate-slow 12s ease-in-out infinite,
+ orb-pulse 8s ease-in-out infinite;
}
.particle-orb:nth-child(2) {
width: 500px; height: 500px;
bottom: -150px; left: -150px;
- background: radial-gradient(circle, #5865f2 0%, transparent 70%);
+ background: radial-gradient(circle, rgba(99, 102, 241, 0.5) 0%, transparent 70%);
filter: blur(100px);
- animation: orb-drift 30s ease-in-out infinite reverse;
+ animation: orb-drift-enhanced 35s ease-in-out infinite reverse,
+ hue-rotate-slow 14s ease-in-out infinite reverse,
+ orb-pulse 10s ease-in-out infinite;
+ animation-delay: -5s;
}
.particle-orb:nth-child(3) {
width: 350px; height: 350px;
top: 40%; left: 60%;
- background: radial-gradient(circle, #6366f1 0%, transparent 70%);
+ background: radial-gradient(circle, rgba(99, 102, 241, 0.4) 0%, transparent 70%);
filter: blur(100px);
- animation: orb-drift 35s ease-in-out infinite;
- animation-delay: -8s;
+ animation: orb-drift-enhanced 40s ease-in-out infinite,
+ hue-rotate-slow 16s ease-in-out infinite,
+ orb-pulse 6s ease-in-out infinite;
+ animation-delay: -10s;
}
.particle-orb:nth-child(4) {
width: 250px; height: 250px;
top: 10%; left: 20%;
- background: radial-gradient(circle, var(--color-success) 0%, transparent 70%);
- opacity: 0.3;
+ background: radial-gradient(circle, rgba(16, 185, 129, 0.4) 0%, transparent 70%);
filter: blur(100px);
- animation: orb-drift 20s ease-in-out infinite;
+ animation: orb-drift-enhanced 25s ease-in-out infinite reverse,
+ hue-rotate-slow 18s ease-in-out infinite,
+ orb-pulse 12s ease-in-out infinite;
animation-delay: -3s;
}
-@keyframes orb-drift {
- 0% { transform: translate(0, 0); }
- 25% { transform: translate(40px, -30px); }
- 50% { transform: translate(-30px, 20px); }
- 75% { transform: translate(20px, -40px); }
- 100% { transform: translate(0, 0); }
+.particle-orb:nth-child(5) {
+ width: 180px; height: 180px;
+ top: 70%; left: 30%;
+ background: radial-gradient(circle, rgba(59, 130, 246, 0.35) 0%, transparent 70%);
+ filter: blur(80px);
+ animation: orb-drift-enhanced 20s ease-in-out infinite,
+ hue-rotate-slow 10s ease-in-out infinite,
+ orb-pulse 5s ease-in-out infinite;
+ animation-delay: -7s;
}
-@media (max-width: 768px) { .particle-bg { display: none; } }
+@media (max-width: 768px) {
+ .particle-bg { display: none; }
+ .app-shell::after { display: none; }
+}
/* ── Theme Toggle ────────────────────────────────────── */
.theme-toggle-btn {
diff --git a/services/frontend/frontend/src/styles/reset.css b/services/frontend/frontend/src/styles/reset.css
index e20b797..0a5f669 100644
--- a/services/frontend/frontend/src/styles/reset.css
+++ b/services/frontend/frontend/src/styles/reset.css
@@ -13,7 +13,7 @@ html {
}
body {
- font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.6;
min-height: 100vh;
overflow-x: hidden;
diff --git a/services/frontend/frontend/src/styles/utilities.css b/services/frontend/frontend/src/styles/utilities.css
index 101ec9b..ec02ea9 100644
--- a/services/frontend/frontend/src/styles/utilities.css
+++ b/services/frontend/frontend/src/styles/utilities.css
@@ -218,3 +218,56 @@
.grid-cols-2 { grid-template-columns: 1fr; }
.grid-cols-3 { grid-template-columns: 1fr; }
}
+
+/* ── Extra Utilities (component-used aliases) ────────── */
+.active { background: var(--gradient-primary); color: white; }
+.is-active { background: var(--gradient-primary); color: white; }
+.is-deleted { opacity: 0.6; }
+.is-connecting { animation: pulse-connecting 1s ease-in-out infinite; }
+.is-scroll { overflow-x: auto; flex-wrap: nowrap; }
+.separated > * + * { margin-top: 0.625rem; padding-top: 0.625rem; border-top: 1px solid var(--surface-border); }
+.tall { height: 112px; width: 64px; }
+.typing { animation: pulse-dot 1.5s ease-in-out infinite; }
+.mascot { font-variant-numeric: tabular-nums; }
+
+.ml-0\.5 { margin-left: 2px; }
+.space-y-2 > * + * { margin-top: var(--space-2); }
+.tracking-tight { letter-spacing: -0.025em; }
+.text-foreground { color: var(--text-primary); }
+.text-muted-foreground { color: var(--text-tertiary); }
+.text-destructive { color: var(--color-error); }
+.text-\[10px\] { font-size: 10px; }
+.h-3\.5 { height: 14px; }
+.w-3\.5 { width: 14px; }
+
+.bg-background { background: var(--surface-base); }
+.bg-card { background: var(--surface-raised); }
+.border-input { border-color: var(--surface-border); }
+
+.head-left { display: flex; align-items: center; gap: var(--space-3); }
+.head-right { display: flex; align-items: center; gap: var(--space-4); margin-left: auto; }
+.head-status { display: flex; align-items: center; gap: var(--space-1); font-size: 0.75rem; color: var(--text-secondary); }
+.head-status-dot { width: 8px; height: 8px; border-radius: 50%; transition: all var(--transition-fast); }
+
+.card-bordered { border: 1px solid var(--surface-border); }
+.card-elevated { box-shadow: var(--shadow-md); }
+
+.empty-state-icon { font-size: 3rem; margin-bottom: var(--space-4); opacity: 0.5; }
+
+.input-error { border-color: var(--color-error); }
+.input-soft { background: var(--surface-container); border: 1px solid transparent; }
+
+.theme-toggle { width: 44px; height: 44px; border: 1px solid var(--surface-border); border-radius: var(--radius-md); background: var(--surface-overlay); color: var(--text-tertiary); cursor: pointer; font-size: 1.25rem; display: flex; align-items: center; justify-content: center; transition: all var(--transition-fast); }
+.theme-toggle:hover { color: var(--color-primary); border-color: var(--color-primary); }
+
+.mascot-controls { display: flex; align-items: center; gap: var(--space-2); }
+.mascot-inline { display: flex; align-items: center; gap: var(--space-1); }
+
+.placeholder-muted-foreground::placeholder { color: var(--text-tertiary); }
+
+.md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+@media (max-width: 768px) { .md\:grid-cols-2 { grid-template-columns: 1fr; } }
+
+.bg-destructive\/15 { background: rgba(239, 68, 68, 0.15); }
+.border-border\/50 { border-color: rgba(255, 255, 255, 0.03); }
+[data-theme="light"] .border-border\/50 { border-color: rgba(0, 0, 0, 0.03); }
diff --git a/services/frontend/shared-types/src/message.rs b/services/frontend/shared-types/src/message.rs
index a363d75..d6f47d7 100644
--- a/services/frontend/shared-types/src/message.rs
+++ b/services/frontend/shared-types/src/message.rs
@@ -45,6 +45,29 @@ pub struct MessageMetadata {
pub embeds: Option>,
#[serde(skip_serializing_if = "Option::is_none")]
pub channel: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reference: Option,
+}
+
+/// Information about a referenced (replied-to) message.
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct ReferenceInfo {
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub message_id: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub channel_id: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub guild_id: Option,
+ #[serde(rename = "type")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ref_type: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub content: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub replied_username: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub replied_user_id: Option,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@@ -197,6 +220,8 @@ pub struct MessageRecord {
pub channel_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub thread_id: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reference_message_id: Option,
pub user_id: String,
pub username: String,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -206,6 +231,10 @@ pub struct MessageRecord {
pub edited_content: Option,
#[serde(rename = "type")]
pub msg_type: String, // "text" | "edited" | "deleted"
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub is_reply: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub is_forward: Option,
pub created_at: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub edited_at: Option,