/* =============================================================================
   app-toast.css  —  wwwroot/appcss/common/

   A dark toast on a light app. Deliberate: this is a message that arrives
   unannounced over whatever the user was reading, and a white card on a white
   page has to shout with borders and shadows to be noticed at all. A dark slab
   is simply seen — so it can be quieter about everything else.

   It also lets the colour do one job properly. On white, the type had to be
   carried by a 3px rail that was barely visible; on dark, a single lit icon
   says success / error / warning at a glance and nothing else needs colouring.

   STRUCTURE
     .toastify.aura-shell     positioning only — Toastify owns it, we blank it
       └ .aura-toast          the visible card, ours, safe to animate
           ├ .aura-toast-icon
           ├ .aura-toast-msg
           ├ .aura-toast-close
               (the countdown is a ring inside .aura-toast-icon)

   Two things drove that split:

   1. Toastify centres a toast with margin:auto and applies `offset` as an
      inline transform: translate(). Anything animated on .toastify overrides
      that offset. The card is ours, so it animates freely.

   2. Toastify's own close button is appended as a SIBLING of the node, so it
      sits outside the card. Ours is inside it, which is the only way it stays
      put as the message length changes.
   ============================================================================= */


/* ============================================================
   SHELL — stripped back to a positioner

   width:max-content is what stops the stretch: without it the
   card filled its max-width and left a gap of dead space before
   the close button on every short message.
   ============================================================ */

.toastify.aura-shell {
    display: block;
    width: max-content;
    max-width: min(440px, calc(100vw - 32px));
    padding: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
}


/* ============================================================
   CARD
   ============================================================ */

.aura-toast {
    --aura-surface: 22, 28, 38;

    position: relative;
    display: flex;
    align-items: center;
    gap: 13px;
    overflow: hidden;
    padding: 12px 14px;
    border: 1px solid rgba(255, 255, 255, .09);
    border-radius: 14px;
    background: rgb(var(--aura-surface));
    /* The inset line along the top is the whole trick behind a card looking
       lifted rather than printed: it reads as the one edge catching the light. */
    box-shadow: 0 18px 42px rgba(8, 13, 22, .34),
                0 4px 12px rgba(8, 13, 22, .22),
                inset 0 1px 0 rgba(255, 255, 255, .09);
    animation: aura-toast-in .3s cubic-bezier(.22, .61, .36, 1) both;
}

/* Frosted where the browser allows it. Behind glass the page still shows
   through, so the toast sits IN the app rather than on top of a screenshot of
   it — and it needs an alpha surface for that, hence the rgb() split above. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .aura-toast {
        background: rgba(var(--aura-surface), .86);
        -webkit-backdrop-filter: blur(20px) saturate(150%);
        backdrop-filter: blur(20px) saturate(150%);
    }
}

/* In dark mode the page is already near this value, so the toast lifts a step
   above it — otherwise it stops looking like something that arrived. */
[data-bs-theme="dark"] .aura-toast {
    --aura-surface: 44, 54, 68;
}

/* A breath of the type's colour bleeding out from behind the icon. Barely
   there on purpose — enough that a red toast feels warm before it is read,
   not so much that it competes with the message. */
.aura-toast::before {
    content: "";
    position: absolute;
    top: -60%;
    left: -30px;
    width: 190px;
    height: 220%;
    pointer-events: none;
    background: radial-gradient(closest-side,
                                rgba(var(--aura-tone-rgb), .28),
                                transparent);
    opacity: .75;
}

/* ---- the type ----
   One variable per type; everything below reads from it.

   These are the bright Velzon values, not the darkened ink used elsewhere:
   that darkening exists for coloured text on WHITE. Here the ground is dark,
   so the relationship inverts and the bright tone is the legible one. Primary
   (#405189) is too dark to read against this surface at all, so info borrows
   the lighter --vz-info. */
.aura-toast-success {
    --aura-tone: #0ab39c;
    --aura-tone-rgb: 10, 179, 156;
}

.aura-toast-danger {
    --aura-tone: #f06548;
    --aura-tone-rgb: 240, 101, 72;
}

.aura-toast-warning {
    --aura-tone: #f7b84b;
    --aura-tone-rgb: 247, 184, 75;
}

.aura-toast-primary {
    --aura-tone: #299cdb;
    --aura-tone-rgb: 41, 156, 219;
}


/* ---- icon ----
   The only coloured thing on the card, and the only thing that needs to be.
   The ring gives it a little depth so it reads as lit rather than printed. */
.aura-toast-icon {
    position: relative;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    line-height: 1;
    color: var(--aura-tone);
    background: rgba(var(--aura-tone-rgb), .15);
    box-shadow: 0 0 18px rgba(var(--aura-tone-rgb), .25);
    animation: aura-toast-icon-in .34s cubic-bezier(.34, 1.4, .64, 1) .06s both;
}


/* ---- countdown ring ----
   Wraps the icon and empties anticlockwise as the toast's life runs out.
   Replaces the bar that used to sit along the bottom edge, which spent most
   of its life as a part-width line in the corner and read as a rendering
   fault rather than as time passing.

   Same pathLength="1" trick as the glyph: the circle's real circumference is
   irrelevant, so the drain is one rule with no measuring. */
.aura-toast-ring {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    fill: none;
    transform: rotate(-90deg);   /* start the ring at twelve o'clock */
}

    .aura-toast-ring .track {
        stroke: rgba(255, 255, 255, .1);
        stroke-width: 1.6;
    }

    .aura-toast-ring .prog {
        stroke: var(--aura-tone);
        stroke-width: 1.6;
        stroke-linecap: round;
        stroke-dasharray: 1;
        stroke-dashoffset: 0;
    }

