/*--------------------------------------------------------------
# Field validation (field-validation.js, VALIDATION_UX_PLAN.md)
#
# An invalid field gets a red border (and a ! for text fields); its message shows in one floating bubble for the
# focused or hovered field, so an error never pushes the form's rows apart. The script sets html.fv-ready; until
# then (or if it fails) the old inline messages show as before.
--------------------------------------------------------------*/
:root {
    --fv-danger: #dc3545;
    --fv-ring: rgba(220, 53, 69, 0.22);
    --fv-bubble-bg: #fff;
    --fv-bubble-fg: #1f2937;
    --fv-bubble-border: #c9ced6;
    --fv-bubble-shadow: 0 6px 18px rgba(16, 24, 40, 0.18), 0 1px 3px rgba(16, 24, 40, 0.12);
    --fv-badge-bg: #e8590c;
    --fv-badge-fg: #fff;
    --fv-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
}

html[data-v2-scene="night"] body.shell-v2 {
    --fv-danger: #ff8b82;
    --fv-ring: rgba(255, 139, 130, 0.25);
    --fv-bubble-bg: var(--n-solid);
    --fv-bubble-fg: var(--n-text);
    --fv-bubble-border: var(--n-edge);
    --fv-bubble-shadow: 0 6px 18px rgba(0, 0, 0, 0.55), 0 1px 3px rgba(0, 0, 0, 0.5);
    --fv-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23ff8b82' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ff8b82' stroke='none'/%3e%3c/svg%3e");
}

/* ---- The invalid field. A state, so it wins over the scene's and the field's own border colours (Date In's
   green, Readiness's amber). .fv-invalid is the visible stand-in of a hidden field (a Select2 box). ---- */
.input-validation-error:is(.form-control, .form-select, .form-check-input),
.fv-invalid,
.fv-invalid.select2-selection {
    border-color: var(--fv-danger) !important;
}
.input-validation-error:is(.form-control, .form-select):focus,
.fv-invalid:focus,
.select2-container--focus .fv-invalid.select2-selection,
.select2-container--open .fv-invalid.select2-selection {
    border-color: var(--fv-danger) !important;
    box-shadow: 0 0 0 0.2rem var(--fv-ring) !important;
}

/* The !, for fields with nothing of their own at the right edge (selects, dates and files have an indicator), and
   room for it (the script sets .fv-noicon on a field under 110px wide). */
html.fv-ready :is(input.form-control:not([type=date], [type=datetime-local], [type=time], [type=month], [type=week], [type=file], [type=color]), textarea.form-control).input-validation-error:not(.fv-noicon) {
    padding-right: calc(1.5em + 0.75rem);
    background-image: var(--fv-icon);
    background-repeat: no-repeat;
    background-position: right calc(0.375em + 0.1875rem) center;
    background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
html.fv-ready textarea.form-control.input-validation-error {
    background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
}

/* ---- The field's own message span: kept for screen readers (the field's aria-describedby), off the layout. A span
   whose field the script cannot find (.fv-unplaced) keeps showing inline, so no message is ever lost. ---- */
html.fv-ready span[data-valmsg-for]:not(.fv-unplaced) {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

/* ---- The bubble: one for the page, in <body>, so no card or modal clips it. ---- */
.fv-bubble {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 2090; /* over modals (1055) and the header; under the header toast (2100) */
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    width: max-content;
    max-width: min(22rem, calc(100vw - 32px));
    padding: 0.45rem 0.7rem 0.45rem 0.5rem;
    border: 1px solid var(--fv-bubble-border);
    border-radius: 6px;
    background: var(--fv-bubble-bg);
    box-shadow: var(--fv-bubble-shadow);
    color: var(--fv-bubble-fg);
    font-size: 0.875rem;
    font-weight: 400;
    line-height: 1.35;
    text-align: left;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-3px);
    transition: opacity 0.12s ease, transform 0.12s ease, visibility 0s linear 0.12s;
}
.fv-bubble.is-shown {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity 0.12s ease, transform 0.12s ease, visibility 0s;
}
.fv-bubble.is-above { transform: translateY(3px); }
.fv-bubble.is-above.is-shown { transform: none; }

.fv-bubble-badge {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    border-radius: 3px;
    background: var(--fv-badge-bg);
    color: var(--fv-badge-fg);
    font-weight: 700;
    font-size: 0.85rem;
    line-height: 1;
}
.fv-bubble-text { padding-top: 0.05rem; overflow-wrap: anywhere; }

/* The arrow: a rotated square sharing the bubble's border; the script sets --fv-arrow-x. */
.fv-bubble::before {
    content: "";
    position: absolute;
    left: var(--fv-arrow-x, 1.25rem);
    top: -6px;
    width: 10px;
    height: 10px;
    margin-left: -5px;
    background: var(--fv-bubble-bg);
    border-left: 1px solid var(--fv-bubble-border);
    border-top: 1px solid var(--fv-bubble-border);
    transform: rotate(45deg);
}
.fv-bubble.is-above::before {
    top: auto;
    bottom: -6px;
    transform: rotate(225deg);
}

@media (prefers-reduced-motion: reduce) {
    .fv-bubble, .fv-bubble.is-shown { transition: none; transform: none; }
}

/* ---- The summary of errors that belong to no field (asp-validation-summary="ModelOnly"). ---- */
.validation-summary-errors {
    margin: 0 0 0.75rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid color-mix(in srgb, var(--fv-danger) 35%, transparent);
    border-left: 3px solid var(--fv-danger);
    border-radius: var(--alma-radius, 0.375rem);
    background: color-mix(in srgb, var(--fv-danger) 7%, transparent);
    color: var(--fv-danger);
    font-size: 0.9rem;
}
.validation-summary-errors ul { margin: 0; padding-left: 1.1rem; }
.validation-summary-errors ul:only-child li:only-child { list-style: none; margin-left: -1.1rem; }
