@tailwind base; @tailwind components; @tailwind utilities; @layer base { body { @apply bg-gray-50 text-gray-900; -webkit-tap-highlight-color: transparent; } /* Prevent zoom on input focus on iOS */ input, select, textarea { font-size: 16px !important; } @media (min-width: 640px) { input, select, textarea { font-size: inherit !important; } } } @layer components { .btn-primary { @apply bg-primary-600 text-white px-4 py-2.5 rounded-lg font-medium; @apply transition-all duration-200 ease-out; @apply hover:bg-primary-700 hover:shadow-md hover:-translate-y-0.5; @apply active:scale-95 active:translate-y-0; @apply text-sm sm:text-base; } .btn-secondary { @apply bg-gray-200 text-gray-700 px-4 py-2.5 rounded-lg font-medium; @apply transition-all duration-200 ease-out; @apply hover:bg-gray-300 hover:shadow-sm hover:-translate-y-0.5; @apply active:scale-95 active:translate-y-0; @apply text-sm sm:text-base; } .btn-danger { @apply bg-red-600 text-white px-4 py-2.5 rounded-lg font-medium; @apply transition-all duration-200 ease-out; @apply hover:bg-red-700 hover:shadow-md hover:-translate-y-0.5; @apply active:scale-95 active:translate-y-0; @apply text-sm sm:text-base; } .card { @apply bg-white rounded-lg shadow-sm border border-gray-200 p-4 sm:p-6; @apply transition-all duration-300 ease-out; } .card-hover { @apply hover:shadow-lg hover:-translate-y-1 hover:border-gray-300; } .input { @apply border border-gray-300 rounded-lg px-3 py-2.5 w-full; @apply focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent; @apply transition-all duration-200 ease-out; @apply text-base sm:text-sm; } .label { @apply block text-sm font-medium text-gray-700 mb-1; } /* Mobile-friendly touch targets */ .touch-target { @apply min-h-[44px] min-w-[44px]; } /* Page titles */ .page-title { @apply text-2xl sm:text-3xl font-bold text-gray-900; } /* Modal styles with animations */ .modal-overlay { @apply fixed inset-0 bg-black/50 flex items-end sm:items-center justify-center z-50; animation: fadeIn 0.2s ease-out; } .modal-content { @apply bg-white w-full sm:max-w-lg sm:mx-4 sm:rounded-lg rounded-t-2xl max-h-[90vh] overflow-hidden; animation: slideUp 0.3s ease-out; } @media (min-width: 640px) { .modal-content { animation: scaleIn 0.2s ease-out; } } /* Safe area for bottom navigation */ .safe-bottom { padding-bottom: env(safe-area-inset-bottom, 0); } /* Animated list items */ .list-item-animated { @apply transition-all duration-200 ease-out; animation: fadeSlideIn 0.3s ease-out backwards; } /* Stagger animation delays */ .stagger-1 { animation-delay: 0.05s; } .stagger-2 { animation-delay: 0.1s; } .stagger-3 { animation-delay: 0.15s; } .stagger-4 { animation-delay: 0.2s; } .stagger-5 { animation-delay: 0.25s; } .stagger-6 { animation-delay: 0.3s; } /* Pulse animation for loading states */ .pulse-soft { animation: pulseSoft 2s ease-in-out infinite; } /* Shimmer loading effect */ .shimmer { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: shimmer 1.5s infinite; } /* Icon animation */ .icon-bounce { @apply transition-transform duration-200; } .icon-bounce:hover { animation: iconBounce 0.4s ease-out; } /* Floating action button */ .fab { @apply fixed bottom-20 right-4 lg:bottom-8 lg:right-8; @apply w-14 h-14 rounded-full shadow-lg; @apply flex items-center justify-center; @apply bg-primary-600 text-white; @apply transition-all duration-300 ease-out; @apply hover:shadow-xl hover:scale-110; @apply active:scale-95; @apply disabled:opacity-50 disabled:cursor-not-allowed; animation: fabIn 0.4s ease-out; z-index: 40; } /* Secondary FAB */ .fab-secondary { @apply fixed bottom-36 right-4 lg:bottom-24 lg:right-8; @apply w-12 h-12 rounded-full shadow-md; @apply flex items-center justify-center; @apply bg-white text-gray-700 border border-gray-200; @apply transition-all duration-300 ease-out; @apply hover:shadow-lg hover:scale-105; @apply active:scale-95; animation: fabIn 0.4s ease-out 0.1s backwards; z-index: 39; } /* Empty state */ .empty-state { @apply text-center py-12 px-4; } .empty-state-icon { @apply h-16 w-16 sm:h-20 sm:w-20 text-gray-300 mx-auto mb-4; animation: fadeSlideIn 0.5s ease-out; } .empty-state-text { @apply text-gray-500 text-sm sm:text-base mb-4; animation: fadeSlideIn 0.5s ease-out 0.1s backwards; } /* Touch feedback for list items */ .list-item-touch { @apply relative overflow-hidden; -webkit-tap-highlight-color: transparent; } .list-item-touch::before { content: ''; @apply absolute inset-0 bg-gray-900/5 opacity-0 transition-opacity duration-150; pointer-events: none; } .list-item-touch:active::before { @apply opacity-100; } /* Mobile card improvements */ @media (max-width: 640px) { .card { @apply rounded-xl; } .btn-primary, .btn-secondary, .btn-danger { @apply py-3; } } /* Pull to refresh indicator */ .pull-indicator { @apply flex items-center justify-center py-4 text-gray-400; animation: fadeIn 0.2s ease-out; } /* Swipeable item */ .swipe-item { @apply relative; touch-action: pan-y; } /* Bottom sheet handle */ .sheet-handle { @apply w-10 h-1 bg-gray-300 rounded-full mx-auto mb-2; } /* iOS-style segment control */ .segment-control { @apply flex rounded-lg bg-gray-100 p-1 relative; } .segment-item { @apply flex-1 py-2 px-3 rounded-md text-sm font-medium transition-colors relative z-10; @apply flex items-center justify-center; } .segment-item-active { @apply text-gray-900; } .segment-item-inactive { @apply text-gray-500; } .segment-indicator { @apply absolute top-1 bottom-1 bg-white rounded-md shadow-sm transition-all duration-300 ease-out; } /* Status indicator */ .status-online { @apply w-2 h-2 bg-green-500 rounded-full; animation: pulseSoft 2s ease-in-out infinite; } .status-offline { @apply w-2 h-2 bg-gray-400 rounded-full; } /* Quick action buttons */ .quick-action { @apply flex flex-col items-center justify-center p-3 rounded-xl; @apply bg-gray-50 hover:bg-gray-100 active:bg-gray-200; @apply transition-all duration-200; @apply min-w-[72px]; } .quick-action-icon { @apply w-10 h-10 rounded-full flex items-center justify-center mb-1; @apply transition-transform duration-200; } .quick-action:active .quick-action-icon { @apply scale-90; } .quick-action-label { @apply text-xs text-gray-600 font-medium; } /* Badge animation */ .badge-pulse { @apply relative; } .badge-pulse::after { content: ''; @apply absolute inset-0 rounded-full bg-current opacity-40; animation: badgePulse 1.5s ease-out infinite; } /* Skeleton loading */ .skeleton { @apply bg-gray-200 rounded; animation: skeleton 1.5s ease-in-out infinite; } /* Tooltip animation */ .tooltip { animation: tooltipIn 0.15s ease-out; } /* Success checkmark animation */ .success-check { animation: successCheck 0.4s ease-out; } /* Shake animation for errors */ .shake { animation: shake 0.5s ease-out; } /* Slide transitions */ .slide-enter { animation: slideIn 0.3s ease-out; } .slide-exit { animation: slideOut 0.2s ease-in forwards; } /* Rotate animation for refresh icons */ .spin-once { animation: spinOnce 0.5s ease-out; } /* Number counter animation */ .counter { @apply tabular-nums; transition: all 0.3s ease-out; } /* Progress bar */ .progress-bar { @apply h-1 bg-primary-600 rounded-full; animation: progressGrow 0.5s ease-out; } /* Ripple effect */ .ripple { @apply relative overflow-hidden; } .ripple::after { content: ''; @apply absolute inset-0 bg-white/30 rounded-full scale-0; transition: transform 0.4s ease-out, opacity 0.4s ease-out; } .ripple:active::after { @apply scale-150 opacity-0; } } /* Keyframes */ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } @keyframes slideUp { from { opacity: 0; transform: translateY(100%); } to { opacity: 1; transform: translateY(0); } } @keyframes slideDown { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(100%); } } @keyframes scaleIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } @keyframes fadeSlideIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @keyframes slideIn { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } } @keyframes slideOut { from { opacity: 1; transform: translateX(0); } to { opacity: 0; transform: translateX(20px); } } @keyframes pulseSoft { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } } @keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } @keyframes iconBounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } } @keyframes fabIn { from { opacity: 0; transform: scale(0) rotate(-180deg); } to { opacity: 1; transform: scale(1) rotate(0); } } @keyframes badgePulse { 0% { transform: scale(1); opacity: 0.4; } 100% { transform: scale(2); opacity: 0; } } @keyframes skeleton { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @keyframes tooltipIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } } @keyframes successCheck { 0% { transform: scale(0); } 50% { transform: scale(1.2); } 100% { transform: scale(1); } } @keyframes shake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-8px); } 40% { transform: translateX(8px); } 60% { transform: translateX(-4px); } 80% { transform: translateX(4px); } } @keyframes spinOnce { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes progressGrow { from { width: 0; } } @keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } } @keyframes wiggle { 0%, 100% { transform: rotate(0deg); } 25% { transform: rotate(-5deg); } 75% { transform: rotate(5deg); } } /* Leaflet map container */ .leaflet-container { height: 100%; width: 100%; border-radius: 0.5rem; z-index: 1; } /* Fix leaflet controls on mobile */ .leaflet-control-zoom { border: none !important; box-shadow: 0 2px 6px rgba(0,0,0,0.15) !important; } .leaflet-control-zoom a { width: 36px !important; height: 36px !important; line-height: 36px !important; transition: background-color 0.2s ease; } /* Custom scrollbar for mobile */ @media (max-width: 640px) { ::-webkit-scrollbar { width: 4px; height: 4px; } ::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 4px; } } /* Hide scrollbar but keep functionality */ .hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; } .hide-scrollbar::-webkit-scrollbar { display: none; } /* Smooth scroll on iOS */ .smooth-scroll { -webkit-overflow-scrolling: touch; } /* Pull to refresh indicator space */ @supports (padding-top: env(safe-area-inset-top)) { .safe-top { padding-top: env(safe-area-inset-top); } } /* Page transition wrapper */ .page-transition { animation: pageIn 0.3s ease-out; } @keyframes pageIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* Blob animation for login page */ @keyframes blob { 0%, 100% { transform: translate(0, 0) scale(1); } 33% { transform: translate(30px, -50px) scale(1.1); } 66% { transform: translate(-20px, 20px) scale(0.9); } } .animate-blob { animation: blob 7s infinite; } .animation-delay-2000 { animation-delay: 2s; } .animation-delay-4000 { animation-delay: 4s; } /* Shake animation for errors */ @keyframes shake { 0%, 100% { transform: translateX(0); } 10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); } 20%, 40%, 60%, 80% { transform: translateX(10px); } } /* Reduced motion */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } }