79651bc41d
- Widgets scale in with staggered delays (60ms per widget, up to 12) - Widget hover: lift 3px + accent border glow (dark mode: accent shadow) - Inner numbers animate up with count-up effect - Progress bars grow from left with spring curve (800ms delay for content-first feel) - All wrapped in prefers-reduced-motion guard Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
746 lines
22 KiB
CSS
746 lines
22 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/* ============================================================
|
|
ACCENT PRESETS
|
|
Applied via data-accent="<key>" on <html>
|
|
============================================================ */
|
|
|
|
/* 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 <html>
|
|
============================================================ */
|
|
: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;
|
|
}
|
|
}
|