:root {
    --ui-shadow: 0 28px 80px rgba(15, 23, 42, 0.12);
    --ui-soft-border: rgba(15, 23, 42, 0.08);
}

/* ================= ALERT ================= */
.ui-alert {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 18px;
    align-items: center;
    padding: 22px;
    border-radius: 22px;
    background: #ffffff;
    border: 1px solid var(--ui-soft-border);
    box-shadow: var(--ui-shadow);
    color: #0f172a;
    overflow: hidden;
    animation: ui-fade-in 0.26s ease both;
    margin-bottom: 20px;
}

.ui-alert-icon {
    width: 52px;
    height: 52px;
    display: grid;
    place-items: center;
    border-radius: 18px;
    font-size: 1.15rem;
    flex-shrink: 0;
}

.ui-alert-body {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.ui-alert-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.ui-alert-message {
    margin: 0;
    color: #475569;
    line-height: 1.7;
    font-size: 0.95rem;
    /* Left, not justified: an alert is usually one short sentence or a list of
       them, and justification stretches the word gaps of a line that never
       reaches the edge. */
    text-align: left;
}

/* A slot carrying a list of validation errors. */
.ui-alert-message ul {
    margin: 0;
    padding-left: 20px;
}

.ui-alert-message p + p {
    margin-top: 4px;
}

.ui-alert-actions {
    margin-top: 6px;
}

/* An action posted rather than followed - "Post Interest" is a POST, not a
   link, so it arrives in the slot as a form rather than through actionHref. */
.ui-alert-message form {
    margin-top: 12px;
}

.ui-alert-close {
    border: none;
    background: transparent;
    color: #667085;
    cursor: pointer;
    padding: 10px;
    border-radius: 999px;
    transition: transform 0.2s ease, color 0.2s ease;
}

.ui-alert-close:hover {
    color: #0f172a;
    transform: scale(1.08);
}

.ui-alert-info {
    border-color: rgba(16, 186, 203, 0.18);
}

.ui-alert-info .ui-alert-icon {
    background: rgba(16, 186, 203, 0.14);
    color: var(--primary-dark);
}

.ui-alert-success {
    border-color: rgba(16, 186, 203, 0.14);
}

.ui-alert-success .ui-alert-icon {
    background: rgba(16, 186, 203, 0.14);
    color: #0f766e;
}

.ui-alert-warning {
    border-color: rgba(245, 158, 11, 0.16);
}

.ui-alert-warning .ui-alert-icon {
    background: rgba(245, 158, 11, 0.14);
    color: #b45309;
}

.ui-alert-danger {
    border-color: rgba(239, 68, 68, 0.16);
}

.ui-alert-danger .ui-alert-icon {
    background: rgba(239, 68, 68, 0.14);
    color: #b91c1c;
}

.ui-alert-neutral {
    border-color: rgba(148, 163, 184, 0.16);
}

.ui-alert-neutral .ui-alert-icon {
    background: rgba(148, 163, 184, 0.14);
    color: #334155;
}

/* ================= TOASTS ================= */
.ui-toast-stack {
    position: fixed;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    gap: 16px;
    z-index: 1100;
    width: min(420px, calc(100vw - 32px));
    padding: 0;
}

.ui-toast {
    display: grid;
    gap: 14px;
    padding: 18px 20px;
    border-radius: 22px;
    background: #ffffff;
    border: 1px solid rgba(15, 23, 42, 0.08);
    box-shadow: var(--ui-shadow);
    overflow: hidden;
    animation: ui-toast-slide-in 0.35s ease forwards;
    opacity: 0;
    transform: translateX(18px);
}

.ui-toast-exit {
    opacity: 0;
    transform: translateX(18px);
    transition: opacity 0.2s ease, transform 0.2s ease;
}

.ui-toast-meta {
    display: flex;
    align-items: center;
    gap: 16px;
}

.ui-toast-icon {
    width: 46px;
    min-width: 46px;
    height: 46px;
    border-radius: 16px;
    display: grid;
    place-items: center;
    background: rgba(16, 186, 203, 0.14);
    color: var(--primary-dark);
    font-size: 1rem;
}

.ui-toast-content {
    flex: 1;
    display: grid;
    gap: 6px;
}

.ui-toast-title {
    margin: 0;
    font-weight: 700;
    font-size: 0.98rem;
    color: #0f172a;
}

.ui-toast-message {
    margin: 0;
    font-size: 0.95rem;
    color: #475569;
    line-height: 1.7;
    text-align: justify;
}

.ui-toast-close {
    border: none;
    background: transparent;
    color: #64748b;
    cursor: pointer;
    padding: 8px;
    border-radius: 999px;
    transition: color 0.2s ease, transform 0.2s ease;
}

.ui-toast-close:hover {
    color: #0f172a;
    transform: scale(1.05);
}

.ui-toast-action {
    display: flex;
    justify-content: flex-end;
}

.ui-toast-info .ui-toast-icon {
    background: rgba(16, 186, 203, 0.14);
}

.ui-toast-success .ui-toast-icon {
    background: rgba(16, 186, 203, 0.14);
}

.ui-toast-warning .ui-toast-icon {
    background: rgba(245, 158, 11, 0.14);
}

.ui-toast-danger {
    background: #fee2e2;
    border-color: #fecaca;
}

.ui-toast-danger .ui-toast-icon {
    background: rgba(239, 68, 68, 0.14);
    color: #dc2626;
}

.ui-toast-danger .ui-toast-message {
    color: #7f1d1d;
}

.ui-toast-neutral .ui-toast-icon {
    background: rgba(148, 163, 184, 0.14);
}

/* ================= MODAL ================= */
.ui-modal {
    visibility: hidden;
    opacity: 0;
    position: fixed;
    inset: 0;
    display: grid;
    place-items: center;
    z-index: 1200;
    transition: opacity 0.24s ease, visibility 0.24s ease;
}

.ui-modal-open {
    visibility: visible;
    opacity: 1;
}

.ui-modal-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.58);
    backdrop-filter: blur(6px);
}

