/* ---- Inter (self-hosted, latin) ----
   Inter ships true 400/500/600/700 faces; Lato had only 400/700, so the design's 500 (labels) and
   600 (titles) silently rendered as 400/700 and the weight hierarchy collapsed into two bins. */

@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/inter-400.woff2") format("woff2");
}

@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("fonts/inter-500.woff2") format("woff2");
}

@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("fonts/inter-600.woff2") format("woff2");
}

@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("fonts/inter-700.woff2") format("woff2");
}

/* ---- Itility design tokens ---- */

:root {
    --it-ink: #00102e;
    --it-petrol-950: #05252d;
    --it-petrol-900: #0b333e;
    --it-petrol-700: #183b4a;
    /* Data-viz ramp for stacked model-mix bars (claude-tokens board): the petrol family for the
       first model ranks, the ink/steel family for ranks 5-8 so two model families stay tellable. */
    --it-viz-1: #183b4a;
    --it-viz-2: #6c99ac;
    --it-viz-3: #b7cdd6;
    --it-viz-4: #d9d9d9;
    --it-viz-5: #00102e;
    --it-viz-6: #5c6b84;
    --it-viz-7: #b9c2d0;
    --it-viz-8: #ececec;
    --it-steel: #6c99ac;
    /* --it-accent is emitted by App.razor from Aurora:Runtime:AccentColor */
    --it-accent-hover: color-mix(in srgb, var(--it-accent), black 15%);
    --it-badge-bg: #e8eef2;
    --it-ghost-hover: #eef1fb;
    --it-code-fg: #dbe6ea;
    --it-code-inline-bg: #eef1f6;
    --it-danger-strong: #b32121;
    --it-font-mono: "Cascadia Code", "Consolas", monospace;
    --it-bg: #f7f9fc;
    --it-surface: #ffffff;
    --it-border: #d3dae4;
    --it-text: #00102e;
    --it-text-muted: #64748b;
    --it-success: #26b050;
    --it-warning: #b78103;
    --it-danger: #e50000;
    /* Sharp geometry, in the style of itility.nl (buttons/tags are radius 0). */
    --it-radius: 0;
    --it-font: "Inter", "Segoe UI", sans-serif;
    --body-font: var(--it-font);

    /* Spacing scale: strict 8px grid. */
    --sp-1: 4px;
    --sp-2: 8px;
    --sp-3: 12px;
    --sp-4: 16px;
    --sp-5: 24px;
    --sp-6: 32px;
    --sp-7: 48px;

    /* Radius scale: crisp/architectural like the brand site, not rounded. */
    --radius-sm: 0;
    --radius: 0;
    /* Deep petrol CTA background, matching the site's primary buttons. */
    --it-cta: #03252d;
    --it-cta-hover: #0b333e;

    /* Type scale of five steps, floor at 12px: 28 / 20 / 15 / 13 / 12.
       Titles run tight (1.2), data/body runs at 1.4: one line-height per size = a real baseline grid. */
    --fs-page-title: 28px;
    --lh-page-title: 1.2;
    --fs-section: 20px;
    --lh-section: 1.2;
    --fs-card-title: 15px;
    --lh-card-title: 1.2;
    --fs-body: 15px;
    --lh-body: 1.4;
    --fs-label: 13px;
    --lh-label: 1.4;
    --fs-small: 12px;
    --lh-small: 1.4;
    --ls-label: 0.04em;
}

/* 16px root: MudBlazor's rem sizing assumes a 16px root. Scaling the root to 15px (the old
   `html { font-size: var(--fs-body) }`) made every Mud rem land on a fractional px (12.1875,
   13.125, 0.8px borders) and clashed with the app's own px values. Body text size belongs on
   <body>, not on the rem reference. */
html {
    height: 100%;
    font-size: 16px;
}

body {
    height: 100%;
    margin: 0;
    font-family: var(--it-font);
    font-size: var(--fs-body);
    line-height: var(--lh-body);
    color: var(--it-text);
    background: var(--it-bg);
}

h1, h2, h3, h4 {
    color: var(--it-ink);
    font-weight: 700;
    letter-spacing: -0.01em;
}

h1:focus {
    outline: none;
}

a {
    /* Follows the accent unless a deployment sets a darker link colour, which a bright brand
       accent needs to stay legible as small text on white. */
    color: var(--it-link, var(--it-accent));
}

a:hover {
    color: var(--it-steel);
}

/* Visible keyboard focus in the brand accent (AA-safe on white). h1 keeps outline:none for the
   skip-to-heading behaviour above. */
a:focus-visible,
button:focus-visible,
[role="button"]:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline: 2px solid var(--it-accent);
    outline-offset: 2px;
}

/* h1 (and any tabindex="-1" element) is only ever focused programmatically for skip-to-heading;
   excluding tabindex="-1" above stops the accent ring boxing the title on every navigation. */

/* MudChip keeps its own 12px pill radius and ignores the theme's DefaultBorderRadius, so force crisp. */
.mud-chip {
    border-radius: 0;
}

/* ---- Shell: top-bar (MudAppBar) + drawer (MudDrawer) + content ---- */

/* House-style top-bar: deep petrol, seamless with the sidebar (no divider, no shadow). */
.aurora-appbar {
    border-bottom: 0;
}

.appbar-right {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    min-width: 0;
}

/* Account menu (identity + log out): a native disclosure anchored top-right. */
.account-menu-details {
    position: relative;
}

.account-chip {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-1) var(--sp-2);
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: #fff;
    list-style: none;
}

.account-chip::-webkit-details-marker {
    display: none;
}

.account-chip:hover {
    background: rgba(255, 255, 255, 0.1);
}

.account-chip-name {
    font-size: var(--fs-label);
    font-weight: 700;
    max-width: 12ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.account-menu {
    position: absolute;
    right: 0;
    top: calc(100% + var(--sp-1));
    z-index: 1300;
    display: flex;
    flex-direction: column;
    min-width: 220px;
    padding: var(--sp-2);
    gap: var(--sp-2);
    background: var(--it-surface);
    border: 1px solid var(--it-border);
    border-radius: var(--radius);
    box-shadow: 0 8px 28px rgba(0, 16, 46, 0.15);
}

.account-identity {
    display: flex;
    flex-direction: column;
    padding: var(--sp-2);
    border-bottom: 1px solid var(--it-border);
}

.account-name {
    font-weight: 700;
    color: var(--it-ink);
}

.account-team {
    font-size: var(--fs-label);
    color: var(--it-text-muted);
}

/* Administration, reached through your own name rather than everyone's sidebar. */
.account-link {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2);
    color: var(--it-accent);
    text-decoration: none;
    border-radius: var(--radius-sm);
}

.account-link:hover {
    /* Without this the global a:hover steel drops the contrast on the ghost fill below AA, and
       the adjacent log-out button (not an anchor) would keep the accent. */
    background: var(--it-ghost-hover);
    color: var(--it-accent-hover);
}

.account-logout {
    text-align: left;
    background: none;
    border: none;
    padding: var(--sp-2);
    font: inherit;
    color: var(--it-accent);
    cursor: pointer;
    border-radius: var(--radius-sm);
}

.account-logout:hover {
    background: var(--it-ghost-hover);
}

/* Drawer keeps the petrol brand gradient unless a deployment sets its own chrome, which is then
   painted flat: a gradient mixed toward a fixed tint reads muddy on any chrome but our petrol. */
.aurora-drawer {
    background: linear-gradient(180deg, var(--it-petrol-950) 0%, var(--it-petrol-900) 100%);
    background: var(--it-chrome, linear-gradient(180deg, var(--it-petrol-950) 0%, var(--it-petrol-900) 100%));
}

.aurora-brand {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 1.4rem 1.25rem 1.2rem;
    color: #fff;
    text-decoration: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.aurora-brand .brand-mark {
    flex-shrink: 0;
}

/* A customer logo stands in for the built-in mark; bounded so any upload keeps the header height. */
.aurora-brand .brand-logo {
    flex-shrink: 0;
    max-height: 2rem;
    max-width: 8rem;
    object-fit: contain;
}

.aurora-brand .brand-name {
    font-size: 1.35rem;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: 0.01em;
}

.aurora-brand .brand-sub {
    display: block;
    font-size: 0.72rem;
    font-weight: 400;
    color: var(--it-steel);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    margin-top: 0.15rem;
}

/* Compact customer switcher, legible on the dark petrol top-bar. */
.customer-switcher {
    min-width: 160px;
    max-width: 220px;
}

.customer-switcher .mud-input,
.customer-switcher .mud-input-slot,
.customer-switcher .mud-select-input,
.customer-switcher .mud-icon-root {
    color: rgba(255, 255, 255, 0.9);
}

.customer-switcher .mud-input-outlined-border {
    border-color: rgba(255, 255, 255, 0.28);
}

/* Keep the top-bar cluster on-screen on narrow viewports. */
@media (max-width: 720px) {
    .customer-switcher {
        min-width: 130px;
    }

    .account-chip-name {
        display: none;
    }
}

.aurora-nav {
    display: flex;
    flex-direction: column;
    padding: 1rem 0.75rem;
    gap: 0.15rem;
}

.aurora-nav .nav-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--it-steel);
    padding: 0.9rem 0.55rem 0.35rem;
}

.aurora-nav a {
    display: block;
    padding: 0.55rem 0.75rem;
    border-radius: var(--it-radius);
    color: rgba(255, 255, 255, 0.8);
    text-decoration: none;
    /* Chrome sits under the data: nav is navigation, not the numbers the tool is about. */
    font-size: var(--fs-label);
    border-left: 3px solid transparent;
}

.aurora-nav a:hover {
    background: rgba(255, 255, 255, 0.06);
    color: #fff;
}

.aurora-nav a.active {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    border-left-color: var(--it-steel);
    font-weight: 700;
}

/* The way out of the administration area sits above its pages, with a rule between the two so it
   reads as leaving rather than as one more admin page. */
.aurora-nav .back-to-portal {
    color: var(--it-steel);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    margin-bottom: var(--sp-2);
    padding-bottom: 0.55rem;
}

.aurora-nav .back-to-portal:hover {
    color: #fff;
}

/* Tab bar: give it a baseline the active slider stands on, and a real active signal.
   Was border-bottom:0 with active/inactive only #183b4a vs #253342 apart, which was invisible. */
.mud-tabs-toolbar {
    border-bottom: 1px solid var(--it-border);
}

.mud-tabs .mud-tab {
    font-weight: 500;
    color: var(--it-text-muted);
}

.mud-tabs .mud-tab.mud-tab-active {
    font-weight: 700;
    color: var(--it-accent);
}

.aurora-content {
    max-width: 1600px;
    margin: 0 auto;
    padding: var(--sp-6) var(--sp-6) var(--sp-7);
    box-sizing: border-box;
}

