/* /Components/Layout/DashboardSidebar.razor.rz.scp.css */
/* ════════════════════════════════════════════════════════════════
   SecureIQ dashboard — sidebar (interactive island)
   Design tokens are inherited from .shell (DashboardLayout).
   ════════════════════════════════════════════════════════════════ */

.sidebar[b-8472zf39e6] {
    position: sticky;
    top: 0;
    height: 100vh;
    width: 248px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 0;
    /* No horizontal padding — children own their horizontal spacing */
    padding: 24px 0 20px;
    background: var(--bg-side);
    border-right: 1px solid var(--line);
    overflow: hidden;
    transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.sidebar--collapsed[b-8472zf39e6] { width: 64px; }

/* ─── Brand block: evalian on top, customer logo + toggle below ── */

.brand[b-8472zf39e6] {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 15px 22px;
    gap: 14px;
}

/* evalian logo, centred. Expanded shows the full lockup; collapsing shrinks the
   row width so the wordmark clips off the left and only the cube (pinned right)
   remains. Row height is fixed, so nothing below shifts. */
.brand-inner[b-8472zf39e6] {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    overflow: hidden;
    width: 162px;
    height: 26px;
    transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.sidebar--collapsed .brand-inner[b-8472zf39e6] { width: 30px; }

/* Bottom row: customer logo + toggle. The toggle stays in flow, so the row keeps
   its height when the logo folds — the nav below never jumps on collapse. */
.brand-bottom[b-8472zf39e6] {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 28px;
}

.customer-logo[b-8472zf39e6] {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    max-width: 150px;
    overflow: hidden;
    transition: opacity 0.18s ease, max-width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.customer-logo img[b-8472zf39e6] {
    display: block;
    /* Ceilings on both axes, never a fixed height: with height pinned at 26px, max-width clamping a
       wide wordmark squashed it (stretched-logo UAT bug). A wide logo now caps at the row width and
       gives up height instead, keeping its aspect ratio. */
    max-height: 26px;
    max-width: 100%;
    width: auto;
    height: auto;
}

/* Collapsed rail is too narrow for the wordmark — fold its width away (the toggle
   holds the row height, so nothing below shifts) */
.sidebar--collapsed .customer-logo[b-8472zf39e6] {
    opacity: 0;
    max-width: 0;
    pointer-events: none;
}

/* Toggle pinned to the right of the row; the customer logo stays centred. As the
   sidebar narrows, right:0 glides the toggle left smoothly (no jump). */
.brand-bottom > .sidebar-toggle[b-8472zf39e6] {
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
}

/* pinned to the right of .brand-inner, so shrinking the row reveals the cube */
.brand-logo[b-8472zf39e6] {
    display: block;
    height: 26px;
    width: auto;
    flex: none;
}

/* ─── Collapse toggle ──────────────────────────────────────────── */

.sidebar-toggle[b-8472zf39e6] {
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    display: grid;
    place-items: center;
    border: 1px solid var(--line);
    border-radius: 0;
    background: transparent;
    color: var(--faint);
    cursor: pointer;
    transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}

.sidebar-toggle:hover[b-8472zf39e6] {
    color: var(--text);
    border-color: rgba(255, 255, 255, 0.28);
    background: rgba(255, 255, 255, 0.06);
}

.sidebar-toggle[b-8472zf39e6]  svg {
    width: 15px;
    height: 15px;
    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.sidebar--collapsed .sidebar-toggle[b-8472zf39e6]  svg { transform: rotate(180deg); }

/* ─── Navigation ───────────────────────────────────────────────── */

.nav[b-8472zf39e6] {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
}

.nav[b-8472zf39e6]::-webkit-scrollbar { width: 4px; }
.nav[b-8472zf39e6]::-webkit-scrollbar-track { background: transparent; }
.nav[b-8472zf39e6]::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 4px; }
.nav[b-8472zf39e6]::-webkit-scrollbar-thumb:hover { background: var(--faint); }

.nav-spacer[b-8472zf39e6] { flex: 1; }

.nav-link[b-8472zf39e6] {
    display: flex;
    align-items: center;
    gap: 11px;
    /* 12px left = icon at ~21.5px from sidebar left (expanded)
       22px left = icon at ~31.5px from sidebar left (collapsed, centred in 64px) */
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    color: var(--muted);
    text-decoration: none;
    font-weight: 500;
    /* several nav-links are controls (role="button") with no href, so set the pointer explicitly
       rather than relying on the anchor default */
    cursor: pointer;
    overflow: hidden;
    position: relative;
    transition: color 0.18s ease, background 0.18s ease,
                padding 0.3s cubic-bezier(0.4, 0, 0.2, 1),
                gap 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.nav-link:hover[b-8472zf39e6] { color: var(--text); background: rgba(255, 255, 255, 0.07); }
.sidebar:not(.sidebar--collapsed) .nav-link:hover[b-8472zf39e6] { transform: translateX(2px); }

.nav-link.active[b-8472zf39e6] {
    color: var(--text);
    background: var(--surface-2);
    box-shadow: inset 2px 0 0 0 var(--accent);
}
.nav-link.active:hover[b-8472zf39e6] { color: var(--text); background: var(--surface-2); }
.nav-link.active .nav-icon[b-8472zf39e6] { color: var(--accent); }

/* Collapsed: widen left padding to centre icon, drop gap so text is fully clipped */
.sidebar--collapsed .nav-link[b-8472zf39e6] { padding: 9px 22px; gap: 0; }

.nav-icon[b-8472zf39e6] {
    width: 19px;
    height: 19px;
    flex: none;
    display: grid;
    place-items: center;
}

.nav-icon[b-8472zf39e6]  svg { width: 19px; height: 19px; }

/* Nav text: opacity only — overflow:hidden on .sidebar clips it during width transition */
.nav-text[b-8472zf39e6] {
    white-space: nowrap;
    /* Expanding: fade in after sidebar wide enough */
    transition: opacity 0.2s ease 0.18s;
}

.sidebar--collapsed .nav-text[b-8472zf39e6] {
    opacity: 0;
    pointer-events: none;
    /* Collapsing: disappear instantly */
    transition: opacity 0.06s ease;
}

/* Tooltip on collapsed icons */
.sidebar--collapsed .nav-link[b-8472zf39e6]::after {
    content: attr(data-tip);
    position: absolute;
    left: calc(100% + 10px);
    top: 50%;
    transform: translateY(-50%) translateX(-4px);
    background: var(--surface-2);
    color: var(--text);
    padding: 6px 12px;
    border-radius: 0;
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    border: 1px solid var(--line);
    box-shadow: 0 6px 20px rgba(0,0,0,0.35);
    z-index: 300;
    transition: opacity 0.15s ease, transform 0.15s ease;
}

.sidebar--collapsed .nav-link:hover[b-8472zf39e6]::after {
    opacity: 1;
    transform: translateY(-50%) translateX(0);
}

/* ─── Nav group (parent item + collapsible sub-menu) ──────────── */

.nav-group[b-8472zf39e6] { display: flex; flex-direction: column; }

.nav-chevron[b-8472zf39e6] {
    margin-left: auto;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    opacity: 0.4;
    transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease 0.18s;
}

.nav-chevron[b-8472zf39e6]  svg { width: 14px; height: 14px; }

.nav-chevron.open[b-8472zf39e6] { transform: rotate(180deg); }

.sidebar--collapsed .nav-chevron[b-8472zf39e6] {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.06s ease;
}

.nav-sub[b-8472zf39e6] {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

.nav-sub.open[b-8472zf39e6] { max-height: 320px; }

.sidebar--collapsed .nav-sub.open[b-8472zf39e6] { max-height: 0; }

.nav-sub-link[b-8472zf39e6] {
    display: block;
    /* indent aligns text with parent label: 12px padding + 19px icon + 11px gap = 42px */
    padding: 5px 12px 5px 42px;
    font-size: 14px;
    font-weight: 500;
    color: var(--muted);
    text-decoration: none;
    border-radius: var(--radius-sm);
    white-space: nowrap;
    cursor: pointer;
    opacity: 0.8;
    transition: background 0.15s ease, color 0.15s ease, opacity 0.15s ease;
}

.nav-sub-link:hover[b-8472zf39e6] { background: rgba(255, 255, 255, 0.07); color: var(--text); opacity: 1; }

.nav-sub-link.active[b-8472zf39e6] {
    color: var(--accent-2);
    background: var(--accent-soft);
    opacity: 1;
}

/* ─── Sidebar footer ───────────────────────────────────────────── */

.sidebar-footer[b-8472zf39e6] {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding-top: 14px;
    border-top: 1px solid var(--line-soft);
}

.user-chip[b-8472zf39e6] {
    display: flex;
    align-items: center;
    gap: 11px;
    /* 12px matches nav-link, 15px centres avatar (34px) in 64px when collapsed */
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    overflow: hidden;
    transition: padding 0.3s cubic-bezier(0.4, 0, 0.2, 1), gap 0.3s ease;
}

.sidebar--collapsed .user-chip[b-8472zf39e6] { padding: 10px 15px; gap: 0; }

.avatar[b-8472zf39e6] {
    width: 34px;
    height: 34px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 50%;
    font-size: 12px;
    font-weight: 700;
    color: #04201D;
    background: linear-gradient(140deg, #5EEAD9, #14B8A6);
}

.user-meta[b-8472zf39e6] {
    display: grid;
    line-height: 1.3;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    transition: opacity 0.2s ease 0.2s;
}

.sidebar--collapsed .user-meta[b-8472zf39e6] {
    opacity: 0;
    transition: opacity 0.06s ease;
}

.user-meta strong[b-8472zf39e6] { font-size: 13px; overflow: hidden; text-overflow: ellipsis; }
.user-meta span[b-8472zf39e6] { font-size: 11.5px; color: var(--faint); overflow: hidden; text-overflow: ellipsis; }

.nav-link.subtle[b-8472zf39e6] { font-size: 13px; padding: 8px 12px; }
.sidebar--collapsed .nav-link.subtle[b-8472zf39e6] { padding: 8px 22px; }

/* ─── Staff-only Admin console button (faintly outlined, pinned low) ── */

.admin-cta[b-8472zf39e6] {
    display: flex;
    align-items: center;
    gap: 11px;
    margin: 0 12px 12px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--muted);
    text-decoration: none;
    font-weight: 600;
    overflow: hidden;
    position: relative;
    transition: color 0.18s ease, background 0.18s ease, border-color 0.18s ease,
                padding 0.3s cubic-bezier(0.4, 0, 0.2, 1), margin 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.admin-cta:hover[b-8472zf39e6] { color: var(--text); border-color: var(--accent-line); background: rgba(255, 255, 255, 0.05); }

.admin-cta.active[b-8472zf39e6] {
    color: var(--text);
    border-color: var(--accent-line);
    background: var(--surface-2);
}
.admin-cta.active .nav-icon[b-8472zf39e6] { color: var(--accent); }

/* Collapsed rail: centre the icon, drop the label + margins */
.sidebar--collapsed .admin-cta[b-8472zf39e6] { margin: 0 8px 12px; padding: 10px 0; gap: 0; justify-content: center; }

/* Tooltip on the collapsed Admin button (mirrors the nav-link tooltip) */
.sidebar--collapsed .admin-cta[b-8472zf39e6]::after {
    content: attr(data-tip);
    position: absolute;
    left: calc(100% + 10px);
    top: 50%;
    transform: translateY(-50%) translateX(-4px);
    background: var(--surface-2);
    color: var(--text);
    padding: 6px 12px;
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    border: 1px solid var(--line);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
    z-index: 300;
    transition: opacity 0.15s ease, transform 0.15s ease;
}
.sidebar--collapsed .admin-cta:hover[b-8472zf39e6]::after { opacity: 1; transform: translateY(-50%) translateX(0); }

/* ─── Clickable profile chip + photo ──────────────────────────── */

.user-chip[b-8472zf39e6] { text-decoration: none; cursor: pointer; }
.user-chip:hover[b-8472zf39e6] { border-color: var(--accent-line); background: var(--surface-2); }

.avatar img[b-8472zf39e6] {
    width: 100%;
    height: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: 50%;
    display: block;
}

/* ─── Responsive: collapse to a top bar ───────────────────────── */

@media (max-width: 900px) {
    .sidebar[b-8472zf39e6] {
        position: static;
        width: 100% !important;
        height: auto;
        flex-direction: row;
        align-items: center;
        overflow-x: auto;
        overflow-y: visible;
        padding: 10px 14px;
        border-right: 0;
        border-bottom: 1px solid var(--line-soft);
        transition: none;
    }
    .sidebar-deco[b-8472zf39e6] { display: none; }
    .brand-bottom[b-8472zf39e6] { display: none; }
    .brand[b-8472zf39e6] { flex-direction: row !important; align-items: center !important; padding: 0 8px 0 0 !important; gap: 8px; }
    .brand-inner[b-8472zf39e6] { overflow: visible; gap: 8px !important; transform: none !important; }
    .brand-name[b-8472zf39e6] { opacity: 1 !important; transition: none; }
    .sidebar-toggle[b-8472zf39e6] { display: none; }
    .nav[b-8472zf39e6] { flex-direction: row; flex: none; overflow: visible; }
    .nav-spacer[b-8472zf39e6] { display: none; }
    .nav-group[b-8472zf39e6] { flex-direction: row; }
    .nav-chevron[b-8472zf39e6] { display: none; }
    .nav-sub[b-8472zf39e6] { display: none; }
    .nav-link[b-8472zf39e6] { padding: 8px 10px !important; gap: 0 !important; }
    .nav-text[b-8472zf39e6] { display: none; }
    .admin-cta[b-8472zf39e6] { margin: 0 6px 0 0 !important; padding: 8px 10px !important; gap: 0 !important; }
    .sidebar-footer[b-8472zf39e6] { margin-left: auto; flex-direction: row; border-top: 0; padding-top: 0; }
    .user-meta[b-8472zf39e6] { display: none !important; }
    .user-chip[b-8472zf39e6] { padding: 8px 10px !important; gap: 0 !important; }
}
/* /Components/Layout/ImpersonationBanner.razor.rz.scp.css */
/* ════════════════════════════════════════════════════════════════
   Cross-tenant "viewing as" / impersonation banner.
   Full-width teal/green bar pinned to the top of the app so it is
   unmistakable that the current session is not the admin's own.
   Colours are hard-coded here so the banner does not depend on the
   .shell token scope (it renders above the shell).
   ════════════════════════════════════════════════════════════════ */

.imp-banner[b-r7p8l4bo7h] {
    /* Sits above the customer setup gate (z-index 800) so an admin testing the gate
       via impersonation can always reach the Exit button. */
    position: sticky;
    top: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 20px;
    /* Pinned to the height the top bar offsets itself by (--imp-banner-h, declared on html in
       site/layout.css). The two have to agree or the bar sticks over this banner, or under it. */
    box-sizing: border-box;
    min-height: var(--imp-banner-h, 42px);
    background: rgba(45, 212, 191, 0.14);
    border-bottom: 1px solid rgba(45, 212, 191, 0.42);
    color: #EAEEEF;
    font-family: "Inter", "Segoe UI", system-ui, sans-serif;
    font-size: 13px;
    line-height: 1.3;
}

.imp-dot[b-r7p8l4bo7h] {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #2DD4BF;
    box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.22);
}

.imp-text[b-r7p8l4bo7h] { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.imp-text strong[b-r7p8l4bo7h] { color: #5EEAD9; font-weight: 700; }
.imp-sub[b-r7p8l4bo7h] { color: #8A9296; font-size: 12px; }

.imp-exit[b-r7p8l4bo7h] {
    margin-left: auto;
    flex: none;
    padding: 6px 14px;
    border: 1px solid rgba(45, 212, 191, 0.42);
    border-radius: 0;
    background: transparent;
    color: #2DD4BF;
    font: 600 12.5px "Inter", "Segoe UI", system-ui, sans-serif;
    cursor: pointer;
    transition: background .18s ease, color .18s ease;
}

.imp-exit:hover[b-r7p8l4bo7h] {
    background: #2DD4BF;
    color: #0E1011;
}

.imp-exit:focus-visible[b-r7p8l4bo7h] {
    outline: 2px solid #2DD4BF;
    outline-offset: 2px;
}
/* /Components/Layout/MainLayout.razor.rz.scp.css */
.page[b-vz7096s1l5] {
    position: relative;
    display: flex;
    flex-direction: column;
}

main[b-vz7096s1l5] {
    flex: 1;
}

.sidebar[b-vz7096s1l5] {
    background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}

.top-row[b-vz7096s1l5] {
    background-color: #f7f7f7;
    border-bottom: 1px solid #d6d5d5;
    justify-content: flex-end;
    height: 3.5rem;
    display: flex;
    align-items: center;
}

    .top-row[b-vz7096s1l5]  a, .top-row[b-vz7096s1l5]  .btn-link {
        white-space: nowrap;
        margin-left: 1.5rem;
        text-decoration: none;
    }

    .top-row[b-vz7096s1l5]  a:hover, .top-row[b-vz7096s1l5]  .btn-link:hover {
        text-decoration: underline;
    }

    .top-row[b-vz7096s1l5]  a:first-child {
        overflow: hidden;
        text-overflow: ellipsis;
    }

@media (max-width: 640.98px) {
    .top-row[b-vz7096s1l5] {
        justify-content: space-between;
    }

    .top-row[b-vz7096s1l5]  a, .top-row[b-vz7096s1l5]  .btn-link {
        margin-left: 0;
    }
}

@media (min-width: 641px) {
    .page[b-vz7096s1l5] {
        flex-direction: row;
    }

    .sidebar[b-vz7096s1l5] {
        width: 250px;
        height: 100vh;
        position: sticky;
        top: 0;
    }

    .top-row[b-vz7096s1l5] {
        position: sticky;
        top: 0;
        z-index: 1;
    }

    .top-row.auth[b-vz7096s1l5]  a:first-child {
        flex: 1;
        text-align: right;
        width: 0;
    }

    .top-row[b-vz7096s1l5], article[b-vz7096s1l5] {
        padding-left: 2rem !important;
        padding-right: 1.5rem !important;
    }
}

#blazor-error-ui[b-vz7096s1l5] {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss[b-vz7096s1l5] {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }
/* /Components/Layout/NavMenu.razor.rz.scp.css */
.navbar-toggler[b-q19jq43con] {
    appearance: none;
    cursor: pointer;
    width: 3.5rem;
    height: 2.5rem;
    color: white;
    position: absolute;
    top: 0.5rem;
    right: 1rem;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}

.navbar-toggler:checked[b-q19jq43con] {
    background-color: rgba(255, 255, 255, 0.5);
}

.top-row[b-q19jq43con] {
    min-height: 3.5rem;
    background-color: rgba(0,0,0,0.4);
}

.navbar-brand[b-q19jq43con] {
    font-size: 1.1rem;
}

.bi[b-q19jq43con] {
    display: inline-block;
    position: relative;
    width: 1.25rem;
    height: 1.25rem;
    margin-right: 0.75rem;
    top: -1px;
    background-size: cover;
}

.bi-house-door-fill-nav-menu[b-q19jq43con] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}

.bi-plus-square-fill-nav-menu[b-q19jq43con] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}

.bi-list-nested-nav-menu[b-q19jq43con] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}

.nav-item[b-q19jq43con] {
    font-size: 0.9rem;
    padding-bottom: 0.5rem;
}

    .nav-item:first-of-type[b-q19jq43con] {
        padding-top: 1rem;
    }

    .nav-item:last-of-type[b-q19jq43con] {
        padding-bottom: 1rem;
    }

    .nav-item[b-q19jq43con]  .nav-link {
        color: #d7d7d7;
        background: none;
        border: none;
        border-radius: 4px;
        height: 3rem;
        display: flex;
        align-items: center;
        line-height: 3rem;
        width: 100%;
    }

.nav-item[b-q19jq43con]  a.active {
    background-color: rgba(255,255,255,0.37);
    color: white;
}

.nav-item[b-q19jq43con]  .nav-link:hover {
    background-color: rgba(255,255,255,0.1);
    color: white;
}

.nav-scrollable[b-q19jq43con] {
    display: none;
}

.navbar-toggler:checked ~ .nav-scrollable[b-q19jq43con] {
    display: block;
}

@media (min-width: 641px) {
    .navbar-toggler[b-q19jq43con] {
        display: none;
    }

    .nav-scrollable[b-q19jq43con] {
        /* Never collapse the sidebar for wide screens */
        display: block;

        /* Allow sidebar to scroll for tall menus */
        height: calc(100vh - 3.5rem);
        overflow-y: auto;
    }
}
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
/* ════════════════════════════════════════════════════════════════
   ReconnectModal.razor.css - the circuit-recovery dialog
   ----------------------------------------------------------------
   Replaces the Blazor template's white rounded box with a portal
   dialog: square, --surface card, 1px --line border, accent eyebrow,
   one solid-accent action, the same scrim and rise as the admin kit's
   .modal-overlay / .modal.

   THEME TOKENS ARE DECLARED LOCALLY. The tokens live on .shell (see
   site/layout.css), and this dialog is rendered by App.razor outside
   it, next to <Routes>, so it inherits nothing. Same situation as
   #app-splash, .ob-shell and .login-shell, and the same answer: the
   dark values are the base and html[data-theme="light"] (set pre-paint
   by App.razor's inline script) re-skins them. Keep the values in step
   with .shell in site/layout.css and app.css.

   VISIBILITY IS THE FRAMEWORK'S. It toggles one state class on the
   dialog (components-reconnect-show / -retrying / -failed / -paused /
   -resume-failed) and nothing else, so each block carries the matching
   *-visible class and the pairs below are the whole mapping.
   ════════════════════════════════════════════════════════════════ */

#components-reconnect-modal[b-h0aav78qbn] {
    --surface:     #1A1D1F;
    --surface-2:   #23272A;
    --line:        rgba(255, 255, 255, 0.09);
    --text:        #EAEEEF;
    --muted:       #8A9296;
    --accent:      #2DD4BF;
    --bad:         #FF6B6B;
    --bad-soft:    rgba(255, 107, 107, 0.12);
    --bad-line:    rgba(255, 107, 107, 0.42);
    --btn-ink:     #04201D;
    --font-display: "Inter", "Segoe UI", system-ui, sans-serif;
    --font-body:    "Inter", "Segoe UI", system-ui, sans-serif;

    position: fixed;
    inset: 0;
    margin: auto;
    width: min(400px, calc(100vw - 32px));
    height: fit-content;
    max-height: calc(100vh - 48px);
    overflow: auto;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 30px 70px -20px rgba(0, 0, 0, 0.8);
}

html[data-theme="light"] #components-reconnect-modal[b-h0aav78qbn] {
    --surface:     #FFFFFF;
    --surface-2:   #EDF0F3;
    --line:        rgba(16, 24, 32, 0.13);
    --text:        #171A1D;
    --muted:       #566069;
    --accent:      #0E9E8E;
    --bad:         #D14343;
    --bad-soft:    rgba(209, 67, 67, 0.10);
    --bad-line:    rgba(209, 67, 67, 0.38);

    box-shadow: 0 30px 70px -30px rgba(16, 24, 32, 0.45);
}

#components-reconnect-modal[open][b-h0aav78qbn] {
    animation: rc-rise-b-h0aav78qbn .22s cubic-bezier(.22, .9, .3, 1) both;
}

/* Written literally rather than through a token: ::backdrop only inherits custom properties from its
   originating element in recent browsers, so a var() here would fall back to one theme's scrim in the
   others. Same two values as the admin kit's .modal-overlay. */
#components-reconnect-modal[b-h0aav78qbn]::backdrop {
    background: rgba(7, 9, 10, 0.66);
    backdrop-filter: blur(3px);
    animation: rc-fade-b-h0aav78qbn .18s ease both;
}

html[data-theme="light"] #components-reconnect-modal[b-h0aav78qbn]::backdrop {
    background: rgba(16, 24, 32, 0.42);
}

