/* /Components/Layout/MainLayout.razor.rz.scp.css */
#blazor-error-ui[b-zqz6d1j8nk] {
    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-zqz6d1j8nk] {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
.components-reconnect-first-attempt-visible[b-uhd7yet49t],
.components-reconnect-repeated-attempt-visible[b-uhd7yet49t],
.components-reconnect-failed-visible[b-uhd7yet49t],
.components-pause-visible[b-uhd7yet49t],
.components-resume-failed-visible[b-uhd7yet49t],
.components-rejoining-animation[b-uhd7yet49t] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-uhd7yet49t],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-uhd7yet49t],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-uhd7yet49t],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-uhd7yet49t],
#components-reconnect-modal.components-reconnect-retrying[b-uhd7yet49t],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-uhd7yet49t],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-uhd7yet49t],
#components-reconnect-modal.components-reconnect-failed[b-uhd7yet49t],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-uhd7yet49t] {
    display: block;
}


#components-reconnect-modal[b-uhd7yet49t] {
    background-color: var(--c-surface-raised);
    color: var(--c-text);
    width: 20rem;
    margin: 20vh auto;
    padding: 2rem;
    border: 0;
    border-radius: 0.5rem;
    box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-uhd7yet49t 0.5s both;
    &[open]

{
    animation: components-reconnect-modal-slideUp-b-uhd7yet49t 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-uhd7yet49t 0.5s ease-in-out 0.3s;
    animation-fill-mode: both;
}

}

