/* ---- Brand fonts (F-125, BR8): files of the app, with their OFL licences in fonts/. Never a third-party host. ---- */
@font-face {
    font-family: "Bricolage Grotesque";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("fonts/bricolage-grotesque-latin-500-normal.woff2") format("woff2");
}

@font-face {
    font-family: "Bricolage Grotesque";
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url("fonts/bricolage-grotesque-latin-800-normal.woff2") format("woff2");
}

@font-face {
    font-family: "JetBrains Mono";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("fonts/jetbrains-mono-latin-500-normal.woff2") format("woff2");
}

@font-face {
    font-family: "JetBrains Mono";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("fonts/jetbrains-mono-latin-700-normal.woff2") format("woff2");
}

h1:focus {
    outline: none;
}

/* F-83: the template's validation and error colours read the theme. */
.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--mud-palette-success);
}

.invalid {
    outline: 1px solid var(--mud-palette-error);
}

.validation-message {
    color: var(--mud-palette-error);
}

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

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

.darker-border-checkbox.form-check-input {
    border-color: var(--mud-palette-lines-inputs);
}

.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;
}
/* ---- Visual language (F-43, BR5) ----
   The three scales every .app-* rule reads instead of a length of its own. A literal that one of these covers
   fails AppCssScaleTests. The steps are the values the kit already used, rounded onto one ladder; a rule that
   genuinely needs something else says so in a comment on the same line.
   Type: 0.75 / 0.8125 / 0.875 / 0.9375 (body) / 1 / 1.375rem.
   Space: 0.25 / 0.5 / 0.75 / 1 / 1.5 / 2 / 2.5rem.
   Radius (F-125, BR7): 6 tags and chips / 12 buttons and fields (the theme's default) / 20px cards and dialogs, and a pill.
   Mono (F-125): JetBrains Mono for code, grids of numbers and keyboard shortcuts.
   Keycap (F-125): the edge under a filled button is 4px deep; pressed, the key moves 2px down and the edge shrinks. */
:root {
    --app-font-mono: "JetBrains Mono", ui-monospace, Consolas, monospace;
    --app-keycap-depth: 4px;
    --app-keycap-press: 2px;
    --app-font-xs: 0.75rem;
    --app-font-sm: 0.8125rem;
    --app-font-md: 0.875rem;
    --app-font-base: 0.9375rem;
    --app-font-lg: 1rem;
    --app-font-xl: 1.375rem;
    --app-space-1: 0.25rem;
    --app-space-2: 0.5rem;
    --app-space-3: 0.75rem;
    --app-space-4: 1rem;
    --app-space-5: 1.5rem;
    --app-space-6: 2rem;
    --app-space-7: 2.5rem;
    --app-radius-sm: 6px;
    --app-radius-md: 12px;
    --app-radius-lg: 20px;
    --app-radius-pill: 999px;
}

/* ---- UI kit (F-1). Interaction states live here and in SimulabTheme, never on a page. ---- */

a, button, [role="button"], [role="menuitem"], .mud-button-root, .mud-icon-button, .mud-radio, .mud-checkbox, .mud-link, .mud-nav-link, .mud-table-sort-label {
    cursor: pointer;
}

/* F-125, palette 16: the focus ring is the info blue, 3px, 2px away. Inside the navy sidebar it takes the drawer's text
   colour, since the blue reads at 2.6:1 on the navy. */
:is(a, button, input, select, textarea, [tabindex], .mud-button-root):focus-visible {
    outline: 3px solid var(--mud-palette-info);
    outline-offset: 2px;
}

.app-drawer :focus-visible {
    outline-color: var(--mud-palette-drawer-text);
}

.mud-button-root, .mud-icon-button {
    min-width: 24px;
    min-height: 24px;
}

code, kbd, pre, samp {
    font-family: var(--app-font-mono);
}

/* ---- Buttons (F-125, BR4): the four variants of the brand, styled on the classes MudBlazor renders, so a page keeps
   its Variant and Color and changes nothing. Written from MudBlazor 9.9.0's own CSS (rule: ui-project). ---- */
.mud-button-root.mud-button {
    min-height: 44px;
    border-radius: var(--app-radius-md);
    transition: background-color 150ms ease, transform 80ms ease, box-shadow 80ms ease;
}

.mud-button-root.mud-button-filled-size-small,
.mud-button-root.mud-button-outlined-size-small,
.mud-button-root.mud-button-text-size-small {
    min-height: 36px;
}

/* Primary, action and destructive are keycaps: the fill, its contrast text and the edge under it. */
.mud-button-filled.mud-button-filled-primary {
    background-color: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
    box-shadow: 0 var(--app-keycap-depth) 0 var(--app-keycap-primary-edge);
}

.mud-button-filled.mud-button-filled-tertiary {
    background-color: var(--mud-palette-tertiary);
    color: var(--mud-palette-tertiary-text);
    box-shadow: 0 var(--app-keycap-depth) 0 var(--app-keycap-tertiary-edge);
}

.mud-button-filled.mud-button-filled-error {
    background-color: var(--mud-palette-error);
    color: var(--mud-palette-error-text);
    box-shadow: 0 var(--app-keycap-depth) 0 var(--app-keycap-error-edge);
}

.mud-button-filled.mud-button-filled-secondary,
.mud-button-filled.mud-button-filled-success {
    box-shadow: 0 var(--app-keycap-depth) 0 var(--app-keycap-secondary-edge);
}

/* Pressed, the key moves down while its edge shrinks by as much. */
.mud-button-filled:active:not(:disabled) {
    transform: translateY(var(--app-keycap-press));
    box-shadow: 0 var(--app-keycap-press) 0 var(--app-keycap-primary-edge);
}

.mud-button-filled.mud-button-filled-tertiary:active:not(:disabled) {
    box-shadow: 0 var(--app-keycap-press) 0 var(--app-keycap-tertiary-edge);
}

.mud-button-filled.mud-button-filled-error:active:not(:disabled) {
    box-shadow: 0 var(--app-keycap-press) 0 var(--app-keycap-error-edge);
}

/* Disabled: the sunken fill, ink-muted text, no edge; the button keeps its real disabled attribute. */
.mud-button-filled:disabled {
    background-color: var(--mud-palette-background-gray) !important;
    color: var(--mud-palette-text-secondary);
    box-shadow: none;
}

/* Secondary: the surface inside a 2px line-strong border, ink text. */
.mud-button-root.mud-button-outlined {
    background-color: var(--mud-palette-surface);
    border: 2px solid var(--mud-palette-lines-inputs);
    color: var(--mud-palette-text-primary);
}

/* A destructive outlined button keeps the error colour for its text and its border. */
.mud-button-outlined.mud-button-outlined-error {
    border-color: var(--mud-palette-error);
    color: var(--mud-palette-error);
}

.mud-button-outlined:disabled {
    border-color: var(--mud-palette-background-gray);
    color: var(--mud-palette-text-secondary);
}

/* Text: a link — no fill, the link role (F-148, BR6), underlined. A destructive text button keeps the error colour. */
.mud-button-root.mud-button-text {
    background-color: transparent;
    color: var(--app-link);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.mud-button-text.mud-button-text-error {
    color: var(--mud-palette-error);
}

.mud-button-text:disabled {
    color: var(--mud-palette-text-secondary);
}

@media (prefers-reduced-motion: reduce) {
    .mud-button-root.mud-button {
        transition: none;
    }
}

/* ---- Action shortcut (F-148, BR8): Ctrl + Alt + the amber key, or the amber key alone (AppActionButton). One element is
   clickable; Ctrl, Alt and the plus signs are decorative. It sits on the page, the surface or white, never on the sunken
   fill or the tint. ---- */
.app-action {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-2);
}

.app-action-modkey {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--app-space-4);
    border-radius: var(--app-radius-md);
    font-size: var(--app-font-md);
    font-weight: 800;
    user-select: none;
    transition: transform 80ms ease, box-shadow 80ms ease;
}

.app-action-ctrl {
    background-color: var(--mud-palette-secondary);
    color: var(--mud-palette-secondary-text);
    box-shadow: 0 var(--app-keycap-depth) 0 var(--app-keycap-secondary-edge);
}

.app-action-alt {
    background-color: var(--mud-palette-info);
    color: var(--mud-palette-info-text);
    box-shadow: 0 var(--app-keycap-depth) 0 var(--app-keycap-info-edge);
}

.app-action-plus {
    color: var(--app-brand-name);
    font-weight: 800;
}

.app-action-key {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-2);
    min-height: 44px;
    padding: 0 var(--app-space-5);
    border: 0;
    border-radius: var(--app-radius-md);
    background-color: var(--mud-palette-tertiary);
    color: var(--mud-palette-tertiary-text);
    box-shadow: 0 var(--app-keycap-depth) 0 var(--app-keycap-tertiary-edge);
    font: inherit;
    font-weight: 800;
    transition: background-color 150ms ease, transform 80ms ease, box-shadow 80ms ease;
}

.app-action-key:hover:not(:disabled) {
    background-color: var(--mud-palette-tertiary-darken);
}

/* Pressed, the amber key moves down while its edge shrinks; in the shortcut Ctrl and Alt sink too. */
.app-action-key:active:not(:disabled) {
    transform: translateY(var(--app-keycap-press));
    box-shadow: 0 var(--app-keycap-press) 0 var(--app-keycap-tertiary-edge);
}

.app-action:has(.app-action-key:active:not(:disabled)) .app-action-ctrl {
    transform: translateY(var(--app-keycap-press));
    box-shadow: 0 var(--app-keycap-press) 0 var(--app-keycap-secondary-edge);
}

.app-action:has(.app-action-key:active:not(:disabled)) .app-action-alt {
    transform: translateY(var(--app-keycap-press));
    box-shadow: 0 var(--app-keycap-press) 0 var(--app-keycap-info-edge);
}

.app-action-icon {
    display: inline-flex;
    transition: transform 0.4s ease;
}

.app-action-key:active:not(:disabled) .app-action-icon {
    transform: rotate(200deg);
}

/* F-126: a key whose icon rests turned (AppActionButton.IconRestRotation, passed as --app-action-rest) turns half a
   circle more while it is pressed, instead of the 200-degree spin. */
.app-action-icon-rested {
    transform: rotate(var(--app-action-rest));
}

.app-action-key:active:not(:disabled) .app-action-icon-rested {
    transform: rotate(calc(var(--app-action-rest) + 180deg));
}

/* Disabled: the sunken fill, ink-muted text, no edge; Ctrl and Alt of a disabled shortcut sink to the same fill. */
.app-action-key:disabled,
.app-action-disabled .app-action-ctrl,
.app-action-disabled .app-action-alt {
    background-color: var(--mud-palette-background-gray);
    color: var(--mud-palette-text-secondary);
    box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
    .app-action-key,
    .app-action-modkey {
        transition: none;
    }

    .app-action-icon {
        transition: none;
    }
}

/* ---- Surfaces (F-125, BR6 and BR7): no diffuse shadow (the theme draws none), a 1px line around each surface,
   20px corners on cards and dialogs. Menus and lists keep the field radius. ---- */
.mud-paper:not(.mud-paper-square):not(.mud-popover) {
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-lg);
}

.mud-dialog {
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-lg);
}

.mud-popover {
    border: 1px solid var(--mud-palette-divider);
}

.mud-appbar {
    border-bottom: 1px solid var(--mud-palette-divider);
}

.app-drawer.mud-drawer {
    border-right: 1px solid var(--mud-palette-divider);
}

.mud-chip.mud-chip-size-small,
.mud-chip.mud-chip-size-medium,
.mud-chip.mud-chip-size-large {
    border-radius: var(--app-radius-sm);
}

.app-page-header {
    margin-bottom: var(--app-space-5);
}

.app-breadcrumbs {
    padding: 0 0 var(--app-space-1) 0;
}

.app-page-header-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--app-space-4);
}

.app-page-title {
    flex: 1 1 auto;
}

