html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.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."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.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;
}
/* Tabular figures for numeric table cells, so digits share a width and prices line up down a
   column. Scoped to the cells that carry numbers — prose in a monospace stack reads worse. */
.trading-numeric {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
    text-align: right;
}

/* Sign-in page. Written against the MudBlazor theme variables rather than its components, because
   this page is static SSR by necessity — the auth cookie can't be written from a Blazor circuit. */
.login-shell {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    background: var(--mud-palette-background);
}

.login-card {
    width: 100%;
    max-width: 23rem;
    padding: 2rem;
    border-radius: 10px;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    box-shadow: 0 10px 30px rgb(0 0 0 / 35%);
}

.login-brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1.75rem;
}

.login-mark {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 8px;
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
    font-weight: 700;
    letter-spacing: 0.02em;
}

.login-brand h1 {
    margin: 0;
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

.login-brand p {
    margin: 0.15rem 0 0;
    font-size: 0.8rem;
    color: var(--mud-palette-text-secondary);
}

.login-label {
    display: block;
    margin-bottom: 0.35rem;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--mud-palette-text-secondary);
}

.login-input {
    width: 100%;
    padding: 0.6rem 0.75rem;
    margin-bottom: 1.1rem;
    border-radius: 6px;
    border: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-background);
    color: var(--mud-palette-text-primary);
    font-size: 0.9rem;
    font-family: inherit;
}

.login-input:focus {
    outline: none;
    border-color: var(--mud-palette-primary);
    box-shadow: 0 0 0 3px rgb(100 181 246 / 20%);
}

.login-button {
    width: 100%;
    padding: 0.65rem 1rem;
    border: none;
    border-radius: 6px;
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
    font-size: 0.9rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
}

.login-button:hover { filter: brightness(1.08); }

.login-error {
    margin-bottom: 1.1rem;
    padding: 0.65rem 0.8rem;
    border-radius: 6px;
    font-size: 0.85rem;
    background: rgb(239 83 80 / 14%);
    border: 1px solid rgb(239 83 80 / 40%);
    color: var(--mud-palette-error);
}

/* Top-bar navigation (desktop). The active link needs to be obvious at a glance without shouting,
   so it's an underline plus a lift in contrast rather than a filled button. */
.nav-top-link {
    text-transform: none;
    opacity: 0.82;
    border-radius: 6px;
}

.nav-top-link:hover {
    opacity: 1;
    /* Tinted from the text colour, so it darkens a light bar and lightens a dark one. */
    background: color-mix(in srgb, currentColor 7%, transparent);
}

.nav-top-link-active {
    opacity: 1;
    background: color-mix(in srgb, currentColor 10%, transparent);
    box-shadow: inset 0 -2px 0 0 currentColor;
}

/* Run-summary bar. Explicit gaps rather than utility classes: the groups are label/value pairs with
   no borders of their own, so without real spacing "Candidates 22" and "Run at ..." read as one
   run-on string. Row gap is larger than you'd expect because the bar wraps to several lines on a
   phone and the pairs have to stay visually separate when stacked. */
.summary-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.25rem 2.25rem;
}

.summary-bar .mud-divider-vertical {
    align-self: stretch;
    min-height: 2.25rem;
}

.summary-field {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: max-content;
}

/* Radios sit side by side on a wide screen and stack on a narrow one, each keeping its DTE caption
   directly under its own label rather than colliding with the neighbouring option. */
.profile-choice .mud-radio-group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.5rem;
}

.profile-choice .mud-radio {
    margin-right: 0;
}

@media (max-width: 600px) {
    .summary-bar {
        gap: 1rem 1.5rem;
    }

    .summary-bar .mud-divider-vertical {
        display: none;
    }
}

/* The leg table on an approval card. Every column except the leg name is a price, and one rule
   covers the heading and the cell together — aligning them separately is what put each figure a
   column to the left of its own heading, which read as the last column being empty.

   !important because this stylesheet is linked BEFORE MudBlazor's in App.razor, so on any tie of
   specificity MudBlazor wins on source order. That is also why .trading-numeric, which already sets
   text-align:right, was losing here: a single class cannot out-specify MudBlazor's own
   `.mud-simple-table td`, and loading first means it does not get the tie-break either. */
.approval-legs th:not(:first-child),
.approval-legs td:not(:first-child) {
    text-align: right !important;
}

/* Comfortable rather than cramped: six columns of small numbers is the point of the card, and the
   default cell padding runs them together. */
.approval-legs th,
.approval-legs td {
    padding-left: 12px !important;
    padding-right: 12px !important;
    white-space: nowrap;
}

/* The detail panel behind a ticker: strikes, management rules and research, which used to be three
   columns of their own. Bounded because the research summary is free prose from a model — a few
   sentences usually, but nothing stops it being a paragraph, and an unbounded tooltip would stretch
   to the width of the longest one and cover the table it is explaining. */
.candidate-detail {
    max-width: 320px;
    white-space: normal;
    line-height: 1.35;
}

/* The ticker popup on a card rather than MudBlazor's dark-grey tooltip. The popup carries coloured
   text - the red earnings line, the grey captions - and the theme's colours are chosen to read on the
   page's own surface, not on that grey, where red was close to unreadable. Keyed on the content so the
   one-line tooltips elsewhere keep the usual look. */
.mud-tooltip.mud-tooltip-default:has(.candidate-detail) {
    background-color: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border: 1px solid var(--mud-palette-lines-default);
    box-shadow: var(--mud-elevation-8);
    font-weight: 400;
    padding: 8px 12px;
}

