/* Shared "record sheet" pattern used by every Details page (Subscriptions, Subscribers,
   Products, and — soon — Services): a single white card with a header/breadcrumb, a
   two-column property sheet of label/value rows, a bottom action bar, and a footer.

   This lives here, in a global stylesheet, rather than in each page's own component CSS
   because Blazor CSS isolation is per-component: three Details pages built on this pattern
   already had near-identical copies of these rules, and a fourth (Services) was about to add
   a fourth copy. Left alone, a tweak to one page (e.g. the label gutter width) silently stops
   matching the others. The rules below are extracted from the three pages' isolated
   stylesheets — identical in all of them — so the pattern is defined once. Anything genuinely
   page-specific (e.g. Subscriptions' bold cost figure, or its inline conflict alert) stays in
   that page's own *.razor.css.

   A note on ::deep: in per-component CSS, reaching into a MudBlazor component (which never
   carries the scope attribute) requires `::deep` from a real scoped HTML ancestor. There is no
   scope attribute at all in a global stylesheet, so `::deep` is neither needed nor valid here —
   it has been stripped from every rule moved into this file. Where the ancestor in the original
   rule was a real DOM parent (not just a scoping trick), it has been kept as an ordinary
   descendant selector, both because that's the true structure and because it preserves the
   original rule's specificity relative to MudBlazor's own CSS (see the note on
   `.sd-row .sd-value .mud-input-control` below). */

/* Single white card on the page's light ground. The status chips in the header state every
   fact in words, so there is no colour-only spine — the card is a plain block. */
.sd-card {
    background: var(--mud-palette-white);
    border: 1px solid #e4e7ea;
    border-radius: 10px;
    overflow: hidden;
}

.sd-content {
    min-width: 0;
}

/* Page-level navigation lives above the card, matching Index.razor's Back button so the two
   pages read as one system. Record-level actions (Save/Resend/Delete/etc.) stay in the card's
   own action bar. .sd-page is a plain scoped div in each page that directly wraps the
   MudButton — a real ancestor, not just a scoping anchor — so it is kept as a descendant
   selector with ::deep dropped (there is no scope attribute here to drop it *for*). */
.sd-page .sd-page-back-btn {
    margin-bottom: 12px;
}

/* ---------------- Header ---------------- */
.sd-header {
    padding: 14px 17px;
    border-bottom: 1px solid #e4e7ea;
}

.sd-header-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
}

.sd-header-titles {
    min-width: 0;
}

.sd-crumb {
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
}

/* .sd-crumb is the real parent div wrapping the MudLink; kept as a descendant selector (::deep
   dropped) for the same reason as .sd-page above. */
.sd-crumb .sd-crumb-link {
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
}

.sd-subline {
    margin-top: 2px;
    font-size: 13.5px;
    color: var(--mud-palette-text-secondary);
}

.sd-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
}

/* Chips are plain spans (not MudChip), so this treatment needs no ::deep in either isolated or
   global CSS — they are real HTML.

   A PILL WITH NO BORDER, which is the whole point of the shape. These were a 6px radius with a 1px
   hairline, and that combination reads as a small button rather than as a tag — somebody looks for
   what happens when they click it. The radius alone does not fix it; the visible edge is what says
   "control", so the edge is what had to go.

   THE TINTS ARE DEEPER THAN THEY LOOK LIKE THEY SHOULD BE, deliberately. They used to be nearly
   white because the hairline was defining the shape; with the border gone a #eef5f0 fill is a faint
   smudge on a white card, so each one moved a step darker. Text colours did not move, so contrast
   stays well clear of AA. Lighten these again and the chips stop reading as objects.

   THERE IS NO STATUS DOT, and removing it lost nothing: every label is a full phrase — "Inactive",
   "Acknowledgement required", "Failed" — per the craft rule that chips are never abbreviations. The
   dot restated what the word already said. Do not reintroduce one to carry meaning the label should
   carry itself; if a state needs a dot to be understood, the label is wrong. */
.sd-chip {
    display: inline-flex;
    align-items: center;
    font-size: 12.5px;
    font-weight: 500;
    padding: 2px 10px;
    border-radius: 999px;
    white-space: nowrap;
}