/* No timer, no ring: a full circle that never moves would promise a countdown
   that is not coming. */
.aura-toast:not(.has-timer) .aura-toast-ring {
    display: none;
}

.aura-toast.has-timer .aura-toast-ring .prog {
    animation: aura-ring-drain var(--aura-toast-dur, 3000ms) linear both;
}

/* stopOnFocus is on, so hovering holds the toast open — the ring has to hold
   with it or it lies about how long is left. */
.toastify.aura-shell:hover .aura-toast-ring .prog,
.aura-toast.is-leaving .aura-toast-ring .prog {
    animation-play-state: paused;
}


/* ---- the glyph draws itself ----
   A tick that is stroked on is read as "it just happened". The same tick
   simply present is read as a label. The difference costs one animation.

   Every path carries pathLength="1", which normalises its length whatever
   its actual geometry — so a tick, a cross and a stem all draw with this one
   rule and nothing has to be measured in JS. */
.aura-toast-glyph {
    width: 16px;
    height: 16px;
    overflow: visible;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

    .aura-toast-glyph path {
        stroke-dasharray: 1;
        stroke-dashoffset: 1;
        animation: aura-draw .4s cubic-bezier(.65, 0, .35, 1) .14s both;
    }

    /* The cross's second stroke follows the first rather than racing it. */
    .aura-toast-glyph path.s2 {
        animation-delay: .28s;
    }

    /* The dot on ! and i lands after its stem, the way it would be written. */
    .aura-toast-glyph .dot {
        fill: currentColor;
        stroke: none;
        transform: scale(0);
        transform-box: fill-box;
        transform-origin: center;
        animation: aura-dot .3s cubic-bezier(.34, 1.5, .64, 1) .4s both;
    }


/* ---- message ---- */
.aura-toast-msg {
    flex: 1 1 auto;
    min-width: 0;
    padding-right: 2px;
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.45;
    color: rgba(255, 255, 255, .93);
    word-break: break-word;
}


/* ---- repeat counter ----
   Appears only when the same message fires again while its toast is up. */
.aura-toast-count {
    flex-shrink: 0;
    align-self: center;
    padding: 1px 7px;
    border-radius: 20px;
    background: rgba(var(--aura-tone-rgb), .2);
    color: var(--aura-tone);
    font-size: 11.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;   /* x9 -> x10 must not shuffle */
    line-height: 1.6;
}

    /* A pulse on each repeat, so a count that changes while being looked at
       is noticed. Without it the number silently ticks and nothing says a
       new event arrived. */
    .aura-toast-count.is-bumped {
        animation: aura-count-pop .34s cubic-bezier(.34, 1.5, .64, 1);
    }


/* ---- close ---- */
.aura-toast-close {
    flex-shrink: 0;
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: none;
    font-size: 16px;
    line-height: 1;
    color: #fff;
    opacity: .45;
    cursor: pointer;
    transition: opacity .15s ease, background-color .15s ease;
}

    .aura-toast-close:hover {
        opacity: .95;
        background: rgba(255, 255, 255, .1);
    }


/* ============================================================
   MOTION
   ============================================================ */

@keyframes aura-toast-in {
    from {
        opacity: 0;
        transform: translateY(-18px) scale(.95);
    }
}

/* ---- exit ----
   Left on its own, Toastify only fades the shell — the toast dims in place,
   which after a spring entrance reads as the page giving up rather than the
   toast leaving. This sends it back the way it came.

   240ms, comfortably inside the 400ms Toastify waits before destroying the
   node, so it always finishes. */
.aura-toast.is-leaving {
    animation: aura-toast-out .24s cubic-bezier(.4, 0, 1, 1) both;
}

@keyframes aura-toast-out {
    to {
        opacity: 0;
        transform: translateY(-12px) scale(.94);
    }
}

/* The disc lands a beat after the card, so the eye is drawn to what kind of
   message this is rather than to the box arriving. */
@keyframes aura-toast-icon-in {
    from {
        opacity: 0;
        transform: scale(.3);
    }
}

@keyframes aura-ring-drain {
    to {
        stroke-dashoffset: 1;
    }
}

@keyframes aura-draw {
    to {
        stroke-dashoffset: 0;
    }
}

@keyframes aura-dot {
    to {
        transform: scale(1);
    }
}

@keyframes aura-count-pop {
    35% {
        transform: scale(1.22);
    }
}

@media (prefers-reduced-motion: reduce) {
    .aura-toast,
    .aura-toast-icon,
    .aura-toast.is-leaving,
    .aura-toast-count.is-bumped {
        animation: none;
    }

    /* The glyph is drawn instantly rather than not at all — with the draw
       animation removed and no override, dashoffset would stay at 1 and the
       icon would simply be invisible. */
    .aura-toast-glyph path {
        animation: none;
        stroke-dashoffset: 0;
    }

    .aura-toast-glyph .dot {
        animation: none;
        transform: scale(1);
    }

    /* The ring stays, drawn whole — it reports state rather than decorating,
       and a toast with no ring reads as a different kind of toast. */
    .aura-toast.has-timer .aura-toast-ring .prog {
        animation: none;
        stroke-dashoffset: 0;
    }

    .aura-toast-close {
        transition: none;
    }
}


/* ============================================================
   SMALL SCREENS
   ============================================================ */

@media (max-width: 575.98px) {
    .toastify.aura-shell {
        max-width: calc(100vw - 24px);
    }
}