.app-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--app-space-3);
    padding: var(--app-space-7) var(--app-space-4);
    text-align: center;
}

/* ---- Page state (F-134, BR6): the error and not-found pages. Icon, title, message, actions; colours from the theme only. ---- */
.app-page-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--app-space-3);
    max-width: 36rem;
    margin: 0 auto;
    padding: var(--app-space-7) var(--app-space-4);
    text-align: center;
}

.app-page-state-title {
    margin: 0;
    font-size: var(--app-font-xl);
    font-weight: 800;
    color: var(--app-brand-name);
}

.app-page-state-message {
    margin: 0;
    max-width: 100%;
    overflow-wrap: anywhere;
    color: var(--mud-palette-text-secondary);
}

.app-page-state-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: var(--app-space-3);
    margin-top: var(--app-space-3);
}

.app-page-state-request-id {
    margin: var(--app-space-3) 0 0 0;
    max-width: 100%;
    overflow-wrap: anywhere;
    font-family: var(--app-font-mono);
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
}

/* ---- Home (F-134, BR3): one card per menu section, in a grid that falls to one column on a phone. ---- */
.app-home-sections {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
    gap: var(--app-space-4);
    margin-top: var(--app-space-5);
}

.app-home-sections > .app-section-card {
    margin-top: 0;
}

.app-home-links {
    display: flex;
    flex-direction: column;
    gap: var(--app-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-home-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-3);
    margin-top: var(--app-space-4);
}

.app-home-link {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-2);
    min-height: 24px;
}

/* ---- AI diagnostics (F-134, BR7): the answer's figures in a grid, the numbers in the mono font. ---- */
.ai-metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
    gap: var(--app-space-4);
    margin: var(--app-space-4) 0 0 0;
}

.ai-metric-label {
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.ai-metric-value {
    margin: 0;
    font-family: var(--app-font-mono);
    font-size: var(--app-font-md);
    overflow-wrap: anywhere;
}

.app-form-actions {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: var(--app-space-2);
    margin-top: var(--app-space-5);
}

.app-row-actions {
    display: inline-flex;
    align-items: center;
    /* Tighter than space-1: icon buttons already carry their own 24px target (rule: ui). */
    gap: 0.125rem;
}

.app-data-table .app-cell-actions {
    text-align: right;
    white-space: nowrap;
    width: 1%;
}

.app-data-table .app-cell-number {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.app-data-table .app-cell-number .column-header {
    justify-content: flex-end;
}

.app-data-table .app-cell-date {
    white-space: nowrap;
}

/* F-44: the line under a cell's main text (the scope's state or city). Without a style it ran into the
   text before it: "MunicipalSalvador". */
.app-data-table .app-cell-secondary {
    display: block;
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-sm);
}

.app-table-search {
    max-width: 22rem;
    /* F-34: with filters beside it the toolbar would squeeze the search to a few pixels. */
    min-width: 14rem;
}

.app-truncate {
    display: inline-block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* F-171: the result page layout (what the kit controls do not draw): the message, the score line, the counts, the essay line. */
.app-result-message {
    margin: 0 0 var(--app-space-5);
    color: var(--mud-palette-text-secondary);
    overflow-wrap: anywhere;
}

.app-result-hero {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--app-space-3) var(--app-space-5);
}

.app-result-score {
    margin: 0;
    color: var(--app-brand-name);
}

.app-result-score-value {
    font-size: 2.5rem;
    line-height: 2.75rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
}

.app-result-outof {
    margin-left: var(--app-space-2);
    font-size: var(--app-font-lg);
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
}

.app-result-verdict {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--app-space-2) var(--app-space-3);
}

.app-result-cutoff {
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-md);
}

.app-result-caption {
    margin: var(--app-space-2) 0 0;
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-md);
}

.app-result-counts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-2);
    margin: var(--app-space-4) 0 0;
    padding: var(--app-space-4) 0 0;
    border-top: 1px solid var(--mud-palette-divider);
}

.app-result-counts > div {
    flex: 1 1 7rem;
    min-width: 0;
    padding: var(--app-space-2) var(--app-space-3);
    background-color: var(--mud-palette-background-gray);
    border-radius: var(--app-radius-md);
}

.app-result-counts dt {
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-sm);
}

.app-result-counts dd {
    margin: 0;
    font-size: var(--app-font-xl);
    line-height: 1.75rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

.app-result-alert {
    margin-top: var(--app-space-4);
    margin-bottom: 0;
    overflow-wrap: anywhere;
}

.app-result-alert-first {
    margin-bottom: 0;
    overflow-wrap: anywhere;
}

.app-result-sep {
    color: var(--mud-palette-text-secondary);
}

.app-result-essay-status {
    margin-bottom: var(--app-space-3);
}

.app-result-essay {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-2) var(--app-space-6);
    margin: 0;
}

.app-result-essay dt {
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-sm);
}

.app-result-essay dd {
    margin: 0;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

.app-result-actions {
    margin-top: var(--app-space-5);
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-3);
}

@media (max-width: 599.98px) {
    .app-result-counts > div {
        flex: 1 1 calc(50% - var(--app-space-2));
    }

    .app-result-score-value {
        font-size: 2.125rem;
        line-height: 2.5rem;
    }
}

/* F-171: AppSummaryTable, a plain table of figures held whole. */
.app-summary-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--app-font-md);
}

.app-summary-table th,
.app-summary-table td {
    padding: var(--app-space-2) var(--app-space-3);
    border-bottom: 1px solid var(--mud-palette-divider);
    text-align: left;
    vertical-align: top;
}

.app-summary-table thead th {
    font-weight: 700;
    white-space: nowrap;
}

.app-summary-table tbody th {
    font-weight: 500;
}

.app-summary-table .app-summary-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.app-summary-table tbody tr:hover {
    background-color: var(--mud-palette-background-gray);
}

.app-summary-table tfoot th,
.app-summary-table tfoot td {
    font-weight: 700;
    border-bottom: 0;
    border-top: 2px solid var(--mud-palette-lines-inputs);
}

@media (max-width: 599.98px) {
    .app-summary-table,
    .app-summary-table tbody,
    .app-summary-table tfoot,
    .app-summary-table tr,
    .app-summary-table th,
    .app-summary-table td {
        display: block;
    }

    /* The header row stays for screen readers; each cell shows its column name instead. */
    .app-summary-table thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
    }

    .app-summary-table tr {
        border: 1px solid var(--mud-palette-divider);
        border-radius: var(--app-radius-md);
        padding: var(--app-space-2) 0;
        margin-bottom: var(--app-space-2);
    }

    .app-summary-table tfoot tr {
        border: 2px solid var(--mud-palette-lines-inputs);
    }

    .app-summary-table th,
    .app-summary-table td,
    .app-summary-table tfoot th,
    .app-summary-table tfoot td {
        border: 0;
        padding: var(--app-space-1) var(--app-space-3);
    }

    .app-summary-table tbody th,
    .app-summary-table tfoot th {
        font-weight: 700;
        padding-bottom: var(--app-space-2);
    }

    .app-summary-table td.app-summary-num {
        display: flex;
        justify-content: space-between;
        gap: var(--app-space-3);
        text-align: right;
    }

    .app-summary-table td.app-summary-num::before {
        content: attr(data-label);
        color: var(--mud-palette-text-secondary);
        font-weight: 500;
        text-align: left;
    }

    /* A stacked row wraps a long label instead of cutting it. */
    .app-summary-table .app-truncate-host {
        display: block;
    }

    .app-summary-table .app-truncate {
        max-width: none !important;
        white-space: normal;
        overflow: visible;
        overflow-wrap: anywhere;
    }
}

.gallery-section {
    margin-bottom: 2.5rem;
}

.gallery-section > .mud-typography-h2 {
    margin-bottom: 0.75rem;
}

.gallery-form {
    max-width: 32rem;
}

.gallery-icons {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
    gap: 0.75rem;
}

.gallery-icon {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

/* F-125: the mono font is wider than the system one; a long name breaks instead of widening the page. */
.gallery-icon code {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* F-125: the identity sections of the gallery. */
.gallery-button-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1.5rem;
}

.gallery-button-states {
    margin: 0 0 1rem;
    padding-left: 1.5rem;
}

.gallery-type-scale {
    display: grid;
    grid-template-columns: minmax(10rem, max-content) minmax(0, 1fr);
    align-items: baseline;
    gap: 0.5rem 1.5rem;
    margin: 0;
}

.gallery-type-scale dd {
    margin: 0;
}

.gallery-mono-grid {
    font-size: 0.8125rem;
    line-height: 1.25rem;
    font-variant-numeric: tabular-nums;
}

.gallery-palette {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: 0.75rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.gallery-swatch {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.gallery-swatch-chip {
    flex: none;
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: 6px;
}

/* ---- Composition patterns (F-43) ---- */

.app-section-card {
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-lg);
    padding: var(--app-space-4);
}

.app-section-card + .app-section-card {
    margin-top: var(--app-space-4);
}

.app-section-card-head {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
    margin-bottom: var(--app-space-4);
}

/* F-126, BR1: the number of a section, drawn in place of its icon. Decorative (aria-hidden): the heading keeps its name. */
.app-section-card-number {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--app-radius-sm);
    background-color: var(--mud-palette-primary-lighten);
    color: var(--app-brand-name);
    font-size: var(--app-font-md);
    font-weight: 800;
}

.app-section-card-heading {
    flex: 1 1 auto;
}

.app-section-card-title {
    margin: 0;
    font-size: var(--app-font-lg);
    font-weight: 600;
    color: var(--app-brand-name);
}

.app-section-card-subtitle {
    margin: var(--app-space-1) 0 0 0;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-form-grid {
    display: grid;
    gap: var(--app-space-4);
}

.app-form-grid-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.app-form-grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* One column on a phone: two short fields side by side stop being short at 375px. */
@media (max-width: 599.98px) {
    .app-form-grid-2,
    .app-form-grid-3 {
        grid-template-columns: minmax(0, 1fr);
    }
}

.app-radio-cards {
    display: grid;
    gap: var(--app-space-3);
}

.app-radio-cards-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.app-radio-cards-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 599.98px) {
    .app-radio-cards-2,
    .app-radio-cards-3 {
        grid-template-columns: minmax(0, 1fr);
    }
}

.app-radio-card {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
    padding: var(--app-space-3);
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-md);
}

.app-radio-card-selected {
    border-color: var(--mud-palette-primary);
    background-color: var(--mud-palette-primary-lighten);
}

.app-radio-card-text {
    display: flex;
    flex-direction: column;
    gap: var(--app-space-1);
}

.app-radio-card-title {
    font-size: var(--app-font-base);
    font-weight: 600;
}

.app-radio-card-description {
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-conditional-field:empty {
    display: none;
}

.app-status-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-2);
    padding: var(--app-space-1) var(--app-space-3);
    border-radius: var(--app-radius-sm);
    /* Measured on screen: the divider colour reads 1.42:1 on the dark card, so the pill vanished and the chip
       became loose text. The field-border colour is the one F-17 already took above 3:1 in both themes. */
    border: 1px solid var(--mud-palette-lines-inputs);
    font-size: var(--app-font-xs);
    white-space: nowrap;
}

/* The dot is never the only sign of the state: the text is always beside it (WCAG 2.2 1.4.1). */
.app-status-chip-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--mud-palette-text-secondary);
}

.app-status-chip-success .app-status-chip-dot {
    background: var(--mud-palette-success);
}

.app-status-chip-warning .app-status-chip-dot {
    background: var(--mud-palette-warning);
}

.app-status-chip-error .app-status-chip-dot {
    background: var(--mud-palette-error);
}

.app-status-chip-info .app-status-chip-dot {
    background: var(--mud-palette-info);
}

.app-error-summary {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
    padding: var(--app-space-3) var(--app-space-4);
    margin-bottom: var(--app-space-4);
    border: 1px solid var(--mud-palette-error);
    border-radius: var(--app-radius-md);
}

.app-error-summary-icon {
    color: var(--mud-palette-error);
}

