@layer components { /* ── Toast stack: fixed top-right overlay for all flash messages ────── */ .app-toast-stack { position: fixed; top: calc(var(--app-spacing) * 1.5); right: calc(var(--app-spacing) * 1.5); z-index: 1100; display: flex; flex-direction: column; gap: calc(var(--app-spacing) * 0.5); width: 400px; max-width: calc(100vw - var(--app-spacing) * 3); pointer-events: none; } .app-toast-stack:empty { display: none; } /* In admin layout: offset below topbar */ .app-container ~ .app-toast-stack, body:has(.app-container) .app-toast-stack { top: calc(52px + var(--app-spacing)); } @media (max-width: 576px) { .app-toast-stack { top: calc(var(--app-spacing) * 1); right: calc(var(--app-spacing) * 1); left: calc(var(--app-spacing) * 1); width: auto; } body:has(.app-container) .app-toast-stack { top: calc(52px + var(--app-spacing) * 0.5); right: calc(var(--app-spacing) * 0.5); left: calc(var(--app-spacing) * 0.5); } } /* ── Individual toast notice ─────────────────────────────────────────── */ .app-toast-stack .notice { --app-notice-border-color: var(--app-muted-border-color); --app-notice-background-color: var(--app-background-color); --app-notice-accent-color: var(--app-color); --_toast-tint: transparent; background: linear-gradient(var(--_toast-tint), var(--_toast-tint)), var(--app-background-color); border: 1px solid color-mix(in srgb, var(--app-notice-border-color) 40%, transparent); border-left: 3px solid var(--app-notice-border-color); border-radius: var(--app-border-radius); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), 0 1px 3px rgba(0, 0, 0, 0.08); padding: calc(var(--app-spacing) * 0.625) calc(var(--app-spacing) * 0.875); padding-right: calc(var(--app-spacing) * 0.5); display: flex; align-items: center; justify-content: space-between; gap: calc(var(--app-spacing) * 0.75); position: relative; overflow: hidden; color: var(--app-notice-border-color); font-size: 0.875rem; line-height: 1.4; pointer-events: auto; margin: 0; animation: toast-slide-in 250ms ease-out; } .app-toast-stack .notice.toast-dismissing { animation: toast-slide-out 200ms ease-in forwards; } @keyframes toast-slide-in { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } } @keyframes toast-slide-out { from { opacity: 1; transform: translateX(0); } to { opacity: 0; transform: translateX(20px); } } @media (prefers-reduced-motion: reduce) { .app-toast-stack .notice, .app-toast-stack .notice.toast-dismissing { animation: none; } } /* ── Variant colors ──────────────────────────────────────────────────── */ .app-toast-stack .notice strong { color: var(--app-notice-accent-color); } .app-toast-stack .notice[data-variant=success] { --app-notice-border-color: var(--app-notice-success-border-color); --_toast-tint: var(--app-notice-success-background-color); --app-notice-accent-color: var(--app-notice-success-color); } .app-toast-stack .notice[data-variant=warning] { --app-notice-border-color: var(--app-notice-warning-border-color); --_toast-tint: var(--app-notice-warning-background-color); --app-notice-accent-color: var(--app-notice-warning-color); } .app-toast-stack .notice[data-variant=info] { --app-notice-border-color: var(--app-notice-info-border-color); --_toast-tint: var(--app-notice-info-background-color); --app-notice-accent-color: var(--app-notice-info-color); } .app-toast-stack .notice[data-variant=error] { --app-notice-border-color: var(--app-notice-error-border-color); --_toast-tint: var(--app-notice-error-background-color); --app-notice-accent-color: var(--app-notice-error-color); } /* ── Progress bar timer ──────────────────────────────────────────────── */ .app-toast-stack .notice.flash-timed::after { content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; background: var(--app-notice-border-color); transform-origin: left; transform: scaleX(0); animation: flash-progress var(--flash-timeout, 4000ms) linear forwards; opacity: 0.7; } .app-toast-stack .notice.flash-timed:hover::after { animation-play-state: paused; } @keyframes flash-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } } @media (prefers-reduced-motion: reduce) { .app-toast-stack .notice.flash-timed::after { animation: none; } } /* ── Dismiss button ──────────────────────────────────────────────────── */ .app-toast-stack .notice button[type=submit], .app-toast-stack .notice button[type=button] { border: none !important; background: transparent; color: var(--app-notice-border-color); opacity: 0.6; cursor: pointer; padding: 2px 4px; line-height: 1; flex-shrink: 0; } .app-toast-stack .notice button[type=submit]:hover, .app-toast-stack .notice button[type=button]:hover { opacity: 1; } /* ── List formatting inside notices ──────────────────────────────────── */ .app-toast-stack .notice ul { margin: 0; padding-left: 0; } .app-toast-stack .notice li { list-style: none; } .app-toast-stack .notice li:last-child { margin: 0; } }