html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

/* Hide the framework error banner by default; Blazor sets inline display:block on a real error. */
/* The crash bar's styles moved to GVS.Components (gvs-status.css), where all four products
   pick them up from one place. */

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ==========================================================================
   Grid + layout utilities — self-contained replacement for bootstrap.min.css.
   Reproduces only the Bootstrap classes still used by the (restyled) pages so
   the Bootstrap dependency can be dropped. box-sizing:border-box is set globally
   by gvs-myhome.css. Breakpoints match Bootstrap (sm 576 / md 768 / lg 992).
   ========================================================================== */
.row { display: flex; flex-wrap: wrap; margin-right: -0.5rem; margin-left: -0.5rem; }
.row > * { padding-right: 0.5rem; padding-left: 0.5rem; }
.row.g-1 { margin-right: -0.125rem; margin-left: -0.125rem; row-gap: 0.25rem; }
.row.g-1 > * { padding-right: 0.125rem; padding-left: 0.125rem; }
.row.g-2 { margin-right: -0.25rem; margin-left: -0.25rem; row-gap: 0.5rem; }
.row.g-2 > * { padding-right: 0.25rem; padding-left: 0.25rem; }
.row.g-3 { margin-right: -0.5rem; margin-left: -0.5rem; row-gap: 1rem; }
.row.g-3 > * { padding-right: 0.5rem; padding-left: 0.5rem; }

/* columns: full width until their breakpoint (matches Bootstrap col-*-N) */
.col-12,
[class*="col-sm-"], [class*="col-md-"], [class*="col-lg-"] { flex: 0 0 auto; width: 100%; }

@media (min-width: 576px) {
    .col-sm-6 { width: 50%; }
    .col-sm-12 { width: 100%; }
}
@media (min-width: 768px) {
    .col-md-3 { width: 25%; }
    .col-md-4 { width: 33.3333%; }
    .col-md-5 { width: 41.6667%; }
    .col-md-6 { width: 50%; }
    .col-md-7 { width: 58.3333%; }
    .col-md-8 { width: 66.6667%; }
}
@media (min-width: 992px) {
    .col-lg-4 { width: 33.3333%; }
    .col-lg-8 { width: 66.6667%; }
}

