/* Minimal shell styling. The full stylesheet (incl. the .layout.dark colour overrides that drive dark
   mode without swapping a Syncfusion theme) is ported alongside the real layout. */

html, body {
    margin: 0;
    padding: 0;
    font-family: Roboto, "Segoe UI", system-ui, sans-serif;
    height: 100%;
}

/* Tokens are defined here as well as on .layout, because MudBlazor renders dialogs and snackbars OUTSIDE
   the layout div — anything in a dialog that reads --surface or --text would otherwise get nothing. The
   .layout copies further down still win inside the app for specificity. */
:root {
    --surface: #ffffff;
    --app-bg: #f8fafc;
    --border: #e2e8f0;
    --text: #0f172a;
    --text-muted: #64748b;
    --hover: #f1f5f9;
    /* Toolbar button outline. Deliberately darker than --border: the Filter / Columns / Export buttons sit
       on white with nothing behind them, and the page border tone disappears at that size. Matches the
       weight of the stock MudBlazor input line the sibling 365 toolkit uses. */
    --btn-border: #94a3b8;
    /* Brand blue. Kept in step with AppTheme.Blue, which supplies the same value to MudBlazor — the layout
       overrides this per section from AppNav.ActiveTab().Color. */
    --accent: #1a73e8;
}

body:has(.layout.dark) {
    --surface: #1c1c1e;
    --app-bg: #121214;
    --border: #303034;
    --text: #ededef;
    --text-muted: #a0a0a6;
    --hover: #26262a;
    --btn-border: #4a4a50;

    /* The layout fills the viewport, but a rubber-band scroll can still expose the body behind it, which
       would flash white in dark mode. */
    background: #121214;
}

/* Theme tokens. --accent is set per-section on the layout wrapper so the active nav highlight follows
   whichever section owns the current route. */
.layout {
    --surface: #ffffff;
    --app-bg: #f8fafc;
    --border: #e2e8f0;
    --text: #0f172a;
    --text-muted: #64748b;
    --hover: #f1f5f9;
    /* Toolbar button outline. Deliberately darker than --border: the Filter / Columns / Export buttons sit
       on white with nothing behind them, and the page border tone disappears at that size. Matches the
       weight of the stock MudBlazor input line the sibling 365 toolkit uses. */
    --btn-border: #94a3b8;
    --nav-text: #334155;
    --nav-icon: #94a3b8;
    --accent: #1a73e8;

    display: flex;
    flex-direction: column;
    height: 100vh;
}

/* Neutral greys rather than the blue-tinted slate scale. Slate reads as "dark blue app"; this reads as
   dark, and it lets the one blue on screen — the accent — actually be the blue.

   Nothing is pure black. #000 against white text is the highest contrast a screen can produce and is
   tiring to read for long; a near-black ground with surfaces a step lighter keeps the panel edges visible
   without a border doing all the work. */
.layout.dark {
    --surface: #1c1c1e;
    --app-bg: #121214;
    --border: #303034;
    --text: #ededef;
    --text-muted: #a0a0a6;
    --hover: #26262a;
    --btn-border: #4a4a50;
    --nav-text: #d2d2d6;
    --nav-icon: #85858c;
    /* Kept blue: on a neutral ground it is the only hue on screen, which is what an accent is for. */
    --accent: #4c8ff0;
}

/* Dark in both themes — it's the band that grounds the top of the app, so it doesn't follow --surface.
   A deep navy rather than the teal it used to be: teal was a fourth hue with nothing else to relate to. */
.titlebar {
    display: flex;
    align-items: center;
    gap: 12px;
    height: 48px;
    padding: 0 16px;
    background: #0f2740;
    border-bottom: 1px solid #0a1c2e;
    color: #f1f5f9;
    flex: 0 0 auto;
}

/* Dark mode: a near-black band, darker than the page background behind it. */
/* A step ABOVE the page rather than below it: the bar is chrome sitting over the content, and darker
   than everything else made it read as a hole in the window. */
.layout.dark .titlebar {
    background: #202024;
    border-bottom-color: #303034;
}

.titlebar .brand {
    display: flex;
    align-items: center;
    gap: 9px;
    font-weight: 600;
    font-size: 15px;
    flex: 0 0 auto;
}

/* The mark ships on its own white ground rather than transparent, so it is given a small radius to sit as a
   deliberate tile on the dark bar instead of a square patch of the page showing through. */
.brand-logo {
    height: 24px;
    width: auto;
    display: block;
    border-radius: 3px;
}

.titlebar-spacer { flex: 1; }

/* An initials badge on the dark band. Sized to match the theme toggle beside it so the right-hand
   controls read as one row of equal marks. */
.titlebar-user {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .02em;
    color: #f1f5f9;
    background: rgba(255, 255, 255, .14);
    border: 1px solid rgba(255, 255, 255, .22);
    border-radius: 50%;
    cursor: default;
    user-select: none;
}

.titlebar-user:hover,
.titlebar-user:focus-visible {
    background: rgba(255, 255, 255, .24);
    outline: none;
}

.titlebar-user-wrap { position: relative; flex: 0 0 auto; }

/* Two lines: who you are, then what that lets you do.

   Anchored to the RIGHT edge, not centred on the badge. The badge is the last control in the bar, so a
   centred card would extend past the viewport and give the whole page a horizontal scrollbar. */
.titlebar-user-card {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 1300;
    display: none;
    flex-direction: column;
    gap: 2px;
    padding: 8px 12px;
    white-space: nowrap;
    text-align: left;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, .18);
}

/* focus-within as well as hover, so the card is reachable by keyboard. */
.titlebar-user-wrap:hover .titlebar-user-card,
.titlebar-user-wrap:focus-within .titlebar-user-card { display: flex; }

.titlebar-user-name { font-size: 12px; font-weight: 600; }
.titlebar-user-role { font-size: 11px; color: var(--text-muted); }

/* The titlebar is dark slate in both themes, so this button is styled against it rather than the tokens. */
.theme-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 8px;
    color: rgba(255, 255, 255, 0.75);
    cursor: pointer;
    transition: background 0.15s;
}

.theme-toggle:hover { background: rgba(255, 255, 255, 0.1); }

/* ---- Toasts ----

   MudBlazor paints the whole card in the severity colour, so a routine "Saved" arrives as a block of solid
   green. This is the sibling 365 toolkit's treatment: a neutral card with a coloured left bar and a coloured
   icon, which says the same thing without shouting it.

   The dark rules carry a :has() selector as well as the plain one because a snackbar renders OUTSIDE the
   layout div — the same reason the theme tokens are declared on :root as well as on .layout. */
.mud-snackbar.mud-alert-filled-normal,
.mud-snackbar.mud-alert-filled-info,
.mud-snackbar.mud-alert-filled-success,
.mud-snackbar.mud-alert-filled-warning,
.mud-snackbar.mud-alert-filled-error {
    background-color: #fff;
    color: #242424;
    font-weight: 400;
    border: 1px solid rgba(0, 0, 0, .09);
    border-left-width: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .14);
}

