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: block;
    width: 2.75rem;
    height: 2.75rem;
    flex-shrink: 0;
}

/* The page's content, below the top bar: no wider than the widest table needs, centred on a wide
   monitor. The top bar stays full width, so the logo keeps to the left edge and the alerts to the right. */
.page-body {
    max-width: 1440px;
    margin-inline: auto;
}

/* The logo and name in the top bar, a link home. The name gives way to the logo alone on a phone,
   where the bar also holds the menu button and the alerts. */
.brand {
    color: inherit;
    text-decoration: none;
}

.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 popup is hover's one-line preview; a tap or click opens the whole panel. So it never shows
   beside that panel - it would otherwise stay up over it until the pointer moved away - and never on a
   touch screen, where a tap reads as a hover and would open the popup and the panel together. */
body:has(.mud-dialog-container) .mud-tooltip:has(.candidate-detail) {
    display: none !important;
}

@media (hover: none) {
    .mud-tooltip:has(.candidate-detail) {
        display: none !important;
    }
}

/* A candidate's place on its run's shortlist, before the ticker. */
.shortlist-rank {
    display: inline-block;
    min-width: 1.8em;
    margin-right: 4px;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--mud-palette-primary);
    font-variant-numeric: tabular-nums;
    vertical-align: middle;
}

/* The shortlist's detail rows: an approval card under the row it is for, and nothing (not even an
   empty line) under the rest. The rows are striped by index, as the detail rows would stripe them all. */
.candidate-grid tr:has(> td.mud-table-child-content > .no-row-detail) {
    /* Over the phone layout's display: block, which would draw an empty card. */
    display: none !important;
}

/* A table's stripes; on a phone each row is a card already. */
@media (min-width: 600px) {
    .candidate-grid .mud-table-body tr.row-alt > td {
        background-color: var(--mud-palette-table-striped);
    }
}

.candidate-grid .mud-table-child-content .approval-card {
    padding: 4px 0;
}

/* 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: block;
    width: fit-content;
    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;
}

/* A password box with Show / Hide inside it, on the right (js/show-password.js). */
.password-field {
    position: relative;
}

.password-field .login-input {
    padding-right: 4.2rem;
}

.password-toggle {
    position: absolute;
    top: 0.3rem;
    right: 0.35rem;
    padding: 0.35rem 0.55rem;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--mud-palette-primary);
    font-size: 0.78rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
}

.password-toggle:hover {
    background: var(--mud-palette-action-default-hover);
}

.password-toggle:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 1px;
}

/* ---- Tables on a phone -----------------------------------------------------------------------------
   MudBlazor stacks a table below its breakpoint, each cell a flex row of its label and then everything
   in the cell side by side. A cell holding a symbol, a trend, a help icon and a price put them all on
   one line, and the rows ran into one another with a hairline between them. Here each row is a card,
   and a cell's contents stack on the right under one another. The same rules for both breakpoints the
   app's tables use: xs for the grids, sm for most tables. .cell-inline keeps a cell's contents on one
   line, for a row of icon buttons.
   Every selector starts .mud-table.mud-xs-table / .mud-sm-table, one class more than MudBlazor's own:
   its stylesheet loads after this one, and at equal weight its table-row-group on the body won, so the
   cards shrank to their contents. */