@keyframes rc-rise-b-h0aav78qbn { from { opacity: 0; transform: translateY(12px) scale(.99); } to { opacity: 1; transform: none; } }
@keyframes rc-fade-b-h0aav78qbn { from { opacity: 0; } to { opacity: 1; } }

/* ─── State visibility ────────────────────────────────────────────
   Hidden by default; one state class on the dialog reveals its own
   block. The two dialog-level entries are the template's own: they
   keep the card displayed in the states where the framework did not
   call showModal itself. */

.components-reconnect-first-attempt-visible[b-h0aav78qbn],
.components-reconnect-repeated-attempt-visible[b-h0aav78qbn],
.components-reconnect-failed-visible[b-h0aav78qbn],
.components-pause-visible[b-h0aav78qbn],
.components-resume-failed-visible[b-h0aav78qbn] {
    display: none;
}

#components-reconnect-modal.components-reconnect-retrying[b-h0aav78qbn],
#components-reconnect-modal.components-reconnect-failed[b-h0aav78qbn] {
    display: block;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-h0aav78qbn],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-h0aav78qbn],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-h0aav78qbn],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-h0aav78qbn],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-h0aav78qbn] {
    display: grid;
}

/* ─── Card ────────────────────────────────────────────────────────
   Bootstrap is still linked ahead of this file, so the card sets its
   own font rather than inheriting the body's. */