/* The shortlist grid, condensed.
   A table stretched to 100% of a wide screen divides the surplus equally between its columns, so a
   four-character score sat in the middle of a 125px cell and every value was further from its
   neighbour than from its own heading — the eye has to travel between a row's numbers to compare
   them, and that distance was all padding.

   width:auto lets the table shrink-wrap to its contents instead, and the reduced cell padding
   tightens what remains. max-width keeps it from overflowing when the content really is that wide;
   below the responsive breakpoint the table goes back to filling the screen, because by then most
   columns are hidden and the few left should use the space. */
.candidate-grid table {
    width: auto !important;
    max-width: 100%;
}

.candidate-grid td,
.candidate-grid th {
    padding-left: 8px !important;
    padding-right: 8px !important;
}

/* Headings wrap onto two lines rather than widening the column they sit above: "Net Credit" is
   longer than any value beneath it, and without this the widest heading sets the column width. */
.candidate-grid th {
    white-space: normal;
    line-height: 1.2;
}

@media (max-width: 960px) {
    .candidate-grid table {
        width: 100% !important;
    }
}

/* The shortlist's action buttons, a notch under MudBlazor's smallest size (5px padding around a 20px
   glyph), so the Actions column stays narrow. !important because this sheet loads before MudBlazor's,
   which wins a tie on source order. */
.candidate-grid .action-icon {
    padding: 3px !important;
}

.candidate-grid .action-icon .mud-icon-root {
    font-size: 1.05rem !important;
}

/* Responsive table columns.
   960px rather than a phone-width breakpoint: a 13-column grid is already unusable on a tablet in
   portrait, so the cut happens well before the screen is actually small. */
@media (max-width: 960px) {
    .hide-sm {
        display: none !important;
    }
}

/* The inverse — a compact summary that only appears once the columns above have been dropped, so the
   values still reach the reader instead of simply vanishing on a phone. */
.show-sm {
    display: none;
}

@media (max-width: 960px) {
    .show-sm {
        display: block;
    }
}

/* Secondary text and links on the sign-in card. Sized to stay legible on a phone without pushing
   the card taller than the viewport, which is where these pages are most often read. */
.login-note {
    margin: 0 0 1.1rem;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--mud-palette-text-secondary);
}

.login-link {
    display: inline-block;
    margin-top: 1rem;
    font-size: 0.85rem;
    color: var(--mud-palette-primary);
    text-decoration: none;
}

.login-link:hover { text-decoration: underline; }

/* Settings rows. Two columns on a desktop so the control lines up down the page, and stacked on a
   phone — a label and a field side by side at 375px leaves neither of them readable. */
.setting-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.5rem 1.5rem;
    padding: 1rem 0;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.setting-row:last-child { border-bottom: none; }

@media (min-width: 900px) {
    .setting-row {
        grid-template-columns: minmax(0, 3fr) minmax(260px, 2fr);
        align-items: start;
    }
}

.setting-description {
    display: block;
    color: var(--mud-palette-text-secondary);
    line-height: 1.45;
}

.setting-origin {
    display: block;
    margin-top: 0.35rem;
    color: var(--mud-palette-text-disabled);
}

.setting-control {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    align-items: flex-start;
}

/* Sweep indicator in the app bar. Muted by default — it is ambient information, not a call to
   action — and narrow enough that the nav links keep their room. Hidden on a phone, where the bar
   has no width to spare and the drawer already carries the destinations. */
.sweep-indicator {
    font-size: 0.78rem;
    opacity: 0.75;
    white-space: nowrap;
    margin-right: 0.5rem;
}

@media (max-width: 600px) {
    .sweep-indicator { display: none !important; }
}

.sweep-spinning {
    animation: sweep-spin 1.6s linear infinite;
}

@keyframes sweep-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* The Orders page's Outcome column. The broker's rejection reason can run to a paragraph, and
   unwrapped it set the width of the whole table; capped and wrapped, it grows downward instead.
   Only above the phone breakpoint: below it the table stacks into labelled lines, which already wrap. */
@media (min-width: 600px) {
    .outcome-cell {
        max-width: 260px;
        white-space: normal !important;
        overflow-wrap: anywhere;
    }
}

/* Read by a screen reader, not shown: the words behind a meaning carried by colour alone, such as a
   ticker coloured red for earnings before expiry. */
.visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* The ticker detail panel: sections one under another, each a heading and a few lines. */
.ticker-detail-body section {
    margin-bottom: 14px;
}

.ticker-facts {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 2px 12px;
    margin: 4px 0;
    font-size: 0.875rem;
}

.ticker-facts dt {
    color: var(--mud-palette-text-secondary);
}

.ticker-facts dd {
    margin: 0;
}

/* Analysts' grades as one bar: buy, hold, sell in the theme's success, grey and error colours. */
.analyst-bar {
    display: flex;
    height: 8px;
    border-radius: 4px;
    overflow: hidden;
    margin: 6px 0 4px;
    max-width: 320px;
    background-color: var(--mud-palette-lines-default);
}

.analyst-buy { background-color: var(--mud-palette-success); }
.analyst-hold { background-color: var(--mud-palette-text-disabled); }
.analyst-sell { background-color: var(--mud-palette-error); }

.ticker-trigger:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
    border-radius: 4px;
}

/* A row of counts read at a glance, in place of a card each: the number bold, its meaning after it. */
.stat-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 20px;
    font-size: 0.875rem;
    color: var(--mud-palette-text-secondary);
}

.stat-strip b {
    color: var(--mud-palette-text-primary);
    font-size: 1rem;
}
