/* NextSnap - Mobile-first responsive styles */ :root { --bg-primary: #1a1a2e; --bg-secondary: #16213e; --bg-tertiary: #0f1729; --text-primary: #ffffff; --text-secondary: #a0a0a0; --accent: #4a90e2; --success: #4caf50; --warning: #ff9800; --error: #f44336; --offline: #757575; } @media (prefers-color-scheme: light) { :root { --bg-primary: #ffffff; --bg-secondary: #f5f5f5; --bg-tertiary: #e0e0e0; --text-primary: #212121; --text-secondary: #757575; } } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; min-height: 100vh; } #app { max-width: 480px; margin: 0 auto; min-height: 100vh; } /* Utility classes */ .hidden { display: none !important; } .container { padding: 1rem; } .btn { display: block; width: 100%; padding: 1rem; font-size: 1rem; font-weight: 600; border: none; border-radius: 8px; cursor: pointer; text-align: center; transition: all 0.2s; min-height: 48px; } .btn-primary { background: var(--accent); color: white; } .btn-primary:active { opacity: 0.8; transform: scale(0.98); } /* Top Bar */ .top-bar { position: fixed; top: 0; left: 0; right: 0; background: var(--bg-secondary); border-bottom: 1px solid rgba(255, 255, 255, 0.1); z-index: 1000; height: 56px; } .top-bar-content { max-width: 480px; margin: 0 auto; height: 100%; display: flex; align-items: center; justify-content: space-between; padding: 0 1rem; } .app-title { font-size: 1.25rem; font-weight: 700; margin: 0; } .top-bar-indicators { display: flex; align-items: center; gap: 0.75rem; } /* Connectivity Indicator */ .connectivity-indicator { width: 12px; height: 12px; border-radius: 50%; position: relative; } .connectivity-indicator.online { background: var(--success); box-shadow: 0 0 8px var(--success); } .connectivity-indicator.offline { background: var(--offline); } .connectivity-indicator.syncing { background: var(--warning); animation: pulse 1.5s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(1.2); } } /* Pending Count Badge */ .pending-count { background: var(--error); color: white; font-size: 0.75rem; font-weight: 700; padding: 0.25rem 0.5rem; border-radius: 12px; min-width: 24px; text-align: center; } /* Main Content - Account for top bar and bottom nav */ #app { padding-top: 56px; padding-bottom: 70px; } /* Bottom Navigation */ .bottom-nav { position: fixed; bottom: 0; left: 0; right: 0; background: var(--bg-secondary); border-top: 1px solid rgba(255, 255, 255, 0.1); display: flex; justify-content: space-around; z-index: 1000; padding-bottom: env(safe-area-inset-bottom); } .nav-item { flex: 1; display: flex; flex-direction: column; align-items: center; padding: 0.5rem 0; text-decoration: none; color: var(--text-secondary); transition: color 0.2s; min-height: 56px; justify-content: center; } .nav-item:active { background: rgba(255, 255, 255, 0.05); } .nav-item.active { color: var(--accent); } .nav-icon { font-size: 1.5rem; margin-bottom: 0.25rem; } .nav-label { font-size: 0.75rem; font-weight: 500; } /* Responsive adjustments for larger screens */ @media (min-width: 480px) { .top-bar-content, .bottom-nav { max-width: 480px; margin: 0 auto; } } /* Queue-specific improvements */ .btn-outline:active:not(:disabled) { opacity: 0.8; transform: scale(0.98); } .btn-outline:disabled { opacity: 0.5; cursor: not-allowed; } /* Scrollbar styling for webkit browsers */ .queue-list::-webkit-scrollbar { width: 8px; } .queue-list::-webkit-scrollbar-track { background: var(--bg-tertiary); border-radius: 4px; } .queue-list::-webkit-scrollbar-thumb { background: var(--text-secondary); border-radius: 4px; } .queue-list::-webkit-scrollbar-thumb:hover { background: var(--accent); } /* Loading animation */ @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .btn-icon.spinning { display: inline-block; animation: spin 1s linear infinite; } /* ============================================ POLISH & ANIMATIONS - Module 14 ============================================ */ /* Smooth page transitions */ #app { animation: fadeIn 0.3s ease-in; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* Button interactions */ .btn { position: relative; overflow: hidden; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); } .btn:active:not(:disabled) { transform: translateY(0); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } /* Ripple effect for buttons */ .btn::before { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.3); transform: translate(-50%, -50%); transition: width 0.6s, height 0.6s; } .btn:active::before { width: 300px; height: 300px; } /* Enhanced connectivity indicator pulse */ @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); box-shadow: 0 0 0 0 var(--warning); } 50% { opacity: 0.8; transform: scale(1.1); box-shadow: 0 0 0 8px transparent; } } /* Smooth connectivity state transitions */ .connectivity-indicator { transition: all 0.3s ease; } /* Loading spinner enhancement */ @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } /* Skeleton loader for content */ @keyframes shimmer { 0% { background-position: -468px 0; } 100% { background-position: 468px 0; } } .skeleton { background: linear-gradient( 90deg, var(--bg-secondary) 0px, rgba(255, 255, 255, 0.1) 40px, var(--bg-secondary) 80px ); background-size: 800px 100px; animation: shimmer 2s infinite; border-radius: 4px; } /* Form input enhancements */ input:not([type="file"]), textarea, select { transition: all 0.2s ease; } input:focus:not([type="file"]), textarea:focus, select:focus { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(74, 144, 226, 0.2); } /* Card hover effects */ .queue-item, .thumbnail, .file-item { transition: all 0.2s ease; } .queue-item:hover, .thumbnail:hover, .file-item:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); } /* Toast notification slide-in */ .toast { animation: slideUp 0.3s ease-out; } @keyframes slideUp { from { opacity: 0; transform: translate(-50%, 20px); } to { opacity: 1; transform: translate(-50%, 0); } } /* Modal fade-in */ .modal { animation: modalFadeIn 0.2s ease-out; } @keyframes modalFadeIn { from { opacity: 0; } to { opacity: 1; } } .modal-content { animation: modalSlideUp 0.3s ease-out; } @keyframes modalSlideUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } } /* Navigation item active state */ .nav-item { position: relative; transition: all 0.2s ease; } .nav-item::after { content: ''; position: absolute; bottom: 0; left: 50%; width: 0; height: 2px; background: var(--accent); transform: translateX(-50%); transition: width 0.3s ease; } .nav-item.active::after, .nav-item:hover::after { width: 80%; } /* Focus visible for accessibility */ *:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* Improve button focus states */ button:focus-visible, .btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* Smooth scroll behavior */ html { scroll-behavior: smooth; } /* Loading bar at top of page */ .loading-bar { position: fixed; top: 0; left: 0; right: 0; height: 3px; background: var(--accent); transform-origin: left; animation: loadingBar 1s ease-in-out infinite; z-index: 9999; } @keyframes loadingBar { 0% { transform: scaleX(0); } 50% { transform: scaleX(0.5); } 100% { transform: scaleX(1); } } /* Status badge animations */ .badge, .status-badge { animation: badgeFadeIn 0.3s ease-out; } @keyframes badgeFadeIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } } /* Image fade-in on load */ img { animation: imageFadeIn 0.4s ease-out; } @keyframes imageFadeIn { from { opacity: 0; } to { opacity: 1; } } /* Thumbnail grid stagger animation */ .photo-thumbnails .thumbnail { animation: staggerFadeIn 0.4s ease-out backwards; } .photo-thumbnails .thumbnail:nth-child(1) { animation-delay: 0.05s; } .photo-thumbnails .thumbnail:nth-child(2) { animation-delay: 0.1s; } .photo-thumbnails .thumbnail:nth-child(3) { animation-delay: 0.15s; } .photo-thumbnails .thumbnail:nth-child(4) { animation-delay: 0.2s; } .photo-thumbnails .thumbnail:nth-child(5) { animation-delay: 0.25s; } @keyframes staggerFadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* Enhanced empty state */ .empty-state { animation: emptyStateFadeIn 0.5s ease-out; } @keyframes emptyStateFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } /* Pending count badge bounce */ .pending-count { animation: badgeBounce 0.5s ease-out; } @keyframes badgeBounce { 0% { transform: scale(0); } 50% { transform: scale(1.2); } 100% { transform: scale(1); } } /* Enhanced spinner */ .spinner, .spinner-small { animation: spin 1s cubic-bezier(0.4, 0, 0.2, 1) infinite; } /* Delete button shake on hover */ .queue-item-delete:hover, .btn-danger:hover { animation: shake 0.5s; } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-2px); } 75% { transform: translateX(2px); } } /* Success checkmark animation */ @keyframes checkmark { 0% { transform: scale(0) rotate(0deg); } 50% { transform: scale(1.2) rotate(180deg); } 100% { transform: scale(1) rotate(360deg); } } .success-icon { animation: checkmark 0.6s ease-out; } /* Table row hover effect */ .user-table tr { transition: all 0.2s ease; } .user-table tr:hover { transform: scale(1.01); } /* Form validation feedback */ input:invalid:not(:placeholder-shown) { border-color: var(--error); animation: inputShake 0.3s; } input:valid:not(:placeholder-shown) { border-color: var(--success); } @keyframes inputShake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } } /* Reduced motion for accessibility */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } /* Dark mode enhancements */ @media (prefers-color-scheme: dark) { .btn:hover:not(:disabled) { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); } input:focus:not([type="file"]), textarea:focus, select:focus { box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3); } } /* Progress bar for uploads */ .upload-progress { position: relative; height: 4px; background: var(--bg-tertiary); border-radius: 2px; overflow: hidden; } .upload-progress-bar { height: 100%; background: var(--accent); transition: width 0.3s ease; position: relative; } .upload-progress-bar::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient( 90deg, transparent, rgba(255, 255, 255, 0.3), transparent ); animation: shimmer 1.5s infinite; } /* Improved disabled state */ button:disabled, .btn:disabled, input:disabled { cursor: not-allowed; opacity: 0.5; filter: grayscale(50%); } /* Connection status pulse enhancement */ .connectivity-indicator.online { animation: successPulse 2s ease-in-out infinite; } @keyframes successPulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7); } 50% { box-shadow: 0 0 0 8px rgba(76, 175, 80, 0); } } /* Error state pulse */ .connectivity-indicator.offline { animation: errorPulse 2s ease-in-out infinite; } @keyframes errorPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } /* Micro-interaction for clickable items */ [role="button"], .clickable { cursor: pointer; user-select: none; transition: all 0.2s ease; } [role="button"]:active, .clickable:active { transform: scale(0.97); } /* Smooth color transitions */ * { transition-property: color, background-color, border-color; transition-duration: 0.2s; transition-timing-function: ease; } /* Override for elements that shouldn't have color transitions */ button, .btn, input, textarea, select { transition-property: all; } /* Offline mode label */ .offline-label { font-size: 0.75rem; color: var(--offline, #888); font-weight: 600; } /* Logout button in top bar */ .logout-btn { background: none; border: 1px solid rgba(255, 255, 255, 0.2); color: var(--text-secondary); font-size: 0.75rem; padding: 0.25rem 0.6rem; border-radius: 4px; cursor: pointer; min-height: 28px; } .logout-btn:active { opacity: 0.7; }