.app-error-summary-title {
    margin: 0;
    font-weight: 600;
}

.app-error-summary-list {
    margin: var(--app-space-2) 0 0 0;
    padding-left: var(--app-space-4);
}

.app-form-aside {
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
    padding: var(--app-space-4);
    /* F-43, BR4: the aside stays visible while the reader scrolls a long form. F-126: it sits under the fixed app bar
       and, when its content is taller than the room left, scrolls inside itself (a media rule below lifts the cap when
       the aside is stacked under the form). */
    position: sticky;
    top: calc(var(--mud-appbar-height) + var(--app-space-4));
    max-height: calc(100dvh - var(--mud-appbar-height) - 2 * var(--app-space-4));
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* F-126, BR3: the content slot (the publication block of the question form): a divider, the heading and subtitle the kit
   draws, then what the page passes. */
.app-form-aside-content {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-3);
    margin-top: var(--app-space-4);
    padding-top: var(--app-space-4);
    border-top: 1px solid var(--mud-palette-divider);
}

.app-form-aside-content .app-form-aside-title {
    margin: 0;
}

.app-form-aside-content-subtitle {
    margin: 0;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

/* Every child of the slot takes a row of its own. The buttons and the amber key a page puts there as direct children keep
   that row beside the form, stacked 12px apart (room for the amber key's edge), at the full width of the aside; a long
   label wraps inside its button. */
.app-form-aside-content > * {
    flex: 1 0 100%;
    min-width: 0;
}

.app-form-aside-content .mud-tooltip-root,
.app-form-aside-content .mud-button-root,
.app-form-aside-content .app-action,
.app-form-aside-content .app-action-key {
    width: 100%;
    justify-content: center;
    text-align: center;
}

.app-form-aside-content .mud-button-root {
    height: auto;
    min-height: 44px;
}

.app-form-aside-title {
    margin: 0 0 var(--app-space-3) 0;
    font-size: var(--app-font-md);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
}

.app-form-aside-rows {
    margin: 0;
}

.app-form-aside-row + .app-form-aside-row {
    margin-top: var(--app-space-3);
}

.app-form-aside-row-label {
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-form-aside-row-value {
    margin: 0;
    font-size: var(--app-font-sm);
    overflow-wrap: anywhere;
}

.app-form-aside-row-empty {
    color: var(--mud-palette-text-secondary);
    font-style: italic;
}

.app-form-aside-checklist-title {
    margin: var(--app-space-4) 0 var(--app-space-2) 0;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-form-aside-checklist {
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-form-aside-checklist li {
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
    font-size: var(--app-font-sm);
}

.app-form-aside-checklist li + li {
    margin-top: var(--app-space-1);
}

.app-item-rows-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-item-row {
    display: flex;
    align-items: center;
    gap: var(--app-space-3);
    padding: var(--app-space-3);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
}

.app-item-row + .app-item-row {
    margin-top: var(--app-space-2);
}

.app-item-row-content {
    flex: 1 1 auto;
    min-width: 0;
}

.app-item-rows-empty {
    margin: 0;
    padding: var(--app-space-3);
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
}

/* ---- Notice subjects of an edition (F-74): kit parts composed, no new colour. ---- */

.app-notice-subjects-group + .app-notice-subjects-group {
    margin-top: var(--app-space-4);
}

.app-notice-subjects-group-title {
    margin: 0 0 var(--app-space-2);
}

.app-notice-subject-row {
    display: flex;
    align-items: center;
    gap: var(--app-space-3);
    min-width: 0;
}

/* F-75: two lines in the content column - the label, then what the row covers (chips) or "Not mapped". */
.app-notice-subject-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--app-space-1);
}

.app-notice-subject-main .app-truncate-host {
    min-width: 0;
    max-width: 100%;
}

.app-notice-subject-count {
    flex: none;
    align-self: flex-start;
    min-width: 6.5rem;
    padding-top: 1px;
    text-align: right;
    font-size: var(--app-font-sm);
    font-variant-numeric: tabular-nums;
    color: var(--mud-palette-text-secondary);
}

/* F-76: the kit's open/close block and the student's syllabus built on it. */
.app-disclosure-button {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-1);
    padding: var(--app-space-1) 0;
    background: none;
    border: 0;
    font: inherit;
    color: var(--app-link);
    cursor: pointer;
}

.app-disclosure-button:focus-visible {
    outline: 3px solid var(--mud-palette-info);
    outline-offset: 2px;
}

.app-disclosure-icon {
    transition: transform 0.15s;
}

.app-disclosure-icon.is-open {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .app-disclosure-icon {
        transition: none;
    }
}

.app-disclosure-content {
    margin-top: var(--app-space-2);
}

.app-syllabus {
    margin-top: var(--app-space-2);
}

.app-syllabus-empty {
    margin: var(--app-space-2) 0 0;
    font-size: var(--app-font-sm);
}

.app-syllabus-group + .app-syllabus-group {
    margin-top: var(--app-space-3);
}

.app-syllabus-group-title {
    margin: 0 0 var(--app-space-2);
    font-size: var(--app-font-sm);
    font-weight: 600;
}

.app-syllabus-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.app-syllabus-row {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
    padding: var(--app-space-2) 0;
    border-top: 1px solid var(--mud-palette-lines-default);
}

.app-syllabus-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--app-space-1);
}

.app-syllabus-not-mapped {
    font-size: var(--app-font-sm);
}

.app-syllabus-count {
    flex: none;
    min-width: 6.5rem;
    text-align: right;
    font-size: var(--app-font-sm);
    font-variant-numeric: tabular-nums;
    color: var(--mud-palette-text-secondary);
}

.app-syllabus-total {
    margin: var(--app-space-3) 0 0;
    font-size: var(--app-font-sm);
}

.app-notice-subjects-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--app-space-3);
    margin-top: var(--app-space-3);
}

.app-notice-subjects-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--app-space-3);
}

.app-notice-subjects-total {
    margin: 0;
    font-size: var(--app-font-sm);
    text-align: right;
}

.app-notice-subjects-total-sum {
    display: block;
    font-weight: 700;
}

.app-notice-subjects-total-unstated {
    display: block;
}

/* F-75: the right side of the footer stacks the sum and, below it, how many rows are not mapped yet. */
.app-notice-subjects-summary {
    text-align: right;
}

.app-notice-subjects-unmapped {
    margin: 0;
    font-size: var(--app-font-sm);
}

/* ---- Form + read-only aside layout (F-43, BR1, BR4): the reference screen's two-column page. ---- */

.app-form-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 20rem;
    gap: var(--app-space-4);
    align-items: start;
}

/* Below 1280px the aside stacks under the form instead of beside it: it is the only place that lists what
   is still missing, so it is never hidden, only moved (owner, screen question 2). */
@media (max-width: 1279.98px) {
    .app-form-layout {
        grid-template-columns: minmax(0, 1fr);
    }

    /* F-126, BR9: stacked under the form the aside is neither sticky nor capped, and its actions return to their natural
       width in one wrapping row, in the same order. */
    .app-form-aside {
        position: static;
        max-height: none;
    }

    .app-form-aside-content > .mud-tooltip-root,
    .app-form-aside-content > .mud-button-root,
    .app-form-aside-content > .app-action {
        flex: 0 0 auto;
    }

    .app-form-aside-content .mud-tooltip-root,
    .app-form-aside-content .mud-button-root,
    .app-form-aside-content .app-action,
    .app-form-aside-content .app-action-key {
        width: auto;
    }
}

.app-form-card {
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-lg);
    padding: var(--app-space-5);
}

/* ---- App shell (F-2) ---- */

.app-skip-link {
    position: fixed;
    left: var(--app-space-2);
    top: var(--app-space-2);
    z-index: 2000;
    padding: var(--app-space-2) var(--app-space-3);
    border-radius: var(--app-radius-sm);
    background: var(--mud-palette-surface);
    color: var(--app-link);
    font-weight: 600;
    transform: translateY(-200%);
}

.app-skip-link:focus {
    transform: none;
}

/* F-125, BR9: the brand in the app bar — the key symbol, then "Simulab" over "Control Alt Systems". The link shrinks
   before the switches do: each line stays on one line and the company line ends in an ellipsis at 375px. */
.app-name.mud-link {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-3);
    min-width: 0;
    margin-left: var(--app-space-1);
    color: var(--mud-palette-appbar-text);
}

.app-brand-symbol {
    flex: none;
    height: 1.75rem;
    width: auto;
}

.app-brand-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    white-space: nowrap;
}

.app-brand-name {
    color: var(--app-brand-name);
    font-size: var(--app-font-lg);
    font-weight: 800;
    line-height: 1.25rem;
    letter-spacing: -0.01em;
}

.app-brand-company {
    overflow: hidden;
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-xs);
    line-height: 1rem;
    text-overflow: ellipsis;
}

/* At 375px the switches keep their 48px targets, so the symbol shrinks first: measured, "Control Alt Systems" needs
   117px and has 118px left with a 12px symbol, the small gap and no margin; the ellipsis stays as the fallback. */
@media (max-width: 599.98px) {
    .app-name.mud-link {
        gap: var(--app-space-1);
        margin-left: 0;
    }

    .app-brand-symbol {
        height: 0.75rem;
    }
}

.mud-appbar .mud-toolbar {
    min-width: 0;
}

.app-user-menu:empty {
    display: none;
}

#main-content:focus {
    outline: none;
}

.app-nav-menu {
    padding: var(--app-space-2) 0;
}

.app-nav-section.mud-typography {
    display: block;
    padding: var(--app-space-4) var(--app-space-4) var(--app-space-1) var(--app-space-4);
    opacity: 0.7;
}

/* F-125: the drawer's tints are its own text colour at a share, so no literal colour is left (BR2). */
.app-nav-divider.mud-divider {
    margin: var(--app-space-2) 0;
    border-color: color-mix(in srgb, var(--mud-palette-drawer-text) 22%, transparent);
}

/* One menu item per line. MudBlazor's own .mud-tooltip-inline (inline-block) came later with the same weight
   and won, so short items sat side by side (F-14 validation); two classes outweigh it. */
.mud-tooltip-root.app-nav-tooltip {
    display: block;
}

.app-drawer .mud-nav-link {
    border-left: 3px solid transparent;
    color: inherit;
}

.app-drawer .mud-nav-link .mud-nav-link-icon {
    color: inherit;
}

.app-drawer .mud-nav-link:not(.mud-nav-link-disabled):hover {
    background-color: color-mix(in srgb, var(--mud-palette-white) 8%, transparent);
}

/* B-17: the selected item lightens its background and used to keep the drawer's translucent text, so the entry
   the user is looking at read worse (4.37:1 dark) than the ones around it. Full white on the composited surface
   reads 12.5:1 dark and 9.4:1 light. ThemeContrastTests computes both from the stylesheet. */
.app-drawer .mud-navmenu .mud-nav-link.active:not(.mud-nav-link-disabled),
.app-drawer .mud-navmenu .mud-nav-link[aria-current="page"] {
    background-color: color-mix(in srgb, var(--mud-palette-white) 14%, transparent);
    border-left-color: var(--mud-palette-white);
    color: var(--mud-palette-white);
    font-weight: 600;
}

/* Before the viewport is known (first render), keep the drawer out of the way on small screens. */
@media (max-width: 959.98px) {
    .app-shell-pending .app-drawer {
        display: none;
    }

    .app-shell-pending .mud-main-content {
        padding-left: 0 !important;
        margin-left: 0 !important;
    }
}

/* ---------------------------------------------------------------------------
   Auth screens (F-4). Declared exception of the ui rule: no app menu, no
   breadcrumb; the card is centred on the app background.
   --------------------------------------------------------------------------- */
.app-auth {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--mud-palette-background);
}

.app-auth-bar {
    display: flex;
    align-items: center;
    gap: var(--app-space-1);
    width: 100%;
    max-width: 760px;
    margin: 0 auto;
    padding: var(--app-space-3) var(--app-space-4);
}