#components-reconnect-modal[b-uhd7yet49t]::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
    animation: components-reconnect-modal-fadeInOpacity-b-uhd7yet49t 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp-b-uhd7yet49t {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-uhd7yet49t {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-uhd7yet49t {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-uhd7yet49t] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p[b-uhd7yet49t] {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button[b-uhd7yet49t] {
    border: 0;
    background-color: var(--c-primary);
    color: var(--c-primary-ink);
    padding: 4px 24px;
    border-radius: var(--radius-control);
}

    #components-reconnect-modal button:hover[b-uhd7yet49t] {
        background-color: var(--c-focus);
    }

    #components-reconnect-modal button:active[b-uhd7yet49t] {
        background-color: var(--c-primary);
    }

.components-rejoining-animation[b-uhd7yet49t] {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div[b-uhd7yet49t] {
        position: absolute;
        border: 3px solid var(--c-primary);
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation-b-uhd7yet49t 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2)[b-uhd7yet49t] {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation-b-uhd7yet49t {
    0% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    4.9% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    5% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 1;
    }

    100% {
        top: 0px;
        left: 0px;
        width: 80px;
        height: 80px;
        opacity: 0;
    }
}
/* /Components/Pages/Admin/ManageUsers.razor.rz.scp.css */
/* ---- View user modal -------------------------------------------------------
   Six titled cards, in the vocabulary My Profile already uses for a read-only record:
   `.panel.settings-card` for the box, `.settings-card-head` for the tinted title band,
   `.fact-row` / `.fact-label` / `.fact-value` for the pairs. All of that is shell.css's and none of
   it is redrawn here — `.fact-value` is already a wrapping flex row, which is what lets a value carry
   a badge or a trailing note beside it.

   What IS written here is the arrangement. My Profile stacks its cards down a full page; this is an
   860px modal, wide enough for two columns, and cards that size to their own content rather than to
   the tallest one beside them.

   Scoped CSS, so every selector matches only markup in ManageUsers.razor. */

/* auto-fit, so the same rule gives two columns in the modal and one on a narrow screen without a
   media query. 320px is the floor at which "Password expires" and its countdown badge still share a
   line; `align-items: start` stops a three-row card being stretched to the height of an eight-row
   one. */
.view-cards[b-r6t17qfd17] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: var(--sp-4);
    align-items: start;
}

/* No margin reset here: `.panel` carries a top margin for a page's flow, but `.settings-card` already
   zeroes it and wins on source order, so the grid's own gap is the only spacing between cards. */

/* The chamber card holds a wrapping set, not pairs — full width, so the chips run across the card
   instead of down a half-width ribbon. */
.view-card-wide[b-r6t17qfd17] { grid-column: 1 / -1; }

/* The trailing qualifier on a value — "scopes chamber visibility", "by admin", "last 03-Aug 14:12".
   Quieter than the value it follows, because the value is what was looked up. */
.view-cards .fact-value .hint[b-r6t17qfd17] { font-size: 0.75rem; }

/* A channel, as icon + word. `.fact-value` already lays its children out in a wrapping flex row, so
   this only has to bind the glyph to its label. */
.view-chan[b-r6t17qfd17] { display: inline-flex; align-items: center; gap: var(--sp-1); }

/* Chips, not rows: a chamber is `CODE Name via group`, which is short, and forty of them read as a
   set at a glance where forty table rows would need scrolling to count. */
.view-chips[b-r6t17qfd17] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.view-chip[b-r6t17qfd17] {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
    padding: var(--sp-1) var(--sp-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-control);
    font-size: 0.8125rem;
}

.view-chip .hint[b-r6t17qfd17] { font-size: 0.6875rem; }

/* The sentence that stands in for an empty or short list: spaced off the chips it qualifies, but
   flush when it IS the body — the "no chambers at all" case has nothing above it to clear. */
.view-note[b-r6t17qfd17] { margin: var(--sp-2) 0 0; }
.settings-card-body > .view-note:first-child[b-r6t17qfd17] { margin-top: 0; }

/* ---- Set password modal ---------------------------------------------------
   The fields and the requirements list are the same .form-grid / .policy-check the auth screens use,
   where the card's own generous padding already opens them up. Inside a 640px modal body they read as
   one dense block: short fields, and a checklist whose rules sit almost on its border. The looser
   rhythm is scoped to this page so /login and /change-password keep the measurements auth.css
   documents (the 480px card there has no room for a wider column gap).

   Scoped CSS, so every selector below is rewritten to match only elements written in ManageUsers.razor
   — no ::deep is needed; .form-grid, its inputs and the checklist are all in this component's markup. */

/* Vertical rhythm comes from the wrapper, not from paragraph margins — the lead hint's own margins are
   dropped so the gap between the three blocks is one number rather than a sum of collapsing margins. */
.pw-form[b-r6t17qfd17] {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
}

.pw-form > .hint[b-r6t17qfd17] { margin: 0; }

/* Label to field, and the field's own box. A password field is typed into blind and confirmed twice,
   so it gets a taller target than the read-back-and-check fields elsewhere in this page's forms. */
.pw-form .form-grid label[b-r6t17qfd17] { gap: var(--sp-2); }
.pw-form .form-grid input[b-r6t17qfd17] { padding: var(--sp-3); }

/* ---- Show / hide on the password fields ------------------------------------
   The wrapper, not the label, is what establishes the containing block for the button: a
   `.form-grid label` is a flex column holding the caption, the field, a hint and an error, so
   centring against it would put the eye somewhere in the middle of that stack rather than in the box.

   `.field-reveal` itself is auth.css's and is reused rather than redrawn — it already sizes the 32px
   target, mutes the glyph and swaps eye / eye-off off the button's own aria-pressed. Only the box it
   sits in belongs to this page. Applies to the create-user form and the Set password modal alike;
   both are in this component, so the scoped selector reaches both. */
.pw-reveal[b-r6t17qfd17] { position: relative; display: block; }

/* The input no longer stretches on its own — it is inside a span now, not a direct flex child of the
   label — and it needs room at the end so a long password never runs underneath the button. The
   padding mirrors auth.css's `.field-control-reveal input`. */
.pw-reveal input[b-r6t17qfd17] { width: 100%; padding-right: calc(var(--sp-1) + 32px); }

/* The checklist box: room inside its border, and a real column gutter. At 640px there is ~280px per
   column against a ~194px longest rule, so widening the gutter cannot push a rule into wrapping the
   way it would in the auth card. */
.pw-form .policy-check[b-r6t17qfd17] { padding: var(--sp-4); }
.pw-form .policy-lead[b-r6t17qfd17] { margin: 0 0 var(--sp-3); }
.pw-form .policy-list[b-r6t17qfd17] { gap: var(--sp-2) var(--sp-4); }
/* /Components/Pages/Dashboard.razor.rz.scp.css */
/* Dashboard instrument cards (docs/16 §5.1). Status via locked --s-* tokens. No raw hex. */

.clock[b-25apdd4f13] {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    color: var(--c-text-muted);
    font-size: 0.875rem;
}

.collector-banner[b-25apdd4f13] {
    background: color-mix(in srgb, var(--s-alarm) 12%, transparent);
    border: 1px solid var(--s-alarm);
    color: var(--s-alarm);
    border-radius: var(--radius-control);
    padding: var(--sp-2) var(--sp-3);
    margin-bottom: var(--sp-4);
    font-size: 0.875rem;
}

.pending-strip[b-25apdd4f13] { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.pending-chip[b-25apdd4f13] {
    background: color-mix(in srgb, var(--s-info) 10%, transparent);
    border: 1px solid var(--s-info);
    color: var(--s-info);
    border-radius: var(--radius-control);
    padding: 2px var(--sp-3);
    font-size: 0.8125rem;
}

/* The zero-state is .empty-state from shell.css — same reasoning as the badge above: one
   declaration rather than a scoped copy that happens to agree. */

.card-grid[b-25apdd4f13] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: var(--sp-4);
}

.chamber-card[b-25apdd4f13] {
    background: var(--c-surface-raised);
    border: 1px solid var(--c-border);
    border-top: 3px solid var(--s-offline);
    border-radius: var(--radius-card);
    box-shadow: var(--elev-raised);
    padding: var(--sp-4);
    /* Containing block for the stretched card link below. */
    position: relative;
}
.chamber-card.status-normal[b-25apdd4f13] { border-top-color: var(--s-normal); }
.chamber-card.status-alert[b-25apdd4f13] { border-top-color: var(--s-alert); }
.chamber-card.status-alarm[b-25apdd4f13] { border-top-color: var(--s-alarm); }
.chamber-card.status-offline[b-25apdd4f13] { border-top-color: var(--s-offline); }

.card-top[b-25apdd4f13] { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2); }
.chamber-code[b-25apdd4f13] { font-family: var(--font-display); font-weight: 600; letter-spacing: 0.06em; font-size: 0.8125rem; }

