/* /Components/AllocationLifecycleRibbon.razor.rz.scp.css */
/* The allocation lifecycle ribbon: a wrapping cluster of StatusBadge steps joined by arrows. The badge owns
   its own pill skin; this sheet only styles the connective arrow, the per-step relative time (a dimmer,
   normal-weight inline next to the bold label), and the trailing status reason. */

/* One step and the connector that leads to it, as ONE wrapping unit: the row breaks between steps, so a connector
   is never left dangling at the end of a line with its step on the next one. */
.lifecycle-ribbon__step[b-imeuwfascq] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-cluster-gap, var(--ps-space-2));
    min-inline-size: 0;
}

.lifecycle-ribbon__arrow[b-imeuwfascq] {
    font-size: var(--ps-text-sm);
}

/* The relative time inside a step: a lighter weight than the bold step label so the label stays primary. It keeps the
   badge's full text color (an AA-paired tone against the soft fill) — an opacity dim would dilute that tone toward the
   fill and drop the pair below the WCAG AA contrast floor (it did, on the colored Done/Current pills). */
.lifecycle-ribbon__time[b-imeuwfascq] {
    font-weight: var(--ps-weight-normal);
}

/* The step's dwell figure, trailing its relative time on the same pill: same lighter weight, same badge text color,
   for the contrast reason above. */
.lifecycle-ribbon__dwell[b-imeuwfascq] {
    font-weight: var(--ps-weight-normal);
}

.lifecycle-ribbon__reason[b-imeuwfascq] {
    margin-inline-start: var(--ps-space-2);
    font-size: var(--ps-text-sm);
}
/* /Components/ArgumentsDisplay.razor.rz.scp.css */
/* Argument tokens are arbitrary user data: the chip releases the badge primitive's nowrap and caps
   itself at the cluster's width (the max-inline-size clamps the flex item's automatic minimum), so a
   long token wraps inside the pane instead of widening the page. */
.args[b-axejt7q1cc]  .ps-badge {
    max-inline-size: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
}

.args[b-axejt7q1cc]  .ps-badge > code {
    min-inline-size: 0;
}
/* /Components/ArgumentsEditor.razor.rz.scp.css */
.arguments-editor[b-9ufjyxvse3] {
    min-inline-size: 0;
}

.arguments-editor__row[b-9ufjyxvse3] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    margin-block-end: var(--ps-space-2);
    min-inline-size: 0;
}

.arguments-editor__input-wrap[b-9ufjyxvse3] {
    position: relative;
    flex: 1;
    min-inline-size: 0;
}

.arguments-editor__input[b-9ufjyxvse3] {
    inline-size: 100%;
}

.arguments-editor__input--clearable[b-9ufjyxvse3] {
    padding-inline-end: 2.25rem;
}

/* The inline clear affordance is a real interactive target: it fills the input's height and carries the WCAG
   2.5.8 inline floor too (a bare icon left it ~14px wide). */
.arguments-editor__clear[b-9ufjyxvse3] {
    position: absolute;
    inset-inline-end: var(--ps-space-1);
    inset-block: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--ps-target-min);
    padding: 0;
    border: 0;
    background: none;
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.arguments-editor__clear:hover[b-9ufjyxvse3] {
    color: var(--ps-color-text);
}

.arguments-editor__row[b-9ufjyxvse3]  .arguments-editor__remove {
    color: var(--ps-color-danger);
}
/* /Components/ClaimableHeadroomCard.razor.rz.scp.css */
/* The claimable-headroom display: per-resource capacity bars + a compact staircase step chart. Every color rides a
   theme-aware design token, so light and dark are covered without a hand-written flip; the chart lives in an
   overflow-x container so a narrow card scrolls it rather than overflowing. */

.claimable-headroom[b-wwuytm9d02] {
    --ps-stack-gap: var(--ps-space-4);
}

.claimable-headroom__caption[b-wwuytm9d02] {
    margin: 0;
}

.claimable-resource[b-wwuytm9d02] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
}

.claimable-resource__head[b-wwuytm9d02] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ps-space-2);
}

.claimable-resource__label[b-wwuytm9d02] {
    font-size: var(--ps-text-sm);
    font-weight: 600;
    color: var(--ps-color-text);
}

.claimable-resource__figure[b-wwuytm9d02] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* The capacity bar: the CLAIMED (peak) band and the HEADROOM band tile the track (their widths sum to 100%). */
.claimable-bar[b-wwuytm9d02] {
    display: flex;
    inline-size: 100%;
    block-size: 0.5rem;
    border-radius: 999px;
    overflow: hidden;
    background: var(--ps-color-surface-sunken);
    border: 1px solid var(--ps-color-border);
}

.claimable-bar__peak[b-wwuytm9d02] {
    block-size: 100%;
    background: var(--ps-color-warning);
}

.claimable-bar__headroom[b-wwuytm9d02] {
    block-size: 100%;
    background: var(--ps-color-success);
}

/* The chart scrolls inside its own container: on a narrow card the SVG shrinks to a min inline size and then
   scrolls, never widening the card past its column (the LayoutProbe pins this). */
.claimable-chart-scroll[b-wwuytm9d02] {
    inline-size: 100%;
    overflow-x: auto;
    overflow-y: hidden;
}

.claimable-chart[b-wwuytm9d02] {
    display: block;
    inline-size: 100%;
    min-inline-size: 15rem;
    block-size: 2.75rem;
}

/* The headroom staircase: a filled area whose descending silhouette is the step function (full headroom on the
   left, dropping as older spikes enter the span). Lead-in and steps share the claimable fill; a single silhouette
   polyline strokes only the descending top edge, so the knees read as one staircase line rather than a comb of
   outlined columns. */
.claimable-chart__lead[b-wwuytm9d02] {
    fill: var(--ps-color-success-soft);
}

.claimable-chart__step[b-wwuytm9d02] {
    fill: var(--ps-color-success-soft);
}

.claimable-chart__silhouette[b-wwuytm9d02] {
    fill: none;
    stroke: var(--ps-color-success);
    stroke-width: 1.25;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
}

/* The dashed reference marker at the system look-back: ties the capacity bar's headline number to a point on the
   staircase. Non-scaling so the non-uniform viewBox stretch never fattens it. */
.claimable-chart__lookback[b-wwuytm9d02] {
    stroke: var(--ps-color-text-muted);
    stroke-width: 1;
    stroke-dasharray: 3 2;
    vector-effect: non-scaling-stroke;
}

.claimable-chart__baseline[b-wwuytm9d02] {
    stroke: var(--ps-color-border-strong);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
}
/* /Components/ClearableInput.razor.rz.scp.css */
.clearable-input-wrapper[b-5guf9m57we] {
    position: relative;
}

/* Room for the clear button at the trailing edge while it is shown. */
.clearable-input-field[b-5guf9m57we] {
    padding-inline-end: 2.25rem;
}

.clearable-input-button[b-5guf9m57we] {
    position: absolute;
    inset-inline-end: var(--ps-space-2);
    inset-block: 0;
    display: inline-flex;
    align-items: center;
    padding: 0 calc(var(--ps-space-1) / 2);
    border: 0;
    background: none;
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.clearable-input-button:hover[b-5guf9m57we] {
    color: var(--ps-color-text);
}
/* /Components/ClusterGrid.razor.rz.scp.css */
/* The cluster grid: node cards in a RAM grid (auto-fill, one readable basis at EVERY width) that packs wider on big
   screens and collapses to one column on a phone. The band is FULL WIDTH and the tracks STRETCH (1fr) — no track
   ceiling, no centering — so a wall display fills its row with real cards instead of stranding them beside phantom
   tracks.

   The track MIN is a LEGIBILITY floor, not a fit floor (--ps-cluster-card-min). A card must hold the node-level
   segmented bars at a width where a small share still reads as a segment rather than a sliver, AND a GPU row whose
   two mini-bars stay usable with the occupant chip on the same line. A 1366px content column therefore carries ONE
   wide card rather than two cramped ones — the deliberate trade (legibility beats density; the cards-per-row count is
   the consequence, not the goal). Tokens only. */
.cluster-grid[b-9py9plrbow] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--ps-cluster-card-min), 100%), 1fr));
    gap: var(--ps-space-4);
    /* NO height cap and NO overflow of its own: the PAGE owns the one scrollbar (the shell's scroll-container
       decision, MainLayout.razor.css) — a height-capped overflow:auto grid here would nest a second vertical
       scrollport inside the document scroller. */

    /* Cards STRETCH to their row track, so every card in one row ends at the same bottom edge. The grid already
       sizes each ROW track to its tallest card; `start` (the retired value) then let every shorter card shrink back
       to its content inside that track, which is what left a row of ragged card bottoms — a head node's short card
       beside a full DGX card, a sunken offline card beside live ones. Stretching moves the frame only: the card's
       innards stay in normal flow from its top edge (no space-between anywhere inside), so the surplus accrues as
       empty space at the card's BOTTOM and meters are never torn away from the table under them. */
    align-items: stretch;
}

/* EVERY node card is a size container at EVERY viewport width — not behind a media query. This single rule is what
   makes a card's contents reflow to the CARD's width rather than the window's, so a card in a one-column phone
   layout and the same card on a wide screen each lay out for the box they actually got. Gating it behind a
   wall-display tier (as the retired occupancy view did) is exactly why the dashboard read sparse while the topology
   page read dense at the same width. The Card is a child component (its root never carries this scope), so reach it
   via ::deep.
   NAMED, because a device row's trailing slot is a container of its own (below): an unnamed query written against
   "the card" would silently retarget to the slot the moment its subject sits inside one, and the two ask about very
   different widths. Every query in this file names which. */
.cluster-grid[b-9py9plrbow]  .ps-card {
    container-type: inline-size;
    container-name: ps-node-card;
}

/* A dark node: the whole card sinks. Deliberately outside the occupied/free vocabulary — this is the ABSENCE of a
   live read, not another grade; the header badge and the offline line carry the word. */
.cluster-grid[b-9py9plrbow]  .ps-card.is-offline {
    background: var(--ps-color-surface-sunken);
}

/* A host-only (GPU-less) node card takes the SAME track as every other card — no content-sizing special case. The
   retired cap left a stub card wedged into a full-width row; now that a node card folds its GPU bars by default, a
   head node's CPU/RAM pair is no thinner than its neighbours' standing content. The .is-host-only class survives
   for what it MEANS (this node has no GPUs, hence no device rows and no fold chevron) rather than for a size. */

/* --- Node header. --- */
.cluster-node__head[b-9py9plrbow] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.cluster-node__host[b-9py9plrbow] {
    margin: 0;
    /* clamp()ed so a wall display reads the hostname from across the room without overflowing a phone. */
    font-size: clamp(var(--ps-text-sm), 0.85rem + 0.4vw, var(--ps-text-lg));
    font-weight: var(--ps-weight-semibold);
    overflow-wrap: anywhere;
}

.cluster-node__host a[b-9py9plrbow] {
    color: var(--ps-color-text);
}

.cluster-node__host a:hover[b-9py9plrbow] {
    color: var(--ps-color-accent);
}

/* The header's trailing cluster: the admin-only scale read, then the GPU-bar fold. It takes the row's slack so the
   hostname keeps the start edge whichever of the two is present. */
.cluster-node__actions[b-9py9plrbow] {
    margin-inline-start: auto;
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    flex: none;
}

.cluster-node__count[b-9py9plrbow] {
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
    white-space: nowrap;
}

/* The PIN: the header's one signed-in control, sharing the fold's icon-button shape (target floor, focus ring, no
   chrome until hovered) so the two read as one cluster rather than as two kinds of control. Pressed is carried by
   COLOR and by a filled glyph, never by position: the card does not move within its band when a reader looks at it,
   so the state must be legible on the button itself. */
.cluster-node__pin[b-9py9plrbow] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: var(--ps-target-min);
    block-size: var(--ps-target-min);
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--ps-radius-sm);
    background: none;
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.cluster-node__pin:hover:not(:disabled)[b-9py9plrbow] {
    border-color: var(--ps-color-border);
    background: var(--ps-color-surface-sunken);
    color: var(--ps-color-text);
}

.cluster-node__pin:focus-visible[b-9py9plrbow] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: 1px;
}

/* In flight: the control keeps its place and its glyph and simply stops taking clicks — a control that vanished
   would move the two beside it while the reader's pointer was still on them. */
.cluster-node__pin:disabled[b-9py9plrbow] {
    cursor: default;
    opacity: 0.5;
}

/* PINNED. The accent color says it, and the glyph is filled — lucide draws a pin as an outline, so painting its
   interior is what makes "pinned" readable at a glance rather than a hue a reader has to compare against a
   neighbour. The head is the only closed path in the icon, so the fill lands exactly where it should. */
.cluster-node__pin[aria-pressed="true"][b-9py9plrbow] {
    color: var(--ps-color-accent);
}

.cluster-node__pin[aria-pressed="true"][b-9py9plrbow]  .ps-icon {
    fill: currentColor;
}

.cluster-node__pin[b-9py9plrbow]  .ps-icon {
    inline-size: 1em;
    block-size: 1em;
}

/* The GPU-bar fold: an ICON button on a header row that already exists, so the disclosure costs no vertical space.
   It clears the WCAG 2.5.8 target floor, keeps a real focus ring (it is a keyboard control, not decoration), and
   rotates its chevron a quarter turn when open — one glyph for both states. */
.cluster-node__fold[b-9py9plrbow] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: var(--ps-target-min);
    block-size: var(--ps-target-min);
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--ps-radius-sm);
    background: none;
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.cluster-node__fold:hover[b-9py9plrbow] {
    border-color: var(--ps-color-border);
    background: var(--ps-color-surface-sunken);
    color: var(--ps-color-text);
}

.cluster-node__fold:focus-visible[b-9py9plrbow] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: 1px;
}

.cluster-node__fold[b-9py9plrbow]  .ps-icon {
    inline-size: 1em;
    block-size: 1em;
    transition: rotate var(--ps-motion-fast) var(--ps-ease);
}

.cluster-node__fold[aria-expanded="true"][b-9py9plrbow]  .ps-icon {
    rotate: 90deg;
}

/* The card body's own rhythm, deliberately TIGHTER than the page stack it sits in (ruled 2026-08-12: both personas a
   notch denser). It is a LOCAL override of the stack knob, not a change to the spacing scale — the tokens are shared
   with every other surface, and the node grid is the one place where a card's whole point is how much of the cluster
   fits on one screen. Its children are the meters, the user table and the interconnect groups; the rows inside a
   group are NOT children of this stack (see .cluster-devrows), so this gap never lands between two GPU rows. */
.cluster-node__body[b-9py9plrbow] {
    --ps-stack-gap: var(--ps-space-2);
}

.cluster-node__offline[b-9py9plrbow] {
    margin: 0;
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* The ONLINE-but-not-reporting note, where a healthy card would carry its bars. Muted like the offline note — both
   say the same thing about the numbers (there are none to show) — while the card's header badge carries the warning
   tone that distinguishes a node that stopped reporting from one that left. */
.cluster-node__stale[b-9py9plrbow] {
    margin: 0;
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* --- The node-level metric bars: the card's headline, one row per rendered metric. --- */
.cluster-node__meters[b-9py9plrbow] {
    display: grid;
    gap: var(--ps-space-1);
}

/* --- Online / offline status dots (live coloring). --- */
.cluster-status-dot[b-9py9plrbow] {
    inline-size: 0.625rem;
    block-size: 0.625rem;
    border-radius: var(--ps-radius-full);
    flex: none;
}

.cluster-status-dot--online[b-9py9plrbow] {
    background: var(--ps-color-success);
}

.cluster-status-dot--offline[b-9py9plrbow] {
    background: var(--ps-color-text-faint);
}

/* --- Interconnect domains (admin view: the host labels the groups). --- */
/* The domain frame carries a tighter BLOCK padding than inline: it wraps rows that already carry their own
   block padding, so a full space step above and below only restated it. */
.cluster-domain[b-9py9plrbow] {
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    padding: var(--ps-space-1) var(--ps-space-2);
}

.cluster-domain__head[b-9py9plrbow] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    margin-block-end: var(--ps-space-1);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* --- A group's GPU rows. --- */
/* .cluster-devrows carries NO declarations, deliberately — plain block flow, which is exactly the rhythm the rows
   want, and a restated default would only look like a decision. It is here for what its PRESENCE does: the rows used
   to be emitted straight into the card body, which is a ps-stack, so where there are no interconnect-domain boxes
   (every persona below the administrator) each row became a stack CHILD and picked up the stack's gap between every
   pair, while an administrator's rows sat inside a domain box and were flush. Same rows, two rhythms and two card
   heights, for a difference no reader could see. With the box always present the rows measure the same at every
   persona and the domain frame is the only thing the administrator tier adds. */

/* --- One GPU row. --- */
/* A NAMED-AREA grid so every row on a card lines up column for column and can reflow without depending on child
   order: expander, label, the two headline meters, then the occupancy slot. The meter tracks carry a real MINIMUM
   (not just a fraction) so they never shrink below the width at which a segmented bar stops being readable — that
   minimum, plus the slot's, is what sets --ps-cluster-card-min.
   THE LOCK TRACK IS RESERVED, NOT CONDITIONAL. The administrator's template carries a fixed 1.1em column between the
   expander and the label for the viewer's own yield-lock marker, and it is there on every row of the persona —
   marked or not. Putting the icon inside the label cell instead would have made "GPU03" start further right on
   exactly the rows that carry one, so a reader scanning the column would read the indent as information; and the
   label track is a hard 3.4em that an icon would have eaten into, ellipsizing the very name it sits beside. Reserving
   the width costs one em on a track that has it to spare and keeps the labels in ONE column at both states. Below
   the administrator tier the cell is not rendered at all, so that persona's template (and its 4.2em trailing column)
   is unchanged.
   THE SURPLUS GOES TO THE SLOT (ruled 2026-08-12). The meters used to take two thirds of every surplus pixel, which
   left a 1054px-wide card drawing two ~250px bars for a "0%" reading while the trailing slot — carrying a job NAME,
   an unbounded user-supplied string — ellipsized to two characters. A bar here is a SCAN aid, not a measurement
   surface (the numeric tail beside it and the hover breakdown are where a value is read), so its fraction is now the
   smaller one and the slot's is the larger: the meters keep a legible length at every card width and every pixel
   above that lands where the unbounded content is. */
.cluster-devrow[b-9py9plrbow] {
    display: grid;
    grid-template-columns:
        1.3em
        1.1em
        3.4em
        minmax(7em, 0.6fr)
        minmax(7em, 0.6fr)
        minmax(13em, 1.8fr);
    grid-template-areas: "expander lock label sm memory slot";
    align-items: center;
    gap: var(--ps-space-2);
    padding-block: 2px;
    font-size: var(--ps-text-sm);
}

/* Below the management tier there is no chip — just one word — so the trailing column stops claiming chip width and
   gives it back to the meters (there is no unbounded string in the slot at that tier, so the surplus rule above has
   nothing to protect). The modifier is on the GRID, not the row: the persona is one fact for the whole band, and
   repeating it on every row of every card would say the same thing hundreds of times. */
.cluster-grid--plain .cluster-devrow[b-9py9plrbow] {
    grid-template-columns:
        1.3em
        3.4em
        minmax(7em, 1fr)
        minmax(7em, 1fr)
        4.2em;
    /* The AREAS come with the columns, always. This persona renders no lock cell, so its template must not name one
       either: a five-track column list under a six-name area map leaves the area template one column short, and the
       browser answers by inventing an implicit zero-width track — which silently shifts every cell one column right
       (the label lands in a meter's track, the slot lands in the 0px one). The rule is therefore: any override of
       one of these two properties overrides BOTH. */
    grid-template-areas: "expander label sm memory slot";
}

.cluster-devrow__expander-cell[b-9py9plrbow] {
    grid-area: expander;
    display: flex;
}

/* The reserved yield-lock cell (administrator persona). It holds its width whether or not a marker is in it — that
   is the whole reason it is a cell — and centers the icon so a column of markers reads as a column. */
.cluster-devrow__lock-cell[b-9py9plrbow] {
    grid-area: lock;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* The marker itself: quiet by default. It is a STANDING RULING, not an alert — the reader made it themselves — so it
   carries the muted tone the row's other non-status glyphs use rather than a status color, and its meaning arrives
   through the hover title and the visually-hidden text beside it. */
.cluster-devrow__lock[b-9py9plrbow] {
    display: inline-flex;
    align-items: center;
    color: var(--ps-color-text-muted);
}

.cluster-devrow__lock[b-9py9plrbow]  .ps-icon {
    inline-size: 0.9em;
    block-size: 0.9em;
}

.cluster-devrow__meter--sm[b-9py9plrbow] {
    grid-area: sm;
    min-inline-size: 0;
}

.cluster-devrow__meter--memory[b-9py9plrbow] {
    grid-area: memory;
    min-inline-size: 0;
}

/* The stale meter cell's em dash. Muted and tabular so a column of them lines up with the meters it replaces, and
   deliberately NOT a zero-length bar: an empty bar still reads as a measurement of nothing, where a dash reads as
   the absence of a measurement. */
.cluster-devrow__stale[b-9py9plrbow] {
    font-size: var(--ps-text-sm);
    font-variant-numeric: tabular-nums;
    color: var(--ps-color-text-muted);
}

/* A card narrower than the one-line row needs (a phone, or the single stretched track of a small viewport) reflows
   the row onto TWO lines rather than overflowing the page: identity and occupancy on top, the two meters beneath.
   Keyed on the CARD (the grid's cards are inline-size containers at every width), never on the viewport — the same
   card is narrow in a one-column phone layout and wide on a wall. */
@container ps-node-card (max-width: 37rem) {
    .cluster-devrow[b-9py9plrbow] {
        /* The administrator's reflow keeps the reserved lock column — the alignment it buys matters MORE on a narrow
           card, where the label has least room to be pushed around — and lets the SM meter span it on the second
           line, since nothing is placed there. */
        grid-template-columns: 1.3em 1.1em minmax(0, 1fr) minmax(0, 1fr);
        grid-template-areas:
            "expander lock label slot"
            "expander sm   sm    memory";
        row-gap: 1px;
    }

    .cluster-grid--plain .cluster-devrow[b-9py9plrbow] {
        grid-template-columns: 1.3em minmax(0, 1fr) minmax(0, 1fr);
        grid-template-areas:
            "expander label slot"
            "expander sm    memory";
        row-gap: 1px;
    }
}

.cluster-devrow__expander[b-9py9plrbow] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 100%;
    padding: 0;
    border: none;
    background: none;
    color: var(--ps-color-text-faint);
    cursor: pointer;
}

.cluster-devrow__expander:hover[b-9py9plrbow] {
    color: var(--ps-color-text);
}

.cluster-devrow__expander[b-9py9plrbow]  .ps-icon {
    inline-size: 0.9em;
    block-size: 0.9em;
}

/* The device label is the row's link to the device page; the accent color marks it as one. */
.cluster-devrow__label[b-9py9plrbow] {
    grid-area: label;
    color: var(--ps-color-accent);
    font-weight: var(--ps-weight-medium);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-inline-size: 0;
}

/* The occupancy slot: one line, always. Its children ellipsize; the row never grows a second line.
   ONE SHRINKABLE ELEMENT (ruled 2026-08-08). The slot clips what does not fit, so whatever refuses to shrink and
   sits LAST is what gets chopped. A flex item's default `min-width: auto` floors it at its own min-content, and a
   job name is frequently ONE unbreakable token — so the chip refused to shrink, pushed the trailing reclaim badge
   past the slot's edge, and the badge lost its second half to the clip (the reported defect: an ellipsized job name
   AND a half-drawn "Occupied · reclaimable"). The fix is rigidity with a sacrificial neighbour: the badge and the
   one-word reads reserve their natural width (their vocabulary is short and fixed), and the chip is the only thing
   that gives — releasing its min-content floor so the ellipsis it already carries actually engages.
   That ordering stands, but it is now the LAST resort rather than the first (ruled 2026-08-12): the slot's own share
   of the row grew, and where it is still too narrow the badge beside the chip drops to its ICON before the chip
   gives anything (the slot's container queries below). The chip's ellipsis is what catches a name no slot could
   have held — it is no longer what pays for an ordinary one. */
.cluster-devrow__slot[b-9py9plrbow] {
    grid-area: slot;
    /* A size CONTAINER of its own, named so nothing here is ever asked about the card by accident. The two
       degradations below are decisions about how much room THIS slot got — a quantity the card's width only
       approximates (it also depends on the persona's track template and on where the auto-fill tracks landed), so
       asking the slot directly is both simpler and honest. Its width comes from the grid track, never from its
       content, so there is no cycle. */
    container-type: inline-size;
    container-name: ps-devrow-slot;
    display: flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-inline-size: 0;
    overflow: hidden;
}

/* The reclaim badge DEGRADES TO ITS ICON once the slot is too narrow to carry both it and a readable job name.
   It is the OCCUPIED-BUT-IDLE badge that matters: that is the one state that renders beside an occupant chip, so it
   is the one competing with an unbounded user-supplied string for a fixed slot — and the ruling is that the NAME is
   what a reader came for, so the badge is what gives. (The free-device badges — Reclaimable, Claimable now — share
   their slot with one short word and keep their wording at every width.) Collapsing the LABEL keeps the row one line
   at a constant height (never a second line, never a squeezed half-word): the icon and its amber still distinguish
   the state, the badge's own title tooltip still carries the full explanation, and the label text stays in the DOM,
   so assistive technology reads exactly what a wide card shows. The icon is re-sized explicitly because font-size:0
   would otherwise collapse the em-based icon token along with the text.
   24rem is where the slot stops holding "Occupied · reclaimable" (~11rem) plus a job name worth reading. */
@container ps-devrow-slot (max-width: 24rem) {
    .cluster-devrow__slot[b-9py9plrbow]  .reclaim-indicator--occupied-idle .ps-badge {
        gap: 0;
        padding-inline: var(--ps-space-1);
        font-size: 0;
    }

    .cluster-devrow__slot[b-9py9plrbow]  .reclaim-indicator--occupied-idle .ps-badge .ps-icon {
        inline-size: var(--ps-icon-size-sm);
        block-size: var(--ps-icon-size-sm);
    }
}

/* Narrower still — a phone-width card's reflowed slot — and EVERY reclaim badge drops to its icon, not just the one
   competing with a chip: at this width even a free row's one-word occupancy read plus the widest free-device badge
   ("Claimable now") is wider than the whole slot, so reserving the badge's natural width would push it past the
   slot's clipping edge instead. 10rem is measured against that pair. */
@container ps-devrow-slot (max-width: 10rem) {
    .cluster-devrow__slot[b-9py9plrbow]  .reclaim-indicator .ps-badge {
        gap: 0;
        padding-inline: var(--ps-space-1);
        font-size: 0;
    }

    .cluster-devrow__slot[b-9py9plrbow]  .reclaim-indicator .ps-badge .ps-icon {
        inline-size: var(--ps-icon-size-sm);
        block-size: var(--ps-icon-size-sm);
    }
}

/* The quieting badge's ATTRIBUTION suffix ("· last <user>") is dropped where the slot cannot hold it. Above this
   width it stays in the BODY on purpose — "who was last" is the field an operator reads a quiet GPU for, and hiding
   it behind a hover would answer the report with the opposite of what it asked. Below it, a name would arrive as
   two characters and an ellipsis, which tells a reader less than nothing; the accrual keeps the whole slot and the
   badge's title carries the unabbreviated name (it always does — see DeviceReclaimIndicator). A name longer than
   even a wide slot still ellipsizes inside its own span, which is the backstop this step does not replace. */
@container ps-devrow-slot (max-width: 17rem) {
    .cluster-devrow__slot[b-9py9plrbow]  .reclaim-indicator__quieting-user {
        display: none;
    }
}

/* The compound occupant chip (admin): job · role #ordinal · group · state, one line, ellipsized. */
.cluster-devrow__chip[b-9py9plrbow] {
    display: inline-block;
    max-inline-size: 100%;
    /* The slot's ONLY shrinkable element: min-inline-size releases the flex min-content floor an unbreakable job
       name would otherwise impose, so this ellipsizes instead of evicting the badge beside it. */
    flex: 0 1 auto;
    min-inline-size: 0;
    padding: 0 var(--ps-space-1);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-sm);
    background: var(--ps-color-surface-sunken);
    color: var(--ps-color-text);
    font-size: var(--ps-text-xs);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The trailing reclaim badge never shrinks and never wraps: its wording is a short closed vocabulary, so reserving
   its natural width is bounded — and a half-drawn status badge is worse than a shorter job name. ::deep because the
   indicator is a child component's own element. */
.cluster-devrow__slot[b-9py9plrbow]  .reclaim-indicator {
    flex: 0 0 auto;
    flex-wrap: nowrap;
}

/* …except the QUIETING accrual, which is not a fixed vocabulary: it carries a live duration and an unbounded
   username, so reserving its natural width would push IT past the slot instead (measured at phone width). It is
   therefore the slot's second shrinkable element, and its own user-suffix span — built to ellipsize for exactly
   this reason — is what gives. */
.cluster-devrow__slot[b-9py9plrbow]  .reclaim-indicator--quieting {
    flex: 0 1 auto;
    min-inline-size: 0;
}

.cluster-devrow__chip-part[b-9py9plrbow] {
    color: var(--ps-color-text-muted);
}

/* The state segment renders only for a DEVIATING state, in the shared badge tone for that state. */
.cluster-devrow__chip-state--neutral[b-9py9plrbow] {
    color: var(--ps-color-text-muted);
}

.cluster-devrow__chip-state--info[b-9py9plrbow] {
    color: var(--ps-color-info);
}

.cluster-devrow__chip-state--warning[b-9py9plrbow] {
    color: var(--ps-color-warning);
}

.cluster-devrow__chip-state--danger[b-9py9plrbow] {
    color: var(--ps-color-danger);
}

/* The one-word occupancy reads ("Busy (unmanaged)", "Free", …) are the chip's alternatives and share its rigidity
   rule from the other side: a fixed short vocabulary that reserves its width, so the badge beside it stays whole. */
.cluster-devrow__word[b-9py9plrbow] {
    flex: 0 0 auto;
    font-size: var(--ps-text-xs);
    white-space: nowrap;
}

.cluster-devrow__word--free[b-9py9plrbow] {
    color: var(--ps-color-success);
    font-weight: var(--ps-weight-medium);
}

.cluster-devrow__word--busy[b-9py9plrbow] {
    color: var(--ps-color-accent-emphasis);
}

/* The administrator-only unmanaged grades: amber, the same family the reclaim vocabulary uses for "someone else is
   here". Never the green of Free. */
.cluster-devrow__word--external[b-9py9plrbow] {
    color: var(--ps-color-warning);
}

.cluster-devrow__word--offline[b-9py9plrbow] {
    color: var(--ps-color-text-muted);
}

.cluster-devrow__slot[b-9py9plrbow]  .reclaim-indicator {
    display: flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-inline-size: 0;
    font-size: var(--ps-text-xs);
}

/* The expanded engine panel: the GPU's remaining metrics, inset under its row. */
.cluster-devrow__detail[b-9py9plrbow] {
    display: grid;
    gap: var(--ps-space-1);
    margin-block: 2px var(--ps-space-1);
    margin-inline-start: 4.7em;
    padding: var(--ps-space-2);
    border-radius: var(--ps-radius-sm);
    background: var(--ps-color-surface-sunken);
}
/* /Components/ClusterSummaryCard.razor.rz.scp.css */
/* The cluster summary card: full width (it is a band, not a track in a grid), with the header's counts sitting
   inline beside its title. Tokens only. */
.cluster-summary__title[b-oa1ws2qtc7] {
    font-size: var(--ps-text-md);
    font-weight: var(--ps-weight-semibold);
}

.cluster-summary__stats[b-oa1ws2qtc7] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-1) var(--ps-space-4);
    margin-inline-start: auto;
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.cluster-summary__stats strong[b-oa1ws2qtc7] {
    color: var(--ps-color-text);
    font-weight: var(--ps-weight-semibold);
}

/* The claimable statistic reads in the reclaim vocabulary's amber, the same tone the per-device badge uses. */
.cluster-summary__reclaimable[b-oa1ws2qtc7],
.cluster-summary__reclaimable strong[b-oa1ws2qtc7] {
    color: var(--ps-color-warning);
}

/* The body is a SIZE CONTAINER and nothing else: the split inside it reflows against the CARD's own width, which is
   the width that decides whether the meters and the table fit side by side. Keying that on the viewport instead
   would be wrong by exactly the shell's sidebar and gutters. */
.cluster-summary__body[b-oa1ws2qtc7] {
    container-type: inline-size;
}

.cluster-summary__split[b-oa1ws2qtc7] {
    --ps-stack-gap: var(--ps-space-3);
}

/* Wide enough for both measures at once — the capped meter column plus a readable table — so the two sit side by
   side, top-aligned, instead of stacking and leaving the card's whole right half empty. Below it the split falls
   back to the plain stack the ps-stack utility already gives it. */
@container (min-width: 52rem) {
    .cluster-summary__split[b-oa1ws2qtc7] {
        display: grid;
        /* The meters take up to their cap; the table's column is CONTENT-sized, so a narrow-ish card gives the table
           what it needs first and the meters absorb the rest — the table never gets squeezed into its own scrollbar
           to satisfy a meter measure that is only ever an upper bound. */
        grid-template-columns: minmax(0, var(--ps-cluster-summary-meter-max)) auto;
        gap: var(--ps-space-6);
        align-items: start;
    }
}

/* The bars cap their measure; the table beside them deliberately does not (see the component's note). */
.cluster-summary__meters[b-oa1ws2qtc7] {
    display: grid;
    gap: var(--ps-space-1);
    max-inline-size: var(--ps-cluster-summary-meter-max);
}

/* The table is NOT capped — it is CONTENT-sized, which is the same freedom stated the other way round: it grows with
   its columns and its longest username and stops there. Stretching it to a wall-wide row instead would hand the whole
   surplus to the name column and strand every reading a metre from the name it belongs to. */
.cluster-summary__split[b-oa1ws2qtc7]  table.ps-usertable {
    inline-size: auto;
    table-layout: auto;
}

.cluster-summary__split[b-oa1ws2qtc7]  table.ps-usertable th,
.cluster-summary__split[b-oa1ws2qtc7]  table.ps-usertable td {
    padding-inline: var(--ps-space-2);
}
/* /Components/DeviceReclaimIndicator.razor.rz.scp.css */
/* The reclaim-status indicator: a badge over a supporting detail line. Token-driven only — every color is a
   light-dark() status token (resolved through color-scheme), so both themes hold contrast with no hardcoded hex. */
.reclaim-indicator[b-viwrx8v8rt] {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
}

/* The "idle since X" supporting line: muted, dense, kept on the same row as the badge but wrapping
   gracefully on a narrow phone cell rather than overflowing. */
.reclaim-indicator__detail[b-viwrx8v8rt] {
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-xs);
    line-height: var(--ps-line-dense);
    white-space: nowrap;
}