.app-auth-name {
    font-weight: 700;
    color: var(--app-brand-name);
}

.app-auth-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--app-space-2) var(--app-space-4) var(--app-space-7);
    outline: none;
}

.app-auth-card {
    width: 100%;
    max-width: 440px;
    /* The auth card's own breathing room, wider than the scale's top step (F-4). */
    padding: 28px 26px;
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-lg);
    background: var(--mud-palette-surface);
}

.app-auth-card-wide {
    max-width: 760px;
}

/* F-85: the documents the acceptance page asks the user to read, one row each. */
.app-accept-documents {
    display: flex;
    flex-direction: column;
    gap: var(--app-space-3);
    margin: var(--app-space-4) 0;
    padding: 0;
    list-style: none;
}

.app-accept-document {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--app-space-2) var(--app-space-4);
    padding: var(--app-space-3) var(--app-space-4);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
}

.app-accept-document > div {
    display: flex;
    flex-direction: column;
}

.app-accept-data {
    margin-top: var(--app-space-4);
}

/* The kit link (B-6): underlined, in the link role (F-148, BR6), AA in both themes. */
.app-link {
    color: var(--app-link);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.app-link:hover {
    text-decoration-thickness: 2px;
}

/* Plain links on the sign-in pages look like links: underlined, in the text colour, which keeps AA contrast
   in both themes (the primary blue is 3.5:1 on the dark card). Found on screen in F-7: "Forgot your
   password?" and "Create an account" read as plain text. */
.app-auth-card p a,
.app-auth-footer-line a {
    color: var(--mud-palette-text-primary);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Text for screen readers only (a live region the page announces without showing it twice). */
.app-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.app-sign-in-forgot {
    margin: -4px 0 var(--app-space-4);
    font-size: var(--app-font-md);
    text-align: right;
}

.app-auth-footer-line {
    margin-top: var(--app-space-4);
    font-size: var(--app-font-md);
    text-align: center;
}

/* F-20: "Continue with Google". Google's mark keeps its own colours; the label follows the theme's text colour. */
.app-google-button .mud-button-label {
    gap: var(--app-space-3);
    color: var(--mud-palette-text-primary);
}

.app-google-mark {
    flex: none;
}

/* A rule with a word in the middle ("or"), in the secondary text colour. */
.app-divider {
    display: flex;
    align-items: center;
    gap: var(--app-space-3);
    margin: var(--app-space-4) 0;
    font-size: var(--app-font-md);
    color: var(--mud-palette-text-secondary);
}

.app-divider::before,
.app-divider::after {
    content: "";
    flex: 1;
    border-top: 1px solid var(--mud-palette-lines-default);
}

.app-auth-footer {
    display: flex;
    gap: var(--app-space-4);
    margin-top: 20px;
    font-size: var(--app-font-md);
}

.app-auth-title {
    font-size: var(--app-font-xl);
    font-weight: 700;
    line-height: 1.25;
    margin: 0 0 6px;
}

.app-auth-subtitle {
    font-size: var(--app-font-md);
    color: var(--mud-palette-text-secondary);
    margin: 0 0 22px;
}

/* Result pages: verified, expired, invalid, check your email. */
.app-auth-result {
    text-align: center;
}

.app-auth-badge {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 14px;
}

.app-auth-result p {
    font-size: var(--app-font-md);
    color: var(--mud-palette-text-secondary);
    line-height: 1.6;
    margin: 0 0 14px;
}

/* ---------------------------------------------------------------------------
   Form fields (F-4 kit additions).
   --------------------------------------------------------------------------- */
.app-field {
    margin-bottom: var(--app-space-4);
}

.app-field-label {
    display: block;
    font-size: var(--app-font-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 6px;
}

.app-field-required {
    color: var(--mud-palette-error);
    margin-left: 3px;
}

/* B-16: this was text-disabled, a translucent MudBlazor default nobody had measured - 1.90:1 dark, 2.64:1 light.
   text-secondary reads 6.92:1 light and 4.89:1 dark on the card. It is the label's colour too, but the label is
   uppercase, 600 and letter-spaced, so the two never read as the same thing. ThemeContrastTests holds the numbers. */
.app-field-hint {
    display: block;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
    margin-top: 5px;
    line-height: 1.45;
}

.app-field-error-text {
    display: block;
    font-size: var(--app-font-xs);
    font-weight: 600;
    color: var(--mud-palette-error);
    margin-top: 5px;
}

/* F-105 AC18: the counter of a text field sits right-aligned on the line of the hint or the error text, as text
   in the secondary colour (the same token as the hint), so the count never depends on colour and never replaces
   the message. */
.app-field-description-counter {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--app-space-2);
}

.app-field-counter {
    margin-left: auto;
    margin-top: 5px;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* F-183, the text kinds. A code and a one-time code are read character by character, so they are monospace; the six
   digits are spread so each one is seen apart. */
/* MudBlazor's ".mud-input > input.mud-input-root" sets font-family: inherit, so the rule names the same input. */
.app-code-field .mud-input > input.mud-input-root {
    font-family: var(--app-font-mono);
}

.app-otp-field .mud-input > input.mud-input-root {
    font-family: var(--app-font-mono);
    letter-spacing: 0.4em;
    font-variant-numeric: tabular-nums;
}

.app-otp-field .mud-input-control {
    max-width: 14rem;
}

/* The URL field's "open link" sits over the end of the input; the input keeps room for it. */
.app-field-control {
    position: relative;
}

.app-field-has-end input {
    padding-right: var(--app-space-7);
}

.app-url-open {
    position: absolute;
    top: 50%;
    right: var(--app-space-2);
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    padding: var(--app-space-1);
    border-radius: var(--app-radius-sm);
    color: var(--app-link);
}

.app-url-open:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

/* F-181, the number controls: the unit and the stepper buttons sit over the end of the input, which keeps room for
   them. A number is read digit by digit, so the digits line up (tabular) and the text is not italic or cut. */
.app-integer-field input.mud-input-root,
.app-decimal-field input.mud-input-root {
    font-variant-numeric: tabular-nums;
}

.app-integer-field.app-field-has-end input,
.app-decimal-field.app-field-has-end input {
    padding-right: calc(var(--app-space-7) * 2.5);
}

.app-number-end {
    position: absolute;
    top: 50%;
    right: var(--app-space-2);
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-1);
}

.app-number-unit {
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}

.app-number-step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2rem;
    min-height: 2rem;
    padding: var(--app-space-1);
    border: 0;
    border-radius: var(--app-radius-sm);
    background: transparent;
    color: var(--app-link);
    cursor: pointer;
}

.app-number-step:disabled {
    color: var(--mud-palette-text-disabled);
    cursor: default;
}

.app-number-step:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

/* F-184, the choice controls. The selected state is never colour alone: a segment has a heavier border and a bold
   word, a dot is filled (not only tinted), a switch moves its thumb. */
.app-segments {
    display: inline-flex;
    flex-wrap: wrap;
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-md);
    overflow: hidden;
}

.app-segment {
    min-height: 44px;
    padding: 0 var(--app-space-4);
    border: 0;
    border-right: 1px solid var(--mud-palette-lines-inputs);
    background: transparent;
    color: var(--mud-palette-text-primary);
    font: inherit;
    cursor: pointer;
}

.app-segment:last-child {
    border-right: 0;
}

.app-segment-selected {
    background-color: var(--mud-palette-primary-lighten);
    box-shadow: inset 0 0 0 2px var(--mud-palette-primary);
    font-weight: 700;
}

.app-segment:disabled {
    color: var(--mud-palette-text-disabled);
    cursor: default;
}

/* F-172: four options do not fit one row on a phone. Under 600px they become a 2 x 2 grid with a line between the rows.
   Scoped to four options: two or three stay in one row. */
@media (max-width: 599.98px) {
    .app-segments-four {
        display: grid;
        grid-template-columns: 1fr 1fr;
        width: 100%;
    }

    .app-segments-four .app-segment {
        padding: 0 var(--app-space-2);
    }

    .app-segments-four .app-segment:nth-child(even) {
        border-right: 0;
    }

    .app-segments-four .app-segment:nth-child(-n+2) {
        border-bottom: 1px solid var(--mud-palette-lines-inputs);
    }
}

.app-segment:focus-visible,
.app-switch:focus-visible,
.app-level-dot:focus-visible,
.app-level-clear:focus-visible,
.app-level-chip:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

.app-switch {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-3);
    min-height: 44px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--mud-palette-text-primary);
    font: inherit;
    cursor: pointer;
}

.app-switch:disabled {
    color: var(--mud-palette-text-disabled);
    cursor: default;
}

.app-switch-track {
    position: relative;
    width: 2.5rem;
    height: 1.5rem;
    border: 2px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-pill);
    background: transparent;
}

.app-switch-thumb {
    position: absolute;
    top: 50%;
    left: 0.15rem;
    width: 1rem;
    height: 1rem;
    border-radius: var(--app-radius-pill);
    background: var(--mud-palette-text-secondary);
    transform: translateY(-50%);
}

.app-switch-on .app-switch-track {
    border-color: var(--mud-palette-primary);
    background: var(--mud-palette-primary-lighten);
}

.app-switch-on .app-switch-thumb {
    left: 1.15rem;
    background: var(--mud-palette-primary);
}

.app-level-control {
    display: flex;
    align-items: center;
    gap: var(--app-space-3);
    min-height: 44px;
}

.app-level-dots {
    display: inline-flex;
    gap: var(--app-space-1);
}

.app-level-dot {
    width: 1rem;
    height: 1rem;
    padding: 0;
    border: 2px solid var(--mud-palette-text-secondary);
    border-radius: var(--app-radius-pill);
    background: transparent;
    cursor: pointer;
}

.app-level-dot-on {
    background: var(--mud-palette-text-secondary);
}

.app-level-dots .app-level-dot {
    box-sizing: content-box;
    margin: 0.35rem;
}

.app-level-dot:disabled {
    cursor: default;
}

.app-level-name {
    font-weight: 600;
}

.app-level-clear {
    display: inline-flex;
    align-items: center;
    padding: var(--app-space-1);
    border: 0;
    border-radius: var(--app-radius-sm);
    background: transparent;
    color: var(--app-link);
    cursor: pointer;
}

.app-level-mini {
    display: inline-flex;
    gap: 0.2rem;
}

.app-level-mini .app-level-dot,
.app-level-badge .app-level-dot {
    width: 0.5rem;
    height: 0.5rem;
    border-width: 1px;
    cursor: default;
}

.app-level-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-2);
}

.app-level-filter {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-2);
}

.app-level-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-2);
    min-height: 44px;
    padding: 0 var(--app-space-3);
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-pill);
    background: transparent;
    color: var(--mud-palette-text-primary);
    font: inherit;
    cursor: pointer;
}

.app-level-chip-on {
    border: 2px solid var(--mud-palette-primary);
    background-color: var(--mud-palette-primary-lighten);
    font-weight: 700;
}

.app-bool-icon {
    vertical-align: middle;
    margin-right: var(--app-space-1);
}

/* F-185, the date range: toggle buttons like the level chips; the chosen one has a heavier border and a bold word,
   never colour alone. The custom range is two date fields side by side. */
.app-range-presets {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-2);
}

.app-range-preset {
    min-height: 44px;
    padding: 0 var(--app-space-3);
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-pill);
    background: transparent;
    color: var(--mud-palette-text-primary);
    font: inherit;
    cursor: pointer;
}

.app-range-preset-on {
    border: 2px solid var(--mud-palette-primary);
    background-color: var(--mud-palette-primary-lighten);
    font-weight: 700;
}

.app-range-preset:disabled {
    color: var(--mud-palette-text-disabled);
    cursor: default;
}

.app-range-preset:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

.app-range-custom {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--app-space-3);
    margin-top: var(--app-space-3);
}

.app-relative-time {
    color: var(--mud-palette-text-secondary);
}