.rc-card[b-h0aav78qbn] {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 20px 22px 22px;
    font-family: var(--font-body);
    font-size: 14px;
    line-height: 1.45;
}

.rc-eyebrow[b-h0aav78qbn] {
    margin: 0;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--accent);
}

.rc-state[b-h0aav78qbn] {
    grid-template-columns: auto 1fr;
    column-gap: 14px;
    align-items: center;
}

.rc-copy[b-h0aav78qbn] { min-width: 0; }

.rc-title[b-h0aav78qbn] {
    margin: 0;
    font-family: var(--font-display);
    font-size: 17px;
    font-weight: 600;
    color: var(--text);
}

.rc-note[b-h0aav78qbn] {
    margin: 4px 0 0;
    font-size: 13px;
    color: var(--muted);
}

/* ─── State marks ─────────────────────────────────────────────────
   One 34px disc in every state, so the card does not resize as the
   state changes. The spinner matches #app-splash's. */

.rc-mark[b-h0aav78qbn] {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    box-sizing: border-box;
}

.rc-spinner[b-h0aav78qbn] {
    border: 3px solid var(--line);
    border-top-color: var(--accent);
    animation: rc-spin-b-h0aav78qbn .8s linear infinite;
}

@keyframes rc-spin-b-h0aav78qbn { to { transform: rotate(360deg); } }