.sd-chip.ok { background: #e3efe8; color: #1b4a2b; }

.sd-chip.att { background: #fbeed3; color: #7a5108; }

.sd-chip.off { background: #eceef1; color: #454b52; }

/* No Details-page fact is an error, which is why this tint did not exist at first. A failed billing
   record is one. Derived from the same #b42318 as the Failed tab's dot, and deliberately not the
   brand red (#BA0C2F), which means "WashU" everywhere else and should not also mean "broken". */
.sd-chip.err { background: #fbe6e4; color: #8a271e; }

/* ---------------- Property sheet ---------------- */
.sd-sheet {
    display: grid;
    grid-template-columns: 1fr 1fr;
}

.sd-col {
    display: flex;
    flex-direction: column;
    padding: 4px 17px 8px;
}

/* The Notes section (right column) absorbs whatever vertical slack is left once the grid row
   height is set by the taller column, so the two columns land roughly level regardless of which
   optional Billing rows (Free Tier / Subsidized / standard) are showing. */
.sd-section-grow {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

.sd-row-grow {
    flex: 1 1 auto;
    align-items: stretch;
}

.sd-value-grow {
    display: flex;
}

/* Comments gets real room: a generous minimum height plus MudBlazor's AutoGrow so it can still
   expand further for longer notes. Overrides the shared 260px input cap (see note above on
   .sd-row .sd-value .mud-input-control) because a notes box benefits from the column's full
   width, not just its height. */
.sd-row.sd-row-grow .sd-value.sd-value-grow .mud-input-control {
    max-width: none;
    width: 100%;
}

.sd-notes-field textarea {
    min-height: 220px;
    height: 100%;
}

.sd-col + .sd-col {
    border-left: 1px solid #e4e7ea;
}

.sd-section {
    padding: 14px 0;
}

.sd-section + .sd-section {
    border-top: 1px solid #e4e7ea;
}

.sd-section-title {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 8px;
}

/* A variant of .sd-section-title, worn alongside it rather than instead of it, for the one
   section title on a record sheet that names an action instead of a category of data -- the PHI
   Exceptions Decide panel, where Approve, Grant Secure, Take under review and Decline all live.
   At the same muted grey and 12px weight as every other title it read as one more static label
   among the rest of the form, which is what asked for this: bigger, heavier, and in the theme's
   own primary colour rather than the neutral grey the data labels use. Still no fill, border or
   icon -- restrained, the same idiom as the rest of the sheet, just louder within it. Compound
   selector so it wins over the plain .sd-section-title rule above without !important. */
.sd-section-title.sd-section-title-action {
    font-size: 15px;
    font-weight: 700;
    color: var(--mud-palette-primary);
}

/* 170px label gutter, muted label, value inline, shared by every Details page so the eye
   tracks one edge across the whole app. Editable Mud inputs sit dense/outlined right in the
   value cell, so it reads as one property sheet rather than a form. */
.sd-row {
    display: grid;
    grid-template-columns: 170px 1fr;
    align-items: center;
    gap: 8px;
    min-height: 30px;
    padding: 3px 0;
}

.sd-label {
    font-size: 12.5px;
    color: var(--mud-palette-text-secondary);
}

.sd-value {
    font-size: 13.5px;
    overflow-wrap: anywhere;
}

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

.sd-tag {
    font-size: 11px;
    margin-left: 6px;
}

/* Money, dates, IDs and codes get tabular figures so columns of numbers align. */
.num {
    font-variant-numeric: tabular-nums;
}

/* Dense outlined Mud inputs living inline in a value cell — constrain their width so they
   don't stretch the whole column, and kill the default top margin MudBlazor reserves for a
   floating label (this layout supplies the label externally, in .sd-label).
   Specificity note: MudBlazor ships `.mud-input-control.mud-input-control-margin-dense.mud-input-outlined-with-label
   { margin-top: 8px; ... }`, a 3-class compound selector (0,3,0) — all three classes on the
   same element, no descendant combinator. The
   original isolated rule was `.sd-value ::deep .mud-input-control`, which compiles to
   `.sd-value[b-xxx] .mud-input-control` — also (0,3,0) (the scope attribute counts as a third
   selector) — and won only because app CSS loads after MudBlazor's. A naive global rewrite of
   `.sd-value .mud-input-control` would drop to (0,2,0) and start losing that tie. Every
   `.sd-value` in this pattern sits directly inside a `.sd-row` (real markup structure, not an
   anchor added for specificity), so `.sd-row` is added back here to restore the original
   3-selector count and keep winning under MudBlazor's own stylesheet, which still loads first
   in Components/App.razor. */
.sd-row .sd-value .mud-input-control {
    max-width: 260px;
    margin-top: 0;
}

/* ---------------- Action bar ---------------- */
.sd-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 17px;
    background: #fbfcfd;
    border-top: 1px solid #e4e7ea;
}

/* .sd-actions is the real parent div wrapping these MudButtons; kept as a descendant selector
   (::deep dropped) for the same reason as .sd-page above. Neither compound selector below
   targets a mud-* class, but these buttons render as a MudButton root carrying
   `mud-button-text mud-button-text-default`, whose `color` rule is also (0,2,0) — a genuine
   specificity tie that would otherwise be decided by load order alone. .sd-card (real markup:
   these buttons' actual ancestor, one level above .sd-actions) is added back here so the
   selector reaches (0,3,0) and wins on specificity rather than on load order. */
.sd-card .sd-actions .sd-quiet-btn {
    color: var(--mud-palette-text-secondary);
}

.sd-card .sd-actions .sd-delete-btn {
    margin-left: auto;
    color: var(--mud-palette-text-secondary);
    text-decoration: underline;
}

.sd-confirm {
    padding: 10px 17px;
    background: #fdf6e8;
    border-top: 1px solid #eeddb6;
}

/* ---------------- Footer ---------------- */
.sd-footer {
    padding: 10px 17px;
    border-top: 1px solid #e4e7ea;
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
}

/* ---------------- Attachment panels ----------------
   Shared "related data" panels pattern: a group of sections describing records attached to
   the one being viewed (Services Details' Acceptance Content / Entra Group Mappings / Email
   Templates today; other Details pages may grow their own attachment panels later). Matches
   the flat, hairline-divided treatment of the property sheet above it — plain divs, no
   MudCard elevation/rounding/margins — so the group reads as part of the record rather than
   widgets bolted onto it. */
.sd-panels {
    padding: 4px 17px 14px;
    border-top: 1px solid #e4e7ea;
}

/* That top border separates the panel stack from the property sheet above it on a Details page.
   On Admin Settings the stack IS the card's content, so there is nothing above it to separate
   from and the border would just double up on the card's own edge. */
.sd-card > .sd-panels:first-child {
    border-top: none;
}

.sd-panels-heading {
    padding: 14px 0 10px;
}

.sd-panels-heading .sd-section-title {
    margin-bottom: 4px;
}

.sd-panels-heading-sub {
    font-size: 13px;
    color: var(--mud-palette-text-secondary);
}

.sd-panel {
    padding: 14px 0;
}

.sd-panel + .sd-panel {
    border-top: 1px solid #e4e7ea;
}

/* Same hairline, bridging the one other adjacent-panel boundary that the selector above cannot
   reach: a .sd-panel-pair (two panels side by side) is not itself a .sd-panel, so "Requester and
   request" and "Their answers" sat directly against each other with nothing marking the zone
   boundary the design otherwise draws everywhere else. */
.sd-panel-pair + .sd-panel {
    border-top: 1px solid #e4e7ea;
}

.sd-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
}

.sd-panel-header-titles {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Row/entry count, visible at rest so scale — including zero — reads without opening the
   table or scanning an empty state for it. */
.sd-count-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 18px;
    padding: 0 6px;
    border-radius: 9px;
    background: #f3f4f6;
    color: var(--mud-palette-text-secondary);
    font-size: 11.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Demoted from the filled/outlined buttons these used to be: a quiet text action so it
   doesn't compete with Save, the page's one primary action. .sd-card is added back for the
   same specificity reason documented on .sd-quiet-btn/.sd-delete-btn above — MudButton's own
   text-variant color rule is a (0,2,0) compound selector on the rendered root. */
.sd-card .sd-panel-header .sd-panel-action {
    color: var(--mud-palette-text-secondary);
}

.sd-panel-caption {
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--mud-palette-text-secondary);
}

.sd-panel-body {
    margin-top: 10px;
}

/* Acceptance Content holds a single configured value, not a list, so it is presented as
   plain label/value facts (matching .sd-row's rhythm) rather than a one-row table. */
.sd-fact-row {
    display: flex;
    gap: 8px;
    align-items: baseline;
    padding: 3px 0;
    font-size: 13.5px;
}

.sd-fact-row .sd-label {
    flex: none;
    width: 170px;
    font-size: 12.5px;
    color: var(--mud-palette-text-secondary);
}

.sd-fact-row .sd-value {
    overflow-wrap: anywhere;
}

/* --- Question and answer rows ("Their answers") -------------------------------------------------
   The question on the left, with the prompt the person was shown; their answer on the right. The
   answer column gets the larger share because it holds the prose. */
.sd-qa-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 10px;
}

.sd-qa {
    display: grid;
    grid-template-columns: minmax(200px, 2fr) 3fr;
    gap: 6px 24px;
    align-items: start;
}

/* The question's name. Same size and colour as .sd-label, so the panel reads as one set of
   labels; the weight is what lets a reviewer find each question in a run of answers. */
.sd-answer-label {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
    padding-bottom: 2px;
}

.sd-qa-prompt {
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--mud-palette-text-secondary);
}

/* The heading over the three tool rows, as on the form. A rule above it marks where the general
   questions end and the per-tool ones begin. */
.sd-qa-group {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
    border-top: 1px solid #e4e7ea;
    padding-top: 12px;
}

/* Empty states name the consequence of the absence, not just the absence itself, so they
   stay short but actionable. */
.sd-empty-title {
    font-size: 13.5px;
    font-weight: 600;
}

.sd-empty-sub {
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--mud-palette-text-secondary);
}