.ui-modal-panel {
    position: relative;
    /* Gutters are folded into the width, not added as a margin: the panel is a
       percentage of the overlay, so `100%` plus any margin overflows the
       viewport by exactly that margin. Padding on the overlay is no good
       either - the wizard panels below size themselves in vw. */
    width: min(100% - 32px, 560px);
    background: #ffffff;
    border-radius: 28px;
    padding: 36px 32px 28px;
    box-shadow: 0 40px 120px rgba(15, 23, 42, 0.16);
    transform: translateY(24px) scale(0.96);
    opacity: 0;
    transition: transform 0.24s ease, opacity 0.24s ease;
}

/* A panel taller than the viewport runs off both ends of a fixed grid centring
   box with nothing to scroll, so bound it and let it scroll itself.

   This was previously scoped to phones, on the assumption that only a narrow
   viewport could be shorter than a modal. Height is what matters, not width: a
   laptop window, or any browser with toolbars open, cuts the footer off a long
   form the same way and left the confirm button unreachable. Native select
   popups are not clipped by this, and no modal here uses a custom dropdown.

   The release wizard is excluded: its panel is bounded below and hands the
   scrolling to .release-modal-scroll so its hero and footer stay pinned. */
.ui-modal-panel:not(:has(.release-modal-scroll)) {
    max-height: 88dvh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
}

/* A panel whose slot brings its own scroll region needs a bounded height and a
   column layout, or that region has no height to scroll within: the panel sizes
   to its content, grows past the fixed non-scrolling .ui-modal overlay, and the
   overflow becomes unreachable. Scoped rather than applied to every modal —
   .ui-modal-body is a clipping context once it scrolls, which would trap
   dropdowns and popovers in the short modals that render fine today. */
.ui-modal-panel:has(.release-modal-scroll) {
    display: flex;
    flex-direction: column;
    max-height: 90dvh;
}

.ui-modal-panel:has(.release-modal-scroll) .ui-modal-status,
.ui-modal-panel:has(.release-modal-scroll) .ui-modal-content,
.ui-modal-panel:has(.release-modal-scroll) .ui-modal-actions {
    flex: 0 0 auto;
}