.rc-mark.is-bad[b-h0aav78qbn],
.rc-mark.is-idle[b-h0aav78qbn] {
    display: grid;
    place-items: center;
    border: 2px solid var(--bad-line);
    background: var(--bad-soft);
    color: var(--bad);
}

.rc-mark.is-idle[b-h0aav78qbn] {
    border-color: var(--line);
    background: var(--surface-2);
    color: var(--muted);
}

.rc-mark svg[b-h0aav78qbn] { width: 17px; height: 17px; }

/* ─── Action ──────────────────────────────────────────────────────
   The solid accent of the chromeless cards (.ob-card .btn-primary)
   rather than the admin kit's tinted primary: there is one action on
   the card and it is the way out. */

.rc-foot[b-h0aav78qbn] { justify-items: end; }

.rc-btn[b-h0aav78qbn] {
    padding: 9px 18px;
    border: 1px solid var(--accent);
    border-radius: 0;
    background: var(--accent);
    color: var(--btn-ink);
    font: 600 13px var(--font-body);
    cursor: pointer;
    transition: filter .18s ease;
}

.rc-btn:hover[b-h0aav78qbn] { filter: brightness(1.08); }
.rc-btn:focus-visible[b-h0aav78qbn] { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
    #components-reconnect-modal[open][b-h0aav78qbn] { animation: none; }
    #components-reconnect-modal[b-h0aav78qbn]::backdrop { animation: none; }
    .rc-spinner[b-h0aav78qbn] { animation-duration: 2s; }
}
/* /Components/Pages/ThreatMap.razor.rz.scp.css */
/* ════════════════════════════════════════════════════════════════
   Live threat map — dotted world, dark/teal, square. Tokens from .shell.
   ════════════════════════════════════════════════════════════════ */