/* flex + spacing utilities */
.d-flex { display: flex; }
.flex-wrap { flex-wrap: wrap; }
.flex-column { flex-direction: column; }
.align-items-center { align-items: center; }
.align-items-start { align-items: flex-start; }
.justify-content-between { justify-content: space-between; }
.justify-content-end { justify-content: flex-end; }
.justify-content-center { justify-content: center; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 1rem; }
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 1rem; }
.mt-4 { margin-top: 1.5rem; }
.p-2 { padding: 0.5rem; }
.p-3 { padding: 1rem; }
.p-4 { padding: 1.5rem; }
.border { border: 1px solid var(--gvs-border, #dde3e8); }
.rounded { border-radius: 0.375rem; }
.text-muted { color: var(--gvs-fg-3, #6e7a84); }
.small { font-size: 0.875em; }

/* Users admin dialogs — widen beyond the default modal so the create form and the
   tabbed manage dialog have room. The class lands on the MudDialog paper. */
.gvs-user-dialog { max-width: 560px !important; width: 96vw !important; }
.gvs-user-dialog-wide { max-width: 820px !important; width: 96vw !important; }

/* Users table — avatar, @username handle, brand-coloured product chips */
.gvs-user-cell { display: flex; align-items: center; gap: 12px; }
.gvs-row-avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--gvs-navy-700, #1f3c5b);
    color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 13px;
    font-weight: 600; flex-shrink: 0; }
.gvs-user-name { font-weight: 600; line-height: 1.2; }
.gvs-user-handle { font-size: 12px; color: var(--gvs-fg-3); line-height: 1.2; }
.gvs-product-chip { display: inline-flex; align-items: center; gap: 6px; padding: 2px 10px; margin: 2px 4px 2px 0;
    border-radius: 999px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.gvs-product-chip i { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }

/* Reserve the scrollbar gutter so locking the page (below) doesn't shift content. */
html { scrollbar-gutter: stable; }

/* Lock background page scroll while any dialog is open. Every dialog in the suite
   renders the shared `.mh-scrim` overlay, so this :has() rule applies to all current
   and future dialogs with no per-dialog wiring. */
html:has(.mh-scrim),
body:has(.mh-scrim) { overflow: hidden; }

/* Small inline spinner (Material Symbols progress_activity) for in-flight toggles. */
.gvs-spin { display: inline-block; animation: gvs-spin 0.8s linear infinite; }
@keyframes gvs-spin { to { transform: rotate(360deg); } }

/* Dark-mode readability for the account-menu "Suite admin" badge (library renders
   .mh-admin-badge with navy-900, which is invisible on the navy-800 dark surface). */
[data-theme="dark"] .mh-admin-badge { background: var(--gvs-navy-500); }

/* The launcher cards are anchors so the browser's own "open in new tab/window" gestures work
   (see ProductLauncherCard). The library styles .mh-launch element-agnostically but never
   neutralises link decoration, so without this every card title and label would be underlined
   and the visited colour would leak in. */
a.mh-launch { color: inherit; text-decoration: none; }
a.mh-launch:hover, a.mh-launch:focus-visible { text-decoration: none; }

/* ==========================================================================
   Map page — lock to the viewport, no page scrollbar.
   Every other page keeps the shell's normal padded canvas and lets the whole document scroll
   when its content is tall (mh-root/mh-shell/mh-sidebar only ever declare *min*-height, never
   height, in gvs-myhome.css). The map is a live, full-bleed canvas that must instead fill exactly
   what is left below the header with no page scroll of its own.
   A hardcoded "viewport minus header" number cannot be trusted to do that: GVS.Components changes
   the header's actual height at its own breakpoint (.mh-bar drops from 64px to 56px at
   max-width:560px, gvs-myhome.css), and drops mh-shell's internal flex/stretch entirely at
   max-width:900px (sidebar becomes an off-canvas drawer). So instead of computing that arithmetic
   ourselves, scoped to the one page that renders .map-page: turn mh-root into a two-row flex
   column (header row + "the rest"), which makes the second row exactly as tall as the header
   actually rendered, at any breakpoint, with nothing to keep in sync. mh-main and mh-canvas are
   then forced to fill that row in turn (mh-main would otherwise fall back to its own content
   height once mh-shell's 900px breakpoint drops its stretch), and mh-canvas's built-in padding is
   removed so Map.razor.css's own flex:1 (.map-page) fills the row exactly.
   ========================================================================== */
.mh-root:has(.map-page)
{
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
}

.mh-root:has(.map-page) > .mh-shell
{
    flex: 1 1 auto;
    min-height: 0;
}

.mh-root:has(.map-page) .mh-main
{
    height: 100%;
}

.mh-canvas:has(> .map-page)
{
    height: 100%;
    padding: 0;
    display: flex;
    flex-direction: column;
}

/* Belt-and-braces: the flex chain above should already land exactly on the viewport height, but
   guarantee no stray scrollbar from a sub-pixel rounding. Other pages are untouched -- they never
   render .map-page. */
html:has(.map-page),
body:has(.map-page) { overflow: hidden; }

/* ==========================================================================
   Dashboard — let the operational overview card take exactly what is left.
   Same problem as the map page above and solved the same way, for the same reason: a fixed height
   for the card cannot be right at every size. Too tall and the dashboard scrolls; too short and it
   leaves a band of white below it. Both were observed while tuning a number, which is the signal
   that a number is the wrong tool -- the space above the card (greeting, search hero, product
   launcher) comes from GVS.Components and changes at its own breakpoints, so only the browser knows
   what is left.
   So: turn the dashboard into a viewport-height flex column and let the card's section be the one
   row that grows. No arithmetic, nothing to keep in sync, and no scroll or gap at any width.
   Scoped to .dm-card so it applies to this dashboard only, and only above the phone breakpoint --
   below that the greeting, hero and launcher genuinely need more room than one viewport, and
   squeezing a 3D card into what little is left would serve nobody. A phone keeps the clamp in
   DashboardMap.razor.css and scrolls normally.
   ========================================================================== */
@media (min-width: 769px)
{
    .mh-root:has(.dm-card)
    {
        display: flex;
        flex-direction: column;
        height: 100vh;
        overflow: hidden;
    }

    .mh-root:has(.dm-card) > .mh-shell
    {
        flex: 1 1 auto;
        min-height: 0;
    }

    .mh-root:has(.dm-card) .mh-main
    {
        height: 100%;
        min-height: 0;
    }

    /* min-height:0 on every link in the chain: a flex item defaults to min-height:auto, which
       refuses to shrink below its content and would push the card's own height back out again,
       reintroducing the scrollbar this exists to remove. */
    .mh-canvas:has(.dm-card)
    {
        height: 100%;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

    .mh-canvas:has(.dm-card) > .mh-section:has(.dm-card)
    {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

}
/* ---------- Home: recents card ------------------------------------------- */
/* The card is app-local, so its new pieces live here rather than in the shared
   stylesheet - no package bump to change a screen only the suite has.
   The star OVERRIDES the shared rule that hid it until hover: it was decoration then and it is a
   control now. A control you cannot see has no affordance on a touch screen, cannot be reached by
   keyboard, and - the part that matters most here - leaves you unable to tell which places are
   already pinned without hovering each one in turn. */
.mh-rec-row { padding: 0 8px 0 18px; }
.mh-rec-open {
  display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0;
  padding: 11px 0; border: none; background: none; cursor: pointer; font: inherit;
  text-align: left; color: inherit; border-radius: var(--gvs-radius-md);
}
.mh-rec-open:focus-visible { outline: 2px solid var(--gvs-primary); outline-offset: -2px; }

.mh-rec-star {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border: none; background: none; cursor: pointer;
  border-radius: 50%; color: var(--gvs-fg-3);
  opacity: .45;
  transition: opacity var(--gvs-dur-fast), color var(--gvs-dur-fast), background var(--gvs-dur-fast);
}
.mh-rec-row:hover .mh-rec-star, .mh-rec-star:focus-visible { opacity: 1; }
.mh-rec-star:hover { background: var(--gvs-surface-sunken); }
.mh-rec-star:focus-visible { outline: 2px solid var(--gvs-primary); outline-offset: -2px; }
/* Pinned: always fully visible, because "which of these are favourites" is the question the
   list has to answer at a glance. */
.mh-rec-star.on { opacity: 1; color: var(--gvs-amber-500, #E8A317); }
.mh-rec-star.on .material-symbols-rounded { font-variation-settings: 'FILL' 1; }

.mh-rec-empty { padding: 22px 18px; font-size: 13px; color: var(--gvs-fg-3); }
.mh-rec-notice {
  margin: 6px 18px 0; padding: 8px 10px; border-radius: var(--gvs-radius-md);
  background: var(--gvs-amber-50); color: var(--gvs-amber-700); font-size: 12.5px;
}
[data-theme="dark"] .mh-rec-notice { background: transparent; color: var(--gvs-amber-300); }
.mh-rec-clear {
  display: block; margin: 8px 12px 12px auto; padding: 6px 10px;
  border: none; background: none; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--gvs-fg-3); border-radius: var(--gvs-radius-md);
}
.mh-rec-clear:hover { background: var(--gvs-surface-sunken); color: var(--gvs-fg-1); }

/* Needs-attention: the quiet state. An empty card with no words reads as a failed load, so it says
   what it means - but quietly. A big green tick shouts congratulations at somebody who was looking
   for problems; the tone here is "checked, nothing to do". */
.mh-att-empty {
  display: flex; align-items: center; gap: 14px;
  padding: 26px 18px 30px;
}
/* flex-basis AND aspect-ratio, because a flex item squeezes on either axis: `flex: none` alone
   still let the row's height decide, and the circle came out an oval. */
.mh-att-empty-badge {
  flex: 0 0 40px; width: 40px; height: 40px; aspect-ratio: 1;
  border-radius: 50%; background: var(--gvs-success-50);
  display: inline-flex; align-items: center; justify-content: center;
}
.mh-att-empty-badge .material-symbols-rounded {
  font-size: 22px; line-height: 1; color: var(--gvs-success-500);
}
.mh-att-empty-title { font-size: 13.5px; font-weight: 600; color: var(--gvs-fg-1); }
.mh-att-empty-hint { font-size: 12px; color: var(--gvs-fg-3); margin-top: 2px; }
