/* ==========================================================================
   ALERTS — the single definition, for every area of the system.

   Before this file, `.alert` and its variants were redefined in 28 separate
   stylesheets (~110 rule blocks) that had drifted into three different design
   lineages: this project's own tokens, Tailwind's emerald, and Bootstrap 4's
   green — all three claiming to be "success". Changing how an alert looked
   meant editing 28 files, which is why it never happened.

   MIGRATION CONTRACT
   The class names here are deliberately the ones already in the markup
   (.alert, .alert-success, .alert-danger, .alert-warning, .alert-info, plus
   the .alert-error spelling used in two places). 46 existing alerts keep
   working untouched; adopting this file is a matter of DELETING the
   duplicated blocks from the per-page stylesheets, not rewriting views.

   THE DESIGN — "chip card"
   The card stays white and the severity lives in a small solid chip, so a
   page showing three alerts at once reads as a list rather than a wall of
   colour, and the message text is never dimmed by a tint. Errors are the one
   exception: they keep a tinted fill, because a lone red chip does not carry
   enough weight when someone has just lost their work. One loud level, and it
   stays loud precisely because nothing else uses it.

   Depends on the tokens in assets/css/style.css.
   ========================================================================== */

.alert {
  /* Overridden per variant below. Info is the default so an .alert with no
     variant class still renders as something deliberate. */
  --alert-accent: #2c67a8;
  --alert-fill: var(--color-white, var(--color-white));
  --alert-border: var(--color-border, rgba(15, 23, 42, 0.12));
  --alert-glyph: "i";

  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  margin-bottom: 1rem;
  padding: 0.9rem 1rem;
  background: var(--alert-fill);
  border: 1px solid var(--alert-border);
  border-radius: 11px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 6px 16px rgba(15, 23, 42, 0.06);
  color: var(--color-ink, var(--color-ink));
  font-size: var(--text-sm, 0.9rem);
  line-height: 1.5;
}

/* --------------------------------------------------------------------------
   The severity chip.

   Two ways in, because the markup has two shapes: 12 alerts already carry a
   Font Awesome <i>, and 34 carry none. An existing icon becomes the chip; an
   alert without one gets a generated chip carrying a plain text glyph — no
   icon font needed, so it cannot silently render as a blank box if Font
   Awesome fails to load.
   -------------------------------------------------------------------------- */
.alert > i:first-child,
.alert > svg:first-child {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: var(--alert-accent);
  color: var(--badge-text);
  font-size: 0.85rem;
  line-height: 1;
  /* Pull the chip up so its centre matches the centre of the FIRST line of
     text, not its top edge. align-items: flex-start lines up the tops, and a
     30px chip beside a ~19px line box then hangs about 5px low — subtle
     enough to read as sloppiness rather than as a bug. Centring the flex row
     instead would fix the single-line case and break every wrapped one, by
     floating the chip to the middle of a three-line paragraph. */
  margin-top: calc((1.5em - 30px) / 2);
}

/* :has() lets an iconless alert generate its own chip. Where it is
   unsupported the alert simply renders without one — still legible, still
   correctly coloured, never broken. */
.alert:not(:has(> i:first-child)):not(:has(> svg:first-child))::before {
  content: var(--alert-glyph);
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: var(--alert-accent);
  color: var(--badge-text);
  font-family: var(--font-sans, system-ui, sans-serif);
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1;
  margin-top: calc((1.5em - 30px) / 2); /* see the note on the icon chip above */
}

/* --------------------------------------------------------------------------
   Variants
   -------------------------------------------------------------------------- */
.alert-success {
  --alert-accent: var(--status-success-solid);
  --alert-glyph: "\2713"; /* check */
}

.alert-info {
  --alert-accent: #2c67a8;
  --alert-glyph: "i";
}

.alert-warning {
  --alert-accent: #a8761a;
  --alert-glyph: "!";
}

/* .alert-error is the spelling used in two views; kept as an alias rather
   than renamed, so no view has to change to adopt this file. */