/* Strip MudTable's card-like elevation/rounded corners and restyle it to match the flat,
   hairline-divided property sheet above. Scoped to .sd-panel-table (applied via the
   component's Class parameter) rather than to mud-table globally, so any other MudTable
   elsewhere in the app is untouched. No ::deep is used or valid here: this is a global
   stylesheet, and MudBlazor's rendered markup never carries a CSS-isolation scope attribute
   regardless of where the rule lives. MudTable's own .mud-table-container already scrolls
   horizontally on overflow, which is what keeps a full, unwrapped EntraGroupId from ever
   forcing the page body itself to scroll sideways — see .sd-id-caption below, which wraps
   long ids first and only relies on that built-in scroll as a fallback. */
.sd-panel-table.mud-table {
    background: transparent;
    border-radius: 0;
    box-shadow: none;
}

.sd-panel-table thead .mud-table-cell {
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    border-bottom: 1px solid #e4e7ea;
}

.sd-panel-table tbody .mud-table-row {
    background: transparent;
}

.sd-panel-table tbody .mud-table-cell {
    font-size: 13.5px;
    border-bottom: 1px solid #eef0f2;
    white-space: normal;
    overflow-wrap: anywhere;
}

/* The rule above wraps anywhere so a full Entra group id can break rather than clip, which is right
   for ids and wrong for short phrases: "8 services" was breaking into "8" / "services", where the
   number reads as its own value. Kept as a (0,4,0) override rather than by relaxing the rule above,
   which the group id column depends on. */
.sd-panel-table tbody .mud-table-cell.sg-nowrap {
    white-space: nowrap;
    overflow-wrap: normal;
}

.sd-panel-table tbody .mud-table-row:hover {
    background: #f7f8f9;
}

/* Full EntraGroupId, never truncated: tabular figures, and room to wrap rather than clip or
   ellipsize. */
.sd-id-caption {
    display: block;
    white-space: normal;
    overflow-wrap: anywhere;
}

.sd-actions-col {
    text-align: right;
    white-space: nowrap;
}

/* Hover-revealed row actions. Hidden at rest via opacity (not display/visibility), so the
   element stays in the layout, stays focusable, and the row never reflows on hover. Revealed
   on row hover; on :focus-within (a keyboard user who has tabbed into either button in the
   row); and unconditionally under (hover: none) so touch users — who can never "hover" — are
   not locked out. Each button's accessible name is set per-row in the markup (e.g.
   aria-label="Remove ADOBE-CC-USERS mapping"), not here. */
.sd-row-actions {
    display: inline-flex;
    gap: 2px;
    opacity: 0;
    transition: opacity 120ms ease;
}

.sd-panel-table tbody .mud-table-row:hover .sd-row-actions,
.sd-panel-table tbody .mud-table-row:focus-within .sd-row-actions {
    opacity: 1;
}

