/* /Components/Autopilot/ActivityStrip.razor.rz.scp.css */
/* Autopilot: cross-device live-session strip at the top of hud-center. */
.activity-strip[b-2ev0adtrtk] {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: 1rem;
}

.session-card[b-2ev0adtrtk] {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    padding: .4rem .7rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    font: inherit;
    color: var(--ink);
    transition: border-color .15s ease;
}
.session-card:hover[b-2ev0adtrtk] { border-color: var(--brand); }

.session-card.running[b-2ev0adtrtk]::before {
    content: "";
    flex: 0 0 auto;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: var(--ok);
    animation: sa-pulse 1.2s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .session-card.running[b-2ev0adtrtk]::before { animation: none; } }

.session-card.awaiting[b-2ev0adtrtk] { border-color: var(--warn-border); background: var(--warn-bg-soft); }
.session-card.awaiting[b-2ev0adtrtk]::before {
    content: "";
    flex: 0 0 auto;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: var(--warn);
}

.sess-device[b-2ev0adtrtk] { color: var(--muted); font-size: .78rem; }
.sess-project[b-2ev0adtrtk] { font-weight: 600; }
.session-card .wo-num[b-2ev0adtrtk] { color: var(--muted); font-variant-numeric: tabular-nums; font-size: .8rem; }
.sess-status[b-2ev0adtrtk] { font-size: .78rem; color: var(--muted); }
.session-card.awaiting .sess-status[b-2ev0adtrtk] { color: var(--warn); font-weight: 600; }
.sess-cost[b-2ev0adtrtk] { font-variant-numeric: tabular-nums; font-size: .8rem; color: var(--muted); }
/* /Components/Autopilot/IdeasPanel.razor.rz.scp.css */
/* Autopilot M3: co-located styles for IdeasPanel. Self-contained and mapped onto the Hub's real
   :root tokens (--surface, --surface-2, --border, --muted, --ink, --radius, --shadow-sm, --brand,
   --danger). The submit box sits atop a column of idea rows; a pending row (mutation in flight) dims
   and blocks pointer input; the credit-note flags a paused (usage-limit) state. */

.ideas-panel[b-yppso0070s] {
    display: flex;
    flex-direction: column;
    gap: .35rem;
}

