Files
ridebus-admin/frontend/src/index.css
Zakhar Timoshenko f218f0123a Add SVG assets for login devices and passkey icons
- Created a new SVG file for login devices with detailed paths and styles.
- Added a new SVG file for the passkey icon with multiple vector paths and fill attributes.
2026-09-17 20:31:10 +03:00

921 lines
25 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--control-radius: 8px;
--page-title-size: 38px;
--page-title-line-height: 48px;
}
@media (max-width: 1199px) {
:root { --page-title-size: 30px; --page-title-line-height: 40px; }
}
@media (max-width: 767px) {
:root { --page-title-size: 28px; --page-title-line-height: 36px; }
}
/* Dodo IS reference: 264px sidebar, neutral surfaces, 8–12px corners. */
@layer components {
.app-shell { min-height: 100vh; }
.app-sidebar {
@apply sticky top-0 z-40 flex flex-col;
background: #f0f0f0;
}
.sidebar-brand-row { @apply flex items-center justify-between px-4 py-3; }
.sidebar-brand { @apply flex items-center gap-2 text-lg font-bold text-gray-900; }
.brand-logo { display: block; width: 168px; height: 34px; object-fit: contain; }
.brand-mark { @apply flex h-8 w-8 shrink-0 items-center justify-center rounded-lg; background: #ff6900; }
.sidebar-menu-button { @apply flex h-11 w-11 items-center justify-center rounded-lg hover:bg-black/[0.04] lg:hidden; }
.sidebar-content { @apply hidden min-h-0 flex-col px-4 pb-4; max-height: calc(100dvh - 68px); overflow-y: auto; }
.sidebar-content.is-open { display: flex; }
.sidebar-account { @apply flex items-center justify-between gap-3 rounded-xl bg-white px-3 py-2 text-sm; min-height: 56px; }
.city-selector { position: relative; margin-top: 12px; }
.sidebar-city-card { @apply flex w-full items-center justify-between gap-3 rounded-xl bg-white px-3 py-2 text-sm transition-colors hover:bg-gray-50; min-height: 64px; }
.city-emblem { @apply relative flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary-50 text-lg; overflow: hidden; }
.city-emblem > img { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; object-fit: cover; }
.city-emblem-fallback { @apply flex h-full w-full items-center justify-center; }
.city-emblem.is-small { @apply h-8 w-8 text-base; }
.city-emblem.is-large { @apply h-14 w-14 rounded-xl text-3xl; }
.city-switcher { position: absolute; z-index: 50; top: calc(100% + 8px); left: 0; right: 0; min-width: 232px; padding: 8px; border-radius: 12px; background: #fff; box-shadow: 0 12px 32px #00000026, 0 2px 8px #0000000d; }
.city-switcher-heading { @apply px-3 pb-2 pt-1 text-xs uppercase tracking-wide text-gray-500; }
.city-switcher-option { @apply flex min-h-[44px] w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm text-gray-800 hover:bg-gray-50; }
.city-switcher-divider { height: 1px; margin: 6px 4px; background: #f0f0f0; }
.city-switcher-add { @apply text-primary-700; }
.sidebar-group-title { @apply px-3 pb-2 pt-5 text-xs font-normal uppercase tracking-wide text-gray-500; }
.sidebar-link { @apply flex min-h-[44px] items-center gap-3 rounded-lg px-3 py-3 text-sm text-gray-600 transition-colors hover:bg-black/[0.04]; }
.sidebar-link.is-active { @apply bg-black/[0.04] font-semibold text-gray-900; }
.sidebar-footer { @apply mt-auto pt-8; }
.app-main { @apply min-w-0 px-4 py-6 sm:px-6 sm:py-8; }
.app-content { @apply mx-auto w-full; max-width: 1206px; }
.app-main.app-main-wide { padding: 24px; }
.app-main-wide .app-content { max-width: 1608px; }
.profile-menu { position: fixed; z-index: 60; padding: 8px; background: #fff; border-radius: 12px; box-shadow: 0 12px 32px #00000026, 0 2px 8px #0000000d; }
.profile-menu-item { @apply flex w-full items-center gap-4 px-4 py-4 text-left text-sm text-gray-900 hover:bg-gray-50; border-radius: var(--control-radius); min-height: 56px; }
.profile-menu-item svg { flex-shrink: 0; }
.profile-menu-divider { margin: 4px 16px 4px 56px; height: 1px; background: #f0f0f0; }
.route-badge { display: flex; flex: 0 0 auto; align-items: center; justify-content: center; min-width: 48px; max-width: 96px; min-height: 48px; padding: 8px; border-radius: var(--control-radius); color: #fff; font-size: 16px; font-weight: 600; line-height: 24px; overflow-wrap: anywhere; }
.skip-link { @apply fixed left-4 top-4 z-50 -translate-y-24 rounded-lg bg-white px-4 py-3 text-primary-600 focus:translate-y-0; }
.segmented-control { @apply flex w-fit max-w-full flex-wrap gap-1 rounded-lg bg-black/[0.04] p-1; }
.segmented-option { @apply min-h-[44px] rounded-md px-3 py-2 text-sm text-gray-600 transition-colors hover:text-gray-900; }
.segmented-option[aria-pressed="true"] { @apply bg-white font-medium text-gray-900 shadow-sm; }
.stat-card { @apply flex h-full flex-col; }
.stat-card-icon { @apply flex h-10 w-10 items-center justify-center rounded-lg border border-gray-200 text-gray-600; }
.stat-card-link { @apply mt-auto flex items-center justify-between border-t border-gray-100 pt-3 text-sm text-primary-600; }
.dashboard-intro { @apply flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between; }
.cities-page { @apply space-y-6; }
.cities-heading { @apply flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between; }
.cities-heading > div > p { @apply mt-2 text-gray-500; }
.city-form { @apply space-y-5; }
.city-form h2 { @apply text-lg font-semibold; }
.city-form h2 + p { @apply mt-1 text-sm text-gray-500; }
.city-form-grid { @apply grid gap-4 sm:grid-cols-2; }
.city-form-hint { @apply mt-1 block text-xs text-gray-500; }
.city-form-error { @apply rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700; }
.city-form-notice { @apply rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-800; }
.city-avatar-field { @apply flex flex-col gap-3; }
.city-avatar-preview { @apply h-20 w-20 rounded-xl border border-gray-200 bg-gray-50 object-cover; }
.city-center-picker { @apply space-y-2 sm:col-span-2; }
.city-center-map { height: 320px; overflow: hidden; border: 1px solid #e5e7eb; border-radius: 12px; }
.city-center-coordinates { @apply text-sm text-gray-600; }
.city-center-coordinates strong { @apply text-gray-900; }
.city-avatar-editor { @apply ml-auto flex shrink-0 flex-col items-end gap-2; }
.city-avatar-editor .btn-secondary { @apply inline-flex items-center gap-2; }
.city-avatar-editor-error { @apply max-w-[200px] text-right text-xs text-red-700; }
.cities-grid { @apply grid gap-4 md:grid-cols-2; }
.city-network-card { @apply flex flex-wrap items-center gap-4; min-height: 104px; }
.city-network-card.is-active { border-color: #0958d9; box-shadow: inset 0 0 0 1px #0958d9; }
.city-network-card h2 { @apply text-lg font-semibold; }
.city-network-card p { @apply mt-1 text-sm text-gray-500; }
.city-current { @apply inline-flex items-center gap-1 rounded-full bg-primary-50 px-2 py-1 text-xs font-medium text-primary-700; }
.city-isolation-note { @apply flex items-start gap-3 rounded-xl bg-primary-50 p-4 text-sm text-primary-700; }
.city-isolation-note svg { @apply mt-0.5 shrink-0; }
.login-surface {
display: grid;
min-height: 100dvh;
place-items: start center;
padding: max(32px, calc((100dvh - 595px) / 2)) 16px;
background: #fff;
}
.login-layout { width: min(100%, 332px); }
.login-card {
width: 100%;
padding: 40px 40px 32px;
border-radius: 12px;
background: #fff;
box-shadow: 0 24px 30px rgb(0 10 28 / 15%);
}
.login-brand {
display: flex;
width: 130px;
height: 44px;
align-items: flex-start;
justify-content: center;
margin: 0 auto 28px;
padding-bottom: 14px;
}
.brand-logo.login-brand-logo { width: 130px; height: 27px; }
.login-methods { width: 100%; }
.login-password-choice,
.login-passkey-button,
.login-password-submit {
display: inline-flex;
width: 100%;
align-items: center;
justify-content: center;
border: 0;
font-size: 15px;
font-weight: 400;
line-height: 16px;
transition: background-color 160ms ease, box-shadow 160ms ease, opacity 160ms ease;
}
.login-password-choice {
height: 44px;
padding: 0 16px;
border-radius: 8px;
color: #000;
background: #f2f3f7;
}
.login-password-choice:hover:not(:disabled) { background: #e8ebf0; }
.login-divider {
position: relative;
height: 1px;
margin: 28px 0 44px;
background: #8c98a9;
}
.login-divider span {
position: absolute;
top: 50%;
left: 50%;
padding: 0 16px;
color: #8c98a9;
background: #fff;
font-size: 15px;
line-height: 19px;
transform: translate(-50%, -50%);
}
.login-passkey-button {
height: 48px;
gap: 4px;
padding: 1px 16px;
border-radius: 8px;
color: #fff;
background: #61a246;
}
.login-passkey-button:hover:not(:disabled) { background: #518b3a; }
.login-passkey-button img,
.login-passkey-button svg { width: 24px; height: 24px; flex: 0 0 24px; }
.login-passkey-button:disabled,
.login-password-choice:disabled,
.login-password-submit:disabled { opacity: .55; }
.login-passkey-hint {
margin: 28px 0 0;
color: #8c98a9;
font-size: 15px;
line-height: 19.2px;
text-align: center;
}
.login-error {
display: flex;
align-items: flex-start;
gap: 8px;
margin: 0 0 16px;
padding: 10px 12px;
border: 1px solid #fecaca;
border-radius: 8px;
color: #b42318;
background: #fef2f2;
font-size: 13px;
line-height: 18px;
}
.login-error svg { width: 18px; height: 18px; flex: 0 0 18px; }
.login-password-form { display: grid; gap: 16px; }
.login-password-heading {
display: grid;
grid-template-columns: 32px 1fr 32px;
align-items: center;
margin: 0 0 4px;
}
.login-password-heading h2 {
margin: 0;
color: #000;
font-size: 16px;
font-weight: 500;
line-height: 20px;
text-align: center;
}
.login-back-button {
display: grid;
width: 32px;
height: 32px;
place-items: center;
border: 0;
border-radius: 8px;
color: #536174;
background: transparent;
}
.login-back-button:hover:not(:disabled) { background: #f2f3f7; }
.login-back-button svg { width: 20px; height: 20px; }
.login-field { display: grid; gap: 6px; color: #536174; font-size: 13px; line-height: 16px; }
.login-input-wrap { position: relative; display: block; }
.login-input-wrap svg {
position: absolute;
top: 50%;
left: 12px;
width: 18px;
height: 18px;
color: #8c98a9;
transform: translateY(-50%);
}
.login-input-wrap input {
width: 100%;
height: 44px;
padding: 0 12px 0 40px;
border: 1px solid #c9cfd8;
border-radius: 8px;
color: #000;
background: #fff;
font-size: 15px !important;
line-height: 20px;
}
.login-input-wrap input::placeholder { color: #8c98a9; }
.login-input-wrap input:focus { border-color: #0958d9; outline: none; box-shadow: 0 0 0 2px rgb(9 88 217 / 15%); }
.login-password-submit {
height: 44px;
gap: 8px;
margin-top: 4px;
border-radius: 8px;
color: #fff;
background: #ff6900;
font-weight: 500;
}
.login-password-submit:hover:not(:disabled) { background: #e95f00; }
.login-password-submit svg { width: 18px; height: 18px; }
.login-device-card {
display: flex;
min-height: 104px;
align-items: flex-start;
gap: 16px;
margin-top: 16px;
padding: 28px 40px;
border-radius: 12px;
color: #000;
background: #fff;
box-shadow: 0 24px 30px rgb(0 10 28 / 15%);
}
.login-device-card img { width: 72px; height: 45px; flex: 0 0 72px; }
.login-device-card p { margin: -1px 0 0; font-size: 15px; line-height: 20.8px; }
.login-footer { min-height: 64px; margin-top: 16px; color: #8c98a9; font-size: 13px; line-height: 20px; text-align: center; }
.login-footer p { margin: 0; }
@media (max-width: 374px) {
.login-card { padding: 28px 24px; }
.login-device-card { padding: 24px; }
}
@media (max-width: 767px) {
.app-main.app-main-wide { padding: 24px 16px; }
}
@media (min-width: 1024px) {
.app-sidebar { position: fixed; inset: 0 auto 0 0; width: 264px; padding: 16px; }
.sidebar-brand-row { padding: 8px 8px 24px; }
.sidebar-content { display: flex; flex: 1; max-height: none; padding: 0; }
.app-main { margin-left: 264px; padding: 56px 40px; }
}
@media (min-width: 1536px) {
.app-main { padding-inline: 80px; }
}
}
@layer base {
button { border-radius: var(--control-radius); }
:where(a, button, input, select, textarea):focus-visible {
outline: 2px solid #0958d9;
outline-offset: 3px;
}
button:disabled { cursor: not-allowed; }
body {
@apply text-gray-900;
background: #f5f5f5;
-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-500 text-white px-4 py-2.5 rounded-lg font-medium;
@apply transition-all duration-200 ease-out;
@apply hover:bg-primary-700;
@apply active:scale-[0.98];
@apply text-sm sm:text-base;
}
.btn-secondary {
@apply bg-black/[0.04] text-gray-900 px-4 py-2.5 rounded-lg font-medium;
@apply transition-all duration-200 ease-out;
@apply hover:bg-black/[0.08];
@apply active:scale-[0.98];
@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-[0.98];
@apply text-sm sm:text-base;
}
.btn-primary, .btn-secondary, .btn-danger, .input { border-radius: var(--control-radius); }
.card {
@apply bg-white rounded-xl border border-transparent p-4 sm:p-6;
@apply transition-all duration-300 ease-out;
}
.card-hover {
@apply hover:border-gray-200;
}
.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 font-semibold text-gray-900;
font-size: var(--page-title-size);
line-height: var(--page-title-line-height);
letter-spacing: -0.6px;
overflow-wrap: anywhere;
}
/* 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-lg 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;
}
}