/* F-89, the file field: the input is the only control the browser knows, so it stays focusable but invisible, and the
   button next to it is a label that opens it. The focus ring is drawn on the row's first button when the input has
   the keyboard, so the reader sees where Enter or Space will act. A long file name wraps; it is never cut. */
.app-file-input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    border: 0;
}

.app-file-input:focus-visible ~ .app-file-row label.mud-button-root {
    outline: 3px solid var(--mud-palette-info);
    outline-offset: 2px;
}

.app-file-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--app-space-2) var(--app-space-3);
}

.app-file-stored {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--app-space-2);
    min-width: 0;
}

.app-file-name {
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: break-word;
}

.app-file-size {
    margin-top: 0;
}

.app-field-error .app-file-row label.mud-button-root {
    border-color: var(--mud-palette-error);
}

/* F-35, BR16: the date field says what is wrong in its own description below (one place, announced through
   aria-describedby). The picker's own helper line would repeat it with MudBlazor's wording. */
.app-date-field .mud-input-helper-text {
    display: none;
}

.app-password-strength {
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
    margin-top: var(--app-space-2);
}

.app-password-strength-bar {
    flex: 1;
    height: 5px;
}

.app-password-strength-text {
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}

.app-checkbox {
    margin-bottom: var(--app-space-1);
}

.app-checkbox-label {
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
    line-height: 1.5;
}

.app-alert {
    margin-bottom: 18px;
}

.app-alert-content {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

/* F-9: role and user administration. Text sits in text-primary on every surface, so it keeps AA contrast
   in both themes (the primary blue on its lighten tone is 4.3:1 in the light theme). */
.app-badge {
    display: inline-block;
    margin-left: var(--app-space-2);
    padding: 1px var(--app-space-2);
    border: 1px solid var(--mud-palette-primary);
    border-radius: var(--app-radius-sm);
    background: var(--mud-palette-primary-lighten);
    color: var(--mud-palette-text-primary);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    vertical-align: middle;
}

/* The read-only chip (F-75 AppChip). A list of them (.app-chip-list, .app-mp-chips) spaces them with its own gap. */
.app-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    margin: 2px var(--app-space-1) 2px 0;
    padding: 2px 10px;
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-sm);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    font-size: var(--app-font-xs);
    line-height: 1.35;
}

.app-chip .app-chip-icon {
    flex: none;
    width: 0.875rem;
    height: 0.875rem;
    color: var(--mud-palette-text-secondary);
}

.app-chip-text {
    min-width: 0;
    overflow-wrap: anywhere;
}

.app-chip-secondary {
    color: var(--mud-palette-text-secondary);
}

/* A chip in a table cell holds a short label that must stay on one line: with overflow-wrap: anywhere the browser
   sizes the column to one character and the label breaks letter by letter (F-97, the Type column). */
.app-cell-nowrap .app-chip {
    white-space: nowrap;
}

.app-cell-nowrap .app-chip-text {
    overflow-wrap: normal;
}

.app-chip.is-removable {
    padding-right: 2px;
}

/* A chip the Api refused: the border and the text say it, and the words say why (never the colour alone). */
.app-chip.is-marked {
    border-color: var(--mud-palette-error);
    border-width: 2px;
}

.app-chip-mark {
    color: var(--mud-palette-error);
    font-weight: 700;
}

/* The remove button is 24 px inside a 28 px chip (rule ui: a target is at least 24 px). */
.app-chip .app-chip-remove {
    flex: none;
    width: 24px;
    height: 24px;
    min-width: 24px;
    padding: 0;
    color: var(--mud-palette-action-default);
}

.app-chip-list,
.app-mp-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-1) 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-chip-list .app-chip,
.app-mp-chips .app-chip {
    margin: 0;
}

/* Chips field (F-77): a text input over a list of removable chips. The input has the anatomy of the multi-pick
   input without its icon; the chips go under the description, wrapping inside the field's width. */
.app-chips-input {
    display: flex;
    align-items: center;
    height: 42px;
    padding: 0 var(--app-space-3);
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-md);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
}

.app-chips-input:focus-within {
    border-color: var(--mud-palette-primary);
    box-shadow: 0 0 0 1px var(--mud-palette-primary);
}

.app-field-error .app-chips-input {
    border-color: var(--mud-palette-error);
}

.app-chips-input input {
    flex: 1;
    min-width: 0;
    height: 100%;
    border: 0;
    outline: none;
    background: transparent;
    color: var(--mud-palette-text-primary);
    font: inherit;
    font-size: var(--app-font-base);
}

.app-chips-input input::placeholder {
    color: var(--mud-palette-text-secondary);
}

.app-chips-input input:disabled {
    cursor: not-allowed;
}

.app-chips-list {
    margin-top: var(--app-space-2);
}

/* "overflow-wrap: anywhere" also lowers the min-content width of the item to one character, so a chip in this
   flex list would shrink to a sliver and break letter by letter. break-word wraps a long name only when the line is
   full and leaves the min-content width at its longest word. */
.app-chips-list .app-chip-text {
    overflow-wrap: break-word;
}

/* ---------------------------------------------------------------------------
   Multi-pick field (F-75): a search over a short local list, grouped, to pick several. Built from the field anatomy
   of AppTextField (label above, description below) with a listbox popup under the input.
   --------------------------------------------------------------------------- */
.app-mp-anchor {
    position: relative;
}

.app-mp-input {
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
    height: 42px;
    padding: 0 var(--app-space-3);
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-md);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
}

.app-mp-input:focus-within {
    border-color: var(--mud-palette-primary);
    box-shadow: 0 0 0 1px var(--mud-palette-primary);
}

.app-field-error .app-mp-input {
    border-color: var(--mud-palette-error);
}

.app-mp-input .mud-icon-root {
    flex: none;
    color: var(--mud-palette-action-default);
}

.app-mp-input input {
    flex: 1;
    min-width: 0;
    height: 100%;
    border: 0;
    outline: none;
    background: transparent;
    color: var(--mud-palette-text-primary);
    font: inherit;
    font-size: var(--app-font-base);
}

.app-mp-input input::placeholder {
    color: var(--mud-palette-text-secondary);
}

.app-mp-input input:disabled {
    cursor: not-allowed;
}

.app-mp-list {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 25;
    max-height: 300px;
    overflow-y: auto;
    padding: var(--app-space-1) 0;
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
    background: var(--mud-palette-surface);
}

.app-mp-group + .app-mp-group {
    border-top: 1px solid var(--mud-palette-divider);
}

.app-mp-option {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-2);
    min-height: 36px;
    padding: 7px var(--app-space-3);
    cursor: pointer;
    font-size: var(--app-font-sm);
    line-height: 1.4;
}

.app-mp-option.is-member {
    padding-left: var(--app-space-7);
}

.app-mp-option.is-group .app-mp-option-name {
    font-weight: 700;
}

.app-mp-option:hover,
.app-mp-option:focus {
    background: var(--mud-palette-primary-lighten);
}

.app-mp-option:focus-visible {
    outline: 3px solid var(--mud-palette-info);
    outline-offset: -3px;
}

.app-mp-option[aria-selected="true"] {
    background: var(--mud-palette-primary-lighten);
}

.app-mp-option[aria-disabled="true"] {
    cursor: not-allowed;
    color: var(--mud-palette-text-secondary);
}

.app-mp-option[aria-disabled="true"]:hover,
.app-mp-option[aria-disabled="true"]:focus {
    background: transparent;
}

/* The check box is drawn here: a picked option fills it and shows a check mark made of two borders. */
.app-mp-check {
    flex: none;
    width: 18px;
    height: 18px;
    margin-top: 1px;
    border: 2px solid var(--mud-palette-lines-inputs);
    border-radius: 3px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.app-mp-option[aria-selected="true"] .app-mp-check {
    background: var(--mud-palette-primary);
    border-color: var(--mud-palette-primary);
}

.app-mp-option[aria-selected="true"] .app-mp-check::after {
    content: "";
    width: 8px;
    height: 4px;
    margin-top: -2px;
    border-left: 2px solid var(--mud-palette-primary-text);
    border-bottom: 2px solid var(--mud-palette-primary-text);
    transform: rotate(-45deg);
}

.app-mp-option[aria-disabled="true"] .app-mp-check {
    border-style: dashed;
}

.app-mp-option-text {
    flex: 1;
    min-width: 0;
}

.app-mp-option-secondary {
    display: block;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-mp-empty {
    padding: var(--app-space-3);
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
}

.app-mp-status {
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
}

.app-mp-status .app-mp-spinner {
    width: 12px;
    height: 12px;
}

.app-mp-countline {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--app-space-2);
    min-height: 28px;
    margin-top: var(--app-space-2);
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-mp-chips {
    margin-top: var(--app-space-1);
}

.app-mp-none {
    margin: var(--app-space-1) 0 0;
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
}

.app-muted {
    color: var(--mud-palette-text-secondary);
}

.app-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--mud-palette-text-primary);
}

.app-status::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--mud-palette-text-secondary);
}

.app-status-active::before {
    background: var(--mud-palette-success);
}

.app-status-pending::before {
    background: var(--mud-palette-warning);
}

.app-table-filter {
    min-width: 220px;
    margin-left: var(--app-space-4);
}

/* F-14: an active filter with its remove button, next to the table's other filters. */
.app-filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 0 2px 0 var(--app-space-3);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-sm);
    color: var(--mud-palette-text-primary);
    font-size: var(--app-font-md);
}

.app-history-changes {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.app-permission-group {
    margin: 0 0 var(--app-space-3);
    padding: var(--app-space-1) 14px var(--app-space-2);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
}

.app-permission-group-head {
    display: flex;
    gap: var(--app-space-4);
    justify-content: space-between;
    padding: 0 var(--app-space-1);
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-xs);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.app-permission {
    display: flex;
    flex-direction: column;
}

.app-permission-name {
    color: var(--mud-palette-text-primary);
    font-weight: 600;
    font-size: var(--app-font-md);
}

.app-permission-description {
    color: var(--mud-palette-text-secondary);
}

.app-permission-technical {
    color: var(--mud-palette-text-secondary);
    font-family: var(--app-font-mono);
    font-size: var(--app-font-xs);
}

/* F-10: the danger zone on /account, set apart from the profile card by its border, not by colour alone. */
.app-danger-zone {
    border-color: var(--mud-palette-error);
}

.app-danger-zone-title {
    margin: 0 0 var(--app-space-2);
    font-size: var(--app-font-lg);
    font-weight: 700;
    color: var(--mud-palette-error);
}

/* F-16: the "Your data" card on /account, same heading size as the danger zone in the ordinary text colour. */
.app-data-title {
    margin: 0 0 var(--app-space-2);
    font-size: var(--app-font-lg);
    font-weight: 700;
}

/* F-11: the two-factor card on /account/security. */
.app-security-title {
    margin: 0 0 var(--app-space-3);
    font-size: var(--app-font-lg);
    font-weight: 700;
}

.app-security-steps {
    margin: 0 0 var(--app-space-4);
    padding-left: 20px;
}

.app-security-steps li {
    margin-bottom: var(--app-space-2);
}

.app-security-qr {
    /* A QR code needs a light quiet zone to scan, in both themes. */
    background: #ffffff;
    padding: var(--app-space-2);
    border-radius: 4px;
    image-rendering: pixelated;
}

.app-security-secret,
.app-security-codes code {
    font-family: var(--app-font-mono);
    font-size: 0.95rem;
    letter-spacing: 0.05em;
    color: var(--mud-palette-text-primary);
}

.app-security-codes {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--app-space-2) var(--app-space-5);
    margin: 0 0 var(--app-space-4);
    padding: 0;
    list-style: none;
}

.app-security-code-tools,
.app-security-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-2);
}

.app-security-actions {
    justify-content: flex-end;
    margin-top: var(--app-space-4);
}

/* F-31: the caller's own recent account events, on the same card shape as the two-factor card above. */
.app-security-activity-list {
    display: flex;
    flex-direction: column;
    gap: var(--app-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-security-activity-item {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--app-space-2);
    padding: var(--app-space-2) 0;
    border-bottom: 1px solid var(--mud-palette-divider);
}

.app-security-activity-item:last-child {
    border-bottom: none;
}

.app-security-activity-when {
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-sm);
    white-space: nowrap;
}