/* The quieting accrual reads as an info pill (not the success/warning of the reclaimable tiers) so the three
   actionable states stay visually distinct: quieting (info) → reclaimable (success) → occupied-but-idle
   (warning). Built inline (not via StatusBadge) because it wraps a locally-ticked static accrual span plus an
   ellipsizing user suffix, not a plain label. */
.reclaim-indicator__quieting-badge[b-viwrx8v8rt] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    padding-block: calc(var(--ps-space-1) / 2);
    padding-inline: var(--ps-space-2);
    border: 1px solid color-mix(in srgb, var(--ps-color-info) 25%, transparent);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-info-soft);
    color: var(--ps-color-info);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-medium);
    line-height: var(--ps-line-dense);
    white-space: nowrap;
    /* Bounded by the host cell so the shrinkable user suffix below actually ellipsizes there instead of the
       whole pill overflowing a 360px-wide cell. The min-width reset is load-bearing: as a flex item of
       .reclaim-indicator the pill's automatic minimum is its full nowrap content, which would refuse the
       shrink and overflow the cell however narrow the suffix is willing to get. */
    max-width: 100%;
    min-width: 0;
}

/* The accrued-quiet reading ("quiet 2m 00s") never shrinks: a truncated duration would misreport the accounting,
   and the segment is short by construction. */
.reclaim-indicator__quieting-accrual[b-viwrx8v8rt] {
    flex: none;
}

/* The "· last <user>" attribution suffix: a username has no length bound, so this segment is the ONE allowed to
   shrink — ellipsized, never overflowing the badge (LayoutProbe's no-overflow oracle covers the host cells). */
.reclaim-indicator__quieting-user[b-viwrx8v8rt] {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* /Components/EnvironmentVariablesDisplay.razor.rz.scp.css */
/* Env values are arbitrary user data (CUDA device lists, allocation-facts JSON, pathological corpus
   strings): the chip releases the badge primitive's nowrap and caps itself at the cluster's width — the
   max-inline-size clamps the flex item's automatic minimum, so a huge value wraps inside the pane instead
   of widening the page. */
.env-vars[b-999xrv6ths]  .ps-badge {
    max-inline-size: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
}

.env-vars[b-999xrv6ths]  .ps-badge > code {
    min-inline-size: 0;
}
/* /Components/EnvironmentVariablesEditor.razor.rz.scp.css */
.environment-variables-editor[b-qhv15lea9e] {
    min-inline-size: 0;
}

.env-row[b-qhv15lea9e] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    margin-block-end: var(--ps-space-2);
    min-inline-size: 0;
}

/* --- The Overwrite/Append/Prepend behavior toggle: joined icon buttons sharing one divider. Each radio is a
   transparent full-size overlay of its icon (the control + the pointer target), so it clears the ≥24px floor. --- */
.env-row__behavior[b-qhv15lea9e] {
    display: inline-flex;
}

.env-row__behavior-option[b-qhv15lea9e] {
    position: relative;
    display: inline-flex;
    margin-inline-start: -1px;
    cursor: pointer;
}

.env-row__behavior-option:first-of-type[b-qhv15lea9e] {
    margin-inline-start: 0;
}

.env-row__behavior-input[b-qhv15lea9e] {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.env-row__behavior-content[b-qhv15lea9e] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--ps-control-height);
    min-block-size: var(--ps-control-height);
    border: 1px solid var(--ps-color-border-strong);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
}

.env-row__behavior-option:first-of-type .env-row__behavior-content[b-qhv15lea9e] {
    border-start-start-radius: var(--ps-radius-md);
    border-end-start-radius: var(--ps-radius-md);
}

.env-row__behavior-option:last-of-type .env-row__behavior-content[b-qhv15lea9e] {
    border-start-end-radius: var(--ps-radius-md);
    border-end-end-radius: var(--ps-radius-md);
}

.env-row__behavior-option:hover .env-row__behavior-content[b-qhv15lea9e] {
    background: var(--ps-color-surface-sunken);
}

.env-row__behavior-option:has(.env-row__behavior-input:checked)[b-qhv15lea9e] {
    z-index: 1;
}

.env-row__behavior-option:has(.env-row__behavior-input:checked) .env-row__behavior-content[b-qhv15lea9e] {
    background: var(--ps-color-accent);
    border-color: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
}

.env-row__behavior-option:has(.env-row__behavior-input:focus-visible) .env-row__behavior-content[b-qhv15lea9e] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: 2px;
}

.env-row__behavior-option:has(.env-row__behavior-input:disabled)[b-qhv15lea9e] {
    cursor: not-allowed;
}

.env-row__behavior-option:has(.env-row__behavior-input:disabled) .env-row__behavior-content[b-qhv15lea9e] {
    opacity: 0.55;
}

/* --- The key=value fields. --- */
.env-row__field[b-qhv15lea9e] {
    position: relative;
    min-inline-size: 0;
}

.env-row__field--key[b-qhv15lea9e] {
    flex: 0 1 12rem;
}

.env-row__field--value[b-qhv15lea9e] {
    flex: 1 1 14rem;
}

.env-row__input[b-qhv15lea9e] {
    inline-size: 100%;
}

.env-row__input--clearable[b-qhv15lea9e] {
    padding-inline-end: 2.25rem;
}

.env-row__equals[b-qhv15lea9e] {
    color: var(--ps-color-text-muted);
}

/* The inline clear affordance is a real interactive target: it fills the input's height and carries the WCAG
   2.5.8 inline floor too (a bare icon left it ~14px wide). */
.env-row__clear[b-qhv15lea9e] {
    position: absolute;
    inset-inline-end: var(--ps-space-1);
    inset-block: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--ps-target-min);
    padding: 0;
    border: 0;
    background: none;
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.env-row__clear:hover[b-qhv15lea9e] {
    color: var(--ps-color-text);
}

.env-row[b-qhv15lea9e]  .env-row__remove {
    color: var(--ps-color-danger);
}
/* /Components/Gallery/Fixtures/ChartsFixture.razor.rz.scp.css */
.charts-row[b-54dv6gzqch] {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--ps-space-4);
}
/* /Components/Gallery/Gallery.razor.rz.scp.css */
/* The token surface fixtures sit on — what a migrated page's background will be in both schemes. */
.gallery-surface[b-q2owt88rgg] {
    background: var(--ps-color-surface);
    color: var(--ps-color-text);
    padding: var(--ps-space-4);
    border-radius: var(--ps-radius-md);
}

/* The per-fixture heading is a de-emphasized caption (the fixture content below it is the subject). */
.gallery-fixture-title[b-q2owt88rgg] {
    font-size: var(--ps-text-lg);
    font-weight: var(--ps-weight-medium);
}
/* /Components/HomeDashboard.razor.rz.scp.css */
/* The home dashboard: the shared PageHeader masthead, then TWO full-width bands — the node grid (hero) and the
   cluster summary card. Each band owns its own internal layout (.cluster-grid packs the node cards; the summary card
   is one full-width card), so density comes from packing INSIDE a full-width band and never from placing two
   independent-height columns beside each other — the arrangement that left a 4K wall display half empty. There is no
   viewport tier and no view mode here: both bands render at every width, and neither needs a media query. The
   ps-stack utility owns the vertical rhythm between bands. Tokens only. */
.home-dashboard[b-mtnz823jdn] {
    --ps-stack-gap: var(--ps-space-6);
}

/* The loading skeleton stands in for the two-band shape, so the page does not visibly re-flow when data lands. */
.home-dashboard__skeleton[b-mtnz823jdn] {
    display: grid;
    gap: var(--ps-space-6);
}
/* /Components/ImmediateAddMemberPanel.razor.rz.scp.css */
/* The admin immediate member-add panel. The chip visuals restate the immediate-grow panel's gpu-chip look (Blazor
   scoped CSS is per-component, so the shared look is restated rather than inherited) in its READ-ONLY form: the chips
   here are placement feedback, not a multi-select, so there is no hover/selected affordance and no pointer cursor. */

.immediate-add-member-panel__chips[b-xz64aja5iw] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.immediate-add-member-panel__actions[b-xz64aja5iw] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
}

.gpu-chip[b-xz64aja5iw] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-block-size: var(--ps-target-min);
    padding-block: calc(var(--ps-space-1) / 2);
    padding-inline: var(--ps-space-2);
    border: 1px solid var(--ps-color-border-strong);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-dense);
}

/* A read-only chip is informational: default cursor, no interactive affordance. */
.gpu-chip.chip-readonly[b-xz64aja5iw] {
    cursor: default;
}

/* A blocked candidate reads as a full-strength danger pill (soft danger fill + a solid danger border/text) so the
   can't-place state stays obvious beside its reason icon — the same treatment the immediate-grow panel gives it. */
.gpu-chip.chip-blocked[b-xz64aja5iw] {
    background: var(--ps-color-danger-soft);
    border-color: var(--ps-color-danger);
    color: var(--ps-color-danger);
}
/* /Components/ImmediateGrowPanel.razor.rz.scp.css */
/* The admin immediate-grow chip grid. The chip visuals mirror the job-form DeviceCandidatePicker's gpu-chip (Blazor
   scoped CSS is per-component, so the shared look is restated here rather than inherited) — a selectable pill with the
   accent fill when selected and the dimmed not-allowed state when blocked. */

.immediate-grow-panel__chips[b-6a5clblwoa] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
}

.immediate-grow-panel__actions[b-6a5clblwoa] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
}

.gpu-chip[b-6a5clblwoa] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-block-size: var(--ps-target-min);
    padding-block: calc(var(--ps-space-1) / 2);
    padding-inline: var(--ps-space-2);
    border: 1px solid var(--ps-color-border-strong);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-dense);
    cursor: pointer;
}

.gpu-chip:hover:not(:disabled)[b-6a5clblwoa] {
    border-color: var(--ps-color-accent);
}

.gpu-chip.selected[b-6a5clblwoa] {
    background: var(--ps-color-accent);
    border-color: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
}

/* A blocked candidate reads as a full-strength danger pill (soft danger fill + a solid danger border/text) so the
   can't-grow state stays obvious beside its reason icon — NOT a faded chip. It is :disabled too, so the danger styling
   must win over the generic disabled dimming below; the disabled opacity is scoped to the non-blocked case so it never
   washes the danger affordance out. */
.gpu-chip.chip-blocked[b-6a5clblwoa] {
    background: var(--ps-color-danger-soft);
    border-color: var(--ps-color-danger);
    color: var(--ps-color-danger);
}

.gpu-chip:disabled[b-6a5clblwoa] {
    cursor: not-allowed;
}

/* Only a plain disabled chip (e.g. while a grow is in flight) dims; a blocked chip keeps its full-strength danger look. */
.gpu-chip:disabled:not(.chip-blocked)[b-6a5clblwoa] {
    opacity: 0.55;
}
/* /Components/InboundTunnels/InboundTunnelFormEditor.razor.rz.scp.css */
/* The shared tunnel create/edit form. FormSection/FormField + .ps-control carry the field skin and the
   RAM grid; this sheet styles the editor's own affordances: the live preview, inbound-access block, the
   inline switches, and the action row. var(--ps-*) only. */

.tunnel-form__note[b-vg97uq34ie] {
    margin-block: 0 var(--ps-space-3);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.tunnel-form__field-error[b-vg97uq34ie] {
    margin-block-start: var(--ps-space-1);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-danger);
}

/* The declared-ports quick-pick sits above the manual port input. */
.tunnel-form__declared-ports[b-vg97uq34ie] {
    margin-block-end: var(--ps-space-1);
}

/* --- Live resolution preview. --- */
.tunnel-form__preview[b-vg97uq34ie] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-1) var(--ps-space-2);
    padding: var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-sunken);
    font-size: var(--ps-text-sm);
}

.tunnel-form__preview-ok[b-vg97uq34ie] {
    color: var(--ps-color-success);
}

.tunnel-form__preview-warn[b-vg97uq34ie] {
    color: var(--ps-color-warning);
}

.tunnel-form__preview-spinner[b-vg97uq34ie] {
    display: inline-flex;
    animation: tunnel-form-spin-b-vg97uq34ie calc(var(--ps-motion-slow) * 3) linear infinite;
}

@keyframes tunnel-form-spin-b-vg97uq34ie {
    to {
        transform: rotate(360deg);
    }
}

.tunnel-form__preview-url[b-vg97uq34ie] {
    font-size: var(--ps-text-xs);
    overflow-wrap: anywhere;
}

/* --- Inbound access. --- */
.tunnel-form__keys[b-vg97uq34ie] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
    margin-block-end: var(--ps-space-3);
}

.tunnel-form__keys-label[b-vg97uq34ie] {
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
}

/* The "grant access to a key" picker + button: the field grows, the button hugs its content and sits on
   the field's trailing edge while there is room, wrapping beneath it in a narrow pane. */
.tunnel-form__grant[b-vg97uq34ie] {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: var(--ps-space-2);
}

.tunnel-form__grant[b-vg97uq34ie]  .ps-form-field {
    flex: 1 1 16rem;
    min-inline-size: 0;
}

.tunnel-form__divider[b-vg97uq34ie] {
    border: none;
    border-block-start: 1px solid var(--ps-color-border);
    margin-block: var(--ps-space-3);
}

/* A label that wraps a .ps-control checkbox — keeps the ≥24px target and aligns the caption beside it. */
.tunnel-form__switch[b-vg97uq34ie] {
    display: flex;
    align-items: start;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
    cursor: pointer;
}

.tunnel-form__shared-token[b-vg97uq34ie] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    margin-block-start: var(--ps-space-2);
    max-inline-size: 32rem;
}

.tunnel-form__shared-token[b-vg97uq34ie]  input {
    flex: 1 1 16rem;
    min-inline-size: 0;
}
/* /Components/JobCommandEditor.razor.rz.scp.css */
.execution-command-editor__args[b-dznnszfnwd] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}
/* /Components/JobDeclarationHistoryPanel.razor.rz.scp.css */
/* The declaration-history panel: one disclosure per generation, stacked. The rows carry the rhythm rather than each
   row carrying a margin, so a panel of one generation and a panel of ten read with the same spacing. */
.declaration-history[b-leyt3mvqsu] {
    display: grid;
    gap: var(--ps-space-2);
}

/* The differences of one generation, as a list — a save changes several things at once, and a run-on paragraph of
   them is unreadable. Each line may carry an operator-authored role or value, which wraps on the shared user-text
   rule rather than widening the card. */
.declaration-history__changes[b-leyt3mvqsu] {
    display: grid;
    gap: var(--ps-space-1);
    font-size: var(--ps-text-sm);
}

.declaration-history__original[b-leyt3mvqsu] {
    margin: 0;
}

/* The restore sits under the differences it would bring back, not beside the row's own summary: a reader decides
   after reading what it says, and the collapsed row deliberately offers nothing to press. */
.declaration-history__actions[b-leyt3mvqsu] {
    margin-block-start: var(--ps-space-2);
}
/* /Components/JobForm/ArgumentModeToggle.razor.rz.scp.css */
.argument-mode-toggle[b-42o2bkd4d7] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    flex-wrap: wrap;
}

.argument-mode-toggle__group[b-42o2bkd4d7] {
    display: inline-flex;
}

.argument-mode-toggle__option[b-42o2bkd4d7] {
    position: relative;
    display: inline-flex;
    margin-inline-start: -1px;
    cursor: pointer;
    user-select: none;
}

.argument-mode-toggle__option:first-of-type[b-42o2bkd4d7] {
    margin-inline-start: 0;
}

/* The radio fills its option — full-size and transparent (the control + the pointer target). */
.argument-mode-toggle__input[b-42o2bkd4d7] {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.argument-mode-toggle__content[b-42o2bkd4d7] {
    display: inline-flex;
    align-items: center;
    min-block-size: var(--ps-control-height);
    padding-inline: var(--ps-space-3);
    border: 1px solid var(--ps-color-border-strong);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-dense);
}

.argument-mode-toggle__option:first-of-type .argument-mode-toggle__content[b-42o2bkd4d7] {
    border-start-start-radius: var(--ps-radius-md);
    border-end-start-radius: var(--ps-radius-md);
}

.argument-mode-toggle__option:last-of-type .argument-mode-toggle__content[b-42o2bkd4d7] {
    border-start-end-radius: var(--ps-radius-md);
    border-end-end-radius: var(--ps-radius-md);
}

.argument-mode-toggle__option:hover .argument-mode-toggle__content[b-42o2bkd4d7] {
    background: var(--ps-color-surface-sunken);
}

.argument-mode-toggle__option:has(.argument-mode-toggle__input:checked)[b-42o2bkd4d7] {
    z-index: 1;
}

.argument-mode-toggle__option:has(.argument-mode-toggle__input:checked) .argument-mode-toggle__content[b-42o2bkd4d7] {
    background: var(--ps-color-accent);
    border-color: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
}

.argument-mode-toggle__option:has(.argument-mode-toggle__input:focus-visible) .argument-mode-toggle__content[b-42o2bkd4d7] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: 2px;
}

.argument-mode-toggle__hint[b-42o2bkd4d7] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}
/* /Components/JobForm/CountConstraintEditor.razor.rz.scp.css */
.count-constraint-editor[b-bau208eswk] {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

/* The shape cell hosts a container-queried SegmentedChoice, which contributes no intrinsic width — so the cell
   states its own basis (a content-sized track would collapse it) and caps where the three options stop needing
   more room. */
.count-constraint-editor__shape[b-bau208eswk] {
    display: flex;
    flex-direction: column;
    flex: 1 1 14rem;
    max-inline-size: 22rem;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

.count-constraint-editor__enumerated[b-bau208eswk] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

.count-constraint-editor__label[b-bau208eswk] {
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
}

.count-constraint-editor__values[b-bau208eswk] {
    flex: 1 1 14rem;
    min-inline-size: 0;
}

.count-constraint-editor__field[b-bau208eswk] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.count-constraint-editor__affix[b-bau208eswk] {
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* A count is two digits, never a measure. The CAP is what does the work: hosted inside a FormField (the elastic
   member counts are), the pattern's fill rule stretches every .ps-control to 100%, which turned min / max /
   multiple into three full-width rows with their affixes stranded above them. max-inline-size is not part of that
   rule, so it bounds the fill without a specificity duel. */
.count-constraint-editor__num[b-bau208eswk] {
    inline-size: 6rem;
    max-inline-size: 6rem;
}
/* /Components/JobForm/DependencyEditor.razor.rz.scp.css */
.dependency-editor[b-cxc11a8yux] {
    min-inline-size: 0;
}

/* Icon alignment comes from the shared .ps-icon-lede recipe (the icon tracks the first line). */
.dependency-editor__empty[b-cxc11a8yux] {
    margin: 0 0 var(--ps-space-2);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.dependency-row[b-cxc11a8yux] {
    padding: var(--ps-space-2);
    margin-block-end: var(--ps-space-2);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    min-inline-size: 0;
}

.dependency-row--phantom[b-cxc11a8yux] {
    border-style: dashed;
}

/* Condition + target + remove on one row that collapses to a stack on narrow panes. */
.dependency-row__fields[b-cxc11a8yux] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.dependency-row__condition[b-cxc11a8yux] {
    flex: 0 1 16rem;
    min-inline-size: 0;
}

.dependency-row__target[b-cxc11a8yux] {
    flex: 1 1 14rem;
    min-inline-size: 0;
}

/* A <select>'s intrinsic width is its WIDEST OPTION, and a target-job option carries a user-supplied job name.
   Without an explicit fill rule the control keeps that intrinsic width and pushes straight out of its cell — the
   page grew ~80px past a 360px viewport the moment the picker had a real candidate to offer (a state no probe
   reached, because no seeded scenario gives the administrator a job of their own to depend on). */
.dependency-row__target > .ps-control[b-cxc11a8yux] {
    inline-size: 100%;
    min-inline-size: 0;
}

.dependency-row__remove[b-cxc11a8yux] {
    margin-inline-start: auto;
}

.dependency-row__remove[b-cxc11a8yux]  .dependency-row__remove-button {
    color: var(--ps-color-danger);
}

.dependency-row__gloss[b-cxc11a8yux] {
    margin: var(--ps-space-1) 0 0;
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

.dependency-editor__note[b-cxc11a8yux] {
    margin: var(--ps-space-1) 0 0;
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}
/* /Components/JobForm/DependencyGraphView.razor.rz.scp.css */
/* The dependency-graph SVG colours live here, not in presentation attributes: var() is invalid in an SVG
   presentation attribute (and Chromium will not substitute it), whereas an author CSS rule cleanly overrides
   the zero-specificity attribute — so every fill/stroke resolves from a --ps-color-* token and flips with the
   three-state theme. The node/edge/text elements are built via RenderTreeBuilder, which Blazor does NOT stamp
   with the CSS-isolation scope attribute; ::deep matches them as descendants of the scoped .dependency-graph
   wrapper instead. The token light-values track the former hard-coded light hex closely (surface-raised #fff,
   accent-soft #e3effc vs #e7f0ff, border #d4d9e0 vs #cdd2d8, text #1a1d23 vs #1f2329), so the light rendering
   stays visually equivalent; dark mode now reads against the dark surface instead of staying stark white. */

.dependency-graph[b-s0cmjdlh0x]  .dep-node-box {
    fill: var(--ps-color-surface-raised);
    stroke: var(--ps-color-border);
    stroke-width: 1.5;
}

.dependency-graph[b-s0cmjdlh0x]  .dep-node-box--self {
    fill: var(--ps-color-accent-soft);
    stroke: var(--ps-color-accent);
}

.dependency-graph[b-s0cmjdlh0x]  .dep-node-label {
    fill: var(--ps-color-text);
}

.dependency-graph[b-s0cmjdlh0x]  .dep-node-state {
    fill: var(--ps-color-text-muted);
}

.dependency-graph[b-s0cmjdlh0x]  .dep-node-shortid {
    fill: var(--ps-color-text-faint);
}

.dependency-graph[b-s0cmjdlh0x]  .dep-edge {
    stroke: var(--ps-color-border-strong);
    stroke-width: 1.5;
}

.dependency-graph[b-s0cmjdlh0x]  .dep-edge-label {
    fill: var(--ps-color-text-muted);
}

.dependency-graph[b-s0cmjdlh0x]  .dep-arrowhead {
    fill: var(--ps-color-border-strong);
}

/* Monospace for the data-volatile short-id: the 8 hex glyphs are fixed-width so the right-anchored mask edge
   stays put run-to-run (the same load-bearing reason ShortId uses .ps-mono). */
.dependency-graph[b-s0cmjdlh0x]  .dep-mono {
    font-family: var(--ps-font-mono);
}

/* The "no relationships" / singleton notes: a small inline icon + label (the design-system replacement for
   the former icon-font glyphs + Bootstrap text utilities). */
.dependency-graph__note[b-s0cmjdlh0x] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-1);
    margin: 0;
    font-size: var(--ps-text-sm);
}

.dependency-graph__note--singletons[b-s0cmjdlh0x] {
    margin-block-start: var(--ps-space-1);
}
/* /Components/JobForm/DeviceCandidatePicker.razor.rz.scp.css */
/* The picker is its own query container: how many node blocks share a row is decided by the width of the PANE
   the picker got (a member card in a phone-width form column vs. the same card on a wall display), never by the
   viewport. */
.device-candidate-picker[b-ez9bxj6xus] {
    min-inline-size: 0;
    container-type: inline-size;
    container-name: ps-picker;
}

/* Node blocks pack as many ≥--ps-picker-node-min columns as the pane affords (one on a phone). The chips inside
   a node still wrap in rows — this grid only decides how the NODES lay out against each other. */
.device-candidate-picker__nodes[b-ez9bxj6xus] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--ps-picker-node-min), 100%), 1fr));
    gap: var(--ps-space-2);
    margin-block-end: var(--ps-space-2);
}

.device-candidate-picker__node[b-ez9bxj6xus] {
    padding: var(--ps-space-2) var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    min-inline-size: 0;
}

.device-candidate-picker__node-head[b-ez9bxj6xus] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    margin-block-end: var(--ps-space-2);
    font-size: var(--ps-text-sm);
}

.device-candidate-picker__hostname[b-ez9bxj6xus] {
    font-weight: var(--ps-weight-semibold);
}

.device-candidate-picker__whole-node[b-ez9bxj6xus] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    margin-inline-start: var(--ps-space-1);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.device-candidate-picker__chips[b-ez9bxj6xus] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
}

.device-candidate-picker__footer[b-ez9bxj6xus] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    margin-block-start: var(--ps-space-1);
}

.device-candidate-picker__note[b-ez9bxj6xus] {
    margin: 0;
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* The filter-semantics caption at the top of the picker (item 8): candidate filters, any = unselected, one node. */
.device-candidate-picker__caption[b-ez9bxj6xus] {
    margin: 0 0 var(--ps-space-2);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* The cross-node reminder and the single-node feasibility warning sit below the footer as their own lines
   (icon alignment comes from the shared .ps-icon-lede recipe). */
.device-candidate-picker__cross-node[b-ez9bxj6xus],
.device-candidate-picker__infeasible[b-ez9bxj6xus] {
    margin-block-start: var(--ps-space-2);
}

.device-candidate-picker__infeasible[b-ez9bxj6xus] {
    color: var(--ps-color-warning);
}

/* The cluster-level tri-state "Select all GPUs" parent, footer-aligned with the note and the Clear button. */
.device-candidate-picker__select-all[b-ez9bxj6xus] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

/* --- The GPU chip: a selectable pill carrying the online dot, label, co-domain, and live SM badge. --- */
.gpu-chip[b-ez9bxj6xus] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-block-size: var(--ps-target-min);
    padding-block: calc(var(--ps-space-1) / 2);
    padding-inline: var(--ps-space-2);
    border: 1px solid var(--ps-color-border-strong);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-dense);
    cursor: pointer;
}

.gpu-chip:hover:not(:disabled)[b-ez9bxj6xus] {
    border-color: var(--ps-color-accent);
}

.gpu-chip.selected[b-ez9bxj6xus] {
    background: var(--ps-color-accent);
    border-color: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
}

.gpu-chip.chip-offline[b-ez9bxj6xus] {
    opacity: 0.6;
}

.gpu-chip:disabled[b-ez9bxj6xus] {
    cursor: not-allowed;
    opacity: 0.45;
}

.gpu-chip__domain[b-ez9bxj6xus] {
    padding-inline-start: var(--ps-space-1);
    border-inline-start: 1px solid currentColor;
    font-size: var(--ps-text-xs);
    opacity: 0.8;
}

.gpu-chip__usage[b-ez9bxj6xus] {
    padding-block: calc(var(--ps-space-1) / 2);
    padding-inline: var(--ps-space-1);
    border-radius: var(--ps-radius-sm);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-medium);
    font-variant-numeric: tabular-nums;
}

.gpu-chip__usage--success[b-ez9bxj6xus] {
    background: var(--ps-color-success-soft);
    color: var(--ps-color-success);
}

.gpu-chip__usage--warning[b-ez9bxj6xus] {
    background: var(--ps-color-warning-soft);
    color: var(--ps-color-warning);
}

.gpu-chip__usage--danger[b-ez9bxj6xus] {
    background: var(--ps-color-danger-soft);
    color: var(--ps-color-danger);
}

/* On a selected (accent-filled) chip the soft usage fills would clash; let them read on the accent instead. */
.gpu-chip.selected .gpu-chip__usage[b-ez9bxj6xus] {
    background: rgb(255 255 255 / 0.22);
    color: var(--ps-color-on-accent);
}

/* --- The online dot, shared by node heads and chips. --- */
.online-dot[b-ez9bxj6xus] {
    display: inline-block;
    inline-size: 0.55rem;
    block-size: 0.55rem;
    border-radius: var(--ps-radius-full);
    flex: 0 0 auto;
}

.online-dot.online[b-ez9bxj6xus] {
    background: var(--ps-color-success);
}

.online-dot.offline[b-ez9bxj6xus] {
    background: var(--ps-color-text-faint);
}
/* /Components/JobForm/FieldErrors.razor.rz.scp.css */
.field-errors[b-0w2q349g0p] {
    margin-block-start: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-danger);
}

.field-errors li + li[b-0w2q349g0p] {
    margin-block-start: calc(var(--ps-space-1) / 2);
}
/* /Components/JobForm/GpuRequestEditor.razor.rz.scp.css */
/* The GPU ask is its own query container so its two visible cells (Sharing, Count) pair up or stack on the
   width THIS editor got, never on the viewport — the same editor sits in a phone-width member card and in a
   wall-display one. */
.gpu-request-editor[b-zi4i5x1oqs] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-3);
    min-inline-size: 0;
    container-type: inline-size;
    container-name: ps-gpu-request;
}

/* A grid, not a wrapping flex row: the cells host container-queried segmented controls, which contribute no
   intrinsic width, so a content-sized track would collapse them. Grid tracks are definite at every width. */
.gpu-request-editor__top[b-zi4i5x1oqs] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(var(--ps-field-min), 100%), 1fr));
    gap: var(--ps-space-4);
    align-items: start;
}

.gpu-request-editor__cell[b-zi4i5x1oqs] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

.gpu-request-editor__label[b-zi4i5x1oqs] {
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
}

/* The advanced sub-region: a dashed-framed inset distinct from the member card it lives in. In a NARROW card
   the frame is dropped for a plain heading rule — four levels of nested box (card → section → disclosure →
   inset) otherwise spend a quarter of a phone's width on borders and padding. */
.gpu-request-editor__advanced[b-zi4i5x1oqs] {
    padding: var(--ps-space-3);
    border: 1px dashed var(--ps-color-border-strong);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-sunken);
}

@container ps-member-card (max-width: 30rem) {
    .gpu-request-editor__advanced[b-zi4i5x1oqs] {
        padding: 0;
        padding-block-start: var(--ps-space-2);
        border: 0;
        border-block-start: 1px solid var(--ps-color-border);
        border-radius: 0;
        background: none;
    }
}
/* /Components/JobForm/IntSetEditor.razor.rz.scp.css */
.int-set-editor[b-let68ejpwy] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

.int-set-editor__chip[b-let68ejpwy] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    padding-block: calc(var(--ps-space-1) / 2);
    padding-inline: var(--ps-space-2);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
    font-size: var(--ps-text-xs);
    line-height: var(--ps-line-dense);
}

/* The chip's remove control is a real interactive target, so it carries the WCAG 2.5.8 floor in BOTH axes (it
   used to be an ~14px icon — under the floor on every axis, and simply unreachable with a thumb). */
.int-set-editor__remove[b-let68ejpwy] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--ps-target-min);
    min-block-size: var(--ps-target-min);
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    cursor: pointer;
    opacity: 0.85;
}

.int-set-editor__remove:hover[b-let68ejpwy] {
    opacity: 1;
}

.int-set-editor__input[b-let68ejpwy] {
    inline-size: 5.5rem;
}
/* /Components/JobForm/MemberCommandOverrideEditor.razor.rz.scp.css */
.member-command-override[b-npz18opiy0] {
    min-inline-size: 0;
}

.member-command-override__switch[b-npz18opiy0] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
    cursor: pointer;
}

/* The dashed inset distinguishes the override's fields from the card around them. In a NARROW card the frame is
   dropped for a plain heading rule: the override already sits inside card → section → disclosure, and a fourth
   box would spend the phone's remaining width on chrome. */
.member-command-override__body[b-npz18opiy0] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-3);
    margin-block-start: var(--ps-space-2);
    padding: var(--ps-space-3);
    border: 1px dashed var(--ps-color-border-strong);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-sunken);
}

@container ps-member-card (max-width: 30rem) {
    .member-command-override__body[b-npz18opiy0] {
        padding: 0;
        padding-block-start: var(--ps-space-2);
        border: 0;
        border-block-start: 1px solid var(--ps-color-border);
        border-radius: 0;
        background: none;
    }
}

.member-command-override__field[b-npz18opiy0] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

/* This editor's OWN text controls fill the row they already have. They are NOT FormFields (the inherit switch is
   this editor's shape, stated per field), so FormField's "a field's text control fills its column" rule never
   reaches them, and a bare input keeps the browser's intrinsic ~20-character width inside a row many times wider:
   a replacement executable or working directory — long values, both, the same ones the base command editor gives a
   full row — was read through a keyhole, with the clearable input's clear button stranded at the far end of the
   wrapper it is positioned against. Same fill rule DependencyEditor states for its own target cell, kept to this
   editor's DIRECT children so the nested arguments / environment editors keep sizing their own inputs. */
.member-command-override__field[b-npz18opiy0] >  .clearable-input-wrapper > .clearable-input-field {
    inline-size: 100%;
}

.member-command-override__check[b-npz18opiy0] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
    cursor: pointer;
}

.member-command-override__label[b-npz18opiy0] {
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
}

.member-command-override__inherit[b-npz18opiy0] {
    margin: 0;
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

.member-command-override__arg-mode[b-npz18opiy0] {
    margin-block-end: var(--ps-space-1);
}
/* /Components/JobForm/MemberEditor.razor.rz.scp.css */
/* The member card is its own query container: how its fields pack, and how much padding the card and its nested
   insets spend, follow the width THIS card got — never the viewport (the house container-first rule). A phone's
   form column is ~19rem inside the page and card padding; four levels of nested box used to eat about a quarter
   of it, which is what the padding diet below is for. */
.member-card[b-ux2ifl7fme] {
    margin-block-end: var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-inline-start: 3px solid var(--ps-color-accent);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-raised);
    min-inline-size: 0;
    container-type: inline-size;
    container-name: ps-member-card;
}