.tm[b-vl91muolxn] {
    border: 1px solid var(--line);
    background: var(--surface);
    display: flex;
    flex-direction: column;
}

/* ─── Header bar ──────────────────────────────────────────────── */

.tm-bar[b-vl91muolxn] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Wraps: the title, the window and one legend entry per source add up to more than a narrow panel,
       and an unwrapped row was the widest thing in the dashboard's grid, so it pushed the panel past the
       column edge where it was cut off. */
    flex-wrap: wrap;
    gap: 8px 16px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--line-soft);
}

.tm-title[b-vl91muolxn] { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; min-width: 0; }
.tm-title h2[b-vl91muolxn] { margin: 0; font-family: var(--font-display); font-size: 16px; font-weight: 600; }
.tm-sub[b-vl91muolxn] { font-size: 12.5px; color: var(--faint); }

.tm-live[b-vl91muolxn] {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 0 rgba(45, 212, 191, 0.5);
    animation: tm-pulse-b-vl91muolxn 2s ease-out infinite;
}
@keyframes tm-pulse-b-vl91muolxn {
    0%   { box-shadow: 0 0 0 0 rgba(45, 212, 191, 0.5); }
    70%  { box-shadow: 0 0 0 8px rgba(45, 212, 191, 0); }
    100% { box-shadow: 0 0 0 0 rgba(45, 212, 191, 0); }
}
.tm-legend[b-vl91muolxn] { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 12px; color: var(--muted); }
.tm-legend span[b-vl91muolxn] { display: inline-flex; align-items: center; gap: 6px; }
.tm-legend .tm-legdot[b-vl91muolxn] { width: 9px; height: 9px; display: inline-block; }

/* a chosen source whose table isn't in this workspace — still named, visibly not plotted */
.tm-legend span.is-off[b-vl91muolxn] { opacity: 0.4; text-decoration: line-through; }

/* ─── Map stage ───────────────────────────────────────────────── */

/* Sized from the map's own viewBox aspect rather than a fixed height: under `meet` a
   stage taller than the map just letterboxes it, and the column is fluid. */
.tm-stage[b-vl91muolxn] {
    position: relative;
    aspect-ratio: 1000 / 420;
    min-height: 300px;
    max-height: 470px;
    overflow: hidden;
    background:
        radial-gradient(720px 360px at 50% 35%, rgba(45, 212, 191, 0.022), transparent 65%),
        var(--bg);
}

.tm-map[b-vl91muolxn] { display: block; width: 100%; height: 100%; cursor: grab; touch-action: none; }
.tm-map.tm-grabbing[b-vl91muolxn] { cursor: grabbing; }
.tm-pan[b-vl91muolxn] { will-change: transform; }

/* real continents */
.tm-land[b-vl91muolxn] {
    fill: var(--map-land);
    stroke: rgba(255, 255, 255, 0.08);
    stroke-width: 0.4;
    stroke-linejoin: round;
}

/* country borders — thin, subtle lines over the land */
.tm-borders[b-vl91muolxn] {
    fill: none;
    stroke: rgba(255, 255, 255, 0.16);
    stroke-width: 0.5;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
    pointer-events: none;
}

/* ─── Zoom-invariant chrome ───────────────────────────────────────
   --tm-k is 1/zoom, published by threatmap.js on the <svg> (default 1 before it runs). Anything
   whose SIZE should stay constant on screen multiplies by it; anything whose POSITION should move
   with the map is left alone. Sizes go through the CSS geometry property `r` where the shape is a
   circle — no transform, so nothing can shift off its own centre — and through a transform only for
   paths, which have no equivalent property. */

/* glow + on-map label for the selected origin. Both carry the selected dot's own rosette offset (see
   `.tm-evt.is-spread`), which is a no-op at the default 0 for a dot that sits on its own coordinate. */
.tm-selglow[b-vl91muolxn] {
    pointer-events: none;
    /* Sized to sit around one dot and its neighbours in the same rosette, so it came down with them
       (it was 48). A glow wider than the whole knot picks out a region, not the address that was
       pressed. */
    r: calc(34px * var(--tm-k, 1));
    transform: translate(calc(var(--tm-dx, 0) * 1px * var(--tm-k, 1)),
                         calc(var(--tm-dy, 0) * 1px * var(--tm-k, 1)));
}
.tm-maplabel[b-vl91muolxn] { pointer-events: none; }
.tm-maplabel line[b-vl91muolxn] { stroke: rgba(255, 255, 255, 0.5); stroke-width: 1; vector-effect: non-scaling-stroke; }
.tm-maplabel text[b-vl91muolxn] {
    fill: var(--text);
    font: 600 11px var(--font-body);
    letter-spacing: 0.04em;
    font-variant-numeric: tabular-nums;
}

/* The label is a leader line plus text, so it can't shrink by radius. It scales about the end
   nearest the marker, so the annotation stays pinned to the dot it belongs to at every zoom. The
   translate carries the marker's rosette offset; it comes first, and transform-origin doesn't apply to
   it, so the scale still happens about the leader's own left edge. */
.tm-maplabel-fx[b-vl91muolxn] {
    transform: translate(calc(var(--tm-dx, 0) * 1px * var(--tm-k, 1)),
                         calc(var(--tm-dy, 0) * 1px * var(--tm-k, 1)))
               scale(var(--tm-k, 1));
    transform-box: fill-box;
    transform-origin: left center;
}

/* ─── Connections in flight ───────────────────────────────────────
   One missile per arc, launched on a staggered cycle (see BuildShots) so only a handful are
   travelling at once. Opacity and motion are both SMIL on the elements themselves — the browser
   drives them, so a Blazor Server circuit sends nothing per frame. Everything here starts at
   opacity 0: an arc is only visible while its own shot is in the air. */

.tm-shots[b-vl91muolxn] { pointer-events: none; }

/* The resting opacity of a trail and a missile is an ATTRIBUTE on the element, not a rule here: a
   CSS declaration outranks a SMIL-animated presentation attribute (and scoped CSS raises the
   specificity further), so `opacity: 0` in this file would pin them invisible for good. */
.tm-trail[b-vl91muolxn] {
    fill: none;
    stroke-width: 0.7;
    vector-effect: non-scaling-stroke;
}

/* One symmetric dart rather than a separate head and tail: its bounding box is centred on the path
   point, so cancelling the zoom about its own centre shrinks it exactly in place. Two shapes at
   fixed offsets would have drifted apart as they shrank — a transform scales size, while the
   offsets holding them together would have stayed at full size. */
.tm-missile-body[b-vl91muolxn] {
    stroke: none;
    transform: scale(var(--tm-k, 1));
    transform-box: fill-box;
    transform-origin: center;
}

/* The customer's own sites are where the arcs start from and are NOT drawn as markers: an egress
   address geolocates to the ISP's own centroid, so a pin on it claimed a place for the customer's
   office that the data can't support. */

/* live blocked-traffic events — pop in, pulse, fade out over their lifespan */
.tm-evt[b-vl91muolxn] {
    cursor: pointer;
    animation-name: tm-life-b-vl91muolxn;
    animation-timing-function: ease;
    animation-fill-mode: both;
}