.app-security-activity-event {
    font-weight: 600;
}

.app-security-activity-detail,
.app-security-activity-address {
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-sm);
}

.app-security-activity-empty {
    color: var(--mud-palette-text-secondary);
}

/* ---------------------------------------------------------------------------
   Lookup field (F-34): the searchable parent picker of the kit.
   --------------------------------------------------------------------------- */
.app-lookup-option {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 2px 0;
}

.app-lookup-option-text {
    font-size: var(--app-font-md);
}

.app-lookup-option-secondary {
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-lookup-message {
    padding: var(--app-space-3) var(--app-space-4);
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
}

.app-lookup-retry {
    margin-left: var(--app-space-2);
    min-width: 0;
}

/* ---------------------------------------------------------------------------
   Select field (F-114): the list the page holds whole. An arrow at the end says "a list opens before you type";
   the leading icon (F-43) is drawn by the kit over the input's left padding, because the library has one
   adornment slot and the arrow uses it. Icons are actionDefault, the focus ring is the outline's (primary),
   the error border is the library's (error); no new colour.
   --------------------------------------------------------------------------- */
.app-select {
    position: relative;
}

/* The arrow: 24 px glyph inside a 32 px round target (rule ui: at least 24 px). */
.app-select .mud-input-adornment-end .mud-icon-button {
    width: 32px;
    height: 32px;
    padding: var(--app-space-1);
    color: var(--mud-palette-action-default);
}

.app-select .mud-input-adornment-end .mud-icon-root {
    width: 24px;
    height: 24px;
}

.app-select.app-select-disabled,
.app-select.app-select-disabled input,
.app-select.app-select-disabled .mud-input-adornment-end .mud-icon-button {
    cursor: not-allowed;
}

.app-select-leading {
    position: absolute;
    top: 50%;
    left: var(--app-space-3);
    z-index: 1;
    width: 20px;
    height: 20px;
    transform: translateY(-50%);
    color: var(--mud-palette-action-default);
    pointer-events: none;
}

.app-field .app-select.app-select-has-icon .mud-input input {
    padding-left: var(--app-space-7);
}

/* Loading: a 16 px progress where the arrow would be; the input still takes focus. */
.app-select-progress {
    position: absolute;
    top: 50%;
    right: var(--app-space-2);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    transform: translateY(-50%);
    pointer-events: none;
}

.app-select-progress .mud-progress-circular {
    width: 16px;
    height: 16px;
}

.app-field .app-select.app-select-loading .mud-input input {
    padding-right: 44px;
}

/* An option wider than the list wraps; the text of a long option never runs out of the popover. */
.app-select-option {
    display: block;
    white-space: normal;
    overflow-wrap: anywhere;
}

.app-select-message {
    padding: var(--app-space-3) var(--app-space-4);
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
    overflow-wrap: anywhere;
}

/* Field kinds gallery section (F-114): one column per kind, the kind's states stacked under a caption. */
.gallery-kind-title {
    margin-bottom: var(--app-space-3);
}

.gallery-state-caption {
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
    margin-bottom: var(--app-space-2);
    font-size: var(--app-font-xs);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

.gallery-state-caption::after {
    content: "";
    flex: 1;
    border-top: 1px dashed var(--mud-palette-divider);
}

.gallery-state-na {
    margin-bottom: var(--app-space-4);
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
}

/* ---------------------------------------------------------------------------
   Table filters (F-34): the fields a list offers beside its search box, inside
   the data table's toolbar. The toolbar is a flex row, so the fields lay out as
   one and drop their bottom margin; a kit field is a block by default and would
   otherwise stack and spill out of the card.
   --------------------------------------------------------------------------- */
.app-table-filters {
    display: flex;
    align-items: flex-start;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--app-space-3);
    /* The toolbar's spacer grows, so the wrapper has to refuse to shrink: with `min-width: 0` the
       spacer took every pixel and the three fields collapsed to zero width (found on screen). */
    flex: 0 0 auto;
    /* Without a ceiling it stays as wide as all its fields on one line and never wraps (B-38: 1288 px on a phone). */
    max-width: 100%;
}

.app-table-filters .app-field {
    margin-bottom: 0;
    flex: 0 0 12rem;
}

/* The toolbar holds the search and the filters; when they no longer fit on one line it wraps instead
   of squeezing the search (found on screen at 800 px). The library gives it a fixed height, so it also
   has to grow with its rows - otherwise the wrapped line lands on top of the table header. */
.app-data-table .mud-toolbar {
    /* The library's toolbar is `position: sticky` with no z-index, which is a stacking context of its own at the
       lowest level: a filter popup inside it (F-100) was painted under the sticky header cells (z-index 2 and 3)
       and its options mixed with the table's text (found on screen). */
    z-index: 4;
    flex-wrap: wrap;
    row-gap: var(--app-space-3);
    height: auto;
    min-height: 64px;
    padding-top: var(--app-space-2);
    padding-bottom: var(--app-space-2);
}

.app-table-filters .app-field-label {
    white-space: nowrap;
}

/* A page that marks its content `.app-page-wide` (F-100: the question list) gets the layout's content area up to
   1800 px instead of 1280 px; the other pages keep the layout's own width. Under 1800 px of room it changes
   nothing: the container is as wide as the room. */
.mud-container-maxwidth-lg:has(> .app-page-wide) {
    max-width: 1800px;
}

/* A toolbar with many filters (F-100: six fields and a clear button): the filters take their own full line under
   the search box and flow into as many columns as fit, then wrap onto more rows, instead of one row that is wider
   than the card (found on screen). */
.app-table-filters-grid {
    flex: 1 1 100%;
    display: grid;
    /* auto-fit, not auto-fill: the tracks nobody uses collapse, so six filters share the whole line (measured on
       a 1920 px screen: 191 px each with auto-fill, which kept two empty tracks, and about 255 px with auto-fit). */
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    justify-content: stretch;
    align-items: start;
    gap: var(--app-space-3);
    min-width: 0;
}

.app-table-filters-grid .app-field {
    flex: initial;
    width: 100%;
    min-width: 0;
}

.app-table-filters-grid .app-filter-clear {
    justify-self: start;
    align-self: end;
}

/* ---------------------------------------------------------------------------
   Rich text field (F-95): the kit's AppRichTextField around the Quill 2 editor. The editor's own styles
   (lib/quill/quill.snow.css) are overridden here with the theme's variables, so there is no literal colour.
   --------------------------------------------------------------------------- */
.app-rt-frame {
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-md);
    background: var(--mud-palette-surface);
    overflow: hidden;
}

/* The same 2px primary ring every outlined field takes on focus. */
.app-rt-frame:focus-within {
    border-color: var(--mud-palette-primary);
    box-shadow: 0 0 0 1px var(--mud-palette-primary);
}

.app-field-error .app-rt-frame {
    border-color: var(--mud-palette-error);
}

.app-rich-text-disabled .app-rt-frame {
    opacity: 0.55;
}

.app-rt-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    padding: var(--app-space-1) var(--app-space-2);
}

.app-rt-sep {
    align-self: stretch;
    width: 1px;
    margin: 0 var(--app-space-1);
    background: var(--mud-palette-divider);
}

/* Quill's snow theme paints its own toolbar and buttons; these rules put them on the theme. */
.app-rt-frame .ql-toolbar.ql-snow {
    border: 0;
    border-bottom: 1px solid var(--mud-palette-divider);
    padding: 0;
    font-family: inherit;
}

.app-rt-frame .ql-toolbar.ql-snow .app-rt-btn {
    float: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: var(--app-radius-sm);
    background: transparent;
    color: var(--mud-palette-action-default);
    cursor: pointer;
}

.app-rt-frame .ql-toolbar.ql-snow .app-rt-btn svg {
    float: none;
    width: 1.25rem;
    height: 1.25rem;
}

.app-rt-frame .ql-toolbar.ql-snow .app-rt-btn:hover {
    background: var(--mud-palette-primary-lighten);
    color: var(--mud-palette-action-default);
}

/* Pressed is the primary text on its lighten tone (4.79:1 light, 5.70:1 dark); the state is also aria-pressed. */
.app-rt-frame .ql-toolbar.ql-snow .app-rt-btn[aria-pressed="true"] {
    background: var(--mud-palette-primary-lighten);
    color: var(--app-link);
}

.app-rt-frame .ql-toolbar.ql-snow .app-rt-btn:disabled {
    cursor: default;
    opacity: 0.5;
}

.app-rt-frame .ql-container.ql-snow {
    border: 0;
    font-family: inherit;
    font-size: var(--app-font-md);
}

/* The editable grows with its content: no inner scroll, long words wrap. 7.5rem and 2.5rem are the editor's
   own minimum heights, which no step of the spacing scale covers. */
.app-rt-frame .ql-editor {
    min-height: 7.5rem;
    padding: var(--app-space-2) var(--app-space-3);
    overflow-wrap: anywhere;
    color: var(--mud-palette-text-primary);
    font-size: var(--app-font-md);
    line-height: 1.65;
}

.app-rich-text-compact .ql-editor {
    min-height: 2.5rem;
    padding: 6px var(--app-space-3);
}

.app-rt-frame .ql-editor.ql-blank::before {
    left: var(--app-space-3);
    right: var(--app-space-3);
    color: var(--mud-palette-text-secondary);
    font-style: normal;
}

.app-rt-frame .ql-editor blockquote {
    border-left: 3px solid var(--mud-palette-lines-inputs);
    color: var(--mud-palette-text-secondary);
}

/* ---- Option rows of the question form: letter badge, radio, compact editor, move and remove. ---- */
.app-option-row {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-2);
    min-width: 0;
}

.app-option-row .app-field {
    flex: 1 1 auto;
    min-width: 0;
    margin-bottom: 0;
}

/* A circle with the letter (or, F-106, the number): the primary text on its lighten tone, a pair the theme tests
   already measure. */
.app-option-letter,
.app-match-number {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    margin-top: 6px;
    border-radius: var(--app-radius-pill);
    background: var(--mud-palette-primary-lighten);
    color: var(--mud-palette-text-primary);
    font-size: var(--app-font-sm);
    font-weight: 700;
}

/* F-107: a blank of the statement in the preview, an underlined box with its number (or, revealed, its first answer).
   A fixed minimum width, so the box gives no hint of the answer's length (BR15); the text pair is the body's. */
.app-blank {
    display: inline-block;
    min-width: 4.5em;
    padding: 0 var(--app-space-2);
    border-bottom: 2px solid var(--mud-palette-text-primary);
    border-radius: var(--app-radius-sm) var(--app-radius-sm) 0 0;
    background: var(--mud-palette-primary-lighten);
    color: var(--mud-palette-text-primary);
    font-weight: 700;
    text-align: center;
}

/* F-107: one blank of the key section: its title, then its answers, each a field with its remove action. */
.app-blank-row {
    padding: var(--app-space-3) 0;
    border-top: 1px solid var(--mud-palette-lines-default);
}

.app-blank-row:first-child {
    border-top: none;
}

.app-blank-title {
    margin: 0 0 var(--app-space-2);
    font-size: var(--app-font-sm);
    font-weight: 700;
}

.app-blank-answer {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-2);
}

.app-blank-answer > :first-child {
    flex: 1;
}

.app-preview-blanks {
    margin: var(--app-space-2) 0 var(--app-space-3);
    padding-left: var(--app-space-4);
}

/* F-109: the rubric of an open-answer or essay question, one row per criterion; the points are numbers, right aligned. */
.app-preview-rubric {
    width: 100%;
    margin: var(--app-space-2) 0 var(--app-space-3);
    border-collapse: collapse;
}

.app-preview-rubric th,
.app-preview-rubric td {
    padding: var(--app-space-2);
    border-bottom: 1px solid var(--mud-palette-lines-default);
    text-align: left;
    vertical-align: top;
    overflow-wrap: anywhere;
}

