/*
Toast (sonner's stack)

<section class="toaster" popover>       top layer, fills the viewport, takes no pointer
  <ol class="toaster-list">              one per position, fixed at an edge, toaster-width wide
    <li class="toast">                   absolutely placed at the list's edge, moved with transform

Collapsed, the newest toast sits at the edge and the older ones peek out
behind it, each a gap further in and slightly smaller, their content
hidden and their height the front toast's. Expanded (hover, focus, the
hotkey, or expand), each sits above the last by its own height plus the
gap. Layout numbers come from the controller as custom properties:

  --index, --z-index       place in the stack (0 is the newest)
  --live-before            toasts in front of this one that are not leaving
  --heights-before         their summed heights (px)
  --initial-height         this toast's natural height
  --front-toast-height     (list) the newest toast's height
  --swipe-amount-x / -y    the drag offset while swiping

Base UI's names are set too: --toast-index, --toast-height,
--toast-offset-y, --toast-swipe-movement-x / -y and (list)
--toast-frontmost-height.

Only transform, opacity and height change between states, so nothing
around a toast moves and a toast never changes width.
*/

@layer components {

.toaster {
  --toaster-width:  var(--toast-width);
  --toaster-gap:    var(--space-3-5);
  --toast-motion:   calc(var(--duration-enter) * 2);

  /* Undo the user agent's popover box: the region spans the viewport and
     lets every press through except on a toast. */
  position:       fixed;
  inset:          0;
  width:          auto;
  height:         auto;
  max-width:      none;
  max-height:     none;
  margin:         0;
  padding:        0;
  border:         0;
  background:     transparent;
  color:          var(--overlay-ink);
  overflow:       visible;
  pointer-events: none;
  outline:        none;
}

.toaster:not(:popover-open) {
  /* Before the browser attaches (or without popover support) the region
     still shows, fixed above the page. */
  display: block;
  z-index: var(--z-toast);
}

.toaster-list {
  --toaster-offset-top:    var(--space-6);
  --toaster-offset-right:  var(--space-6);
  --toaster-offset-bottom: var(--space-6);
  --toaster-offset-left:   var(--space-6);
  --toaster-mobile-offset-top:    var(--space-4);
  --toaster-mobile-offset-right:  var(--space-4);
  --toaster-mobile-offset-bottom: var(--space-4);
  --toaster-mobile-offset-left:   var(--space-4);

  position:    fixed;
  width:       var(--toaster-width);
  max-width:   calc(100vw - var(--toaster-offset-left) - var(--toaster-offset-right));
  margin:      0;
  padding:     0;
  list-style:  none;
  outline:     none;
  font-family: var(--font-ui);
  font-size:   var(--text-ui);
  column-gap:  var(--toaster-gap);
  transition:  transform var(--toast-motion) var(--ease-enter);
}

.toaster-list:focus-visible {
  outline: none;
}

.toaster-list[data-x-position="right"]  { right: var(--toaster-offset-right); }
.toaster-list[data-x-position="left"]   { left:  var(--toaster-offset-left); }
.toaster-list[data-x-position="center"] { left: 50%; transform: translateX(-50%); }
.toaster-list[data-y-position="top"]    { top:    var(--toaster-offset-top); }
.toaster-list[data-y-position="bottom"] { bottom: var(--toaster-offset-bottom); }

/* The Toast */

.toast {
  --y:      translateY(100%);
  --lift:   -1;
  --lift-amount: calc(var(--lift) * var(--toaster-gap));
  --offset: calc(var(--live-before) * var(--toaster-gap) + var(--heights-before));
  --toast-offset-y: var(--offset);

  position:       absolute;
  z-index:        var(--z-index);
  box-sizing:     border-box;
  width:          100%;
  opacity:        0;
  transform:      var(--y);
  touch-action:   none;
  outline:        none;
  overflow-wrap:  anywhere;
  pointer-events: auto;
  transition:     transform var(--toast-motion) var(--ease-enter),
                  opacity var(--toast-motion) var(--ease-enter),
                  height var(--toast-motion) var(--ease-enter),
                  box-shadow var(--duration-enter) var(--ease-enter);
}

.toast {
  /* The surface a toast's buttons sit on, so the cancel button can tint
     it: the overlay, the inverse or the rich color. */
  --toast-surface: var(--overlay-bg);
}

.toast[data-styled="true"] {
  display:       flex;
  align-items:   center;
  gap:           var(--space-1-5);
  padding:       var(--overlay-padding-lg);
  background:    var(--toast-surface);
  color:         var(--overlay-ink);
  border:        var(--overlay-border-width) solid var(--overlay-border);
  border-radius: var(--radius-overlay);
  box-shadow:    var(--overlay-shadow);
  font-size:     var(--text-ui);
  line-height:   var(--leading-snug);
}

.toast:focus-visible {
  box-shadow: var(--overlay-shadow), var(--focus-ring);
}

.toast[data-y-position="top"] {
  top:    0;
  --y:    translateY(-100%);
  --lift: 1;
}

.toast[data-y-position="bottom"] {
  bottom: 0;
  --y:    translateY(100%);
  --lift: -1;
}

.toast[data-x-position="right"] { right: 0; }
.toast[data-x-position="left"]  { left: 0; }

.toast-icon {
  display:       grid;
  flex:          none;
  place-items:   center;
  width:         var(--icon-size);
  height:        var(--icon-size);
  margin-inline: calc(var(--space-0-5) * -1) var(--space-1);
  color:         var(--ink-dim);
}

.toast-icon > * {
  grid-area: 1 / 1;
}

.toast-icon .icon {
  width:  var(--icon-size);
  height: var(--icon-size);
}

.toast[data-type="success"] .toast-icon { color: var(--success); }
.toast[data-type="info"]    .toast-icon { color: var(--info); }
.toast[data-type="warning"] .toast-icon { color: var(--warning); }
.toast[data-type="error"]   .toast-icon { color: var(--danger); }

/* A promise toast's loader fades out as the result icon fades in, in the
   same cell, so the icon box never changes size. */
.toast-loader {
  transition: opacity var(--duration-exit) var(--ease-exit);
}

.toast-loader[data-visible="false"] {
  opacity: 0;
}

.toast[data-promise="true"] .toast-icon > .icon {
  animation: toast-icon-in var(--toast-motion) var(--ease-enter) both;
}

@keyframes toast-icon-in {
  from {
    opacity:   0;
    transform: scale(var(--enter-scale));
  }
}

.toast-content {
  display:         flex;
  flex:            1;
  flex-direction:  column;
  justify-content: center;
  align-self:      stretch;
  gap:             var(--space-0-5);
  min-width:       0;
  min-height:      0;
}

/* A toast never grows past the viewport, less the list's offsets at both
   edges: a very long description scrolls inside it, and the title, the
   buttons and the close button stay on screen. */
.toast[data-styled="true"] {
  max-height: calc(100dvh - var(--toaster-offset-top) - var(--toaster-offset-bottom));
}

.toast-title {
  flex-shrink: 0;
}

.toast-description {
  min-height: 0;
  overflow-y: auto;
}

/* toast.custom(): the caller's node fills the toast. */
.toast-custom {
  flex:      1;
  min-width: 0;
}

.toast-title {
  font-weight: var(--weight-strong);
  line-height: var(--leading-snug);
  color:       inherit;
}

.toast-description {
  font-size:   var(--text-caption);
  line-height: var(--leading-body);
  color:       var(--ink-dim);
}

/* Action and cancel: small control-tier buttons at the inline end. */
.toast-button {
  display:        inline-flex;
  flex:           none;
  align-items:    center;
  height:         var(--control-height-sm);
  padding-inline: var(--control-padding-x-sm);
  margin-inline-start: auto;
  border:         0;
  border-radius:  var(--radius-control);
  font-family:    var(--font-ui);
  font-size:      var(--text-ui-sm);
  font-weight:    var(--weight-ui);
  text-transform: var(--case-ui);
  cursor:         pointer;
  outline:        none;
  transition:     opacity var(--toast-motion) var(--ease-enter), box-shadow var(--duration-enter) var(--ease-enter);
}

.toast-button + .toast-button {
  margin-inline-start: 0;
}

.toast-button:focus-visible {
  box-shadow: var(--focus-ring);
}

/* Pressed: a deeper paint; nothing moves. */
.toast-button:active {
  transform: none;
}

/* A button that does nothing looks it and does not press; this also undoes
   @elements/style's bare button:active drop. */
.toast-button[aria-disabled="true"] {
  cursor:    not-allowed;
  opacity:   var(--opacity-disabled);
  transform: none;
}

.toast-action {
  background: var(--accent);
  color:      var(--accent-ink);
}

/* The cancel button is the toast's own ink mixed into its surface (sonner's
   translucent cancel), so it reads as a button on any toast: the overlay in
   every look, light and dark, an inverted toast, a rich color. */
.toast-cancel {
  background: color-mix(in oklab, currentColor 12%, var(--toast-surface));
  color:      inherit;
}

.toast-cancel:hover:not([aria-disabled="true"]) {
  background: color-mix(in oklab, currentColor 18%, var(--toast-surface));
}

.toast-action:active:not([aria-disabled="true"]) {
  background: color-mix(in oklab, var(--accent-deep) 82%, var(--ink));
}

.toast-cancel:active:not([aria-disabled="true"]) {
  background: color-mix(in oklab, currentColor 26%, var(--toast-surface));
}

/* An action or cancel element of your own (sonner's JSX action) takes the
   button's place in the row; it brings its own look. */
.toast-slot {
  display: contents;
}

.toast-close {
  position:        absolute;
  top:             0;
  inset-inline-start: 0;
  z-index:         1;
  display:         grid;
  place-items:     center;
  width:           calc(var(--icon-size) + var(--space-1));
  height:          calc(var(--icon-size) + var(--space-1));
  padding:         0;
  border:          var(--overlay-border-width) solid var(--overlay-border);
  border-radius:   var(--radius-pill);
  background:      var(--overlay-bg);
  color:           var(--overlay-ink);
  cursor:          pointer;
  outline:         none;
  transform:       translate(-35%, -35%);
  transition:      opacity var(--duration-instant) var(--ease-enter), background var(--duration-enter) var(--ease-enter);
}

.toast-close:dir(rtl) {
  transform: translate(35%, -35%);
}

.toast-close .icon {
  width:  var(--icon-size-sm);
  height: var(--icon-size-sm);
}

.toast-close:hover:not([data-disabled="true"]) {
  background: var(--item-highlight);
}

.toast-close:focus-visible {
  box-shadow: var(--focus-ring);
}

.toast-close[data-disabled="true"] {
  cursor: not-allowed;
}

/* Rich Colors And Invert */

.toast[data-invert="true"] {
  --toast-surface: var(--bg-inverse);
}

.toast[data-styled="true"][data-invert="true"] {
  background:   var(--bg-inverse);
  color:        var(--ink-inverse);
  border-color: var(--bg-inverse);
}

.toast[data-invert="true"] .toast-description {
  color: inherit;
  opacity: 0.8;
}

.toast[data-rich-colors="true"][data-type="success"] { --toast-rich-bg: var(--success-soft); --toast-rich-edge: var(--success-edge); --toast-rich-ink: var(--success-ink); }
.toast[data-rich-colors="true"][data-type="info"]    { --toast-rich-bg: var(--info-soft);    --toast-rich-edge: var(--info-edge);    --toast-rich-ink: var(--info-ink); }
.toast[data-rich-colors="true"][data-type="warning"] { --toast-rich-bg: var(--warning-soft); --toast-rich-edge: var(--warning-edge); --toast-rich-ink: var(--warning-ink); }
.toast[data-rich-colors="true"][data-type="error"]   { --toast-rich-bg: var(--danger-soft);  --toast-rich-edge: var(--danger-edge);  --toast-rich-ink: var(--danger-ink); }

.toast[data-styled="true"][data-rich-colors="true"]:is([data-type="success"], [data-type="info"], [data-type="warning"], [data-type="error"]),
.toast[data-rich-colors="true"]:is([data-type="success"], [data-type="info"], [data-type="warning"], [data-type="error"]) .toast-close {
  background:   var(--toast-rich-bg);
  border-color: var(--toast-rich-edge);
  color:        var(--toast-rich-ink);
}

/* The rich close button tints on hover like the plain one; this rule must
   outrank the rich background above. */
.toast[data-rich-colors="true"]:is([data-type="success"], [data-type="info"], [data-type="warning"], [data-type="error"]) .toast-close:hover:not([data-disabled="true"]) {
  background: color-mix(in oklab, var(--toast-rich-ink) 12%, var(--toast-rich-bg));
}

.toast[data-rich-colors="true"]:is([data-type="success"], [data-type="info"], [data-type="warning"], [data-type="error"]) {
  --toast-surface: var(--toast-rich-bg);
}

.toast[data-rich-colors="true"]:is([data-type="success"], [data-type="info"], [data-type="warning"], [data-type="error"]) :is(.toast-icon, .toast-description) {
  color: inherit;
}

/* Stack States */

/* While swiping, a wide hit area keeps the pointer over the toast. */
.toast[data-swiping="true"]::before {
  content:  "";
  position: absolute;
  inset-inline: -100%;
  height:   100%;
  z-index:  -1;
}

.toast[data-y-position="top"][data-swiping="true"]::before {
  bottom:    50%;
  transform: scaleY(3) translateY(50%);
}

.toast[data-y-position="bottom"][data-swiping="true"]::before {
  top:       50%;
  transform: scaleY(3) translateY(-50%);
}

.toast[data-swiping="false"][data-removed="true"]::before {
  content:   "";
  position:  absolute;
  inset:     0;
  transform: scaleY(2);
}

/* Fills the gap between expanded toasts so the pointer never falls through. */
.toast[data-expanded="true"]::after {
  content:  "";
  position: absolute;
  left:     0;
  width:    100%;
  height:   calc(var(--toaster-gap) + 1px);
  bottom:   100%;
}

.toast[data-y-position="top"][data-expanded="true"]::after {
  bottom: auto;
  top:    100%;
}

.toast[data-mounted="true"] {
  --y:     translateY(0);
  opacity: 1;
}

.toast[data-expanded="false"][data-front="false"] {
  --y:    translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(1 - var(--toasts-before) * 0.05));
  height: var(--front-toast-height);
}