/* A real origin is a measurement over a window, not an event at an instant, so it holds — only its
   ripple pulses. Marker lifetimes are what the synthesized feed used to be built out of: a dot that
   appeared and faded read as "a connection just happened", which is precisely what the data can't
   say. */
.tm-evt.is-origin[b-vl91muolxn] { animation: none; opacity: 1; }
/* Radius (not a transform) carries the zoom cancellation, so the dot shrinks around cx/cy and can
   never drift off its own coordinates. The markup's r= is the unzoomed size; these win over it.

   2.4 rather than the 3.4 this shipped at, in step with RingGap coming down from 7 to 5: the two
   numbers are one setting, and a rosette drawn at the old pair covered several countries at the
   default framing. */
.tm-evt .core[b-vl91muolxn] {
    transition: r .15s ease;   /* fill set inline, per source */
    r: calc(2.4px * var(--tm-k, 1));
    vector-effect: non-scaling-stroke;
}

/* The pointer target, invisible and a shade wider than the dot. RingGap/2, so by construction it
   reaches halfway to the next marker in the same rosette and never past it: the dots got smaller
   without the click getting harder, and no marker can swallow its neighbour's press. `transparent`,
   not `fill: none` — a circle with no fill isn't hit-tested. */
.tm-evt .hit[b-vl91muolxn] {
    fill: transparent;
    r: calc(2.5px * var(--tm-k, 1));
}

/* fill-box anchors the scale to the ripple's own centre (not the SVG viewport),
   so rings expand in place instead of flying across the map */
.tm-evt .ripple[b-vl91muolxn] {
    fill: none; stroke-width: 1.4; /* stroke set inline, per source */
    r: calc(3.6px * var(--tm-k, 1));
    vector-effect: non-scaling-stroke;
    transform-box: fill-box;
    transform-origin: center;
    animation: tm-ring-b-vl91muolxn 2.4s ease-out infinite;
}

.tm-evt:hover .core[b-vl91muolxn] { r: calc(4px * var(--tm-k, 1)); }
.tm-evt:focus[b-vl91muolxn] { outline: none; }
.tm-evt:focus .core[b-vl91muolxn] { r: calc(4px * var(--tm-k, 1)); stroke: #fff; stroke-width: 1.5; }

/* pinned while inspected — hold steady */
.tm-evt.is-active[b-vl91muolxn] {
    animation: none;
    opacity: 1;
}
.tm-evt.is-active .core[b-vl91muolxn] { r: calc(4.4px * var(--tm-k, 1)); stroke: #fff; stroke-width: 2; }

/* ─── Co-located addresses ────────────────────────────────────────
   Whole hosting regions share a single centroid, and stacked dots are one dot with the rest unreachable
   underneath. Every address keeps its own marker; the ones that share a place carry a small rosette
   offset so each stays visible and clickable (see `Rosette` in the code-behind).

   The offset is in map units at zoom 1 and is multiplied by --tm-k, so it is really SCREEN space: the
   rosette holds one size on screen and shrinks geographically as the reader zooms in, until every dot
   converges on the coordinate it belongs to. That is the difference from the spiral this replaced,
   whose offsets were DEGREES and had no bound — forty addresses at one hosting region reached 2.6° out
   and put the outermost dots ~290km into the Atlantic while the panel named the real city.

   It goes on the marker group, whose position comes from its parent's transform attribute, so a CSS
   transform here replaces nothing. The dots inside still shrink by `r`, which a parent translate can't
   disturb. */
.tm-evt.is-spread[b-vl91muolxn] {
    transform: translate(calc(var(--tm-dx, 0) * 1px * var(--tm-k, 1)),
                         calc(var(--tm-dy, 0) * 1px * var(--tm-k, 1)));
}

/* One pulse per place, not one per address. The ripple sweeps out to 3.4x its own radius, which is
   wider than the rosette it sits in, so nineteen of them overlapping turned a knot of dots into a
   pool of fading rings with no dots legible in it. Only the address ON the coordinate (the busiest,
   at zero offset) keeps the pulse; its ring expands through the rest of the group, which is the place
   reading as live. Everything in the group still hovers, clicks and selects the same. */
.tm-evt.is-spread .ripple[b-vl91muolxn] { display: none; }

/* whole-event lifecycle: fade/scale in, hold, fade out (duration set inline) */
@keyframes tm-life-b-vl91muolxn {
    0%   { opacity: 0; }
    10%  { opacity: 1; }
    82%  { opacity: 1; }
    100% { opacity: 0; }
}

@keyframes tm-ring-b-vl91muolxn {
    0%   { transform: scale(0.5); opacity: 0.8; }
    100% { transform: scale(3.4); opacity: 0; }
}

/* ─── Empty stage ─────────────────────────────────────────────── */
/* Centred over the map rather than replacing it: the world stays, so an empty window reads as
   "nothing to plot" instead of a panel that failed to load. */

.tm-empty[b-vl91muolxn] {
    position: absolute;
    inset: 0;
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 24px;
    text-align: center;
    font-size: 13px;
    color: var(--muted);
    pointer-events: none;
}

/* ─── Zoom controls ───────────────────────────────────────────── */

.tm-zoomctl[b-vl91muolxn] {
    position: absolute;
    left: 16px;
    bottom: 16px;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--line);
    background: rgba(14, 16, 17, 0.7);
    backdrop-filter: blur(4px);
}
.tm-zoomctl button[b-vl91muolxn] {
    width: 30px; height: 30px;
    border: none;
    background: transparent;
    color: var(--muted);
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    transition: color .15s ease, background .15s ease;
}
.tm-zoomctl button:first-child[b-vl91muolxn] { border-bottom: 1px solid var(--line); }
.tm-zoomctl button:hover[b-vl91muolxn] { color: var(--accent); background: rgba(45, 212, 191, 0.08); }

/* ─── Detail panel ──────────────────────────────────────────────
   The card is sized to FIT the stage, not to scroll inside it. It opens on top of the map, so a reader
   who has to scroll it has to scroll a thing that is covering what they clicked; the panel shipped with
   ~450px of content in a stage that can be 300px tall.

   Three rules keep it inside: every value is one line (clamped, full text on a `title`), every row has
   an explicit line-height so the height is arithmetic rather than a guess, and the sparkline is the one
   row allowed to give up its own height. `overflow-y: auto` stays as a last resort for a font stack that
   measures larger than expected, never as the layout. */

.tm-detail[b-vl91muolxn] {
    position: absolute;
    top: 0; right: 0;
    width: 300px; max-width: 80%; height: 100%;
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    background: rgba(12, 14, 15, 0.9);
    backdrop-filter: blur(8px);
    border-left: 1px solid var(--accent-line);
    box-shadow: -20px 0 40px -20px rgba(0, 0, 0, 0.7);
    transform: translateX(100%);
    opacity: 0;
    pointer-events: none;
    transition: transform .32s cubic-bezier(0.4, 0, 0.2, 1), opacity .2s ease;
    overflow-y: auto;
}
.tm--detail-open .tm-detail[b-vl91muolxn] { transform: translateX(0); opacity: 1; pointer-events: auto; }

/* Only the sparkline flexes (below). Everything else holds its measured height, so nothing the reader
   came to read can be squeezed away. Written as an exclusion rather than as a blanket `> *` that the
   sparkline's own rule then has to outrank: the two would tie on specificity and be settled by which
   one happens to sit lower in the file. */
.tm-detail > :not(.tm-spark)[b-vl91muolxn] { flex: 0 0 auto; }

/* One line, ellipsised, full value on the element's title. These are all tokens the reader recognises
   at a glance (an address, a table name, a count), so a clipped tail costs nothing and a wrapped one is
   what turned a card that fits into a card that scrolls. The two prose lines are the exception below. */
.tm-ip[b-vl91muolxn], .tm-srcpill[b-vl91muolxn], .tm-stats dt[b-vl91muolxn], .tm-stats dd[b-vl91muolxn] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The place and the action line are sentences, not tokens: "Connection success · Connection failed"
   read as "Connection faile…" on one line, which tells the reader nothing. They wrap instead, bounded
   at two lines so the card's height stays arithmetic, with the full value still on a title. */
.tm-place[b-vl91muolxn], .tm-what[b-vl91muolxn] {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.tm-close[b-vl91muolxn] {
    position: absolute; top: 10px; right: 12px;
    width: 24px; height: 24px;
    border: 1px solid var(--line);
    background: transparent; color: var(--muted);
    font-size: 16px; line-height: 1; cursor: pointer;
    transition: color .15s ease, border-color .15s ease;
}
.tm-close:hover[b-vl91muolxn] { color: var(--text); border-color: var(--accent-line); }

.tm-eyebrow[b-vl91muolxn] {
    margin: 0 0 3px;
    font-size: 10.5px; line-height: 1.3; font-weight: 600; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--accent);
}
.tm-ip[b-vl91muolxn] {
    margin: 0;
    font-family: var(--font-display);
    font-size: 21px;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.01em;
    font-variant-numeric: tabular-nums;
}
.tm-place[b-vl91muolxn] { margin: 3px 0 8px; color: var(--muted); font-size: 12.5px; line-height: 1.35; }

.tm-inc[b-vl91muolxn] {
    color: var(--accent);
    font-weight: 700;
    cursor: pointer;
    border-bottom: 1px dashed var(--accent-line);
}
.tm-noinc[b-vl91muolxn] { color: var(--faint); font-weight: 500; }

/* Stacked, not a row: the pill alone is over half the 300px panel, so anything beside it was left with
   too little to say what the traffic was. The action line gets the panel's whole width underneath. */
.tm-cat[b-vl91muolxn] { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin-bottom: 8px; min-width: 0; }
.tm-what[b-vl91muolxn] { font-size: 12.5px; line-height: 1.35; color: var(--muted); min-width: 0; max-width: 100%; }

/* source pill — colour set inline to match the map dot */
.tm-srcpill[b-vl91muolxn] {
    display: inline-flex;
    align-items: center;
    /* The pill names the source, so it keeps its whole width and a long one clips at the panel edge
       rather than shrinking: a source name squeezed to nothing reads as a different source. */
    flex: 0 0 auto;
    max-width: 100%;
    padding: 3px 9px;
    font-size: 11px;
    line-height: 1.3;
    font-weight: 600;
    border: 1px solid currentColor;
    background: transparent;
}

/* The panel's slack lives here: it grows into a tall stage and gives its height back on a short one,
   which is what keeps the stats below it above the fold without a scrollbar. The floor is what pays for
   a second line above it, so it is set as low as the shape still reads at. */
.tm-spark[b-vl91muolxn] {
    width: 100%;
    flex: 1 1 32px;
    min-height: 20px;
    max-height: 96px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--line-soft);
}
.tm-spark polyline[b-vl91muolxn] {
    fill: none; stroke: var(--accent); stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke;
}