/* The whole card opens the chamber's status page, via one real anchor stretched over it rather than
   a click handler on the article. The ::after is invisible and carries no colour — the link's own
   text keeps the card's typography, so the card does not suddenly read as a hyperlink. */
.chamber-code-link[b-25apdd4f13] { color: inherit; text-decoration: none; }
.chamber-code-link[b-25apdd4f13]::after { content: ''; position: absolute; inset: 0; border-radius: inherit; }
/* Focus belongs on the card, not on the six characters of the code: the click target IS the card, and
   an outline drawn around the label would point at the wrong thing. */
.chamber-card:has(.chamber-code-link:focus-visible)[b-25apdd4f13] { outline: 2px solid var(--c-primary); outline-offset: 2px; }
/* The only hover affordance. Deliberately not a lift or a shadow change: the card's top border is a
   status colour, and animating the card would put motion on a screen whose job is to be read. */
.chamber-card:has(.chamber-code-link:hover) .chamber-code[b-25apdd4f13] { color: var(--c-primary); }

/* Status chip + settings gear as one right-hand group, so space-between cannot strand the chip in
   the middle of the row. Raised above the stretched link so the gear stays clickable — its own
   -5px hit-area ::after comes with it. */
.card-top-actions[b-25apdd4f13] { display: flex; align-items: center; gap: var(--sp-2); position: relative; z-index: 1; }

/* Not `.btn.icon-btn` from shell.css: that carries a border and a filled surface, which beside the
   status chip reads as a second badge and gives every card a control competing with its reading.
   This one is chrome — muted and borderless at rest, and it only takes the primary colour on hover
   or keyboard focus. */
.card-settings[b-25apdd4f13] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-1);
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-control);
    color: var(--c-text-muted);
    cursor: pointer;
    position: relative;
    transition: color 120ms ease, background-color 120ms ease, border-color 120ms ease;
}
/* The icon is 15px, which leaves the button under the 24px minimum on its own. This widens what the
   pointer hits without widening what is drawn — the same trick .btn.icon-btn uses. */
.card-settings[b-25apdd4f13]::after { content: ''; position: absolute; inset: -5px; border-radius: inherit; }
.card-settings:hover[b-25apdd4f13] {
    color: var(--c-primary);
    border-color: var(--c-border);
    background: var(--c-surface);
}
.card-settings:focus-visible[b-25apdd4f13] { outline: 2px solid var(--c-primary); outline-offset: 1px; color: var(--c-primary); }

/* The status chip is .wf-badge from shell.css — docs/16 §5.4 requires one badge used identically
   on the dashboard, in tables and in the report queue. A scoped copy lived here and had drifted
   apart from the shared rule in nothing but name; it is gone deliberately, not by oversight. */

.card-meta[b-25apdd4f13] { color: var(--c-text-muted); font-size: 0.8125rem; margin-top: var(--sp-1); }

.readings[b-25apdd4f13] { display: flex; gap: var(--sp-8); margin: var(--sp-4) 0; }
.value[b-25apdd4f13] { font-family: var(--font-mono); font-weight: 500; font-size: 2.75rem; font-variant-numeric: tabular-nums; line-height: 1; }
.status-offline .value[b-25apdd4f13] { color: var(--c-text-muted); }
.unit[b-25apdd4f13] { font-family: var(--font-mono); color: var(--c-text-muted); font-size: 60%; letter-spacing: 0.04em; margin-left: 2px; }
.setpoint[b-25apdd4f13] { color: var(--c-text-muted); font-size: 0.75rem; margin-top: var(--sp-2); }
.card-foot[b-25apdd4f13] { color: var(--c-text-muted); font-family: var(--font-mono); font-size: 0.75rem; border-top: 1px solid var(--c-border); padding-top: var(--sp-2); }