@media (max-width: 959.98px) {
    .mud-table.mud-sm-table.mud-table-outlined { border: none; }
    .mud-table.mud-sm-table,
    .mud-table.mud-sm-table .mud-table-container { background: transparent; }
    .mud-table.mud-sm-table .mud-table-root,
    .mud-table.mud-sm-table .mud-table-body { display: block; border-top: none; width: 100%; box-sizing: border-box; }

    .mud-table.mud-sm-table .mud-table-body > .mud-table-row {
        display: block;
        /* Said outright: some phone browsers size a table's parts to their contents once they are not a table. */
        width: 100%;
        box-sizing: border-box;
        margin: 0 0 10px;
        border: 1px solid var(--mud-palette-lines-default);
        border-radius: 8px;
        /* Over the table's own striping and hover, which colour the row: the cards already part the lines. */
        background: var(--mud-palette-surface) !important;
        overflow: hidden;
    }

    .mud-table.mud-sm-table .mud-table-body > .mud-table-row > .mud-table-cell:last-child { border-bottom: none; }

    .mud-table.mud-sm-table .mud-table-body .mud-table-cell:not(.cell-inline) {
        display: grid;
        grid-template-columns: max-content minmax(0, 1fr);
        column-gap: 12px;
        justify-items: end;
        align-items: center;
        text-align: end;
    }

    .mud-table.mud-sm-table .mud-table-body .mud-table-cell:not(.cell-inline)::before {
        grid-column: 1;
        grid-row: 1 / span 20;
        align-self: start;
        justify-self: start;
        text-align: start;
    }

    .mud-table.mud-sm-table .mud-table-body .mud-table-cell:not(.cell-inline) > * {
        grid-column: 2;
        min-width: 0;
        max-width: 100%;
        overflow-wrap: anywhere;
    }
}

@media (max-width: 599.98px) {
    .mud-table.mud-xs-table.mud-table-outlined { border: none; }
    .mud-table.mud-xs-table,
    .mud-table.mud-xs-table .mud-table-container { background: transparent; }
    .mud-table.mud-xs-table .mud-table-root,
    .mud-table.mud-xs-table .mud-table-body { display: block; border-top: none; width: 100%; box-sizing: border-box; }

    .mud-table.mud-xs-table .mud-table-body > .mud-table-row {
        display: block;
        /* Said outright: some phone browsers size a table's parts to their contents once they are not a table. */
        width: 100%;
        box-sizing: border-box;
        margin: 0 0 10px;
        border: 1px solid var(--mud-palette-lines-default);
        border-radius: 8px;
        /* Over the table's own striping and hover, which colour the row: the cards already part the lines. */
        background: var(--mud-palette-surface) !important;
        overflow: hidden;
    }

    .mud-table.mud-xs-table .mud-table-body > .mud-table-row > .mud-table-cell:last-child { border-bottom: none; }

    .mud-table.mud-xs-table .mud-table-body .mud-table-cell:not(.cell-inline) {
        display: grid;
        grid-template-columns: max-content minmax(0, 1fr);
        column-gap: 12px;
        justify-items: end;
        align-items: center;
        text-align: end;
    }

    .mud-table.mud-xs-table .mud-table-body .mud-table-cell:not(.cell-inline)::before {
        grid-column: 1;
        grid-row: 1 / span 20;
        align-self: start;
        justify-self: start;
        text-align: start;
    }

    .mud-table.mud-xs-table .mud-table-body .mud-table-cell:not(.cell-inline) > * {
        grid-column: 2;
        min-width: 0;
        max-width: 100%;
        overflow-wrap: anywhere;
    }
}

/* ---- About page: label and value side by side, the value under its label on a phone -------------- */
.about-grid {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    column-gap: 24px;
    row-gap: 12px;
    align-items: baseline;
}

.about-label {
    color: var(--mud-palette-text-secondary);
    font-weight: 500;
}

@media (max-width: 599.98px) {
    .about-grid { grid-template-columns: minmax(0, 1fr); row-gap: 2px; }
    .about-grid .about-label { margin-top: 10px; }
}

/* ---- The shortlist on a phone ---------------------------------------------------------------------
   Below 960px the shortlist hides its secondary columns and folds them into one unlabelled line under
   the symbol: the right trade-off while it is still a table on a tablet. On a phone each row is a
   card with room for every field, so there they come back as labelled lines, and the folded line goes. */
@media (max-width: 599.98px) {
    .mud-table.mud-xs-table.candidate-grid .mud-table-body .mud-table-cell.hide-sm { display: grid !important; }
    .mud-table.mud-xs-table.candidate-grid .show-sm { display: none !important; }
}