.sd-row-actions .mud-icon-button:focus-visible {
    opacity: 1;
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

@media (hover: none) {
    .sd-row-actions {
        opacity: 1;
    }
}

/* Inline per-row removal confirmation (Entra Group Mappings / Email Templates). Once a row is
   pending confirmation it must stay fully visible no matter where the pointer goes -- otherwise
   the user nudges off the row, the cell fades per the hover rules above, and they are left with
   a pending destructive confirmation they can neither see nor cancel. `.is-confirming` is added
   as a second class on the same element that already carries `.sd-row-actions` (specificity
   0,1,0 at rest), making the selector below 0,2,0. That beats every rule that can hide the
   actions at rest: the base `.sd-row-actions { opacity: 0 }` (0,1,0) and the
   `@media (hover: none) { .sd-row-actions { opacity: 1 } }` rule (also 0,1,0, and already 1
   anyway). It does not need to out-rank the `:hover`/`:focus-within` reveal rules (0,4,1) because
   those also set opacity: 1 -- there is no conflict to win, only the "hidden at rest" rules need
   beating, and 0,2,0 > 0,1,0 does that without !important. */
.sd-row-actions.is-confirming {
    opacity: 1;
    gap: 8px;
}

.sd-row-confirm-q {
    font-size: 12.5px;
    white-space: normal;
}

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

/* ---------------- Grid composite columns ----------------
   Used by the Subscriptions Index grid to compose Person/Service identity into a two-line
   cell (primary line + demoted secondary line), matching the Details pages' label/value
   voice instead of inventing a new one. */
.sg-primary {
    font-size: 13.5px;
}

.sg-secondary {
    margin-top: 2px;
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
}

/* Chips inline in a grid cell rather than under a header, so the header-only top margin from
   .sd-chips is removed and wrapping is allowed within the narrower column. */
.sg-chips {
    margin-top: 0;
}

.sg-cost {
    text-align: right;
    font-weight: 600;
}

/* A secondary line under a cost shares its right edge -- otherwise the amount is flush right and
   the cost center under it is flush left, and the two stop reading as one cell. */
.sg-cost + .sg-secondary {
    text-align: right;
}

/* Date ranges and other pairs that must not be split across lines. A bare date range wrapping
   leaves the year's last digits orphaned on their own line, which reads as a second value. */
.sg-nowrap {
    white-space: nowrap;
}

/* ---------------- Search omnibox ----------------
   The Subscriptions filter bar: one bordered region holding a token per active filter and a
   free-text input. Tokens are plain spans and buttons (not MudChip), so these rules need no
   ::deep and live here globally rather than in a page-local sheet. The chip vocabulary is
   .sd-chip's -- same 6px radius, same 12.5px weight-600 text, same tinted hairline -- so a
   filter token reads as a relative of the state chips in the grid it filters. */
.sd-omni-bar {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.sd-omni {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    border: 1px solid #d4d8dd;
    border-radius: 8px;
    background: var(--mud-palette-surface);
    cursor: text;
}

/* Focus lives on the whole region, not the bare input, so the box reads as one control. The
   inner input's own outline is removed for the same reason. */
.sd-omni.is-focused {
    border-color: var(--mud-palette-primary);
    box-shadow: 0 0 0 2px rgba(160, 27, 62, 0.12);
}

.sd-omni-field {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
    padding: 2px 0;
}

.sd-omni-input {
    flex: 1 1 180px;
    min-width: 140px;
    border: none;
    outline: none;
    background: transparent;
    font: inherit;
    font-size: 13.5px;
    color: var(--mud-palette-text-primary);
    padding: 3px 2px;
}

.sd-omni-token {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    font-weight: 600;
    padding: 2px 4px 2px 8px;
    border-radius: 6px;
    max-width: 100%;
    background: #f3f4f6;
    color: #3c434a;
    border: 1px solid #e1e4e8;
}

/* An exclusion is announced in words ("not Service Adobe"), not by colour alone -- the tint
   below is a second signal, never the only one. */
.sd-omni-token.neg {
    background: #fdf1f1;
    color: #7a2020;
    border-color: #eecccc;
}

.sd-omni-not {
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 10.5px;
}

.sd-omni-key {
    font-weight: 400;
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
}

.sd-omni-token.neg .sd-omni-key {
    color: #97544f;
}

.sd-omni-val {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 240px;
}

.sd-omni-x {
    border: none;
    background: transparent;
    color: inherit;
    opacity: 0.55;
    cursor: pointer;
    font-size: 15px;
    line-height: 1;
    padding: 0 4px;
    border-radius: 4px;
}

.sd-omni-x:hover,
.sd-omni-x:focus-visible {
    opacity: 1;
    background: rgba(0, 0, 0, 0.06);
}

/* Counts sit at the trailing edge with tabular figures so the number does not jitter as the
   result set changes width. */
.sd-omni-meta {
    display: flex;
    gap: 10px;
    flex: none;
    font-size: 12px;
    white-space: nowrap;
}

/* Errors are advisory: the valid part of a query has already been applied, so they are listed
   compactly beneath the box rather than as a blocking wall. */
.sd-omni-errors {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.sd-omni-err {
    font-size: 12px;
    padding: 2px 8px;
    border-radius: 6px;
    background: #fdf1f1;
    color: #7a2020;
    border: 1px solid #eecccc;
}

.sd-omni-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

.sd-omni-panel {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 4px 16px;
    padding: 12px 14px 14px;
    border: 1px solid #e4e7ea;
    border-radius: 8px;
}

.sd-omni-cell {
    min-width: 0;
}

/* The popup is absolutely positioned against this, not against .sd-omni, so the box itself keeps
   overflow visible and the list can extend past the bar without resizing it. */
.sd-omni-wrap {
    position: relative;
}

.sd-omni-help {
    flex: none;
    width: 22px;
    height: 22px;
    border: 1px solid #d4d8dd;
    border-radius: 50%;
    background: transparent;
    color: var(--mud-palette-text-secondary);
    font-size: 12px;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    padding: 0;
}

.sd-omni-help:hover,
.sd-omni-help:focus-visible {
    border-color: var(--mud-palette-primary);
    color: var(--mud-palette-primary);
}

/* ---------------- Suggestions ---------------- */
.sd-omni-suggest {
    position: absolute;
    z-index: 30;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    /* Sized so the longest list OmniboxSuggestions can build fits without scrolling: the arrow keys
       move the active row, and pulling an off-screen row back into view would need JS interop this
       component deliberately does not have. Worst case is a bare word — Person, Service, Product,
       Filter, each capped at OmniboxSuggestions.DefaultCap = 5, plus the free-text row:
       4 x (19.65 header + 5 x 24.9 row) + (19.65 + 24.9) + 10 (padding + border) = 631.15px.
       800px leaves room to raise the cap again. Covering more of the grid while typing is an
       accepted cost; on a short laptop screen the longest list runs below the fold.
       overflow-y stays: if a future group escapes the cap the list scrolls rather than spills. */
    max-height: 800px;
    overflow-y: auto;
    padding: 4px 0;
    border: 1px solid #d4d8dd;
    border-radius: 8px;
    background: var(--mud-palette-surface);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
}

/* The group header carries the "showing 5 of 12" note, so a trimmed list says so in the same place
   a screen reader reads the group name. */
/* Heights here are load-bearing — see the arithmetic on .sd-omni-suggest — so line-height is
   stated rather than inherited: header 5 + 1 + 10.5 x 1.3 = 19.65px. */
.sd-omni-grp {
    padding: 5px 12px 1px;
    line-height: 1.3;
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--mud-palette-text-secondary);
}

/* Row height is fixed by the same arithmetic: 4 + 4 + 13 x 1.3 = 24.9px. */
.sd-omni-opt {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 4px 12px;
    line-height: 1.3;
    font-size: 13px;
    cursor: pointer;
}

.sd-omni-opt:hover {
    background: #f3f4f6;
}

/* The active option is a keyboard position, not a hover -- it needs a border as well as a tint so
   it is still distinguishable where colour is not. */
.sd-omni-opt.is-active {
    background: #f6eaee;
    box-shadow: inset 2px 0 0 var(--mud-palette-primary);
}

.sd-omni-opt-main {
    font-weight: 600;
    color: var(--mud-palette-text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* What the typed words matched. The name is already semi-bold, so weight could not say it; the
   tint is the att chip's, so the highlight reads as part of the same system. No padding: the row
   height is load-bearing (see .sd-omni-suggest), and a mark must not change it. */
.sd-omni-hit {
    background: #fbeed3;
    color: inherit;
    border-radius: 2px;
    padding: 0;
}

.sd-omni-opt-sub {
    font-size: 11.5px;
    color: var(--mud-palette-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Live regions and the combobox hint: read aloud, never drawn. Not display:none -- that would take
   them out of the accessibility tree along with the layout. */
.sd-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---------------- Search syntax help dialog ---------------- */
.sd-help {
    font-size: 13.5px;
    line-height: 1.55;
}

.sd-help code,
.sd-help kbd {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12.5px;
    padding: 1px 5px;
    border-radius: 4px;
    background: #f3f4f6;
    border: 1px solid #e1e4e8;
    white-space: nowrap;
}

.sd-help-lede {
    margin: 0 0 4px;
    color: var(--mud-palette-text-secondary);
}

.sd-help-h {
    margin: 18px 0 6px;
    font-size: 11.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--mud-palette-primary);
}

.sd-help p {
    margin: 0 0 6px;
}

.sd-help-table {
    width: 100%;
    border-collapse: collapse;
}

.sd-help-table th,
.sd-help-table td {
    text-align: left;
    vertical-align: top;
    padding: 3px 10px 3px 0;
    font-weight: 400;
}

.sd-help-table thead th {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
    border-bottom: 1px solid #e4e7ea;
}

.sd-help-list {
    margin: 0;
    padding-left: 18px;
}

.sd-help-list li {
    margin-bottom: 4px;
}

/* The already-holds-fallback explanation on the PHI Exceptions Decide panel, previously a
   MudAlert Info fill. Rendered as plain text instead: the shaded box was the one tinted surface
   in that area, and this is a fact for the reviewer to read before choosing, not a warning to
   set apart. Same size and colour as the rest of the panel's copy -- plain except for the bold
   headline .sd-help-list already supplies the bullets for. */
.sd-decide-note {
    font-size: 13.5px;
    line-height: 1.5;
}

/* The one worked example that stays open, set off from the filter table above it. */
.sd-help-eg-lead {
    margin-top: 10px;
}

/* Progressive disclosure in the two search-syntax dialogs. A new user needs three things: the
   key:value shape, the list of filters, and one worked example. Everything else is reference
   material they come back for, so it starts closed and the dialog opens short instead of opening
   as a wall of rules.

   Native <details>/<summary> rather than a component: it is keyboard-operable and announced as a
   disclosure with no state of our own to keep in step, and it still works if scripting is having
   a bad day. */
.sd-help-more {
    border-top: 1px solid #e4e7ea;
}

.sd-help-more > summary {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 0;
    cursor: pointer;
    font-size: 11.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--mud-palette-primary);
    /* Both, because Safari uses its own marker pseudo-element and ignores list-style. */
    list-style: none;
}

.sd-help-more > summary::-webkit-details-marker {
    display: none;
}

/* Drawn rather than a glyph so it is identical across browsers and can rotate. */
.sd-help-more > summary::after {
    content: "";
    width: 0;
    height: 0;
    border-left: 4px solid currentColor;
    border-top: 3.5px solid transparent;
    border-bottom: 3.5px solid transparent;
    transition: transform 120ms ease;
}

.sd-help-more[open] > summary::after {
    transform: rotate(90deg);
}

.sd-help-more > summary:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
    border-radius: 3px;
}

/* Breathing room under an opened section, without margins on the closed summary. */
.sd-help-more[open] > :last-child {
    margin-bottom: 12px;
}

@media (prefers-reduced-motion: reduce) {
    .sd-help-more > summary::after {
        transition: none;
    }
}

.sd-help-eg {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px;
}

.sd-help-note {
    color: var(--mud-palette-text-secondary);
    margin-top: 6px !important;
}

/* ---------------- Tabs ----------------
   MudBlazor's default tab is uppercase text whose only selected state is `color: primary` plus a
   2px slider. With this theme that reads as WashU red on near-black at small caps — two dark
   colours carrying the whole signal — so which tab you were on was genuinely hard to see.

   These are ordinary tabs, not a segmented control: a rule under the whole strip, the selected tab
   underlined onto it, hairline separators between the rest. Selection is still carried by more than
   one channel — underline, weight, text colour and an inverted count — so it survives even where the
   red reads as just another dark colour.

   Every rule is scoped under .sd-tabs (applied to MudTabs' own Class, which lands on the root
   .mud-tabs element) and reaches its target as a descendant. That puts the compound selectors at
   (0,3,0) against MudBlazor's own (0,2,0) `.mud-tab.mud-tab-active`, so these win on specificity
   rather than on stylesheet load order — the same reasoning documented on .sd-quiet-btn. */
.sd-tabs .mud-tabs-tabbar {
    background: transparent;
    border-bottom: 1px solid #e4e7ea;
}

/* width:100% on .mud-tab makes every tab share the strip evenly; auto lets each one size to its
   own label so the four read as distinct targets rather than four equal blank areas. */
.sd-tabs .mud-tab {
    width: auto;
    min-height: 38px;
    padding: 8px 16px;
    gap: 8px;
    /* Reserved transparent so activating a tab doesn't shift its label by two pixels, and pulled
       down one pixel so the active underline lands ON the strip's rule rather than above it —
       otherwise the two hairlines stack as a visible double line. */
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    font-size: 13px;
    font-weight: 500;
    letter-spacing: normal;
    text-transform: none;
    color: var(--mud-palette-text-secondary);
}

/* Separators sit between tabs, never before the first, and are inset vertically so they read as
   light dividers rather than as a full grid.

   Drawn on the wrapper MudBlazor puts around each tab, NOT on .mud-tab itself: every tab is the
   only child of its own wrapper div, so `.mud-tab + .mud-tab` matches nothing — the tabs are never
   each other's siblings. .mud-tab also carries .mud-ripple, which is overflow:hidden and would clip
   the pseudo-element even where the selector did match. Matched as `> * + *` rather than by the
   wrapper's utility class name so this survives that class changing. */
.sd-tabs .mud-tabs-tabbar-wrapper > * + * {
    position: relative;
}

.sd-tabs .mud-tabs-tabbar-wrapper > * + *::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 1px;
    height: 16px;
    background: #e4e7ea;
}

.sd-tabs .mud-tab.mud-tab-active {
    border-bottom-color: var(--mud-palette-primary);
    color: var(--mud-palette-text-primary);
    font-weight: 600;
}

.sd-tabs .mud-tab:hover:not(.mud-tab-active) {
    color: var(--mud-palette-text-primary);
    border-bottom-color: #d8dce0;
}

/* The count inverts on the selected tab only, so the eye has a second place to confirm which
   filter is live without re-reading the labels. */
.sd-tabs .mud-tab.mud-tab-active .sd-count-pill {
    background: var(--mud-palette-text-primary);
    color: var(--mud-palette-white);
}

/* Status dot on the billing tabs. Redundant with the label beside it, never the only signal — it
   exists so the four tabs are told apart at a glance, not so colour means something on its own.
   The chips used to carry a matching .sd-cd disc; that one is gone (see .sd-chip), and this is now
   the only status dot in the design system. It survives because a TAB STRIP is scanned as a set,
   where a colour cue does work the label alone cannot; a chip is read one at a time. */
.sd-tabs .sd-tab-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex: none;
}

.sd-tabs .sd-tab-dot.pending { background: #8f5f09; }
.sd-tabs .sd-tab-dot.exported { background: #a8aeb4; }
.sd-tabs .sd-tab-dot.confirmed { background: #215732; }

/* The chip vocabulary has ok/att/off but no error tint, because no Details-page fact is an error.
   A failed billing record is one. This is deliberately not the brand red (#BA0C2F) — that colour
   means "WashU" everywhere else in the app, and reusing it for failure would blur both. */
.sd-tabs .sd-tab-dot.failed { background: #b42318; }

/* ---------------- Responsive ---------------- */
@media (max-width: 900px) {
    .sd-sheet {
        grid-template-columns: 1fr;
    }

    .sd-qa {
        grid-template-columns: 1fr;
    }

    .sd-col + .sd-col {
        border-left: none;
        border-top: 1px solid #e4e7ea;
    }
}

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

/* --- Two panels side by side ---------------------------------------------------------------
   Panels normally stack, which is right when each one is a different KIND of thing. A pair that
   answers "who is asking" and "what they asked for" is read together, so on a wide screen they sit
   together. Collapses back to the stack below 1100px, where two columns of 170px labels and their
   values no longer fit without wrapping every value. */
.sd-panel-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 28px;
}

.sd-panel-pair > .sd-panel + .sd-panel {
    /* The stacked rule draws a top border between consecutive panels. Side by side that line lands
       across the top of the right-hand column, which reads as a heading rule that is not there. */
    border-top: none;
}

@media (max-width: 1100px) {
    .sd-panel-pair {
        grid-template-columns: 1fr;
    }

    .sd-panel-pair > .sd-panel + .sd-panel {
        border-top: 1px solid #e4e7ea;
    }
}

/* Narrower labels inside a pair: each column is about half as wide, and 170px of label against a
   short value like a job title leaves the value looking detached from it. */
.sd-panel-pair .sd-fact-row .sd-label {
    width: 120px;
}

/* --- The requester card ---------------------------------------------------------------------
   The identity block at the top of the requester panel: avatar, name, and how to reach them. A
   reviewer's decision is partly about WHO is asking, so this is given the weight of a person rather
   than of another labelled row. */
.sd-person {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding-bottom: 12px;
}

.sd-person-avatar {
    flex: none;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    font-weight: 500;
    letter-spacing: 0.5px;
    color: #fff;
    /* Hue comes from the person (PersonCard.AvatarHue, inline as a custom property); saturation and
       lightness are fixed here so every avatar sits at the same muted weight as the rest of the
       sheet and none can come out fluorescent. Deliberately NOT the theme accent -- an avatar is
       not a status, and tinting them all alike makes every requester look like a highlighted row.
       Falls back to a slate if the property is absent. */
    background: hsl(var(--sd-avatar-hue, 210) 32% 42%);
    user-select: none;
}

.sd-person-identity {
    min-width: 0;
}

.sd-person-name {
    font-size: 16px;
    font-weight: 500;
    line-height: 1.25;
    overflow-wrap: anywhere;
}

.sd-person-secondary {
    /* display:block so this can be a SPAN wherever it sits inside another span -- a div inside a
       span is invalid HTML, and the browser closes the span early and hoists the div out of it. */
    display: block;
    font-size: 12.5px;
    color: var(--mud-palette-text-secondary);
    overflow-wrap: anywhere;
}

/* Inline beside the name or the manager, not a button row underneath. Contacting somebody is an
   action ON that person, and a row of wide buttons under the card reads as page-level actions --
   which is also how "Email manager" ended up nowhere near the manager. */
.sd-person-actions {
    display: inline-flex;
    gap: 2px;
    align-items: center;
    vertical-align: middle;
}

.sd-person-actions .mud-icon-button {
    padding: 4px;
}

/* At rest they sit in the sheet's own secondary ink, like every other piece of furniture here --
   two saturated glyphs next to a name pulled the eye away from the name, which is the thing being
   read. The channel colour arrives on hover, where it confirms what you are about to click rather
   than competing for attention. */
.sd-person-actions .mud-icon-button {
    color: #5d646b;
}

.sd-person-actions .mud-icon-button:hover { background: #eef2f6; }
.sd-action-mail:hover { color: #2f5d8a; }
.sd-action-teams:hover { color: #5b5fa8; }

.sd-person-name-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

/* The designation tokens as chips rather than one opaque comma-delimited string. This is the fact
   the whole exception hangs on, so it earns being read at a glance instead of parsed. */
.sd-designations {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

/* A directory token, not a status -- and the SIZE is what says so. Same pill shape and borderless
   fill as .sd-chip, deliberately one step smaller and letter-spaced, so a dpEES beside an Enabled
   chip does not read as another piece of the record's state. Collapsing the two into one class was
   considered and rejected for exactly that reason.

   Tints deepened with the border removal, for the same reason .sd-chip's were: the hairline was
   defining the shape. */
.sd-designation {
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.2px;
    padding: 1px 8px;
    border-radius: 999px;
    background: #e2e9f4;
    color: #2f4a6d;
    font-variant-numeric: tabular-nums;
}

/* dpPHI is the one that means PHI, so it is the one a reviewer is looking for. Shares .sd-chip.att's
   tint -- tinted, not alarming: it is why the request exists, not a fault. */
.sd-designation.phi {
    background: #fbeed3;
    color: #7a5108;
}


/* --- Two-up facts ---------------------------------------------------------------------------
   A full-width panel holding short labelled values wastes most of its width: a date or a Yes/No
   leaves the rest of the row empty, and four of them read as a narrow strip down the left. These
   pack two to a row and drop to one on narrow screens.

   Prose is the exception -- an explanation or a justification wants the full measure, so it opts
   out with .sd-fact-span and keeps the whole row. */
.sd-facts-2up {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 28px;
}

.sd-facts-2up > .sd-fact-span {
    grid-column: 1 / -1;
}

@media (max-width: 1100px) {
    .sd-facts-2up {
        grid-template-columns: 1fr;
    }
}

/* The history table sat in a full-width panel with three narrow columns, so most of its width was
   empty. Paired beside the subscription panel it fills its column instead. */
.sd-panel-pair .sd-table {
    width: 100%;
}

/* NOTE: .sd-header-top already sets justify-content: space-between higher up. Adding a second
   rule here to "fix" alignment did not override that -- it pushed the title block to the far right
   while an avatar sat at the far left. The header carries no avatar now; this note stays so the
   next person does not repeat it. */

/* A real photo replaces the initials circle, so it takes the same size and shape and drops the
   generated background -- object-fit keeps a non-square rendition from stretching. */
.sd-avatar-photo {
    background: none;
    object-fit: cover;
}

/* A read-only switch has to LOOK unavailable as well as behave that way -- it is deliberately not
   Disabled, so that it stays focusable and its explanation stays reachable, and without this it
   would look perfectly usable. Colour alone is not the signal: the lock icon beside it carries
   the meaning for anyone who cannot see the dimming. */
.sd-switch-locked {
    opacity: 0.55;
    cursor: not-allowed;
}

/* A deviation is a caution, not a fault -- amber, and the same muted weight as .sd-chip.att rather
   than the error red, which would read as something being broken. */
.sd-deviation-icon {
    color: #8f5f09;
}

.sd-deviation-text {
    font-size: 12.5px;
    color: #7a5108;
}

/* ---------------- Page section nav ----------------
   A sticky left-hand list of one page's sections (PageSectionNav), beside the content it points
   at. Admin Settings is the first page with enough distinct areas to need one; the layout and the
   nav are both general, so the second page to want one adds no CSS.

   Global rather than component-scoped for the same reason as everything else in this file: the
   nav reaches into MudBlazor's MudIcon, which never carries a scope attribute. */

/* The two columns. A fixed nav width rather than a fraction, because the nav holds short labels
   and would otherwise grow with the viewport while the content -- tables that genuinely want the
   room -- stayed the same. */
.sd-with-sidenav {
    display: grid;
    grid-template-columns: 208px minmax(0, 1fr);
    gap: 24px;
    align-items: start;
}

/* minmax(0, 1fr) above, not 1fr: a grid item's default min-width is auto, so a wide table inside
   the content column would push the grid wider than the page instead of scrolling within it.
   This is the same trap the billing tables hit. */

@media (max-width: 960px) {
    /* Stacked, with the nav above the content. A 208px column against a phone-width viewport
       leaves the tables unusable, and the nav is short enough to read as a header strip. */
    .sd-with-sidenav {
        grid-template-columns: minmax(0, 1fr);
        gap: 16px;
    }

    .sd-sidenav {
        position: static;
    }

    /* Horizontal when stacked: a vertical list at the top of a narrow screen pushes the content
       a whole screen down. */
    .sd-sidenav-list {
        display: flex;
        flex-wrap: wrap;
        gap: 4px;
    }
}

.sd-sidenav {
    position: sticky;
    /* Clear of the app bar, which the sections also allow for -- see .sd-section-anchor. */
    top: 88px;
}

.sd-sidenav-title {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    padding: 0 10px 6px;
}

.sd-sidenav-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* A button styled as a nav row. Reset first, because a <button> brings its own font and
   background and would otherwise sit in the list looking like a form control. */
.sd-sidenav-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 10px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    font: inherit;
    font-size: 13.5px;
    color: var(--mud-palette-text-primary);
    text-align: left;
    cursor: pointer;
}

.sd-sidenav-item:hover {
    background: #f3f4f6;
}

/* The current section. A tinted ground AND a weight change, not colour alone: the aria-current
   carries it for a screen reader, and this has to be visible to somebody who cannot distinguish
   the tint. */
.sd-sidenav-item.is-current {
    background: #eef2f7;
    font-weight: 600;
    color: #1f2933;
}

.sd-sidenav-item:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

.sd-sidenav-icon {
    flex: none;
    color: var(--mud-palette-text-secondary);
}

.sd-sidenav-item.is-current .sd-sidenav-icon {
    color: #1f2933;
}

.sd-sidenav-label {
    flex: 1 1 auto;
    /* Long labels wrap rather than being clipped -- a nav item is useless truncated, and the
       column is narrow enough that two lines is a fair outcome. */
    overflow-wrap: anywhere;
}

/* The count sits at the right edge of the row, and does not shrink the label. */
.sd-sidenav-item .sd-count-pill {
    flex: none;
}

/* Where a nav item scrolls TO. Without the margin the heading lands flush against the top of the
   viewport, under the app bar, so the section you asked for is the one thing you cannot see.
   scroll-margin rather than padding, so it costs nothing visually. */
.sd-section-anchor {
    scroll-margin-top: 88px;
}

/* ---------------- Progress with a caption ----------------
   A bar plus the count underneath it. The count is the part that actually reassures -- "88 of
   250" says the work is moving and roughly how much is left, where a bar alone only says
   something is happening. */
.sd-progress-caption {
    margin-top: 6px;
    font-size: 12.5px;
    color: var(--mud-palette-text-secondary);
}

/* ---------------- Lifecycle flow diagram (home page) ----------------
   One inline SVG scaled by CSS rather than a set of breakpoints: the viewBox does the work, so
   there is nothing here to keep in step with the diagram's own coordinates. */
.sd-flow {
    display: block;
    width: 100%;
    /* Its own aspect ratio, so it never stretches. Capped so the boxes do not become billboards
       on a wide monitor -- the diagram is a diagram, not a hero image. Matches the viewBox width,
       so at full size it renders at 1:1 and the text is at its designed weight. */
    max-width: 788px;
    height: auto;
    margin: 4px auto 0;
}

/* Text INSIDE the svg. Sized in user units, not px, so it scales with the diagram; a fixed px
   size would grow illegibly small as the svg shrank on a phone. */
.sd-flow-label {
    font-size: 13px;
    font-weight: 600;
    fill: #1f2933;
    text-anchor: middle;
    /* The app's own stack, so the diagram does not read as a pasted-in picture. */
    font-family: inherit;
}

.sd-flow-sub {
    font-size: 11px;
    fill: #454b52;
    text-anchor: middle;
    font-family: inherit;
}

/* The words on the arrows between the gates -- lighter than a box label, because an edge is a
   condition rather than a thing. */
.sd-flow-edge text {
    font-size: 10.5px;
    fill: #454b52;
    text-anchor: middle;
    font-family: inherit;
    letter-spacing: 0.02em;
}