/* Everything the card lays out lives one level in — a container element cannot be styled by its own query, and
   the padding IS one of the things the diet below steps down. */
.member-card__inner[b-ux2ifl7fme] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-3);
    padding: var(--ps-space-4);
    min-inline-size: 0;
}

/* The card's own header row: which member this is, and Remove. It WRAPS (the shared Card header rule, applied
   here because a member card is not a Card primitive) so the two never collide. */
.member-card__header[b-ux2ifl7fme] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.member-card__heading[b-ux2ifl7fme] {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-semibold);
}

/* The live role echoed beside the ordinal, so a card is identifiable while its fields are scrolled away. */
.member-card__heading-role[b-ux2ifl7fme] {
    min-inline-size: 0;
    font-weight: var(--ps-weight-normal);
    color: var(--ps-color-text-muted);
}

.member-card__header[b-ux2ifl7fme]  .member-card__remove-button {
    color: var(--ps-color-danger);
}

/* The host's one quiet line under the header (what is running under this kind right now). It rides the card's own
   stack rhythm rather than a margin of its own, so adding it never shifts the fields below by an odd amount. */
.member-card__note[b-ux2ifl7fme] {
    margin: 0;
    font-size: var(--ps-text-sm);
}

/* The four defining fields on one RAM grid — the same shape a FormSection lays its fields out on, so the labels
   align and the controls fill without any per-card fill rule. */
.member-card__fields[b-ux2ifl7fme] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(var(--ps-field-min), 100%), 1fr));
    gap: var(--ps-space-3);
    align-items: start;
    min-inline-size: 0;
}

/* Replicas is a two-digit count, not a measure — it keeps its own narrow box inside its grid cell. */
.member-card__replicas-input[b-ux2ifl7fme] {
    max-inline-size: 8rem;
}

.member-card__section[b-ux2ifl7fme] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

/* The name of an interior section. Deliberately NOT an <hN>: the page's only real heading is its (visually
   hidden) h1 — card titles are CardTitle spans and form groups use a real <legend> — so an h4 in here jumps two
   levels and axe's heading-order rule fails the route. Promoting the whole ladder is a shared-primitive decision,
   not a member card's to make. */
.member-card__section-title[b-ux2ifl7fme] {
    margin: 0;
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
    line-height: var(--ps-line-heading);
}

/* The box tracks the FIRST line of its label: several of these labels carry a long muted parenthetical that wraps
   to two or three lines, and a centered box then floats halfway down the paragraph. */
.member-card__check[b-ux2ifl7fme] {
    display: flex;
    align-items: flex-start;
    gap: var(--ps-space-2);
    min-inline-size: 0;
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-body);
    cursor: pointer;
}

/* All of a checkbox's prose is ONE flex item beside the box: split into several, a label and its muted
   parenthetical become two narrow columns that wrap independently at phone widths. */
.member-card__check-text[b-ux2ifl7fme] {
    min-inline-size: 0;
}

.member-card__muted[b-ux2ifl7fme],
.member-card__hint[b-ux2ifl7fme] {
    color: var(--ps-color-text-muted);
}

.member-card__hint[b-ux2ifl7fme] {
    margin: 0;
    font-size: var(--ps-text-sm);
}

.member-card__bounds[b-ux2ifl7fme] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
}

.member-card__affix[b-ux2ifl7fme] {
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.member-card__bounds-num[b-ux2ifl7fme] {
    inline-size: 6rem;
}

/* --- Node selector: a wrapping row of toggle buttons + the prefix label. --- */
.member-card__nodes[b-ux2ifl7fme] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
}

.member-card__nodes-prefix[b-ux2ifl7fme] {
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.member-card__node[b-ux2ifl7fme] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-block-size: var(--ps-target-min);
    padding-inline: var(--ps-space-2);
    border: 1px solid var(--ps-color-border-strong);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-dense);
    cursor: pointer;
}

.member-card__node:hover[b-ux2ifl7fme] {
    border-color: var(--ps-color-accent);
}

.member-card__node.is-selected[b-ux2ifl7fme] {
    background: var(--ps-color-accent);
    border-color: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
}

/* The warning chips take the shared icon-and-prose alignment (.ps-icon-lede) — the icon tracks the first line. */
.member-card__warn[b-ux2ifl7fme] {
    margin: 0;
    font-size: var(--ps-text-sm);
    color: var(--ps-color-warning);
}

/* --- The padding diet. A narrow card spends its width on controls, not on chrome: the card's own padding steps
   down and the nested insets (the GPU editor's advanced region, the command-override body) drop their box in
   favour of a plain heading rule — see the sibling stylesheets, which key off this same container. --- */
@container ps-member-card (max-width: 30rem) {
    .member-card__inner[b-ux2ifl7fme] {
        gap: var(--ps-space-2);
        padding: var(--ps-space-2);
    }
}
/* /Components/JobForm/MembersCard.razor.rz.scp.css */
/* The header's add-member cluster: a tight pair that reflows under the title when the header wraps. */
.members-card__actions[b-rv2tfjhv97] {
    --ps-cluster-gap: var(--ps-space-2);
}

/* The rule between the member list and the group-size field that binds those members into replica groups. */
.members-card__divider[b-rv2tfjhv97] {
    border: none;
    border-block-start: 1px solid var(--ps-color-border);
    margin-block: var(--ps-space-4);
}

/* A group size is a small count, not a measure. */
.members-card__group-size[b-rv2tfjhv97] {
    max-inline-size: 8rem;
}

/* The live group-arithmetic preview under the input ("8 members = 2 groups x 4"). */
.members-card__group-preview[b-rv2tfjhv97] {
    margin: var(--ps-space-2) 0 0;
    font-size: var(--ps-text-sm);
}
/* /Components/JobForm/SchedulingPolicyCard.razor.rz.scp.css */
/* The rules between the card's three groups. */
.scheduling-policy__divider[b-8dj881knte] {
    border: none;
    border-block-start: 1px solid var(--ps-color-border);
    margin-block: var(--ps-space-4);
}

/* The box tracks the FIRST line of its label, which wraps to two lines in a narrow column. */
.scheduling-policy__check[b-8dj881knte] {
    display: flex;
    align-items: flex-start;
    gap: var(--ps-space-2);
    min-inline-size: 0;
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-body);
    cursor: pointer;
}

/* The numeric group narrows the field-grid minimum for ITSELF (a custom property on a wrapper the nested
   FormSection inherits), so five two-digit inputs pack into a compact grid instead of five 16rem text tracks —
   and their labels sit on one aligned row per grid line. */
.scheduling-policy__numbers[b-8dj881knte] {
    --ps-field-min: 11rem;
    min-inline-size: 0;
}
/* /Components/JobForm/SegmentedChoice.razor.rz.scp.css */
/* The control is its OWN query container: which presentation it wears depends on the width IT gets, not on the
   viewport (the house container-first rule) and not on how deep in the form it happens to sit.

   contain:inline-size means the control contributes NOTHING to a content-sized parent's width, so a parent that
   sizes to its contents would collapse it to zero. The min-inline-size floor makes that impossible; hosts that
   want a wider control give their cell a definite basis (a grid track, a flex-basis) rather than relying on the
   floor. min(100%, …) keeps the floor from overflowing a genuinely narrow pane. */
.segmented-choice[b-mj06k1wfg7] {
    min-inline-size: min(100%, 11rem);
    container-type: inline-size;
    container-name: ps-segmented;
}

/* --- The DEFAULT presentation: a stacked list. Every option is its own full-width rounded row, so nothing
   overlaps and nothing rounds the wrong end. The joined-row presentation below overrides these five custom
   properties (and only these) when the control's container proves the row fits on one line. --- */
.segmented-choice__group[b-mj06k1wfg7] {
    display: flex;
    flex-direction: var(--ps-segmented-flow, column);
    gap: var(--ps-segmented-gap, var(--ps-space-1));
    min-inline-size: 0;
    border: var(--ps-segmented-group-border, 0) solid var(--ps-color-border-strong);
    border-radius: var(--ps-radius-md);
}

/* Each option is a positioned label hosting a transparent full-size radio overlay (the real control + the
   pointer target) plus the visible content. */
.segmented-choice__option[b-mj06k1wfg7] {
    position: relative;
    display: flex;
    flex: var(--ps-segmented-option-flex, 0 1 auto);
    min-inline-size: 0;
    cursor: pointer;
    user-select: none;
}

/* The radio fills its option — full-size (≥ the control height, so it clears the WCAG/probe target floor) and
   transparent, so the keyboard/assistive-tech control IS the pointer target while the content paints the look. */
.segmented-choice__input[b-mj06k1wfg7] {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.segmented-choice__input:disabled[b-mj06k1wfg7] {
    cursor: not-allowed;
}

.segmented-choice__content[b-mj06k1wfg7] {
    display: inline-flex;
    align-items: center;
    justify-content: var(--ps-segmented-justify, flex-start);
    gap: var(--ps-space-1);
    inline-size: 100%;
    min-block-size: var(--ps-control-height);
    padding-inline: var(--ps-space-3);
    border: var(--ps-segmented-option-border, 1px) solid var(--ps-color-border-strong);
    /* In the joined row the SEAM is the one divider between neighbours: the option border collapses to 0 and only
       this inline-end edge is drawn (the last option resets it below), so no negative margin is ever needed. */
    border-inline-end-width: var(--ps-segmented-seam-border, var(--ps-segmented-option-border, 1px));
    border-radius: var(--ps-segmented-radius, var(--ps-radius-md));
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-dense);
    transition:
        background-color var(--ps-motion-fast) var(--ps-ease),
        border-color var(--ps-motion-fast) var(--ps-ease),
        color var(--ps-motion-fast) var(--ps-ease);
}

/* The outer ends round in BOTH presentations (stacked: every option is already fully rounded; joined: the row's
   two ends). Document order is safe here because the joined presentation never wraps — that is what it means. */
.segmented-choice__option:first-of-type .segmented-choice__content[b-mj06k1wfg7] {
    border-start-start-radius: var(--ps-radius-md);
    border-end-start-radius: var(--ps-radius-md);
}

.segmented-choice__option:last-of-type .segmented-choice__content[b-mj06k1wfg7] {
    border-start-end-radius: var(--ps-radius-md);
    border-end-end-radius: var(--ps-radius-md);
    border-inline-end-width: var(--ps-segmented-option-border, 1px);
}

.segmented-choice__option:hover .segmented-choice__content[b-mj06k1wfg7] {
    background: var(--ps-color-surface-sunken);
}

.segmented-choice__option:has(.segmented-choice__input:checked) .segmented-choice__content[b-mj06k1wfg7] {
    background: var(--ps-color-accent);
    border-color: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
}

.segmented-choice__option--danger .segmented-choice__content[b-mj06k1wfg7] {
    color: var(--ps-color-danger);
    border-color: var(--ps-color-danger);
}

.segmented-choice__option--danger:has(.segmented-choice__input:checked) .segmented-choice__content[b-mj06k1wfg7] {
    background: var(--ps-color-danger);
    border-color: var(--ps-color-danger);
    color: var(--ps-color-on-danger);
}

/* Keyboard focus rides the real (transparent) radio. */
.segmented-choice__option:has(.segmented-choice__input:focus-visible) .segmented-choice__content[b-mj06k1wfg7] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: 2px;
}

.segmented-choice__option:has(.segmented-choice__input:disabled)[b-mj06k1wfg7] {
    cursor: not-allowed;
}

.segmented-choice__option:has(.segmented-choice__input:disabled) .segmented-choice__content[b-mj06k1wfg7] {
    opacity: 0.55;
}

.segmented-choice__gloss[b-mj06k1wfg7] {
    display: block;
    margin-block-start: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* --- The JOINED ROW presentation, one query per fit rung (see the component's estimator). The rung literals
   here and the C# ladder are the same numbers on both sides of a condition that cannot read a variable. Each
   query only re-points the custom properties above; nothing is re-declared, so the two presentations can never
   drift apart. --- */
@container ps-segmented (min-width: 12rem) {
    .segmented-choice[data-segmented-fit="12"] .segmented-choice__group[b-mj06k1wfg7] {
        --ps-segmented-flow: row;
        --ps-segmented-gap: 0;
        --ps-segmented-group-border: 1px;
        --ps-segmented-option-border: 0;
        --ps-segmented-seam-border: 1px;
        --ps-segmented-radius: 0;
        --ps-segmented-justify: center;
        --ps-segmented-option-flex: 1 1 auto;
    }
}

@container ps-segmented (min-width: 14rem) {
    .segmented-choice[data-segmented-fit="14"] .segmented-choice__group[b-mj06k1wfg7] {
        --ps-segmented-flow: row;
        --ps-segmented-gap: 0;
        --ps-segmented-group-border: 1px;
        --ps-segmented-option-border: 0;
        --ps-segmented-seam-border: 1px;
        --ps-segmented-radius: 0;
        --ps-segmented-justify: center;
        --ps-segmented-option-flex: 1 1 auto;
    }
}

@container ps-segmented (min-width: 18rem) {
    .segmented-choice[data-segmented-fit="18"] .segmented-choice__group[b-mj06k1wfg7] {
        --ps-segmented-flow: row;
        --ps-segmented-gap: 0;
        --ps-segmented-group-border: 1px;
        --ps-segmented-option-border: 0;
        --ps-segmented-seam-border: 1px;
        --ps-segmented-radius: 0;
        --ps-segmented-justify: center;
        --ps-segmented-option-flex: 1 1 auto;
    }
}

@container ps-segmented (min-width: 22rem) {
    .segmented-choice[data-segmented-fit="22"] .segmented-choice__group[b-mj06k1wfg7] {
        --ps-segmented-flow: row;
        --ps-segmented-gap: 0;
        --ps-segmented-group-border: 1px;
        --ps-segmented-option-border: 0;
        --ps-segmented-seam-border: 1px;
        --ps-segmented-radius: 0;
        --ps-segmented-justify: center;
        --ps-segmented-option-flex: 1 1 auto;
    }
}

@container ps-segmented (min-width: 26rem) {
    .segmented-choice[data-segmented-fit="26"] .segmented-choice__group[b-mj06k1wfg7] {
        --ps-segmented-flow: row;
        --ps-segmented-gap: 0;
        --ps-segmented-group-border: 1px;
        --ps-segmented-option-border: 0;
        --ps-segmented-seam-border: 1px;
        --ps-segmented-radius: 0;
        --ps-segmented-justify: center;
        --ps-segmented-option-flex: 1 1 auto;
    }
}

@container ps-segmented (min-width: 32rem) {
    .segmented-choice[data-segmented-fit="32"] .segmented-choice__group[b-mj06k1wfg7] {
        --ps-segmented-flow: row;
        --ps-segmented-gap: 0;
        --ps-segmented-group-border: 1px;
        --ps-segmented-option-border: 0;
        --ps-segmented-seam-border: 1px;
        --ps-segmented-radius: 0;
        --ps-segmented-justify: center;
        --ps-segmented-option-flex: 1 1 auto;
    }
}

@container ps-segmented (min-width: 40rem) {
    .segmented-choice[data-segmented-fit="40"] .segmented-choice__group[b-mj06k1wfg7] {
        --ps-segmented-flow: row;
        --ps-segmented-gap: 0;
        --ps-segmented-group-border: 1px;
        --ps-segmented-option-border: 0;
        --ps-segmented-seam-border: 1px;
        --ps-segmented-radius: 0;
        --ps-segmented-justify: center;
        --ps-segmented-option-flex: 1 1 auto;
    }
}

@container ps-segmented (min-width: 48rem) {
    .segmented-choice[data-segmented-fit="48"] .segmented-choice__group[b-mj06k1wfg7] {
        --ps-segmented-flow: row;
        --ps-segmented-gap: 0;
        --ps-segmented-group-border: 1px;
        --ps-segmented-option-border: 0;
        --ps-segmented-seam-border: 1px;
        --ps-segmented-radius: 0;
        --ps-segmented-justify: center;
        --ps-segmented-option-flex: 1 1 auto;
    }
}
/* /Components/JobForm/TcpPortsEditor.razor.rz.scp.css */
.tcp-ports-editor__row[b-13xfqvkx18] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    margin-block-end: var(--ps-space-2);
    min-inline-size: 0;
}

/* The mode cell carries a container-queried SegmentedChoice, which contributes no intrinsic width — so the cell
   states its own basis rather than sizing to (absent) content. It caps out: a two-option toggle stretched across
   the row would read as the row's subject rather than its qualifier. */
.tcp-ports-editor__mode[b-13xfqvkx18] {
    flex: 1 1 12rem;
    max-inline-size: 18rem;
    min-inline-size: 0;
}

.tcp-ports-editor__values[b-13xfqvkx18] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.tcp-ports-editor__num[b-13xfqvkx18] {
    inline-size: 7rem;
}

.tcp-ports-editor__affix[b-13xfqvkx18] {
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}
/* /Components/JobLifecycleChain.razor.rz.scp.css */
/* The job lifecycle chain: a stack of ATTEMPTS, each a wrapping ribbon of step pills. The attempts stack vertically at
   every width (an attempt is a paragraph of the job's story, not a column), and the steps INSIDE an attempt go from a
   wrapping row to a vertical list as the column narrows — which is what keeps a ten-attempt chain inside the page at
   360px without a horizontal scroller. Container queries, not media queries: the chain lives inside a card whose
   width has nothing to do with the viewport's. */

.lifecycle-chain[b-g9v94aphn0] {
    container-type: inline-size;
    container-name: ps-lifecycle-chain;
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
    /* The chain never widens its host: a long attempt wraps instead of pushing the page sideways. */
    min-inline-size: 0;
    max-inline-size: 100%;
}

/* One attempt: its leading connector over either its bare step trail or the disclosure that holds it. A column, so
   the connector sits above what it leads into whether that is a heading or a toggle row. (The --open modifier marks
   the attempt rendered WITHOUT a disclosure — the current one — and carries no style of its own.) */
.lifecycle-chain__attempt[b-g9v94aphn0] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

/* The connector between two attempts: the ribbon's step arrow, rotated to run DOWN the stack — attempts never form a
   row, so this one always points down, and it is the same glyph and the same rotation the phone form applies to the
   step arrows. It is what carries the eye from one attempt's last step into the next attempt's header, so the attempts
   read as one concatenated chain rather than as a pile of separate boxes. Indented to sit under the pills it joins;
   a flex item, so the rotation applies without the span needing a display of its own. */
.lifecycle-chain__link[b-g9v94aphn0] {
    align-self: flex-start;
    margin-inline-start: var(--ps-space-2);
    font-size: var(--ps-text-sm);
    line-height: 1;
    transform: rotate(90deg);
}

/* The current attempt's heading line: which attempt it is, plus any honesty aside. Only rendered once the job has more
   than one attempt — a job on its first attempt needs no "attempt 1" label. */
.lifecycle-chain__heading[b-g9v94aphn0] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
}

.lifecycle-chain__label[b-g9v94aphn0] {
    font-weight: var(--ps-weight-medium);
}

/* Which generation of the declaration the attempt ran — an identifying annotation beside the attempt's own name, so it
   is quiet rather than a second heading, and it rides the heading's own baseline row (no margin of its own). */
.lifecycle-chain__generation[b-g9v94aphn0] {
    font-size: var(--ps-text-sm);
}

.lifecycle-chain__legacy[b-g9v94aphn0] {
    font-size: var(--ps-text-sm);
    font-style: italic;
}

/* PHONE FORM. Below this the steps stop being a row: each step takes its own line, left-aligned, and the arrows
   turn to point down the list. A wrapping row of pills at 360px degenerates into ragged one-and-a-half-pill rows
   whose reading order is ambiguous; a vertical list has one obvious order and no ragged edge. */
@container ps-lifecycle-chain (max-width: 26rem) {
    /* ::deep — the ribbon and the disclosure panel are CHILD components, so their elements carry their own scope. */
    .lifecycle-chain[b-g9v94aphn0]  .lifecycle-ribbon,
    .lifecycle-chain[b-g9v94aphn0]  .lifecycle-ribbon__step {
        flex-direction: column;
        align-items: flex-start;
    }

    .lifecycle-chain[b-g9v94aphn0]  .lifecycle-ribbon__arrow {
        /* The connector between two stacked steps reads downward. A rotation (rather than a second glyph) keeps the
           one arrow character the row form uses, so the two forms cannot drift apart. */
        transform: rotate(90deg);
        margin-inline-start: var(--ps-space-2);
    }
}
/* /Components/Layout/MainLayout.razor.rz.scp.css */
/* ===================================================================================================
   The app shell (feature K phase 3), token-only, four tiers. The viewport media queries below are the
   ONE sanctioned place for them (components stay container-first); the literals mirror the tokens.css
   breakpoints (var() is illegal in @media conditions):
     base  <48rem  (768px)  phone — sticky topbar + hamburger → native popover drawer
     md    ≥48rem           tablet — icon rail (--ps-shell-rail-width), labels off
     lg    ≥64rem  (1024px) desktop — full sidebar (--ps-shell-nav-width), collapsible to the rail
                            via html[data-nav="collapsed"] (the ps-nav cookie, flipped by site.js)
     3xl   ≥120rem (1920px) wide — content capped at --ps-content-max and centered: extra space
                            becomes margin (pack, never stretch), nav never widens

   SCROLL-CONTAINER DECISION: the PAGE scrolls (the document owns the one scrollbar); the shell does
   NOT wrap <main> in its own scroller. Native behaviors stay free (scroll restoration, fragment jumps,
   mobile address-bar collapse), and position:sticky inside page content sticks to the viewport. The
   topbar is sticky at the top, so in-page sticky elements must clear it: .ps-shell__content sets
   --ps-table-sticky-top (consumed by DataTable's sticky header) to the topbar height. A consumer that
   makes its OWN height-capped scrollport must reset --ps-table-sticky-top to 0 inside it — there the
   scrollport's own edge is the sticky line.
   =================================================================================================== */

.ps-shell[b-8udm1yrra4] {
    display: grid;
    grid-template-areas:
        "topbar"
        "main";
    grid-template-rows: auto 1fr;
    /* minmax(0, 1fr): wide legacy content must overflow INSIDE the main area, never widen the grid. */
    grid-template-columns: minmax(0, 1fr);
    min-block-size: 100dvh;
}

@media (min-width: 48rem) {
    .ps-shell[b-8udm1yrra4] {
        grid-template-areas:
            "nav topbar"
            "nav main";
        grid-template-columns: var(--ps-shell-rail-width) minmax(0, 1fr);
    }
}

@media (min-width: 64rem) {
    .ps-shell[b-8udm1yrra4] {
        grid-template-columns: var(--ps-shell-nav-width) minmax(0, 1fr);
    }

    :root[data-nav="collapsed"] .ps-shell[b-8udm1yrra4] {
        grid-template-columns: var(--ps-shell-rail-width) minmax(0, 1fr);
    }
}

/* --- Topbar: sticky over the page scroller. --- */
.ps-shell__topbar[b-8udm1yrra4] {
    grid-area: topbar;
    position: sticky;
    inset-block-start: 0;
    z-index: var(--ps-z-shell);
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    block-size: var(--ps-shell-topbar-height);
    padding-inline: var(--ps-space-3) var(--ps-space-4);
    background: var(--ps-color-surface-raised);
    border-block-end: 1px solid var(--ps-color-border);
}

.ps-shell__brand[b-8udm1yrra4] {
    font-size: var(--ps-text-lg);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text);
    text-decoration: none;
    white-space: nowrap;
}

.ps-shell__brand:hover[b-8udm1yrra4] {
    color: var(--ps-color-text);
}

/* The leading buttons are Ui/Button components (unscoped roots), hence ::deep off the scoped topbar. */
.ps-shell__topbar[b-8udm1yrra4]  .ps-shell__nav-toggle {
    display: none;
}

@media (min-width: 48rem) {
    .ps-shell__topbar[b-8udm1yrra4]  .ps-shell__menu-button {
        display: none;
    }
}

@media (min-width: 64rem) {
    .ps-shell__topbar[b-8udm1yrra4]  .ps-shell__nav-toggle--collapse {
        display: inline-flex;
    }

    :root[data-nav="collapsed"] .ps-shell__topbar[b-8udm1yrra4]  .ps-shell__nav-toggle--collapse {
        display: none;
    }

    :root[data-nav="collapsed"] .ps-shell__topbar[b-8udm1yrra4]  .ps-shell__nav-toggle--expand {
        display: inline-flex;
    }
}

/* --- Topbar actions (view-as, bell, identity, session, theme). --- */
.ps-shell__actions[b-8udm1yrra4] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    margin-inline-start: auto;
    min-inline-size: 0;
}

/* The view-as switcher's own styles (indicator, trigger, menu, options) moved to ViewAsSwitcher.razor.css with its
   markup — Blazor scoped CSS only reaches the elements a component itself renders. */

/* The identity name shrinks away on compact tiers (title keeps it), returning at ≥lg. */
.ps-shell__user[b-8udm1yrra4] {
    display: none;
}

@media (min-width: 64rem) {
    .ps-shell__user[b-8udm1yrra4] {
        display: inline-block;
        max-inline-size: 14rem;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        color: var(--ps-color-text-muted);
        font-size: var(--ps-text-sm);
    }
}

.ps-shell__session[b-8udm1yrra4] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-block-size: var(--ps-target-min);
    padding-inline: var(--ps-space-1);
    color: var(--ps-color-accent);
    font-size: var(--ps-text-sm);
    text-decoration: none;
    white-space: nowrap;
}

.ps-shell__session:hover[b-8udm1yrra4] {
    color: var(--ps-color-accent-emphasis);
    text-decoration: underline;
}

/* The topbar theme toggle yields to the drawer-footer instance below md (the phone topbar is full). */
.ps-shell__theme[b-8udm1yrra4] {
    display: none;
}

@media (min-width: 48rem) {
    .ps-shell__theme[b-8udm1yrra4] {
        display: inline-flex;
    }
}

/* --- The md+ navigation host: sticky full-height column beside the page scroller. --- */
.ps-shell__nav[b-8udm1yrra4] {
    display: none;
}

@media (min-width: 48rem) {
    .ps-shell__nav[b-8udm1yrra4] {
        grid-area: nav;
        position: sticky;
        inset-block-start: 0;
        block-size: 100dvh;
        display: flex;
        flex-direction: column;
        overflow-y: auto;
        background: var(--ps-color-surface-sunken);
        border-inline-end: 1px solid var(--ps-color-border);
    }
}

/* --- The phone nav drawer: a popover, so it rides the top layer (no z-index) with native light
   dismiss. Closed state keeps the UA's display:none; only :popover-open restyles. --- */
.ps-shell__drawer[b-8udm1yrra4] {
    position: fixed;
    inset: 0 auto 0 0;
    margin: 0;
    inline-size: min(var(--ps-shell-nav-width), 85vw);
    block-size: 100dvh;
    max-block-size: 100dvh;
    padding: 0;
    border: 0;
    border-inline-end: 1px solid var(--ps-color-border);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    box-shadow: var(--ps-shadow-3);
}

.ps-shell__drawer:popover-open[b-8udm1yrra4] {
    display: flex;
    flex-direction: column;
    /* Entry slide; instant under reduced motion (0ms tokens), instant exit by design. */
    transition: transform var(--ps-motion-medium) var(--ps-ease);
}

@starting-style {
    .ps-shell__drawer:popover-open[b-8udm1yrra4] {
        transform: translateX(-100%);
    }
}

.ps-shell__drawer[b-8udm1yrra4]::backdrop {
    background: var(--ps-color-backdrop);
}

/* A drawer left open across a resize into the rail/sidebar tiers must not linger in the top layer. */
@media (min-width: 48rem) {
    .ps-shell__drawer:popover-open[b-8udm1yrra4] {
        display: none;
    }
}

.ps-shell__drawer-header[b-8udm1yrra4] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ps-space-2);
    flex-shrink: 0;
    block-size: var(--ps-shell-topbar-height);
    padding-inline: var(--ps-space-4) var(--ps-space-2);
    border-block-end: 1px solid var(--ps-color-border);
}

.ps-shell__drawer-footer[b-8udm1yrra4] {
    display: flex;
    justify-content: flex-start;
    flex-shrink: 0;
    padding: var(--ps-space-3) var(--ps-space-4);
    border-block-start: 1px solid var(--ps-color-border);
}

/* --- Content. Padding stays compatible with the unmigrated Bootstrap pages rendering inside. --- */
.ps-shell__main[b-8udm1yrra4] {
    grid-area: main;
    min-inline-size: 0;
}

.ps-shell__content[b-8udm1yrra4] {
    /* In-page sticky headers clear the sticky topbar (see the scroll-container decision above). */
    --ps-table-sticky-top: var(--ps-shell-topbar-height);
    padding-block: var(--ps-space-4) var(--ps-space-8);
    padding-inline: var(--ps-space-4);
}

@media (min-width: 64rem) {
    .ps-shell__content[b-8udm1yrra4] {
        padding-inline: var(--ps-space-6);
    }
}

/* ===================================================================================================
   THE LARGE-SCREEN COMPOSITION RULE (the one deliberate system every wide tier follows):
   the shell composes ONE bounded content column and CENTERS it — surplus width always becomes
   SYMMETRIC gutters, never a single trailing void and never a stretched column. Inside that column
   each content archetype bounds ITSELF and centers any residual surplus the same way:
     · prose            → --ps-measure            (utilities .ps-measure)
     · forms            → --ps-form-max + rail    (FormLayout, justify-content centered at 4xl)
     · lists / tables   → --ps-table-max          (utilities .ps-content--list, centered at 4xl)
                          --ps-table-max-narrow   (.ps-content--list-narrow — the same rule for a
                          LOW-COLUMN table, whose few cells would otherwise share the whole surplus)
     · detail pages     → --ps-detail-max         (DetailLayout main+rail, centered at 4xl)
     · width-hungry grids (dashboard, topology, usage lanes) → fill the column, but their grids
       widen tracks up to a readable cap and CENTER partial rows (occupancy / topology canvases).
   So a 4K frame reads as a composed, deliberately-centered page at every archetype — not a 1080p
   layout pinned to the top-left corner of the wall.
   =================================================================================================== */

/* 3xl: cap and center — a wide viewport gains symmetric margin, never a stretched single column. */
@media (min-width: 120rem) {
    .ps-shell__content[b-8udm1yrra4] {
        max-inline-size: var(--ps-content-max);
        margin-inline: auto;
    }
}

/* 4xl wall-display tier — the cap relaxes so width-hungry content reaches true-4K width, bounded by
   min(calc(100% − 2×gutter), wide-cap): on a 4K screen the main-area term wins (content ≈ viewport −
   sidebar − both gutters); the 220rem ceiling only binds beyond ~4K. CENTERED (the composition rule
   above): the surplus splits into two equal gutters instead of consolidating into one right void.
   (100% here is the main grid cell = viewport − sidebar.) ≤1920 unchanged. */
@media (min-width: 160rem) {
    .ps-shell__content[b-8udm1yrra4] {
        max-inline-size: min(calc(100% - 2 * var(--ps-content-gutter-wide)), var(--ps-content-max-wide));
        margin-inline: auto;
    }
}

/* --- The dead-circuit overlay (markup owned by the shell). blazor.web.js flips it to display:block.
   A terminated circuit leaves the last render on screen, and a page of live-looking readings that stopped moving
   is worse than an error: it is a LIE the reader has no way to spot. So this is a full-viewport scrim, not the
   template's bottom strip — the content behind is dimmed and blurred (visibly not-current, still legible enough to
   recognise the page), and the only affordance is the reload. --- */
#blazor-error-ui[b-8udm1yrra4] {
    position: fixed;
    inset: 0;
    z-index: var(--ps-z-toast);
    display: none;
    box-sizing: border-box;
    /* The scrim is the invalidation. backdrop-filter is progressive: where it is unsupported the colour layer alone
       still dims the page, so the message never depends on it. */
    background: var(--ps-color-backdrop);
    backdrop-filter: blur(3px) grayscale(0.7);
    padding: var(--ps-space-6);
    overflow-y: auto;
}

/* blazor.web.js sets display:block inline, which wins over a stylesheet display — so the centring is done with
   margins on the panel rather than by a display mode this element is not allowed to keep. */
#blazor-error-ui .ps-circuit-notice[b-8udm1yrra4] {
    box-sizing: border-box;
    inline-size: min(100%, 34rem);
    margin: max(var(--ps-space-16), 12vh) auto var(--ps-space-8);
    padding: var(--ps-space-6);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    border: 1px solid var(--ps-color-danger);
    border-radius: var(--ps-radius-lg);
    box-shadow: var(--ps-shadow-3);
    text-align: center;
}

#blazor-error-ui .ps-circuit-notice__headline[b-8udm1yrra4] {
    margin: 0;
    font-size: var(--ps-text-lg);
    font-weight: 600;
    color: var(--ps-color-danger);
}

#blazor-error-ui .ps-circuit-notice__detail[b-8udm1yrra4] {
    margin: var(--ps-space-3) 0 0;
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
    text-wrap: pretty;
}

#blazor-error-ui .ps-circuit-notice__action[b-8udm1yrra4] {
    display: inline-block;
    margin-block-start: var(--ps-space-5);
    padding: var(--ps-space-2) var(--ps-space-6);
    min-block-size: var(--ps-target-min);
    background: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
    border-radius: var(--ps-radius-md);
    font-weight: 600;
    text-decoration: none;
}

#blazor-error-ui .ps-circuit-notice__action:hover[b-8udm1yrra4] {
    background: var(--ps-color-accent-emphasis);
}
/* /Components/Layout/NavMenu.razor.rz.scp.css */
/* Token-only nav styling for both shell hosts (the md+ sidebar/rail and the phone drawer). The viewport
   media queries below are the SANCTIONED shell-tier switches — their literals mirror --ps-breakpoint-md
   (48rem) and --ps-breakpoint-lg (64rem) from tokens.css, repeated because CSS forbids var() in @media
   conditions. Rail presentation (icons only, labels off) applies when this menu sits in the shell's
   .ps-shell__nav host AND either the viewport is in the md..lg band or the ≥lg sidebar is collapsed
   (html[data-nav="collapsed"], the ps-nav cookie); the drawer instance never rails. */

