html, body {
    font-family: var(--wl-font-sans);
    color: var(--wl-fg-1);
    background: var(--wl-bg);
    margin: 0;
}

/* Code takes the mono token, the one face besides DM Sans that Threshold allows. Without this the
   audit-event codes render in the browser default `monospace`, which wl-font-probe reports. */
code, kbd, samp, pre {
    font-family: var(--wl-font-mono);
}

/* Skip link (#1438): first tab stop on every portal page, visible only on focus. */
.skip-link {
    position: absolute;
    left: 0.75rem;
    top: -4rem;
    z-index: 1000;
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    padding: 0 1rem;
    border-radius: var(--wl-radius-sm);
    background: var(--wl-ink);
    color: var(--wl-paper);
    font-weight: 600;
    text-decoration: none;
}

.skip-link:focus {
    top: 0.75rem;
}

main:focus {
    outline: none;
}

/* Touch targets (#1438): small buttons were 36-38px tall. */
body .wl-btn.wl-btn--sm {
    min-height: 2.75rem;
}

/* ── Utility and component base (#981) ──────────────────────────────────────
   The portal's markup is written in the Blazor Web App template's Bootstrap
   vocabulary — `btn`, `alert`, `form-control`, `d-flex`, `spinner-border`, the
   12-column grid — and App.razor linked `lib/bootstrap/dist/css/bootstrap.min.css`
   to supply it. That file has never existed in this repository: there is no
   `wwwroot/lib`, no libman/npm step, nothing in the Dockerfile, and no commit
   ever added one. The link 404'd in every environment, so every one of those
   classes matched no rule at all — `btn` had no padding (anchor buttons rendered
   as tinted text), `d-flex` did not lay out, `spinner-border` was 0px wide and
   never animated — and the Threshold overrides *below* were restyling a base
   that was not there.

   Vendoring Bootstrap now would pull 232 KB of Reboot plus components the
   Threshold redesign (#966) was neither designed nor reviewed against, so this
   defines the subset the portal actually uses instead, in the design system's
   own values. Declared here, ahead of the overrides below, so those keep the
   last word on colour. Spacing follows Bootstrap's scale (1 = .25rem,
   2 = .5rem, 3 = 1rem, 4 = 1.5rem) because the markup was written against it.
   PortalStylesheetContractTests fails if the markup starts using a class
   nothing defines, or if the host page links a stylesheet that is not there. */

/* Buttons. No colour here: `.btn-primary` and friends below own that. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    box-sizing: border-box;
    padding: 0.55rem 1.2rem;
    border: 1px solid transparent;
    border-radius: var(--wl-radius-sm);
    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.25;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    background-color: transparent;
}
.btn:disabled, .btn.disabled { opacity: 0.55; cursor: not-allowed; }
.btn-sm { padding: 0.35rem 0.85rem; font-size: 0.8rem; }
.btn-outline-secondary { color: var(--wl-fg-1); border-color: var(--wl-border-strong); }
.btn-outline-secondary:hover:not(:disabled) { background-color: var(--wl-bg-sunken); }
.btn-outline-primary { color: var(--wl-mid); border-color: var(--wl-mid); }
.btn-outline-primary:hover:not(:disabled) { color: var(--wl-fg-on-brand); background-color: var(--wl-mid); }
.btn-outline-danger { color: var(--wl-danger); border-color: var(--wl-danger); }
.btn-outline-danger:hover:not(:disabled) { color: var(--wl-fg-on-brand); background-color: var(--wl-danger); }
.btn-danger { color: var(--wl-fg-on-brand); background-color: var(--wl-danger); border-color: var(--wl-danger); }
.btn-warning { color: var(--wl-fg-on-brand); background-color: var(--wl-warning); border-color: var(--wl-warning); }
.btn-danger:hover:not(:disabled), .btn-warning:hover:not(:disabled) { opacity: 0.85; }
/* A button that reads as a link — the one on ProfileDetail. Undoes the box above. */
.btn-link { padding: 0; border-color: transparent; text-decoration: underline; }

/* LEGACY notice box, kept ONLY for Account/Plan.razor's single `alert alert-danger`
   (being edited elsewhere for locale pricing). Every other page uses <WlAlert>; delete
   this and the `.alert*` rules below in the follow-up that converts Plan.razor. */
.alert { padding: 0.75rem 1rem; margin-bottom: 1rem; }

/* Text inputs. `width: 100%` needs border-box or the padding overflows the field. */
.form-control {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 0.5rem 0.75rem;
    border: 1px solid;
    line-height: 1.4;
}
.input-group { display: flex; align-items: stretch; }
.input-group > .form-control { flex: 1 1 auto; width: 1%; }

/* Pill badge. The invoice status tints override the fill in Invoices.razor.css. */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.6rem;
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.4;
    border-radius: var(--wl-radius-pill);
}

/* Busy indicator inside a button. */
.spinner-border {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    vertical-align: -0.125em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: spinner-border 0.75s linear infinite;
}
.spinner-border-sm { width: 0.75rem; height: 0.75rem; border-width: 1.5px; }
@keyframes spinner-border { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .spinner-border { animation-duration: 1.5s; }
}