.toast > * {
  transition: opacity var(--toast-motion) var(--ease-enter);
}

.toast[data-expanded="false"][data-front="false"][data-styled="true"] > * {
  opacity: 0;
}

.toast[data-visible="false"] {
  opacity:        0;
  pointer-events: none;
}

.toast[data-mounted="true"][data-expanded="true"] {
  --y:    translateY(calc(var(--lift) * var(--offset)));
  height: var(--initial-height);
}

.toast[data-removed="true"][data-front="true"][data-swipe-out="false"] {
  --y:     translateY(calc(var(--lift) * -100%));
  opacity: 0;
}

.toast[data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="true"] {
  --y:     translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));
  opacity: 0;
}

.toast[data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="false"] {
  --y:        translateY(40%);
  opacity:    0;
  transition: transform var(--toast-motion) var(--ease-exit), opacity var(--duration-exit) var(--ease-exit);
}

.toast[data-removed="true"][data-front="false"]::before {
  height: calc(var(--initial-height) + 20%);
}

.toast[data-swiping="true"] {
  transform:  var(--y) translateY(var(--swipe-amount-y)) translateX(var(--swipe-amount-x));
  transition: none;
}

.toast[data-swiped="true"] {
  user-select: none;
}

.toast[data-swipe-out="true"] {
  animation-duration:        var(--duration-exit);
  animation-timing-function: var(--ease-exit);
  animation-fill-mode:       forwards;
}