.mud-snackbar.mud-alert-filled-success { border-left-color: #107c10; }
.mud-snackbar.mud-alert-filled-info    { border-left-color: #0f6cbd; }
.mud-snackbar.mud-alert-filled-warning { border-left-color: #d67f00; }
.mud-snackbar.mud-alert-filled-error   { border-left-color: #a4262c; }
.mud-snackbar.mud-alert-filled-normal  { border-left-color: #616161; }

/* The icon carries the severity; the text stays neutral. */
.mud-snackbar.mud-alert-filled-success .mud-snackbar-icon { color: #107c10; opacity: 1; }
.mud-snackbar.mud-alert-filled-info    .mud-snackbar-icon { color: #0f6cbd; opacity: 1; }
.mud-snackbar.mud-alert-filled-warning .mud-snackbar-icon { color: #d67f00; opacity: 1; }
.mud-snackbar.mud-alert-filled-error   .mud-snackbar-icon { color: #a4262c; opacity: 1; }
.mud-snackbar.mud-alert-filled-normal  .mud-snackbar-icon { color: #616161; opacity: 1; }

/* Dark: dark surface, the same hues brightened enough to hold their own against it. */
.layout.dark .mud-snackbar.mud-alert-filled-normal,
.layout.dark .mud-snackbar.mud-alert-filled-info,
.layout.dark .mud-snackbar.mud-alert-filled-success,
.layout.dark .mud-snackbar.mud-alert-filled-warning,
.layout.dark .mud-snackbar.mud-alert-filled-error,
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-normal,
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-info,
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-success,
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-warning,
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-error {
    background-color: #2b2b2b;
    color: #f3f3f3;
    border-color: rgba(255, 255, 255, .10);
    box-shadow: 0 4px 14px rgba(0, 0, 0, .45);
}

body:has(.layout.dark) .mud-snackbar.mud-alert-filled-success { border-left-color: #5ec75e; }
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-info    { border-left-color: #4aa3f0; }
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-warning { border-left-color: #e0a13a; }
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-error   { border-left-color: #e04b4b; }
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-normal  { border-left-color: #9a9a9a; }

body:has(.layout.dark) .mud-snackbar.mud-alert-filled-success .mud-snackbar-icon { color: #5ec75e; }
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-info    .mud-snackbar-icon { color: #4aa3f0; }
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-warning .mud-snackbar-icon { color: #e0a13a; }
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-error   .mud-snackbar-icon { color: #e04b4b; }
body:has(.layout.dark) .mud-snackbar.mud-alert-filled-normal  .mud-snackbar-icon { color: #9a9a9a; }

/* ---- Reset password dialog ---- */

.pwd-dialog { min-width: 320px; }

.pwd-who { margin: 0 0 14px; font-size: 13px; color: var(--text); }
.pwd-sam { margin-left: 8px; color: var(--text-muted); }

.pwd-row { display: flex; align-items: center; gap: 4px; }

/* A plain input rather than a Mud field: it shares its shape with the edit panel's boxes, and a password
   field is one place where the browser's own behaviour is worth not fighting. */
.pwd-input {
    flex: 1 1 auto;
    height: 34px;
    padding: 0 9px;
    border: 1px solid var(--btn-border);
    border-radius: 5px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    /* Tabular figures so a generated password is read back a character at a time without the eye slipping. */
    font-variant-numeric: tabular-nums;
}

.pwd-input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }

.pwd-generate {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-top: 8px;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 12.5px;
    color: var(--accent);
    cursor: pointer;
}

.pwd-generate:hover { text-decoration: underline; }
.pwd-check { margin-top: 16px; }

/* ---- New user page ---- */

/* Wider than the panel's two columns: this page has the room, and a three-up grid keeps the form from
   running past a screen. */
.create-fields { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: 900px; }

@media (max-width: 1100px) { .create-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* A sub-heading inside a section, quieter than the section head above it. */
.create-sub {
    margin-top: 22px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.create-preview { margin-top: 12px; margin-bottom: 0; }

.create-preview code {
    padding: 2px 6px;
    border-radius: 3px;
    background: var(--hover);
    font-size: 12px;
    color: var(--text);
}

.create-groups { margin-top: 16px; max-width: 620px; }
.create-pw { margin-top: 6px; max-width: 260px; }

/* The create page's action bar, stuck to the foot of the scrolling area. Not position:fixed — the page
   body is what scrolls, so sticky keeps it inside the form's own column and clear of the sidebar. */
/* The create form takes the page's height and scrolls INSIDE itself, the same way a report page lets only
   the grid scroll. Sticky was the obvious way to pin the action bar and did not hold — the card sits inside
   several flex ancestors — so the bar is simply outside the scrolling area instead, where it cannot move. */
.layout main:has(.create-page) { overflow: hidden; }

.layout main:has(.create-page) .content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    padding-bottom: 1.5rem;
}

.create-page { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

.create-page .bulk-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

.create-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 4px; }

.create-bar {
    flex: 0 0 auto;
    margin: 0;
    padding: 12px 0;
    border-top: 1px solid var(--border);
    background: var(--surface);
}

/* Says which mode the form is in, once, at the top — the whole point of the mode being explicit. */
.mode-banner {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0 0 14px;
    padding: 10px 14px;
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
    border-radius: 6px;
    background: color-mix(in srgb, var(--accent) 8%, transparent);
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--text);
    max-width: 780px;
}

.mode-banner .mud-icon-root { color: var(--accent); margin-top: 1px; }

/* Title on its own line, explanation under it: one run of text meant the name and the sentence after it
   ran together, which is what made "templateSales" read as a word. */
.mode-banner-text { display: flex; flex-direction: column; gap: 2px; }
.mode-banner-title { font-weight: 600; }
.mode-banner-note { color: var(--text-muted); }

/* ---- Template list ---- */

.tmpl-table { width: 100%; border-collapse: collapse; max-width: 980px; }

.tmpl-table th {
    text-align: left;
    padding: 8px 10px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    background: var(--hover);
    border-bottom: 1px solid var(--border);
}

.tmpl-table td { padding: 7px 10px; font-size: 13px; border-bottom: 1px solid var(--border); }
.tmpl-table tbody tr:hover td { background: var(--hover); }

.tmpl-example { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; color: var(--text-muted); }
.tmpl-dn { color: var(--text-muted); font-size: 12px; max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tmpl-actions { text-align: right; white-space: nowrap; }
.tmpl-actions .mud-icon-root { font-size: 1.05rem; color: var(--text-muted); }

/* A control and something beside it — a button and the value it set, or a dropdown and its example. */
.prop-inline { display: flex; align-items: center; gap: 10px; }
.prop-inline .prop-input { flex: 0 1 260px; }

.prop-btn { height: 28px; padding: 0 10px; font-size: 12.5px; flex: 0 0 auto; }
.prop-value { font-size: 12px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* What the chosen naming rule produces, which is the only way the rule names mean anything. */
.prop-example {
    font-size: 12px;
    color: var(--accent);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.prop-note { margin: 0; font-size: 12px; line-height: 1.6; color: var(--text-muted); max-width: 62ch; }
.prop-pw { margin-top: 8px; max-width: 260px; }

/* ---- Properties tab: one field per row ---- */

.prop-page { max-width: 780px; padding: 6px 2px 4px; }

.prop-head {
    margin: 22px 0 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.prop-page > .prop-head:first-child { margin-top: 6px; }

/* Label in a fixed column so every box on the tab starts at the same x — that alignment is most of what
   makes forty rows readable. */
.prop-row { display: flex; align-items: center; gap: 16px; padding: 3px 0; }

.prop-row > label {
    flex: 0 0 165px;
    font-size: 12.5px;
    color: var(--text-muted);
}

.prop-control { flex: 1 1 auto; min-width: 0; max-width: 520px; }

/* Smaller than the create form's boxes: these are read down a column rather than filled one at a time. */
/* A filled triangle, not a chevron: the Mud autocomplete in the row below draws Material's arrow_drop_down,
   and two dropdowns side by side have to agree on what a dropdown looks like. */
select.prop-input {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 28px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='5' viewBox='0 0 10 5'%3E%3Cpath d='M0 0h10L5 5z' fill='%23616161'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    /* 7px + half the 10px triangle = 12px, the same axis the autocomplete's icon centres on. See .prop-auto. */
    background-position: right 7px center;
}

.prop-input {
    width: 100%;
    height: 28px;
    padding: 0 8px;
    box-sizing: border-box;
    border: 1px solid var(--btn-border);
    border-radius: 4px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 12.5px;
}

.prop-input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }

/* A Mud autocomplete in a prop row: its own root is width:100%, which the row already constrains. */
.prop-auto .mud-input.mud-input-outlined { height: 28px; }
.prop-auto .mud-input.mud-input-outlined input { height: 26px; padding-top: 0; padding-bottom: 0; font-size: 12.5px; }

/* Template (a native select) and Copy from user (a Mud autocomplete) sit in consecutive rows, so their
   right edges and their arrows have to agree — two dropdowns an inch apart that don't line up read as a
   mistake even when nobody can say why.
   Two things pulled them apart, and both are fixed here rather than by nudging one of them until it looks
   close: Mud's field root sizes itself, so it stopped short of the select's 100%; and Mud's arrow is an
   icon BUTTON whose padding put its centre further from the edge than the select's background triangle.
   Both are pinned to the same geometry below, and select.prop-input's background-position is derived from
   it — 7px + half of a 10px triangle puts the point of the triangle on the same axis as the icon. */
.prop-auto,
.prop-auto .mud-input-control,
.prop-auto .mud-input {
    width: 100%;
    margin: 0;
}

.prop-auto .mud-input-adornment-end { margin-right: 0; padding-right: 2px; }

.prop-auto .mud-input-adornment-end .mud-icon-button {
    padding: 0;
    width: 20px;
    height: 20px;
    min-width: 20px;
}

.prop-hint { display: block; margin-top: 3px; font-size: 11px; color: var(--text-muted); }

/* The tab strip reuses the edit panel's, so a tab looks the same wherever it appears; only the spacing
   differs, because this one sits at the top of a page rather than under a panel header. */
/* Tabs on the left, the form's own actions on the right, sharing one rule. They act on this form, so they
   belong level with its tabs rather than up in the page chrome beside the breadcrumb. */
.create-tabbar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--border);
}

.create-tabs { margin: 0; border-bottom: 0; }

.create-tabbar-actions { display: flex; align-items: center; gap: 8px; padding-bottom: 6px; }

.create-tab-count {
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 9px;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent);
    font-size: 10.5px;
    font-weight: 700;
}

/* Collapsed sections for the long tail of the create form. A header that is a button, and nothing else
   until it is opened — the point is that the whole form's shape stays visible in one scroll. */
.create-more { border-top: 1px solid var(--border); }
.create-more:first-child { border-top: 0; }

.create-more-head {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 2px;
    border: 0;
    background: none;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    text-align: left;
    cursor: pointer;
}

.create-more-head:hover { color: var(--accent); }
.create-more-head.on { color: var(--accent); }

/* Says a collapsed section is not empty, so a value a template set can never hide behind a closed header. */
.create-more-dot {
    padding: 1px 7px;
    border-radius: 9px;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.create-more-body { padding: 2px 2px 16px; }
.create-wide { max-width: 620px; }
.create-hint { margin-top: 6px; margin-bottom: 0; }
.create-expiry { display: flex; flex-direction: column; gap: 10px; }

/* ---- Supported columns dialog ---- */

/* Scrolls inside the dialog: forty rows is taller than a dialog should ever be, and a dialog that grows
   past the viewport puts its own Close button out of reach. */
.cols-dialog { max-height: 60vh; overflow-y: auto; }

.cols-table { width: 100%; border-collapse: collapse; margin-top: 12px; }
.cols-table-flush { margin-top: 0; }

.cols-table th {
    position: sticky;
    top: 0;
    text-align: left;
    padding: 6px 8px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* Names like "Proxy Addresses (email aliases)" and physicalDeliveryOfficeName are the point of this table;
   wrapping them costs more width than letting the row stay on one line. */
.cols-table td {
    padding: 5px 8px;
    font-size: 12.5px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
.cols-table tbody tr:hover td { background: var(--hover); }

/* The attribute, in a face that makes it obviously a literal rather than prose. */
.cols-attr {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11.5px;
    color: var(--text-muted);
}

.cols-req {
    padding: 1px 7px;
    border-radius: 9px;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

/* ---- Rename dialog ---- */

.rename-dialog { min-width: 380px; }

/* The prefix takes the room; the suffix is a label, not a field. */
.rename-upn { display: flex; align-items: center; gap: 6px; }
.rename-upn .pwd-input { flex: 1 1 auto; min-width: 0; }

.rename-suffix {
    flex: 0 0 auto;
    font-size: 12.5px;
    color: var(--text-muted);
    white-space: nowrap;
}

.rename-note { margin-top: 14px; margin-bottom: 0; }

.rename-note code {
    padding: 1px 5px;
    border-radius: 3px;
    background: var(--hover);
    font-size: 12px;
}

/* ---- Confirm dialogs ---- */

/* MudBlazor's message box draws its answers as bare text, which reads as a link rather than a decision —
   and these are the dialogs that disable accounts and delete things. Given real buttons here rather than at
   each call site, since every confirm in the app goes through ShowMessageBoxAsync.

   Only the buttons that asked for no variant: a dialog that sets Variant.Filled or Outlined itself has
   already made this decision, and overriding it would undo the choice. */
.mud-dialog-actions .mud-button-root:not(.mud-button-filled):not(.mud-button-outlined) {
    min-width: 88px;
    padding: 5px 16px;
    border: 1px solid var(--btn-border);
    border-radius: 5px;
    color: var(--text);
    font-weight: 400;
    text-transform: none;
}

.mud-dialog-actions .mud-button-root:not(.mud-button-filled):not(.mud-button-outlined):hover {
    background-color: var(--hover);
}

/* The primary answer is the one the dialog is asking for, so it carries the weight. */
.mud-dialog-actions .mud-button-root.mud-button-text-primary:not(.mud-button-filled):not(.mud-button-outlined) {
    border-color: var(--accent);
    background-color: var(--accent);
    color: #fff;
}

.mud-dialog-actions .mud-button-root.mud-button-text-primary:not(.mud-button-filled):not(.mud-button-outlined):hover {
    background-color: var(--accent);
    filter: brightness(.94);
}

/* ---- Domain switcher ---- */

/* A native select, styled for the dark title bar: the same low-opacity white chrome as the theme toggle
   beside it, with its own chevron because appearance:none removes the platform's. */
.domain-select {
    height: 2rem;
    max-width: 260px;
    padding: 0 26px 0 10px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 8px;
    background-color: transparent;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23cbd5e1' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 9px center;
    color: rgba(255, 255, 255, 0.9);
    font: inherit;
    font-size: 12.5px;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
}

.domain-select:hover { background-color: rgba(255, 255, 255, 0.1); }
.domain-select:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.5); outline-offset: 1px; }

/* The list itself is drawn by the OS, which paints it on a light ground — white text would vanish. */
.domain-select option { color: #0f172a; background: #ffffff; }

/* Sync state in the title bar. Sized and bordered like the theme toggle beside it, so the right-hand end of
   the bar reads as one set of controls. The dot carries the state; the label carries the age. */
.sync-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 2rem;
    padding: 0 10px;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 8px;
    color: #e2e8f0;
    font: inherit;
    font-size: 12px;
    cursor: pointer;
    white-space: nowrap;
}

.sync-chip:hover { background: rgba(255, 255, 255, 0.1); }
.sync-chip:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.6); outline-offset: 1px; }

.sync-chip-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    background: #94a3b8;
}

.sync-chip.ok .sync-chip-dot { background: #22c55e; }
.sync-chip.fail .sync-chip-dot { background: #f87171; }
.sync-chip.run .sync-chip-dot { background: #fbbf24; animation: sync-chip-pulse 1.2s ease-in-out infinite; }

@keyframes sync-chip-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .35; }
}

@media (prefers-reduced-motion: reduce) {
    .sync-chip.run .sync-chip-dot { animation: none; }
}

/* The age is the point; below a narrow bar the dot alone still says whether anything is wrong. */
@media (max-width: 700px) {
    .sync-chip-text { display: none; }
    .sync-chip { padding: 0 8px; }
}

.layout-body {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    background: var(--app-bg);
}

/* Flex column so NavMenu's version footer can pin itself to the bottom with margin-top:auto.
   --surface, not a fixed colour: the sidebar is the raised surface and the page body behind it is
   --app-bg, which is what gives the separation in both themes. */
/* Width comes from the nav itself — a 52px rail alone when collapsed, rail plus panel when not — so
   collapsing really does hand the space to the page instead of leaving an empty column. Scrolling belongs
   to the panel inside, not here, so the rail stays put while a long menu scrolls past it. */
/* Grey in light mode. The pages beside it are largely white now, and a white sidebar against a white page
   leaves only a hairline saying where one ends and the other begins. Dark mode keeps --surface, where the
   sidebar is already the lighter of the two. */
.sidebar {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    background: var(--app-bg);
    border-right: 1px solid var(--border);
    overflow: hidden;
}

.layout.dark .sidebar { background: var(--surface); }

/* The main column: a white title bar pinned at the top, page body below it on the app background. */
.layout main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    overflow: auto;
    background: var(--app-bg);
}

.page-title {
    display: flex;
    align-items: center;
    /* Separates the page's own actions from the sync state beside them. */
    gap: 10px;
    height: 3.25rem;
    /* Horizontal padding matches .content so the title lines up with the body below it. */
    padding: 1rem 1.5rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 1;
    flex: 0 0 auto;
}

.page-title h4 {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
}

.page-title-spacer { flex: 1; }

.content {
    padding: 1.5rem 1.5rem 2rem;
}

/* On a report page let ONLY the grid scroll, not the page — otherwise there are two scrollbars and the
   grid never fills the space left under the toolbar. */
.layout main:has(.report-box) { overflow: hidden; }

.layout main:has(.report-box) .content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    padding-bottom: 1.5rem;
}

/* A report page is its grid. The card that used to frame it added a border, a radius and a shadow around
   something that already fills the page, and floated it on a grey surround that nothing else used — so the
   page takes the surface colour and the frame comes off. The toolbar's own bottom rule still separates the
   controls from the rows.

   Explicit --surface rather than leaning on MudPaper's default: the grid inside is recoloured by the
   .layout.dark overrides, so this has to land on exactly the same surface or a different shade shows
   through behind the grid. */
.layout main:has(.report-box) { background: var(--surface); }
.layout main:has(.report-box) .content { background: var(--surface); }

.report-box {
    overflow: hidden;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    background-color: var(--surface);
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* The grid's own wrapper inside .report-box.
   The grid is Height="100%", which resolves against this box — so this is what gives it a definite height
   to fill. Without it the grid overflows .report-box and its pager, which renders at the bottom of the
   grid, gets clipped by the overflow:hidden above. */
.report-content-area {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
}

/* Report toolbar (search, filters, count, actions) sitting above the grid inside .report-box. */
.toolbar-search { max-width: 280px; }
.toolbar-select { max-width: 160px; }

/* One height for every control in the row.

   Three libraries draw this toolbar and each sizes its own control: Mud's dense outlined field lands taller
   than a Syncfusion button, and a bare <select> is shorter than both, so the row read as three different
   sizes. Pinned here rather than per control so they cannot drift apart again. */
.grid-toolbar .toolbar-search .mud-input.mud-input-outlined,
.grid-toolbar .e-btn.e-outline,
.grid-toolbar .e-dropdown-btn.e-outline,
.grid-toolbar .toolbar-select {
    height: 36px !important;
}

.grid-toolbar .e-btn.e-outline,
.grid-toolbar .e-dropdown-btn.e-outline {
    display: inline-flex;
    align-items: center;
    line-height: 1;
}

.grid-toolbar .toolbar-search .mud-input.mud-input-outlined input {
    height: 34px;
    padding-top: 0;
    padding-bottom: 0;
}

/* The search box draws its own outline from the Mud palette, which is lighter than the buttons beside it. */
.grid-toolbar .toolbar-search .mud-input-outlined-border {
    border-color: var(--btn-border) !important;
}

/* Inline label for a toolbar control, in place of a floating one. margin-left gives it separation from
   whatever sits to its left; the toolbar's own gap handles the space to the control. */
.toolbar-label {
    margin-left: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    white-space: nowrap;
}

/* Same notch-closing as .field, for toolbar controls that are labelled from outside. */
.grid-toolbar .mud-input.mud-input-outlined > .mud-input-outlined-border legend,
.grid-toolbar .mud-input.mud-input-outlined.mud-shrink > .mud-input-outlined-border legend,
.grid-toolbar .mud-input.mud-input-outlined:focus-within > .mud-input-outlined-border legend {
    width: 0;
    padding: 0;
}

/* ---- Outlined controls in the report header ----
   Three libraries draw this row and each picks its own grey: the Mud search box uses
   --mud-palette-lines-inputs, Syncfusion's .e-outline uses a bootstrap5 grey, and MudButton outlined uses a
   translucent text-primary. Pin all of them to the search box's colour so the row reads as one set of
   controls. Following the Mud variable rather than --border means these track the input in dark mode too.
   !important because Syncfusion's own .e-outline rules are more specific than this. */
.grid-toolbar .e-btn.e-outline,
.grid-toolbar .e-dropdown-btn.e-outline {
    border: 1px solid var(--btn-border) !important;
}

/* Resting label and icon colour. Without this the buttons keep Syncfusion's own outline grey until you
   hover them, which reads noticeably lighter than the sibling 365 toolkit's buttons. */
.grid-toolbar .e-btn.e-outline,
.grid-toolbar .e-dropdown-btn.e-outline {
    color: var(--text) !important;
}

/* Sentence case on every Mud button, dialog actions included.

   Material's convention is ALL CAPS, but this app pairs Mud with Syncfusion buttons that don't uppercase, so
   any Mud button in a mixed row shouted — and "CANCEL"/"APPLY" in a dialog shouts at nothing at all. Set once
   here rather than per component: this had already been patched separately for the sync bar, the sync dialog
   and the OU picker before the dialogs were noticed. */
.mud-button-root { text-transform: none; }

/* The search box darkens its border on hover; match that rather than letting each library do its own thing.
   The background matters as much as the border: Syncfusion's own .e-outline hover/focus fills the button
   with its bootstrap5 grey (#6c757d), which is both off-brand and a fill nothing else in the toolbar has.
   :focus is included because a button keeps focus after the dialog it opened is closed — the state you're
   left looking at, not a transient one. */
.grid-toolbar .e-btn.e-outline:hover,
.grid-toolbar .e-btn.e-outline:focus,
.grid-toolbar .e-btn.e-outline:active,
.grid-toolbar .e-dropdown-btn.e-outline:hover,
.grid-toolbar .e-dropdown-btn.e-outline:focus,
.grid-toolbar .e-dropdown-btn.e-outline:active,
/* .e-active is a CLASS Syncfusion adds for as long as the dropdown's popup is open — not a pseudo-class, so
   the three above never matched it. It is the state you look at longest, since it lasts the whole time the
   menu is showing. */
.grid-toolbar .e-dropdown-btn.e-outline.e-active,
.grid-toolbar .e-dropdown-btn.e-outline.e-active:hover {
    border-color: var(--mud-palette-action-default) !important;
    background-color: var(--hover) !important;
    color: var(--text) !important;
    box-shadow: none !important;
}

/* Icons on the Filter / Columns / Export buttons.

   The glyph takes the BUTTON's colour rather than Syncfusion's own, so it stays correct in all three states
   these buttons have: neutral outline, grey hover, and the filled accent the OU and Filter buttons switch to
   when they're narrowing results — where the label goes white and an icon left on the default colour would
   disappear into the fill. */
.grid-toolbar .e-btn .e-btn-icon,
.grid-toolbar .e-dropdown-btn .e-btn-icon {
    color: inherit !important;
    font-size: 14px;
    margin-right: 6px;
}

/* Export opens a menu, but the icon and label already say so and the caret only adds width. */
.grid-toolbar .e-dropdown-btn .e-caret { display: none; }

/* ---- User detail slide-out ---- */

/* A clickable cell inside the Syncfusion grid. Deliberately not underlined at rest: a whole column of
   underlined text is noise, and the column header plus the hover state carry the affordance. */
.cell-link {
    color: var(--accent);
    cursor: pointer;
    font-weight: 400;
}

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

.detail-scrim {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, .32);
    z-index: 1400;
    opacity: 0;
    pointer-events: none;
    transition: opacity .18s ease;
}

.detail-scrim.detail-open { opacity: 1; pointer-events: auto; }

.detail-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(680px, 100vw);
    z-index: 1401;
    display: flex;
    flex-direction: column;
    background: var(--app-bg);
    border-left: 1px solid var(--border);
    box-shadow: -8px 0 28px -14px rgba(15, 23, 42, .45);
    transform: translateX(100%);
    transition: transform .2s ease;
}

.detail-panel.detail-open { transform: translateX(0); }

@media (prefers-reduced-motion: reduce) {
    .detail-panel, .detail-scrim { transition: none; }
}

/* Dialogs above the slide-out, not level with it.

   MudBlazor puts a dialog at z-index 1400, which is exactly where the panel's scrim sits - so a confirm
   raised BY the panel ("discard your changes?") rendered underneath that scrim: visible through the dim,
   and unclickable, because the scrim was taking every click. Answering it did nothing and the panel could
   not be closed at all.

   Lifted here rather than by lowering the panel, because the panel deliberately sits above the page chrome.
   1450 stays below Mud's popover layer (1500), which matters the other way round: a select inside a dialog
   has to open above it. */
.mud-overlay-dialog { z-index: 1450; }
.mud-dialog-container { z-index: 1451; }

.detail-head {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 16px 18px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    flex: 0 0 auto;
}

.detail-avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
}

.detail-ident { flex: 1; min-width: 0; }

.detail-name {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 16px;
    font-weight: 600;
    color: var(--text);
}

.detail-pill {
    font-size: 10.5px;
    font-weight: 600;
    padding: 1px 7px;
    border-radius: 999px;
    letter-spacing: .02em;
}

.detail-pill-off { background: #fee2e2; color: #b91c1c; }
.layout.dark .detail-pill-off, body:has(.layout.dark) .detail-pill-off { background: #401d1d; color: #f87171; }

.detail-sub {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 3px;
    font-size: 12.5px;
    color: var(--text-muted);
    min-width: 0;
}

.detail-sub > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.detail-dot { color: var(--border); }

/* Fixed width so the two values start at the same x, rather than the longer label pushing its own line
   further right than the other. */
.detail-key {
    flex: 0 0 56px;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--border-strong, var(--text-muted));
    opacity: 0.75;
}

/* Takes the rest of the row and does the truncating, since the label must never be the thing that clips. */
.detail-val { min-width: 0; flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* One line, truncated with a tooltip for the rest. An OU named "Azure AD Connect NOT Synced" wrapped onto a
   second line and turned a two-line header into four. */
.detail-path {
    font-size: 11.5px;
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.detail-head-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

/* Moved out of the header to the foot of the panel: it was the least important thing on screen and it sat
   next to the close button, which is the most. The title bar carries sync state on every page anyway. */
.detail-synced {
    display: block;
    margin-top: 4px;
    padding: 10px 18px 16px;
    font-size: 11px;
    color: var(--text-muted);
    border-top: 1px solid var(--border);
}

.detail-close {
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 13px;
}

.detail-close:hover { background: var(--hover); color: var(--text); }

.detail-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 14px 18px 28px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.detail-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 14px 16px;
}

.detail-card h4 {
    margin: 0 0 10px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.detail-card dl { margin: 0; display: flex; flex-direction: column; gap: 7px; }

/* Label column fixed so values line up down the card; it wraps rather than truncating, because a truncated
   attribute name is worse than a two-line one. */
.detail-row { display: grid; grid-template-columns: 170px 1fr; gap: 14px; align-items: baseline; }

.detail-row dt {
    font-size: 12.5px;
    color: var(--text-muted);
    overflow-wrap: anywhere;
}

.detail-row dd {
    margin: 0;
    font-size: 12.5px;
    color: var(--text);
    overflow-wrap: anywhere;
}

.detail-empty { color: var(--text-muted); }

/* Multi-valued attributes (memberOf, proxyAddresses) stack one per line. */
.detail-multi { display: flex; flex-direction: column; gap: 2px; }

.detail-pill-type {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent);
}

.detail-count {
    margin-left: 8px;
    font-weight: 400;
    font-size: 11.5px;
    color: var(--text-muted);
}

.detail-loading {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 12.5px;
    color: var(--text-muted);
    padding: 4px 0;
}

/* Wider column for the OS chart, narrower for the facts list — the bars benefit from length, a label/value
   list does not. */
.dash-secondary {
    display: grid;
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    gap: 16px;
    align-items: start;
}

@media (max-width: 1000px) {
    .dash-secondary { grid-template-columns: 1fr; }
}

.dash-note { margin: 0; font-size: 12.5px; color: var(--text-muted); }

.dash-subhead {
    margin: 14px 0 4px;
    font-size: 10.5px;
    letter-spacing: .08em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--text-muted);
    padding-top: 10px;
    border-top: 1px solid var(--border);
}

/* ---- Recently created lists ---- */
/* Two lines per row, so align to the top rather than the shared baseline the count rows use. */
.recent-row { align-items: flex-start; }

.recent-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }

.recent-name {
    font-size: 13px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.recent-detail {
    font-size: 11px;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---- Security assessment ---- */
/* Shown only before the first run. One sentence and the button that acts on it — the page has nothing else
   to say until there are results. */
.sec-intro { max-width: 74ch; font-size: 14px; color: var(--text); }
.sec-intro-lead { margin: 0; }
.sec-intro-actions { margin-top: 16px; }

/* Still used by the scheduler's empty state, which borrows this intro block. */
.sec-intro p { margin: 0 0 10px; }
.sec-intro-note { color: var(--text-muted); font-size: 13px; }

/* One divided strip: a score tile then one tile per category, each carrying a small pass/warn/fail bar so
   the red shows without reading. Clicking a category filters the grid. The score's colour is the verdict —
   green, amber, red at the desktop toolkit's thresholds (80 / 60). */
.sec-strip {
    display: grid;
    grid-template-columns: 220px repeat(5, minmax(0, 1fr));
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-bottom: 14px;
    overflow: hidden;
    /* The page body is a flex column that gives the grid the remaining height. overflow:hidden drops this
       item's automatic minimum to zero, so without this it is squeezed to a 40px sliver. */
    flex-shrink: 0;
}
@media (max-width: 1100px) { .sec-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .sec-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Explicit column layout on the buttons: a global button rule lays children out inline. */
.sec-strip > * {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    text-align: left;
    padding: 12px 16px;
    border: 0;
    border-right: 1px solid var(--border);
    border-radius: 0;
    background: transparent;
    font: inherit;
    color: var(--text);
    min-width: 0;
}
.sec-strip > *:last-child { border-right: 0; }

.sec-score-label { font-size: 11px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--text-muted); }
.sec-score-value { font-size: 30px; font-weight: 650; letter-spacing: -.03em; line-height: 1.1; margin: 2px 0 4px; font-variant-numeric: tabular-nums; }
.sec-score.ok .sec-score-value { color: #16a34a; }
.sec-score.warn .sec-score-value { color: #d97706; }
.sec-score.fail .sec-score-value { color: #dc2626; }
.sec-score-sub { font-size: 12px; color: var(--text-muted); }
.sec-score-sub b { font-weight: 600; color: var(--text); }
.sec-score-value { display: flex; align-items: baseline; gap: 8px; }
.sec-delta { font-size: 12px; font-weight: 600; letter-spacing: 0; padding: 1px 7px; border-radius: 10px; }
.sec-delta.up { color: #16a34a; background: color-mix(in srgb, #16a34a 12%, transparent); }
.sec-delta.down { color: #dc2626; background: color-mix(in srgb, #dc2626 12%, transparent); }

/* A card carrying one short list rather than ten rows; sits above the two-column row. */
.dash-card-narrow { max-width: 420px; }

/* Three equal cards across the full width. Stretched rather than start-aligned so the row reads as one band
   instead of three cards of different heights with a ragged bottom edge. */
.dash-pair {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
    align-items: stretch;
}
@media (max-width: 1200px) { .dash-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 800px) { .dash-pair { grid-template-columns: 1fr; } }

/* A stretched card puts its note at the bottom rather than leaving a gap under it. */
.dash-pair .dash-card { display: flex; flex-direction: column; }
.dash-pair .dash-card .dash-note:last-child { margin-top: auto; }

/* Four cards across one row. Drops to two columns before the lists inside start wrapping their labels. */
/* Deliberately mirrors .kpis — same column count, same gap, same breakpoints — so each donut sits under the
   number it breaks down. Any change to the KPI grid has to be made here too. */
.dash-donuts {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    align-items: stretch;
    margin-bottom: 16px;
}
@media (max-width: 900px) { .dash-donuts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .dash-donuts { grid-template-columns: 1fr; } }

/* The lists below, which have nothing to line up with. */
.dash-trio {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
    align-items: stretch;
}
@media (max-width: 1100px) { .dash-trio { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px) { .dash-trio { grid-template-columns: 1fr; } }

.dash-donuts .dash-card,
.dash-trio .dash-card { display: flex; flex-direction: column; }

/* ---- Pie: security groups by type ---- */

/* Four categorical hues in a fixed order, so a category keeps its colour whatever the counts do. Each set was
   checked for colour-vision separation and for contrast against its own surface, rather than the dark steps
   being an automatic lightening of the light ones. */
:root {
    --pie-1: #1a73e8;
    --pie-2: #e11d48;
    --pie-3: #14b8a6;
    --pie-4: #f59e0b;
}
.layout.dark, body:has(.layout.dark) {
    --pie-1: #4c8ff0;
    --pie-2: #fb7185;
    --pie-3: #2dd4bf;
    --pie-4: #fbbf24;
}

.pie-wrap { display: flex; justify-content: center; padding: 2px 0 4px; }

/* The total sits in the hole, where it reads as the whole the ring divides. */
.pie-total { font-size: 20px; font-weight: 600; fill: var(--text); }
.pie-total-label { font-size: 9px; letter-spacing: .08em; fill: var(--text-muted); }

/* One row per slice, counts right-aligned so the four numbers form a column to compare down. */
.pie-legend { display: flex; flex-direction: column; gap: 7px; margin-top: 8px; }

.pie-key {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: 12.5px;
    color: var(--text);
    cursor: pointer;
    text-align: left;
    min-width: 0;
}
.pie-key:hover .pie-key-label { color: var(--accent); }

/* A square rather than a dot: it matches the flat end of a ring segment better than a circle does. */
.pie-dot { width: 10px; height: 10px; border-radius: 2px; flex-shrink: 0; }

/* The swatch carries the colour; the label and count stay in text ink, so identity is never colour alone. */
.pie-key-label { color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pie-key-value { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---- Inactive users ---- */

.inactive-rows { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }

.inactive-row {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 0;
    background: none;
    border: 0;
    font: inherit;
    color: var(--text);
    cursor: pointer;
    text-align: left;
}
.inactive-row:hover .inactive-days { color: var(--accent); }

/* The window and its count boxed together, so the pair reads as one label rather than two columns. */
.inactive-window {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 1px;
    flex: 0 0 62px;
    padding: 5px 9px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--surface);
}
.inactive-days { font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.inactive-count { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; }

.inactive-track {
    flex: 1 1 auto;
    height: 22px;
    border-radius: 4px;
    background: var(--hover);
    overflow: hidden;
}
/* One hue for all three: same measure, different windows. A colour per row would imply different things. */
.inactive-bar { display: block; height: 100%; border-radius: 4px; background: var(--accent); min-width: 3px; }

.inactive-pct {
    flex: 0 0 40px;
    font-size: 12px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* ---- Topology: site links ---- */

.topo-links { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }

.topo-links-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.topo-links-title { font-size: 14px; font-weight: 600; color: var(--text); }
.topo-links-sub { font-size: 12px; color: var(--text-muted); }

.topo-link-table { border-collapse: collapse; width: 100%; max-width: 900px; font-size: 13px; }
.topo-link-table th {
    text-align: left;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 4px 16px 5px 0;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
.topo-link-table td { padding: 5px 16px 5px 0; border-bottom: 1px solid var(--border); }
.topo-link-table tr:last-child td { border-bottom: 0; }
.topo-link-name { font-weight: 600; white-space: nowrap; }

/* The site sits under the controller name: which site a controller is in matters for reading the row, but
   not enough to spend a column on. */
.topo-conn-site { display: block; font-weight: 400; font-size: 11.5px; color: var(--text-muted); }
.topo-conn-off { opacity: .6; }
.topo-link-num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---- Scheduler ---- */

/* Toolbar above the table. */
.sched-bar {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 14px;
}

.sched-search { max-width: 300px; }

.sched-table { border: 1px solid var(--border); border-radius: 6px; }

/* Tight enough that four of them fit the Actions column without the row growing. */
.sched-icons { display: flex; align-items: center; gap: 0; }
.sched-icons .mud-icon-button { padding: 5px; }
.sched-icons .mud-icon-root { font-size: 1.15rem; color: var(--text-muted); }

.sched-toggle { display: flex; align-items: center; gap: 4px; }
.sched-toggle .mud-switch { margin: 0; }
.sched-on { font-size: 12.5px; color: var(--text); }
.sched-off { font-size: 12.5px; color: var(--text-muted); }

.sched-link-name { font-size: 13px; font-weight: 600; color: var(--text); text-decoration: none; }
.sched-link-name:hover { color: var(--accent); text-decoration: underline; }

.sched-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.sched-ok { font-size: 12.5px; color: var(--text); }
.sched-fail { font-size: 12.5px; color: #dc2626; }

.sched-pill {
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--hover);
    border-radius: 10px;
    padding: 2px 8px;
}

/* The dialog's fields, stacked with the same rhythm as a settings page. */
.sched-form { display: flex; flex-direction: column; min-width: 380px; }

/* Two fields side by side on a settings form, stacking on a narrow window. */
.form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 4px 16px;
}
@media (max-width: 700px) { .form-grid { grid-template-columns: 1fr; } }

/* A plain select sitting in a grid toolbar, sized to match the outline buttons beside it. */
.toolbar-select {
    /* Height comes from the shared toolbar rule near the top, so the row stays one size. */
    /* The arrow is drawn here rather than by the browser. A native select pins its own arrow a fixed
       distance from the border and ignores padding-right, so there was no way to give it breathing room
       without taking it over. background-position sets the gap; padding-right keeps text off the arrow. */
    appearance: none;
    -webkit-appearance: none;
    padding: 0 30px 0 10px;
    font: inherit;
    font-size: 13px;
    color: var(--text);
    background-color: var(--surface);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23888' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    border: 1px solid var(--btn-border);
    border-radius: 4px;
    flex-shrink: 0;
}

/* The run picker sits with the page actions; sized to match the outline buttons beside it. */
.sec-runs {
    height: 32px;
    padding: 0 8px;
    font: inherit;
    font-size: 13px;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 4px;
    margin-right: 8px;
}

.sec-cat { cursor: pointer; }
.sec-cat:hover { background: var(--hover); }
.sec-cat.on { background: color-mix(in srgb, var(--accent) 8%, transparent); box-shadow: inset 0 -2px 0 var(--accent); }
.sec-cat-name { display: flex; justify-content: space-between; gap: 8px; font-size: 13px; font-weight: 600; margin-bottom: 6px; }
.sec-cat-name span { font-weight: 500; font-size: 12px; white-space: nowrap; }
.sec-cat.ok .sec-cat-name span { color: #16a34a; }
.sec-cat.warn .sec-cat-name span { color: #d97706; }
.sec-cat.fail .sec-cat-name span { color: #dc2626; }
.sec-cat-bar { display: flex; gap: 2px; height: 8px; border-radius: 4px; overflow: hidden; background: var(--hover); }
.sec-cat-bar span { display: block; height: 100%; }
.sec-cat-bar .p { background: #16a34a; }
.sec-cat-bar .w { background: #d97706; }
.sec-cat-bar .f { background: #dc2626; }
.sec-cat-sub { font-size: 12px; color: var(--text-muted); margin-top: 6px; }

.sec-sev, .sec-status { font-weight: 600; }
.sec-sev.critical { color: #dc2626; }
.sec-sev.high { color: #ea580c; }
.sec-sev.medium { color: #d97706; }
.sec-sev.info { color: var(--accent); }
.sec-status.pass { color: #16a34a; }
.sec-status.fail { color: #dc2626; }
.sec-status.warning { color: #d97706; }
.sec-rec { color: var(--text-muted); white-space: normal; }

/* The expanded row: the objects behind the count as a wrapped list of chips, and a link to the report that
   lists them all where one exists. */
.sec-detail { padding: 10px 14px 12px 48px; font-size: 13px; }
.sec-detail-list { display: flex; flex-wrap: wrap; gap: 6px; }
.sec-detail-item {
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--hover);
    border: 1px solid var(--border);
    color: var(--text);
    font-size: 12.5px;
}
/* The objects behind a finding, as a small table: name, where it sits, enabled or not, and the one fact the
   check is about. Columns with nothing in them for this check are left out. */
.sec-objs { border-collapse: collapse; width: 100%; max-width: 1100px; font-size: 12.5px; }
.sec-objs th {
    text-align: left; font-weight: 600; font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
    color: var(--text-muted); padding: 4px 14px 5px 0; border-bottom: 1px solid var(--border);
}
.sec-objs td { padding: 4px 14px 4px 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.sec-objs tr:last-child td { border-bottom: 0; }
.sec-objs-name { font-weight: 600; white-space: nowrap; }
.sec-objs-loc { color: var(--text-muted); }
.sec-objs-off { color: var(--text-muted); }
.sec-detail-none { color: var(--text-muted); display: inline-block; margin-top: 6px; }
.sec-detail-link { display: inline-block; margin-top: 8px; margin-left: 12px; color: var(--accent); font-size: 12.5px; text-decoration: none; }
.sec-detail-link:hover { text-decoration: underline; }

.layout.dark .sec-sev.critical, .layout.dark .sec-status.fail { color: #f87171; }
.layout.dark .sec-sev.high { color: #fb923c; }
.layout.dark .sec-sev.medium, .layout.dark .sec-status.warning, .layout.dark .sec-cat.warn .sec-cat-state { color: #fbbf24; }
.layout.dark .sec-status.pass, .layout.dark .sec-cat.ok .sec-cat-state { color: #4ade80; }

/* ---- Tools ---- */
/* Target on the left, what you can do to it on the right. The action list stays put while the selection
   changes, so an admin learns where a button lives rather than hunting a toolbar that shifts with context. */
.tool-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 260px;
    gap: 16px;
    align-items: stretch;
    /* Fills the page so the grid can scroll internally rather than the whole page growing. */
    height: calc(100vh - 150px);
}

@media (max-width: 900px) {
    .tool-layout { grid-template-columns: minmax(0, 1fr); height: auto; }
}

/* Page-level actions: things that don't act on the selected row. Kept in their own bar above the grid's
   search, so "New user" can't read as something that applies to the highlighted account. Flat buttons with
   accent icons rather than button chrome — six bordered buttons in a row would out-shout the grid. */
.tool-actionbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
}

.tool-actionbar-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 10px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

/* Export and refresh sit in the same bar as the page actions but at the far edge: they act on the view
   rather than on the directory, and the gap is what says so. */
.tool-actionbar-right { display: flex; align-items: center; gap: 6px; margin-left: auto; }

/* The same three grid controls, on a page with no action bar to put them in - Computers links to no bulk
   pages, so it has one toolbar rather than two. */
.grid-toolbar .toolbar-right { display: flex; align-items: center; gap: 6px; margin-left: auto; }

/* ---- Group membership dialog ----
   One account or forty, the same dialog. Global rather than scoped: see the note on the sidebar's styles
   above, and CLAUDE.md on what an assembly rename does to a scoped bundle. */

.grpdlg { display: flex; flex-direction: column; }

.grpdlg-head {
    margin: 14px 0 6px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.grpdlg-head:first-child { margin-top: 0; }

.grpdlg-state { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--text-muted); }

.grpdlg-empty {
    padding: 10px 12px;
    border: 1px dashed var(--border);
    border-radius: 5px;
    font-size: 12.5px;
    color: var(--text-muted);
}

/* Current membership as chips: it is a set to glance at, not a list to work down - the rows below are
   where the working happens. */
.grpdlg-current { display: flex; flex-wrap: wrap; gap: 6px; max-height: 116px; overflow-y: auto; }

.grpdlg-chip {
    padding: 2px 8px;
    border: 1px solid var(--border);
    border-radius: 11px;
    background: var(--surface);
    font-size: 12px;
    color: var(--text);
}

.grpdlg-list {
    margin-top: 8px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--surface);
    max-height: 220px;
    overflow-y: auto;
}

.grpdlg-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 5px 6px 5px 12px;
    border-top: 1px solid var(--border);
}

.grpdlg-row:first-child { border-top: 0; }

.grpdlg-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Add and Remove as two states of one control, so the direction is visible per row without a legend. */
.grpdlg-seg {
    display: inline-flex;
    flex: 0 0 auto;
    border: 1px solid var(--border);
    border-radius: 5px;
    overflow: hidden;
    background: var(--surface);
}

.grpdlg-seg button {
    border: 0;
    background: transparent;
    font: inherit;
    font-size: 12px;
    padding: 3px 10px;
    color: var(--text-muted);
    cursor: pointer;
}

.grpdlg-seg button + button { border-left: 1px solid var(--border); }
.grpdlg-seg button.on { background: var(--accent); color: #fff; }

/* Removing is the destructive half, and the one worth being sure about before Apply. */
.grpdlg-seg button.on.rm { background: #b91c1c; }

.grpdlg-summary { margin-top: 12px; font-size: 12.5px; color: var(--text-muted); }

.tool-actionbar-btn:hover { background: var(--hover); }
.tool-actionbar-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tool-actionbar-btn .mud-icon-root { font-size: 17px; color: var(--accent); }

/* The grid's own bar: search on the left with the count beside it, export and refresh on the right.
   No side padding — the grid under it sits flush to the page edge, so an inset toolbar left the search box
   starting somewhere the first column does not. */
.grid-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 0 20px;
    /* The toolbar must not stretch when the grid below it takes the remaining height. */
    flex: 0 0 auto;
}

.grid-toolbar .toolbar-left {
    display: flex;
    align-items: center;
    gap: 8px;
    /* Shrink-only, not 1 1 auto: growing would push the count away from the search box it belongs to. */
    flex: 0 1 auto;
    min-width: 0;
}

.grid-toolbar .toolbar-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.grid-toolbar .count-text {
    flex: 0 0 auto;
    font-size: 13px;
    white-space: nowrap;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

.usr-search {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 30px;
    box-sizing: border-box;
    flex: 0 1 520px;
    min-width: 280px;
    padding: 0 12px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
}

.usr-search:focus-within { border-color: var(--accent); }
.usr-search-icon { color: var(--text-muted); font-size: 17px; }

.usr-search-input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    font: inherit;
    font-size: 13px;
    color: var(--text);
}

/* OU scope and Filter, beside the search box. Outlined until they are actually narrowing the list, then
   filled — a short list with no visible reason is the thing this prevents. */
.usr-scope-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 11px;
    border: 1px solid var(--btn-border);
    border-radius: 4px;
    background: var(--surface);
    font: inherit;
    font-size: 12.5px;
    color: var(--text);
    cursor: pointer;
    white-space: nowrap;
}

.usr-scope-btn:hover { background: var(--hover); }
.usr-scope-btn .mud-icon-root { font-size: 1.05rem; color: var(--text-muted); }

.usr-scope-btn.on {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--accent);
}

.usr-scope-btn.on .mud-icon-root { color: var(--accent); }

.usr-search-btn {
    height: 30px;
    box-sizing: border-box;
    border: none;
    border-radius: 4px;
    padding: 0 20px;
    background: var(--accent);
    color: #fff;
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

.usr-search-btn:hover { filter: brightness(0.95); }
.usr-search-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The rail is one panel of stacked sections, each with its own header — Actions, then Account info, the way
   the desktop toolkit stacks them. */
.tool-rail {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    align-self: stretch;
}

/* A quiet label, not a banner. Two solid accent bars on a page whose content is a grid of text pulled the
   eye to the headings rather than to what sits under them — and the rail is the least important column on
   the page. Pale tint, accent text, a rule beneath to keep the section boundary. */
.tool-rail-head {
    padding: 7px 14px;
    background: color-mix(in srgb, var(--accent) 8%, transparent);
    border-bottom: 1px solid var(--border);
    color: var(--text);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.tool-rail-body { padding: 10px 12px 14px; }

.tool-rail-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 18px 0 10px;
    color: var(--text-muted);
    text-align: center;
}

.tool-rail-empty p { margin: 0; font-size: 12.5px; }

.tool-info-name {
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
    overflow-wrap: anywhere;
    margin-bottom: 5px;
}

.tool-info-badge {
    display: inline-block;
    margin-bottom: 12px;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 600;
}

.tool-info-badge.on { background: #dcfce7; color: #059669; }
.tool-info-badge.off { background: #fee2e2; color: #dc2626; }
.layout.dark .tool-info-badge.on { background: #14311f; color: #4ade80; }
.layout.dark .tool-info-badge.off { background: #3a1d1d; color: #f87171; }

/* What is WRONG with the account, at the top. A healthy one shows a single green chip and nothing else —
   a row of green "OK" chips trains people to stop reading the line that matters. */
.tool-chips { display: flex; flex-wrap: wrap; gap: 5px; margin: 8px 0 4px; }

.tool-chip {
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
}

.tool-chip.good { background: color-mix(in srgb, #16a34a 14%, transparent); color: #15803d; }
.tool-chip.warn { background: color-mix(in srgb, #f59e0b 22%, transparent); color: #b45309; }
.tool-chip.bad { background: color-mix(in srgb, #dc2626 14%, transparent); color: #b91c1c; }

/* A group heading: it separates, it does not announce. The rule above it does most of the work. */
.tool-info-head {
    margin: 14px 0 5px;
    padding-top: 11px;
    border-top: 1px solid var(--border);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.tool-rail-body > .tool-info-head:first-of-type { margin-top: 10px; }

/* Label left, value right. Stacked label-over-value pairs read as one block of text; a fixed label column
   gives the eye a line to run down. The column is narrow, so the label is short and the value wraps under
   itself rather than being pushed to a far edge. */
.tool-info-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 2px 0;
}

.tool-info-label {
    flex: 0 0 72px;
    font-size: 11.5px;
    color: var(--text-muted);
}

.tool-info-value {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 12.5px;
    color: var(--text);
    overflow-wrap: anywhere;
    margin-bottom: 9px;
    overflow-wrap: anywhere;
}

.tool-action {
    display: flex;
    align-items: center;
    /* Explicit rather than relying on the default: with no justification set, any future change to the row's
       content (a trailing badge, say) would re-centre the pair. */
    justify-content: flex-start;
    gap: 10px;
    width: 100%;
    padding: 7px 8px;
    border: none;
    border-radius: 5px;
    background: none;
    text-align: left;
    font: inherit;
    font-size: 13px;
    color: var(--text);
    cursor: pointer;
}

/* Fixed icon column. Without this the icon is free to shrink, and since the glyphs are not all the same
   width the labels start at slightly different x positions down the list — which reads as a ragged left
   edge rather than a list. */
.tool-action .mud-icon-root {
    flex: 0 0 20px;
    width: 20px;
    font-size: 18px;
    color: var(--accent);
}

.tool-action > span { flex: 1 1 auto; min-width: 0; text-align: left; }

.tool-action:hover:not(:disabled) { background: var(--hover); color: var(--accent); }
.tool-action:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Disabled until an account is selected — greyed rather than hidden, so the page shows what it can do. */
.tool-action:disabled { color: var(--text-muted); cursor: default; opacity: .55; }

/* ---- Bulk attribute panel ----
   A tick and a value per attribute. The tick is what separates "set this to nothing" from "this was not
   part of what I asked for" - without it, an empty box is ambiguous and the ambiguous reading is the one
   that empties a field on forty accounts. */

.bulkattr-rows { display: flex; flex-direction: column; }

.bulkattr-row {
    display: grid;
    grid-template-columns: minmax(0, 150px) minmax(0, 1fr);
    gap: 10px;
    align-items: center;
    padding: 5px 0;
    border-bottom: 1px solid var(--line, var(--border));
}

.bulkattr-row:last-child { border-bottom: 0; }

/* Ticked rows carry a tint, so what the run will touch reads as a set rather than as scattered ticks. */
.bulkattr-row.on { background: color-mix(in srgb, var(--accent) 5%, transparent); }

.bulkattr-tick {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    font-size: 12.5px;
    color: var(--text);
    cursor: pointer;
}

.bulkattr-tick input { margin: 0; cursor: pointer; }
.bulkattr-tick span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.bulkattr-input { width: 100%; }

.bulkattr-off { font-size: 12px; color: var(--text-muted); }

/* Amber, not red: clearing is a legitimate thing to want, and colouring it as an error would train people
   to ignore the one warning on the panel. */
.bulkattr-warn {
    margin-top: 14px;
    padding: 9px 12px;
    border-radius: 6px;
    border: 1px solid #fde68a;
    background: #fffbeb;
    color: #92400e;
}

.layout.dark .bulkattr-warn {
    border-color: rgba(251, 191, 36, .35);
    background: rgba(251, 191, 36, .12);
    color: #fbbf24;
}

/* ---- A rail of grouped actions and nothing else ----
   The headings run edge to edge inside the rail, so a group reads as a band rather than as a line of small
   capitals floating above some buttons. The body's own padding would inset them, hence the reset here and
   the padding moved onto the buttons. */
.tool-rail-actions { padding: 0 0 8px; }
.tool-rail-actions .tool-rail-head { margin-top: 0; border-top: 1px solid var(--border); }
.tool-rail-actions .tool-rail-head:first-child { border-top: 0; }
.tool-rail-actions .tool-action { padding-left: 14px; padding-right: 14px; border-radius: 0; }

/* Delete is the one action that cannot be undone, and it now sits alone under a heading that says so. The
   colour is on the word as well as the icon: a red icon beside black text reads as decoration. */
.tool-rail-head.danger { color: #b91c1c; background: color-mix(in srgb, #b91c1c 7%, transparent); }
.layout.dark .tool-rail-head.danger { color: #f87171; background: color-mix(in srgb, #f87171 10%, transparent); }

.tool-action.danger { color: #b91c1c; }
.tool-action.danger .mud-icon-root { color: #b91c1c; }
.tool-action.danger:hover:not(:disabled) { background: color-mix(in srgb, #b91c1c 8%, transparent); color: #b91c1c; }
.layout.dark .tool-action.danger,
.layout.dark .tool-action.danger .mud-icon-root { color: #f87171; }

/* ---- Domain facts ---- */
.dash-facts { margin: 0; display: flex; flex-direction: column; }

/* Label at the left edge, value at the right, hairline between.

   Not a fixed label column: at 165px the value began a third of the way across and the remaining half of a
   wide card was dead space, so the pair read as two columns crammed together with nothing to the right of
   them. Pushed apart, the values still line up — on the card's right edge instead of an arbitrary offset —
   and the rule gives the eye something to track across the gap. */
.dash-fact {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 24px;
    padding: 7px 0;
    border-bottom: 1px solid var(--border);
}

/* The card's own edge closes the list. */
.dash-fact:last-child { border-bottom: none; }

/* Full text colour, matching the breakdown cards' labels: a label is what the reader scans for, and greying
   it beside a black value made the two halves of one fact read as unequal. */
.dash-fact dt {
    font-size: 13px;
    color: var(--text);
    /* Wraps rather than truncating: a clipped policy name is worse than a two-line one. */
    overflow-wrap: anywhere;
}

.dash-fact dd {
    margin: 0;
    font-size: 13px;
    color: var(--text);
    text-align: right;
    overflow-wrap: anywhere;
    font-variant-numeric: tabular-nums;
}

/* A policy setting that weakens security — the value carries the warning tone, the label still says what it
   is, so colour is never the only signal. */
.dash-fact-flag dd { color: #b45309; font-weight: 600; }
.layout.dark .dash-fact-flag dd { color: #fbbf24; }

/* ---- Fine-grained password policies ---- */
.pso-list { display: flex; flex-direction: column; gap: 8px; }

.pso {
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--app-bg);
}

.pso-head { display: flex; align-items: baseline; gap: 10px; }
.pso-name { font-size: 12.5px; font-weight: 600; color: var(--text); }

.pso-precedence {
    margin-left: auto;
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.pso-detail { margin-top: 2px; font-size: 11.5px; color: var(--text-muted); }
.pso-applies { margin-top: 3px; font-size: 11.5px; color: var(--text); overflow-wrap: anywhere; }

/* A policy applied to nobody does nothing — usually a half-finished change rather than a decision. */
.pso-applies-none { color: #b45309; }
.layout.dark .pso-applies-none { color: #fbbf24; }

/* ---- Computers dashboard ---- */
.kpis {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 14px;
}

@media (max-width: 900px) { .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .kpis { grid-template-columns: 1fr; } }

.kpi {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 14px 16px;
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    font: inherit;
    transition: border-color .12s, background .12s;
}

.kpi:hover { border-color: var(--accent); background: var(--hover); }
.kpi:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Same eyebrow-then-headline treatment as the overview's object cards, so a KPI strip and a breakdown card
   read as the same kind of thing rather than two house styles on adjacent rows. */
.kpi-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--text-muted);
    line-height: 1.4;
}

.kpi-value {
    font-size: 34px;
    font-weight: 650;
    letter-spacing: -.025em;
    color: var(--text);
    line-height: 1.1;
    margin-top: 6px;
    font-variant-numeric: tabular-nums;
}

/* Sits at the end of a card header, opposite the label — a count or a denominator, not a heading. */
.dash-card-sub { margin-left: auto; font-size: 11.5px; font-weight: 400; color: var(--text-muted); }

/* A single-value coverage meter: one fill against a neutral track. Not a two-series chart — the remainder is
   the absence of the thing being measured, not a second category. */
.meter {
    height: 8px;
    border-radius: 4px;
    background: var(--hover);
    overflow: hidden;
    margin-bottom: 8px;
}

.meter-fill { display: block; height: 100%; background: var(--accent); border-radius: 4px; }

/* ---- Computers by OS ----
   A grid, not flex rows. Flex sizes each row on its own, so a content-width label would start every bar at a
   different x; a grid sizes each COLUMN across all rows, so the label column grows to the longest OS name and
   every bar still shares a baseline. Nothing is set to 1fr, so the leftover width of a wide card simply stays
   unused at the right rather than being absorbed by the label or stretching the bar. */
/* The bar track flexes so the chart fills its card instead of stopping short and leaving dead space, but the
   whole plot is capped — past about 900px a longer bar compares values no better, it just looks stretched.
   The same rules serve the narrow slot on the Overview and the full-width card on the Computers page. */
.os-rows {
    display: grid;
    grid-template-columns: minmax(0, max-content) minmax(120px, 1fr) max-content;
    column-gap: 14px;
    max-width: 900px;
}

/* subgrid lets each row stay a real button — with its own hover and focus ring — while still taking its
   columns from the parent grid. */
.os-row {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
    align-items: center;
    padding: 6px 2px;
    background: transparent;
    border: none;
    border-radius: 3px;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.os-row:hover { background: var(--hover); }
.os-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Sized by the column, so it fits the longest name. The ellipsis only bites if a name is long enough to
   push the whole chart past the card. */
.os-label {
    min-width: 0;
    font-size: 12.5px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.os-track {
    height: 10px;
    background: var(--hover);
    border-radius: 3px;
    overflow: hidden;
}

/* One hue: length carries the magnitude, so colour has nothing left to encode. */
.os-bar { display: block; height: 100%; background: var(--accent); border-radius: 3px; }

.os-value {
    text-align: right;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* ---- Stale accounts ---- */
.stale-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

/* A segmented control rather than a dropdown: five options, and the current one should be readable without
   opening anything. */
.seg { display: inline-flex; flex-shrink: 0; border: 1px solid var(--mud-palette-lines-inputs, #cbd5e1); border-radius: 4px; overflow: hidden; }
.seg-btn { white-space: nowrap; }

.seg-btn {
    padding: 4px 11px;
    font: inherit;
    font-size: 12px;
    color: var(--text-muted);
    background: var(--surface);
    border: none;
    border-right: 1px solid var(--mud-palette-lines-inputs, #cbd5e1);
    cursor: pointer;
}

.seg-btn:last-child { border-right: none; }
.seg-btn:hover { background: var(--hover); color: var(--text); }
.seg-btn.on { background: var(--accent); color: #ffffff; font-weight: 600; }
.seg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.stale-hint { font-size: 11.5px; color: var(--text-muted); }

/* Four across on a wide screen, so eight tiles are two tidy rows rather than a ragged wrap. */
.stale-tiles {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 10px;
    margin-bottom: 14px;
}

@media (max-width: 1000px) { .stale-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .stale-tiles { grid-template-columns: 1fr; } }

.stale-tile {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 14px 16px;
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    font: inherit;
    transition: border-color .12s, background .12s;
}

.stale-tile:hover { background: var(--hover); }
.stale-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.stale-tile.on {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

/* Eyebrow then headline, the same treatment as the overview cards and the computers KPIs. Wraps rather than
   truncating: these labels are the longest of the three ("Computers never logged on"), and a tile that reads
   "Computers never logg…" is worse than a two-line one. */
.stale-tile-label {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--text-muted);
    line-height: 1.4;
}

.stale-tile-value {
    margin-top: 6px;
    font-size: 30px;
    font-weight: 650;
    letter-spacing: -.025em;
    color: var(--text);
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

/* Marks the tiles the inactivity window actually moves. */
.stale-clock { font-size: 11px; color: var(--accent); }

/* ---- Topology ---- */
/* The diagram sets its own min-width, so wide forests scroll inside this box rather than making the page
   scroll sideways. */
.topo-canvas {
    overflow-x: auto;
    padding: 8px 0 4px;
}

/* Natural size, shrinking only when the container is narrower. width:100% would scale a small forest's
   diagram up to fill a wide screen, which blows the 11px labels up to headline size. */
.topo-canvas svg {
    display: block;
    max-width: 100%;
    height: auto;
    color: var(--text);
}

.topo-legend {
    display: flex;
    align-items: baseline;
    gap: 16px;
    flex-wrap: wrap;
    padding-top: 10px;
    border-top: 1px solid var(--border);
    font-size: 11.5px;
    color: var(--text-muted);
}

.topo-legend > span { display: inline-flex; align-items: baseline; gap: 6px; }
.topo-note { margin-left: auto; }

.topo-swatch {
    width: 9px;
    height: 9px;
    border-radius: 2px;
    display: inline-block;
}

.topo-swatch-role { background: var(--accent); }

/* Replication map: a line rather than a block, since that's what the swatch stands for. */
.topo-swatch-inter, .topo-swatch-intra { height: 2px; width: 18px; border-radius: 0; align-self: center; }
.topo-swatch-inter { background: var(--accent); }
.topo-swatch-intra { background: var(--text); opacity: .35; }

/* ---- Dashboard ---- */
.dash-loading {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--text-muted);
    font-size: 13px;
    padding: 8px 0;
}

/* auto-fit so the cards fill a wide screen and reflow rather than shrinking to unreadable widths.
   Grid items stretch by default, which is what keeps every card in a row the same height — the object types
   have different numbers of rows, and ragged card bottoms read as a layout fault rather than as data. */

/* A fixed track count rather than auto-fit, because a card can now span two columns — auto-fit resolves its
   track count from the container width alone, and a spanning item can then end up wider than the grid.
   Five tracks at the top end so the five object cards sit on one row, as they did before spanning existed. */
.dash-grid {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 16px;
    margin-bottom: 16px;
}

@media (max-width: 1500px) { .dash-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 1200px) { .dash-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 950px) { .dash-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px) { .dash-grid { grid-template-columns: 1fr; } }

/* Each slot is a grid item; the card inside fills it, so cards in a row stay the same height. */
.dash-slot { display: flex; }
.dash-slot > * { flex: 1; min-width: 0; }

/* Charts and long label/value lists read badly at count-list width. */
.dash-slot-wide { grid-column: span 2; }

@media (max-width: 700px) { .dash-slot-wide { grid-column: span 1; } }

.dash-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 16px 18px;
}

.dash-card-head {
    display: flex;
    align-items: center;
    gap: 11px;
    padding-bottom: 12px;
}

/* Wraps rather than truncating. A card title is the one string on the card that has to be readable, and
   "Recently created users" ellipsised to "Recently creat…" in a single-column slot — a two-line heading
   costs a few pixels, a clipped one costs the meaning. */
.dash-card-label {
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
    line-height: 1.3;
}

/* For titles short enough to fit their column: keeps the header one line tall so the card's first row starts
   at the same height as its neighbours'. Only use it where the title genuinely fits — the wrap above is the
   safe default precisely because a clipped title is worse than a tall one. */
.dash-card-label-tight { white-space: nowrap; }

/* Tinted rather than grey: the tile identifies the card at a glance when five of them are on screen. */
.dash-card-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex-shrink: 0;
    border-radius: 8px;
    background: color-mix(in srgb, var(--accent) 13%, transparent);
    color: var(--accent);
}

/* ---- Object breakdown cards ---- */
/* One large figure per card, everything else deliberately small. Five cards of uniformly sized rows gave the
   eye no entry point; the size difference is the hierarchy, so don't level it back out. The head/rows styles
   above are still used by the cards that are genuinely lists (OS spread, domain facts, password policy). */

.dash-eyebrow {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--text-muted);
    /* Long names ("Organizational units") wrap rather than truncate — the eyebrow is the card's only title. */
    line-height: 1.4;
}

/* A button, not a heading: the total is the card's primary link, to the unfiltered report. */
.dash-hero {
    display: block;
    width: 100%;
    margin: 12px 0 14px;
    padding: 0;
    border: none;
    background: none;
    text-align: left;
    cursor: pointer;
    font: inherit;
    font-size: 34px;
    font-weight: 650;
    letter-spacing: -.025em;
    line-height: 1.1;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

.dash-hero:hover { color: var(--accent); }
.dash-hero:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* No hairline per row here. At 12px with only a handful of rows the rules added more texture than tracking
   help — the single rule under the hero is enough to separate headline from detail. */
.dash-subrows { border-top: 1px solid var(--border); padding-top: 4px; }

.dash-subrow {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    padding: 4px 2px;
    border: none;
    background: none;
    text-align: left;
    cursor: pointer;
    font: inherit;
    font-size: 12px;
}

.dash-subrow:hover { background: var(--hover); }
.dash-subrow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Label and figure both at full text colour, matching the row cards on the other dashboards. The label was
   muted on the reasoning that the number is what gets compared down the column - but these labels are what
   the reader is looking FOR, and a grey label beside a black number reads as though the label is the less
   reliable half. Hover still turns it accent, so the row's affordance is unchanged. */
.dash-subrow-label {
    min-width: 0;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dash-subrow:hover .dash-subrow-label { color: var(--accent); }

.dash-subrow-value { color: var(--text); font-variant-numeric: tabular-nums; }

.dash-rows { display: flex; flex-direction: column; border-top: 1px solid var(--border); }

/* A hairline under every row. With eight rows of label-and-number the eye needs a rule to track along,
   especially where the two columns are far apart on a wide card. */
.dash-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
    width: 100%;
    padding: 7px 2px;
    text-align: left;
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--border);
    cursor: pointer;
    font: inherit;
    color: inherit;
}

/* The card's own edge closes the list, so the last row doesn't need one. */
.dash-row:last-child { border-bottom: none; }

.dash-row:hover { background: var(--hover); }
.dash-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Text colour, not accent. Every row is a link, but colouring all of them blue turns five cards into a wall
   of link text with nothing standing out — the row hover and the pointer carry the affordance instead. */
.dash-row-label {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dash-row:hover .dash-row-label { color: var(--accent); }

/* One colour and one weight for every figure: the eye can then run down the column comparing magnitudes
   without weight or hue implying an importance the number doesn't have. Tabular so they line up. */
.dash-row-value {
    font-size: 13px;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* Total is the card's headline, and the only figure that is emphasised. */
.dash-row-total .dash-row-value { font-size: 15px; font-weight: 600; }

/* Status marks, used ONLY by the "Needs attention" cards — those are lists of findings, where severity is
   the point. The object-breakdown cards deliberately have none: they are a census, and marking some rows as
   problems made every card compete for attention. */
.dash-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex-shrink: 0;
    align-self: center;
}

/* The dot alone carries status — the figure stays the same colour as every other figure, so the column
   still reads as one set of comparable numbers. */
.dash-row-warning .dash-dot { background: #b45309; }
.dash-row-critical .dash-dot { background: #b91c1c; }

/* Lightened on the dark ground, where the light-mode steps lose contrast. */
.layout.dark .dash-row-warning .dash-dot { background: #fbbf24; }
.layout.dark .dash-row-critical .dash-dot { background: #f87171; }

/* ---- OU contents ---- */
/* Wraps rather than scrolls: four figures should stay visible together, since the comparison between them is
   the point (an OU with 400 users and 0 computers reads differently from the reverse). */
.ou-counts { display: flex; flex-wrap: wrap; gap: 10px; }

.ou-count {
    flex: 1 1 90px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--app-bg);
    font-size: 11.5px;
    color: var(--text-muted);
}

.ou-count-n {
    font-size: 17px;
    font-weight: 600;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* ---- Group members ---- */
/* Scrolls inside the card so a 200-row list can't push the rest of the panel out of reach. */
.member-list {
    display: flex;
    flex-direction: column;
    max-height: 260px;
    overflow-y: auto;
    margin: 0 -6px;
}

.member-row {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 3px 6px;
    font-size: 12.5px;
    border-radius: 3px;
}

.member-row:hover { background: var(--hover); }
.member-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Fixed width so the names line up in a column rather than stepping in and out. */
.member-kind {
    flex: 0 0 62px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.member-kind-group { color: var(--accent); }
/* Unresolved and foreign members read as muted rather than alarming — both are normal states. */
.member-kind-foreign,
.member-kind-unknown { opacity: .7; }

.member-more {
    margin-top: 8px;
    font-size: 11.5px;
    color: var(--text-muted);
}

@media (max-width: 560px) {
    .detail-row { grid-template-columns: 1fr; gap: 1px; }
}

/* ---- Logon hours ---- */
.hours-title { font-size: 12px; color: var(--text-muted); margin-bottom: 8px; }
.hours-grid { display: flex; flex-direction: column; gap: 2px; overflow-x: auto; }

.hours-scale,
.hours-day {
    display: grid;
    grid-template-columns: 34px repeat(24, 1fr);
    gap: 2px;
    align-items: center;
    min-width: 380px;
}

.hours-tick { font-size: 9px; color: var(--text-muted); text-align: left; }
.hours-label { font-size: 11px; color: var(--text-muted); }

.hours-cell {
    height: 13px;
    border-radius: 2px;
    background: var(--hover);
    border: 1px solid var(--border);
}

.hours-cell.on { background: var(--accent); border-color: var(--accent); }

.hours-legend {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 10px;
    font-size: 11px;
    color: var(--text-muted);
}

.hours-legend > span { display: inline-flex; align-items: center; gap: 5px; }
.hours-legend .hours-cell { width: 11px; height: 11px; }
.hours-note { margin-left: auto; }

/* ---- OU picker ----
   Drawn to read like ADUC rather than a Material list: ~25px rows, 13px text, small square checkboxes, and
   an amber folder for an OU against a grey one for a container. Density is the point — an admin scans this
   tree, and Mud's 40px touch targets show a third as many rows in the same dialog.

   --ou-folder is defined on both :root and .layout for the same reason the other tokens are: this renders
   inside a Mud dialog, which is outside the layout div. */
:root { --ou-folder: #b5822e; }
.layout.dark { --ou-folder: #d9a441; }
body:has(.layout.dark) { --ou-folder: #d9a441; }

/* Resizable from the bottom-right corner, like the desktop toolkit's browser — a deep OU path runs out of
   room fast at a fixed width.

   The handle goes on the DIALOG, not on the tree inside it: the browser draws its grip at the corner of
   whatever carries `resize`, and on the inner element that landed in the middle of the footer text, above
   the buttons, looking like a stray box. On the dialog it sits where a resize grip belongs. Everything
   between then has to stretch, which is what the flex rules below are for. */
.mud-dialog:has(.ou-picker) {
    display: flex;
    flex-direction: column;
    width: 500px;
    height: 560px;
    min-width: 400px;
    min-height: 360px;
    max-width: 95vw !important;
    max-height: 88vh;
    /* resize only applies when overflow is not visible. */
    overflow: hidden;
    resize: both;
}

.mud-dialog:has(.ou-picker) .mud-dialog-content {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    display: flex;
}

/* Room for the grip, so it doesn't sit against the Apply button. */
.mud-dialog:has(.ou-picker) .mud-dialog-actions { padding-right: 22px; }

.ou-picker {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    min-height: 0;
}

.ou-picker-state {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 18px 12px;
    color: var(--text-muted);
    font-size: 13px;
}

.ou-search { margin-bottom: 10px; }

/* Takes whatever height the picker has, so dragging the corner grows the tree rather than the padding.
   min-height: 0 is what lets a flex child actually shrink; without it the tree refuses to get smaller than
   its content and the dialog can only grow. */
.ou-tree {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
    padding: 3px 0;
}

.ou-row {
    display: flex;
    align-items: center;
    gap: 7px;
    height: 25px;
    padding-right: 10px;
    font-size: 13px;
    color: var(--text);
    user-select: none;
}

.ou-row:hover { background: var(--hover); }
.ou-row-on { background: color-mix(in srgb, var(--accent) 13%, transparent); }
.ou-row-on .ou-name { font-weight: 600; }

/* The chevron is its own hit target: clicking it expands, clicking anywhere else ticks the row. Without the
   split, opening a branch to look inside would also add it to the filter. */
.ou-chev {
    width: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--text-muted);
    cursor: pointer;
}

.ou-chev-none { cursor: default; }

.ou-hit {
    display: flex;
    align-items: center;
    gap: 7px;
    flex: 1;
    min-width: 0;
    cursor: pointer;
}

.ou-cb {
    width: 13px;
    height: 13px;
    border: 1.5px solid var(--mud-palette-lines-inputs, #cbd5e1);
    border-radius: 2.5px;
    background: var(--surface);
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.ou-cb-on {
    background: var(--accent);
    border-color: var(--accent);
    color: #ffffff;
}

.ou-ico { flex-shrink: 0; }
.ou-ico-ou { color: var(--ou-folder); }
.ou-ico-container { color: var(--text-muted); opacity: .75; }
.ou-ico-domain { color: #5b7fa6; }

.ou-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The root is a DN, not a name — monospaced so the DC= components stay readable at 12px. */
.ou-name-dn {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 11.5px;
    color: var(--text-muted);
}

.ou-spin {
    width: 9px;
    height: 9px;
    border: 1.5px solid var(--border);
    border-top-color: var(--text-muted);
    border-radius: 50%;
    display: inline-block;
    animation: ou-spin .7s linear infinite;
}

@keyframes ou-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .ou-spin { animation-duration: 2.4s; }
}

/* A search hit is two lines — name over location — so it needs more room than a tree row. */
.ou-row-result { height: auto; padding-top: 3px; padding-bottom: 3px; cursor: pointer; }
.ou-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }

.ou-path {
    font-size: 11px;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ou-picker-foot {
    flex: 0 0 auto;
    padding-top: 8px;
    font-size: 11.5px;
    color: var(--text-muted);
}

.ou-search { flex: 0 0 auto; }

.ou-empty {
    padding: 14px 12px;
    color: var(--text-muted);
    font-size: 13px;
}

.ou-btn { font-size: 13px; }

.ou-btn-apply {
    background-color: var(--accent);
    color: #ffffff;
}

.ou-btn-apply:hover {
    background-color: color-mix(in srgb, var(--accent) 85%, #000);
}

/* ---- User browser dialog ----
   The OU picker's tree, with a second pane beside it for the accounts in whichever OU is open. Both panes
   scroll independently and the dialog itself does not, so the footer count stays put while you work down a
   long OU. */

.ub-picker {
    display: flex;
    flex-direction: column;
    min-height: 0;
    height: 62vh;
}

.ub-panes {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: 12px;
    flex: 1 1 auto;
    min-height: 0;
}

.ub-pane {
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
}

.ub-pane-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    height: 24px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.ub-pane-head > span:first-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* "Include sub-OUs" changes what the pane lists, so it sits on the pane rather than in the toolbar. */
.ub-sub {
    display: flex;
    align-items: center;
    gap: 5px;
    flex-shrink: 0;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--text-muted);
    cursor: pointer;
}

.ub-sub input { margin: 0; cursor: pointer; }

.ub-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
    padding: 3px 0;
}

.ub-row {
    height: auto;
    padding: 3px 10px 3px 9px;
    cursor: pointer;
}

.ub-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1 1 auto;
    line-height: 1.3;
}

.ub-text .ou-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Disabled accounts are the ones people most often mean to act on, so the state is on the row rather than
   something to check afterwards. */
.ub-flag {
    flex-shrink: 0;
    font-size: 10.5px;
    padding: 1px 6px;
    border-radius: 9px;
    color: var(--text-muted);
    background: var(--hover);
}

.ub-filter {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 6px;
}

.ub-filter-box { flex: 1 1 auto; min-width: 0; }

.ub-all { flex-shrink: 0; }

.ub-empty {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 14px 12px;
    border: 1px dashed var(--border);
    border-radius: 4px;
    color: var(--text-muted);
    font-size: 13px;
}

.ub-note {
    flex: 0 0 auto;
    padding-top: 6px;
    font-size: 11.5px;
    color: var(--text-muted);
}

/* ---- Bulk actions, step 1 ----
   The search box on its own line, the other three sources grouped under it, and what they produced on a
   bar of its own. Five controls of four different shapes on one row wrapped onto two lines and lined up
   with nothing. */

/* Steps 1 and 2 as two columns. Each keeps its own margin-bottom, so the grid needs a column gap only.
   Below 1100px the columns would be too narrow for the search box and the action descriptions both, so
   they stack again - which is the layout this replaced, and still the right one on a narrow window. */
.act-setup {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 10px;
    /* Stretch, not start: two bands of different heights side by side read as one finished and one still
       loading. They are two halves of the same step, so they end on the same line. */
    align-items: stretch;
}

@media (max-width: 1100px) {
    .act-setup { grid-template-columns: minmax(0, 1fr); }
}

.act-search { max-width: 420px; }

.act-sources-label {
    margin-top: 14px;
    margin-bottom: 6px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.act-sources {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* The CSV import is a label wrapping a hidden file input, so it has to be told to look and size like the
   buttons beside it rather than inheriting the file control's own box. */
.act-import { cursor: pointer; }

/* The result of the step, under a rule so it does not read as one more control in the same list. */
.act-selected {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 16px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
    color: var(--text-muted);
}

.act-count {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.act-none { font-size: 13px; }

.act-from {
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.act-view { margin-left: 4px; }

.act-spacer { flex: 1 1 auto; }

.act-missed { margin-top: 8px; }

/* Scrolls rather than growing: two hundred accounts should not push the actions off the screen even when
   the list is open. */
.act-list-box {
    margin-top: 8px;
    max-height: 260px;
    overflow: auto;
}

/* The toolbar button, when a scope is set. Matches the active-filter treatment beside it. */

/* ---- Active toolbar state (OU scope set, or filter conditions applied) ----
   Solid accent with white text, in every state including :hover and :focus. Deliberately not accent-on-white:
   that has to fight Syncfusion's own hover and focus fills, and losing once leaves blue text on a dark grey
   button. Filled reads as "this is narrowing your results" at a glance and can't become unreadable. */
.grid-toolbar .e-btn.ou-active,
.grid-toolbar .e-btn.ou-active:hover,
.grid-toolbar .e-btn.ou-active:focus,
.grid-toolbar .e-btn.ou-active:active,
.grid-toolbar .e-btn.adv-filter-active,
.grid-toolbar .e-btn.adv-filter-active:hover,
.grid-toolbar .e-btn.adv-filter-active:focus,
.grid-toolbar .e-btn.adv-filter-active:active {
    border-color: var(--accent) !important;
    background-color: var(--accent) !important;
    color: #ffffff !important;
    font-weight: 500;
    box-shadow: none !important;
}

.grid-toolbar .e-btn.ou-active:hover,
.grid-toolbar .e-btn.adv-filter-active:hover {
    background-color: color-mix(in srgb, var(--accent) 86%, #000) !important;
    border-color: color-mix(in srgb, var(--accent) 86%, #000) !important;
}

.adv-filter-logic {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: .5rem;
}

.adv-filter-logic-label {
    font-weight: 600;
    color: var(--text-muted);
}

/* Wraps so a narrow dialog stacks the controls rather than overflowing them. */
.adv-filter-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .5rem;
    flex-wrap: wrap;
}

.adv-filter-empty {
    color: var(--text-muted);
    margin: .5rem 0;
}

/* Column option in the field autocomplete: name on the left, its section pushed to the right so a scan down
   the list reads as two columns rather than one run-on string. */
.adv-filter-option {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    width: 100%;
}

.adv-filter-option-group {
    font-size: 11px;
    color: var(--text-muted);
}

/* Sits next to the search box so it reads as the result count for what's typed there. white-space:nowrap
   stops "17,406 users" wrapping when the toolbar tightens. */
.grid-count {
    color: var(--text-muted);
    font-size: 13px;
    white-space: nowrap;
}

/* The grid supplies its own surface and borders; the Paper around it shouldn't add a second frame. */
.report-box .e-grid { border: none; }

/* ---- Form fields (Field.razor) ----
   The label is a sibling above the control, not MudBlazor's floating label, so the notch in the outlined
   border has nothing to sit in. The rules below close that notch for any input inside a .field — without
   them MudBlazor still reserves the gap and the border reads as broken. */
.field { margin-bottom: 1.25rem; }

.field-label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text);
}

.field-hint {
    margin-top: 6px;
    font-size: 12px;
    line-height: 1.4;
    color: var(--text-muted);
}

/* A settings page with no card around it. The card was doing the padding as well as the border, so with it
   gone the content needs its own breathing room at the top - the page title sits directly above. */
.page-plain { padding-top: 4px; }

/* ---- Plain form controls ----
   A label over a bare input, for forms that are the whole page rather than a few fields in a panel. Ported
   from the 365 toolkit so the two products' settings pages read the same, but drawn from this app's tokens
   rather than its hardcoded greys, so dark mode needs no second set of rules. */

.field-input {
    display: block;
    box-sizing: border-box;
    width: 100%;
    height: 38px;
    margin: 0;
    padding: 8px 12px;
    font: inherit;
    font-size: 13.5px;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    outline: none;
    transition: border-color .15s;
}

.field-input:hover:not(:focus) { border-color: var(--text-muted); }
.field-input:focus { border-color: var(--accent); }

/* The native arrow differs per browser and per platform; this one matches the pickers elsewhere. */
select.field-input {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 32px;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
}

/* align-items: flex-end so a labelled field and a bare button in the same row sit on one baseline. */
.field-row { display: flex; gap: 12px; align-items: flex-end; }
.field-grow { flex: 1 1 0; min-width: 0; }

.field-btn,
.field-btn-primary {
    height: 38px;
    padding: 0 16px;
    border-radius: 6px;
    font: inherit;
    font-size: 13.5px;
    font-weight: 500;
    cursor: pointer;
    transition: background .15s, opacity .15s;
}

.field-btn {
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
}

.field-btn:hover { background: var(--hover); }

.field-btn-primary {
    color: #fff;
    background: var(--accent);
    border: 1px solid var(--accent);
}

.field-btn-primary:hover { opacity: .9; }
.field-btn:disabled, .field-btn-primary:disabled { opacity: .5; cursor: not-allowed; }

/* ---- Email server page ----
   One form, no card: a panel drawn around the only thing on the page marks a boundary against nothing. */

.email-form { max-width: 560px; }

/* Separated by rules rather than by a card each: the test is a different job from the settings above it,
   and Save belongs to the form as a whole. */
.email-test {
    margin: 22px 0;
    padding: 16px 0;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}

/* The legend is what cuts the gap into the top border for a floating label. With the label moved out it
   would leave an empty notch, so keep it collapsed.

   Mud's own base legend is already width:0, but it re-opens the notch on .mud-shrink and :focus-within
   with a 4-class selector — so these have to match that structure to outweigh it. Do not simplify to
   `.field legend`: it loses and the border gaps open the moment the control takes focus.

   Vertical padding is deliberately NOT touched here. Mud's dense outlined padding (10.5px top and bottom)
   already centres the text once there is no floating label to make room for; overriding padding-top
   pushes the text onto the top edge of the box. */
.field .mud-input.mud-input-outlined > .mud-input-outlined-border legend,
.field .mud-input.mud-input-outlined.mud-shrink > .mud-input-outlined-border legend,
.field .mud-input.mud-input-outlined:focus-within > .mud-input-outlined-border legend {
    width: 0;
    padding: 0;
}

/* A disabled field still has to be readable — Mud's default disabled text is too faint against --surface. */
.field .mud-input.mud-disabled { opacity: 0.75; }

/* Checkboxes label themselves inline, so they don't go through Field. This just lines a run of them up
   with the fields above and keeps the text on the theme colour. */
.field-checks { margin-bottom: 1.25rem; }
.field-checks .mud-checkbox { display: flex; }

/* ---- Sync status cards (shared by the dialog and the settings page) ---- */
/* Colours come from the theme tokens so these flip with dark mode automatically. Status hues stay fixed —
   red means failed in either theme — but are lightened under .layout.dark for contrast. */
/* Superseded by the rule further down, which puts the status mark and the title together on the left.
   Left here as a single line so the two are easy to see as one thing rather than two competing ones. */
.sync-head { display: flex; align-items: center; gap: 14px; }
.sync-title { font-size: 16px; font-weight: 600; color: var(--text); }
.sync-subtitle { font-size: 12.5px; color: var(--text); opacity: .82; margin-top: 2px; }

/* MudButton outlined draws its border from text-primary at partial opacity, which against a near-black text
   token reads as a hard dark line. Pinned to the same light grey the inputs use, so the buttons sit at the
   weight of the table rules around them rather than outranking them. */
.sync-btn.mud-button-outlined {
    border-color: var(--mud-palette-lines-inputs);
    color: var(--text);
}

.sync-btn.mud-button-outlined:hover {
    border-color: var(--mud-palette-action-default);
    background-color: var(--hover);
}

/* One pass over the directory, so one status block: four facts in a row, then the last run's changes.
   The six-row source table this replaced repeated the same schedule six times and gave the one thing an
   admin opens this for — "did it see my change" — no room at all. */
.sync-block { margin-top: 14px; }
.sync-block + .sync-block { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--border); }

.sync-domain { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 10px; }

.sync-facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }

.sync-k {
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.sync-v {
    font-size: 14px;
    color: var(--text);
    margin-top: 3px;
    font-variant-numeric: tabular-nums;
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.sync-muted { color: var(--text-muted); }

/* A separator with weight — a band, not a hairline — because what's above (this run) and what's below
   (recent history) are different things and the first version ran them together. */
.sync-sec {
    display: flex;
    align-items: baseline;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    /* No negative margins to bleed to the dialog edge: this component also renders on the Sync settings
       page, whose padding differs, and a bleed sized for one host overflows the other. Square bottom
       corners and no bottom margin: the column headers sit directly beneath and join it. */
    margin: 20px 0 0;
    padding: 8px 12px;
    border-radius: 5px 5px 0 0;
    background: var(--hover);
    border: 1px solid var(--border);
}

.sync-sec-r { margin-left: auto; font-weight: 400; color: var(--text-muted); font-size: 12.5px; }

.sync-empty { color: var(--text-muted); font-size: 13px; padding: 8px 0; }

/* Time · action · type · object. Fixed columns for the three short fields so the object column — the only
   one that varies — absorbs the width, and every row's name starts at the same x. No header row: the pill
   and the type name say what they are, and a second row of small capitals was half the clutter. */
.sync-log { display: flex; flex-direction: column; font-size: 13px; }

.sync-row {
    display: grid;
    /* 70px fits "12:22 PM"; at 52px the meridiem wrapped onto a second line. */
    grid-template-columns: 70px 78px 92px minmax(0, 1fr);
    gap: 10px;
    align-items: baseline;
    padding: 7px 0;
    border-bottom: 1px solid var(--border);
}

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

/* Indented to the band's inner edge so the columns line up with its title. */
.sync-row { padding-left: 12px; padding-right: 12px; }

.sync-row-hdr {
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding-top: 6px;
    padding-bottom: 6px;
}

.sync-t { color: var(--text-muted); font-size: 12.5px; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The action as a small tinted pill: one colour per kind, sentence case, no letter-spacing. */
.sync-pill {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 1px 8px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--accent);
}
.sync-pill.add { background: color-mix(in srgb, #16a34a 14%, transparent); color: #16a34a; }
.sync-pill.del { background: color-mix(in srgb, #b91c1c 12%, transparent); color: #b91c1c; }
.layout.dark .sync-pill.add, body:has(.layout.dark) .sync-pill.add { color: #4ade80; }
.layout.dark .sync-pill.del, body:has(.layout.dark) .sync-pill.del { color: #f87171; }

.sync-ty { color: var(--text-muted); font-size: 12.5px; white-space: nowrap; }

.sync-what { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sync-o { font-weight: 500; }
.sync-a { color: var(--text-muted); }

/* The message is why anyone opened this dialog, so it gets its own line rather than a tooltip. */
.sync-error {
    color: #dc2626;
    font-size: 12px;
    white-space: pre-wrap;
    padding: 8px 10px;
    margin-top: 10px;
    border-radius: 4px;
    background: color-mix(in srgb, #dc2626 8%, transparent);
}

.layout.dark .sync-error, body:has(.layout.dark) .sync-error { color: #f87171; }

.sync-foot {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
}

/* Pushes the primary action to the right, away from the one that discards every watermark. */
.sync-foot-spacer { flex: 1; }

.sync-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.sync-dot.ok { background: #16a34a; }
.sync-dot.fail { background: #dc2626; }
.sync-dot.warn { background: #f59e0b; }
.sync-dot.none { background: var(--border); }

.layout.dark .sync-running { color: #60a5fa; }
.layout.dark .sync-fail,
.layout.dark .sync-error { color: #f87171; }
.layout.dark .sync-warn { color: #fbbf24; }

/* ---- Dark mode for Syncfusion grids ----
   One Syncfusion theme (light) is always loaded; dark is applied here as colour overrides scoped to
   `.layout.dark`, so switching never swaps a stylesheet — no themeless frame and no text reflow. */
.layout.dark .e-grid,
.layout.dark .e-grid .e-gridheader,
.layout.dark .e-grid .e-headercontent,
.layout.dark .e-grid .e-content,
.layout.dark .e-grid .e-gridcontent,
.layout.dark .e-grid .e-table,
.layout.dark .e-grid .e-gridpager,
.layout.dark .e-grid .e-pager,
.layout.dark .e-grid .e-toolbar,
.layout.dark .e-grid .e-toolbar .e-toolbar-items {
    background-color: var(--surface) !important;
    color: var(--text) !important;
    border-color: var(--border) !important;
}

.layout.dark .e-grid .e-headercell,
.layout.dark .e-grid .e-headercelldiv,
.layout.dark .e-grid .e-detailheadercell,
.layout.dark .e-grid .e-filterbarcell,
.layout.dark .e-grid .e-columnheader .e-headercell,
.layout.dark .e-grid .e-row .e-rowcell,
.layout.dark .e-grid .e-altrow .e-rowcell {
    background-color: var(--surface) !important;
    color: var(--text) !important;
    border-color: var(--border) !important;
}

.layout.dark .e-grid .e-row,
.layout.dark .e-grid .e-altrow { background-color: var(--surface) !important; }

.layout.dark .e-grid .e-headertext { color: var(--text) !important; }

/* The "no records" row's td has no .e-rowcell class, so none of the rules above reach it and the theme's
   dark text would land on a dark background. */
.layout.dark .e-grid .e-emptyrow td {
    background-color: var(--surface) !important;
    color: var(--text-muted) !important;
}

.layout.dark .e-grid .e-row:hover .e-rowcell { background-color: var(--hover) !important; }

.layout.dark .e-grid .e-row.e-rowselect .e-rowcell,
.layout.dark .e-grid .e-row.e-rowselect.e-altrow .e-rowcell,
.layout.dark .e-grid .e-row .e-rowcell.e-selectionbackground,
.layout.dark .e-grid tr.e-row[aria-selected="true"] .e-rowcell {
    background-color: #3b4d6b !important;
    color: #f8fafc !important;
}

.layout.dark .e-pager,
.layout.dark .e-pager *,
.layout.dark .e-pager .e-pagercontainer,
.layout.dark .e-pager .e-pagesizes,
.layout.dark .e-pager .e-numericcontainer,
.layout.dark .e-pager .e-parentmsgbar,
.layout.dark .e-pager .e-pagecountmsg,
.layout.dark .e-pager .e-pagerdropdown,
.layout.dark .e-pager .e-pagerconstant,
.layout.dark .e-pager .e-pagerexternalmsg {
    background-color: var(--surface) !important;
    color: var(--text) !important;
    border-color: var(--border) !important;
}

.layout.dark .e-pager .e-link,
.layout.dark .e-pager .e-numericitem {
    color: var(--text) !important;
    background-color: transparent !important;
}

.layout.dark .e-pager .e-currentitem {
    background-color: var(--accent) !important;
    color: #ffffff !important;
}

.layout.dark .e-pager .e-input-group,
.layout.dark .e-pager .e-input-group .e-input,
.layout.dark .e-pager .e-dropdownlist {
    background-color: var(--app-bg) !important;
    color: var(--text) !important;
    border-color: var(--border) !important;
}

.layout.dark .e-grid .e-icons,
.layout.dark .e-grid .e-sortfilterdiv { color: #cbd5e1 !important; }

/* Syncfusion's outline buttons in the report toolbars.
   border-color deliberately follows --mud-palette-lines-inputs, not --border: these sit beside the Mud
   search box, and matching the box is what matters here. Mud flips that variable with the theme, so this
   stays correct in dark mode without a second value. */
.layout.dark .e-btn.e-outline,
.layout.dark .e-dropdown-btn.e-outline {
    color: var(--text) !important;
    border-color: var(--mud-palette-lines-inputs) !important;
    background-color: transparent !important;
}

.layout.dark .e-btn.e-outline:hover,
.layout.dark .e-dropdown-btn.e-outline:hover { background-color: var(--hover) !important; }

/* Reconnect modal - Blazor toggles the outer classes when the SignalR circuit drops. */
.reconnect-modal {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, .45);
    z-index: 2000;
    align-items: center;
    justify-content: center;
}

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: flex;
}

/* The reconnect modal is a sibling of .layout, not inside it, so .layout.dark can't reach it — hence the
   :has() on body. Worth getting right: this is the one thing on screen when the circuit drops. */
.reconnect-box {
    background: #fff;
    color: #0f172a;
    border-radius: 8px;
    padding: 24px 32px;
    text-align: center;
    min-width: 280px;
}

body:has(.layout.dark) .reconnect-box {
    background: #1c1c1e;
    color: #ededef;
}

body:has(.layout.dark) .reconnect-box a { color: #60a5fa; }

.reconnect-spinner {
    width: 28px;
    height: 28px;
    margin: 0 auto 12px;
    border: 3px solid #cbd5e1;
    border-top-color: #1a73e8;
    border-radius: 50%;
    animation: reconnect-spin 1s linear infinite;
}

@keyframes reconnect-spin {
    to { transform: rotate(360deg); }
}

/* Only the message matching the current circuit state is shown. */
.reconnect-trying, .reconnect-failed, .reconnect-rejected { display: none; margin: 0; }
#components-reconnect-modal.components-reconnect-show .reconnect-trying { display: block; }
#components-reconnect-modal.components-reconnect-failed .reconnect-failed { display: block; }
#components-reconnect-modal.components-reconnect-rejected .reconnect-rejected { display: block; }
#components-reconnect-modal.components-reconnect-show .reconnect-spinner { display: block; }
#components-reconnect-modal.components-reconnect-failed .reconnect-spinner,
#components-reconnect-modal.components-reconnect-rejected .reconnect-spinner { display: none; }

/* ---- True Last Logon ---- */

/* Run metadata sits at the end of the toolbar rather than in a header strip: it is provenance for the grid
   below it — how many controllers answered, and when — not a page-level fact. */
.tll-meta {
    margin-left: auto;
    font-size: 12px;
    color: var(--text-muted);
    white-space: nowrap;
}

.tll-toggle {
    flex-shrink: 0;
    margin: 0;
}

/* A run that could not reach every controller still shows its rows; this says which ones are missing, since
   the newest logon may be the one that did not answer. */
.tll-warning {
    margin: 0 0 12px;
    padding: 8px 12px;
    font-size: 13px;
    color: var(--text);
    background: color-mix(in srgb, #b45309 12%, var(--surface));
    border: 1px solid color-mix(in srgb, #b45309 35%, var(--surface));
    border-radius: 4px;
}

/* ---- Title-bar search ---- */

.appsearch {
    position: relative;
    flex: 0 1 460px;
    min-width: 200px;
}

.appsearch-icon {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: #94a3b8;
    pointer-events: none;
}

/* Its own light field on the dark band, the way a command bar reads — not a dark input that disappears
   into the header. */
.appsearch-input {
    width: 100%;
    height: 30px;
    padding: 0 12px 0 32px;
    font: inherit;
    font-size: 13px;
    color: #0f172a;
    background: #fff;
    border: 1px solid #cbd5e1;
    border-radius: 4px;
}

.appsearch-input::placeholder { color: #64748b; }

.appsearch-input:focus {
    outline: none;
    border-color: #1a73e8;
    box-shadow: 0 0 0 2px rgba(26, 115, 232, .35);
}

/* The native clear affordance sits under our own padding and duplicates Escape. */
.appsearch-input::-webkit-search-cancel-button { display: none; }

.appsearch-results {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    z-index: 1300;   /* above Mud's app bar layer, below its dialogs */
    max-height: 60vh;
    overflow-y: auto;
    padding: 4px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, .18);
}

.appsearch-hit {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: 4px;
    cursor: pointer;
    color: var(--text);
}

.appsearch-hit:hover,
.appsearch-hit.active { background: var(--hover); }

.appsearch-hit-label {
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The matched run. Weight rather than a colour or a fill: several rows match at once, and highlighting
   five of them in colour is a lightshow rather than an answer. */
.appsearch-hit-label strong { font-weight: 700; }

/* Where the report lives. Given a chip so it reads as a distinct piece of information rather than trailing
   grey text — it is often the only thing separating two near-identical names. */
.appsearch-hit-section {
    margin-left: auto;
    flex-shrink: 0;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 500;
    color: var(--text-muted);
    background: var(--hover);
    white-space: nowrap;
}

/* On the highlighted row the chip would disappear into the row's own fill. */
.appsearch-hit.active .appsearch-hit-section,
.appsearch-hit:hover .appsearch-hit-section {
    background: var(--surface);
    color: var(--text);
}

.appsearch-empty {
    padding: 12px 10px;
    font-size: 13px;
    color: var(--text-muted);
}

/* Below the sidebar's breakpoint the bar has no room to centre anything. */
@media (max-width: 900px) {
    .appsearch { display: none; }
}

/* ---- Demo instance ---- */

/* A strip under the title bar, above everything else. Amber rather than red: this is a statement about the
   data, not a warning about a fault. */
.demo-banner {
    flex: 0 0 auto;
    padding: 6px 16px;
    font-size: 12.5px;
    font-weight: 500;
    color: #7c2d12;
    background: #fde9c8;
    border-bottom: 1px solid #f0c98a;
}

.layout.dark .demo-banner {
    color: #fcd9a8;
    background: #2f2210;
    border-bottom-color: #46341a;
}

/* Column-resize cursor.

   Syncfusion sets the heavy "col-resize" pointer — the fat double-bar glyph — while the pointer is over a
   column border. The lighter "ew-resize" double-arrow says the same thing without the weight. Three
   selectors because .e-rcursor lands on the grid root, a header cell, or a stacked header cell depending on
   where the pointer is. Matches the 365 toolkit. */
.e-grid.e-rcursor,
.e-grid .e-gridheader .e-rcursor,
.e-grid .e-gridheader .e-stackedheadercell.e-rcursor {
    cursor: ew-resize !important;
}

/* ---- Placeholder page for tools that are listed but not built ---- */

.soon {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 36px 24px;
    text-align: center;
}

.soon-icon { color: var(--text-muted); }

.soon-title {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--text);
}

/* Says what the tool WILL do, so the entry is a plan rather than a dead end. */
.soon-text {
    margin: 0;
    max-width: 62ch;
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-muted);
}

/* ---- Schedule editor ---- */

.sched-page { max-width: 1000px; }

.sched-rows { display: flex; flex-direction: column; gap: 18px; }

/* Label in a fixed column on the left, control on the right — the shape of a settings form rather than a
   stack of floating labels. */
.sched-row { display: flex; align-items: flex-start; gap: 16px; }

.sched-label {
    flex: 0 0 110px;
    /* Centres the label against a 36px control rather than sitting above its text. */
    padding-top: 9px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.sched-control { flex: 1 1 auto; min-width: 0; }

/* Mud's dense outlined field is still ~48px tall, which on a page of six stacked rows reads as a form of
   oversized boxes. Pinned to the same 36px the grid toolbar uses, so a control is the same height wherever
   it appears in the app. The inner input needs its own height and zero vertical padding — Mud sets both,
   and without this the text sits below the shortened outline. */
.sched-page .mud-input.mud-input-outlined,
.sched-page .seg-btn {
    height: 36px !important;
}

.sched-page .mud-input.mud-input-outlined input {
    height: 34px;
    padding-top: 0;
    padding-bottom: 0;
}

/* Adornment icons scale with the field; left at their default they force the row back open. */
.sched-page .mud-input.mud-input-outlined .mud-input-adornment .mud-icon-root { font-size: 1.15rem; }

.sched-page .mud-input-control { margin-top: 0; }

.sched-hint {
    display: block;
    margin-top: 4px;
    font-size: 11.5px;
    color: var(--text-muted);
}

/* ---- Report picker ---- */

/* The report row is two columns: the list, and everything that describes the report it chose. */
.sched-report {
    display: flex;
    align-items: flex-start;
    gap: 24px;
}

.sched-side {
    flex: 1 1 260px;
    min-width: 240px;
    max-width: 380px;
}

.sched-side-block { margin-top: 18px; }

.sched-side-head {
    margin-bottom: 6px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.sched-picker {
    flex: 0 0 620px;
    max-width: 620px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    overflow: hidden;
}

.sched-picker-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
}

.sched-picker-head .mud-input-control { flex: 1 1 auto; }

.sched-picker-count {
    flex: 0 0 auto;
    font-size: 11.5px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* Fixed height: the list is long, and a picker that grows the page pushes Save off the bottom. */
.sched-picker-list { height: 280px; overflow-y: auto; padding: 4px; }

.sched-picker-group {
    position: sticky;
    top: 0;
    padding: 7px 8px 3px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--surface);
}

.sched-picker-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    /* The 3px makes room for the selected row's left bar, so nothing shifts when a row is picked. */
    padding: 6px 10px 6px 13px;
    border-left: 3px solid transparent;
    border-radius: 4px;
    font-size: 13px;
    color: var(--text);
    cursor: pointer;
}

.sched-picker-item:hover { background: var(--hover); }

/* Three signals, because a tinted fill on its own is indistinguishable from the hover state: a solid accent
   bar down the left edge, the accent as text, and a tick at the end of the row. */
.sched-picker-item.on {
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    border-left-color: var(--accent);
    border-radius: 0 4px 4px 0;
    color: var(--accent);
    font-weight: 600;
}

.sched-picker-item.on .mud-icon-root { font-size: 1.05rem; }

.sched-picker-empty { padding: 14px 10px; font-size: 13px; color: var(--text-muted); }

/* Restates the choice below the list, so it stays visible after you scroll the list away from it. */
.sched-chosen {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    font-size: 13px;
    color: var(--text);
}

.sched-chosen .mud-icon-root { color: #15803d; }
.sched-chosen-name { font-weight: 600; }

.sched-chosen-cat {
    padding: 1px 7px;
    border-radius: 4px;
    font-size: 11px;
    color: var(--text-muted);
    background: var(--hover);
}

/* ---- Columns and filters ---- */

/* In the narrow right-hand column these stack: the state, then the button that changes it. */
.sched-summary {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
}

.sched-summary-text { font-size: 13px; color: var(--text-muted); }

.sched-summary-buttons { display: flex; align-items: center; gap: 6px; }

/* Mud draws an outlined button's border from its own palette, which is far darker than anything else on
   this page. Same tone as the report toolbar's buttons and the input outlines here — and the label and icon
   at the page's normal text weight and colour rather than Mud's bold near-black. */
.sched-page .mud-button-outlined {
    border-color: var(--btn-border);
    font-weight: 400;
    color: var(--text);
}

.sched-page .mud-button-outlined .mud-icon-root {
    color: var(--text-muted);
    font-size: 1.1rem;
}

.sched-page .mud-button-outlined:hover {
    border-color: var(--btn-border);
    background-color: var(--hover);
}

/* ---- When it runs ---- */

.sched-when {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
}

.sched-when-label { font-size: 13px; color: var(--text-muted); }

/* Wrappers, not the Mud controls themselves — see the comment on the markup. */
.sched-when .sched-num { flex: 0 0 84px; }
.sched-when .sched-day { flex: 0 0 150px; }

/* "hours" belongs to the select on its LEFT, so it sits tight against it and the next pair starts after a
   full gap. */
.sched-when .sched-num + .sched-when-label { margin-right: 6px; }

/* ---- Name and actions, pinned under the form ---- */

.sched-actions {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 12px;
    margin-top: 28px;
    padding-top: 18px;
    border-top: 1px solid var(--border);
}

/* Mud's field root is width:100% and the inline max-width lands on the inner control, so the root still
   claimed the whole row and pushed the buttons to the far edge. Size the root itself and the buttons sit
   beside the box. */
.sched-actions > .mud-input-control {
    flex: 0 0 360px;
    width: 360px;
}

/* ---- User edit panel ---- */

/* Geometry, animation and z-index come from .detail-panel, shared with the report slide-outs. Only what a
   FORM needs differs: a little more width for two columns of fields, and the surface colour, since inputs
   on the page background read as sunken. */
.upanel {
    width: min(600px, 100vw);
    background: var(--surface);
}

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

.upanel-head { padding: 12px 16px 0; border-bottom: 1px solid var(--border); }

.upanel-top { display: flex; align-items: flex-start; gap: 12px; }
.upanel-id { flex: 1 1 auto; min-width: 0; }
.upanel-name { font-size: 15px; font-weight: 600; color: var(--text); }

.upanel-sub {
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.upanel-tabs { display: flex; gap: 2px; margin-top: 10px; }

.upanel-tabs button {
    padding: 7px 12px;
    border: 0;
    border-bottom: 2px solid transparent;
    background: none;
    font: inherit;
    font-size: 13px;
    color: var(--text-muted);
    cursor: pointer;
}

.upanel-tabs button:hover:not(:disabled) { color: var(--text); }
.upanel-tabs button:disabled { opacity: .45; cursor: default; }

.upanel-tabs button.on {
    color: var(--accent);
    border-bottom-color: var(--accent);
    font-weight: 600;
    background: color-mix(in srgb, var(--accent) 7%, transparent);
    border-radius: 4px 4px 0 0;
}

.upanel-tabs button { position: relative; display: inline-flex; align-items: center; gap: 6px; }

/* Only one tab is on screen at a time, so a tab holding unsaved edits has to say so. */
.upanel-tabdot { width: 6px; height: 6px; border-radius: 50%; background: #b45309; }

/* The extra top padding separates the first section heading from the tab underline directly above it —
   without it the two rules of text sit close enough to read as one block. */
.upanel-body { flex: 1 1 auto; overflow-y: auto; padding: 20px 16px 14px; }

/* Options read as a list of statements about the account, so they stack rather than sitting in columns. */
.upanel-checks { display: flex; flex-direction: column; gap: 8px; }

.upanel-check {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 13px;
    color: var(--text);
    cursor: pointer;
}

.upanel-check input { accent-color: var(--accent); cursor: pointer; }

.upanel-note {
    margin-bottom: 12px;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--text-muted);
}

.upanel-note code {
    padding: 1px 5px;
    border-radius: 3px;
    background: var(--hover);
    font-size: 12px;
}

/* The chosen-groups list on the create form. (The edit panel shows membership as a table instead, since it
   has scope and type to show; this one is just names you picked.) */
.upanel-groups {
    margin: 0;
    padding: 0;
    list-style: none;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
}

.upanel-groups li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 2px 4px 2px 11px;
    font-size: 13px;
    color: var(--text);
    border-bottom: 1px solid var(--border);
}

.upanel-groups li:last-child { border-bottom: 0; }
.upanel-groups li:hover { background: var(--hover); }

/* Only on hover: six rows each carrying a permanent × is a column of noise, and the row is not going
   anywhere while you read it. */
.upanel-groups li .mud-icon-button { opacity: 0; transition: opacity .12s; }
.upanel-groups li:hover .mud-icon-button,
.upanel-groups li .mud-icon-button:focus-visible { opacity: 1; }
.upanel-groups .mud-icon-root { font-size: 1.05rem; color: var(--text-muted); }

.upanel-add { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.upanel-add-box { flex: 1 1 auto; }

/* Group membership as a table rather than a list: scope and type are what tell two similarly named groups
   apart, and ticking several to remove them in one go is how the desktop toolkit does it. */
.upanel-table {
    width: 100%;
    border-collapse: collapse;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    table-layout: fixed;
}

.upanel-table th {
    padding: 7px 9px;
    text-align: left;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-muted);
    background: var(--hover);
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

.upanel-table td {
    padding: 6px 9px;
    font-size: 12.5px;
    color: var(--text);
    border-bottom: 1px solid var(--border);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.upanel-table tr:last-child td { border-bottom: 0; }
.upanel-table tbody tr { cursor: pointer; }
.upanel-table tbody tr:hover td { background: var(--hover); }

/* The whole row is the hit target, so the selected state has to read at row level too. */
.upanel-table tbody tr.on td { background: color-mix(in srgb, var(--accent) 12%, transparent); }

.upanel-tick { width: 34px; }
.upanel-tick input { accent-color: var(--accent); cursor: pointer; }

/* A DN is far too long for any column that fits; it is shown truncated with the full value on hover. */
.upanel-dn { color: var(--text-muted); font-size: 12px; }

/* A section heading outranks the labels under it, so it carries the darker ink. Uppercase at 11px was
   already quiet enough without also being grey — the two together read as the section being LESS important
   than its own fields. */
.upanel-section {
    margin: 20px 0 10px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text);
}

.upanel-section:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

.upanel-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 14px; }
.upanel-field.wide { grid-column: 1 / -1; }

.upanel-field label {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 4px;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-muted);
}

/* Plain inputs rather than Mud fields: twelve of them in a scrolling panel, all the same shape, and the
   app's own border tone is the one thing they need to share with everything else on the page. */
.upanel-field input {
    width: 100%;
    height: 34px;
    padding: 0 9px;
    box-sizing: border-box;
    border: 1px solid var(--btn-border);
    border-radius: 5px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 13px;
}

.upanel-field input:focus {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
    border-color: var(--accent);
}

/* An edited field says so on itself, not only in the count at the foot — with three sections scrolling,
   "2 unsaved changes" alone does not tell you WHICH two. */
.upanel-field.dirty input { border-color: #b45309; background: color-mix(in srgb, #f59e0b 9%, transparent); }

.upanel-edited {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: #b45309;
}

.upanel-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    border-top: 1px solid var(--border);
    background: var(--hover);
}

/* Takes the slack, which is what pushes the buttons to the right edge. */
.upanel-dirty { flex: 1 1 auto; font-size: 12.5px; color: #b45309; }

/* Mud's disabled filled button keeps its white label over a pale grey fill, which is unreadable. Save spends
   most of its life disabled here — nothing has been edited yet — so that is the state to get right. */
.upanel-foot .mud-button-filled:disabled {
    background-color: #e2e8f0 !important;
    color: var(--text-muted) !important;
    box-shadow: none;
}

.upanel-foot .mud-button-outlined {
    border-color: var(--btn-border);
    color: var(--text);
    font-weight: 400;
}

.upanel-foot .mud-button-outlined:hover {
    border-color: var(--btn-border);
    background-color: var(--surface);
}

/* ---- Bulk pages ---- */

/* A page reached from another page says so in its title. Ported from the sibling 365 toolkit, which uses
   the same three-step bulk layout. */
.breadcrumb-title { margin: 0; display: flex; align-items: center; gap: 8px; }
.breadcrumb-title a { color: var(--accent); text-decoration: none; }
.breadcrumb-title a:hover { text-decoration: underline; }
.breadcrumb-title .bc-sep { color: var(--text-muted); }

/* The tool LIST pages lose the card too, so a tool page is a tool page whether it holds a form or a grid.
   The rail keeps its border, which is now the only thing dividing it from the list — and that is enough,
   because the two are plainly different content. */
.layout main:has(.tool-layout) { background: var(--surface); }
.layout main:has(.tool-layout) .content { background: var(--surface); }

.tool-layout .report-box,
.tool-layout .tool-rail {
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
}

/* The one edge worth keeping: it separates what you are looking at from what you can do to it. */
.tool-layout .tool-rail { border-left: 1px solid var(--border); }

/* Tool pages are one white surface, not a white card on a grey page.
   A card says "this block is a thing among other things", which is true of a report grid sitting beside a
   dashboard's other cards — and false of a form that IS the page. So the working area takes the surface
   colour and the card's border, radius and shadow come off. */
.layout main:has(.bulk-page) { background: var(--surface); }
.layout main:has(.bulk-page) .content { background: var(--surface); padding-top: 1rem; }

/* No width cap. The things on these pages that want to be narrow already say so — the create form's rows
   are 780px, the template list 980px — so a cap here only ever shortened the one thing that wants the room,
   which is the import preview: forty columns that cannot be read in a 1200px box. */
.bulk-page { padding: 0; }

.bulk-body {
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* Each step is a tinted band. It was a hairline rule between bands, on the reasoning that these are
   stages of one task rather than three unrelated panels - which is true, and a rule turned out to be too
   little to say it with. A page of options with one thin line between groups reads as one long form, and
   the step headings get lost in it.

   The tint is the accent at 4%, not a grey: it follows the section colour the sidebar sets, and it is faint
   enough that the controls inside still sit on what reads as the page rather than inside a box. */
.bulk-section {
    padding: 14px 16px;
    margin-bottom: 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: color-mix(in srgb, var(--accent) 4%, var(--surface));
}

/* Dark mode cannot use the same trick - 4% of a blue over a near-black surface is invisible - so the band
   is lifted off the background instead of tinted. */
.layout.dark .bulk-section { background: color-mix(in srgb, #fff 4%, var(--surface)); }

.bulk-section-head { font-size: 14.5px; font-weight: 600; margin-bottom: 6px; color: var(--text); }

.bulk-hint { margin: 0 0 12px; font-size: 13px; color: var(--text-muted); max-width: 92ch; }
.bulk-hint-tight { margin-bottom: 6px; }
.bulk-hint strong { color: var(--text); font-weight: 600; }

.bulk-actions-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.bulk-apply-row { margin-top: 16px; }

.bulk-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 34px;
    padding: 0 14px;
    border: 1px solid var(--btn-border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    text-decoration: none;
    cursor: pointer;
}

.bulk-btn:hover:not(:disabled) { background: var(--hover); }
.bulk-btn:disabled { opacity: .55; cursor: default; }

.bulk-btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.bulk-btn-primary:hover:not(:disabled) { filter: brightness(.94); background: var(--accent); }
.bulk-btn-primary .mud-icon-root { color: #fff; }

.bulk-filename {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12.5px;
    color: var(--text-muted);
}

.bulk-supported { margin-top: 10px; font-size: 11.5px; line-height: 1.7; color: var(--text-muted); }
.bulk-ignored { color: #b45309; }
.bulk-alert { margin: 8px 0; }
.bulk-summary { font-size: 13px; font-weight: 600; color: var(--text); }

/* Options in two columns: password and naming on the left, what the account IS on the right. */
.bulk-options { display: flex; flex-wrap: wrap; gap: 40px; }
.bulk-optcol { display: flex; flex-direction: column; gap: 7px; min-width: 300px; }

.bulk-optlabel {
    margin-top: 8px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.bulk-optcol > .bulk-optlabel:first-child { margin-top: 0; }
.bulk-inline { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text); }

/* A three-state option: the radios sit under their label rather than beside it, because "Do not change" is
   as long as the choices and a row of them wraps at any sensible width. */
.bulk-choice { display: flex; flex-direction: column; gap: 4px; margin-bottom: 4px; }

/* ---- Offboarding ---- */

.off-picker { max-width: 560px; }

/* A pasted list of logon names: wide enough for a DN-length line, short enough not to own the step. */
.bulk-paste { max-width: 560px; }

/* The action list: a tick with its consequence under it. The description is not a tooltip because on
   this page the consequence IS what is being chosen. */
.act-list { max-width: 620px; }
.act-item { margin-bottom: 14px; }
.act-label { display: flex; align-items: center; gap: 9px; font-size: 13.5px; font-weight: 600; color: var(--text); cursor: pointer; }
.act-label input { accent-color: var(--accent); cursor: pointer; }
.act-note { margin-left: 25px; font-size: 12px; color: var(--text-muted); }
.act-item.destructive .act-label { color: #b91c1c; }
.act-item.destructive .act-label input { accent-color: #b91c1c; }
.act-item.destructive .act-note { color: #b91c1c; }
.act-sub { margin: -6px 0 14px 25px; padding-left: 12px; border-left: 2px solid var(--border); max-width: 420px; }
.bulk-paste-box { width: 100%; height: auto; padding: 8px 9px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; resize: vertical; }
.off-list { max-width: 560px; margin-top: 4px; }

.off-actions { gap: 2px; min-width: 340px; }

/* An action and the sentence explaining it, indented under the box so the note lines up with the label
   rather than the tick. */
.off-action { margin-bottom: 10px; }
.off-note { display: block; margin-left: 25px; font-size: 11.5px; color: var(--text-muted); }

/* The controls belonging to a ticked action. */
.off-sub {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 0 0 12px 25px;
    padding-left: 12px;
    border-left: 2px solid var(--border);
    max-width: 420px;
}

/* The irreversible one, kept away from the rest. */
.off-danger {
    margin-top: 16px;
    padding: 12px 14px;
    border: 1px solid color-mix(in srgb, #dc2626 35%, transparent);
    border-radius: 6px;
    background: color-mix(in srgb, #dc2626 5%, transparent);
    max-width: 560px;
}

.off-danger .off-action { margin-bottom: 0; }
.off-danger .off-note { color: #b91c1c; }

/* The preview scrolls in both directions: forty columns wide is normal for this file, and the header has
   to stay put while you read down it. */
/* The import page holds its height and lets the PREVIEW scroll, rather than scrolling the whole page.
   The steps above it are three lines each and the table is a hundred rows; scrolling the page pushed the
   thing you are checking off the screen along with the Create button under it. */
/* Scrolls, unlike the report pages. These steps have a natural height - five actions, a paste box, a
   preview that will not go under ten rows - and on a laptop that total is simply taller than the viewport.
   Locking the page and letting the preview absorb the difference squeezed it to three rows, and then to
   nothing: the last thing to go was the Apply button, with no scrollbar to reach it by. */
.layout main:has(.bulk-import) { overflow: auto; }

.layout main:has(.bulk-import) .content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.bulk-import { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.bulk-import .bulk-body { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

/* Step 3 is sized by what is in it, not by what is left over. Taking "the rest" cut both ways: on a short
   window there was no rest and the table got three rows, and on a tall one a single-row preview was stretched
   to 300px of empty tint with the Apply button below the fold. */
.bulk-preview-section { display: flex; flex-direction: column; flex: 0 0 auto; min-height: 0; }

/* The empty state for step 3. The step is always on the page — one that appears only once it is satisfied
   leaves the form looking like it has two steps and no way to finish. */
.bulk-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    flex: 1 1 auto;
    min-height: 160px;
    border: 1px dashed var(--border);
    border-radius: 6px;
    color: var(--text-muted);
}

.bulk-empty p { margin: 0; font-size: 13px; }
.bulk-empty .mud-icon-root { color: var(--border); }

/* As tall as its rows, up to about fifteen of them, then it scrolls. One row is one row high - the button
   under it stays on screen - and a long file still gets a table worth reading rather than the three rows a
   leftover-space box was squeezed to. */
.bulk-preview-wrap {
    flex: 0 1 auto;
    min-height: 0;
    max-height: 460px;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: 6px;
}

.bulk-preview { width: 100%; border-collapse: collapse; font-size: 12.5px; }

.bulk-preview th,
.bulk-preview td {
    text-align: left;
    padding: 6px 10px;
    white-space: nowrap;
    border-bottom: 1px solid var(--border);
}

.bulk-preview thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--hover);
    font-weight: 600;
    color: var(--text-muted);
}

.bulk-preview .bulk-idx { width: 34px; color: var(--text-muted); }

/* The key column carries no emphasis of its own. It was coloured, then bold, on the idea that the column
   identifying each row deserves marking - but it is a table of names and every one of them identifies its
   row. Two weights in a grid of short strings reads as a fault rather than a hierarchy. The header still
   marks which column it is. */
.bulk-preview .bulk-key { font-weight: inherit; }
.bulk-tmpl-cell { max-width: 220px; overflow: hidden; text-overflow: ellipsis; }

.bulk-status { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; }
.bulk-status .mud-icon-root { font-size: 1rem; }
.bulk-status.ok { color: #16a34a; }
.bulk-status.warn { color: #b45309; }
.bulk-status.err { color: #dc2626; }
.bulk-status.pending { color: var(--text-muted); }

/* ---- Password expiry dashboard ---- */

/* Same shape as a report page: the page itself does not scroll, the grid does, and the surface is white
   rather than a card floating on grey. */
.layout main:has(.pwd-dash) { overflow: hidden; background: var(--surface); }

.layout main:has(.pwd-dash) .content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    background: var(--surface);
    padding-bottom: 1.5rem;
}

.pwd-dash { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; gap: 14px; }

.pwd-cards {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 12px;
}

@media (max-width: 1100px) { .pwd-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 700px) { .pwd-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* A card is a button: the number is the question and the rows underneath are the answer, so clicking it
   filters rather than navigating. */
.pwd-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 14px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    font: inherit;
    cursor: pointer;
    transition: border-color .12s, box-shadow .12s;
}

.pwd-card:hover { border-color: var(--btn-border); }

/* The selected band is the one the table is showing, so it carries a real edge rather than a tint alone. */
.pwd-card.on { border-color: currentColor; box-shadow: inset 0 0 0 1px currentColor; }

.pwd-card-value { font-size: 26px; font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums; }
.pwd-card-label { font-size: 12px; color: var(--text-muted); text-align: center; }

/* Colour by urgency, not by decoration: the tone says how soon, and the label says how soon in words. */
.pwd-card.urgent { color: #dc2626; background: color-mix(in srgb, #dc2626 6%, var(--surface)); }
.pwd-card.warn { color: #b45309; background: color-mix(in srgb, #f59e0b 8%, var(--surface)); }
.pwd-card.ok { color: #15803d; background: color-mix(in srgb, #16a34a 6%, var(--surface)); }
.pwd-card.info { color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--surface)); }
.pwd-card.flat { color: #b91c1c; background: color-mix(in srgb, #dc2626 5%, var(--surface)); }

.pwd-toolbar { display: flex; align-items: center; gap: 10px; }
.pwd-toolbar-label { font-size: 12.5px; color: var(--text-muted); }
.pwd-select { width: 220px; }
.pwd-search { width: 260px; }
.pwd-spacer { flex: 1 1 auto; }
.pwd-updated { font-size: 12px; color: var(--text-muted); }

.pwd-grid { flex: 1 1 auto; min-height: 0; }

.pwd-days { font-variant-numeric: tabular-nums; }
.pwd-days-urgent { color: #dc2626; font-weight: 600; }
.pwd-days-warn { color: #b45309; font-weight: 600; }
.pwd-days-over { color: #dc2626; font-weight: 600; }

/* ---------------------------------------------------------------------------------------------------
   Cleanup

   Two columns that both fill the page height: the checks and their counts on the left, whatever is
   selected on the right. The counts are the point of the left column — nine numbers visible at once is
   the finding, before any one of them is opened — so the column is sized to the numbers, not the labels,
   and it does not scroll away with the results.
   --------------------------------------------------------------------------------------------------- */
/* No rail on this page: the actions belong to the ticked rows, not to the page, so they sit over the
   results and the whole width is the results' — which the DN column needs. Same page-height rule as
   .tool-layout, so the table scrolls inside rather than the page growing. */
.cleanup-page {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 150px);
}

.layout main:has(.cleanup-page) { background: var(--surface); }
.layout main:has(.cleanup-page) .content { background: var(--surface); }

@media (max-width: 900px) {
    .cleanup-page { height: auto; }
    .cleanup { flex-direction: column; }
    .cleanup-checks { flex: 0 0 auto; border-right: 0; padding-right: 0; }
}

.cleanup {
    display: flex;
    gap: 20px;
    align-items: stretch;
    flex: 1 1 auto;
    min-height: 0;
}

.cleanup-checks {
    flex: 0 0 288px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 4px 0;
    border-right: 1px solid var(--border);
    padding-right: 16px;
    overflow-y: auto;
}

.cleanup-group {
    margin: 14px 0 4px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.cleanup-group:first-child { margin-top: 0; }

.cleanup-check {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 7px 10px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
}

.cleanup-check:hover { background: var(--hover); }
.cleanup-check:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Selected has to differ from hover in KIND, not degree. It used to be var(--hover) — the very same value
   the row takes under the cursor — so the highlight read as "the mouse is here" and vanished the moment you
   moved the mouse over any other check. The tint is accent-derived, which nothing else in the list can
   look like. */
.cleanup-check.on {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    box-shadow: inset 3px 0 0 var(--accent);
    color: var(--accent);
    font-weight: 500;
}

.cleanup-check.on:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); }

/* The count too, or the row reads as half-selected — accent label beside a muted number. */
.cleanup-check.on .cleanup-count { color: var(--accent); }

.cleanup-check-label { flex: 1 1 auto; min-width: 0; }

/* Tabular figures so the column of counts lines up down the list rather than jittering by digit width. */
.cleanup-count {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

/* Nothing to do recedes; it is still worth showing, because a zero is an answer. */
.cleanup-count.zero { color: var(--text-muted); font-weight: 400; }

.cleanup-recount {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 16px;
    padding: 6px 10px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font: inherit;
    font-size: 12px;
    cursor: pointer;
    align-self: flex-start;
}

.cleanup-recount:hover:not(:disabled) { background: var(--hover); color: var(--text); }
.cleanup-recount:disabled { cursor: default; }
.cleanup-recount .mud-icon-root { font-size: 15px; }

.cleanup-results {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
}

.cleanup-head { padding: 4px 0 12px; }

.cleanup-head h3 { margin: 0; font-size: 15px; font-weight: 600; }
.cleanup-head p { margin: 3px 0 0; font-size: 13px; color: var(--text-muted); }

/* The threshold decides the entire result set, so it leads the action bar rather than sitting in a corner
   as though it were a caption. Its label takes the body colour — a muted one read as metadata, which is
   what made it easy to miss. */
.cleanup-days {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-right: 14px;
    margin-right: 4px;
    border-right: 1px solid var(--border);
    font-size: 13px;
    color: var(--text);
    white-space: nowrap;
}

.cleanup-days label { cursor: default; }

.cleanup-days input {
    width: 64px;
    height: 30px;
    padding: 0 8px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--app-bg);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.cleanup-days input:hover { border-color: var(--accent); }

.cleanup-days input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.cleanup-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 0;
    border-top: 1px solid var(--border);
}

.cleanup-selected {
    font-size: 13px;
    color: var(--text-muted);
    margin-right: 6px;
    font-variant-numeric: tabular-nums;
}

/* Delete is the one action here that cannot be walked back, so it is the one that looks different. */
.tool-actionbar-btn.danger .mud-icon-root { color: var(--mud-palette-error, #c62828); }
.tool-actionbar-btn:disabled { color: var(--text-muted); cursor: default; }
.tool-actionbar-btn:disabled .mud-icon-root { color: var(--text-muted); }
.tool-actionbar-btn:disabled:hover { background: transparent; }

.cleanup-capped {
    margin-left: auto;
    font-size: 12px;
    color: var(--text-muted);
}

/* The grid takes whatever height is left under the action bar and scrolls inside itself, the same as a
   report page's content area. */
.cleanup-grid {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

/* Columns and export act on the view rather than on the ticked rows, so they sit at the far edge away
   from the actions that write to the directory. */
.cleanup-actions-right { display: flex; align-items: center; gap: 6px; margin-left: auto; }

.cleanup-lock {
    margin-left: 8px;
    padding: 1px 6px;
    border-radius: 4px;
    background: var(--hover);
    border: 1px solid var(--border);
    font-size: 11px;
    font-weight: 400;
    color: var(--text-muted);
}

.cleanup-empty {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    color: var(--text-muted);
    border-top: 1px solid var(--border);
}

.cleanup-empty p { margin: 0; font-size: 13px; }
.cleanup-empty .mud-icon-root { color: var(--border); font-size: 40px; }

/* A column header whose friendly name doesn't say plainly which attribute it is. The dotted underline is
   the long-standing convention for "there is a definition under this", and it has to be visible or the
   tooltip is only found by accident. Inherits the header's own colour and weight — this marks a word, it
   does not restyle it. */
.col-head-hint {
    border-bottom: 1px dotted currentColor;
    cursor: help;
}

/* A hint that reads as one sentence or not at all - a column list broken mid-way looks like two lists.
   Scrolls rather than wraps on a narrow window, so the line is never cut off. */
.bulk-hint-nowrap {
    white-space: nowrap;
    overflow-x: auto;
    max-width: 100%;
}

/* ---- Settings > SSL ---------------------------------------------------------------------------- */

.ssl-tabs { display: flex; gap: 4px; margin: 16px 0 0; border-bottom: 1px solid var(--border); }

.ssl-tab {
    padding: 7px 14px;
    border: none;
    border-bottom: 2px solid transparent;
    background: transparent;
    color: var(--text-muted);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.ssl-tab:hover { color: var(--text); }
.ssl-tab.on { color: var(--accent); border-bottom-color: var(--accent); font-weight: 500; }

.ssl-panel { padding: 14px 0 4px; }
.ssl-panel-head { font-size: 13px; font-weight: 600; margin-bottom: 10px; }

/* Label/value pairs rather than a table: it is a handful of facts about one certificate, and a table
   draws borders around them that suggest rows you can act on. */
.ssl-facts {
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: 6px 16px;
    margin: 0;
    font-size: 13px;
}

.ssl-facts dt { color: var(--text-muted); }
.ssl-facts dd { margin: 0; overflow-wrap: anywhere; }

.ssl-thumb { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; }

.ssl-pill {
    display: inline-block;
    padding: 1px 9px;
    border-radius: 10px;
    font-size: 11.5px;
    font-weight: 500;
    color: #fff;
}

.ssl-pill.good { background: #16a34a; }
.ssl-pill.bad { background: #dc2626; }

/* A command to be copied, so it is selectable, monospaced, and never wrapped mid-flag. */
.ssl-cmd {
    margin-top: 8px;
    padding: 8px 10px;
    border-radius: 4px;
    background: var(--app-bg);
    border: 1px solid var(--border);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
    overflow-x: auto;
    white-space: nowrap;
    user-select: all;
}

/* ---- Settings > Licence ------------------------------------------------------------------------ */

/* Label and value, one fact per line. The panel it replaced put the same three things in a sentence,
   which meant deciding what to say when one of them was absent - so a perpetual licence and one with no
   recorded customer read as different kinds of statement instead of the same list. */
.lic-status {
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: 7px 14px;
    max-width: 460px;
    margin: 0 0 20px;
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--app-bg);
}

.lic-status dt { font-size: 12.5px; color: var(--text-muted); }
.lic-status dd { margin: 0; font-size: 12.5px; }

/* Only the licensed state is coloured. "Free Edition" is a fact about the install, not a fault, and
   colouring it would make every unlicensed page look like it were reporting a problem. */
.lic-status dd.on { color: #16a34a; font-weight: 600; }
.layout.dark .lic-status dd.on { color: #86d9a3; }

.lic-dim { color: var(--text-muted); margin-left: 4px; }

/* What the limited edition actually does, in the same place it says you are running it. A feature
   comparison belongs on a website; this is three numbers and what they mean. */
.lic-limits {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    margin-bottom: 4px;
}

.lic-limits td { padding: 7px 12px 7px 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.lic-limits td:first-child { width: 200px; }
.lic-limits td:nth-child(2) { width: 190px; font-weight: 500; }
.lic-limits td:last-child { color: var(--text-muted); }

/* Wider first column in the dialog than the page had room for: the descriptions wrap less, and the table
   is the only thing in there. */
.mud-dialog .lic-limits td:last-child { color: var(--text); opacity: .8; }

/* A band that keeps its border and its spacing but not its tint. For a section whose content already has
   its own structure - a ruled table - where the tint is a second way of saying the same grouping. */
.bulk-section-plain { background: var(--surface); }

/* A MudTable draws itself on its own elevated white paper. Inside a tinted band that is a card again -
   which is the thing the bands replaced - so tables in a band sit directly on it. The rules between rows
   still separate them; the box around all of them was never doing that. */
.bulk-section .mud-table {
    background: transparent;
    box-shadow: none;
}

.bulk-section .mud-table .mud-table-container,
.bulk-section .mud-table .mud-table-root { background: transparent; }

/* Mud tints its own header row off its palette, which against a band reads as a second surface. */
.bulk-section .mud-table th { background: transparent; }

/* ---- Licence page bands ----
   Tinted sections, the way the bulk import pages divide their steps: the page has two jobs, and on a flat
   white page nothing said where one ended. A band groups without drawing a box around everything. */

/* Three facts across the whole band. There was an edition summary beside them - an icon, "Current edition"
   and a sentence - which said in three lines what the Status row says in one word.

   The list drops its own box here: it carried one from when it sat on a plain white page, and inside a
   tinted band that is a panel drawn inside a panel. */
.lic-band .lic-status {
    margin: 0;
    padding: 0;
    max-width: none;
    border: 0;
    background: transparent;
}

/* ---- Activate band: the ways in, and the two questions that go with them ---- */

/* White, unlike band 1. This band is where the work happens - a key is typed and a button pressed - and a
   form on a tint reads as a preview of a form. The tinted panel moves inside it, onto the aside, which is
   the part that is reference rather than input. */
.lic-band-2 {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 300px);
    gap: 28px;
    align-items: start;
    background: var(--surface);
}

/* Licensed, the aside isn't rendered at all - without this the reserved 300px track stays behind as a gap. */
.lic-band-2.one-col { grid-template-columns: minmax(0, 1fr); }

@media (max-width: 900px) {
    .lic-band-2 { grid-template-columns: minmax(0, 1fr); }
}

.lic-activate { min-width: 0; }

/* One field, so one column. form-grid pairs fields two to a row, which left a licence key - the longest
   single value this app ever asks for - in half the width with nothing beside it. */
.lic-activate .form-grid { grid-template-columns: minmax(0, 1fr); }

/* The tint that band 2 gave up. Same recipe as the bulk pages' step bands, so the two products' sections
   are one colour rather than two blues that nearly match. */
.lic-aside {
    display: flex;
    flex-direction: column;
    gap: 18px;
    padding: 16px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--accent) 5%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 12%, var(--border));
}

.layout.dark .lic-aside {
    background: color-mix(in srgb, #fff 4%, var(--surface));
    border-color: var(--border);
}

.lic-aside-item { display: flex; align-items: flex-start; gap: 11px; }

/* White circles now that the panel behind them is tinted - on the old white panel they were the tint. */
.lic-aside-i {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--surface);
    color: var(--text-muted);
}

.lic-aside-i.info { color: var(--accent); }

.lic-aside-h { font-size: 13px; font-weight: 600; color: var(--text); }
.lic-aside-n { margin: 2px 0 6px; font-size: 12.5px; line-height: 1.5; color: var(--text); opacity: .78; }

/* The link that opens that table. A button because it opens a dialog, drawn as a link because it goes to a
   reference rather than doing anything. The anchor beside it in the aside shares the rule. */
.lic-what { margin: 4px 0 0; }

a.lic-what-link { display: inline-block; }

.lic-what-link {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 13px;
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}

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

.lic-tabs { display: flex; gap: 4px; margin: 16px 0 0; border-bottom: 1px solid var(--border); }

/* ---- Limited edition locks -------------------------------------------------------------------- */

/* Sits where the page's own content would have started rather than centred in an empty page,
   which reads as something that failed to load. The amber edge is the only ornament, and it is there to
   tie this to the Free Edition badge in the title bar. */
.locked {
    max-width: 620px;
    padding: 14px 18px;
    border: 1px solid var(--border);
    border-left: 3px solid #f5b800;
    border-radius: 6px;
    background: var(--surface);
}

.locked-note { margin: 0; font-size: 13px; line-height: 1.6; color: var(--text-muted); }
.locked-msg { margin: 10px 0 0; font-size: 13px; line-height: 1.6; color: var(--text); }
.locked-msg a { color: var(--accent); }

/* Inline, for one locked control inside a form that otherwise works. Quiet on purpose: the rest of the
   form is usable and this must not read as an error. */
.prop-locked {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 10px 12px;
    margin: 2px 0 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--app-bg);
    font-size: 12.5px;
    color: var(--text-muted);
    line-height: 1.6;
}

.prop-locked .mud-icon-root { color: var(--accent); flex: 0 0 auto; margin-top: 1px; }
.prop-locked a { color: var(--accent); }

/* Beside the product name, because that is what it is - the name of this copy, not a warning about
   the page you happen to be on. Amber rather than the app's own blue: the title bar is dark navy and the
   accent is a blue that sits into it, so a badge in the accent disappears exactly where it needs to be
   noticed. Amber is also the one colour the app does not use for anything else, so it cannot be mistaken
   for a status or a warning from the page below. */
.edition-badge {
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 10px;
    margin-left: 10px;
    border-radius: 10px;
    background: #f5b800;
    color: #2b2000;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
    text-decoration: none;
}

.edition-badge:hover { background: #ffc927; color: #2b2000; }

/* Outlined where the edition badge is filled. Both sit in the same place and can appear together, so they
   have to be distinguishable at a glance rather than reading as two of the same thing - and only one of
   them is asking to be acted on. */
.update-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: 20px;
    padding: 0 9px 0 7px;
    margin-left: 10px;
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, .45);
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
    text-decoration: none;
}

.update-badge:hover { background: rgba(255, 255, 255, .14); color: #fff; }

/* The icon ships at 24px with its own line-height, which pushes it off centre inside a 20px pill. */
.update-badge .mud-icon-root { font-size: 14px; width: 14px; height: 14px; }

/* Stands where the New schedule button would be, so the reason the button is missing is in the button's
   own place rather than discovered at save time. */
.sched-limit {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    color: var(--text-muted);
}

.sched-limit .mud-icon-root { color: var(--accent); }
.sched-limit a { color: var(--accent); }

/* Where the Add domain button would be, so the reason it is missing is in its place. */
.dom-limit { margin: 14px 0 0; font-size: 13px; color: var(--text-muted); }
.dom-limit a { color: var(--accent); }

/* ---- Data sync dialog (rebuilt) ---------------------------------------------------------------- */

/* One domain at a time. Stacked, two domains put two facts blocks and two tables in a fixed-height
   dialog, so the second was always cut mid-row and the dialog scrolled inside its own scrolling list. */
.sync-tabs { display: flex; gap: 2px; margin: 14px 0 0; border-bottom: 1px solid var(--border); }

.sync-tab {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 12px;
    border: none;
    border-bottom: 2px solid transparent;
    background: transparent;
    color: var(--text-muted);
    font: inherit;
    font-size: 12.5px;
    cursor: pointer;
}

.sync-tab:hover { color: var(--text); }
.sync-tab.on { color: var(--text); font-weight: 500; border-bottom-color: var(--accent); }
.sync-tab-n { font-size: 11px; color: var(--text-muted); }

/* Four stacked label/value pairs became one line: the labels were taller than the values and each said
   something the value already implies. */
.sync-facts {
    display: flex;
    align-items: center;
    gap: 7px;
    flex-wrap: wrap;
    padding: 11px 0;
    font-size: 12.5px;
    color: var(--text-muted);
    border-bottom: 1px solid var(--border);
}

.sync-facts b { color: var(--text); font-weight: 500; }
.sync-sep { color: var(--border); }

/* ---- The header's status mark ----
   A tick or a cross beside the title, read from across a desk in a way a line of text is not. Tinted circle
   rather than a bare glyph so it reads as a badge and not as a stray icon. */

.sync-mark {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 46px;
    height: 46px;
    border-radius: 50%;
}

.sync-mark.ok { background: #dcfce7; color: #16a34a; }
.sync-mark.fail { background: #fee2e2; color: #dc2626; }
.sync-mark.busy, .sync-mark.none { background: var(--hover); color: var(--text-muted); }

.layout.dark .sync-mark.ok { background: rgba(22, 163, 74, .18); color: #4ade80; }
.layout.dark .sync-mark.fail { background: rgba(220, 38, 38, .18); color: #f87171; }

.sync-head { display: flex; align-items: center; gap: 14px; }

/* ---- Status banner ----
   The one thing on the panel that is a state rather than a value, so it gets a band of its own - with the
   time on the right, because "healthy" and "when" are read together or not at all. */

.sync-banner {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 20px;
    margin-top: 16px;
    padding: 13px 16px;
    border: 1px solid transparent;
    border-radius: 8px;
}

.sync-banner.ok { background: #f0fdf4; border-color: #dcfce7; }
.sync-banner.fail { background: #fef2f2; border-color: #fee2e2; }
.sync-banner.busy, .sync-banner.none { background: var(--app-bg); border-color: var(--border); }

.layout.dark .sync-banner.ok { background: rgba(22, 163, 74, .10); border-color: rgba(22, 163, 74, .28); }
.layout.dark .sync-banner.fail { background: rgba(220, 38, 38, .10); border-color: rgba(220, 38, 38, .28); }

.sync-banner-l { display: flex; align-items: flex-start; gap: 11px; }
.sync-banner-l .sync-dot { margin-top: 6px; }
.sync-banner-r { text-align: right; white-space: nowrap; }

.sync-banner-b { font-size: 14px; font-weight: 600; color: var(--text); }
.sync-banner-n { font-size: 12.5px; color: var(--text); opacity: .78; margin-top: 1px; }

.sync-banner.ok .sync-banner-l .sync-banner-b { color: #15803d; }
.sync-banner.fail .sync-banner-l .sync-banner-b { color: #b91c1c; }
.layout.dark .sync-banner.ok .sync-banner-l .sync-banner-b { color: #4ade80; }
.layout.dark .sync-banner.fail .sync-banner-l .sync-banner-b { color: #f87171; }

/* ---- The three facts ----
   Label above value rather than beside it, so the values line up as a row of answers rather than as the
   tail of three sentences. Divided by rules, not by cards - three boxes for three short values is more
   chrome than content. */

.sync-tiles { display: flex; align-items: stretch; margin: 18px 0 0; }

.sync-tile {
    display: flex;
    align-items: center;
    gap: 11px;
    flex: 1 1 0;
    min-width: 0;
    padding: 4px 16px;
    border-left: 1px solid var(--border);
}

.sync-tile:first-child { border-left: 0; padding-left: 0; }

.sync-tile-i {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--hover);
    color: var(--text-muted);
}

.sync-tile-k { font-size: 11.5px; color: var(--text); opacity: .7; }
.sync-tile-v { font-size: 14px; font-weight: 600; color: var(--text); margin-top: 1px; }

/* ---- The disclosure the log hides behind ----
   A row rather than a bare link: it is the one thing on the panel you can open, and a tinted strip says so
   before the words do. */

.sync-disc {
    display: flex;
    align-items: center;
    gap: 11px;
    margin-top: 16px;
    padding: 10px 14px;
    border-radius: 8px;
    background: var(--app-bg);
    font-size: 13px;
}

.sync-disc.none { background: transparent; padding-left: 0; padding-right: 0; }

.sync-disc-i {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--surface);
    color: var(--accent);
}

.sync-disc.none .sync-disc-i { background: var(--hover); color: var(--text-muted); }

.sync-disc-none { color: var(--text); opacity: .78; }

.sync-disc-btn {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 13px;
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.sync-disc-btn:hover { text-decoration-thickness: 2px; }

/* Points down once the list is open, so the row says which way it will go. */
.sync-disc-chev {
    display: flex;
    margin-left: auto;
    color: var(--text-muted);
    transition: transform .15s;
}

.sync-disc-chev.on { transform: rotate(90deg); }

/* ---- The change log, as a table ----
   Three headed columns instead of a run heading with a burst of rows under it. The heading was doing two
   jobs at once - saying when a pass ran and separating it from the next - and the time is what a reader
   wants beside the object anyway. */

.sync-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }

.sync-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 7px 12px 7px 10px;
    background: var(--app-bg);
    border-bottom: 1px solid var(--border);
    text-align: left;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted);
    white-space: nowrap;
}

.sync-table td {
    padding: 8px 12px 8px 10px;
    border-bottom: 1px solid var(--line, var(--border));
    vertical-align: top;
}

.sync-table tr:last-child td { border-bottom: 0; }

/* Fixed so the two columns that vary - the name and what changed - take the width between them. */
.sync-t-when { width: 118px; white-space: nowrap; color: var(--text); opacity: .82; }
.sync-t-obj { width: 210px; }

/* The type under the name rather than beside it: it qualifies the name, and as a third column it was
   another 74px of width spent on a word that is the same on most rows in a burst. */
.sync-t-obj .sync-o { display: block; font-weight: 500; overflow-wrap: anywhere; }
.sync-t-obj .sync-ty { display: block; margin-top: 1px; }

.sync-t-det { color: var(--text); opacity: .82; overflow-wrap: anywhere; }
.sync-t-det .sync-act { margin-left: 0; margin-right: 6px; }

/* The heading that replaced the Time column: written once per pass, with room for the date. */
.sync-run {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 7px 10px;
    background: var(--app-bg);
    border-bottom: 1px solid var(--border);
    font-size: 11.5px;
    color: var(--text);
    opacity: .82;
}

.sync-run b { color: var(--text); font-weight: 600; font-size: 12px; }

.sync-log {
    margin-top: 12px;
    max-height: 320px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 6px;
}

.sync-row {
    display: grid;
    grid-template-columns: 74px 1fr;
    gap: 12px;
    padding: 7px 10px;
    border-bottom: 1px solid var(--border);
    font-size: 12.5px;
    align-items: baseline;
}

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

.sync-ty {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text);
    opacity: .72;
}

.sync-what { min-width: 0; overflow-wrap: anywhere; }
.sync-o { font-weight: 500; }
/* "description, pwdlastset", "2 added" - the substance of the row and the reason the dialog is open, so it
   gets the page's ink rather than the caption grey. Contrast comes from size, not from fading it out. */
.sync-a { color: var(--text); opacity: .82; margin-left: 6px; }

/* Only for the actions that are NOT an ordinary change - "changed" on nine rows in ten was a column of
   noise, so the word now appears only when it carries information. */
.sync-act {
    margin-left: 6px;
    padding: 0 6px;
    border-radius: 8px;
    background: var(--hover);
    font-size: 10.5px;
    color: var(--text-muted);
}

/* ---- First-run setup wizard --------------------------------------------------------------------- */

.wiz-head { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.wiz-title { font-size: 16px; font-weight: 600; }

.wiz-steps { display: flex; align-items: center; gap: 8px; margin-left: auto; }

.wiz-step {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 12.5px;
    color: var(--text-muted);
}

.wiz-step.on { color: var(--text); font-weight: 500; }

.wiz-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 1px solid var(--border);
    font-size: 11px;
}

.wiz-step.on .wiz-num { border-color: var(--accent); color: var(--accent); }
.wiz-step.done .wiz-num { background: var(--accent); border-color: var(--accent); color: #fff; }

.wiz-line { width: 26px; height: 1px; background: var(--border); }
.wiz-line.done { background: var(--accent); }

.wiz-lede { margin: 0 0 16px; font-size: 13px; line-height: 1.6; color: var(--text-muted); }
.wiz-note { margin-top: 6px; font-size: 12px; line-height: 1.55; }

/* What has arrived so far. Big figures because watching them climb is the whole content of this step. */
.wiz-sync {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 10px;
    margin-bottom: 14px;
}

.wiz-count {
    padding: 12px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--app-bg);
    text-align: center;
}

.wiz-count-n {
    display: block;
    font-size: 20px;
    font-weight: 600;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}

.wiz-count-k { display: block; margin-top: 2px; font-size: 11.5px; color: var(--text-muted); }

.wiz-progress {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--text-muted);
}

.wiz-progress .wiz-ok { color: #16a34a; }
.wiz-progress .wiz-bad { color: #dc2626; }

/* Said before Finish, because it changes who can use the app and there is no undo short of the roster. */
.wiz-owner {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-top: 18px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 6px;
    background: var(--app-bg);
}

.wiz-owner .mud-icon-root { color: var(--accent); flex: 0 0 auto; }
.wiz-owner-t { font-size: 13px; font-weight: 600; }
.wiz-owner-s { margin-top: 2px; font-size: 12.5px; line-height: 1.55; color: var(--text-muted); }

/* One field per row, each the full width of the dialog. Deliberately not .form-grid: two columns put the
   account beside its password and left Domain half-width beside nothing, so the longest values on the
   form - an FQDN and a DOMAIN\user - had the narrowest boxes. */
.wiz-form { display: grid; gap: 6px; }

/* Beside the real total, so "4,812 users · Free Edition - limited to 50 results" reads as one sentence -
   either half alone misleads. Plain text in a muted yellow rather than a filled pill: a solid amber block in
   a toolbar shouts at the reader on every page, and this is a standing fact about the install, not an alert. */
.limited-rows {
    margin-left: 10px;
    font-size: 12px;
    color: #b8860b;
    white-space: nowrap;
    text-decoration: none;
}

.layout.dark .limited-rows { color: #d9b657; }
.limited-rows:hover { text-decoration: underline; }

/* On the About page's version row. Amber for "there is something newer", the same colour the free-edition
   note uses, because both are standing facts about this install rather than faults. */
.upd-new { margin-left: 10px; font-size: 12px; color: #b8860b; }
.upd-new a { margin-left: 6px; color: var(--accent); }
.upd-current { margin-left: 10px; font-size: 12px; color: var(--text-muted); }

/* ---- Password reset (helpdesk) ------------------------------------------------------------------ */

.pwr-find { padding: 4px 0 16px; }
.pwr-search { max-width: 520px; }

.pwr-card {
    max-width: 520px;
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--app-bg);
    margin-bottom: 20px;
}

.pwr-who { display: flex; align-items: center; gap: 12px; }

.pwr-avatar {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent);
    font-size: 12.5px;
    font-weight: 600;
    flex: 0 0 auto;
}

.pwr-name { font-size: 15px; font-weight: 600; }
.pwr-sub { font-size: 12.5px; color: var(--text-muted); }
.pwr-sub span { margin-left: 4px; }

/* The checklist: a question per row, answered yes or no.
   The label column is fixed so every answer starts on the same axis - that alignment is what makes five
   rows scannable rather than five sentences to read. */
.pwr-check {
    display: grid;
    grid-template-columns: 170px 1fr;
    gap: 7px 14px;
    margin: 12px 0 0;
    padding-top: 11px;
    border-top: 1px solid var(--border);
}

.pwr-check dt { font-size: 12.5px; color: var(--text-muted); }
.pwr-check dd { margin: 0; font-size: 12.5px; }

/* Only the bad answers. Yes is not consistently good - locked out yes is bad, enabled yes is fine - so the
   word cannot carry the verdict and this does. Colouring the good ones too would make a healthy account
   five green rows all asking to be read. */
.pwr-check dd.bad { color: #c0332f; font-weight: 600; }
.layout.dark .pwr-check dd.bad { color: #f08a86; }

.pwr-check .pwr-dim { margin-left: 8px; font-weight: 400; color: var(--text-muted); }

/* Last logon and the account options: real, but neither answers "can they sign in", so they sit under the
   checklist rather than adding rows to it that have no verdict. */
.pwr-extra {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 11px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-muted);
}

.pwr-extra b { color: var(--text); font-weight: 500; }
.pwr-sep { color: var(--border); }

.pwr-unavailable {
    margin-top: 12px;
    padding-top: 11px;
    border-top: 1px solid var(--border);
    font-size: 12.5px;
    color: var(--text-muted);
}

.pwr-warn { color: #b8860b; font-size: 12px; }
.layout.dark .pwr-warn { color: #d9b657; }

/* One row, one height. The Field wrapper is deliberately not used here: Mud's dense outlined field reserves
   space beneath itself for helper text, so a button aligned to the bottom of that field sat level with the
   reserved space rather than with the box. A plain input has no such space and the row simply lines up. */
.pwr-form { max-width: 560px; margin-top: 4px; }
.pwr-label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 5px; }

.pwr-pw { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; }

.pwr-pw-box {
    flex: 1 1 auto;
    min-width: 0;
    height: 34px;
    padding: 0 10px;
    border: 1px solid var(--btn-border);
    border-radius: 5px;
    background: var(--surface);
    color: var(--text);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 13.5px;
}

.pwr-pw-box:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.pwr-pw .bulk-btn { height: 34px; flex: 0 0 auto; }

.pwr-run { margin-top: 16px; }

.pwr-done {
    margin-top: 20px;
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-left: 3px solid #16a34a;
    border-radius: 6px;
    background: var(--surface);
    max-width: 560px;
}

.pwr-done-t { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; }
.pwr-done-t .mud-icon-root { color: #16a34a; }

.pwr-shown-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; }

/* Big and monospaced, because it is about to be read down a telephone. */
.pwr-shown {
    display: inline-block;
    padding: 7px 12px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--app-bg);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 16px;
    user-select: all;
}

.pwr-once { margin-top: 8px; font-size: 12px; color: #b8860b; }
.layout.dark .pwr-once { color: #d9b657; }

/* Room between the checkboxes and the row above, and between each other - they were touching the password
   row once the styles that spaced them went missing. */
.pwr-form .upanel-check { margin-bottom: 6px; }

/* ---- Segmented options (bulk update) ------------------------------------------------------------ */

/* Label left, options right, one question per row. As stacked radio groups these three took eleven lines
   against the five of the question beside them, so the column was three times the height of its neighbour
   for no more meaning - and all three ask the same shape of thing. */
.bulk-seg-rows {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 10px 16px;
    align-items: center;
}

.bulk-seg-k { font-size: 12.5px; color: var(--text-muted); }

.bulk-seg {
    display: inline-flex;
    border: 1px solid var(--btn-border);
    border-radius: 6px;
    overflow: hidden;
    background: var(--surface);
    width: fit-content;
}

.bulk-seg-btn {
    padding: 5px 12px;
    border: none;
    background: transparent;
    color: var(--text-muted);
    font: inherit;
    font-size: 12.5px;
    white-space: nowrap;
    cursor: pointer;
}

.bulk-seg-btn + .bulk-seg-btn { border-left: 1px solid var(--btn-border); }
.bulk-seg-btn:hover { color: var(--text); background: var(--hover); }
.bulk-seg-btn.on { background: var(--accent); color: #fff; }

/* "Do not change" selected is a RESTING state, not a chosen one. Painting it like a decision implied one
   had been taken, when it is simply what happens if the reader does nothing - and on this form most people
   change one of the three and leave the others exactly as they found them. */
.bulk-seg-btn.on.keep { background: var(--surface); color: var(--text); font-weight: 600; }
.bulk-seg-btn.on.keep:hover { background: var(--hover); }


/* ---- Sidebar rail and panel (NavMenu) ----
   Was NavMenu.razor.css, a scoped stylesheet. Moved here after renaming the assembly broke the scoping:
   the b-xxxxxxxxxx attribute that pairs the markup with its rules is derived from the assembly, and a
   mismatch takes every rule with it silently - the sidebar renders as an unstyled list of links and
   nothing in the console says why. These class names are unique to this component anyway, so the scoping
   was buying isolation nothing needed. ::deep is gone with it, since these are now ordinary selectors.
   ------------------------------------------------------------------------------------------------- */
/* The rail and the panel beside it. The shell owns the width, so collapsing the panel really does give the
   page the space back rather than leaving an empty column. */
.nav-shell {
    display: flex;
    height: 100%;
    min-height: 0;
}

/* ---- Icon rail: the five sections, always present ---- */

.nav-rail {
    flex: 0 0 76px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 10px 0;
    /* Inherits the sidebar's ground rather than setting its own, so the rail and the panel beside it stay
       one surface — the divider between them is the border, not a colour change. */
    background: transparent;
    border-right: 1px solid var(--border);
}

.rail-btn {
    position: relative;
    width: 66px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 7px 2px;
    border: none;
    border-radius: 8px;
    background: none;
    /* One step down the scale from --nav-icon (#94a3b8), not all the way to --text (#0f172a): these are
       navigation, so they should be legible rather than decorative, but the selected section still has to
       stand out against them. */
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.rail-label {
    font-size: 12px;
    font-weight: 400;
    line-height: 1.1;
    letter-spacing: -0.01em;
    text-align: center;
    white-space: nowrap;
}

.rail-btn:hover { background: var(--hover); color: var(--nav-text); }

.rail-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* The section the panel is showing. Grey rather than the accent: the sidebar is chrome, and a coloured
   fill on it competed with the page beside it for attention. Darker than the hover fill so the two states
   stay distinguishable. */
.rail-btn.on {
    background: color-mix(in srgb, var(--text) 11%, transparent);
    color: var(--text);
}

/* Pinned to the bottom, away from the section icons — it acts on the panel, not on a section. */
.rail-toggle { margin-top: auto; }

/* ---- Panel: one section's items ---- */

/* Sized so the longest label — "Organizational units" — sits on one line. The icon, gap and padding take
   about 45px of this, so the text column is roughly what the old 250px sidebar gave it; the rail is extra
   width rather than a share of the same budget. Collapsing gives all of it back. */
.nav-panel {
    flex: 0 0 244px;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow-y: auto;
}

.nav-list {
    display: flex;
    flex-direction: column;
    padding: 0.5rem 0;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 2.2rem;
    margin: 1px 0.4rem;
    padding: 0 0.6rem;
    border-radius: 8px;
    font-size: 0.85rem;
    color: var(--nav-text);
    text-decoration: none;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.nav-link .nav-icon { color: var(--nav-icon); }
.nav-link:hover { background: var(--hover); }

/* Grey, matching the rail. The fill and a darker ink carry the selection — deliberately NOT weight, which
   changes the label's width and nudges everything after it. */
.nav-link.active {
    background: color-mix(in srgb, var(--text) 11%, transparent);
    color: var(--text);
}

.nav-link.active .nav-icon { color: var(--text); }

.nav-group-header {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 2.2rem;
    margin: 1px 0.4rem;
    padding: 0 0.6rem;
    border-radius: 8px;
    font-size: 0.85rem;
    color: var(--nav-text);
    cursor: pointer;
    user-select: none;
    transition: background 0.15s;
}

.nav-group-header:hover { background: var(--hover); }
.nav-group-header .nav-icon { color: var(--nav-icon); }
.nav-group-header .nav-chevron { color: var(--nav-icon); }
.nav-group-label { flex: 1; }

.nav-children {
    display: flex;
    flex-direction: column;
    margin-bottom: 0.25rem;
}

/* Report names wrap rather than truncate — a clipped "Recently Modified Compu…" is worse than two lines. */
.nav-child {
    padding-left: 2.4rem !important;
    padding-top: 0.3rem !important;
    padding-bottom: 0.3rem !important;
    min-height: 1.9rem !important;
    height: auto !important;
    line-height: 1.25 !important;
    font-size: 0.8rem !important;
}

.nav-subheader {
    padding: 0.5rem 1rem 0.2rem 2.4rem;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--nav-icon);
}

/* The same label one level up, above a group of categories rather than a group of reports.

   A chip around the word, not a band across the panel. align-self keeps it hugging its own text inside the
   column, so the fill marks the label rather than striping the whole sidebar — four full-width fills in a
   menu this short would out-weigh the one row that actually matters, the selected report.

   Lighter than the selected row's fill, so the one row that matters still reads as the strongest thing in
   the panel. */
.nav-subheader-item {
    align-self: flex-start;
    padding: 0.15rem 0.45rem;
    /* A little more air above than below: the gap is what separates this group from the one before it. */
    margin: 1.4rem 0 0.3rem 0.9rem;
    border-radius: 4px;
    border-top: none;
    color: var(--text-muted);
    background: var(--hover);
}

/* Less air above the first one — it sits directly under the panel title. */
.nav-list > .nav-subheader-item:first-child { margin-top: 0.15rem; }

.sidebar-version {
    margin-top: auto;            /* pin to the bottom of the flex-column panel */
    padding: 0.75rem 1.25rem;
    border-top: 1px solid var(--border);
    font-size: 0.7rem;
    color: var(--nav-icon);
}

