h1:focus {
    outline: none;
}

/* card-header-tabs sit on the card header's light gray background, where Bootstrap's
   default $primary link color (#0d6efd) falls just under the WCAG AA 4.5:1 contrast
   minimum (axe: color-contrast). Darken inactive tab links to restore contrast without
   changing the active tab's own (already-compliant) styling. */
.card-header-tabs .nav-link:not(.active) {
    color: #0a58ca;
}

/* <code> defaults to Bootstrap's $code-color (pink, #d63384), which falls short of the
   WCAG AA 4.5:1 contrast minimum (axe: color-contrast) on the yellow .alert-warning
   background used to show one-time secrets (recovery keys, reset tokens) - these are
   exactly the values a user most needs to actually read. Darken it there specifically. */
.alert-warning code {
    color: #7d1a4a;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* Toast sits below the navbar in the right corner; alerts use mb-0 so Bootstrap's
   default alert margin-bottom doesn't leave empty space under a single toast. */
.notification-toast-host {
    position: fixed;
    top: 4.5rem;
    right: 1rem;
    width: min(28rem, calc(100vw - 2rem));
    z-index: 1080;
    pointer-events: none;
}

.notification-toast-host .alert {
    pointer-events: auto;
    box-shadow: var(--bs-box-shadow);
}

/* Keep application modals clear of the navigation bar. */
.modal .app-modal-dialog {
    margin-top: 4.5rem;
}

.notification-menu {
    min-width: 18rem;
    max-width: min(24rem, calc(100vw - 2rem));
    max-height: 20rem;
    overflow-y: auto;
}

.net-worth-chart {
    height: 300px;
    max-width: 800px;
}

.net-worth-chart-axis {
    stroke: var(--bs-border-color);
    stroke-width: 1;
}

.net-worth-chart-zero-line {
    stroke: var(--bs-secondary);
    stroke-width: 1;
    stroke-dasharray: 4 4;
}

.net-worth-chart-series {
    stroke: var(--bs-primary);
    stroke-width: 2;
}

.net-worth-chart-label {
    fill: var(--bs-secondary-color);
    font-size: 12px;
}

.privacy-page {
    display: flex;
    flex-direction: column;
    height: calc(100dvh - 8rem);
    min-height: 0;
    overflow: hidden;
}

.privacy-page > * {
    flex-shrink: 0;
}

.privacy-notice {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.privacy-notice > :first-child {
    margin-top: 0;
}

.privacy-notice h2 {
    font-size: 1.25rem;
    margin-top: 1.5rem;
}

.modal-body .privacy-notice {
    flex: none;
    overflow: visible;
}

.passphrase-generated {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Bootstrap's .visually-hidden is position:absolute, so with no positioned ancestor its
   containing block is the page. Inside a .table-responsive that lets it escape the
   container's horizontal clipping and widen the whole document on a narrow viewport —
   the page then scrolls sideways instead of the table scrolling inside its own box.
   Anchoring cells keeps such a span inside the scrolling container. */
.table-responsive th,
.table-responsive td {
    position: relative;
}

/* Shared/CurrencyInput: an amount is never wider than it needs to be, so a currency
   field doesn't stretch across a whole card. It still shrinks below this on narrow
   viewports because the max-width, not a fixed width, is what's capped. */
.currency-input {
    max-width: 12rem;
}

/* Money reads as a column of figures, so every digit must take the same width: a 1 that
   sits narrower than an 8 stops amounts lining up down a table or between the two rows of
   a form. This is the app's own font with tabular figures switched on, not a second font
   family — a monospace face would read as code beside the rest of the UI. Covers both
   halves of a money field: the CurrencyInput a user types into, and the MoneyDisplay that
   renders the result. Dates line up for the same reason and by the same means — DateDisplay
   renders .date-value. */
.currency-input input,
.money-amount,
.date-value {
    font-variant-numeric: tabular-nums;
}


/* Modules/Transactions/TransactionsPage: the selection checkbox column carries no header
   text, so nothing else would stop it taking an equal share of the table's width. */
.transaction-select-column {
    width: 1%;
    white-space: nowrap;
}

/* Modules/Transactions/TransactionsPage: the from/to pair renders as one input group.
   Bootstrap's input-group rules target immediate children, and an AccountPicker's immediate
   child is its own wrapper div rather than the .form-control — so the radius reset has to be
   pushed down to the input itself, and the wrapper has to be told to share the row's width
   the way a .form-control would.

   align-items: flex-start matters more than it looks: the inline popover renders in flow
   (see .account-picker-inline below), so an open picker grows taller, and the default
   stretch would drag the other side's input to the same height. */
.transaction-sides {
    align-items: flex-start;
    flex-wrap: nowrap;
    min-width: 22rem;
}

.input-group > .account-picker {
    flex: 1 1 auto;
    width: 1%;
    min-width: 0;
}

.input-group > .account-picker:not(:first-child) .form-control {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.input-group > .account-picker:not(:last-child) .form-control {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

/* Modules/Transactions/AccountPicker: the popover is anchored to the input's own box,
   so the wrapper is the positioning context Bootstrap's absolutely positioned
   .dropdown-menu needs (.dropdown-menu without Popper leaves top/left at auto). */
.account-picker {
    position: relative;
}

.account-picker-menu {
    top: 100%;
    left: 0;
    width: 100%;
    margin-top: 0.125rem;
}

/* The compact form factor sits inside a .table-responsive, whose overflow would clip an
   absolutely positioned popover instead of letting it hang over the row. In flow the row
   simply grows, so nothing is ever hidden behind the container's scroll box. */
.account-picker-inline .account-picker-menu {
    position: static;
    float: none;
    margin-top: 0.25rem;
}

/* The option list scrolls rather than growing past the viewport. Focus deliberately
   stays on the combobox input (aria-activedescendant), so this is never a tab stop of
   its own — budgetAppCombobox.scrollOptionIntoView keeps the active option in view
   while the arrow keys move it. */
.account-picker-options {
    max-height: 16rem;
    overflow-y: auto;
}

.account-picker-option {
    cursor: pointer;
}

/* Shown until the Blazor circuit attaches — see AppLoadingOverlay. Above Bootstrap's modal
   (1055) and the toast host (1080), because a prerendered page can already carry an open
   modal and nothing underneath is usable yet. */
.app-loading-overlay {
    position: fixed;
    inset: 0;
    z-index: 1090;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(var(--bs-body-bg-rgb), 0.75);

    /* The safety valve. If the circuit never attaches — JavaScript blocked, a handshake that
       never completes — a blocking overlay would leave the app permanently unusable, which is
       worse than the dead controls it exists to prevent. After 20 seconds it gets out of the
       way and the page behaves exactly as it did before this overlay existed. Pure CSS on
       purpose: a timer in C# cannot run when the circuit is what failed. */
    animation: app-loading-overlay-give-up 0s linear 20s forwards;
}

@keyframes app-loading-overlay-give-up {
    to {
        opacity: 0;
        visibility: hidden;
    }
}

@media (prefers-reduced-motion: reduce) {
    .app-loading-overlay .spinner-border {
        animation-duration: 2s;
    }
}

/* Modules/Transactions/TransactionsPage: a row's own match status, as an accent on the
   row rather than another badge in the Status cell — the outside-budget-period badge
   already lives there, and two badges side by side read as one compound claim. An inset
   shadow rather than a border-left: a table cell's own borders are already spoken for,
   and a shadow cannot shift the column's width. */
.transaction-match-danger > td:first-child {
    box-shadow: inset 0.25rem 0 0 var(--bs-danger);
}

.transaction-match-warning > td:first-child {
    box-shadow: inset 0.25rem 0 0 var(--bs-warning);
}

.transaction-match-success > td:first-child {
    box-shadow: inset 0.25rem 0 0 var(--bs-success);
}
