html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

h1:focus {
    outline: none;
}

a, .btn-link {
    color: #0071c1;
}

.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;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid red;
}

.validation-message {
    color: red;
}

#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.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."
    }

.loading-progress {
    position: absolute;
    display: block;
    width: 8rem;
    height: 8rem;
    inset: 20vh 0 auto 0;
    margin: 0 auto 0 auto;
}

    .loading-progress circle {
        fill: none;
        stroke: #e0e0e0;
        stroke-width: 0.6rem;
        transform-origin: 50% 50%;
        transform: rotate(-90deg);
    }

        .loading-progress circle:last-child {
            stroke: #1b6ec2;
            stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
            transition: stroke-dasharray 0.05s ease-in-out;
        }

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

    .loading-progress-text:after {
        content: var(--blazor-load-percentage-text, "Loading");
    }

code {
    color: #c02d76;
}

.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;
}
/* Radzen's material theme shouts every button label: material-base.css sets
   text-transform: uppercase on .rz-button-xs, -sm, -md and -lg (-sm sits inside a grouped
   selector with the pager and fileupload rules, which is how it got missed first time). The labels are already written as
   sentences in the markup -- "Add a spelling", not "ADD A SPELLING" -- so this only stops the
   theme rewriting them. app.css loads after material-base.css, so matching its specificity is
   enough and no !important is needed. */
.rz-button-xs,
.rz-button-sm,
.rz-button-md,
.rz-button-lg {
    text-transform: none;
}

/* The same shout, on badges. material-base.css uppercases .rz-badge, and the roster screen's
   badges are sentences about a policy -- "2026-02-26 · stated, short paid" -- not labels. */
.rz-badge {
    text-transform: none;
}

/* The sheet (DYNA-51; design-sheets.md §3 and §10). Type and colour follow a spreadsheet's
   defaults where nothing argues otherwise (item 20): a grey header row, light gridlines, dense
   rows, her columns plain and the system's tinted (item 9), and a fill on the Status cell keyed to
   the family of its state (item 33) so colour says "something is open here" and the word says
   what. Selectors go through .rz-grid-table because the theme's own cell rules are that specific:
   a bare .sheet-system td loses to them and paints nothing (DYNA-49 found this the hard way). */
.sheet-toolbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 0.5rem;
}

.sheet-toolbar-note {
    color: #5f6368;
    font-size: 0.9rem;
}

/* What the last saved decision did (DYNA-86 AC 14). This was .sheet-stale, warning that the
   figures on screen were from before the decision; the recalculation now happens inside the
   write, so there is nothing to warn about and AC 15 forbids warning about it. What is left is
   an ordinary confirmation, so it keeps the toolbar's grey rather than the amber a warning
   earned -- it sits beside a row count and is meant to be read as furniture. */
.sheet-decision-notice {
    font-weight: 600;
}

.sheet-layout {
    display: flex;
    align-items: flex-start;
}

.sheet-layout.dragging {
    cursor: col-resize;
    user-select: none;
}

.sheet-grid {
    flex: 1;
    min-width: 16rem;
}

/* The divider (AC 19 to AC 23): the reader's handle on how the screen is shared. Its width is
   the only gap between the sheet and the panel, so the panel's share is measured against the
   whole layout. touch-action: none keeps a finger-drag from scrolling the page instead. */
.sheet-divider {
    flex: 0 0 10px;
    align-self: stretch;
    cursor: col-resize;
    touch-action: none;
    background-color: #f1f3f4;
    border-left: 1px solid #d6d5d5;
    border-right: 1px solid #d6d5d5;
}

.sheet-divider:hover,
.sheet-divider.dragging {
    background-color: #c9d6ea;
}

/* Beside the sheet, not over it (AC 9): the sheet keeps its width, scroll and selection, and
   the panel keeps its own. The share is a percent of the layout (AC 20, AC 21), set by
   js/sheet-split.js on the layout; 40 % until the reader moves it. The floor is AC 22's second
   half -- the percent bounds are the first. */
.sheet-panel {
    flex: 0 0 var(--sheet-panel-share, 40%);
    min-width: 16rem;
    max-height: calc(100vh - 9rem);
    overflow: auto;
    padding-left: 1rem;
    outline: none;
}

.sheet-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.sheet-panel h2 {
    font-size: 1.1rem;
    margin: 0;
}

.sheet-panel h3 {
    font-size: 0.95rem;
    margin: 1rem 0 0.35rem;
}

.sheet-total {
    float: right;
    font-weight: normal;
    color: #5f6368;
}

.sheet-none {
    color: #5f6368;
    font-size: 0.9rem;
    margin: 0;
}

/* DYNA-98: the two toggles that take finished rows out of view. A label around the box and its
   words, so the words are the control's accessible name and clicking them works -- RadzenCheckBox
   renders its own div and passes no aria-label through to it. */
.sheet-toolbar-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.9rem;
    cursor: pointer;
    white-space: nowrap;
}

/* DYNA-103: parking a row approved, under the note it is saved with. A label for the same reason
   as the toolbar toggles above. */