.ui-modal-open .ui-modal-panel {
    transform: translateY(0) scale(1);
    opacity: 1;
}


/* -------------------------------------------------------------------------
   Modal forms.

   A two-column grid of stacked label/control pairs, which is what every
   accountant modal that takes input is built from.

   These rules lived in pages/accountant/expenses.css, so the only modal that
   got them was the one on that page. The savings and time deposit modals use
   the same markup and load a different page stylesheet, so their fields
   rendered as raw inline inputs with the labels running into them. Moved here
   beside the modal component itself, which every layout loads, rather than
   copied a third and fourth time.

   `components/responsive.css` collapses .add-expense-form to one column below
   820px and loads after this file, so the grid still folds on a phone.
   ------------------------------------------------------------------------- */
/* Row and field spacing are deliberately tighter than the page forms. A modal
   is bounded by the viewport, and every extra pixel per field is multiplied by
   the number of fields - the seven-field deposit form ran past the bottom of a
   laptop window at the old 18px/10px/14px rhythm. */
.add-expense-form {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px 16px;
}

.add-expense-form .field-group {
    display: grid;
    gap: 6px;
    align-content: start;
}

.add-expense-form label {
    color: var(--text-dark, #1f2937);
    font-weight: 700;
    font-size: 0.85rem;
}

/* The "(optional)" qualifier some labels carry. Muted and lighter so the
   field's name still reads as the label. */
.add-expense-form label span {
    color: #94a3b8;
    font-weight: 500;
}

.add-expense-form input,
.add-expense-form select,
.add-expense-form textarea {
    width: 100%;
    border: 1px solid #D1D5DB;
    border-radius: 12px;
    background: #F8FAFC;
    color: var(--text-dark, #1f2937);
    padding: 10px 14px;
    font-size: 0.92rem;
    font-family: inherit;
    outline: none;
}

/* Two rows of text, not four. A remarks box is a note, and the field can be
   dragged taller when it needs to be. */
.add-expense-form textarea {
    resize: vertical;
    min-height: 68px;
}

.add-expense-form select {
    background: #ffffff;
}

.add-expense-form input:focus,
.add-expense-form select:focus,
.add-expense-form textarea:focus {
    border-color: var(--accent, #10bacb);
    box-shadow: 0 0 0 4px rgba(16, 186, 203, 0.12);
}

/* Guidance under a control - "Per month, as negotiated with this depositor".
   Its own line under the input rather than trailing the field, which is what
   made the hint text run into the next label. */
.add-expense-form .field-group small {
    color: #64748b;
    font-size: 0.78rem;
    line-height: 1.45;
}

.add-expense-form .field-group.full-width {
    grid-column: 1 / -1;
}

/* A form modal's hero, trimmed.

   The 84px badge, the wide title and a subtitle wrapped to two lines by its
   82% cap are right for a confirmation dialog, where they are most of the
   content. Above a seven-field form they cost roughly a hundred pixels before
   the first label, which is what pushed the deposit form's footer off screen.

   Scoped with :has() so only the modals that carry a form are affected and
   every confirmation dialog in the app keeps the hero it was designed with -
   the same technique this file already uses for the release wizard. */
.ui-modal-panel:has(.add-expense-form) {
    padding-top: 26px;
}

.ui-modal-panel:has(.add-expense-form) .ui-modal-status {
    width: 56px;
    height: 56px;
    margin-bottom: 14px;
    box-shadow: 0 10px 24px rgba(16, 186, 203, 0.12);
}

.ui-modal-panel:has(.add-expense-form) .ui-modal-status i {
    font-size: 1.2rem;
}

.ui-modal-panel:has(.add-expense-form) .ui-modal-content {
    margin-bottom: 16px;
}

.ui-modal-panel:has(.add-expense-form) .ui-modal-title {
    font-size: 1.2rem;
}

.ui-modal-panel:has(.add-expense-form) .ui-modal-subtitle {
    margin-top: 6px;
    font-size: 0.88rem;
    line-height: 1.5;
    /* The 82% cap is what forced this one line of copy onto two. */
    max-width: 100%;
}

.ui-modal-panel:has(.add-expense-form) .ui-modal-body {
    margin-bottom: 18px;
}

.ui-modal-close {
    position: absolute;
    top: 18px;
    right: 18px;
    border: none;
    background: rgba(148, 163, 184, 0.12);
    color: #334155;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: transform 0.2s ease, background 0.2s ease;
}

.ui-modal-close:hover {
    transform: scale(1.05);
    background: rgba(16, 186, 203, 0.14);
    color: var(--primary-dark);
}

.ui-modal-status {
    width: 84px;
    height: 84px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    margin: 0 auto 22px;
    box-shadow: 0 18px 40px rgba(16, 186, 203, 0.12);
}

.ui-modal-status i {
    font-size: 1.8rem;
}

.ui-modal-info {
    background: rgba(16, 186, 203, 0.14);
    color: var(--primary-dark);
}

.ui-modal-success {
    background: rgba(16, 186, 203, 0.14);
    color: #0f766e;
}

.ui-modal-warning {
    background: rgba(245, 158, 11, 0.14);
    color: #b45309;
}

.ui-modal-danger {
    background: rgba(239, 68, 68, 0.14);
    color: #b91c1c;
}

.ui-modal-neutral {
    background: rgba(148, 163, 184, 0.14);
    color: #334155;
}

.ui-modal-content {
    text-align: center;
    margin-bottom: 20px;
}

.ui-modal-title {
    margin: 0;
    font-size: 1.35rem;
    font-weight: 700;
    color: #0f172a;
}

.ui-modal-subtitle {
    margin: 10px auto 0;
    color: #334155;
    font-size: 1rem;
    line-height: 1.7;
    max-width: 82%;
}

.ui-modal-copy {
    margin: 16px auto 0;
    color: #475569;
    line-height: 1.75;
    font-size: 0.98rem;
    max-width: 88%;
    text-align: justify;
}

.ui-modal-body {
    margin-bottom: 26px;
    color: #334155;
    font-size: 0.97rem;
    line-height: 1.75;
    text-align: justify;
}

/* Hand the scrolling down to .release-modal-scroll so the slot's own hero
   header and footer stay pinned while only the sections between them move. */
.ui-modal-panel:has(.release-modal-scroll) .ui-modal-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

.ui-modal-actions {
    display: flex;
    justify-content: center;
    gap: 14px;
    flex-wrap: wrap;
}

.ui-modal-close {
    border: none;
    background: rgba(148, 163, 184, 0.12);
    color: #334155;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: transform 0.2s ease, background 0.2s ease;
}

.ui-modal-close:hover {
    transform: scale(1.05);
    background: rgba(16, 186, 203, 0.14);
    color: var(--primary-dark);
}

.ui-modal-button {
    min-width: 110px;
}

@keyframes ui-toast-slide-in {
    from {
        opacity: 0;
        transform: translateX(18px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes ui-fade-in {
    from {
        opacity: 0;
        transform: translateY(-8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@media (max-width: 768px) {
    .ui-modal-actions .ui-modal-button {
        flex: 1 1 auto;
    }
}

@media (max-width: 520px) {
    .ui-toast-stack {
        right: 12px;
        left: 12px;
        width: auto;
    }

    .ui-modal-panel {
        padding: 24px 18px 20px;
        border-radius: 24px;
    }

    .ui-modal-panel:not(:has(.release-modal-scroll)) {
        max-height: 90dvh;
    }

    /* The 84px status disc plus its 22px margin costs a quarter of a phone
       screen before any content shows. */
    .ui-modal-status {
        width: 60px;
        height: 60px;
        margin-bottom: 16px;
    }

    .ui-modal-status i {
        font-size: 1.3rem;
    }

    .ui-modal-close {
        top: 12px;
        right: 12px;
        width: 38px;
        height: 38px;
    }

    .ui-modal-subtitle,
    .ui-modal-copy {
        max-width: 100%;
    }

    /* Justified text on a ~280px measure opens rivers between words. */
    .ui-modal-copy,
    .ui-modal-body {
        text-align: left;
    }

    .ui-modal-actions {
        flex-direction: column-reverse;
    }

    .ui-modal-actions .ui-modal-button {
        width: 100%;
    }
}
