/* ---- What the app says back ------------------------------------------------
   One job: the primitives that answer the reader rather than hold content.
   The notice at the top of the panel it concerns (7.9), the empty state where
   a list has nothing in it yet (7.10), the dialog (7.11), and the in-flight
   marks a request leaves while it is still going (8, with the skeleton it
   loads behind). Split out of `primitives.css`, which keeps the surfaces.

   The rule they share: **an answer belongs where the thing it answers is.**
   A notice sits inside its panel, an empty state inside the list that is
   empty, an in-flight bar on the panel being saved. Nothing here floats over
   the page, and nothing here is a status word: that vocabulary is
   `status.css`, and a notice borrows its tones without redefining them.
   -------------------------------------------------------------------------- */

/* ---- 7.9 Notices ----------------------------------------------------------
   At the top of the panel it concerns, never floating. Icon 16 at the left, a
   title in weight 500, one or two lines. */

.notice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-12);
  background: var(--surface-sunk);
  color: var(--ink-2);
  border-radius: var(--radius-md);
  padding: var(--sp-12) var(--sp-16);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}
.notice__icon {
  flex: none;
  width: 1rem;
  height: 1rem;
  margin-top: 0.15em;
  color: var(--slate);
}
.notice__body { display: grid; gap: var(--sp-4); min-width: 0; }
.notice__title {
  margin: 0;
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  color: var(--ink);
}
.notice p { margin: 0; }
.notice-ok { background: var(--ok-soft); }
.notice-ok .notice__icon { color: var(--ok); }
.notice-warn { background: var(--warn-soft); }
.notice-warn .notice__icon { color: var(--warn); }
.notice-crit { background: var(--crit-soft); }
.notice-crit .notice__icon { color: var(--crit); }

/* What is missing, one line each, naming the requirement the message names.
   Inside `.notice__body`, which is already a grid, so the list only owns its
   own bullets. */
.notice__list { margin: 0; padding-left: var(--sp-16); }
.notice__list li + li { margin-top: var(--sp-4); }
.notice__actions { display: flex; flex-wrap: wrap; gap: var(--sp-8); margin-top: var(--sp-4); }

/* The stale-save comparison: the latest saved value beside the typed one.
   Two columns from `lg` up, stacked below it, which is the one breakpoint
   this pattern needs (style guide 5.7). */
.notice__compare { margin: 0; display: grid; gap: var(--sp-12); }
.notice__compare-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-12); }
.notice__compare dt {
  color: var(--slate);
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
}
.notice__compare dd { margin: 0; }
@media (max-width: 64rem) {
  .notice__compare-pair { grid-template-columns: 1fr; }
}

/* ---- 7.10 The empty state -------------------------------------------------
   What is missing, why it matters, what to do, under the empty cell of the
   status vocabulary. Centred inside the panel, table or card that holds it.
   Never an apology and never a bare "No ... yet." */

.empty-state {
  display: grid;
  justify-items: center;
  gap: var(--sp-8);
  padding: var(--sp-24) var(--sp-16);
  text-align: center;
}
/* 24 is the one size an icon is drawn at outside text and chrome (5.8), and
   this is the only place it happens. */
.empty-state__icon {
  width: 24px;
  height: 24px;
  color: var(--line-strong);
}
.empty-state__what {
  margin: 0;
  font-size: var(--fs-ui);
  line-height: var(--lh-ui);
  font-weight: var(--fw-medium);
  color: var(--ink);
}
.empty-state__why {
  margin: 0;
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: var(--slate);
  max-width: 46ch;
}
.empty-state__act { margin: 0; }

/* ---- 7.11 Dialog ----------------------------------------------------------
   A destructive confirm and nothing else: title, one sentence, the verb, and
   Cancel. `showModal()` gives the focus trap, Escape and the backdrop; this
   block only dresses them (`static/js/confirm-dialog.js`). */

.dialog {
  width: min(30rem, calc(100vw - var(--sp-32)));
  padding: var(--sp-20);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-drawer);
  z-index: var(--z-dialog);
}
.dialog::backdrop { background: color-mix(in srgb, var(--ink) 40%, transparent); }
.dialog__title {
  margin: 0 0 var(--sp-8);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  font-weight: var(--fw-semibold);
}
.dialog__body {
  margin: 0 0 var(--sp-20);
  color: var(--ink-2);
  font-size: var(--fs-ui);
  line-height: var(--lh-ui);
}
.dialog__actions { display: flex; flex-wrap: wrap; gap: var(--sp-8); justify-content: flex-end; }

/* ---- 8 In flight ----------------------------------------------------------
   A 2px accent bar along the top of the panel that is waiting, and only after
   `--dur-wait`, so a fast answer never flashes it. The panel is what carries
   the class because the forms inside it say `hx-indicator="closest .panel"`;
   without that attribute htmx marks the form instead and this rule never
   matches. The wait is an `animation-delay`, which the global reduced-motion
   block in `base.css` deliberately leaves alone: waiting is not motion, and
   flattening it would make the bar flash on every fast answer. Only the fade
   itself is shortened there. */

.panel.htmx-request::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 2px;
  background: var(--accent);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  animation: panel-in-flight var(--dur-1) var(--ease) var(--dur-wait) both;
}
@keyframes panel-in-flight {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* A panel whose content is on its way: two grey lines, never an empty box. */
.skeleton { display: grid; gap: var(--sp-8); }
.skeleton::before,
.skeleton::after {
  content: "";
  height: 0.75rem;
  border-radius: var(--radius-sm);
  background: var(--neutral-soft);
}
.skeleton::after { width: 60%; }