/* ...but not all at once: a card shows the ticker, score, credit and max loss, with the action buttons
   always there at its foot (phone-last), and a tap opens the rest (phone-more). The ticker and the buttons act on their own and leave the card
   as it is. Detail rows (an approval card) are not cards to open. */
@media (max-width: 599.98px) {
    .mud-table.mud-xs-table.candidate-grid .mud-table-body > tr.mud-table-row:not(:has(> .mud-table-child-content)) {
        display: flex;
        flex-direction: column;
        cursor: pointer;
    }

    .mud-table.mud-xs-table.candidate-grid .mud-table-body > tr.mud-table-row:not(.row-opened) > td.phone-more {
        display: none !important;
    }

    .mud-table.mud-xs-table.candidate-grid .mud-table-body > tr.mud-table-row > td.phone-last { order: 1; }

    .mud-table.mud-xs-table.candidate-grid .mud-table-body > tr.mud-table-row:not(:has(> .mud-table-child-content))::after {
        content: "More ▾";
        order: 2;
        padding: 4px 16px 8px;
        text-align: center;
        font-size: 0.8rem;
        color: var(--mud-palette-primary);
    }

    .mud-table.mud-xs-table.candidate-grid .mud-table-body > tr.mud-table-row.row-opened::after {
        content: "Less ▴";
    }
}

/* Scenario results: a window over the page on a desktop, the whole screen on a phone, where a window
   inside the page leaves no room for a table. */
.scenario-results-content {
    max-height: calc(100vh - 220px);
    overflow-y: auto;
}

@media (max-width: 600px) {
    .scenario-results-dialog {
        margin: 0 !important;
        width: 100vw !important;
        max-width: 100vw !important;
        height: 100dvh;
        max-height: 100dvh !important;
        border-radius: 0 !important;
    }

    .scenario-results-content {
        max-height: none;
        flex: 1 1 auto;
    }
}

.scenario-steps {
    margin: 0;
    padding-left: 1.25rem;
}

.scenario-steps li {
    margin-bottom: 4px;
}

/* The Schedules menu entry while a job runs: its icon turns, as the sweep indicator's does. */
.schedules-running .mud-icon-root {
    animation: sweep-spin 1.6s linear infinite;
}

/* Refresh prices while a pricing pass runs: its icon turns. */
.refreshing .mud-icon-root {
    animation: sweep-spin 1.6s linear infinite;
}

/* The Today page's countdowns: amber in the last quarter hour, grey once out. */
.countdown-soon {
    color: var(--mud-palette-warning);
    font-weight: 600;
}

.countdown-done {
    color: var(--mud-palette-text-secondary);
}

/* The ticker panel's headlines: a plain list, a little space between. */
.why-moving .headlines {
    margin: 4px 0 6px;
    padding-left: 1.1rem;
}

.why-moving .headlines li {
    margin-bottom: 6px;
}

/* Gemini's key drivers, as Google lists its own: bulleted, each label bold. */
.why-moving .key-drivers {
    margin: 2px 0 4px;
    padding-left: 1.1rem;
}

.why-moving .key-drivers li {
    margin-bottom: 4px;
}