.ps-nav[b-emd1fi8k2n] {
    flex: 1 1 auto;
    min-block-size: 0;
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
    padding: var(--ps-space-2);
}

/* NavLink renders the anchor (no scope attribute), hence ::deep from the scoped nav root. */
.ps-nav[b-emd1fi8k2n]  .ps-nav__link {
    display: flex;
    align-items: center;
    gap: var(--ps-space-3);
    min-block-size: var(--ps-control-height);
    padding-block: var(--ps-space-1);
    padding-inline: var(--ps-space-3);
    border-radius: var(--ps-radius-md);
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
    text-decoration: none;
    white-space: nowrap;
}

.ps-nav[b-emd1fi8k2n]  .ps-nav__link:hover {
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
}

.ps-nav[b-emd1fi8k2n]  .ps-nav__link.active {
    background: var(--ps-color-accent-soft);
    color: var(--ps-color-accent);
}

/* The version stamp is the LAST child inside the nav landmark (it moved in to satisfy axe "region"); the auto
   top margin absorbs the column's free space, pinning it to the bottom exactly as the former sibling layout did. */
.ps-nav__footer[b-emd1fi8k2n] {
    flex-shrink: 0;
    margin-block-start: auto;
    padding-block: var(--ps-space-2);
    padding-inline: var(--ps-space-4);
}

/* text-muted (≥4.5:1 on every surface), not text-faint (the ≥3:1 placeholder tier), so the version string
   meets WCAG AA normal-text contrast in light mode (axe "color-contrast"); dark stays AA either way. */
.ps-nav__version[b-emd1fi8k2n] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    white-space: nowrap;
}

/* --- Rail presentation: md..lg always; display:none (not visual hiding) so the labels leave the
   accessibility tree cleanly — the links' aria-label/title carry the names. --- */
@media (min-width: 48rem) and (max-width: 63.9375rem) {
    .ps-shell__nav .ps-nav[b-emd1fi8k2n]  .ps-nav__link {
        justify-content: center;
        padding-inline: 0;
    }

    .ps-shell__nav .ps-nav__label[b-emd1fi8k2n],
    .ps-shell__nav .ps-nav__version[b-emd1fi8k2n] {
        display: none;
    }
}

/* --- Rail presentation: ≥lg while the sidebar is collapsed (same rules; CSS cannot OR a media query
   with a selector condition). --- */
@media (min-width: 64rem) {
    :root[data-nav="collapsed"] .ps-shell__nav .ps-nav[b-emd1fi8k2n]  .ps-nav__link {
        justify-content: center;
        padding-inline: 0;
    }

    :root[data-nav="collapsed"] .ps-shell__nav .ps-nav__label[b-emd1fi8k2n],
    :root[data-nav="collapsed"] .ps-shell__nav .ps-nav__version[b-emd1fi8k2n] {
        display: none;
    }
}
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
/* The framework's reconnect dialog, restyled onto the design system.
   Two things changed beyond the palette. First it is THEME-AWARE: the template's hard-coded white panel and blue
   button rendered a bright card over a dark app. Second, and the point of the batch, its backdrop now visibly
   INVALIDATES the page behind it — a disconnected page keeps its last frame, and a cluster read that stopped moving
   looks exactly like one that is steady, so a drop that is not visible is a drop that gets misread as live state. */

.components-reconnect-first-attempt-visible[b-kbqu3eswti],
.components-reconnect-repeated-attempt-visible[b-kbqu3eswti],
.components-reconnect-failed-visible[b-kbqu3eswti],
.components-pause-visible[b-kbqu3eswti],
.components-resume-failed-visible[b-kbqu3eswti],
.components-rejoining-animation[b-kbqu3eswti] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-retrying[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-failed[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-kbqu3eswti] {
    display: block;
}


#components-reconnect-modal[b-kbqu3eswti] {
    box-sizing: border-box;
    inline-size: min(100% - 2 * var(--ps-space-4), 24rem);
    margin: 20vh auto;
    padding: var(--ps-space-6);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-lg);
    box-shadow: var(--ps-shadow-3);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-kbqu3eswti 0.5s both;
}

    #components-reconnect-modal[open][b-kbqu3eswti] {
        animation: components-reconnect-modal-slideUp-b-kbqu3eswti 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-kbqu3eswti 0.5s ease-in-out 0.3s;
        animation-fill-mode: both;
    }

    /* showModal() moves focus to the dialog itself when no control inside it is focusable — the rejoining state
       has none — and the shell's focus ring then draws a thick accent border around the whole panel, which reads
       as a control rather than as a message. The ring is suppressed on the CONTAINER only: the retry / resume
       buttons keep theirs, and they are the only things in here anyone can operate. */
    #components-reconnect-modal:focus[b-kbqu3eswti],
    #components-reconnect-modal:focus-visible[b-kbqu3eswti] {
        outline: none;
    }

/* The invalidation: the same scrim treatment the dead-circuit overlay uses, so a paused page and a dead one read as
   the same KIND of thing (not current) and differ only in what the panel says about it. The blur is progressive —
   where it is unsupported the colour layer alone still dims. */
#components-reconnect-modal[b-kbqu3eswti]::backdrop {
    background: var(--ps-color-backdrop);
    backdrop-filter: blur(3px) grayscale(0.7);
    animation: components-reconnect-modal-fadeInOpacity-b-kbqu3eswti 0.5s ease-in-out;
    opacity: 1;
}

/* The failed states are the ones a reader may sit in indefinitely, so the panel itself takes the danger edge —
   "this is not coming back on its own" — rather than looking like the transient rejoin. */
#components-reconnect-modal.components-reconnect-failed[b-kbqu3eswti],
#components-reconnect-modal.components-reconnect-resume-failed[b-kbqu3eswti] {
    border-color: var(--ps-color-danger);
}

@keyframes components-reconnect-modal-slideUp-b-kbqu3eswti {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-kbqu3eswti {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-kbqu3eswti {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-kbqu3eswti] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ps-space-4);
}

#components-reconnect-modal p[b-kbqu3eswti] {
    margin: 0;
    text-align: center;
    text-wrap: pretty;
}

/* The one line that is about the PAGE rather than the connection: quieter than the state message above it, and
   present in every state, so no reader has to infer that a frozen page is frozen. */
#components-reconnect-modal .components-reconnect-frozen[b-kbqu3eswti] {
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

#components-reconnect-modal button[b-kbqu3eswti] {
    min-block-size: var(--ps-target-min);
    padding: var(--ps-space-2) var(--ps-space-6);
    background: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
    border: 0;
    border-radius: var(--ps-radius-md);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

    #components-reconnect-modal button:hover[b-kbqu3eswti] {
        background: var(--ps-color-accent-emphasis);
    }

    #components-reconnect-modal button:active[b-kbqu3eswti] {
        background: var(--ps-color-accent);
    }

.components-rejoining-animation[b-kbqu3eswti] {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div[b-kbqu3eswti] {
        position: absolute;
        border: 3px solid var(--ps-color-accent);
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation-b-kbqu3eswti 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2)[b-kbqu3eswti] {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation-b-kbqu3eswti {
    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/Layout/ThemeToggle.razor.rz.scp.css */
/* The selected state keys off html[data-theme] — the one source the server stamps and site.js flips — so
   the visual is correct with or without a circuit. Targets stay >= the --ps-target-min WCAG floor. */
.ps-theme-toggle[b-rim9kf2tdm] {
    display: inline-flex;
    gap: var(--ps-space-1);
}

.ps-theme-toggle button[b-rim9kf2tdm] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--ps-target-min);
    min-block-size: var(--ps-target-min);
    padding: var(--ps-space-1);
    border: 0;
    border-radius: var(--ps-radius-sm);
    background: transparent;
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.ps-theme-toggle button:hover[b-rim9kf2tdm] {
    background: var(--ps-color-surface-sunken);
    color: var(--ps-color-text);
}

:root[data-theme="light"] .ps-theme-toggle [data-set-theme="light"][b-rim9kf2tdm],
:root[data-theme="auto"] .ps-theme-toggle [data-set-theme="auto"][b-rim9kf2tdm],
:root[data-theme="dark"] .ps-theme-toggle [data-set-theme="dark"][b-rim9kf2tdm] {
    background: var(--ps-color-accent-soft);
    color: var(--ps-color-accent);
}
/* /Components/Layout/ViewAsSwitcher.razor.rz.scp.css */
/* The administrator preview-lens switcher (its own interactive island). These rules moved here from
   MainLayout.razor.css with the markup: Blazor scoped CSS only styles the elements a component itself renders, so
   they must live with the switcher to keep applying. Tokens only. */

.ps-shell__viewas-indicator[b-1e11ti1wtd] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    padding-block: var(--ps-space-1);
    padding-inline: var(--ps-space-2);
    border: 1px solid var(--ps-color-warning);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-warning-soft);
    color: var(--ps-color-warning);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-semibold);
    white-space: nowrap;
}

/* The "View as" trigger keeps the popover anchored to itself (anchor-name set inline, mirroring the table
   popovers). The wrapper carries no layout of its own — the popover rides the top layer. */
.ps-shell__viewas[b-1e11ti1wtd] {
    display: inline-flex;
}

/* The view-as menu: a native top-layer popover (zero JS), opening UNDER its trigger, inline-end aligned, like
   the data-table chooser. Anchor positioning is Baseline 2026; the @position-try flips it below-start if the
   default placement would overflow. */
.ps-shell__viewas-menu[b-1e11ti1wtd] {
    min-inline-size: 14rem;
    margin: 0;
    padding: var(--ps-space-1);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-raised);
    box-shadow: var(--ps-shadow-2);
    inset: auto;
    position-area: block-end span-inline-start;
    position-try-fallbacks: --ps-viewas-menu-above;
}

@position-try --ps-viewas-menu-above {
    position-area: block-start span-inline-start;
}

.ps-shell__viewas-option[b-1e11ti1wtd] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    inline-size: 100%;
    min-block-size: var(--ps-target-min);
    padding-block: var(--ps-space-1);
    padding-inline: var(--ps-space-2);
    border-radius: var(--ps-radius-sm);
    color: var(--ps-color-text);
    font-size: var(--ps-text-sm);
    text-align: start;
    cursor: pointer;
}

.ps-shell__viewas-option:hover[b-1e11ti1wtd],
.ps-shell__viewas-option:focus-visible[b-1e11ti1wtd] {
    background: var(--ps-color-surface-sunken);
}

/* Compact tiers shrink the view-as control to its icon (title/aria-label keep the name); the label/indicator
   text returns at ≥lg. */
.ps-shell__viewas-label[b-1e11ti1wtd],
.ps-shell__viewas-text[b-1e11ti1wtd] {
    display: none;
}

@media (min-width: 64rem) {
    .ps-shell__viewas-label[b-1e11ti1wtd],
    .ps-shell__viewas-text[b-1e11ti1wtd] {
        display: inline;
    }
}
/* /Components/LoginRequired.razor.rz.scp.css */
/* Sits the sign-in prompt a little down from the top of an otherwise-empty login-gated page.
   EmptyState owns the centering and padding. */
.login-required[b-qptnkrlxvk] {
    margin-block-start: var(--ps-space-8);
}
/* /Components/MemberYieldCell.razor.rz.scp.css */
/* The whole-job requeue tier is a destructive escalation, so its button reads in the danger color. Owned here
   (a token-based rule) rather than borrowed from a page's scoped stylesheet, so every surface that renders this
   shared component gets the colour — a page's ::deep rule would not reach the component's own markup. */
button.member-yield--danger[b-crjyxaprgy] {
    color: var(--ps-color-danger);
}
/* /Components/NotificationBell.razor.rz.scp.css */
/* The MainLayout notification bell + its dropdown panel, on the design tokens. The shell's topbar flex gap
   owns the spacing around the bell; this sheet owns the unread-count overlay, the absolutely-positioned
   dropdown panel, and the per-item layout. The dropdown Card is a scoped child, so its overrides reach it
   via ::deep (CSS isolation does not stamp a component's own root from outside). */

.notification-bell[b-1wwkqqm47n] {
    position: relative;
    display: inline-flex;
}

/* The bell glyph + the unread-count overlay share one positioning context inside the ghost button. */
.notification-bell__icon[b-1wwkqqm47n] {
    position: relative;
    display: inline-flex;
}

/* The unread pill: a small danger badge anchored to the bell's top-right corner. */
.notification-bell__count[b-1wwkqqm47n] {
    position: absolute;
    inset-block-start: -0.4rem;
    inset-inline-start: 100%;
    transform: translateX(-55%);
    min-inline-size: 1.1rem;
    padding-block: 0;
    padding-inline: 0.3rem;
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-danger);
    color: var(--ps-color-on-danger);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-semibold);
    line-height: 1.1rem;
    text-align: center;
}

/* The dropdown panel: a native top-layer popover (light-dismiss on outside click / Esc, zero JS), anchored under
   the bell and extending toward the inline-start so it never overflows the topbar's inline-end edge — the same
   anchor-positioning pattern as the view-as menu. inset:auto + margin:0 override the UA popover centering; the
   @position-try flips it above when the default placement would overflow the viewport block-end.
   The panel is a child <Card> whose root does NOT carry this component's isolation scope (like .ps-card__body
   below), so these rules MUST reach it through ::deep off .notification-bell — a plain `.notification-dropdown`
   selector gets a `[b-*]` stamp the Card root lacks, so it never matches and the popover falls back to the UA
   viewport-centering. (The Card stays a DOM child of .notification-bell even in the top layer, so ::deep holds.) */
.notification-bell[b-1wwkqqm47n]  .notification-dropdown {
    inset: auto;
    margin: 0;
    position-anchor: --ps-notification-anchor;
    position-area: block-end span-inline-start;
    position-try-fallbacks: --ps-notification-panel-above;
    inline-size: 24rem;
    max-inline-size: 90vw;
    box-shadow: var(--ps-shadow-3);
}

@position-try --ps-notification-panel-above {
    position-area: block-start span-inline-start;
}

.notification-dropdown__title[b-1wwkqqm47n] {
    font-weight: var(--ps-weight-semibold);
}

.notification-dropdown__actions[b-1wwkqqm47n] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
}

/* Card body padding would crowd the list rows, so the dropdown body resets it and the rows carry their own.
   Reached from the bell-scoped wrapper (the .notification-dropdown Card root is a child-component root and
   does not carry this component's isolation scope, so ::deep must hang off .notification-bell). */
.notification-bell[b-1wwkqqm47n]  .ps-card__body {
    padding: 0;
}

.notification-dropdown__body[b-1wwkqqm47n] {
    max-block-size: 28rem;
    overflow-y: auto;
}

.notification-dropdown__empty[b-1wwkqqm47n] {
    margin: 0;
    padding: var(--ps-space-3) var(--ps-space-4);
}

.notification-item[b-1wwkqqm47n] {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--ps-space-2);
    padding: var(--ps-space-3) var(--ps-space-4);
    border-block-start: 1px solid var(--ps-color-border);
}

.notification-item:first-child[b-1wwkqqm47n] {
    border-block-start: 0;
}

.notification-item__text[b-1wwkqqm47n] {
    min-inline-size: 0;
}

.notification-item__head[b-1wwkqqm47n] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
}

.notification-item__body[b-1wwkqqm47n] {
    font-size: var(--ps-text-sm);
}

.notification-item__time[b-1wwkqqm47n] {
    font-size: var(--ps-text-sm);
}

.notification-item__ack[b-1wwkqqm47n] {
    flex: none;
}

/* Unread emphasis: a tinted fill + an accent rule (bell dropdown rows; the full inbox uses its own class). */
.notification-unread[b-1wwkqqm47n] {
    background: var(--ps-color-accent-soft);
    border-inline-start: 3px solid var(--ps-color-accent);
}
/* /Components/OutboundTunnels/OutboundTunnelFormEditor.razor.rz.scp.css */
/* The shared outbound-tunnel register/edit FORM BODY hosted in the create/edit page's FormLayout (the actions live in
   the page's FormLayout action cluster). FormSection/FormField + .ps-control carry the field skin and the RAM grid;
   this sheet styles the editor's own affordances: the live name echo, the "what a job sees" preview, and the
   phantom-row allowlist editor. var(--ps-*) only. */

.outbound-tunnel-form__note[b-kwq31an903] {
    /* When this note sits directly in a FormSection's multi-column RAM grid (the register/edit identity sections),
       span ALL columns — otherwise it lands in a single ~field-min cell and wraps into a narrow ribbon at wide widths.
       Harmless inside a Card (a non-grid container ignores grid-column). Capped to a readable measure. */
    grid-column: 1 / -1;
    margin-block: 0 var(--ps-space-3);
    max-inline-size: var(--ps-measure);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* The live "jobs reference it as `name`" echo under the Name field — confirms the verbatim authored handle. */
.outbound-tunnel-form__echo[b-kwq31an903] {
    margin-block: var(--ps-space-1) 0;
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* --- Live "what a job sees" preview --- */
/* The allowlist roster chips ("trainer (any role)" …) in the preview. */
.outbound-tunnel-form__roster[b-kwq31an903] {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--ps-space-1);
}

/* The Warning-toned preview lines (read-write without prompt; deny-all). */
.outbound-tunnel-form__warn[b-kwq31an903] {
    color: var(--ps-color-warning);
}

/* The one plain-language recap sentence under the preview list. */
.outbound-tunnel-form__sentence[b-kwq31an903] {
    margin-block: var(--ps-space-3) 0;
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text);
}

/* --- Allowlist editor (phantom-row, self-cleaning) --- */
.outbound-tunnel-form__allowlist[b-kwq31an903] {
    margin-block: var(--ps-space-2) var(--ps-space-3);
}

/* One allowlist row: the kind toggle hugs its content, the job-reference field grows, the role field is narrower,
   and the remove button hugs its trailing edge — all wrapping in a narrow pane. */
.outbound-tunnel-form__allowlist-row[b-kwq31an903] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
}

/* The trailing phantom row reads quieter until it is completed (then it commits to a real row). */
.outbound-tunnel-form__allowlist-row--phantom[b-kwq31an903] {
    opacity: 0.85;
}

.outbound-tunnel-form__allowlist-ref[b-kwq31an903] {
    flex: 1 1 14rem;
    min-inline-size: 0;
}

.outbound-tunnel-form__allowlist-ref input[b-kwq31an903] {
    inline-size: 100%;
}

.outbound-tunnel-form__allowlist-error[b-kwq31an903] {
    margin-block: var(--ps-space-1) 0;
    font-size: var(--ps-text-xs);
    color: var(--ps-color-danger);
}

.outbound-tunnel-form__allowlist-role[b-kwq31an903] {
    flex: 1 1 8rem;
    min-inline-size: 0;
}

.outbound-tunnel-form__allowlist-remove[b-kwq31an903] {
    flex: 0 0 auto;
}

/* The field wrapper positions the inline clear (×) over the input while focused (matches the env-var editor). */
.outbound-tunnel-form__allowlist-field[b-kwq31an903] {
    position: relative;
    min-inline-size: 0;
}

.outbound-tunnel-form__allowlist-field input[b-kwq31an903] {
    inline-size: 100%;
}

.outbound-tunnel-form__allowlist-input--clearable[b-kwq31an903] {
    padding-inline-end: 2.25rem;
}

.outbound-tunnel-form__allowlist-clear[b-kwq31an903] {
    position: absolute;
    inset-inline-end: var(--ps-space-2);
    inset-block: 0;
    display: inline-flex;
    align-items: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.outbound-tunnel-form__allowlist-clear:hover[b-kwq31an903] {
    color: var(--ps-color-text);
}
/* /Components/Output/OutputPane.razor.rz.scp.css */
/* The shared chrome for every scrolling output/log surface: the stack spacing, the toolbar filter/display clusters,
   the trimmed banner, and the bounded sunken scroll viewport. Per-LINE styling (stream color / ANSI / log level) lives
   in each consumer's own scoped stylesheet, because the consumer authors the per-line markup (CSS isolation scopes it
   to the consumer, not here). */
.output-pane[b-g4tf0sqqzv] {
    gap: var(--ps-space-2);
}

.output-pane__filters[b-g4tf0sqqzv],
.output-pane__display[b-g4tf0sqqzv] {
    align-items: center;
    gap: var(--ps-space-2);
    flex-wrap: wrap;
}

.output-pane__toggle[b-g4tf0sqqzv] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.output-pane__trimmed[b-g4tf0sqqzv] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    margin: 0;
}

/* The scrolling viewport: monospace, sunken surface, bounded height so it never pushes the page; the auto-scroll state
   machine pins it to the bottom while following. */
.output-pane__lines[b-g4tf0sqqzv] {
    max-block-size: 31rem;
    overflow: auto;
    margin: 0;
    padding: var(--ps-space-3) var(--ps-space-4);
    font-family: var(--ps-font-mono);
    font-size: var(--ps-text-xs);
    line-height: 1.5;
    background: var(--ps-color-surface-sunken);
    border-radius: var(--ps-radius-md);
    white-space: pre-wrap;
    word-break: break-word;
}

.detail-fine[b-g4tf0sqqzv] {
    font-size: var(--ps-text-xs);
}

/* The "+N new lines" catch-up chip — shown below the scroll viewport while auto-scroll is off and new output is held
   back (the selection-safe freeze). A sibling of the <pre>, never inside it, so its updates never diff the selection
   container. */
.output-pane__catch-up[b-g4tf0sqqzv] {
    align-self: center;
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    padding: var(--ps-space-1) var(--ps-space-3);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-on-accent, #fff);
    background: var(--ps-color-accent);
    border: 1px solid var(--ps-color-accent-emphasis, var(--ps-color-accent));
    border-radius: var(--ps-radius-md);
    cursor: pointer;
}

.output-pane__catch-up:hover[b-g4tf0sqqzv] {
    background: var(--ps-color-accent-emphasis, var(--ps-color-accent));
}
/* /Components/Output/OutputSection.razor.rz.scp.css */
/* The per-line OUTPUT styling — the shared chrome (stack, toolbar clusters, trimmed banner, scroll viewport,
   auto-scroll) lives in OutputPane.razor.css. CSS isolation scopes these to THIS component, which authors the per-line
   markup (the by-stream / by-role filter <select> and the ANSI line spans), so they belong here, not in the shell. */
/* Layout-only override on the canonical .ps-control skin: the filter dropdowns size to their content rather than
   filling the toolbar (the skin supplies border / radius / padding / min-height / the ≥16px iOS-no-zoom font). */
.output-section__select[b-1sf81rfeen] {
    inline-size: auto;
}

.output-section__toggle[b-1sf81rfeen] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.output-section__line[b-1sf81rfeen] {
    display: block;
}

/* The always-on per-stream color (B.9) — stdout uses the body text color, stderr the danger color. Applied to the
   line so an un-ANSI-colored run inherits it; an ANSI foreground span overrides it. */
.output-section__line--stdout[b-1sf81rfeen] {
    color: var(--ps-color-text);
}

.output-section__line--stderr[b-1sf81rfeen] {
    color: var(--ps-color-danger);
}

.output-section__prefix[b-1sf81rfeen] {
    /* Muted, not faint, so the line prefixes clear AA contrast (4.5:1) on the sunken scroll viewport in both schemes. */
    color: var(--ps-color-text-muted);
    margin-inline-end: var(--ps-space-2);
    user-select: none;
}

.output-section__prefix--member[b-1sf81rfeen] {
    color: var(--ps-color-accent);
}

/* ---- ANSI segment styling -----------------------------------------------------------------------------
   Exactly the bounded set of classes AnsiLineParser/OutputSection can emit — no class is ever built from
   attacker-controlled bytes, so this is the WHOLE palette a line can paint itself in. Colors resolve through
   the theme tokens so light/dark both read well; the standard band reuses the design-system status hues and
   the bright band leans on the accent/emphasis tokens. */
.ansi-bold[b-1sf81rfeen] {
    font-weight: var(--ps-weight-bold);
}

.ansi-italic[b-1sf81rfeen] {
    font-style: italic;
}

.ansi-underline[b-1sf81rfeen] {
    text-decoration: underline;
}

