- {/* Metric Cards */}
- {[
- { title: "Active Users", value: stats.users.active.toLocaleString(), label: `${stats.users.total.toLocaleString()} total registered`, icon: Users, color: "from-purple-500 to-pink-500" },
- { title: "Items in Circulation", value: stats.economy.totalItems.toLocaleString(), label: "Total items owned", icon: Package, color: "from-blue-500 to-cyan-500" },
- { title: "Commands registered", value: stats.commands.total, label: "Total system capabilities", icon: Zap, color: "from-yellow-500 to-orange-500" },
- { title: "Avg Latency", value: `${stats.ping.avg}ms`, label: "WebSocket heartbeat", icon: Activity, color: "from-emerald-500 to-teal-500" },
- ].map((metric, i) => (
-
-
- {metric.title}
-
-
-
-
-
- {metric.value}
- {metric.label}
-
-
- ))}
-
-
-
-
-
-
- Economy Overview
-
-
Global wealth and progression statistics
-
-
-
-
- Uptime: {Math.floor(stats.uptime / 3600)}h {Math.floor((stats.uptime % 3600) / 60)}m
-
-
-
-
-
-
-
-
-
Total Distributed Wealth
-
- {BigInt(stats.economy.totalWealth).toLocaleString()} AU
-
-
-
-
-
-
Avg Level
-
{stats.economy.avgLevel}
-
-
-
Peak Streak
-
{stats.economy.topStreak} days
-
-
-
-
-
-
-
- {/* Administrative Control Panel */}
-
-
- {/* Active Lootdrop Alert */}
- {stats.activeLootdrops && stats.activeLootdrops.length > 0 && (
-
-
-
-
-
-
-
-
-
-
- ACTIVE LOOTDROP
-
-
-
-
-
- {stats.activeLootdrops[0]?.rewardAmount} {stats.activeLootdrops[0]?.currency}
-
-
- Expires {stats.activeLootdrops[0]?.expiresAt ? new Date(stats.activeLootdrops[0].expiresAt).toLocaleTimeString() : 'Never'}
-
-
-
-
+
+ {/* Navigation */}
+
+
+ {/* Bot Avatar */}
+ {stats?.bot?.avatarUrl ? (
+
+ ) : (
+
)}
- {/* Recent Events Feed */}
-
-
- Recent Events
- Live system activity feed
-
-
-
- {stats.recentEvents.length === 0 ? (
-
- ) : (
- stats.recentEvents.slice(0, 6).map((event, i) => (
-
-
-
-
- {event.message}
-
-
- {new Date(event.timestamp).toLocaleTimeString()}
-
-
-
- ))
- )}
-
- {stats.recentEvents.length > 0 && (
-
- View Event Logs
-
+ Aurora
+
+ {/* Live Status Badge */}
+
+
+ {isConnected && (
+
)}
-
-
+
+
+
+ {isConnected ? "Live" : "Offline"}
+
+
-
- {/* Leaderboards Section */}
-
-
-
-
-
-
-
- Top Levels
- Highest ranked users
-
-
-
-
- {stats.leaderboards?.topLevels.map((user, i) => (
-
-
-
- {i + 1}
-
-
{user.username}
-
-
Lvl {user.level}
-
- )) ||
No data available
}
-
-
-
+
+
+ Home
+
+
+ Design System
+
+
+
-
-
-
-
-
-
- Richest Users
- Highest net worth
-
-
-
-
- {stats.leaderboards?.topWealth.map((user, i) => (
-
-
-
- {i + 1}
-
-
{user.username}
-
-
{BigInt(user.balance).toLocaleString()} AU
-
- )) ||
No data available
}
-
-
-
-
+ {/* Content Placeholder */}
+
+
+
Dashboard Overview
+
+ Real-time connection status:
+ {isConnected ? "Connected" : "Disconnected"}
+
+
+
+
);
}
diff --git a/web/src/pages/DesignSystem.tsx b/web/src/pages/DesignSystem.tsx
new file mode 100644
index 0000000..6dda9ce
--- /dev/null
+++ b/web/src/pages/DesignSystem.tsx
@@ -0,0 +1,305 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import { Badge } from "../components/ui/badge";
+import { Card, CardHeader, CardTitle, CardContent } from "../components/ui/card";
+import { Button } from "../components/ui/button";
+import { Switch } from "../components/ui/switch";
+import { FeatureCard } from "../components/feature-card";
+import { InfoCard } from "../components/info-card";
+import { SectionHeader } from "../components/section-header";
+import { TestimonialCard } from "../components/testimonial-card";
+
+export function DesignSystem() {
+ return (
+
+ {/* Navigation */}
+
+
+
+
+ Home
+
+
+ Dashboard
+
+
+
+
+
+ {/* Header Section */}
+
+
+ {/* Color Palette */}
+
+
+
+ Color Palette
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Badges & Pills */}
+
+
+
+ Badges & Tags
+
+
+ Primary
+ Secondary
+ Solaris
+ Celestial Glass
+ Outline
+ Destructive
+
+
+
+ {/* Animations & Interactions */}
+
+
+
+ Animations & Interactions
+
+
+
+
Hover Lift
+
Smooth upward translation with enhanced depth.
+
+
+
Hover Glow
+
Subtle border and shadow illumination on hover.
+
+
+
+ Press Interaction
+
+
+
+
+
+ {/* Gradients & Special Effects */}
+
+
+
+ Gradients & Effects
+
+
+
+
The Solaris Gradient (Background)
+
+ Celestial Void
+
+
+
+
Glassmorphism
+
+
+ Frosted Celestial Glass
+
+
+
+
+
+
+ {/* Components Showcase */}
+
+
+
+ Component Showcase
+
+
+ {/* Action Card with Tags */}
+
+
+
+ Celestial Action
+ New
+
+
+
+ Quest
+ Level 15
+
+
+ Experience the warmth of the sun in every interaction and claim your rewards.
+
+
+
+ Ascend
+
+
+
+
+
+ {/* Profile/Entity Card with Tags */}
+
+
+
+ Stellar Navigator
+ Level 42 Mage
+
+
+
+ Astronomy
+ Pyromancy
+ Leadership
+
+
+
+
+
+ {/* Interactive Card with Tags */}
+
+
+
+ Beta
+
+ System Settings
+
+
+
+
+
Starry Background
+
Enable animated SVG stars
+
+
+
+
+
+
+ Solar Flare Glow
+ Pro
+
+
Add bloom to primary elements
+
+
+
+
+
+
+
+
+ {/* Refactored Application Components */}
+
+
+
+ Application Components
+
+
+
+ {/* Section Header Demo */}
+
+
+
+
+ {/* Feature Cards Demo */}
+
+
}
+ />
+
+
+ Custom Child Content
+
+
+
+
+ {/* Info Cards Demo */}
+
+ }
+ title="Info Card"
+ description="Compact card for highlighting features or perks with an icon."
+ iconWrapperClassName="bg-primary/20 text-primary"
+ />
+
+
+ {/* Testimonial Cards Demo */}
+
+
+
+
+
+
+ {/* Typography */}
+
+ Fluid Typography
+
+
+
+
+
+
+
+
+
+
+
+ Try resizing your browser window to see the text scale smoothly.
+
+
+
+
+ );
+}
+
+function TypographyRow({ step, className, label }: { step: string, className: string, label: string }) {
+ return (
+
+
Step {step}
+
{label}
+
+ );
+}
+
+function ColorSwatch({ label, color, text = "text-foreground", border = false }: { label: string, color: string, text?: string, border?: boolean }) {
+ return (
+
+ );
+}
+
+export default DesignSystem;
diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx
new file mode 100644
index 0000000..fef426b
--- /dev/null
+++ b/web/src/pages/Home.tsx
@@ -0,0 +1,236 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import { Badge } from "../components/ui/badge";
+import { Button } from "../components/ui/button";
+import { FeatureCard } from "../components/feature-card";
+import { InfoCard } from "../components/info-card";
+import { SectionHeader } from "../components/section-header";
+import { TestimonialCard } from "../components/testimonial-card";
+import {
+ GraduationCap,
+ Coins,
+ Package,
+ ShieldCheck,
+ Zap,
+ Trophy
+} from "lucide-react";
+
+export function Home() {
+ return (
+
+ {/* Navigation (Simple) */}
+
+
+
+
+ Dashboard
+
+
+ Design System
+
+
+
+
+ {/* Hero Section */}
+
+
+ The Ultimate Academic Strategy RPG
+
+
+
+
+ Rise to the Top
+
+
+ of the Elite Academy
+
+
+
+
+ Aurora is a competitive academic RPG bot where students are assigned to Classes A through D, vying for supremacy in a high-stakes elite school setting.
+
+
+
+
+ Join our Server
+
+
+ Explore Documentation
+
+
+
+
+ {/* Features Section (Bento Grid) */}
+
+
+
+ {/* Class System */}
+
}
+ >
+
+ Constellation Units
+ Special Exams
+
+
+
+ {/* Economy */}
+
}
+ />
+
+ {/* Inventory */}
+
}
+ />
+
+ {/* Exams */}
+
+
+
+
+ Island Exam
+ Active
+
+
+
+
+ {/* Trading & Social */}
+
}
+ >
+
+ Constellation Units
+ Special Exams
+
+
+
+ {/* Tech Stack */}
+
+
+ BUN 1.0+
+ DISCORD.JS
+ DRIZZLE
+ POSTGRES
+
+
+
+
+
+ {/* Unique Features Section */}
+
+
+
+
+
+ }
+ title="Merit-Based Society"
+ description="Your class standing determines your privileges. Earn points to rise, or lose them and face the consequences of falling behind."
+ iconWrapperClassName="bg-primary/20 text-primary"
+ />
+ }
+ title="Psychological Warfare"
+ description="Form alliances, uncover spies, and execute strategies during Special Exams where trust is the most valuable currency."
+ iconWrapperClassName="bg-secondary/20 text-secondary"
+ />
+ }
+ title="Dynamic World"
+ description="The school rules change based on the actions of the student body. Your decisions shape the future of the academy."
+ iconWrapperClassName="bg-primary/20 text-primary"
+ />
+
+
+
+
+ {/* Testimonials Section */}
+
+
+ {/* Footer */}
+
+
+
+
+
+ © 2026 Aurora Project. Licensed under MIT.
+
+
+
+
+
+
+ );
+}
+
+export default Home;
diff --git a/web/src/pages/Settings.tsx b/web/src/pages/Settings.tsx
deleted file mode 100644
index 3aea38c..0000000
--- a/web/src/pages/Settings.tsx
+++ /dev/null
@@ -1,509 +0,0 @@
-import { useState, useEffect } from "react";
-import { toast } from "sonner";
-import { Loader2, Save, RefreshCw, Smartphone, Coins, Trophy, Shield, Users, Terminal, MessageSquare } from "lucide-react";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Card, CardContent } from "@/components/ui/card";
-import { Switch } from "@/components/ui/switch";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
-
-// Types matching the backend response
-interface RoleOption { id: string; name: string; color: string; }
-interface ChannelOption { id: string; name: string; type: number; }
-
-interface SettingsMeta {
- roles: RoleOption[];
- channels: ChannelOption[];
- commands: { name: string; category: string }[];
-}
-
-import { type GameConfigType } from "@shared/lib/config";
-
-// Recursive partial type for nested updates
-type DeepPartial
= {
- [P in keyof T]?: T[P] extends object ? DeepPartial : T[P];
-};
-
-export function Settings() {
- // We use a DeepPartial type for the local form state to allow for safe updates
- const [config, setConfig] = useState | null>(null);
- const [meta, setMeta] = useState({ roles: [], channels: [], commands: [] });
- const [loading, setLoading] = useState(true);
- const [saving, setSaving] = useState(false);
- const [activeTab, setActiveTab] = useState("general");
-
- useEffect(() => {
- fetchData();
- }, []);
-
- const fetchData = async () => {
- setLoading(true);
- try {
- const [configRes, metaRes] = await Promise.all([
- fetch("/api/settings"),
- fetch("/api/settings/meta")
- ]);
-
- if (configRes.ok && metaRes.ok) {
- const configData = await configRes.json();
- const metaData = await metaRes.json();
- setConfig(configData);
- setMeta(metaData);
- }
- } catch (error) {
- console.error("Failed to fetch settings:", error);
- } finally {
- setLoading(false);
- }
- };
-
- const handleSave = async () => {
- setSaving(true);
- const toastId = toast.loading("Saving configuration...");
- try {
- const response = await fetch("/api/settings", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(config)
- });
-
- if (!response.ok) throw new Error("Failed to save");
- // Reload to satisfy any server-side transformations
- await fetchData();
- toast.success("Settings saved successfully", { id: toastId });
- } catch (error) {
- console.error("Failed to save settings:", error);
- toast.error("Failed to save settings", {
- id: toastId,
- description: "Please check your input and try again."
- });
- } finally {
- setSaving(false);
- }
- };
-
- // Helper to update nested state
- const updateConfig = (path: string, value: any) => {
- if (!path) return;
- setConfig((prev: any) => {
- const newConfig = { ...prev };
- const parts = path.split('.');
- let current = newConfig;
-
- for (let i = 0; i < parts.length - 1; i++) {
- const part = parts[i];
- if (part === undefined) return prev;
- // Clone nested objects to ensure immutability
- if (!current[part]) current[part] = {};
- current[part] = { ...current[part] };
- current = current[part];
- }
-
- const lastPart = parts[parts.length - 1];
- if (lastPart !== undefined) {
- current[lastPart] = value;
- }
-
- return newConfig;
- });
- };
-
- if (loading || !config) {
- return (
-
-
-
- );
- }
-
- const tabs = [
- { id: "general", label: "General", icon: Smartphone },
- { id: "economy", label: "Economy", icon: Coins },
- { id: "leveling", label: "Leveling", icon: Trophy },
- { id: "moderation", label: "Moderation", icon: Shield },
- { id: "roles", label: "Roles", icon: Users },
- { id: "system", label: "System", icon: Terminal },
- ];
-
- return (
-
-
-
-
Settings
-
Manage global bot configuration
-
-
-
-
- Reset
-
-
- {saving ? : }
- Save Changes
-
-
-
-
- {/* Tabs Navigation */}
-
- {tabs.map((tab) => (
- setActiveTab(tab.id)}
- className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all border ${activeTab === tab.id
- ? "bg-primary/20 border-primary/50 text-white shadow-[0_0_10px_rgba(var(--primary),0.2)]"
- : "bg-white/5 border-white/5 text-white/50 hover:bg-white/10 hover:text-white"
- }`}
- >
-
- {tab.label}
-
- ))}
-
-
-
-
- {activeTab === "general" && (
-
-
-
- updateConfig("welcomeChannelId", v)}
- />
- updateConfig("feedbackChannelId", v)}
- />
-
-
-
-
- updateConfig("terminal.channelId", v)}
- />
- updateConfig("terminal.messageId", v)}
- placeholder="ID of the pinned message"
- />
-
-
- )}
-
- {activeTab === "economy" && (
-
-
-
- updateConfig("economy.daily.amount", v)}
- />
- updateConfig("economy.daily.streakBonus", v)}
- />
- updateConfig("economy.daily.weeklyBonus", v)}
- />
-
-
-
-
-
updateConfig("economy.transfers.minAmount", v)}
- />
-
- Allow Self Transfer
- updateConfig("economy.transfers.allowSelfTransfer", checked)}
- />
-
-
-
-
-
- updateConfig("lootdrop.spawnChance", Number(v))}
- />
- updateConfig("lootdrop.cooldownMs", Number(v))}
- />
- updateConfig("lootdrop.minMessages", Number(v))}
- />
- updateConfig("lootdrop.reward.min", Number(v))}
- />
- updateConfig("lootdrop.reward.max", Number(v))}
- />
-
-
- )}
-
- {activeTab === "leveling" && (
-
-
-
- updateConfig("leveling.base", Number(v))}
- />
- updateConfig("leveling.exponent", Number(v))}
- />
-
-
-
-
- updateConfig("leveling.chat.minXp", Number(v))}
- />
- updateConfig("leveling.chat.maxXp", Number(v))}
- />
- updateConfig("leveling.chat.cooldownMs", Number(v))}
- />
-
-
- )}
-
- {activeTab === "roles" && (
-
-
-
- updateConfig("studentRole", v)}
- />
- updateConfig("visitorRole", v)}
- />
-
-
-
- {/* Multi-select for color roles is complex, simpler impl for now */}
-
-
Available Color Roles
-
- {(config?.colorRoles || []).map((roleId: string | undefined) => {
- if (!roleId) return null;
- const role = meta.roles.find(r => r.id === roleId);
- return (
-
-
- {role?.name || roleId}
- updateConfig("colorRoles", (config?.colorRoles || []).filter((id: string | undefined) => id !== roleId))}
- className="hover:text-red-400 ml-1"
- >
- ×
-
-
- );
- })}
-
-
- {
- if (value && !(config?.colorRoles || []).includes(value)) {
- updateConfig("colorRoles", [...(config?.colorRoles || []), value]);
- }
- }}
- >
-
-
-
-
- {meta.roles.map((r) => (
-
-
-
- {r.name}
-
-
- ))}
-
-
-
-
-
- )}
- {activeTab === "moderation" && (
-
-
-
- updateConfig("moderation.prune.maxAmount", Number(v))}
- />
- updateConfig("moderation.prune.confirmThreshold", Number(v))}
- />
-
-
- )}
-
- {activeTab === "system" && (
-
-
-
- {meta.commands.length === 0 ? (
-
- No commands found in metadata.
-
- ) : (
- Object.entries(
- meta.commands.reduce((acc, cmd) => {
- const cat = cmd.category || 'Uncategorized';
- if (!acc[cat]) acc[cat] = [];
- acc[cat].push(cmd);
- return acc;
- }, {} as Record
)
- ).sort(([a], [b]) => a.localeCompare(b)).map(([category, commands]) => (
-
-
{category}
-
- {commands.map(cmd => (
-
-
- /{cmd.name}
-
- {config?.commands?.[cmd.name] === false ? "Disabled" : "Enabled"}
-
-
-
updateConfig(`commands.${cmd.name}`, checked)}
- />
-
- ))}
-
-
- ))
- )}
-
- )}
-
-
-
- );
-}
-
-// Sub-components for cleaner code
-
-function SectionTitle({ icon: Icon, title, description }: { icon: any, title: string, description: string }) {
- return (
-
-
-
-
-
-
{title}
-
{description}
-
-
- );
-}
-
-function InputField({ label, value, onChange, type = "text", placeholder }: { label: string, value: any, onChange: (val: string) => void, type?: string, placeholder?: string }) {
- return (
-
- {label}
- onChange(e.target.value)}
- placeholder={placeholder}
- className="bg-black/20 border-white/10 text-white focus:border-primary/50"
- />
-
- );
-}
-
-function SelectField({ label, value, options, onChange }: { label: string, value: string | undefined, options: any[], onChange: (val: string) => void }) {
- return (
-
- {label}
-
-
-
-
-
- {options.map((opt) => (
-
- {opt.name}
-
- ))}
-
-
-
- );
-}
diff --git a/web/styles/globals.css b/web/styles/globals.css
index ba9da65..572524b 100644
--- a/web/styles/globals.css
+++ b/web/styles/globals.css
@@ -39,68 +39,178 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
+
+ --text-step--2: var(--step--2);
+ --text-step--1: var(--step--1);
+ --text-step-0: var(--step-0);
+ --text-step-1: var(--step-1);
+ --text-step-2: var(--step-2);
+ --text-step-3: var(--step-3);
+ --text-step-4: var(--step-4);
+ --text-step-5: var(--step-5);
}
:root {
- --radius: 1rem;
- --background: oklch(0.12 0.02 260);
- --foreground: oklch(0.98 0.01 260);
- --card: oklch(0.16 0.03 260 / 0.5);
- --card-foreground: oklch(0.98 0.01 260);
- --popover: oklch(0.14 0.02 260 / 0.8);
- --popover-foreground: oklch(0.98 0.01 260);
- --primary: oklch(0.65 0.18 250);
- --primary-foreground: oklch(1 0 0);
- --secondary: oklch(0.25 0.04 260);
- --secondary-foreground: oklch(0.98 0.01 260);
- --muted: oklch(0.2 0.03 260 / 0.6);
- --muted-foreground: oklch(0.7 0.02 260);
- --accent: oklch(0.3 0.05 250 / 0.4);
- --accent-foreground: oklch(0.98 0.01 260);
- --destructive: oklch(0.6 0.18 25);
- --border: oklch(1 0 0 / 10%);
- --input: oklch(1 0 0 / 5%);
- --ring: oklch(0.65 0.18 250 / 50%);
- --chart-1: oklch(0.6 0.18 250);
- --chart-2: oklch(0.7 0.15 160);
- --chart-3: oklch(0.8 0.12 80);
- --chart-4: oklch(0.6 0.2 300);
- --chart-5: oklch(0.6 0.25 20);
- --sidebar: oklch(0.14 0.02 260 / 0.6);
- --sidebar-foreground: oklch(0.98 0.01 260);
- --sidebar-primary: oklch(0.65 0.18 250);
- --sidebar-primary-foreground: oklch(1 0 0);
- --sidebar-accent: oklch(1 0 0 / 5%);
- --sidebar-accent-foreground: oklch(0.98 0.01 260);
- --sidebar-border: oklch(1 0 0 / 8%);
- --sidebar-ring: oklch(0.65 0.18 250 / 50%);
-}
+ --step--2: clamp(0.5002rem, 0.449rem + 0.2273vw, 0.6252rem);
+ --step--1: clamp(0.7072rem, 0.6349rem + 0.3215vw, 0.884rem);
+ --step-0: clamp(1rem, 0.8977rem + 0.4545vw, 1.25rem);
+ --step-1: clamp(1.414rem, 1.2694rem + 0.6427vw, 1.7675rem);
+ --step-2: clamp(1.9994rem, 1.7949rem + 0.9088vw, 2.4992rem);
+ --step-3: clamp(2.8271rem, 2.538rem + 1.2851vw, 3.5339rem);
+ --step-4: clamp(3.9976rem, 3.5887rem + 1.8171vw, 4.997rem);
+ --step-5: clamp(5.6526rem, 5.0745rem + 2.5694vw, 7.0657rem);
-@layer base {
- * {
- @apply border-border outline-ring/50;
- }
-
- body {
- @apply bg-background text-foreground selection:bg-primary/30;
- font-family: 'Outfit', 'Inter', system-ui, sans-serif;
- background-image:
- radial-gradient(at 0% 0%, oklch(0.25 0.1 260 / 0.15) 0px, transparent 50%),
- radial-gradient(at 100% 0%, oklch(0.35 0.12 300 / 0.1) 0px, transparent 50%);
- background-attachment: fixed;
- }
+ --radius: 0.5rem;
+ --background: oklch(0.12 0.015 40);
+ --foreground: oklch(0.98 0.01 60);
+ --card: oklch(0.16 0.03 40 / 0.6);
+ --card-foreground: oklch(0.98 0.01 60);
+ --popover: oklch(0.14 0.02 40 / 0.85);
+ --popover-foreground: oklch(0.98 0.01 60);
+ --primary: oklch(0.82 0.18 85);
+ --primary-foreground: oklch(0.12 0.015 40);
+ --secondary: oklch(0.65 0.2 55);
+ --secondary-foreground: oklch(0.98 0.01 60);
+ --muted: oklch(0.22 0.02 40 / 0.6);
+ --muted-foreground: oklch(0.7 0.08 40);
+ --accent: oklch(0.75 0.15 70 / 0.15);
+ --accent-foreground: oklch(0.98 0.01 60);
+ --destructive: oklch(0.55 0.18 25);
+ --border: oklch(1 0 0 / 12%);
+ --input: oklch(1 0 0 / 8%);
+ --ring: oklch(0.82 0.18 85 / 40%);
+ --chart-1: oklch(0.82 0.18 85);
+ --chart-2: oklch(0.65 0.2 55);
+ --chart-3: oklch(0.75 0.15 70);
+ --chart-4: oklch(0.55 0.18 25);
+ --chart-5: oklch(0.9 0.1 95);
+ --sidebar: oklch(0.14 0.02 40 / 0.7);
+ --sidebar-foreground: oklch(0.98 0.01 60);
+ --sidebar-primary: oklch(0.82 0.18 85);
+ --sidebar-primary-foreground: oklch(0.12 0.015 40);
+ --sidebar-accent: oklch(1 0 0 / 8%);
+ --sidebar-accent-foreground: oklch(0.98 0.01 60);
+ --sidebar-border: oklch(1 0 0 / 12%);
+ --sidebar-ring: oklch(0.82 0.18 85 / 40%);
}
@layer utilities {
- .glass {
- @apply bg-card backdrop-blur-xl border border-white/10 shadow-2xl;
+ .bg-aurora-page {
+ background: radial-gradient(circle at 50% -20%, oklch(0.25 0.1 50) 0%, var(--background) 70%);
+ background-attachment: fixed;
}
- .glass-sidebar {
- @apply bg-sidebar backdrop-blur-2xl border-r border-white/5;
+ .bg-aurora {
+ background-image: linear-gradient(135deg, oklch(0.82 0.18 85) 0%, oklch(0.65 0.2 55) 100%);
}
- .text-glow {
- text-shadow: 0 0 10px oklch(var(--primary) / 0.5);
+ .glass-card {
+ background: var(--card);
+ backdrop-filter: blur(12px);
+ border: 1px solid var(--border);
+ }
+
+ .sun-flare {
+ box-shadow: 0 0 40px oklch(0.82 0.18 85 / 0.12);
+ }
+
+ /* Entrance Animations */
+ .animate-in {
+ animation-duration: 0.6s;
+ animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
+ animation-fill-mode: forwards;
+ }
+
+ .fade-in {
+ opacity: 0;
+ animation-name: fade-in;
+ }
+
+ .slide-up {
+ opacity: 0;
+ transform: translateY(20px);
+ animation-name: slide-up;
+ }
+
+ .zoom-in {
+ opacity: 0;
+ transform: scale(0.95);
+ animation-name: zoom-in;
+ }
+
+ @keyframes fade-in {
+ to {
+ opacity: 1;
+ }
+ }
+
+ @keyframes slide-up {
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
+ @keyframes zoom-in {
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+ }
+
+ /* Interaction Utilities */
+ .hover-lift {
+ transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s ease;
+ }
+
+ .hover-lift:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 10px 25px -5px oklch(0 0 0 / 0.3), 0 0 20px oklch(0.82 0.18 85 / 0.1);
+ }
+
+ .hover-glow {
+ transition: box-shadow 0.3s ease, border-color 0.3s ease;
+ }
+
+ .hover-glow:hover {
+ border-color: oklch(0.82 0.18 85 / 0.4);
+ box-shadow: 0 0 20px oklch(0.82 0.18 85 / 0.15);
+ }
+
+ .hover-scale {
+ transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
+ }
+
+ .hover-scale:hover {
+ transform: scale(1.02);
+ }
+
+ .active-press {
+ transition: transform 0.1s ease;
+ }
+
+ .active-press:active {
+ transform: scale(0.97);
+ }
+
+ /* Staggered Delay Utilities */
+ .delay-100 {
+ animation-delay: 100ms;
+ }
+
+ .delay-200 {
+ animation-delay: 200ms;
+ }
+
+ .delay-300 {
+ animation-delay: 300ms;
+ }
+
+ .delay-400 {
+ animation-delay: 400ms;
+ }
+
+ .delay-500 {
+ animation-delay: 500ms;
}
}
\ No newline at end of file