/* ============================================================================
   AVFX CRM — MODAL DIALOGUE
   Specification: docs/components/03-modal.md §6
   Two widths only, 400 and 560 (docs/03-UX-PATTERNS.md §12).
   ========================================================================== */

.modal {
  --modal-w: 560px;
  /* reset.css sets "* { margin: 0 }", which removes the automatic margin the
     browser uses to centre a dialogue in the top layer. Put it back here
     rather than exempting <dialog> globally. */
  margin: auto;
  inline-size: var(--modal-w);
  max-inline-size: calc(100vw - var(--space-8) * 2);
  max-block-size: calc(100vh - var(--space-9) * 2);
  padding: 0;
  display: flex; flex-direction: column;
  background: var(--color-surface);
  color: var(--color-text-primary);
  border: none;
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-3);
}
/* A CLOSED DIALOGUE IS NOT ON THE PAGE.
   The browser's own stylesheet says "dialog:not([open]) { display: none }",
   but the "display: flex" above is an author rule and beats it — so a dialogue
   written into the markup and left closed rendered in full at the foot of the
   page. Found 2026-08-14 on the client Contacts tab, which is the first screen
   to DECLARE its dialogues rather than build them on demand: confirm-delete
   creates its modal, uses it and destroys it, so nothing had ever sat here
   closed before. Restoring the browser's behaviour explicitly. */
.modal:not([open]) { display: none; }

.modal--sm { --modal-w: 400px; }

/* A confirmation focuses the dialogue itself (Packet 91) so Enter reaches the
   default button. The dialogue is a container, not a control: no ring. */
.modal:focus { outline: none; }

.modal::backdrop { background: var(--color-backdrop); }

.modal[open] { animation: modal-in var(--dur-base) var(--ease-out); }
@keyframes modal-in { from { opacity: 0; transform: translateY(8px); } }
@media (prefers-reduced-motion: reduce) { .modal[open] { animation: none; } }

/* --- HEAD ----------------------------------------------------------------- */

.modal__head {
  display: flex; align-items: center; gap: var(--space-5);
  padding: var(--space-5) var(--space-5) var(--space-5) var(--space-6);
  border-block-end: 1px solid var(--color-border);
}
.modal__title { flex: 1; font-size: var(--fs-md); font-weight: var(--fw-semi); }
.modal__x { flex: none; color: var(--color-text-tertiary); }

/* --- BODY ----------------------------------------------------------------
   Only this scrolls, so the title and the buttons never leave the screen. */

.modal__body {
  flex: 1; min-block-size: 0; overflow-y: auto;
  padding: var(--space-6);
  display: flex; flex-direction: column; gap: var(--space-5);
  font-size: var(--fs-sm);
}

/* --- ERROR ---------------------------------------------------------------- */

.modal__err {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: 0 var(--space-6) var(--space-5);
  font-size: var(--fs-xs); color: var(--color-danger);
}
.modal__err svg { inline-size: 14px; block-size: 14px; flex: none; margin-block-start: 1px; }
/* "display: flex" beats the hidden attribute, which would otherwise leave a
   bare error icon sitting under every dialogue. */
.modal__err[hidden] { display: none; }

/* --- FOOT ----------------------------------------------------------------
   Primary last, because it is the destination.                            */

.modal__foot {
  display: flex; justify-content: flex-end; gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  border-block-start: 1px solid var(--color-border);
  background: var(--color-surface-sunken);
  border-end-start-radius: var(--radius-lg);
  border-end-end-radius: var(--radius-lg);
}
.modal__foot:empty { display: none; }

/* The page behind must not scroll under the dialogue. Counted in modal.js so
   nested layers cannot unlock it early. */
.is-modal-open { overflow: hidden; }

/* --- FIELD GROUP INSIDE A DIALOGUE ---------------------------------------- */

/* A <fieldset> brings its own border and legend notch, which nothing in the
   reset clears. Grouped radios need the fieldset semantics, so the styling is
   removed here instead of abandoning the correct element. */
.modal__field {
  display: flex; flex-direction: column; gap: var(--space-3);
  border: 0; min-inline-size: 0;
}
.modal__field > legend,
.modal__field > .modal__label {
  font-size: var(--fs-xs); font-weight: var(--fw-medium);
  color: var(--color-text-secondary); margin-block-end: var(--space-3);
}
.modal__opt { display: flex; flex-direction: column; gap: var(--space-2); }
/* The caption lines up under the control's label, not under its box. */
.modal__opt .caption { padding-inline-start: calc(16px + var(--space-4)); }

/* Two short field groups side by side. Short groups only — a stack of two
   three-item lists reads as one long list and the operator loses which
   heading they are under. Collapses to one column if the dialogue is ever
   narrowed below the point where the labels fit. */
.modal__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-5) var(--space-6);
  align-items: start;
}
