@tailwind base; @tailwind components; @tailwind utilities; /* ============================================================ ACCENT PRESETS Applied via data-accent="" on ============================================================ */ /* Default / Schaeffler Green */ :root, [data-accent="green"] { --color-accent: #00893d; --color-accent-hover: #006e31; --color-accent-light: #e6f4ec; --color-accent-text: #ffffff; } [data-accent="blue"] { --color-accent: #2563eb; --color-accent-hover: #1d4ed8; --color-accent-light: #dbeafe; --color-accent-text: #ffffff; } [data-accent="purple"] { --color-accent: #7c3aed; --color-accent-hover: #6d28d9; --color-accent-light: #ede9fe; --color-accent-text: #ffffff; } [data-accent="amber"] { --color-accent: #d97706; --color-accent-hover: #b45309; --color-accent-light: #fef3c7; --color-accent-text: #ffffff; } [data-accent="teal"] { --color-accent: #0d9488; --color-accent-hover: #0f766e; --color-accent-light: #ccfbf1; --color-accent-text: #ffffff; } /* ============================================================ LIGHT THEME (default) ============================================================ */ :root { /* Surfaces */ --color-bg-app: #f8f9fb; --color-bg-surface: #ffffff; --color-bg-surface-hover: #f5f6f8; --color-bg-muted: #f1f3f5; /* Text */ --color-text: #18181b; --color-text-secondary: #52525b; --color-text-muted: #a1a1aa; --color-text-inverse: #ffffff; /* Borders */ --color-border: #e4e4e7; --color-border-light: #f1f3f5; /* Status — Success */ --color-status-success-bg: #dcfce7; --color-status-success-text: #166534; /* Status — Warning */ --color-status-warning-bg: #fef9c3; --color-status-warning-text: #854d0e; /* Status — Error */ --color-status-error-bg: #fee2e2; --color-status-error-text: #991b1b; /* Status — Info */ --color-status-info-bg: #dbeafe; --color-status-info-text: #1e40af; /* Extended badge colors */ --color-badge-purple-bg: rgba(124, 58, 237, 0.1); --color-badge-purple-text: #6d28d9; --color-badge-orange-bg: rgba(234, 88, 12, 0.1); --color-badge-orange-text: #c2410c; --color-badge-teal-bg: rgba(13, 148, 136, 0.1); --color-badge-teal-text: #0f766e; } /* ============================================================ DARK THEME Applied via .dark class on ============================================================ */ :root.dark { /* Surfaces — neutral grays instead of blue-slate */ --color-bg-app: #09090b; --color-bg-surface: #18181b; --color-bg-surface-hover: #27272a; --color-bg-muted: #1c1c1f; /* Text */ --color-text: #fafafa; --color-text-secondary: #a1a1aa; --color-text-muted: #71717a; --color-text-inverse: #09090b; /* Borders — subtle */ --color-border: rgba(255, 255, 255, 0.08); --color-border-light: rgba(255, 255, 255, 0.04); /* Status — Success */ --color-status-success-bg: rgba(34, 197, 94, 0.12); --color-status-success-text: #4ade80; /* Status — Warning */ --color-status-warning-bg: rgba(234, 179, 8, 0.12); --color-status-warning-text: #facc15; /* Status — Error */ --color-status-error-bg: rgba(239, 68, 68, 0.12); --color-status-error-text: #f87171; /* Status — Info */ --color-status-info-bg: rgba(59, 130, 246, 0.12); --color-status-info-text: #60a5fa; /* Extended badge colors */ --color-badge-purple-bg: rgba(124, 58, 237, 0.15); --color-badge-purple-text: #a78bfa; --color-badge-orange-bg: rgba(234, 88, 12, 0.15); --color-badge-orange-text: #fb923c; --color-badge-teal-bg: rgba(13, 148, 136, 0.15); --color-badge-teal-text: #2dd4bf; } /* Dark accent-light overrides (rgba instead of solid pastel) */ :root.dark, :root.dark [data-accent="green"] { --color-accent-light: rgba(0, 137, 61, 0.12); } :root.dark [data-accent="blue"] { --color-accent-light: rgba(37, 99, 235, 0.12); } :root.dark [data-accent="purple"] { --color-accent-light: rgba(124, 58, 237, 0.12); } :root.dark [data-accent="amber"] { --color-accent-light: rgba(217, 119, 6, 0.12); } :root.dark [data-accent="teal"] { --color-accent-light: rgba(13, 148, 136, 0.12); } /* ============================================================ BASE LAYER ============================================================ */ @layer base { body { @apply antialiased; font-family: 'Inter', system-ui, -apple-system, sans-serif; background-color: var(--color-bg-app); color: var(--color-text); transition: background-color 200ms ease, color 200ms ease; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11'; letter-spacing: -0.01em; } /* Native color scheme for form controls */ :root { color-scheme: light; } :root.dark { color-scheme: dark; } /* Checkbox / radio accent color */ input[type="checkbox"], input[type="radio"] { accent-color: var(--color-accent); } /* Better default scrollbar (thin, subtle) */ * { scrollbar-width: thin; scrollbar-color: var(--color-border) transparent; } *::-webkit-scrollbar { width: 6px; height: 6px; } *::-webkit-scrollbar-track { background: transparent; } *::-webkit-scrollbar-thumb { background-color: var(--color-border); border-radius: 3px; } /* Smoother global transitions */ a, button, input, select, textarea { transition: all 150ms ease; } /* Better selection color */ ::selection { background-color: var(--color-accent); color: white; } } /* ============================================================ COMPONENT CLASSES ============================================================ */ @layer components { /* ── Buttons ─────────────────────────────────────────────── */ .btn { @apply inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg font-medium text-sm transition-all duration-150 ease-out focus:outline-none focus:ring-2 focus:ring-offset-1 disabled:opacity-50 disabled:cursor-not-allowed; } .btn-primary { @apply btn shadow-sm; background-color: var(--color-accent); color: var(--color-accent-text); --tw-ring-color: var(--color-accent); } .btn-primary:hover:not(:disabled) { background-color: var(--color-accent-hover); transform: translateY(-0.5px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12); } .btn-primary:active:not(:disabled) { transform: translateY(0); } .btn-secondary { @apply btn border; background-color: var(--color-bg-surface); color: var(--color-text-secondary); border-color: var(--color-border); --tw-ring-color: var(--color-accent); } .btn-secondary:hover:not(:disabled) { background-color: var(--color-bg-surface-hover); border-color: var(--color-text-muted); } .btn-danger { @apply btn bg-red-600 text-white shadow-sm hover:bg-red-700 focus:ring-red-500; } .btn-danger:hover:not(:disabled) { transform: translateY(-0.5px); } /* Icon-only button variant */ .btn-icon { @apply inline-flex items-center justify-center p-1.5 rounded-lg transition-all duration-150 ease-out hover:bg-surface-hover focus:outline-none focus:ring-1; --tw-ring-color: var(--color-accent); } /* ── Cards ───────────────────────────────────────────────── */ .card { @apply rounded-xl border; background-color: var(--color-bg-surface); border-color: var(--color-border); box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.04), 0 1px 2px -1px rgba(0, 0, 0, 0.03); } :root.dark .card { box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.03); } /* ── Badges ──────────────────────────────────────────────── */ .badge { @apply inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium tracking-wide; } .badge-green { @apply badge; background-color: var(--color-status-success-bg); color: var(--color-status-success-text); } .badge-yellow { @apply badge; background-color: var(--color-status-warning-bg); color: var(--color-status-warning-text); } .badge-red { @apply badge; background-color: var(--color-status-error-bg); color: var(--color-status-error-text); } .badge-blue { @apply badge; background-color: var(--color-status-info-bg); color: var(--color-status-info-text); } .badge-gray { @apply badge; background-color: var(--color-bg-muted); color: var(--color-text-secondary); } .badge-purple { @apply badge; background-color: var(--color-badge-purple-bg); color: var(--color-badge-purple-text); } .badge-orange { @apply badge; background-color: var(--color-badge-orange-bg); color: var(--color-badge-orange-text); } .badge-teal { @apply badge; background-color: var(--color-badge-teal-bg); color: var(--color-badge-teal-text); } /* ── Inputs ──────────────────────────────────────────────── */ .input-base { @apply w-full px-3 py-2 rounded-lg text-sm border focus:outline-none focus:ring-2 focus:ring-offset-0 transition-all duration-150; background-color: var(--color-bg-surface); color: var(--color-text); border-color: var(--color-border); --tw-ring-color: var(--color-accent); } .input-base::placeholder { color: var(--color-text-muted); } .input-base:focus { border-color: var(--color-accent); } /* Small input variant (used in admin tables) */ .input-sm { @apply px-2 py-1 rounded-md text-sm border focus:outline-none focus:ring-1 focus:ring-offset-0 transition-all duration-150; background-color: var(--color-bg-surface); color: var(--color-text); border-color: var(--color-border); --tw-ring-color: var(--color-accent); } .input-sm:focus { border-color: var(--color-accent); } /* ── Tables ──────────────────────────────────────────────── */ table th { @apply text-xs uppercase tracking-wider font-semibold; color: var(--color-text-muted); letter-spacing: 0.05em; } } /* ============================================================ ANIMATIONS & MICRO-INTERACTIONS ============================================================ */ /* ── Keyframes ─────────────────────────────────────────────── */ @keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } @keyframes fadeInUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } } @keyframes fadeInScale { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } } @keyframes slideInRight { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: translateX(0); } } @keyframes slideInLeft { from { opacity: 0; transform: translateX(-16px); } to { opacity: 1; transform: translateX(0); } } @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } @keyframes subtlePulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } @keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } @keyframes progressGrow { from { width: 0%; } } @keyframes popIn { 0% { opacity: 0; transform: scale(0.8); } 70% { transform: scale(1.02); } 100% { opacity: 1; transform: scale(1); } } @keyframes breathe { 0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb, 0, 137, 61), 0.15); } 50% { box-shadow: 0 0 0 6px rgba(var(--accent-rgb, 0, 137, 61), 0); } } @keyframes countUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } /* ── Utility animation classes ─────────────────────────────── */ .animate-fade-in { animation: fadeIn 300ms ease-out both; } .animate-fade-in-up { animation: fadeInUp 400ms ease-out both; } .animate-fade-in-scale { animation: fadeInScale 250ms ease-out both; } .animate-slide-in-right { animation: slideInRight 300ms ease-out both; } .animate-slide-in-left { animation: slideInLeft 300ms ease-out both; } .animate-slide-up { animation: slideUp 350ms cubic-bezier(0.16, 1, 0.3, 1) both; } .animate-pop-in { animation: popIn 300ms cubic-bezier(0.16, 1, 0.3, 1) both; } .animate-subtle-pulse { animation: subtlePulse 2s ease-in-out infinite; } .animate-breathe { animation: breathe 2s ease-in-out infinite; } .animate-count-up { animation: countUp 400ms ease-out both; } /* ── Card hover micro-interaction ──────────────────────────── */ .card { transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease; } .card:hover { /* Subtle lift + glow on hover — only for non-table cards */ } /* Cards in grid layouts get the hover lift */ .grid > .card:hover, .card.hover-lift:hover { transform: translateY(-2px); box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.15), 0 4px 8px -2px rgba(0, 0, 0, 0.08); } :root.dark .grid > .card:hover, :root.dark .card.hover-lift:hover { box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.06); } /* ── Staggered grid item entrance ──────────────────────────── */ .grid > * { animation: fadeInUp 350ms ease-out both; } .grid > *:nth-child(1) { animation-delay: 0ms; } .grid > *:nth-child(2) { animation-delay: 30ms; } .grid > *:nth-child(3) { animation-delay: 60ms; } .grid > *:nth-child(4) { animation-delay: 90ms; } .grid > *:nth-child(5) { animation-delay: 120ms; } .grid > *:nth-child(6) { animation-delay: 150ms; } .grid > *:nth-child(7) { animation-delay: 180ms; } .grid > *:nth-child(8) { animation-delay: 210ms; } .grid > *:nth-child(9) { animation-delay: 240ms; } .grid > *:nth-child(10) { animation-delay: 270ms; } .grid > *:nth-child(11) { animation-delay: 300ms; } .grid > *:nth-child(12) { animation-delay: 330ms; } .grid > *:nth-child(n+13) { animation-delay: 350ms; } /* ── Table row entrance ────────────────────────────────────── */ tbody > tr { animation: fadeIn 250ms ease-out both; } tbody > tr:nth-child(1) { animation-delay: 0ms; } tbody > tr:nth-child(2) { animation-delay: 25ms; } tbody > tr:nth-child(3) { animation-delay: 50ms; } tbody > tr:nth-child(4) { animation-delay: 75ms; } tbody > tr:nth-child(5) { animation-delay: 100ms; } tbody > tr:nth-child(n+6) { animation-delay: 120ms; } /* ── Sidebar nav link animations ───────────────────────────── */ nav a, nav button { transition: all 180ms ease-out; position: relative; } /* Active nav link: left accent bar */ nav a.bg-accent-light { border-left: 3px solid var(--color-accent); padding-left: calc(0.75rem - 3px); } /* Nav link hover: subtle slide right */ nav a:not(.bg-accent-light):hover, nav button:hover { transform: translateX(2px); } /* ── Badge animations ──────────────────────────────────────── */ .badge, [class*="badge-"] { transition: all 150ms ease-out; } .badge:hover, [class*="badge-"]:hover { transform: scale(1.05); } /* Status badges in tables — subtle entrance */ td .badge, td [class*="badge-"] { animation: popIn 250ms ease-out both; } /* ── Progress bar animation ────────────────────────────────── */ [role="progressbar"] > div, .bg-green-500, .bg-red-500, .bg-amber-500, .bg-blue-500 { transition: width 600ms cubic-bezier(0.16, 1, 0.3, 1); } /* Render progress bars */ div[class*="bg-status-success"], div[class*="bg-green"] { transition: width 600ms cubic-bezier(0.16, 1, 0.3, 1); } /* ── Modal / dialog animations ─────────────────────────────── */ /* Backdrop fade */ .fixed.inset-0.z-50 { animation: fadeIn 200ms ease-out; } /* Modal panel pop-in */ .fixed.inset-0.z-50 > div[role="dialog"], .fixed.inset-0.z-50 > div:not([class*="absolute"]) > div { animation: fadeInScale 250ms cubic-bezier(0.16, 1, 0.3, 1); } /* ── Floating action bar slide-up ──────────────────────────── */ .fixed.bottom-6, .fixed.bottom-0 { animation: slideUp 300ms cubic-bezier(0.16, 1, 0.3, 1); } /* ── Toast notifications ───────────────────────────────────── */ [data-sonner-toaster] [data-sonner-toast] { animation: slideInRight 300ms cubic-bezier(0.16, 1, 0.3, 1) !important; } /* ── Loading skeleton shimmer ──────────────────────────────── */ .animate-pulse { animation: subtlePulse 1.5s ease-in-out infinite !important; } .skeleton { background: linear-gradient( 90deg, var(--color-bg-muted) 25%, var(--color-bg-surface-hover) 50%, var(--color-bg-muted) 75% ); background-size: 200% 100%; animation: shimmer 1.5s ease-in-out infinite; border-radius: 0.5rem; } /* ── Image/thumbnail hover zoom ────────────────────────────── */ .group img, .group video { transition: transform 300ms ease-out; } .group:hover img, .group:hover video { transform: scale(1.03); } /* ── Number/stat counter entrance ──────────────────────────── */ .text-2xl, .text-3xl, .text-4xl { animation: countUp 500ms ease-out both; } /* ── Checkbox/toggle animation ─────────────────────────────── */ input[type="checkbox"] { transition: all 150ms ease-out; } input[type="checkbox"]:checked { animation: popIn 200ms ease-out; } /* ── Link hover underline animation ────────────────────────── */ a.hover\:underline { text-decoration: none; background-image: linear-gradient(currentColor, currentColor); background-position: 0% 100%; background-repeat: no-repeat; background-size: 0% 1px; transition: background-size 200ms ease-out; } a.hover\:underline:hover { background-size: 100% 1px; } /* ── Dropdown/select hover ─────────────────────────────────── */ select { transition: all 150ms ease-out; cursor: pointer; } select:hover { border-color: var(--color-text-muted); } /* ── Notification bell wiggle ──────────────────────────────── */ @keyframes wiggle { 0%, 100% { transform: rotate(0deg); } 25% { transform: rotate(8deg); } 75% { transform: rotate(-8deg); } } .notification-bell:hover { animation: wiggle 400ms ease-in-out; } /* ── Smooth scroll behavior ────────────────────────────────── */ html { scroll-behavior: smooth; } /* ── Dashboard widget animations ───────────────────────────── */ /* Dashboard widgets: staggered scale-in entrance */ .grid > div > .card { animation: widgetEnter 450ms cubic-bezier(0.16, 1, 0.3, 1) both; } @keyframes widgetEnter { from { opacity: 0; transform: scale(0.94) translateY(8px); } to { opacity: 1; transform: scale(1) translateY(0); } } /* Staggered delays for dashboard widgets */ .grid > div:nth-child(1) > .card { animation-delay: 0ms; } .grid > div:nth-child(2) > .card { animation-delay: 60ms; } .grid > div:nth-child(3) > .card { animation-delay: 120ms; } .grid > div:nth-child(4) > .card { animation-delay: 180ms; } .grid > div:nth-child(5) > .card { animation-delay: 240ms; } .grid > div:nth-child(6) > .card { animation-delay: 300ms; } .grid > div:nth-child(7) > .card { animation-delay: 360ms; } .grid > div:nth-child(8) > .card { animation-delay: 420ms; } .grid > div:nth-child(9) > .card { animation-delay: 480ms; } .grid > div:nth-child(10) > .card { animation-delay: 540ms; } .grid > div:nth-child(11) > .card { animation-delay: 600ms; } .grid > div:nth-child(12) > .card { animation-delay: 660ms; } .grid > div:nth-child(n+13) > .card { animation-delay: 700ms; } /* Dashboard widget hover: subtle glow + lift */ .grid > div > .card:hover { transform: translateY(-3px); box-shadow: 0 12px 32px -6px rgba(0, 0, 0, 0.15), 0 4px 12px -2px rgba(0, 0, 0, 0.08); border-color: var(--color-accent); transition: all 250ms cubic-bezier(0.16, 1, 0.3, 1); } :root.dark .grid > div > .card:hover { box-shadow: 0 12px 32px -6px rgba(0, 0, 0, 0.5), 0 0 0 1px var(--color-accent), 0 0 20px -4px rgba(var(--accent-rgb, 0, 137, 61), 0.15); } /* Widget inner content numbers — count-up effect */ .card .text-2xl, .card .text-3xl, .card .text-4xl { animation: countUp 600ms cubic-bezier(0.16, 1, 0.3, 1) both; animation-delay: inherit; } /* Widget progress bars — animated grow from left */ .card [class*="bg-green"], .card [class*="bg-red"], .card [class*="bg-amber"], .card [class*="bg-blue"], .card [class*="bg-status"] { animation: progressGrow 800ms cubic-bezier(0.16, 1, 0.3, 1) both; animation-delay: 400ms; } /* ── Reduced motion: respect user preference ───────────────── */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } html { scroll-behavior: auto; } }