@layer components {
  /* Toasts

     Fixed to the bottom-right corner and out of the flow, so being told
     something never costs the page its position. Bottom-right and not
     bottom-left because the left corner is the view button's, and not
     bottom-centre because the middle of the screen is where the work is.

     The shelf is inert; only the toasts inside it take the pointer, so the
     empty region does not sit over the page swallowing clicks. It is also
     data-turbo-permanent — see shared/_flashes for why that is the whole fix
     for a toast being morphed away by somebody else's broadcast. */

  .flashes {
    position: fixed;
    z-index: 40;
    bottom: calc(var(--space-3) + var(--safe-bottom));
    right: calc(var(--gutter) + var(--safe-right));
    left: calc(var(--gutter) + var(--safe-left));
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space);
    pointer-events: none;
  }

  /* A panel, in the language every other floating thing in the product speaks:
     --radius-lg, one --space of padding, the surface, the line and the single
     shadow. It used to be a flat --radius box with a 4px coloured rail down
     its leading edge — a shape that appears nowhere else here, and one that
     made the kind of message a stripe of colour rather than something with a
     mark on it.

     Because it is padded, everything inside it is drawn at --radius-inset:
     that is the rule in _tokens.css about two rounded rectangles only reading
     as nested when the inner one curves less by exactly the padding. */
  .flash {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: var(--space);
    max-width: min(30rem, 100%);
    padding: var(--space);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-card);
    /* The type on it is the page's secondary size and not its body size: a
       toast is read in one glance from the corner of the eye, and at 19px in
       a box that lifts off the page it starts to argue with the work. */
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--ink);
  }

  /* The mark, in a tint of its own signal colour. Both a shape and a colour,
     so which kind of message this is survives being read by somebody who does
     not separate green from red — and so a toast is identifiable at the edge
     of vision before any of it has been read. */
  .flash__mark {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: var(--control-h-sm);
    height: var(--control-h-sm);
    border-radius: var(--radius-inset);
    background: var(--go-sunk);
    color: var(--go);
  }

  .flash--alert .flash__mark { background: var(--danger-sunk); color: var(--danger); }

  .flash__icon { width: 1.125rem; height: 1.125rem; }

  .flash__body {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 1 1 auto;
    min-width: 0;
    min-height: var(--control-h-sm);
    padding-inline: var(--space);
    border-radius: var(--radius-inset);
    color: var(--ink);
    text-decoration: none;
    transition: background var(--transition);
  }

  .flash__message { min-width: 0; }

  /* The task's number, and it is what says the toast goes somewhere.

     There used to be an "Open" out on the right of the panel, set as a label
     and tinted, doing this job with a word. The number does it better: it is
     what the task is called on every other surface in the product, it is the
     half of the sentence anybody is actually looking for, and it is already in
     the message — so the toast lost a word and gained nothing to draw.

     The mono face is the one the references are set in everywhere else — a
     list row's rail, the task detail's header — which is what makes this read
     as ACME-142 rather than as an emphasis. Underlined, because it is the only
     thing in a toast that leads anywhere and the accent alone has to be a
     colour somebody can fail to see. */
  .flash__ref {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 0.2em;
  }

  .flash__body--link:hover { background: var(--surface-hover); }
  .flash__body--link:focus-visible { outline-offset: var(--focus-offset-inset); }

  /* The dismiss reads as a menu row's quiet control rather than as a divider
     with a cross in it: no rule down the middle of a panel, a tint on hover,
     and the same inset corner as everything else sitting in here. */
  .flash__close {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: var(--control-h-sm);
    height: var(--control-h-sm);
    border: 0;
    border-radius: var(--radius-inset);
    background: none;
    color: var(--ink-faint);
    cursor: pointer;
    transition: background var(--transition), color var(--transition), var(--responds);
  }

  .flash__close:hover { background: var(--surface-hover); color: var(--ink); }
  /* Inset, because the toast has a hard edge and an outset ring would be
     clipped by it. The offset is a token so "inside" is one decision. */
  .flash__close:focus-visible { outline-offset: var(--focus-offset-inset); }

  /* It comes up from the edge it is pinned to and goes back down it. Arriving
     is one declaration rather than a class the controller has to add, so
     nothing has to be in step for a toast to appear.

     The leaving half is outside the motion query on purpose. It used to be
     inside, so under reduced motion there was no transition at all and
     therefore no transitionend — which is why flash_controller treats its
     timeout as the path that does the removing. On the exit clock it is 150ms
     normally and 100ms under reduce, and the transition fires either way: the
     toast still goes, it simply goes without travelling. */
  .flash {
    animation: fade-in var(--transition-enter);
    transition: opacity var(--transition-exit);
  }

  .flash--leaving { opacity: 0; }

  /* On a phone the view button owns the bottom edge, so the toasts stand on
     top of it instead of beside it, and take the full width they are given. */
  @media (max-width: 48rem) {
    .flashes {
      bottom: calc(var(--space-3) + var(--control-h) + var(--space) + var(--safe-bottom));
      align-items: stretch;
    }

    .flash { max-width: none; }
  }

  /* What a form says went wrong, and it sits inside the form it belongs to
     rather than floating in the corner like a toast — a toast is for something
     that has already happened elsewhere; this is about the box underneath it.

     Drawn as a tinted block rather than as loose red text, for the same reason
     a field is sunk rather than outlined: a paragraph in a signal colour with
     nothing behind it reads as prose somebody set in red, and it was sitting
     directly on the panel's own paper with no edge of its own. --danger-sunk is
     the strength of a background; --danger is the strength of a mark, and the
     text on top of it stays the one that clears contrast. */
  .errors {
    color: var(--danger);
    font-size: var(--text-sm);
    background: var(--danger-sunk);
    border-radius: var(--radius);
    padding: var(--space-2);
    max-width: var(--measure);
  }

  /* The composer's error is rendered on every task page whether or not there
     is anything to say — it cannot be swapped in later, because the composer
     is turbo-permanent (see comments/_error.html.erb). Empty, it used to be
     red text of no length and cost nothing; with paper behind it, it was a
     pink bar sitting under every timeline in the product. */
  .errors:empty { display: none; }

  .errors ul { margin: 0; padding-inline-start: var(--space-2); }
  .errors li::marker { color: var(--danger); }
}