/* Prose/reading pages stay readable inside the wide shell. */
.markdown-body,
.docs-content {
    max-width: 860px;
}

@media (max-width: 768px) {
    .aurora-content {
        padding: 1.25rem 1rem 2.5rem;
    }

    .hero {
        padding: 2rem 1.25rem;
    }

    .card-grid {
        grid-template-columns: 1fr;
    }
}

/* ---- Hero (home) ---- */

.hero {
    background: linear-gradient(120deg, var(--it-petrol-950) 0%, var(--it-petrol-700) 100%);
    color: #fff;
    border-radius: var(--it-radius);
    padding: 3rem 2.5rem;
    margin-bottom: 2.5rem;
    position: relative;
    overflow: hidden;
}

.hero h1 {
    color: #fff;
    font-size: 2.3rem;
    margin: 0 0 0.5rem;
    max-width: 30ch;
}

.hero p {
    color: rgba(255, 255, 255, 0.8);
    font-size: 1.1rem;
    margin: 0;
    max-width: 60ch;
}

.hero .hero-mark {
    position: absolute;
    right: 2.5rem;
    bottom: -1rem;
    opacity: 0.35;
}

/* ---- Page headers ---- */

.page-intro {
    margin-bottom: 1.75rem;
}

.page-intro h1 {
    margin: 0.4rem 0 0.3rem;
    font-size: var(--fs-page-title);
    line-height: var(--lh-page-title);
}

.lead {
    color: var(--it-text-muted);
    max-width: 70ch;
    margin: 0;
}

.page-intro-link {
    display: inline-block;
    margin-top: var(--sp-2);
    font-weight: 700;
}

.back-link {
    font-size: 0.85rem;
    text-decoration: none;
    color: var(--it-text-muted);
}

.back-link:hover {
    color: var(--it-accent);
}

.section-heading {
    font-size: var(--fs-section);
    line-height: var(--lh-section);
    margin: var(--sp-6) 0 var(--sp-4);
}

/* ---- Cards ---- */

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
    gap: var(--sp-5);
}

/* One elevation language: border in rust, no shadow; on hover a subtle lift + steel border. */
.card {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    padding: var(--sp-5);
    background: var(--it-surface);
    border: 1px solid var(--it-border);
    border-radius: var(--radius);
    transition: border-color 120ms ease, box-shadow 120ms ease;
}

.card:hover {
    border-color: var(--it-steel);
    box-shadow: 0 4px 14px rgba(0, 16, 46, 0.08);
}

.card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
}

.card-head h1,
.card-head h3 {
    margin: 0;
    font-size: var(--fs-card-title);
    line-height: var(--lh-card-title);
}

.card-summary {
    color: var(--it-text);
    margin: 0;
    line-height: 1.5;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.card-meta {
    color: var(--it-text-muted);
    font-size: 0.8rem;
    margin: 0;
}

.card-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.card-actions {
    margin-top: auto;
    padding-top: var(--sp-3);
}

/* ---- Badges & buttons ---- */

.badge-type {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--it-petrol-700);
    background: var(--it-badge-bg);
    border-radius: var(--radius-sm);
    padding: 0.2rem 0.55rem;
    white-space: nowrap;
}

.badge-cis {
    font-size: 0.75rem;
    font-weight: 700;
    color: #fff;
    background: var(--it-steel);
    border-radius: var(--radius-sm);
    padding: 0.18rem 0.55rem;
    white-space: nowrap;
}

.badge-version {
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--it-petrol-700);
    border: 1px solid var(--it-border);
    background: var(--it-bg);
    border-radius: var(--radius-sm);
    padding: 0.18rem 0.55rem;
    white-space: nowrap;
}

.btn {
    display: inline-block;
    font-family: var(--it-font);
    font-size: 0.9rem;
    font-weight: 700;
    border-radius: var(--radius-sm);
    padding: 0.5rem 1.1rem;
    text-decoration: none;
    cursor: pointer;
    border: 1px solid transparent;
    line-height: 1.2;
}

.btn-accent {
    background: var(--it-cta);
    color: #fff;
}

.btn-accent:hover {
    background: var(--it-cta-hover);
    color: #fff;
}

.btn-outline {
    background: transparent;
    color: var(--it-petrol-700);
    border-color: var(--it-border);
}

.btn-outline:hover {
    border-color: var(--it-steel);
    color: var(--it-ink);
}

.btn-ghost {
    background: transparent;
    color: var(--it-accent);
    border: none;
    padding: 0.35rem 0.7rem;
    font-weight: 700;
}

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

/* AuroraSegments: on a screen narrower than the row, the choices wrap instead of widening the page. */
.aurora-segments {
    flex-wrap: wrap;
    max-width: 100%;
}

/* ---- Catalog toolbar ---- */

.catalog-toolbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.catalog-search {
    flex: 1;
    max-width: 420px;
}

.result-count {
    color: var(--it-text-muted);
    font-size: 0.85rem;
}

/* ---- Detail header ---- */

.detail-header {
    padding-bottom: 1.5rem;
    margin-bottom: 1.5rem;
    border-bottom: 1px solid var(--it-border);
}

.detail-header-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-top: 0.45rem;
}

.detail-title {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.detail-title h1 {
    margin: 0;
    font-size: var(--fs-page-title);
    line-height: var(--lh-page-title);
}

.detail-header .lead {
    margin-top: 0.6rem;
}

.detail-section {
    margin-top: var(--sp-6);
}

.use-cases {
    margin: 0;
    padding-left: 1.25rem;
    line-height: 1.8;
}

/* ---- Tabs ---- */

.variant-tabs {
    margin-top: 0.75rem;
}

.variant-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin: 1rem 0 0.75rem;
}

.variant-hint {
    color: var(--it-text-muted);
    font-size: 0.85rem;
    max-width: 60ch;
}

/* ---- Code viewer ---- */

.file-tabs {
    margin-top: 0.25rem;
}

.code-file {
    border: 1px solid var(--it-border);
    border-radius: var(--it-radius);
    overflow: hidden;
    margin: 0.75rem 0 0.25rem;
    background: var(--it-surface);
}

.code-file-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.35rem 0.5rem 0.35rem 0.95rem;
    background: var(--it-bg);
    border-bottom: 1px solid var(--it-border);
}

.file-name {
    font-family: var(--it-font-mono);
    font-size: 0.8rem;
    color: var(--it-text-muted);
}

/* Copy button: icon + label, with a clear success state on "Copied". */
.copy-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
}

.copy-btn.is-copied {
    color: var(--it-success);
}

.code-block {
    margin: 0;
    padding: 1.1rem 1.2rem;
    overflow: auto;
    max-height: 460px;
    background: var(--it-petrol-950);
    color: var(--it-code-fg);
    font-family: var(--it-font-mono);
    font-size: 0.83rem;
    line-height: 1.6;
    tab-size: 2;
}

/* ---- Markdown docs ---- */

.markdown-body {
    background: var(--it-surface);
    border: 1px solid var(--it-border);
    border-radius: var(--it-radius);
    padding: 1.75rem 2rem;
    line-height: 1.65;
    max-width: 90ch;
}

.markdown-body h1 {
    font-size: 1.5rem;
    margin-top: 0;
}

.markdown-body h2 {
    font-size: 1.2rem;
    margin-top: 1.75rem;
    padding-bottom: 0.3rem;
    border-bottom: 1px solid var(--it-border);
}

.markdown-body h3 {
    font-size: 1.02rem;
}

.markdown-body pre {
    background: var(--it-petrol-950);
    color: var(--it-code-fg);
    border-radius: var(--it-radius);
    padding: 1rem 1.2rem;
    overflow-x: auto;
    font-size: 0.83rem;
    line-height: 1.6;
}

.markdown-body code {
    font-family: var(--it-font-mono);
    font-size: 0.86em;
    background: var(--it-code-inline-bg);
    border-radius: var(--radius-sm);
    padding: 0.1em 0.35em;
}

.markdown-body pre code {
    background: transparent;
    padding: 0;
}

.code-block-wrap {
    position: relative;
}

/* Always visible, not hover-revealed: the readers who need the button most are the ones who would
   never discover it. Reserved padding keeps it off the first line of a command. */
.code-block-wrap .copy-btn {
    position: absolute;
    top: var(--sp-2);
    right: var(--sp-2);
    background: var(--it-petrol-900);
    color: var(--it-code-fg);
}

.code-block-wrap pre {
    padding-right: 5.5rem;
}

.markdown-body table {
    border-collapse: collapse;
    display: block;
    overflow-x: auto;
    max-width: 100%;
}

.markdown-body th,
.markdown-body td {
    border: 1px solid var(--it-border);
    padding: 0.45rem 0.8rem;
    text-align: left;
}

.markdown-body th {
    background: var(--it-bg);
}

/* ---- Docs-as-code pages ---- */

.docs-layout {
    display: flex;
    gap: 2rem;
    align-items: flex-start;
}

.docs-nav {
    flex: 0 0 220px;
    position: sticky;
    top: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.docs-nav .nav-label {
    margin: 0.9rem 0 0.3rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--it-text-muted);
}

.docs-nav .docs-link {
    display: block;
    padding: 0.35rem 0.7rem;
    border-radius: var(--it-radius);
    color: var(--it-text-muted);
    text-decoration: none;
    font-size: 0.92rem;
}

.docs-nav .docs-link:hover {
    background: var(--it-ghost-hover);
    color: var(--it-text);
}

.docs-nav .docs-link.active {
    background: var(--it-badge-bg);
    color: var(--it-ink);
    font-weight: 600;
}

.docs-content {
    flex: 1;
    min-width: 0;
}

@media (max-width: 900px) {
    .docs-layout {
        flex-direction: column;
    }

    .docs-nav {
        position: static;
        flex-basis: auto;
    }
}

/* ---- Health indicator ---- */

.health-indicator {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: 0.8rem;
}

.health-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.health-healthy .health-dot {
    background: var(--it-success);
}

.health-degraded .health-dot {
    background: var(--it-warning);
}

.health-unavailable .health-dot {
    background: var(--it-danger);
}

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

.aurora-nav a .health-indicator {
    float: right;
    margin-top: 0.35rem;
}

/* ---- States ---- */

.empty-state,
.loading-state,
.error-state {
    color: var(--it-text-muted);
}

.error-state {
    color: var(--it-danger-strong);
}

.empty-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
    background: var(--it-surface);
    border: 1px dashed var(--it-border);
    border-radius: var(--it-radius);
    padding: var(--sp-7) var(--sp-5);
    text-align: center;
    color: var(--it-text-muted);
}

.empty-panel .empty-icon {
    color: var(--it-steel);
    margin-bottom: var(--sp-1);
}

