﻿/* All state colors below use Bootstrap 5.3's theme-aware form tokens
   (--bs-form-invalid-color / --bs-form-valid-color and their -border-color
   variants) rather than raw --bs-danger / --bs-success. The raw palette
   fails WCAG AA as dark-theme TEXT (#dc3545 on #212529 is 3.4:1; #198754
   doesn't even clear the 3:1 graphics threshold) — the form tokens swap to
   the emphasis shades (#ea868f / #75b798) in dark, which is exactly what
   Bootstrap's own .is-invalid/.is-valid use. */
.field-validation-error {
    color: var(--bs-form-invalid-color, #dc3545);
    font-style: italic;
}

.input-validation-error {
    border-color: var(--bs-form-invalid-border-color, #dc3545);
    outline-color: var(--bs-form-invalid-border-color, #dc3545);
}

.input-validation-valid {
    border-color: var(--bs-form-valid-border-color, #198754);
    outline-color: var(--bs-form-valid-border-color, #198754);
}

.validation-summary-errors {
    color: var(--bs-form-invalid-color, #dc3545);
    font-style: italic;
}

/* MVC scaffolding conventionally pairs the message span / summary div with
   Bootstrap's .text-danger utility, whose !important #dc3545 would override
   the theme-aware color above and fail AA on dark backgrounds (3.4:1).
   Match its weight in dark only — light-theme #dc3545 on white passes. */
[data-bs-theme="dark"] .field-validation-error,
[data-bs-theme="dark"] .validation-summary-errors {
    color: var(--bs-form-invalid-color, #ea868f) !important;
}

.validation-summary-valid span {
    display: none;
}

/* App-side conditional checks ("email required only when a name was entered") need
   their own class: aspnet-validation's passes swap .input-validation-error /
   .input-validation-valid freely, so a custom check that reuses those classes gets
   its flag stripped by the library's next pass. Declared AFTER .input-validation-valid
   so the red border wins when a field carries both classes. */
.conditional-required-error {
    border-color: var(--bs-form-invalid-border-color, #dc3545);
    outline-color: var(--bs-form-invalid-border-color, #dc3545);
}

/* Focus must not wash out a validation state: capstone-bootstrap.css paints a
   brand-green focus ring on every .form-control/.form-select — and the
   ValidationService focuses the first invalid field on submit, so the focused
   field is exactly the one that must keep reading as invalid. Mirror
   Bootstrap's own .is-invalid:focus convention: keep the state border, tint
   the ring to the state color. Specificity is deliberate — these (0,3,0)
   selectors tie [data-bs-theme=dark] .form-control:focus and win on source
   order (this sheet loads after capstone-bootstrap.css). Valid before error,
   same as the glyph rules: a focused both-class field stays red. */
.form-control.input-validation-valid:focus,
.form-select.input-validation-valid:focus {
    border-color: var(--bs-form-valid-border-color, #198754);
    box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb, 25, 135, 84), 0.25);
}

.form-control.input-validation-error:focus,
.form-select.input-validation-error:focus,
.form-control.conditional-required-error:focus,
.form-select.conditional-required-error:focus {
    border-color: var(--bs-form-invalid-border-color, #dc3545);
    box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb, 220, 53, 69), 0.25);
}

/* --- Bootstrap's own state icons are suppressed ---
   Bootstrap is the stock CDN build, and it paints its own background-image SVGs
   (a bare tick, an outlined exclamation) on .is-valid / .is-invalid — a different
   icon vocabulary from the filled bootstrap-icons glyphs below and from the
   badges the Capstone pickers draw. One vocabulary: strip the SVGs here so a
   server-side ModelState .is-invalid and a client-side .input-validation-error
   look identical. The select rule matches Bootstrap's own (0,4,0) specificity
   and wins on source order; its extra right padding is left as-is (harmless
   clearance for the glyph). */
.form-control.is-valid,
.form-control.is-invalid,
.was-validated .form-control:valid,
.was-validated .form-control:invalid {
    background-image: none;
}

.form-select.is-valid:not([multiple]):not([size]),
.form-select.is-valid:not([multiple])[size="1"],
.form-select.is-invalid:not([multiple]):not([size]),
.form-select.is-invalid:not([multiple])[size="1"],
.was-validated .form-select:valid:not([multiple]):not([size]),
.was-validated .form-select:valid:not([multiple])[size="1"],
.was-validated .form-select:invalid:not([multiple]):not([size]),
.was-validated .form-select:invalid:not([multiple])[size="1"] {
    --bs-form-select-bg-icon: none;
}

/* --- Validation state icons ---
   The borders above color the field; these add the matching foreground glyph for
   plain form-control/form-select fields inside .form-floating — the same
   bootstrap-icons approach the Capstone components use for their own badges.
   Covers both the aspnet-validation classes (.input-validation-*) and Bootstrap's
   own .is-valid / .is-invalid so server-rendered and client-side states match.
   :has() keeps it markup-free, so dynamically added rows are covered
   automatically, and the > child combinator keeps it off component internals.
   The autocomplete's search input also sits as a direct child of a .form-floating
   and carries .is-valid/.is-invalid, but it draws its own icon in a slot left of
   its clear ×; the zero-specificity :where() exclusion keeps this glyph off it
   without disturbing the valid-before-error rule order below. The plain `content`
   declaration is the fallback for engines without alt-text syntax; the `/ ""` form
   marks the glyph decorative for screen readers. */
.form-floating:has(> .input-validation-valid)::after,
.form-floating:has(> .input-validation-error)::after,
.form-floating:has(> .conditional-required-error)::after,
.form-floating:has(> .is-valid:where(:not(.capstone-autocomplete-input)))::after,
.form-floating:has(> .is-invalid:where(:not(.capstone-autocomplete-input)))::after {
    font-family: "bootstrap-icons";
    position: absolute;
    top: 50%;
    right: 0.75rem;
    transform: translateY(-50%);
    line-height: 1;
    pointer-events: none;
    z-index: 5;
}

.form-floating:has(> .input-validation-valid)::after,
.form-floating:has(> .is-valid:where(:not(.capstone-autocomplete-input)))::after {
    content: "\f26a"; /* bi-check-circle-fill */
    content: "\f26a" / "";
    color: var(--bs-form-valid-color, #198754);
}

/* Error rules come AFTER the valid rule: a field carrying both classes (a
   format-valid but empty input flagged by an app-side conditional check) shows
   the red exclamation, not the green check. */
.form-floating:has(> .input-validation-error)::after,
.form-floating:has(> .conditional-required-error)::after,
.form-floating:has(> .is-invalid:where(:not(.capstone-autocomplete-input)))::after {
    content: "\f332"; /* bi-exclamation-circle-fill */
    content: "\f332" / "";
    color: var(--bs-form-invalid-color, #dc3545);
}

/* Selects carry the dropdown chevron at the right edge — sit the icon left of it. */
.form-floating:has(> .form-select.input-validation-valid)::after,
.form-floating:has(> .form-select.input-validation-error)::after,
.form-floating:has(> .form-select.conditional-required-error)::after,
.form-floating:has(> .form-select.is-valid)::after,
.form-floating:has(> .form-select.is-invalid)::after {
    right: 2.25rem;
}

/* Reserve room so the value / floating label never slides under the icon. */
.form-control.input-validation-valid,
.form-control.input-validation-error,
.form-control.conditional-required-error,
.form-control.is-valid,
.form-control.is-invalid {
    padding-right: 2.25rem;
}

.form-select.input-validation-valid,
.form-select.input-validation-error,
.form-select.conditional-required-error {
    padding-right: 3.75rem;
}