.tm-stats[b-vl91muolxn] { margin: 0; display: grid; gap: 6px; align-content: start; }
.tm-stats div[b-vl91muolxn] { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; min-width: 0; }
.tm-stats dt[b-vl91muolxn] { margin: 0; flex: 0 0 auto; font-size: 11.5px; line-height: 1.3; color: var(--faint); }
.tm-stats dd[b-vl91muolxn] { margin: 0; font-size: 13px; line-height: 1.3; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; text-align: right; }

/* ─── Responsive ──────────────────────────────────────────────── */

@media (max-width: 760px) {
    /* The panel is full width here and the stage is at its shortest, so the card sheds the sparkline and
       pairs the stats up rather than falling back to scrolling. 200px left it short of even that. */
    .tm-stage[b-vl91muolxn] { min-height: 240px; max-height: 360px; }
    .tm-detail[b-vl91muolxn] { width: 100%; max-width: 100%; }
    .tm-legend[b-vl91muolxn] { display: none; }

    .tm-spark[b-vl91muolxn] { display: none; }
    .tm-stats[b-vl91muolxn] { grid-template-columns: 1fr 1fr; column-gap: 18px; }
    .tm-stats .tm-wide[b-vl91muolxn] { grid-column: 1 / -1; }
}

@media (prefers-reduced-motion: reduce) {
    .tm-live[b-vl91muolxn], .tm-evt .ripple[b-vl91muolxn], .tm-evt[b-vl91muolxn] { animation: none !important; }

    /* SMIL ignores CSS animation rules, so the shots are taken off the map entirely rather than
       left running. The dots carry the same information without the movement. */
    .tm-shots[b-vl91muolxn] { display: none; }
}
/* /Components/SetupPrompt.razor.rz.scp.css */
/* ════════════════════════════════════════════════════════════════
   Customer setup gate — full-viewport overlay shown until both the
   Defender consent and the Sentinel/Lighthouse delegation are live.
   Renders above (outside) .shell, so the palette is declared locally
   here to match the dashboard tokens (same as ImpersonationBanner).
   ════════════════════════════════════════════════════════════════ */

.setup-gate[b-o8axy9pki8] {
    --bg:        #131517;
    --surface:   #1A1D1F;
    --surface-2: #23272A;
    --line:      rgba(255, 255, 255, 0.09);
    --line-soft: rgba(255, 255, 255, 0.05);
    --text:      #EAEEEF;
    --muted:     #8A9296;
    --faint:     #5C6468;
    --accent:    #2DD4BF;
    --accent-2:  #5EEAD9;
    --accent-line: rgba(45, 212, 191, 0.42);
    --accent-soft: rgba(45, 212, 191, 0.14);
    --azure:     #0A84FF;

    position: fixed;
    inset: 0;
    z-index: 800;             /* below the impersonation banner (1000) so admins can exit */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 40px 20px;
    overflow-y: auto;
    background:
        radial-gradient(1100px 520px at 85% -12%, rgba(45, 212, 191, 0.08), transparent 60%),
        var(--bg);
    color: var(--text);
    font-family: "Inter", "Segoe UI", system-ui, sans-serif;
    font-size: 14px;
    line-height: 1.5;
    animation: gate-in-b-o8axy9pki8 .25s ease;
}

@keyframes gate-in-b-o8axy9pki8 { from { opacity: 0; } to { opacity: 1; } }

.setup-card[b-o8axy9pki8] {
    width: min(680px, 100%);
    margin: auto;
    padding: 34px 36px 26px;
    background: var(--surface);
    border: 1px solid var(--line);
    box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.85);
    animation: card-in-b-o8axy9pki8 .35s ease both;
}