.ansi-fg-black[b-1sf81rfeen] { color: var(--ps-color-text); }
.ansi-fg-red[b-1sf81rfeen] { color: var(--ps-color-danger); }
.ansi-fg-green[b-1sf81rfeen] { color: var(--ps-color-success); }
.ansi-fg-yellow[b-1sf81rfeen] { color: var(--ps-color-warning); }
.ansi-fg-blue[b-1sf81rfeen] { color: var(--ps-color-accent); }
.ansi-fg-magenta[b-1sf81rfeen] { color: light-dark(#a3329b, #e07ad6); }
.ansi-fg-cyan[b-1sf81rfeen] { color: var(--ps-color-info); }
.ansi-fg-white[b-1sf81rfeen] { color: var(--ps-color-text-muted); }

.ansi-fg-bright-black[b-1sf81rfeen] { color: var(--ps-color-text-faint); }
.ansi-fg-bright-red[b-1sf81rfeen] { color: light-dark(#e04a52, #ff9aa0); }
.ansi-fg-bright-green[b-1sf81rfeen] { color: light-dark(#1f9143, #74d889); }
.ansi-fg-bright-yellow[b-1sf81rfeen] { color: light-dark(#a8740a, #e8be5e); }
.ansi-fg-bright-blue[b-1sf81rfeen] { color: var(--ps-color-accent-emphasis); }
.ansi-fg-bright-magenta[b-1sf81rfeen] { color: light-dark(#bc44b4, #ec97e3); }
.ansi-fg-bright-cyan[b-1sf81rfeen] { color: var(--ps-color-accent-emphasis); }
.ansi-fg-bright-white[b-1sf81rfeen] { color: var(--ps-color-text); }

.ansi-bg-black[b-1sf81rfeen] { background-color: var(--ps-color-surface-sunken); }
.ansi-bg-red[b-1sf81rfeen] { background-color: var(--ps-color-danger-soft); }
.ansi-bg-green[b-1sf81rfeen] { background-color: var(--ps-color-success-soft); }
.ansi-bg-yellow[b-1sf81rfeen] { background-color: var(--ps-color-warning-soft); }
.ansi-bg-blue[b-1sf81rfeen] { background-color: var(--ps-color-accent-soft); }
.ansi-bg-magenta[b-1sf81rfeen] { background-color: light-dark(#f6e0f4, #2c1530); }
.ansi-bg-cyan[b-1sf81rfeen] { background-color: var(--ps-color-info-soft); }
.ansi-bg-white[b-1sf81rfeen] { background-color: var(--ps-color-surface-raised); }

.ansi-bg-bright-black[b-1sf81rfeen] { background-color: var(--ps-color-surface); }
.ansi-bg-bright-red[b-1sf81rfeen] { background-color: var(--ps-color-danger-soft); }
.ansi-bg-bright-green[b-1sf81rfeen] { background-color: var(--ps-color-success-soft); }
.ansi-bg-bright-yellow[b-1sf81rfeen] { background-color: var(--ps-color-warning-soft); }
.ansi-bg-bright-blue[b-1sf81rfeen] { background-color: var(--ps-color-accent-soft); }
.ansi-bg-bright-magenta[b-1sf81rfeen] { background-color: light-dark(#f6e0f4, #2c1530); }
.ansi-bg-bright-cyan[b-1sf81rfeen] { background-color: var(--ps-color-info-soft); }
.ansi-bg-bright-white[b-1sf81rfeen] { background-color: var(--ps-color-surface-raised); }
/* /Components/Pages/ApiKeyCreated.razor.rz.scp.css */
/* The reveal-once result page is a focused, readable column (not a full-width form). */
.api-key-created[b-qbtgucohsn] {
    max-inline-size: 48rem;
}

/* The secret + its copy button: prominent, copyable, wrap-safe at any pane width. */
.api-key-created__secret[b-qbtgucohsn] {
    display: flex;
    align-items: flex-start;
    gap: var(--ps-space-2);
    margin-block-start: var(--ps-space-3);
}

.api-key-created__value[b-qbtgucohsn] {
    flex: 1 1 auto;
    min-inline-size: 0;
    padding: var(--ps-space-2) var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    font-size: var(--ps-text-lg);
    overflow-wrap: anywhere;
}

.api-key-created__actions[b-qbtgucohsn] {
    display: flex;
    justify-content: flex-end;
}

.api-keys-grants[b-qbtgucohsn] {
    --ps-cluster-gap: var(--ps-space-1);
}

/* Badges are nowrap by default; a long parameterized grant (tunnels:use:<long-name>) must wrap in the readable
   column instead of overflowing it. */
.api-keys-grants[b-qbtgucohsn]  .ps-badge {
    white-space: normal;
    overflow-wrap: anywhere;
}
/* /Components/Pages/ApiKeyNew.razor.rz.scp.css */
/* The hierarchical grant tree shown under the Custom preset (the closed PermissionNodes catalogue + live tunnels:use). */
.api-keys-grant-groups[b-1dimyphppv] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(12rem, 100%), 1fr));
    gap: var(--ps-space-4);
}

.api-keys-grant-group__title[b-1dimyphppv] {
    margin-block-end: var(--ps-space-1);
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-semibold);
}

.api-keys-grant[b-1dimyphppv] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    min-block-size: var(--ps-target-min);
    font-size: var(--ps-text-sm);
}

.api-keys-grant-note[b-1dimyphppv] {
    font-size: var(--ps-text-xs);
}

/* The "what this key can do" grant chips in the rail. */
.api-keys-grants[b-1dimyphppv] {
    --ps-cluster-gap: var(--ps-space-1);
}

/* Badges are nowrap by default; a long parameterized grant (tunnels:use:<long-name>) must wrap inside the
   narrow rail instead of overflowing it. */
.api-keys-grants[b-1dimyphppv]  .ps-badge {
    white-space: normal;
    overflow-wrap: anywhere;
}
/* /Components/Pages/ApiKeys.razor.rz.scp.css */
.api-keys-toggle[b-2lflorl0zg] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
}

.api-keys-label[b-2lflorl0zg] {
    font-weight: var(--ps-weight-semibold);
    /* Pathological unbroken labels wrap instead of stretching the pane. */
    overflow-wrap: anywhere;
}

.api-keys-grants[b-2lflorl0zg] {
    --ps-cluster-gap: var(--ps-space-1);
}

.api-keys-actions[b-2lflorl0zg] {
    display: inline-flex;
    gap: var(--ps-space-1);
}

/* The destructive row action (delete) reads danger, matching the tunnels pages' icon-Ghost delete. */
.page-api-keys[b-2lflorl0zg]  .api-keys-actions button.api-keys-action--danger {
    color: var(--ps-color-danger);
}

/* Revoked keys read muted across the row (badges keep their own colors, as the legacy row did). */
.page-api-keys[b-2lflorl0zg]  tr.api-keys-row--revoked {
    color: var(--ps-color-text-muted);
}
/* /Components/Pages/ComputeDeviceDetail.razor.rz.scp.css */
/* The column-B live-usage gauges: the two realtime metrics side by side, wrapping on a narrow card. */
.device-usage-gauges[b-jc578chha4] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-4);
    justify-content: center;
}

.device-usage-gauge[b-jc578chha4]  .ps-gauge {
    --ps-gauge-size: 9rem;
}

/* A host device's finest sampled layers (per-logical-CPU heat strip, per-NUMA memory meters): a titled block under
   the gauges in the same card, one block per layer. */
.device-host-detail[b-jc578chha4] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-3);
    margin-block-start: var(--ps-space-4);
}

.device-host-detail__title[b-jc578chha4] {
    margin: 0;
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text);
}

/* The reclaim verdict line above the live gauges: a small breathing gap so it reads as its own row, not crowding the
   gauges. The indicator owns its own internal layout/colors (DeviceReclaimIndicator.razor.css). */
.device-reclaim-line[b-jc578chha4] {
    margin-block-end: var(--ps-space-3);
}
/* /Components/Pages/EditJobDeclaration.razor.rz.scp.css */
/* The declaration editor. It is the create form's layout with a shorter card list, so almost everything it needs is
   already in the shared form primitives; what lives here is the page's own rejection surface and save report. */

/* The rejection banner is a focus target (a failed save scrolls to it and focuses it), so it must not draw a focus
   ring of its own — it is a scroll anchor, not a control. It is also EMPTY on a page with nothing to report, and an
   empty flow element must not open a gap in the card stack. */
.edit-declaration-errors:focus[b-cpc71vb2dr] {
    outline: none;
}

.edit-declaration-errors:empty[b-cpc71vb2dr] {
    display: none;
}

/* The rejected-save messages and the tunnel warnings read as lists rather than as one run-on paragraph: a save can
   fail several rules at once, and a rename can strand several tunnels. */
.edit-declaration-object-errors[b-cpc71vb2dr],
.edit-declaration-warnings[b-cpc71vb2dr] {
    display: grid;
    gap: var(--ps-space-1);
}

/* The rail's difference sentences, on the same terms as the history panel's: a list of short lines rather than one
   run-on paragraph. Both it and the empty state that replaces it stand OFF the two generation facts above by the
   same step, so the card does not read as a three-row list one of whose rows is a sentence. */
.edit-declaration-pending[b-cpc71vb2dr],
.edit-declaration-pending-empty[b-cpc71vb2dr] {
    margin-block-start: var(--ps-space-3);
    font-size: var(--ps-text-sm);
}

.edit-declaration-pending[b-cpc71vb2dr] {
    display: grid;
    gap: var(--ps-space-1);
}

/* The one quiet line beside a disabled Save, on the same terms the create form states its own. */
.edit-declaration-submit-hint[b-cpc71vb2dr] {
    font-size: var(--ps-text-sm);
}
/* /Components/Pages/InboundTunnels.razor.rz.scp.css */
.tunnels-name[b-b7bq3yh36t] {
    font-weight: var(--ps-weight-semibold);
    /* Pathological unbroken names wrap instead of stretching the pane. */
    overflow-wrap: anywhere;
}

.tunnels-status-note[b-b7bq3yh36t] {
    margin-inline-start: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* Long unbroken URLs/tokens wrap instead of overflowing the pane (cards at phone widths). */
.tunnels-url[b-b7bq3yh36t],
.tunnels-upstream[b-b7bq3yh36t],
.tunnels-token__value[b-b7bq3yh36t] {
    overflow-wrap: anywhere;
}

/* …but anywhere-wrap erases the Proxy URL column's min-content floor, and the auto table layout then
   syllable-wrapped the one long-content column while short columns (Bearer token, Created) idled. A
   readable floor keeps the URL a URL. */
.tunnels-url[b-b7bq3yh36t] {
    display: inline-block;
    min-inline-size: 14rem;
}

.tunnels-upstream[b-b7bq3yh36t] {
    font-size: var(--ps-text-xs);
}

.tunnels-token[b-b7bq3yh36t] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
}

.tunnels-actions[b-b7bq3yh36t] {
    display: inline-flex;
    gap: var(--ps-space-1);
}

/* Ghost buttons read accent; the destructive one reads danger (the legacy outline-danger tier). */
.tunnels-actions[b-b7bq3yh36t]  button.tunnels-action--danger {
    color: var(--ps-color-danger);
}
/* /Components/Pages/JobDetail.razor.rz.scp.css */
/* --- Decision-trace fine print inside the pending alert. --- */
.trace-meta[b-tzy02l35xq] {
    margin-block-start: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

.trace-rejections[b-tzy02l35xq] {
    margin: var(--ps-space-1) 0 0;
    padding-inline-start: var(--ps-space-5);
    font-size: var(--ps-text-xs);
}

/* --- Inline priority/remark editors inside the fact sheet. --- */
.inline-edit[b-tzy02l35xq] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
}

.inline-edit[b-tzy02l35xq]  .clearable-input-wrapper {
    flex: 1;
    min-inline-size: 0;
}

.inline-edit[b-tzy02l35xq]  button.clearable-input-button {
    top: 0;
    height: 100%;
}

.priority-input[b-tzy02l35xq] {
    max-inline-size: 8rem;
}

.inline-edit-trigger[b-tzy02l35xq] {
    margin-inline-start: var(--ps-space-2);
}

/* --- Member spec blocks. --- */
.member-block[b-tzy02l35xq] {
    border-inline-start: 3px solid var(--ps-color-border);
    padding-inline-start: var(--ps-space-3);
    margin-block-end: var(--ps-space-3);
}

.member-block__head[b-tzy02l35xq] {
    margin-block-end: var(--ps-space-1);
}

.member-block__role[b-tzy02l35xq] {
    font-weight: var(--ps-weight-semibold);
}

.member-block__override[b-tzy02l35xq] {
    margin-block-start: var(--ps-space-1);
}

.member-block__selectors[b-tzy02l35xq] {
    margin-block-end: var(--ps-space-1);
    gap: var(--ps-space-1);
    font-size: var(--ps-text-sm);
}

.member-block__selector-label[b-tzy02l35xq] {
    color: var(--ps-color-text-muted);
}

.member-block__node-chip[b-tzy02l35xq] {
    display: inline-flex;
    align-items: center;
    padding-inline: var(--ps-space-2);
    padding-block: 1px;
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-surface-sunken);
    font-size: var(--ps-text-xs);
    line-height: var(--ps-leading-tight);
}

/* --- Allocation status strip. --- */
.detail-divider[b-tzy02l35xq] {
    border: none;
    border-block-start: 1px solid var(--ps-color-border);
    margin-block: var(--ps-space-4);
}

.allocation-ribbon[b-tzy02l35xq] {
    margin-block: var(--ps-space-2) var(--ps-space-3);
}

/* The strip's own gap is DELIBERATELY wider than a milestone's internal one: each milestone now carries up to three
   figures of its own (the launch and the warm-up became separate spans once the lifecycle log recorded the hand-over
   between them), and at the old one-step difference the boundary between two milestones read the same as the
   boundary inside one. */
.allocation-times[b-tzy02l35xq] {
    --ps-cluster-gap: var(--ps-space-4);
    margin-block-end: var(--ps-space-2);
}

/* One milestone: its label, its instant, and the step it closed, spaced as a tight cluster so the three read as one
   unit and the strip wraps BETWEEN milestones rather than mid-milestone. */
.allocation-times__item[b-tzy02l35xq] {
    --ps-cluster-gap: var(--ps-space-1);
}

/* A milestone's closed-step duration: same muted fine print as its row, kept on ONE line so a narrow column never
   orphans the figure from the label that explains it. */
.allocation-times__span[b-tzy02l35xq] {
    white-space: nowrap;
}

/* The round's SETTLED summary figures — how long it ran, how long the whole round lasted — are the answers a reader
   opens a finished job for, so they read as strongly as the accruals do rather than as fine print among the
   milestones. */
.allocation-times__figure[b-tzy02l35xq] {
    color: var(--ps-color-text);
    white-space: nowrap;
}

/* The two accruing steps (running / displacing) are live facts rather than fine print, so they take the full text
   color at the same size — the strongest reading in the strip without a second type size. */
.allocation-times__live[b-tzy02l35xq] {
    color: var(--ps-color-text);
    white-space: nowrap;
}

.warmup-note[b-tzy02l35xq] {
    margin-block-end: var(--ps-space-2);
}

.detail-fine[b-tzy02l35xq] {
    font-size: var(--ps-text-sm);
}

.page-job-detail[b-tzy02l35xq]  .countdown-strong {
    font-weight: var(--ps-weight-semibold);
}

/* --- Placed-member row severity accents (DataTable RowClass). --- */
.page-job-detail[b-tzy02l35xq]  tr.member-row--lost {
    background: var(--ps-color-danger-soft);
}

.page-job-detail[b-tzy02l35xq]  tr.member-row--draining {
    background: var(--ps-color-warning-soft);
}

/* --- Member-group brackets (memberGroupSize > 1): every grouped row carries an inset accent bar on its lead
   cell so the rows under one group header visually bind into one unit; the header row itself is the DataTable's
   shared .ps-table__group-row recipe. Member-granular jobs never carry these classes (pinned identity). --- */
.page-job-detail[b-tzy02l35xq]  tr.member-row--grouped > .ps-table__cell:first-child {
    box-shadow: inset 3px 0 0 var(--ps-color-border-strong);
}

.member-group-head[b-tzy02l35xq] {
    --ps-cluster-gap: var(--ps-space-2);
    align-items: center;
}

.member-group-head__span[b-tzy02l35xq] {
    color: var(--ps-color-text-muted);
    font-weight: var(--ps-weight-normal);
}

/* The Interrupt action (a hard, terminal stop) reads in the danger color while keeping the Secondary chrome.
   The per-member yield-requeue button — once styled by this same rule — now owns its colour in the shared
   MemberYieldCell component's stylesheet, so this rule is scoped to the page's own danger-text actions. */
.page-job-detail[b-tzy02l35xq]  button.job-action--danger {
    color: var(--ps-color-danger);
}

/* --- Dependencies. --- */
.editor-actions[b-tzy02l35xq] {
    margin-block-start: var(--ps-space-3);
}

.dependency-list li[b-tzy02l35xq] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
    margin-block-end: var(--ps-space-1);
    min-inline-size: 0;
}

.dependency-empty[b-tzy02l35xq] {
    margin: 0;
}

/* --- Idle-reclaim override card: the muted inherit line reads like the other empty-state notes. --- */
.idle-reclaim-inherit[b-tzy02l35xq] {
    margin: 0;
}

/* Candidate-GPU UUID chips are long unbroken tokens: release the badge's nowrap and cap the chip at
   the cluster's width (the max-inline-size clamps the flex item's automatic minimum) so they wrap in
   narrow panes instead of widening the page. */
.candidate-chips[b-tzy02l35xq]  .ps-badge {
    max-inline-size: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
}

.candidate-chips[b-tzy02l35xq]  .ps-badge > code {
    min-inline-size: 0;
}

/* --- Recent output: one unified, time-ordered stream, each line attributed to its member. --- */
.output-tail[b-tzy02l35xq] {
    max-block-size: 22rem;
    overflow: auto;
    margin: 0;
    padding: var(--ps-space-3) var(--ps-space-4);
    font-family: var(--ps-font-mono);
    font-size: var(--ps-text-xs);
    background: var(--ps-color-surface-sunken);
    border-radius: var(--ps-radius-md);
    white-space: pre-wrap;
    word-break: break-word;
}

.output-line[b-tzy02l35xq] {
    white-space: pre-wrap;
    word-break: break-word;
}

/* The per-line member attribution (role #index) reads as a muted prefix so the line text leads. */
.output-attribution[b-tzy02l35xq] {
    color: var(--ps-color-text-muted);
}

.output-trimmed[b-tzy02l35xq] {
    display: block;
    margin-block-end: var(--ps-space-2);
}

/* --- Confirm dialog. --- */
.confirm-body[b-tzy02l35xq] {
    margin: 0;
}

.confirm-blast[b-tzy02l35xq] {
    margin: var(--ps-space-2) 0 0;
}

/* The whole-job-yield wrapper is a pure test/data hook: display:contents keeps its button flowing in the
   glance-actions cluster (one wrapping action row, no orphaned second row). */
.glance-actions__yield[b-tzy02l35xq] {
    display: contents;
}

/* The cluster measures ITSELF, because its width is not the viewport's: from the 3xl tier up the glance card
   docks into a 22rem rail, so the WIDEST screens give this row the LEAST room — the same measure a phone gives
   it, and a media query would have said "plenty" at both. */
.glance-actions[b-tzy02l35xq] {
    container-type: inline-size;
    container-name: glance-actions;
}

/* Below the measure that fits the six verbs in three rows, the Displace button sheds its outcome gloss
   ("(will requeue)") — the widest single element here, and the one piece of the row that is not a verb. Nothing
   is lost: the same words stay in the button's own title, and the job's declared displacement response is stated
   in full on the Overview card a few centimetres away. Dropping it is what keeps a sixth control from costing a
   fourth row on exactly the two widths that could not afford one. */
@container glance-actions (max-width: 30rem) {
    .glance-actions .detail-fine[b-tzy02l35xq] {
        display: none;
    }
}

/* --- Per-member live progress cell (owner/admin tier): phase, whole-job fraction, optional unit i/N, and the
   ordinal-sorted key=value metrics. No chart / bar in this batch. The wrap itself is the shared .ps-user-text
   rule on the markup (phase/metric text is user-supplied and can be one unbreakable token); the width cap here
   keeps the anywhere-wrapped column from dominating the auto table layout at desktop widths — Worker /
   Reserved devices / Yield keep real columns beside a paragraph-length progress report. --- */
.member-progress[b-tzy02l35xq] {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--ps-space-2);
    max-inline-size: 32rem;
    font-size: var(--ps-text-xs);
}

.member-fraction[b-tzy02l35xq] {
    font-weight: var(--ps-weight-semibold);
}

.member-unit[b-tzy02l35xq] {
    color: var(--ps-color-text-muted);
}

.member-metrics[b-tzy02l35xq] {
    color: var(--ps-color-text-muted);
    font-variant-numeric: tabular-nums;
}

/* --- Minimal tier: the occupied-GPU card, one cluster per NODE. The hostname anchors the row and the cards
   trail it as chip LINKS to their device pages, so the whole clickable chip is the target (a bare underlined
   badge would fight the pill's own border). --- */
.occupied-node__host[b-tzy02l35xq] {
    font-weight: var(--ps-weight-medium);
}

.occupied-node__gpu[b-tzy02l35xq] {
    display: inline-flex;
    color: inherit;
    text-decoration: none;
    border-radius: var(--ps-radius-full);
}

.occupied-node__gpu:hover[b-tzy02l35xq]  .ps-badge,
.occupied-node__gpu:focus-visible[b-tzy02l35xq]  .ps-badge {
    border-color: var(--ps-color-accent);
    color: var(--ps-color-text);
}
/* /Components/Pages/Jobs.razor.rz.scp.css */
.jobs-search[b-yn5yteitrf] {
    display: flex;
    gap: var(--ps-space-2);
}

.jobs-search[b-yn5yteitrf]  .clearable-input-wrapper {
    flex: 1;
    min-inline-size: 0;
}

/* The legacy clear affordance sized itself against Bootstrap's input; track the token input instead. */
.jobs-search[b-yn5yteitrf]  button.clearable-input-button {
    top: 0;
    height: 100%;
}

.jobs-filter[b-yn5yteitrf] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
}

.jobs-filter__name[b-yn5yteitrf] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* The anywhere-wrap for pathological unbroken names/remarks is the shared .ps-user-text rule on the markup;
   these keep only the page-local weight/measure. */
.jobs-name[b-yn5yteitrf] {
    font-weight: var(--ps-weight-semibold);
}

/* Two-line clamp (ps-clamp-2 in the markup) rather than nowrap-ellipsis: wrapping text contributes only
   its longest word to the table's min-content, so a long remark can never stretch the column past the
   pane the way a single nowrap line does (the shared user-text anywhere-wrap covers unbroken tokens too). */
.jobs-remark[b-yn5yteitrf] {
    max-inline-size: 28rem;
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

.jobs-actions[b-yn5yteitrf] {
    display: inline-flex;
    gap: var(--ps-space-1);
}

/* Ghost buttons read accent; the destructive pair reads danger (the legacy outline-danger tier). */
.jobs-actions[b-yn5yteitrf]  button.jobs-action--danger {
    color: var(--ps-color-danger);
}

.jobs-more[b-yn5yteitrf] {
    text-align: center;
}
/* /Components/Pages/NewJob.razor.rz.scp.css */
/* The create-job page: the FormLayout pattern owns the form column + actions (sticky bottom bar below 3xl,
   sticky summary rail from 3xl); this sheet styles only the per-card content this page still renders itself
   (the Members and Scheduling policy cards carry their own). */

/* --- Copy from history. --- */
.new-job-copy[b-64ues8jz6i] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
}

.new-job-copy__label[b-64ues8jz6i] {
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
}

.new-job-copy__row[b-64ues8jz6i] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
}

/* A <select>'s intrinsic width is its WIDEST OPTION, and every option here carries a user-supplied job label —
   so the control is pinned to its cell (min 0, max 100%) instead of growing to the longest name in the history. */
.new-job-copy__select[b-64ues8jz6i] {
    flex: 1 1 18rem;
    min-inline-size: 0;
    max-inline-size: 100%;
}

/* The object-level error banner: a focus target (the failed submit moves focus here), so it must not paint a
   focus ring of its own — the alerts inside it are what the operator reads. */
.new-job-errors:focus[b-64ues8jz6i] {
    outline: none;
}

.new-job-errors:empty[b-64ues8jz6i] {
    display: none;
}

.new-job-object-errors[b-64ues8jz6i] {
    margin: 0;
}

/* The one quiet line naming the first unmet submit gate, beside the disabled button in the sticky bar. */
.new-job-submit-hint[b-64ues8jz6i] {
    flex: 1 1 100%;
    font-size: var(--ps-text-sm);
    text-align: end;
}
/* /Components/Pages/NotFound.razor.rz.scp.css */
/* The 404 is a deliberate centered hero (not a cramped empty-pane), filling most of the content viewport so it
   reads as an intentional destination. */
.page-not-found[b-83vyw4oz31] {
    display: grid;
    justify-items: center;
    align-content: center;
    gap: var(--ps-space-3);
    min-block-size: 60vh;
    padding-block: var(--ps-space-12);
    padding-inline: var(--ps-space-4);
    text-align: center;
}

/* A large, faint numeral — the recognizable 404 cue, decorative (the <h1> carries the meaning). */
.page-not-found__code[b-83vyw4oz31] {
    margin: 0;
    font-size: clamp(4rem, 2.5rem + 9vw, 7rem);
    font-weight: var(--ps-weight-bold);
    line-height: 1;
    letter-spacing: 0.04em;
    color: var(--ps-color-text-faint);
}

.page-not-found__title[b-83vyw4oz31] {
    margin: 0;
    font-size: var(--ps-text-2xl);
    font-weight: var(--ps-weight-semibold);
}

.page-not-found__message[b-83vyw4oz31] {
    margin: 0;
    max-inline-size: 34rem;
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
}

/* The admin-only "you're previewing" hint: bound to the same readable column as the message, left-aligned (it is
   prose, not a centered label). */
.page-not-found__note[b-83vyw4oz31] {
    inline-size: 100%;
    max-inline-size: 34rem;
    margin-block-start: var(--ps-space-2);
    text-align: start;
}

.page-not-found__actions[b-83vyw4oz31] {
    margin-block-start: var(--ps-space-4);
}
/* /Components/Pages/OutboundTunnelForm.razor.rz.scp.css */
/* The outbound-tunnel register/edit page. The form body and the sticky actions are FormLayout's; the only thing
   scoped here is the status rail's label for the per-member transport breakdown — the list's Transport cell folds
   those reports into counted badges, so this page is where the member-by-member reading lives and it needs a name
   to be found under the direct-serve badges. var(--ps-*) only. */

.outbound-tunnel-form__settled-heading[b-390r3olsqi] {
    margin-block-start: var(--ps-space-2);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-medium);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ps-color-text-muted);
}
/* /Components/Pages/OutboundTunnels.razor.rz.scp.css */
.outbound-tunnels-name[b-3kwaaz20xe] {
    font-weight: var(--ps-weight-semibold);
    /* Pathological unbroken names wrap instead of stretching the pane. */
    overflow-wrap: anywhere;
}

/* The derived-transports affix under the resource summary — the list speaks the same transport vocabulary as the form. */
.outbound-tunnels-transports-affix[b-3kwaaz20xe] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    letter-spacing: 0.02em;
}

.outbound-tunnels-status-note[b-3kwaaz20xe] {
    margin-inline-start: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* The liveness breadcrumb line under the Live/Offline badge ("since …" / "down (reason) …") and the two
   data-plane instants — muted one-liners that must not compete with the badge they qualify. */
.outbound-tunnels-status-line[b-3kwaaz20xe],
.outbound-tunnels-data-plane[b-3kwaaz20xe] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

.outbound-tunnels-allowlist-count[b-3kwaaz20xe] {
    font-size: var(--ps-text-sm);
}

/* Long unbroken resource summaries (paths/URLs) wrap instead of overflowing the pane (cards at phone widths). */
.outbound-tunnels-resource[b-3kwaaz20xe] {
    font-size: var(--ps-text-xs);
    overflow-wrap: anywhere;
}

.outbound-tunnels-actions[b-3kwaaz20xe] {
    display: inline-flex;
    gap: var(--ps-space-1);
}

/* Ghost buttons read accent; the destructive one reads danger (the legacy outline-danger tier). */
.outbound-tunnels-actions[b-3kwaaz20xe]  button.outbound-tunnels-action--danger {
    color: var(--ps-color-danger);
}

/* The Transport cell: at most TWO lines whatever the member count — the offered rung (badge + its advertised
   endpoint(s)) above, the members' settled transports folded to one counted badge per rung below. Each line is a
   wrapping cluster, so a narrow column reflows within the line instead of growing the row. */
.outbound-tunnels-transport[b-3kwaaz20xe] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
    align-items: flex-start;
}

.outbound-tunnels-transport__line[b-3kwaaz20xe] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

.outbound-tunnels-direct-endpoint[b-3kwaaz20xe] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    overflow-wrap: anywhere;
}
/* /Components/Pages/ProcessDetail.razor.rz.scp.css */
.process-output[b-n5hzjxrytd] {
    max-block-size: 31rem;
    overflow: auto;
    margin: 0;
    padding: var(--ps-space-3) var(--ps-space-4);
    font-family: var(--ps-font-mono);
    font-size: var(--ps-text-xs);
    background: var(--ps-color-surface-sunken);
    border-radius: var(--ps-radius-md);
    white-space: pre-wrap;
    word-break: break-word;
}

.process-output__stderr[b-n5hzjxrytd] {
    color: var(--ps-color-danger);
}

/* The Copy / Download actions on the Output card header; the card header is a space-between flex, so this cluster
   lands at the right of the title. */
.output-actions[b-n5hzjxrytd] {
    flex-wrap: wrap;
}

.detail-fine[b-n5hzjxrytd] {
    font-size: var(--ps-text-xs);
}
/* /Components/Pages/Settings.razor.rz.scp.css */
/* The notification-settings page: a tab strip selecting Card-wrapped sections (channel matrix, Web Push,
   contact, full inbox). var(--ps-*) only. */

.settings-note[b-beo46zbaqf] {
    margin-block: 0 var(--ps-space-3);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.settings-note--tight[b-beo46zbaqf] {
    margin-block: var(--ps-space-2) 0;
}

.settings-subheading[b-beo46zbaqf] {
    margin-block: 0 var(--ps-space-2);
    font-size: var(--ps-text-md);
    font-weight: var(--ps-weight-semibold);
}

.settings-divider[b-beo46zbaqf] {
    border: none;
    border-block-start: 1px solid var(--ps-color-border);
    margin-block: var(--ps-space-4);
}

/* --- Tab strip: a row of token-styled tabs that wrap (and scroll if a narrow pane forces it). The active
   tab carries an accent underline. --- */
.settings-tabs[b-beo46zbaqf] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-1);
    border-block-end: 1px solid var(--ps-color-border);
}

.settings-tabs__tab[b-beo46zbaqf] {
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
    /* WCAG 2.5.8 target floor + room for the underline. */
    min-block-size: var(--ps-target-min);
    padding-inline: var(--ps-space-3);
    padding-block: var(--ps-space-2);
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
    border-block-end: 2px solid transparent;
    margin-block-end: -1px;
}

.settings-tabs__tab:hover[b-beo46zbaqf] {
    color: var(--ps-color-text);
}

.settings-tabs__tab--active[b-beo46zbaqf] {
    color: var(--ps-color-accent);
    border-block-end-color: var(--ps-color-accent);
}

.settings-tabs__tab:focus-visible[b-beo46zbaqf] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: 2px;
    border-radius: var(--ps-radius-sm);
}

/* --- Event × channel matrix: one collapsible group per category, each holding a compact table whose first row
   is the group's per-channel master. CONTAINER-first: the layout answers to the card's own width, not the
   viewport, so the same markup reads correctly in a narrow pane and on a wall display. --- */
.settings-events[b-beo46zbaqf] {
    container-type: inline-size;
    container-name: ps-settings-events;
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
}

.settings-events__table[b-beo46zbaqf] {
    border-collapse: collapse;
    inline-size: 100%;
    font-size: var(--ps-text-sm);
}

.settings-events__table th[b-beo46zbaqf],
.settings-events__table td[b-beo46zbaqf] {
    padding: var(--ps-space-2);
    border-block-end: 1px solid var(--ps-color-border);
    text-align: start;
    vertical-align: middle;
}

.settings-events__table thead th[b-beo46zbaqf] {
    color: var(--ps-color-text-muted);
    font-weight: var(--ps-weight-medium);
    white-space: nowrap;
}

/* Each group reads as ONE object whether it is open or closed: the shared disclosure's toggle is the header of a
   bordered box, and the panel is its body. ::deep because the disclosure's own markup carries its component's
   scope, not this page's. */
.settings-events[b-beo46zbaqf]  .ps-disclosure {
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    overflow: hidden;
}

.settings-events[b-beo46zbaqf]  .ps-disclosure__toggle {
    border: 0;
    border-radius: 0;
}

.settings-events[b-beo46zbaqf]  .ps-disclosure--open .ps-disclosure__toggle {
    border-block-end: 1px solid var(--ps-color-border);
}

/* The panel's own gap would separate the table from the header it belongs to; the box supplies the breathing room. */
.settings-events[b-beo46zbaqf]  .ps-disclosure__panel {
    margin-block-start: 0;
    padding: var(--ps-space-2);
}

/* The channel columns hug their control: the surplus width belongs to the event name, which is the only
   column carrying prose. The alignment selector has to out-specify the `text-align: start` on the table's own
   th/td rule above, or the control sits under the LEFT edge of a wider column header instead of under its middle. */
.settings-events__table .settings-events__channel[b-beo46zbaqf] {
    inline-size: 1%;
    text-align: center;
    white-space: nowrap;
}

.settings-events__event[b-beo46zbaqf] {
    /* A <th> is bold by default; the title span inside carries the weight that earns it. */
    font-weight: var(--ps-weight-normal);
}

/* The flex sits on an INNER span, never on the <th>: a display:flex table header stops generating a table cell, so
   its row separator and its vertical centring part company with the channel columns beside it. */
.settings-events__event-inner[b-beo46zbaqf] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--ps-space-2);
}

.settings-events__title[b-beo46zbaqf] {
    font-weight: var(--ps-weight-medium);
}

.settings-events__token[b-beo46zbaqf] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    overflow-wrap: anywhere;
}

/* The master row reads as the group's own header line, not as one more event. */
.settings-events__row--master[b-beo46zbaqf] {
    background: var(--ps-color-surface-sunken);
}

.settings-events__row--master .settings-events__title[b-beo46zbaqf] {
    font-weight: var(--ps-weight-semibold);
}

/* The per-cell channel name only exists for the stacked form below; the column headers name them otherwise. */
.settings-events__cell-label[b-beo46zbaqf] {
    display: none;
}

/* --- Narrow container: the eight-column matrix stops being a grid and becomes one block per event, each
   listing its channels as labelled rows. This is what keeps the page off a horizontal scrollbar at phone
   width WITHOUT hiding the controls inside a scroll region the reader has to discover. --- */
@container ps-settings-events (max-width: 47.9375rem) {
    .settings-events__table[b-beo46zbaqf],
    .settings-events__table tbody[b-beo46zbaqf],
    .settings-events__table tr[b-beo46zbaqf],
    .settings-events__table th[b-beo46zbaqf],
    .settings-events__table td[b-beo46zbaqf] {
        display: block;
    }

    /* The column headers name channels the stacked cells now name themselves. */
    .settings-events__table thead[b-beo46zbaqf] {
        display: none;
    }

    .settings-events__row[b-beo46zbaqf] {
        border: 1px solid var(--ps-color-border);
        border-radius: var(--ps-radius-md);
        padding: var(--ps-space-1);
        margin-block-end: var(--ps-space-2);
    }

    .settings-events__table th[b-beo46zbaqf],
    .settings-events__table td[b-beo46zbaqf] {
        border-block-end: 0;
    }

    /* Specificity has to BEAT the display:block above (which is two class selectors deep), or the channel name and
       its control stack instead of sitting on one line — the difference between a readable phone form and a column
       of 63 orphaned labels. */
    .settings-events__table .settings-events__channel[b-beo46zbaqf] {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--ps-space-2);
        inline-size: auto;
        text-align: start;
        /* Enough to keep two 24px targets from touching without doubling the block's height. */
        padding-block: var(--ps-space-1);
        padding-inline: var(--ps-space-2);
    }

    /* The event's own header is the block's title line; the channel rows below it are the form. */
    .settings-events__table .settings-events__event[b-beo46zbaqf] {
        padding-block-end: var(--ps-space-1);
    }

    .settings-events__cell-label[b-beo46zbaqf] {
        display: block;
        font-size: var(--ps-text-sm);
        color: var(--ps-color-text-muted);
    }
}

/* --- Runtime overrides: its own compact table, whose row HEADER is prose rather than a short name. It once
   borrowed the preference matrix's row-header class (white-space: nowrap, correct for a two-word name) and laid
   each setting's multi-sentence description out as one unbreakable ~500-character line, so the table's
   min-content width ran to several thousand px and the wrapping .ps-scroll-x showed a long horizontal
   scrollbar. The header wraps at a readable measure instead and the value/effective columns hug their short
   contents, so the five columns fit a laptop content column with no scroll at all (the container survives as
   the phone-width escape hatch, which is what a declared scroll container is for). --- */
.settings-runtime[b-beo46zbaqf] {
    border-collapse: collapse;
    inline-size: 100%;
    font-size: var(--ps-text-sm);
}

.settings-runtime th[b-beo46zbaqf],
.settings-runtime td[b-beo46zbaqf] {
    padding: var(--ps-space-2);
    border-block-end: 1px solid var(--ps-color-border);
    text-align: start;
    vertical-align: middle;
}

.settings-runtime thead th[b-beo46zbaqf] {
    color: var(--ps-color-text-muted);
    font-weight: var(--ps-weight-medium);
    white-space: nowrap;
}

.settings-runtime__setting[b-beo46zbaqf] {
    white-space: normal;
    /* A <th> is bold by default; only the setting's NAME earns that here (the channel matrix resets the same way). */
    font-weight: var(--ps-weight-normal);
    /* A readable column for the sentence, well inside --ps-measure so the description never eats the columns it
       explains. The floor keeps the name off a one-word-per-line ladder when the pane is narrow. */
    inline-size: 24rem;
    min-inline-size: 12rem;
}

.settings-runtime__name[b-beo46zbaqf] {
    font-weight: var(--ps-weight-medium);
}

.settings-runtime__description[b-beo46zbaqf] {
    /* Fine print, clamped to two lines with the full text on hover (the .ps-clamp-2 utility's stated purpose):
       these descriptions run to several hundred characters, and letting every one of them wrap in full turns a
       four-row table into a wall — the row's job is to show base / override / effective and take a new value.
       anywhere-wrap guarantees even a pathological unbroken token (a config path) breaks instead of re-widening
       the table. */
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    overflow-wrap: anywhere;
}

.settings-runtime__key[b-beo46zbaqf] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    overflow-wrap: anywhere;
}

/* The three read-out columns state short scalars ("512", "01:30:00") — let them hug so the surplus width goes
   to the prose header and the editor, not to three mostly-empty columns. */
.settings-runtime td:not(:last-child)[b-beo46zbaqf] {
    inline-size: 1%;
    white-space: nowrap;
}

/* --- Web Push device list. --- */
.settings-subscriptions[b-beo46zbaqf] {
    margin-block-start: var(--ps-space-3);
}

.settings-device-list[b-beo46zbaqf] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
}

.settings-device[b-beo46zbaqf] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--ps-space-2);
    padding: var(--ps-space-2) var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
}

.settings-device__info[b-beo46zbaqf] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.settings-device__name[b-beo46zbaqf] {
    overflow-wrap: anywhere;
}

.settings-device__added[b-beo46zbaqf] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

.settings-device__remove[b-beo46zbaqf] {
    color: var(--ps-color-danger);
}

/* --- Contact phone row: the input grows, the Save button hugs its content. --- */
.settings-phone[b-beo46zbaqf] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
    max-inline-size: 24rem;
}

.settings-phone input[b-beo46zbaqf] {
    flex: 1 1 12rem;
    min-inline-size: 0;
}

/* --- Language. --- */
/* The picker is a two-item CHOICE, not a data field: capped at the phone row's measure so it reads as a control
   instead of a full-bleed input stretched across a laptop card (its longest option needs ~184px). The cap sits above
   a phone's card width, so there it still fills the column, which is right at that size. */
.settings-language[b-beo46zbaqf] {
    max-inline-size: 24rem;
}

/* --- Full inbox. --- */
.settings-inbox-filter[b-beo46zbaqf] {
    margin-block-end: var(--ps-space-3);
}

.settings-inbox-filter__label[b-beo46zbaqf] {
    font-size: var(--ps-text-sm);
}

.settings-inbox-filter__select[b-beo46zbaqf] {
    inline-size: auto;
}

.settings-inbox[b-beo46zbaqf] {
    block-size: 30rem;
    overflow-y: auto;
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
}

.settings-inbox__item[b-beo46zbaqf] {
    padding: var(--ps-space-2) var(--ps-space-3);
    border-block-end: 1px solid var(--ps-color-border);
}

.settings-inbox__item--unread[b-beo46zbaqf] {
    background: var(--ps-color-accent-soft);
    border-inline-start: 3px solid var(--ps-color-accent);
}

.settings-inbox__head[b-beo46zbaqf] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
}

.settings-inbox__title[b-beo46zbaqf] {
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.settings-inbox__time[b-beo46zbaqf] {
    margin-inline-start: auto;
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    white-space: nowrap;
}

.settings-inbox__body[b-beo46zbaqf] {
    font-size: var(--ps-text-sm);
}

.settings-inbox__link[b-beo46zbaqf] {
    font-size: var(--ps-text-sm);
}

/* 4xl wall-display tier (160rem literal; mirrors tokens.css — var() is illegal in media conditions). Even with the
   page's left-anchored list cap, on a wall display the compact tables would still stretch their columns edge-to-
   edge and the descriptive sentence would run the full card width. Cap them at intentional readable measures so they
   read as composed, not stretched. Gated at 4xl so ≤1920 is byte-identical. */
@media (min-width: 160rem) {
    /* Size the tables to their content (not the full card width) so they read as intentionally-sized matrices. */
    .settings-events__table[b-beo46zbaqf],
    .settings-runtime[b-beo46zbaqf] {
        inline-size: auto;
        max-inline-size: 100%;
    }

    /* Wrap the descriptive prose at a readable measure instead of running edge-to-edge across the wide card. */
    .settings-note[b-beo46zbaqf] {
        max-inline-size: var(--ps-measure);
    }
}
/* /Components/Pages/Usage.razor.rz.scp.css */
/* The analytics page: query controls reflow into a responsive grid; the filter chip-sets wrap; the lane stack is a
   gutter-label + compact stacked-area chart per row, grouped into faint per-node boxes. Tokens only. */
.page-usage[b-2f34aax5nx] {
    --ps-stack-gap: var(--ps-space-5);
    /* Establish a containment context so the lane-stack container query (bottom of file) can fire. A container query
       never matches the element it targets, so the gutter-width var is defined on the descendant lane groups below and
       overridden there under the query. */
    container-type: inline-size;
}

.usage-controls[b-2f34aax5nx] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--ps-space-3) var(--ps-space-4);
    align-items: end;
}

/* ---- Filter chip-sets (metrics, devices). ---- */
.usage-filter[b-2f34aax5nx] {
    margin-block-start: var(--ps-space-4);
}

.usage-filter__head[b-2f34aax5nx] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ps-space-3);
    margin-block-end: var(--ps-space-2);
}