.toast[data-swipe-out="true"][data-swipe-direction="left"]  { animation-name: toast-swipe-out-left; }
.toast[data-swipe-out="true"][data-swipe-direction="right"] { animation-name: toast-swipe-out-right; }
.toast[data-swipe-out="true"][data-swipe-direction="up"]    { animation-name: toast-swipe-out-up; }
.toast[data-swipe-out="true"][data-swipe-direction="down"]  { animation-name: toast-swipe-out-down; }

@keyframes toast-swipe-out-left {
  from { transform: var(--y) translateX(var(--swipe-amount-x)); opacity: 1; }
  to   { transform: var(--y) translateX(calc(var(--swipe-amount-x) - 100%)); opacity: 0; }
}

@keyframes toast-swipe-out-right {
  from { transform: var(--y) translateX(var(--swipe-amount-x)); opacity: 1; }
  to   { transform: var(--y) translateX(calc(var(--swipe-amount-x) + 100%)); opacity: 0; }
}

@keyframes toast-swipe-out-up {
  from { transform: var(--y) translateY(var(--swipe-amount-y)); opacity: 1; }
  to   { transform: var(--y) translateY(calc(var(--swipe-amount-y) - 100%)); opacity: 0; }
}

@keyframes toast-swipe-out-down {
  from { transform: var(--y) translateY(var(--swipe-amount-y)); opacity: 1; }
  to   { transform: var(--y) translateY(calc(var(--swipe-amount-y) + 100%)); opacity: 0; }
}