@keyframes card-in-b-o8axy9pki8 {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
}

.setup-brand[b-o8axy9pki8] { height: 26px; width: auto; margin-bottom: 24px; }

.setup-eyebrow[b-o8axy9pki8] {
    margin: 0 0 6px;
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent);
}

.setup-title[b-o8axy9pki8] {
    margin: 0 0 10px;
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text);
}

.setup-intro[b-o8axy9pki8] {
    margin: 0 0 26px;
    max-width: 54ch;
    color: var(--muted);
}

/* ─── Steps ───────────────────────────────────────────────────── */

.setup-steps[b-o8axy9pki8] { display: flex; flex-direction: column; gap: 14px; }

.setup-step[b-o8axy9pki8] {
    display: flex;
    gap: 16px;
    padding: 20px;
    background: var(--bg);
    border: 1px solid var(--line);
    transition: border-color .2s ease, background .2s ease;
}

.setup-step.is-connected[b-o8axy9pki8] {
    border-color: var(--accent-line);
    background: linear-gradient(0deg, var(--accent-soft), transparent), var(--bg);
}

.setup-step-icon[b-o8axy9pki8] {
    flex: none;
    width: 42px;
    height: 42px;
    display: grid;
    place-items: center;
    color: var(--accent);
    background: var(--accent-soft);
    border: 1px solid var(--accent-line);
}
.setup-step-icon svg[b-o8axy9pki8] { width: 22px; height: 22px; }

.setup-step-body[b-o8axy9pki8] { flex: 1; min-width: 0; }

.setup-step-top[b-o8axy9pki8] {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 6px;
}
.setup-step-top h2[b-o8axy9pki8] {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    flex: 1;
}
.setup-step-body p[b-o8axy9pki8] {
    margin: 0 0 14px;
    color: var(--muted);
    font-size: 13px;
}

/* Required/optional marker beside a step's name. Defender carries "Optional" because not every
   customer licenses it; the Sentinel delegation is the only step that gates the portal. */
.setup-optional[b-o8axy9pki8] {
    margin-left: 8px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--faint);
}
.setup-optional.is-required[b-o8axy9pki8] { color: var(--accent-2); }

.setup-step-note[b-o8axy9pki8] {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--accent-2);
}

/* ─── Status pill ─────────────────────────────────────────────── */

.setup-pill[b-o8axy9pki8] {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 4px 11px;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--faint);
    border: 1px solid var(--line);
    background: var(--surface-2);
}
.setup-pill-dot[b-o8axy9pki8] {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--faint);
}
.setup-pill.is-good[b-o8axy9pki8] { color: var(--accent-2); border-color: var(--accent-line); background: var(--accent-soft); }
.setup-pill.is-good .setup-pill-dot[b-o8axy9pki8] {
    background: var(--accent);
    box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.22);
}

/* ─── Action buttons ──────────────────────────────────────────── */

.setup-btn[b-o8axy9pki8] {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    padding: 9px 16px;
    font: 600 13px "Inter", "Segoe UI", system-ui, sans-serif;
    text-decoration: none;
    cursor: pointer;
    border: 1px solid transparent;
    transition: filter .18s ease, transform .18s ease, background .18s ease, border-color .18s ease;
}
.setup-btn svg[b-o8axy9pki8] { width: 15px; height: 15px; }
.setup-btn:hover[b-o8axy9pki8] { transform: translateY(-1px); }

.setup-btn.primary[b-o8axy9pki8] { background: var(--accent); border-color: var(--accent); color: #04201D; }
.setup-btn.primary:hover[b-o8axy9pki8] { filter: brightness(1.08); }

.setup-btn.azure[b-o8axy9pki8] { background: var(--azure); border-color: var(--azure); color: #fff; }
.setup-btn.azure:hover[b-o8axy9pki8] { filter: brightness(1.08); }
.setup-btn.azure svg:first-child[b-o8axy9pki8] { width: 16px; height: 16px; }

.setup-btn.is-disabled[b-o8axy9pki8] {
    pointer-events: none;
    opacity: 0.5;
    transform: none;
}

/* ─── Footer / auto-poll indicator ────────────────────────────── */

.setup-foot[b-o8axy9pki8] {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 24px;
    padding-top: 18px;
    border-top: 1px solid var(--line-soft);
    font-size: 12.5px;
    color: var(--faint);
}

.setup-foot-spin[b-o8axy9pki8] {
    flex: none;
    width: 14px;
    height: 14px;
    border: 2px solid var(--line);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin-b-o8axy9pki8 0.8s linear infinite;
}
@keyframes spin-b-o8axy9pki8 { to { transform: rotate(360deg); } }

.setup-recheck[b-o8axy9pki8] {
    margin-left: auto;
    padding: 6px 13px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--text);
    font: 600 12.5px "Inter", "Segoe UI", system-ui, sans-serif;
    cursor: pointer;
    transition: border-color .18s ease, background .18s ease;
}
.setup-recheck:hover:not(:disabled)[b-o8axy9pki8] { border-color: var(--accent-line); background: var(--surface-2); }
.setup-recheck:disabled[b-o8axy9pki8] { opacity: 0.5; cursor: default; }

/* "Continue to the portal" — sits where the re-check button does, on the optional-step beat. */
.setup-foot-go[b-o8axy9pki8] { margin-left: auto; flex: none; }

.setup-error[b-o8axy9pki8] {
    margin: 14px 0 0;
    font-size: 12.5px;
    color: #FF6B6B;
}

/* ─── Success beat ────────────────────────────────────────────── */

.setup-card.is-done[b-o8axy9pki8] { text-align: center; }

.setup-done-mark[b-o8axy9pki8] {
    width: 60px;
    height: 60px;
    margin: 6px auto 18px;
    display: grid;
    place-items: center;
    color: var(--accent);
    border: 2px solid var(--accent-line);
    border-radius: 50%;
    background: var(--accent-soft);
    animation: pop-b-o8axy9pki8 .4s ease both;
}
.setup-done-mark svg[b-o8axy9pki8] { width: 30px; height: 30px; }
@keyframes pop-b-o8axy9pki8 {
    0% { transform: scale(0.6); opacity: 0; }
    60% { transform: scale(1.08); }
    100% { transform: scale(1); opacity: 1; }
}
.setup-card.is-done .setup-intro[b-o8axy9pki8] { margin-bottom: 6px; }

/* ─── Responsive ──────────────────────────────────────────────── */

@media (max-width: 560px) {
    .setup-card[b-o8axy9pki8] { padding: 26px 20px 20px; }
    .setup-title[b-o8axy9pki8] { font-size: 22px; }
    .setup-step[b-o8axy9pki8] { flex-direction: column; gap: 12px; }
    .setup-foot[b-o8axy9pki8] { flex-wrap: wrap; }
    .setup-recheck[b-o8axy9pki8], .setup-foot-go[b-o8axy9pki8] { margin-left: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .setup-gate[b-o8axy9pki8], .setup-card[b-o8axy9pki8], .setup-done-mark[b-o8axy9pki8] { animation: none; }
    .setup-foot-spin[b-o8axy9pki8] { animation-duration: 2s; }
    .setup-btn:hover[b-o8axy9pki8] { transform: none; }
}