.usage-filter__title[b-2f34aax5nx] {
    font-weight: var(--ps-weight-semibold);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.usage-filter__all[b-2f34aax5nx] {
    border: none;
    background: none;
    padding: 0;
    font-size: var(--ps-text-sm);
    color: var(--ps-color-accent);
    cursor: pointer;
}

.usage-filter__all:hover[b-2f34aax5nx] {
    text-decoration: underline;
}

.usage-filter__chips[b-2f34aax5nx] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
}

.usage-chip[b-2f34aax5nx] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    padding: var(--ps-space-1) var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-surface);
    font-size: var(--ps-text-sm);
    cursor: pointer;
    user-select: none;
}

.usage-chip--on[b-2f34aax5nx] {
    border-color: var(--ps-color-accent);
    background: var(--ps-color-accent-soft);
    color: var(--ps-color-accent-emphasis);
}

/* The chip's checkbox wears the canonical .ps-control skin (controls.css): a custom-drawn box sized to
   --ps-target-min (24px), so the chip clears the WCAG 2.5.8 phone touch-target floor and reads identically to every
   other checkbox in the system. The chip pill grows to fit it, so no per-element sizing lives here. */

/* ---- The node → device disclosure tree (the device filter). ---- */
.usage-device-tree[b-2f34aax5nx] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
}

.usage-device-node[b-2f34aax5nx] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
}

.usage-device-node__head[b-2f34aax5nx] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-3);
}

/* The chevron + node label toggle (mirrors the dashboard disclosure). */
.usage-device-node__toggle[b-2f34aax5nx] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    min-block-size: var(--ps-target-min);
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text);
}

.usage-device-node__toggle:hover[b-2f34aax5nx] {
    color: var(--ps-color-accent);
}

.usage-device-node__toggle:focus-visible[b-2f34aax5nx] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: 2px;
    border-radius: var(--ps-radius-sm);
}

.usage-device-node__name[b-2f34aax5nx] {
    min-inline-size: 0;
    overflow-wrap: anywhere;
}

/* The node-level "All" toggle: select / clear every device on the node. */
.usage-device-node__all[b-2f34aax5nx] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
    cursor: pointer;
    user-select: none;
}

/* The node "All" checkbox wears the canonical .ps-control skin (controls.css), so it is the same custom-drawn
   --ps-target-min (24px) box as the chips above — clearing the phone touch-target floor with no per-element sizing. */

.usage-device-node__devices[b-2f34aax5nx] {
    margin-inline-start: var(--ps-space-5);
}

/* ---- The Apply bar at the bottom of the Query card. ---- */
.usage-apply[b-2f34aax5nx] {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: var(--ps-space-3);
    margin-block-start: var(--ps-space-4);
}

.usage-apply__hint[b-2f34aax5nx] {
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* ---- The shared per-user legend (doubles as the user filter). ---- */
.usage-legend[b-2f34aax5nx] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
}

.usage-legend__item[b-2f34aax5nx] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    padding: var(--ps-space-1) var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-surface);
    font-size: var(--ps-text-sm);
    cursor: pointer;
}

.usage-legend__item--off[b-2f34aax5nx] {
    opacity: 0.45;
    text-decoration: line-through;
}

/* Others / Idle are shown but not toggleable. */
.usage-legend__item--static[b-2f34aax5nx] {
    cursor: default;
    color: var(--ps-color-text-muted);
}

.usage-legend__swatch[b-2f34aax5nx] {
    flex: none;
    inline-size: 0.8rem;
    block-size: 0.8rem;
    border-radius: var(--ps-radius-sm);
    border: 1px solid color-mix(in srgb, var(--ps-color-border) 60%, transparent);
}

.usage-legend__swatch--others[b-2f34aax5nx] {
    background: var(--ps-usage-others);
}

.usage-legend__swatch--idle[b-2f34aax5nx] {
    background: var(--ps-usage-idle);
}

.usage-legend__label[b-2f34aax5nx] {
    overflow-wrap: anywhere;
}

/* ---- Query-duration fine print: the ticking "elapsed N s" readout and the completed "query ran N s" lines. ---- */
.usage-duration[b-2f34aax5nx] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    font-variant-numeric: tabular-nums;
}

/* ---- The multi-metric key strip. ---- */
.usage-metric-key[b-2f34aax5nx] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-3);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.usage-metric-key__item[b-2f34aax5nx] {
    font-variant-numeric: tabular-nums;
}

/* ---- The lane stack + the aggregate (same lane row shape). ---- */
.usage-lanes[b-2f34aax5nx],
.usage-aggregate[b-2f34aax5nx] {
    /* The lane label gutter width — shared by the lanes, the aggregate, and the time ruler so they align; collapsed by
       the container query below. Defined here (a descendant of the .page-usage container) so the query can override it. */
    --ps-usage-label-w: 12rem;
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-3);
}

.usage-node[b-2f34aax5nx] {
    /* The faint per-node group box. */
    margin: 0;
    padding: var(--ps-space-2) var(--ps-space-3) var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
}

.usage-node__label[b-2f34aax5nx] {
    padding: 0 var(--ps-space-2);
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text-muted);
}

.usage-lane[b-2f34aax5nx] {
    display: grid;
    grid-template-columns: var(--ps-usage-label-w) minmax(0, 1fr);
    align-items: center;
    gap: var(--ps-space-3);
}

.usage-lane__label[b-2f34aax5nx] {
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.usage-lane__chart[b-2f34aax5nx] {
    min-inline-size: 0;
}

.usage-lane--aggregate .usage-lane__label[b-2f34aax5nx] {
    font-weight: var(--ps-weight-semibold);
}

/* The single shared time ruler under the lane stack, aligned to the chart column. */
.usage-lanes__ruler[b-2f34aax5nx] {
    display: flex;
    justify-content: space-between;
    /* Align the ruler ticks to the lane CHART column: the chart's left edge sits past the node box's left border (1px)
       + padding (--ps-space-3), then the label gutter, then the lane grid gap. Mirror the node padding+border on the
       right so the last tick lands on the chart's right edge too. */
    margin-inline-start: calc(1px + var(--ps-space-3) + var(--ps-usage-label-w) + var(--ps-space-3));
    margin-inline-end: calc(1px + var(--ps-space-3));
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* Narrow containers: collapse the wide label gutter so lanes keep their width (the var lives on the lane groups, the
   container query's descendants — it cannot restyle .page-usage, the container element itself). */
@container (max-width: 40rem) {
    .usage-lanes[b-2f34aax5nx],
    .usage-aggregate[b-2f34aax5nx] {
        --ps-usage-label-w: 6rem;
    }
}
/* /Components/Pages/WorkerDetail.razor.rz.scp.css */
.device-name[b-y4bceeh277] {
    min-inline-size: 0;
}

.device-name > span:first-child[b-y4bceeh277] {
    overflow-wrap: anywhere;
}

/* The reclaim column's compact two-shortfall claimable-headroom summary, under the reclaim badge — a fine muted line
   (the full per-resource detail lives on the device page). */
.reclaim-summary[b-y4bceeh277] {
    margin-block-start: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* The MEMBER-CAPS card. Container-first: every row is a cluster that wraps on its own, so the card reads the same
   whether it has the full page width or a phone's, and nothing here depends on the viewport. */
.member-caps[b-y4bceeh277] {
    container-type: inline-size;
    container-name: ps-member-caps;
}

.member-caps__intro[b-y4bceeh277] {
    margin: 0;
    font-size: var(--ps-text-sm);
}

/* A cap is a small count, so its box is sized for one — anything wider only invites a reader to expect a range.
   It still shrinks on a narrow container rather than pushing the row into a horizontal scroll. */
.member-caps__input[b-y4bceeh277] {
    inline-size: 7rem;
    min-inline-size: 0;
}

/* The node-wide box's wrapper: inside a FormField the control fills its field by contract, so the WIDTH lives here
   and the input goes on filling it (see the markup note). */
.member-caps__box[b-y4bceeh277] {
    display: inline-block;
    inline-size: 7rem;
    min-inline-size: 0;
}

/* The "add a cap" picker carries an account, which is as long as an address; it takes what the row can give and
   truncates rather than dictating the row's width. */
.member-caps__picker[b-y4bceeh277] {
    inline-size: min(22rem, 100%);
    min-inline-size: 0;
}

/* The rows wrap as clusters; each control keeps the shared 24px minimum target the design system sets. */
.member-caps__row[b-y4bceeh277] {
    align-items: center;
    row-gap: var(--ps-space-2);
}

@container ps-member-caps (max-width: 26rem) {
    /* On a phone-width card the two boxes take the whole line, so a label and its control are never split across a
       wrap in a way that reads as two unrelated fields. */
    .member-caps__input[b-y4bceeh277],
    .member-caps__box[b-y4bceeh277],
    .member-caps__picker[b-y4bceeh277] {
        inline-size: 100%;
    }
}
/* /Components/Pages/Workers.razor.rz.scp.css */
.workers-host[b-tyex6qmb0k] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
}

.workers-hostname[b-tyex6qmb0k] {
    font-weight: var(--ps-weight-semibold);
}

.workers-override[b-tyex6qmb0k] {
    display: inline-flex;
    color: var(--ps-color-text-muted);
}
/* /Components/Pages/WorkerSettingsOverridePage.razor.rz.scp.css */
/* A form-shaped page (a card of short numeric fields): bound it at the form measure and center it, aligning it with
   the FormLayout family's readable column instead of a full-bleed card — at 4K the uncapped card stretched ~3400px
   with seven tiny inputs huddled in its corner (the worst wall-tier offender). Inert below ~64rem content width;
   centering follows the shell's large-screen composition rule (MainLayout.razor.css). */
.page-settings-override[b-somsvo20wc] {
    inline-size: 100%;
    max-inline-size: var(--ps-form-max);
    margin-inline: auto;
}
/* /Components/ReconfigurationHistoryPanel.razor.rz.scp.css */
/* The member's reconfiguration-history panel: a vertical epoch timeline above its active-set report table. All color,
   spacing, and radius come from the design tokens so both schemes track automatically (light-dark in the tokens). */

.reconfig-history__section[b-4k8r650kgz] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-3);
}

.reconfig-history__heading[b-4k8r650kgz] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--ps-space-2);
    margin: 0;
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text);
}

.reconfig-history__heading-note[b-4k8r650kgz] {
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-normal);
}

.detail-fine[b-4k8r650kgz] {
    font-size: var(--ps-text-sm);
}

/* --- The epoch timeline: a connected column of event markers. The connector is a single line drawn down the marker
   gutter via a pseudo-element, so each event's marker sits ON the thread regardless of body height. --- */
.reconfig-timeline[b-4k8r650kgz] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-4);
    position: relative;
}

.reconfig-timeline__event[b-4k8r650kgz] {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--ps-space-3);
    align-items: start;
    position: relative;
}

/* The vertical thread connecting one marker to the next — drawn from this marker's center down to the next event's
   marker. The last event has no following marker, so it draws no thread. */
.reconfig-timeline__event:not(:last-child) .reconfig-timeline__marker[b-4k8r650kgz]::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 100%;
    transform: translateX(-50%);
    width: 2px;
    height: calc(100% + var(--ps-space-4));
    background: var(--ps-color-border);
}

.reconfig-timeline__marker[b-4k8r650kgz] {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--ps-radius-full);
    border: 1px solid var(--ps-color-border);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text-muted);
    flex-shrink: 0;
}

.reconfig-timeline__marker--success[b-4k8r650kgz] {
    color: var(--ps-color-success);
    border-color: var(--ps-color-success);
    background: var(--ps-color-success-soft);
}

.reconfig-timeline__marker--info[b-4k8r650kgz] {
    color: var(--ps-color-info);
    border-color: var(--ps-color-info);
    background: var(--ps-color-info-soft);
}

.reconfig-timeline__marker--warning[b-4k8r650kgz] {
    color: var(--ps-color-warning);
    border-color: var(--ps-color-warning);
    background: var(--ps-color-warning-soft);
}

.reconfig-timeline__marker--danger[b-4k8r650kgz] {
    color: var(--ps-color-danger);
    border-color: var(--ps-color-danger);
    background: var(--ps-color-danger-soft);
}

.reconfig-timeline__body[b-4k8r650kgz] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
    min-width: 0;
}

.reconfig-timeline__head[b-4k8r650kgz] {
    --ps-cluster-gap: var(--ps-space-2);
}

.reconfig-timeline__title[b-4k8r650kgz] {
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text);
}

/* A gang-wide reform the member merely lived through reads with a faint accent rail, so it is visually distinct from
   the member's own transitions without shouting. */
.reconfig-timeline__event--wide .reconfig-timeline__body[b-4k8r650kgz] {
    border-inline-start: 2px solid var(--ps-color-accent-soft);
    padding-inline-start: var(--ps-space-3);
    margin-inline-start: calc(-1 * var(--ps-space-2));
}

/* The event's when + how-long reading, trailing the head badges: a tighter cluster than the head's own, so the instant
   and its dwell read as one unit beside the badges. The dwell never breaks internally, so a narrow column wraps it
   whole instead of splitting the figure off its label. */
.reconfig-timeline__when[b-4k8r650kgz] {
    --ps-cluster-gap: var(--ps-space-1);
}

.reconfig-timeline__held[b-4k8r650kgz] {
    white-space: nowrap;
}

.reconfig-timeline__detail[b-4k8r650kgz] {
    margin: 0;
}

.reconfig-timeline__devices[b-4k8r650kgz] {
    --ps-cluster-gap: var(--ps-space-2);
    margin-top: var(--ps-space-1);
}

.reconfig-timeline__device[b-4k8r650kgz] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    font-family: var(--ps-font-mono);
    padding: var(--ps-space-1) var(--ps-space-2);
    border-radius: var(--ps-radius-sm);
    overflow-wrap: anywhere;
}

.reconfig-timeline__device--added[b-4k8r650kgz] {
    color: var(--ps-color-success);
    background: var(--ps-color-success-soft);
}

.reconfig-timeline__device--removed[b-4k8r650kgz] {
    color: var(--ps-color-warning);
    background: var(--ps-color-warning-soft);
}

/* --- The reports table: a diverged row is muted-danger so the fault pops without re-reading the badge. --- */
.reconfig-reports__devices[b-4k8r650kgz] {
    --ps-cluster-gap: var(--ps-space-1);
}

.reconfig-reports__device[b-4k8r650kgz] {
    overflow-wrap: anywhere;
}

[b-4k8r650kgz] .reconfig-reports__row--diverged {
    background: var(--ps-color-danger-soft);
}
/* /Components/UI/Alert.razor.rz.scp.css */
.ps-alert[b-nqdf78sn1e] {
    display: flex;
    align-items: flex-start;
    gap: var(--ps-space-3);
    padding-block: var(--ps-space-3);
    padding-inline: var(--ps-space-4);
    border: 1px solid;
    border-radius: var(--ps-radius-lg);
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-body);
}

/* Tracks the first text line's height so the icon centers against it. */
.ps-alert__icon[b-nqdf78sn1e] {
    display: inline-flex;
    align-items: center;
    block-size: calc(1em * var(--ps-line-body));
}

.ps-alert__content[b-nqdf78sn1e] {
    /* gap spaces the consumer's children without reaching into their scope. */
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.ps-alert__title[b-nqdf78sn1e] {
    margin: 0;
    font-weight: var(--ps-weight-semibold);
}

.ps-alert__actions[b-nqdf78sn1e] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
}

/* --- Severities: the status strong color on its soft fill (an AA pair by the token contract). --- */
.ps-alert--info[b-nqdf78sn1e] {
    background: var(--ps-color-info-soft);
    border-color: color-mix(in srgb, var(--ps-color-info) 30%, transparent);
    color: var(--ps-color-info);
}

.ps-alert--success[b-nqdf78sn1e] {
    background: var(--ps-color-success-soft);
    border-color: color-mix(in srgb, var(--ps-color-success) 30%, transparent);
    color: var(--ps-color-success);
}

.ps-alert--warning[b-nqdf78sn1e] {
    background: var(--ps-color-warning-soft);
    border-color: color-mix(in srgb, var(--ps-color-warning) 30%, transparent);
    color: var(--ps-color-warning);
}

.ps-alert--danger[b-nqdf78sn1e] {
    background: var(--ps-color-danger-soft);
    border-color: color-mix(in srgb, var(--ps-color-danger) 30%, transparent);
    color: var(--ps-color-danger);
}
/* /Components/UI/Button.razor.rz.scp.css */
/* The .ps-button family lives in wwwroot/css/controls.css (GLOBAL, unlayered): it is shared by two
   primitives — Button (a <button>) and LinkButton (a real <a href>) — so a scoped sheet could only style
   one component's element. Nothing component-private remains here. */
/* /Components/UI/Card.razor.rz.scp.css */
/* The card pairs its own foreground with its surface (a primitive must stay legible while legacy pages
   still paint the body with Bootstrap colors). */
.ps-card[b-x3ib8fhsjo] {
    background: var(--ps-color-surface-raised);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-lg);
    box-shadow: var(--ps-shadow-1);
    color: var(--ps-color-text);
    min-inline-size: 0;
}

/* The header WRAPS: a title and an action cluster that cannot share one line reflow to two rows instead of
   colliding (the Members card's title + "Add head"/"Add worker" pair at phone widths). min-inline-size on the
   children releases their min-content floor, which is what lets the wrap actually happen rather than the row
   growing past the card. */
.ps-card__header[b-x3ib8fhsjo] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--ps-space-3);
    padding-block: var(--ps-space-3);
    padding-inline: var(--ps-space-4);
    border-block-end: 1px solid var(--ps-color-border);
    font-weight: var(--ps-weight-semibold);
}

.ps-card__header > *[b-x3ib8fhsjo] {
    min-inline-size: 0;
}

.ps-card__body[b-x3ib8fhsjo] {
    padding: var(--ps-space-4);
}

.ps-card__footer[b-x3ib8fhsjo] {
    padding-block: var(--ps-space-3);
    padding-inline: var(--ps-space-4);
    border-block-start: 1px solid var(--ps-color-border);
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
}
/* /Components/UI/CardTitle.razor.rz.scp.css */
/* The card / section title: the leading icon and the text share a baseline-centered inline-flex row,
   gapped by the same token the hand-rolled `.card-title` idiom used. Typography is inherited from the
   surrounding header (no hardcoded size/weight/color) so a title reads exactly like the header it sits in.
   A title that carries a parenthetical also WRAPS (and releases its min-content floor), so a long title in a
   wrapping card header reflows onto a second line rather than pushing the header's action cluster out of the card. */
.ps-card-title[b-g707zm1xtl] {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.ps-card-title__text[b-g707zm1xtl] {
    min-inline-size: 0;
}

/* The parenthetical gloss: same line where it fits, muted and at normal weight against the semibold title. */
.ps-card-title__subtitle[b-g707zm1xtl] {
    min-inline-size: 0;
    font-weight: var(--ps-weight-normal);
    color: var(--ps-color-text-muted);
}
/* /Components/UI/Charts/BarChart.razor.rz.scp.css */
/* Label · track · value rows. The label column sizes to content up to a cap then ellipsizes; the track
   flexes to fill; the value column is right-aligned tabular numerals. Container-driven: in a narrow pane
   the label column shrinks. Tokens only — the default fill is the accent; a row override rides inline. */
.ps-bar-chart[b-xmt041y64d] {
    display: grid;
    gap: var(--ps-space-2);
    container-type: inline-size;
}

.ps-bar-chart__row[b-xmt041y64d] {
    display: grid;
    grid-template-columns: minmax(0, var(--ps-bar-chart-label-w, 8rem)) 1fr auto;
    align-items: center;
    gap: var(--ps-space-3);
    font-size: var(--ps-text-sm);
}

.ps-bar-chart__label[b-xmt041y64d] {
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--ps-color-text-muted);
}

.ps-bar-chart__track[b-xmt041y64d] {
    block-size: var(--ps-space-2);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-surface-sunken);
    border: 1px solid var(--ps-color-border);
    overflow: hidden;
    min-inline-size: 0;
}

.ps-bar-chart__fill[b-xmt041y64d] {
    block-size: 100%;
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-accent);
    transition: inline-size var(--ps-motion-medium) var(--ps-ease);
}

.ps-bar-chart__value[b-xmt041y64d] {
    font-variant-numeric: tabular-nums;
    color: var(--ps-color-text);
    text-align: end;
}

.ps-bar-chart__empty[b-xmt041y64d] {
    margin: 0;
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
}

/* Below a narrow pane the label column would crowd the bar out — give the label its own line. */
@container (max-width: 18rem) {
    .ps-bar-chart__row[b-xmt041y64d] {
        grid-template-columns: 1fr auto;
    }

    .ps-bar-chart__label[b-xmt041y64d] {
        grid-column: 1 / -1;
        white-space: normal;
    }
}
/* /Components/UI/Charts/Donut.razor.rz.scp.css */
/* The donut is a square that the consumer sizes via --ps-donut-size (defaults to a compact tile glyph);
   the center label is absolutely centered over the hole. Tokens only — segment colors arrive on the
   stroke attribute, the empty track from the border token. */
.ps-donut[b-64isj60w4u] {
    position: relative;
    inline-size: var(--ps-donut-size, 7rem);
    aspect-ratio: 1;
    flex: none;
}

.ps-donut__svg[b-64isj60w4u] {
    inline-size: 100%;
    block-size: 100%;
    display: block;
}

/* Round caps would overlap adjacent slices; butt caps keep proportions honest. */
.ps-donut__arc[b-64isj60w4u] {
    stroke-linecap: butt;
}

.ps-donut__empty[b-64isj60w4u] {
    stroke: var(--ps-color-surface-sunken);
}

.ps-donut__center[b-64isj60w4u] {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--ps-space-1);
    text-align: center;
    pointer-events: none;
}

.ps-donut__center-value[b-64isj60w4u] {
    font-size: var(--ps-text-lg);
    font-weight: var(--ps-weight-semibold);
    font-variant-numeric: tabular-nums;
    color: var(--ps-color-text);
    line-height: 1;
}

.ps-donut__center-label[b-64isj60w4u] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    line-height: 1;
}
/* /Components/UI/Charts/Gauge.razor.rz.scp.css */
/* A semicircle gauge sized by --ps-gauge-size; the reading is centered in the lower-middle, under the arc.
   The value arc uses round caps so a small value still reads as a pill; the track is the sunken surface. */
.ps-gauge[b-2t0iwlq611] {
    position: relative;
    inline-size: var(--ps-gauge-size, 9rem);
    aspect-ratio: 100 / 58;
}

.ps-gauge__svg[b-2t0iwlq611] {
    inline-size: 100%;
    block-size: 100%;
    display: block;
}

.ps-gauge__track[b-2t0iwlq611] {
    stroke: var(--ps-color-surface-sunken);
    stroke-linecap: round;
}

.ps-gauge__value[b-2t0iwlq611] {
    stroke-linecap: round;
    transition: stroke-dashoffset var(--ps-motion-medium) var(--ps-ease);
}

.ps-gauge__center[b-2t0iwlq611] {
    position: absolute;
    inset-block-end: 0;
    inset-inline: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ps-space-1);
    text-align: center;
    pointer-events: none;
}

.ps-gauge__value-text[b-2t0iwlq611] {
    font-size: var(--ps-text-xl);
    font-weight: var(--ps-weight-semibold);
    font-variant-numeric: tabular-nums;
    color: var(--ps-color-text);
    line-height: 1;
}

.ps-gauge__label[b-2t0iwlq611] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    line-height: 1;
}
/* /Components/UI/Charts/HeatStrip.razor.rz.scp.css */
/* A row of equal cells filling the width; each cell keeps a minimum tappable-ish width but shares the
   remaining space equally. A thin hairline separates cells so adjacent similar tints stay distinct. */
.ps-heat-strip[b-9w4tk15dgt] {
    inline-size: 100%;
}

.ps-heat-strip__cells[b-9w4tk15dgt] {
    display: flex;
    gap: 2px;
    block-size: var(--ps-heat-strip-height, 0.75rem);
    border-radius: var(--ps-radius-sm);
    overflow: hidden;
}

.ps-heat-strip__cell[b-9w4tk15dgt] {
    flex: 1 1 0;
    min-inline-size: 0.25rem;
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-sm);
}

.ps-heat-strip__empty[b-9w4tk15dgt] {
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
}
/* /Components/UI/Charts/Sparkline.razor.rz.scp.css */
/* A glyph that fills its container; the consumer sizes it (block-size via --ps-sparkline-height). The line
   keeps a constant visual weight despite the non-uniform scale by using vector-effect: non-scaling-stroke;
   the area fill rides a soft tint of the stroke color via opacity. */
.ps-sparkline[b-f19kpcyddq] {
    inline-size: 100%;
    block-size: var(--ps-sparkline-height, 2rem);
}

.ps-sparkline__svg[b-f19kpcyddq] {
    inline-size: 100%;
    block-size: 100%;
    display: block;
    overflow: visible;
}

.ps-sparkline__line[b-f19kpcyddq] {
    stroke-width: 1.5;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

.ps-sparkline__area[b-f19kpcyddq] {
    opacity: 0.15;
}

.ps-sparkline__marker[b-f19kpcyddq] {
    r: 2;
}
/* /Components/UI/Charts/StackedAreaChart.razor.rz.scp.css */
/* The compact absolute stacked-area chart: a short SVG that fills its width and a fixed (caller-set) height so many
   lanes pack per screen. No per-lane Y axis (the scale is implicit, explained once by the page). Token-themed; the
   band fills are inline (per-user palette / Others / Idle tokens). */
.ps-stacked-area[b-rgegc2315x] {
    position: relative;
    inline-size: 100%;
}

.ps-stacked-area__svg[b-rgegc2315x] {
    display: block;
    inline-size: 100%;
    block-size: var(--ps-stacked-height, 2.5rem);
    /* A hairline frame so an all-idle (near-empty) lane is still a visible row. */
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-sm);
    background: var(--ps-color-surface);
    overflow: hidden;
}

.ps-stacked-area__band[b-rgegc2315x] {
    stroke: none;
}

.ps-stacked-area__guide[b-rgegc2315x] {
    stroke: var(--ps-color-text-muted);
    stroke-width: 1;
    stroke-dasharray: 2 2;
    opacity: 0.7;
    pointer-events: none;
}

.ps-stacked-area__hit[b-rgegc2315x] {
    cursor: crosshair;
}

.ps-stacked-area__empty[b-rgegc2315x] {
    margin: 0;
    padding: var(--ps-space-2) 0;
    font-size: var(--ps-text-sm);
}

/* The compact x-axis labels (aggregate only; lanes share one ruler). */
.ps-stacked-area__axis[b-rgegc2315x] {
    display: flex;
    justify-content: space-between;
    margin-block-start: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* The pointer tooltip: anchored along the x by --ps-tooltip-x, floated above the lane. */
.ps-stacked-area__tooltip[b-rgegc2315x] {
    position: absolute;
    inset-block-end: calc(100% + var(--ps-space-1));
    inset-inline-start: clamp(0px, var(--ps-tooltip-x, 50%), 100%);
    transform: translateX(-50%);
    z-index: var(--ps-z-popover);
    min-inline-size: 12rem;
    padding: var(--ps-space-2) var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-raised);
    box-shadow: var(--ps-shadow-2);
    pointer-events: none;
}

.ps-stacked-area__tooltip-time[b-rgegc2315x] {
    margin-block-end: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text-muted);
}

.ps-stacked-area__tooltip-row[b-rgegc2315x] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
}

.ps-stacked-area__tooltip-label[b-rgegc2315x] {
    flex: 1 1 auto;
    min-inline-size: 0;
    overflow-wrap: anywhere;
}

.ps-stacked-area__tooltip-value[b-rgegc2315x] {
    flex: none;
    font-variant-numeric: tabular-nums;
}

.ps-stacked-area__swatch[b-rgegc2315x] {
    flex: none;
    inline-size: 0.7rem;
    block-size: 0.7rem;
    border-radius: var(--ps-radius-sm);
}
/* /Components/UI/Charts/TimelineChart.razor.rz.scp.css */
/* A multi-series line chart that fills its container. The SVG stretches to the pane (non-uniform scale, so the lines
   keep a constant visual weight via vector-effect: non-scaling-stroke); the axis row and legend sit below it. Tokens
   only. */
.ps-timeline[b-3mlids44sv] {
    /* The left value-axis gutter, shared by the plot row and the time-axis row so the X ticks line up under the
       SVG column rather than under the Y labels. */
    --ps-timeline-gutter: 3.5rem;
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
    inline-size: 100%;
}

/* The plot row: a left Y-axis gutter (the value scale) beside the chart pane. The pane flexes to fill the rest. */
.ps-timeline__plot[b-3mlids44sv] {
    display: flex;
    align-items: stretch;
    gap: var(--ps-space-2);
}

/* The pane is the positioning context shared by the SVG and the absolutely-positioned hover tooltip, so the
   tooltip is placed against the very box the SVG fills. It is also a query container so the tooltip can adapt to
   the space the chart actually has (a card in a narrow rail vs. full-width) rather than to the viewport. */
.ps-timeline__pane[b-3mlids44sv] {
    position: relative;
    flex: 1 1 auto;
    min-inline-size: 0;
    container-type: inline-size;
    container-name: ps-timeline-pane;
}

.ps-timeline__svg[b-3mlids44sv] {
    inline-size: 100%;
    block-size: var(--ps-timeline-height, 12rem);
    display: block;
    background: var(--ps-color-surface-sunken);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    overflow: hidden;
}

/* The hover affordances: a vertical guideline on the hovered bucket, the per-series value markers on the curves,
   and transparent full-height hit columns that capture the pointer. The cursor/markers keep a constant visual
   weight under the non-uniform stretch (non-scaling-stroke). The hit columns must show a pointer affordance. */
.ps-timeline__cursor[b-3mlids44sv] {
    stroke: var(--ps-color-border-strong);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
}

.ps-timeline__marker[b-3mlids44sv] {
    stroke: var(--ps-color-surface-raised);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
}

.ps-timeline__hit[b-3mlids44sv] {
    cursor: crosshair;
}

/* The hover tooltip: a raised card anchored to the hovered bucket's horizontal position. The inline custom property
   --ps-timeline-tooltip-x is the bucket's percent across the pane; the translate centers the card on it. The anchor is
   clamped by the card's ≈8rem half-width so the centered card stays inside the pane at the first/last bucket on ANY
   width (not just a narrow container). It rides above the chart on the popover tier and never intercepts the pointer
   (it is a read-only readout). */
.ps-timeline__tooltip[b-3mlids44sv] {
    position: absolute;
    inset-block-start: var(--ps-space-2);
    inset-inline-start: clamp(min(8rem, 50%), var(--ps-timeline-tooltip-x, 50%), calc(100% - min(8rem, 50%)));
    transform: translateX(-50%);
    z-index: var(--ps-z-popover);
    pointer-events: none;
    max-inline-size: min(16rem, 90%);
    padding: var(--ps-space-2) var(--ps-space-3);
    background: var(--ps-color-surface-raised);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    box-shadow: var(--ps-shadow-2);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text);
}

