diff --git a/services/frontend/src/app/(dashboard)/channels/view.tsx b/services/frontend/src/app/(dashboard)/channels/view.tsx index 26c5cb68..580d3c07 100644 --- a/services/frontend/src/app/(dashboard)/channels/view.tsx +++ b/services/frontend/src/app/(dashboard)/channels/view.tsx @@ -23,7 +23,7 @@ export function ChannelsView({ CHANNEL ROSTER · SIGNAL_MAP -
+
NODES: {cultures?.length ?? 0}
diff --git a/services/frontend/src/app/(dashboard)/messages/view.tsx b/services/frontend/src/app/(dashboard)/messages/view.tsx index ed1574f8..f0e75cdb 100644 --- a/services/frontend/src/app/(dashboard)/messages/view.tsx +++ b/services/frontend/src/app/(dashboard)/messages/view.tsx @@ -399,7 +399,7 @@ export function MessagesView({ diff --git a/services/frontend/src/app/(dashboard)/recordings/view.tsx b/services/frontend/src/app/(dashboard)/recordings/view.tsx index f2215d3e..aa0286f6 100644 --- a/services/frontend/src/app/(dashboard)/recordings/view.tsx +++ b/services/frontend/src/app/(dashboard)/recordings/view.tsx @@ -202,7 +202,7 @@ export function RecordingsView({ href={r.download_url} target="_blank" rel="noreferrer" - className="inline-flex items-center gap-1 rounded-[5px] border border-hairline bg-surface-2 px-2 py-1 font-mono text-[10px] text-ink-soft transition-colors hover:bg-surface hover:text-ink" + className="inline-flex items-center gap-1 rounded-md border border-hairline bg-surface-2 px-2 py-1 font-mono text-[10px] text-ink-soft transition-colors hover:bg-surface hover:text-ink" > RAW @@ -211,7 +211,7 @@ export function RecordingsView({ type="button" onClick={() => onDelete(r.id)} disabled={del.isPending} - className="inline-flex items-center gap-1 rounded-[5px] border border-vermilion/30 bg-vermilion/10 px-2 py-1 font-mono text-[10px] text-vermilion transition-colors hover:bg-vermilion/20" + className="inline-flex items-center gap-1 rounded-md border border-vermilion/30 bg-vermilion/10 px-2 py-1 font-mono text-[10px] text-vermilion transition-colors hover:bg-vermilion/20" > PURGE diff --git a/services/frontend/src/app/globals.css b/services/frontend/src/app/globals.css index c72e1ae3..44af3732 100644 --- a/services/frontend/src/app/globals.css +++ b/services/frontend/src/app/globals.css @@ -166,14 +166,7 @@ border-color: var(--panel-border-hover); } - .glass-2 { - background: var(--panel-bg-subtle); - border: 1px solid var(--panel-border); - border-radius: var(--radius-r); - } - /* HUD Tactical Card */ - .linear-card, .hud-card { position: relative; background: var(--panel-bg); @@ -184,7 +177,6 @@ transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); } - .linear-card:hover, .hud-card:hover { border-color: var(--panel-border-hover); transform: translateY(-1px); @@ -263,14 +255,105 @@ 100% { transform: scale(2.2); opacity: 0; } } +@keyframes shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +@keyframes eq { + 0%, 100% { transform: scaleY(0.35); } + 50% { transform: scaleY(1); } +} + +@keyframes fade-up { + from { opacity: 0; transform: translateY(6px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes toast-in { + from { opacity: 0; transform: translateY(8px) scale(0.98); } + to { opacity: 1; transform: translateY(0) scale(1); } +} + +@keyframes scan { + 0% { transform: translateY(-100%); } + 100% { transform: translateY(100%); } +} + .animate-breathe { animation: breathe 2.5s ease-in-out infinite; } .animate-spin-disc { animation: spin-disc 10s linear infinite; } .animate-pulse-ring { animation: pulse-ring 2s cubic-bezier(0.16, 1, 0.3, 1) infinite; } +/* Skeleton shimmer — theme-aware base + moving highlight sheen. */ +.animate-shimmer { + background-color: var(--panel-bg-subtle); + background-image: linear-gradient( + 105deg, + transparent 40%, + color-mix(in oklab, var(--text-primary) 6%, transparent) 50%, + transparent 60% + ); + background-size: 200% 100%; + animation: shimmer 1.6s ease-in-out infinite; +} + +/* Live equalizer bars (mini-player / recording player). */ +.animate-eq { + transform-origin: bottom; + animation: eq 0.9s ease-in-out infinite; +} + +/* Entrance for popovers, toasts, mini-player. */ +.animate-fade-up { animation: fade-up 0.18s cubic-bezier(0.16, 1, 0.3, 1); } +.animate-toast-in { animation: toast-in 0.2s cubic-bezier(0.16, 1, 0.3, 1); } + +/* Sweeping glow line across a hero/placeholder panel. */ +.scan-line { + position: absolute; + inset: 0; + height: 100%; + pointer-events: none; + background: linear-gradient( + 180deg, + transparent, + var(--accent-signal-glow), + transparent + ); + opacity: 0.5; + animation: scan 2.6s ease-in-out infinite; +} + +/* Mobile nav hover/active sweep fill. */ +.game-sweep { + position: absolute; + inset: 0; + overflow: hidden; + border-radius: inherit; + pointer-events: none; +} + +.game-sweep i { + position: absolute; + inset: 0; + background: var(--accent-signal-bg); + transform: translateY(100%); + transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1); +} + +.game-nav-item.is-active .game-sweep i, +.game-nav-item:hover .game-sweep i { + transform: translateY(0); +} + @media (prefers-reduced-motion: reduce) { .animate-breathe, .animate-spin-disc, - .animate-pulse-ring { + .animate-pulse-ring, + .animate-shimmer, + .animate-eq, + .animate-fade-up, + .animate-toast-in, + .scan-line { animation: none !important; } *, diff --git a/services/frontend/src/components/CategoryDrilldown.tsx b/services/frontend/src/components/CategoryDrilldown.tsx index 43a65277..3a775e50 100644 --- a/services/frontend/src/components/CategoryDrilldown.tsx +++ b/services/frontend/src/components/CategoryDrilldown.tsx @@ -71,7 +71,7 @@ export function CategoryDrilldown({ {c.name} -
+
Math.max(m, h.total), 0); const intensity = (v: number) => { - if (max <= 0) return "bg-white/5"; + if (max <= 0) return "bg-hairline"; const t = Math.max(0, Math.min(1, v / max)); - if (t < 0.25) return "bg-white/[0.06]"; + if (t < 0.25) return "bg-surface-2"; if (t < 0.5) return "bg-signal/25"; if (t < 0.75) return "bg-signal/50"; return "bg-vermilion/60"; diff --git a/services/frontend/src/components/ScamDomains.tsx b/services/frontend/src/components/ScamDomains.tsx index 6478e4b7..152824a5 100644 --- a/services/frontend/src/components/ScamDomains.tsx +++ b/services/frontend/src/components/ScamDomains.tsx @@ -48,9 +48,9 @@ export function ScamDomains({ domains }: { domains: FlaggedDomain[] }) { {d.domain} -
+
diff --git a/services/frontend/src/components/TopChannels.tsx b/services/frontend/src/components/TopChannels.tsx index a61cf94d..21aa12e7 100644 --- a/services/frontend/src/components/TopChannels.tsx +++ b/services/frontend/src/components/TopChannels.tsx @@ -53,9 +53,9 @@ export function TopChannels({ channels }: { channels: FlaggedChannel[] }) { {c.channel_name ?? c.channel_id} -
+
diff --git a/services/frontend/src/components/TopicTrends.tsx b/services/frontend/src/components/TopicTrends.tsx index 431630df..6f90dc0c 100644 --- a/services/frontend/src/components/TopicTrends.tsx +++ b/services/frontend/src/components/TopicTrends.tsx @@ -29,7 +29,7 @@ function BarRow({ return (
{label} -
+
( ))} diff --git a/services/frontend/src/components/chatbot/chatbot.tsx b/services/frontend/src/components/chatbot/chatbot.tsx index 3b32e373..935f02c8 100644 --- a/services/frontend/src/components/chatbot/chatbot.tsx +++ b/services/frontend/src/components/chatbot/chatbot.tsx @@ -169,7 +169,7 @@ export function Chatbot() { diff --git a/services/frontend/src/components/command/command-palette.tsx b/services/frontend/src/components/command/command-palette.tsx index 1e4ce792..d9d4e7f5 100644 --- a/services/frontend/src/components/command/command-palette.tsx +++ b/services/frontend/src/components/command/command-palette.tsx @@ -139,7 +139,7 @@ export function CommandPalette() { placeholder="Type a command or search…" className="flex-1 bg-transparent text-sm text-ink outline-none placeholder:text-ink-faint" /> - + ESC
@@ -156,11 +156,11 @@ export function CommandPalette() { key={c.id} className={`flex w-full items-center gap-3 rounded-[10px] px-3 py-2.5 text-left text-sm transition-colors ${ i === active - ? "bg-signal/12 text-ink" - : "text-ink-soft hover:bg-white/5" + ? "bg-signal/15 text-ink" + : "text-ink-soft hover:bg-surface" }`} > - + {c.icon} {c.label} diff --git a/services/frontend/src/components/media/mini-player.tsx b/services/frontend/src/components/media/mini-player.tsx index 99ea8015..0f266237 100644 --- a/services/frontend/src/components/media/mini-player.tsx +++ b/services/frontend/src/components/media/mini-player.tsx @@ -63,7 +63,7 @@ export function MiniPlayer() { className="flex min-w-0 flex-1 items-center gap-3" aria-label="Open full media player" > - + {current.thumbnailUrl ? ( // biome-ignore lint/performance/noImgElement: external CDN thumbnails, next/image needs remote allowlist skip.mutate()} disabled={skip.isPending} aria-label="Skip to next track" - className="flex size-8 items-center justify-center rounded-full text-ink-soft transition-colors hover:bg-white/10 hover:text-signal active:scale-95" + className="flex size-8 items-center justify-center rounded-full text-ink-soft transition-colors hover:bg-surface hover:text-signal active:scale-95" > @@ -151,7 +151,7 @@ export function MiniPlayer() { className={`hidden size-8 items-center justify-center rounded-full text-xs transition-colors sm:flex ${ media?.loop ? "bg-signal/15 text-signal" - : "text-ink-faint hover:bg-white/10 hover:text-ink" + : "text-ink-faint hover:bg-surface hover:text-ink" }`} > ↻ diff --git a/services/frontend/src/components/primitives/badge.tsx b/services/frontend/src/components/primitives/badge.tsx index 983719ff..758b9af6 100644 --- a/services/frontend/src/components/primitives/badge.tsx +++ b/services/frontend/src/components/primitives/badge.tsx @@ -39,7 +39,7 @@ export function Badge({ return ( @@ -39,7 +39,7 @@ export function Spinner({ className }: { className?: string }) { return ( diff --git a/services/frontend/src/components/primitives/select.tsx b/services/frontend/src/components/primitives/select.tsx index 965f60bc..0e0e9d1e 100644 --- a/services/frontend/src/components/primitives/select.tsx +++ b/services/frontend/src/components/primitives/select.tsx @@ -46,7 +46,7 @@ export function Select({ type="button" onClick={() => setOpen((o) => !o)} className={cn( - "flex w-full items-center justify-between gap-2 rounded-[11px] border border-hairline bg-white/5 text-left text-ink transition-colors hover:border-signal/40", + "flex w-full items-center justify-between gap-2 rounded-[8px] border border-hairline bg-surface-2 text-left text-ink transition-colors hover:border-signal/40", "focus:outline-none focus:border-signal/60", size === "sm" ? "h-9 px-3 text-xs" : "h-10 px-3.5 text-sm", )} @@ -79,10 +79,10 @@ export function Select({ setOpen(false); }} className={cn( - "flex w-full items-center justify-between gap-2 rounded-[9px] px-3 py-2 text-left text-sm transition-colors", + "flex w-full items-center justify-between gap-2 rounded-[8px] px-3 py-2 text-left text-sm transition-colors", o.value === value ? "bg-signal/15 text-signal" - : "text-ink hover:bg-white/6", + : "text-ink hover:bg-surface", )} > {o.label} diff --git a/services/frontend/src/components/primitives/skeleton.tsx b/services/frontend/src/components/primitives/skeleton.tsx index 9534851d..830da44c 100644 --- a/services/frontend/src/components/primitives/skeleton.tsx +++ b/services/frontend/src/components/primitives/skeleton.tsx @@ -7,7 +7,7 @@ export function Skeleton({ return (
x.id !== t.id); emit(); }} - className="rounded-md p-1 text-ink-faint transition-colors hover:bg-white/8 hover:text-ink" + className="rounded-md p-1 text-ink-faint transition-colors hover:bg-surface hover:text-ink" > diff --git a/services/frontend/src/components/shared/markdown.tsx b/services/frontend/src/components/shared/markdown.tsx index 772181c7..9b0eaa2c 100644 --- a/services/frontend/src/components/shared/markdown.tsx +++ b/services/frontend/src/components/shared/markdown.tsx @@ -33,7 +33,7 @@ function renderInline(text: string, keyBase: string): ReactNode[] { nodes.push( {m[4]} , @@ -56,7 +56,7 @@ function renderBlock(content: string, keyBase: string): ReactNode[] { return (
           
             {code.replace(/\n$/, "")}
diff --git a/services/frontend/src/components/shared/states.tsx b/services/frontend/src/components/shared/states.tsx
index 520004fc..53345a56 100644
--- a/services/frontend/src/components/shared/states.tsx
+++ b/services/frontend/src/components/shared/states.tsx
@@ -62,7 +62,7 @@ export function SkeletonRows({
       {Array.from({ length: rows }).map((_, i) => (
         
@@ -111,7 +111,7 @@ export function EmptyState({ )} > {icon && ( - + {icon} )} diff --git a/services/frontend/src/components/shell/mobile-nav.tsx b/services/frontend/src/components/shell/mobile-nav.tsx index 70a5b66b..24348f9a 100644 --- a/services/frontend/src/components/shell/mobile-nav.tsx +++ b/services/frontend/src/components/shell/mobile-nav.tsx @@ -26,14 +26,14 @@ export function MobileNav() { className={cn( "game-nav-item relative flex min-h-[44px] flex-1 flex-col items-center justify-center rounded-t-[20px] px-2 pt-2 pb-[calc(0.4rem+env(safe-area-inset-bottom))] md:hidden", active - ? "is-active text-white" + ? "is-active text-signal" : "text-ink-faint hover:text-ink-soft", )} > {active && (