.empty-panel h3 {
    margin: 0;
    color: var(--it-ink);
}

.empty-panel p {
    margin: 0;
    max-width: 44ch;
}

/* ---- Blazor defaults ---- */

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--it-success);
}

.invalid {
    outline: 1px solid var(--it-danger);
}

.validation-message {
    color: var(--it-danger);
}

.blazor-error-boundary {
    background: var(--it-danger-strong);
    padding: 1rem;
    color: white;
}

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

/* ---- Data grids: shared, non-scoped utilities ----
   app.css is global, so these reach into MudDataGrid internals without ::deep (unlike a plugin's
   scoped .razor.css). Wrap any grid in <div class="data-grid-wrap"> to make it full-width, and put
   HeaderClass/CellClass="col-num" on numeric columns to right-align the cell AND the sortable header
   label (a bare text-align on the th does not move the flex header content). */
.data-grid-wrap {
    width: 100%;
}

.data-grid-wrap .mud-table-container,
.data-grid-wrap .mud-drop-container,
.data-grid-wrap .mud-table-root {
    width: 100% !important;
}

.col-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.col-num .mud-table-cell-content {
    justify-content: flex-end;
}

/* Puppet dashboard filter row: anchors on the MudTextField root, unreachable from scoped css. */
.puppet-filters {
    display: flex;
    gap: var(--sp-3);
    align-items: center;
    flex-wrap: wrap;
}

.puppet-filters .puppet-search {
    flex: 1 1 240px;
    min-width: 200px;
}

/* ---- Administration ---- */

.admin-grid {
    width: 100%;
}

/* MudDataGrid renders content-width without these; see the plugin grids for the same fix. */
.admin-grid .mud-table-container,
.admin-grid .mud-drop-container,
.admin-grid .mud-table-root {
    width: 100% !important;
}

.admin-grid .admin-row {
    cursor: pointer;
}

.admin-toolbar {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-3);
}

.admin-group-staged,
.admin-add-user-staged {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-1);
    margin-bottom: var(--sp-3);
}

.admin-section-heading {
    margin: var(--sp-6) 0 0;
    font-size: var(--fs-card-title);
}

.admin-add-customer-hint {
    margin: 0;
    max-width: 60ch;
    color: var(--it-text-muted);
}

/* Decommission sits last among the customer's own settings: the action and, beside it, what it does.
   Where it is not offered, one muted line says why. */
.admin-decommission {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2) var(--sp-3);
    margin-top: var(--sp-4);
}

/* One sentence pair beside a button reads as one line where there is room for it. */
.admin-decommission .admin-add-customer-hint {
    max-width: 80ch;
}

.admin-decommission-unavailable {
    margin-top: var(--sp-3);
}

/* The customers in service and the decommissioned ones are two tables that open with the same two
   columns; equal widths line the second table up under the first. */
.admin-customer-name-column {
    width: 26%;
}

.admin-customer-id-column {
    width: 22%;
}

/* A row's own action takes the width of its button and sits against the table's right edge. Wide
   layout only: stacked, a cell is a full-width row that already ends on the right. */
@media (min-width: 600px) {
    .admin-row-action {
        width: 1%;
        white-space: nowrap;
        text-align: right;
    }
}

.customer-internal-tag {
    margin-left: var(--sp-1);
    padding: 1px 6px;
    border-radius: var(--it-radius);
    background: var(--it-badge-bg);
    color: var(--it-text-muted);
    font-size: 11px;
}

.admin-group-form {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

.admin-group-section {
    margin-bottom: var(--sp-5);
}

.admin-group-heading {
    font-size: var(--fs-card-title);
    margin: 0 0 var(--sp-1);
}

.admin-group-hint {
    margin: 0 0 var(--sp-2);
    color: var(--it-text-muted);
}

.admin-group-plugins {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}

.admin-group-warning {
    margin: var(--sp-2) 0 0;
    color: var(--it-warning);
}

.admin-group-add {
    display: grid;
    grid-template-columns: 1fr 1fr auto;
    align-items: start;
    gap: var(--sp-2);
    margin-bottom: var(--sp-3);
}

/* The Entra link sits above the member list it decides: one field, save and sync beside it. */
.admin-group-entra {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: start;
    gap: var(--sp-2);
    margin-bottom: var(--sp-2);
}

.admin-group-add-actions,
.admin-add-user-person {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
}

.admin-add-user-person > .mud-autocomplete {
    flex: 1;
}

.admin-group-members {
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid var(--it-border);
    border-radius: var(--it-radius);
}

.admin-group-member {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    min-height: 44px;
    padding: 0 var(--sp-2);
    border-bottom: 1px solid var(--it-border);
}

.admin-group-member:last-child {
    border-bottom: none;
}

.admin-group-member-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.admin-group-member-name {
    color: var(--it-ink);
}

.admin-group-member-id {
    font-family: var(--it-font-mono);
    font-size: 11px;
    color: var(--it-text-muted);
}

.group-no-plugins {
    color: var(--it-warning);
}

.admin-tab-intro {
    margin-top: var(--sp-3);
    max-width: 70ch;
    color: var(--it-text-muted);
}

.admin-signingkey-form {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    max-width: 60ch;
    margin-bottom: var(--sp-3);
}

.admin-collector-tenant-id {
    font-family: var(--it-font-mono);
    font-size: 12px;
}

.admin-user {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.admin-user-name {
    font-weight: 500;
    color: var(--it-text);
}

.admin-user-sub {
    font-size: 12px;
    font-family: var(--it-font-mono);
    color: var(--it-text-muted);
    word-break: break-all;
}

.admin-role-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
}

.admin-role-chip {
    display: inline-flex;
    align-items: center;
}

.admin-role-chip .mud-icon-button {
    margin-left: -6px;
    padding: 2px;
}

.admin-plugin-list {
    display: flex;
    flex-direction: column;
    margin-top: var(--sp-3);
    border: 1px solid var(--it-border);
    border-radius: var(--it-radius);
    background: var(--it-surface);
}

.admin-plugin-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
}

.admin-plugin-row + .admin-plugin-row {
    border-top: 1px solid var(--it-border);
}

.admin-plugin-name {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.admin-plugin-id {
    font-size: 12px;
    color: var(--it-text-muted);
}

.admin-panel {
    padding: var(--sp-4);
    margin-top: var(--sp-3);
}

.admin-panel-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--it-text);
    margin-bottom: var(--sp-1);
}

.admin-panel-actions {
    display: flex;
    justify-content: flex-end;
    padding-top: var(--sp-2);
}

/* Integrations administration. Every class below sits on a plain element inside a Mud panel or
   dialog, so the rules must live here: a scoped .razor.css cannot reach past a component root. */
.admin-integration-form,
.admin-integration-fields,
.admin-integration-inputs {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    max-width: 60ch;
}

.admin-integration-person {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
}

.admin-integration-error {
    color: var(--it-danger, #e50000);
    font-size: var(--fs-label);
    margin-top: var(--sp-1);
}

.admin-integration-setup {
    font-weight: 600;
}

.admin-integration-secret-state {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-3);
    font-size: var(--fs-label);
    color: var(--it-text-muted);
}

.admin-integration-note {
    font-size: var(--fs-small);
    color: var(--it-text-muted);
}

.admin-integration-probe {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    margin-top: var(--sp-3);
}

.admin-integration-probe-step {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-label);
}

.admin-integration-probe-name {
    font-weight: 600;
}

.admin-integration-mono {
    font-family: var(--it-font-mono);
    font-size: var(--fs-small);
    overflow-wrap: anywhere;
}

.admin-scope-items {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-1);
    padding: var(--sp-2) 0;
}

.admin-scope-input {
    max-width: 420px;
}

.admin-add-user {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.admin-filters {
    display: flex;
    gap: var(--sp-2);
    flex-wrap: wrap;
    margin-bottom: var(--sp-1);
}

.admin-filter {
    min-width: 168px;
    flex: 0 1 220px;
}

/* MudBlazor cascades Class onto the picker's inner inputs, where the flex-basis above becomes a
   220px HEIGHT inside the column-flex input container (live-measured: 232px -> 51px with this reset). */
.admin-filters .mud-picker.admin-filter {
    flex: 0 1 260px;
}

.admin-filters .mud-picker.admin-filter .admin-filter {
    flex: initial;
    min-width: 0;
}

/* The two date inputs keep their intrinsic width otherwise (content 281px in a 260px box), pushing
   the calendar adornment half outside the outline; let them shrink and share the row instead. */
.admin-filters .mud-picker.admin-filter .mud-input.admin-filter {
    width: 100%;
}

.admin-filters .mud-picker.admin-filter .mud-input-slot.admin-filter {
    flex: 1 1 0;
}

.admin-drawer {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.admin-audit-detail {
    margin: 0;
    padding: var(--sp-2) var(--sp-3);
    font-family: var(--it-font-mono);
    font-size: 12px;
    color: var(--it-text);
    background: var(--it-bg);
    white-space: pre-wrap;
    word-break: break-word;
}

/* Global search (Ctrl+K): a quiet appbar affordance in the petrol bar, styled like the account chip. */
.global-search-button {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-1) var(--sp-2);
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: var(--radius-sm);
    background: transparent;
    color: #fff;
    cursor: pointer;
    font: inherit;
}

.global-search-button:hover {
    background: rgba(255, 255, 255, 0.1);
}

.global-search-label {
    font-size: 0.85rem;
}

.global-search-kbd {
    font-family: var(--it-font-mono);
    font-size: 0.7rem;
    padding: 0 var(--sp-1);
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: var(--radius-sm);
    opacity: 0.8;
}

.global-search-dialog .mud-dialog-content {
    padding-top: var(--sp-2);
}

.global-search-results {
    max-height: 420px;
    overflow-y: auto;
}

.global-search-hit-active {
    background: var(--it-ghost-hover);
    border-radius: var(--radius-sm);
}

.global-search-hit-title {
    color: var(--it-text);
}

.global-search-hit-subtitle {
    color: var(--it-text-muted);
    font-size: 0.8rem;
}

.global-search-empty {
    color: var(--it-text-muted);
    padding: var(--sp-3);
    text-align: center;
}

/* ---- Administration: assistant evaluation ---- */

/* Who is asked and the run on the left, the run files on the right; on a phone they wrap under each other. */
.eval-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2) var(--sp-3);
}

/* MudSelect puts Class on its inner control; the outer root is the flex item, and it grows unless told. */
.eval-toolbar > .mud-select {
    flex: 1 1 160px;
    min-width: 140px;
    max-width: 260px;
}

.eval-toolbar-end {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-1);
    margin-left: auto;
}

.eval-note {
    margin: var(--sp-3) 0 0;
    color: var(--it-text-muted);
}