.ps-timeline__tooltip-time[b-3mlids44sv] {
    margin: 0 0 var(--ps-space-1);
    font-weight: var(--ps-weight-semibold);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.ps-timeline__tooltip-rows[b-3mlids44sv] {
    display: grid;
    gap: var(--ps-space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ps-timeline__tooltip-row[b-3mlids44sv] {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: var(--ps-space-2);
}

.ps-timeline__tooltip-label[b-3mlids44sv] {
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--ps-color-text-muted);
}

.ps-timeline__tooltip-value[b-3mlids44sv] {
    font-variant-numeric: tabular-nums;
    color: var(--ps-color-text);
    text-align: end;
}

/* In a narrow pane the centered card would clip at the edges — let it span almost the full pane and pin to the
   start so it stays fully visible (the labels still ellipsize). */
@container ps-timeline-pane (max-width: 20rem) {
    .ps-timeline__tooltip[b-3mlids44sv] {
        inset-inline: var(--ps-space-1) auto;
        transform: none;
        max-inline-size: calc(100% - var(--ps-space-2));
    }
}

/* The Y-axis: each tick is absolutely positioned at its gridline's vertical percent (set inline from the same
   plot geometry the SVG uses), so a label always sits exactly on its line regardless of the pane height. The
   gutter spans the pane height; tabular numerals keep the column from jittering. */
.ps-timeline__y-axis[b-3mlids44sv] {
    position: relative;
    inline-size: var(--ps-timeline-gutter);
    flex: none;
    block-size: var(--ps-timeline-height, 12rem);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    font-variant-numeric: tabular-nums;
}

.ps-timeline__y-tick[b-3mlids44sv] {
    position: absolute;
    inset-inline-end: 0;
    line-height: 1;
    /* Center the label on its gridline (top is the line's position). */
    transform: translateY(-50%);
    white-space: nowrap;
}

.ps-timeline__grid[b-3mlids44sv] {
    stroke: var(--ps-color-border);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
    opacity: 0.6;
}

.ps-timeline__line[b-3mlids44sv] {
    stroke-width: 1.5;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

.ps-timeline__axis[b-3mlids44sv] {
    /* Indent past the Y-axis gutter (+ the plot gap) so a time tick sits under its column in the chart pane. */
    margin-inline-start: calc(var(--ps-timeline-gutter) + var(--ps-space-2));
    display: flex;
    justify-content: space-between;
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    font-variant-numeric: tabular-nums;
}

.ps-timeline__legend[b-3mlids44sv] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ps-space-2) var(--ps-space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ps-timeline__legend-item[b-3mlids44sv] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
    min-inline-size: 0;
}

.ps-timeline__swatch[b-3mlids44sv] {
    inline-size: 0.75rem;
    block-size: 0.75rem;
    border-radius: var(--ps-radius-sm);
    flex: none;
}

.ps-timeline__legend-label[b-3mlids44sv] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-inline-size: 12rem;
}

.ps-timeline__empty[b-3mlids44sv] {
    margin: 0;
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
}

/* The screen-reader data table wrapper. Its child <table> is one row per included bucket (≈1440 at a 24h
   one-minute range before downsampling), and a `display: table` box does NOT honor overflow:hidden/height:1px
   as a clip — so hiding the table directly would still extend the page by one row per bucket. This wrapper is a
   BLOCK, so its overflow:hidden + 1px size genuinely clip the table out of layout AND out of the scroll height.
   Blazor emits scoped CSS UNLAYERED, so this rule wins over the layered global .visually-hidden no matter the
   cascade — defense-in-depth alongside the global class the element also carries. */
.ps-timeline__sr-table[b-3mlids44sv] {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(1px, 1px, 1px, 1px);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
/* /Components/UI/CooldownButton.razor.rz.scp.css */
/* The countdown suffix sits inside the (disabled, dimmed) button; tabular numerals keep it from
   jittering as digits change. */
.ps-cooldown-button__countdown[b-7793cp402w] {
    font-variant-numeric: tabular-nums;
    opacity: 0.8;
}
/* /Components/UI/EmptyState.razor.rz.scp.css */
.ps-empty-state[b-d2ld61ueg2] {
    display: grid;
    justify-items: center;
    gap: var(--ps-space-2);
    /* Roomy but not hollow: the previous 3rem block padding made a one-sentence empty section read as a
       ~250px void inside its card. */
    padding-block: var(--ps-space-8);
    padding-inline: var(--ps-space-4);
    text-align: center;
}

/* The Icon tracks 1em, so the wrapper's font-size is the icon size. */
.ps-empty-state__icon[b-d2ld61ueg2] {
    font-size: var(--ps-text-2xl);
    color: var(--ps-color-text-faint);
}

.ps-empty-state__title[b-d2ld61ueg2] {
    font-weight: var(--ps-weight-semibold);
}

.ps-empty-state__description[b-d2ld61ueg2] {
    max-inline-size: var(--ps-card-min);
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
}

.ps-empty-state__action[b-d2ld61ueg2] {
    margin-block-start: var(--ps-space-2);
}
/* /Components/UI/Icon.razor.rz.scp.css */
/* Sized exclusively from the --ps-icon-size-* tokens; the slight negative baseline shift seats the
   icon optically next to text the way icon fonts did. flex: none keeps flex toolbars from squashing it. */
.ps-icon[b-lw2cycicke] {
    display: inline-block;
    flex: none;
    vertical-align: -0.125em;
}

.ps-icon--text[b-lw2cycicke] {
    inline-size: var(--ps-icon-size-text);
    block-size: var(--ps-icon-size-text);
}

.ps-icon--small[b-lw2cycicke] {
    inline-size: var(--ps-icon-size-sm);
    block-size: var(--ps-icon-size-sm);
}

.ps-icon--medium[b-lw2cycicke] {
    inline-size: var(--ps-icon-size-md);
    block-size: var(--ps-icon-size-md);
}

.ps-icon--large[b-lw2cycicke] {
    inline-size: var(--ps-icon-size-lg);
    block-size: var(--ps-icon-size-lg);
}
/* /Components/UI/KeyValueList.razor.rz.scp.css */
.ps-kv[b-juj9mqcjbz] {
    container: ps-kv / inline-size;
}

.ps-kv__list[b-juj9mqcjbz] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    column-gap: var(--ps-space-6);
}

/* 36rem of container width (literal: var() is illegal in container conditions) fits a ~16rem term column
   beside a still-readable value column; below it terms stack above their values. */
@container ps-kv (min-width: 36rem) {
    .ps-kv__list[b-juj9mqcjbz] {
        grid-template-columns: fit-content(var(--ps-field-min)) minmax(0, 1fr);
    }
}

/* 4xl wall-display tier (160rem literal; mirrors tokens.css — var() is illegal in @media conditions). The detail
   main column FILLS the wide fluid shell at 4K (so its width-hungry tables / output panes use the room), which would
   otherwise stretch a key-value card's reading VALUE column to wall width. Cap the value track at a readable measure
   so the prose stays composed while the surrounding width-hungry content still fills. The cap is nested under the
   same 36rem container condition that unlocks the two-column layout, so a NARROW KV card (e.g. one docked in the
   detail rail) keeps its stacked single-column shape at 4xl. Gated at 4xl so ≤1920 is byte-identical (this is the
   wall-display reading-content cap, the peer of FormField's standalone-control cap). */
@media (min-width: 160rem) {
    @container ps-kv (min-width: 36rem) {
        .ps-kv__list[b-juj9mqcjbz] {
            grid-template-columns: fit-content(var(--ps-field-min)) minmax(0, var(--ps-measure));
        }
    }
}
/* /Components/UI/KeyValueRow.razor.rz.scp.css */
/* grid-column: 1 / -1 + subgrid columns make the row span and inherit the list's track(s): one column in
   a narrow container (dt stacks above dd), two aligned columns in a wide one. The column gap comes from
   the list; the row only spaces its own stacked lines. */
.ps-kv__row[b-k7yl2tyage] {
    display: grid;
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
    row-gap: var(--ps-space-1);
    padding-block: var(--ps-space-2);
    border-block-end: 1px solid var(--ps-color-border);
}

.ps-kv__row:last-child[b-k7yl2tyage] {
    border-block-end: none;
}

.ps-kv__term[b-k7yl2tyage] {
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
}

.ps-kv__description[b-k7yl2tyage] {
    margin: 0;
    min-inline-size: 0;
}
/* /Components/UI/Patterns/ConfirmModal.razor.rz.scp.css */
/* The confirm body sits flush — the modal panel owns the padding. */
.ps-confirm-modal__body[b-hubv6amhtw] {
    margin: 0;
}
/* /Components/UI/Patterns/DataTable.razor.rz.scp.css */
/* Container-first: every threshold below queries the ps-table container (the pane, not the viewport).
   Literal rem thresholds because var() is illegal in container conditions — 48/40rem mirror the md/sm
   breakpoint tiers, 28rem is the card-fallback line from the K plan. */
.ps-table[b-uuao6wth87] {
    container: ps-table / inline-size;
}

.ps-table__table[b-uuao6wth87] {
    inline-size: 100%;
    font-size: var(--ps-text-sm);
    line-height: var(--ps-line-dense);
    color: var(--ps-color-text);
}

/* --- Toolbar: the column-chooser trigger, end-aligned above the table (only rendered with Preferences). --- */
.ps-table__toolbar[b-uuao6wth87] {
    display: flex;
    justify-content: flex-end;
    margin-block-end: var(--ps-space-2);
}

.ps-table__chooser-trigger[b-uuao6wth87] {
    gap: var(--ps-space-1);
}

/* --- Popover surfaces (chooser + per-column value pickers) — native top-layer popovers (zero JS). Each pair
   carries its own anchor (anchor-name on the trigger / position-anchor on the popover, set inline per instance),
   so the popover opens UNDER its trigger, inline-end aligned. Anchor positioning is Baseline 2026; if the
   default placement would overflow the viewport, the @position-try flips it above instead. --- */
.ps-table__chooser[b-uuao6wth87] {
    min-inline-size: 12rem;
    max-block-size: 60vh;
    margin: 0;
    padding: var(--ps-space-2);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-raised);
    box-shadow: var(--ps-shadow-2);
    overflow-y: auto;
    inset: auto;
    position-area: block-end span-inline-start;
    position-try-fallbacks: --ps-table-chooser-above;
}

@position-try --ps-table-chooser-above {
    position-area: block-start span-inline-start;
}

.ps-table__chooser-title[b-uuao6wth87] {
    margin: 0 0 var(--ps-space-1);
    padding-inline: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text-muted);
}

.ps-table__chooser-option[b-uuao6wth87] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    min-block-size: var(--ps-target-min);
    padding-inline: var(--ps-space-1);
    font-size: var(--ps-text-sm);
    white-space: nowrap;
    cursor: pointer;
}

.ps-table__chooser-option:hover[b-uuao6wth87] {
    background: var(--ps-color-surface-sunken);
    border-radius: var(--ps-radius-sm);
}

.ps-table__cell[b-uuao6wth87] {
    padding-block: var(--ps-space-2);
    padding-inline: var(--ps-space-3);
    border-block-end: 1px solid var(--ps-color-border);
    text-align: start;
    vertical-align: middle;
}

.ps-table__cell--end[b-uuao6wth87] {
    text-align: end;
}

/* Sticky header: sticks to the nearest scrollport (the page, or a height-capped wrapper a consumer adds).
   --ps-table-sticky-top lets the shell offset it under a fixed topbar. The opaque background is what rows
   scrolling beneath disappear behind; override --ps-table-head-background when the table sits on a card. */
.ps-table__cell--head[b-uuao6wth87] {
    position: sticky;
    inset-block-start: var(--ps-table-sticky-top, 0);
    z-index: 1;
    background: var(--ps-table-head-background, var(--ps-color-surface));
    border-block-end: 1px solid var(--ps-color-border-strong);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text-muted);
    white-space: nowrap;
}

.ps-table__sort[b-uuao6wth87] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-block-size: var(--ps-target-min);
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

/* --- Per-column filter row: a second header row, sticky just under the labels. Each control fills its
   cell (the bare .ps-control sets no width) so the inputs never overlap (the control-overlap probe). --- */
.ps-table__cell--filter[b-uuao6wth87] {
    position: sticky;
    inset-block-start: calc(var(--ps-table-sticky-top, 0) + var(--ps-control-height));
    z-index: 1;
    padding-block: var(--ps-space-1);
    background: var(--ps-table-head-background, var(--ps-color-surface));
    border-block-end: 1px solid var(--ps-color-border);
    font-weight: var(--ps-weight-normal);
}

.ps-table__filter-input[b-uuao6wth87] {
    inline-size: 100%;
    min-block-size: var(--ps-target-min);
    font-size: var(--ps-text-xs);
}

.ps-table__filter-values[b-uuao6wth87] {
    display: flex;
    justify-content: flex-start;
}

.ps-table__cell--end .ps-table__filter-values[b-uuao6wth87] {
    justify-content: flex-end;
}

.ps-table__filter-trigger[b-uuao6wth87] {
    gap: var(--ps-space-1);
}

.ps-table__filter-count[b-uuao6wth87] {
    min-inline-size: 1.25rem;
    padding-inline: var(--ps-space-1);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-semibold);
}

.ps-table__sort:hover[b-uuao6wth87] {
    color: var(--ps-color-text);
}

th[aria-sort] .ps-table__sort[b-uuao6wth87] {
    color: var(--ps-color-text);
}

.ps-table__cell--end .ps-table__sort[b-uuao6wth87] {
    justify-content: flex-end;
}

/* --- Rows. --- */
.ps-table__row--clickable[b-uuao6wth87] {
    cursor: pointer;
}

.ps-table__row--clickable:hover[b-uuao6wth87] {
    background: var(--ps-color-surface-sunken);
}

.ps-table__row--clickable:focus-visible[b-uuao6wth87] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: -2px;
}

/* --- Whole-row link (RowHref): the primary cell hosts a REAL <a>, and its stretched ::after overlay covers the
   entire row, so the BROWSER navigates on a click anywhere in the row — independent of any Blazor @onclick handler
   lifecycle (a self-refreshing table's live re-render cannot silently drop the click) and with native keyboard /
   middle-click / screen-reader behavior. All rules are scoped to --linked rows, so non-link tables are unchanged.
   An interactive (StopRowClick) cell lifts above the overlay so its controls stay clickable. --- */
.ps-table__row--linked[b-uuao6wth87] {
    position: relative;
}

.ps-table__row-link[b-uuao6wth87] {
    display: block;
    color: inherit;
    text-decoration: none;
}

.ps-table__row-link[b-uuao6wth87]::after {
    content: "";
    position: absolute;
    inset: 0;
}

.ps-table__row-link:focus-visible[b-uuao6wth87]::after {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: -2px;
}

.ps-table__row--linked .ps-table__cell--fenced[b-uuao6wth87] {
    position: relative;
    z-index: 1;
}

.ps-table__cell--empty[b-uuao6wth87] {
    padding: 0;
}

/* --- Virtualized density: the fixed-height contract Virtualize requires (rows clip to one line). --- */
.ps-table--virtualized .ps-table__row[b-uuao6wth87] {
    block-size: var(--ps-row-height);
}

.ps-table--virtualized .ps-table__cell[b-uuao6wth87] {
    padding-block: 0;
    white-space: nowrap;
}

/* The empty row is a message surface, not a data row: it wraps freely (a nowrap EmptyState description
   would stretch the document past a phone viewport). */
.ps-table--virtualized .ps-table__cell--empty[b-uuao6wth87] {
    white-space: normal;
}

/* --- Column-priority reveal (cards below 28rem re-show every column, so the hide bands are gated). --- */
@container ps-table (min-width: 28rem) and (max-width: 47.9375rem) {
    .ps-table:not(.ps-table--virtualized) .ps-table__cell--p3[b-uuao6wth87] {
        display: none;
    }
}

@container ps-table (min-width: 28rem) and (max-width: 39.9375rem) {
    .ps-table:not(.ps-table--virtualized) .ps-table__cell--p2[b-uuao6wth87] {
        display: none;
    }
}

/* Virtualized tables never card-ify (identical-height contract), so their bands run all the way down. */
@container ps-table (max-width: 47.9375rem) {
    .ps-table--virtualized .ps-table__cell--p3[b-uuao6wth87] {
        display: none;
    }
}

@container ps-table (max-width: 39.9375rem) {
    .ps-table--virtualized .ps-table__cell--p2[b-uuao6wth87] {
        display: none;
    }
}

/* --- Group-header rows (RowGroupHeader): a full-width bracket row opening each row group (e.g. an atomic
   replica group of gang members). A quiet section heading inside the body: sunken background, semibold
   small text, a strong top rule so the bracket reads as "these rows belong together". --- */
.ps-table__cell--group[b-uuao6wth87] {
    padding-block: var(--ps-space-1);
    background: var(--ps-color-surface-sunken);
    border-block-start: 1px solid var(--ps-color-border-strong);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text-muted);
}

/* --- Card fallback below 28rem: rows become labeled cards projected from the same columns. The display
   changes would strip the native table semantics, which is why the markup carries explicit ARIA roles;
   the header row hides ACCESSIBLY (the .visually-hidden recipe inline, since scoped css cannot reuse the
   global class) so columnheaders survive for assistive tech. --- */
@container ps-table (max-width: 27.9375rem) {
    /* Small-screen feature shedding: the complex per-column filter row sheds at the card tier (the column
       chooser + sort survive — they ride the toolbar / card-mode header). The core read path is intact. */
    .ps-table:not(.ps-table--virtualized) .ps-table__filter-row[b-uuao6wth87] {
        display: none;
    }

    .ps-table:not(.ps-table--virtualized) .ps-table__table[b-uuao6wth87],
    .ps-table:not(.ps-table--virtualized) .ps-table__body[b-uuao6wth87] {
        display: block;
    }

    .ps-table:not(.ps-table--virtualized) .ps-table__head[b-uuao6wth87] {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }

    .ps-table:not(.ps-table--virtualized) .ps-table__row[b-uuao6wth87] {
        display: grid;
        gap: var(--ps-space-2);
        padding: var(--ps-space-3);
        border: 1px solid var(--ps-color-border);
        border-radius: var(--ps-radius-lg);
        background: var(--ps-color-surface-raised);
        margin-block-end: var(--ps-space-3);
    }

    .ps-table:not(.ps-table--virtualized) .ps-table__cell[b-uuao6wth87] {
        display: block;
        /* Grid items keep a min-width:auto floor; releasing it lets truncating/clamping cell content
           size to the CARD instead of forcing the card as wide as its longest nowrap line. */
        min-inline-size: 0;
        padding: 0;
        border: 0;
        text-align: start;
    }

    /* Every cell self-labels from its column header… */
    .ps-table:not(.ps-table--virtualized) .ps-table__cell[b-uuao6wth87]::before {
        content: attr(data-label);
        display: block;
        margin-block-end: calc(var(--ps-space-1) / 2);
        font-size: var(--ps-text-xs);
        font-weight: var(--ps-weight-medium);
        color: var(--ps-color-text-muted);
    }

    /* …except the first column, which IS the card title, and the structural empty/skeleton rows. */
    .ps-table:not(.ps-table--virtualized) .ps-table__cell:first-child[b-uuao6wth87]::before,
    .ps-table:not(.ps-table--virtualized) .ps-table__cell--empty[b-uuao6wth87]::before,
    .ps-table:not(.ps-table--virtualized) .ps-table__row--skeleton .ps-table__cell[b-uuao6wth87]::before {
        content: none;
    }

    .ps-table:not(.ps-table--virtualized) .ps-table__cell:first-child[b-uuao6wth87] {
        font-size: var(--ps-text-md);
        font-weight: var(--ps-weight-semibold);
    }
}

/* 4xl wall-display tier (160rem literal; mirrors tokens.css — var() is illegal in @media conditions, and this is a
   wall-display COMPOSITION concern, not a container-relative one). A per-column text filter caps at a readable field
   width so it does not grow absurdly wide when its column is wide on an ultrawide display; it still fills a narrow
   cell below the cap. Gated at 4xl so ≤1920 is byte-identical. */
@media (min-width: 160rem) {
    .ps-table__filter-input[b-uuao6wth87] {
        max-inline-size: var(--ps-field-min);
    }
}

/* Card mode (below 28rem): the group bracket flattens into a compact section heading between the row cards —
   never a bordered card of its own (the row-card recipe above would otherwise style it as one). */
@container ps-table (max-width: 27.9375rem) {
    .ps-table:not(.ps-table--virtualized) .ps-table__group-row[b-uuao6wth87] {
        padding: var(--ps-space-1) var(--ps-space-2);
        border: 0;
        border-radius: 0;
        background: none;
        margin-block-end: var(--ps-space-1);
    }

    .ps-table:not(.ps-table--virtualized) .ps-table__cell--group[b-uuao6wth87] {
        background: none;
        border-block-start: 0;
    }
}
/* /Components/UI/Patterns/DetailLayout.razor.rz.scp.css */
/* Single column: the at-a-glance rail leads the section stack. Both cells release their min-content
   floor so wide table/code content scrolls inside its own container instead of widening the page. */
.ps-detail[b-phve8utmnw] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--ps-space-4);
    align-items: start;
}

.ps-detail__rail[b-phve8utmnw],
.ps-detail__main[b-phve8utmnw] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-4);
    min-inline-size: 0;
}

/* 3xl (120rem literal — var() is illegal in media conditions; mirrors tokens.css): the multi-pane tier.
   The rail docks at the inline end and pins under the sticky topbar (--ps-table-sticky-top is set by the
   shell on its content scroller). */
@media (min-width: 120rem) {
    .ps-detail[b-phve8utmnw] {
        grid-template-columns: minmax(0, 1fr) var(--ps-detail-rail-width);
        gap: var(--ps-space-6);
    }

    .ps-detail__main[b-phve8utmnw] {
        grid-column: 1;
        grid-row: 1;
    }

    .ps-detail__rail[b-phve8utmnw] {
        grid-column: 2;
        grid-row: 1;
        position: sticky;
        inset-block-start: calc(var(--ps-table-sticky-top, 0px) + var(--ps-space-4));
    }
}

/* 4xl wall-display tier (160rem literal; mirrors tokens.css) — widen the docked rail and the gutter. The pair's
   readable BOUND (--ps-detail-max, centered per the shell's large-screen composition rule — MainLayout.razor.css)
   is owned by the PAGE root's .ps-content--detail utility, so the masthead above this grid caps and centers WITH
   it; without the bound the main column stretched across the full fluid shell (~3000px key-value rows on a 4K
   wall). Single-rail two-pane is kept (no third rail); the rail's sticky docking carries over from the 3xl tier.
   ≤1920 unchanged. */
@media (min-width: 160rem) {
    .ps-detail[b-phve8utmnw] {
        grid-template-columns: minmax(0, 1fr) var(--ps-detail-rail-width-wide);
        gap: var(--ps-space-8);
    }
}
/* /Components/UI/Patterns/Disclosure.razor.rz.scp.css */
/* The collapsible sub-region: a quiet full-width toggle row over the panel. The row is a real button, so it
   carries the control height (the ≥24px target floor) and wraps rather than clipping its summary. */
.ps-disclosure[b-2qmt57pgug] {
    min-inline-size: 0;
}

.ps-disclosure__toggle[b-2qmt57pgug] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    inline-size: 100%;
    min-block-size: var(--ps-target-min);
    padding-block: var(--ps-space-1);
    padding-inline: var(--ps-space-2);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-sunken);
    color: var(--ps-color-text);
    font: inherit;
    font-size: var(--ps-text-sm);
    text-align: start;
    cursor: pointer;
}

.ps-disclosure__toggle:hover[b-2qmt57pgug] {
    border-color: var(--ps-color-accent);
}

.ps-disclosure__toggle:focus-visible[b-2qmt57pgug] {
    outline: 2px solid var(--ps-color-focus);
    outline-offset: 2px;
}

/* The label shrinks and wraps its OWN text rather than being pushed onto a line of its own: a label a few pixels
   too wide for a phone-width card otherwise left the chevron sitting alone on the first row. */
.ps-disclosure__label[b-2qmt57pgug] {
    min-inline-size: 0;
    font-weight: var(--ps-weight-medium);
}

/* The collapsed summary states the configuration; it wraps (never truncates) so the operator always reads it whole. */
.ps-disclosure__summary[b-2qmt57pgug] {
    min-inline-size: 0;
    color: var(--ps-color-text-muted);
}

.ps-disclosure__panel[b-2qmt57pgug] {
    margin-block-start: var(--ps-space-3);
    min-inline-size: 0;
}
/* /Components/UI/Patterns/Drawer.razor.rz.scp.css */
.ps-drawer[b-yh1sygzqyl] {
    /* Dock to the inline-end edge: kill the UA's auto-centering margins and the fit-content caps. */
    margin: 0;
    margin-inline-start: auto;
    inline-size: min(var(--ps-drawer-width), 100vw);
    max-inline-size: 100vw;
    block-size: 100dvh;
    max-block-size: 100dvh;
    padding: 0;
    border: 0;
    border-inline-start: 1px solid var(--ps-color-border);
    border-radius: 0;
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    box-shadow: var(--ps-shadow-3);
}

/* See Modal.razor.css on backdrop custom-property inheritance. */
.ps-drawer[b-yh1sygzqyl]::backdrop {
    background: var(--ps-color-backdrop);
}

/* Entry slide from the end edge (physical X — the app is LTR); instant under reduced motion (0ms tokens),
   instant exit by design. */
.ps-drawer[open][b-yh1sygzqyl] {
    transition: transform var(--ps-motion-medium) var(--ps-ease);
}

@starting-style {
    .ps-drawer[open][b-yh1sygzqyl] {
        transform: translateX(100%);
    }
}

.ps-drawer__panel[b-yh1sygzqyl] {
    display: flex;
    flex-direction: column;
    block-size: 100%;
    min-block-size: 0;
}

.ps-drawer__header[b-yh1sygzqyl] {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--ps-space-3);
    padding-block: var(--ps-space-3);
    padding-inline: var(--ps-space-4);
    border-block-end: 1px solid var(--ps-color-border);
}

.ps-drawer__heading[b-yh1sygzqyl] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.ps-drawer__title[b-yh1sygzqyl] {
    margin: 0;
    font-size: var(--ps-text-lg);
    font-weight: var(--ps-weight-semibold);
    line-height: var(--ps-line-heading);
}

.ps-drawer__body[b-yh1sygzqyl] {
    flex: 1 1 auto;
    padding: var(--ps-space-4);
    overflow-y: auto;
    min-block-size: 0;
}

.ps-drawer__footer[b-yh1sygzqyl] {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--ps-space-2);
    padding-block: var(--ps-space-3);
    padding-inline: var(--ps-space-4);
    border-block-start: 1px solid var(--ps-color-border);
}
/* /Components/UI/Patterns/FilterBar.razor.rz.scp.css */
.ps-filterbar[b-5qlv2bckfp] {
    container: ps-filterbar / inline-size;
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.ps-filterbar__row[b-5qlv2bckfp] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2) var(--ps-space-3);
}

.ps-filterbar__search[b-5qlv2bckfp] {
    /* Grows to a readable search width, then stops (--ps-field-max): an unbounded 1 1 basis let the search input
       swallow the whole first row on a desktop pane, pushing every other filter control onto a second line and
       making the bar read as a giant search page. Capped, the search and the filter cluster share one line
       wherever they fit; narrow panes still wrap below (the container query keeps the phone behavior). */
    flex: 1 1 var(--ps-field-min);
    max-inline-size: var(--ps-field-max);
    min-inline-size: 0;
}

.ps-filterbar__filters[b-5qlv2bckfp] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

/* The slot owns the layout: the search input fills the flexing slot (its SKIN is the shared .ps-control
   class — controls.css — which deliberately sets no width). */
.ps-filterbar__search[b-5qlv2bckfp]  input {
    inline-size: 100%;
}

/* Below the sm-ish container tier (literal: var() is illegal in container conditions) the search input
   takes the whole first line and the filter cluster wraps beneath it. */
@container ps-filterbar (max-width: 35.9375rem) {
    .ps-filterbar__search[b-5qlv2bckfp] {
        flex-basis: 100%;
    }
}

.ps-filterbar__chips[b-5qlv2bckfp] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
}

.ps-filterbar__chip-list[b-5qlv2bckfp] {
    /* The list is semantic only; the chips join the chips row's own flex flow. */
    display: contents;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ps-filterbar__chip[b-5qlv2bckfp] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    min-block-size: var(--ps-target-min);
    padding-inline: var(--ps-space-2) var(--ps-space-1);
    border: 1px solid color-mix(in srgb, var(--ps-color-accent) 25%, transparent);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-accent-soft);
    color: var(--ps-color-accent);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-medium);
    white-space: nowrap;
}

.ps-filterbar__chip-remove[b-5qlv2bckfp] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--ps-target-min);
    min-block-size: var(--ps-target-min);
    padding: 0;
    border: 0;
    border-radius: var(--ps-radius-full);
    background: none;
    color: inherit;
    cursor: pointer;
}

.ps-filterbar__chip-remove:hover[b-5qlv2bckfp] {
    background: color-mix(in srgb, var(--ps-color-accent) 15%, transparent);
}
/* /Components/UI/Patterns/FormField.razor.rz.scp.css */
.ps-form-field[b-gsx2vfhswr] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

/* Spans the FormSection RAM grid; grid-column on the item needs no reach into the parent's stylesheet. */
.ps-form-field--full[b-gsx2vfhswr] {
    grid-column: 1 / -1;
}

.ps-form-field__label[b-gsx2vfhswr] {
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
}

.ps-form-field__required[b-gsx2vfhswr] {
    margin-inline-start: calc(var(--ps-space-1) / 2);
    color: var(--ps-color-danger);
}

/* Help text is PROSE, so it takes the same readable measure cap FormSection's description does — without it a
   field's two-sentence explanation runs the whole width of a wide form column as one unreadable line. */
.ps-form-field__help[b-gsx2vfhswr] {
    max-inline-size: var(--ps-measure);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
}

/* --- The control SKIN itself is the shared .ps-control class (wwwroot/css/controls.css — the one skin
   source FormField and FilterBar children both consume); the consumer puts it on the slotted control.
   Here live only the field-scoped layout and validation reaches: a field's text control fills its grid
   column, and any error message paints the control border. --- */
.ps-form-field__control[b-gsx2vfhswr]  .ps-control:not([type="checkbox"]):not([type="radio"]) {
    inline-size: 100%;
}

/* A single labeled control caps at a readable measure (--ps-field-max) at EVERY width, so a lone FormField placed
   directly in a Card body (one NOT sized by a FormSection RAM grid cell, e.g. the api-keys "Preset" select or a
   form's Name input) never stretches a select/input across the whole form column — the same readable-control rule
   on a 1440 laptop and a 4K wall. Inside a FormSection the grid cell is already ≤ this cap, so the bound stays
   inert there; a FullWidth field is exempt because it is deliberately wide (a command/URL/textarea spanning the
   whole grid). The control stays inline-size:100% of this capped box, so it still fills a narrow cell. */
.ps-form-field:not(.ps-form-field--full) .ps-form-field__control[b-gsx2vfhswr] {
    max-inline-size: var(--ps-field-max);
}

.ps-form-field--invalid[b-gsx2vfhswr]  .ps-control {
    border-color: var(--ps-color-danger);
}
/* /Components/UI/Patterns/FormLayout.razor.rz.scp.css */
/* Single column at every width below 3xl: the form column, with the actions as a sticky bottom bar beneath it.
   The cells release their min-content floor so wide table/code content scrolls inside its own container rather
   than widening the page. */
.ps-form-layout[b-154ocv9ddh] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--ps-space-4);
    align-items: start;
}

.ps-form-layout__main[b-154ocv9ddh] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-4);
    min-inline-size: 0;
}

/* The sticky bottom action bar (shown below 3xl): pinned to the viewport bottom (dvh-safe), submit on the
   trailing edge. The grid is taller than the viewport on a real form, so the bar floats over the scroll. */
.ps-form-layout__bar[b-154ocv9ddh] {
    position: sticky;
    inset-block-end: 0;
    z-index: var(--ps-z-sticky);
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: var(--ps-space-2);
    padding-block: var(--ps-space-3);
    margin-block-start: var(--ps-space-2);
    border-block-start: 1px solid var(--ps-color-border);
    background: var(--ps-color-surface);
}

/* The context rail is a 3xl-only affordance: hidden until the multi-pane tier unlocks. */
.ps-form-layout__aside[b-154ocv9ddh] {
    display: none;
}

/* 3xl (120rem literal — var() is illegal in media conditions; mirrors tokens.css): the multi-pane tier. The
   form keeps its readable column; the Aside (live summary/preview + the actions) docks at the inline end and
   pins under the sticky topbar, and the bottom bar gives way to the rail's copy of the actions. */
@media (min-width: 120rem) {
    .ps-form-layout[b-154ocv9ddh] {
        grid-template-columns: minmax(0, 1fr) var(--ps-detail-rail-width);
        gap: var(--ps-space-6);
    }

    .ps-form-layout__main[b-154ocv9ddh] {
        grid-column: 1;
        grid-row: 1;
    }

    .ps-form-layout__bar[b-154ocv9ddh] {
        display: none;
    }

    .ps-form-layout__aside[b-154ocv9ddh] {
        grid-column: 2;
        grid-row: 1;
        display: flex;
        flex-direction: column;
        gap: var(--ps-space-4);
        min-inline-size: 0;
        position: sticky;
        inset-block-start: calc(var(--ps-table-sticky-top, 0px) + var(--ps-space-4));
    }

    /* The rail's action cluster mirrors the bottom bar's trailing-edge layout. */
    .ps-form-layout__rail-actions[b-154ocv9ddh] {
        display: flex;
        flex-wrap: wrap;
        gap: var(--ps-space-2);
        padding-block-start: var(--ps-space-3);
        border-block-start: 1px solid var(--ps-color-border);
    }
}

/* 4xl wall-display tier (160rem literal; mirrors tokens.css) — cap the form column at a readable max
   (--ps-form-max) AND dock the context Aside at a capped rail width (--ps-detail-rail-width-wide, the same
   pinned-side-context width DetailLayout uses), so the form + rail compose to a readable pair and the surplus
   consolidates into ONE calm trailing gutter. A 1fr Aside instead claimed ALL the surplus (~2400px at 4K),
   stranding a thin summary/status card in a wall-wide empty band rather than an honest gutter. ≤1920 unchanged. */
@media (min-width: 160rem) {
    .ps-form-layout[b-154ocv9ddh] {
        grid-template-columns: minmax(0, var(--ps-form-max)) var(--ps-detail-rail-width-wide);
        gap: var(--ps-space-8);
        /* The capped form + rail pair CENTERS in the shell column (the large-screen composition rule —
           MainLayout.razor.css): surplus splits into symmetric gutters instead of one trailing void. */
        justify-content: center;
    }
}
/* /Components/UI/Patterns/FormSection.razor.rz.scp.css */
.ps-form-section[b-dbf1fmmpdw] {
    min-inline-size: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.ps-form-section__legend[b-dbf1fmmpdw] {
    /* Legends ignore normal flow; float resets it so the description/grid stack beneath predictably. */
    float: inline-start;
    inline-size: 100%;
    padding: 0;
    font-size: var(--ps-text-lg);
    font-weight: var(--ps-weight-semibold);
    line-height: var(--ps-line-heading);
}

.ps-form-section__description[b-dbf1fmmpdw] {
    margin: 0;
    margin-block-start: var(--ps-space-1);
    clear: both;
    max-inline-size: var(--ps-measure);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

.ps-form-section__fields[b-dbf1fmmpdw] {
    clear: both;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--ps-field-min), 100%), 1fr));
    gap: var(--ps-space-4);
    margin-block-start: var(--ps-space-3);
}
/* /Components/UI/Patterns/Modal.razor.rz.scp.css */
.ps-modal[b-uin8hh7o0g] {
    padding: 0;
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-lg);
    background: var(--ps-color-surface-raised);
    color: var(--ps-color-text);
    box-shadow: var(--ps-shadow-3);
    max-block-size: calc(100dvh - var(--ps-space-12));
}

/* The UA centers an open dialog (margin auto in the top layer); only the width tiers vary. */
.ps-modal--small[b-uin8hh7o0g] {
    inline-size: min(var(--ps-modal-width-sm), calc(100vw - var(--ps-space-8)));
}

.ps-modal--medium[b-uin8hh7o0g] {
    inline-size: min(var(--ps-modal-width-md), calc(100vw - var(--ps-space-8)));
}

.ps-modal--large[b-uin8hh7o0g] {
    inline-size: min(var(--ps-modal-width-lg), calc(100vw - var(--ps-space-8)));
}

/* ::backdrop inherits custom properties from the dialog per the updated spec (all evergreen engines by
   2026); on an engine predating that the backdrop simply stays transparent — the modal itself still works. */
.ps-modal[b-uin8hh7o0g]::backdrop {
    background: var(--ps-color-backdrop);
}

