/* ═══════════════════════════════════════════════════════════
   Toasts — transient confirmation of something that just happened.

   The system had only .tn-alert, which is a PERSISTENT, in-flow banner: it
   occupies layout and stays until dismissed. That's right for "this form
   has errors" and wrong for "checked in 12 units" — an action confirmation
   that pushes the page down every time an operator scans something makes
   the layout jump under their hands.

   A toast is the other half: overlaid, self-dismissing, and it never moves
   the content. For a scanning flow this is the main feedback channel, so
   two things matter more than usual.

   • It must be readable at arm's length without stopping work. Hence the
     bottom placement on phones (near the thumb, away from the top bar) and
     the icon+text pairing.
   • Success must not depend on colour alone — an operator glancing at a
     green smear needs the icon and the count to tell them what happened.

   Anything the user must ACT on is not a toast. If it needs a decision, it
   is a modal; if it must persist, it is an alert.

     <div class="tn-toaster">                       <- one per app, fixed
       <div class="tn-toast tn-toast--success">
         <i class="bi bi-check-circle-fill tn-toast-icon"></i>
         <div class="tn-toast-body">
           <p class="tn-toast-title">Incheckad</p>
           <p class="tn-toast-text">12 st Pallyft 2000 · Centrallager</p>
         </div>
         <button class="tn-toast-close">…</button>
       </div>
     </div>
   ═══════════════════════════════════════════════════════════ */

.tn-toaster {
    position: fixed;
    z-index: var(--tn-z-modal);
    display: flex;
    flex-direction: column;
    gap: var(--tn-space-sm);
    /* Bottom-right on desktop: out of the way of both the top bar and the
       content the user is reading. */
    right: var(--tn-page-pad-x);
    bottom: var(--tn-page-pad-x);
    width: min(380px, calc(100vw - 2 * var(--tn-page-pad-x)));
    /* The stack is a pass-through layer; only the toasts take pointer events,
       so it never blocks clicks on the page underneath. */
    pointer-events: none;
}

.tn-toast {
    display: flex;
    align-items: flex-start;
    gap: var(--tn-space-sm);
    padding: var(--tn-space-md);
    background: var(--tn-bg-card);
    border: 1px solid var(--tn-border);
    border-left: 3px solid var(--tn-text-muted);
    border-radius: var(--tn-radius-lg);
    box-shadow: var(--tn-shadow-lg);
    pointer-events: auto;
    animation: tnToastIn 0.22s cubic-bezier(0.32, 0.72, 0, 1);
}

@keyframes tnToastIn {
    from { opacity: 0; transform: translateY(10px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}

/* Leaving is a class the app adds before removing the node, so the exit is
   animated rather than the toast vanishing mid-read. */
.tn-toast.is-leaving {
    animation: tnToastOut 0.16s ease forwards;
}

@keyframes tnToastOut {
    to { opacity: 0; transform: translateY(6px); }
}

.tn-toast-icon {
    flex-shrink: 0;
    font-size: 1.1rem;
    line-height: 1.3;
}

.tn-toast-body {
    flex: 1;
    min-width: 0;
}

.tn-toast-title {
    margin: 0;
    font-size: var(--tn-font-size-base);
    font-weight: 700;
    color: var(--tn-text-primary);
}

.tn-toast-text {
    margin: 2px 0 0;
    font-size: var(--tn-font-size-sm);
    color: var(--tn-text-secondary);
    overflow-wrap: anywhere;
}

/* An optional single action — "Undo" after a destructive-ish operation.
   One action only: a toast the user has to study is the wrong component. */
.tn-toast-action {
    align-self: flex-start;
    margin-top: var(--tn-space-sm);
    padding: 0;
    border: none;
    background: none;
    color: var(--tn-accent);
    font-family: inherit;
    font-size: var(--tn-font-size-sm);
    font-weight: 700;
    cursor: pointer;
}

.tn-toast-action:hover {
    text-decoration: underline;
}

.tn-toast-close {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    margin: -4px -4px 0 0;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--tn-text-muted);
    cursor: pointer;
    transition: background var(--tn-transition), color var(--tn-transition);
}

.tn-toast-close:hover {
    background: var(--tn-bg-hover);
    color: var(--tn-text-primary);
}

.tn-toast-close:focus-visible,
.tn-toast-action:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--tn-accent);
}

/* ── Tones. The left border carries the state at a glance; the icon carries
   it for anyone who can't use the colour. Both, always. ── */
.tn-toast--success { border-left-color: var(--tn-success); }
.tn-toast--success .tn-toast-icon { color: var(--tn-success); }

.tn-toast--error { border-left-color: var(--tn-danger); }
.tn-toast--error .tn-toast-icon { color: var(--tn-danger); }

.tn-toast--warning { border-left-color: var(--tn-warning); }
.tn-toast--warning .tn-toast-icon { color: var(--tn-warning); }

.tn-toast--info { border-left-color: var(--tn-accent); }
.tn-toast--info .tn-toast-icon { color: var(--tn-accent); }

/* ── Phones — full width at the BOTTOM, above the bottom nav.
   Top placement would collide with the fixed top bar, and on a scanning
   screen the operator's attention is already at the bottom of the phone. ── */
@media (max-width: 640px) {
    .tn-toaster {
        left: var(--tn-page-pad-x);
        right: var(--tn-page-pad-x);
        width: auto;
        /* Clear the bottom nav plus the home indicator */
        bottom: calc(var(--tn-space-sm) + 64px + env(safe-area-inset-bottom, 0px));
    }
}

/* A toast that slides is motion; a toast that appears is not. Keep the
   appearance, drop the movement. */
@media (prefers-reduced-motion: reduce) {
    .tn-toast,
    .tn-toast.is-leaving {
        animation: none;
    }
}