.eval-alert {
    margin-top: var(--sp-3);
}

/* One line under the toolbar: what the table shows now, and the way back from an opened run. */
.eval-status {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-1) var(--sp-3);
    min-height: var(--sp-6);
    margin-top: var(--sp-3);
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-text-muted);
    font-variant-numeric: tabular-nums;
}

.eval-status-warning {
    color: var(--it-warning);
}

.eval-progress {
    margin-top: var(--sp-1);
}

/* Four figures in a row, two over two on a phone. */
.eval-figures {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--sp-3);
    margin-top: var(--sp-3);
}

.eval-figure-rules {
    display: flex;
    min-width: 0;
}

.eval-figure-rules > * {
    flex: 1 1 auto;
}

@media (max-width: 599.98px) {
    .eval-figures {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* The set by topic: rows of 40px, one line each from 600px; the question takes the spare width and elides. */
.eval-table {
    margin-top: var(--sp-4);
}

.eval-table .mud-table-root > tbody > tr {
    height: 40px;
}

.eval-table col.eval-col-result {
    width: 120px;
}

.eval-table col.eval-col-outcome {
    width: 36%;
}

.eval-table col.eval-col-time {
    width: 88px;
}

@media (min-width: 600px) {
    .eval-table .mud-table-root td {
        max-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.eval-group-row > .mud-table-cell {
    background: var(--it-bg);
}

.eval-group {
    font-size: var(--fs-label);
}

.eval-group-name {
    font-weight: 600;
    color: var(--it-ink);
}

/* A phone keeps the question and its result; the rest of a row is in its drawer. */
@media (max-width: 599.98px) {
    .eval-table .eval-wide {
        display: none;
    }
}

.eval-row {
    cursor: pointer;
}

/* The question and, when it was asked on a customer, that customer: the question elides first. */
.eval-question {
    display: flex;
    align-items: baseline;
    gap: var(--sp-1);
    min-width: 0;
}

/* The question opens its details: the row's words in the link colour. A button centres what overflows it,
   so the words sit in a span of their own that elides at its end. */
.eval-open {
    display: flex;
    min-width: 0;
    max-width: 100%;
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    text-align: left;
    color: var(--it-link, var(--it-accent));
    cursor: pointer;
}

@media (min-width: 600px) {
    .eval-open-text {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.eval-open:hover {
    text-decoration: underline;
}

.eval-tag {
    flex: none;
    padding: 1px 6px;
    border-radius: var(--it-radius);
    background: var(--it-badge-bg);
    color: var(--it-text-muted);
    font-size: 11px;
}

.eval-outcome {
    font-size: var(--fs-label);
    color: var(--it-text-muted);
}

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

/* The drawer: per run its result, the tools, every rule with its outcome, then the answer as returned. */
.eval-drawer-side + .eval-drawer-side {
    margin-top: var(--sp-5);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--it-border);
}

.eval-drawer-heading {
    margin-bottom: var(--sp-2);
    font-weight: 600;
    color: var(--it-ink);
}

.eval-drawer-result {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-bottom: var(--sp-2);
}

.eval-drawer-title {
    margin: var(--sp-4) 0 var(--sp-2);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--it-ink);
}

.eval-rules {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.eval-rule {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--sp-1) var(--sp-2);
    font-size: var(--fs-label);
    line-height: var(--lh-label);
}

.eval-rule-text {
    min-width: 0;
    font-family: var(--it-font-mono);
    overflow-wrap: anywhere;
}

.eval-rule-detail {
    flex-basis: 100%;
    color: var(--it-text-muted);
    overflow-wrap: anywhere;
}

.eval-answer {
    max-height: 50vh;
    margin: 0;
    padding: var(--sp-3);
    overflow: auto;
    border: 1px solid var(--it-border);
    border-radius: var(--it-radius);
    background: var(--it-bg);
    font-family: var(--it-font);
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-text);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.eval-drawer-note {
    margin: 0;
    font-size: var(--fs-label);
    color: var(--it-text-muted);
}

/* ---- Home: foundation health scorecard ---- */

.hero-compact {
    padding: 1.5rem 2rem;
    margin-bottom: 2rem;
}

.hero-compact h1 {
    font-size: 1.6rem;
}

.hero-compact .hero-mark {
    right: 1.5rem;
    bottom: -0.75rem;
}

/* Cards can now carry two actions (dashboard + catalog). */
.card .card-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

/* ---- Home: the answer band, then the detail regions, on one set of columns for the whole page: the
   band's three columns and their gutters are the columns every region lies on - a region spans one, two
   or all three - and the tiles inside a region lie on them too, so the band's dividers, the regions' edges
   and the tiles' rules line up. Nothing a person reads is cut: titles, reasons, figures and what they
   measure wrap, and only a post's summary stops after a few lines, with the whole post one press away.
   The launchpad is the container every Home layout reads its width from - not the viewport, which does
   not know whether the drawer is docked - so the columns follow the space they really have. The band,
   the regions and the tiles switch at the same launchpad widths and at no other: a region says how many
   of the band's columns it spans, and its tiles take exactly that many. The largest text - the band's
   answers and the titles - is balanced, and the lines under it never end on a lone word. */
.launchpad {
    display: flex;
    flex-direction: column;
    gap: var(--sp-5);
    container: launchpad / inline-size;
}

/* One grammar for every region: a bordered surface with its head inside - the name in ink at card-title
   size, a quiet count, an action at the end - on one fixed height whether or not an action sits in it,
   so the regions of a row start their rows on one line. Each region is a container too, so its rows
   follow the width the region really got. */
.home-panel {
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: var(--it-surface);
    border: 1px solid var(--it-border);
    container: panel / inline-size;
}

.panel-head {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    height: 48px;
    box-sizing: border-box;
    padding: 0 var(--sp-4);
    border-bottom: 1px solid var(--it-border);
}

.panel-title {
    margin: 0;
    min-width: 0;
    font-size: var(--fs-card-title);
    line-height: var(--lh-card-title);
    font-weight: 600;
    letter-spacing: 0;
    color: var(--it-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* How much is there before a row is read: a figure on the badge ground, never a colour of its own. */
.panel-count {
    min-width: 20px;
    box-sizing: border-box;
    padding: 0 var(--sp-1);
    font-size: var(--fs-small);
    line-height: 20px;
    font-weight: 600;
    text-align: center;
    color: var(--it-text-muted);
    background: var(--it-badge-bg);
    font-variant-numeric: tabular-nums;
}

/* A head's action is sized to the head, never over it: label size, petrol, one line, at the end. A Mud
   root class, so global by necessity. */
.panel-head .panel-action {
    flex: none;
    height: 32px;
    min-width: 0;
    margin: 0 calc(-1 * var(--sp-2)) 0 auto;
    padding: 0 var(--sp-2);
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    white-space: nowrap;
    color: var(--it-petrol-700);
}

.panel-head .panel-action .mud-button-icon-start {
    margin-left: 0;
    margin-right: var(--sp-1);
}

.panel-body {
    display: flex;
    flex-direction: column;
    flex: 1 0 auto;
    min-width: 0;
}

/* One row of a region: a hairline between rows, 16px in from the edge, at least 44px tall. The ghost
   hover only on a row that goes somewhere; buttons and links share it. */
.panel-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    column-gap: var(--sp-3);
    width: 100%;
    box-sizing: border-box;
    min-height: 44px;
    padding: var(--sp-3) var(--sp-4);
    border: 0;
    border-top: 1px solid var(--it-border);
    background: none;
    font: inherit;
    text-align: left;
    text-decoration: none;
    color: var(--it-text);
}

.panel-body > .panel-row:first-child {
    border-top: 0;
}

a.panel-row:hover,
button.panel-row:hover {
    background: var(--it-ghost-hover);
    color: var(--it-text);
}

button.panel-row {
    cursor: pointer;
}

.panel-row-arrow {
    color: var(--it-text-muted);
    align-self: center;
}

/* The one plain sentence of an empty region, and the muted line that says a source did not answer. */
.panel-empty {
    margin: 0;
    padding: var(--sp-3) var(--sp-4);
    color: var(--it-text-muted);
    text-wrap: pretty;
}

.panel-note {
    margin: 0;
    padding: var(--sp-2) var(--sp-4);
    border-top: 1px solid var(--it-border);
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-text-muted);
    text-wrap: pretty;
}

/* ---- Home intro: the person's greeting, then the customer the page is about. */
/* The page's head is the portal's own petrol block - the surface its app bar, drawer and portfolio hero
   carry - and the answer band under the name is its lower half, so the page opens on one statement and
   the working regions under it stay white. Text sits 16px in, on the band's and the regions' own inset.
   The chevron in the corner is the brand's mark, as on the hero, at the hero's strength. */
.home-intro {
    position: relative;
    margin-bottom: 0;
    padding: var(--sp-5) var(--sp-4) var(--sp-4);
    background: var(--it-petrol-950);
    overflow: hidden;
}

.home-intro h1 {
    position: relative;
    margin: 0;
    color: var(--it-surface);
}

.home-mark {
    position: absolute;
    top: var(--sp-4);
    right: var(--sp-4);
    line-height: 0;
    opacity: 0.35;
}

.home-greeting {
    position: relative;
    margin: 0 0 var(--sp-1);
    font-size: var(--fs-body);
    line-height: var(--lh-body);
    color: var(--it-steel);
}

/* ---- The answer band: one strip, one cell per question, on the page's columns with the page's gutter
   between them. Each cell draws its own 2px top rule across its column and half of each gutter - the
   border grey, or a colour when its answer carries a meaning: petrol when something is there for this
   person, red for a measured failure, green for a recent healthy check - so neighbouring rules meet at
   the divider, which stands in the middle of the gutter where the regions below leave theirs, and never
   leave a grey stub between two colours. The first and last cells lay their rule over the strip's side
   borders, so a colour runs into the corner. A quiet customer's band stays grey. The cells share the
   width equally however many a page has: three answers, or two where a source is not there to ask. */
.glance {
    --glance-line: color-mix(in srgb, var(--it-surface) 16%, transparent);
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    column-gap: var(--sp-5);
    background: var(--it-petrol-950);
    border: 0;
}

/* A band with no source to ask - no database, no identity and no solution publishing health - is not a
   grey line. */
.glance:not(:has(> *)) {
    display: none;
}

.glance-cell {
    --glance-rule: var(--glance-line);
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    min-width: 0;
    padding: calc(var(--sp-3) + 2px) var(--sp-4) var(--sp-4);
}

.glance-cell::after {
    content: "";
    position: absolute;
    top: 0;
    left: calc(var(--sp-5) / -2);
    right: calc(var(--sp-5) / -2);
    border-top: 2px solid var(--glance-rule);
}

.glance-cell:first-child::after {
    left: 0;
}

.glance-cell:last-child::after {
    right: 0;
}

.glance-cell + .glance-cell::before {
    content: "";
    position: absolute;
    top: var(--sp-4);
    bottom: var(--sp-4);
    left: calc(var(--sp-5) / -2);
    border-left: 1px solid var(--glance-line);
}

.glance-tone-active {
    --glance-rule: var(--it-steel);
}

.glance-tone-failure {
    --glance-rule: var(--it-danger);
}

.glance-tone-healthy {
    --glance-rule: var(--it-success);
}

/* The cell's head is a region's head: the same sentence-case name, and the way to the list pinned to the
   cell's right edge at every width, on one line. */
.glance-head {
    display: flex;
    align-items: baseline;
    gap: var(--sp-3);
    min-width: 0;
}

.glance-label {
    margin: 0;
    min-width: 0;
    font-size: var(--fs-card-title);
    line-height: var(--lh-card-title);
    font-weight: 600;
    letter-spacing: 0;
    color: var(--it-steel);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.glance-link {
    flex: none;
    margin-left: auto;
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    font-weight: 600;
    color: var(--it-surface);
    text-decoration: none;
    white-space: nowrap;
}

.glance-link:hover {
    color: var(--it-surface);
    text-decoration: underline;
}

.glance-link-arrow {
    margin-left: var(--sp-1);
}

/* The answer: the one step of the type scale between the page title and a row, so it is read first, and
   read whole - it wraps. A post's title may run to 120 characters, so the one that opens a post stops
   after three lines, with the whole of it in its title and in the dialog it opens. */
.glance-answer {
    margin: 0;
    min-width: 0;
    font-size: var(--fs-section);
    line-height: var(--lh-section);
    font-weight: 600;
    color: var(--it-surface);
    overflow-wrap: anywhere;
    text-wrap: balance;
}

/* On petrol the strong red goes dark; the bright one reads at the answer's size (3.3:1, large text). */
.glance-tone-failure .glance-answer {
    color: var(--it-danger);
}

/* "Nothing needs you", "Nothing reported", "No news yet": a quiet day reads quieter than a busy one. */
.glance-answer-quiet {
    font-weight: 500;
    color: var(--it-steel);
}

.glance-answer-button {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    max-width: 100%;
    overflow: hidden;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    line-height: inherit;
    color: inherit;
    text-align: left;
    overflow-wrap: anywhere;
    cursor: pointer;
}

.glance-answer-button:hover {
    text-decoration: underline;
}

.glance-answer-button:focus-visible,
.glance-retry:focus-visible,
.glance-link:focus-visible {
    outline: 2px solid var(--it-surface);
    outline-offset: 2px;
}

.glance-detail {
    margin: 0;
    min-width: 0;
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-steel);
    text-wrap: pretty;
}

/* A line of parts breaks between its parts, never inside one: "2 budget approvals" stays together. Every
   part leads with its separator in a box of one fixed width, and the parts are pulled left by exactly that
   width inside a clipping line, so the separator of whichever part starts a line falls outside it: no
   line starts or ends on a dot. A part longer than the whole line is cut short rather than pushing the
   cell wider. */
.glance-parts {
    --glance-sep-width: 14px;
    overflow: hidden;
}

.glance-parts-run {
    display: block;
    margin-left: calc(-1 * var(--glance-sep-width));
}

.glance-segment {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    vertical-align: top;
}

.glance-sep {
    display: inline-block;
    width: var(--glance-sep-width);
    text-align: center;
}

/* A count reads as a number: ink and weight on the muted line, in figures that line up. */
.glance-figure {
    font-weight: 600;
    color: var(--it-surface);
    font-variant-numeric: tabular-nums;
}

.glance-note {
    margin: 0;
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-steel);
    text-wrap: pretty;
}

.glance-retry {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: 600;
    color: var(--it-surface);
    text-decoration: underline;
    cursor: pointer;
}

.glance-skeleton {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
}

/* The loading pulse on petrol: the band's own line colour, not the light badge grey. Three classes deep,
   because the component's scoped rule loads after this sheet and would win a tie. */
.glance .glance-skeleton .skeleton-block {
    background: var(--glance-line);
}

/* ---- The regions, on the band's columns: in pairs whose spans add up to the row - what needs attention
   beside the posture, a system beside the feed, the links beside the dashboards - and a region whose
   partner has nothing to add takes the whole row. The regions of a row stretch to one height, so their
   feet line up. Narrower than the band's three columns, every region takes the width, in DOM order. */
.home-grid {
    --tile-columns: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-5);
}

/* The foundation's health is one named section of the page, and its two regions lie on the page's own
   columns. */
.home-health {
    display: grid;
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
    row-gap: var(--sp-5);
}

/* Platform status: a mark, the system, and its state in a word right after the name - one column for
   the whole card, so the words line up however long a name is - and on the line under the name what
   backs it, whole: on the day of an outage that line says what is wrong, so it wraps and is never cut,
   and the day it started stays with its last words. The word always carries the state; the mark repeats
   it for the eye, round for a measured lane and square for a reported status. Red belongs to a measured
   failure only; a reported incident is a filled petrol square, a known issue an open one, and a check
   too old to count a muted ring - never a green mark without a recent measurement. A healthy lane steps
   back to muted. */
.platform-list {
    display: grid;
    grid-template-columns: auto minmax(0, max-content) auto minmax(0, 1fr);
    column-gap: var(--sp-2);
    align-content: start;
}

.platform-list > * {
    grid-column: 1 / -1;
}

.platform-row {
    grid-template-columns: subgrid;
    row-gap: 2px;
}

.platform-mark {
    grid-column: 1;
    grid-row: 1;
    align-self: center;
    width: 10px;
    height: 10px;
    box-sizing: border-box;
}

.platform-down .platform-mark,
.platform-up .platform-mark,
.platform-no-recent-check .platform-mark {
    border-radius: 50%;
}

.platform-down .platform-mark {
    background: var(--it-danger);
}

.platform-up .platform-mark {
    background: var(--it-success);
}

.platform-no-recent-check .platform-mark {
    border: 2px solid var(--it-text-muted);
}

.platform-incident .platform-mark {
    background: var(--it-petrol-700);
}

.platform-known-issue .platform-mark {
    border: 2px solid var(--it-petrol-700);
}

.platform-name {
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
    font-weight: 600;
    color: var(--it-ink);
    overflow-wrap: anywhere;
    text-wrap: balance;
}

.platform-state {
    grid-column: 3;
    grid-row: 1;
    padding-left: var(--sp-2);
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    font-weight: 600;
    white-space: nowrap;
    color: var(--it-petrol-700);
}

.platform-down .platform-state {
    color: var(--it-danger-strong);
}

.platform-up .platform-name {
    font-weight: 500;
    color: var(--it-text-muted);
}

.platform-up .platform-state,
.platform-no-recent-check .platform-state {
    font-weight: 400;
    color: var(--it-text-muted);
}

.platform-detail {
    grid-column: 2 / -1;
    grid-row: 2;
    min-width: 0;
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-text-muted);
    overflow-wrap: anywhere;
    text-wrap: pretty;
}

.platform-detail-when {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Open statuses that name no system: counted in one muted line, because a title is not a system. */
.platform-unnamed {
    color: var(--it-text-muted);
}

/* ---- Tiles: the ways in, the links and the posture, on the page's columns - as many as the region
   spans of the band's columns (--tile-columns, set where the region's span is, from the launchpad's width
   and never the region's own) - with the page's gutter between them, so a tile's text starts where the
   band's and the regions' text does. A tile is one column less a pixel shared out, so a row never wraps on
   a rounding. Every tile draws its top rule across its column and half of each gutter, and its left rule
   in the middle of the gutter, so the rules meet in one line; the grid clips the halves that fall outside
   it and is pulled up under the head's rule. The last tile takes what its row leaves, so no row ends in an
   empty cell or a rule that steps. */
.tile-grid {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    column-gap: var(--sp-5);
    align-content: flex-start;
    margin-top: -1px;
    overflow: hidden;
    isolation: isolate;
}

.tile-grid > * {
    position: relative;
    flex: 0 0 calc((100% - (var(--tile-columns) - 1) * var(--sp-5) - 1px) / var(--tile-columns));
    min-width: 0;
    box-sizing: border-box;
}

.tile-grid > :last-child {
    flex-grow: 1;
}

.tile-grid > *::before {
    content: "";
    position: absolute;
    z-index: -1;
    inset: 0 calc(var(--sp-5) / -2);
    border-top: 1px solid var(--it-border);
}

.tile-grid > *::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--sp-5) / -2);
    border-left: 1px solid var(--it-border);
}

.tile-grid > a:hover::before {
    background: var(--it-ghost-hover);
}

/* Quick access: one line per way in - its name, then at the end one short muted word that says what kind
   of place it is (a dashboard's category as its plugin declares it) with the arrow after it. Both are read
   whole: where the line cannot hold them, the word and its arrow move under the name together rather than
   being cut. A plugin's description is a sentence that no row can hold, so it is the row's title. */
.launch-item {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--sp-3);
    row-gap: 2px;
    min-height: 44px;
    box-sizing: border-box;
    padding: var(--sp-3) var(--sp-4);
    color: var(--it-text);
    text-decoration: none;
}

.launch-item:hover {
    color: var(--it-text);
}

.launch-name {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
    color: var(--it-ink);
    overflow-wrap: anywhere;
    text-wrap: balance;
}

.launch-end {
    display: flex;
    flex: none;
    align-items: baseline;
    gap: var(--sp-3);
    margin-left: auto;
}

.launch-category {
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-text-muted);
    white-space: nowrap;
}

.launch-arrow {
    flex: none;
    align-self: center;
    color: var(--it-text-muted);
}

/* A customer's link: its name, then the host it goes to on a line of its own under it, and the
   external-link mark at the end - the same two lines in every tile at every width, so neighbouring links
   never differ by whether a host happened to fit beside a name. The host is read whole: it wraps. */
.launch-item.quick-link-external {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    column-gap: var(--sp-3);
    row-gap: 2px;
}

.quick-link-external .launch-name {
    grid-column: 1;
    grid-row: 1;
}

.quick-link-host {
    grid-column: 1;
    grid-row: 2;
    min-width: 0;
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-text-muted);
    overflow-wrap: anywhere;
    text-wrap: pretty;
}

.quick-link-external .launch-arrow {
    grid-column: 2;
    grid-row: 1 / span 2;
}

