@font-face { font-family: 'Iosevka Patrick'; src: url('/static/fonts/IosevkaPatrickNerdFont-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: 'Iosevka Patrick'; src: url('/static/fonts/IosevkaPatrickNerdFont-Medium.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; } @font-face { font-family: 'Iosevka Patrick'; src: url('/static/fonts/IosevkaPatrickNerdFont-SemiBold.woff2') format('woff2'); font-weight: 600; font-style: normal; font-display: swap; } @tailwind base; @tailwind components; @tailwind utilities; @layer base { h1, h2, h3 { @apply font-semibold; } /* Touch targets for interactive elements only */ button, input[type="submit"], input[type="button"] { min-height: 44px; min-width: 44px; } /* Prevent iOS zoom on input focus */ @media (max-width: 768px) { input, select, textarea { font-size: 16px; } } } @layer components { /* Page Containers */ .page-container { @apply mx-auto px-2 sm:px-4; } .page-container-sm { @apply page-container max-w-2xl; } .page-container-md { @apply page-container max-w-3xl; } .page-container-lg { @apply page-container max-w-4xl; } .page-container-xl { @apply page-container max-w-6xl; } /* Cards and Containers */ .card { @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-xl shadow-xl border border-brown-300; } .card-inner { @apply p-6; } .card-sm { @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-lg shadow-md border border-brown-300; } /* Section box for lighter content areas */ .section-box { @apply bg-brown-50 rounded-lg p-4 border border-brown-200; } /* Buttons */ .btn { @apply inline-flex items-center justify-center px-4 py-2 rounded-lg font-medium transition-colors cursor-pointer; } .btn-primary { @apply btn bg-gradient-to-br from-brown-700 to-brown-900 text-white hover:from-brown-800 hover:to-brown-900 shadow-md; } .btn-secondary { @apply btn bg-brown-300 text-brown-900 hover:bg-brown-400; } .btn-tertiary { @apply btn bg-gradient-to-br from-brown-500 to-brown-600 text-white hover:from-brown-600 hover:to-brown-700; } .btn-link { @apply text-brown-700 hover:text-brown-900 font-medium underline transition-colors cursor-pointer; } .btn-danger { @apply text-red-600 hover:text-red-800 font-medium underline transition-colors cursor-pointer; } /* Forms */ .form-label { @apply block text-sm font-medium text-brown-900 mb-2; } .form-input { @apply rounded-lg border-2 border-brown-300 shadow-sm focus:border-brown-600 focus:ring-brown-600 text-base py-2 px-3 bg-white; } .form-input-lg { @apply form-input py-3 px-4; } .form-select { @apply form-input truncate max-w-full; } .form-textarea { @apply form-input min-h-[100px]; } /* Tables */ .table-container { @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-lg shadow-md overflow-hidden border border-brown-300; } .table { @apply min-w-full divide-y divide-brown-300; } .table-header { @apply bg-brown-200; } .table-th { @apply px-6 py-3 text-left text-xs font-medium text-brown-900 uppercase tracking-wider; } .table-body { @apply bg-brown-100 divide-y divide-brown-200; } .table-row { @apply hover:bg-brown-100 transition-colors; } .table-td { @apply px-6 py-4 whitespace-nowrap text-sm text-brown-800; } /* Modals */ .modal-backdrop { @apply fixed inset-0 bg-black/40 backdrop-blur-sm flex items-center justify-center z-50 p-4; } .modal-content { @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-xl shadow-2xl p-6 max-w-md w-full border border-brown-300 max-h-[90vh] overflow-y-auto; } .modal-title { @apply text-xl font-semibold text-brown-900 mb-4; } /* Native Dialog Element */ .modal-dialog { @apply p-0 bg-transparent border-none shadow-none max-w-md w-full; } .modal-dialog::backdrop { @apply bg-black/40 backdrop-blur-sm; } /* Dialog content wrapper (nested inside dialog) */ .modal-dialog .modal-content { @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-xl shadow-2xl p-6 w-full border border-brown-300 max-h-[90vh] overflow-y-auto; } /* Entity suggestion typeahead dropdown */ .suggestions-dropdown { @apply absolute z-50 left-0 right-0 mt-1 bg-white rounded-lg shadow-lg border border-brown-200 max-h-48 overflow-y-auto; } .suggestions-item { @apply w-full text-left px-3 py-2 flex items-center gap-2 hover:bg-brown-50 transition-colors cursor-pointer border-b border-brown-100 last:border-b-0; } /* Typography */ .section-title { @apply text-2xl font-bold text-brown-900 mb-4; } .page-title { @apply text-3xl font-bold text-brown-900; } /* Feed Components */ .feed-card { @apply bg-gradient-to-br from-brown-50 to-brown-100 rounded-lg shadow-md border border-brown-200 p-3 sm:p-4 hover:shadow-lg transition-shadow; } .feed-content-box { @apply bg-white/60 backdrop-blur rounded-lg p-3 sm:p-4 border border-brown-200; } .feed-content-box-sm { @apply bg-white/60 backdrop-blur rounded-lg p-2 sm:p-3 border border-brown-200; } /* Avatar - base styles */ .avatar { @apply rounded-full object-cover transition-all duration-200; } .avatar-sm { @apply avatar w-8 h-8 ring-2 ring-brown-500 hover:ring-brown-400 hover:ring-4; } .avatar-md { @apply avatar w-12 h-12 ring-2 ring-brown-500 hover:ring-brown-400 hover:ring-4; } .avatar-lg { @apply avatar w-20 h-20 border-2 border-brown-300 hover:border-brown-400 hover:border-4; } /* Avatar placeholder - for missing images */ .avatar-placeholder { @apply rounded-full bg-brown-300 flex items-center justify-center transition-all duration-200; } .avatar-placeholder-sm { @apply avatar-placeholder w-8 h-8 ring-2 ring-brown-500 hover:ring-brown-400 hover:ring-4; } .avatar-placeholder-md { @apply avatar-placeholder w-12 h-12 ring-2 ring-brown-500 hover:ring-brown-400 hover:ring-4; } .avatar-placeholder-lg { @apply avatar-placeholder w-20 h-20 hover:bg-brown-400; } /* Avatar text sizing */ .avatar-text-sm { @apply text-brown-600 text-sm; } .avatar-text-md { @apply text-brown-600 text-base; } .avatar-text-lg { @apply text-brown-600 text-2xl; } /* Text Utilities */ .text-helper { @apply text-sm text-brown-700 mt-1; } .text-meta { @apply text-xs text-brown-600; } .text-meta-sm { @apply text-sm text-brown-600; } .text-label { @apply text-brown-600; } /* Badges */ .badge-rating { @apply inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-amber-100 text-amber-900 flex-shrink-0; } .badge-rating-sm { @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-amber-100 text-amber-900; } /* Links */ .link { @apply text-brown-700 hover:text-brown-900 hover:underline transition-colors; } .link-bold { @apply font-medium text-brown-700 hover:text-brown-900 hover:underline transition-colors; } /* Like Button */ .like-btn { @apply inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors min-h-[44px]; } .like-btn-liked { @apply like-btn bg-brown-100 text-red-600 hover:bg-brown-200; animation: like-pop 400ms ease-out; } .like-btn-unliked { @apply like-btn bg-brown-100 text-brown-600 hover:bg-brown-200; animation: like-shrink 200ms ease-out; } /* Share Button */ .share-btn { @apply inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors bg-brown-100 text-brown-600 hover:bg-brown-200 min-h-[44px]; } /* Comment Button */ .comment-btn { @apply inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors bg-brown-100 text-brown-600 hover:bg-brown-200 min-h-[44px]; } /* Comment Section */ .comment-section { @apply mt-8 pt-6 border-t-2 border-brown-200; } .comment-section-header { @apply mb-5; } .comment-count-badge { @apply inline-flex items-center justify-center text-xs font-bold bg-brown-700 text-brown-100 rounded-full min-w-[1.375rem] h-[1.375rem] px-1.5; } .comment-login-prompt { @apply flex items-center gap-3 bg-brown-50 rounded-lg p-4 mb-5 border border-dashed border-brown-300; } .comment-compose { @apply bg-brown-50 rounded-lg p-4 mb-5 border border-brown-200 flex flex-col gap-2; } .comment-textarea { @apply w-full rounded-lg border-2 border-brown-200 bg-white px-3 py-2.5 text-base text-brown-900 placeholder-brown-400 resize-none transition-colors focus:border-brown-500 focus:ring-0 focus:outline-none; } .comment-list { @apply space-y-1; } .comment-empty-state { @apply text-center py-8; } .comment-item { @apply relative rounded-lg p-3 transition-colors hover:bg-brown-50/60; } .comment-item-inner { @apply relative; } .comment-depth-1 { @apply ml-6 pl-4; } .comment-depth-2 { @apply ml-12 pl-4; } .comment-thread-line { @apply absolute left-0 top-3 bottom-3 w-0.5 bg-brown-200 rounded-full; } .comment-reply-btn { @apply inline-flex items-center gap-1 text-brown-400 hover:text-brown-700 transition-colors text-xs font-medium; } .comment-delete-btn { @apply text-brown-300 hover:text-brown-600 transition-colors; } .comment-reply-form { @apply flex flex-col gap-2 bg-brown-50 rounded-lg p-3 border border-brown-200; } /* Action Bar */ .action-bar { @apply flex items-center gap-2 mt-3 pt-3 border-t border-brown-200; } /* Action bar inside brew view container - no separator needed */ .brew-view-actions .action-bar { @apply mt-0 pt-0 border-t-0; } /* Compact action bar variant for comments */ .comment-item .action-bar { @apply mt-1 border-t-0 gap-1 bg-brown-100 rounded-lg px-1.5 py-1 inline-flex items-center; } .comment-item .action-btn, .comment-item .like-btn { @apply px-2 py-1 text-xs min-h-[28px] bg-transparent; } .action-btn { @apply inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors bg-brown-100 text-brown-600 hover:bg-brown-200 cursor-pointer min-h-[44px]; } .action-btn-liked { @apply text-red-600; animation: like-pop 400ms ease-out; } .action-btn-disabled { @apply opacity-50 cursor-not-allowed hover:bg-brown-100; } /* Action Menu (More dropdown) */ .action-menu { @apply absolute left-1/2 -translate-x-1/2 w-36 bg-white rounded-lg shadow-lg border border-brown-200 py-1 z-50; } /* Dropdown menu (top-positioned, for headers/nav) */ .dropdown-menu { @apply absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-brown-200 py-1 z-50; } .dropdown-item { @apply block px-4 py-2 text-sm text-brown-700 hover:bg-brown-50 transition-colors; } .dropdown-item-disabled { @apply block px-4 py-2 text-sm text-brown-400 cursor-not-allowed; } .dropdown-item-mod { @apply text-amber-700 hover:bg-amber-50; } .dropdown-header { @apply px-4 py-2 border-b border-brown-100; } .dropdown-divider { @apply border-t border-brown-100 mt-1 pt-1; } .action-menu-item { @apply flex items-center gap-2 w-full px-3 py-2 text-sm text-brown-700 hover:bg-brown-100 transition-colors cursor-pointer text-left; } .action-menu-item-danger { @apply text-red-600 hover:bg-red-50; } .action-menu-item-warning { @apply text-amber-600 hover:bg-amber-50; } .action-menu-divider { @apply border-t border-brown-200 my-1; } /* Hidden record indicator badge */ .hidden-badge { @apply inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-amber-100 text-amber-700 rounded; } } /* ======================================== HTMX Transition Classes ======================================== */ /* CRITICAL: Prevent content from being stuck invisible during history restore */ /* Override HTMX transition classes during history restoration */ body.htmx-history-restoring main, body.htmx-history-restoring .htmx-swapping, body.htmx-history-restoring .htmx-transitioning { opacity: 1 !important; transform: none !important; animation: none !important; transition: none !important; } /* During swap transition */ .htmx-swapping { opacity: 0; transform: translateY(5px); transition: opacity 100ms ease-out, transform 100ms ease-out; } /* After content is added */ .htmx-added { opacity: 0; animation: fade-in-slide-up 200ms ease-out forwards; } /* During settling phase */ .htmx-settling { opacity: 1; transform: translateY(0); transition: opacity 100ms ease-in, transform 100ms ease-in; } /* Transitioning state (no overlay - looks bad) */ .htmx-transitioning { position: relative; /* Removed overlay ::after pseudo-element - it created unwanted shadow effects */ } /* ======================================== Component-Specific Transitions ======================================== */ /* Feed cards appear with stagger effect */ .feed-card { animation: fade-in-slide-up 300ms ease-out backwards; } .feed-card:nth-child(1) { animation-delay: 0ms; } .feed-card:nth-child(2) { animation-delay: 50ms; } .feed-card:nth-child(3) { animation-delay: 100ms; } .feed-card:nth-child(4) { animation-delay: 150ms; } .feed-card:nth-child(5) { animation-delay: 200ms; } .feed-card:nth-child(n + 6) { animation-delay: 250ms; } /* Table rows slide in with stagger effect (dynamic content) */ .table-body tr { animation: fade-in-slide-up 300ms ease-out backwards; } .table-body tr:nth-child(1) { animation-delay: 0ms; } .table-body tr:nth-child(2) { animation-delay: 30ms; } .table-body tr:nth-child(3) { animation-delay: 60ms; } .table-body tr:nth-child(4) { animation-delay: 90ms; } .table-body tr:nth-child(5) { animation-delay: 120ms; } .table-body tr:nth-child(n + 6) { animation-delay: 150ms; } /* Modal transitions (enhanced) */ .modal-backdrop { animation: fade-in 100ms ease-out; } .modal-content { animation: modal-appear 150ms ease-out; } @keyframes modal-appear { from { opacity: 0; transform: scale(0.9) translateY(-20px); } to { opacity: 1; transform: scale(1) translateY(0); } } /* HTML5 Dialog Element Transitions */ .modal-dialog { opacity: 0; transform: scale(0.95); transition: opacity 100ms ease-out, transform 100ms ease-out, overlay 100ms ease-out allow-discrete, display 100ms ease-out allow-discrete; } .modal-dialog[open] { opacity: 1; transform: scale(1); } /* Backdrop fade-in transition */ .modal-dialog::backdrop { background-color: rgba(0, 0, 0, 0); transition: background-color 100ms ease-out, backdrop-filter 100ms ease-out, display 100ms ease-out allow-discrete, overlay 100ms ease-out allow-discrete; } .modal-dialog[open]::backdrop { background-color: rgba(0, 0, 0, 0.4); } /* Starting style for new browsers that support @starting-style */ @supports (transition-behavior: allow-discrete) { @starting-style { .modal-dialog[open] { opacity: 0; transform: scale(0.95); } .modal-dialog[open]::backdrop { background-color: rgba(0, 0, 0, 0); } } } /* Modal content inside dialog - no separate animation needed */ /* The dialog element itself handles the transition */ /* Form elements slide in */ .form-input, .form-select, .form-textarea { transition: border-color 100ms ease, box-shadow 100ms ease, transform 50ms ease; } .form-input:focus, .form-select:focus, .form-textarea:focus { transform: translateY(-1px); } /* ======================================== Keyframes ======================================== */ @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes fade-in-slide-up { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* Like button pop animation */ @keyframes like-pop { 0% { transform: scale(1); } 15% { transform: scale(1.3); } 30% { transform: scale(0.9); } 45% { transform: scale(1.15); } 60% { transform: scale(0.95); } 75% { transform: scale(1.05); } 100% { transform: scale(1); } } /* Like button shrink animation for unlike */ @keyframes like-shrink { 0% { transform: scale(1); } 50% { transform: scale(0.8); } 100% { transform: scale(1); } } /* ======================================== Loading States ======================================== */ /* Enhance skeleton loading animations */ .animate-pulse { animation: enhanced-pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite; } @keyframes enhanced-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } /* ======================================== Reduced Motion Override ======================================== */ /* Respect user's motion preferences */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } }