@layer components {
  /* The app's toasts: bottom right, or centered above the mobile shell's bars. The newest in front, the two before it
     peeking behind; hovered, focused or touched the pile opens (toasts_controller). Only the toasts take the pointer,
     never the pile's box. */
  .toasts {
    --toast-bottom: calc(16px + var(--safe-bottom));
    position: fixed; z-index: 30; bottom: var(--toast-bottom); inset-inline-end: calc(7px + var(--safe-right));
    /* The box holds what shows: the front toast, the two peeks and the room for the corner cross. */
    height: calc(var(--toast-front-height, 0px) + 37px); box-sizing: border-box;
    width: min(400px, calc(100vw - 32px)); pointer-events: none;
  }
  .toasts[data-expanded] { height: calc(var(--toast-open-height, 0px) + 9px); }
  .toasts:empty { display: none; }
  /* Sonner's look: a plain white card, 16 px in, a soft shadow; an error in its own reds. */
  .toast {
    --toast-y: calc(var(--toast-index, 0) * -14px);
    --toast-scale: calc(1 - var(--toast-index, 0) * 0.05);
    --toast-bg: var(--popover); --toast-border: var(--border); --toast-text: var(--popover-foreground);
    position: absolute; bottom: 0; inset-inline-end: 9px; pointer-events: auto; display: flex; align-items: center; gap: 6px;
    width: min(356px, calc(100% - 18px)); box-sizing: border-box; touch-action: pan-y;
    transform: translate(var(--toast-swipe, 0px), var(--toast-y)) scale(var(--toast-scale)); transform-origin: bottom center;
    transition: transform 0.4s var(--ease-out), opacity 0.3s, height 0.4s var(--ease-out);
    padding: 16px; border: 1px solid var(--toast-border); border-radius: 8px;
    box-shadow: 0 4px 12px light-dark(oklch(0 0 0 / 0.1), oklch(0 0 0 / 0.4));
    background: var(--toast-bg); color: var(--toast-text); font-size: 13px; line-height: 1.4;
  }
  .toast--error {
    --toast-bg: light-dark(hsl(359 100% 97%), hsl(358 76% 10%));
    --toast-border: light-dark(hsl(359 100% 94%), hsl(357 89% 16%));
    --toast-text: light-dark(hsl(360 100% 38%), hsl(358 100% 81%));
  }
  @starting-style { .toast:not([data-settled]) { opacity: 0; transform: translateY(100%); } }
  /* Behind the front toast: its size, its content hidden so no words show in the peek. */
  .toast:not([data-front]) { height: var(--toast-front-height); overflow: hidden; }
  .toast > * { transition: opacity 0.2s; }
  .toast:not([data-front]) > * { opacity: 0; }
  .toast[data-beyond] { visibility: hidden; opacity: 0; }
  .toasts[data-expanded] .toast {
    --toast-y: calc(var(--toast-offset, 0px) * -1); --toast-scale: 1;
    height: var(--toast-height); overflow: visible;
  }
  .toasts[data-expanded] .toast > * { opacity: 1; }
  /* The gap between two toasts of an open pile stays inside the pile for the pointer. */
  .toasts[data-expanded] .toast::before { content: ""; position: absolute; inset-inline: 0; bottom: 100%; height: 9px; }
  .toasts[data-measuring] .toast { height: auto; }
  .toast[data-swiping] { transition: none; }
  .toast[data-leaving], .toasts[data-expanded] .toast[data-leaving] {
    opacity: 0; pointer-events: none; --toast-y: calc(var(--toast-offset, 0px) * -1 + 100%);
  }
  @media (prefers-reduced-motion: reduce) { .toast, .toast > * { transition: none; } }
  .toast-icon { display: inline-flex; flex: none; margin-inline: -3px 4px; }
  /* The error's mark is a filled disc, its stroke cut out in the toast's own background. */
  .toast--error .toast-icon circle { fill: currentColor; }
  .toast--error .toast-icon line { stroke: var(--toast-bg); stroke-width: 2.25; }
  .toast-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .toast-title { margin: 0; font-weight: 500; line-height: 1.5; }
  .toast-message { margin: 0; overflow-wrap: anywhere; }
  .toast-title + .toast-message { color: light-dark(oklch(0.37 0 0), oklch(0.93 0 0)); }
  .toast--error .toast-title + .toast-message { color: inherit; }
  .toast-form { display: contents; }
  /* The action: a small filled button in the text's colour, as sonner draws it. */
  .toast .toast-action {
    position: relative; flex: none; margin-inline-start: auto; height: 24px; min-height: 0; padding: 0 8px;
    border: 0; border-radius: 4px; box-shadow: none; background: var(--toast-text); color: var(--toast-bg);
    font-size: 13px; font-weight: 500; line-height: 24px;
  }
  @media (hover: hover) { .toast .toast-action:hover { opacity: 0.85; } }
  /* The cross sits on the toast's top left corner, a small disc. */
  .toast-close {
    position: absolute; z-index: 1; top: 0; inset-inline-start: 0; translate: -35% -35%;
    display: inline-grid; place-items: center; width: 20px; height: 20px; padding: 0;
    border: 1px solid var(--toast-border); border-radius: 999px; background: var(--toast-bg); color: var(--toast-text);
    cursor: pointer;
  }
  .toast-close:is(:hover, :active) { background: light-dark(oklch(0.97 0 0), oklch(0.27 0 0)); }
  .toast--error .toast-close:is(:hover, :active) { background: var(--toast-border); }
  .toast-close svg { width: 10px; height: 10px; }

  @media (pointer: coarse) {
    /* Offsets count from the padding box: the 1 px border leaves 18 px, so -13 px makes the 44 px touch square. */
    .toast-close::after { content: ""; position: absolute; inset: -13px; }
    .toast .toast-action::after { content: ""; position: absolute; inset: -10px -4px; }
  }

  @media (max-width: 1099px) {
    .toasts {
      --toast-bottom: calc(var(--tab-bar-height) + var(--tab-bar-bottom) + 8px);
      inset-inline: 0; margin-inline: auto;
    }
    .toast { inset-inline: 0; margin-inline: auto; }
    /* A screen of the pile has its own bottom bar instead of the tabs; an open keyboard hides the tabs. */
    :root:has(.app-main--stack) .toasts { --toast-bottom: calc(var(--stack-clearance) + 8px); }
    :root[data-keyboard] .toasts { --toast-bottom: 8px; }
  }

  /* A page with a composer keeps the pile above it, so "Envoyer" and the input line stay uncovered. An open keyboard
     keeps its own rule. */
  :root:has(.composer):where(:not([data-keyboard])) .toasts { --toast-bottom: calc(150px + var(--safe-bottom)); }

  /* In the settings drawer the toast rides above the footer buttons (Ajouter, Annuler), never over them. */
  :root:root .sheet--drawer .toasts { --toast-bottom: calc(76px + var(--safe-bottom)); }

  /* No tab bar under the viewer or the reader. `:root` matches the phone rules' specificity; coming later, it wins. */
  :root :is(.image-viewer, .markdown-reader) .toasts { --toast-bottom: calc(16px + var(--safe-bottom)); }
}
