diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx
new file mode 100644
index 0000000..8aa4ecc
--- /dev/null
+++ b/src/pages/HomePage.tsx
@@ -0,0 +1,351 @@
+import React, { useMemo } from 'react';
+import type { CSSProperties } from 'react';
+import { useTranslation } from 'react-i18next';
+import { StatCard } from '../components/StatCard';
+import { SummaryCard } from '../components/SummaryCard';
+import { QuickActionCard } from '../components/QuickActionCard';
+import { SectionCard } from '../components/SectionCard';
+import { theme } from '../theme';
+import DashboardLayout from '../layout/DashboardLayout';
+import { useDashboardScaffold } from '../hooks/useDashboardScaffold';
+
+/**
+ * HomePage Component
+ *
+ * Web replication of the Pennyzo Home Screen layout:
+ * 1. Premium top bar with language selector, notifications, profile
+ * 2. Company selector card
+ * 3. Decorative divider
+ * 4. Quick stats row (horizontal scroll)
+ * 5. AI insights summary list
+ * 6. Suggested actions grid
+ */
+
+interface QuickStat {
+ id: string;
+ label: string;
+ value: string;
+ iconColor: string;
+ trend?: {
+ value: string;
+ isPositive: boolean;
+ };
+ icon: React.ReactNode;
+}
+
+interface InsightItem {
+ id: string;
+ label: string;
+ count: number;
+ iconColor: string;
+ icon: React.ReactNode;
+}
+
+interface QuickAction {
+ id: string;
+ label: string;
+ description: string;
+ iconColor: string;
+ icon: React.ReactNode;
+ badge?: number;
+}
+
+export const HomePage: React.FC = () => {
+ const { t } = useTranslation();
+ const userName = 'Sarah'; // TODO: wire up to auth context
+ const {
+ topBarProps,
+ } = useDashboardScaffold();
+
+ const quickStats: QuickStat[] = useMemo(
+ () => [
+ {
+ id: 'revenue',
+ label: t('home.revenue'),
+ value: 'โน1.2M',
+ iconColor: theme.colors.highlight,
+ trend: { value: '+18%', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'employees',
+ label: t('home.employees'),
+ value: '42',
+ iconColor: theme.colors.highlightAlt,
+ trend: { value: '+5', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'growth',
+ label: t('home.growth'),
+ value: '23%',
+ iconColor: theme.colors.success,
+ trend: { value: '+8%', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'net-promoter',
+ label: t('home.net_promoter'),
+ value: '72',
+ iconColor: theme.colors.info,
+ trend: { value: '+4', isPositive: true },
+ icon: (
+
+ ),
+ },
+ ],
+ [t],
+ );
+
+ const aiInsights: InsightItem[] = useMemo(
+ () => [
+ {
+ id: 'insight-1',
+ label: t('home.pending_approvals'),
+ count: 5,
+ iconColor: theme.colors.warning,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'insight-2',
+ label: t('home.compliance_alerts'),
+ count: 12,
+ iconColor: theme.colors.success,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'insight-3',
+ label: t('home.recent_hires'),
+ count: 3,
+ iconColor: theme.colors.error,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'insight-4',
+ label: t('home.expiring_contracts'),
+ count: 2,
+ iconColor: theme.colors.highlightAlt,
+ icon: (
+
+ ),
+ },
+ ],
+ [t],
+ );
+
+ const quickActions: QuickAction[] = useMemo(
+ () => [
+ {
+ id: 'salary-slips',
+ label: t('home.review_approvals'),
+ description: t('home.review_approvals_desc'),
+ iconColor: theme.colors.highlight,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'renew-contract',
+ label: t('home.update_payroll'),
+ description: t('home.update_payroll_desc'),
+ iconColor: theme.colors.warning,
+ icon: (
+
+ ),
+ badge: 1,
+ },
+ {
+ id: 'review-reports',
+ label: t('home.view_reports'),
+ description: t('home.view_reports_desc'),
+ iconColor: theme.colors.success,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'schedule-meeting',
+ label: t('home.onboard_employees'),
+ description: t('home.onboard_employees_desc'),
+ iconColor: theme.colors.info,
+ icon: (
+
+ ),
+ },
+ ],
+ [t],
+ );
+
+ const getGreeting = () => {
+ const hour = new Date().getHours();
+ if (hour < 12) return t('home.good_morning');
+ if (hour < 18) return t('home.good_afternoon');
+ return t('home.good_evening');
+ };
+
+ // Layout styles --------------------------------------------------------
+ const greetingStyles: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing.sm,
+ color: theme.colors.textPrimary,
+ };
+
+ const greetingTitleStyles: CSSProperties = {
+ ...theme.typography.h1,
+ display: 'flex',
+ alignItems: 'center',
+ gap: theme.spacing.sm,
+ };
+
+ const greetingSubtitleStyles: CSSProperties = {
+ ...theme.typography.body,
+ color: theme.colors.textSecondary,
+ };
+
+ const dividerStyles: CSSProperties = {
+ height: '1px',
+ width: '100%',
+ backgroundColor: theme.colors.border,
+ opacity: 0.6,
+ };
+
+ const statsRowStyles: CSSProperties = {
+ display: 'grid',
+ gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
+ gap: theme.spacing.md,
+ width: '100%',
+ };
+
+ const actionsGridStyles: CSSProperties = {
+ display: 'grid',
+ gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
+ gap: theme.spacing.md,
+ };
+
+ return (
+
+
+
+
+ {getGreeting()}, {userName} ๐
+
+
+ {t('home.highlights_message')}
+
+
+
+
+
+
+
+ {t('home.overview')}
+
+
+
+ {quickStats.map((stat) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+ {quickActions.map((action) => (
+ {
+ // Placeholder for navigation / callbacks
+ console.log(`Triggered action: ${action.id}`);
+ }}
+ />
+ ))}
+
+
+
+
+ );
+};
+
+export default HomePage;
diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx
index 9d8ec52..cd71814 100644
--- a/src/pages/LandingPage.tsx
+++ b/src/pages/LandingPage.tsx
@@ -1,170 +1,3 @@
-import React, { useMemo, useState, useEffect, useCallback } from 'react';
-import { useNavigate } from 'react-router-dom';
-import TopBar from '../components/TopBar';
-import SidebarNav from '../components/SidebarNav';
-import AgentPanel from '../components/AgentPanel';
-import type { Agent } from '../components/AgentPanel';
-import DisplayPanel from '../components/DisplayPanel';
-import ChatDock from '../components/ChatDock';
-import type { ChatMessage } from '../components/ChatDock';
-import CreditsScreen from '../components/CreditsModal';
+import AgentsPage from './AgentsPage';
-const LandingPage: React.FC = () => {
- // Companies for top dropdown
- const companies = ['Pennyzo Labs', 'Pennyzo Retail', 'Pennyzo Finance'];
- const [company, setCompany] = useState(companies[0]);
-
- // Sidebar
- const sidebarItems = [
- { id: 'dashboard', label: 'Dashboard', icon:
๐ },
- { id: 'agents', label: 'Agents', icon:
๐ค },
- { id: 'projects', label: 'Projects', icon:
๐ },
- { id: 'reports', label: 'Reports', icon:
๐ },
- { id: 'settings', label: 'Settings', icon:
โ๏ธ },
- ];
- const [activeNav, setActiveNav] = useState('dashboard');
-
- // Agents
- const agents: Agent[] = useMemo(() => ([
- { id: 'hr', name: 'HR Command', color: 'from-luxury-gold/30 to-brass/30', emoji: '๐ฅ' },
- { id: 'finance', name: 'Finance Sage', color: 'from-bronze/30 to-luxury-gold/30', emoji: '๐ฐ' },
- { id: 'project', name: 'Project Sensei', color: 'from-brass/30 to-bronze/30', emoji: '๐' },
- { id: 'marketing', name: 'Marketing Genius', color: 'from-luxury-gold/30 to-cream/30', emoji: '๐ฏ' },
- { id: 'analytics', name: 'Data Wizard', color: 'from-bronze/30 to-brass/30', emoji: '๐' },
- { id: 'security', name: 'Security Guard', color: 'from-deep-forest/40 to-luxury-gold/30', emoji: '๐ก๏ธ' },
- { id: 'support', name: 'Support Hero', color: 'from-brass/30 to-rich-brown/30', emoji: '๐ง' },
- { id: 'research', name: 'Research Bot', color: 'from-luxury-gold/30 to-bronze/30', emoji: '๐' }
- ]), []);
- const [activeAgent, setActiveAgent] = useState(agents[0].id);
-
- // Chat
- const [messages, setMessages] = useState
([
- { id: 'm1', role: 'assistant', content: 'Hello! Choose an agent and tell me what to do.' },
- ]);
-
- // Credits Modal
- const [showCredits, setShowCredits] = useState(false);
-
- const navigate = useNavigate();
-
- // Listen for credits modal trigger from TopBar
- useEffect(() => {
- const handleOpenCredits = () => {
- setShowCredits(true);
- };
-
- window.addEventListener('openCredits', handleOpenCredits);
- const handleOpenBuyCredits = () => navigate('/buy-credits');
- window.addEventListener('openBuyCredits', handleOpenBuyCredits as EventListener);
- return () => window.removeEventListener('openCredits', handleOpenCredits);
- }, []);
-
- const handleSend = (text: string) => {
- const userMsg: ChatMessage = { id: crypto.randomUUID(), role: 'user', content: text };
- const assistantMsg: ChatMessage = { id: crypto.randomUUID(), role: 'assistant', content: `Got it! The ${agents.find(a=>a.id===activeAgent)?.name} will process: "${text}"` };
- setMessages((prev) => [...prev, userMsg, assistantMsg]);
- };
-
- // Sidebar collapse / hover logic ----------------------------------------
- const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
- try {
- const stored = localStorage.getItem('pz.sidebarCollapsed');
- return stored === 'true';
- } catch { return false; }
- });
- const [sidebarHoverOpen, setSidebarHoverOpen] = useState(false);
-
- const setCollapsed = useCallback((val: boolean) => {
- setSidebarCollapsed(val);
- try { localStorage.setItem('pz.sidebarCollapsed', String(val)); } catch {}
- }, []);
-
- const sidebarOpen = !sidebarCollapsed || sidebarHoverOpen; // actual visual open state
-
- // Compute dynamic left offsets used by ChatDock & content padding
- const sideOffsets = {
- mobile: sidebarOpen ? 'pl-16' : 'pl-0', // matches compact sidebar w-16
- desktop: sidebarOpen ? 'md:pl-56' : 'md:pl-0' // matches md sidebar w-56
- };
- const chatLeftOffset = sidebarOpen ? 'left-16 md:left-56' : 'left-0';
-
- return (
-
- {/* Top bar */}
-
-
- {/* Hover activation strip (only needed when collapsed) */}
- {sidebarCollapsed && (
-
setSidebarHoverOpen(true)}
- onClick={() => setSidebarHoverOpen(true)}
- >
- {/* subtle gradient indicator */}
-
-
- )}
-
- {/* Sliding Sidebar Wrapper */}
-
{ if (sidebarCollapsed) setSidebarHoverOpen(false); }}
- className={`fixed top-16 md:top-20 left-0 h-[calc(100vh-4rem)] md:h-[calc(100vh-5rem)] z-40 transition-transform duration-300 ease-in-out ${sidebarOpen ? 'translate-x-0' : '-translate-x-full'} w-16 md:w-56`}
- >
-
-
- {/* Collapse button */}
-
-
-
-
- {/* Main content (padded when sidebar open) */}
-
-
- {/* Chat dock at bottom */}
-
-
- {/* Credits Screen */}
-
setShowCredits(false)}
- sidebarOpen={sidebarOpen}
- />
-
- );
-};
-
-export default LandingPage;
+export default AgentsPage;
diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx
index b660e3c..57c8b1e 100644
--- a/src/pages/LoginPage.tsx
+++ b/src/pages/LoginPage.tsx
@@ -1,5 +1,6 @@
-import React, { useMemo, useState } from 'react';
+import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
+import bgImage from '../assets/bg pennyzo.png';
const LoginPage: React.FC = () => {
const [email, setEmail] = useState('');
@@ -13,7 +14,7 @@ const LoginPage: React.FC = () => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false);
- navigate('/home');
+ navigate('/dashboard');
}, 2000);
};
@@ -22,73 +23,28 @@ const LoginPage: React.FC = () => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false);
- navigate('/home');
+ navigate('/dashboard');
}, 2000);
};
- // Pre-generate particle positions once to avoid layout thrash on state updates
- const particles = useMemo(
- () => Array.from({ length: 12 }, (_, i) => ({
- id: i,
- left: Math.random() * 100,
- top: Math.random() * 100,
- delay: Math.random() * 3,
- duration: 2 + Math.random() * 3,
- })),
- []
- );
-
return (
- {/* Retro Grid Background */}
-
+ {/* Background Image */}
+
+
+ {/* Dark overlay for better readability */}
+
- {/* Floating Particles (memoized positions & reduced count) */}
-
- {particles.map(p => (
-
- ))}
-
-
- {/* Ambient Gradient Glows */}
-
-
-
-
- {/* outer soft glow */}
-
+
+
{/* inner highlight border */}
-
- {/* top-left sheen */}
-
- {/* subtle texture (dot pattern) */}
-
+
{/* Header */}
diff --git a/src/pages/MorePage.tsx b/src/pages/MorePage.tsx
new file mode 100644
index 0000000..664d9e3
--- /dev/null
+++ b/src/pages/MorePage.tsx
@@ -0,0 +1,173 @@
+import React from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import DashboardLayout from '../layout/DashboardLayout';
+import { SectionCard } from '../components/SectionCard';
+import { useDashboardScaffold } from '../hooks/useDashboardScaffold';
+import ListButton from '../components/ListButton';
+import { QuickActionCard } from '../components/QuickActionCard';
+import PrimaryButton from '../components/PrimaryButton';
+import { theme } from '../theme';
+
+const MorePage: React.FC = () => {
+ const { t } = useTranslation();
+ const { topBarProps } = useDashboardScaffold();
+ const navigate = useNavigate();
+
+ return (
+
+
+
+
+
+
+
{t('more.curate_cockpit')}
+
+ {t('more.cockpit_desc')}
+
+
+
navigate('/profile')} />
+ navigate('/buy-credits')}
+ />
+
+
+
+
+
+
+ }
+ label={t('more.profile_menu')}
+ description={t('more.profile_menu_desc')}
+ onClick={() => navigate('/profile')}
+ />
+
+
+
+ }
+ label={t('more.credits_menu')}
+ description={t('more.credits_menu_desc')}
+ onClick={() => navigate('/buy-credits')}
+ />
+
+
+
+ }
+ label={t('more.generate_menu')}
+ description={t('more.generate_menu_desc')}
+ onClick={() => navigate('/generate')}
+ />
+
+
+
+ }
+ label={t('more.leadership_settings')}
+ description={t('more.leadership_settings_desc')}
+ onClick={() => navigate('/settings')}
+ />
+
+
+
+
+
+
+ }
+ onPress={() => navigate('/more?section=support')}
+ />
+
+
+
+ }
+ onPress={() => navigate('/settings')}
+ />
+
+
+
+ }
+ onPress={() => navigate('/settings')}
+ />
+
+
+
+
+
+
+ {[
+ {
+ title: t('more.cxo_brief'),
+ description: t('more.cxo_brief_desc'),
+ },
+ {
+ title: t('more.investor_dataroom'),
+ description: t('more.investor_desc'),
+ },
+ {
+ title: t('more.brand_voice'),
+ description: t('more.brand_voice_desc'),
+ },
+ {
+ title: t('more.hiring_sprint'),
+ description: t('more.hiring_desc'),
+ },
+ {
+ title: t('more.board_readiness'),
+ description: t('more.board_desc'),
+ },
+ {
+ title: t('more.global_expansion'),
+ description: t('more.expansion_desc'),
+ },
+ ].map((item) => (
+
+
{item.title}
+
{item.description}
+
navigate('/tasks')}
+ />
+
+ ))}
+
+
+
+
+ );
+};
+
+export default MorePage;
diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx
index e69de29..09422bb 100644
--- a/src/pages/ProfilePage.tsx
+++ b/src/pages/ProfilePage.tsx
@@ -0,0 +1,377 @@
+import React, { useMemo, useState, useRef } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import DashboardLayout from '../layout/DashboardLayout';
+import { useDashboardScaffold } from '../hooks/useDashboardScaffold';
+import { SectionCard } from '../components/SectionCard';
+import ListButton from '../components/ListButton';
+import PrimaryButton from '../components/PrimaryButton';
+import { useUser } from '../context/UserContext';
+
+type PreferenceKey = 'weeklyDigest' | 'aiBriefings' | 'loginAlerts' | 'dataExports';
+
+const ProfilePage: React.FC = () => {
+ const { topBarProps } = useDashboardScaffold();
+ const { t } = useTranslation();
+ const { profile, updateProfile, updateProfileImage } = useUser();
+ const navigate = useNavigate();
+ const fileInputRef = useRef
(null);
+
+ const [saving, setSaving] = useState(false);
+ const [localProfile, setLocalProfile] = useState(profile);
+ const [preferences, setPreferences] = useState>({
+ weeklyDigest: true,
+ aiBriefings: true,
+ loginAlerts: true,
+ dataExports: false,
+ });
+
+ const togglePreference = (key: PreferenceKey) => {
+ setPreferences((prev) => ({ ...prev, [key]: !prev[key] }));
+ };
+
+ const handleProfileChange = (field: keyof typeof localProfile) => (event: React.ChangeEvent) => {
+ setLocalProfile((prev) => ({ ...prev, [field]: event.target.value }));
+ };
+
+ const handleSaveProfile = (event: React.FormEvent) => {
+ event.preventDefault();
+ setSaving(true);
+ updateProfile(localProfile);
+ window.setTimeout(() => setSaving(false), 1200);
+ };
+
+ const handleProfileImageChange = (event: React.ChangeEvent) => {
+ const file = event.target.files?.[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const result = e.target?.result as string;
+ updateProfileImage(result);
+ };
+ reader.readAsDataURL(file);
+ }
+ };
+
+ const scrollToSection = (sectionId: string) => {
+ const element = document.getElementById(sectionId);
+ if (element) {
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+ };
+
+ const menuItems = useMemo(
+ () => [
+ {
+ id: 'user-info',
+ label: t('profile.user_info'),
+ description: t('profile.user_info_desc'),
+ icon: (
+
+ ),
+ action: () => scrollToSection('profile-details'),
+ },
+ {
+ id: 'subscription',
+ label: t('profile.subscription'),
+ description: t('profile.subscription_desc'),
+ icon: (
+
+ ),
+ action: () => navigate('/buy-credits'),
+ },
+ {
+ id: 'integrations',
+ label: t('profile.integrations'),
+ description: t('profile.integrations_desc'),
+ icon: (
+
+ ),
+ action: () => scrollToSection('integration-preferences'),
+ },
+ {
+ id: 'settings',
+ label: t('profile.settings'),
+ description: t('profile.settings_desc'),
+ icon: (
+
+ ),
+ action: () => navigate('/settings'),
+ },
+ {
+ id: 'help',
+ label: t('profile.help_support'),
+ description: t('profile.help_support_desc'),
+ icon: (
+
+ ),
+ action: () => navigate('/more?section=support'),
+ },
+ {
+ id: 'logout',
+ label: t('profile.logout'),
+ description: t('profile.logout_desc'),
+ icon: (
+
+ ),
+ action: () => navigate('/login'),
+ },
+ ],
+ [navigate, t],
+ );
+
+ return (
+
+
+
+
+
+
+
fileInputRef.current?.click()}
+ >
+ {profile.profileImage ? (
+

+ ) : (
+
+
+ {profile.name.charAt(0).toUpperCase()}
+
+
+ )}
+
+
+
+ Live
+
+
+
+
+
{profile.name}
+
{profile.role}
+
+
+ Member since 2022
+
+
+ Pennyzo Labs HQ
+
+
+
+
+
+
+
+
+
+ }
+ onClick={() => fileInputRef.current?.click()}
+ />
+
+
+
+ }
+ onClick={() => navigate('/settings')}
+ />
+
+
+
+
+
+
+ {menuItems.map((item) => (
+
+ ))}
+
+
+
+
+
+
+
+
+ {(
+ [
+ { key: 'weeklyDigest', label: 'Weekly performance digest', description: 'Sunday 9 AM email recapping pipeline momentum' },
+ { key: 'aiBriefings', label: 'Concierge AI briefings', description: 'Instant push when high priority tasks get unblocked' },
+ { key: 'loginAlerts', label: 'Login alerts', description: 'Notify me when a new device signs in to Pennyzo' },
+ { key: 'dataExports', label: 'Auto data exports', description: 'Send CSV exports to finance every Friday evening' },
+ ] satisfies { key: PreferenceKey; label: string; description: string }[]
+ ).map((item) => (
+
+
+
+
{item.label}
+
{item.description}
+
+
+
+
+ ))}
+
+
+
+
+
+ );
+};
+
+export default ProfilePage;
diff --git a/src/pages/ResetPasswordOtpPage.tsx b/src/pages/ResetPasswordOtpPage.tsx
index 3d2dd08..d1475bd 100644
--- a/src/pages/ResetPasswordOtpPage.tsx
+++ b/src/pages/ResetPasswordOtpPage.tsx
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
+import bgImage from '../assets/bg pennyzo.png';
const ResetPasswordOtpPage: React.FC = () => {
const [otp, setOtp] = useState(['', '', '', '', '', '']);
@@ -22,19 +23,23 @@ const ResetPasswordOtpPage: React.FC = () => {
};
return (
-
- {/* Ambient Gradient Glows */}
-
+
+ {/* Background Image */}
+
+
+ {/* Dark overlay for better readability */}
+
-
-
-
-
-
-
+
+
+ {/* inner highlight border */}
+
diff --git a/src/pages/ResetPasswordTokenPage.tsx b/src/pages/ResetPasswordTokenPage.tsx
index fc59449..1db073f 100644
--- a/src/pages/ResetPasswordTokenPage.tsx
+++ b/src/pages/ResetPasswordTokenPage.tsx
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
+import bgImage from '../assets/bg pennyzo.png';
const ResetPasswordTokenPage: React.FC = () => {
const [password, setPassword] = useState('');
@@ -17,19 +18,23 @@ const ResetPasswordTokenPage: React.FC = () => {
};
return (
-
- {/* Ambient Gradient Glows */}
-
+
+ {/* Background Image */}
+
+
+ {/* Dark overlay for better readability */}
+
-
-
-
-
-
-
+
+
+ {/* inner highlight border */}
+
diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx
new file mode 100644
index 0000000..5208bc7
--- /dev/null
+++ b/src/pages/SettingsPage.tsx
@@ -0,0 +1,301 @@
+import React, { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import DashboardLayout from '../layout/DashboardLayout';
+import { useDashboardScaffold } from '../hooks/useDashboardScaffold';
+import { SectionCard } from '../components/SectionCard';
+import PrimaryButton from '../components/PrimaryButton';
+
+type ToggleGroup
= Record;
+
+type SecurityToggle = 'twoFactor' | 'deviceApprovals' | 'sessionTimeout';
+type NotificationToggle = 'email' | 'sms' | 'whatsapp' | 'slack';
+
+type WorkspacePreference = {
+ timezone: string;
+ language: string;
+ weekStartsOn: string;
+};
+
+const SettingsPage: React.FC = () => {
+ const { topBarProps } = useDashboardScaffold();
+ const { t } = useTranslation();
+
+ const [security, setSecurity] = useState>({
+ twoFactor: true,
+ deviceApprovals: true,
+ sessionTimeout: false,
+ });
+
+ const [notifications, setNotifications] = useState>({
+ email: true,
+ sms: false,
+ whatsapp: true,
+ slack: true,
+ });
+
+ const [workspace, setWorkspace] = useState({
+ timezone: 'Asia/Kolkata (IST)',
+ language: 'English',
+ weekStartsOn: 'Monday',
+ });
+
+ const toggleSecurity = (key: SecurityToggle) => {
+ setSecurity((prev) => ({ ...prev, [key]: !prev[key] }));
+ };
+
+ const toggleNotification = (key: NotificationToggle) => {
+ setNotifications((prev) => ({ ...prev, [key]: !prev[key] }));
+ };
+
+ const handleWorkspaceChange = (field: keyof WorkspacePreference) => (event: React.ChangeEvent) => {
+ setWorkspace((prev) => ({ ...prev, [field]: event.target.value }));
+ };
+
+ return (
+
+
+
+
{t('settings.workspace_settings')}
+
+ {t('settings.workspace_settings_desc')}
+
+
+
+
+
+ {(
+ [
+ {
+ key: 'twoFactor' as SecurityToggle,
+ label: 'Two-factor authentication',
+ description: 'Require OTP or authenticator approval on every new login.',
+ },
+ {
+ key: 'deviceApprovals' as SecurityToggle,
+ label: 'Device approvals',
+ description: 'Concierge pings admins when a new laptop or phone signs in.',
+ },
+ {
+ key: 'sessionTimeout' as SecurityToggle,
+ label: '90 minute session timeout',
+ description: 'Auto-sign out idle consoles to protect sensitive briefs.',
+ },
+ ]
+ ).map((item) => (
+
+
+
+
{item.label}
+
{item.description}
+
+
+
+
+ ))}
+
+
+
+
+
Admin alerting
+
+ Pennyzo will escalate high-risk sign-ins to {security.deviceApprovals ? 'leadership Slack' : 'email only'} and include geo-location snapshots.
+
+
+
+
+
+
+
+
+ {(
+ [
+ {
+ key: 'email' as NotificationToggle,
+ label: 'Email digests',
+ description: 'Daily summary of agent runs, documents prepared, and credit snapshots.',
+ },
+ {
+ key: 'sms' as NotificationToggle,
+ label: 'SMS nudges',
+ description: 'Instant message when concierge requires leadership approval.',
+ },
+ {
+ key: 'whatsapp' as NotificationToggle,
+ label: 'WhatsApp alerts',
+ description: 'Pipeline wins and budget approvals piped to the CXO group.',
+ },
+ {
+ key: 'slack' as NotificationToggle,
+ label: 'Slack channel updates',
+ description: 'Live feed in #pennyzo-warroom of agent completions and credit usage.',
+ },
+ ]
+ ).map((item) => (
+
+
+
+
{item.label}
+
{item.description}
+
+
+
+
+ ))}
+
+
+
+
+
+
Escalation window
+
Escalate to leadership if concierge waits more than 4 hours for approvals.
+
+
+
+
+
+
+
+
+
+
+
+
+ Preferred export format
+
+ {['CSV', 'Excel', 'Google Sheets'].map((option) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+ {[{
+ name: 'Slack',
+ status: 'Live',
+ description: 'Real-time agent completions in #pennyzo-warroom channel.',
+ }, {
+ name: 'HubSpot',
+ status: 'Live',
+ description: 'Sync CRM deals to concierge for follow-up summaries.',
+ }, {
+ name: 'Google Drive',
+ status: 'Pending approval',
+ description: 'Route auto-prepped decks to investor-ready folders.',
+ }, {
+ name: 'SAP SuccessFactors',
+ status: 'Live',
+ description: 'Push payroll exports and compliance letters on schedule.',
+ }].map((integration) => (
+
+
+
{integration.name}
+
+ {integration.status}
+
+
+
{integration.description}
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export default SettingsPage;
diff --git a/src/pages/TasksPage.tsx b/src/pages/TasksPage.tsx
new file mode 100644
index 0000000..66000f5
--- /dev/null
+++ b/src/pages/TasksPage.tsx
@@ -0,0 +1,491 @@
+import React, { useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useParams, useNavigate } from 'react-router-dom';
+import DashboardLayout from '../layout/DashboardLayout';
+import { useDashboardScaffold } from '../hooks/useDashboardScaffold';
+import { StatCard } from '../components/StatCard';
+import { SummaryCard } from '../components/SummaryCard';
+import { QuickActionCard } from '../components/QuickActionCard';
+import { SectionCard } from '../components/SectionCard';
+import { theme } from '../theme';
+
+interface StatDefinition {
+ id: string;
+ label: string;
+ value: string;
+ iconColor: string;
+ trend?: {
+ value: string;
+ isPositive: boolean;
+ };
+ icon: React.ReactNode;
+}
+
+interface SummaryItem {
+ id: string;
+ label: string;
+ count: number;
+ iconColor: string;
+ icon: React.ReactNode;
+}
+
+interface QuickActionDefinition {
+ id: string;
+ label: string;
+ description: string;
+ iconColor: string;
+ icon: React.ReactNode;
+}
+
+type TaskTabId = 'hr' | 'crm' | 'strategy';
+
+const TasksPage: React.FC = () => {
+ const { topBarProps } = useDashboardScaffold();
+ const { t } = useTranslation();
+ const { section } = useParams<{ section?: string }>();
+ const navigate = useNavigate();
+ const activeTab = (section || 'hr') as TaskTabId;
+
+ // Set active tab based on URL parameter
+ useEffect(() => {
+ if (!section || !['hr', 'crm', 'strategy'].includes(section)) {
+ // Redirect to HR if no valid section specified
+ navigate('/tasks/hr', { replace: true });
+ }
+ }, [section, navigate]);
+
+ const statCards: Record = {
+ hr: [
+ {
+ id: 'headcount',
+ label: t('tasks.headcount'),
+ value: '178',
+ iconColor: theme.colors.highlight,
+ trend: { value: '+12', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'present',
+ label: t('tasks.present_today'),
+ value: '161',
+ iconColor: theme.colors.success,
+ trend: { value: '91%', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'on-leave',
+ label: t('tasks.on_leave'),
+ value: '12',
+ iconColor: theme.colors.warning,
+ trend: { value: '-3', isPositive: false },
+ icon: (
+
+ ),
+ },
+ ],
+ crm: [
+ {
+ id: 'pipeline',
+ label: t('tasks.pipeline_value'),
+ value: 'โน3.1Cr',
+ iconColor: theme.colors.highlight,
+ trend: { value: '+22%', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'conversion',
+ label: t('tasks.conversion_rate'),
+ value: '34%',
+ iconColor: theme.colors.success,
+ trend: { value: '+6%', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'escalations',
+ label: t('tasks.escalations'),
+ value: '7',
+ iconColor: theme.colors.error,
+ trend: { value: '+2', isPositive: false },
+ icon: (
+
+ ),
+ },
+ ],
+ strategy: [
+ {
+ id: 'okrs',
+ label: t('tasks.okrs_on_track'),
+ value: '82%',
+ iconColor: theme.colors.highlight,
+ trend: { value: '+5%', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'initiatives',
+ label: t('tasks.initiatives'),
+ value: '24',
+ iconColor: theme.colors.info,
+ trend: { value: '+3', isPositive: true },
+ icon: (
+
+ ),
+ },
+ {
+ id: 'markets',
+ label: t('tasks.markets_assessed'),
+ value: '11',
+ iconColor: theme.colors.warning,
+ trend: { value: '+2', isPositive: true },
+ icon: (
+
+ ),
+ },
+ ],
+ };
+
+ const summaryItems: Record = {
+ hr: [
+ {
+ id: 'leadership',
+ label: t('tasks.leadership_approvals'),
+ count: 3,
+ iconColor: theme.colors.highlight,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'product',
+ label: t('tasks.product_onboarding'),
+ count: 8,
+ iconColor: theme.colors.info,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'operations',
+ label: t('tasks.operations_followups'),
+ count: 6,
+ iconColor: theme.colors.warning,
+ icon: (
+
+ ),
+ },
+ ],
+ crm: [
+ {
+ id: 'follow-ups',
+ label: t('tasks.followups_today'),
+ count: 9,
+ iconColor: theme.colors.highlight,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'contracts',
+ label: t('tasks.contracts_in_review'),
+ count: 4,
+ iconColor: theme.colors.info,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'responses',
+ label: t('tasks.awaiting_responses'),
+ count: 5,
+ iconColor: theme.colors.warning,
+ icon: (
+
+ ),
+ },
+ ],
+ strategy: [
+ {
+ id: 'strengths',
+ label: t('tasks.strengths_reviewed'),
+ count: 6,
+ iconColor: theme.colors.success,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'opportunities',
+ label: t('tasks.opportunities'),
+ count: 3,
+ iconColor: theme.colors.info,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'risks',
+ label: t('tasks.risks_tracked'),
+ count: 4,
+ iconColor: theme.colors.error,
+ icon: (
+
+ ),
+ },
+ ],
+ };
+
+ const quickActions: Record = {
+ hr: [
+ {
+ id: 'payroll',
+ label: t('tasks.process_payroll'),
+ description: t('tasks.process_payroll_desc'),
+ iconColor: theme.colors.highlight,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'salary-slips',
+ label: t('tasks.send_salary_slips'),
+ description: t('tasks.send_salary_slips_desc'),
+ iconColor: theme.colors.info,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'reports',
+ label: t('tasks.generate_reports'),
+ description: t('tasks.generate_reports_desc'),
+ iconColor: theme.colors.warning,
+ icon: (
+
+ ),
+ },
+ ],
+ crm: [
+ {
+ id: 'concierge',
+ label: t('tasks.client_concierge'),
+ description: t('tasks.client_concierge_desc'),
+ iconColor: theme.colors.info,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'launch-campaign',
+ label: t('tasks.launch_campaign'),
+ description: t('tasks.launch_campaign_desc'),
+ iconColor: theme.colors.highlight,
+ icon: (
+
+ ),
+ },
+ ],
+ strategy: [
+ {
+ id: 'executive-summary',
+ label: t('tasks.executive_summary'),
+ description: t('tasks.executive_summary_desc'),
+ iconColor: theme.colors.highlight,
+ icon: (
+
+ ),
+ },
+ {
+ id: 'steering',
+ label: t('tasks.steering_committee'),
+ description: t('tasks.steering_committee_desc'),
+ iconColor: theme.colors.info,
+ icon: (
+
+ ),
+ },
+ ],
+ };
+
+ const insightsCopy: Record = {
+ hr: {
+ icon: (
+
+ ),
+ title: t('tasks.retention_watchlist'),
+ body: t('tasks.retention_watchlist_desc'),
+ },
+ crm: {
+ icon: (
+
+ ),
+ title: t('tasks.pipeline_sentiment'),
+ body: t('tasks.pipeline_sentiment_desc'),
+ },
+ strategy: {
+ icon: (
+
+ ),
+ title: t('tasks.swot_briefing'),
+ body: t('tasks.swot_briefing_desc'),
+ },
+ };
+
+ const swotMatrix = {
+ strengths: t('tasks.swot_strengths'),
+ weaknesses: t('tasks.swot_weaknesses'),
+ opportunities: t('tasks.swot_opportunities'),
+ threats: t('tasks.swot_threats'),
+ };
+
+ // Get page title based on active tab
+ const getPageTitle = () => {
+ const titles = {
+ hr: t('tasks.hr'),
+ crm: t('tasks.crm'),
+ strategy: t('tasks.strategy'),
+ };
+ return titles[activeTab] || titles.hr;
+ };
+
+ const getPageSubtitle = () => {
+ const subtitles = {
+ hr: 'Monitor headcount, attendance, and HR workflows in real-time',
+ crm: 'Track pipeline, conversion rates, and customer engagement',
+ strategy: 'Review OKRs, initiatives, and strategic market insights',
+ };
+ return subtitles[activeTab] || subtitles.hr;
+ };
+
+ return (
+
+
+ {/* Page Header */}
+
+
{getPageTitle()}
+
+ {getPageSubtitle()}
+
+
+
+
+
+ {statCards[activeTab].map((stat) => (
+
+ ))}
+
+
+
+
+
+
+
+
+ {insightsCopy[activeTab].icon}
+
+
+
+ {insightsCopy[activeTab].title}
+
+
+ {insightsCopy[activeTab].body}
+
+
+
+
+
+
+
+ {quickActions[activeTab].map((action) => (
+ console.log(`Action: ${action.id}`)} />
+ ))}
+
+
+ {activeTab === 'strategy' && (
+
+ {Object.entries(swotMatrix).map(([key, value]) => (
+
+
+ {value}
+
+
+ ))}
+
+ )}
+
+
+
+ );
+};
+
+export default TasksPage;
diff --git a/src/styles/shared.css b/src/styles/shared.css
new file mode 100644
index 0000000..83b673c
--- /dev/null
+++ b/src/styles/shared.css
@@ -0,0 +1,266 @@
+/**
+ * Shared Component Styles
+ * Ensures uniform design across all navigation, buttons, and interactive elements
+ */
+
+/* ===== NAVIGATION ITEMS ===== */
+.nav-item {
+ @apply flex items-center gap-3 h-12 rounded-xl px-4 pl-5;
+ @apply cursor-pointer relative;
+ @apply transition-all duration-200 ease-in-out;
+}
+
+.nav-item-inactive {
+ @apply bg-transparent text-[#C8D9D4];
+}
+
+.nav-item-inactive:hover {
+ @apply bg-[rgba(203,169,81,0.12)] text-[#F2E5B5];
+}
+
+.nav-item-active {
+ @apply bg-[rgba(203,169,81,0.18)];
+ @apply text-[#F2E5B5];
+}
+
+/* Active indicator bar */
+.nav-item-indicator {
+ @apply absolute left-0 top-1/2 -translate-y-1/2;
+ @apply w-1 h-8 rounded-r-full;
+ background: linear-gradient(180deg, rgba(203, 169, 81, 1) 0%, rgba(203, 169, 81, 0.8) 100%);
+ box-shadow: 0 0 12px rgba(203, 169, 81, 0.6);
+}
+
+/* Icon wrapper */
+.nav-icon {
+ @apply w-10 h-10 rounded-lg flex items-center justify-center;
+ @apply transition-all duration-200 flex-shrink-0;
+}
+
+.nav-icon-inactive {
+ @apply bg-[rgba(108,125,120,0.15)] text-[#8A9D97];
+}
+
+.nav-icon-inactive:hover {
+ @apply bg-[rgba(203,169,81,0.2)] text-[#CBA951];
+}
+
+.nav-icon-active {
+ @apply bg-[rgba(203,169,81,0.3)] text-[#F2E5B5];
+ box-shadow: 0 0 8px rgba(203, 169, 81, 0.3);
+}
+
+/* Label text */
+.nav-label {
+ @apply font-montserrat text-[13px] tracking-[0.5px] uppercase;
+ @apply transition-colors duration-200;
+}
+
+.nav-label-inactive {
+ @apply text-[#C8D9D4] font-semibold;
+}
+
+.nav-label-active {
+ @apply text-[#F8F5EB] font-bold;
+}
+
+/* ===== CARDS & CONTAINERS ===== */
+.pennyzo-card {
+ @apply rounded-2xl border backdrop-blur-xl;
+ @apply bg-gradient-to-br from-[rgba(6,35,31,0.95)] to-[rgba(11,49,42,0.88)];
+ @apply border-[rgba(203,169,81,0.25)];
+ @apply shadow-[0_12px_28px_rgba(0,0,0,0.35)];
+ @apply transition-all duration-300;
+}
+
+.pennyzo-card:hover {
+ @apply border-[rgba(203,169,81,0.4)];
+ @apply shadow-[0_16px_36px_rgba(203,169,81,0.15)];
+}
+
+/* ===== BUTTONS ===== */
+.pennyzo-btn {
+ @apply inline-flex items-center justify-center gap-2;
+ @apply uppercase tracking-[0.08em] font-montserrat font-extrabold;
+ @apply rounded-full transition-all duration-150 ease-in-out;
+ @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2;
+ @apply focus-visible:ring-luxury-gold/70 focus-visible:ring-offset-deep-forest;
+ @apply min-h-[56px] px-6 text-base;
+}
+
+.pennyzo-btn-primary {
+ @apply bg-luxury-gold text-deep-forest border-2 border-luxury-gold/80;
+ @apply shadow-[0_12px_24px_rgba(203,169,81,0.35)];
+ @apply hover:opacity-90 active:scale-[0.98] active:opacity-85;
+}
+
+.pennyzo-btn-secondary {
+ @apply bg-gradient-to-br from-forest-green/80 to-deep-forest/80;
+ @apply text-cream border-2 border-luxury-gold/60;
+ @apply shadow-[0_12px_24px_rgba(203,169,81,0.35)];
+ @apply hover:opacity-90 active:scale-[0.98] active:opacity-85;
+}
+
+.pennyzo-btn-ghost {
+ @apply bg-transparent text-cream border-2 border-luxury-gold/40;
+ @apply hover:bg-luxury-gold/10 hover:border-luxury-gold/60;
+ @apply active:scale-[0.98];
+}
+
+/* ===== LIST BUTTONS ===== */
+.pennyzo-list-btn {
+ @apply flex w-full items-center gap-4 rounded-2xl cursor-pointer;
+ @apply border-2 border-luxury-gold/30 bg-[#0B312A];
+ @apply px-5 py-4 text-left transition-all duration-200;
+ @apply hover:-translate-y-[1px] hover:border-luxury-gold/60;
+ @apply hover:bg-[#0B312A]/90 hover:shadow-[0_14px_28px_rgba(203,169,81,0.22)];
+ @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-luxury-gold/60;
+}
+
+.pennyzo-list-btn-icon {
+ @apply flex h-10 w-10 items-center justify-center rounded-xl;
+ @apply bg-[#1A4036]/80 text-luxury-gold text-lg flex-shrink-0;
+}
+
+.pennyzo-list-btn-content {
+ @apply flex flex-1 flex-col gap-1;
+}
+
+.pennyzo-list-btn-label {
+ @apply font-montserrat text-sm font-semibold uppercase tracking-[0.18em] text-cream;
+}
+
+.pennyzo-list-btn-description {
+ @apply text-xs text-cream/60;
+}
+
+.pennyzo-list-btn-chevron {
+ @apply text-luxury-gold/70 transition-transform duration-200;
+}
+
+.pennyzo-list-btn:hover .pennyzo-list-btn-chevron {
+ @apply translate-x-1;
+}
+
+/* ===== ACTION CARDS ===== */
+.pennyzo-action-card {
+ @apply flex items-center gap-4 rounded-2xl cursor-pointer;
+ @apply bg-[#0B312A] border-2 border-luxury-gold/25;
+ @apply px-5 py-4 transition-all duration-200;
+ @apply hover:-translate-y-[2px] hover:border-luxury-gold/40;
+ @apply hover:shadow-[0_12px_28px_rgba(203,169,81,0.18)];
+ @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-luxury-gold/60;
+}
+
+.pennyzo-action-card-icon {
+ @apply relative w-14 h-14 rounded-xl flex items-center justify-center;
+ @apply flex-shrink-0 transition-all duration-200;
+}
+
+.pennyzo-action-card-badge {
+ @apply absolute -top-1.5 -right-1.5 min-w-[24px] h-6 px-1.5;
+ @apply rounded-full bg-red-500 border-2 border-deep-forest;
+ @apply flex items-center justify-center;
+ @apply text-white text-[11px] font-bold;
+ @apply shadow-[0_0_12px_rgba(239,68,68,0.4)];
+}
+
+.pennyzo-action-card-content {
+ @apply flex-1 min-w-0;
+}
+
+.pennyzo-action-card-label {
+ @apply font-montserrat text-sm font-bold text-cream;
+ @apply tracking-wide mb-1;
+}
+
+.pennyzo-action-card-description {
+ @apply text-xs text-cream/60;
+}
+
+.pennyzo-action-card-chevron {
+ @apply flex-shrink-0 text-luxury-gold text-2xl;
+}
+
+/* ===== INPUTS & FORMS ===== */
+.pennyzo-input {
+ @apply w-full px-4 py-3 rounded-xl;
+ @apply bg-deep-forest/70 border border-luxury-gold/40;
+ @apply text-cream placeholder:text-cream/40;
+ @apply focus:outline-none focus:ring-2 focus:ring-luxury-gold/40;
+ @apply transition-all duration-200;
+}
+
+.pennyzo-select {
+ @apply w-full px-4 py-3 rounded-xl;
+ @apply bg-deep-forest/70 border border-luxury-gold/40;
+ @apply text-cream;
+ @apply focus:outline-none focus:ring-2 focus:ring-luxury-gold/40;
+ @apply transition-all duration-200;
+}
+
+/* ===== DROPDOWNS & MODALS ===== */
+.pennyzo-dropdown {
+ @apply bg-gradient-to-br from-deep-forest/95 to-rich-brown/90;
+ @apply backdrop-blur-xl border border-luxury-gold/30;
+ @apply rounded-2xl shadow-[0_20px_40px_rgba(0,0,0,0.4)];
+ @apply animate-fadeInUp;
+}
+
+.pennyzo-dropdown-item {
+ @apply flex items-center gap-3 px-3 py-2.5 rounded-xl;
+ @apply text-cream/90 bg-deep-forest/50;
+ @apply border border-transparent;
+ @apply hover:border-luxury-gold/30 hover:bg-luxury-gold/10 hover:text-cream;
+ @apply transition-all duration-200;
+}
+
+/* ===== BADGES ===== */
+.pennyzo-badge {
+ @apply inline-flex items-center justify-center;
+ @apply min-w-[24px] h-6 px-2 rounded-full;
+ @apply bg-red-500 text-white text-xs font-bold;
+ @apply shadow-[0_0_12px_rgba(239,68,68,0.4)];
+}
+
+/* ===== DIVIDERS ===== */
+.pennyzo-divider {
+ @apply border-t border-[rgba(203,169,81,0.12)];
+}
+
+/* ===== SECTION HEADERS ===== */
+.pennyzo-section-label {
+ @apply font-montserrat text-[11px] tracking-[0.8px] uppercase;
+ @apply text-[rgba(200,217,212,0.5)] font-bold;
+}
+
+/* ===== SCROLLBARS ===== */
+.pennyzo-scroll::-webkit-scrollbar {
+ @apply w-1;
+}
+
+.pennyzo-scroll::-webkit-scrollbar-track {
+ @apply bg-transparent;
+}
+
+.pennyzo-scroll::-webkit-scrollbar-thumb {
+ @apply bg-[rgba(203,169,81,0.25)] rounded;
+}
+
+.pennyzo-scroll::-webkit-scrollbar-thumb:hover {
+ @apply bg-[rgba(203,169,81,0.4)];
+}
+
+/* ===== TRANSITIONS ===== */
+.pennyzo-transition {
+ @apply transition-all duration-200 ease-in-out;
+}
+
+.pennyzo-transition-slow {
+ @apply transition-all duration-300 ease-in-out;
+}
+
+/* ===== FOCUS STATES ===== */
+.pennyzo-focus:focus-visible {
+ @apply outline-none ring-2 ring-luxury-gold/70 ring-offset-2 ring-offset-deep-forest;
+}
diff --git a/src/theme/index.ts b/src/theme/index.ts
new file mode 100644
index 0000000..4702a38
--- /dev/null
+++ b/src/theme/index.ts
@@ -0,0 +1,325 @@
+/**
+ * Pennyzo Design System - Theme Configuration
+ *
+ * Complete theme system with colors, typography, spacing, shadows,
+ * border radius, and icon sizes based on the Pennyzo mobile app design.
+ */
+
+// ============================================
+// COLOR PALETTE
+// ============================================
+
+export const colors = {
+ // Background colors - Deep emerald/teal theme
+ background: '#041815', // Primary dark background
+ surface: '#06231F', // Card/container surfaces
+ surfaceAlt: '#0B312A', // Alternative surface (lighter)
+ accent: '#1A4036', // Accent containers
+ primary: '#0E5F4F', // Primary emerald
+
+ // Highlight colors - Luxurious gold
+ highlight: '#CBA951', // Primary gold
+ highlightAlt: '#F2E5B5', // Light cream gold
+
+ // Text colors
+ textPrimary: '#F8F5EB', // Primary text (cream white)
+ textSecondary: '#C8D9D4', // Secondary text (light teal)
+
+ // Borders & decorative
+ border: 'rgba(203, 169, 81, 0.35)', // Subtle gold border
+ borderStrong: 'rgba(203, 169, 81, 0.6)', // Strong gold border
+
+ // Shadows & effects
+ shadow: 'rgba(0, 0, 0, 0.4)',
+ shadowLight: 'rgba(0, 0, 0, 0.2)',
+ glowGold: 'rgba(203, 169, 81, 0.25)',
+ glowEmerald: 'rgba(14, 95, 79, 0.3)',
+
+ // Special colors
+ tabIconInactive: '#6C7D78',
+ overlayDark: 'rgba(4, 24, 21, 0.6)',
+
+ // Status colors
+ success: '#4ADE80', // Green (positive metrics, active status)
+ warning: '#FBBF24', // Yellow (warnings, idle status)
+ error: '#EF4444', // Red (alerts, busy status)
+ info: '#3B82F6', // Blue (informational)
+} as const;
+
+// ============================================
+// TYPOGRAPHY SYSTEM
+// ============================================
+
+export const typography = {
+ // Display - Hero sections
+ display: {
+ fontSize: '38px',
+ fontWeight: 900,
+ letterSpacing: '1px',
+ lineHeight: '46px',
+ },
+
+ // Headings
+ h1: {
+ fontSize: '32px',
+ fontWeight: 800,
+ letterSpacing: '0.8px',
+ lineHeight: '40px',
+ },
+ h2: {
+ fontSize: '26px',
+ fontWeight: 800,
+ letterSpacing: '0.6px',
+ lineHeight: '34px',
+ },
+ h3: {
+ fontSize: '22px',
+ fontWeight: 700,
+ letterSpacing: '0.5px',
+ lineHeight: '30px',
+ },
+ h4: {
+ fontSize: '18px',
+ fontWeight: 700,
+ letterSpacing: '0.4px',
+ lineHeight: '26px',
+ },
+
+ // Body text
+ body: {
+ fontSize: '15px',
+ fontWeight: 400,
+ letterSpacing: '0.2px',
+ lineHeight: '22px',
+ },
+ bodyBold: {
+ fontSize: '15px',
+ fontWeight: 700,
+ letterSpacing: '0.3px',
+ lineHeight: '22px',
+ },
+
+ // Supporting text
+ caption: {
+ fontSize: '13px',
+ fontWeight: 600,
+ letterSpacing: '0.2px',
+ lineHeight: '18px',
+ },
+
+ // Buttons
+ button: {
+ fontSize: '16px',
+ fontWeight: 800,
+ letterSpacing: '0.8px',
+ textTransform: 'uppercase' as const,
+ },
+ buttonSmall: {
+ fontSize: '14px',
+ fontWeight: 700,
+ letterSpacing: '0.6px',
+ textTransform: 'uppercase' as const,
+ },
+
+ // Tab labels
+ tabLabel: {
+ fontSize: '11px',
+ fontWeight: 700,
+ letterSpacing: '0.5px',
+ textTransform: 'uppercase' as const,
+ },
+} as const;
+
+// ============================================
+// SPACING SYSTEM
+// ============================================
+
+export const spacing = {
+ xs: '4px',
+ sm: '8px',
+ md: '12px',
+ lg: '16px',
+ xl: '20px',
+ xxl: '24px',
+ xxxl: '32px',
+ huge: '40px',
+ massive: '48px',
+} as const;
+
+// ============================================
+// BORDER RADIUS
+// ============================================
+
+export const borderRadius = {
+ sm: '8px',
+ md: '12px',
+ lg: '16px',
+ xl: '20px',
+ xxl: '24px',
+ round: '999px',
+} as const;
+
+// ============================================
+// ICON SIZES
+// ============================================
+
+export const iconSizes = {
+ xs: '16px',
+ sm: '20px',
+ md: '24px',
+ lg: '28px',
+ xl: '32px',
+ xxl: '40px',
+} as const;
+
+// ============================================
+// SHADOW SYSTEM
+// ============================================
+
+export const shadows = {
+ sm: '0 4px 8px rgba(0, 0, 0, 0.2)',
+ md: '0 8px 12px rgba(0, 0, 0, 0.3)',
+ lg: '0 12px 16px rgba(0, 0, 0, 0.4)',
+ xl: '0 16px 20px rgba(0, 0, 0, 0.5)',
+ glow: '0 8px 12px rgba(203, 169, 81, 0.4)',
+ glowStrong: '0 12px 20px rgba(203, 169, 81, 0.6)',
+} as const;
+
+// ============================================
+// ANIMATION DURATIONS
+// ============================================
+
+export const animations = {
+ fast: '150ms',
+ normal: '200ms',
+ slow: '300ms',
+ fadeOut: '140ms',
+ fadeIn: '180ms',
+ pulse: '1500ms',
+ glow: '2000ms',
+} as const;
+
+// ============================================
+// BREAKPOINTS
+// ============================================
+
+export const breakpoints = {
+ mobile: '0px',
+ tablet: '641px',
+ desktop: '1025px',
+} as const;
+
+// ============================================
+// Z-INDEX LAYERS
+// ============================================
+
+export const zIndex = {
+ base: 0,
+ dropdown: 100,
+ sticky: 200,
+ fixed: 300,
+ modalBackdrop: 400,
+ modal: 500,
+ popover: 600,
+ tooltip: 700,
+} as const;
+
+// ============================================
+// COMPONENT-SPECIFIC DIMENSIONS
+// ============================================
+
+export const dimensions = {
+ // Button dimensions
+ buttonHeight: '56px',
+ buttonHeightSmall: '44px',
+ buttonBorderWidth: '2px',
+
+ // Tab bar
+ tabBarHeight: '65px',
+ tabBarBorderWidth: '1.5px',
+ tabBarMargin: '10px',
+
+ // Avatar sizes
+ avatarXs: '32px',
+ avatarSm: '40px',
+ avatarMd: '50px',
+ avatarLg: '80px',
+ avatarXl: '120px',
+ avatarXxl: '200px',
+
+ // Card padding
+ cardPadding: '16px',
+ cardPaddingLarge: '24px',
+
+ // Input dimensions
+ inputHeight: '50px',
+ inputBorderWidth: '2px',
+
+ // Floating assistant button
+ assistantButtonSize: '72px',
+ assistantButtonBottom: '90px',
+ assistantButtonRight: '20px',
+
+ // Status indicator
+ statusIndicatorSize: '24px',
+ statusIndicatorBorder: '3px',
+} as const;
+
+// ============================================
+// OVERLAY STYLES
+// ============================================
+
+export const overlays = {
+ modalBackdrop: 'rgba(4, 24, 21, 0.7)',
+ backgroundOverlay: 'rgba(4, 24, 21, 0.75)',
+ backgroundBlur: '8px',
+} as const;
+
+// ============================================
+// GRADIENT DEFINITIONS
+// ============================================
+
+export const gradients = {
+ goldShine: 'linear-gradient(135deg, #CBA951 0%, #F2E5B5 100%)',
+ emeraldDepth: 'linear-gradient(180deg, #0E5F4F 0%, #041815 100%)',
+ surfaceGlow: 'linear-gradient(135deg, rgba(203, 169, 81, 0.1) 0%, rgba(14, 95, 79, 0.1) 100%)',
+} as const;
+
+// ============================================
+// TRANSITION PRESETS
+// ============================================
+
+export const transitions = {
+ default: 'all 200ms ease',
+ fast: 'all 150ms ease',
+ slow: 'all 300ms ease',
+ transform: 'transform 200ms ease',
+ opacity: 'opacity 200ms ease',
+ colors: 'background-color 200ms ease, border-color 200ms ease, color 200ms ease',
+} as const;
+
+// ============================================
+// COMPLETE THEME OBJECT
+// ============================================
+
+export const theme = {
+ colors,
+ typography,
+ spacing,
+ borderRadius,
+ iconSizes,
+ shadows,
+ animations,
+ breakpoints,
+ zIndex,
+ dimensions,
+ overlays,
+ gradients,
+ transitions,
+} as const;
+
+// Export type for TypeScript
+export type Theme = typeof theme;
+
+// Default export
+export default theme;