/* Phone (sonner's 600px) */

@media (max-width: 37.5rem) {
  .toaster-list,
  .toaster-list[data-x-position] {
    left:      var(--toaster-mobile-offset-left);
    right:     var(--toaster-mobile-offset-right);
    width:     auto;
    max-width: none;
    transform: none;
  }

  .toaster-list[data-y-position="bottom"] { bottom: var(--toaster-mobile-offset-bottom); }
  .toaster-list[data-y-position="top"]    { top:    var(--toaster-mobile-offset-top); }

  .toast {
    left:  0;
    right: 0;
  }

  .toast[data-styled="true"] {
    max-height: calc(100dvh - var(--toaster-mobile-offset-top) - var(--toaster-mobile-offset-bottom));
  }
}

/* Anchored (Base UI) */

/* A toast with an anchor gets a list of its own, placed against the anchor by
   float() (left/top, data-side, --transform-origin). It is one toast, not a
   stack: sized to its content and scaling in from the anchor. After the
   phone block, so the phone's full-width list does not apply to it. */
.toaster-list[data-anchored] {
  --toast-arrow-size: var(--space-2-5);

  width:     max-content;
  max-width: min(var(--toaster-width), var(--available-width, 100vw));
  transform: none;
}

.toaster-list[data-anchored] > .toast {
  --y:              scale(var(--enter-scale));
  position:         relative;
  inset:            auto;
  width:            auto;
  height:           auto;
  transform-origin: var(--transform-origin);
}