.alert-danger,
.alert-error {
  --alert-accent: var(--status-danger-fill);
  --alert-glyph: "!";
  /* The one tinted variant — see the note at the top of this file. */
  --alert-fill: var(--status-danger, rgba(203, 31, 31, 0.13));
  --alert-border: var(--status-danger-border, rgba(203, 31, 31, 0.26));
  color: var(--status-danger-text, #cb2b2b);
  box-shadow: none;
}

/* --------------------------------------------------------------------------
   Content inside an alert
   -------------------------------------------------------------------------- */
/* The message wrapper is a BLOCK, not a column flex container.

   It used to be `display: flex; flex-direction: column`, which works only when
   the wrapper holds block-level children. Most alerts hold a plain sentence
   with an inline <strong> in it - and flex made every one of those fragments
   its own flex line, blockifying the <strong> to full width. On the Update
   Status card, "This application is already Qualified. Its status can no
   longer be changed." rendered as four stacked lines 309px tall, with
   "Qualified" orphaned on its own row and the next line opening on a stray
   full stop.

   As a block, an inline sentence flows as a sentence and stacked <p> children
   still stack - the gap is restored by the sibling margin below. */
.alert > div,
.alert > span:not(:first-child),
.alert-body {
  flex: 1 1 auto;
  min-width: 0;
}

/* Replaces the flex `gap` for the alerts that genuinely stack blocks. */
.alert > div > * + *,
.alert > span:not(:first-child) > * + *,
.alert-body > * + * {
  margin-top: 0.25rem;
}

.alert p {
  margin: 0;
}

.alert strong,
.alert .alert-title {
  font-weight: 700;
}

.alert a {
  color: inherit;
  font-weight: 600;
  text-underline-offset: 2px;
}

/* An alert that offers a next step — the thing most of the current messages
   are missing. Styled as a link, not a button, so it never competes with the
   page's real primary action. */
.alert .alert-action {
  align-self: flex-start;
  margin-top: 0.15rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--alert-accent);
  font: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.alert .alert-action:hover {
  filter: brightness(0.9);
}

/* --------------------------------------------------------------------------
   Dismissible / auto-dismissing alerts.

   Generalised from the version in assets/css/user/profile.css, which was
   scoped to .profile-container and so could not be used by any other page
   even though it was the best alert in the system.
   -------------------------------------------------------------------------- */
.alert.auto-dismiss {
  position: relative;
  padding-right: 40px;   /* room for the close button */
  overflow: hidden;      /* clips the countdown bar to the rounded corners */
  max-height: 40rem;
  transition: opacity 0.35s ease, transform 0.35s ease, max-height 0.35s ease,
              margin-bottom 0.35s ease, padding 0.35s ease;
}

/* Collapses rather than just fading, so the page below moves up to fill the
   gap instead of leaving a hole where the message was. */
.alert.auto-dismiss.is-dismissed {
  opacity: 0;
  transform: translateY(-6px);
  max-height: 0;
  margin-bottom: 0;
  padding-top: 0;
  padding-bottom: 0;
  pointer-events: none;
}

.alert-close {
  position: absolute;
  top: 6px;
  right: 8px;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: inherit;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.6;
  transition: opacity 0.2s ease, background 0.2s ease;
}

/* Neutral tint rather than a coloured one: these banners are green as often
   as they are red. */
.alert-close:hover {
  opacity: 1;
  background: rgba(0, 0, 0, 0.08);
}

/* Thin bar along the bottom showing how long the message has left, so it
   never simply vanishes without warning. */
.alert-timer {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 3px;
  width: 100%;
  background: var(--alert-accent);
  opacity: 0.4;
  transform-origin: left center;
  transform: scaleX(1);
}

.alert-timer.is-counting {
  transform: scaleX(0);
  transition: transform linear var(--alert-timer-duration, 8s);
}

/* --------------------------------------------------------------------------
   Small screens: the chip is the first thing to give up its width.
   -------------------------------------------------------------------------- */
@media (max-width: 480px) {
  .alert {
    gap: 0.55rem;
    padding: 0.8rem 0.85rem;
  }

  .alert > i:first-child,
  .alert > svg:first-child,
  .alert:not(:has(> i:first-child)):not(:has(> svg:first-child))::before {
    width: 26px;
    height: 26px;
    border-radius: 8px;
    margin-top: calc((1.5em - 26px) / 2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .alert.auto-dismiss,
  .alert-timer.is-counting {
    transition: none;
  }

  /* A bar that cannot animate conveys nothing and reads as a stray rule —
     hide it rather than leave it sitting there full-width. Taken from the
     registration and password-reset pages, which had this and the alert
     component did not. */
  .alert-timer {
    display: none;
  }
}