.sheet-park-approved {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-top: 0.35rem;
    font-size: 0.9rem;
    cursor: pointer;
}

/* DYNA-96: the things waiting for an answer on a row, each one said, and read without hovering
   and without opening the row. A list rather than one run-together sentence: several waiting
   things are several questions, and separating them with semicolons left the reader doing the
   separating. They wrap -- a phrase cut short would send her back to the hover this replaced. */
/* The pencil marking a cell that becomes an editor when the row is opened -- To decide and PR
   Notes, which are the only two. Opening a row changes those two cells and leaves every other one
   read-only, and nothing on a closed row said which. Floated, so it costs no height and the rows
   stay as tall as they were. Muted, because it says where to answer and the words beside it are
   what she is reading. */
.sheet-edit-mark {
    float: right;
    margin-left: 0.3rem;
    font-size: 1rem;
    opacity: 0.55;
}

.sheet-decisions {
    margin: 0;
    padding: 0;
    list-style: none;
    line-height: 1.3;
}

.sheet-decisions li + li {
    margin-top: 0.25rem;
}

/* Where the grid would be when there is no roster to lay out (DYNA-55 AC 3). Sized and coloured
   like the panel's "none" sentences, but given the room a whole page's worth of nothing needs.
   It stands in the grid's place inside the layout, so it takes the grid's share of it: the
   evidence panel opens beside this sentence exactly as it opens beside the sheet. */
.sheet-empty {
    flex: 1;
    min-width: 16rem;
    color: #5f6368;
    margin: 2rem 0 0;
}

.rz-grid-table thead th {
    background-color: #f1f3f4;
}

.rz-grid-table thead th.sheet-system,
.rz-grid-table tbody td.sheet-system {
    background-color: #eef3fb;
}

/* The row she marked One Policy is greyed: it is part of the row above. Keyed on its Status cell
   with :has() rather than on a row class from RowRender, because a class set there replaces the
   grid's own and takes the selection marking with it. */
.rz-grid-table tbody tr:has(.sheet-status.status-none) > td {
    color: #80868b;
}

/* Sized from what the cells need, not from what is left over (DYNA-80). An unsized column used to
   be handed the remainder of the container, which is zero once the declared widths exceed it.

   No min-width: 100%. With it the table fills the container and the surplus is spread back across
   the columns, so the container decides the widths again and the cap below stops biting. Grids are
   narrower than their container now, which is the trade for a column being the width of its cell.

   Global rather than scoped to SheetGrid, because the matching steps, the import review and two
   panels build a RadzenDataGrid directly -- scoping this left the matching steps at 0px.

   table-layout is not set here and Radzen's fixed is left alone. Measured on the roster, fixed and
   auto produce the same twenty-six columns to within a pixel once width: max-content is set, so
   overriding it either way would be a change with no effect. */
.rz-grid-table {
    width: max-content;
}

/* One line per heading. Headings used to wrap against a 4rem cap -- the two worked only as a pair,
   because max-content is "how wide with nothing wrapped" and an uncapped heading reports its full
   single-line width whatever the wrap says. Both are gone: that pair existed because a heading
   too long for its column had no other answer, and DYNA-97 put the width, the name and the
   hiding in her hands. A heading she has narrowed past its name ends in an ellipsis. */
.rz-grid-table thead th,
.rz-grid-table thead th .rz-column-title,
.rz-grid-table thead th .rz-column-title-content {
    white-space: nowrap;
}

.rz-grid-table thead th .rz-column-title-content {
    overflow: hidden;
    text-overflow: ellipsis;
}

.sheet-status {
    display: inline-block;
    padding: 0 0.45rem;
    border-radius: 2px;
    white-space: nowrap;
}

/* What each stream was owed, paid and left outstanding (DYNA-68 AC 3, AC 4). Three short lines in
   one cell: the stream's own word, the two figures, and what the difference was where there is
   one. Stacked rather than in six columns, so the commission and the override stay comparable
   down the row without the sheet growing half a screen wider. */
.sheet-money {
    line-height: 1.25;
    padding: 0.15rem 0;
}

.sheet-money-word {
    font-weight: 600;
}

.sheet-money-figures {
    font-size: 0.85em;
    color: #5f6368;
    white-space: nowrap;
}

/* Which period a multi-period row's word and figures are about (DYNA-72). Quieter than the
   figures: it is the scope of what is said rather than part of what is said. */
.sheet-money-scope {
    font-size: 0.8em;
    color: #80868b;
    font-style: italic;
    white-space: nowrap;
}

/* How many of a side's periods are paid in full, beside its word (DYNA-119). The word says what
   is still open; this says how much of the rest did match. */
.sheet-money-count {
    font-size: 0.8em;
    color: #5f6368;
    white-space: nowrap;
}

.sheet-money-difference {
    font-size: 0.85em;
    font-weight: 600;
    color: #a1442a;
    white-space: nowrap;
}