/* Entry fade+drop via @starting-style; the motion tokens are 0ms unless the user allows motion, so this
   is reduced-motion-clean with no extra query. Exit is deliberately instant (animating display:none would
   need allow-discrete juggling for no real UX gain). */
.ps-modal[open][b-uin8hh7o0g] {
    transition:
        opacity var(--ps-motion-medium) var(--ps-ease),
        transform var(--ps-motion-medium) var(--ps-ease);
}

@starting-style {
    .ps-modal[open][b-uin8hh7o0g] {
        opacity: 0;
        transform: translateY(var(--ps-space-2));
    }
}

.ps-modal__panel[b-uin8hh7o0g] {
    display: flex;
    flex-direction: column;
    max-block-size: calc(100dvh - var(--ps-space-12));
    min-block-size: 0;
}

.ps-modal__header[b-uin8hh7o0g] {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--ps-space-3);
    padding-block: var(--ps-space-3);
    padding-inline: var(--ps-space-4);
    border-block-end: 1px solid var(--ps-color-border);
}

.ps-modal__heading[b-uin8hh7o0g] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.ps-modal__title[b-uin8hh7o0g] {
    margin: 0;
    font-size: var(--ps-text-lg);
    font-weight: var(--ps-weight-semibold);
    line-height: var(--ps-line-heading);
}

.ps-modal__body[b-uin8hh7o0g] {
    padding: var(--ps-space-4);
    overflow-y: auto;
    min-block-size: 0;
}

.ps-modal__footer[b-uin8hh7o0g] {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--ps-space-2);
    padding-block: var(--ps-space-3);
    padding-inline: var(--ps-space-4);
    border-block-start: 1px solid var(--ps-color-border);
}
/* /Components/UI/Patterns/PageHeader.razor.rz.scp.css */
.ps-page-header[b-rkv80af4on] {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--ps-space-3) var(--ps-space-4);
}

/* A page-FUNCTION masthead with no actions carries only its visually-hidden h1, which is position:absolute and
   therefore not a flex item at all. The header box around it would still be one, spending a whole --ps-stack-gap
   on an invisible row at the top of the page — display:contents removes the box so the deleted heading's space
   collapses cleanly (the ruling's no-orphan-margin requirement). With actions the header stays a real row. */
.ps-page-header--boxless[b-rkv80af4on] {
    display: contents;
}

.ps-page-header__text[b-rkv80af4on] {
    min-inline-size: 0;
}

.ps-page-header__title[b-rkv80af4on] {
    /* base.css already sizes h1; restated so the component reads correctly while Bootstrap coexists. */
    font-size: var(--ps-text-2xl);
    line-height: var(--ps-line-heading);
    font-weight: var(--ps-weight-semibold);
    margin: 0;
}

.ps-page-header__description[b-rkv80af4on] {
    margin: 0;
    margin-block-start: var(--ps-space-1);
    max-inline-size: var(--ps-measure);
    font-size: var(--ps-text-sm);
    color: var(--ps-color-text-muted);
}

/* The Toolbar root is the child component's element, so it needs a ::deep reach from the scoped header. */
.ps-page-header[b-rkv80af4on]  .ps-page-header__actions {
    margin-inline-start: auto;
}
/* /Components/UI/Patterns/StatTileGrid.razor.rz.scp.css */
/* The RAM grid (repeat, auto-fill, minmax): min() prevents phone overflow when the pane is narrower than
   the basis; auto-fill keeps phantom tracks so a half-empty 4K row packs instead of stretching the tiles.
   A4xl note: this is auto-fill at a fixed readable basis, NOT a hard column cap — a real many-tile dashboard
   packs across the wide shell, while a sparse demo leaves a right gutter by design (tiles stay readable, never
   ballooned to fill 4K). */
.ps-tile-grid[b-wrx4ilqaub] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--ps-tile-grid-min, var(--ps-tile-min)), 100%), 1fr));
    gap: var(--ps-space-4);
    /* Tiles size to their own content and align at the top: a short Stat next to a tall Gauge no longer both
       stretch to the row's tallest height (which left their bodies — gauge centered, stat top-aligned) visibly
       misaligned). auto-fill above already keeps a half-empty wide row packed instead of over-widening tiles. */
    align-items: start;
}
/* /Components/UI/Patterns/WideGrid.razor.rz.scp.css */
/* Below the 4xl tier: a plain vertical stack — flex column with the stack gap token. This is byte-identical to the
   ps-stack utility (same display, direction, and --ps-stack-gap default), so a page that swaps its outer ps-stack for
   a WideGrid is unchanged at ≤1920. The full-span class is inert here (one column already spans the full width). */
.ps-wide-grid[b-jbkn02pq5b] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-stack-gap, var(--ps-space-4));
}

/* 4xl wall-display tier (160rem literal — var() is illegal in @media conditions; mirrors tokens.css). The page's
   stacked sections reflow into TWO equal columns: minmax(0, 1fr) so a wide table/chart never widens its own track past
   the column, align-items:start so a short card next to a tall one does not stretch. The surplus 4K width becomes a
   second column of useful content instead of a margin — reducing vertical scrolling. ≤1920 unchanged. */
@media (min-width: 160rem) {
    .ps-wide-grid[b-jbkn02pq5b] {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: var(--ps-wide-grid-gap);
        align-items: start;
    }

    /* A full-span row: width-hungry content (timelines, output/log panes, wide tables, lifecycle ribbons) spans BOTH
       columns so it is never halved below its own collapse thresholds. The full-span element is page CHILD CONTENT (the
       page's scope, not WideGrid's), so the selector must ::deep past the scope boundary to reach it — a plain scoped
       selector would carry WideGrid's scope attribute and never match the differently-scoped child. */
    .ps-wide-grid[b-jbkn02pq5b]  .ps-wide-grid__full {
        grid-column: 1 / -1;
    }
}
/* /Components/UI/ProgressMeter.razor.rz.scp.css */
.ps-meter[b-uq7tmwymvw] {
    display: grid;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

.ps-meter__header[b-uq7tmwymvw] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ps-space-3);
    font-size: var(--ps-text-sm);
}

.ps-meter__label[b-uq7tmwymvw] {
    color: var(--ps-color-text);
    font-weight: var(--ps-weight-medium);
}

.ps-meter__value[b-uq7tmwymvw] {
    color: var(--ps-color-text-muted);
    font-variant-numeric: tabular-nums;
}

.ps-meter__track[b-uq7tmwymvw] {
    block-size: var(--ps-space-2);
    border-radius: var(--ps-radius-full);
    background: var(--ps-color-surface-sunken);
    border: 1px solid var(--ps-color-border);
    overflow: hidden;
}

.ps-meter__fill[b-uq7tmwymvw] {
    block-size: 100%;
    border-radius: var(--ps-radius-full);
    transition: inline-size var(--ps-motion-medium) var(--ps-ease);
}

.ps-meter--neutral .ps-meter__fill[b-uq7tmwymvw] {
    background: var(--ps-color-border-strong);
}

.ps-meter--info .ps-meter__fill[b-uq7tmwymvw] {
    background: var(--ps-color-info);
}

.ps-meter--active .ps-meter__fill[b-uq7tmwymvw] {
    background: var(--ps-color-accent);
}

.ps-meter--success .ps-meter__fill[b-uq7tmwymvw] {
    background: var(--ps-color-success);
}

.ps-meter--warning .ps-meter__fill[b-uq7tmwymvw] {
    background: var(--ps-color-warning);
}

.ps-meter--danger .ps-meter__fill[b-uq7tmwymvw] {
    background: var(--ps-color-danger);
}
/* /Components/UI/SegmentedMeter.razor.rz.scp.css */
/* The segmented meter: [label] [track] [total] on one baseline row. The label column is a custom property so a host
   (a device row, an inset engine panel) can retune that gutter without a second component. The TAIL column is
   `auto` — sized by the reading's own fixed reservation below, NOT by the reading — so every bar in a card still
   ends at the same x. Tokens only; light-dark aware through the token layer. */
.ps-segmeter[b-sc5bb1k51k] {
    display: grid;
    grid-template-columns: var(--ps-segmeter-label-size, 2.6em) 1fr auto;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

/* A meter inside a row that already carries its own caption (a GPU row's SM/Mem pair) drops the label column. */
.ps-segmeter--unlabelled[b-sc5bb1k51k] {
    grid-template-columns: 1fr auto;
}

.ps-segmeter__label[b-sc5bb1k51k] {
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    text-align: end;
    font-variant-caps: all-small-caps;
    letter-spacing: 0.02em;
}

/* The track is the POSITIONING CONTEXT of the popover as well as the idle background: idle is never drawn, so the
   track's own surface is the reading for "unused". It must NOT clip (the popover escapes it), so the clipping job
   belongs to the fills layer below. */
.ps-segmeter__track[b-sc5bb1k51k] {
    position: relative;
    block-size: var(--ps-segmeter-track-size, 0.625rem);
    border-radius: var(--ps-radius-sm);
    background: var(--ps-color-surface-sunken);
    border: 1px solid var(--ps-color-border);
    min-inline-size: 0;
}

/* The segment row, clipped to the track: hairline minimums can push the drawn total past the track's width, and the
   surplus must be cut off rather than spill across the reading beside it. */
.ps-segmeter__fills[b-sc5bb1k51k] {
    position: absolute;
    inset: 0;
    display: flex;
    overflow: hidden;
    border-radius: inherit;
}

.ps-segmeter__segment[b-sc5bb1k51k] {
    block-size: 100%;
    flex: 0 0 auto;
    /* A real but tiny share must still read as a mark rather than vanish — the meter's whole point is that presence
       is visible before magnitude is. */
    min-inline-size: 1px;
    transition: inline-size var(--ps-motion-medium) var(--ps-ease);
}

.ps-segmeter__segment:first-child[b-sc5bb1k51k] {
    border-start-start-radius: var(--ps-radius-sm);
    border-end-start-radius: var(--ps-radius-sm);
}

/* The reading, and the RESERVATION that holds it. The width is FIXED — identical on every meter — because that is
   what lines the bars up down a card: a reader compares SM against SM instead of chasing a ragged edge. What it is
   fixed AT is the width of the widest reading the meter can ever show, "100%", plus a hair.
   The unit is `ch` OF THIS ELEMENT — the advance of a digit in the reading's own tabular type. That is the whole
   point of declaring the reservation here rather than as a grid track on the parent: the parent's `em` is the HOST's
   font (a card's 16px, a device row's 15px, an inset panel's own), so the same reservation had to be re-guessed per
   host and every guess was too generous — the tail reserved 44.8px for a 37.2px reading, and a right-aligned "7%"
   ended up sitting nearer the NEXT bar than its own. In `ch` the reservation tracks the actual font: "100%" measures
   ~4.5ch across the sans faces this renders in (three digits are 3ch by definition and the percent sign runs
   ~1.5ch), so 4.8ch holds it everywhere with a fraction of a character to spare and hands the rest to the bar. */
.ps-segmeter__total[b-sc5bb1k51k] {
    inline-size: var(--ps-segmeter-total-size, 4.8ch);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text);
    text-align: end;
    font-variant-numeric: tabular-nums;
}

/* The dense variant for a device row: a thinner track, so a GPU row with two meters stays a single line. */
.ps-segmeter--dense[b-sc5bb1k51k] {
    --ps-segmeter-track-size: 0.5rem;
    gap: var(--ps-space-1);
}

/* --- The hover popover: the full per-user breakdown of THIS bar. --- */
/* Pointer-only by construction (the accessible path is the visually-hidden mirror), so it never intercepts a
   pointer and never becomes a focus stop. It rides the popover z tier and is anchored to the track's start edge,
   pulled inside the viewport by the max() clamp on narrow cards. */
.ps-segmeter__tip[b-sc5bb1k51k] {
    display: none;
    position: absolute;
    inset-block-start: calc(100% + var(--ps-space-1));
    inset-inline-start: 0;
    z-index: var(--ps-z-popover);
    min-inline-size: 11rem;
    max-inline-size: 20rem;
    padding: var(--ps-space-2) var(--ps-space-3);
    border: 1px solid var(--ps-color-border);
    border-radius: var(--ps-radius-md);
    background: var(--ps-color-surface-raised);
    box-shadow: var(--ps-shadow-3);
    pointer-events: none;
    font-size: var(--ps-text-xs);
}

.ps-segmeter__track:hover > .ps-segmeter__tip[b-sc5bb1k51k] {
    display: block;
}

.ps-segmeter__tip-title[b-sc5bb1k51k] {
    margin-block-end: var(--ps-space-1);
    color: var(--ps-color-text-muted);
    font-variant-caps: all-small-caps;
    letter-spacing: 0.02em;
}

.ps-segmeter__tip-row[b-sc5bb1k51k] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    padding-block: 1px;
    font-variant-numeric: tabular-nums;
}

.ps-segmeter__tip-row--idle[b-sc5bb1k51k] {
    color: var(--ps-color-text-muted);
}

.ps-segmeter__tip-dot[b-sc5bb1k51k] {
    inline-size: 0.5rem;
    block-size: 0.5rem;
    border-radius: var(--ps-radius-full);
    border: 1px solid var(--ps-color-border);
    flex: none;
}

.ps-segmeter__tip-name[b-sc5bb1k51k] {
    flex: 1 1 auto;
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-segmeter__tip-value[b-sc5bb1k51k] {
    flex: none;
}
/* /Components/UI/Skeleton.razor.rz.scp.css */
/* Bones are painted with the border tokens — the one neutral pair that contrasts against EVERY surface
   tier in both schemes (surface-on-surface would all but vanish on the dark page background). The shimmer
   sweeps a border-strong sheen; duration derives from the motion tokens (0ms under reduced-motion = a
   static placeholder). */
.ps-skeleton[b-jz9n95dh6p] {
    background: linear-gradient(
        100deg,
        var(--ps-color-border) 40%,
        color-mix(in srgb, var(--ps-color-border) 50%, var(--ps-color-border-strong)) 50%,
        var(--ps-color-border) 60%
    );
    background-size: 200% 100%;
    animation: ps-skeleton-shimmer-b-jz9n95dh6p calc(var(--ps-motion-slow) * 5) linear infinite;
}

.ps-skeleton--text[b-jz9n95dh6p] {
    block-size: 1em;
    inline-size: 100%;
    border-radius: var(--ps-radius-sm);
}

.ps-skeleton--block[b-jz9n95dh6p] {
    block-size: var(--ps-space-16);
    inline-size: 100%;
    border-radius: var(--ps-radius-md);
}

.ps-skeleton--circle[b-jz9n95dh6p] {
    block-size: var(--ps-space-10);
    inline-size: var(--ps-space-10);
    border-radius: var(--ps-radius-full);
}

@keyframes ps-skeleton-shimmer-b-jz9n95dh6p {
    from {
        background-position: 200% 0;
    }

    to {
        background-position: -200% 0;
    }
}
/* /Components/UI/Stat.razor.rz.scp.css */
.ps-stat[b-juitf2uf5s] {
    display: grid;
    gap: var(--ps-space-1);
    min-inline-size: 0;
}

.ps-stat__label[b-juitf2uf5s] {
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-medium);
}

.ps-stat__value[b-juitf2uf5s] {
    color: var(--ps-color-text);
    font-size: var(--ps-text-2xl);
    font-weight: var(--ps-weight-semibold);
    line-height: var(--ps-line-heading);
    font-variant-numeric: tabular-nums;
}

.ps-stat__meta[b-juitf2uf5s] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
}

.ps-stat__delta[b-juitf2uf5s] {
    font-weight: var(--ps-weight-medium);
    font-variant-numeric: tabular-nums;
}

.ps-stat__delta--neutral[b-juitf2uf5s] {
    color: var(--ps-color-text-muted);
}

.ps-stat__delta--positive[b-juitf2uf5s] {
    color: var(--ps-color-success);
}

.ps-stat__delta--negative[b-juitf2uf5s] {
    color: var(--ps-color-danger);
}

.ps-stat__hint[b-juitf2uf5s] {
    color: var(--ps-color-text-muted);
}

/* Textual (identity / unit-bearing) values — "worker #0", "node-a", "74.5 GiB": the numeral display scale
   mid-wraps them inside a rail tile. Opt-in via class="ps-stat--text": the value steps down to the large
   text size and wraps between words only. */
.ps-stat--text .ps-stat__value[b-juitf2uf5s] {
    font-size: var(--ps-text-lg);
    overflow-wrap: break-word;
}
/* /Components/UI/StatusBadge.razor.rz.scp.css */
/* A pill whose soft fill + strong text are AA pairs from the status tokens; the hairline border keeps the
   pill legible when its soft fill sits on a same-family surface (cards in dark scheme). Active is the one
   solid fill — see the component comment. */
.ps-badge[b-yhhvlo2611] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    padding-block: calc(var(--ps-space-1) / 2);
    padding-inline: var(--ps-space-2);
    border: 1px solid transparent;
    border-radius: var(--ps-radius-full);
    font-size: var(--ps-text-xs);
    font-weight: var(--ps-weight-medium);
    line-height: var(--ps-line-dense);
    white-space: nowrap;
}

.ps-badge--neutral[b-yhhvlo2611] {
    background: var(--ps-color-surface-sunken);
    border-color: var(--ps-color-border);
    color: var(--ps-color-text-muted);
}

.ps-badge--info[b-yhhvlo2611] {
    background: var(--ps-color-info-soft);
    border-color: color-mix(in srgb, var(--ps-color-info) 25%, transparent);
    color: var(--ps-color-info);
}

.ps-badge--active[b-yhhvlo2611] {
    background: var(--ps-color-accent);
    border-color: var(--ps-color-accent);
    color: var(--ps-color-on-accent);
}

.ps-badge--success[b-yhhvlo2611] {
    background: var(--ps-color-success-soft);
    border-color: color-mix(in srgb, var(--ps-color-success) 25%, transparent);
    color: var(--ps-color-success);
}

.ps-badge--warning[b-yhhvlo2611] {
    background: var(--ps-color-warning-soft);
    border-color: color-mix(in srgb, var(--ps-color-warning) 25%, transparent);
    color: var(--ps-color-warning);
}

.ps-badge--danger[b-yhhvlo2611] {
    background: var(--ps-color-danger-soft);
    border-color: color-mix(in srgb, var(--ps-color-danger) 25%, transparent);
    color: var(--ps-color-danger);
}
/* /Components/UI/Toolbar.razor.rz.scp.css */
.ps-toolbar[b-hf3grgbxya] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

.ps-toolbar__end[b-hf3grgbxya] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ps-space-2);
    margin-inline-start: auto;
}
/* /Components/UsageDonut.razor.rz.scp.css */
/* A breakdown card: a centered title, the donut glyph, then a legend list. Sits inside a dashboard Card. */
.usage-donut[b-nk1qer6tkf] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ps-space-3);
    block-size: 100%;
}

.usage-donut__title[b-nk1qer6tkf] {
    margin: 0;
    font-size: var(--ps-text-sm);
    font-weight: var(--ps-weight-semibold);
    color: var(--ps-color-text);
    text-align: center;
}

.usage-donut__empty[b-nk1qer6tkf] {
    margin: 0;
    padding-block: var(--ps-space-6);
    color: var(--ps-color-text-muted);
    font-size: var(--ps-text-sm);
}

.usage-donut__legend[b-nk1qer6tkf] {
    inline-size: 100%;
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-1);
}

.usage-donut__legend-item[b-nk1qer6tkf] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    font-size: var(--ps-text-sm);
    min-inline-size: 0;
}

.usage-donut__swatch[b-nk1qer6tkf] {
    inline-size: 0.75rem;
    block-size: 0.75rem;
    border-radius: var(--ps-radius-sm);
    border: 1px solid var(--ps-color-border);
    flex: none;
}

.usage-donut__legend-label[b-nk1qer6tkf] {
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--ps-color-text);
}

.usage-donut__legend-value[b-nk1qer6tkf] {
    margin-inline-start: auto;
    font-variant-numeric: tabular-nums;
    color: var(--ps-color-text-muted);
}
/* /Components/UsageLeaderboardChart.razor.rz.scp.css */
/* The board: the ranked bars, with the zero-tail expander sitting under them as its own line. The bars STRETCH to
   the card's width — the chart is an inline-size container, so its own width cannot come from its contents, and
   shrink-wrapping it (align-items: flex-start) collapses every bar to nothing. Only the fold shrinks to its text. */
.usage-leaderboard[b-1hw6q4exik] {
    display: flex;
    flex-direction: column;
    gap: var(--ps-space-2);
}

/* The in-place expander: a link-toned text button, so the fold reads as "there is more here", not as a control bar
   (the same idiom the cluster card's user table folds its tail with). */
.usage-leaderboard__fold[b-1hw6q4exik] {
    align-self: flex-start;
    padding: 0;
    border: none;
    background: none;
    color: var(--ps-color-accent);
    font: inherit;
    font-size: var(--ps-text-sm);
    cursor: pointer;
}

.usage-leaderboard__fold:hover[b-1hw6q4exik] {
    text-decoration: underline;
}
/* /Components/UsageUserTable.razor.rz.scp.css */
/* The horizontal scroll shell — see the component's note. Sideways only: the page owns the one VERTICAL scroller. */
.ps-usertable-scroll[b-sq8vwsdu7v] {
    overflow-x: auto;
}

/* The per-occupant share table: dense and tabular, filling its card while every metric column holds one narrow
   reading. The MIN is the width below which the name column stops being a name; past it the shell scrolls rather
   than truncating every identity to two letters. Tokens only. */
.ps-usertable[b-sq8vwsdu7v] {
    inline-size: 100%;
    min-inline-size: 26rem;
    border-collapse: collapse;
    font-size: var(--ps-text-xs);
    font-variant-numeric: tabular-nums;
    table-layout: fixed;
}

.ps-usertable th[b-sq8vwsdu7v],
.ps-usertable td[b-sq8vwsdu7v] {
    padding: 1px var(--ps-space-1);
    font-weight: var(--ps-weight-normal);
    text-align: end;
    vertical-align: baseline;
}

/* NO rule under the header row: small-caps in the muted tone already separates the headings from the data, and the
   border was reading as a divider between the card's meters and its table — a line the card does not need. */
.ps-usertable thead th[b-sq8vwsdu7v] {
    color: var(--ps-color-text-muted);
    font-variant-caps: all-small-caps;
    letter-spacing: 0.02em;
}

/* The metric columns are the narrow ones; the leading user column absorbs the rest of the row. */
.ps-usertable__metric[b-sq8vwsdu7v] {
    inline-size: 3.4em;
}

.ps-usertable__user[b-sq8vwsdu7v] {
    text-align: start;
}

/* The swatch + name pair. It is an inner box, NOT the cell: the cell must stay a real table-cell to be the frozen
   first column below (a flex `th` gets wrapped in an anonymous table-cell, and a sticky box cannot escape the
   anonymous cell it lives in — it would read as "sticky" and never move). */
.ps-usertable__identity[b-sq8vwsdu7v] {
    display: flex;
    align-items: center;
    gap: var(--ps-space-2);
    min-inline-size: 0;
}

/* --- The FROZEN identity column. --- */
/* Every row's first cell stays put while the shell scrolls sideways, so the reader dragging a metric column into
   view never loses the name it belongs to. Three things make it work:
   - It must be OPAQUE, because the scrolled data cells pass UNDERNEATH it. The card's own surface is the background
     that makes the frozen cell disappear into the card in BOTH themes (the token is light-dark aware) and in both
     card variants that render this table. The one card variant with a different surface — a DARK node card, painted
     sunken — renders no table at all (an offline node claims nothing), so there is no second background to match.
   - `th:first-child` deliberately, not every first cell: the fold row's cell spans EVERY column, so there is
     nothing to freeze there and pinning it would drag the expander across the data.
   - z-index 1 is all a positioned cell needs to paint over the unpositioned ones sliding beneath it, and it is
     deliberately far BELOW --ps-z-popover: a meter's hover breakdown hangs down over this table from the bars above
     and must keep painting on top of the frozen column. */
.ps-usertable tr > th:first-child[b-sq8vwsdu7v] {
    position: sticky;
    inset-inline-start: 0;
    z-index: 1;
    background: var(--ps-color-surface-raised);
}

.ps-usertable__dot[b-sq8vwsdu7v] {
    inline-size: 0.5rem;
    block-size: 0.5rem;
    border-radius: var(--ps-radius-full);
    border: 1px solid var(--ps-color-border);
    flex: none;
}

/* A username can be one unbreakable token; ellipsize rather than widen the card. */
.ps-usertable__name[b-sq8vwsdu7v] {
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A metric this occupant has no share of: "—", faint. Deliberately NOT "0%", which claims a measured zero. */
.ps-usertable__value--absent[b-sq8vwsdu7v] {
    color: var(--ps-color-text-faint);
}

/* The two pseudo-occupants read as capacity accounting, not identities: muted throughout. */
.ps-usertable__row--idle[b-sq8vwsdu7v],
.ps-usertable__row--system[b-sq8vwsdu7v] {
    color: var(--ps-color-text-muted);
}

.ps-usertable__fold-row td[b-sq8vwsdu7v] {
    text-align: start;
    padding-block-start: var(--ps-space-1);
}

/* The in-place expander: a link-toned text button, so the fold reads as "there is more here", not as a control bar. */
.ps-usertable__fold[b-sq8vwsdu7v] {
    padding: 0;
    border: none;
    background: none;
    color: var(--ps-color-accent);
    font: inherit;
    cursor: pointer;
}

.ps-usertable__fold:hover[b-sq8vwsdu7v] {
    text-decoration: underline;
}

/* --- The NARROW card: the identity column stops FILLING and takes only the name. --- */
/* Below this the card cannot give the table its 26rem fill measure, so the shell scrolls sideways (accepted) — but
   with `table-layout: fixed` the leading column still absorbs ALL of the slack, which on a phone is most of the
   card: the reader scrolls past a wide band of white space before the first reading appears. Sizing the table to its
   CONTENT instead makes the identity column exactly as wide as the widest name it actually rendered, so the metric
   columns arrive immediately. `max-content` rather than `auto`, because a table's `auto` width is shrink-to-fit —
   it would squeeze the columns to the card instead of scrolling, which is the truncation this shell exists to avoid.

   Keyed on the CARD (a container query — the idiom the node cards already use for their GPU-row fold) and never on
   the window: the same card is narrow in a one-column phone layout and wide on a wall display, and the shell's
   sidebar and gutters make the two numbers different anyway. WIDER cards keep today's fill behavior untouched. */
@container (max-width: 30rem) {
    .ps-usertable[b-sq8vwsdu7v] {
        inline-size: max-content;
        min-inline-size: 0;
        table-layout: auto;
    }

    /* The long-name pathology: one absurd identity must not push the data columns back off-screen. The name caps at
       a measure that still reads as a name and ellipsizes past it — the ellipsis rule above is already in force, so
       this only supplies the definite width it needs. */
    .ps-usertable__name[b-sq8vwsdu7v] {
        max-inline-size: 12ch;
    }
}
/* /Components/WorkerLog/WorkerLogSection.razor.rz.scp.css */
/* The per-ENTRY worker-log styling — the shared chrome (stack, toolbar clusters, trimmed banner, scroll viewport,
   auto-scroll) lives in OutputPane.razor.css. CSS isolation scopes these to THIS component, which authors the per-entry
   markup, so the level colors + prefixes + exception disclosure belong here. */

.worker-log__filter[b-vispqffvgr] {
    align-items: center;
    gap: var(--ps-space-1);
}

/* Layout-only override on the canonical .ps-control skin: the select sizes to its content rather than filling the
   toolbar (the skin supplies border / radius / padding / min-height / the ≥16px iOS-no-zoom font). */
.worker-log__select[b-vispqffvgr] {
    inline-size: auto;
}

.worker-log__toggle[b-vispqffvgr] {
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    font-size: var(--ps-text-xs);
    color: var(--ps-color-text-muted);
    cursor: pointer;
}

.worker-log__entry[b-vispqffvgr] {
    display: block;
}

.worker-log__line[b-vispqffvgr] {
    display: block;
}

/* The always-on per-level color: each entry's MESSAGE reads in its severity hue, resolved through the design-system
   status tokens so both light and dark schemes keep accessible contrast (no ad-hoc colors). Verbose/Debug recede to
   muted (AA on the sunken viewport), Information is the body text, Warning/Error/Fatal escalate through the status hues;
   Fatal adds weight. Severity is never color-only — the always-on level prefix (default) carries a textual cue too. */
.worker-log__entry--verbose[b-vispqffvgr] { color: var(--ps-color-text-muted); }
.worker-log__entry--debug[b-vispqffvgr] { color: var(--ps-color-text-muted); }
.worker-log__entry--information[b-vispqffvgr] { color: var(--ps-color-text); }
.worker-log__entry--warning[b-vispqffvgr] { color: var(--ps-color-warning); }
.worker-log__entry--error[b-vispqffvgr] { color: var(--ps-color-danger); }
.worker-log__entry--fatal[b-vispqffvgr] {
    color: var(--ps-color-danger);
    font-weight: var(--ps-weight-bold);
}
.worker-log__entry--unspecified[b-vispqffvgr] { color: var(--ps-color-text-muted); }

/* The display prefixes recede so the message leads; the level prefix keeps its entry's severity color (inherited),
   while time + category read as muted metadata (muted, not faint, so they clear AA contrast on the sunken viewport). */
.worker-log__prefix[b-vispqffvgr] {
    margin-inline-end: var(--ps-space-2);
    user-select: none;
}

.worker-log__prefix--time[b-vispqffvgr],
.worker-log__prefix--category[b-vispqffvgr] {
    color: var(--ps-color-text-muted);
}

.worker-log__prefix--level[b-vispqffvgr] {
    font-weight: var(--ps-weight-semibold);
}

.worker-log__prefix--category[b-vispqffvgr] {
    font-style: italic;
}

/* The expandable exception disclosure: a compact summary that opens to the monospace stack trace, indented so it reads
   as belonging to its entry. The stack stays within the bounded scroll viewport (it wraps rather than widening it). */
.worker-log__exception[b-vispqffvgr] {
    margin-block-start: var(--ps-space-1);
    margin-inline-start: var(--ps-space-3);
    color: var(--ps-color-danger);
    font-size: var(--ps-text-xs);
}

.worker-log__exception > summary[b-vispqffvgr] {
    cursor: pointer;
    user-select: none;
    display: inline-flex;
    align-items: center;
    gap: var(--ps-space-1);
    /* Keep the disclosure toggle above the 24px interactive-target floor at the phone width (WCAG 2.5.8). */
    min-block-size: var(--ps-target-min);
}

/* The disclosure chevron carries the open/closed STATE on its own (inline-flex on the summary removes the native
   ::marker triangle, and the static TriangleAlert never changes): it points right when collapsed, rotating down on
   open. The rotation rides the motion token, so reduced-motion renders the flip instantly. The chevron is an <Icon>
   svg authored by the child Icon component, so it does NOT carry THIS component's CSS-isolation scope — ::deep reaches
   it as a descendant of the scoped <summary>/<details> (the ::deep idiom this codebase uses to style nested components). */
.worker-log__exception > summary[b-vispqffvgr]  .worker-log__exception-chevron {
    display: inline-flex;
    transition: transform var(--ps-motion-fast) var(--ps-ease);
}

.worker-log__exception[open] > summary[b-vispqffvgr]  .worker-log__exception-chevron {
    transform: rotate(90deg);
}

.worker-log__stack[b-vispqffvgr] {
    margin: var(--ps-space-1) 0 0;
    padding: var(--ps-space-2) var(--ps-space-3);
    background: var(--ps-color-surface-raised);
    border-radius: var(--ps-radius-sm);
    color: var(--ps-color-text-muted);
    white-space: pre-wrap;
    word-break: break-word;
}

.detail-fine[b-vispqffvgr] {
    font-size: var(--ps-text-xs);
}
/* /Components/WorkerSettingsOverrideEditor.razor.rz.scp.css */
.editor-note[b-k6h7hza86m] {
    margin: 0;
    font-size: var(--ps-text-sm);
}

/* Short numeric siblings pack side by side when the hosting pane allows (the RAM pattern). The 13rem basis keeps
   each cell wide enough for its two-or-three-word label to read on one-to-two lines instead of mid-wrapping. */
.editor-fields[b-k6h7hza86m] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(13rem, 100%), 1fr));
    gap: var(--ps-space-3);
}

/* The action cluster rides the trailing edge (primary last), mirroring FormLayout's bottom-bar convention. */
.editor-actions[b-k6h7hza86m] {
    justify-content: flex-end;
}
/* /Components/YieldAuditPanel.razor.rz.scp.css */
/* The audit table's fine print: the requester display name leads, the scheme + device UUID read muted. */
.yield-audit__requester[b-ky60ykdewi] {
    font-weight: var(--ps-weight-semibold);
}

.detail-fine[b-ky60ykdewi] {
    font-size: var(--ps-text-sm);
}

/* The request→apply latency under the applied instant: fine print kept on one line, so a narrow Applied cell wraps
   the sentence as a whole rather than orphaning "after the request" from its figure. */
.yield-audit__latency[b-ky60ykdewi] {
    white-space: nowrap;
}

/* The device UUID renders compact (a leading prefix, the full value on hover) like the member id's ShortId, so it
   never widens the target cell; keep the wrap guard as a backstop for any non-abbreviated value. */
.yield-audit__device code[b-ky60ykdewi] {
    overflow-wrap: anywhere;
}

/* The operator-supplied reason (up to 512 chars) reads as muted fine print under the requester; clamp it to two
   lines with an ellipsis so a long reason never blows out the row — the full text stays on hover via the title. */
.yield-audit__reason[b-ky60ykdewi] {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}

/* In the DataTable card-mode (below 28rem — phone / narrow touch), there is no hover to reveal the clamped title, so
   the reason for a destructive yield would be unreadable. Drop the clamp at the card breakpoint (mirroring the
   table's own card fallback at the same container width) so the full text wraps in the card. */
@container ps-table (max-width: 27.9375rem) {
    .yield-audit__reason[b-ky60ykdewi] {
        display: block;
        -webkit-line-clamp: none;
        line-clamp: none;
        overflow: visible;
    }
}