/* Links filed in categories: a block per category, each a tile of the grid - so its column, its rules and
   its gutter are the page's - headed by the category and holding its links one per row, each the two
   lines a link always is. The heading is the region's own head grammar a step down: ink and semibold at
   label size, over a rule that runs across half of each gutter, so the headings of a row stand on one
   line. The links under it carry no rules of their own: the blocks of a row hold different numbers of
   links, and a rule that stopped at a shorter neighbour would step. A link answers a pointer across its
   whole column, as a tile does. */
.quick-link-group-heading {
    position: relative;
    margin: 0;
    padding: var(--sp-3) var(--sp-4);
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    font-weight: 600;
    letter-spacing: 0;
    color: var(--it-ink);
    overflow-wrap: anywhere;
    text-wrap: balance;
}

.quick-link-group-heading::after {
    content: "";
    position: absolute;
    left: calc(var(--sp-5) / -2);
    right: calc(var(--sp-5) / -2);
    bottom: 0;
    border-bottom: 1px solid var(--it-border);
}

.quick-link-group .launch-item {
    position: relative;
}

.quick-link-group .launch-item::before {
    content: "";
    position: absolute;
    z-index: -1;
    inset: 0 calc(var(--sp-5) / -2);
}

.quick-link-group .launch-item:hover::before {
    background: var(--it-ghost-hover);
}

/* ---- A customer's foundation health: Needs attention, then Posture. Needs attention is laid out like
   Platform status - a mark, the check, its figure right after the name in one column for the whole list,
   and on the line under them what the solution writes about the figure, whole. The mark is round, as a
   measured lane's is, and petrol: filled where the solution alerts, open where it warns. No green and no
   red: a tile carries no check time, so it cannot claim health now, and red belongs to a lane measured
   not answering. Amber is not among them either: on Home it would read as "due soon", which is owned
   elsewhere. */
.attention-list {
    display: grid;
    grid-template-columns: auto minmax(0, max-content) minmax(0, 1fr) auto;
    column-gap: var(--sp-2);
    align-content: start;
}

.attention-list > * {
    grid-column: 1 / -1;
}

.attention-row {
    grid-template-columns: subgrid;
    row-gap: 2px;
}

.attention-dot {
    grid-column: 1;
    grid-row: 1;
    align-self: center;
    width: 10px;
    height: 10px;
    box-sizing: border-box;
    border-radius: 50%;
}

.attention-alert .attention-dot {
    background: var(--it-petrol-700);
}

.attention-warn .attention-dot {
    border: 2px solid var(--it-petrol-700);
}

.attention-label {
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
    font-weight: 600;
    color: var(--it-ink);
    overflow-wrap: anywhere;
    text-wrap: balance;
}

.attention-value {
    grid-column: 3;
    grid-row: 1;
    min-width: 0;
    padding-left: var(--sp-2);
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    font-weight: 600;
    color: var(--it-petrol-700);
    overflow-wrap: anywhere;
    font-variant-numeric: tabular-nums;
}

.attention-context {
    grid-column: 2 / 4;
    grid-row: 2;
    min-width: 0;
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-text-muted);
    overflow-wrap: anywhere;
    text-wrap: pretty;
}

.attention-arrow {
    grid-column: 4;
    grid-row: 1 / 3;
    align-self: center;
    color: var(--it-text-muted);
}

/* A phone-width region: the figure gets a line of its own under the check. */
@container panel (width < 440px) {
    .attention-value {
        grid-column: 2 / 4;
        grid-row: 2;
        padding-left: 0;
    }

    .attention-context {
        grid-row: 3;
    }

    .attention-arrow {
        grid-row: 1 / 4;
    }
}

/* Posture: everything else that was assessed, a tile each - the check's name, the figure at row size and
   what the solution writes about it - with no mark, because a fine check carries no check time and a
   figure such as the month's spend is no check at all. */
.posture-tile {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--sp-3) var(--sp-4);
    text-decoration: none;
    color: var(--it-text);
}

.posture-tile:hover {
    color: var(--it-text);
}

.posture-tile-label {
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    font-weight: 600;
    color: var(--it-text-muted);
    overflow-wrap: anywhere;
}

.posture-tile-value {
    font-size: var(--fs-card-title);
    line-height: var(--lh-body);
    font-weight: 600;
    color: var(--it-ink);
    overflow-wrap: anywhere;
    font-variant-numeric: tabular-nums;
    text-wrap: balance;
}

.posture-tile-context {
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-text-muted);
    overflow-wrap: anywhere;
    text-wrap: pretty;
}

/* n/a tiles never sit among the numbers: a muted strip of their own at the foot of the region. */
.not-assessed {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border-top: 1px solid var(--it-border);
}

.panel-body > .not-assessed:first-child {
    border-top: 0;
}

.not-assessed-label {
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    font-weight: 600;
    color: var(--it-text-muted);
}

.not-assessed-chip {
    padding: 0 var(--sp-2);
    font-size: var(--fs-small);
    line-height: 20px;
    background: var(--it-badge-bg);
    color: var(--it-text-muted);
    text-decoration: none;
}

.not-assessed-chip:hover {
    color: var(--it-text);
}

/* The band's three columns: the regions lie on them in their pairs, on both kinds of home - what needs
   attention beside the posture, a system beside the feed, the links beside the ways in - and each region's
   tiles lie on the columns it spans, so every rule that sets a span sets the tiles' columns with it. */
@container launchpad (width >= 960px) {
    .home-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .home-grid > *,
    .home-health > * {
        grid-column: 1 / -1;
        --tile-columns: 3;
    }

    .region-attention,
    .region-updates,
    .region-quick {
        grid-column: span 2;
        --tile-columns: 2;
    }

    .region-posture,
    .region-status,
    .region-links {
        grid-column: span 1;
        --tile-columns: 1;
    }

    /* A pair whose partner is not drawn gives the whole row to the one that is. */
    .home-health:not(:has(> .region-posture)) > .region-attention,
    .home-health:not(:has(> .region-attention)) > .region-posture,
    .home-grid:not(:has(> .region-status)) > .region-updates,
    .home-grid:not(:has(> .region-updates)) > .region-status,
    .home-grid:not(:has(> .region-links)) > .region-quick {
        grid-column: 1 / -1;
        --tile-columns: 3;
    }

    /* Links filed in categories are blocks on all three columns, so their region takes the row - and the
       ways in, whose partner it was, the next. */
    .home-grid > .region-links-grouped,
    .home-grid:has(> .region-links-grouped) > .region-quick {
        grid-column: 1 / -1;
        --tile-columns: 3;
    }

    /* Internal with neither a feed nor links: the ways in take the feed's place beside the systems. */
    .home-grid-internal:not(:has(> .region-updates)):not(:has(> .region-links)) > .region-status {
        grid-column: span 1;
        --tile-columns: 1;
    }

    .home-grid-internal:not(:has(> .region-updates)):not(:has(> .region-links)):has(> .region-status) > .region-quick {
        grid-column: span 2;
        --tile-columns: 2;
    }
}

/* Narrower than three readable cells (a docked drawer at a laptop width): the band's cells stack, one
   question per row, each keeping its own rule across the strip. */
@container launchpad (width < 960px) {
    .glance {
        grid-auto-flow: row;
        grid-template-columns: minmax(0, 1fr);
    }

    .glance-cell {
        padding-bottom: var(--sp-3);
    }

    .glance-cell::after,
    .glance-cell:first-child::after,
    .glance-cell:last-child::after {
        left: 0;
        right: 0;
    }

    .glance-cell + .glance-cell::before {
        content: none;
    }
}

/* With the width for it, a stacked cell reads as a line of a summary: the question on the left in one
   column for all three, the answer and what backs it beside it, and the way to the list at the end. Every
   region takes the width, and its tiles two columns of it. */
@container launchpad (600px <= width < 960px) {
    .home-grid {
        --tile-columns: 2;
    }

    .glance-cell {
        display: grid;
        grid-template-columns: 136px minmax(0, 1fr) auto;
        column-gap: var(--sp-4);
        row-gap: var(--sp-1);
        align-items: baseline;
    }

    .glance-head {
        display: contents;
    }

    .glance-label {
        grid-column: 1;
        grid-row: 1;
    }

    .glance-link {
        grid-column: 3;
        grid-row: 1;
    }

    .glance-answer {
        grid-column: 2;
        grid-row: 1;
    }

    .glance-cell > :not(.glance-head, .glance-answer) {
        grid-column: 2 / -1;
    }
}

/* ============================================================================
   Component-root classes. Blazor CSS isolation never stamps its scope
   attribute on a child component's root element, so classes passed as
   Class="..." to Mud* components are only reachable from GLOBAL css. Scoped
   .razor.css rules for these silently never match - keep them here.
   ========================================================================= */

/* Customer links dialog (MudDialog Class="quick-links-dialog"): a Mud root class, so global by necessity. */
.quick-links-form {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

/* The one inline validation message in the launchpad's dialogs: the rejection no MaxLength or
   helper text can express. */
.quick-links-field-error,
.update-form-error {
    font-size: var(--fs-small);
    color: var(--it-danger);
}

.quick-links-form-actions {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.quick-links-count {
    margin-left: auto;
    font-size: var(--fs-small);
    color: var(--it-text-muted);
    font-variant-numeric: tabular-nums;
}

/* The categories the customer already has, under the Category field: one press puts a spelling in the
   field instead of retyping it. Small and quiet - a suggestion, not the form's action, so it keeps its
   distance from the row that holds that action - and never narrower than its name: a name that does not
   fit moves to the next line of buttons. Three classes deep, because the Mud button's own size rule is two. */
.quick-links-categories {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin-bottom: var(--sp-2);
}

.quick-links-categories .quick-links-category.mud-button-root {
    min-width: 0;
    padding: 0 var(--sp-2);
    border-color: var(--it-border);
    font-size: var(--fs-small);
    line-height: 22px;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--it-text);
}

/* A category's links in the dialog: the heading the home page shows, over the same bordered list. The
   heading carries the gap the list has when it stands alone. */
.quick-links-group-heading {
    margin: var(--sp-4) 0 var(--sp-1);
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    font-weight: 600;
    color: var(--it-ink);
    overflow-wrap: anywhere;
}

.quick-links-group-heading + .quick-links-list {
    margin-top: 0;
}

.quick-links-list {
    list-style: none;
    margin: var(--sp-4) 0 0;
    padding: 0;
    border: 1px solid var(--it-border);
    border-radius: var(--it-radius);
}

.quick-links-row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: 44px;
    padding: var(--sp-1) var(--sp-3);
    border-top: 1px solid var(--it-border);
}

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

.quick-links-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
}

.quick-links-label {
    font-weight: 600;
    color: var(--it-text);
}