/* A ticker's company name under it: small, grey, and cut short rather than widening its column. */
.company-name {
    font-size: 0.75rem;
    line-height: 1.2;
    max-width: 16rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The button beside a ticker: small enough to sit on the symbol's line without making the row taller. */
.ticker .ticker-open {
    padding: 2px;
}

.ticker .ticker-open .mud-icon-root {
    font-size: 1rem;
}

/* Level 2: a compact ladder, bids green and asks red, the best row bold; numbers in columns that line up. */
.level2-ladder td, .level2-ladder th {
    padding: 2px 6px !important;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.level2-ladder .level2-bid {
    color: var(--mud-palette-success);
}

.level2-ladder .level2-ask {
    color: var(--mud-palette-error);
    text-align: left;
}

.level2-ladder td.level2-size:last-child {
    text-align: left;
}

.level2-ladder tr.level2-best td {
    font-weight: 700;
}

/* The trades under a book: green where a buyer took the offer, red where a seller hit the bid. */
.level2-prints tr.level2-print-ask td {
    color: var(--mud-palette-success);
}

.level2-prints tr.level2-print-bid td {
    color: var(--mud-palette-error);
}

/* Scenario results, Strategies: the winner's row and each column's best stand out; a combination reads apart. */
.strategy-standings tr.strategy-winner-row td {
    background: color-mix(in srgb, var(--mud-palette-warning) 10%, transparent);
}

.strategy-grid td.strategy-best-cell {
    font-weight: 700;
    background: color-mix(in srgb, var(--mud-palette-warning) 10%, transparent);
}

.strategy-grid tr.strategy-combination-row td {
    font-style: italic;
}

/* The shortlist's filters: a line each, labelled down the left; on a phone the label sits above its line. */
.candidate-filters .candidate-filter-row {
    display: grid;
    grid-template-columns: 6.5rem 1fr;
    align-items: center;
    column-gap: 0.75rem;
    padding: 4px 0;
}

.candidate-filters .candidate-filter-row + .candidate-filter-row {
    border-top: 1px solid var(--mud-palette-lines-default);
}

.candidate-filters .candidate-filter-label {
    color: var(--mud-palette-text-secondary);
    font-weight: 600;
}

@media (max-width: 600px) {
    .candidate-filters .candidate-filter-row {
        grid-template-columns: 1fr;
        row-gap: 2px;
    }
}

/* The Show line: the amount at its own width and the credit/debit chips right beside it, not pushed across. */
.candidate-filters .candidate-filter-show {
    justify-content: flex-start;
}

.candidate-filters .candidate-filter-amount {
    flex: 0 0 8rem;
    width: 8rem;
}

.candidate-filters .candidate-filter-amount .mud-input-control {
    flex: 0 0 auto;
}

.candidate-filters .credit-filter {
    flex: 0 0 auto;
    width: auto;
    margin: 0;
}

/* The market averages above every page: a dark band in either theme, like a ticker tape, set apart from the page
   below it; its own green and red, bright enough on the dark. One line, wrapping on a phone. */
.market-strip {
    /* Held just under the app bar as the page scrolls, so the averages stay in sight; above the page's content,
       under the app bar's menus and the dialogs. */
    position: sticky;
    top: calc(var(--mud-appbar-height, 64px) + 4px);
    z-index: 5;
    gap: 4px 16px;
    margin: -8px 0 12px;
    padding: 5px 12px;
    border-radius: 6px;
    font-size: 0.82rem;
    background: #0b1424;
    color: #9fb0c7;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 2px 6px rgba(0, 0, 0, 0.25);
}

.market-strip-item {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    white-space: nowrap;
}

.market-strip-name {
    font-weight: 600;
    color: #e8eef6;
}

.market-strip-last {
    font-variant-numeric: tabular-nums;
    color: #e8eef6;
}

.market-strip-change {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.market-strip-change.up {
    color: #3fd27f;
}

.market-strip-change.down {
    color: #ff6b6b;
}

.market-strip-source {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border: 1px solid rgba(159, 176, 199, 0.45);
    border-radius: 4px;
    padding: 0 4px;
}

/* The market's mood and stress at the strip's end (MarketStripBar): fear red through grey to greed green; stress
   green, amber, red. Pushed to the right on a wide screen, onto its own line on a phone. */
.market-mood {
    display: inline-flex;
    gap: 5px;
    white-space: nowrap;
}

.mood-chip,
.stress-chip {
    border-radius: 4px;
    padding: 0 5px;
    font-size: 0.76rem;
    font-weight: 600;
    cursor: help;
    color: #0b1424;
}

.mood-extreme-fear { background: #ff6b6b; }
.mood-fear { background: #ffa36b; }
.mood-neutral { background: #9fb0c7; }
.mood-greed { background: #8fdcaa; }
.mood-extreme-greed { background: #3fd27f; }
.stress-calm { background: #3fd27f; }
.stress-elevated { background: #ffc94d; }
.stress-high { background: #ff6b6b; }

/* Today → Earnings: the week's reports by expected move (EarningsWeekChart). A chart on a wide screen, lists on a phone. */
.ewc {
    --ewc-pre: var(--mud-palette-primary);
    --ewc-post: var(--mud-palette-text-primary);
    --ewc-unknown: var(--mud-palette-text-secondary);
    --ewc-grid: var(--mud-palette-lines-default);
    --ewc-panel: var(--mud-palette-surface);
    --ewc-short: var(--mud-palette-success);
    --ewc-held: var(--mud-palette-warning);
    border: 1px solid var(--ewc-grid);
    border-radius: 10px;
    padding: 12px 16px;
    margin-bottom: 16px;
    min-width: 0;
}

.ewc-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 16px; }
.ewc-legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 13px; color: var(--mud-palette-text-secondary); align-items: center; }
.ewc-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px; vertical-align: -1px; margin-right: 5px; }
.ewc-swatch.pre { background: var(--ewc-pre); }
.ewc-swatch.post { background: var(--ewc-post); }
.ewc-swatch.outline { border: 2px solid var(--ewc-grid); }
.ewc-swatch.outline.short { border-color: var(--ewc-short); }
.ewc-swatch.outline.held { border-color: var(--ewc-held); border-style: dashed; }

.ewc-scroll { overflow-x: auto; margin-top: 12px; }
.ewc-chart { position: relative; font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; }
.ewc-grid { position: absolute; right: 0; border-top: 1px solid var(--ewc-grid); }
.ewc-tick { position: absolute; left: 0; width: 40px; text-align: right; transform: translateY(-50%); font-size: 12px; color: var(--mud-palette-text-secondary); }
.ewc-day { position: absolute; top: 0; bottom: 0; border-left: 1px dashed var(--ewc-grid); }
.ewc-day.past { background: color-mix(in srgb, var(--ewc-grid) 25%, transparent); }
.ewc-dayname { position: absolute; top: -2px; left: 0; right: 0; text-align: center; font-family: var(--mud-typography-default-family); font-weight: 600; font-size: 13px; letter-spacing: .06em; text-transform: uppercase; }
.ewc-dayname small { display: block; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--mud-palette-text-secondary); }
.ewc-session { position: absolute; bottom: 8px; font-family: var(--mud-typography-default-family); font-size: 11px; padding: 3px 8px; border-radius: 999px; transform: translateX(-50%); white-space: nowrap; }
.ewc-session.pre { background: var(--ewc-pre); color: var(--mud-palette-primary-text); }
.ewc-session.post { background: var(--ewc-post); color: var(--ewc-panel); }
.ewc-session.unknown { background: var(--ewc-unknown); color: var(--ewc-panel); }
.ewc-session.none { color: var(--mud-palette-text-secondary); border: 1px dashed var(--ewc-grid); }

.ewc-tile { position: absolute; width: 58px; transform: translateY(-14px); border-radius: 7px; border: 1.5px solid var(--ewc-grid); background: var(--ewc-panel); text-align: center; box-shadow: 0 1px 2px rgb(0 0 0 / .08); cursor: default; }
.ewc-tile .ewc-pct { display: block; font-size: 11.5px; font-weight: 600; border-radius: 5px 5px 0 0; padding: 1px 0; }
.ewc-tile.pre .ewc-pct { background: var(--ewc-pre); color: var(--mud-palette-primary-text); }
.ewc-tile.post .ewc-pct { background: var(--ewc-post); color: var(--ewc-panel); }
.ewc-tile.unknown .ewc-pct { background: var(--ewc-unknown); color: var(--ewc-panel); }
.ewc-tile .ewc-sym { display: block; font-size: 13px; font-weight: 600; padding: 3px 0 1px; }
.ewc-tile .ewc-past { display: block; font-size: 10px; color: var(--mud-palette-text-secondary); padding-bottom: 3px; }
.ewc-tile.short { border-color: var(--ewc-short); border-width: 2px; }
.ewc-tile.held { border-color: var(--ewc-held); border-style: dashed; border-width: 2px; }
.ewc-tile:hover, .ewc-tile:focus { z-index: 5; outline: 2px solid var(--ewc-pre); }
.ewc-card { display: none; position: absolute; z-index: 10; top: -4px; left: 64px; width: 240px; text-align: left; background: var(--ewc-panel); border: 1px solid var(--ewc-grid); border-radius: 8px; padding: 10px 12px; font-family: var(--mud-typography-default-family); font-size: 12.5px; box-shadow: 0 6px 18px rgb(0 0 0 / .18); pointer-events: none; }
.ewc-card.left { left: auto; right: 64px; }
.ewc-tile:hover .ewc-card, .ewc-tile:focus .ewc-card { display: block; }
.ewc-card b { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; }
.ewc-row { display: flex; justify-content: space-between; gap: 10px; color: var(--mud-palette-text-secondary); }
.ewc-row span:last-child { color: var(--mud-palette-text-primary); font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; }
.ewc-note { margin-top: 4px; font-weight: 600; }
.ewc-note.short { color: var(--ewc-short); }
.ewc-note.held { color: var(--ewc-held); }

/* Phones: each day a list, biggest move first, the move as a bar with the stock's usual move marked on it. */
.ewc-phone { display: none; margin-top: 10px; }
@media (max-width: 720px) {
    .ewc { padding: 10px 12px; }
    .ewc-scroll { display: none; }
    .ewc-phone { display: grid; gap: 14px; }
}
.ewc-pday h3 { font-size: 13px; letter-spacing: .06em; text-transform: uppercase; margin: 0 0 6px; display: flex; justify-content: space-between; gap: 8px; }
.ewc-pday h3 small { font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--mud-palette-text-secondary); }
.ewc-prow { display: grid; grid-template-columns: 64px 1fr 58px; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 7px; border: 1.5px solid transparent; }
.ewc-prow + .ewc-prow { margin-top: 2px; }
.ewc-prow.short { border-color: var(--ewc-short); }
.ewc-prow.held { border-color: var(--ewc-held); border-style: dashed; }
.ewc-who { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-weight: 600; font-size: 13px; display: flex; align-items: center; gap: 6px; }
.ewc-dot { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.ewc-dot.pre, .ewc-fill.pre { background: var(--ewc-pre); }
.ewc-dot.post, .ewc-fill.post { background: var(--ewc-post); }
.ewc-dot.unknown, .ewc-fill.unknown { background: var(--ewc-unknown); }
.ewc-bar { position: relative; height: 10px; background: var(--ewc-grid); border-radius: 5px; }
.ewc-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 5px; }
.ewc-avg { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--mud-palette-text-primary); }
.ewc-num { text-align: right; font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 13px; font-weight: 600; }
.ewc-num small { display: block; font-size: 10.5px; font-weight: 500; color: var(--mud-palette-text-secondary); }

/* A working order's runner, running in place while the order is out at the broker trying to fill. */
.order-working .mud-chip-icon {
    animation: order-run 0.5s ease-in-out infinite alternate;
}

@keyframes order-run {
    from { transform: translateX(-1px) rotate(-4deg); }
    to { transform: translateX(2px) rotate(4deg); }
}

@media (prefers-reduced-motion: reduce) {
    .order-working .mud-chip-icon { animation: none; }
}
