diff --git a/web/src/pages/Home/ModeTab.jsx b/web/src/pages/Home/ModeTab.jsx
index e9dd7bea5..05a9801f1 100644
--- a/web/src/pages/Home/ModeTab.jsx
+++ b/web/src/pages/Home/ModeTab.jsx
@@ -10,7 +10,7 @@ export const ModeTab = ({ mode, active, onClick, rotation = 0 }) => (
onClick={onClick}
className={`flex h-8 min-w-0 flex-1 items-center justify-center rounded-full transition-colors duration-150 ${
active
- ? 'bg-primary text-primary-content shadow-sm'
+ ? 'bg-primary text-primary-content'
: 'text-base-content/50 hover:text-base-content'
}`}
>
diff --git a/web/src/style.css b/web/src/style.css
index d6e88a1e3..66e027788 100644
--- a/web/src/style.css
+++ b/web/src/style.css
@@ -11,8 +11,8 @@
--color-base-200: oklch(97.466% 0.011 259.822);
--color-base-300: oklch(93.268% 0.016 262.751);
--color-base-content: oklch(41.886% 0.053 255.824);
- --color-primary: oklch(56.86% 0.255 257.57);
- --color-primary-content: oklch(91.372% 0.051 257.57);
+ --color-primary: oklch(45% 0.255 257.57);
+ --color-primary-content: oklch(97.5% 0.015 257.57);
--color-secondary: oklch(42.551% 0.161 282.339);
--color-secondary-content: oklch(88.51% 0.032 282.339);
--color-accent: oklch(59.939% 0.191 335.171);
@@ -119,8 +119,8 @@
--color-base-200: oklch(93.299% 0.01 261.788);
--color-base-300: oklch(89.925% 0.016 262.749);
--color-base-content: oklch(32.437% 0.022 264.182);
- --color-primary: oklch(59.435% 0.077 254.027);
- --color-primary-content: oklch(11.887% 0.015 254.027);
+ --color-primary: oklch(44.5% 0.077 254.027);
+ --color-primary-content: oklch(98% 0.012 254.027);
--color-secondary: oklch(69.651% 0.059 248.687);
--color-secondary-content: oklch(13.93% 0.011 248.687);
--color-accent: oklch(77.464% 0.062 217.469);
@@ -420,7 +420,7 @@ html[data-theme='coffee'] {
}
@theme {
- --font-logo: 'Montserrat', sans-serif;
+ --font-logo: 'Montserrat', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
@source inline("{,sm:,md:,lg:,xl:}col-span-{1..12}");
@@ -445,11 +445,25 @@ html[data-theme='coffee'] {
@layer utilities {
.profile-list-drag-selected-item {
- @apply ring-primary/50 bg-primary/10 shadow-md ring-2;
+ @apply ring-primary/50 bg-primary/10 ring-2;
}
.profile-card-container.drop-highlight {
@apply bg-primary/10;
}
+ .scrollbar-none {
+ scrollbar-width: none;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
+
+ /* Prevent double-tap to zoom on interactive elements which can cause visual jumping when clicked rapidly */
+ button,
+ a,
+ input[type='button'],
+ input[type='submit'] {
+ touch-action: manipulation;
+ }
}
@custom-variant hmd {
@@ -457,3 +471,402 @@ html[data-theme='coffee'] {
@slot;
}
}
+
+/* Mobile Search Morph Transitions */
+@media (max-width: 639px) {
+ .morph-track-exit {
+ transition:
+ transform 200ms cubic-bezier(0.645, 0.045, 0.355, 1),
+ opacity 200ms cubic-bezier(0.645, 0.045, 0.355, 1);
+ will-change: transform, opacity;
+ }
+
+ .morph-track-enter {
+ transition:
+ transform 250ms cubic-bezier(0.23, 1, 0.32, 1),
+ opacity 250ms cubic-bezier(0.23, 1, 0.32, 1);
+ will-change: transform, opacity;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .morph-track-exit,
+ .morph-track-enter {
+ transition: none !important;
+ }
+}
+
+/* ── Profile card accordion animation ── */
+
+/* Chevron rotates 90° when expanded */
+.profile-card-chevron {
+ transition: transform 280ms cubic-bezier(0.165, 0.84, 0.44, 1);
+}
+.profile-card-chevron.expanded {
+ transform: rotate(90deg);
+}
+
+/* Accordion container: grid trick for height 0 → auto */
+.profile-card-accordion {
+ display: grid;
+ grid-template-rows: 0fr;
+ transition: grid-template-rows 280ms cubic-bezier(0.165, 0.84, 0.44, 1);
+}
+.profile-card-accordion[data-expanded='true'] {
+ grid-template-rows: 1fr;
+}
+
+/* Content fades + slides up as it reveals */
+.profile-card-content {
+ opacity: 0;
+ transform: translateY(-6px);
+ transition:
+ opacity 220ms cubic-bezier(0.165, 0.84, 0.44, 1) 60ms,
+ transform 280ms cubic-bezier(0.165, 0.84, 0.44, 1);
+ will-change: opacity, transform;
+}
+.profile-card-accordion[data-expanded='true'] .profile-card-content {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+/* Respect reduced motion */
+@media (prefers-reduced-motion: reduce) {
+ .profile-card-chevron,
+ .profile-card-accordion,
+ .profile-card-content {
+ transition: none !important;
+ }
+}
+
+/* Elevate card stacking context when its actions dropdown is open */
+.profile-card-container:has(.action-dropdown-open),
+.card:has(.action-dropdown-open) {
+ position: relative !important;
+ z-index: 50 !important;
+ overflow: visible !important;
+}
+
+/* Elevate header stacking context when its actions dropdown is open to prevent overlapping by sticky mobile search */
+.profile-list-header:has(.action-dropdown-open) {
+ z-index: 50 !important;
+}
+
+/* ── Action dropdown transitions (fully decoupled from DaisyUI) ── */
+.action-dropdown {
+ display: inline-block;
+ position: relative;
+}
+
+/* Menu panel: always in the DOM, hidden via opacity+visibility+transform.
+ No display:none — that kills CSS transitions. */
+.action-dropdown .action-dropdown-menu {
+ position: absolute;
+ top: 100%;
+ right: 0;
+ z-index: 999;
+
+ /* Closed state */
+ opacity: 0;
+ visibility: hidden;
+ transform: scale(0.95) translateY(-4px);
+ pointer-events: none;
+
+ /* Exit: 120ms (~20% faster than entrance) */
+ transition:
+ transform 120ms cubic-bezier(0.23, 1, 0.32, 1),
+ opacity 120ms cubic-bezier(0.23, 1, 0.32, 1),
+ visibility 120ms step-end;
+ transform-origin: top right;
+ will-change: transform, opacity;
+}
+
+/* Open state */
+.action-dropdown.action-dropdown-open .action-dropdown-menu {
+ opacity: 1;
+ visibility: visible;
+ transform: scale(1) translateY(0);
+ pointer-events: auto;
+
+ /* Enter: 150ms */
+ transition:
+ transform 150ms cubic-bezier(0.23, 1, 0.32, 1),
+ opacity 150ms cubic-bezier(0.23, 1, 0.32, 1),
+ visibility 150ms step-start;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .action-dropdown .action-dropdown-menu {
+ transition: none;
+ }
+}
+
+/* Premium hardware-accelerated animated focus ring for inputs */
+.input {
+ outline: 2px solid transparent;
+ outline-offset: 0px;
+ transition:
+ outline-color 150ms ease,
+ outline-offset 150ms ease,
+ border-color 150ms ease,
+ box-shadow 150ms ease;
+}
+
+.input:focus-within {
+ outline: 2px solid var(--color-primary);
+ outline-offset: 2px;
+}
+
+/* Prevent double focus outlines and unify caret color on nested inputs */
+.input input {
+ caret-color: var(--color-primary);
+}
+
+.input input:focus {
+ outline: none !important;
+ box-shadow: none !important;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .input {
+ transition: none !important;
+ }
+}
+
+/* Premium hardware-accelerated slide-down transition for mobile search */
+@media (max-width: 639px) {
+ /* Outer sticky element: has NO transforms to avoid breaking WebKit sticky positioning */
+ .search-slide-sticky {
+ position: sticky;
+ top: 0;
+ z-index: 30;
+ height: 64px;
+ margin-bottom: -64px;
+ pointer-events: none;
+ }
+
+ .search-slide-sticky-active {
+ pointer-events: auto;
+ }
+
+ /* Inner animated container: handles GPU transforms and opacity transitions */
+ .search-slide-container {
+ opacity: 0;
+ transform: translateY(-16px) scale(0.98);
+ /* Exit */
+ transition:
+ transform 150ms ease,
+ opacity 150ms ease;
+ will-change: transform, opacity;
+ }
+
+ .search-slide-sticky-active .search-slide-container {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ /* Enter */
+ transition:
+ transform 250ms cubic-bezier(0.175, 0.885, 0.32, 1.1),
+ opacity 250ms cubic-bezier(0.175, 0.885, 0.32, 1.1);
+ }
+
+ .profiles-list-content {
+ /* Exit */
+ transition: transform 150ms ease;
+ will-change: transform;
+ }
+
+ .profiles-list-content.search-active {
+ transform: translateY(64px);
+ /* Enter */
+ transition: transform 250ms cubic-bezier(0.175, 0.885, 0.32, 1.1);
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .search-slide-container,
+ .profiles-list-content {
+ transition: none !important;
+ }
+}
+
+/* Fix DaisyUI v5 alert text contrast/legibility by ensuring content colors override base-content */
+.alert-warning {
+ color: var(--color-warning-content);
+}
+.alert-error {
+ color: var(--color-error-content);
+}
+.alert-success {
+ color: var(--color-success-content);
+}
+.alert-info {
+ color: var(--color-info-content);
+}
+
+/* Sidebar morph transitions */
+.sidebar-transition {
+ padding-top: 1.25rem; /* 20px */
+ padding-bottom: 1.25rem; /* 20px */
+ padding-left: 1rem; /* 16px */
+ padding-right: 1rem; /* 16px */
+ transition: width 280ms cubic-bezier(0.34, 1.15, 0.64, 1),
+ translate 280ms cubic-bezier(0.34, 1.15, 0.64, 1),
+ transform 280ms cubic-bezier(0.34, 1.15, 0.64, 1);
+ will-change: width, translate, transform;
+}
+
+.sidebar-backdrop-transition {
+ transition: opacity 280ms cubic-bezier(0.34, 1.15, 0.64, 1);
+ will-change: opacity;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .sidebar-transition,
+ .sidebar-backdrop-transition {
+ transition: none !important;
+ }
+}
+
+
+.sidebar-label {
+ display: inline-block;
+ vertical-align: middle;
+ white-space: nowrap;
+ will-change: opacity, transform;
+}
+
+.sidebar-label-collapsed {
+ opacity: 0;
+ transform: translateX(-8px);
+ pointer-events: none;
+ transition: opacity 120ms cubic-bezier(0.215, 0.61, 0.355, 1),
+ transform 180ms cubic-bezier(0.215, 0.61, 0.355, 1);
+}
+
+.sidebar-label-expanded {
+ opacity: 1;
+ transform: translateX(0);
+ transition: opacity 180ms ease-out 100ms,
+ transform 220ms cubic-bezier(0.34, 1.15, 0.64, 1) 80ms;
+}
+
+.sidebar-logo-img {
+ width: 150px;
+ max-width: none !important;
+ flex-shrink: 0;
+ will-change: opacity, transform;
+}
+
+.sidebar-logo-collapsed {
+ opacity: 0;
+ transform: translateX(-8px);
+ pointer-events: none;
+ transition: opacity 120ms cubic-bezier(0.215, 0.61, 0.355, 1),
+ transform 180ms cubic-bezier(0.215, 0.61, 0.355, 1);
+}
+
+.sidebar-logo-expanded {
+ opacity: 1;
+ transform: translateX(0);
+ transition: opacity 180ms ease-out 100ms,
+ transform 220ms cubic-bezier(0.34, 1.15, 0.64, 1) 80ms;
+}
+
+.nav-btn-transition {
+ width: 100%;
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 0.75rem !important; /* 12px — overrides DaisyUI btn-md padding */
+ padding-right: 0.75rem !important; /* 12px — overrides DaisyUI btn-md padding */
+ justify-content: flex-start !important; /* overrides DaisyUI btn justify-center */
+ transition: background-color 150ms ease,
+ color 150ms ease;
+ will-change: background-color, color;
+}
+
+.nav-btn-collapsed {
+ /* Buttons are 100% width; the sidebar container controls sizing */
+}
+
+/* Sidebar social icons and copyright footer transition rules */
+.sidebar-footer {
+ will-change: opacity, transform;
+ width: 250px;
+ flex-shrink: 0;
+ margin-top: 1rem;
+}
+
+.sidebar-footer-collapsed {
+ opacity: 0;
+ transform: translateX(-8px);
+ pointer-events: none;
+ transition: opacity 120ms cubic-bezier(0.215, 0.61, 0.355, 1),
+ transform 180ms cubic-bezier(0.215, 0.61, 0.355, 1);
+}
+
+.sidebar-footer-expanded {
+ opacity: 1;
+ transform: translateX(0);
+ transition: opacity 180ms ease-out 100ms,
+ transform 220ms cubic-bezier(0.34, 1.15, 0.64, 1) 80ms;
+}
+
+.chevron-rotate-transition {
+ transition: transform 280ms cubic-bezier(0.34, 1.15, 0.64, 1),
+ rotate 280ms cubic-bezier(0.34, 1.15, 0.64, 1);
+ will-change: transform, rotate;
+}
+
+/* Progressive loading transitions */
+.progressive-transition-container {
+ display: grid;
+ grid-template-columns: 100%;
+ width: 100%;
+}
+
+.progressive-transition-container > * {
+ grid-column: 1 / 2;
+ grid-row: 1 / 2;
+}
+
+.progressive-transition-fade-out {
+ animation: progressive-fade-out 250ms cubic-bezier(0.215, 0.61, 0.355, 1) forwards;
+ will-change: opacity, transform;
+ pointer-events: none;
+}
+
+@keyframes progressive-fade-out {
+ from {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+}
+
+.progressive-transition-fade-in {
+ animation: progressive-fade-in 250ms cubic-bezier(0.215, 0.61, 0.355, 1) forwards;
+ will-change: opacity;
+}
+
+@keyframes progressive-fade-in {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .progressive-transition-fade-out {
+ animation: none;
+ opacity: 0;
+ transform: none;
+ }
+ .progressive-transition-fade-in {
+ animation: none;
+ opacity: 1;
+ transform: none;
+ }
+}