- 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.
921 lines
25 KiB
CSS
921 lines
25 KiB
CSS
@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;
|
||
}
|
||
}
|