.quick-links-url {
    font-family: var(--it-font-mono);
    font-size: var(--fs-small);
    color: var(--it-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.quick-links-actions {
    display: flex;
    flex-shrink: 0;
}

/* ---- Band 3 (Updates): the team's own feed for this customer ----
   One bordered surface with hairline separators, the grammar .attention-list and .quick-links-list
   already use, so the band reads as a sibling of the measured bands rather than a new module. Every
   post is a two-column row like .attention-row: what was written on the left, where it came from on
   the right. No card grid, no shadow, no radius. The reading dialog's list is this same surface with
   this same row (HomeCustomerUpdateFeed / HomeCustomerUpdateRow), so everything below serves both. */

/* The tile's box, in one place. 72 is 48's 3:2 partner and both land on whole pixels. The tile drives
   the row only on the shortest post there is: a one-line title over a one-line body is 18 + 8 + 21 =
   47px of prose, against the tile's 48 plus the 2px it sits down by. Every post with a second body line
   is taller than the tile and is driven by its text, and three posts stay three rows either way.
   Redefined once in the 768px query, where 40 + 2 does fit inside those 47px. On :root rather than the
   feed: the compose dialog's preview is this tile too, and a dialog renders outside any feed, where a
   box declared on the feed does not exist and the picture would come out at its own size. */
:root {
    --update-thumb-w: 72px;
    --update-thumb-h: 48px;
}

.update-feed {
    display: flex;
    flex-direction: column;
    background: var(--it-surface);
    border: 1px solid var(--it-border);
}

.update-post {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    gap: var(--sp-3);
    /* The 2px gutter is reserved on every post, so an open status's edge never shifts its text; the
       padding gives the 2px back, landing the text at the same x as .attention-row one band up. */
    padding: var(--sp-3) var(--sp-4) var(--sp-3) calc(var(--sp-4) - 2px);
    border-top: 1px solid var(--it-border);
    border-left: 2px solid transparent;
}

.update-post:first-child {
    border-top: 0;
}

/* A feed whose rows show a picture - the reading dialog's, where every post has one, its own or the
   portal's - carries the track on every post, so the whole feed keeps one left edge for its prose. The
   home card's rows show no picture, and so have no track and no gutter to leave empty. */
.update-feed-imaged .update-post {
    grid-template-columns: var(--update-thumb-w) minmax(0, 1fr) auto;
}

.update-feed-imaged .update-post-main {
    grid-column: 2;
}

.update-feed-imaged .update-post-aside {
    grid-column: 3;
}

/* A portal admin's feed reserves one more track at the end of every row for the row's menu, in all
   four combinations, so the aside keeps its column whether or not a menu follows it. */
.update-feed-managed .update-post {
    grid-template-columns: minmax(0, 1fr) auto auto;
}

.update-feed-imaged.update-feed-managed .update-post {
    grid-template-columns: var(--update-thumb-w) minmax(0, 1fr) auto auto;
}

/* The link owns the tile's place in the grid, and the portal's own picture takes exactly that place where
   a post has none. Fixed in both axes and aligned on its own, so no grid or flex parent can stretch it:
   a height alone lets a panorama widen the text column, and a box sized when the bytes land is a layout
   shift on a page already being read. .update-post is baseline-aligned and a 48px box on a baseline is
   nonsense; 2px is the rounded half-leading of the title's line box, which puts the tile's top on the
   title's cap height. */
.update-post-default,
.update-post-thumb-link {
    grid-column: 1;
    align-self: start;
    display: block;
    width: var(--update-thumb-w);
    height: var(--update-thumb-h);
    margin-top: 2px;
}

.update-post-thumb-link:focus-visible {
    outline: 2px solid var(--it-petrol-700);
    outline-offset: 2px;
}

/* A post with no picture of its own shows the portal's: the brand's chevrons, larger than the tile, on
   the petrol of the page's head, turned by an angle taken from the post's id. It fills the whole box -
   there is no logo to keep whole, so none of an uploaded tile's quiet margin - and the tile crops what
   the turn pushes past its edges. */
.update-post-default {
    overflow: hidden;
    line-height: 0;
    background: var(--it-petrol-950);
}

.update-post-default svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* Contain, not cover: what people post are logos and service icons, and a cropped logo is a broken
   one. The 4px of padding inside the border-box is a quiet margin on the tile's own ground, so a logo
   shows whole and a photograph sits framed on the grey rather than reading as mis-sized. The fill is
   the reserved box, the loading state and the failed state in one - a picture is decoration on a
   post, and an error state for it would be louder than the post. The compose dialog's preview is this
   same tile, so what is posted is what was seen. */
.update-post-thumb {
    display: block;
    box-sizing: border-box;
    width: var(--update-thumb-w);
    height: var(--update-thumb-h);
    padding: var(--sp-1);
    object-fit: contain;
    object-position: center;
    overflow: hidden;
    background: var(--it-badge-bg);
    /* Only so ::before below has a box to fill; with no offsets it moves nothing. */
    position: relative;
}

/* The fill above is the reserved box and the loading state; this makes it the failed state too.
   Chrome paints its own broken-image glyph inside a sized box, and the alt text beside it, clipped
   mid-line - louder than a post's decoration should be, and louder still on a pinned row, where the
   tile inverts to white. A generated box is rendered on an image only while it is failing, never
   over one that loaded, so this covers the glyph without script and without an onerror handler.
   background-color: inherit takes the tile's own colour, pinned or not, from the rules above. */
.update-post-thumb::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: inherit;
}

/* A pinned row is filled with exactly the tile's own placeholder colour, so a loading or failed
   thumbnail would be invisible. Inverted the same way the band already inverts .status-chip. */
.update-post-pinned .update-post-thumb {
    background: var(--it-surface);
}

/* An open status wears a word, an edge, a time label and the top of the feed; news wears nothing.
   Petrol, never amber or red: in this portal those mean "due soon" and "act now" and are owned by the
   measured Needs attention band directly above - a typed statement in amber could not be told apart
   from a measured warning. A resolved status drops the edge and reads as history. */
.update-post-open {
    border-left-color: var(--it-petrol-700);
}

/* The house tonal fill: --it-bg is a 2% step off the feed surface and reads as nothing, which left
   the pin carried by one word at the far right of the quietest line. The chip on that row takes the
   surface back, so it stays a chip instead of dissolving into a fill of its own colour. */
.update-post-pinned {
    background: var(--it-badge-bg);
}

.update-post.update-post-pinned .status-chip {
    background: var(--it-surface);
}

.update-post-main {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    min-width: 0;
}

.update-post-aside {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    text-align: right;
}

/* 600 rather than the 700 of the global heading rule: a post must not out-shout a measured number.
   Breaks anywhere rather than scrolling the page sideways under a pasted address. */
.update-post-title {
    margin: 0;
    font-size: var(--fs-card-title);
    line-height: var(--lh-card-title);
    font-weight: 600;
    color: var(--it-ink);
    overflow-wrap: anywhere;
    text-wrap: balance;
}

/* Focus lands on the title after an action and on Read more, so a keyboard user sees where they are;
   a pointer user, who did not move focus themselves, is not shown a ring. */
.update-post-title:focus-visible {
    outline: 2px solid var(--it-petrol-700);
    outline-offset: 2px;
}

.update-post-title:focus:not(:focus-visible) {
    outline: none;
}

/* Body text is the size this portal reads prose at, not label size: a post is the one thing on this
   page a person reads rather than scans. It carries the same size as its title with 200 less weight,
   which is the contrast that lets a reader skim titles and drop into a body without a size jolt. */
.update-post-body {
    margin: 0;
    max-width: 70ch;
    font-size: var(--fs-body);
    line-height: var(--lh-body);
    color: var(--it-text);
    white-space: pre-line;
    overflow-wrap: anywhere;
    text-wrap: pretty;
}

/* A resolved status reads as done: its body steps back to muted but stays readable. */
.update-post-resolved .update-post-body {
    color: var(--it-text-muted);
}

/* The one line under a resolved status saying what fixed it: label size, below the body. */
.update-post-resolution {
    margin: 0;
    max-width: 70ch;
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-text);
    overflow-wrap: anywhere;
}

.update-post-resolution-label {
    font-weight: 600;
}

.update-feed-more-label {
    /* The deployment's link colour when it sets one: a bright brand accent is not legible as small
       text on white, which is why the global a rule reads the same pair. */
    color: var(--it-link, var(--it-accent));
}

.update-feed-more:hover .update-feed-more-label {
    color: var(--it-steel);
}

/* The kind on a full post, first in the aside: the same words the home card's line starts with. */
.update-post-kind {
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    font-weight: 600;
    color: var(--it-text-muted);
    white-space: nowrap;
}

.update-post-date {
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-text-muted);
    white-space: nowrap;
}

.update-post-meta {
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-text-muted);
}

/* One quiet menu per row in the managed track. Pulled into the row's padding corner so its 30px button
   never adds height to a row. A Mud root class, so global by necessity. */
.update-post-menu {
    grid-column: -2 / -1;
    align-self: start;
    margin: calc(-1 * var(--sp-1)) calc(-1 * var(--sp-2)) 0 0;
}