.idea-new[b-yppso0070s] {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    padding: .5rem .6rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
/* The textarea is sa-textarea now; keep the compact inline-capture height + disabled cue. */
.idea-new textarea.new-idea[b-yppso0070s] { min-height: 3.2rem; resize: none; }
.idea-new textarea.new-idea:disabled[b-yppso0070s] { opacity: .5; cursor: not-allowed; }
/* Submit is sa-btn sa-btn-sm now; keep the brand-outline emphasis + flex placement. */
.idea-new .submit[b-yppso0070s] {
    align-self: flex-start;
    border-color: var(--brand);
    color: var(--brand);
    font-weight: 600;
}

/* Usage-limit note: idea processing paused. */
.credit-note[b-yppso0070s] {
    margin: 0;
    padding: .35rem .6rem;
    color: var(--danger);
    font-size: .85rem;
}

.idea-row[b-yppso0070s] {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    padding: .5rem .6rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
/* A pending row (mutation in flight) dims slightly and blocks pointer input. */
.idea-row.pending[b-yppso0070s] {
    opacity: .6;
    pointer-events: none;
}
.idea-text[b-yppso0070s] {
    flex: 1;
    color: var(--ink);
}
.idea-status[b-yppso0070s] {
    color: var(--muted);
    font-size: .85rem;
    white-space: nowrap;
}
/* Process is sa-btn sa-btn-sm now — no scoped geometry needed. */

/* WO-020: inline edit. The textarea takes the flexible middle (where .idea-text sits in read mode);
   compact + resize:none, matching the capture box (auto-grow is handled globally by
   autogrow-textarea.js). Save carries the brand-outline emphasis (mirrors the submit button). */
.idea-edit[b-yppso0070s] {
    flex: 1;
    min-height: 3.2rem;
    resize: none;
}
.idea-row .save-edit[b-yppso0070s] {
    border-color: var(--brand);
    color: var(--brand);
    font-weight: 600;
}
@media (max-width: 900px) {
    .idea-edit[b-yppso0070s] { min-height: 2.8rem; }
}

/* WO-001: lazily-loaded processed section at the bottom of the pane. */
.processed-block[b-yppso0070s] {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    margin-top: .35rem;
}
.processed-toggle[b-yppso0070s] {
    align-self: flex-start;
    color: var(--muted);
}
/* Processed rows read as history: slightly recessed against the live column above.
   WO-028: they also wrap, so the idea text can lead on its own line as primary content while the
   timestamp / status / chips fall to a second, muted metadata line (see .idea-text below). */
.idea-row.processed[b-yppso0070s] {
    background: var(--surface-2);
    flex-wrap: wrap;
    row-gap: .35rem;
}
/* WO-028: the idea text is the PRIMARY content of a processed row. It is hoisted above the
   metadata (order:-1 — the <time> stays the first DOM child, honouring the WO-006 contract; only
   its visual position moves) and given the full row width (flex-basis 100%), which forces the
   timestamp / status / chips to wrap onto a second line beneath it. Long idea text now wraps
   cleanly at readable line-height instead of being crammed against the leading metadata, and the
   metadata can never be squeezed or overlapped by it. Colour stays --ink (inherited from the base
   .idea-text), so contrast against the recessed --surface-2 matches the active rows. */
.idea-row.processed .idea-text[b-yppso0070s] {
    order: -1;
    flex: 1 0 100%;
    line-height: 1.45;
    overflow-wrap: break-word;
}
/* WO-006/WO-028: the processed-at timestamp leads the muted metadata line beneath the text.
   Muted + non-shrinking so it reads as a caption, not primary content. */
.idea-row.processed .idea-when[b-yppso0070s] {
    flex: 0 0 auto;
    color: var(--muted);
    font-size: .85rem;
    white-space: nowrap;
}
/* At the ≤900px shell breakpoint trim the timestamp label and let several WO chips wrap rather than
   overflow, so the metadata line reflows readably in a narrow column (the text already leads on its
   own full-width line, so the whole row stacks cleanly on a phone). */
@media (max-width: 900px) {
    .idea-row.processed .idea-when[b-yppso0070s] { font-size: .8rem; }
    .idea-row.processed .idea-chips[b-yppso0070s] { flex-wrap: wrap; }
}
.processed-loading[b-yppso0070s],
.processed-end[b-yppso0070s] {
    margin: 0;
    padding: .35rem .6rem;
    color: var(--muted);
    font-size: .85rem;
}
/* The observer needs a box; keep it invisible and out of layout flow. */
.processed-sentinel[b-yppso0070s] {
    height: 1px;
}

/* Idea queue (2026-07-19): the active-row status label and the processed row's created-WO chips. */
.idea-status.status-queued[b-yppso0070s] { opacity: .85; }
.idea-status.status-processing[b-yppso0070s] { font-style: italic; }

.idea-chips[b-yppso0070s] {
    display: inline-flex;
    gap: .25rem;
    margin-left: .5rem;
}
.wo-chip[b-yppso0070s] {
    display: inline-block;
    padding: 0 .45rem;
    border-radius: var(--radius);
    font-size: .75rem;
    line-height: 1.4;
    color: var(--muted);
    background: var(--surface-2);
    border: 1px solid var(--border);
    white-space: nowrap;
}

/* WO-002: Delete + its inline confirm. Danger-tinted; confirm-yes is the destructive action. */
.idea-row .delete[b-yppso0070s] {
    border-color: var(--danger);
    color: var(--danger);
}
.confirm-delete[b-yppso0070s] {
    display: inline-flex;
    align-items: baseline;
    gap: .4rem;
    white-space: nowrap;
}
.confirm-text[b-yppso0070s] {
    color: var(--danger);
    font-size: .85rem;
}
.confirm-delete .confirm-yes[b-yppso0070s] {
    border-color: var(--danger);
    color: var(--danger);
    font-weight: 600;
}

/* WO-015: phone-width stacking for the ACTIVE idea rows. The row is a flat flex line
   (align-items: baseline) whose children are .idea-text (flex:1) followed by the status label and
   the nowrap Edit / Process / Delete buttons; at true phone widths those non-shrinking buttons eat
   60–70% of the line and crush the text into a narrow left column that wraps over many lines.

   Breakpoint is 600px — deliberately TIGHTER than the shell's ≤900px media query (used elsewhere in
   this file). 900px is tablet width where the inline layout still reads fine; the crushing only bites
   at real phone widths, so the stack kicks in there and the wide/tablet layout is untouched. Scoped to
   :not(.processed): processed rows carry no buttons and don't crowd, and they keep their own ≤900px
   rule above.

   Effect: flex-wrap lets the row break onto multiple lines; .idea-text / .idea-edit take the full card
   width (flex-basis 100%) on the first line, so the status label + buttons (and the inline edit's
   Save/Cancel and the delete-confirm) wrap onto a following line that justify-content: flex-end pushes
   to the right of the card. */
@media (max-width: 600px) {
    .idea-row:not(.processed)[b-yppso0070s] {
        flex-wrap: wrap;
        justify-content: flex-end;
    }
    .idea-row:not(.processed) .idea-text[b-yppso0070s],
    .idea-edit[b-yppso0070s] {
        flex: 1 0 100%;
    }
    /* The delete-confirm ("Delete this idea? / Delete / Cancel") is nowrap inline-flex; let it wrap
       inside the card so it can't overflow horizontally on a narrow phone row. */
    .confirm-delete[b-yppso0070s] {
        flex-wrap: wrap;
        white-space: normal;
    }
}
/* /Components/Autopilot/MultiLineList.razor.rz.scp.css */
/* Layout only — visuals come from the shared sa-input / sa-btn classes. */
.mll-label[b-skjhbmz4v6] {
    display: block;
    font-weight: 600;
    font-size: .82rem;
    color: var(--muted);
    margin-bottom: .3rem;
}
.mll-row[b-skjhbmz4v6] {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin-bottom: .35rem;
}
/* /Components/Autopilot/ParkedPanel.razor.rz.scp.css */
/* Autopilot M3: co-located styles for ParkedPanel. Self-contained and mapped onto the Hub's real
   :root tokens (--surface, --surface-2, --border, --muted, --ink, --radius, --shadow-sm, --brand,
   --danger) rather than any undefined upstream token set. Parked rows are actionable cards; a
   ReadyToResume row uses the base look; a Resolved row is dimmed/struck with no controls; a pending
   row (mutation in flight) dims and blocks pointer input. */

.parked-panel[b-rqb5akl9te] {
    display: flex;
    flex-direction: column;
    gap: .35rem;
}

.parked-row[b-rqb5akl9te] {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    padding: .5rem .6rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--brand);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
.parked-row.ready[b-rqb5akl9te] { border-left-color: var(--muted); }

/* Resolved rows are answered/history: dimmed and struck, no action controls rendered. */
.parked-row.resolved[b-rqb5akl9te] {
    opacity: .55;
    border-left-color: var(--border);
    box-shadow: none;
    background: var(--surface-2);
}
.parked-row.resolved .parked-reason[b-rqb5akl9te],
.parked-row.resolved .parked-question[b-rqb5akl9te] { text-decoration: line-through; }

/* A pending row (mutation in flight) dims slightly and blocks pointer input. */
.parked-row.pending[b-rqb5akl9te] {
    opacity: .6;
    pointer-events: none;
}

.parked-head[b-rqb5akl9te] {
    display: flex;
    align-items: baseline;
    gap: .5rem;
}
.wo-num[b-rqb5akl9te] {
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    font-size: .85rem;
}
.parked-reason[b-rqb5akl9te] { font-weight: 600; }
.parked-question[b-rqb5akl9te] {
    margin: 0;
    color: var(--ink);
}
/* WO-078: the operator's typed answer, rendered under the question on a ReadyToResume / processed
   row so the Q→A pair reads at a glance. A brand-accented, indented block distinguishes it from the
   question above. pre-wrap keeps a multi-line answer's newlines; overflow-wrap breaks a long unbroken
   token so the block wraps inside the row and never widens the panel or forces horizontal page scroll,
   including at the ≤900px shell breakpoint. */
.parked-answer[b-rqb5akl9te] {
    margin: .25rem 0 0;
    padding-left: .5rem;
    border-left: 2px solid var(--brand);
    color: var(--ink);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* WO-048: the grant-refusal / repeat-park note — a muted advisory line, not an error banner
   (approving still resumes; this only explains why it can't widen the allowlist). */
.parked-grant-note[b-rqb5akl9te] {
    margin: .25rem 0 0;
    font-size: .85rem;
    color: var(--muted, #8a7f6a);
}

/* WO-049: the budget park's cost-of-continuing hint — a muted advisory line (like the grant note),
   so "Continue" is read as "one more fresh-budget session", not "finish it whatever it takes". */
.parked-budget-hint[b-rqb5akl9te] {
    margin: .25rem 0 0;
    font-size: .85rem;
    color: var(--muted, #8a7f6a);
}

/* The textarea is sa-textarea now; keep the compact inline-answer height + disabled cue. */
.parked-row textarea.answer[b-rqb5akl9te] { min-height: 3.2rem; resize: none; }
.parked-row textarea.answer:disabled[b-rqb5akl9te] { opacity: .5; cursor: not-allowed; }

.parked-actions[b-rqb5akl9te] {
    display: flex;
    gap: .35rem;
}
/* Action buttons are sa-btn sa-btn-sm now; only the brand emphasis stays scoped. */
.parked-actions .answer[b-rqb5akl9te] {
    border-color: var(--brand);
    color: var(--brand);
    font-weight: 600;
}
/* WO-030: Approve is the primary one-click affordance — brand-filled to lead the Answer/Resume pair. */
.parked-actions .approve[b-rqb5akl9te] {
    border-color: var(--brand);
    background: var(--brand);
    color: #fff;
    font-weight: 600;
}

/* WO-016: lazily-loaded processed (Resolved) section at the bottom of the pane (mirrors IdeasPanel). */
.processed-block[b-rqb5akl9te] {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    margin-top: .35rem;
}
.processed-toggle[b-rqb5akl9te] {
    align-self: flex-start;
    color: var(--muted);
}
/* A processed row lays its resolved timestamp on the LEFT and the head/question in the middle;
   overriding the base column direction just for these rows so the timestamp leads. */
.parked-row.processed[b-rqb5akl9te] {
    flex-direction: row;
    align-items: baseline;
    gap: .5rem;
}
.parked-row.processed .parked-body[b-rqb5akl9te] {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    flex: 1;
    min-width: 0;
}
/* WO-016: when-resolved timestamp, pinned to the LEFT of the row (it leads the markup). Muted +
   non-shrinking so the question keeps the flexible middle; scoped to processed rows so active rows
   are visually unchanged. */
.parked-row.processed .parked-when[b-rqb5akl9te] {
    flex: 0 0 auto;
    color: var(--muted);
    font-size: .85rem;
    white-space: nowrap;
}
.processed-loading[b-rqb5akl9te],
.processed-end[b-rqb5akl9te] {
    margin: 0;
    padding: .35rem .6rem;
    color: var(--muted);
    font-size: .85rem;
}
/* The observer needs a box; keep it invisible and out of layout flow. */
.processed-sentinel[b-rqb5akl9te] {
    height: 1px;
}
/* Respect the ≤900px shell breakpoint: trim the label so a narrow row still fits. */
@media (max-width: 900px) {
    .parked-row.processed .parked-when[b-rqb5akl9te] { font-size: .8rem; }
}
/* /Components/Autopilot/ProjectPicker.razor.rz.scp.css */
/* WO-007: co-located styles for the Touched / Untouched split in ProjectPicker. The outer
   .project-picker is the vertical stack (app.css); each section renders its cards in a .picker-grid
   (the same auto-fill card grid the whole picker used before the split, so card appearance and
   layout are unchanged). The untouched block mirrors the WO-001 Processed Ideas collapse: a
   ghost-button toggle, collapsed by default, revealing the untouched grid below it. */

.picker-grid[b-tuz55beao6] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: .75rem;
}

.untouched-block[b-tuz55beao6] {
    display: flex;
    flex-direction: column;
    gap: .75rem;
    margin-top: .25rem;
}

.untouched-toggle[b-tuz55beao6] {
    align-self: flex-start;
    color: var(--muted);
}
/* /Components/Autopilot/ShipRulesModal.razor.rz.scp.css */
/* Autopilot M5: scoped styling for the ship-rules editor. Self-contained on the Hub's :root
   tokens (--border/--muted/--surface-2/--radius), mirroring the other Autopilot *.razor.css. */

.hint[b-q100yb4x3l] {
    margin: 0 0 .75rem;
    color: var(--muted);
    font-size: .85rem;
}

.ship-grid[b-q100yb4x3l] {
    display: flex;
    flex-direction: column;
    gap: .6rem;
    max-height: 60vh;
    overflow-y: auto;
}

.fld[b-q100yb4x3l] {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    gap: .5rem;
    padding: .3rem .4rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-2);
}

.fld-label[b-q100yb4x3l] {
    font-size: .85rem;
    font-weight: 600;
}

.fld input[type="number"][b-q100yb4x3l],
.fld select[b-q100yb4x3l],
.fld input[type="text"][b-q100yb4x3l] {
    min-width: 8rem;
}

.dod-fld input.dod[b-q100yb4x3l] {
    grid-column: 1 / -1;
    width: 100%;
}

.inherit[b-q100yb4x3l] {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
}

.inherit-hint[b-q100yb4x3l] {
    color: var(--muted);
    font-size: .75rem;
    font-style: italic;
}

.clear-dod-label[b-q100yb4x3l] {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-size: .75rem;
    color: var(--muted);
}

/* Greyed look for a control whose inherit box is ticked. */
.fld input:disabled[b-q100yb4x3l],
.fld select:disabled[b-q100yb4x3l] {
    opacity: .5;
}
/* /Components/Autopilot/StatePill.razor.rz.scp.css */
/* Autopilot M3: co-located tints for StatePill. Upstream's StatePill had no
   co-located css (the .state-pill rules lived in app.css against a token set —
   surface-alt / accent-tint / *-strong — that the Hub does not define), so this
   is a minimal per-state stylesheet mapped onto the Hub's design tokens:
     new                → neutral surface
     approved/inprogress → accent (info blue)
     done               → ok (green)
     removed            → danger (red)
   Plus the two Autopilot affordances: quarantined reuses the solid-danger
   attention treatment (it is dropped from auto-selection until a human acts) and
   question reuses the warn/amber attention tint. */
.state-pill[b-xlujlekmi7] {
    display: inline-block;
    padding: .1rem .5rem;
    border-radius: var(--radius);
    font-size: .8rem;
    font-weight: 600;
    white-space: nowrap;
}
.state-pill.new[b-xlujlekmi7]          { background: var(--surface-2); color: var(--muted); }
.state-pill.approved[b-xlujlekmi7],
.state-pill.inprogress[b-xlujlekmi7]   { background: color-mix(in srgb, var(--info) 14%, transparent); color: var(--info); }
.state-pill.done[b-xlujlekmi7]         { background: color-mix(in srgb, var(--ok) 14%, transparent); color: var(--ok); }
.state-pill.removed[b-xlujlekmi7]      { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--danger); }
/* quarantined — solid danger so a dropped work order draws the eye */
.state-pill.quarantined[b-xlujlekmi7]  { background: var(--danger); color: #fff; }
/* question — warn/attention tint for a work order waiting on a human answer */
.state-pill.question[b-xlujlekmi7]     { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
/* epic — warn/attention tint (WO-080 follow-up): the worker refuses this work order for its
   size and says nothing, so the row must say it instead. Deliberately the warn tint rather
   than solid danger: nothing is broken and no work is lost — the backlog just needs a split.
   It sits beside the state pill, so an Approved epic reads "Approved · Epic — needs splitting"
   instead of looking like a project with nothing to do. */
.state-pill.epic[b-xlujlekmi7]         { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
/* /Components/Autopilot/UsageChip.razor.rz.scp.css */
/* Autopilot M3: co-located styles for UsageChip. Upstream's UsageChip.razor.css
   (SharpOrchestrator.Web) is keyed to a "deep-sea chrome" token set the Hub does
   not define (--chrome-bg-raised / --chrome-border / --chrome-text / --accent /
   --radius-sm / --text) and to a different markup shape (.usage-row / .usage-bar
   as the track). Rather than reference undefined tokens, this is a minimal
   self-contained stylesheet mapped onto the Hub's real :root tokens (--surface-2,
   --border, --muted, --radius, --info/--warn/--danger, --danger), matching this
   component's markup. The .usage-fill.low/.mid/.high buckets escalate with load
   (info accent < 70 %, amber warn >= 70 %, red danger >= 90 %); .credit-badge and
   .usage-unavailable keep their upstream names. */
.usage-chip[b-6hn1ac8eoh] {
    display: inline-flex;
    flex-direction: row;
    align-items: stretch;
    gap: .55rem;
    padding: .3rem .55rem;
    border-radius: var(--radius);
    background: var(--surface-2);
    border: 1px solid var(--border);
    font-size: .72rem;
    line-height: 1.2;
    color: var(--ink);
}

/* Each limit is a stacked group: a name line, a full-width bar, and a pct value. */
.usage-bar[b-6hn1ac8eoh] {
    display: flex;
    flex-direction: column;
    gap: .2rem;
    flex: 1 1 0;
    min-width: 8.5rem;
}
.usage-bar + .usage-bar[b-6hn1ac8eoh] {
    margin-left: .55rem;
    padding-left: .55rem;
    border-left: 1px solid var(--border);
}
.usage-label[b-6hn1ac8eoh] {
    font-weight: 600;
    opacity: .85;
    white-space: nowrap;
}
.usage-pct[b-6hn1ac8eoh] {
    opacity: .9;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.usage-track[b-6hn1ac8eoh] {
    display: block;
    width: 100%;
    height: .4rem;
    background: var(--border);
    border-radius: 999px;
    overflow: hidden;
}
/* The fill escalates with usage: info accent under load, amber warn as it climbs,
   red danger near the limit (buckets: low < 70 %, mid >= 70 %, high >= 90 %). */
.usage-fill[b-6hn1ac8eoh] {
    display: block;
    height: 100%;
    transition: width .3s ease;
    background: var(--info);
}
.usage-fill.low[b-6hn1ac8eoh]  { background: var(--info); }
.usage-fill.mid[b-6hn1ac8eoh]  { background: var(--warn); }
.usage-fill.high[b-6hn1ac8eoh] { background: var(--danger); }

/* credit hold — a solid-danger badge so a paused device draws the eye. */
.credit-badge[b-6hn1ac8eoh] {
    align-self: center;
    padding: .1rem .4rem;
    border-radius: 999px;
    background: var(--danger);
    color: #fff;
    font-weight: 600;
    white-space: nowrap;
}

.usage-unavailable[b-6hn1ac8eoh] {
    font-style: italic;
    opacity: .75;
    color: var(--muted);
}

/* WO-002: the non-Claude provider pill. Same muted treatment as .usage-unavailable (no new
   colours — the Hub's --muted token, reused), but a DIFFERENT class and a different sentence,
   because the two states are not the same claim: "unavailable" is a fault report about a device
   that should have numbers, this is a statement of scope about a device that never will. The
   note is styled rather than the chip so a credit-hold badge sitting beside it keeps its own
   solid-danger treatment and is not muted along with the text. */
.usage-provider .usage-provider-note[b-6hn1ac8eoh] {
    align-self: center;
    font-style: italic;
    opacity: .75;
    color: var(--muted);
    white-space: nowrap;
}

/* Pace tick — a neutral playhead at the elapsed fraction of the window, so the gap between
   it and the fill edge shows over- or under-consumption. It is a SIBLING of .usage-track,
   not a child: the track keeps overflow:hidden (that clipping is what rounds the fill's left
   cap), so the tick needs its own positioning context to stand proud of the track. Colour is
   deliberately neutral ink — the fill already owns the colour axis via .low/.mid/.high, and a
   second colour system on a 6px bar collides with it. */
.usage-gauge[b-6hn1ac8eoh] {
    position: relative;
    display: block;
    width: 100%;
}
.usage-pace[b-6hn1ac8eoh] {
    position: absolute;
    top: 50%;
    width: 2px;
    height: .72rem;
    margin-left: -1px;   /* straddle the position rather than starting at it */
    border-radius: 1px;
    background: var(--ink);
    transform: translateY(-50%);
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .95);   /* legible over fill AND empty track */
}

/* Stale — the report is older than the chip's freshness gate, so it no longer speaks for the
   present. The figures deliberately STAY on screen (an offline device's last-known usage is the
   thing the operator wants), but the fill surrenders the colour axis: low/mid/high encode urgency,
   and a number we cannot vouch for must not assert urgency. The bucket class stays on the element
   so the markup still records the real bucket — only the paint changes. Each override needs the
   bucket class to outrank .usage-fill.low (0-2-0); a bare `.usage-stale .usage-fill` would merely
   tie and depend on declaration order. */
.usage-chip.usage-stale[b-6hn1ac8eoh] {
    opacity: .85;
}
.usage-stale .usage-fill.low[b-6hn1ac8eoh],
.usage-stale .usage-fill.mid[b-6hn1ac8eoh],
.usage-stale .usage-fill.high[b-6hn1ac8eoh] {
    background: var(--muted);
}

/* The age reading — how stale, not just that it is stale. Sits in the badge strip beside
   .credit-badge, matching .usage-pct's muted, tabular treatment. */
.usage-age[b-6hn1ac8eoh] {
    align-self: center;
    color: var(--muted);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* WO-044: dormant — the device's Claude CLI login is intact but idle. The figures deliberately STAY
   visible (this is a dormant device's last-known usage, the thing the operator wants — the same call
   the staleness treatment makes), so this only ADDS an "idle" note rather than blanking anything. It
   is NOT a fault treatment: no red, no warn tint — a muted, italic footnote, the same register as
   .usage-provider-note and .usage-age. Dormancy nearly always coincides with staleness (a dormant
   device stops producing fresh polls), so .usage-stale usually mutes the fill alongside this. */
.usage-dormant-note[b-6hn1ac8eoh] {
    align-self: center;
    font-style: italic;
    opacity: .8;
    color: var(--muted);
    white-space: nowrap;
}

/* WO-069: window rolled over — the bar's stored reset instant has passed, so the last poll's figure
   is about a window that no longer exists. The track renders EMPTY (the fill is width:0 with the
   .usage-reset class, which carries no bucket colour) and takes a distinct DASHED outline so it reads
   as "a fresh window, not yet measured" — visually distinct from BOTH the solid bucket fills and the
   muted-but-present .usage-stale fill. box-sizing keeps its outer height identical to a normal track so
   the two bars stay aligned. This supersedes WO-044's .usage-window-idle (its every case is a rolled
   window), so that class is gone. */
.usage-track.usage-track-reset[b-6hn1ac8eoh] {
    box-sizing: border-box;
    background: transparent;
    border: 1px dashed var(--border);
}

/* The rolled-over note — names the fact and its age ("window reset 1h ago"). A muted italic footnote in
   the same register as .usage-dormant-note / .usage-age, sitting under its own gauge (either bar). */
.usage-reset-note[b-6hn1ac8eoh] {
    color: var(--muted);
    font-style: italic;
    white-space: nowrap;
}
/* /Components/Autopilot/UsageProbeWarning.razor.rz.scp.css */
/* WO-068: co-located styles for the demoted usage-probe warning. It is now a 16px warning triangle
   in the Hub's warn colour (--warn / --warn-bg / --warn-border, the same tokens .banner-warn uses),
   sized to sit inline inside .screen-head-actions beside the usage chip. The disclosed text is
   absolutely positioned so revealing it NEVER reflows the header row (no wrap, no page scrollbar —
   WO-068 AC #13), and is width-capped to the viewport so it stays readable on a phone. */

.usage-probe-alert[b-23iothqqij] {
    position: relative;
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
}

/* The triangle itself is a real <button> so it is keyboard- and screen-reader-reachable (it carries
   the accessible name) and can toggle the inline disclosure on touch, where there is no hover. */
.usage-probe-glyph[b-23iothqqij] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: none;
    border-radius: var(--radius);
    background: transparent;
    color: var(--warn);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}
.usage-probe-glyph:hover[b-23iothqqij],
.usage-probe-glyph:focus-visible[b-23iothqqij] {
    background: var(--warn-bg);
    outline: none;
}

/* The tap-disclosed detail (touch has no hover). Absolutely positioned and right-anchored so it opens
   below the triangle without pushing the header layout; max-width is clamped to the viewport so a long
   sentence wraps inside the screen rather than forcing a horizontal page scroll on a phone. */
.usage-probe-pop[b-23iothqqij] {
    position: absolute;
    top: calc(100% + .35rem);
    right: 0;
    z-index: 30;
    width: max-content;
    max-width: min(22rem, 84vw);
    padding: .5rem .65rem;
    border-radius: var(--radius);
    background: var(--warn-bg);
    color: var(--warn);
    border: 1px solid var(--warn-border);
    font-size: .72rem;
    line-height: 1.35;
    white-space: normal;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
}

/* AC #13: at the mobile breakpoint the triangle wraps within the already-wrapping .screen-head-actions
   (WO-035), so the RESTING indicator never forces the header onto an extra line. The DISCLOSED text is
   the risk: an absolutely positioned popover anchored to a 16px glyph can spill past the viewport edge
   and force a horizontal page scroll. Pinning it to the viewport with fixed positioning and symmetric
   insets guarantees it stays fully on screen and readable, whatever column the triangle wrapped into. */
@media (max-width: 900px) {
    .usage-probe-pop[b-23iothqqij] {
        position: fixed;
        left: .75rem;
        right: .75rem;
        top: auto;
        bottom: .75rem;
        width: auto;
        max-width: none;
        z-index: 60;
    }
}
/* /Components/Autopilot/WorkOrdersTable.razor.rz.scp.css */
/* Autopilot M3: co-located styles for WorkOrdersTable. Upstream's BacklogTable.razor.css
   (SharpOrchestrator.Web) styles a <table> (.pbis / tr.next / td.pbi-id) against a token set the
   Hub does not define (--surface-alt / --rule / --accent-tint / --ok-tint / --text-dim / --radius-sm
   / --font-mono). This component renders div rows (.backlog-row) with a ⋮ row-menu instead, so
   rather than reference undefined tokens this is a self-contained stylesheet mapped onto the Hub's
   real :root tokens (--surface, --surface-2, --border, --muted, --ink, --radius, --shadow-sm,
   --danger), keeping the upstream visual intent: a clean card, incoming rows, an active tint, a
   collapsed "Processed" section, and a friendly empty state. */

.backlog-table[b-iwcao1ywbp] {
    display: flex;
    flex-direction: column;
    gap: .35rem;
}

/* Each active row is a flex line: a title button, the state pill, then the actions cell pushed to
   the right. Processed rows reuse the base look without the interactive affordances. */
.backlog-row[b-iwcao1ywbp] {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .45rem .6rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
.backlog-row.active[b-iwcao1ywbp] { border-left: 3px solid var(--brand); }

/* A pending row (mutation in flight) dims slightly and blocks pointer input while its spinner runs. */
.backlog-row.pending[b-iwcao1ywbp] {
    opacity: .6;
    pointer-events: none;
}

/* The title button is a borderless click target that fills the available width and opens Preview. */
.row-title[b-iwcao1ywbp] {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
    color: inherit;
    cursor: pointer;
    font: inherit;
}
.row-title:hover .wo-title[b-iwcao1ywbp] { text-decoration: underline; }

.wo-num[b-iwcao1ywbp] {
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    font-size: .85rem;
}
.wo-title[b-iwcao1ywbp] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* WO-017: estimated-effort (story points) badge. A quiet pill so it reads as metadata next to the
   state pill, not a call to action. Only rendered when effort > 0 (see EffortLabel), so there is no
   "0 pts" noise. Tabular numerals keep multi-row estimates aligned. */
.wo-effort[b-iwcao1ywbp] {
    flex: 0 0 auto;
    padding: .05rem .4rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--muted);
    font-size: .72rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* WO-018: per-WO cost + time-spent, shown only on processed (Done) rows. Quiet metadata that sits
   between the effort badge and the state pill: cost reads in the brand-ish ink, time in muted, both
   tabular so figures line up down the collapsed processed list. The empty state ("—") is muted so a
   WO with no attributed sessions reads as "no data", not "$0.00". */
.wo-cost[b-iwcao1ywbp],
.wo-time[b-iwcao1ywbp] {
    flex: 0 0 auto;
    font-size: .78rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.wo-cost[b-iwcao1ywbp] {
    font-weight: 600;
    color: var(--ink);
}
.wo-time[b-iwcao1ywbp] { color: var(--muted); }
.wo-cost-empty[b-iwcao1ywbp] {
    font-weight: 400;
    color: var(--muted);
}

/* A small spinning ring shown while a row's mutation is applying. */
.row-spinner[b-iwcao1ywbp] {
    display: inline-block;
    width: .9rem;
    height: .9rem;
    border: 2px solid var(--border);
    border-top-color: var(--brand);
    border-radius: 50%;
    animation: wo-spin-b-iwcao1ywbp .7s linear infinite;
    flex: 0 0 auto;
}
@keyframes wo-spin-b-iwcao1ywbp { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .row-spinner[b-iwcao1ywbp] { animation: none; }
}

/* Actions cell sits at the far right; the ⋮ menu anchors to it. */
.row-actions[b-iwcao1ywbp] {
    position: relative;
    display: flex;
    align-items: center;
    gap: .35rem;
    margin-left: auto;
    flex: 0 0 auto;
}

/* Action buttons are sa-btn sa-btn-sm now; only the brand-outline emphasis stays scoped. */
.row-actions .approve[b-iwcao1ywbp] {
    border-color: var(--brand);
    color: var(--brand);
    font-weight: 600;
}
.row-actions .menu-btn[b-iwcao1ywbp] {
    padding: .2rem .45rem;
    line-height: 1;
}

/* The single-open ⋮ dropdown: a floating card of stacked full-width action buttons. */
.row-menu[b-iwcao1ywbp] {
    position: absolute;
    top: calc(100% + .25rem);
    right: 0;
    z-index: 20;
    display: flex;
    flex-direction: column;
    min-width: 9rem;
    padding: .25rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
/* Dropdown items stay borderless (not sa-btn); they carry the geometry the old
   .row-actions button base rule used to give them. */
.row-menu button[b-iwcao1ywbp] {
    width: 100%;
    text-align: left;
    padding: .2rem .55rem;
    border: 0;
    border-radius: calc(var(--radius) - 2px);
    background: none;
    color: var(--ink);
    cursor: pointer;
    font: inherit;
    font-size: .82rem;
}
.row-menu button:hover:not(:disabled)[b-iwcao1ywbp] { background: var(--surface-2); }
.row-menu button:disabled[b-iwcao1ywbp] { opacity: .5; cursor: not-allowed; }
.row-menu button.danger[b-iwcao1ywbp] { color: var(--danger); }

/* Closed-by-default "Processed (N)" collapsible holding Done/Removed rows. */
.processed-work-orders[b-iwcao1ywbp] {
    margin-top: .5rem;
}
.processed-work-orders > summary[b-iwcao1ywbp] {
    cursor: pointer;
    padding: .35rem .2rem;
    color: var(--muted);
    font-size: .82rem;
    font-weight: 600;
    user-select: none;
}
.processed-work-orders > .backlog-row.processed[b-iwcao1ywbp] {
    margin-top: .35rem;
    box-shadow: none;
    background: var(--surface-2);
}
/* WO-011: the Processed row right-anchors its trailing metadata (effort / cost / time / state pill)
   into aligned columns, matching the active rows' intent (supersedes WO-029's flex: 0 1 auto
   left-packing). The .row-title button (a clickable read-only-preview control, inheriting the base
   pointer cursor + hover underline) now GROWS like an active title (flex: 1 1 auto) and
   ellipsis-truncates via .wo-title, pushing the metadata group to a consistent right edge — so the
   state pill's right edge lines up across every processed row regardless of title length. Cost and
   time become fixed-min-width, right-aligned columns (tabular numerals) so the figures read straight
   down the collapsed list; min-width (not a hard width) means a rare oversized value grows rather
   than clipping. A no-session row renders only the "—" cost (no .wo-time element — a contract the
   markup keeps), so its cost cell carries a right margin equal to the time column + the row gap,
   reserving the missing time column so the "—" still lands in the same cost column as populated rows. */
.processed-work-orders > .backlog-row.processed .row-title[b-iwcao1ywbp] {
    flex: 1 1 auto;
}
.processed-work-orders > .backlog-row.processed .wo-cost[b-iwcao1ywbp] {
    min-width: 4.25rem;
    text-align: right;
}
.processed-work-orders > .backlog-row.processed .wo-time[b-iwcao1ywbp] {
    min-width: 4rem;
    text-align: right;
}
.processed-work-orders > .backlog-row.processed .wo-cost-empty[b-iwcao1ywbp] {
    margin-right: calc(4rem + .55rem);
}

/* Friendly empty state — a centred card with an icon + hint when there's no active work. */
.backlog-empty[b-iwcao1ywbp] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .35rem;
    padding: 2.75rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    color: var(--muted);
    text-align: center;
}
/* The Icon strokes with currentColor, so colouring the wrapper tints the glyph. */
.backlog-empty .icon[b-iwcao1ywbp] { color: var(--brand); opacity: .8; }
/* /Components/Consoles/ConsoleWall.razor.rz.scp.css */
/* WO-058: the Work Assist console wall — a grid of live terminals. Scoped so it never touches the autopilot
   console pane (which owns its own .autopilot-console rules) or the /console page.
   WO-063: rows + a viewport-fitted, paged grid. */

.console-wall-root[b-dy61nh2zq3] { display: flex; flex-direction: column; gap: .5rem; }

/* WO-064: fullscreen wall mode. The root promotes to a fixed, viewport-filling layer that paints OVER the
   Hub chrome; a body class (set by ConsoleWall.razor.js) also hides that chrome for the CSS-only fallback
   (see app.css). The stage's JS height-fit then re-measures against the bigger viewport, so same rows ×
   columns = genuinely taller tiles. Applied by C# BEFORE the (best-effort) Fullscreen API request, so the
   mode is never a dead control where the API is absent. Session-only — nothing here is persisted. */
.console-wall-root.fullscreen[b-dy61nh2zq3] {
    position: fixed;
    inset: 0;
    z-index: 400;            /* above the phone header (305), off-canvas nav (310) and hamburger (320) */
    margin: 0;
    padding: .5rem .75rem;
    background: var(--surface-2);
    overflow: hidden;
}

/* WO-074 / WO-075: inside the fullscreen layer the wall's OWN head is trimmed to controls. The tray note is
   explanatory copy (not a control), so it leaves the layer at EVERY width — a class rule, not a media query.
   The `New console` card is different: WO-074 hid it at every width, but WO-075 keeps it ABOVE 900px (a desktop
   wall is multi-tile, and starting the next console is the likely next action), hiding it ONLY at ≤900px (a
   1×1 phone wall is one console — the card would be most of the screen). So the card hide moved INTO the 900px
   breakpoint block below; here only the tray note is dropped. Both are hidden, NOT un-rendered: the FilterSelect
   and the story input stay alive so a destination picked or a story number typed survives enter/exit intact.
   .wall-layout (Columns/Rows/Fullscreen) is untouched, so above 900px those controls remain. */
.console-wall-root.fullscreen .wall-tray-note[b-dy61nh2zq3] { display: none; }

.wall-fullscreen-btn[b-dy61nh2zq3] { margin-left: auto; white-space: nowrap; }
.wall-fullscreen-btn.is-on[b-dy61nh2zq3] { border-color: var(--brand); color: var(--ink); background: var(--brand-pale); }

/* --- Head: the always-reachable start form + the column/row controls (WO-057 form, WO-058/WO-063 shape). --- */
.wall-head[b-dy61nh2zq3] {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem 1.25rem;
    align-items: flex-start;
    justify-content: space-between;
}
.wall-head .console-open[b-dy61nh2zq3] { flex: 1 1 28rem; margin: 0; }
.console-open-row[b-dy61nh2zq3] { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.console-open-row[b-dy61nh2zq3]  .filter-select { flex: 1 1 22rem; min-width: 16rem; }
.console-open-story[b-dy61nh2zq3] { flex: 0 1 14rem; min-width: 10rem; }
.console-open-hint[b-dy61nh2zq3] { margin: .4rem 0 0; color: var(--muted); font-size: .85rem; }

.wall-layout[b-dy61nh2zq3] { display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; align-items: center; }
.wall-columns[b-dy61nh2zq3], .wall-rows[b-dy61nh2zq3] { display: flex; align-items: center; gap: .35rem; padding-top: .25rem; }
.wall-columns-label[b-dy61nh2zq3] { color: var(--muted); font-size: .8rem; margin-right: .15rem; }
.wall-col-btn[b-dy61nh2zq3], .wall-row-btn[b-dy61nh2zq3] {
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    font: inherit;
    font-weight: 600;
}
.wall-col-btn:hover[b-dy61nh2zq3], .wall-row-btn:hover[b-dy61nh2zq3] { color: var(--ink); }
.wall-col-btn.active[b-dy61nh2zq3], .wall-row-btn.active[b-dy61nh2zq3] { border-color: var(--brand); color: var(--ink); background: var(--brand-pale); }

.wall-tray-note[b-dy61nh2zq3] { margin: 0; color: var(--muted); font-size: .8rem; }

/* --- Empty state (AC-9 of WO-058). --- */
.wall-empty[b-dy61nh2zq3] {
    border: 1px dashed var(--border);
    border-radius: 8px;
    padding: 1.5rem;
    text-align: center;
}
.wall-empty-title[b-dy61nh2zq3] { margin: 0 0 .35rem; font-weight: 600; }
.wall-empty-hint[b-dy61nh2zq3] { margin: 0; color: var(--muted); font-size: .9rem; }

/* --- The paged, viewport-fitted stage (WO-063). ---
   `.wall-stage` fills the pane height (its pixel height is measured and applied by ConsoleWall.razor.js, so
   the grid divides real available space by the row count — rows=1 is "one console as tall as this screen").
   The viewport clips the horizontally-overflowing track; chevrons + indicator pin to the pane edges. */
.wall-stage[b-dy61nh2zq3] {
    position: relative;
    min-height: 16rem;              /* floor before JS measures / if JS is unavailable */
    display: flex;
}
.console-wall-viewport[b-dy61nh2zq3] {
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    overflow: hidden;               /* off-screen pages are clipped, NOT unmounted (AC-6) */
}

/* ONE track holds EVERY tile. Column auto-flow fills each grid column top-to-bottom (`rows` tiles), so every
   `cols` columns is exactly one 100%-wide page and translateX(-page × 100%) lands on page boundaries. The
   inter-tile gap is a per-tile margin (below), NOT a grid `gap`, so it never widens a page past 100%. */
.console-wall-track[b-dy61nh2zq3] {
    display: grid;
    grid-template-rows: repeat(var(--wall-rows, 2), minmax(0, 1fr));
    grid-auto-flow: column;
    grid-auto-columns: calc(100% / var(--wall-cols, 2));
    height: 100%;
    transform: translateX(calc(var(--wall-page, 0) * -100%));
    transition: transform .32s cubic-bezier(.4, 0, .2, 1);   /* the horizontal slide (AC-5) */
    will-change: transform;
}
/* Instant swap when the OS asks for reduced motion — the class is JS-reported; the media query is a no-JS
   safety net. Both say "no slide". */
.console-wall-track.reduced-motion[b-dy61nh2zq3] { transition: none; }
@media (prefers-reduced-motion: reduce) { .console-wall-track[b-dy61nh2zq3] { transition: none; } }

/* Expand is CSS-only (WO-058): the picked tile fills the whole pane, its siblings hide. Every tile keeps its
   render-tree position and @key, so their LiveConsole instances (and stream subscriptions) are NOT torn down. */
.console-wall-track.has-expanded[b-dy61nh2zq3] {
    grid-template-rows: 1fr;
    grid-auto-columns: 100%;
    transform: none;
    transition: none;
}
.console-wall-track.has-expanded .console-tile:not(.expanded)[b-dy61nh2zq3] { display: none; }

.console-tile[b-dy61nh2zq3] {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    margin: .375rem;                /* visual gap that does NOT widen the grid track (keeps pages at 100%) */
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--panel, #0e141b);
    overflow: hidden;
}
.console-tile.dead[b-dy61nh2zq3] { opacity: .85; border-style: dashed; }

.tile-head[b-dy61nh2zq3] {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem .5rem;
    border-bottom: 1px solid var(--border);
    background: var(--panel-2, #0b1117);
    flex: 0 0 auto;
}
.console-story-chip[b-dy61nh2zq3] {
    padding: .1rem .5rem;
    border: 1px solid var(--brand);
    border-radius: 999px;
    background: var(--brand-pale);
    color: var(--ink);
    font-size: .72rem;
    font-weight: 600;
    white-space: nowrap;
}
.tile-dir[b-dy61nh2zq3] {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--muted);
    font-size: .78rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tile-provider[b-dy61nh2zq3] {
    color: var(--muted);
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .03em;
}
.tile-status[b-dy61nh2zq3] {
    padding: .05rem .45rem;
    border-radius: 999px;
    background: var(--banner-warn-bg, #3a2a00);
    color: var(--banner-warn-fg, #ffd98a);
    font-size: .68rem;
    font-weight: 600;
    text-transform: uppercase;
}
.tile-expand[b-dy61nh2zq3], .tile-close[b-dy61nh2zq3] { padding: .15rem .4rem; line-height: 1; }
/* WO-073: the tile control's icon is an inline SVG (currentColor), not a pictograph a phone font may lack.
   `is-exit` gives the exit state a brand treatment so enter/exit are distinct by colour as well as by icon. */
.tile-expand[b-dy61nh2zq3] { display: inline-flex; align-items: center; justify-content: center; }
.tile-ctrl-icon[b-dy61nh2zq3] { display: block; }
.tile-expand.is-exit[b-dy61nh2zq3] { border-color: var(--brand); color: var(--ink); background: var(--brand-pale); }

/* The terminal body fills the remaining tile height so a tile is truly `1/rows` of the pane. LiveConsole's own
   18rem min-height is relaxed here so a compact (rows=4) tile does not overflow its cell. */
.tile-body[b-dy61nh2zq3] { flex: 1 1 auto; display: flex; min-height: 0; }
.tile-body[b-dy61nh2zq3]  .live-console { width: 100%; }
.tile-body[b-dy61nh2zq3]  .term-host { min-height: 0; }

/* --- Paging affordances (AC-3/AC-4). --- */
.wall-chevron[b-dy61nh2zq3] {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    width: 2.2rem;
    height: 3.2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--panel-2, #0b1117);
    color: var(--ink);
    cursor: pointer;
    font-size: 1.4rem;
    line-height: 1;
    opacity: .92;
}
.wall-chevron:hover:not(:disabled)[b-dy61nh2zq3] { background: var(--brand-pale); border-color: var(--brand); }
.wall-chevron:disabled[b-dy61nh2zq3] { opacity: .3; cursor: default; }
.wall-chevron-left[b-dy61nh2zq3] { left: -.25rem; }
.wall-chevron-right[b-dy61nh2zq3] { right: -.25rem; }
.wall-pageind[b-dy61nh2zq3] {
    position: absolute;
    bottom: .4rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2;
    padding: .1rem .6rem;
    border-radius: 999px;
    background: var(--panel-2, #0b1117);
    border: 1px solid var(--border);
    color: var(--muted);
    font-size: .78rem;
    font-variant-numeric: tabular-nums;
}

@media (max-width: 900px) {
    /* Single column AND single row regardless of the selected counts (AC-9): one full-height console per page,
       the chevrons doing the navigating. The effective 1×1 is already applied to the grid vars in C#, so this
       is a no-JS safety net that also holds before the first SetViewport report lands. */
    .console-wall-track[b-dy61nh2zq3] {
        grid-template-rows: 1fr !important;
        grid-auto-columns: 100% !important;
    }
    .console-wall-track.has-expanded[b-dy61nh2zq3] { grid-template-columns: 1fr; }

    /* WO-073 AC-2: the Columns group, the Rows group and the wall Fullscreen button (all inside .wall-layout)
       answer nothing at 1×1, so they are removed here too. The C# `!_mobile` gate drops them from the DOM once
       JS reports ≤900px; THIS rule is the belt-and-braces that holds on the very first paint (before any
       SetViewport report) and forever if JS never reports — so a phone never flashes the desktop controls. */
    .wall-layout[b-dy61nh2zq3] { display: none !important; }

    /* WO-074 AC-1 / WO-075 AC-6: at ≤900px the `New console` card stays hidden in fullscreen. This rule lives
       INSIDE the breakpoint block (not beside the always-on tray-note hide) so it does NOT apply above 900px,
       where WO-075 keeps the card in the mode. Hidden, not un-rendered, so the draft survives enter/exit. */
    .console-wall-root.fullscreen .console-open[b-dy61nh2zq3] { display: none; }

    /* WO-074: at ≤900px the only head content is the start card (.wall-layout is hidden above), and in
       fullscreen that card is hidden too (rule above) — so the head has nothing left to show. Collapse the
       whole head in the mode so its space reaches the terminal (JS re-fits the stage from its top edge) rather
       than leaving a gap above the tile. Outside fullscreen the head still renders the start card, so this is
       mode-scoped. Above 900px the head is untouched, so the card and the shape/paging controls all render. */
    .console-wall-root.fullscreen .wall-head[b-dy61nh2zq3] { display: none; }
}
/* /Components/Consoles/LiveConsole.razor.rz.scp.css */
.live-console[b-0mwukovuhm] {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    min-height: 0;
    height: 100%;
}

/* The emulator host. xterm renders its own DOM inside here (styled by the global xterm.css); this only
   reserves a bounded, scrollable box. On desktop it grows to fill the pane; the terminal owns its own
   scrollback so the box itself never grows unbounded (AC-7). */
.term-host[b-0mwukovuhm] {
    flex: 1 1 auto;
    min-height: 18rem;
    background: #0b0f14;
    border: 1px solid var(--border, #2a3441);
    border-radius: 6px;
    padding: .35rem .5rem;
    overflow: hidden;
}

.console-deadbanner[b-0mwukovuhm] {
    padding: .5rem .75rem;
    border-radius: 6px;
    background: var(--banner-warn-bg, #3a2a00);
    color: var(--banner-warn-fg, #ffd98a);
    border: 1px solid var(--banner-warn-border, #6b5200);
    font-size: .9rem;
}

/* WO-077 AC-7: the folder-trust prompt notice — a distinct, actionable banner over a LIVE console (the terminal
   is waiting for a keypress, not dead), so it reads as informational rather than an error. */
.console-trustbanner[b-0mwukovuhm] {
    padding: .5rem .75rem;
    border-radius: 6px;
    background: var(--banner-info-bg, #0d2a3a);
    color: var(--banner-info-fg, #8fd0ff);
    border: 1px solid var(--banner-info-border, #1f5a80);
    font-size: .9rem;
}

.console-sendbox[b-0mwukovuhm] {
    display: flex;
    gap: .5rem;
    align-items: center;
}

.console-sendbox .sa-input[b-0mwukovuhm] {
    flex: 1 1 auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

.console-keyhint[b-0mwukovuhm] {
    margin: 0;
    font-size: .78rem;
    color: var(--muted, #8b98a5);
}

/* Mobile (≤900px): the terminal stays read-scrollable and the send box is the primary input. The keyhint
   makes the desktop-only scope explicit rather than shipping half-working modifier keys (AC-8). */
@media (max-width: 900px) {
    .term-host[b-0mwukovuhm] {
        min-height: 14rem;
        overflow: auto;
    }
}
/* /Components/Layout/MainLayout.razor.rz.scp.css */
#blazor-error-ui[b-78uree0zvu] {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss[b-78uree0zvu] {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

    /* WO-040: crash reference id + copy control, inline after the Reload link. Hidden until the
       browser reporter has an id to show, so the bar looks unchanged when there is no reference yet. */
    #blazor-error-ui .crash-reference[b-78uree0zvu] {
        margin-left: 0.75rem;
        white-space: nowrap;
    }

    #blazor-error-ui .crash-reference code[b-78uree0zvu] {
        font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    }

    #blazor-error-ui .crash-copy[b-78uree0zvu] {
        margin-left: 0.35rem;
        cursor: pointer;
        font: inherit;
        color: inherit;
        background: transparent;
        border: 1px solid rgba(0, 0, 0, 0.35);
        border-radius: 4px;
        padding: 0 0.4rem;
    }
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
.components-reconnect-first-attempt-visible[b-ogrn4tj2db],
.components-reconnect-repeated-attempt-visible[b-ogrn4tj2db],
.components-reconnect-failed-visible[b-ogrn4tj2db],
.components-pause-visible[b-ogrn4tj2db],
.components-resume-failed-visible[b-ogrn4tj2db],
.components-rejoining-animation[b-ogrn4tj2db] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-ogrn4tj2db],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-ogrn4tj2db],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-ogrn4tj2db],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-ogrn4tj2db],
#components-reconnect-modal.components-reconnect-retrying[b-ogrn4tj2db],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-ogrn4tj2db],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-ogrn4tj2db],
#components-reconnect-modal.components-reconnect-failed[b-ogrn4tj2db],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-ogrn4tj2db] {
    display: block;
}


#components-reconnect-modal[b-ogrn4tj2db] {
    background-color: white;
    width: 20rem;
    margin: 20vh auto;
    padding: 2rem;
    border: 0;
    border-radius: 0.5rem;
    box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-ogrn4tj2db 0.5s both;
    &[open]

{
    animation: components-reconnect-modal-slideUp-b-ogrn4tj2db 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-ogrn4tj2db 0.5s ease-in-out 0.3s;
    animation-fill-mode: both;
}

}

#components-reconnect-modal[b-ogrn4tj2db]::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
    animation: components-reconnect-modal-fadeInOpacity-b-ogrn4tj2db 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp-b-ogrn4tj2db {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-ogrn4tj2db {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-ogrn4tj2db {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-ogrn4tj2db] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p[b-ogrn4tj2db] {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button[b-ogrn4tj2db] {
    border: 0;
    background-color: #6b9ed2;
    color: white;
    padding: 4px 24px;
    border-radius: 4px;
}

    #components-reconnect-modal button:hover[b-ogrn4tj2db] {
        background-color: #3b6ea2;
    }

    #components-reconnect-modal button:active[b-ogrn4tj2db] {
        background-color: #6b9ed2;
    }

.components-rejoining-animation[b-ogrn4tj2db] {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div[b-ogrn4tj2db] {
        position: absolute;
        border: 3px solid #0087ff;
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation-b-ogrn4tj2db 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2)[b-ogrn4tj2db] {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation-b-ogrn4tj2db {
    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/ReleaseBanner.razor.rz.scp.css */
/* Full-width strip under the app header; phase classes tint it. */
.release-banner[b-rz16lt9ikw] {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .4rem 1rem;
    font-size: .9rem;
    background: var(--panel, #1d2430);
    border-bottom: 1px solid var(--border, #2c3444);
    color: white;
}
.release-banner a[b-rz16lt9ikw] { font-weight: 600; }
.release-banner.phase-draining[b-rz16lt9ikw] { border-left: 4px solid #d9a521; }
.release-banner.phase-approved[b-rz16lt9ikw] { border-left: 4px solid #3b82f6; }
.release-banner.phase-pending[b-rz16lt9ikw]  { border-left: 4px solid #6b7280; }
/* /Components/Pages/AccessDenied.razor.rz.scp.css */
/* Standalone Access Denied page — self-contained styles.
   Consumes only the global brand tokens from app.css :root (design tokens, not a
   reused component); no dependency on any .app-* / shared component class. */

.ad-viewport[b-eam2pof5fd] {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1.25rem;
    background:
        radial-gradient(120% 90% at 50% -10%, var(--brand-pale, #eef4e9), transparent 60%),
        var(--surface-2, #f4f6f3);
}

.ad-card[b-eam2pof5fd] {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    max-width: 30rem;
    width: 100%;
    padding: 2.5rem 2rem 2.25rem;
    background: var(--surface, #fff);
    border: 1px solid var(--border, #dde3dc);
    border-radius: calc(var(--radius, 8px) * 2);
    box-shadow: var(--shadow, 0 4px 14px rgba(0, 0, 0, .10));
}

/* Mascot on a soft brand-green glow */
.ad-figure[b-eam2pof5fd] {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    margin-bottom: 1.25rem;
}

.ad-glow[b-eam2pof5fd] {
    position: absolute;
    width: 78%;
    aspect-ratio: 1;
    border-radius: 50%;
    background: radial-gradient(circle,
        var(--brand-light, #7ba85f) 0%,
        rgba(123, 168, 95, 0.28) 42%,
        transparent 70%);
    filter: blur(2px);
    opacity: 0.55;
    z-index: 0;
}

.ad-mascot[b-eam2pof5fd] {
    position: relative;
    z-index: 1;
    width: 220px;
    max-width: 62%;
    height: auto;
    object-fit: contain;
    filter: drop-shadow(0 8px 16px rgba(36, 61, 41, 0.18));
}

.ad-title[b-eam2pof5fd] {
    margin: 0 0 0.5rem;
    font-size: 1.6rem;
    font-weight: 700;
    letter-spacing: 0.2px;
    color: var(--brand-darkest, #243d29);
}

.ad-message[b-eam2pof5fd] {
    margin: 0 0 1.75rem;
    max-width: 24rem;
    font-size: 0.95rem;
    line-height: 1.55;
    color: var(--muted, #5f6b63);
}

.ad-signout[b-eam2pof5fd] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.6rem 1.6rem;
    border-radius: 999px;
    font-size: 0.95rem;
    font-weight: 600;
    text-decoration: none;
    color: #fff;
    background: linear-gradient(90deg, var(--brand-dark, #2f5236), var(--brand, #4f8a47));
    box-shadow: 0 2px 8px rgba(47, 82, 54, 0.28);
    transition: filter 0.15s ease, transform 0.05s ease;
}

.ad-signout:hover[b-eam2pof5fd] {
    filter: brightness(1.06);
}

.ad-signout:active[b-eam2pof5fd] {
    transform: translateY(1px);
}

.ad-signout:focus-visible[b-eam2pof5fd] {
    outline: 3px solid var(--brand-light, #7ba85f);
    outline-offset: 2px;
}

/* Mobile */
@media (max-width: 480px) {
    .ad-card[b-eam2pof5fd] {
        padding: 1.75rem 1.25rem 1.5rem;
        border-radius: var(--radius, 8px);
    }

    .ad-mascot[b-eam2pof5fd] {
        width: 160px;
        max-width: 70%;
    }

    .ad-title[b-eam2pof5fd] {
        font-size: 1.35rem;
    }
}
/* /Components/Pages/Autopilot.razor.rz.scp.css */
/* Autopilot page-specific chrome on top of the shared HUD shell. Everything structural
   (hud, menu-item, submenu, tabs, screen-head, banners) is global in app.css. */

.screen-head-title[b-7g99scqhtj] { flex: 1 1 auto; min-width: 0; }

/* WO-003: continued breadcrumb on the project view (Fleet overview › device Autopilot overview).
   The single-segment "‹ Fleet overview" button on the device/empty views reuses the bare Dashboard
   idiom and needs no container. */
.hud-breadcrumb[b-7g99scqhtj] { display: flex; align-items: center; gap: .25rem; }
.hud-breadcrumb .crumb-sep[b-7g99scqhtj] { color: var(--muted); }

/* Live-session pulse in the project submenu. */
.running-dot[b-7g99scqhtj] {
    flex: 0 0 auto;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: var(--ok);
    animation: sa-pulse 1.2s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .running-dot[b-7g99scqhtj] { animation: none; } }

.proj-mode[b-7g99scqhtj] { flex: 0 0 auto; color: var(--brand-dark); font-size: .8rem; }

/* Amber attention variant of the shared menu-badge (parked counts). */
.menu-badge.warn[b-7g99scqhtj] { background: var(--warn); }

/* Loop toggle switch (replaces the bare checkbox). */
.loop-switch[b-7g99scqhtj] {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    font-size: .85rem;
    font-weight: 600;
    color: var(--muted);
    cursor: pointer;
    user-select: none;
}
.loop-switch input[b-7g99scqhtj] { position: absolute; opacity: 0; width: 0; height: 0; }
.loop-switch .slider[b-7g99scqhtj] {
    flex: 0 0 auto;
    width: 2.1rem;
    height: 1.15rem;
    border-radius: 999px;
    background: var(--border);
    position: relative;
    transition: background .15s ease;
}
.loop-switch .slider[b-7g99scqhtj]::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: .85rem;
    height: .85rem;
    border-radius: 50%;
    background: #fff;
    box-shadow: var(--shadow-sm);
    transition: left .15s ease;
}
.loop-switch input:checked + .slider[b-7g99scqhtj] { background: var(--brand); }
.loop-switch input:checked + .slider[b-7g99scqhtj]::after { left: calc(100% - .85rem - 2px); }
.loop-switch input:disabled + .slider[b-7g99scqhtj] { opacity: .5; cursor: not-allowed; }

/* D1: blocked-projects strip directly under the screen-head (both variants). */
.blocked-projects[b-7g99scqhtj] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem .9rem;
    margin: 0 0 .75rem;
}
.blocked-item[b-7g99scqhtj] {
    display: inline-flex;
    /* User-approved deviation from the plan CSS (2026-07-16): wrap within an item so the
       resume button drops below the nowrap pill at phone widths (390px) instead of clipping. */
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
}

/* Awaiting-merge pill in the header actions. */
.await-merge-pill[b-7g99scqhtj] {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .15rem .6rem;
    border-radius: 999px;
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn);
    font-size: .8rem;
    font-weight: 600;
    white-space: nowrap;
}

/* Count badge inside a tab. */
.tab-badge[b-7g99scqhtj] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.15rem;
    height: 1.15rem;
    padding: 0 .3rem;
    margin-left: .35rem;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--muted);
    font-size: .7rem;
    font-weight: 700;
}
.tab-badge.warn[b-7g99scqhtj] { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn); }
/* /Components/Pages/CostOverview.razor.rz.scp.css */
.cost-controls[b-xhdan6okcl] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}

.cost-periods[b-xhdan6okcl],
.cost-nav[b-xhdan6okcl] {
    display: flex;
    align-items: center;
    gap: .35rem;
}

.cost-period-label[b-xhdan6okcl] {
    min-width: 11rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

.cost-total[b-xhdan6okcl] {
    margin-left: auto;
    display: flex;
    align-items: baseline;
    gap: .5rem;
}

.cost-total-label[b-xhdan6okcl] {
    color: var(--muted);
    text-transform: uppercase;
    font-size: .75rem;
    letter-spacing: .05em;
}

.cost-total-value[b-xhdan6okcl] {
    font-size: 1.5rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.cost-total-sub[b-xhdan6okcl] {
    color: var(--muted);
    font-size: .8rem;
}

.cost-card[b-xhdan6okcl] {
    margin-bottom: 1rem;
}

.cost-table[b-xhdan6okcl] {
    width: 100%;
    border-collapse: collapse;
    margin-top: .5rem;
}

.cost-table th[b-xhdan6okcl],
.cost-table td[b-xhdan6okcl] {
    padding: .35rem .6rem;
    border-bottom: 1px solid var(--border, rgba(255, 255, 255, .08));
    text-align: left;
}

.cost-table th[b-xhdan6okcl] {
    color: var(--muted);
    font-weight: 500;
    font-size: .8rem;
}

.cost-table .num[b-xhdan6okcl] {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.cost-footnote[b-xhdan6okcl] {
    color: var(--muted);
    font-size: .8rem;
    max-width: 60rem;
}

/*
 * Phone layout. Reuses the shared Hub mobile breakpoint (max-width:640px) used by
 * the rest of the mobile polish (WO-014 header scroll-away, WO-016 FleetNav
 * hamburger, WO-019 admin overlay) rather than inventing a new value.
 *
 * Treatment: show-less-not-smaller (WO-038), plus horizontal-scroll-within-card
 * (WO-027) as the long-label safety net. On a phone the three token columns
 * (In/Out/Cache, tagged .tok on header th and body td alike) are hidden entirely,
 * leaving label / Cost / Sessions — the numbers the page is actually consulted
 * for at a glance. This deliberately supersedes WO-027's "no metric silently
 * removed" trade-off: token detail is now intentionally desktop-only, with no
 * disclosure affordance. The .cost-table-scroll wrapper is KEPT: three columns
 * fit ~390px, but a long group label (project path, Work Order title, device
 * name) still scrolls sideways inside its own card rather than pushing the page
 * horizontally. Above this breakpoint the .tok cells reappear and the wrapper
 * adds no styling, so desktop/tablet render exactly as before (same six-column
 * table, same right-aligned tabular numerics). The .cost-controls header already
 * flex-wraps and fits at 390px (nav ≈ 253px < content width), so its 11rem
 * period-label min-width and the Total's margin-left:auto needed no adjustment.
 */
@media (max-width: 640px) {
    .cost-table-scroll[b-xhdan6okcl] {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .cost-table .tok[b-xhdan6okcl] {
        display: none;
    }
}
/* /Components/Pages/ErrorOverview.razor.rz.scp.css */
.err-controls[b-0yo6isi2d5] {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}

.err-count[b-0yo6isi2d5] {
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.err-table[b-0yo6isi2d5] {
    width: 100%;
    border-collapse: collapse;
}

.err-table th[b-0yo6isi2d5],
.err-table td[b-0yo6isi2d5] {
    padding: .35rem .6rem;
    border-bottom: 1px solid var(--border, rgba(255, 255, 255, .08));
    text-align: left;
    vertical-align: top;
}

.err-table th[b-0yo6isi2d5] {
    color: var(--muted);
    font-weight: 500;
    font-size: .8rem;
}

.err-caret-col[b-0yo6isi2d5] {
    width: 1.75rem;
}

.err-caret[b-0yo6isi2d5] {
    padding: 0 .35rem;
}

.err-time[b-0yo6isi2d5] {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--muted);
}

.err-type[b-0yo6isi2d5] {
    font-family: var(--mono, ui-monospace, monospace);
    font-size: .82rem;
    white-space: nowrap;
}

.err-msg[b-0yo6isi2d5] {
    max-width: 32rem;
    overflow-wrap: anywhere;
}

/* WO-115: recurrence marker on a row whose ErrorId has been ingested more than once — the count is the
   real signal that a "fixed" error came back, so it sits right beside the message. */
.err-recur[b-0yo6isi2d5] {
    display: inline-block;
    margin-left: .5rem;
    padding: .02rem .4rem;
    border-radius: 999px;
    font-size: .72rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: #f5b342;
    background: rgba(245, 179, 66, .12);
    border: 1px solid rgba(245, 179, 66, .3);
}

.err-actions[b-0yo6isi2d5] {
    white-space: nowrap;
    text-align: right;
}

.err-actions-col[b-0yo6isi2d5] {
    width: 1%;
}

/* A reviewed row recedes so the eye lands on the unreviewed ones. */
.err-row.reviewed td[b-0yo6isi2d5] {
    opacity: .55;
}

/* Severity pills — visually distinct at a glance. */
.sev[b-0yo6isi2d5] {
    display: inline-block;
    padding: .05rem .45rem;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .03em;
    border: 1px solid transparent;
}

.sev-error[b-0yo6isi2d5] {
    color: #f5b342;
    background: rgba(245, 179, 66, .12);
    border-color: rgba(245, 179, 66, .35);
}

.sev-fatal[b-0yo6isi2d5] {
    color: #ff6b6b;
    background: rgba(255, 107, 107, .14);
    border-color: rgba(255, 107, 107, .4);
}

.err-detail-row td[b-0yo6isi2d5] {
    background: rgba(255, 255, 255, .02);
}

.err-detail[b-0yo6isi2d5] {
    padding: .25rem .25rem .5rem;
}

.err-detail-title[b-0yo6isi2d5] {
    color: var(--muted);
    text-transform: uppercase;
    font-size: .7rem;
    letter-spacing: .05em;
    margin: .5rem 0 .2rem;
}

.err-fields[b-0yo6isi2d5] {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: .1rem .75rem;
    margin: 0;
}

.err-fields dt[b-0yo6isi2d5] {
    color: var(--muted);
    font-size: .8rem;
}

.err-fields dd[b-0yo6isi2d5] {
    margin: 0;
    overflow-wrap: anywhere;
}

.err-inner[b-0yo6isi2d5] {
    margin: 0;
    padding-left: 1.1rem;
}

.err-stack[b-0yo6isi2d5] {
    margin: 0;
    padding: .5rem .6rem;
    background: rgba(0, 0, 0, .25);
    border-radius: .35rem;
    font-size: .78rem;
    line-height: 1.35;
    overflow-x: auto;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

@media (max-width: 900px) {
    .err-msg[b-0yo6isi2d5] {
        max-width: none;
    }
}
/* /Components/Pages/ErrorTriageControl.razor.rz.scp.css */
.triage-controls[b-9k57v3d0hy] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem .75rem;
    margin-bottom: 1rem;
}

.triage-reason[b-9k57v3d0hy] {
    color: var(--muted);
    font-size: .82rem;
    overflow-wrap: anywhere;
}

/* Status strip — an observability line, not an error. Muted by default; per-state accents below. */
.triage-strip[b-9k57v3d0hy] {
    margin-bottom: 1rem;
    padding: .5rem .75rem;
    border-radius: .4rem;
    font-size: .88rem;
    border: 1px solid var(--border, rgba(255, 255, 255, .12));
    background: rgba(255, 255, 255, .03);
    overflow-wrap: anywhere;
}

.triage-strip.running[b-9k57v3d0hy] {
    border-color: rgba(96, 165, 250, .4);
    background: rgba(96, 165, 250, .1);
}

.triage-strip.completed[b-9k57v3d0hy] {
    border-color: rgba(74, 222, 128, .35);
    background: rgba(74, 222, 128, .09);
}

.triage-strip.failed[b-9k57v3d0hy] {
    border-color: rgba(245, 179, 66, .35);
    background: rgba(245, 179, 66, .1);
}

.triage-link[b-9k57v3d0hy] {
    color: var(--accent, #60a5fa);
    text-decoration: underline;
}

.triage-link.wo-chip[b-9k57v3d0hy] {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Modal body */
.triage-preview[b-9k57v3d0hy] {
    margin: 0 0 .35rem;
    font-size: 1rem;
}

.triage-deferred[b-9k57v3d0hy],
.triage-explain[b-9k57v3d0hy] {
    color: var(--muted);
    font-size: .85rem;
}

.triage-explain[b-9k57v3d0hy] {
    margin: 0 0 1rem;
}

.triage-target-label[b-9k57v3d0hy] {
    display: block;
    color: var(--muted);
    font-size: .8rem;
    margin-bottom: .25rem;
}

.triage-target[b-9k57v3d0hy] {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}

.triage-no-target[b-9k57v3d0hy],
.triage-confirm-reason[b-9k57v3d0hy] {
    color: var(--muted);
    font-size: .85rem;
    margin: .5rem 0 0;
}

.triage-error[b-9k57v3d0hy] {
    color: #ff6b6b;
    font-size: .85rem;
    margin: .5rem 0 0;
}
/* /Components/Pages/RemoteConnect.razor.rz.scp.css */
/* SharpRemote SR-128 — the connect page. Same card/chip vocabulary as the rest of the Hub; the only
   page-specific shapes are the mode rows and the expiry countdown. */
.rc-mode[b-pqvehfqwuh] {
    display: flex;
    align-items: baseline;
    gap: .45rem;
    padding: .2rem 0;
}

.rc-mode input[disabled] + span[b-pqvehfqwuh] {
    color: var(--muted);
}

.rc-reason[b-pqvehfqwuh] {
    color: var(--muted);
    font-size: .82rem;
}

.rc-countdown[b-pqvehfqwuh] {
    font-variant-numeric: tabular-nums;
}

.rc-session p[b-pqvehfqwuh] {
    margin: .5rem 0;
}
/* /Components/Pages/RemoteDevices.razor.rz.scp.css */
/* SharpRemote SR-128 — the /remote and /remote/connect tables. Deliberately the same treatment as
   ErrorOverview's table (the admin-page precedent the card names) rather than a parallel look. */
.rc-table[b-fu2e9jps99] {
    width: 100%;
    border-collapse: collapse;
}

.rc-table th[b-fu2e9jps99],
.rc-table td[b-fu2e9jps99] {
    padding: .35rem .6rem;
    border-bottom: 1px solid var(--border, rgba(255, 255, 255, .08));
    text-align: left;
    vertical-align: middle;
}

.rc-table th[b-fu2e9jps99] {
    color: var(--muted);
    font-weight: 500;
    font-size: .8rem;
}

.rc-actions-col[b-fu2e9jps99] {
    width: 1%;
}

.rc-actions[b-fu2e9jps99] {
    white-space: nowrap;
    text-align: right;
}

.rc-modes[b-fu2e9jps99] {
    color: var(--muted);
}

/* The reason a row's Connect is disabled, for the readers a title= tooltip never reaches:
   touch (no hover) and screen readers. Muted so a fleet of idle devices does not read as alarming. */
.rc-reason-row td[b-fu2e9jps99] {
    border-bottom: 1px solid var(--border, rgba(255, 255, 255, .08));
}

.rc-reason[b-fu2e9jps99] {
    padding-top: 0;
    color: var(--muted);
    font-size: .82rem;
}

/* SR-130: the kill-switch confirm's note. Same muted treatment as a refusal reason — it is a statement of
   what the next click does, not an alarm — but it is about to change something, so it is not dimmed away. */
.rc-kill-note[b-fu2e9jps99] {
    color: var(--text, inherit);
}

/* SharpRemote SR-312 — phone width. A five-column table with two buttons in the last cell cannot be made
   readable at 390px by tightening padding: it either overflows the pane (and the HUD grows a horizontal
   scrollbar, which the ≤900px rules in app.css exist to prevent) or the device name wraps one character per
   line. So below the breakpoint the table stops being a table and each device becomes a card, with the
   header row's meaning carried onto each cell by `data-label`.

   `display: block` on the table parts is the whole mechanism; nothing here is an inline style, because
   /remote is one hop from the CSP-scoped pages and the same discipline is cheaper to keep than to restore. */
@media (max-width: 900px) {
    .rc-table th[b-fu2e9jps99],
    .rc-table td[b-fu2e9jps99] {
        padding: .3rem .35rem;
    }

    .rc-table[b-fu2e9jps99],
    .rc-table tbody[b-fu2e9jps99],
    .rc-table tr[b-fu2e9jps99],
    .rc-table td[b-fu2e9jps99] {
        display: block;
        width: auto;
    }

    /* The header row's labels move onto the cells (below), so the row itself has nothing left to say. */
    .rc-table thead[b-fu2e9jps99] {
        display: none;
    }

    /* One rule per device, drawn ABOVE it, so a device and its refusal line stay one block whether or not
       the refusal line is there — a bottom rule would fall between them when it is. */
    .rc-row[b-fu2e9jps99] {
        padding: .5rem 0 0;
        border-top: 1px solid var(--border, rgba(255, 255, 255, .08));
    }

    .rc-table .rc-row td[b-fu2e9jps99] {
        border-bottom: 0;
        padding: .2rem 0;
    }

    .rc-device[b-fu2e9jps99] {
        font-weight: 600;
        font-size: 1rem;
    }

    /* The column name, in front of its value — without it a lone "—" in the Session cell names nothing. */
    .rc-table td[data-label][b-fu2e9jps99]::before {
        content: attr(data-label) ": ";
        color: var(--muted);
        font-size: .8rem;
    }

    /* Thumb-sized targets, and the actions on their own line rather than squeezed against the right edge.
       `td.rc-actions` rather than `.rc-actions`: the `display: block` above is written as `.rc-table td`,
       which after CSS isolation is `.rc-table td[b-…]` — an element selector MORE specific than a lone
       class, so a bare `.rc-actions` here loses to it and the row stays a block (measured in Edge: the
       buttons kept their content width). Qualifying with the element wins it back. */
    .rc-table td.rc-actions[b-fu2e9jps99] {
        display: flex;
        flex-wrap: wrap;
        gap: .4rem;
        text-align: left;
        white-space: normal;
        margin-top: .35rem;
    }

    .rc-actions .sa-btn[b-fu2e9jps99] {
        flex: 1 1 8rem;
        padding: .5rem .8rem;
    }

    /* The refusal / kill-switch note belongs to the device above it, so it carries no rule of its own. */
    .rc-reason-row[b-fu2e9jps99] {
        display: block;
    }

    .rc-reason-row td[b-fu2e9jps99] {
        display: block;
        border-bottom: 0;
        padding: .1rem 0 .55rem;
    }
}
/* /Components/Pages/RemoteSelfTest.razor.rz.scp.css */
/* SharpRemote SR-302 — the self-test report. Deliberately plain: it has to be readable on a phone
   (SR-313 turns this page on in Production so the owner can run it there) and it must never depend on the
   HUD shell, because a broken suite is exactly when the rest of the page might not be trustworthy. */

.selftest[b-ifv1vfuk5t] {
    padding: 1rem;
    max-width: 70rem;
    margin: 0 auto;
}

.selftest-running[b-ifv1vfuk5t] {
    color: var(--muted);
}

.selftest-summary[b-ifv1vfuk5t] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1rem;
    align-items: baseline;
    padding: 0.6rem 0.9rem;
    border-radius: 6px;
    margin-bottom: 1rem;
}

.selftest-summary.ok[b-ifv1vfuk5t] {
    background: color-mix(in srgb, var(--ok, #2e7d32) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--ok, #2e7d32) 45%, transparent);
}

.selftest-summary.bad[b-ifv1vfuk5t] {
    background: color-mix(in srgb, var(--danger, #c62828) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--danger, #c62828) 50%, transparent);
}

.selftest-table[b-ifv1vfuk5t] {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.selftest-table th[b-ifv1vfuk5t],
.selftest-table td[b-ifv1vfuk5t] {
    text-align: left;
    padding: 0.3rem 0.5rem;
    border-bottom: 1px solid var(--line, rgba(128, 128, 128, 0.25));
    vertical-align: top;
    /* Failure messages carry hex strings hundreds of characters long; wrapping them keeps the table on a
       phone screen instead of forcing a horizontal page scroll. */
    overflow-wrap: anywhere;
}

.selftest-suite[b-ifv1vfuk5t] {
    color: var(--muted);
    white-space: nowrap;
}

.selftest-fail td[b-ifv1vfuk5t] {
    color: var(--danger, #c62828);
}

@media (max-width: 900px) {
    .selftest[b-ifv1vfuk5t] {
        padding: 0.75rem 1rem;
    }

    .selftest-suite[b-ifv1vfuk5t] {
        white-space: normal;
    }
}
/* /Components/Pages/RemoteSessionsOverview.razor.rz.scp.css */
/* SharpRemote SR-129 — deliberately the same table treatment as ErrorOverview and SR-128's /remote
   (the admin-page precedent the card names), not a parallel look. */
.rs-table[b-0ruuz4po0s] {
    width: 100%;
    border-collapse: collapse;
}

.rs-table th[b-0ruuz4po0s],
.rs-table td[b-0ruuz4po0s] {
    padding: .35rem .6rem;
    border-bottom: 1px solid var(--border, rgba(255, 255, 255, .08));
    text-align: left;
    vertical-align: top;
}

.rs-table th[b-0ruuz4po0s] {
    color: var(--muted);
    font-weight: 500;
    font-size: .8rem;
}

.rs-time[b-0ruuz4po0s],
.rs-duration[b-0ruuz4po0s],
.rs-relay[b-0ruuz4po0s],
.rs-ip[b-0ruuz4po0s] {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.rs-time[b-0ruuz4po0s],
.rs-user[b-0ruuz4po0s] {
    color: var(--muted);
}

/* The staleness caveat on a live row's relay figure, and the paired-time secondary reading: present but
   never competing with the number itself (WO-068's muted-disclosure treatment). */
.rs-sub[b-0ruuz4po0s] {
    display: block;
    color: var(--muted);
    font-size: .74rem;
    white-space: nowrap;
}

/* A live row reads forward, the closed history behind it recedes — the inverse of ErrorOverview's
   reviewed-row fade, for the same reason: the eye should land on what is still happening. */
.rs-row.rs-live td[b-0ruuz4po0s] {
    background: rgba(255, 255, 255, .03);
}

/* SharpRemote SR-407 — the header tiles. Four readings of the same 30-day window, so they share one row
   and one weight; the figure leads and the label recedes, the usage-chip treatment. */
.rs-tiles[b-0ruuz4po0s] {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    margin: 0 0 .6rem;
}

.rs-tile[b-0ruuz4po0s] {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    min-width: 9rem;
    padding: .5rem .7rem;
    border: 1px solid var(--border, rgba(255, 255, 255, .08));
    border-radius: 8px;
}

.rs-tile-value[b-0ruuz4po0s] {
    font-size: 1.15rem;
    font-variant-numeric: tabular-nums;
}

.rs-tile-label[b-0ruuz4po0s] {
    color: var(--muted);
    font-size: .74rem;
}

/* The §15.5 egress line and the "these are a floor" caveat: prose, muted, never styled as a warning —
   neither is a fault, they are the evidence the owner rules on (WO-068's severity discipline). */
.rs-budget[b-0ruuz4po0s],
.rs-truncated[b-0ruuz4po0s] {
    color: var(--muted);
    font-size: .8rem;
    margin: 0 0 .4rem;
}

.rs-filters[b-0ruuz4po0s] {
    display: flex;
    flex-wrap: wrap;
    gap: .8rem;
    margin: 0 0 .6rem;
}

.rs-filters label[b-0ruuz4po0s] {
    display: flex;
    align-items: center;
    gap: .35rem;
    color: var(--muted);
    font-size: .8rem;
}

@media (max-width: 900px) {
    .rs-table th[b-0ruuz4po0s],
    .rs-table td[b-0ruuz4po0s] {
        padding: .3rem .35rem;
    }

    .rs-tile[b-0ruuz4po0s] {
        flex: 1 1 8rem;
        min-width: 0;
    }
}
/* /Components/Pages/RemoteView.razor.rz.scp.css */
/* SharpRemote SR-304 — the viewer's host page. BareLayout gives it the whole viewport; this makes the
   viewer fill it rather than sit in a card, because a remote desktop shown at half height is not usable on
   the device it is most often read on. */
.remote-view[b-x0os0xm97z] {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding: .6rem;
    /* Dynamic viewport units: on iOS the browser chrome collapses and `100vh` leaves a strip of the page
       unreachable under it. `100dvh` is the measured size; the `100vh` line before it is the fallback for
       engines that do not know it. */
    height: 100vh;
    height: 100dvh;
    box-sizing: border-box;
}

.rv-head[b-x0os0xm97z] {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex: 0 0 auto;
}

/* SharpRemote SR-307 — the §15 FALLBACK fullscreen, for the browsers that have no Fullscreen API (iOS Safari
   on an iPhone). It cannot touch the browser's own chrome — only the home-screen PWA does that — so what it
   does is give the whole of THIS page to the picture: the back link goes and the padding with it. The class
   is set by the viewer's module on this element; `RemoteViewer.razor.css` hides the toolbar and the notes. */
.remote-view.rv-filled[b-x0os0xm97z] {
    padding: 0;
    gap: 0;
}

.remote-view.rv-filled .rv-head[b-x0os0xm97z] {
    display: none;
}

@media (max-width: 900px) {
    .remote-view[b-x0os0xm97z] {
        padding: .35rem;
        /* Safe-area insets: a phone's rounded corners and home indicator overlap the viewport, and the End
           button must not sit under either. `viewport-fit=cover` in the page's own viewport meta (SR-307) is
           what makes these read anything but 0 on iOS. */
        padding-bottom: calc(.35rem + env(safe-area-inset-bottom, 0px));
        padding-left: calc(.35rem + env(safe-area-inset-left, 0px));
        padding-right: calc(.35rem + env(safe-area-inset-right, 0px));
    }

    /* Filled, the insets are the ONLY padding left: the picture takes everything the phone's own hardware is
       not already covering. */
    .remote-view.rv-filled[b-x0os0xm97z] {
        padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
                 env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
    }
}
/* /Components/Pages/Welcome.razor.rz.scp.css */
/* Standalone public landing page — self-contained styles.
   Consumes only the global brand tokens from app.css :root (design tokens, not a
   reused component); no dependency on any .app-* / shared component class. */

.wl-viewport[b-ysip2zo8my] {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1.25rem;
    background:
        radial-gradient(120% 90% at 50% -10%, var(--brand-pale, #eef4e9), transparent 60%),
        var(--surface-2, #f4f6f3);
}

.wl-card[b-ysip2zo8my] {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    max-width: 34rem;
    width: 100%;
    padding: 2.5rem 2rem 2.25rem;
    background: var(--surface, #fff);
    border: 1px solid var(--border, #dde3dc);
    border-radius: calc(var(--radius, 8px) * 2);
    box-shadow: var(--shadow, 0 4px 14px rgba(0, 0, 0, .10));
}

.wl-figure[b-ysip2zo8my] {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    margin-bottom: 1rem;
}

.wl-glow[b-ysip2zo8my] {
    position: absolute;
    width: 78%;
    aspect-ratio: 1;
    border-radius: 50%;
    background: radial-gradient(circle,
        var(--brand-light, #7ba85f) 0%,
        rgba(123, 168, 95, 0.28) 42%,
        transparent 70%);
    filter: blur(2px);
    opacity: 0.55;
    z-index: 0;
}

.wl-mascot[b-ysip2zo8my] {
    position: relative;
    z-index: 1;
    width: 240px;
    max-width: 64%;
    height: auto;
    object-fit: contain;
    filter: drop-shadow(0 8px 16px rgba(36, 61, 41, 0.18));
}

.wl-wordmark[b-ysip2zo8my] {
    width: 220px;
    max-width: 70%;
    height: auto;
    margin: 0 0 0.5rem;
}

.wl-tagline[b-ysip2zo8my] {
    margin: 0 0 1.5rem;
    font-size: 1rem;
    line-height: 1.5;
    color: var(--muted, #5f6b63);
}

.wl-signin[b-ysip2zo8my] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.65rem 1.9rem;
    border-radius: 999px;
    font-size: 1rem;
    font-weight: 600;
    text-decoration: none;
    color: #fff;
    background: linear-gradient(90deg, var(--brand-dark, #2f5236), var(--brand, #4f8a47));
    box-shadow: 0 2px 8px rgba(47, 82, 54, 0.28);
    transition: filter 0.15s ease, transform 0.05s ease;
}

.wl-signin:hover[b-ysip2zo8my] { filter: brightness(1.06); }
.wl-signin:active[b-ysip2zo8my] { transform: translateY(1px); }
.wl-signin:focus-visible[b-ysip2zo8my] {
    outline: 3px solid var(--brand-light, #7ba85f);
    outline-offset: 2px;
}

.wl-what[b-ysip2zo8my] {
    width: 100%;
    margin: 2.25rem 0 0;
    padding-top: 1.75rem;
    border-top: 1px solid var(--border, #dde3dc);
    text-align: left;
}

.wl-what-title[b-ysip2zo8my] {
    margin: 0 0 1rem;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted, #5f6b63);
    text-align: center;
}

.wl-features[b-ysip2zo8my] {
    margin: 0;
    display: grid;
    gap: 0.9rem;
}

.wl-feature[b-ysip2zo8my] { margin: 0; }

.wl-feature dt[b-ysip2zo8my] {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--brand-darkest, #243d29);
}

.wl-feature dd[b-ysip2zo8my] {
    margin: 0.15rem 0 0;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--muted, #5f6b63);
}

.wl-note[b-ysip2zo8my] {
    margin: 1.75rem 0 0;
    font-size: 0.85rem;
    color: var(--muted, #5f6b63);
}

/* Mobile */
@media (max-width: 480px) {
    .wl-card[b-ysip2zo8my] {
        padding: 1.75rem 1.25rem 1.5rem;
        border-radius: var(--radius, 8px);
    }
    .wl-mascot[b-ysip2zo8my] { width: 180px; max-width: 72%; }
    .wl-wordmark[b-ysip2zo8my] { width: 170px; }
}
/* /Components/Remote/RemoteConnectActions.razor.rz.scp.css */
/* SharpRemote SR-312 — the connect page's two actions. They WRAP rather than scroll: at phone width two
   full-width buttons stacked are readable, whereas a row that overflows hides the demoted one exactly where
   the demoted one is the only thing a desktop-mis-read-as-a-phone can still use. */
.rc-action-bar[b-11rabb27jr] {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin: .5rem 0 0;
}

.rc-action-note[b-11rabb27jr] {
    margin: .45rem 0 0;
    color: var(--muted);
    font-size: .82rem;
}

@media (max-width: 900px) {
    /* Thumb targets, and one action per line so the leading one is unmistakable. */
    .rc-action-bar[b-11rabb27jr] {
        flex-direction: column;
        align-items: stretch;
    }

    .rc-action[b-11rabb27jr] {
        padding: .55rem .8rem;
    }
}
/* /Components/Remote/RemoteViewer.razor.rz.scp.css */
/* SharpRemote SR-304 — the browser viewer's surface (spec §9.3).

   THE CANVAS OWNS ITS OWN PIXEL SIZE. Its `width`/`height` ATTRIBUTES are the backing-store size and are set
   by the module from the element's box times devicePixelRatio; everything here only decides the BOX. A rule
   that set width/height in CSS on top of that would stretch the backing store and the picture with it.

   `touch-action: none` is here rather than in SR-307 for a reason SR-005 measured: it (with
   `overscroll-behavior`) is what actually stops the page moving under a drag — `user-scalable=no` is a belt.
   The gestures themselves are SR-308's; this is the surface not fighting them. */

.remote-viewer[b-q28wys5d29] {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    min-height: 0;
    flex: 1 1 auto;
}

/* Not `banner-error`: a locked target, a reconnect and "connecting" are states of a working session, not
   faults — the same judgement WO-068 made demoting the usage-probe warning, and the same one the WinForms
   StateBanner makes with its colours. */
.rv-banner[b-q28wys5d29] {
    background: var(--banner-warn-bg, #332b00);
    color: var(--banner-warn-fg, #f2d672);
    border-radius: 6px;
    padding: .4rem .7rem;
    font-size: .9rem;
}

/* SR-306 — the toolbar. It WRAPS rather than clipping: the phone layout is SR-307's, but a strip that put its
   own controls off the right-hand edge at 390 px would be broken before that card ever ran. */
.rv-toolbar[b-q28wys5d29] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

.rv-tool[b-q28wys5d29] {
    display: flex;
    align-items: center;
    gap: .35rem;
}

.rv-tool-label[b-q28wys5d29] {
    font-size: .78rem;
    color: var(--muted, #9aa0a6);
}

.rv-tool-select[b-q28wys5d29] {
    min-width: 7rem;
}

.rv-keys[b-q28wys5d29] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
}

/* Muted, not a banner: where the keyboard goes is information, not a fault — the same judgement WO-068 made
   demoting the usage-probe warning out of a banner block. */
.rv-keyboard-note[b-q28wys5d29] {
    margin: 0;
    font-size: .78rem;
    color: var(--muted, #9aa0a6);
}

/* The canvas is focusable (the module sets tabindex) so the keyboard has a home; the outline says where the
   keystrokes are going, which is the one thing a user of a remote desktop most needs to know. */
.rv-canvas:focus-visible[b-q28wys5d29] {
    outline: 2px solid var(--accent, #4a9eff);
    outline-offset: -2px;
}

.rv-stage[b-q28wys5d29] {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #000;
    border-radius: 6px;
    overflow: hidden;
}

.rv-canvas[b-q28wys5d29] {
    display: block;
    max-width: 100%;
    max-height: 100%;
    touch-action: none;
    overscroll-behavior: none;
    /* SharpRemote SR-308 — the platform's OWN long-press behaviours, which `touch-action` does not cover:
       iOS answers a held finger with the selection callout, and both platforms will start a text selection
       from a drag. A viewer whose long press is answered by the BROWSER rather than by the target is exactly
       what §9.3's "long-press = right click" cannot survive, so they are turned off here as well as being
       `preventDefault`ed on `pointerdown` — this is the half the event cannot reach. */
    -webkit-touch-callout: none;
    user-select: none;
    /* The remote desktop's own pixels, not the browser's idea of a smooth photo: a 1:1 or magnified view of
       text is legible when it is crisp and mush when it is interpolated. */
    image-rendering: pixelated;
}

.rv-actions[b-q28wys5d29] {
    display: flex;
    gap: .5rem;
    justify-content: flex-end;
}

/* SharpRemote SR-307 — the muted screen note, beside the keyboard one and for the same reason: what is
   actually available is information, not a fault (WO-068's judgement, restated). */
.rv-screen-note[b-q28wys5d29] {
    margin: 0;
    font-size: .78rem;
    color: var(--muted, #9aa0a6);
}

/* SharpRemote SR-307 — THE THUMB BAR. It exists only on the touch layout (`.rv-touch`, set from the module's
   `matchMedia("(pointer: coarse)")` reading — never `navigator.maxTouchPoints`, §15.11) and it sits at the
   BOTTOM of the viewer, which is the band a thumb reaches without re-gripping the phone.

   44px is the minimum comfortable target both platforms' guidance settles on, and it is a MIN-height rather
   than a height so a long label cannot clip. */
.rv-actionbar[b-q28wys5d29] {
    display: flex;
    align-items: stretch;
    gap: .4rem;
    flex: 0 0 auto;
}

.rv-ab[b-q28wys5d29] {
    flex: 1 1 0;
    min-height: 44px;
    min-width: 44px;
    font-size: 1.05rem;
    background: var(--panel, #1b1d21);
    color: var(--fg, #e8eaed);
    border: 1px solid var(--border, #33363b);
    border-radius: 8px;
    cursor: pointer;
}

.rv-ab:disabled[b-q28wys5d29] {
    opacity: .45;
    cursor: default;
}

/* The zoom reading is also the reset, so it is wider than the two steppers and reads as a value. */
.rv-ab-zoom[b-q28wys5d29] {
    flex: 1.4 1 0;
    font-variant-numeric: tabular-nums;
}

.rv-ab-end[b-q28wys5d29] {
    background: var(--danger-bg, #4a1f1f);
    border-color: var(--danger-border, #7a3333);
}

/* SharpRemote SR-309 — a mode that is ON has to LOOK on: the keyboard and the trackpad are toggles among
   four push buttons, and a label alone ("Touch" vs "Trackpad") is a sentence the user has to parse mid-drag. */
.rv-ab-on[b-q28wys5d29] {
    background: var(--accent-bg, #16324a);
    border-color: var(--accent, #4a90d9);
}

/* SharpRemote SR-309 — THE HIDDEN INPUT (§15.11). Moved off screen rather than hidden: FOCUS is the only
   thing that raises a phone's keyboard, and `display:none` / `visibility:hidden` / `width:0` are all
   unfocusable. It keeps a real size for the same reason — a zero-sized focused input is one some engines
   decline to scroll to and therefore decline to raise the keyboard for. `opacity:0` and the off-screen
   position are what make it invisible; `pointer-events:none` is what stops it eating a tap on the picture. */
.rv-osk[b-q28wys5d29] {
    position: absolute;
    left: -9999px;
    top: 0;
    width: 1px;
    height: 1px;
    opacity: 0;
    border: 0;
    padding: 0;
    pointer-events: none;
}

/* SharpRemote SR-309 — the quick-key row. It WRAPS (unlike the toolbar, which shrinks) because twelve keys
   cannot be squeezed onto one phone-width line and still be thumb-sized, and because it is only on screen
   while the keyboard is up — i.e. while the picture is already half covered by the platform's own keyboard. */
.rv-keybar[b-q28wys5d29] {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    flex: 0 0 auto;
}

.rv-kb[b-q28wys5d29] {
    flex: 1 0 auto;
    min-width: 44px;
    min-height: 40px;
    padding: 0 .4rem;
    font-size: .95rem;
    background: var(--panel, #1b1d21);
    color: var(--fg, #e8eaed);
    border: 1px solid var(--border, #33363b);
    border-radius: 8px;
    cursor: pointer;
}

/* The three that ARM are separated by treatment as well as by position: pressing one changes what the NEXT
   press does, which is a different kind of act from sending a key and must not look the same. */
.rv-kb-sticky[b-q28wys5d29] {
    font-weight: 600;
}

.rv-kb-armed[b-q28wys5d29] {
    background: var(--accent-bg, #16324a);
    border-color: var(--accent, #4a90d9);
}

/* SharpRemote SR-309 — the input note, in the same muted register as SR-307's two and standing down in the
   same two bands. */
.rv-input-note[b-q28wys5d29] {
    margin: 0;
    font-size: .78rem;
    color: var(--muted, #9aa0a6);
}

/* SharpRemote SR-310 — THE CONNECTION PILL. Three readings about the link, never a control: `role="status"`,
   no hit target, and `.rv-pill-sep` is `aria-hidden` so a screen reader gets three values rather than three
   values and two middots.

   `margin-inline-start: auto` pushes it to the far end of the toolbar's last row, which is where an
   indicator belongs relative to controls, and `white-space: nowrap` keeps the three figures on one line. */
.rv-pill[b-q28wys5d29] {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin-inline-start: auto;
    padding: .15rem .55rem;
    border: 1px solid var(--border, #33363b);
    border-radius: 999px;
    background: var(--panel, #1b1d21);
    font-size: .76rem;
    /* Tabular figures: a round trip walking 9 → 10 → 9 ms must not shuffle the two readings beside it, which
       is the kind of movement the eye reads as something happening. */
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--muted, #9aa0a6);
}

.rv-pill-link[b-q28wys5d29] {
    color: var(--fg, #e8eaed);
    font-weight: 600;
}

.rv-pill-sep[b-q28wys5d29] {
    opacity: .5;
}

/* SR-310 — the pill's TOUCH home: a row of its own above the thumb bar (SR-309's carry-over), centred,
   because it is the only thing on its line there and a left-hugging pill under six stretched buttons reads
   as a mistake. */
.rv-pill-row[b-q28wys5d29] {
    display: flex;
    justify-content: center;
    flex: 0 0 auto;
}

.rv-pill-row .rv-pill[b-q28wys5d29] {
    margin-inline-start: 0;
}

/* SR-310 — why the browser chose a profile the user never picked. Same muted register as the other three
   notes, and it stands down in the same two bands. */
.rv-quality-note[b-q28wys5d29] {
    margin: 0;
    font-size: .78rem;
    color: var(--muted, #9aa0a6);
}

/* SR-402 — the clipboard row: a toggle and the two gestures a browser is allowed. It wraps like the toolbar
   above it, because at 390 px the three controls do not fit on one line and a "Copy from remote" pushed off
   the right-hand edge is the same failure SR-214 sized the native strip against. */
.rv-clip[b-q28wys5d29] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem .6rem;
    padding: 0 .1rem;
}

.rv-clip-share[b-q28wys5d29] {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: .82rem;
    cursor: pointer;
}

/* SR-401's "state shown in the viewer", in the same muted register as the other four notes — the clipboard
   being off is not a fault and must not read like one (WO-068's register). */
.rv-clip-note[b-q28wys5d29] {
    margin: 0;
    font-size: .78rem;
    color: var(--muted, #9aa0a6);
}

/* What the last press did. Slightly brighter than the standing sentence beside it, because it is the one
   part of the line that changed in response to the user. */
.rv-clip-result[b-q28wys5d29] {
    color: var(--fg, #e8eaed);
}

/* One End, never two: the thumb bar carries it on the touch layout, so the desktop actions row stands down
   rather than putting the same action on screen twice. */
.rv-touch .rv-actions[b-q28wys5d29] {
    display: none;
}

/* SR-307 — the §15 FALLBACK fullscreen. `.rv-filled` is set by the module on the PAGE's root, so this rule
   crosses the isolation boundary the only way scoped CSS allows: the ancestor carries no scope attribute and
   the elements being hidden carry this component's. What it can hide is this page's own chrome; the
   browser's needs the home-screen PWA, which is exactly what the note under the toolbar says. */
.rv-filled .rv-toolbar[b-q28wys5d29],
.rv-filled .rv-keyboard-note[b-q28wys5d29],
.rv-filled .rv-screen-note[b-q28wys5d29],
.rv-filled .rv-quality-note[b-q28wys5d29],
.rv-filled .rv-clip[b-q28wys5d29],
.rv-filled .rv-clip-note[b-q28wys5d29],
.rv-filled .rv-files[b-q28wys5d29],
.rv-filled .rv-file-note[b-q28wys5d29],
.rv-filled .rv-input-note[b-q28wys5d29] {
    display: none;
}

/* SharpRemote SR-404 — the file picker, in the clipboard row's own register (it is the second thing on this
   page that moves data rather than pixels, and the two read as one group). */
.rv-files[b-q28wys5d29] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem .6rem;
    padding: 0 .1rem;
}

/* The real `<input type=file>` is hidden INSIDE its label rather than `display:none`: a hidden input is
   still focusable and still clickable through the label, which is what keeps the control reachable from a
   keyboard. `display:none` would take it out of the accessibility tree entirely. */
.rv-file-pick[b-q28wys5d29] {
    display: inline-flex;
    align-items: center;
    cursor: pointer;
}

.rv-file-input[b-q28wys5d29] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    overflow: hidden;
}

/* The bar draws only while something is being measured (`RemoteViewerFilesView.Percent` answers null
   otherwise) — the "only draw what was measured" rule the usage chip and the connection pill both hold. */
.rv-file-bar[b-q28wys5d29] {
    flex: 1 1 8rem;
    min-width: 6rem;
    height: .4rem;
    border-radius: .2rem;
    background: var(--surface-2, #2a2d31);
    overflow: hidden;
}

.rv-file-fill[b-q28wys5d29] {
    height: 100%;
    background: var(--accent, #8ab4f8);
    transition: width .15s linear;
}

/* Muted, like every other standing note on this page: a browser that can only send one file at a time is a
   limitation to state, not a fault to flag (WO-068's register). */
.rv-file-note[b-q28wys5d29] {
    margin: 0;
    font-size: .78rem;
    color: var(--muted, #9aa0a6);
}

.rv-file-status[b-q28wys5d29] {
    color: var(--fg, #e8eaed);
}

@media (max-width: 900px) {
    .rv-banner[b-q28wys5d29] {
        font-size: .82rem;
    }

    /* Portrait AND landscape: the toolbar's controls shrink rather than wrapping into three rows, because on
       a phone every row it takes is a row of the remote desktop nobody can see. */
    .rv-toolbar[b-q28wys5d29] {
        gap: .35rem;
    }

    .rv-tool-select[b-q28wys5d29] {
        min-width: 5.5rem;
    }

    .rv-keyboard-note[b-q28wys5d29],
    .rv-screen-note[b-q28wys5d29],
    .rv-quality-note[b-q28wys5d29],
    .rv-input-note[b-q28wys5d29] {
        font-size: .72rem;
    }
}

/* Landscape on a phone is the mode a remote desktop is actually usable in, and it is also the one with the
   least vertical room — so the explanatory notes stand down and the picture keeps the height. Both are still
   reachable by turning the phone, which is the gesture that made them scarce. */
@media (max-height: 500px) and (pointer: coarse) {
    .rv-keyboard-note[b-q28wys5d29],
    .rv-screen-note[b-q28wys5d29],
    .rv-quality-note[b-q28wys5d29],
    .rv-input-note[b-q28wys5d29] {
        display: none;
    }

    /* SR-310 — the PILL survives this band, like the quick-key row and unlike the notes: it is a live
       reading rather than an explanation, and landscape on a phone is exactly where a user wants to know
       whether they are on the relay. It only gets smaller. */
    .rv-pill[b-q28wys5d29] {
        font-size: .7rem;
        padding: .1rem .4rem;
    }

    /* SR-309 — the quick-key row SURVIVES this band, unlike the notes. In landscape on a phone the platform
       keyboard takes most of the height and the remaining strip is where the user is actually typing, so the
       row is the one thing here that is not explanatory: it gets smaller rather than going away. */
    .rv-kb[b-q28wys5d29] {
        min-height: 34px;
        font-size: .85rem;
    }
}