/* Twelve-column grid, gutters carried on the children like the markup expects. */
.row {
    --pg-gutter-x: 1.5rem;
    --pg-gutter-y: 0rem;
    display: flex;
    flex-wrap: wrap;
    margin-top: calc(-1 * var(--pg-gutter-y));
    margin-right: calc(-0.5 * var(--pg-gutter-x));
    margin-left: calc(-0.5 * var(--pg-gutter-x));
}
.row > * {
    box-sizing: border-box;
    width: 100%;
    max-width: 100%;
    margin-top: var(--pg-gutter-y);
    padding-right: calc(0.5 * var(--pg-gutter-x));
    padding-left: calc(0.5 * var(--pg-gutter-x));
}
.col { flex: 1 0 0%; }
.col-3 { width: 25%; }
.col-6 { width: 50%; }
.col-12 { width: 100%; }
.g-2 { --pg-gutter-x: 0.5rem; --pg-gutter-y: 0.5rem; }
.g-3 { --pg-gutter-x: 1rem; --pg-gutter-y: 1rem; }

/* Layout utilities. */
.d-flex { display: flex; }
.d-block { display: block; }
.d-none { display: none; }
.flex-column { flex-direction: column; }
.flex-shrink-0 { flex-shrink: 0; }
.align-items-center { align-items: center; }
.justify-content-between { justify-content: space-between; }
.align-middle { vertical-align: middle; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 1rem; }
.gap-4 { gap: 1.5rem; }
.w-100 { width: 100%; box-sizing: border-box; }
.img-fluid { max-width: 100%; height: auto; }
.table-responsive { overflow-x: auto; }

/* Spacing utilities, only the steps the markup uses. */
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 1rem; }
.mt-4 { margin-top: 1.5rem; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 1rem; }
.mb-4 { margin-bottom: 1.5rem; }
.my-3 { margin-top: 1rem; margin-bottom: 1rem; }
.ms-2 { margin-left: 0.5rem; }
.p-0 { padding: 0; }
.pt-3 { padding-top: 1rem; }

/* Type utilities. */
.small { font-size: 0.875rem; }
.h4 { font-size: 1.125rem; font-weight: 600; }
.fw-semibold { font-weight: 600; }
.text-center { text-align: center; }
.text-end { text-align: right; }
.text-capitalize { text-transform: capitalize; }
.text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.text-break { overflow-wrap: break-word; word-break: break-word; }
.text-dark { color: var(--wl-fg-1); }

/* Shown from the small breakpoint up, alongside a `d-none` default. Declared
   after `.d-none` so it wins at equal specificity without !important. */
@media (min-width: 576px) {
    .d-sm-inline { display: inline; }
}

/* `.btn` is deliberately absent: the `font` shorthand would reset the size and
   weight the button base above sets, which is what it did to `.btn-sm` (#981). */
button, input, select, textarea { font: inherit; border-radius: var(--wl-radius-sm); }
button, .btn { transition: background-color var(--wl-dur-fast) var(--wl-ease), border-color var(--wl-dur-fast) var(--wl-ease), box-shadow var(--wl-dur-fast) var(--wl-ease), transform var(--wl-dur-fast) var(--wl-ease); }
.btn-primary { color: var(--wl-fg-on-brand); background-color: var(--wl-mid); border-color: var(--wl-mid); }
.btn-primary:hover { background-color: var(--wl-navy); border-color: var(--wl-navy); }
.form-control, .form-select, input, select, textarea { color: var(--wl-fg-1); background-color: var(--wl-bg-elev); border-color: var(--wl-border-strong); }
.form-control:focus, .form-select:focus, input:focus, select:focus, textarea:focus, button:focus-visible, .btn:focus-visible { border-color: var(--wl-border-focus); box-shadow: var(--wl-shadow-focus); outline: none; }
.text-muted { color: var(--wl-fg-3) !important; }
a, .btn-link { color: var(--wl-fg-link); }

/* LEGACY: Threshold hairline treatment for the hand-rolled alert box. Only
   Account/Plan.razor still uses it (WlAlertAdoptionTests pins that); remove with it. */
.alert { border: 0; border-radius: var(--wl-radius-md); }
.alert-danger {
    background: color-mix(in srgb, var(--wl-danger) 8%, transparent);
    color: var(--wl-danger);
    border-top: 1px solid color-mix(in srgb, var(--wl-danger) 35%, transparent);
}
.text-danger { color: var(--wl-danger) !important; }

.btn:active { transform: translateY(1px); }

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--wl-success);
}

.invalid {
    outline: 1px solid var(--wl-danger);
}

.validation-message { color: var(--wl-danger); }

.blazor-error-boundary {
    /* Warning-triangle icon is a Blazor-template data-URI asset, left as shipped; the
       surrounding fill and text colour are re-pointed at the design tokens below so this
       banner stops carrying its own one-off red (#856 sweep, dev-hub DESIGN.md §3/§4). */
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, var(--wl-danger);
    padding: 1rem 1rem 1rem 3.7rem;
    color: var(--wl-white);
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

/* Dashboard UX improvements (#371) */
.avatar {
    width: 36px;
    height: 36px;
    border-radius: var(--wl-radius-md);
    background: var(--wl-ink);
    color: var(--wl-paper);
    font-weight: 600;
    font-size: 0.85rem;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    user-select: none;
    text-transform: uppercase;
}
.avatar--muted { background: var(--wl-fg-4); }

.stat-card {
    display: block;
    text-decoration: none;
    color: inherit;
    border-top: 1px solid var(--wl-border);
    padding-top: 1rem;
    transition: border-color var(--wl-dur-fast) var(--wl-ease);
}
.stat-card:hover {
    border-color: var(--wl-border-strong);
    color: inherit;
}

.bar-chart-cell { min-width: 120px; }
.bar-chart-bar {
    height: 4px;
    border-radius: 2px;
    background: var(--wl-cyan-deep);
    min-width: 4px;
}

.row-inactive { opacity: 0.55; }

.min-width-0 { min-width: 0; }

