The codebase that powers boop.cat boop.cat
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

build cache and new ui

scanash00 2c247640 5ccb4861

+1185 -545
+32 -6
backend-go/deploy/build.go
··· 64 64 RootDir string 65 65 Env []string 66 66 Logger func(string) 67 + Cache *BuildCache 68 + SiteID string 67 69 } 68 70 69 71 func (b *BuildSystem) DetectPackageManager() string { ··· 161 163 pm := b.DetectPackageManager() 162 164 163 165 if fileExists(filepath.Join(b.RootDir, "package.json")) { 164 - installArgs := b.InstallArgs(pm) 165 - 166 - if b.Logger != nil { 167 - b.Logger(fmt.Sprintf("Installing dependencies with %s %v...\n", pm, installArgs)) 166 + cacheHit := false 167 + lockHash := "" 168 + if b.Cache != nil && b.SiteID != "" { 169 + lockHash = b.Cache.LockfileHash(b.RootDir) 170 + if lockHash != "" { 171 + cacheHit = b.Cache.RestoreNodeModules(b.SiteID, b.RootDir, lockHash, b.Logger) 172 + if cacheHit && b.Logger != nil { 173 + b.Logger("Cache hit: restored node_modules from cache\n") 174 + } 175 + } 168 176 } 169 177 170 - if err := b.RunCommand(ctx, pm, installArgs...); err != nil { 171 - return "", fmt.Errorf("install failed: %w", err) 178 + if !cacheHit { 179 + installArgs := b.InstallArgs(pm) 180 + 181 + if b.Logger != nil { 182 + if lockHash != "" { 183 + b.Logger("Cache miss: installing dependencies fresh\n") 184 + } 185 + b.Logger(fmt.Sprintf("Installing dependencies with %s %v...\n", pm, installArgs)) 186 + } 187 + 188 + if err := b.RunCommand(ctx, pm, installArgs...); err != nil { 189 + return "", fmt.Errorf("install failed: %w", err) 190 + } 191 + 192 + if b.Cache != nil && b.SiteID != "" && lockHash != "" { 193 + if b.Logger != nil { 194 + b.Logger("Saving node_modules to cache...\n") 195 + } 196 + b.Cache.SaveNodeModules(b.SiteID, b.RootDir, lockHash, b.Logger) 197 + } 172 198 } 173 199 } 174 200
+108
backend-go/deploy/cache.go
··· 1 + // Copyright 2025 boop.cat 2 + // Licensed under the Apache License, Version 2.0 3 + // See LICENSE file for details. 4 + 5 + package deploy 6 + 7 + import ( 8 + "crypto/sha256" 9 + "fmt" 10 + "os" 11 + "os/exec" 12 + "path/filepath" 13 + ) 14 + 15 + type BuildCache struct { 16 + CacheDir string 17 + } 18 + 19 + var lockfileNames = []string{ 20 + "bun.lockb", 21 + "bun.lock", 22 + "pnpm-lock.yaml", 23 + "yarn.lock", 24 + "package-lock.json", 25 + } 26 + 27 + func (c *BuildCache) LockfileHash(buildDir string) string { 28 + for _, name := range lockfileNames { 29 + path := filepath.Join(buildDir, name) 30 + data, err := os.ReadFile(path) 31 + if err != nil { 32 + continue 33 + } 34 + h := sha256.Sum256(data) 35 + return fmt.Sprintf("%x", h) 36 + } 37 + return "" 38 + } 39 + 40 + func (c *BuildCache) siteCacheDir(siteID string) string { 41 + return filepath.Join(c.CacheDir, siteID) 42 + } 43 + 44 + func (c *BuildCache) hashMarkerPath(siteID string) string { 45 + return filepath.Join(c.siteCacheDir(siteID), ".lockfile-hash") 46 + } 47 + 48 + func (c *BuildCache) RestoreNodeModules(siteID, buildDir, currentHash string, logger func(string)) bool { 49 + if currentHash == "" { 50 + return false 51 + } 52 + 53 + markerPath := c.hashMarkerPath(siteID) 54 + stored, err := os.ReadFile(markerPath) 55 + if err != nil { 56 + return false 57 + } 58 + 59 + if string(stored) != currentHash { 60 + if logger != nil { 61 + logger("Cache miss: lockfile has changed") 62 + } 63 + return false 64 + } 65 + 66 + cachedModules := filepath.Join(c.siteCacheDir(siteID), "node_modules") 67 + if !fileExists(cachedModules) { 68 + return false 69 + } 70 + 71 + dest := filepath.Join(buildDir, "node_modules") 72 + cmd := exec.Command("cp", "-a", cachedModules, dest) 73 + if err := cmd.Run(); err != nil { 74 + if logger != nil { 75 + logger(fmt.Sprintf("Cache restore failed: %v", err)) 76 + } 77 + return false 78 + } 79 + 80 + return true 81 + } 82 + 83 + func (c *BuildCache) SaveNodeModules(siteID, buildDir, lockfileHash string, logger func(string)) { 84 + if lockfileHash == "" { 85 + return 86 + } 87 + 88 + srcModules := filepath.Join(buildDir, "node_modules") 89 + if !fileExists(srcModules) { 90 + return 91 + } 92 + 93 + siteCache := c.siteCacheDir(siteID) 94 + os.MkdirAll(siteCache, 0755) 95 + 96 + cachedModules := filepath.Join(siteCache, "node_modules") 97 + os.RemoveAll(cachedModules) 98 + 99 + cmd := exec.Command("cp", "-a", srcModules, cachedModules) 100 + if err := cmd.Run(); err != nil { 101 + if logger != nil { 102 + logger(fmt.Sprintf("Cache save failed: %v", err)) 103 + } 104 + return 105 + } 106 + 107 + os.WriteFile(c.hashMarkerPath(siteID), []byte(lockfileHash), 0644) 108 + }
+8
backend-go/deploy/engine.go
··· 33 33 CFToken string 34 34 CFAccountID string 35 35 CFNamespaceID string 36 + Cache *BuildCache 36 37 deploymentsMux sync.Mutex 37 38 deployments map[string]context.CancelFunc 38 39 } ··· 42 43 workDir := filepath.Join(os.TempDir(), "fsd-builds") 43 44 os.MkdirAll(workDir, 0755) 44 45 46 + cacheDir := filepath.Join(workDir, "cache") 47 + os.MkdirAll(cacheDir, 0755) 48 + 45 49 return &Engine{ 46 50 DB: database, 47 51 WorkDir: workDir, ··· 51 55 CFToken: cfToken, 52 56 CFAccountID: cfAccount, 53 57 CFNamespaceID: cfNamespace, 58 + Cache: &BuildCache{CacheDir: cacheDir}, 54 59 deployments: make(map[string]context.CancelFunc), 55 60 } 56 61 } 62 + 57 63 58 64 func (e *Engine) DeploySite(siteID, userID string, logStream chan<- string) (*db.Deployment, error) { 59 65 ··· 299 305 RootDir: buildDir, 300 306 Env: envVars, 301 307 Logger: logger, 308 + Cache: e.Cache, 309 + SiteID: siteID, 302 310 } 303 311 304 312 customBuildCmd := ""
+10 -8
client/src/pages/Account.jsx
··· 319 319 <div className="grid2"> 320 320 <div className="panel"> 321 321 <div className="panelTitle"> 322 - <Mail size={18} style={{ marginRight: 8, color: '#e88978' }} /> 322 + <Mail size={18} style={{ marginRight: 8, color: 'var(--accent)' }} /> 323 323 Email Address 324 324 </div> 325 325 <div className="muted" style={{ marginBottom: 16, fontSize: 13 }}> ··· 366 366 367 367 <div className="panel"> 368 368 <div className="panelTitle"> 369 - <Lock size={18} style={{ marginRight: 8, color: '#e88978' }} /> 369 + <Lock size={18} style={{ marginRight: 8, color: 'var(--accent)' }} /> 370 370 Password 371 371 </div> 372 372 <div className="muted" style={{ marginBottom: 16, fontSize: 13 }}> ··· 417 417 {} 418 418 <div className="panel" style={{ marginTop: 8 }}> 419 419 <div className="panelTitle"> 420 - <Link2 size={18} style={{ marginRight: 8, color: '#e88978' }} /> 420 + <Link2 size={18} style={{ marginRight: 8, color: 'var(--accent)' }} /> 421 421 Linked Accounts 422 422 </div> 423 423 <div className="muted" style={{ marginBottom: 16, fontSize: 13 }}> ··· 447 447 alignItems: 'center', 448 448 justifyContent: 'space-between', 449 449 padding: '12px 16px', 450 - background: 'var(--bg-secondary)', 451 - borderRadius: 8, 452 - marginBottom: 8 450 + background: 'var(--card-bg-solid)', 451 + border: '2px solid var(--card-text)', 452 + borderRadius: 12, 453 + marginBottom: 8, 454 + boxShadow: '3px 3px 0 var(--card-text)', 453 455 }} 454 456 > 455 457 <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}> ··· 540 542 {} 541 543 <div className="panel" style={{ marginTop: 8 }}> 542 544 <div className="panelTitle"> 543 - <Key size={18} style={{ marginRight: 8, color: '#e88978' }} /> 545 + <Key size={18} style={{ marginRight: 8, color: 'var(--accent)' }} /> 544 546 API Keys 545 547 </div> 546 548 <div className="muted" style={{ marginBottom: 16, fontSize: 13 }}> ··· 590 592 <button 591 593 className="iconBtn" 592 594 onClick={() => copyToClipboard(newlyCreatedKey.key, newlyCreatedKey.id)} 593 - style={{ width: 28, height: 28, borderRadius: '50%', flexShrink: 0 }} 595 + style={{ width: 28, height: 28, flexShrink: 0 }} 594 596 > 595 597 {copiedKeyId === newlyCreatedKey.id ? <Check size={14} /> : <Copy size={14} />} 596 598 </button>
+76 -26
client/src/pages/DashboardHome.jsx
··· 6 6 import { Link, useOutletContext } from 'react-router-dom'; 7 7 import MillyLogo from '../components/MillyLogo.jsx'; 8 8 9 + const PROJECT_COLORS = [ 10 + '#e88978', '#6ba3e8', '#72d2cf', '#9b87f5', '#f5a524', 11 + '#e87298', '#4ecdc4', '#a78bfa', '#fb923c', '#34d399', 12 + ]; 13 + 14 + function getProjectColor(name) { 15 + let hash = 0; 16 + for (let i = 0; i < (name || '').length; i++) { 17 + hash = name.charCodeAt(i) + ((hash << 5) - hash); 18 + } 19 + return PROJECT_COLORS[Math.abs(hash) % PROJECT_COLORS.length]; 20 + } 21 + 9 22 export default function DashboardHome() { 10 23 const { me, sites } = useOutletContext(); 11 24 const siteLimitReached = sites.length >= 10; 12 25 13 26 return ( 14 27 <div className="page"> 15 - <div className="pageHeader"> 28 + <div className="pageHeader" style={{ padding: '20px 0 40px', display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between' }}> 16 29 <div> 17 - <div className="h">Your Websites</div> 18 - <div className="muted">Manage deployments and environment variables.</div> 30 + <h1 style={{ fontSize: '3rem', fontWeight: 800, margin: '0 0 12px', letterSpacing: '-0.03em', lineHeight: 1 }}> 31 + Your <span style={{ color: 'var(--accent)' }}>Websites</span> 32 + </h1> 33 + <div className="muted" style={{ fontSize: '1.1rem' }}>Manage deployments and environment variables.</div> 19 34 </div> 20 35 <div className="topActions"> 21 36 {siteLimitReached ? ( 22 - <button className="btn primary" disabled> 37 + <button className="btn primary" disabled style={{ padding: '16px 28px', fontSize: '16px' }}> 23 38 + New website 24 39 </button> 25 40 ) : ( 26 - <Link to="/dashboard/new" className="btn primary"> 41 + <Link to="/dashboard/new" className="btn primary" style={{ padding: '16px 28px', fontSize: '16px' }}> 27 42 + New website 28 43 </Link> 29 44 )} ··· 42 57 </div> 43 58 )} 44 59 45 - <div className="gridCards"> 46 - {sites.map((s) => ( 47 - <Link key={s.id} className="projectCard" to={`/dashboard/site/${s.id}`}> 48 - <div className="projectTitle">{s.name}</div> 49 - <div className="muted" style={{ marginTop: 8, fontSize: 13 }}> 50 - {s.domain ? s.domain : s.git?.url?.replace('https://github.com/', '')} 51 - </div> 52 - <div className="projectMeta"> 53 - <span className="chip">{s.git?.branch || 'main'}</span> 54 - {s.git?.subdir && <span className="chip">{s.git.subdir}</span>} 55 - </div> 56 - </Link> 57 - ))} 60 + <div className="bentoGrid"> 61 + {sites.map((s, index) => { 62 + const color = getProjectColor(s.name); 63 + const letter = (s.name || '?')[0].toUpperCase(); 64 + const isHero = index === 0 && sites.length > 0; 65 + 66 + return ( 67 + <Link key={s.id} className={`projectCard ${isHero ? 'bentoHero' : 'bentoNormal'}`} to={`/dashboard/site/${s.id}`}> 68 + <div style={{ display: 'flex', alignItems: 'flex-start', gap: isHero ? 24 : 16 }}> 69 + <div 70 + style={{ 71 + width: isHero ? 76 : 52, 72 + height: isHero ? 76 : 52, 73 + borderRadius: isHero ? 18 : 12, 74 + background: color, 75 + color: '#fff', 76 + display: 'flex', 77 + alignItems: 'center', 78 + justifyContent: 'center', 79 + fontWeight: 900, 80 + fontSize: isHero ? 34 : 24, 81 + flexShrink: 0, 82 + border: '3px solid var(--card-text)', 83 + boxShadow: '3px 3px 0 var(--card-text)', 84 + letterSpacing: '-0.04em', 85 + fontFamily: 'inherit', 86 + }} 87 + > 88 + {letter} 89 + </div> 90 + <div style={{ flex: 1, minWidth: 0 }}> 91 + <div className="projectTitle" style={{ fontSize: isHero ? 24 : 18, marginBottom: isHero ? 8 : 4 }}> 92 + {s.name} 93 + </div> 94 + <div className="muted" style={{ fontSize: isHero ? 15 : 13, wordBreak: 'break-all' }}> 95 + {s.domain ? s.domain : s.git?.url?.replace('https://github.com/', '')} 96 + </div> 97 + </div> 98 + </div> 99 + <div className="projectMeta" style={{ marginTop: 'auto', paddingTop: isHero ? 24 : 16 }}> 100 + <span className="chip" style={{ fontSize: isHero ? 13 : 11 }}>{s.git?.branch || 'main'}</span> 101 + {s.git?.subdir && <span className="chip" style={{ fontSize: isHero ? 13 : 11 }}>{s.git.subdir}</span>} 102 + </div> 103 + </Link> 104 + ); 105 + })} 58 106 59 107 {sites.length === 0 && ( 60 - <div className="panel" style={{ gridColumn: '1 / -1' }}> 61 - <div style={{ textAlign: 'center', padding: '32px 24px' }}> 62 - <MillyLogo size={64} style={{ marginBottom: 20 }} /> 63 - <h3 style={{ fontSize: '1.25rem', fontWeight: 600, marginBottom: 8 }}>Create your first website</h3> 108 + <div className="panel" style={{ gridColumn: '1 / -1', border: '3px dashed var(--card-text)', background: 'var(--card-bg-solid)', boxShadow: '6px 6px 0 var(--card-text)', opacity: 0.85 }}> 109 + <div style={{ textAlign: 'center', padding: '64px 24px' }}> 110 + <div style={{ marginBottom: 32, animation: 'float 4s ease-in-out infinite', cursor: 'default' }}> 111 + <MillyLogo size={96} /> 112 + </div> 113 + <h3 style={{ fontSize: '2rem', fontWeight: 800, marginBottom: 12, letterSpacing: '-0.02em' }}>No websites yet</h3> 64 114 <div 65 115 className="muted" 66 - style={{ marginBottom: 24, maxWidth: 320, marginLeft: 'auto', marginRight: 'auto' }} 116 + style={{ marginBottom: 32, maxWidth: 420, marginLeft: 'auto', marginRight: 'auto', lineHeight: 1.6, fontSize: '1.1rem' }} 67 117 > 68 - Deploy static sites from any Git repository in seconds. 118 + Deploy blazing fast static sites from any Git repository. Connect your GitHub or paste a URL to get started in seconds. 69 119 </div> 70 - <Link to="/dashboard/new" className="btn primary"> 71 - + New website 120 + <Link to="/dashboard/new" className="btn primary" style={{ padding: '16px 32px', fontSize: '16px' }}> 121 + Deploy your first site 72 122 </Link> 73 123 </div> 74 124 </div>
+61 -7
client/src/pages/DashboardLayout.jsx
··· 9 9 Settings, 10 10 LogOut, 11 11 Zap, 12 - Globe, 13 12 AlertTriangle, 14 13 User, 15 14 ChevronDown, ··· 96 95 ); 97 96 } 98 97 98 + const PROJECT_COLORS = [ 99 + '#e88978', '#6ba3e8', '#72d2cf', '#9b87f5', '#f5a524', 100 + '#e87298', '#4ecdc4', '#a78bfa', '#fb923c', '#34d399', 101 + ]; 102 + 103 + function getProjectColor(name) { 104 + let hash = 0; 105 + for (let i = 0; i < (name || '').length; i++) { 106 + hash = name.charCodeAt(i) + ((hash << 5) - hash); 107 + } 108 + return PROJECT_COLORS[Math.abs(hash) % PROJECT_COLORS.length]; 109 + } 110 + 111 + function ProjectAvatar({ name, size = 28 }) { 112 + const color = getProjectColor(name); 113 + const letter = (name || '?')[0].toUpperCase(); 114 + return ( 115 + <div 116 + className="projectAvatar" 117 + style={{ 118 + width: size, 119 + height: size, 120 + borderRadius: size * 0.3, 121 + background: color, 122 + color: '#fff', 123 + display: 'flex', 124 + alignItems: 'center', 125 + justifyContent: 'center', 126 + fontWeight: 900, 127 + fontSize: size * 0.46, 128 + flexShrink: 0, 129 + border: '2px solid var(--card-text)', 130 + letterSpacing: '-0.03em', 131 + boxShadow: '2px 2px 0 var(--card-text)', 132 + fontFamily: 'inherit', 133 + }} 134 + > 135 + {letter} 136 + </div> 137 + ); 138 + } 139 + 99 140 function Sidebar({ sites, selectedId, collapsed, onToggle, user, onLogout, mobileOpen, onMobileClose }) { 100 141 const avatar = getAvatar(user, 64); 101 142 const displayName = user?.username || user?.email || 'User'; ··· 124 165 </div> 125 166 126 167 <nav className="sidebarNav"> 127 - <Link className="sidebarLink" to="/dashboard" title="All websites"> 168 + <Link 169 + className={`sidebarLink ${location.pathname === '/dashboard' || location.pathname === '/dashboard/' ? 'active' : ''}`} 170 + to="/dashboard" 171 + title="All websites" 172 + > 128 173 <LayoutDashboard size={20} /> 129 174 {!isCollapsed && <span>All websites</span>} 130 175 </Link> 131 - <Link className="sidebarLink" to="/dashboard/account" title="Settings"> 176 + <Link 177 + className={`sidebarLink ${location.pathname.startsWith('/dashboard/account') ? 'active' : ''}`} 178 + to="/dashboard/account" 179 + title="Settings" 180 + > 132 181 <Settings size={20} /> 133 182 {!isCollapsed && <span>Settings</span>} 134 183 </Link> 135 - <Link className="sidebarLink" to="/dashboard/api-docs" title="API Docs"> 184 + <Link 185 + className={`sidebarLink ${location.pathname.startsWith('/dashboard/api-docs') ? 'active' : ''}`} 186 + to="/dashboard/api-docs" 187 + title="API Docs" 188 + > 136 189 <Book size={20} /> 137 190 {!isCollapsed && <span>API Docs</span>} 138 191 </Link> ··· 148 201 className={`sidebarProject ${s.id === selectedId ? 'active' : ''}`} 149 202 to={`/dashboard/site/${s.id}`} 150 203 > 151 - <Globe size={16} /> 204 + <ProjectAvatar name={s.name} size={28} /> 152 205 <div className="sidebarProjectInfo"> 153 206 <span className="sidebarProjectName">{s.name}</span> 154 207 <span className="sidebarProjectUrl"> ··· 169 222 {sites.map((s) => ( 170 223 <Link 171 224 key={s.id} 172 - className={`sidebarLink ${s.id === selectedId ? 'active' : ''}`} 225 + className={`sidebarProject ${s.id === selectedId ? 'active' : ''}`} 173 226 to={`/dashboard/site/${s.id}`} 174 227 title={s.name} 228 + style={{ justifyContent: 'center', padding: '8px', position: 'relative' }} 175 229 > 176 - <Globe size={20} /> 230 + <ProjectAvatar name={s.name} size={32} /> 177 231 </Link> 178 232 ))} 179 233 </div>
+31 -10
client/src/pages/DashboardSite.jsx
··· 25 25 EyeOff, 26 26 Trash2, 27 27 Plus, 28 - Loader2 28 + Loader2, 29 + Play, 30 + Square, 31 + Github, 32 + Globe 29 33 } from 'lucide-react'; 30 34 31 35 function Toast({ message, onClose }) { ··· 743 747 744 748 return ( 745 749 <div className="page"> 746 - <div className="pageHeader"> 750 + <div className="pageHeader" style={{ padding: '20px 0 40px', display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between' }}> 747 751 <div> 748 - <div className="crumbs"> 752 + <div className="crumbs" style={{ marginBottom: 12, fontSize: 14 }}> 749 753 <span className="muted">Dashboard</span> 750 - <span className="muted">/</span> 754 + <span className="muted"> / </span> 751 755 <span className="crumb">{site.name}</span> 752 756 </div> 753 - <div className="h">{site.name}</div> 754 - <div className="muted">{site.domain ? site.domain : site.git?.url}</div> 757 + <h1 style={{ fontSize: '3rem', fontWeight: 900, margin: '0 0 10px', letterSpacing: '-0.035em', lineHeight: 1 }}> 758 + {site.name} 759 + </h1> 760 + <div className="muted" style={{ fontSize: '1rem', wordBreak: 'break-all', display: 'flex', alignItems: 'center', gap: 6 }}> 761 + {site.domain ? ( 762 + <><Globe size={14} /> {site.domain}</> 763 + ) : ( 764 + <><Github size={14} /> {site.git?.url?.replace('https://github.com/', '')}</> 765 + )} 766 + </div> 755 767 </div> 756 768 757 769 <div className="topActions"> 758 770 <button className="btn primary" disabled={deploying || (me && me.emailVerified === false)} onClick={deploy}> 759 - {deploying ? 'Deploying...' : 'Deploy'} 771 + {deploying ? ( 772 + <><Loader2 size={16} className="animate-spin" /> Deploying…</> 773 + ) : ( 774 + <><Play size={16} /> Deploy</> 775 + )} 760 776 </button> 761 777 762 778 <button ··· 765 781 onClick={() => activeDeployment && stopDeployment(activeDeployment.id)} 766 782 style={{ marginLeft: 10 }} 767 783 > 768 - Stop 784 + <Square size={16} /> Stop 769 785 </button> 770 786 </div> 771 787 </div> ··· 811 827 Deployments 812 828 </div> 813 829 <div className="muted" style={{ marginBottom: 10 }}> 814 - Deploy + logs + stop 830 + View deployment history and build logs 815 831 </div> 816 832 <div className="deployList"> 817 833 {deployments.map((d) => ( ··· 908 924 </div> 909 925 </div> 910 926 ))} 911 - {deployments.length === 0 ? <div className="muted">No deployments yet.</div> : null} 927 + {deployments.length === 0 ? ( 928 + <div style={{ textAlign: 'center', padding: '32px 16px' }}> 929 + <div style={{ fontSize: 40, marginBottom: 12, opacity: 0.6 }}>🚀</div> 930 + <div className="muted" style={{ fontSize: 14 }}>No deployments yet. Hit <strong>Deploy</strong> above to get started.</div> 931 + </div> 932 + ) : null} 912 933 </div> 913 934 </> 914 935 ) : null}
+28 -2
client/src/pages/NewSite.jsx
··· 529 529 </div> 530 530 531 531 {repos.length === 0 && ( 532 - <div className="repoEmptyState" style={{ textAlign: 'center', padding: '32px' }}> 533 - No repositories found 532 + <div className="repoEmptyState" style={{ textAlign: 'center', padding: '40px 24px' }}> 533 + <Search size={32} style={{ marginBottom: 12, opacity: 0.3 }} /> 534 + <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 4 }}>No repositories found</div> 535 + <div className="muted" style={{ fontSize: 13 }}> 536 + {searchQuery ? 'Try a different search term' : 'No accessible repositories'} 537 + </div> 538 + </div> 539 + )} 540 + 541 + {(hasNextPage || reposPage > 1) && ( 542 + <div style={{ display: 'flex', justifyContent: 'center', gap: 8, marginTop: 16, paddingTop: 16, borderTop: '1px solid var(--card-border)' }}> 543 + <button 544 + className="btn ghost" 545 + disabled={reposPage <= 1} 546 + onClick={() => fetchRepos(reposPage - 1, searchQuery, { background: false })} 547 + > 548 + ← Previous 549 + </button> 550 + <span className="muted" style={{ display: 'flex', alignItems: 'center', fontSize: 13 }}> 551 + Page {reposPage} 552 + </span> 553 + <button 554 + className="btn ghost" 555 + disabled={!hasNextPage} 556 + onClick={() => fetchRepos(reposPage + 1, searchQuery, { background: false })} 557 + > 558 + Next → 559 + </button> 534 560 </div> 535 561 )} 536 562 </>
+831 -486
client/src/styles.css
··· 1 + @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap'); 2 + 1 3 .compatCard { 2 4 margin-top: 12px; 3 5 padding: 16px; ··· 155 157 } 156 158 157 159 :root { 158 - --bg-gradient-1: #fff5f2; 159 - --bg-gradient-2: #ffeee8; 160 - --bg-gradient-3: #ffe4dc; 161 - --card-text: #2d1f1c; 162 - --card-text-light: #7a5a52; 163 - --card-bg: rgba(255, 255, 255, 0.75); 164 - --card-bg-solid: rgba(255, 255, 255, 0.85); 165 - --card-border: rgba(255, 255, 255, 0.8); 166 - --glass-bg: color-mix(in srgb, #f8ded6 60%, white 40%); 167 - --glass-border: rgba(232, 137, 120, 0.15); 168 - --input-bg: rgba(255, 255, 255, 0.8); 169 - --input-border: rgba(232, 137, 120, 0.15); 170 - --sidebar-bg: color-mix(in srgb, #f8ded6 60%, white 40%); 171 - --sidebar-border: rgba(232, 137, 120, 0.15); 172 - --sidebar-hover: rgba(255, 255, 255, 0.6); 173 - --sidebar-active-bg: linear-gradient(135deg, #f4a295 0%, #e88978 100%); 174 - --dropdown-bg: #fff; 175 - --dropdown-border: #f0e2de; 176 - --dropdown-hover: #fdf5f3; 177 - --accent: #e88978; 178 - --accent-gradient: linear-gradient(135deg, #f4a295 0%, #e88978 100%); 179 - --radius-card: 24px; 160 + --bg-gradient-1: #faf7f5; 161 + --bg-gradient-2: #f2ece9; 162 + --bg-gradient-3: #eee5df; 163 + --card-text: #1a1714; 164 + --card-text-light: #6e655f; 165 + --card-bg: rgba(255, 255, 255, 0.95); 166 + --card-bg-solid: #ffffff; 167 + --card-border: rgba(0, 0, 0, 0.04); 168 + --glass-bg: rgba(255, 255, 255, 0.6); 169 + --glass-border: rgba(0, 0, 0, 0.03); 170 + --input-bg: rgba(255, 255, 255, 0.9); 171 + --input-border: rgba(0, 0, 0, 0.08); 172 + --sidebar-bg: transparent; 173 + --sidebar-border: rgba(0, 0, 0, 0.05); 174 + --sidebar-hover: rgba(0, 0, 0, 0.03); 175 + --sidebar-active-bg: rgba(0, 0, 0, 0.06); 176 + --dropdown-bg: #ffffff; 177 + --dropdown-border: rgba(0, 0, 0, 0.06); 178 + --dropdown-hover: #f5f3f1; 179 + --accent: #f472b6; 180 + --accent-gradient: linear-gradient(135deg, #f472b6 0%, #fb7185 100%); 181 + --radius-card: 22px; 180 182 --radius-btn: 14px; 181 - --blue: #1d9bf0; 182 - --orange: #ff7a00; 183 - --fg: #2d1f1c; 184 - --muted: rgba(45, 31, 28, 0.55); 185 - --border: rgba(45, 31, 28, 0.08); 186 - --shadow: 0 4px 24px rgba(45, 20, 15, 0.06); 187 - --shadow-hover: 0 12px 40px rgba(45, 20, 15, 0.1); 183 + --blue: #3b82f6; 184 + --orange: #f97316; 185 + --fg: #1a1714; 186 + --muted: rgba(26, 23, 20, 0.55); 187 + --border: rgba(0, 0, 0, 0.15); 188 + --shadow: 4px 4px 0 var(--border); 189 + --shadow-hover: 8px 8px 0 var(--accent); 190 + --shadow-brutal: 4px 4px 0 var(--card-text); 191 + --shadow-brutal-hover: 8px 8px 0 var(--accent); 188 192 --divider: rgba(0, 0, 0, 0.06); 189 - --code-bg: #fdfaf9; 190 - --modal-bg: color-mix(in srgb, #fce8e2 70%, white 30%); 191 - --modal-border: rgba(255, 255, 255, 0.6); 192 - --modal-inset: rgba(255, 255, 255, 0.6); 193 - --modal-header-bg: rgba(255, 255, 255, 0.5); 194 - --modal-header-border: rgba(232, 137, 120, 0.12); 195 - --icon-btn-bg: rgba(255, 255, 255, 0.6); 196 - --notice-bg: #fef3c7; 197 - --notice-border: #fcd34d; 198 - --notice-text: #92400e; 199 - --error-bg: rgba(254, 226, 226, 0.6); 200 - --error-border: rgba(252, 165, 165, 0.5); 193 + --code-bg: #f8f6f4; 194 + --modal-bg: rgba(255, 255, 255, 0.98); 195 + --modal-border: rgba(0, 0, 0, 0.06); 196 + --modal-inset: rgba(0, 0, 0, 0.03); 197 + --modal-header-bg: rgba(255, 255, 255, 0.8); 198 + --modal-header-border: rgba(0, 0, 0, 0.05); 199 + --icon-btn-bg: rgba(0, 0, 0, 0.04); 200 + --notice-bg: #fef08a; 201 + --notice-border: #facc15; 202 + --notice-text: #854d0e; 203 + --error-bg: #fee2e2; 204 + --error-border: #fca5a5; 201 205 --error-text: #b91c1c; 202 206 } 203 207 204 - [data-theme='dark'] .deployRow { 205 - background: color-mix(in srgb, var(--card-bg) 80%, rgba(255, 255, 255, 0.04) 20%); 206 - border-color: rgba(255, 255, 255, 0.08); 207 - } 208 - 209 208 .deployRowMain { 210 209 display: flex; 211 210 flex-direction: column; ··· 222 221 223 222 .statusPill { 224 223 padding: 4px 10px; 225 - border-radius: 999px; 224 + border-radius: 8px; 226 225 font-size: 12px; 227 - font-weight: 600; 226 + font-weight: 700; 228 227 letter-spacing: 0.02em; 229 228 background: rgba(232, 137, 120, 0.15); 230 229 color: #c45a47; ··· 233 232 .status-building { 234 233 background: rgba(245, 158, 11, 0.12); 235 234 color: #b45309; 235 + animation: pulse-glow 2s ease-in-out infinite; 236 236 } 237 237 238 238 .status-running { 239 239 background: rgba(34, 197, 94, 0.14); 240 240 color: #15803d; 241 + animation: pulse-glow 2s ease-in-out infinite; 241 242 } 242 243 243 244 .status-failed { ··· 289 290 display: inline-flex; 290 291 align-items: center; 291 292 gap: 4px; 292 - padding: 6px 10px; 293 - background: rgba(0, 0, 0, 0.03); 294 - border-radius: 10px; 293 + padding: 5px 10px; 294 + background: var(--code-bg); 295 + border: 1px solid var(--card-border); 296 + border-radius: 8px; 295 297 font-size: 13px; 298 + transition: border-color 0.15s ease; 299 + } 300 + 301 + .linkChip:hover { 302 + border-color: var(--accent); 303 + text-decoration: none; 296 304 } 297 305 298 306 [data-theme='dark'] .linkChip { 299 - background: rgba(255, 255, 255, 0.05); 307 + background: var(--code-bg); 300 308 } 301 309 302 310 .deployCommit { ··· 318 326 319 327 .commitBadge { 320 328 font-family: 'SFMono-Regular', Menlo, Consolas, 'Liberation Mono', monospace; 321 - background: rgba(0, 0, 0, 0.05); 322 - padding: 4px 8px; 323 - border-radius: 8px; 329 + background: var(--code-bg); 330 + padding: 3px 8px; 331 + border-radius: 6px; 324 332 letter-spacing: 0.02em; 325 333 color: var(--card-text); 326 334 text-decoration: none; 335 + border: 1px solid var(--card-border); 336 + font-size: 12px; 337 + font-weight: 600; 338 + transition: border-color 0.15s ease; 339 + } 340 + 341 + a.commitBadge:hover { 342 + border-color: var(--accent); 343 + text-decoration: none; 327 344 } 328 345 329 346 [data-theme='dark'] .commitBadge { 330 - background: rgba(255, 255, 255, 0.08); 347 + background: var(--code-bg); 331 348 color: #e5edff; 332 349 } 333 350 ··· 372 389 } 373 390 374 391 [data-theme='dark'] { 375 - --bg-gradient-1: #1a0f0d; 376 - --bg-gradient-2: #1f1412; 377 - --bg-gradient-3: #251916; 378 - --card-text: #f5ebe8; 379 - --card-text-light: #d6b4a9; 380 - --card-bg: rgba(32, 18, 14, 0.82); 381 - --card-bg-solid: rgba(32, 18, 14, 0.94); 382 - --card-border: rgba(255, 255, 255, 0.14); 383 - --glass-bg: color-mix(in srgb, #26140d 70%, rgba(32, 18, 14, 0.9) 30%); 384 - --glass-border: rgba(255, 255, 255, 0.16); 385 - --input-bg: rgba(32, 18, 14, 0.78); 386 - --input-border: rgba(255, 255, 255, 0.14); 387 - --sidebar-bg: rgba(26, 14, 10, 0.95); 388 - --sidebar-border: rgba(255, 255, 255, 0.12); 389 - --sidebar-hover: rgba(255, 255, 255, 0.08); 390 - --sidebar-active-bg: linear-gradient(135deg, #f4a295 0%, #e88978 100%); 391 - --dropdown-bg: #1f1210; 392 - --dropdown-border: rgba(255, 255, 255, 0.14); 393 - --dropdown-hover: rgba(255, 255, 255, 0.08); 394 - --accent: #f4a295; 395 - --accent-gradient: linear-gradient(135deg, #f4a295 0%, #e88978 100%); 396 - --shadow: 0 6px 28px rgba(0, 0, 0, 0.35); 397 - --shadow-hover: 0 14px 46px rgba(0, 0, 0, 0.45); 398 - --divider: rgba(255, 255, 255, 0.12); 399 - --code-bg: rgba(32, 18, 14, 0.65); 400 - --modal-bg: rgba(30, 17, 14, 0.96); 401 - --modal-border: rgba(255, 255, 255, 0.14); 402 - --modal-inset: rgba(255, 255, 255, 0.08); 403 - --modal-header-bg: rgba(36, 20, 16, 0.7); 404 - --modal-header-border: rgba(255, 255, 255, 0.12); 405 - --icon-btn-bg: rgba(255, 255, 255, 0.14); 406 - --notice-bg: rgba(251, 191, 36, 0.25); 407 - --notice-border: rgba(251, 191, 36, 0.45); 408 - --notice-text: #f7e3a3; 409 - --error-bg: rgba(239, 68, 68, 0.28); 410 - --error-border: rgba(239, 68, 68, 0.45); 411 - --error-text: #fca5a5; 392 + --bg-gradient-1: #0f0d12; 393 + --bg-gradient-2: #0f0d12; 394 + --bg-gradient-3: #0f0d12; 395 + --card-text: #f0ecff; 396 + --card-text-light: #9e96b8; 397 + --card-bg: #1c1824; 398 + --card-bg-solid: #1c1824; 399 + --card-border: rgba(255, 255, 255, 0.10); 400 + --glass-bg: rgba(28, 24, 36, 0.8); 401 + --glass-border: rgba(255, 255, 255, 0.06); 402 + --input-bg: rgba(0, 0, 0, 0.25); 403 + --input-border: rgba(255, 255, 255, 0.10); 404 + --sidebar-bg: #1c1824; 405 + --sidebar-border: rgba(255, 255, 255, 0.10); 406 + --sidebar-hover: rgba(255, 255, 255, 0.04); 407 + --sidebar-active-bg: rgba(244, 114, 182, 0.12); 408 + --dropdown-bg: #241f30; 409 + --dropdown-border: rgba(255, 255, 255, 0.08); 410 + --dropdown-hover: rgba(255, 255, 255, 0.05); 411 + --accent: #f472b6; 412 + --border: rgba(240, 236, 255, 0.18); 413 + --muted: rgba(240, 236, 255, 0.45); 414 + --shadow: 4px 4px 0 var(--border); 415 + --shadow-hover: 8px 8px 0 var(--accent); 416 + --shadow-brutal: 4px 4px 0 var(--card-text); 417 + --shadow-brutal-hover: 8px 8px 0 var(--accent); 418 + --divider: rgba(255, 255, 255, 0.06); 419 + --code-bg: rgba(0, 0, 0, 0.35); 420 + --modal-bg: #1c1824; 421 + --modal-border: rgba(255, 255, 255, 0.10); 422 + --modal-inset: rgba(0, 0, 0, 0.2); 423 + --modal-header-bg: rgba(28, 24, 36, 0.9); 424 + --modal-header-border: rgba(255, 255, 255, 0.06); 425 + --icon-btn-bg: rgba(255, 255, 255, 0.06); 426 + --notice-bg: rgba(251, 191, 36, 0.15); 427 + --notice-border: rgba(251, 191, 36, 0.3); 428 + --notice-text: #fde68a; 429 + --error-bg: rgba(239, 68, 68, 0.15); 430 + --error-border: rgba(239, 68, 68, 0.3); 431 + --error-text: #fecaca; 412 432 } 433 + 434 + 413 435 414 436 .repoGrid { 415 437 display: flex; ··· 455 477 transition: all 0.2s cubic-bezier(0.2, 0.8, 0.2, 1); 456 478 position: relative; 457 479 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02); 480 + animation: repoItemIn 0.3s ease-out backwards; 481 + } 482 + 483 + .repoItem:nth-child(1) { animation-delay: 0.02s; } 484 + .repoItem:nth-child(2) { animation-delay: 0.04s; } 485 + .repoItem:nth-child(3) { animation-delay: 0.06s; } 486 + .repoItem:nth-child(4) { animation-delay: 0.08s; } 487 + .repoItem:nth-child(5) { animation-delay: 0.10s; } 488 + .repoItem:nth-child(6) { animation-delay: 0.12s; } 489 + .repoItem:nth-child(7) { animation-delay: 0.14s; } 490 + .repoItem:nth-child(8) { animation-delay: 0.16s; } 491 + .repoItem:nth-child(9) { animation-delay: 0.18s; } 492 + .repoItem:nth-child(10) { animation-delay: 0.20s; } 493 + 494 + @keyframes repoItemIn { 495 + from { 496 + opacity: 0; 497 + transform: translateX(-6px); 498 + } 499 + 500 + to { 501 + opacity: 1; 502 + transform: translateX(0); 503 + } 458 504 } 459 505 460 506 .repoItem:hover { ··· 629 675 box-sizing: border-box; 630 676 } 631 677 678 + ::-webkit-scrollbar { 679 + width: 8px; 680 + height: 8px; 681 + } 682 + 683 + ::-webkit-scrollbar-track { 684 + background: transparent; 685 + } 686 + 687 + ::-webkit-scrollbar-thumb { 688 + background: var(--border); 689 + border-radius: 99px; 690 + } 691 + 692 + ::-webkit-scrollbar-thumb:hover { 693 + background: var(--muted); 694 + } 695 + 632 696 html { 633 697 scroll-behavior: smooth; 634 - background: linear-gradient(160deg, var(--bg-gradient-1) 0%, var(--bg-gradient-2) 50%, var(--bg-gradient-3) 100%); 635 - background-color: var(--bg-gradient-3); 698 + background-color: var(--bg-gradient-1); 636 699 } 637 700 638 701 html, ··· 643 706 644 707 body { 645 708 font-family: 646 - 'Kumbh Sans', 647 - 'Inter', 648 - ui-sans-serif, 709 + 'Plus Jakarta Sans', 649 710 system-ui, 650 711 -apple-system, 651 712 sans-serif; 652 - background: linear-gradient(160deg, var(--bg-gradient-1) 0%, var(--bg-gradient-2) 50%, var(--bg-gradient-3) 100%); 713 + background-color: var(--bg-gradient-1); 714 + background-image: radial-gradient(var(--border) 1.5px, transparent 1.5px); 715 + background-size: 28px 28px; 653 716 color: var(--card-text); 654 717 line-height: 1.6; 655 718 transition: 656 - background 0.3s ease, 719 + background-color 0.3s ease, 657 720 color 0.3s ease; 658 721 } 659 722 ··· 666 729 text-decoration: none; 667 730 } 668 731 732 + ::selection { 733 + background: rgba(244, 114, 182, 0.25); 734 + color: var(--card-text); 735 + } 736 + 737 + :focus-visible { 738 + outline: 3px solid var(--accent); 739 + outline-offset: 2px; 740 + border-radius: 4px; 741 + } 742 + 743 + .btn:focus-visible, 744 + .tab:focus-visible, 745 + .subTab:focus-visible, 746 + .glass-btn:focus-visible { 747 + outline-offset: 3px; 748 + } 749 + 750 + .input:focus-visible, 751 + .textarea:focus-visible { 752 + outline: none; 753 + } 754 + 669 755 .themeToggle { 670 756 display: flex; 671 757 align-items: center; 672 758 justify-content: center; 673 - width: 40px; 674 - height: 40px; 675 - border-radius: 12px; 676 - border: none; 677 - background: var(--card-bg); 759 + width: 44px; 760 + height: 44px; 761 + border-radius: var(--radius-btn); 762 + border: 3px solid var(--card-text); 763 + background: var(--card-bg-solid); 678 764 backdrop-filter: blur(12px); 679 765 -webkit-backdrop-filter: blur(12px); 680 766 cursor: pointer; 681 767 color: var(--card-text); 682 - transition: all 0.2s ease; 768 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.2s; 683 769 position: relative; 684 770 overflow: hidden; 771 + box-shadow: 4px 4px 0 var(--card-text); 772 + transform: translateY(0); 685 773 } 686 774 687 775 .themeToggle:hover { 688 - background: var(--sidebar-hover); 689 - transform: scale(1.05); 776 + background: var(--card-bg-solid); 777 + box-shadow: 8px 8px 0 var(--accent); 778 + transform: translate(-4px, -4px); 779 + } 780 + 781 + .themeToggle:active { 782 + transform: translate(4px, 4px); 783 + box-shadow: 0 0 0 var(--card-text); 690 784 } 691 785 692 786 .themeToggle svg { ··· 746 840 display: flex; 747 841 justify-content: space-between; 748 842 align-items: center; 749 - background: var(--card-bg); 750 - backdrop-filter: blur(20px); 751 - -webkit-backdrop-filter: blur(20px); 752 - padding: 12px 20px; 753 - border-radius: 20px; 754 - box-shadow: var(--shadow); 755 - border: 1px solid var(--card-border); 756 - transition: 757 - background 0.3s ease, 758 - border 0.3s ease; 843 + background: var(--card-bg-solid); 844 + padding: 12px 16px; 845 + border-radius: 18px; 846 + border: 3px solid var(--card-text); 847 + box-shadow: 6px 6px 0 var(--card-text); 848 + transition: background 0.3s ease, box-shadow 0.3s ease; 849 + } 850 + 851 + [data-theme='dark'] .navbar-content { 852 + box-shadow: 6px 6px 0 var(--accent); 759 853 } 760 854 761 855 .navbar-logo { 762 856 display: flex; 763 857 align-items: center; 764 858 gap: 10px; 765 - font-weight: 700; 859 + font-weight: 800; 766 860 font-size: 1.1rem; 767 861 color: var(--card-text); 862 + letter-spacing: -0.02em; 768 863 } 769 864 770 865 .navbar-logo svg { ··· 781 876 display: inline-flex; 782 877 align-items: center; 783 878 gap: 8px; 784 - padding: 10px 16px; 785 - background: var(--card-bg); 786 - border: 1px solid var(--card-border); 879 + padding: 10px 18px; 880 + background: var(--card-bg-solid); 881 + border: 3px solid var(--card-text); 787 882 border-radius: var(--radius-btn); 788 883 color: var(--card-text); 789 - font-weight: 600; 884 + font-weight: 800; 790 885 font-size: 0.9rem; 791 886 text-decoration: none; 792 887 cursor: pointer; 793 - transition: all 0.2s ease; 888 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); 889 + box-shadow: 4px 4px 0 var(--card-text); 794 890 } 795 891 796 892 .glass-btn:hover { 797 - background: var(--sidebar-hover); 798 - transform: translateY(-1px); 893 + transform: translate(-4px, -4px); 894 + box-shadow: 8px 8px 0 var(--accent); 895 + text-decoration: none; 799 896 } 800 897 801 898 .tangled-icon { ··· 853 950 } 854 951 855 952 .glass-btn.accent { 856 - background: var(--accent-gradient); 857 - color: white; 953 + background: var(--accent); 954 + color: #fff; 955 + border: 3px solid var(--card-text); 956 + box-shadow: 4px 4px 0 var(--card-text); 858 957 } 859 958 860 959 .glass-btn.accent:hover { 861 - background: linear-gradient(135deg, #f4a295 0%, #e88978 100%); 862 - box-shadow: 0 4px 16px rgba(232, 137, 120, 0.3); 960 + transform: translate(-4px, -4px); 961 + box-shadow: 8px 8px 0 var(--card-text); 962 + background: var(--accent); 963 + } 964 + 965 + [data-theme='dark'] .glass-btn { 966 + box-shadow: 4px 4px 0 var(--accent); 967 + } 968 + 969 + [data-theme='dark'] .glass-btn:hover { 970 + box-shadow: 8px 8px 0 var(--accent); 971 + } 972 + 973 + [data-theme='dark'] .glass-btn.accent { 974 + color: #000; 975 + box-shadow: 4px 4px 0 var(--card-text); 976 + } 977 + 978 + [data-theme='dark'] .glass-btn.accent:hover { 979 + box-shadow: 8px 8px 0 var(--card-text); 863 980 } 864 981 865 982 .glass-btn.large { 866 - padding: 14px 24px; 867 - font-size: 1rem; 983 + padding: 14px 28px; 984 + font-size: 1.05rem; 868 985 border-radius: 16px; 869 986 } 870 987 ··· 888 1005 889 1006 .hero-section { 890 1007 text-align: center; 891 - padding: 60px 0 80px; 892 - animation: fadeInUp 0.7s ease-out; 1008 + padding: 80px 0 100px; 1009 + animation: fadeInUp 0.6s ease-out; 1010 + } 1011 + 1012 + .hero-section h1 { 1013 + font-size: 4rem; 1014 + font-weight: 900; 1015 + margin: 0 0 20px; 1016 + color: var(--card-text); 1017 + letter-spacing: -0.04em; 1018 + line-height: 1.0; 893 1019 } 894 1020 895 1021 .hero-icon { ··· 909 1035 @keyframes float { 910 1036 0%, 911 1037 100% { 912 - transform: translateY(0); 1038 + transform: translateY(0) rotate(0deg); 1039 + } 1040 + 1041 + 25% { 1042 + transform: translateY(-8px) rotate(-2deg); 913 1043 } 914 1044 915 1045 50% { 916 - transform: translateY(-10px); 1046 + transform: translateY(-12px) rotate(0deg); 1047 + } 1048 + 1049 + 75% { 1050 + transform: translateY(-8px) rotate(2deg); 917 1051 } 918 1052 } 919 1053 1054 + @keyframes wiggle { 1055 + 0%, 100% { transform: rotate(0deg); } 1056 + 25% { transform: rotate(-3deg); } 1057 + 75% { transform: rotate(3deg); } 1058 + } 1059 + 1060 + @keyframes pulse-glow { 1061 + 0%, 100% { box-shadow: 0 0 0 0 rgba(244, 114, 182, 0); } 1062 + 50% { box-shadow: 0 0 0 8px rgba(244, 114, 182, 0.15); } 1063 + } 1064 + 920 1065 @keyframes fadeInUp { 921 1066 from { 922 1067 opacity: 0; ··· 930 1075 } 931 1076 932 1077 .hero-section h1 { 933 - font-size: 3rem; 934 - font-weight: 800; 935 - margin: 0 0 16px; 1078 + font-size: 4rem; 1079 + font-weight: 900; 1080 + margin: 0 0 20px; 936 1081 color: var(--card-text); 1082 + letter-spacing: -0.04em; 1083 + line-height: 1.0; 937 1084 } 938 1085 939 1086 .hero-desc { ··· 954 1101 .block-card, 955 1102 .step-card, 956 1103 .cta-card { 957 - --card-color: #d0e4f8; 958 - background: color-mix(in srgb, var(--card-color) 60%, transparent 40%); 959 - backdrop-filter: blur(12px); 960 - -webkit-backdrop-filter: blur(12px); 1104 + --card-color: #fdba74; 1105 + background: color-mix(in srgb, var(--card-color) 18%, var(--card-bg-solid) 82%); 961 1106 border-radius: var(--radius-card); 962 - border: 1px solid color-mix(in srgb, var(--card-color) 40%, white 60%); 1107 + border: 3px solid var(--card-text); 963 1108 padding: 28px; 964 1109 position: relative; 965 - box-shadow: var(--shadow); 966 - transition: 967 - transform 0.3s ease, 968 - box-shadow 0.3s ease, 969 - background 0.3s ease; 1110 + box-shadow: 6px 6px 0 var(--card-text); 1111 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); 970 1112 } 971 1113 972 1114 [data-theme='dark'] .block-card, 973 1115 [data-theme='dark'] .step-card, 974 1116 [data-theme='dark'] .cta-card { 975 - background: color-mix(in srgb, var(--card-color) 65%, rgba(12, 18, 32, 0.6) 35%); 976 - border: 1px solid color-mix(in srgb, var(--card-color) 60%, rgba(255, 255, 255, 0.1) 40%); 1117 + background: color-mix(in srgb, var(--card-color) 30%, var(--card-bg-solid) 70%); 1118 + border-color: var(--card-text); 1119 + box-shadow: 6px 6px 0 var(--accent); 977 1120 } 978 1121 979 1122 .block-card:hover, 980 1123 .step-card:hover { 981 - transform: translateY(-6px); 982 - box-shadow: var(--shadow-hover); 1124 + transform: translate(-4px, -4px); 1125 + box-shadow: 10px 10px 0 var(--accent); 983 1126 } 984 1127 985 1128 .features-row { ··· 1032 1175 --card-color: #ffb86b !important; 1033 1176 } 1034 1177 1035 - html[data-theme='dark'] .landing-page .framework-chip:nth-child(1) { 1036 - background: #61dafb33 !important; 1037 - } 1038 - 1039 - html[data-theme='dark'] .landing-page .framework-chip:nth-child(2) { 1040 - background: #42b88333 !important; 1178 + html[data-theme='dark'] .landing-page .framework-chip { 1179 + background: var(--card-bg-solid) !important; 1180 + border-color: var(--card-text); 1181 + box-shadow: 4px 4px 0 var(--accent); 1182 + color: var(--card-text); 1041 1183 } 1042 1184 1043 - html[data-theme='dark'] .landing-page .framework-chip:nth-child(3) { 1044 - background: #ff3e0033 !important; 1045 - } 1046 - 1047 - html[data-theme='dark'] .landing-page .framework-chip:nth-child(4) { 1048 - background: #ff5d0133 !important; 1049 - } 1050 - 1051 - html[data-theme='dark'] .landing-page .framework-chip:nth-child(5) { 1052 - background: #00000022 !important; 1053 - } 1054 - 1055 - html[data-theme='dark'] .landing-page .framework-chip:nth-child(6) { 1056 - background: #646cff33 !important; 1185 + html[data-theme='dark'] .landing-page .framework-chip:hover { 1186 + box-shadow: 7px 7px 0 var(--accent); 1057 1187 } 1058 1188 1059 1189 html[data-theme='dark'] .landing-page .block-card, 1060 1190 html[data-theme='dark'] .landing-page .step-card, 1061 1191 html[data-theme='dark'] .landing-page .cta-card { 1062 - background: color-mix(in srgb, var(--card-color) 55%, rgba(8, 12, 24, 0.75) 45%); 1063 - border: 1px solid color-mix(in srgb, var(--card-color) 50%, rgba(255, 255, 255, 0.08) 50%); 1192 + background: color-mix(in srgb, var(--card-color) 40%, var(--card-bg-solid) 60%); 1193 + border: 3px solid var(--card-text); 1194 + box-shadow: 6px 6px 0 color-mix(in srgb, var(--card-color) 60%, var(--accent) 40%); 1195 + } 1196 + 1197 + html[data-theme='dark'] .landing-page .block-card:hover, 1198 + html[data-theme='dark'] .landing-page .step-card:hover { 1199 + box-shadow: 10px 10px 0 var(--accent); 1064 1200 } 1065 1201 1066 1202 .block-card .card-icon-wrap { ··· 1069 1205 justify-content: center; 1070 1206 width: 48px; 1071 1207 height: 48px; 1072 - background: color-mix(in srgb, var(--card-color) 70%, white 30%); 1073 - border-radius: 14px; 1074 - margin-bottom: 16px; 1208 + background: color-mix(in srgb, var(--card-color) 50%, var(--card-bg-solid) 50%); 1209 + border-radius: 12px; 1210 + border: 2px solid var(--card-text); 1211 + box-shadow: 3px 3px 0 var(--card-text); 1212 + margin-bottom: 20px; 1075 1213 color: var(--card-text); 1076 1214 } 1077 1215 1078 1216 [data-theme='dark'] .block-card .card-icon-wrap { 1079 - background: color-mix(in srgb, var(--card-color) 75%, rgba(15, 22, 40, 0.7) 25%); 1217 + background: color-mix(in srgb, var(--card-color) 60%, var(--card-bg-solid) 40%); 1218 + border-color: var(--card-text); 1219 + box-shadow: 3px 3px 0 var(--card-text); 1080 1220 } 1081 1221 1082 1222 .block-card h3 { ··· 1120 1260 display: inline-flex; 1121 1261 align-items: center; 1122 1262 justify-content: center; 1123 - width: 40px; 1124 - height: 40px; 1125 - background: color-mix(in srgb, var(--card-color) 70%, white 30%); 1126 - border-radius: 12px; 1127 - font-weight: 800; 1128 - font-size: 1.1rem; 1129 - margin-bottom: 12px; 1130 - color: var(--card-text); 1263 + width: 44px; 1264 + height: 44px; 1265 + background: var(--accent); 1266 + color: white; 1267 + border-radius: 10px; 1268 + border: 2px solid var(--card-text); 1269 + box-shadow: 3px 3px 0 var(--card-text); 1270 + font-weight: 900; 1271 + font-size: 1.2rem; 1272 + margin-bottom: 16px; 1131 1273 } 1132 1274 1133 1275 [data-theme='dark'] .step-num { 1134 - background: color-mix(in srgb, var(--card-color) 75%, rgba(15, 22, 40, 0.7) 25%); 1276 + color: #000; 1135 1277 } 1136 1278 1137 1279 .step-card h3 { ··· 1175 1317 } 1176 1318 1177 1319 .framework-chip { 1178 - padding: 10px 20px; 1179 - border-radius: 12px; 1180 - font-weight: 600; 1320 + padding: 8px 18px; 1321 + border-radius: 10px; 1322 + font-weight: 800; 1181 1323 font-size: 0.95rem; 1182 1324 color: var(--card-text); 1183 - transition: transform 0.2s ease; 1325 + border: 3px solid var(--card-text); 1326 + box-shadow: 4px 4px 0 var(--card-text); 1327 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s; 1328 + background: var(--card-bg-solid); 1184 1329 } 1185 1330 1186 1331 .framework-chip:hover { 1187 - transform: scale(1.05); 1332 + transform: translate(-3px, -3px); 1333 + box-shadow: 7px 7px 0 var(--accent); 1188 1334 } 1189 1335 1190 1336 .cta-section { ··· 1319 1465 1320 1466 .auth-card { 1321 1467 width: min(440px, 100%); 1322 - background: var(--glass-bg); 1468 + background: var(--card-bg-solid); 1323 1469 backdrop-filter: blur(16px); 1324 1470 -webkit-backdrop-filter: blur(16px); 1325 - border: 1px solid var(--card-border); 1326 - border-radius: 28px; 1471 + border: 3px solid var(--card-text); 1472 + border-radius: var(--radius-card); 1327 1473 padding: 40px; 1328 - box-shadow: var(--shadow); 1474 + box-shadow: 8px 8px 0 var(--card-text); 1329 1475 animation: fadeInUp 0.5s ease-out; 1330 1476 transition: 1331 1477 background 0.3s ease, 1332 - border 0.3s ease; 1478 + border 0.3s ease, 1479 + box-shadow 0.3s ease; 1480 + } 1481 + 1482 + [data-theme='dark'] .auth-card { 1483 + box-shadow: 8px 8px 0 var(--accent); 1333 1484 } 1334 1485 1335 1486 .auth-card h1 { ··· 1411 1562 align-items: center; 1412 1563 justify-content: center; 1413 1564 gap: 8px; 1414 - border-radius: 14px; 1415 - border: none; 1416 - padding: 12px 20px; 1417 - font-weight: 600; 1565 + border-radius: var(--radius-btn); 1566 + border: 3px solid var(--card-text); 1567 + padding: 10px 20px; 1568 + font-weight: 800; 1418 1569 text-decoration: none; 1419 1570 cursor: pointer; 1420 - background: var(--card-bg); 1571 + background: var(--card-bg-solid); 1421 1572 color: var(--card-text); 1422 1573 min-width: 140px; 1423 - font-size: 14px; 1424 - transition: all 0.2s ease; 1574 + font-size: 15px; 1575 + letter-spacing: -0.01em; 1576 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.2s; 1577 + box-shadow: 4px 4px 0 var(--card-text); 1578 + transform: translateY(0); 1579 + } 1580 + 1581 + .btn:hover:not(:disabled) { 1582 + transform: translate(-4px, -4px); 1583 + background: var(--card-bg-solid); 1584 + box-shadow: 8px 8px 0 var(--accent); 1425 1585 } 1426 1586 1427 - .btn:hover { 1428 - transform: translateY(-2px); 1429 - background: var(--sidebar-hover); 1430 - box-shadow: var(--shadow); 1587 + .btn:active:not(:disabled) { 1588 + transform: translate(4px, 4px); 1589 + box-shadow: 0 0 0 var(--card-text); 1431 1590 } 1432 1591 1433 1592 .btn:disabled { ··· 1437 1596 } 1438 1597 1439 1598 .btn.primary { 1440 - background: var(--accent-gradient); 1441 - color: white; 1599 + background: var(--accent); 1600 + color: #fff; 1601 + border: 3px solid var(--card-text); 1602 + box-shadow: 4px 4px 0 var(--card-text); 1603 + } 1604 + 1605 + .btn.primary:hover:not(:disabled) { 1606 + transform: translate(-4px, -4px); 1607 + box-shadow: 8px 8px 0 var(--card-text); 1608 + background: var(--accent); 1609 + } 1610 + 1611 + .btn.primary:active:not(:disabled) { 1612 + transform: translate(4px, 4px); 1613 + box-shadow: 0 0 0 var(--card-text); 1442 1614 } 1443 1615 1444 - .btn.primary:hover { 1445 - box-shadow: 0 4px 20px rgba(232, 137, 120, 0.4); 1616 + [data-theme='dark'] .btn.primary { 1617 + color: #000; 1446 1618 } 1447 1619 1448 1620 .btn.accent { 1449 - background: linear-gradient(135deg, #e8a36b 0%, #d89058 100%); 1621 + background: #f97316; 1450 1622 color: white; 1623 + border: 3px solid var(--card-text); 1624 + box-shadow: 4px 4px 0 var(--card-text); 1451 1625 } 1452 1626 1453 - .btn.accent:hover { 1454 - background: linear-gradient(135deg, #f0b07a 0%, #e09860 100%); 1455 - box-shadow: 0 4px 20px rgba(212, 143, 90, 0.3); 1627 + .btn.accent:hover:not(:disabled) { 1628 + transform: translate(-4px, -4px); 1629 + box-shadow: 8px 8px 0 var(--accent); 1630 + background: #f97316; 1631 + } 1632 + 1633 + .btn.accent:active:not(:disabled) { 1634 + transform: translate(4px, 4px); 1635 + box-shadow: 0 0 0 var(--card-text); 1456 1636 } 1457 1637 1458 1638 .btn.ghost { 1459 - background: var(--card-bg); 1460 - border: 1px solid var(--glass-border); 1639 + background: transparent; 1640 + border: 3px solid transparent; 1641 + box-shadow: none; 1461 1642 } 1462 1643 1463 - .btn.ghost:hover { 1464 - background: var(--sidebar-hover); 1644 + .btn.ghost:hover:not(:disabled) { 1645 + background: var(--icon-btn-bg); 1646 + box-shadow: none; 1647 + transform: translate(-2px, -2px); 1648 + border: 3px solid var(--card-text); 1649 + box-shadow: 4px 4px 0 var(--accent); 1650 + } 1651 + 1652 + .btn.ghost:active:not(:disabled) { 1653 + background: var(--sidebar-active-bg); 1654 + transform: translate(2px, 2px); 1655 + box-shadow: 0 0 0 var(--card-text); 1465 1656 } 1466 1657 1467 1658 .btn.danger { 1468 - background: rgba(220, 38, 38, 0.1); 1469 - color: #b91c1c; 1659 + background: #ef4444; 1660 + color: white; 1661 + border: 3px solid var(--card-text); 1662 + box-shadow: 4px 4px 0 var(--card-text); 1663 + } 1664 + 1665 + .btn.danger:hover:not(:disabled) { 1666 + background: #f87171; 1667 + transform: translate(-4px, -4px); 1668 + box-shadow: 8px 8px 0 var(--card-text); 1470 1669 } 1471 1670 1472 - .btn.danger:hover { 1473 - background: rgba(220, 38, 38, 0.18); 1671 + .btn.danger:active:not(:disabled) { 1672 + transform: translate(4px, 4px); 1673 + box-shadow: 0 0 0 var(--card-text); 1474 1674 } 1475 1675 1476 1676 .form { ··· 1481 1681 } 1482 1682 1483 1683 .input { 1484 - border-radius: 14px; 1684 + border-radius: 12px; 1485 1685 border: 1px solid var(--input-border); 1486 1686 padding: 14px 16px; 1487 1687 font-size: 15px; ··· 1494 1694 outline: none; 1495 1695 border-color: var(--accent); 1496 1696 background: var(--card-bg-solid); 1497 - box-shadow: 0 0 0 4px rgba(232, 137, 120, 0.15); 1697 + box-shadow: 0 0 0 3px rgba(244, 114, 182, 0.15); 1498 1698 } 1499 1699 1500 1700 .input:disabled { ··· 1527 1727 justify-content: center; 1528 1728 gap: 8px; 1529 1729 padding: 16px 12px; 1530 - background: var(--card-bg); 1531 - border: 1px solid var(--glass-border); 1730 + background: var(--card-bg-solid); 1731 + border: 2px solid var(--card-text); 1532 1732 border-radius: 14px; 1533 1733 color: var(--card-text); 1534 - font-weight: 600; 1734 + font-weight: 700; 1535 1735 font-size: 13px; 1536 1736 text-decoration: none; 1537 1737 cursor: pointer; 1538 - transition: all 0.2s ease; 1738 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); 1539 1739 min-width: 0; 1740 + box-shadow: 3px 3px 0 var(--card-text); 1540 1741 } 1541 1742 1542 1743 .oauth-btn:hover { 1543 - background: var(--sidebar-hover); 1544 - border-color: var(--accent); 1545 - transform: translateY(-2px); 1546 - box-shadow: var(--shadow); 1744 + transform: translate(-3px, -3px); 1745 + box-shadow: 6px 6px 0 var(--accent); 1746 + text-decoration: none; 1747 + } 1748 + 1749 + .oauth-btn:active { 1750 + transform: translate(2px, 2px); 1751 + box-shadow: 1px 1px 0 var(--card-text); 1547 1752 } 1548 1753 1549 1754 .oauth-btn svg { ··· 1629 1834 } 1630 1835 1631 1836 .legal-card { 1632 - background: var(--glass-bg); 1837 + background: var(--card-bg-solid); 1633 1838 backdrop-filter: blur(12px); 1634 1839 -webkit-backdrop-filter: blur(12px); 1635 - border: 1px solid var(--card-border); 1636 - border-radius: 24px; 1840 + border: 3px solid var(--card-text); 1841 + border-radius: var(--radius-card); 1637 1842 padding: 32px; 1638 - box-shadow: var(--shadow); 1843 + box-shadow: 6px 6px 0 var(--card-text); 1639 1844 color: var(--card-text); 1640 1845 max-width: 860px; 1641 1846 margin: 0 auto; 1642 1847 transition: 1643 1848 background 0.3s ease, 1644 1849 border 0.3s ease; 1850 + } 1851 + 1852 + [data-theme='dark'] .legal-card { 1853 + box-shadow: 6px 6px 0 var(--accent); 1645 1854 } 1646 1855 1647 1856 .legal-card a { ··· 1843 2052 1844 2053 .sidebar { 1845 2054 position: fixed; 1846 - left: 0; 1847 - top: 0; 1848 - bottom: 0; 2055 + left: 24px; 2056 + top: 24px; 2057 + bottom: 24px; 1849 2058 width: 260px; 1850 - background: var(--sidebar-bg); 1851 - backdrop-filter: blur(16px); 1852 - -webkit-backdrop-filter: blur(16px); 1853 - border-right: 1px solid var(--sidebar-border); 2059 + border-radius: 20px; 2060 + background: var(--card-bg-solid); 2061 + border: 3px solid var(--card-text); 2062 + box-shadow: 8px 8px 0 var(--card-text); 1854 2063 display: flex; 1855 2064 flex-direction: column; 1856 - transition: 1857 - width 0.25s ease, 1858 - background 0.3s ease, 1859 - border 0.3s ease; 2065 + transition: width 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), transform 0.3s; 1860 2066 z-index: 40; 1861 2067 overflow: hidden; 2068 + } 2069 + 2070 + [data-theme='dark'] .sidebar { 2071 + box-shadow: 8px 8px 0 var(--accent); 1862 2072 } 1863 2073 1864 2074 .sidebar.collapsed { ··· 1869 2079 display: flex; 1870 2080 align-items: center; 1871 2081 justify-content: flex-start; 1872 - padding: 24px 20px; 1873 - border-bottom: 1px solid var(--sidebar-border); 1874 - min-height: 72px; 2082 + padding: 32px 24px 24px; 1875 2083 } 1876 2084 1877 2085 .sidebar.collapsed .sidebarHeader { ··· 1905 2113 } 1906 2114 1907 2115 .sidebarFooter { 1908 - padding: 12px 14px; 1909 - border-top: 1px solid var(--sidebar-border); 2116 + padding: 16px 20px 24px; 1910 2117 } 1911 2118 1912 2119 .sidebarToggle { ··· 1916 2123 justify-content: center; 1917 2124 gap: 10px; 1918 2125 padding: 10px 14px; 1919 - border-radius: 12px; 1920 - border: none; 1921 - background: var(--sidebar-hover); 2126 + border-radius: 10px; 2127 + border: 2px solid var(--card-text); 2128 + background: var(--card-bg-solid); 1922 2129 cursor: pointer; 1923 - color: var(--card-text-light); 1924 - font-weight: 600; 2130 + color: var(--card-text); 2131 + font-weight: 700; 1925 2132 font-size: 13px; 1926 - transition: all 0.2s ease; 2133 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s; 2134 + box-shadow: 3px 3px 0 var(--card-text); 1927 2135 } 1928 2136 1929 2137 .sidebarToggle:hover { 1930 - background: var(--card-bg); 2138 + transform: translate(-2px, -2px); 2139 + box-shadow: 5px 5px 0 var(--accent); 1931 2140 color: var(--card-text); 1932 2141 } 1933 2142 ··· 1936 2145 } 1937 2146 1938 2147 .sidebar.collapsed .sidebarToggle { 1939 - padding: 12px; 2148 + padding: 10px; 2149 + width: 44px; 2150 + height: 44px; 2151 + box-shadow: none; 2152 + border-width: 2px; 2153 + } 2154 + 2155 + .sidebar.collapsed .sidebarToggle:hover { 2156 + box-shadow: none; 2157 + background: color-mix(in srgb, var(--accent) 15%, var(--card-bg-solid) 85%); 2158 + transform: none; 2159 + } 2160 + 2161 + .sidebar.collapsed .sidebarFooter { 2162 + padding: 12px 14px 20px; 2163 + display: flex; 2164 + justify-content: center; 1940 2165 } 1941 2166 1942 2167 .sidebarNav { ··· 1950 2175 display: flex; 1951 2176 align-items: center; 1952 2177 gap: 12px; 1953 - padding: 12px 14px; 1954 - border-radius: 12px; 2178 + padding: 10px 14px; 2179 + border-radius: 10px; 1955 2180 color: var(--card-text-light); 1956 2181 text-decoration: none; 1957 - font-weight: 600; 2182 + font-weight: 700; 1958 2183 font-size: 14px; 1959 - transition: all 0.15s ease; 2184 + transition: all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); 1960 2185 white-space: nowrap; 2186 + position: relative; 2187 + border: 2px solid transparent; 1961 2188 } 1962 2189 1963 2190 .sidebarLink:hover { 1964 - background: var(--sidebar-hover); 2191 + background: color-mix(in srgb, var(--accent) 8%, var(--card-bg-solid) 92%); 1965 2192 color: var(--card-text); 2193 + border-color: var(--card-text); 2194 + box-shadow: 3px 3px 0 var(--card-text); 2195 + transform: translate(-2px, -2px); 1966 2196 text-decoration: none; 1967 2197 } 1968 2198 1969 2199 .sidebarLink.active { 1970 - background: var(--sidebar-active-bg); 1971 - color: #fff; 1972 - box-shadow: 0 4px 12px rgba(232, 137, 120, 0.25); 2200 + background: color-mix(in srgb, var(--accent) 15%, var(--card-bg-solid) 85%); 2201 + color: var(--card-text); 2202 + border: 2px solid var(--card-text); 2203 + box-shadow: 3px 3px 0 var(--accent); 1973 2204 } 1974 2205 1975 2206 .sidebarLink svg { ··· 1984 2215 } 1985 2216 1986 2217 .sidebarLink.active svg { 1987 - color: #fff; 2218 + color: var(--accent); 1988 2219 opacity: 1; 1989 2220 } 1990 2221 ··· 1998 2229 } 1999 2230 2000 2231 .sidebarSection { 2001 - padding: 12px 12px; 2002 - border-top: 1px solid var(--sidebar-border); 2232 + padding: 8px 16px; 2003 2233 margin-top: 8px; 2004 2234 flex: 1; 2005 2235 min-height: 0; ··· 2048 2278 border-radius: 10px; 2049 2279 color: var(--card-text-light); 2050 2280 text-decoration: none; 2051 - transition: all 0.15s ease; 2281 + transition: all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); 2282 + position: relative; 2283 + border: 2px solid transparent; 2052 2284 } 2053 2285 2054 2286 .sidebarProject:hover { 2055 - background: var(--sidebar-hover); 2287 + background: color-mix(in srgb, var(--accent) 8%, var(--card-bg-solid) 92%); 2056 2288 color: var(--card-text); 2289 + border-color: var(--card-text); 2290 + box-shadow: 3px 3px 0 var(--card-text); 2291 + transform: translate(-2px, -2px); 2057 2292 text-decoration: none; 2058 2293 } 2059 2294 2060 2295 .sidebarProject.active { 2061 - background: var(--card-bg); 2062 - color: var(--accent); 2296 + background: color-mix(in srgb, var(--accent) 15%, var(--card-bg-solid) 85%); 2297 + color: var(--card-text); 2298 + border: 2px solid var(--card-text); 2299 + box-shadow: 3px 3px 0 var(--accent); 2063 2300 } 2064 2301 2065 2302 .sidebarProject svg { ··· 2098 2335 2099 2336 .dashMain { 2100 2337 flex: 1; 2101 - margin-left: 260px; 2102 - padding: 24px 32px 32px; 2338 + margin-left: 310px; 2339 + padding: 40px 60px 80px; 2103 2340 min-height: 100vh; 2104 - transition: margin-left 0.25s ease; 2341 + max-width: 1400px; 2342 + transition: margin-left 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); 2343 + } 2344 + 2345 + @media (max-width: 1100px) { 2346 + .dashMain { 2347 + padding: 32px 40px; 2348 + } 2349 + } 2350 + 2351 + @media (max-width: 768px) { 2352 + .sidebar { 2353 + left: 0; 2354 + top: 0; 2355 + bottom: 0; 2356 + border-radius: 0; 2357 + box-shadow: 12px 0 48px rgba(0, 0, 0, 0.1); 2358 + } 2359 + .dashMain { 2360 + margin-left: 0; 2361 + padding: 80px 24px 32px; 2362 + } 2105 2363 } 2106 2364 2107 2365 .dashHeader { ··· 2112 2370 } 2113 2371 2114 2372 .dashWrapper.sidebarCollapsed .dashMain { 2115 - margin-left: 72px; 2373 + margin-left: 120px; 2116 2374 } 2117 2375 2118 2376 .sidebar.collapsed .sidebarProject { ··· 2136 2394 display: none; 2137 2395 align-items: center; 2138 2396 justify-content: center; 2139 - width: 40px; 2140 - height: 40px; 2141 - border-radius: 12px; 2142 - border: none; 2143 - background: var(--card-bg); 2397 + width: 44px; 2398 + height: 44px; 2399 + border-radius: var(--radius-btn); 2400 + border: 3px solid var(--card-text); 2401 + background: var(--card-bg-solid); 2144 2402 backdrop-filter: blur(12px); 2145 2403 -webkit-backdrop-filter: blur(12px); 2146 2404 cursor: pointer; 2147 2405 color: var(--card-text); 2148 - transition: all 0.2s ease; 2406 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.2s; 2407 + box-shadow: 4px 4px 0 var(--card-text); 2408 + transform: translateY(0); 2149 2409 } 2150 2410 2151 2411 .mobileMenuBtn:hover { 2152 - background: var(--sidebar-hover); 2412 + background: var(--card-bg-solid); 2413 + box-shadow: 8px 8px 0 var(--accent); 2414 + transform: translate(-4px, -4px); 2415 + } 2416 + 2417 + .mobileMenuBtn:active { 2418 + transform: translate(4px, 4px); 2419 + box-shadow: 0 0 0 var(--card-text); 2153 2420 } 2154 2421 2155 2422 .sidebarOverlay { ··· 2339 2606 top: calc(100% + 8px); 2340 2607 right: 0; 2341 2608 min-width: 180px; 2342 - background: var(--dropdown-bg); 2343 - border: 1px solid var(--dropdown-border); 2344 - border-radius: 12px; 2345 - box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12); 2609 + background: var(--card-bg-solid); 2610 + border: 2px solid var(--card-text); 2611 + border-radius: 14px; 2612 + box-shadow: 4px 4px 0 var(--card-text); 2346 2613 overflow: hidden; 2347 2614 z-index: 100; 2348 - animation: dropdownFadeIn 0.15s ease; 2615 + animation: dropdownFadeIn 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); 2349 2616 padding: 6px; 2617 + } 2618 + 2619 + [data-theme='dark'] .dropdownMenu { 2620 + box-shadow: 4px 4px 0 var(--accent); 2350 2621 } 2351 2622 2352 2623 @keyframes dropdownFadeIn { ··· 2393 2664 display: flex; 2394 2665 flex-direction: column; 2395 2666 gap: 24px; 2667 + animation: pageFadeIn 0.4s ease-out; 2668 + } 2669 + 2670 + @keyframes pageFadeIn { 2671 + from { 2672 + opacity: 0; 2673 + transform: translateY(12px); 2674 + } 2675 + 2676 + to { 2677 + opacity: 1; 2678 + transform: translateY(0); 2679 + } 2396 2680 } 2397 2681 2398 2682 .pageHeader { ··· 2411 2695 2412 2696 .h { 2413 2697 font-size: 1.75rem; 2414 - font-weight: 700; 2415 - letter-spacing: -0.02em; 2416 - line-height: 1.2; 2698 + font-weight: 800; 2699 + letter-spacing: -0.035em; 2700 + line-height: 1.15; 2417 2701 color: var(--card-text); 2418 2702 } 2419 2703 ··· 2438 2722 } 2439 2723 2440 2724 .panel { 2441 - background: var(--card-bg); 2442 - backdrop-filter: blur(12px); 2443 - -webkit-backdrop-filter: blur(12px); 2444 - border: 1px solid var(--card-border); 2445 - border-radius: 20px; 2446 - padding: 24px; 2447 - box-shadow: var(--shadow); 2448 - transition: 2449 - background 0.3s ease, 2450 - border 0.3s ease; 2725 + background: var(--card-bg-solid); 2726 + border: 3px solid var(--card-text); 2727 + border-radius: var(--radius-card); 2728 + padding: 32px; 2729 + box-shadow: 6px 6px 0 var(--card-text); 2730 + transition: background 0.3s ease; 2451 2731 } 2452 2732 2453 - .panel:hover { 2454 - box-shadow: var(--shadow-hover); 2733 + [data-theme='dark'] .panel { 2734 + box-shadow: 6px 6px 0 var(--accent); 2455 2735 } 2456 2736 2457 2737 .panelTitle { ··· 2471 2751 } 2472 2752 2473 2753 .box { 2474 - background: var(--card-bg); 2475 - backdrop-filter: blur(12px); 2476 - -webkit-backdrop-filter: blur(12px); 2477 - border: 1px solid var(--card-border); 2478 - border-radius: 20px; 2754 + background: var(--card-bg-solid); 2755 + border: 3px solid var(--card-text); 2756 + border-radius: var(--radius-card); 2479 2757 padding: 24px; 2480 - box-shadow: var(--shadow); 2481 - transition: 2482 - background 0.3s ease, 2483 - border 0.3s ease; 2758 + box-shadow: 6px 6px 0 var(--card-text); 2759 + transition: background 0.3s ease; 2760 + } 2761 + 2762 + [data-theme='dark'] .box { 2763 + box-shadow: 6px 6px 0 var(--accent); 2484 2764 } 2485 2765 2486 2766 .divider { ··· 2489 2769 margin: 20px 0; 2490 2770 } 2491 2771 2492 - .gridCards { 2772 + .bentoGrid { 2493 2773 display: grid; 2494 2774 grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); 2495 - gap: 16px; 2775 + grid-auto-rows: minmax(180px, auto); 2776 + gap: 20px; 2777 + } 2778 + 2779 + .bentoNormal { 2780 + display: flex !important; 2781 + flex-direction: column !important; 2782 + } 2783 + 2784 + @media (min-width: 860px) { 2785 + .bentoHero { 2786 + grid-column: span 2; 2787 + grid-row: span 2; 2788 + display: flex !important; 2789 + flex-direction: column !important; 2790 + } 2496 2791 } 2497 2792 2498 2793 .grid2 { ··· 2509 2804 } 2510 2805 2511 2806 .projectCard { 2807 + --card-color: #f472b6; 2808 + --card-shadow-color: var(--card-text); 2512 2809 display: block; 2513 2810 position: relative; 2514 - background: var(--card-bg); 2515 - backdrop-filter: blur(12px); 2516 - -webkit-backdrop-filter: blur(12px); 2517 - border: 1px solid var(--card-border); 2518 - border-radius: 16px; 2519 - padding: 20px; 2520 - box-shadow: var(--shadow); 2811 + background: color-mix(in srgb, var(--card-color) 12%, var(--card-bg-solid) 88%); 2812 + border: 3px solid var(--card-text); 2813 + border-radius: var(--radius-card); 2814 + padding: 24px; 2815 + box-shadow: 6px 6px 0 var(--card-shadow-color); 2521 2816 text-decoration: none; 2522 2817 color: var(--card-text); 2523 - transition: all 0.2s ease; 2818 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); 2819 + animation: cardSlideIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) backwards; 2820 + overflow: hidden; 2821 + } 2822 + 2823 + .projectCard:nth-child(1) { --card-color: #f472b6; --card-shadow-color: #be185d; animation-delay: 0.05s; } 2824 + .projectCard:nth-child(2) { --card-color: #60a5fa; --card-shadow-color: #1d4ed8; animation-delay: 0.1s; } 2825 + .projectCard:nth-child(3) { --card-color: #34d399; --card-shadow-color: #065f46; animation-delay: 0.15s; } 2826 + .projectCard:nth-child(4) { --card-color: #fb923c; --card-shadow-color: #9a3412; animation-delay: 0.2s; } 2827 + .projectCard:nth-child(5) { --card-color: #a78bfa; --card-shadow-color: #4c1d95; animation-delay: 0.25s; } 2828 + .projectCard:nth-child(6) { --card-color: #f87171; --card-shadow-color: #991b1b; animation-delay: 0.3s; } 2829 + .projectCard:nth-child(7) { --card-color: #4ade80; --card-shadow-color: #14532d; animation-delay: 0.35s; } 2830 + .projectCard:nth-child(8) { --card-color: #fbbf24; --card-shadow-color: #78350f; animation-delay: 0.4s; } 2831 + .projectCard:nth-child(9) { --card-color: #22d3ee; --card-shadow-color: #164e63; animation-delay: 0.45s; } 2832 + .projectCard:nth-child(10) { --card-color: #e879f9; --card-shadow-color: #701a75; animation-delay: 0.5s; } 2833 + 2834 + @keyframes cardSlideIn { 2835 + from { 2836 + opacity: 0; 2837 + transform: translate(8px, 8px); 2838 + } 2839 + 2840 + to { 2841 + opacity: 1; 2842 + transform: translate(0, 0); 2843 + } 2524 2844 } 2525 2845 2526 2846 .projectCard:hover { 2527 - transform: translateY(-4px); 2528 - box-shadow: var(--shadow-hover); 2847 + transform: translate(-4px, -4px); 2848 + box-shadow: 10px 10px 0 var(--card-shadow-color); 2529 2849 text-decoration: none; 2530 2850 } 2531 2851 2852 + .projectCard:active { 2853 + transform: translate(4px, 4px); 2854 + box-shadow: 2px 2px 0 var(--card-shadow-color); 2855 + } 2856 + 2857 + [data-theme='dark'] .projectCard { 2858 + background: color-mix(in srgb, var(--card-color) 15%, var(--card-bg-solid) 85%); 2859 + border-color: var(--card-text); 2860 + } 2861 + 2532 2862 .projectTitle { 2533 2863 font-weight: 700; 2534 2864 font-size: 16px; ··· 2545 2875 2546 2876 .chip { 2547 2877 font-size: 11px; 2548 - font-weight: 600; 2549 - padding: 5px 12px; 2550 - border-radius: 999px; 2551 - background: var(--card-bg); 2552 - color: var(--card-text-light); 2878 + font-weight: 700; 2879 + padding: 3px 10px; 2880 + border-radius: 6px; 2881 + background: var(--card-bg-solid); 2882 + color: var(--card-text); 2883 + border: 2px solid var(--card-text); 2884 + box-shadow: 2px 2px 0 var(--card-text); 2885 + letter-spacing: 0.02em; 2553 2886 } 2554 2887 2555 2888 .badge { ··· 2599 2932 .textarea { 2600 2933 width: 100%; 2601 2934 min-height: 160px; 2602 - border-radius: 10px; 2603 - border: 1px solid var(--card-border); 2935 + border-radius: 12px; 2936 + border: 1px solid var(--input-border); 2604 2937 padding: 14px; 2605 2938 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; 2606 2939 font-size: 13px; ··· 2612 2945 2613 2946 .textarea:focus { 2614 2947 outline: none; 2615 - border-color: #e88978; 2616 - box-shadow: 0 0 0 3px rgba(232, 137, 120, 0.1); 2948 + border-color: var(--accent); 2949 + box-shadow: 0 0 0 3px rgba(244, 114, 182, 0.15); 2617 2950 } 2618 2951 2619 2952 .tabs { 2620 2953 display: flex; 2621 - gap: 4px; 2954 + gap: 6px; 2622 2955 align-items: center; 2623 2956 flex-wrap: wrap; 2624 - padding: 4px; 2625 - background: var(--code-bg); 2626 - border-radius: 10px; 2957 + padding: 0; 2958 + background: transparent; 2959 + border-radius: 0; 2627 2960 margin-bottom: 20px; 2628 2961 } 2629 2962 2630 2963 .tab { 2631 2964 display: inline-flex; 2632 2965 align-items: center; 2633 - background: transparent; 2634 - border: none; 2635 - border-radius: 8px; 2636 - padding: 10px 16px; 2637 - font-weight: 600; 2966 + background: var(--card-bg-solid); 2967 + border: 2px solid var(--card-text); 2968 + border-radius: 10px; 2969 + padding: 10px 18px; 2970 + font-weight: 700; 2638 2971 font-size: 13px; 2639 2972 cursor: pointer; 2640 2973 color: var(--card-text-light); 2641 - transition: all 0.15s ease; 2974 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s, background 0.15s, color 0.15s; 2975 + box-shadow: 3px 3px 0 var(--card-text); 2976 + position: relative; 2642 2977 } 2643 2978 2644 2979 .tab:hover { 2645 2980 color: var(--card-text); 2981 + transform: translate(-2px, -2px); 2982 + box-shadow: 5px 5px 0 var(--accent); 2983 + } 2984 + 2985 + .tab:active { 2986 + transform: translate(2px, 2px); 2987 + box-shadow: 1px 1px 0 var(--card-text); 2646 2988 } 2647 2989 2648 2990 .tab.active { 2649 - background: var(--card-bg); 2650 - color: var(--card-text); 2651 - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); 2991 + background: var(--accent); 2992 + color: #fff; 2993 + border-color: var(--card-text); 2994 + box-shadow: 3px 3px 0 var(--card-text); 2995 + } 2996 + 2997 + [data-theme='dark'] .tab.active { 2998 + color: #000; 2652 2999 } 2653 3000 2654 3001 .subTabs { 2655 3002 display: inline-flex; 2656 - padding: 4px; 2657 - background: var(--code-bg); 2658 - border-radius: 10px; 3003 + padding: 0; 3004 + background: transparent; 2659 3005 margin: 12px 0 16px; 2660 - gap: 4px; 3006 + gap: 6px; 2661 3007 } 2662 3008 2663 3009 .subTab { 2664 - background: transparent; 2665 - border: none; 2666 - border-radius: 8px; 3010 + background: var(--card-bg-solid); 3011 + border: 2px solid var(--card-text); 3012 + border-radius: 10px; 2667 3013 padding: 8px 14px; 2668 - font-weight: 600; 3014 + font-weight: 700; 2669 3015 font-size: 13px; 2670 3016 cursor: pointer; 2671 3017 color: var(--card-text-light); 2672 - transition: all 0.15s ease; 3018 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s, background 0.15s, color 0.15s; 3019 + box-shadow: 2px 2px 0 var(--card-text); 2673 3020 } 2674 3021 2675 3022 .subTab:hover { 2676 3023 color: var(--card-text); 3024 + transform: translate(-2px, -2px); 3025 + box-shadow: 4px 4px 0 var(--accent); 2677 3026 } 2678 3027 2679 3028 .subTab.active { 2680 - background: var(--card-bg); 2681 - color: var(--card-text); 2682 - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); 3029 + background: var(--accent); 3030 + color: #fff; 3031 + border-color: var(--card-text); 3032 + box-shadow: 2px 2px 0 var(--card-text); 2683 3033 } 2684 3034 2685 3035 [data-theme='dark'] .tabs { 2686 - background: color-mix(in srgb, var(--sidebar-bg) 70%, #0b1221 30%); 2687 - border: 1px solid var(--sidebar-border); 3036 + background: transparent; 2688 3037 } 2689 3038 2690 3039 [data-theme='dark'] .tab { 2691 - color: color-mix(in srgb, var(--card-text-light) 70%, #f5cdc6 30%); 3040 + background: var(--card-bg-solid); 3041 + border-color: var(--card-text); 3042 + box-shadow: 3px 3px 0 var(--accent); 2692 3043 } 2693 3044 2694 3045 [data-theme='dark'] .tab.active { 2695 - background: color-mix(in srgb, var(--card-bg) 70%, rgba(244, 162, 149, 0.3) 30%); 2696 - box-shadow: 2697 - 0 0 0 1px color-mix(in srgb, var(--card-color, #f4a295) 35%, rgba(255, 255, 255, 0.25) 65%), 2698 - 0 6px 20px rgba(0, 0, 0, 0.35); 2699 - color: #fff5f2; 2700 - } 2701 - 2702 - [data-theme='dark'] .subTabs { 2703 - background: color-mix(in srgb, var(--sidebar-bg) 70%, #0b1221 30%); 2704 - border: 1px solid var(--sidebar-border); 3046 + background: var(--accent); 3047 + color: #000; 3048 + box-shadow: 3px 3px 0 var(--card-text); 2705 3049 } 2706 3050 2707 3051 [data-theme='dark'] .subTab { 2708 - color: color-mix(in srgb, var(--card-text-light) 70%, #f5cdc6 30%); 3052 + box-shadow: 2px 2px 0 var(--accent); 2709 3053 } 2710 3054 2711 3055 [data-theme='dark'] .subTab.active { 2712 - background: color-mix(in srgb, var(--card-bg) 70%, rgba(244, 162, 149, 0.25) 30%); 2713 - color: #fff5f2; 2714 - box-shadow: 2715 - 0 0 0 1px color-mix(in srgb, var(--card-color, #f4a295) 35%, rgba(255, 255, 255, 0.2) 65%), 2716 - 0 6px 20px rgba(0, 0, 0, 0.25); 3056 + color: #000; 3057 + box-shadow: 2px 2px 0 var(--card-text); 2717 3058 } 2718 3059 2719 3060 .deployList { ··· 2727 3068 align-items: flex-start; 2728 3069 justify-content: space-between; 2729 3070 gap: 16px; 2730 - background: var(--card-bg); 2731 - border: 1px solid var(--card-border); 2732 - border-radius: 12px; 2733 - padding: 16px; 2734 - transition: all 0.15s ease; 3071 + background: var(--card-bg-solid); 3072 + border: 2px solid var(--card-text); 3073 + border-radius: 16px; 3074 + padding: 20px 22px; 3075 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s; 3076 + animation: cardSlideIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) backwards; 3077 + box-shadow: 4px 4px 0 var(--card-text); 2735 3078 } 2736 3079 3080 + [data-theme='dark'] .deployRow { 3081 + box-shadow: 4px 4px 0 var(--accent); 3082 + } 3083 + 3084 + .deployRow:nth-child(1) { animation-delay: 0.03s; } 3085 + .deployRow:nth-child(2) { animation-delay: 0.06s; } 3086 + .deployRow:nth-child(3) { animation-delay: 0.09s; } 3087 + .deployRow:nth-child(4) { animation-delay: 0.12s; } 3088 + .deployRow:nth-child(5) { animation-delay: 0.15s; } 3089 + 2737 3090 .deployRow:hover { 2738 - border-color: var(--sidebar-border); 2739 - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); 3091 + border-color: var(--accent); 3092 + box-shadow: 4px 4px 0 var(--accent); 3093 + } 3094 + 3095 + [data-theme='dark'] .deployRow:hover { 3096 + box-shadow: 4px 4px 0 var(--accent); 3097 + } 3098 + 3099 + .deployRow:active { 3100 + transform: translate(1px, 1px); 3101 + box-shadow: 3px 3px 0 var(--card-text); 2740 3102 } 3103 + 3104 + /* Status-colored left accent for deploy rows */ 3105 + .deployRow:has(.status-building) { border-left: 5px solid #f59e0b; } 3106 + .deployRow:has(.status-running) { border-left: 5px solid #22c55e; } 3107 + .deployRow:has(.status-failed) { border-left: 5px solid #ef4444; } 3108 + .deployRow:has(.status-stopped), 3109 + .deployRow:has(.status-canceled) { border-left: 5px solid #94a3b8; } 3110 + .deployRow:has(.status-active), 3111 + .deployRow:has(.status-ready) { border-left: 5px solid #22c55e; } 2741 3112 2742 3113 .deployRow .siteName { 2743 3114 font-weight: 600; ··· 2804 3175 justify-content: center; 2805 3176 padding: 48px 24px; 2806 3177 text-align: center; 2807 - background: var(--code-bg); 2808 - border: 1px dashed var(--card-border); 2809 - border-radius: 12px; 3178 + background: var(--card-bg-solid); 3179 + border: 2px dashed var(--card-border); 3180 + border-radius: 14px; 2810 3181 } 2811 3182 2812 3183 .envEmptyIcon { ··· 2837 3208 2838 3209 .envTable { 2839 3210 border: 1px solid var(--card-border); 2840 - border-radius: 12px; 3211 + border-radius: 14px; 2841 3212 overflow: hidden; 2842 - background: var(--card-bg); 3213 + background: var(--card-bg-solid); 2843 3214 } 2844 3215 2845 3216 .envTableHeader { ··· 2977 3348 } 2978 3349 2979 3350 [data-theme='dark'] .envEmptyState { 2980 - background: color-mix(in srgb, var(--card-bg) 50%, transparent 50%); 2981 - border-color: rgba(255, 255, 255, 0.1); 3351 + border-color: rgba(255, 255, 255, 0.12); 2982 3352 } 2983 3353 2984 3354 [data-theme='dark'] .envEmptyIcon { 2985 3355 background: var(--sidebar-bg); 2986 3356 } 2987 3357 2988 - [data-theme='dark'] .envTable { 2989 - background: color-mix(in srgb, var(--card-bg) 80%, rgba(255, 255, 255, 0.02) 20%); 2990 - border-color: rgba(255, 255, 255, 0.08); 2991 - } 2992 - 2993 3358 [data-theme='dark'] .envTableHeader { 2994 - background: var(--sidebar-bg); 3359 + background: var(--code-bg); 2995 3360 border-color: rgba(255, 255, 255, 0.08); 2996 3361 } 2997 3362 ··· 3200 3565 position: relative; 3201 3566 } 3202 3567 3203 - .dropdownMenu { 3204 - position: absolute; 3205 - top: 100%; 3206 - right: 0; 3207 - margin-top: 4px; 3208 - background: var(--dropdown-bg); 3209 - border: 1px solid var(--dropdown-border); 3210 - border-radius: 8px; 3211 - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 3212 - z-index: 10; 3213 - min-width: 140px; 3214 - overflow: hidden; 3215 - } 3216 - 3217 - .dropdownItem { 3218 - display: flex; 3219 - align-items: center; 3220 - gap: 8px; 3221 - width: 100%; 3222 - padding: 8px 12px; 3223 - background: none; 3224 - border: none; 3225 - font-size: 13px; 3226 - color: var(--card-text); 3227 - cursor: pointer; 3228 - transition: background 0.15s ease; 3229 - } 3230 - 3231 - .dropdownItem:hover { 3232 - background: var(--dropdown-hover); 3233 - } 3234 - 3235 3568 .dropdownItem.danger { 3236 3569 color: #dc2626; 3237 3570 } ··· 3281 3614 3282 3615 [data-theme='dark'] .maskValue { 3283 3616 color: var(--card-text-light); 3284 - } 3285 - 3286 - [data-theme='dark'] .dropdownMenu { 3287 - background: var(--dropdown-bg); 3288 - border-color: var(--dropdown-border); 3289 - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); 3290 - } 3291 - 3292 - [data-theme='dark'] .dropdownItem:hover { 3293 - background: var(--dropdown-hover); 3294 3617 } 3295 3618 3296 3619 [data-theme='dark'] .dropdownItem.danger:hover { ··· 3378 3701 background: var(--notice-bg); 3379 3702 border: 1px solid var(--notice-border); 3380 3703 padding: 14px 18px; 3381 - border-radius: 10px; 3382 - font-weight: 500; 3704 + border-radius: 14px; 3705 + font-weight: 600; 3383 3706 color: var(--notice-text); 3384 3707 font-size: 14px; 3385 3708 display: flex; ··· 3396 3719 background: var(--error-bg); 3397 3720 border: 1px solid var(--error-border); 3398 3721 padding: 14px 18px; 3399 - border-radius: 12px; 3400 - font-weight: 500; 3722 + border-radius: 14px; 3723 + font-weight: 600; 3401 3724 color: var(--error-text); 3402 3725 font-size: 14px; 3403 3726 } ··· 3430 3753 width: min(540px, 96vw); 3431 3754 max-height: calc(100vh - 48px); 3432 3755 overflow-y: auto; 3433 - background: var(--modal-bg); 3756 + background: var(--card-bg-solid); 3434 3757 backdrop-filter: blur(20px); 3435 3758 -webkit-backdrop-filter: blur(20px); 3436 - border: 1px solid var(--modal-border); 3437 - border-radius: 24px; 3438 - box-shadow: 3439 - 0 24px 80px rgba(0, 0, 0, 0.15), 3440 - inset 0 1px 1px var(--modal-inset); 3441 - animation: modalIn 0.25s cubic-bezier(0.16, 1, 0.3, 1); 3759 + border: 3px solid var(--card-text); 3760 + border-radius: var(--radius-card); 3761 + box-shadow: 8px 8px 0 var(--card-text); 3762 + animation: modalIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); 3763 + } 3764 + 3765 + [data-theme='dark'] .modal { 3766 + box-shadow: 8px 8px 0 var(--accent); 3442 3767 } 3443 3768 3444 3769 @keyframes modalIn { ··· 3474 3799 .iconBtn { 3475 3800 width: 36px; 3476 3801 height: 36px; 3477 - border-radius: 12px; 3478 - border: none; 3479 - background: var(--icon-btn-bg); 3802 + border-radius: 10px; 3803 + border: 2px solid var(--card-text); 3804 + background: var(--card-bg-solid); 3480 3805 cursor: pointer; 3481 3806 font-size: 20px; 3482 3807 line-height: 1; 3483 3808 display: flex; 3484 3809 align-items: center; 3485 3810 justify-content: center; 3486 - transition: all 0.15s ease; 3811 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s, background 0.15s; 3487 3812 color: var(--card-text-light); 3813 + box-shadow: 2px 2px 0 var(--card-text); 3488 3814 } 3489 3815 3490 3816 .iconBtn:hover { 3491 - background: rgba(220, 38, 38, 0.12); 3817 + background: rgba(220, 38, 38, 0.08); 3492 3818 color: #dc2626; 3819 + transform: translate(-2px, -2px); 3820 + box-shadow: 4px 4px 0 #dc2626; 3821 + } 3822 + 3823 + .iconBtn:active { 3824 + transform: translate(1px, 1px); 3825 + box-shadow: 1px 1px 0 var(--card-text); 3493 3826 } 3494 3827 3495 3828 .modalBody { ··· 3639 3972 padding: 14px 20px; 3640 3973 background: var(--card-bg-solid); 3641 3974 color: var(--card-text); 3642 - border: 1px solid var(--card-border); 3643 - border-radius: 12px; 3644 - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); 3975 + border: 3px solid var(--card-text); 3976 + border-radius: var(--radius-btn); 3977 + box-shadow: 4px 4px 0 var(--card-text); 3645 3978 font-size: 14px; 3646 - font-weight: 500; 3979 + font-weight: 700; 3647 3980 z-index: 9999; 3648 - animation: toastIn 0.25s ease; 3981 + animation: toastIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); 3649 3982 display: flex; 3650 3983 align-items: center; 3651 3984 gap: 10px; 3652 - backdrop-filter: blur(12px); 3653 - -webkit-backdrop-filter: blur(12px); 3985 + } 3986 + 3987 + [data-theme='dark'] .toast { 3988 + box-shadow: 4px 4px 0 var(--accent); 3654 3989 } 3655 3990 3656 3991 .toast svg { 3657 - color: #4ade80; 3992 + color: #22c55e; 3658 3993 } 3659 3994 3660 3995 @keyframes toastIn { ··· 4074 4409 } 4075 4410 4076 4411 .settingsSection { 4077 - background: var(--code-bg); 4078 - border: 1px solid var(--card-border); 4412 + background: var(--card-bg-solid); 4413 + border: 2px solid var(--card-text); 4079 4414 border-radius: 16px; 4080 4415 padding: 20px; 4081 - transition: all 0.2s ease; 4416 + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s; 4417 + box-shadow: 4px 4px 0 var(--card-text); 4082 4418 } 4083 4419 4084 4420 .settingsSection:hover { 4085 - border-color: var(--glass-border); 4421 + border-color: var(--accent); 4422 + box-shadow: 4px 4px 0 var(--accent); 4086 4423 } 4087 4424 4088 4425 .settingsSectionHeader { ··· 4102 4439 color: white; 4103 4440 border-radius: 10px; 4104 4441 flex-shrink: 0; 4442 + border: 2px solid var(--card-text); 4443 + box-shadow: 2px 2px 0 var(--card-text); 4105 4444 } 4106 4445 4107 4446 .settingsSectionTitle { ··· 4181 4520 } 4182 4521 4183 4522 .settingsDanger { 4184 - background: rgba(239, 68, 68, 0.04); 4185 - border-color: rgba(239, 68, 68, 0.15); 4523 + background: var(--card-bg-solid); 4524 + border-color: #ef4444; 4525 + box-shadow: 4px 4px 0 #ef4444; 4186 4526 } 4187 4527 4188 4528 .settingsDanger:hover { 4189 - border-color: rgba(239, 68, 68, 0.25); 4529 + transform: translate(-2px, -2px); 4530 + box-shadow: 6px 6px 0 #ef4444; 4190 4531 } 4191 4532 4192 4533 .settingsDangerIcon { 4193 - background: rgba(239, 68, 68, 0.12) !important; 4194 - color: #dc2626 !important; 4534 + background: #ef4444 !important; 4535 + color: #fff !important; 4536 + border: 2px solid var(--card-text) !important; 4537 + box-shadow: 2px 2px 0 var(--card-text) !important; 4195 4538 } 4196 4539 4197 4540 .settingsDangerContent { ··· 4215 4558 } 4216 4559 4217 4560 [data-theme='dark'] .settingsSection { 4218 - background: color-mix(in srgb, var(--card-bg) 60%, transparent 40%); 4219 - border-color: rgba(255, 255, 255, 0.08); 4561 + background: var(--card-bg-solid); 4562 + border-color: var(--card-text); 4563 + box-shadow: 4px 4px 0 var(--accent); 4220 4564 } 4221 4565 4222 4566 [data-theme='dark'] .settingsSection:hover { 4223 - border-color: rgba(255, 255, 255, 0.14); 4567 + box-shadow: 4px 4px 0 var(--accent); 4224 4568 } 4225 4569 4226 4570 [data-theme='dark'] .settingsSectionIcon { 4227 - background: rgba(244, 162, 149, 0.2); 4228 - color: #f4a295; 4571 + background: var(--accent); 4572 + color: #000; 4229 4573 } 4230 4574 4231 4575 [data-theme='dark'] .settingsInputSuffix { ··· 4234 4578 } 4235 4579 4236 4580 [data-theme='dark'] .settingsDanger { 4237 - background: rgba(248, 113, 113, 0.06); 4238 - border-color: rgba(248, 113, 113, 0.15); 4581 + background: var(--card-bg-solid); 4582 + border-color: #f87171; 4583 + box-shadow: 4px 4px 0 #f87171; 4239 4584 } 4240 4585 4241 4586 [data-theme='dark'] .settingsDanger:hover { 4242 - border-color: rgba(248, 113, 113, 0.25); 4587 + box-shadow: 6px 6px 0 #f87171; 4243 4588 } 4244 4589 4245 4590 [data-theme='dark'] .settingsDangerIcon {