/* The way from the Updates card to the rest of the feed, when there is more than it shows. */
.update-feed-more {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    width: 100%;
    min-height: 44px;
    padding: var(--sp-3) var(--sp-4);
    border: 0;
    border-top: 1px solid var(--it-border);
    background: none;
    box-sizing: border-box;
    font-family: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.update-feed-more:hover {
    background: var(--it-ghost-hover);
}

.update-feed-more-label {
    font-size: var(--fs-label);
    font-weight: 600;
}

.update-feed-more-arrow {
    color: var(--it-text-muted);
}

@media (max-width: 768px) {
    :root {
        --update-thumb-w: 60px;
        --update-thumb-h: 40px;
    }

    .update-post {
        grid-template-columns: 1fr;
        gap: var(--sp-1);
        padding: var(--sp-3) var(--sp-3) var(--sp-3) calc(var(--sp-3) - 2px);
    }

    /* Two tracks rather than one: at 400px the text column is still about 37 characters, so the tile
       survives instead of stacking. Full-bleed here would make the picture the loudest thing on the
       screen at the most crowded width, and the band's height a function of the image. The tile
       track stays on every post here too, for the one left edge the wide layout keeps. */
    .update-feed-imaged .update-post {
        grid-template-columns: var(--update-thumb-w) minmax(0, 1fr);
        column-gap: var(--sp-3);
    }

    /* The tile anchors top-left across both rows, so the date lines up with the prose rather than
       with the picture. */
    .update-post-default,
    .update-post-thumb-link {
        grid-row: 1 / span 2;
    }

    .update-feed-imaged .update-post-main {
        grid-column: 2;
        grid-row: 1;
    }

    .update-feed-imaged .update-post-aside {
        grid-column: 2;
        grid-row: 2;
    }

    /* The menu keeps the last column of the first row; the aside moves under the prose. */
    .update-feed-managed .update-post {
        grid-template-columns: minmax(0, 1fr) auto;
        column-gap: var(--sp-3);
    }

    .update-feed-imaged.update-feed-managed .update-post {
        grid-template-columns: var(--update-thumb-w) minmax(0, 1fr) auto;
    }

    .update-feed-managed .update-post-main {
        grid-column: 1;
        grid-row: 1;
    }

    .update-feed-managed .update-post-aside {
        grid-column: 1;
        grid-row: 2;
    }

    .update-feed-imaged.update-feed-managed .update-post-main,
    .update-feed-imaged.update-feed-managed .update-post-aside {
        grid-column: 2;
    }

    .update-post-menu {
        grid-row: 1;
    }

    /* One column stacks what was written first and where it came from last: title, body, then the
       date and byline of the aside. */
    .update-post-aside {
        text-align: left;
    }

    .update-post-body,
    .update-post-resolution {
        max-width: none;
    }
}

/* The home card's feed, after the full post's rules at every width so it wins them. The region is the
   surface, so the feed draws no border of its own. A row reads like a news item: the title, whole, then
   the kind, the system a status names and who wrote it, then the body's first line as its summary -
   three lines at most, the whole post one press away. The rows lie on the feed's own columns - a
   subgrid - so every date and title shares one edge down the feed, whatever a date measures: where the
   region has the width the day has a column of its own before the title, and where it is narrow it
   stands above the title. A portal admin's rows keep one more track at the end for the row's menu,
   there for a pointer, a finger and Tab alike, so the menu never covers a word. */
.home-panel .update-feed {
    border: 0;
}

.update-feed-compact {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    column-gap: var(--sp-3);
    align-content: start;
}

.update-feed-compact.update-feed-managed {
    grid-template-columns: minmax(0, 1fr) auto;
}

.update-feed-compact > * {
    grid-column: 1 / -1;
}

.update-feed .update-post.update-post-compact {
    grid-template-columns: subgrid;
    align-items: baseline;
    gap: 2px normal;
    padding: var(--sp-3) var(--sp-4);
    border-left: 0;
}

.update-post-compact > :not(.update-post-menu) {
    grid-column: 1;
}

.update-post-compact .update-post-menu {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: start;
    margin: calc(-1 * var(--sp-1)) calc(-1 * var(--sp-2)) 0 0;
}

.update-post-heading {
    margin: 0;
    min-width: 0;
    font-size: var(--fs-card-title);
    line-height: var(--lh-body);
}

.update-post-compact .update-post-title {
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: 600;
    line-height: inherit;
    color: var(--it-ink);
    text-align: left;
    overflow-wrap: anywhere;
    cursor: pointer;
}

.update-post-compact .update-post-title:hover {
    text-decoration: underline;
}

.update-post-when {
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* The kind chip, the system and who wrote it: the quietest line of the row, wrapping between its parts. */
.update-post-line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--sp-2);
    row-gap: 2px;
    min-width: 0;
    margin: 0;
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-text-muted);
}

.update-post-compact .update-post-line .status-chip {
    flex: none;
    padding-block: 0;
}

.update-post-meta-text {
    min-width: 0;
    overflow-wrap: anywhere;
    text-wrap: pretty;
}

.update-post-excerpt {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    min-width: 0;
    margin: 0;
    overflow: hidden;
    font-size: var(--fs-label);
    line-height: var(--lh-label);
    color: var(--it-text);
    overflow-wrap: anywhere;
    text-wrap: pretty;
}

@container panel (width >= 480px) {
    .update-feed-compact {
        grid-template-columns: max-content minmax(0, 1fr);
    }

    .update-feed-compact.update-feed-managed {
        grid-template-columns: max-content minmax(0, 1fr) auto;
    }

    .update-post-compact > :not(.update-post-menu) {
        grid-column: 2;
    }

    .update-post-compact .update-post-when {
        grid-column: 1;
        grid-row: 1;
    }

    .update-post-compact .update-post-menu {
        grid-column: 3;
    }
}

/* Reading dialog (MudDialog Class="update-reading-dialog"): a Mud root class, so global by necessity.
   Only MudBlazor's own .mud-dialog-content scrolls; the feed inside it never gets a height of its own.
   Post update keeps the top right at every width, on the title's first line beside the close button:
   the title takes what the button leaves and wraps inside itself, and the button never shrinks or
   drops to a line of its own. Baseline, so a title that wraps keeps the button on its first line. */
.update-reading-head {
    display: flex;
    align-items: baseline;
    gap: var(--sp-3);
}

.update-reading-title {
    flex: 1 1 0;
    min-width: 0;
}

.update-reading-post {
    flex: none;
}

/* The section-title · subtitle pattern at the size every other dialog title has. */
.update-reading-sub {
    font-weight: 400;
    color: var(--it-text-muted);
}

.update-reading-dialog .mud-dialog-content {
    padding-bottom: var(--sp-6);
}

@media (max-width: 600px) {
    /* The 16px gutter, and the title on the same left edge as the list under it. */
    .update-reading-dialog .mud-dialog-content,
    .update-reading-dialog .mud-dialog-title {
        padding-inline-start: var(--sp-4);
    }

    .update-reading-dialog .mud-dialog-content {
        padding-inline-end: var(--sp-4);
    }

    /* The label says it; the icon's 24px go to the title beside it, one line fewer at 400px. */
    .update-reading-post .mud-button-icon-start {
        display: none;
    }
}

/* Compose dialog (MudDialog Class="update-compose-dialog") and the resolve confirmation. */
.update-form,
.update-resolve-form {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

.update-resolve-body {
    margin: 0;
    color: var(--it-text);
}

/* The cap sentence on the left, Cancel and Post on the right; on a narrow dialog the sentence takes a
   line of its own above the buttons, which stay right-aligned. */
.update-form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--sp-2);
    width: 100%;
}

.update-form-cap {
    margin-right: auto;
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-text-muted);
}

@media (max-width: 600px) {
    .update-form-cap {
        flex-basis: 100%;
    }
}

/* Label and control as one field: 4px binds the label to its row, where .update-form's own 8px
   between fields would leave it floating equidistant between two fields. */
.update-form-field {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
}

/* The rows in this form that are not a Mud input, so their labels are written out here instead of
   being carried by a control. Label size and muted, the register Mud's own floating labels sit in, so
   the form reads as one column of labelled fields rather than two grammars. */
.update-form-label {
    font-size: var(--fs-label);
    color: var(--it-text-muted);
}

/* News and Status on one line, and a status's Incident and Known issue likewise, wrapping on a narrow
   dialog. The class sits on MudBlazor's outer input wrapper, so the spacing is set on the radio group
   inside it, where the two options are siblings. */
.update-form-category .mud-radio-group,
.update-form-kind .mud-radio-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-1) var(--sp-3);
}

/* The picker, its tile and its two text actions on one line, wrapping on a narrow dialog. */
.update-form-image {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

/* The helper lines, the picked file's name, and the notice where no container is configured. */
.update-form-hint {
    margin: 0;
    font-size: var(--fs-small);
    line-height: var(--lh-small);
    color: var(--it-text-muted);
    text-wrap: pretty;
}


.cost-panel {
    padding: var(--sp-4, 16px);
    border-radius: var(--it-radius, 2px);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3, 12px);
}

/* The id a bill names a subscription or account by, in a column of its own: selected whole on a click, to copy
   into a bill's lookup. A phone card has no room beside a 36-character id, and the exports carry it. */
.cost-id {
    font-family: var(--it-font-mono);
    font-size: var(--fs-small, 12px);
    font-weight: 400;
    color: var(--it-text-muted);
    white-space: nowrap;
    user-select: all;
}

@media (max-width: 599.98px) {
    .cost-col-id {
        display: none !important;
    }
}

/* A cost table whose rows stack into cards on a phone: every cell a line, its column's name (the cell's
   data-label) on the left and the value on the right, like the data grids' own narrow layout. */
@media (max-width: 599.98px) {
    .mud-simple-table.cost-stack-table thead {
        display: none;
    }

    .mud-simple-table.cost-stack-table table,
    .mud-simple-table.cost-stack-table tbody,
    .mud-simple-table.cost-stack-table tr {
        display: block;
    }

    .mud-simple-table.cost-stack-table tr {
        padding: var(--sp-2) 0;
        border-bottom: 1px solid var(--it-border);
    }

    .mud-simple-table.cost-stack-table tr:last-child {
        border-bottom: none;
    }

    .mud-simple-table.cost-stack-table table * tr > td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: var(--sp-3);
        padding: var(--sp-1) var(--sp-4);
        border-bottom: none;
        text-align: right;
    }

    .mud-simple-table.cost-stack-table td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        text-align: left;
        font-size: 11px;
        font-weight: 600;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: var(--it-text-muted);
    }
}

.cost-rd-portal {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

/* WCAG 2.2 SC 2.5.8 (AA, European Accessibility Act): icon-only targets need >=24x24 CSS px.
   These external-link icons rendered ~19x19 in a gap:8px flex row (no spacing exception). */
.cost-rd-portal,
.cost-res-link,
.secret-res-link {
    min-width: 24px;
    min-height: 24px;
    justify-content: center;
}

.cost-dim-select {
    min-width: 180px;
    max-width: 220px;
}

.cost-explorer-search {
    min-width: 200px;
    max-width: 300px;
}

.cost-zero-switch {
    margin-left: var(--sp-2, 8px);
}

.cost-res-link {
    cursor: pointer;
    font-weight: 500;
    word-break: break-word;
}

.cost-crumb {
    cursor: pointer;
}

.opt-search {
    min-width: 220px;
    max-width: 320px;
}

.opt-min-select {
    max-width: 150px;
}

.opt-dismissed .mud-expand-panel {
    border: 1px solid var(--it-border, #d3dae4);
    border-radius: var(--it-radius, 2px);
}

.estate-panel {
    padding: 1rem 1.25rem;
    margin-bottom: 1rem;
}

.gov-panel {
    padding: var(--sp-4, 16px);
    margin-bottom: var(--sp-3, 12px);
}

.gov-search {
    min-width: 260px;
    flex: 0 1 320px;
}

.gov-filter {
    min-width: 168px;
    flex: 0 1 200px;
}

.gov-block-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 13px;
}

.expiry-search {
    min-width: 260px;
    max-width: 360px;
}

.secret-tabs {
    margin-bottom: var(--sp-3);
}

.secret-res-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-weight: 500;
    word-break: break-word;
    color: var(--it-link, #316b83);
}
@media (min-width: 900px) { .admin-entra-group { display: flex; align-items: flex-start; gap: var(--sp-3); } }
.admin-entra-group { margin-top: var(--sp-3); }
.admin-entra-group .mud-input-control { max-width: 26rem; }