.toaster-list[data-anchored] > .toast[data-mounted="true"] {
  --y: scale(1);
}

.toaster-list[data-anchored] > .toast[data-removed="true"][data-swipe-out="false"] {
  --y:     scale(var(--enter-scale));
  opacity: 0;
}

/* The arrow: a square turned 45deg, half of it outside the facing edge. */
.toast-arrow {
  width:          var(--toast-arrow-size);
  height:         var(--toast-arrow-size);
  background:     var(--overlay-bg);
  border:         var(--overlay-border-width) solid var(--overlay-border);
  pointer-events: none;
  transform:      rotate(45deg);
}

.toast[data-invert="true"] .toast-arrow {
  background:   var(--bg-inverse);
  border-color: var(--bg-inverse);
}

.toast[data-rich-colors="true"] .toast-arrow {
  background:   var(--toast-rich-bg, var(--overlay-bg));
  border-color: var(--toast-rich-edge, var(--overlay-border));
}

.toast-arrow[data-side="bottom"] {
  top:                 calc(var(--toast-arrow-size) / -2 - var(--overlay-border-width));
  border-right-color:  transparent;
  border-bottom-color: transparent;
}

.toast-arrow[data-side="top"] {
  bottom:            calc(var(--toast-arrow-size) / -2 - var(--overlay-border-width));
  border-left-color: transparent;
  border-top-color:  transparent;
}

.toast-arrow[data-side="right"] {
  left:               calc(var(--toast-arrow-size) / -2 - var(--overlay-border-width));
  border-top-color:   transparent;
  border-right-color: transparent;
}

.toast-arrow[data-side="left"] {
  right:               calc(var(--toast-arrow-size) / -2 - var(--overlay-border-width));
  border-bottom-color: transparent;
  border-left-color:   transparent;
}

/* Reduced motion: toasts appear, restack, swipe out and change icon at
   once. The spinner keeps its own reduced-motion rule, so a loading toast
   still shows it is working. */
@media (prefers-reduced-motion: reduce) {
  .toast,
  .toast *,
  .toaster-list {
    transition-duration: 0s;
  }

  .toast,
  .toast-icon > .icon {
    animation-duration: 0s;
  }
}

}