.app-preview-rubric th:last-child,
.app-preview-rubric td:last-child {
    text-align: right;
    white-space: nowrap;
}

.app-preview-rubric-total td {
    font-weight: 700;
}

/* F-109: a criterion row of the rubric section: the name and the description share the row with the points and the remove action. */
.app-criterion-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--app-space-2);
    margin-bottom: var(--app-space-3);
}

.app-criterion-row > .app-criterion-name,
.app-criterion-row > .app-criterion-description {
    flex: 1 1 14rem;
}

.app-criterion-row > .app-criterion-points {
    flex: 0 0 7rem;
}

.app-answer-size {
    margin-top: var(--app-space-4);
}

.app-option-radio {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    min-height: 40px;
}

.app-options-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--app-space-3);
    margin-top: var(--app-space-3);
}

.app-options-count {
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
}

/* ---- Matching key (F-106): the numbered items and the lettered matches, side by side on a wide screen. ---- */
.app-matching-columns {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--app-space-4);
}

@media (min-width: 1100px) {
    .app-matching-columns {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

.app-matching-heading {
    margin: 0 0 var(--app-space-2);
    font-size: var(--app-font-sm);
    font-weight: 700;
}

.app-match-select {
    flex: none;
    width: 8rem;
}

.app-match-select .app-field {
    margin-bottom: 0;
}

.app-match-note {
    flex: none;
    align-self: center;
    font-size: var(--app-font-sm);
    color: var(--mud-palette-text-secondary);
}

.app-match-key {
    margin-left: var(--app-space-2);
    font-weight: 700;
}

/* ---- Publication block of the question form (F-126: in the content slot of the aside). ---- */
.app-publication-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--app-space-3);
    font-size: var(--app-font-sm);
}

.app-publication-annulment p {
    margin: 0;
    font-size: var(--app-font-sm);
    overflow-wrap: anywhere;
}

.app-publication-annulment p + p {
    margin-top: var(--app-space-1);
}

.app-publication-reason {
    margin: var(--app-space-2) 0 0;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

/* ---- F-102: the base text picker of the question form (open link, read-only text, create line) and the
   delete row of the base text form. Tokens only, no new colour. ---- */
.app-base-text-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--app-space-2) var(--app-space-5);
    margin-bottom: var(--app-space-2);
    font-size: var(--app-font-sm);
}

.app-base-text-preview {
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
    padding: var(--app-space-3) var(--app-space-4);
    margin-bottom: var(--app-space-3);
    font-size: var(--app-font-sm);
    max-height: 22rem;
    overflow-y: auto;
}

.app-base-text-note {
    display: block;
    margin-top: var(--app-space-3);
    font-size: var(--app-font-xs);
}

.app-base-text-create {
    margin-top: var(--app-space-3);
    padding-top: var(--app-space-3);
    border-top: 1px solid var(--mud-palette-divider);
    font-size: var(--app-font-sm);
}

.app-base-text-create-help {
    margin: var(--app-space-1) 0 0;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-danger-row {
    display: flex;
    align-items: center;
    gap: var(--app-space-3);
    flex-wrap: wrap;
}

/* ---- F-98: the reason of an answer key change, under the options or the true/false key, and in the
   publication block of the aside when a publish is refused for the missing reason. ---- */
.app-change-reason {
    margin-top: var(--app-space-4);
}

/* ---- F-96: where the question came from, above the statement in the preview. ---- */
.app-question-source {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-2);
    margin: 0 0 var(--app-space-3);
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-sm);
}

.app-question-source-line {
    overflow-wrap: anywhere;
}

/* ---- The question as a student reads it (preview dialog): stored HTML, so the look is all here (BR8). ---- */
.app-question-content {
    overflow-wrap: anywhere;
}

.app-question-content p {
    margin: 0;
}

/* F-103 BR15: a picture is scaled down to the width of its column, never up. */
.app-question-content img,
.app-rt-frame .ql-editor img,
.app-rt-image-preview img {
    max-width: 100%;
    height: auto;
}

.app-rt-image-preview {
    margin-top: var(--app-space-2);
    max-height: 240px;
    overflow: auto;
    text-align: center;
}

.app-question-content p + p {
    margin-top: var(--app-space-2);
}

.app-question-content a {
    color: var(--app-link);
    text-decoration: underline;
}

.app-question-content blockquote {
    margin: var(--app-space-2) 0;
    padding-left: var(--app-space-3);
    border-left: 3px solid var(--mud-palette-lines-inputs);
    color: var(--mud-palette-text-secondary);
}

.app-question-content ul,
.app-question-content ol {
    margin: var(--app-space-2) 0;
    padding-left: var(--app-space-5);
}

.app-question-content table {
    display: block;
    max-width: 100%;
    margin: var(--app-space-2) 0;
    overflow-x: auto;
    border-collapse: collapse;
}

.app-question-content th,
.app-question-content td {
    padding: 2px var(--app-space-2);
    border: 1px solid var(--mud-palette-divider);
}

.app-question-content th {
    color: var(--mud-palette-text-secondary);
    font-size: var(--app-font-sm);
    text-align: left;
}

.app-question-base {
    margin-bottom: var(--app-space-4);
    padding: var(--app-space-3) var(--app-space-4);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
    font-size: var(--app-font-sm);
}

.app-question-options {
    display: flex;
    flex-direction: column;
    gap: var(--app-space-2);
    margin: var(--app-space-4) 0 0;
    padding: 0;
    list-style: none;
}

.app-question-option {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
    padding: var(--app-space-2) var(--app-space-3);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
}

/* The key is a 2px success border (3.33:1 light, 4.88:1 dark on the surface) beside an icon and a text. */
.app-question-option.is-correct {
    border: 2px solid var(--mud-palette-success);
}

.app-question-option .app-option-letter,
.app-question-option .app-match-number {
    margin-top: 0;
}

.app-question-option-body {
    flex: 1 1 auto;
    min-width: 0;
}

.app-correct-label {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-1);
    margin-top: var(--app-space-1);
    font-size: var(--app-font-xs);
    font-weight: 700;
}

.app-preview-actions {
    margin-top: var(--app-space-4);
}

.app-preview-answer {
    margin-top: var(--app-space-4);
    padding-top: var(--app-space-4);
    border-top: 1px solid var(--mud-palette-divider);
}

.app-preview-heading {
    margin: 0 0 var(--app-space-2);
    font-size: var(--app-font-sm);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

.app-preview-no-key {
    margin: 0 0 var(--app-space-3);
    font-size: var(--app-font-sm);
}

/* ---------------------------------------------------------------------------
   Exam session (F-169). Declared exception of the ui rule (ui-project): no app menu, no breadcrumb. The question card
   and the answer sheet are the screen; every colour is a theme variable, and --app-primary-selected is the fill of a
   marked answer (SimulabThemeProvider writes it).
   --------------------------------------------------------------------------- */
.app-session-title {
    min-width: 0;
    margin: 0;
    font-size: var(--app-font-lg);
    line-height: 1.25rem;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.app-session-leave {
    font-size: var(--app-font-md);
    white-space: nowrap;
}

/* F-170: the countdown chip of a timed attempt. Neutral while there is time, tinted in the alert (10 minutes) and danger
   (1 minute) states; the note beside the time says it in words, so the colour is never the only cue. */
.app-session-countdown {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-1);
    margin-inline: var(--app-space-2);
    padding: var(--app-space-1) var(--app-space-3);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 999px;
    font-size: var(--app-font-md);
    font-weight: 700;
    white-space: nowrap;
}

.app-session-countdown-value {
    font-variant-numeric: tabular-nums;
}

.app-session-countdown-note {
    font-size: var(--app-font-sm);
    font-weight: 600;
}

.app-session-countdown-alert {
    border-color: var(--mud-palette-warning);
    background: color-mix(in srgb, var(--mud-palette-warning) 18%, transparent);
}

.app-session-countdown-danger {
    border-color: var(--mud-palette-error);
    background: color-mix(in srgb, var(--mud-palette-error) 18%, transparent);
}

.app-session-body {
    padding: var(--app-space-5);
}

/* From md up the answer sheet is a side panel of 300px beside the question; below it the sheet is a drawer. */
.app-session-body-aside {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: var(--app-space-5);
    align-items: start;
}

@media (max-width: 959.98px) {
    .app-session-body {
        padding: var(--app-space-3);
    }
}

.app-session-main {
    width: 100%;
    max-width: 760px;
    margin: 0 auto;
    outline: none;
}

.app-session-aside {
    position: sticky;
    top: 5rem;
    max-height: calc(100vh - 6rem);
    overflow-y: auto;
}

.app-session-section {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--app-space-1) var(--app-space-3);
    margin: 0 0 var(--app-space-2);
    font-size: var(--app-font-xs);
    font-weight: 800;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
    overflow-wrap: anywhere;
}

.app-session-section-range {
    font-weight: 500;
    letter-spacing: 0;
}

.app-session-question-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--app-space-3);
    margin-bottom: var(--app-space-4);
}

/* 1.25rem is the ui-heading step of the type scale, which the three --app-font scales do not carry. */
.app-session-question-title {
    margin: 0;
    font-size: 1.25rem;
    line-height: 1.5rem;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.app-session-question-position {
    font-size: var(--app-font-xs);
    font-variant-numeric: tabular-nums;
    color: var(--mud-palette-text-secondary);
}

.app-session-caption {
    margin: 0 0 var(--app-space-1);
    font-size: var(--app-font-xs);
    font-weight: 800;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
}

.app-session-hint {
    margin: var(--app-space-4) 0 var(--app-space-2);
    font-size: var(--app-font-xs);
    line-height: 1rem;
    color: var(--mud-palette-text-secondary);
}

.app-session-nav {
    display: flex;
    justify-content: space-between;
    gap: var(--app-space-3);
    margin-top: var(--app-space-5);
    padding-top: var(--app-space-4);
    border-top: 1px solid var(--mud-palette-divider);
}

.app-session-submit {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--app-space-3);
}

.app-session-submit-progress,
.app-edition-take-progress {
    display: inline-flex;
    margin-right: var(--app-space-2);
}

.app-session-submit-progress .mud-progress-circular,
.app-edition-take-progress .mud-progress-circular {
    width: 18px;
    height: 18px;
}

@media (max-width: 599.98px) {
    .app-session-nav .mud-button-root,
    .app-session-submit .mud-button-root {
        flex: 1;
    }
}

.app-session-drawer.mud-drawer {
    max-width: 86vw;
}

.app-session-drawer .app-answer-sheet,
.app-session-drawer .app-review-sheet {
    min-height: 100%;
    border: 0;
    border-radius: 0;
}

/* The start action of an edition (F-169): at the end of the row, under the row's text when the row is narrow. */
.app-edition-take {
    margin-inline-start: auto;
}

@media (max-width: 599.98px) {
    .app-edition-take {
        width: 100%;
    }
}

/* ---- Answer options (kit, F-169): rows of at least 44px; a marked row is fill, 2px border and indicator together. ---- */
.app-answer-options {
    display: flex;
    flex-direction: column;
    gap: var(--app-space-2);
}

.app-answer-options[aria-busy="true"] .app-answer-option {
    cursor: progress;
}

.app-answer-option {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
    width: 100%;
    min-height: 44px;
    padding: var(--app-space-2) var(--app-space-3);
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-md);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.app-answer-option:hover {
    background: var(--mud-palette-background);
}

/* The 2px border is a 1px border and a 1px ring, so a marked row does not move its neighbours. */
.app-answer-option-selected,
.app-answer-option-selected:hover {
    border-color: var(--mud-palette-primary);
    background: var(--app-primary-selected);
    box-shadow: inset 0 0 0 1px var(--mud-palette-primary);
}

.app-answer-indicator {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    margin-top: 2px;
    border: 2px solid var(--mud-palette-lines-inputs);
}

.app-answer-indicator-radio {
    border-radius: 50%;
}

