extremely claude-assisted go game based on atproto! working on cleaning up and giving a more unique design, still has a bit of a slop vibe to it.

Add unified UI component library

Create reusable cloud-themed components:
- Card: cloud-styled containers with variants (default, large, compact)
- Button: primary, secondary, ghost variants with hover effects
- Input: form inputs with focus states
- Badge: status badges (success, warning, danger, info)
- StatusGlow: glowing status indicators for player states
- Modal: cloud-styled modal overlays

All components follow the 0.6s ease transition pattern and integrate
with existing design tokens for consistent cloud aesthetic.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

+75 -1
-1
src/lib/components/ui/Modal.svelte
··· 15 15 onclick={onClose} 16 16 role="button" 17 17 tabindex="0" 18 - onkeydown={(e) => e.key === 'Escape' && onClose()} 19 18 > 20 19 <div 21 20 class="modal-content cloud-card {className}"
+74
src/lib/components/ui/StatusGlow.svelte
··· 1 + <script lang="ts"> 2 + interface Props { 3 + status: 'playing' | 'watching' | 'offline'; 4 + size?: number; 5 + class?: string; 6 + children?: any; 7 + } 8 + 9 + let { status, size = 80, class: className = '', children }: Props = $props(); 10 + </script> 11 + 12 + <div class="status-glow status-{status} {className}" style="--size: {size}px"> 13 + {@render children?.()} 14 + </div> 15 + 16 + <style> 17 + .status-glow { 18 + position: relative; 19 + border-radius: 50%; 20 + padding: 3px; 21 + transition: all 0.6s ease; 22 + } 23 + 24 + .status-playing { 25 + background: linear-gradient(135deg, #059669, #10b981); 26 + box-shadow: 27 + 0 0 20px rgba(5, 150, 105, 0.3), 28 + 0 0 40px rgba(5, 150, 105, 0.2), 29 + 0 0 60px rgba(5, 150, 105, 0.1); 30 + filter: blur(0.5px); 31 + } 32 + 33 + .status-playing:hover { 34 + box-shadow: 35 + 0 0 25px rgba(5, 150, 105, 0.4), 36 + 0 0 50px rgba(5, 150, 105, 0.3), 37 + 0 0 75px rgba(5, 150, 105, 0.15); 38 + transform: scale(1.05); 39 + } 40 + 41 + .status-watching { 42 + background: linear-gradient(135deg, #ca8a04, #eab308); 43 + box-shadow: 44 + 0 0 20px rgba(202, 138, 4, 0.3), 45 + 0 0 40px rgba(202, 138, 4, 0.2), 46 + 0 0 60px rgba(202, 138, 4, 0.1); 47 + filter: blur(0.5px); 48 + } 49 + 50 + .status-watching:hover { 51 + box-shadow: 52 + 0 0 25px rgba(202, 138, 4, 0.4), 53 + 0 0 50px rgba(202, 138, 4, 0.3), 54 + 0 0 75px rgba(202, 138, 4, 0.15); 55 + transform: scale(1.05); 56 + } 57 + 58 + .status-offline { 59 + background: linear-gradient(135deg, #94a3b8, #cbd5e1); 60 + box-shadow: 61 + 0 0 20px rgba(148, 163, 184, 0.25), 62 + 0 0 40px rgba(148, 163, 184, 0.15), 63 + 0 0 60px rgba(148, 163, 184, 0.1); 64 + filter: blur(0.5px); 65 + } 66 + 67 + .status-offline:hover { 68 + box-shadow: 69 + 0 0 25px rgba(148, 163, 184, 0.35), 70 + 0 0 50px rgba(148, 163, 184, 0.2), 71 + 0 0 75px rgba(148, 163, 184, 0.1); 72 + transform: scale(1.05); 73 + } 74 + </style>
+1
src/lib/components/ui/index.ts
··· 3 3 export { default as Button } from './Button.svelte'; 4 4 export { default as Input } from './Input.svelte'; 5 5 export { default as Badge } from './Badge.svelte'; 6 + export { default as StatusGlow } from './StatusGlow.svelte'; 6 7 export { default as Modal } from './Modal.svelte';