.status-check { background-color: #f1cbf3; }
.status-missing { background-color: #f6c6c1; }
.status-not-paid { background-color: #fbe0b1; }
.status-stated { background-color: #d6e4f7; }
.status-paid { background-color: #cdebd2; }
.status-waiting { background-color: #e8eaed; }
.status-none { background-color: #f1f3f4; color: #70757a; }

/* What makes one deposit up (DYNA-74). A deposit is shown at its full size beside a policy owed
   a fraction of it, which is the right figure and the wrong impression until the rest of the
   deposit is on the page too. These are the rest of it. */
.evidence-parts-head {
    margin: 0.75rem 0 0.25rem;
    font-size: 0.95rem;
    font-weight: 600;
}

.evidence-parts {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.85rem;
}

.evidence-parts td {
    padding: 0.15rem 0.4rem;
    border-bottom: 1px solid #e8eaed;
}

.evidence-parts tfoot td {
    border-top: 1px solid #9aa0a6;
    border-bottom: none;
    font-weight: 600;
}

.evidence-part-amount {
    text-align: right;
    white-space: nowrap;
}

/* The share belonging to the policy whose evidence this is, so a reader finds their own row in a
   list of twelve without counting. */
.evidence-part-mine {
    background-color: #eef3fb;
    font-weight: 600;
}

.evidence-part-tag {
    margin-left: 0.4rem;
    padding: 0 0.3rem;
    border-radius: 2px;
    background-color: #e8eaed;
    color: #5f6368;
    font-size: 0.8em;
    font-weight: 400;
}

.evidence-parts-sole,
.evidence-parts-closed {
    margin: 0.25rem 0 0;
    font-size: 0.85rem;
    color: #1e7a3c;
}

/* Where the lines and the deposit do not meet (AC 3). Stated in the colour the sheet already uses
   for money that is still open, because that is what it is. */
.evidence-parts-open {
    margin: 0.25rem 0 0;
    font-size: 0.85rem;
    font-weight: 600;
    color: #a1442a;
}

/* Arranging a grid's columns (DYNA-97 item 36): one list, every column in it, with the tick that
   shows it, the box that names it and the handles that move it. Sits directly under the grid's own
   bar so it reads as belonging to the grid it arranges, and scrolls rather than growing the page
   -- the roster has twenty-four columns and a list of all of them is taller than the screen. */
.grid-columns {
    border: 1px solid #e0e0e0;
    border-radius: 4px;
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.5rem;
    background: #fafafa;
    max-height: 22rem;
    overflow-y: auto;
}

.grid-columns-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.grid-columns-head h3 {
    margin: 0;
    font-size: 1rem;
}

/* Item 35: what she arranged is still on the screen; this says only that it was not kept. */
.grid-columns-trouble {
    margin: 0.5rem 0 0;
    font-size: 0.85rem;
    color: #a1442a;
}

.grid-columns-list {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
}

.grid-columns-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.15rem 0;
}

.grid-columns-label {
    flex: 1 1 auto;
    min-width: 8rem;
}

/* A column that ships with no heading: the same space a name would take, saying what the column
   holds, so its row reads as a row rather than as a gap. */
.grid-columns-unnamed {
    flex: 1 1 auto;
    min-width: 8rem;
    padding: 0 0.5rem;
    color: #5f6368;
    font-style: italic;
}

.grid-columns-move {
    display: flex;
    gap: 0.25rem;
}

/* Width is dragged on the grid, not set here, and the panel says so rather than leaving a reader
   to wonder where the fourth thing went. */
.grid-columns-note {
    margin: 0.5rem 0 0;
    font-size: 0.8rem;
    color: #5f6368;
}

/* The eight grids that declare themselves reach the panel through this rather than through the
   shared grid's bar (item 25b). */
.grid-arranger {
    margin: 0.35rem 0 0.75rem;
}

/* The row's Status and the sentence under it, at the top of the panel (DYNA-95 AC 23). Set off
   from the evidence below it with a rule rather than a colour: it is the answer the panel was
   opened for, not a caption on the grids. */
.evidence-status {
    margin: 0 0 0.75rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid #e0e0e0;
    font-size: 0.9rem;
}

/* The payments that make up what this policy was paid (AC 6). */
.evidence-period-note {
    margin: 0.25rem 0 0;
    font-size: 0.85rem;
    color: #5f6368;
}

.sheet-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.6rem;
    margin-top: 0.5rem;
    font-size: 0.85rem;
}

.sheet-legend-empty {
    opacity: 0.45;
}

/* DYNA-101: a state with rows is drawn as a button, because clicking it shows those rows. It has
   to draw identically to the span beside it -- a legend where some entries are visibly controls
   and others are not would read as two kinds of thing, when the only difference is whether there
   is anything to show. So the button keeps the pill and gives up only what a button adds. */
button.sheet-status {
    font: inherit;
    line-height: inherit;
    border: none;
    cursor: pointer;
}

/* Which state is showing (AC 11). An outline rather than a colour: the fill is the status
   family's and must keep meaning that. An outline also costs no room -- it is drawn outside the
   box without taking space, so pressing a state does not move anything on the page. */
.sheet-legend-showing {
    outline: 2px solid;
    outline-offset: 1px;
}
