diff --git a/flutter_app/lib/admin/inbox_screen.dart b/flutter_app/lib/admin/inbox_screen.dart index 6b38373..c7efae2 100644 --- a/flutter_app/lib/admin/inbox_screen.dart +++ b/flutter_app/lib/admin/inbox_screen.dart @@ -165,7 +165,7 @@ class _StatusFilterTile extends StatelessWidget { return ListTile( title: Text(label), selected: selected, - selectedTileColor: TropicalColors.foam, + selectedTileColor: TropicalColors.surface, onTap: onTap, ); } @@ -204,7 +204,7 @@ class _LeadListTile extends StatelessWidget { if (lead.createdAt != null) Text( dateFormat.format(lead.createdAt!), - style: TextStyle(fontSize: 11, color: Colors.grey.shade600), + style: TextStyle(fontSize: 11, color: TropicalColors.disabled), ), ], ), @@ -215,17 +215,16 @@ class _LeadListTile extends StatelessWidget { Color _statusColor(LeadStatus status) { switch (status) { case LeadStatus.newLead: - return TropicalColors.coral; case LeadStatus.contacted: - return TropicalColors.teal; + return TropicalColors.primary; case LeadStatus.quoting: - return Colors.orange; + return TropicalColors.warning; case LeadStatus.booked: - return TropicalColors.palm; + return TropicalColors.success; case LeadStatus.closed: - return Colors.grey; + return TropicalColors.disabled; case LeadStatus.lost: - return Colors.red.shade300; + return TropicalColors.error; } } } @@ -240,7 +239,7 @@ class _StatusChip extends StatelessWidget { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( - color: TropicalColors.foam, + color: TropicalColors.surface, borderRadius: BorderRadius.circular(12), ), child: Text(status.label, style: const TextStyle(fontSize: 11)), diff --git a/flutter_app/lib/admin/lead_detail_screen.dart b/flutter_app/lib/admin/lead_detail_screen.dart index bc0a468..529ceba 100644 --- a/flutter_app/lib/admin/lead_detail_screen.dart +++ b/flutter_app/lib/admin/lead_detail_screen.dart @@ -195,8 +195,8 @@ class _LeadDetailScreenState extends State { style: TextStyle( fontWeight: FontWeight.bold, color: role == 'user' - ? TropicalColors.teal - : TropicalColors.coral, + ? TropicalColors.primary + : TropicalColors.secondary, fontSize: 12, ), ), @@ -215,7 +215,7 @@ class _LeadDetailScreenState extends State { Container( width: 320, decoration: BoxDecoration( - border: Border(left: BorderSide(color: Colors.grey.shade300)), + border: Border(left: BorderSide(color: TropicalColors.disabled.withValues(alpha: 0.4))), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -285,7 +285,7 @@ class _LeadDetailScreenState extends State { dateFormat.format(note.createdAt!), style: TextStyle( fontSize: 11, - color: Colors.grey.shade600, + color: TropicalColors.disabled, ), ), ), @@ -358,7 +358,7 @@ class _DetailRow extends StatelessWidget { width: 100, child: Text( label, - style: TextStyle(color: Colors.grey.shade600), + style: TextStyle(color: TropicalColors.disabled), ), ), Expanded(child: Text(value)), diff --git a/flutter_app/lib/admin/login_screen.dart b/flutter_app/lib/admin/login_screen.dart index ca59a67..3152170 100644 --- a/flutter_app/lib/admin/login_screen.dart +++ b/flutter_app/lib/admin/login_screen.dart @@ -66,21 +66,21 @@ class _AdminLoginScreenState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Icon(Icons.beach_access, size: 48, color: TropicalColors.teal), + Icon(Icons.beach_access, size: 48, color: TropicalColors.primary), const SizedBox(height: 16), Text( 'Vacation Agent CRM', textAlign: TextAlign.center, style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, - color: TropicalColors.ocean, + color: TropicalColors.secondary, ), ), const SizedBox(height: 8), Text( 'Team login only', textAlign: TextAlign.center, - style: TextStyle(color: Colors.grey.shade600), + style: TextStyle(color: TropicalColors.disabled), ), const SizedBox(height: 32), TextField( @@ -103,7 +103,7 @@ class _AdminLoginScreenState extends State { ), if (_error != null) ...[ const SizedBox(height: 12), - Text(_error!, style: const TextStyle(color: Colors.red)), + Text(_error!, style: const TextStyle(color: TropicalColors.error)), ], const SizedBox(height: 24), ElevatedButton( diff --git a/flutter_app/lib/admin/partial_inbox_screen.dart b/flutter_app/lib/admin/partial_inbox_screen.dart index 90c623c..6163bfb 100644 --- a/flutter_app/lib/admin/partial_inbox_screen.dart +++ b/flutter_app/lib/admin/partial_inbox_screen.dart @@ -146,7 +146,7 @@ class _FilterTile extends StatelessWidget { return ListTile( title: Text(label), selected: selected, - selectedTileColor: TropicalColors.foam, + selectedTileColor: TropicalColors.surface, onTap: onTap, ); } @@ -169,8 +169,8 @@ class _PartialListTile extends StatelessWidget { onTap: onTap, leading: CircleAvatar( backgroundColor: partial.status == PartialLeadStatus.partial - ? Colors.orange.shade400 - : TropicalColors.teal, + ? TropicalColors.warning + : TropicalColors.primary, child: Text( label.isNotEmpty ? label[0].toUpperCase() : '?', style: const TextStyle(color: Colors.white), @@ -194,15 +194,15 @@ class _PartialListTile extends StatelessWidget { style: TextStyle( fontSize: 11, color: partial.status == PartialLeadStatus.partial - ? Colors.orange.shade800 - : TropicalColors.teal, + ? TropicalColors.warningDark + : TropicalColors.primary, fontWeight: FontWeight.w600, ), ), if (partial.updatedAt != null) Text( dateFormat.format(partial.updatedAt!), - style: TextStyle(fontSize: 11, color: Colors.grey.shade600), + style: TextStyle(fontSize: 11, color: TropicalColors.disabled), ), ], ), diff --git a/flutter_app/lib/admin/partial_lead_detail_screen.dart b/flutter_app/lib/admin/partial_lead_detail_screen.dart index ed66b1d..884e42a 100644 --- a/flutter_app/lib/admin/partial_lead_detail_screen.dart +++ b/flutter_app/lib/admin/partial_lead_detail_screen.dart @@ -41,8 +41,8 @@ class PartialLeadDetailScreen extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.w600, color: partial.status == PartialLeadStatus.partial - ? Colors.orange.shade800 - : TropicalColors.teal, + ? TropicalColors.warningDark + : TropicalColors.primary, ), ), ), @@ -101,14 +101,14 @@ class PartialLeadDetailScreen extends StatelessWidget { constraints: const BoxConstraints(maxWidth: 520), decoration: BoxDecoration( color: isUser - ? TropicalColors.teal.withValues(alpha: 0.12) - : Colors.grey.shade100, + ? TropicalColors.primary.withValues(alpha: 0.12) + : TropicalColors.surface, borderRadius: BorderRadius.circular(12), ), child: Text( text, style: TextStyle( - color: isUser ? TropicalColors.ocean : Colors.black87, + color: isUser ? TropicalColors.secondary : TropicalColors.onSurface, ), ), ), @@ -166,7 +166,7 @@ class _DetailRow extends StatelessWidget { width: 120, child: Text( label, - style: TextStyle(color: Colors.grey.shade700, fontWeight: FontWeight.w500), + style: TextStyle(color: TropicalColors.disabled, fontWeight: FontWeight.w500), ), ), Expanded(child: Text(value)), diff --git a/flutter_app/lib/chat/chat_screen.dart b/flutter_app/lib/chat/chat_screen.dart index 9b43619..4285ca2 100644 --- a/flutter_app/lib/chat/chat_screen.dart +++ b/flutter_app/lib/chat/chat_screen.dart @@ -155,10 +155,17 @@ class _ChatScreenState extends State { if (_controller.errorMessage != null) Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - _controller.errorMessage!, - textAlign: TextAlign.center, - style: TextStyle(color: Colors.red.shade100), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + decoration: BoxDecoration( + color: TropicalColors.errorSurface.withValues(alpha: 0.95), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + _controller.errorMessage!, + textAlign: TextAlign.center, + style: const TextStyle(color: TropicalColors.error), + ), ), ), ChatInputBar( @@ -190,7 +197,7 @@ class _SubmittedBanner extends StatelessWidget { margin: const EdgeInsets.all(16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: TropicalColors.palm.withValues(alpha: 0.9), + color: TropicalColors.success.withValues(alpha: 0.9), borderRadius: BorderRadius.circular(16), ), child: Column( diff --git a/flutter_app/lib/chat/widgets/chat_widgets.dart b/flutter_app/lib/chat/widgets/chat_widgets.dart index fa022a2..a9435ef 100644 --- a/flutter_app/lib/chat/widgets/chat_widgets.dart +++ b/flutter_app/lib/chat/widgets/chat_widgets.dart @@ -34,7 +34,7 @@ class MessageBubble extends StatelessWidget { ), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.08), + color: TropicalColors.onSurface.withValues(alpha: 0.08), blurRadius: 8, offset: const Offset(0, 2), ), @@ -43,7 +43,7 @@ class MessageBubble extends StatelessWidget { child: Text( text.isEmpty && isStreaming ? '...' : text, style: TextStyle( - color: isUser ? Colors.white : TropicalColors.ocean, + color: isUser ? Colors.white : TropicalColors.secondary, height: 1.4, fontSize: 15, ), @@ -95,7 +95,7 @@ class _TypingIndicatorState extends State width: 8, height: 8, decoration: BoxDecoration( - color: TropicalColors.teal.withValues(alpha: 0.4 + opacity * 0.6), + color: TropicalColors.primary.withValues(alpha: 0.4 + opacity * 0.6), shape: BoxShape.circle, ), ); @@ -141,7 +141,7 @@ class ChatInputBar extends StatelessWidget { : '$queuedCount messages queued…', textAlign: TextAlign.center, style: TextStyle( - color: TropicalColors.ocean.withValues(alpha: 0.85), + color: TropicalColors.secondary.withValues(alpha: 0.85), fontSize: 12, fontWeight: FontWeight.w500, ), @@ -164,7 +164,7 @@ class ChatInputBar extends StatelessWidget { ), const SizedBox(width: 8), Material( - color: sendEnabled ? TropicalColors.teal : Colors.grey.shade400, + color: sendEnabled ? TropicalColors.primary : TropicalColors.disabled, shape: const CircleBorder(), child: InkWell( onTap: sendEnabled ? onSend : null, diff --git a/flutter_app/lib/theme/tropical_theme.dart b/flutter_app/lib/theme/tropical_theme.dart index 3c9cb73..b6c3046 100644 --- a/flutter_app/lib/theme/tropical_theme.dart +++ b/flutter_app/lib/theme/tropical_theme.dart @@ -1,32 +1,60 @@ import 'package:flutter/material.dart'; +/// Sunset Riviera palette — warm terracotta + Mediterranean sapphire. class TropicalColors { - static const teal = Color(0xFF0D9488); - static const tealDark = Color(0xFF0F766E); - static const sand = Color(0xFFF5E6D3); - static const coral = Color(0xFFFF7F6B); - static const ocean = Color(0xFF164E63); - static const palm = Color(0xFF2D6A4F); - static const foam = Color(0xFFE0F2F1); - static const userBubble = Color(0xFF134E4A); - static const assistantBubble = Colors.white; + static const primary = Color(0xFFC05621); + static const primaryDark = Color(0xFF7C2D12); + static const secondary = Color(0xFF1E40AF); + static const surface = Color(0xFFFFF7ED); + static const surfaceElevated = Color(0xFFFFFFFF); + static const onSurface = Color(0xFF292524); + static const error = Color(0xFFB91C1C); + static const errorSurface = Color(0xFFFEE2E2); + static const success = Color(0xFF166534); + static const disabled = Color(0xFF9CA3AF); + static const warning = Color(0xFFEA580C); + static const warningDark = Color(0xFFC2410C); + + static const gradientMid = Color(0xFFFDBA74); + static const gradientEnd = Color(0xFF93C5FD); + + static const userBubble = primaryDark; + static const assistantBubble = surfaceElevated; } class TropicalTheme { + static ColorScheme get _customerScheme => const ColorScheme( + brightness: Brightness.light, + primary: TropicalColors.primary, + onPrimary: Colors.white, + secondary: TropicalColors.secondary, + onSecondary: Colors.white, + surface: TropicalColors.surface, + onSurface: TropicalColors.onSurface, + error: TropicalColors.error, + onError: Colors.white, + ); + + static ColorScheme get _adminScheme => const ColorScheme( + brightness: Brightness.light, + primary: TropicalColors.secondary, + onPrimary: Colors.white, + secondary: TropicalColors.primary, + onSecondary: Colors.white, + surface: TropicalColors.surface, + onSurface: TropicalColors.onSurface, + error: TropicalColors.error, + onError: Colors.white, + ); + static ThemeData get light { - final base = ThemeData( + final scheme = _customerScheme; + + return ThemeData( useMaterial3: true, brightness: Brightness.light, - colorScheme: ColorScheme.fromSeed( - seedColor: TropicalColors.teal, - primary: TropicalColors.teal, - secondary: TropicalColors.coral, - surface: TropicalColors.foam, - ), + colorScheme: scheme, fontFamily: 'Segoe UI', - ); - - return base.copyWith( scaffoldBackgroundColor: Colors.transparent, appBarTheme: const AppBarTheme( backgroundColor: Colors.transparent, @@ -36,17 +64,17 @@ class TropicalTheme { ), inputDecorationTheme: InputDecorationTheme( filled: true, - fillColor: Colors.white.withValues(alpha: 0.92), + fillColor: TropicalColors.surfaceElevated.withValues(alpha: 0.92), border: OutlineInputBorder( borderRadius: BorderRadius.circular(24), borderSide: BorderSide.none, ), contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14), - hintStyle: TextStyle(color: Colors.grey.shade600), + hintStyle: TextStyle(color: TropicalColors.onSurface.withValues(alpha: 0.55)), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( - backgroundColor: TropicalColors.teal, + backgroundColor: TropicalColors.primary, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), shape: RoundedRectangleBorder( @@ -55,7 +83,7 @@ class TropicalTheme { ), ), cardTheme: CardThemeData( - color: Colors.white.withValues(alpha: 0.95), + color: TropicalColors.surfaceElevated.withValues(alpha: 0.95), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), @@ -65,23 +93,38 @@ class TropicalTheme { } static ThemeData get admin { - final base = ThemeData( + final scheme = _adminScheme; + + return ThemeData( useMaterial3: true, brightness: Brightness.light, - colorScheme: ColorScheme.fromSeed( - seedColor: TropicalColors.ocean, - primary: TropicalColors.ocean, - secondary: TropicalColors.teal, - ), - ); - - return base.copyWith( - scaffoldBackgroundColor: const Color(0xFFF8FAFC), + colorScheme: scheme, + scaffoldBackgroundColor: TropicalColors.surface, appBarTheme: const AppBarTheme( - backgroundColor: TropicalColors.ocean, + backgroundColor: TropicalColors.secondary, foregroundColor: Colors.white, elevation: 0, ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: TropicalColors.surfaceElevated, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + cardTheme: CardThemeData( + color: TropicalColors.surfaceElevated, + elevation: 1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: TropicalColors.secondary, + foregroundColor: Colors.white, + ), + ), ); } } @@ -99,10 +142,10 @@ class TropicalBackground extends StatelessWidget { begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ - Color(0xFF0D9488), - Color(0xFF5EEAD4), - Color(0xFFF5E6D3), - Color(0xFFFFAB91), + TropicalColors.primary, + TropicalColors.gradientMid, + TropicalColors.surface, + TropicalColors.gradientEnd, ], stops: [0.0, 0.35, 0.7, 1.0], ), @@ -124,7 +167,7 @@ class TropicalBackground extends StatelessWidget { child: Icon( Icons.park_rounded, size: 100, - color: TropicalColors.palm.withValues(alpha: 0.12), + color: TropicalColors.success.withValues(alpha: 0.12), ), ), Positioned( diff --git a/flutter_app/web/manifest.json b/flutter_app/web/manifest.json index e38256d..d3cf7a2 100644 --- a/flutter_app/web/manifest.json +++ b/flutter_app/web/manifest.json @@ -3,8 +3,8 @@ "short_name": "Paradise Planner", "start_url": ".", "display": "standalone", - "background_color": "#0D9488", - "theme_color": "#0D9488", + "background_color": "#C05621", + "theme_color": "#C05621", "description": "AI-powered vacation planning concierge", "orientation": "portrait-primary", "prefer_related_applications": false