.app-answer-indicator-box {
    border-radius: 4px;
}

.app-answer-indicator .mud-icon-root {
    width: 16px;
    height: 16px;
}

.app-answer-option-selected .app-answer-indicator {
    border-color: var(--mud-palette-primary);
}

.app-answer-option-selected .app-answer-indicator-radio::after {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--mud-palette-primary);
}

.app-answer-option-selected .app-answer-indicator-box {
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
}

.app-answer-letter {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: var(--app-radius-pill);
    background: var(--mud-palette-primary-lighten);
    color: var(--mud-palette-text-primary);
    font-size: var(--app-font-sm);
    font-weight: 800;
}

.app-answer-option-selected .app-answer-letter {
    background: var(--mud-palette-surface);
}

.app-answer-text {
    flex: 1;
    min-width: 0;
    padding-top: 1px;
}

/* ---- Answer sheet (kit, F-169): a nav of numbered cells grouped by section. ---- */
.app-answer-sheet {
    padding: var(--app-space-4);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-lg);
    background: var(--mud-palette-surface);
    /* It paints its own surface, so it names its text too: in the phone's drawer it would inherit the menu's white. */
    color: var(--mud-palette-text-primary);
}

.app-answer-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--app-space-2);
}

.app-answer-sheet-title {
    margin: 0;
    font-size: var(--app-font-lg);
    line-height: 1.25rem;
    font-weight: 800;
}

.app-answer-sheet-progress {
    margin: var(--app-space-1) 0 var(--app-space-2);
    font-size: var(--app-font-md);
    font-variant-numeric: tabular-nums;
}

.app-answer-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-4);
    margin: 0 0 var(--app-space-3);
    padding: 0;
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
    list-style: none;
}

.app-answer-legend li {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-1);
}

.app-answer-legend-swatch {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border: 2px solid var(--mud-palette-lines-inputs);
    border-radius: 4px;
}

.app-answer-legend-swatch .mud-icon-root {
    width: 12px;
    height: 12px;
}

.app-answer-legend-answered {
    border-color: var(--mud-palette-primary);
    background: var(--app-primary-selected);
    color: var(--mud-palette-text-primary);
}

.app-answer-legend-current {
    border-color: var(--mud-palette-text-primary);
}

.app-answer-sheet-section {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--app-space-2);
    margin: var(--app-space-3) 0 var(--app-space-2);
    font-size: var(--app-font-sm);
    line-height: 1.125rem;
    font-weight: 800;
}

.app-answer-sheet-section-label {
    min-width: 0;
    overflow-wrap: anywhere;
}

.app-answer-sheet-range {
    flex: none;
    font-family: var(--app-font-mono);
    font-size: var(--app-font-xs);
    font-weight: 600;
    white-space: nowrap;
    color: var(--mud-palette-text-secondary);
}

.app-answer-cells {
    display: grid;
    grid-template-columns: repeat(auto-fill, 40px);
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-answer-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1px;
    width: 100%;
    height: 36px;
    padding: 0;
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: 8px;
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    font-family: var(--app-font-mono);
    font-size: var(--app-font-sm);
    font-weight: 600;
    cursor: pointer;
}

.app-answer-cell:hover {
    background: var(--mud-palette-background);
}

/* Answered: the fill and a 2px border, and a check beside the number, so it is never colour alone. */
.app-answer-cell-answered,
.app-answer-cell-answered:hover {
    border: 2px solid var(--mud-palette-primary);
    background: var(--app-primary-selected);
}

/* Current: a 2px ink border and bold; with the answered fill it reads as both. */
.app-answer-cell[aria-current="step"] {
    border: 2px solid var(--mud-palette-text-primary);
    font-weight: 800;
}

.app-answer-cell-check.mud-icon-root {
    width: 11px;
    height: 11px;
}

/* ---- Review options (kit, F-172): a read-only list. A marked row is the fill and a 2px border, a key row a 2px border;
   both carry their words, so neither is colour alone. No hover, no pointer: it is not a control. ---- */
.app-review-options {
    display: flex;
    flex-direction: column;
    gap: var(--app-space-2);
    margin: var(--app-space-4) 0 0;
    padding: 0;
    list-style: none;
}

.app-review-option {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
    padding: var(--app-space-2) var(--app-space-3);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
}

/* The 2px border is a 1px border and a 1px ring, so a marked row does not move its neighbours. */
.app-review-option-marked {
    border-color: var(--mud-palette-primary);
    background: var(--app-primary-selected);
    box-shadow: inset 0 0 0 1px var(--mud-palette-primary);
}

.app-review-option-key {
    border-color: var(--mud-palette-success);
    box-shadow: inset 0 0 0 1px var(--mud-palette-success);
}

.app-review-letter {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--app-radius-pill);
    background: var(--mud-palette-primary-lighten);
    color: var(--mud-palette-text-primary);
    font-size: var(--app-font-sm);
    font-weight: 800;
}

.app-review-option-marked .app-review-letter {
    background: var(--mud-palette-surface);
}

.app-review-body {
    flex: 1 1 auto;
    min-width: 0;
    padding-top: 2px;
}

.app-review-tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-1) var(--app-space-3);
    margin-top: var(--app-space-1);
}

.app-review-tag {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-1);
    font-size: var(--app-font-xs);
    line-height: 1rem;
    font-weight: 700;
}

.app-review-tag .mud-icon-root {
    width: 18px;
    height: 18px;
}

/* An icon, not text: success is a fill (F-17 BR5), so it is painted with fill, 3.33:1 on the surface. */
.app-review-tag-key .mud-icon-root {
    fill: var(--mud-palette-success);
}

/* success on the marked fill reads under 3:1, so on the fill the key's icon is ink; its border is judged outside it. */
.app-review-option-marked .app-review-tag-key .mud-icon-root,
.app-review-tag-mark .mud-icon-root {
    fill: var(--mud-palette-text-primary);
}

.app-review-tag-wrong .mud-icon-root {
    color: var(--mud-palette-error);
}

/* ---- Review sheet (kit, F-172): the answer sheet's cells tell how each question ended. ---- */
.app-review-sheet {
    padding: var(--app-space-4);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-lg);
    background: var(--mud-palette-surface);
    /* It paints its own surface, so it names its text too: in the narrow screen's drawer it would inherit the menu's white. */
    color: var(--mud-palette-text-primary);
}

.app-review-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--app-space-2);
    margin-bottom: var(--app-space-2);
}

/* MudBlazor's icon button is a 12px padding around a 24px icon: 48px. The item asks for 40px, so the padding is 8px. */
.app-review-sheet-close.mud-icon-button {
    width: 40px;
    height: 40px;
    padding: var(--app-space-2);
}

.app-review-sheet-title {
    margin: 0;
    font-size: var(--app-font-lg);
    line-height: 1.25rem;
    font-weight: 800;
}

.app-review-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--app-space-2) var(--app-space-3);
    margin: 0 0 var(--app-space-3);
    padding: 0;
    font-size: var(--app-font-xs);
    line-height: 1rem;
    color: var(--mud-palette-text-secondary);
    list-style: none;
}

.app-review-legend li {
    display: inline-flex;
    align-items: center;
    gap: var(--app-space-1);
}

.app-review-swatch {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-sm);
    color: var(--mud-palette-text-secondary);
}

.app-review-swatch .mud-icon-root {
    width: 12px;
    height: 12px;
}

.app-review-sheet-section {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--app-space-2);
    margin: var(--app-space-3) 0 var(--app-space-2);
    font-size: var(--app-font-sm);
    line-height: 1.125rem;
    font-weight: 800;
}

.app-review-sheet-section-label {
    min-width: 0;
    overflow-wrap: anywhere;
}

.app-review-sheet-range {
    flex: none;
    font-family: var(--app-font-mono);
    font-size: var(--app-font-xs);
    font-weight: 600;
    white-space: nowrap;
    color: var(--mud-palette-text-secondary);
}

.app-review-cells {
    display: grid;
    grid-template-columns: repeat(auto-fill, 40px);
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-review-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1px;
    width: 100%;
    height: 36px;
    padding: 0;
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--app-radius-sm);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    font-family: var(--app-font-mono);
    font-size: var(--app-font-sm);
    font-weight: 600;
    cursor: pointer;
}

.app-review-cell:hover {
    background: var(--mud-palette-background);
}

.app-review-cell .mud-icon-root {
    width: 12px;
    height: 12px;
    color: var(--mud-palette-text-secondary);
}

.app-review-cell-right,
.app-review-swatch-right {
    border: 2px solid var(--mud-palette-success);
}

.app-review-cell-right .mud-icon-root,
.app-review-swatch-right .mud-icon-root {
    fill: var(--mud-palette-success);
}

.app-review-cell-wrong,
.app-review-swatch-wrong {
    border: 2px solid var(--mud-palette-error);
}

.app-review-cell-wrong .mud-icon-root,
.app-review-swatch-wrong .mud-icon-root {
    color: var(--mud-palette-error);
}

.app-review-cell-annulled,
.app-review-swatch-annulled {
    border: 2px dashed var(--mud-palette-lines-inputs);
}

/* The current cell keeps its outcome border and adds a cue that is not colour: an outer ring with a gap. */
.app-review-swatch-current {
    margin: var(--app-space-1);
    box-shadow: 0 0 0 2px var(--mud-palette-surface), 0 0 0 4px var(--mud-palette-text-primary);
}

.app-review-cell[aria-current="step"] {
    box-shadow: 0 0 0 2px var(--mud-palette-surface), 0 0 0 4px var(--mud-palette-text-primary);
    font-weight: 800;
}

.app-review-cell[aria-current="step"]:focus-visible {
    outline-offset: 5px;
}

/* ---- The review page (F-172): MainLayout's page. The filter and the sheet button on one row; from Lg up the answer sheet is a
   300px side panel, below it a drawer. Every text wraps in its element and nothing here sets a width wider than the card. ---- */
.app-review-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--app-space-3);
    margin-bottom: var(--app-space-4);
}

.app-review-toolbar .app-segmented-field {
    min-width: 0;
    max-width: 100%;
}

@media (max-width: 599.98px) {
    .app-review-toolbar .app-segmented-field,
    .app-review-sheet-open {
        width: 100%;
    }
}

.app-review-sheet-open.mud-button-root {
    min-height: 44px;
}

.app-review-layout {
    display: block;
}

.app-review-layout-aside {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: var(--app-space-5);
    align-items: start;
}

.app-review-main {
    min-width: 0;
}

.app-review-aside {
    position: sticky;
    top: 5rem;
    max-height: calc(100vh - 6rem);
    overflow-y: auto;
    border-radius: var(--app-radius-lg);
}

.app-review-card {
    overflow-wrap: anywhere;
}

.app-review-outcome {
    margin-bottom: var(--app-space-4);
}

.app-review-alert-before {
    margin-bottom: var(--app-space-4);
}

.app-review-alert-after {
    margin-top: var(--app-space-4);
}

.app-review-note {
    margin: var(--app-space-4) 0 0;
    font-size: var(--app-font-md);
    color: var(--mud-palette-text-secondary);
}

.app-review-base {
    padding: var(--app-space-3) var(--app-space-4);
    margin-bottom: var(--app-space-4);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--app-radius-md);
    font-size: var(--app-font-md);
}

.app-review-back {
    margin-top: var(--app-space-4);
}

.app-review-page-note {
    margin-bottom: var(--app-space-4);
}

/* ---- Save status (kit, F-169) ---- */
.app-save-status {
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
    min-height: 24px;
    margin-top: var(--app-space-2);
    font-size: var(--app-font-xs);
    color: var(--mud-palette-text-secondary);
}

.app-save-status .mud-icon-root {
    width: 16px;
    height: 16px;
}

.app-save-status-spinner {
    display: inline-flex;
}

.app-save-status-spinner .mud-progress-circular {
    width: 14px;
    height: 14px;
}

.gallery-answer-sheet-sample {
    max-width: 300px;
    margin-bottom: var(--app-space-4);
}
