diff --git a/src/components/BlockedNavigationToast.js b/src/components/BlockedNavigationToast.js new file mode 100644 index 0000000..3c95e52 --- /dev/null +++ b/src/components/BlockedNavigationToast.js @@ -0,0 +1,45 @@ +/** + * Muestra un toast de aviso cuando se bloquea la navegación + * @param {string} message - Mensaje a mostrar + * @param {number} duration - Duración en ms (default: 3000) + */ +export function showBlockedNavigationToast(message = 'Guarda los cambios antes de salir', duration = 3000) { + // Eliminar toast existente si hay uno + const existingToast = document.querySelector('.blocked-nav-toast'); + if (existingToast) { + existingToast.remove(); + } + + const toast = document.createElement('div'); + toast.className = 'blocked-nav-toast'; + + toast.innerHTML = /*html*/` +
| Fecha | +Importe | +Referencia | +Tipo | +Nº Transacción | +
|---|---|---|---|---|
| ${formatDate(p.paymentDate)} | +${formatCurrency(p.amount)} | +${p.ref || '-'} | +${p.type || '-'} | +${p.transactionNum || '-'} | +
| + | Nombre / Código | +Tipo | +Estado | +Teléfono | +Acciones | +|
|---|---|---|---|---|---|---|
| Cargando clientes... | +||||||
No hay contactos registrados
'; + + modal.innerHTML = /*html*/` +Página en construcción...
'; - return div; - } + render: renderClientesPage }, { route: 'settings', diff --git a/src/router.js b/src/router.js index a5f7380..89ac757 100755 --- a/src/router.js +++ b/src/router.js @@ -2,16 +2,69 @@ import { auth } from './services/auth.js'; import { renderLoginPage } from './pages/LoginPage.js'; import { createSidebar } from './components/Sidebar.js'; import { getAvailablePages, getPageByRoute } from './pages/pagesRegistry.js'; +import { showConfirmExitModal } from './components/ConfirmExitModal.js'; // 🔧 Modo DEV: cambiar a false para activar login const DEV_MODE = false; +// Sistema de guards para bloquear navegación +const navigationGuards = { + currentGuard: null, + + // Registrar un guard (función que devuelve true si hay cambios sin guardar) + register(checkFn) { + this.currentGuard = checkFn; + }, + + // Eliminar el guard actual + unregister() { + this.currentGuard = null; + }, + + // Verificar si hay cambios pendientes + hasUnsavedChanges() { + return this.currentGuard ? this.currentGuard() : false; + } +}; + +// Exportar para uso en páginas +export { navigationGuards }; + export function initRouter() { const app = document.querySelector('#app'); + let isNavigating = false; + let pendingHash = null; - function navigate() { + async function navigate(forceNavigate = false) { const hash = window.location.hash || (DEV_MODE ? '#dashboard' : '#login'); - const route = hash.substring(1); // Remove the # symbol + const route = hash.substring(1); + + // Si hay un guard activo y no estamos forzando navegación + if (!forceNavigate && navigationGuards.hasUnsavedChanges()) { + // Guardar el hash al que se quiere ir + pendingHash = hash; + + // Volver al hash anterior temporalmente + const previousHash = '#' + (document.querySelector('.main-content')?.dataset?.currentRoute || 'dashboard'); + history.pushState(null, '', previousHash); + + // Mostrar modal de confirmación + const confirmed = await showConfirmExitModal({ + title: 'Cambios sin guardar', + message: 'Tienes cambios sin guardar. ¿Seguro que quieres salir?', + confirmText: 'Salir sin guardar', + cancelText: 'Seguir editando' + }); + + if (confirmed) { + // Usuario confirmó salir - limpiar guard y navegar + navigationGuards.unregister(); + window.location.hash = pendingHash; + } + // Si no confirmó, ya estamos en la página correcta + pendingHash = null; + return; + } if (!DEV_MODE && !auth.checkAuth() && hash !== '#login') { window.location.hash = '#login'; @@ -33,6 +86,7 @@ export function initRouter() { // Create main content area const mainContent = document.createElement('main'); mainContent.className = 'main-content'; + mainContent.dataset.currentRoute = route; // Guardar ruta actual // Get page from registry const page = getPageByRoute(route); @@ -69,6 +123,6 @@ export function initRouter() { } } - window.addEventListener('hashchange', navigate); - navigate(); + window.addEventListener('hashchange', () => navigate(false)); + navigate(true); // Primera navegación sin guard } diff --git a/src/services/clients.js b/src/services/clients.js new file mode 100644 index 0000000..859a037 --- /dev/null +++ b/src/services/clients.js @@ -0,0 +1,35 @@ +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; + +function getAuthHeaders() { + const token = localStorage.getItem('token'); + return { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }; +} + +export async function getClients(limit = 50, page = 1) { + const response = await fetch(`${API_BASE_URL}/api/Clients?limit=${limit}&page=${page}`, { + method: 'GET', + headers: getAuthHeaders() + }); + + if (!response.ok) { + throw new Error('Error al obtener los clientes'); + } + + return await response.json(); +} + +export async function getClientById(id) { + const response = await fetch(`${API_BASE_URL}/api/Clients/${id}`, { + method: 'GET', + headers: getAuthHeaders() + }); + + if (!response.ok) { + throw new Error('Error al obtener el cliente'); + } + + return await response.json(); +} diff --git a/src/services/invoices.js b/src/services/invoices.js index bcfc386..11c8f6f 100644 --- a/src/services/invoices.js +++ b/src/services/invoices.js @@ -80,6 +80,49 @@ export async function addInvoiceLine(id, lineData) { return await response.json(); } +export async function deleteInvoiceLine(invoiceId, lineId) { + const response = await fetch(`${API_BASE_URL}/api/Invoices/${invoiceId}/lines/${lineId}`, { + method: 'DELETE', + headers: getAuthHeaders() + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({})); + throw new Error(error.detail || 'Error al eliminar línea'); + } + + return true; +} + +export async function addPayment(invoiceId, paymentData) { + const response = await fetch(`${API_BASE_URL}/api/Invoices/${invoiceId}/payments`, { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify(paymentData) + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({})); + throw new Error(error.detail || 'Error al registrar el pago'); + } + + return await response.json(); +} + +export async function getPayments(invoiceId) { + const response = await fetch(`${API_BASE_URL}/api/Invoices/${invoiceId}/payments`, { + method: 'GET', + headers: getAuthHeaders() + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({})); + throw new Error(error.detail || 'Error al obtener los pagos'); + } + + return await response.json(); +} + export async function deleteInvoice(id) { // Nota: No hay endpoint DELETE en el OpenAPI spec, // pero podríamos usar el endpoint de cambio de estado a "canceled" diff --git a/src/style.css b/src/style.css index d7b47c7..5ad7f13 100755 --- a/src/style.css +++ b/src/style.css @@ -1099,4 +1099,554 @@ button:focus-visible { .btn-delete-compact { grid-column: 2; } -} \ No newline at end of file +} + +/* ========================================== + CLIENTES PAGE STYLES + ========================================== */ + +.clientes-page { + padding: 2rem; + max-width: 1600px; + margin: 0 auto; +} + +.clientes-header { + margin-bottom: 2rem; +} + +.clientes-header h1 { + color: var(--text-primary); + font-size: 2rem; + margin: 0; +} + +.clientes-filters { + display: flex; + gap: 1rem; + margin-bottom: 1.5rem; + align-items: center; +} + +.clientes-filters .search-input { + flex: 1; + max-width: 500px; +} + +/* Clients Table */ +.clients-table-container { + background: var(--card-bg); + border-radius: 12px; + border: 1px solid var(--border-color); + overflow: hidden; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); +} + +.clients-table { + width: 100%; + border-collapse: collapse; +} + +.clients-table thead { + background: #f8fafc; + border-bottom: 2px solid var(--border-color); +} + +.clients-table th { + font-weight: 600; + color: var(--text-secondary); + font-size: 0.85rem; + text-transform: uppercase; + padding: 1rem 1.5rem; + text-align: left; +} + +.clients-table tbody tr { + border-bottom: 1px solid var(--border-color); + transition: background-color 0.2s; +} + +.clients-table tbody tr:hover { + background-color: #f8fafc; +} + +.clients-table tbody tr:last-child { + border-bottom: none; +} + +.clients-table td { + padding: 1rem 1.5rem; + vertical-align: middle; +} + +.client-avatar-col { + width: 60px; +} + +.client-avatar-cell { + width: 60px; +} + +.client-avatar { + width: 42px; + height: 42px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: 600; + font-size: 0.9rem; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.client-name-wrapper { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.client-company-name { + font-weight: 600; + color: var(--text-primary); +} + +.client-code { + font-size: 0.8rem; + color: var(--text-secondary); + font-family: monospace; +} + +.contact-info { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.contact-name { + color: var(--text-primary); +} + +.contacts-count { + font-size: 0.75rem; + color: var(--primary); + background: rgba(37, 99, 235, 0.1); + padding: 0.15rem 0.5rem; + border-radius: 10px; + display: inline-block; + width: fit-content; +} + +.client-email .email-text { + color: var(--text-secondary); + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; +} + +.client-phone { + color: var(--text-primary); + font-family: monospace; +} + +.client-actions { + text-align: center; +} + +.client-actions .btn-action { + margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; +} + +.clients-table .client-actions-col { + width: 80px; + text-align: center; +} + +.clients-table td.client-actions { + text-align: center; + vertical-align: middle; +} + +.clients-table td.client-actions .btn-view { + margin: 0 auto; +} + +/* Status badge for clients */ +.status-badge-client { + display: inline-block; + padding: 0.2rem 0.6rem; + border-radius: 12px; + font-size: 0.8rem; + font-weight: 500; + white-space: nowrap; +} + +.status-badge-client.status-active { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +.status-badge-client.status-inactive { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-badge-client.status-unknown { + background: rgba(100, 116, 139, 0.1); + color: #64748b; +} + +/* Contacts button */ +.btn-contacts { + position: relative; + background: rgba(37, 99, 235, 0.08); + border: 1px solid rgba(37, 99, 235, 0.2); + color: var(--primary); + border-radius: 6px; + padding: 0.35rem 0.5rem; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + gap: 0.15rem; +} + +.btn-contacts:hover { + background: rgba(37, 99, 235, 0.15); + border-color: var(--primary); +} + +.btn-contacts.active { + background: var(--primary); + color: white; + border-color: var(--primary); +} + +.contacts-badge { + font-size: 0.7rem; + font-weight: 700; + min-width: 16px; + text-align: center; +} + +/* Expandable contacts row */ +.contacts-expand-row td { + padding: 0 !important; + background: #f8fafc; +} + +.contacts-expand-content { + padding: 1rem 1.5rem; + border-top: 1px solid var(--border-color); + border-bottom: 2px solid var(--primary); + animation: expandDown 0.2s ease-out; +} + +@keyframes expandDown { + from { + opacity: 0; + max-height: 0; + } + to { + opacity: 1; + max-height: 500px; + } +} + +.contacts-expand-header h4 { + margin: 0 0 0.75rem 0; + font-size: 0.85rem; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.contacts-expand-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 0.75rem; +} + +.contact-expand-card { + background: white; + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 0.75rem 1rem; +} + +.contact-expand-name { + display: flex; + align-items: center; + gap: 0.5rem; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 0.5rem; + font-size: 0.95rem; +} + +.contact-expand-name svg { + color: var(--primary); + flex-shrink: 0; +} + +.contact-expand-details { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.3rem 1rem; + font-size: 0.8rem; + color: var(--text-secondary); +} + +.contact-expand-details span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.no-clients { + padding: 3rem; + text-align: center; + color: var(--text-secondary); + font-size: 1.1rem; +} + +/* Client Modal */ +.client-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 1rem; +} + +.client-modal { + background: var(--card-bg); + border-radius: 12px; + width: 100%; + max-width: 600px; + max-height: 80vh; + overflow: hidden; + display: flex; + flex-direction: column; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); +} + +.client-modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1.5rem; + border-bottom: 1px solid var(--border-color); + background: #f8fafc; +} + +.client-modal-header h2 { + margin: 0; + font-size: 1.5rem; + color: var(--text-primary); +} + +.btn-close-modal { + background: transparent; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: var(--text-secondary); + padding: 0.25rem 0.5rem; + border-radius: 4px; + transition: all 0.2s; +} + +.btn-close-modal:hover { + background: var(--border-color); + color: var(--text-primary); +} + +.client-modal-body { + padding: 1.5rem; + overflow-y: auto; +} + +.client-info-section, +.contacts-section { + margin-bottom: 1.5rem; +} + +.client-info-section h3, +.contacts-section h3 { + margin: 0 0 1rem 0; + font-size: 1rem; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.info-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; +} + +.info-item { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.info-label { + font-size: 0.8rem; + color: var(--text-secondary); + text-transform: uppercase; +} + +.info-value { + font-weight: 600; + color: var(--text-primary); +} + +.contacts-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.contact-card { + background: #f8fafc; + border-radius: 8px; + padding: 1rem; + border: 1px solid var(--border-color); +} + +.contact-header { + margin-bottom: 0.75rem; +} + +.contact-full-name { + font-weight: 600; + color: var(--text-primary); + font-size: 1.05rem; +} + +.contact-details { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.contact-row { + display: flex; + gap: 0.5rem; + font-size: 0.9rem; + color: var(--text-secondary); +} + +.contact-row .label { + min-width: 120px; +} + +.no-contacts { + color: var(--text-secondary); + text-align: center; + padding: 2rem; + background: #f8fafc; + border-radius: 8px; +} + +/* Responsive Clients */ +@media (max-width: 1024px) { + .clientes-page { + padding: 1rem; + } + + .clients-table th, + .clients-table td { + padding: 0.75rem 1rem; + } + + .client-email-col, + .client-email, + .client-type-col, + .client-type { + display: none; + } +} + +@media (max-width: 768px) { + .clientes-filters { + flex-direction: column; + } + + .clientes-filters .search-input { + max-width: 100%; + } + + .clients-table thead { + display: none; + } + + .clients-table tbody tr.client-item { + display: flex; + flex-wrap: wrap; + padding: 1rem; + gap: 0.5rem; + position: relative; + } + + .clients-table tbody tr.contacts-expand-row { + display: block; + } + + .clients-table td { + padding: 0; + } + + .client-avatar-cell { + width: auto; + margin-right: 1rem; + } + + .client-name { + flex: 1; + } + + .client-status, + .client-phone { + width: 100%; + padding-left: 58px; + } + + .client-type-col, + .client-type { + display: none; + } + + .client-actions { + position: absolute; + right: 1rem; + top: 50%; + transform: translateY(-50%); + } + + .contacts-expand-grid { + grid-template-columns: 1fr; + } + + .contact-expand-details { + grid-template-columns: 1fr; + } + + .info-grid { + grid-template-columns: 1fr; + } +} diff --git a/src/styles/modal.css b/src/styles/modal.css index d4a72fe..5869979 100644 --- a/src/styles/modal.css +++ b/src/styles/modal.css @@ -245,6 +245,38 @@ font-style: italic; } +/* Delete line button */ +.line-actions-col { + width: 50px; +} + +.line-actions { + text-align: center; +} + +.btn-delete-line { + background: none; + border: none; + cursor: pointer; + color: var(--text-secondary); + padding: 0.35rem; + border-radius: 6px; + display: inline-flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.btn-delete-line:hover { + color: var(--danger, #ef4444); + background: rgba(239, 68, 68, 0.1); +} + +.btn-delete-line:disabled { + opacity: 0.5; + cursor: not-allowed; +} + /* Invoice Totals */ .invoice-totals { margin-top: 1.5rem; @@ -265,6 +297,10 @@ font-size: 0.9375rem; } +.total-row.paid { + color: var(--success, #22c55e); +} + .total-row.pending { font-size: 1.125rem; color: var(--primary); @@ -272,6 +308,89 @@ border-top: 2px solid var(--border-color); } +/* Payment Section */ +.payment-section { + border-top: 2px solid var(--border-color); + padding-top: 1.25rem; + margin-top: 1rem; +} + +.payment-section h3 { + margin: 0 0 1rem; + font-size: 1.05rem; + color: var(--text-primary); +} + +.payment-form .form-row { + display: flex; + gap: 1rem; + margin-bottom: 0.75rem; +} + +.payment-form .form-group { + flex: 1; +} + +.payment-form label { + display: block; + font-size: 0.8125rem; + font-weight: 500; + margin-bottom: 0.35rem; + color: var(--text-secondary); +} + +.payment-form input { + width: 100%; + padding: 0.5rem 0.75rem; + border: 1px solid var(--border-color); + border-radius: 6px; + font-size: 0.875rem; + background: var(--card-bg); + color: var(--text-primary); + box-sizing: border-box; +} + +.payment-form input:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); +} + +.payment-hint { + display: block; + font-size: 0.75rem; + color: var(--text-secondary); + margin-top: 0.25rem; +} + +.btn-pay { + padding: 0.5rem 1.25rem; + font-size: 0.875rem; + white-space: nowrap; +} + +.btn-pay:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* Payment History */ +.payments-history-section { + border-top: 2px solid var(--border-color); + padding-top: 1.25rem; + margin-top: 1rem; +} + +.payments-history-section h3 { + margin: 0 0 0.75rem; + font-size: 1.05rem; + color: var(--text-primary); +} + +.payment-amount-cell { + color: var(--success, #22c55e); +} + /* Button Styles */ .btn-small { padding: 0.5rem 1rem; @@ -390,4 +509,242 @@ .footer-actions-right button { flex: 1; } +} + +/* ============================================ + Confirm Exit Modal Styles + ============================================ */ +.confirm-exit-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 2000; + padding: 1rem; + animation: confirmFadeIn 0.2s ease-out; +} + +.confirm-exit-overlay.closing { + animation: confirmFadeOut 0.2s ease-out forwards; +} + +@keyframes confirmFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes confirmFadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +.confirm-exit-modal { + background: var(--card-bg, #ffffff); + border-radius: 16px; + padding: 2rem; + max-width: 400px; + width: 100%; + text-align: center; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + animation: confirmSlideUp 0.3s ease-out; +} + +.confirm-exit-overlay.closing .confirm-exit-modal { + animation: confirmSlideDown 0.2s ease-out forwards; +} + +@keyframes confirmSlideUp { + from { + transform: translateY(20px) scale(0.95); + opacity: 0; + } + to { + transform: translateY(0) scale(1); + opacity: 1; + } +} + +@keyframes confirmSlideDown { + from { + transform: translateY(0) scale(1); + opacity: 1; + } + to { + transform: translateY(20px) scale(0.95); + opacity: 0; + } +} + +.confirm-exit-icon { + width: 72px; + height: 72px; + margin: 0 auto 1.25rem; + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.confirm-exit-icon svg { + color: #d97706; +} + +.confirm-exit-title { + margin: 0 0 0.75rem; + font-size: 1.375rem; + font-weight: 600; + color: var(--text-primary, #1e293b); +} + +.confirm-exit-message { + margin: 0 0 1.75rem; + font-size: 0.9375rem; + color: var(--text-secondary, #64748b); + line-height: 1.5; +} + +.confirm-exit-actions { + display: flex; + gap: 0.75rem; +} + +.confirm-exit-actions button { + flex: 1; + padding: 0.75rem 1.25rem; + border-radius: 8px; + font-size: 0.9375rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + border: none; +} + +.confirm-exit-actions .btn-stay { + background-color: var(--primary, #2563eb); + color: white; +} + +.confirm-exit-actions .btn-stay:hover { + background-color: #1d4ed8; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3); +} + +.confirm-exit-actions .btn-exit { + background-color: #f1f5f9; + color: var(--text-primary, #1e293b); + border: 1px solid var(--border-color, #e2e8f0); +} + +.confirm-exit-actions .btn-exit:hover { + background-color: #e2e8f0; + border-color: #cbd5e1; +} + +/* Responsive Confirm Modal */ +@media (max-width: 480px) { + .confirm-exit-modal { + padding: 1.5rem; + margin: 1rem; + } + + .confirm-exit-icon { + width: 60px; + height: 60px; + } + + .confirm-exit-icon svg { + width: 36px; + height: 36px; + } + + .confirm-exit-title { + font-size: 1.25rem; + } + + .confirm-exit-actions { + flex-direction: column-reverse; + } +} + +/* ============================================ + Blocked Navigation Toast Styles + ============================================ */ +.blocked-nav-toast { + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%) translateY(-100px); + z-index: 3000; + opacity: 0; + transition: all 0.3s ease-out; + pointer-events: none; +} + +.blocked-nav-toast.visible { + transform: translateX(-50%) translateY(0); + opacity: 1; +} + +.blocked-nav-toast.hiding { + transform: translateX(-50%) translateY(-20px); + opacity: 0; +} + +.blocked-nav-toast-content { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.875rem 1.25rem; + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); + border: 1px solid #fbbf24; + border-radius: 10px; + box-shadow: 0 10px 25px -5px rgba(251, 191, 36, 0.3), + 0 4px 6px -2px rgba(0, 0, 0, 0.1); +} + +.blocked-nav-icon { + color: #d97706; + flex-shrink: 0; +} + +.blocked-nav-message { + font-size: 0.9375rem; + font-weight: 500; + color: #92400e; + white-space: nowrap; +} + +@media (max-width: 480px) { + .blocked-nav-toast { + left: 1rem; + right: 1rem; + transform: translateX(0) translateY(-100px); + } + + .blocked-nav-toast.visible { + transform: translateX(0) translateY(0); + } + + .blocked-nav-toast.hiding { + transform: translateX(0) translateY(-20px); + } + + .blocked-nav-message { + white-space: normal; + } } \ No newline at end of file