/*
 * components-v2.css
 *
 * v2 design-language primitives. Loaded after tokens.css so every rule
 * here can pull values from `var(--*)`. No hex literals — hex-literal
 * lint will fail the build if any sneak in.
 *
 * Each primitive owns a single CSS class root (e.g. .ps-btn, .ps-badge)
 * with BEM-style variant/size/state modifiers.
 */

/* ─── Type — display-size weight bump ─────────────────────────────
 *
 * Inter at non-bold weights reads thin and slightly off-balance above
 * ~24px. The v2 design language pushes anything from xl up into
 * semibold/bold by default so display headings pick up presence
 * without scattering inline font-weight overrides per page.
 *
 * Bootstrap's .h1-.h6 utility classes are listed alongside their
 * element counterparts so navbar-brand-style headers (which use .h1)
 * pick up the same weight.
 */
h1, .h1, h2, .h2 {
    font-weight: var(--font-weight-bold);
}
h3, .h3, h4, .h4 {
    font-weight: var(--font-weight-semibold);
}

/* Brand wordmark in the top app bar — Bootstrap's .navbar-brand
   defaults to weight 500, which reads thin against the dark header.
   Bumping it to bold keeps it consistent with the v2 heading scale. */
.navbar-brand {
    font-weight: var(--font-weight-bold);
}

/* ─── Button ─────────────────────────────────────────────────────── */

.ps-btn {
    /* layout */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    flex: 0 0 auto;

    /* type */
    font-family: var(--font-family-ui);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    text-decoration: none;
    white-space: nowrap;

    /* shape */
    border: 1px solid transparent;
    border-radius: var(--radius-sm);

    /* motion */
    transition:
        background-color var(--duration-fast) var(--easing-standard),
        border-color var(--duration-fast) var(--easing-standard),
        color var(--duration-fast) var(--easing-standard),
        box-shadow var(--duration-fast) var(--easing-standard);

    cursor: pointer;
    user-select: none;
}

.ps-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.ps-btn[disabled],
.ps-btn[aria-disabled="true"] {
    cursor: not-allowed;
    opacity: 0.55;
}

.ps-btn--block {
    width: 100%;
}

.ps-btn__label {
    display: inline-block;
}

.ps-btn__icon {
    /* FontAwesome handles its own sizing; this just stops awkward
       baseline shifts when the icon sits next to a label. */
    line-height: 1;
}

/* spinner — inherits currentColor so it matches the variant's text.
   0.8s matches the standalone <Spinner>; the v2 duration tokens
   (max 300ms) read as a frantic strobe even at this small size. */
.ps-btn__spinner {
    display: inline-block;
    width: 0.875em;
    height: 0.875em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: var(--radius-pill);
    animation: ps-btn-spin 0.8s linear infinite;
    vertical-align: -0.125em;
}

@keyframes ps-btn-spin {
    to { transform: rotate(360deg); }
}

.ps-btn--loading .ps-btn__label {
    /* Slight visual nudge so the spinner+label combo reads as one
       unit rather than two stacked elements. */
    opacity: 0.85;
}

/* "Pressed" / selected state — overrides the variant's surface tier so
   the button reads as a depressed toggle regardless of the underlying
   Variant. Used by AND/OR segmented controls, Advanced disclosure
   parity, and any future filter chip pattern.

   Treatment: brand-soft tint background + brand text + brand border +
   an inset shadow for the "pushed in" depth cue. Strong enough to read
   against both surface-raised and surface-sunken parents; the inset
   shadow does most of the depth work so the soft tint can stay subtle. */
/* Specificity bump (.ps-btn chained with .ps-btn--pressed) so this rule
   wins over any variant — variants are declared later in source order
   and would otherwise override the pressed background. */
.ps-btn.ps-btn--pressed {
    background-color: var(--color-brand-soft);
    border-color: var(--color-brand);
    color: var(--color-brand);
    box-shadow: var(--shadow-pressed-inset);
}
.ps-btn.ps-btn--pressed:hover:not([disabled]):not([aria-disabled="true"]) {
    /* Hover slightly shifts text/border to brand-hover so the user gets
       feedback that a second click would un-press it. */
    background-color: var(--color-brand-soft);
    border-color: var(--color-brand-hover);
    color: var(--color-brand-hover);
    box-shadow: var(--shadow-pressed-inset);
}

/* sizes — pinned to the .ps-input heights below so input + button
   compositions (SecretInput, modal footers next to a Field, search
   bars, KeyValueEditor add row, etc.) read as a unified row. The Md
   size used to be 34px, leaving a 2px gap above the 32px Md input;
   trimming buttons by 2px is barely perceptible standalone but
   makes the input + button pairing pixel-aligned. */
.ps-btn--sm {
    font-size: var(--font-size-sm);
    padding: var(--space-1) var(--space-3);
    min-height: 1.75rem; /* 28px — matches .ps-input--sm */
}

.ps-btn--md {
    font-size: var(--font-size-base);
    padding: var(--space-1) var(--space-4);
    min-height: 2rem; /* 32px — matches .ps-input--md */
}

.ps-btn--lg {
    font-size: var(--font-size-lg);
    padding: var(--space-2) var(--space-6);
    min-height: 2.5rem; /* 40px — matches .ps-input--lg */
}

/* variants — primary */
.ps-btn--primary {
    background-color: var(--color-brand);
    border-color: var(--color-brand);
    color: var(--color-ink-on-brand);
}
.ps-btn--primary:hover:not([disabled]):not([aria-disabled="true"]) {
    background-color: var(--color-brand-hover);
    border-color: var(--color-brand-hover);
}
.ps-btn--primary:active:not([disabled]):not([aria-disabled="true"]) {
    background-color: var(--color-brand-active);
    border-color: var(--color-brand-active);
}

/* variants — secondary (outline on surface) */
.ps-btn--secondary {
    background-color: var(--color-surface-raised);
    border-color: var(--color-border-strong);
    color: var(--color-ink);
}
.ps-btn--secondary:hover:not([disabled]):not([aria-disabled="true"]) {
    background-color: var(--color-surface-sunken);
    border-color: var(--color-border-strong);
}
.ps-btn--secondary:active:not([disabled]):not([aria-disabled="true"]) {
    background-color: var(--color-surface-sunken);
    border-color: var(--color-ink-muted);
}

/* variants — ghost (no chrome until hover) */
.ps-btn--ghost {
    background-color: transparent;
    border-color: transparent;
    color: var(--color-ink);
}
.ps-btn--ghost:hover:not([disabled]):not([aria-disabled="true"]) {
    background-color: var(--color-surface-sunken);
}
.ps-btn--ghost:active:not([disabled]):not([aria-disabled="true"]) {
    background-color: var(--color-brand-soft);
}

/* variants — danger */
.ps-btn--danger {
    background-color: var(--color-error-fg);
    border-color: var(--color-error-fg);
    color: var(--color-ink-on-status);
}
.ps-btn--danger:hover:not([disabled]):not([aria-disabled="true"]) {
    /* No dedicated hover token for status fg yet; brightness() nudge
       avoids inventing a new colour value while still giving the
       hover/active affordance. Follow-up issue tracks adding
       --color-error-fg-hover / -active to tokens.css. */
    filter: brightness(0.92);
}
.ps-btn--danger:active:not([disabled]):not([aria-disabled="true"]) {
    filter: brightness(0.84);
}

/* variants — link */
.ps-btn--link {
    background-color: transparent;
    border-color: transparent;
    color: var(--color-brand);
    padding-left: 0;
    padding-right: 0;
    min-height: auto;
}
.ps-btn--link:hover:not([disabled]):not([aria-disabled="true"]) {
    color: var(--color-brand-hover);
    text-decoration: underline;
}
.ps-btn--link:active:not([disabled]):not([aria-disabled="true"]) {
    color: var(--color-brand-active);
}

/* ─── IconButton ─────────────────────────────────────────────────── */

.ps-icon-btn {
    /* Inherit ps-btn's interaction shape via composition: the Razor
       component renders .ps-btn .ps-icon-btn .ps-btn--<variant>.
       This rule only forces a square footprint and centers the icon. */
    padding: 0;
    aspect-ratio: 1 / 1;
}
.ps-icon-btn.ps-btn--sm { width: 1.75rem; min-height: 1.75rem; }
.ps-icon-btn.ps-btn--md { width: 2rem;    min-height: 2rem;    }
.ps-icon-btn.ps-btn--lg { width: 2.5rem;  min-height: 2.5rem;  }

.ps-icon-btn .ps-btn__icon {
    /* Icons pick up the variant's text colour via currentColor — no
       additional rule needed. */
    margin: 0;
}

/* ─── Badge ──────────────────────────────────────────────────────── */

.ps-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-family: var(--font-family-ui);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    white-space: nowrap;
}

.ps-badge--pill {
    border-radius: var(--radius-pill);
}

.ps-badge__icon {
    line-height: 1;
}

/* sizes */
.ps-badge--sm {
    font-size: var(--font-size-xs);
    padding: 1px var(--space-2);
}

.ps-badge--md {
    font-size: var(--font-size-sm);
    padding: 2px var(--space-3);
}

/*
 * Tone × variant matrix.
 *
 * `soft`    — bg-soft + fg + border (the canonical status pill look)
 * `solid`   — fg-as-background + ink-on-status (high emphasis, e.g. counts)
 * `outline` — surface bg + fg text + fg border (low emphasis chip)
 *
 * Neutral and Brand tones reuse the same token vocabulary by
 * mapping onto surface/ink/brand triplets so the matrix stays
 * complete without inventing dedicated tokens.
 */

/* neutral */
.ps-badge--neutral.ps-badge--soft {
    background-color: var(--color-surface-sunken);
    color: var(--color-ink-muted);
    border-color: var(--color-border);
}
.ps-badge--neutral.ps-badge--solid {
    background-color: var(--color-ink-muted);
    color: var(--color-surface-raised);
    border-color: var(--color-ink-muted);
}
.ps-badge--neutral.ps-badge--outline {
    background-color: transparent;
    color: var(--color-ink-muted);
    border-color: var(--color-border-strong);
}

/* brand */
.ps-badge--brand.ps-badge--soft {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
    border-color: var(--color-brand-soft);
}
.ps-badge--brand.ps-badge--solid {
    background-color: var(--color-brand);
    color: var(--color-ink-on-brand);
    border-color: var(--color-brand);
}
.ps-badge--brand.ps-badge--outline {
    background-color: transparent;
    color: var(--color-brand);
    border-color: var(--color-brand);
}

/* status tones share a single template via local CSS variables */
.ps-badge--running { --ps-badge-fg: var(--color-running-fg); --ps-badge-bg: var(--color-running-bg-soft); --ps-badge-border: var(--color-running-border); }
.ps-badge--success { --ps-badge-fg: var(--color-success-fg); --ps-badge-bg: var(--color-success-bg-soft); --ps-badge-border: var(--color-success-border); }
.ps-badge--pending { --ps-badge-fg: var(--color-pending-fg); --ps-badge-bg: var(--color-pending-bg-soft); --ps-badge-border: var(--color-pending-border); }
.ps-badge--error   { --ps-badge-fg: var(--color-error-fg);   --ps-badge-bg: var(--color-error-bg-soft);   --ps-badge-border: var(--color-error-border);   }
.ps-badge--agent   { --ps-badge-fg: var(--color-agent-fg);   --ps-badge-bg: var(--color-agent-bg-soft);   --ps-badge-border: var(--color-agent-border);   }

.ps-badge--running.ps-badge--soft,
.ps-badge--success.ps-badge--soft,
.ps-badge--pending.ps-badge--soft,
.ps-badge--error.ps-badge--soft,
.ps-badge--agent.ps-badge--soft {
    background-color: var(--ps-badge-bg);
    color: var(--ps-badge-fg);
    border-color: var(--ps-badge-border);
}

.ps-badge--running.ps-badge--solid,
.ps-badge--success.ps-badge--solid,
.ps-badge--pending.ps-badge--solid,
.ps-badge--error.ps-badge--solid,
.ps-badge--agent.ps-badge--solid {
    background-color: var(--ps-badge-fg);
    color: var(--color-ink-on-status);
    border-color: var(--ps-badge-fg);
}

.ps-badge--running.ps-badge--outline,
.ps-badge--success.ps-badge--outline,
.ps-badge--pending.ps-badge--outline,
.ps-badge--error.ps-badge--outline,
.ps-badge--agent.ps-badge--outline {
    background-color: transparent;
    color: var(--ps-badge-fg);
    border-color: var(--ps-badge-fg);
}

/* ─── DataTable ──────────────────────────────────────────────────── */

.ps-data-table {
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-subtle);
    overflow: hidden;
}

/* #654 — flush variant. Drops the chrome so the table fills its
   container edge-to-edge. Use when DataTable is embedded inside a
   surface that already provides its own frame (designer bottom-panel
   tab body, drawer body, etc.) — without this, the inset border +
   radius read as frame-within-a-frame. */
.ps-data-table--flush {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

.ps-data-table__scroll {
    /* Horizontal scroll for tables wider than their container without
       bleeding past the rounded outer border. */
    overflow-x: auto;
}

.ps-data-table__table {
    width: 100%;
    margin: 0;
    border-collapse: collapse;
    background-color: var(--color-surface-raised);
    color: var(--color-ink);
    vertical-align: middle;
}

.ps-data-table__table th,
.ps-data-table__table td {
    padding: var(--space-2) var(--space-3);
    vertical-align: middle;
    text-align: left;
}

.ps-data-table__head {
    background-color: var(--color-surface);
    color: var(--color-ink-muted);
}

.ps-data-table__head th {
    /* Header cells read as labels: smaller, muted, with a stronger
       bottom border than body rows. */
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    text-transform: none;
    letter-spacing: 0;
    border-bottom: 1px solid var(--color-border);
}

.ps-data-table__row {
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-data-table__row:last-child {
    border-bottom: none;
}

.ps-data-table__row:hover {
    background-color: var(--color-surface-sunken);
}

.ps-data-table__row--clickable {
    cursor: pointer;
}

/* ─── DataTable — sub-row ──────────────────────────────────────────
 *
 * Full-width secondary row rendered after a main row when the consumer
 * supplies a SubRow fragment. Reads as a continuation of the main row,
 * not as a separate record:
 *   - The main row drops its bottom border so the visual group is
 *     contiguous; the sub-row carries the row-group's bottom border
 *     instead.
 *   - Hovering the main row tints the sub-row too (and vice versa) so
 *     the two rows always share a hover state.
 *   - Sub-row text is muted + small by default — typical use is a
 *     description that should de-emphasise relative to the main row's
 *     name/identifier.
 *   - The cell aligns to the table's left padding so the description
 *     reads as a continuation of the Name column on the row above.
 */

.ps-data-table__row--has-subrow {
    border-bottom: none;
}

.ps-data-table__subrow {
    border-bottom: 1px solid var(--color-border-subtle);
    background-color: inherit;
}

.ps-data-table__subrow:last-child {
    border-bottom: none;
}

.ps-data-table__subrow > td {
    padding-top: 0;
    padding-bottom: var(--space-2);
    color: var(--color-ink-muted);
    font-size: var(--font-size-xs);
    line-height: var(--line-height-snug, 1.35);
}

/* Sub-row tracks its main-row's hover state. The sibling selector
   ties the two rows together visually — hover the main row and the
   sub-row tints too. */
.ps-data-table__row:hover + .ps-data-table__subrow,
.ps-data-table__subrow:hover {
    background-color: var(--color-surface-sunken);
}

.ps-data-table__row--clickable + .ps-data-table__subrow {
    cursor: pointer;
}

/* ─── GroupedDataTable — section header rows ───────────────────────
 *
 * The sectioned-list pattern: one <thead>, then a full-width
 * group-header row before each group's data rows. The header reads as
 * a band that sits above its rows — tinted like the table head, with a
 * disclosure chevron that rotates when the group collapses. The whole
 * row is clickable to toggle; the chevron is also a real <button> so
 * the affordance is keyboard-reachable.
 */

.ps-data-table__group-header {
    background-color: var(--color-surface);
    border-top: 1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border-subtle);
    cursor: pointer;
    user-select: none;
}

/* The first group abuts the <thead>'s own bottom border — no double rule. */
.ps-data-table__group-header:first-child {
    border-top: none;
}

.ps-data-table__group-header:hover {
    background-color: var(--color-surface-sunken);
}

.ps-data-table__group-headline {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-data-table__group-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-ink-muted);
    cursor: pointer;
}

.ps-data-table__group-toggle:hover {
    background-color: var(--color-surface-raised);
    color: var(--color-ink);
}

.ps-data-table__group-chevron {
    font-size: var(--font-size-xs);
    transition: transform 0.15s ease;
}

/* Collapsed → chevron points right (rotated up from the resting "down"). */
.ps-data-table__group-header--collapsed .ps-data-table__group-chevron {
    transform: rotate(-90deg);
}

.ps-data-table__group-label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: var(--font-weight-semibold);
    color: var(--color-ink);
}

.ps-data-table__group-count {
    color: var(--color-ink-muted);
    font-weight: var(--font-weight-normal);
    font-size: var(--font-size-sm);
}

.ps-data-table__footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--color-border);
    background-color: var(--color-surface-raised);
}

.ps-data-table__pageinfo {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-data-table__pager {
    display: inline-flex;
    gap: var(--space-2);
}

/* ─── DataTable — bulk-select column + action bar (#888) ──────────
 *
 * When Selectable=true, the table grows a leading checkbox column and
 * a sticky bulk-action bar above the table that appears once any row
 * is selected. Selected rows get a subtle highlight so the chosen set
 * stays visually distinct from the unchecked rest.
 */

.ps-data-table__select-col {
    width: 1%;
    white-space: nowrap;
    padding-right: 0;
}

.ps-data-table__row--selected {
    background-color: var(--color-surface-selected, var(--color-surface-raised));
}

.ps-data-table__bulk-bar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    border-bottom: 1px solid var(--color-border);
    background-color: var(--color-surface-raised);
    position: sticky;
    top: 0;
    z-index: 1;
}

.ps-data-table__bulk-count {
    color: var(--color-ink);
    font-size: var(--font-size-sm);
}

.ps-data-table__bulk-actions {
    margin-left: auto;
    display: inline-flex;
    gap: var(--space-2);
}

/* ─── DataTable — sortable headers ────────────────────────────────
 *
 * <SortableHeader> renders a <th> that wraps its label in a button.
 * Visual contract:
 *   - The button stretches the cell so the entire header is hot.
 *   - Inactive columns show a faint up/down chevron pair as an
 *     affordance; active columns show a single solid chevron in the
 *     active direction.
 *   - The DOM is identical across states — only the parent th's
 *     modifier class flips. CSS handles visibility so Blazor's render
 *     diff doesn't collide with FontAwesome's SVG replacement.
 */

.ps-data-table__head th.ps-data-table__sortable {
    /* The button owns the padding so it can fill the cell and stay
       clickable across the full label area. */
    padding: 0;
}

.ps-sortable-header {
    /* Reset native button chrome — header still reads like a th label. */
    appearance: none;
    background: transparent;
    border: 0;
    margin: 0;
    width: 100%;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    color: inherit;
    font-family: inherit;
    font-size: inherit;
    font-weight: inherit;
    text-align: left;
    border-radius: 0;
    transition: background-color 120ms ease;
}

.ps-sortable-header:hover {
    background-color: var(--color-surface-sunken);
}

.ps-sortable-header:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -2px;
}

.ps-sortable-header__label {
    flex: 1 1 auto;
    min-width: 0;
}

.ps-sortable-header__icon {
    /* Stacked chevron pair container — always rendered, visibility of
       each chevron is driven by the parent <th>'s state class. */
    flex: 0 0 auto;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    line-height: 1;
    color: var(--color-ink-subtle);
}

.ps-sortable-header__chevron {
    font-size: 0.55em;
    line-height: 1;
}

.ps-sortable-header__chevron--down {
    margin-top: 1px;
}

/* Active asc: emphasize the up chevron, hide the down chevron. */
.ps-data-table__sortable--asc .ps-sortable-header__icon {
    color: var(--color-ink);
}
.ps-data-table__sortable--asc .ps-sortable-header__chevron--up {
    font-size: 0.75em;
}
.ps-data-table__sortable--asc .ps-sortable-header__chevron--down {
    display: none;
}

/* Active desc: emphasize the down chevron, hide the up chevron. */
.ps-data-table__sortable--desc .ps-sortable-header__icon {
    color: var(--color-ink);
}
.ps-data-table__sortable--desc .ps-sortable-header__chevron--down {
    font-size: 0.75em;
    margin-top: 0;
}
.ps-data-table__sortable--desc .ps-sortable-header__chevron--up {
    display: none;
}

/* ─── QueryBar — Advanced predicate builder ─────────────────────
 *
 * The Advanced disclosure renders a nested-group predicate builder.
 * v2 styling:
 *   - Outer panel and inner groups read from token surface/border.
 *   - The AND/OR control is a `.ps-segmented` pair of <Button>s so
 *     it shares font-size + height with the sm form-selects next to
 *     it. Adjacent buttons collapse their shared border into a
 *     single 1px line.
 *   - Per-row/per-group controls are <IconButton> Ghost Sm + small
 *     <Button> Ghost Sm so the entire builder lives at one rhythm.
 */

.query-bar {
    display: flex;
    flex-direction: column;
    margin-bottom: var(--space-3);
}

.query-bar__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.query-bar__input-row {
    display: flex;
    flex: 1 1 auto;
    align-items: stretch;
    min-width: 0;
}

.query-bar__input-row > .ps-input,
.query-bar__input-row > .ps-input-wrap {
    flex: 1 1 auto;
    min-width: 0;
}

/* Fuse the search input and the Advanced toggle into a single
   bordered control: input loses its right corners, button loses its
   left corners, and the shared edge collapses by overlapping the
   button onto the input by 1px so the seam paints as a single line.
   Only applies when an adjacent sibling exists; pages that omit the
   Advanced toggle (no Fields supplied) keep the input's right
   corners rounded. */
.query-bar__input-row > .ps-input:not(:last-child),
.query-bar__input-row > .ps-input-wrap:not(:last-child) > .ps-input {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.query-bar__input-row > .ps-btn {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    margin-left: -1px;
    position: relative;
    /* Match the docked input's resting border + use --color-surface
       for the background so the button reads as adjacent chrome
       rather than a second raised control. The hover/focus rule
       below restores the strong border (matching the input's own
       hover treatment). */
    background-color: var(--color-surface);
    border-color: var(--color-border);
}

.query-bar__input-row > .ps-btn:hover,
.query-bar__input-row > .ps-btn:focus-visible,
.query-bar__input-row > .ps-btn--pressed {
    /* Lift the button border on top of the input border so the
       hover / focus / pressed border colour wins on the seam. */
    z-index: 1;
    border-color: var(--color-border-strong);
}

.query-bar__filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.query-bar__actions-slot {
    margin-left: auto;
}

.query-bar-advanced {
    margin-top: var(--space-2);
    padding: var(--space-3);
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

/*
 * When the Advanced disclosure is open, fuse the search-input row and
 * the builder panel into a single bordered control: drop the bottom
 * corners of the input + the top corners of the panel.
 */
.query-bar--expanded .query-bar__input-row > .ps-input,
.query-bar--expanded .query-bar__input-row > .ps-input-wrap > .ps-input,
.query-bar--expanded .query-bar__input-row > .ps-btn {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.query-bar--expanded .query-bar-advanced {
    margin-top: 0;
    border-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

.query-bar-shortcuts {
    padding: var(--space-2) var(--space-3);
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.query-bar--expanded .query-bar-shortcuts {
    margin-top: 0;
    border-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

/* Predicate row layout — labels + inputs aligned in a horizontal strip.
   The NOT toggle + field/op selects + value editor + remove button all
   sit on a single row. */
.query-bar-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.query-bar-row__not {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    cursor: pointer;
    min-height: 1.75rem;
}

.query-bar-row__not-input {
    margin: 0;
    flex-shrink: 0;
}

.query-bar-row__not-label {
    line-height: 1;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--color-ink-muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.query-bar-row__field {
    flex: 0 1 12rem;
    min-width: 0;
}

.query-bar-row__op {
    flex: 0 1 9rem;
    min-width: 0;
}

.query-bar-row__value {
    flex: 1 1 14rem;
    min-width: 0;
}

.query-bar-row__remove {
    margin-left: auto;
}

.query-bar-group {
    padding: var(--space-2);
    margin-bottom: var(--space-2);
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

.query-bar-group__toolbar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.query-bar-group__remove {
    margin-left: auto;
}

.query-bar-group__nested {
    margin-left: var(--space-3);
}

/* The root group's only sibling is the Reset/Apply row, which now
   owns its own top spacing via .query-bar-actions. Drop the group's
   own margin-bottom so the two don't stack into a doubled gap. */
.query-bar-advanced > .query-bar-group {
    margin-bottom: 0;
}

/* Reset / Apply action row inside the Advanced panel. Token-driven
   spacing replaces Bootstrap's `mt-3 gap-2` so the gap above matches
   the panel's padding-top + padding-bottom (all --space-3). */
.query-bar-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.query-bar-group:last-child {
    margin-bottom: 0;
}

/*
 * Strip the trailing margin on whatever is the last direct child of a
 * group — predicate row, nested-group wrapper, or empty-state hint —
 * so the gap below the final element matches the gap between siblings
 * instead of doubling against the group's own padding-bottom.
 */
.query-bar-group > :last-child {
    margin-bottom: 0;
}

.query-bar-group__empty {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-bottom: var(--space-2);
}

/* Segmented control — collapses a row of <Button>s into a fused unit.
   Used by AND/OR; future tabs / segmented-tabs primitives will share
   this rule. */
.ps-segmented {
    display: inline-flex;
    align-items: stretch;
}

.ps-segmented .ps-btn {
    border-radius: 0;
    position: relative;
}

.ps-segmented .ps-btn + .ps-btn {
    margin-left: -1px;
}

.ps-segmented .ps-btn:hover,
.ps-segmented .ps-btn:focus-visible,
.ps-segmented .ps-btn--pressed {
    /* Keep the active/hovered button's border on top so the fused
       seam doesn't visually break. */
    z-index: 1;
}

.ps-segmented .ps-btn:first-child {
    border-top-left-radius: var(--radius-sm);
    border-bottom-left-radius: var(--radius-sm);
}

.ps-segmented .ps-btn:last-child {
    border-top-right-radius: var(--radius-sm);
    border-bottom-right-radius: var(--radius-sm);
}

/* ─── QueryBar — Advanced toggle border bridge ───────────────────
 *
 * The v2 Button now drives its own visual treatment via .ps-btn /
 * .ps-btn--pressed; nothing extra needed once it's docked inside
 * .query-bar__input-row above.
 */

/* ─── ListPageLayout ─────────────────────────────────────────────── */

.ps-list-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.ps-list-page__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* PageHeader mirrors the list-page header so detail / dashboard pages
   share the same title/subtitle/actions treatment as list pages. */
.ps-page-header {
    /* Two-row layout: row 1 = title + right-aligned actions, row 2 =
       subtitle at full container width. The previous single-row design
       squeezed long descriptions into a narrow column whenever the
       actions area was wide; this layout keeps actions pinned right
       AND lets descriptions use the full width. */
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-bottom: var(--space-3);
}

.ps-page-header__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    /* No flex-wrap: actions stay pinned right even when the title is
       long. The title uses flex: 1 1 auto + min-width: 0 (below) so
       its text wraps inside the available space
       instead of pushing buttons out. */
}

.ps-page-header__title {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    color: var(--color-ink);
    line-height: var(--line-height-tight);
    margin: 0;
    /* Title sits inside .ps-page-header__row alongside the actions —
       must claim its share of the row but allow its text to wrap
       (min-width: 0) when the title is long, so the actions don't
       get pushed out. */
    flex: 1 1 auto;
    min-width: 0;
}

.ps-page-header__subtitle {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: var(--line-height-base);
    margin: 1em 0 0 0;
}

.ps-page-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
}

.ps-list-page__heading {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    flex: 1 1 auto;
    min-width: 0;
}

.ps-list-page__title {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    color: var(--color-ink);
    line-height: var(--line-height-tight);
    margin: 0;
}

.ps-list-page__subtitle {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: var(--line-height-base);
    margin: 0;
}

.ps-list-page__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
}

.ps-list-page__summary {
    /* Page-level stat / status strip. Compact horizontal row of metrics
       and optional contextual actions — sits between the header and
       the Filter slot to signal "facts about the dataset" before the
       user dives into filtering or scanning rows. The content owns its
       own card / chrome; this slot just provides the gap rhythm. */
}

.ps-list-page__filter {
    /* QueryBar ships with `mb-3` (a Bootstrap utility, !important) for
       legacy callers that don't use ListPageLayout. Inside the layout
       the `gap: var(--space-4)` on .ps-list-page already separates
       sections, so neutralise the QueryBar's own margin to avoid a
       doubled (16 + 16 = 32px) gap between the bar and the body. */
}
.ps-list-page__filter > .query-bar {
    margin-bottom: 0 !important;
}

.ps-list-page__body {
    /* Slot for the table / grid / whatever. No surrounding chrome — the
       inner primitive (e.g. DataTable) owns its own border. */
}

/* ─── DetailPageLayout ───────────────────────────────────────────────
 *
 * Detail-page chrome. Stacks: breadcrumb → page header → body (optionally
 * split into main + right meta sidebar). Visually inherits the list-page
 * gap rhythm so detail/list pages feel like the same surface.
 * ─────────────────────────────────────────────────────────────────── */

.ps-detail-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.ps-detail-page__breadcrumb {
    /* Breadcrumb owns its own type styling; this slot just provides
       the rhythm gap to the page header below. */
}

.ps-detail-page__main {
    /* Vertical stack of cards / sections. Children own their own
       chrome; the gap rhythm here keeps cards from butting against
       each other when a page composes several. */
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    min-width: 0;
}

.ps-detail-page__split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 20rem);
    gap: var(--space-4);
    align-items: start;
}

.ps-detail-page__aside {
    /* Sticky on tall viewports so meta stays visible while the main
       content scrolls. Pages that don't want stick-on-scroll can
       override locally. */
    position: sticky;
    top: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
}

@media (max-width: 64rem) {
    .ps-detail-page__split {
        grid-template-columns: minmax(0, 1fr);
    }

    .ps-detail-page__aside {
        position: static;
    }
}

/* ─── Table actions column ───────────────────────────────────────────
 *
 * Convention: every list table in the app puts its action-button
 * column last and right-aligns it. Use this class on both the <th>
 * and the per-row <td>; nowrap keeps the icon-button row on a single
 * line even when the table compresses. Centralised here so future
 * changes (e.g. sticky positioning, fixed minimum width) land in one
 * place rather than 20+ ad-hoc text-end utility classes.
 * ──────────────────────────────────────────────────────────────── */

/* Bumped specificity (0,2,1) so it beats the .ps-data-table__table td
   default (text-align: left, also (0,1,1)). Without the table-scoped
   selectors the right-align silently fails inside DataTable rows. */
.ps-data-table__table th.ps-table-actions,
.ps-data-table__table td.ps-table-actions,
.ps-table-actions {
    text-align: right;
    white-space: nowrap;
}

/* ─── EmptyState ─────────────────────────────────────────────────── */

.ps-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    color: var(--color-ink-muted);
    padding: var(--space-12) var(--space-4);
    gap: var(--space-3);
}

.ps-empty-state__icon {
    font-size: 2.5rem;
    color: var(--color-ink-subtle);
    line-height: 1;
    /* Flex centering so the icon glyph sits on the same axis as the
       text below regardless of whether FA renders it as <i> or <svg>. */
    display: flex;
    align-items: center;
    justify-content: center;
}

.ps-empty-state__title {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--color-ink);
    line-height: var(--line-height-tight);
    margin: 0;
}

.ps-empty-state__message {
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
    margin: 0;
    max-width: 40rem;
}

.ps-empty-state__actions {
    margin-top: var(--space-2);
}

/* ─── Spinner ──────────────────────────────────────────────────────
 *
 * Token-driven loading spinner. The Button primitive ships its own
 * .ps-btn__spinner that pre-dates this rule set (and uses the same
 * geometry); leaving that rule alone for now to avoid disturbing the
 * button's tested behavior. New consumers go through <Spinner>.
 */
.ps-spinner {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-brand);
}

.ps-spinner__circle {
    display: inline-block;
    border-style: solid;
    border-color: currentColor;
    border-right-color: transparent;
    border-radius: var(--radius-pill);
    /* 0.8s matches Bootstrap's spinner-border baseline — the v2 duration
       tokens cap at 300ms which reads as a frantic strobe on a 1.5rem+
       circle. Loading spinners want a calm sweep, not a fast UI animation. */
    animation: ps-spinner-spin 0.8s linear infinite;
    vertical-align: -0.125em;
}

@keyframes ps-spinner-spin {
    to { transform: rotate(360deg); }
}

.ps-spinner__label {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: 1;
}

/* Sizes */
.ps-spinner--sm .ps-spinner__circle {
    width: 0.875em;
    height: 0.875em;
    border-width: 2px;
}
.ps-spinner--md .ps-spinner__circle {
    width: 1.5rem;
    height: 1.5rem;
    border-width: 2.5px;
}
.ps-spinner--lg .ps-spinner__circle {
    width: 3rem;
    height: 3rem;
    border-width: 3px;
}

/* Tones — drive currentColor so the circle inherits it */
.ps-spinner--neutral  { color: var(--color-ink-muted); }
.ps-spinner--brand    { color: var(--color-brand); }
.ps-spinner--inverted { color: var(--color-ink-on-brand); }

/* Centered wrapper — convenience for page-level loading states */
.ps-spinner-center {
    display: flex;
    justify-content: center;
    align-items: center;
}
.ps-spinner-center--sm { padding: var(--space-3) 0; }
.ps-spinner-center--md { padding: var(--space-6) 0; }
.ps-spinner-center--lg { padding: var(--space-8) 0; }

/* ─── LoadingPanel ────────────────────────────────────────────────
 *
 * The single "this surface is loading" arrangement. Layout-only wrapper
 * around the existing .ps-spinner + .ps-spinner__label primitives — it
 * supersedes .ps-spinner-center for the loading case (Spinner + visible
 * label), so it reuses the same --space-3/6/8 padding scale.
 */
.ps-loading-panel {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.ps-loading-panel--centered { justify-content: center; }
.ps-loading-panel--stacked  { flex-direction: column; }
.ps-loading-panel--inline   { flex-direction: row; }

.ps-loading-panel--sm { padding: var(--space-3) 0; }
.ps-loading-panel--md { padding: var(--space-6) 0; }
.ps-loading-panel--lg { padding: var(--space-8) 0; }

/* ─── ProgressBar ─────────────────────────────────────────────────
 *
 * Determinate: width-driven .ps-progress__fill, transition for smooth
 * tick-up. Indeterminate: 30%-wide sweep that translates left→right.
 * Tone tints the fill via status tokens; the track stays surface-sunken
 * so the contrast lifts the colored fill regardless of tone.
 */
.ps-progress {
    display: block;
    width: 100%;
}

.ps-progress__caption {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    margin-bottom: var(--space-1);
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: 1.2;
}

.ps-progress__label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-progress__value {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    color: var(--color-ink);
}

.ps-progress__track {
    position: relative;
    width: 100%;
    background: var(--color-surface-sunken);
    border-radius: var(--radius-pill);
    overflow: hidden;
}

.ps-progress__fill {
    height: 100%;
    background: var(--color-brand);
    border-radius: var(--radius-pill);
    transition: width var(--duration-base) ease-out;
}

/* Sizes */
.ps-progress--sm .ps-progress__track { height: 4px; }
.ps-progress--md .ps-progress__track { height: 8px; }

/* Tones — drive the fill color via status tokens */
.ps-progress--brand   .ps-progress__fill { background: var(--color-brand); }
.ps-progress--success .ps-progress__fill { background: var(--color-success-fg); }
.ps-progress--pending .ps-progress__fill { background: var(--color-pending-fg); }
.ps-progress--error   .ps-progress__fill { background: var(--color-error-fg); }

/* Indeterminate — animated sweep. 2.2s reads as a calm sweep; the v2
   duration tokens (max 300ms) are too fast for an attention-bearing
   loop animation. */
.ps-progress--indeterminate .ps-progress__fill {
    width: 30%;
    transition: none;
    animation: ps-progress-indeterminate 2.2s linear infinite;
    will-change: transform;
}

@keyframes ps-progress-indeterminate {
    0%   { transform: translateX(-110%); }
    100% { transform: translateX(360%); }
}

/* ─── Toast / NotificationStack ───────────────────────────────────
 *
 * Top-right anchored stack of auto-dismissing notifications. Each
 * toast is a raised surface with a tone-tinted left edge, an icon,
 * a body (title + message + optional timestamp), and a dismiss
 * IconButton. Lifecycle (auto-dismiss timing, mounting) is owned by
 * the host component (NotificationStack); Toast itself is purely
 * presentational.
 */
.ps-toast-stack {
    position: fixed;
    top: var(--space-4);
    right: var(--space-4);
    z-index: 1100;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: min(360px, calc(100vw - 2 * var(--space-4)));
    pointer-events: none;
}

.ps-toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3);
    background: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-brand);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-raised);
    color: var(--color-ink);
    pointer-events: auto;
    animation: ps-toast-enter var(--duration-base) ease-out;
}

@keyframes ps-toast-enter {
    from { opacity: 0; transform: translateX(8px); }
    to   { opacity: 1; transform: translateX(0);   }
}

.ps-toast__icon {
    flex: 0 0 auto;
    font-size: var(--font-size-lg);
    line-height: 1.4;
    color: var(--color-brand);
}

.ps-toast__body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.ps-toast__title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--color-ink);
    line-height: 1.3;
}

.ps-toast__message {
    font-size: var(--font-size-base);
    color: var(--color-ink);
    line-height: 1.4;
    word-wrap: break-word;
}

.ps-toast__timestamp {
    font-size: var(--font-size-xs);
    color: var(--color-ink-muted);
    margin-top: var(--space-1);
}

.ps-toast__dismiss {
    flex: 0 0 auto;
    margin-left: var(--space-1);
    /* nudge the dismiss button up so it aligns with the title row */
    margin-top: -2px;
}

/* Tones — drive the icon color and the left-edge stripe */
.ps-toast--info {
    border-left-color: var(--color-running-fg);
}
.ps-toast--info .ps-toast__icon { color: var(--color-running-fg); }

.ps-toast--success {
    border-left-color: var(--color-success-fg);
}
.ps-toast--success .ps-toast__icon { color: var(--color-success-fg); }

.ps-toast--warning {
    border-left-color: var(--color-pending-fg);
}
.ps-toast--warning .ps-toast__icon { color: var(--color-pending-fg); }

.ps-toast--error {
    border-left-color: var(--color-error-fg);
}
.ps-toast--error .ps-toast__icon { color: var(--color-error-fg); }

.ps-toast--agent {
    border-left-color: var(--color-agent-fg);
}
.ps-toast--agent .ps-toast__icon { color: var(--color-agent-fg); }

/* ─── Field — form-field wrapper ──────────────────────────────────
 *
 * Universal label / input / hint stack used by every v2 form
 * primitive. The wrapper carries the `--invalid` modifier so
 * descendant inputs can recolor borders / focus rings via the
 * cascade when the field is in an error state.
 */
.ps-field {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    /* Inherit the body type scale so labels and hints don't pick up
       the surrounding context's font-size by accident. */
    font-size: var(--font-size-base);
}

.ps-field__label {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-ink);
    line-height: var(--line-height-tight);
    /* Bootstrap's .form-label ships with margin-bottom: 0.5rem
       !important, so any consumer who keeps that class on this
       label would otherwise blow out the gap. Match the wrapper's
       gap explicitly here so the rhythm holds. */
    margin-bottom: 0;
}

.ps-field__required {
    margin-left: var(--space-1);
    color: var(--color-error-fg);
}

.ps-field__hint {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: var(--line-height-base);
}

.ps-field__hint--error {
    color: var(--color-error-fg);
}

.ps-field--invalid .ps-field__label {
    color: var(--color-error-fg);
}

/* ─── Input — token-driven <input> ────────────────────────────────
 *
 * Replaces Bootstrap's .form-control. The wrapper variant
 * (.ps-input-wrap) handles icon-left/right slots; the bare input
 * is used directly when no icons are present.
 *
 * Sizing matches the Button scale (sm/md/lg) so a Button rendered
 * next to an Input lines up by default.
 */
.ps-input {
    /* layout */
    display: inline-block;
    width: 100%;
    box-sizing: border-box;

    /* type */
    font-family: var(--font-family-ui);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-regular);
    line-height: var(--line-height-tight);
    color: var(--color-ink);

    /* shape */
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);

    /* motion */
    transition:
        border-color   var(--duration-fast) var(--easing-standard),
        box-shadow     var(--duration-fast) var(--easing-standard),
        background-color var(--duration-fast) var(--easing-standard);
    appearance: none;
    -webkit-appearance: none;
}

.ps-input::placeholder {
    color: var(--color-ink-subtle);
    opacity: 1;
}

.ps-input:hover:not(:disabled):not([readonly]) {
    border-color: var(--color-border-strong);
}

.ps-input:focus,
.ps-input:focus-visible {
    outline: none;
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* Sizes — height matches the Button scale. */
.ps-input--sm {
    height: 28px;
    padding: 0 var(--space-2);
    font-size: var(--font-size-sm);
}

.ps-input--md {
    height: 32px;
    padding: 0 var(--space-3);
    font-size: var(--font-size-base);
}

.ps-input--lg {
    height: 40px;
    padding: 0 var(--space-3);
    font-size: var(--font-size-lg);
}

/* States — chained selectors for higher specificity than the size
   rules so the state always wins. */
.ps-input.ps-input--invalid {
    border-color: var(--color-error-fg);
}

.ps-input.ps-input--invalid:focus,
.ps-input.ps-input--invalid:focus-visible {
    border-color: var(--color-error-fg);
    box-shadow: 0 0 0 3px var(--color-error-bg-soft);
}

.ps-input:disabled,
.ps-input.ps-input--disabled {
    background-color: var(--color-surface-sunken);
    color: var(--color-ink-subtle);
    cursor: not-allowed;
}

.ps-input[readonly] {
    background-color: var(--color-surface-sunken);
    color: var(--color-ink-muted);
    cursor: default;
}

/* Icon wrapper — relative container that absolutely-positions a
   FontAwesome <i> on the left/right edge. The input itself gets
   extra padding on the iconned side. */
.ps-input-wrap {
    position: relative;
    display: block;
    width: 100%;
}

.ps-input-wrap__icon {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    color: var(--color-ink-muted);
    pointer-events: none;
    line-height: 1;
}

.ps-input-wrap__icon--left {
    left: var(--space-3);
}

.ps-input-wrap__icon--right {
    right: var(--space-3);
}

.ps-input--icon-left {
    padding-left: var(--space-8);
}

.ps-input--icon-right {
    padding-right: var(--space-8);
}

/* When an icon is present in the small size, tighten the slot. */
.ps-input-wrap--sm .ps-input-wrap__icon--left  { left: var(--space-2); }
.ps-input-wrap--sm .ps-input-wrap__icon--right { right: var(--space-2); }
.ps-input-wrap--sm .ps-input--icon-left  { padding-left: var(--space-6); }
.ps-input-wrap--sm .ps-input--icon-right { padding-right: var(--space-6); }

/* ─── Textarea — multi-line input ─────────────────────────────────
 *
 * Shares the .ps-input visual contract for border/focus/invalid/
 * disabled — .ps-textarea overrides height/padding/resize for
 * multi-line content. The optional character counter lives below
 * the field in a tight wrapper so it doesn't compete with the
 * Field's hint slot.
 */
.ps-textarea {
    /* Override the fixed height that .ps-input sets — let `rows`
       drive the height. Padding-y compensates for the lack of a
       single-line height anchor. */
    height: auto;
    min-height: calc(var(--space-8) + var(--space-2));
    padding-top: var(--space-2);
    padding-bottom: var(--space-2);
    line-height: var(--line-height-base);
    /* Most consumers don't need horizontal resize and it tends to
       break form layouts; default to vertical only. */
    resize: vertical;
}

.ps-textarea--resize-none {
    resize: none;
}

.ps-textarea--resize-vertical {
    resize: vertical;
}

.ps-textarea-wrap {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.ps-textarea__counter {
    align-self: flex-end;
    font-size: var(--font-size-xs);
    color: var(--color-ink-muted);
    /* Keep the counter visually subordinate — it's a courtesy
       affordance, not the primary hint surface. */
    font-feature-settings: "tnum";
}

/* ─── Checkbox ────────────────────────────────────────────────────
 *
 * Layout: hidden native <input> + visible styled .ps-check__box +
 * label body. The native input is visually hidden but stays in the
 * accessibility tree and the tab order — clicking the wrapping
 * <label> drives it. The box and the embedded check icon are
 * recoloured by the input's :checked / :disabled / :focus pseudos
 * via sibling combinators.
 */
.ps-check {
    display: inline-flex;
    align-items: flex-start;
    gap: var(--space-2);
    cursor: pointer;
    /* Keep the row tight so a Field's label/hint scaffold doesn't
       create a double-spacing situation when a checkbox sits as the
       Field's child. */
    line-height: var(--line-height-tight);
    color: var(--color-ink);
}

.ps-check__input {
    /* Visually hidden but kept in the layout/accessibility tree so
       the focus ring (driven via :focus-visible + sibling) lights
       up and screen readers announce it as a checkbox. */
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.ps-check__box {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    background-color: var(--color-surface-raised);
    transition:
        background-color var(--duration-fast) var(--easing-standard),
        border-color     var(--duration-fast) var(--easing-standard),
        box-shadow       var(--duration-fast) var(--easing-standard);
    /* Nudge down so the visual box sits on the cap height of the
       label's first line rather than the line-box top. */
    margin-top: 1px;
}

.ps-check__icon {
    font-size: 10px;
    line-height: 1;
    color: var(--color-ink-on-brand);
    opacity: 0;
    transition: opacity var(--duration-fast) var(--easing-standard);
}

/* Checked — fill the box with brand and reveal the check. */
.ps-check__input:checked ~ .ps-check__box {
    background-color: var(--color-brand);
    border-color: var(--color-brand);
}

.ps-check__input:checked ~ .ps-check__box .ps-check__icon {
    opacity: 1;
}

/* Indeterminate — same fill as checked but show the dash icon. */
.ps-check__box--indeterminate {
    background-color: var(--color-brand);
    border-color: var(--color-brand);
}
.ps-check__box--indeterminate .ps-check__icon {
    opacity: 1;
}

/* Hover — only when enabled; lets the box pick up emphasis without
   recolouring text the user is reading. */
.ps-check:not(.ps-check--disabled):hover .ps-check__box {
    border-color: var(--color-brand);
}

/* Focus ring — driven from the hidden input via :focus-visible. */
.ps-check__input:focus-visible ~ .ps-check__box {
    box-shadow: 0 0 0 3px var(--color-focus-ring);
    outline: none;
}

/* Disabled */
.ps-check--disabled {
    cursor: not-allowed;
    color: var(--color-ink-subtle);
}
.ps-check--disabled .ps-check__box {
    background-color: var(--color-surface-sunken);
    border-color: var(--color-border);
}
.ps-check--disabled .ps-check__input:checked ~ .ps-check__box {
    background-color: var(--color-ink-subtle);
    border-color: var(--color-ink-subtle);
}

/* Invalid — error border that survives the cascade. */
.ps-check.ps-check--invalid .ps-check__box {
    border-color: var(--color-error-fg);
}
.ps-check.ps-check--invalid .ps-check__input:focus-visible ~ .ps-check__box {
    box-shadow: 0 0 0 3px var(--color-error-bg-soft);
}

.ps-check__body {
    display: inline-flex;
    flex-direction: column;
    gap: var(--space-1);
    /* Bring the first line back onto the box's optical centre. */
    padding-top: 1px;
}

.ps-check__label {
    font-size: var(--font-size-base);
    line-height: var(--line-height-tight);
}

.ps-check__hint {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: var(--line-height-base);
}

/* ─── RadioGroup + Radio ──────────────────────────────────────────
 *
 * Same hidden-native-input + visible-styled-mark pattern as the
 * checkbox. The dot's inner circle is the "checked" indicator —
 * scaled from 0 to 1 by sibling selectors so the transition reads
 * as a quick pop rather than a paint flicker.
 */
.ps-radio-group {
    display: flex;
    gap: var(--space-3);
}

.ps-radio-group--vertical {
    flex-direction: column;
}

.ps-radio-group--horizontal {
    flex-direction: row;
    flex-wrap: wrap;
    /* Horizontal layout reads better with a touch more breathing
       room between options than the vertical stack. */
    gap: var(--space-4);
}

.ps-radio {
    display: inline-flex;
    align-items: flex-start;
    gap: var(--space-2);
    cursor: pointer;
    line-height: var(--line-height-tight);
    color: var(--color-ink);
}

.ps-radio__input {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.ps-radio__dot {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-pill);
    background-color: var(--color-surface-raised);
    margin-top: 1px;
    transition:
        border-color var(--duration-fast) var(--easing-standard),
        box-shadow   var(--duration-fast) var(--easing-standard);
}

.ps-radio__dot-inner {
    width: 8px;
    height: 8px;
    border-radius: var(--radius-pill);
    background-color: var(--color-brand);
    transform: scale(0);
    transition: transform var(--duration-fast) var(--easing-standard);
}

.ps-radio__input:checked ~ .ps-radio__dot {
    border-color: var(--color-brand);
}

.ps-radio__input:checked ~ .ps-radio__dot .ps-radio__dot-inner {
    transform: scale(1);
}

.ps-radio:not(.ps-radio--disabled):hover .ps-radio__dot {
    border-color: var(--color-brand);
}

.ps-radio__input:focus-visible ~ .ps-radio__dot {
    box-shadow: 0 0 0 3px var(--color-focus-ring);
    outline: none;
}

.ps-radio--disabled {
    cursor: not-allowed;
    color: var(--color-ink-subtle);
}
.ps-radio--disabled .ps-radio__dot {
    background-color: var(--color-surface-sunken);
    border-color: var(--color-border);
}
.ps-radio--disabled .ps-radio__input:checked ~ .ps-radio__dot .ps-radio__dot-inner {
    background-color: var(--color-ink-subtle);
}

.ps-radio__body {
    display: inline-flex;
    flex-direction: column;
    gap: var(--space-1);
    padding-top: 1px;
}

.ps-radio__label {
    font-size: var(--font-size-base);
    line-height: var(--line-height-tight);
}

.ps-radio__hint {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: var(--line-height-base);
}

/* ─── Toggle (button[role=switch]) ────────────────────────────────
 *
 * Brand-tinted track when on, neutral when off. Thumb is positioned
 * via translateX rather than left/right so the transition is a
 * single GPU-accelerated transform. Disabled desaturates the on
 * state to ink-subtle to keep the affordance visible without
 * suggesting it's interactive.
 */
.ps-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    line-height: var(--line-height-tight);
    color: var(--color-ink);
}

.ps-toggle--placement-start {
    /* When the label leads, justify so the row reads "label … switch". */
    justify-content: space-between;
    width: 100%;
}

.ps-toggle__track {
    /* The switch itself is a button — strip default button chrome. */
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    padding: 0;
    margin: 0;
    background: var(--color-border-strong);
    border-radius: var(--radius-pill);
    position: relative;
    flex: 0 0 auto;
    cursor: pointer;
    transition:
        background-color var(--duration-fast) var(--easing-standard),
        box-shadow       var(--duration-fast) var(--easing-standard);
}

.ps-toggle__track--md {
    width: 32px;
    height: 18px;
}

.ps-toggle__track--sm {
    width: 26px;
    height: 14px;
}

.ps-toggle__thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    background: var(--color-surface-raised);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-subtle);
    transition: transform var(--duration-base) var(--easing-standard);
}

.ps-toggle__track--md .ps-toggle__thumb {
    width: 14px;
    height: 14px;
}

.ps-toggle__track--sm .ps-toggle__thumb {
    width: 10px;
    height: 10px;
}

/* On — track turns brand, thumb slides right. */
.ps-toggle--on .ps-toggle__track {
    background: var(--color-brand);
}
.ps-toggle--on .ps-toggle__track--md .ps-toggle__thumb {
    transform: translateX(14px);
}
.ps-toggle--on .ps-toggle__track--sm .ps-toggle__thumb {
    transform: translateX(12px);
}

.ps-toggle__track:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.ps-toggle--disabled {
    cursor: not-allowed;
    color: var(--color-ink-subtle);
}
.ps-toggle--disabled .ps-toggle__track {
    cursor: not-allowed;
    background: var(--color-border);
}
.ps-toggle--disabled.ps-toggle--on .ps-toggle__track {
    background: var(--color-ink-subtle);
}

.ps-toggle__body {
    display: inline-flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
}

.ps-toggle__label {
    font-size: var(--font-size-base);
    line-height: var(--line-height-tight);
}

.ps-toggle__hint {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: var(--line-height-base);
}

/* ─── Slider ──────────────────────────────────────────────────────
 *
 * Token-driven range. We keep the native <input type="range"> so the
 * full keyboard contract (←/→ step, Home/End to min/max, PgUp/PgDn
 * coarse step) comes for free — only the visual chrome is overridden
 * via the -webkit/-moz pseudo elements. Track and thumb both compose
 * tokens; the focus ring matches every other v2 control.
 */
.ps-slider {
    display: inline-flex;
    flex-direction: column;
    gap: var(--space-1);
    width: 100%;
}

.ps-slider__input {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 18px;
    background: transparent;
    cursor: pointer;
    margin: 0;
    padding: 0;
}

.ps-slider__input:focus { outline: none; }

/* Track — WebKit / Blink. */
.ps-slider__input::-webkit-slider-runnable-track {
    height: 4px;
    background: var(--color-border-strong);
    border-radius: var(--radius-pill);
    border: 0;
}

/* Track — Firefox. */
.ps-slider__input::-moz-range-track {
    height: 4px;
    background: var(--color-border-strong);
    border-radius: var(--radius-pill);
    border: 0;
}

/* Thumb — WebKit / Blink. margin-top centers the thumb on the 4px
 * track ( (track - thumb) / 2 = (4 - 16) / 2 = -6 ). */
.ps-slider__input::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    margin-top: -6px;
    background: var(--color-brand);
    border: 0;
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-subtle);
    transition: box-shadow var(--duration-fast) var(--easing-standard);
}

/* Thumb — Firefox. */
.ps-slider__input::-moz-range-thumb {
    width: 16px;
    height: 16px;
    background: var(--color-brand);
    border: 0;
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-subtle);
    transition: box-shadow var(--duration-fast) var(--easing-standard);
}

/* Focus ring lives on the thumb — keeps it tight to the interaction
 * target rather than ballooning the whole row. */
.ps-slider__input:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.ps-slider__input:focus-visible::-moz-range-thumb {
    box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.ps-slider--disabled {
    cursor: not-allowed;
}
.ps-slider--disabled .ps-slider__input {
    cursor: not-allowed;
    opacity: 0.6;
}
.ps-slider--disabled .ps-slider__input::-webkit-slider-thumb {
    background: var(--color-ink-subtle);
}
.ps-slider--disabled .ps-slider__input::-moz-range-thumb {
    background: var(--color-ink-subtle);
}

.ps-slider__hint {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
    line-height: var(--line-height-base);
}

/* ─── Select ──────────────────────────────────────────────────────
 *
 * Reuses .ps-input for border / focus / invalid / disabled — only
 * the chevron and the native dropdown styling are overridden here.
 * The wrapper exists purely to anchor the chevron icon; the native
 * <select> still owns the click target and keyboard semantics.
 */
.ps-select-wrap {
    position: relative;
    display: block;
    width: 100%;
}

.ps-select {
    /* Suppress the browser-native chevron so we can render our own. */
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    /* Reserve room on the right for our chevron. */
    padding-right: var(--space-8);
    /* The native select's text rendering is anchored to the line-box;
       the .ps-input baseline matched a text input's. Re-centre by
       letting the text flow at the line-height-base of the
       surrounding form rhythm. */
    line-height: var(--line-height-base);
    cursor: pointer;
    /* Strip the dotted Firefox focus outline — :focus-visible on the
       wrapper drives the v2 focus ring instead. */
    text-overflow: ellipsis;
}

/* Firefox-specific dotted outline on the selected option. */
.ps-select::-moz-focus-inner {
    border: 0;
}
.ps-select:-moz-focusring {
    color: transparent;
    text-shadow: 0 0 0 var(--color-ink);
}

.ps-select:disabled {
    cursor: not-allowed;
}

.ps-select-wrap__chevron {
    position: absolute;
    top: 50%;
    right: var(--space-3);
    transform: translateY(-50%);
    color: var(--color-ink-muted);
    pointer-events: none;
    font-size: 10px;
    line-height: 1;
}

/* Small size — tighten the chevron slot to match .ps-input--sm. */
.ps-select-wrap--sm .ps-select-wrap__chevron {
    right: var(--space-2);
}
.ps-select-wrap--sm .ps-select {
    padding-right: var(--space-6);
}

/* ──────────────────────────────────────────────────────────────────
 * Popover — shared anchored panel primitive (batch 3d.1).
 *
 * Used by combobox / multiselect / future dropdown menus. The panel
 * is position:fixed and gets its top/left assigned by JS interop
 * (psPopoverPosition); CSS only owns chrome and the dismiss overlay.
 *
 * Z-index strategy: overlay sits at 1080 (just below Bootstrap's
 * .modal at 1055-ish, just above .dropdown-menu at 1000), panel at
 * 1081 so a click on the panel is captured by the panel and a click
 * outside is captured by the overlay.
 * ────────────────────────────────────────────────────────────────── */

.ps-popover__overlay {
    position: fixed;
    inset: 0;
    z-index: 1080;
    background: transparent;
    /*
     * Cursor stays default — this is a click-shield, not a backdrop.
     * Pointer events on; nothing visual.
     */
}

.ps-popover__panel {
    position: fixed;
    z-index: 1081;
    /* Initial off-screen position so the unmeasured first frame doesn't flash at 0,0. */
    top: -9999px;
    left: -9999px;
    background: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-raised);
    padding: var(--space-1);
    color: var(--color-ink);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
    /* Cap height so a long list scrolls inside the panel. */
    max-height: min(60vh, 360px);
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* ──────────────────────────────────────────────────────────────────
 * Combobox (3d.2)
 *
 * Trigger reuses .ps-input for border / focus / invalid / disabled
 * chrome; the wrapper anchors the chevron icon (same pattern as
 * .ps-select-wrap). Listbox lives inside a .ps-popover__panel —
 * option rows are styled here.
 * ────────────────────────────────────────────────────────────────── */

.ps-combobox-wrap {
    position: relative;
    display: block;
    width: 100%;
}

.ps-combobox {
    /* Reserve room on the right for the chevron, matching .ps-select. */
    padding-right: var(--space-8);
    cursor: pointer;
    text-overflow: ellipsis;
}

.ps-combobox[readonly] {
    /* Browser default for readonly is a hand-pointer-y feel; force the
       pointer so the trigger reads as a clickable control even in the
       closed state. */
    cursor: pointer;
    /* Strip the slightly-dimmed background some UAs apply to readonly. */
    background-color: var(--color-surface-raised);
    color: var(--color-ink);
}

.ps-combobox:not([readonly]) {
    cursor: text;
}

.ps-combobox__option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--color-ink);
    user-select: none;
    /* Compact line-height so options stack densely without feeling cramped. */
    line-height: var(--line-height-tight);
}

.ps-combobox__option--highlighted {
    background-color: var(--color-brand-soft);
}

.ps-combobox__option--selected {
    color: var(--color-brand);
    font-weight: var(--font-weight-medium);
}

.ps-combobox__option--selected.ps-combobox__option--highlighted {
    /* Same brand-soft surface; the brand-tinted text already reads as selected. */
    background-color: var(--color-brand-soft);
}

.ps-combobox__option--disabled {
    color: var(--color-ink-subtle);
    cursor: not-allowed;
}

.ps-combobox__option--disabled.ps-combobox__option--highlighted {
    background-color: transparent;
}

.ps-combobox__option-icon {
    color: var(--color-ink-muted);
    width: 1em;
    text-align: center;
    flex-shrink: 0;
}

.ps-combobox__option-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-combobox__option-secondary {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    flex-shrink: 0;
}

.ps-combobox__option-check {
    color: var(--color-brand);
    font-size: 11px;
    flex-shrink: 0;
}

.ps-combobox__no-results {
    padding: var(--space-3);
    text-align: center;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}


/* ─── PrincipalPicker (#1058) ───────────────────────────────────────────
 *
 * Async-search single-select. Trigger reuses .ps-input + chevron chrome
 * (same as .ps-combobox); option rows host an avatar + name/secondary +
 * optional type badge. Lives in a .ps-popover__panel.
 * ────────────────────────────────────────────────────────────────────── */

.ps-principal-picker-wrap {
    position: relative;
    display: block;
    width: 100%;
}

.ps-principal-picker {
    padding-right: var(--space-8);
    cursor: pointer;
    text-overflow: ellipsis;
}

.ps-principal-picker[readonly] {
    cursor: pointer;
    background-color: var(--color-surface-raised);
    color: var(--color-ink);
}

.ps-principal-picker:not([readonly]) {
    cursor: text;
}

.ps-principal-picker__option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--color-ink);
    user-select: none;
    line-height: var(--line-height-tight);
}

.ps-principal-picker__option--highlighted {
    background-color: var(--color-brand-soft);
}

.ps-principal-picker__option--selected {
    font-weight: var(--font-weight-medium);
}

.ps-principal-picker__option-body {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1 1 auto;
}

.ps-principal-picker__option-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-principal-picker__option-secondary {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-principal-picker__option-check {
    color: var(--color-brand);
    font-size: 11px;
    flex-shrink: 0;
}

.ps-principal-picker__status {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-principal-picker__no-results {
    text-align: center;
}


/* ─── Multiselect (3d.3) ───────────────────────────────────────────────
 *
 * Trigger is a flex-row container styled like .ps-input that hosts
 * selected chips + an inline filter input that grows to fill the row.
 * Listbox row chrome mirrors .ps-combobox__option exactly so the two
 * primitives feel like siblings.
 * ────────────────────────────────────────────────────────────────── */

.ps-multiselect-wrap {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
    min-height: 32px;
    padding: 3px var(--space-8) 3px var(--space-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background-color: var(--color-surface-raised);
    color: var(--color-ink);
    font-family: var(--font-family-ui);
    font-size: var(--font-size-base);
    line-height: var(--line-height-tight);
    cursor: text;
    transition:
        border-color   var(--duration-fast) var(--easing-standard),
        box-shadow     var(--duration-fast) var(--easing-standard);
    box-sizing: border-box;
}

.ps-multiselect-wrap--sm {
    min-height: 28px;
    font-size: var(--font-size-sm);
}

.ps-multiselect-wrap--lg {
    min-height: 40px;
    padding: 5px var(--space-8) 5px var(--space-2);
    font-size: var(--font-size-lg);
}

.ps-multiselect-wrap--open,
.ps-multiselect-wrap:focus-within {
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px var(--color-focus-ring);
    outline: none;
}

.ps-multiselect-wrap--invalid {
    border-color: var(--color-error-fg);
}

.ps-multiselect-wrap--invalid:focus-within {
    border-color: var(--color-error-fg);
    box-shadow: 0 0 0 3px var(--color-error-bg-soft);
}

.ps-multiselect-wrap--disabled {
    cursor: not-allowed;
    background-color: var(--color-surface-sunken);
    color: var(--color-ink-subtle);
}

.ps-multiselect__chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0 var(--space-1) 0 var(--space-2);
    height: 22px;
    border-radius: var(--radius-sm);
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
    max-width: 100%;
}

.ps-multiselect-wrap--sm .ps-multiselect__chip {
    height: 18px;
    font-size: var(--font-size-xs);
}

.ps-multiselect-wrap--lg .ps-multiselect__chip {
    height: 28px;
}

.ps-multiselect__chip-icon {
    font-size: 11px;
    flex-shrink: 0;
}

.ps-multiselect__chip-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-multiselect__chip-remove {
    appearance: none;
    background: transparent;
    border: 0;
    padding: 0 var(--space-1);
    margin: 0;
    color: inherit;
    opacity: 0.7;
    cursor: pointer;
    border-radius: var(--radius-sm);
    line-height: 1;
}

.ps-multiselect__chip-remove:hover,
.ps-multiselect__chip-remove:focus-visible {
    opacity: 1;
    background-color: rgba(0, 0, 0, 0.06);
    outline: none;
}

.ps-multiselect__filter {
    flex: 1 1 60px;
    min-width: 60px;
    border: 0;
    outline: 0;
    padding: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    line-height: inherit;
}

.ps-multiselect__filter:disabled {
    cursor: not-allowed;
}

.ps-multiselect__chevron {
    position: absolute;
    right: var(--space-3);
    top: 50%;
    transform: translateY(-50%);
    color: var(--color-ink-muted);
    font-size: 12px;
    pointer-events: none;
}

.ps-multiselect__option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--color-ink);
    user-select: none;
    line-height: var(--line-height-tight);
}

.ps-multiselect__option--highlighted {
    background-color: var(--color-brand-soft);
}

.ps-multiselect__option--selected {
    color: var(--color-brand);
    font-weight: var(--font-weight-medium);
}

.ps-multiselect__option--selected.ps-multiselect__option--highlighted {
    background-color: var(--color-brand-soft);
}

.ps-multiselect__option--disabled {
    color: var(--color-ink-subtle);
    cursor: not-allowed;
}

.ps-multiselect__option--disabled.ps-multiselect__option--highlighted {
    background-color: transparent;
}

.ps-multiselect__option-icon {
    color: var(--color-ink-muted);
    width: 1em;
    text-align: center;
    flex-shrink: 0;
}

.ps-multiselect__option-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-multiselect__option-secondary {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    flex-shrink: 0;
}

.ps-multiselect__option-check {
    color: var(--color-brand);
    font-size: 11px;
    flex-shrink: 0;
}

.ps-multiselect__no-results {
    padding: var(--space-3);
    text-align: center;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Modal (4a)
 *
 * Centered overlay with a backdrop scrim. Layered above popovers
 * (z-index 1090) so a popover-anchored picker inside a modal sits
 * inside the modal's panel rather than escaping to the page. The
 * backdrop captures clicks-outside; the panel captures clicks-inside.
 * Focus trap, body-scroll lock, and the open/close lifecycle are
 * driven from JS interop (psModalOpen / psModalClose) so nested
 * modals or drawers compose cleanly.
 * ────────────────────────────────────────────────────────────────── */

.ps-modal__backdrop {
    position: fixed;
    inset: 0;
    z-index: 1090;
    background-color: var(--color-surface-overlay);
}

.ps-modal {
    position: fixed;
    inset: 0;
    z-index: 1091;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    /* Top padding nudges the panel below the viewport edge so a tall
       modal still has breathing room to scroll. */
    padding: var(--space-12) var(--space-4) var(--space-4);
    overflow-y: auto;
    overscroll-behavior: contain;
}

.ps-modal__panel {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    max-height: calc(100vh - var(--space-12) - var(--space-4));
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-modal);
    color: var(--color-ink);
    /* outline:none so :focus on the panel doesn't double-stroke against the border. */
    outline: none;
}

.ps-modal__panel--sm { max-width: var(--modal-width-sm); }
.ps-modal__panel--md { max-width: var(--modal-width-md); }
.ps-modal__panel--lg { max-width: var(--modal-width-lg); }
.ps-modal__panel--xl { max-width: var(--modal-width-xl); }

.ps-modal__header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-6);
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-modal__title {
    flex: 1 1 auto;
    margin: 0;
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    color: var(--color-ink);
}

/* Modal's close button is rendered by <CloseButton/> (Ghost IconButton).
   The flex layout puts it at the trailing edge of the header; its
   own chrome is owned by the .ps-icon-btn / .ps-btn--ghost rules. */

.ps-modal__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--space-6);
    color: var(--color-ink);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
}

.ps-modal__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-6) var(--space-4);
    border-top: 1px solid var(--color-border-subtle);
}

/* Body class set by psModalOpen — locks page scroll behind the modal. */
body.ps-modal-open {
    overflow: hidden;
}


/* ──────────────────────────────────────────────────────────────────
 * FormModal / ConfirmDialog (4b)
 *
 * Both compose Modal v2 + Button v2 — almost no fresh chrome. The
 * only additions are spacing for the LastError banner inside
 * FormModal and a `display:contents` on the inner <form> so it
 * doesn't introduce a new layout box around the body fields.
 * ────────────────────────────────────────────────────────────────── */

.ps-form-modal__form {
    /* Render as if the form element weren't there; field layout is
       owned by ChildContent. The form exists only to host @onsubmit
       and the form="..." association from the footer submit button. */
    display: contents;
}

.ps-form-modal__error {
    margin-bottom: var(--space-3);
}


/* ──────────────────────────────────────────────────────────────────
 * Alert (4b follow-up)
 *
 * Inline status banner — used standalone, inside FormModal's body
 * (above the form fields, surfacing Form.LastError), and inside
 * LoadingContainer's error state. Token-driven; uses the v2 status
 * palette so each tone reads as a recognised status surface rather
 * than ad-hoc colour.
 *
 * Layout: leading icon (tone-default or override), body (optional
 * bold Title above the message), trailing Retry + Dismiss row. The
 * trailing affordances flex to the right edge regardless of body
 * length.
 *
 * Tone selectors — chrome (background / border / foreground / icon
 * tint) is set per-tone; the base layout rule is shared.
 * ────────────────────────────────────────────────────────────────── */

.ps-alert {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-family: var(--font-family-ui);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
}

.ps-alert--error {
    background-color: var(--color-error-bg-soft);
    border-color: var(--color-error-border);
    color: var(--color-error-fg);
}

.ps-alert--warning {
    background-color: var(--color-pending-bg-soft);
    border-color: var(--color-pending-border);
    color: var(--color-pending-fg);
}

.ps-alert--success {
    background-color: var(--color-success-bg-soft);
    border-color: var(--color-success-border);
    color: var(--color-success-fg);
}

.ps-alert--info {
    background-color: var(--color-running-bg-soft);
    border-color: var(--color-running-border);
    color: var(--color-running-fg);
}

.ps-alert__icon {
    flex-shrink: 0;
    color: inherit;
    /* Optical alignment with the first line of body text — the
       icon glyph sits visually below the cap line by default. */
    padding-top: 2px;
}

.ps-alert__body {
    flex: 1 1 auto;
    color: inherit;
    min-width: 0; /* allow long messages to wrap inside the flex row */
}

.ps-alert__title {
    display: block;
    font-weight: var(--font-weight-semibold);
    margin-bottom: var(--space-1);
}

.ps-alert__message {
    display: block;
    word-wrap: break-word;
}


/* ──────────────────────────────────────────────────────────────────
 * Drawer (4a)
 *
 * Side-anchored panel sharing Modal's backdrop, focus-trap, and
 * body-scroll-lock semantics (same psModalOpen / psModalClose
 * helpers). The panel slides in from the configured side; the slide
 * is a CSS transition driven by [data-state="open"|"closed"] so JS
 * doesn't need to time the animation.
 * ────────────────────────────────────────────────────────────────── */

.ps-drawer {
    position: fixed;
    inset: 0;
    z-index: 1091;
    display: flex;
    pointer-events: none;
}

.ps-drawer--right { justify-content: flex-end; }
.ps-drawer--left  { justify-content: flex-start; }

.ps-drawer__panel {
    position: relative;
    display: flex;
    flex-direction: column;
    pointer-events: auto;
    width: 100%;
    height: 100vh;
    background-color: var(--color-surface-raised);
    border-left: 1px solid var(--color-border);
    box-shadow: var(--shadow-modal);
    color: var(--color-ink);
    outline: none;
    transition: transform var(--duration-base) var(--easing-standard);
}

.ps-drawer--left .ps-drawer__panel {
    border-left: 0;
    border-right: 1px solid var(--color-border);
}

.ps-drawer__panel--sm { max-width: var(--drawer-width-sm); }
.ps-drawer__panel--md { max-width: var(--drawer-width-md); }
.ps-drawer__panel--lg { max-width: var(--drawer-width-lg); }
.ps-drawer__panel--xl { max-width: var(--drawer-width-xl); }

/* Animated open/close. JS toggles [data-state] one frame after the
   panel mounts so the closed→open transform actually transitions. */
.ps-drawer--right .ps-drawer__panel[data-state="closed"] { transform: translateX(100%); }
.ps-drawer--left  .ps-drawer__panel[data-state="closed"] { transform: translateX(-100%); }
.ps-drawer__panel[data-state="open"] { transform: translateX(0); }

.ps-drawer__header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-6);
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-drawer__title {
    flex: 1 1 auto;
    margin: 0;
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    color: var(--color-ink);
}

/* Drawer's close button is rendered by <CloseButton/> — see Modal note. */

.ps-drawer__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--space-6);
    color: var(--color-ink);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
}

.ps-drawer__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-6) var(--space-4);
    border-top: 1px solid var(--color-border-subtle);
}


/* ──────────────────────────────────────────────────────────────────
 * Card (5a)
 *
 * Surface container with optional media / header / body / footer
 * regions. Variants control border + shadow; padding is independent
 * of the slot vocabulary so callers can opt out of the default body
 * inset for media-heavy or table-driven layouts.
 *
 * The body padding modifier lives on the body element rather than
 * the root so a Padding=None body coexists with a default-padded
 * header/footer on the same card.
 * ────────────────────────────────────────────────────────────────── */

.ps-card {
    display: flex;
    flex-direction: column;
    background-color: var(--color-surface-raised);
    border-radius: var(--radius-md);
    color: var(--color-ink);
    overflow: hidden;
}

.ps-card--outlined {
    border: 1px solid var(--color-border);
}

.ps-card--elevated {
    border: 0;
    box-shadow: var(--shadow-raised);
}

.ps-card__media {
    /* Flush-to-edge: no padding, no border. Caller's content owns the
       framing (rounded top corners are inherited from the root via
       overflow:hidden). */
    display: block;
    line-height: 0;
}

.ps-card__media > img,
.ps-card__media > video {
    display: block;
    width: 100%;
    height: auto;
}

.ps-card__header {
    padding: var(--space-4) var(--space-4);
    border-bottom: 1px solid var(--color-border-subtle);
    font-weight: var(--font-weight-semibold);
    color: var(--color-ink);
}

.ps-card__body {
    flex: 1 1 auto;
    color: var(--color-ink);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
}

.ps-card__body--p-none { padding: 0; }
.ps-card__body--p-sm   { padding: var(--card-padding-sm); }
.ps-card__body--p-md   { padding: var(--card-padding-md); }
.ps-card__body--p-lg   { padding: var(--card-padding-lg); }

.ps-card__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--color-border-subtle);
}


/* ──────────────────────────────────────────────────────────────────
 * Avatar (5a)
 *
 * Image with initials fallback. The initials span is always rendered
 * so an image-load failure (or null URL) reveals it without a flash.
 * The optional status dot is positioned absolutely on the bottom-right
 * with a thin outline matching the page surface so it reads as cut
 * into the avatar regardless of which surface it's on.
 * ────────────────────────────────────────────────────────────────── */

.ps-avatar {
    /*
     * Positioning context for both the clipped __inner and the
     * status dot. NO overflow:hidden here — the dot must escape the
     * circle. NO border-radius here — the round/square mask lives
     * on __inner so the status sibling stays unclipped.
     */
    position: relative;
    display: inline-block;
    flex: 0 0 auto;
    line-height: 1;
    user-select: none;
    /* Inherit from size modifier — fall back to md for safety. */
    width: var(--avatar-size-md);
    height: var(--avatar-size-md);
}

.ps-avatar--xs { width: var(--avatar-size-xs); height: var(--avatar-size-xs); font-size: 9px; }
.ps-avatar--sm { width: var(--avatar-size-sm); height: var(--avatar-size-sm); font-size: 10px; }
.ps-avatar--md { width: var(--avatar-size-md); height: var(--avatar-size-md); font-size: var(--font-size-sm); }
.ps-avatar--lg { width: var(--avatar-size-lg); height: var(--avatar-size-lg); font-size: var(--font-size-base); }
.ps-avatar--xl { width: var(--avatar-size-xl); height: var(--avatar-size-xl); font-size: var(--font-size-xl); }

.ps-avatar__inner {
    /* The clipped layer — fills the avatar, owns the brand-soft
       fallback fill, and centers the initials. Image (if any) sits
       absolutely on top of the initials. */
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
    font-family: var(--font-family-ui);
    font-weight: var(--font-weight-semibold);
    overflow: hidden;
}

.ps-avatar--circle .ps-avatar__inner { border-radius: var(--radius-pill); }
.ps-avatar--square .ps-avatar__inner { border-radius: var(--radius-md); }

.ps-avatar__initials {
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.ps-avatar__img {
    /* Sits on top of the initials — failed loads hide the img and
       the initials show through. object-fit covers the frame so a
       non-square image still fills the avatar. */
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Avoid a hint of broken-image alt-text when the URL fails before
       Blazor's @onerror fires. */
    color: transparent;
}

.ps-avatar__status {
    position: absolute;
    bottom: 0;
    right: 0;
    width: var(--avatar-status-size-md);
    height: var(--avatar-status-size-md);
    border-radius: var(--radius-pill);
    /* Ring matches the surface the avatar sits on so the dot reads
       cleanly against any background. */
    box-shadow: 0 0 0 2px var(--color-surface-raised);
}

.ps-avatar--xs .ps-avatar__status { width: var(--avatar-status-size-xs); height: var(--avatar-status-size-xs); }
.ps-avatar--sm .ps-avatar__status { width: var(--avatar-status-size-sm); height: var(--avatar-status-size-sm); }
.ps-avatar--md .ps-avatar__status { width: var(--avatar-status-size-md); height: var(--avatar-status-size-md); }
.ps-avatar--lg .ps-avatar__status { width: var(--avatar-status-size-lg); height: var(--avatar-status-size-lg); }
.ps-avatar--xl .ps-avatar__status { width: var(--avatar-status-size-xl); height: var(--avatar-status-size-xl); }

.ps-avatar__status--online  { background-color: var(--color-success-fg); }
.ps-avatar__status--away    { background-color: var(--color-pending-fg); }
.ps-avatar__status--busy    { background-color: var(--color-error-fg); }
.ps-avatar__status--offline { background-color: var(--color-ink-subtle); }


/* ──────────────────────────────────────────────────────────────────
 * Tooltip (5a)
 *
 * Reuses the Popover positioning interop and panel chrome but suppresses
 * the click-shield overlay (tooltips must not block page interaction)
 * and applies its own panel styling: dark fill for high contrast against
 * any surface, smaller padding, no scroll cap. The wrap span is the
 * anchor and hosts the hover/focus event handlers.
 * ────────────────────────────────────────────────────────────────── */

.ps-tooltip-wrap {
    /* Inline-block so the wrap reports the geometry of its child for
       anchored positioning, but doesn't stretch to fill its parent. */
    display: inline-block;
    /* Don't introduce any visible chrome — purely an event-host. */
}

/*
 * Override the Popover panel chrome when used by a tooltip.
 * Selecting on role="tooltip" instead of a class — Blazor's
 * @attributes splat overwrites class= rather than merging, so a
 * class param on <Popover> would clobber .ps-popover__panel and
 * leave the panel unstyled.
 */
.ps-popover__panel[role="tooltip"] {
    background-color: var(--color-ink);
    color: var(--color-ink-on-brand);
    border: 0;
    box-shadow: var(--shadow-raised);
    padding: 0;
    /* Tooltips never scroll. */
    max-height: none;
    overflow: visible;
}

.ps-tooltip__content {
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    white-space: normal;
    max-width: 240px;
}


/* ──────────────────────────────────────────────────────────────────
 * Menu / Dropdown / ContextMenu (6a)
 *
 * The Menu primitive renders a <ul role="menu"> with each item drawn
 * by the parent (children register but paint nothing). Used by both
 * Dropdown (click-triggered) and ContextMenu (right-click triggered);
 * both host the menu inside a Popover panel, so the menu styles assume
 * they sit on top of .ps-popover__panel chrome and override its inner
 * padding (popover defaults to var(--space-1), menus want a flush
 * container with item-level padding).
 * ────────────────────────────────────────────────────────────────── */

.ps-popover__panel:has(> .ps-menu) {
    /* Flush container — let .ps-menu items own the inset. */
    padding: 0;
    overflow: visible;
}

.ps-popover__panel:has(> .ps-asset-switcher__panel) {
    /* Issue #790 follow-up — the AssetSwitcher renders its own framed
       panel (border + shadow + max-height + internal scroll). The
       popover host must NOT cap height or scroll on top of it, or
       the body's scroll nests inside the popover's scroll and the
       user sees two scrollbars (most visible on the type tabs, where
       the browse list is long). */
    padding: 0;
    max-height: none;
    overflow: visible;
}

.ps-menu {
    list-style: none;
    margin: 0;
    padding: var(--space-1) 0;
    min-width: 180px;
    /* Override the focus ring outline that :focus-visible would draw
       on a tabindex=-1 ul; the highlighted row is the visual cue. */
    outline: none;
}

.ps-menu__item {
    /* Reset button defaults so the row reads as a list item. */
    appearance: none;
    background: transparent;
    border: 0;
    width: 100%;
    text-align: left;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    color: var(--color-ink);
    font-family: var(--font-family-ui);
    font-size: var(--font-size-base);
    line-height: var(--line-height-tight);
}

.ps-menu__item--highlighted {
    background-color: var(--color-brand-soft);
}

.ps-menu__item--checked {
    color: var(--color-brand);
    font-weight: var(--font-weight-medium);
}

.ps-menu__item--disabled {
    color: var(--color-ink-subtle);
    cursor: not-allowed;
}

.ps-menu__item--disabled.ps-menu__item--highlighted {
    background-color: transparent;
}

.ps-menu__item-leading {
    /* Reserved column so labels align even when some items have no
       icon. Width matches the FontAwesome 1em glyph + a tiny breathe. */
    width: 1em;
    text-align: center;
    color: var(--color-ink-muted);
    flex-shrink: 0;
}

.ps-menu__item--checked .ps-menu__item-leading {
    color: var(--color-brand);
}

.ps-menu__item-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-menu__item-shortcut {
    flex-shrink: 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    font-family: var(--font-family-mono);
    margin-left: var(--space-4);
}

.ps-menu__divider {
    height: 1px;
    margin: var(--space-1) 0;
    background-color: var(--color-border-subtle);
}

/* Dropdown wrap — purely an event host. inline-block so the wrap reports
   the geometry of its child for anchored positioning. */
.ps-dropdown-wrap {
    display: inline-block;
}

/* ContextMenu wrap — same shape as the dropdown wrap. The synthetic
   anchor div is invisible and pointer-events:none so it never disturbs
   the cursor. */
.ps-context-menu-wrap {
    display: inline-block;
}

.ps-context-menu__anchor {
    /* Inline styles set position/top/left from the click coordinates;
       this rule just keeps a consistent visual baseline. */
    background: transparent;
}


/* ──────────────────────────────────────────────────────────────────
 * Bulk actions strip — appears between the QueryBar and the table on
 * list pages when one or more rows are selected. Hosts a "N selected"
 * label, a dismiss action, and any number of bulk-action buttons.
 * Pages render this conditionally (display-on-selection).
 * ────────────────────────────────────────────────────────────────── */
.ps-bulk-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-3);
    border: 1px solid var(--color-border);
    background-color: var(--color-brand-soft);
    border-radius: var(--radius-md);
    color: var(--color-ink);
}

.ps-bulk-actions__count {
    font-size: var(--font-size-sm);
}

.ps-bulk-actions__count strong {
    color: var(--color-brand);
    font-weight: var(--font-weight-semibold);
}

.ps-bulk-actions__spacer {
    flex: 1;
}

/* ──────────────────────────────────────────────────────────────────
 * Bulk-selection banner — appears above the table when the user has
 * ticked every row on the current page and there are more matching
 * rows than fit, or when the selection has been upgraded to
 * "all matching the current filter". Drives the Gmail/Linear-style
 * select-all-matching workflow that backs server-side bulk delete.
 *
 * Bound to BulkSelectableListViewModel<T> via the <BulkSelectionBanner>
 * shared component.
 * ────────────────────────────────────────────────────────────────── */
.ps-bulk-selection-banner {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-3);
    border: 1px solid var(--color-border);
    background-color: var(--color-info-soft, var(--color-brand-soft));
    border-radius: var(--radius-md);
    color: var(--color-ink);
    font-size: var(--font-size-sm);
}

.ps-bulk-selection-banner__message {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-bulk-selection-banner__link {
    appearance: none;
    background: none;
    border: none;
    padding: 0;
    color: var(--color-brand);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.ps-bulk-selection-banner__link:hover {
    color: var(--color-brand-strong, var(--color-brand));
}

.ps-bulk-selection-banner__link:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Connection access page chrome. Only used by /connections/{id}/access.
 * ────────────────────────────────────────────────────────────────── */
.ps-access-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.ps-access-add-peer {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.ps-access-add-peer__inputs {
    display: flex;
    flex: 1 1 auto;
    gap: var(--space-2);
}

.ps-access-add-peer__inputs > * {
    flex: 1 1 0;
}


/* ──────────────────────────────────────────────────────────────────
 * Connection trigger cards — vertical stack of <Card>s, each with a
 * header row (label + remove IconButton) and a body of v2 fields.
 * The classes are scoped to this composition; nothing else uses them.
 * ────────────────────────────────────────────────────────────────── */
.ps-trigger-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-trigger-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

/* Each trigger Card body holds a vertical stack of <Field>s. Use the
   v2 spacing scale here rather than Bootstrap's .mb-3 wrapper so the
   gap stays in token-driven sync with the rest of the design system. */
.ps-trigger-card__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-trigger-card__app-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    margin-top: var(--space-1);
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
}

.ps-trigger-card__app-warning {
    margin-top: var(--space-2);
}


/* ──────────────────────────────────────────────────────────────────
 * SecretInput — password input with reveal + optional copy. Lays
 * out an Input plus 1–2 trailing IconButtons in a single row; the
 * Input flexes to fill remaining width so the trailing buttons stay
 * aligned at the right edge regardless of value length. Heights are
 * already aligned at the .ps-btn / .ps-input level, so no per-size
 * override is needed here.
 * ────────────────────────────────────────────────────────────────── */
.ps-secret-input {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-secret-input > :first-child {
    flex: 1 1 auto;
    min-width: 0;
}


/* ──────────────────────────────────────────────────────────────────
 * KeyValueEditor — flat string→string dictionary editor. Each row is
 * [key (read-only)] [value] [optional show/hide] [×]; the trailing
 * row is the pending-add inputs with a + button. Layout uses a flex
 * row per entry so the value cell flexes and the trailing buttons
 * stay tight.
 * ────────────────────────────────────────────────────────────────── */
.ps-kv-editor {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-kv-editor__row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* Make the value cell carry the slack so long values don't push the
   key into a tiny column. The first child (key Input wrapper) gets a
   fixed-ish width; the second (value) flexes. */
.ps-kv-editor__row > :nth-child(1) {
    flex: 0 0 14rem;
}

.ps-kv-editor__row > :nth-child(2) {
    flex: 1 1 auto;
}

/* Spacer keeps the + button column-aligned with the × column when
   the secret-mode show/hide toggle isn't present in the add row. */
.ps-kv-editor__spacer {
    width: 28px;
    height: 1px;
    flex: 0 0 auto;
}

.ps-kv-editor__row--add {
    /* Subtle separation between the existing-entries list and the
       pending-add row. Doubles as a visual cue that the row is for
       new input rather than editing. */
    margin-top: var(--space-1);
}


/* ──────────────────────────────────────────────────────────────────
 * QueryShortcutChip — common-case filter pill that reads/writes the
 * surrounding QueryBar text. Trigger is a small pill-shaped button that
 * goes "active" (brand-tinted) when the underlying DSL has a value for
 * its field; the popover renders a simple option list with a check on
 * the selected row(s).
 * ────────────────────────────────────────────────────────────────── */
.ps-query-chip-wrap {
    display: inline-flex;
}

.ps-query-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    height: 32px;
    padding: 0 var(--space-2);
    border-radius: 999px;
    border: 1px solid var(--color-border);
    background-color: var(--color-surface);
    color: var(--color-ink);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    line-height: 1;
    white-space: nowrap;
}

.ps-query-chip:hover {
    border-color: var(--color-border-strong);
    background-color: var(--color-surface-hover);
}

.ps-query-chip:focus-visible {
    outline: none;
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px var(--color-brand-soft);
}

.ps-query-chip--active {
    border-color: var(--color-brand);
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
}

.ps-query-chip:disabled,
.ps-query-chip[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
}

.ps-query-chip--open {
    border-color: var(--color-brand);
}

.ps-query-chip__chevron {
    font-size: 10px;
    opacity: 0.7;
}

.ps-query-chip__list {
    list-style: none;
    margin: 0;
    padding: var(--space-1) 0;
    min-width: 180px;
}

.ps-query-chip__option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-1) var(--space-2);
    background: transparent;
    border: 0;
    color: var(--color-ink);
    font-size: var(--font-size-sm);
    text-align: left;
    cursor: pointer;
}

.ps-query-chip__option:hover,
.ps-query-chip__option:focus-visible {
    background-color: var(--color-surface-hover);
    outline: none;
}

.ps-query-chip__option--selected {
    color: var(--color-brand);
    font-weight: var(--font-weight-medium);
}

.ps-query-chip__option-check {
    width: 16px;
    display: inline-flex;
    justify-content: center;
    flex-shrink: 0;
    color: var(--color-brand);
}

.ps-query-chip__option--clear {
    color: var(--color-ink-subtle);
}

.ps-query-chip__option--clear .ps-query-chip__option-check {
    color: var(--color-ink-subtle);
}

.ps-query-chip__divider {
    height: 1px;
    margin: var(--space-1) 0;
    background-color: var(--color-border-subtle);
}


/* ──────────────────────────────────────────────────────────────────
 * Tabs (7a)
 *
 * Horizontal strip of role="tab" buttons + a single role="tabpanel"
 * for the active tab. Two variants:
 *   - .ps-tabs--underline (default): a tinted underline marks the
 *     active tab; the strip sits over a 1px bottom border so the
 *     underline reads as a thicker overlay on the same baseline.
 *   - .ps-tabs--pill: rounded pill background marks the active tab;
 *     no underline, no shared baseline.
 *
 * Disabled tabs render at the subtle ink colour and never accept
 * keyboard focus (the parent skips them on arrow-key nav).
 * ────────────────────────────────────────────────────────────────── */

.ps-tabs {
    display: flex;
    flex-direction: column;
}

.ps-tabs__list {
    display: flex;
    align-items: stretch;
    gap: var(--space-1);
}

.ps-tabs--underline .ps-tabs__list {
    border-bottom: 1px solid var(--color-border);
}

.ps-tabs__tab {
    appearance: none;
    background: transparent;
    border: 0;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    color: var(--color-ink-muted);
    font-family: var(--font-family-ui);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    transition: color var(--duration-fast) ease;
}

.ps-tabs__tab:hover:not(.ps-tabs__tab--disabled):not(.ps-tabs__tab--active) {
    color: var(--color-ink);
}

.ps-tabs__tab:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

.ps-tabs__tab--disabled {
    color: var(--color-ink-subtle);
    cursor: not-allowed;
}

.ps-tabs__tab-icon {
    color: inherit;
}

/* #672 — badge slot. Layout-only wrapper; the parent `.ps-tabs__tab`
   is already a flex container with `gap: var(--space-2)`, so the
   icon→label gap and label→badge gap match without extra margin. The
   badge itself supplies its own colour / pill via the v2 Badge
   primitive. */
.ps-tabs__tab-badge {
    display: inline-flex;
    align-items: center;
    line-height: 1;
}

/* Underline variant — a bottom border on the active tab, sitting on
   the same baseline as the strip's own bottom border. The 2px width
   visually overlays the 1px strip border without growing layout. */
.ps-tabs--underline .ps-tabs__tab {
    border-bottom: 2px solid transparent;
    /* Pull the tab down by 1px so its 2px border overlaps the strip's
       1px border-bottom rather than stacking below it. */
    margin-bottom: -1px;
}

.ps-tabs--underline .ps-tabs__tab--active {
    color: var(--color-brand);
    border-bottom-color: var(--color-brand);
}

/* Pill variant — rounded background on active, no underline. */
.ps-tabs--pill .ps-tabs__tab {
    border-radius: var(--radius-pill);
    padding: var(--space-1) var(--space-3);
}

.ps-tabs--pill .ps-tabs__tab--active {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
}

/* Segmented variant — adjacent tabs share a bordered container with
   internal dividers, active tab is filled with brand colour. Use for
   inline mode switchers (Designer / YAML / Split, etc.) where the
   strip reads as a single control rather than page-level navigation. */
.ps-tabs--segmented .ps-tabs__list {
    gap: 0;
    border: 1px solid var(--color-border);
    /* Match adjacent <Button> / <IconButton> primitives which use
       --radius-sm so the segmented control + buttons read as a single
       toolbar row (#559 round-2). */
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--color-surface);
}

.ps-tabs--segmented .ps-tabs__tab {
    padding: var(--space-1) var(--space-3);
    color: var(--color-ink);
    font-size: var(--font-size-sm);
    line-height: 1.5;
    background: var(--color-surface);
    transition: background-color var(--duration-fast) ease, color var(--duration-fast) ease;
}

.ps-tabs--segmented .ps-tabs__tab + .ps-tabs__tab {
    border-left: 1px solid var(--color-border);
}

.ps-tabs--segmented .ps-tabs__tab:hover:not(.ps-tabs__tab--disabled):not(.ps-tabs__tab--active) {
    background: var(--color-surface-raised);
    color: var(--color-ink);
}

.ps-tabs--segmented .ps-tabs__tab--active {
    background: var(--color-brand);
    color: var(--color-on-brand);
}

.ps-tabs--segmented .ps-tabs__tab--active:hover {
    background: var(--color-brand-hover);
    color: var(--color-on-brand);
}

/* The segmented variant is an inline control — it shouldn't render a
   tabpanel underneath (the host renders the editor body itself). */
.ps-tabs--segmented .ps-tabs__panel {
    display: none;
}

.ps-tabs__panel {
    padding: var(--space-4) 0;
    color: var(--color-ink);
    outline: none;
}


/* ──────────────────────────────────────────────────────────────────
 * Breadcrumb (7a)
 *
 * Trail of crumbs rendered as <nav><ol>. Each crumb is either a link
 * (ink-muted, brand on hover) or the current page (plain text, ink).
 * Separators sit between crumbs as decorative spans (aria-hidden);
 * default content is a FA chevron-right.
 * ────────────────────────────────────────────────────────────────── */

.ps-breadcrumb {
    display: block;
}

.ps-breadcrumb__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1);
    font-family: var(--font-family-ui);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
}

.ps-breadcrumb__item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-breadcrumb__link {
    color: var(--color-ink-muted);
    text-decoration: none;
    padding: var(--space-1) 0;
    transition: color var(--duration-fast) ease;
}

.ps-breadcrumb__link:hover,
.ps-breadcrumb__link:focus-visible {
    color: var(--color-brand);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.ps-breadcrumb__link:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

.ps-breadcrumb__current {
    color: var(--color-ink);
    font-weight: var(--font-weight-medium);
}

.ps-breadcrumb__icon {
    color: inherit;
    margin-right: var(--space-1);
}

.ps-breadcrumb__separator {
    color: var(--color-ink-subtle);
    font-size: var(--font-size-xs);
    margin: 0 var(--space-1);
    display: inline-flex;
    align-items: center;
}


/* ──────────────────────────────────────────────────────────────────
 * Pagination (7a)
 *
 * Page-number strip with prev/next chevrons. Current page is marked
 * with a brand-soft background + brand foreground; ellipses are
 * decorative spans. Buttons are square-ish so the strip reads as a
 * row of equal-width chips.
 * ────────────────────────────────────────────────────────────────── */

.ps-pagination {
    display: block;
}

.ps-pagination__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-pagination__item {
    display: inline-flex;
    align-items: center;
}

.ps-pagination__page,
.ps-pagination__nav {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    color: var(--color-ink-muted);
    cursor: pointer;
    min-width: 2rem;
    height: 2rem;
    padding: 0 var(--space-2);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-family-ui);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
    transition: background-color var(--duration-fast) ease,
                color var(--duration-fast) ease,
                border-color var(--duration-fast) ease;
}

.ps-pagination__page:hover:not(.ps-pagination__page--current):not(:disabled),
.ps-pagination__nav:hover:not(:disabled) {
    background-color: var(--color-brand-soft);
    color: var(--color-ink);
}

.ps-pagination__page:focus-visible,
.ps-pagination__nav:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-pagination__page--current {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
    border-color: var(--color-brand);
    cursor: default;
}

.ps-pagination__nav:disabled {
    color: var(--color-ink-subtle);
    cursor: not-allowed;
}

.ps-pagination__ellipsis {
    color: var(--color-ink-subtle);
    min-width: 2rem;
    height: 2rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-family-ui);
    font-size: var(--font-size-base);
}


/* ──────────────────────────────────────────────────────────────────
 * Schedule detail page chrome. Only used by /schedules/{id}.
 * ────────────────────────────────────────────────────────────────── */
.ps-schedule-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-schedule-detail__actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-schedule-detail__inline-alert {
    margin-bottom: var(--space-1);
}

.ps-schedule-detail__history-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.ps-schedule-detail__load-older {
    display: flex;
    justify-content: center;
    padding: var(--space-3);
    border-top: 1px solid var(--color-border-subtle);
}

/* dl row layout — label column + value column at small viewport
 * widths and above. Mirrors the .row.col-sm-3/9 grid the v1 page
 * used, without the Bootstrap utilities. */
.ps-schedule-detail__dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-schedule-detail__dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-schedule-detail__dl > dd {
    margin: 0;
}

.ps-schedule-detail__muted {
    color: var(--color-ink-muted);
}

.ps-schedule-detail__muted-sm {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-schedule-detail__error {
    color: var(--color-error-fg);
    font-size: var(--font-size-sm);
}

.ps-schedule-detail__inline {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.ps-schedule-detail__inline-link {
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * New-schedule dialog and its picker components.
 * Used by /schedules (modal) — composed of TargetPicker, CadencePicker,
 * CronBuilder.
 * ────────────────────────────────────────────────────────────────── */
.ps-new-schedule {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-new-schedule__divider {
    margin: var(--space-2) 0;
    border: 0;
    border-top: 1px solid var(--color-border-subtle);
}

.ps-target-picker {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-target-picker__search {
    margin-top: var(--space-1);
}

.ps-target-picker__empty {
    padding: var(--space-3);
    text-align: center;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-target-picker__list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 14rem;
    overflow-y: auto;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
}

.ps-target-picker__list > li + li {
    border-top: 1px solid var(--color-border-subtle);
}

.ps-target-picker__option {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    background: transparent;
    border: 0;
    text-align: left;
    cursor: pointer;
    color: inherit;
    transition: background-color var(--duration-fast) var(--easing-standard);
}

.ps-target-picker__option:hover,
.ps-target-picker__option:focus-visible {
    background-color: var(--color-surface-sunken);
    outline: none;
}

.ps-target-picker__option--selected {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
}

.ps-target-picker__option--selected:hover {
    background-color: var(--color-brand-soft);
}

.ps-target-picker__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.ps-target-picker__title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: var(--font-weight-medium);
}

.ps-target-picker__desc {
    margin-top: var(--space-1);
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
}


.ps-cadence-picker {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-cadence-picker__row {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.ps-cadence-picker__hint {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    padding-bottom: var(--space-2);
}


.ps-cron-builder {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-cron-builder__segmented {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}

.ps-cron-builder__row {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.ps-cron-builder__hint {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin: 0;
}

.ps-cron-builder__inline-hint {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    padding-bottom: var(--space-2);
}

.ps-cron-builder__preview {
    padding: var(--space-2) var(--space-3);
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
}

.ps-cron-builder__preview-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.ps-cron-builder__preview-label {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-cron-builder__preview-gloss {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-cron-builder__upcoming {
    margin-top: var(--space-2);
}

.ps-cron-builder__upcoming > summary {
    cursor: pointer;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-cron-builder__upcoming > ul {
    list-style: none;
    margin: var(--space-1) 0 0 0;
    padding: 0;
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Start-process modal body. Used by Orchestrations/Details + the
 * Processes / Orchestrations Start pages.
 * ────────────────────────────────────────────────────────────────── */
.ps-start-process {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-start-process__lead {
    margin: 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-start-process__required-star {
    color: var(--color-error-fg);
}

.ps-start-process__fields {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}


/* ──────────────────────────────────────────────────────────────────
 * Draft badge — wraps a v2 <Badge> with optional click affordance.
 * Used by every asset-detail header (orchestrations, processes,
 * applications) to surface unpublished drafts.
 * ────────────────────────────────────────────────────────────────── */
.ps-draft-badge {
    display: inline-flex;
    align-items: center;
    padding: 0;
    background: transparent;
    border: 0;
    cursor: pointer;
    font: inherit;
    color: inherit;
}

.ps-draft-badge--static {
    cursor: default;
}

.ps-draft-badge:focus-visible {
    outline: none;
    border-radius: var(--radius-sm);
    box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.ps-draft-badge__text {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-draft-badge__suffix {
    font-size: var(--font-size-xs);
    color: var(--color-pending-fg);
    opacity: 0.85;
}


/* ──────────────────────────────────────────────────────────────────
 * Execution History page chrome. /execution/history.
 * ────────────────────────────────────────────────────────────────── */
.ps-execution-history {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}


.ps-execution-history__id-search {
    max-width: 32rem;
}

.ps-execution-history__search-result-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.ps-execution-history__name {
    font-weight: var(--font-weight-medium);
}

.ps-execution-history__col-check,
.ps-applications__col-check,
.ps-orchestrations__col-check,
.ps-processes__col-check {
    /* #720 / #723 — narrow column for the multi-select checkbox so the
       data columns don't lose horizontal real estate. Matches the
       Execution History pattern; sharing the rule under one selector
       keeps the widths synchronized as the chrome evolves. */
    width: 2.5rem;
}

.ps-execution-history__package-link {
    text-decoration: none;
}

.ps-execution-history__muted {
    color: var(--color-ink-muted);
}

.ps-execution-history__bulk-banner {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    background-color: var(--color-surface-sunken);
    border-top: 1px solid var(--color-border-subtle);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-execution-history__pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--color-border-subtle);
}

.ps-execution-history__count {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-execution-history__pagination-buttons {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}



/* ──────────────────────────────────────────────────────────────────
 * Instance detail page chrome. Used by Orchestrations/Instance and
 * Processes/Instance.
 * ────────────────────────────────────────────────────────────────── */
.ps-instance-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-instance-page__actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-instance-page__header-meta {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: calc(-1 * var(--space-2));
}

.ps-instance-page__parent-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-size: var(--font-size-sm);
    text-decoration: none;
}

.ps-instance-page__muted {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-instance-page__dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-instance-page__dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-instance-page__dl > dd {
    margin: 0;
}

.ps-instance-page__error {
    margin-top: var(--space-3);
}



/* ──────────────────────────────────────────────────────────────────
 * Instances list page. /orchestrations/instances.
 * ────────────────────────────────────────────────────────────────── */
.ps-instances-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-instances-list__filter {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.ps-instances-list__filter-input {
    min-width: 18rem;
    max-width: 28rem;
    flex: 1 1 auto;
}

.ps-instances-list__details-link {
    text-decoration: none;
}

.ps-instances-list__pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--color-border-subtle);
}

.ps-instances-list__count {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Start (orchestration / process) page wrapper.
 * ────────────────────────────────────────────────────────────────── */
.ps-start-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}


/* ──────────────────────────────────────────────────────────────────
 * Application Run page. /applications/run/{id}.
 * ────────────────────────────────────────────────────────────────── */
.ps-app-run {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-app-run__elapsed {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-app-run__status-row {
    margin-top: calc(-1 * var(--space-2));
}

.ps-app-run__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-app-run__stream {
    max-height: 70vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.ps-app-run__event {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
}

.ps-app-run__event-ts {
    color: var(--color-ink-muted);
    min-width: 4.5rem;
}

.ps-app-run__task {
    margin: var(--space-2) 0;
}

.ps-app-run__result-header {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-app-run__icon-success { color: var(--color-success-fg); }
.ps-app-run__icon-error   { color: var(--color-error-fg); }
.ps-app-run__icon-running { color: var(--color-running-fg); }
.ps-app-run__icon-pending { color: var(--color-pending-fg); }

.ps-app-run__muted {
    color: var(--color-ink-muted);
}

.ps-app-run__section-title {
    margin: var(--space-3) 0 var(--space-2) 0;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
}


/* ──────────────────────────────────────────────────────────────────
 * Application Instance page. /applications/{id}/instances/{id}.
 * ────────────────────────────────────────────────────────────────── */
.ps-app-instance {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-app-instance__inline {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-app-instance__muted {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-app-instance__dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-app-instance__dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-app-instance__dl > dd {
    margin: 0;
}

.ps-app-instance__error {
    margin-top: var(--space-3);
}

.ps-app-instance__error-detail > summary {
    cursor: pointer;
    color: var(--color-error-fg);
    font-size: var(--font-size-sm);
    margin-top: var(--space-2);
}

.ps-app-instance__stack {
    margin: var(--space-2) 0 0 0;
    padding: var(--space-3);
    max-height: 400px;
    overflow: auto;
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-xs);
    white-space: pre-wrap;
    word-break: break-all;
}

.ps-app-instance__entrypoint-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.ps-app-instance__entrypoint-label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-app-instance__entrypoint-link {
    text-decoration: none;
}

.ps-app-instance__files-disclosure > summary {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    list-style: none;
}

.ps-app-instance__files-disclosure > summary::-webkit-details-marker {
    display: none;
}

.ps-app-instance__conv-header {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-app-instance__chat-empty {
    padding: var(--space-3) var(--space-4);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-app-instance__chat-thread {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.ps-app-instance__chat-msg {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-app-instance__chat-msg:last-child {
    border-bottom: 0;
}

.ps-app-instance__chat-msg__role {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--color-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: var(--space-1);
}

.ps-app-instance__chat-msg--user .ps-app-instance__chat-msg__role {
    color: var(--color-brand);
}

.ps-app-instance__chat-msg__content {
    color: var(--color-ink);
    white-space: pre-wrap;
    word-break: break-word;
}

.ps-app-instance__composer {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-app-instance__composer-actions {
    display: flex;
    justify-content: flex-end;
}

/* Generic visibility toggle for places where conditional @if/else removal
 * crashes Blazor Server's render-diff (TypeError: Cannot read properties
 * of null reading 'removeChild' / 'insertBefore'). Always render the
 * element; toggle this class to hide it. */
.ps-hidden {
    display: none !important;
}

.ps-app-instance__pending-header {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-pending-fg);
}


/* ──────────────────────────────────────────────────────────────────
 * Application by-package redirect splash. /applications/by-package/{name}.
 * ────────────────────────────────────────────────────────────────── */
.ps-by-package {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.ps-by-package__back-link {
    align-self: flex-start;
    margin-top: var(--space-1);
}


/* ──────────────────────────────────────────────────────────────────
 * Process detail page chrome. /processes/{id}.
 * Mirrors orchestration-detail; kept distinct so the two can diverge
 * (different tab sets) without shared-class regressions.
 * ────────────────────────────────────────────────────────────────── */
.ps-process-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-process-detail__header-meta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: calc(-1 * var(--space-2));
}

.ps-process-detail__dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-process-detail__dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-process-detail__dl > dd {
    margin: 0;
}

.ps-process-detail__source-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.ps-process-detail__source {
    margin: 0;
    padding: var(--space-3);
    max-height: 500px;
    overflow: auto;
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
    white-space: pre-wrap;
}





/* ──────────────────────────────────────────────────────────────────
 * Summary card primitive (compact KPI). Used in /, may be reused.
 * ────────────────────────────────────────────────────────────────── */
.ps-summary-card__title {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.ps-summary-card__body {
    padding: var(--space-3) var(--space-3) var(--space-2);
}

.ps-summary-card__value {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    line-height: 1;
}

.ps-summary-card__icon-row {
    display: flex;
    justify-content: flex-end;
}

.ps-summary-card__icon {
    font-size: 1.5rem;
}

.ps-summary-card__value--brand,
.ps-summary-card__icon--brand   { color: var(--color-brand); }
.ps-summary-card__value--success,
.ps-summary-card__icon--success { color: var(--color-success-fg); }
.ps-summary-card__value--error,
.ps-summary-card__icon--error   { color: var(--color-error-fg); }
.ps-summary-card__value--pending,
.ps-summary-card__icon--pending { color: var(--color-pending-fg); }
.ps-summary-card__value--running,
.ps-summary-card__icon--running { color: var(--color-running-fg); }
.ps-summary-card__value--neutral,
.ps-summary-card__icon--neutral { color: var(--color-ink-muted); }


/* ──────────────────────────────────────────────────────────────────
 * Shared chrome for the Analytics pages (/analytics/overview,
 * /analytics/workflows, /analytics/timeseries, /analytics/errors).
 * ────────────────────────────────────────────────────────────────── */
.ps-analytics {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-analytics__filters {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-3);
}

.ps-analytics__chart {
    height: 400px;
}

.ps-analytics__chart-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--space-3);
}

.ps-analytics__sample-message {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
}


/* ──────────────────────────────────────────────────────────────────
 * Agent analytics dashboard (/admin/analytics/agents, .../costs,
 * .../tools, .../execution/{id}). Issue #108.
 * ────────────────────────────────────────────────────────────────── */
.ps-agent-analytics {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-agent-analytics__nav {
    margin-bottom: var(--space-1);
}

.ps-agent-analytics__summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: var(--space-3);
}

.ps-agent-analytics__chart {
    height: 360px;
}

.ps-agent-analytics__table-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--space-3);
}

.ps-agent-analytics__facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: var(--space-2) var(--space-4);
    margin: 0;
}

.ps-agent-analytics__facts dt {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
}

.ps-agent-analytics__facts dd {
    margin: 0;
    word-break: break-all;
}

/* Iteration timeline */
.ps-agent-timeline {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-agent-timeline__step {
    border-left: 2px solid var(--color-border-subtle);
    padding-left: var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-agent-timeline__head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-agent-timeline__cumulative {
    font-size: var(--font-size-sm);
    color: var(--color-ink-muted);
}

.ps-agent-timeline__llm,
.ps-agent-timeline__tool {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
}

.ps-agent-timeline__tool-name {
    font-family: var(--font-mono, monospace);
}

.ps-agent-timeline__io {
    flex-basis: 100%;
}

.ps-agent-timeline__io pre {
    margin: var(--space-1) 0 0;
    padding: var(--space-2);
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    overflow-x: auto;
    font-size: var(--font-size-xs);
    max-height: 16rem;
}


/* ──────────────────────────────────────────────────────────────────
 * AppErrorBoundary — full-viewport fallback when a render throws.
 * ────────────────────────────────────────────────────────────────── */
.ps-error-boundary {
    min-height: 100vh;
    background-color: var(--color-surface-sunken);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
}

.ps-error-boundary__container {
    width: 100%;
    max-width: 36rem;
}

.ps-error-boundary__title {
    margin: 0;
    color: var(--color-error-fg);
    text-align: center;
    font-size: var(--font-size-xl);
}

.ps-error-boundary__message {
    text-align: center;
    margin-bottom: var(--space-3);
}

.ps-error-boundary__detail {
    margin: 0;
    padding: var(--space-3);
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
    white-space: pre-wrap;
    overflow: auto;
    max-height: 16rem;
}


/* ──────────────────────────────────────────────────────────────────
 * DateRangeFilter — three-up grid (start / end / actions).
 * ────────────────────────────────────────────────────────────────── */
.ps-date-range-filter {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: var(--space-3);
    align-items: end;
}

.ps-date-range-filter__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}


/* ──────────────────────────────────────────────────────────────────
 * TenantSelector — dropdown trigger living in the top app bar.
 * ────────────────────────────────────────────────────────────────── */
.ps-tenant-selector {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-tenant-selector__trigger {
    /* dropdown trigger — Ghost variant + a chevron on the right */
}

.ps-tenant-selector__type {
    margin-left: var(--space-1);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Active turns admin panel (Applications/ActiveTurnsPanel).
 * ────────────────────────────────────────────────────────────────── */
.ps-active-turns__hint {
    padding: var(--space-3);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-active-turns__alert {
    padding: var(--space-3);
}

.ps-active-turns__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-2);
}

.ps-active-turns__title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-active-turns__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.ps-active-turns__row {
    padding: var(--space-3);
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-active-turns__row:last-child {
    border-bottom: none;
}

.ps-active-turns__row-main {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-2);
}

.ps-active-turns__row-body {
    flex: 1 1 auto;
    min-width: 0;
}

.ps-active-turns__row-title {
    font-weight: var(--font-weight-medium);
}

.ps-active-turns__row-meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-active-turns__cancel-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-2);
}


/* ──────────────────────────────────────────────────────────────────
 * PermissionsPicker — fully-controlled permission selection list,
 * used by the Settings/ApiKeys create modal and the
 * Settings/ApiKeyDetails edit page.
 * ────────────────────────────────────────────────────────────────── */
.ps-permissions-picker {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-permissions-picker__alert {
    margin-bottom: var(--space-1);
}

.ps-permissions-picker__toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: stretch;
}

.ps-permissions-picker__toolbar > .ps-input-wrap,
.ps-permissions-picker__toolbar > .ps-input,
.ps-permissions-picker__toolbar > .ps-select {
    flex: 1 1 12rem;
    min-width: 0;
}

.ps-permissions-picker__empty {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    padding: var(--space-3);
}

.ps-permissions-picker__groups {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-permissions-picker__group-header {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
}

.ps-permissions-picker__group-resource {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-permissions-picker__row {
    padding: var(--space-1) 0;
}

.ps-permissions-picker__row--missing {
    border-left: 2px solid var(--color-error-fg);
    padding-left: var(--space-2);
}

.ps-permissions-picker__check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    cursor: pointer;
}

.ps-permissions-picker__check input[type="checkbox"] {
    margin-top: 0.25rem;
    flex-shrink: 0;
}

.ps-permissions-picker__label {
    flex: 1 1 auto;
    min-width: 0;
}

.ps-permissions-picker__desc {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-left: var(--space-1);
}

.ps-permissions-picker__warn {
    margin-left: var(--space-1);
    color: var(--color-pending-fg);
}

.ps-permissions-picker__scope {
    margin-top: var(--space-2);
    margin-left: var(--space-4);
    padding-left: var(--space-3);
    border-left: 2px solid var(--color-border-subtle);
}

.ps-permissions-picker__scope-modes {
    display: inline-flex;
    gap: var(--space-3);
}

.ps-permissions-picker__scope-mode {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    cursor: pointer;
    font-size: var(--font-size-sm);
}

.ps-permissions-picker__scope-hint {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-top: var(--space-1);
}

.ps-permissions-picker__scope-select {
    margin-top: var(--space-1);
    width: 100%;
    min-height: 8rem;
}


/* ──────────────────────────────────────────────────────────────────
 * Schedules/Index residue cleanup — utility classes replaced with
 * scoped helpers under the .ps-schedules-list namespace.
 * ────────────────────────────────────────────────────────────────── */
.ps-schedules-list__alert,
.ps-schedules-list__embedded-filter {
    margin-bottom: var(--space-3);
}

.ps-schedules-list__action {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-schedules-list__cadence {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-1);
}

.ps-schedules-list__cadence-desc {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-schedules-list__last-fire {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-schedules-list__muted {
    color: var(--color-ink-muted);
}

.ps-schedules-list__stale {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-pending-fg);
}

.ps-schedules-list__status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-schedules-list__confirm-meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-bottom: 0;
}


/* ──────────────────────────────────────────────────────────────────
 * Connections/Index page chrome — list rows, create/edit form,
 * and the Reset Conversation modal.
 * ────────────────────────────────────────────────────────────────── */
.ps-connections-page__alert {
    margin-bottom: var(--space-3);
}


.ps-connections-page__webhook {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-connections-page__webhook-url {
    font-size: var(--font-size-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 18.75rem;
}

.ps-connections-page__muted {
    color: var(--color-ink-muted);
}

.ps-connections-page__row-actions {
    display: inline-flex;
    gap: var(--space-1);
}

.ps-connections-page__confirm-meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-bottom: 0;
}

.ps-connection-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-connection-form__section-title {
    margin: var(--space-2) 0 0 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-connection-reset {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-connection-reset__intro {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin: 0;
}

.ps-connection-reset__state {
    display: grid;
    grid-template-columns: minmax(7rem, 10rem) 1fr;
    gap: var(--space-1) var(--space-3);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-connection-reset__state > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-connection-reset__state > dd {
    margin: 0;
}

.ps-connection-reset__state-muted {
    color: var(--color-ink-muted);
}

.ps-connection-reset__state-warn {
    color: var(--color-pending-fg);
    font-weight: var(--font-weight-medium);
}


/* ──────────────────────────────────────────────────────────────────
 * Search page chrome — minimal page with search input + results list.
 * ────────────────────────────────────────────────────────────────── */
.ps-search-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-search-page__results-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-2);
}

.ps-search-page__results-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-search-page__results-count {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-search-page__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.ps-search-page__list-item {
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-search-page__list-item:last-child {
    border-bottom: none;
}


/* ──────────────────────────────────────────────────────────────────
 * Most-used orchestrations chart page.
 * ────────────────────────────────────────────────────────────────── */
.ps-most-used {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-most-used__chart {
    height: 25rem;
    max-height: 50vh;
}


/* ──────────────────────────────────────────────────────────────────
 * Correlation / Message Journey page.
 * ────────────────────────────────────────────────────────────────── */
.ps-correlation {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-correlation__timeline {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-correlation__event-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.ps-correlation__event-title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-correlation__event-time {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    font-family: var(--font-family-mono);
}

.ps-correlation__event-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: var(--space-1) var(--space-3);
    font-size: var(--font-size-sm);
}

.ps-correlation__field-key {
    color: var(--color-ink-muted);
    margin-right: var(--space-1);
}

.ps-correlation__event-alert {
    margin-top: var(--space-2);
}

.ps-correlation__event-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.ps-correlation__event-actions-hint {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Files/Detail page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-file-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-file-detail__sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-file-detail__preview-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-2);
}

.ps-file-detail__dl {
    display: grid;
    grid-template-columns: minmax(7rem, 10rem) 1fr;
    column-gap: var(--space-3);
    row-gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-file-detail__dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-file-detail__dl > dd {
    margin: 0;
}

.ps-file-detail__preview {
    margin: 0;
    padding: var(--space-3);
    max-height: 31.25rem;
    overflow: auto;
    background-color: var(--color-surface-sunken);
    font-size: var(--font-size-sm);
    white-space: pre-wrap;
    word-break: break-all;
}

.ps-file-detail__confirm-meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-bottom: 0;
}


/* ──────────────────────────────────────────────────────────────────
 * Onboarding page (centered card).
 * ────────────────────────────────────────────────────────────────── */
.ps-onboarding {
    display: flex;
    justify-content: center;
    padding: var(--space-4) var(--space-3);
}

.ps-onboarding__container {
    width: 100%;
    max-width: 36rem;
}

.ps-onboarding__intro {
    text-align: center;
    margin-bottom: var(--space-3);
}

.ps-onboarding__heading {
    margin: 0 0 var(--space-1) 0;
}

.ps-onboarding__sub {
    margin: 0;
    color: var(--color-ink-muted);
}

.ps-onboarding__sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.ps-onboarding__section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-onboarding__section-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-onboarding__name-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: var(--space-3);
}


/* ──────────────────────────────────────────────────────────────────
 * Memory/UserMemory page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-user-memory {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-user-memory__tab-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-user-memory__chevron-col {
    width: 2.5rem;
    text-align: center;
    color: var(--color-ink-muted);
}

.ps-user-memory__similarity-col {
    text-align: right;
}

.ps-user-memory__result-summary {
    margin: 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-user-memory__expanded {
    margin: 0;
    padding: var(--space-3);
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
    white-space: pre-wrap;
    word-break: break-word;
}

.ps-user-memory__edit {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-user-memory__edit-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

.ps-user-memory__history-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-user-memory__history-reason {
    margin-top: var(--space-2);
}

.ps-user-memory__detail-label {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-bottom: var(--space-1);
}

.ps-user-memory__actor-annotation {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-left: var(--space-1);
}

.ps-user-memory__diff-old {
    color: var(--color-ink-muted);
}

.ps-user-memory__diff-arrow {
    color: var(--color-ink-muted);
    margin: 0 var(--space-1);
}

.ps-user-memory__pager {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--space-2);
}

.ps-user-memory__page-info {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin: 0;
}



/* ──────────────────────────────────────────────────────────────────
 * Chat/Index page — standard PageHeader on top, two-column grid
 * below. Each column is a v2 .ps-card so the conversation list and
 * the chat surface read as sibling panels with the same border /
 * radius rhythm as every other page. The page itself fills the
 * available vertical space so the chat body can scroll inside its
 * card without pushing the page header off-screen.
 * ────────────────────────────────────────────────────────────────── */
.ps-chat-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    /* Fill the available vertical space inside .ps-app__main so the
       inner cards can stretch to the bottom of the viewport. */
    height: 100%;
    min-height: 0;
}

.ps-chat-page__grid {
    display: grid;
    grid-template-columns: 18rem 1fr;
    gap: var(--space-3);
    flex: 1 1 auto;
    min-height: 0;
}

@media (max-width: 56rem) {
    .ps-chat-page__grid {
        grid-template-columns: 1fr;
    }
}

.ps-chat-page__list-pane {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-height: 0;
}

.ps-chat-page__list-pane > .query-bar {
    margin-bottom: 0;
}

/* Conversation list card — fills the remaining height of the left
   column and scrolls inside. */
.ps-chat-page__list-card {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    background-color: var(--color-surface-raised);
}

/* #719 — search input + show-archived toggle sitting above the thread
   tree inside the list card. Stacks vertically: search on top so the
   field width matches the tree, toggle underneath aligned left. */
.ps-chat-page__list-controls {
    padding: var(--space-2) var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    border-bottom: 1px solid var(--color-border-subtle);
    background-color: var(--color-surface);
}

/* #719 review — inline alert area for archive/restore failures.
   Matches the controls row's horizontal padding so the alert
   visually belongs to the list pane. */
.ps-chat-page__list-action-error {
    padding: var(--space-2) var(--space-3);
}

.ps-chat-page__list-summary {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-border-subtle);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-chat-page__conv-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-3);
    border-bottom: 1px solid var(--color-border-subtle);
    cursor: pointer;
    background: transparent;
    border-left: 0;
    border-right: 0;
    border-top: 0;
    width: 100%;
    text-align: left;
}

.ps-chat-page__conv-row:hover {
    background-color: var(--color-surface-raised);
}

.ps-chat-page__conv-row--selected {
    background-color: var(--color-surface);
    border-left: 3px solid var(--color-brand);
}

.ps-chat-page__conv-row-body {
    flex: 1 1 auto;
    min-width: 0;
}

.ps-chat-page__conv-title {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-chat-page__conv-title--archived {
    color: var(--color-ink-muted);
}

.ps-chat-page__conv-snippet {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-chat-page__conv-meta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

/* Right-pane card — wraps every chat surface (empty state, setup
   wizard step, active conversation). The card itself is a flex
   column so the messages area can take flex: 1 and scroll inside
   while the composer stays pinned at the bottom. */
.ps-chat-page__chat-card {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
    background-color: var(--color-surface-raised);
}

.ps-chat-page__empty-state {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--color-ink-muted);
    text-align: center;
}

.ps-chat-page__empty-icon {
    font-size: 4rem;
}

.ps-chat-page__empty-title {
    margin: var(--space-3) 0 var(--space-2) 0;
}

.ps-chat-page__empty-meta {
    margin: 0;
}

.ps-chat-page__empty-tile-row {
    margin-top: var(--space-4);
    width: min(28rem, 100%);
}

.ps-chat-page__concierge-tile {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-3) var(--space-4);
    background-color: var(--color-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    text-align: left;
    color: inherit;
    cursor: pointer;
    transition: border-color 120ms ease, box-shadow 120ms ease, transform 120ms ease;
}

.ps-chat-page__concierge-tile:hover:not(:disabled) {
    border-color: var(--color-brand);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-brand) 18%, transparent);
}

.ps-chat-page__concierge-tile:focus-visible {
    outline: none;
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-brand) 35%, transparent);
}

.ps-chat-page__concierge-tile:disabled {
    cursor: progress;
    opacity: 0.75;
}

.ps-chat-page__concierge-tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--radius-md);
    background-color: color-mix(in srgb, var(--color-brand) 12%, transparent);
    color: var(--color-brand);
    font-size: 1.125rem;
    flex-shrink: 0;
}

.ps-chat-page__concierge-tile-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1 1 auto;
    min-width: 0;
}

.ps-chat-page__concierge-tile-title {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink);
}

.ps-chat-page__concierge-tile-sub {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-chat-page__concierge-tile-chevron,
.ps-chat-page__concierge-tile-spinner {
    color: var(--color-ink-muted);
    flex-shrink: 0;
    /* Both rendered always so FontAwesome's SVG-replacement and
       Blazor's render diff don't fight over a swapped subtree;
       the parent's --loading modifier picks which one is visible. */
}

/* Default: chevron visible, spinner hidden. */
.ps-chat-page__concierge-tile-spinner {
    display: none;
}

/* Loading: hide the chevron, show the spinner. */
.ps-chat-page__concierge-tile--loading .ps-chat-page__concierge-tile-chevron {
    display: none;
}
.ps-chat-page__concierge-tile--loading .ps-chat-page__concierge-tile-spinner {
    display: inline-flex;
}

.ps-chat-page__setup {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    overflow: auto;
}

.ps-chat-page__setup-header {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.ps-chat-page__setup-title {
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-lg);
}

.ps-chat-page__setup-back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-chat-page__setup-sub {
    margin: 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-chat-page__setup-search {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-chat-page__setup-search > .ps-input,
.ps-chat-page__setup-search > .ps-input-wrap {
    flex: 1 1 auto;
}

.ps-chat-page__setup-empty {
    text-align: center;
    color: var(--color-ink-muted);
    padding: var(--space-4);
}

.ps-chat-page__setup-empty-icon {
    font-size: 2rem;
}

.ps-chat-page__setup-actions {
    display: flex;
    gap: var(--space-2);
}

.ps-chat-page__app-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-chat-page__app-card {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-3);
    text-align: left;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    cursor: pointer;
    width: 100%;
}

.ps-chat-page__app-card--selected {
    border-color: var(--color-brand);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-brand) 25%, transparent);
}

.ps-chat-page__app-radio {
    margin-top: 0.125rem;
    flex-shrink: 0;
}

.ps-chat-page__app-name {
    font-weight: var(--font-weight-medium);
}

.ps-chat-page__app-desc {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-chat-page__inputs {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-chat-page__inputs-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-chat-page__messages {
    flex: 1 1 auto;
    overflow: auto;
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-chat-page__messages-loading {
    text-align: center;
    color: var(--color-ink-muted);
    margin-top: var(--space-4);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
}

/* Chat-history-scalability Phase 6 (#423) — session divider between
 * adjacent chat bubbles whose SessionId differs. Mirrors the drawer's
 * equivalent rule so the page reads consistently. */
.ps-chat-page__session-divider {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: var(--space-3) 0;
    border-top: 1px solid var(--color-border-subtle);
    position: relative;
}

.ps-chat-page__session-divider-label {
    position: relative;
    top: -0.55rem;
    padding: 0 var(--space-2);
    background-color: var(--color-surface);
    font-size: var(--font-size-xs);
    color: var(--color-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ps-chat-page__msg-wrap {
    display: contents;
}

.ps-chat-page__tool-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-chat-page__tool-row--active {
    font-style: italic;
}

.ps-chat-page__status-row {
    display: flex;
    justify-content: center;
    margin: var(--space-2) 0;
}

.ps-chat-page__composer {
    border-top: 1px solid var(--color-border-subtle);
    padding: var(--space-3);
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-chat-page__drop-overlay {
    position: absolute;
    inset: 0;
    background-color: color-mix(in srgb, var(--color-brand) 8%, transparent);
    border: 2px dashed var(--color-brand);
    border-radius: var(--radius-md);
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    color: var(--color-brand);
    font-weight: var(--font-weight-medium);
}

.ps-chat-page__upload-progress {
    margin-bottom: var(--space-1);
}

.ps-chat-page__attached-files {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}

.ps-chat-page__attached-file {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
}

.ps-chat-page__attached-file-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 9.375rem;
}

.ps-chat-page__attached-file-size {
    color: var(--color-ink-muted);
}

.ps-chat-page__composer-row {
    display: flex;
    gap: var(--space-2);
    align-items: flex-end;
}

.ps-chat-page__file-input {
    display: none;
}

.ps-chat-page__attach {
    cursor: pointer;
    flex-shrink: 0;
}

.ps-chat-page__attach--disabled {
    cursor: not-allowed;
    opacity: 0.5;
    pointer-events: none;
}

.ps-chat-page__textarea {
    flex: 1 1 auto;
    resize: none;
    height: 72px;
    max-height: 7.5rem;
}

.ps-chat-page__composer-hint {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * App shell — top bar, drawer, footer. Used by MainLayout.
 * ────────────────────────────────────────────────────────────────── */
/* Shell is viewport-bounded so each panel (rail / main / drawer)
 * owns its own scroll. Without height: 100vh + overflow: hidden here,
 * descendant overflow-y: auto rules never trigger and the whole page
 * scrolls instead — pushing the rail's bottom-anchored account button
 * and the drawer's composer off-screen. */
.ps-app {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
    background-color: var(--color-surface);
    color: var(--color-ink);
}

.ps-app__chrome {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
}

.ps-app__main {
    flex: 1 1 auto;
    padding: var(--space-4);
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
}

.ps-app--empty .ps-app__chrome {
    display: none;
}

/* ──────────────────────────────────────────────────────────────────
 * LeftRail (Phase 2.2a) — 64px icon-only vertical strip.
 *
 * Top:    .ps-rail__modes — five mode icons (Chat / Tasks / Build /
 *         Manage / Admin). One stays anchored to the top.
 * Middle: .ps-rail__recents — flex-grow placeholder for sessions
 *         (Phase 2.2c / Phase 3 fill it).
 * Bottom: .ps-rail__avatar — account dropdown trigger.
 * ────────────────────────────────────────────────────────────────── */
.ps-rail {
    flex: 0 0 64px;
    width: 64px;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: var(--color-surface-sunken);
    border-right: 1px solid var(--color-border);
    padding: var(--space-2) 0;
    gap: var(--space-2);
    overflow-y: auto;
    scrollbar-width: none;
}

.ps-rail::-webkit-scrollbar {
    display: none;
}

.ps-rail__brand {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-1) 0 var(--space-2);
    color: inherit;
    text-decoration: none;
    border-radius: var(--radius-sm);
}

.ps-rail__brand:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-rail__brand-mark {
    width: 30px;
    height: auto;
    display: block;
}

.ps-rail__modes {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
}

.ps-rail__recents {
    flex: 1 1 auto;
    width: 100%;
    min-height: var(--space-4);
}

.ps-rail__recents-placeholder {
    height: 100%;
}

.ps-rail__avatar {
    margin-top: auto;
    width: 100%;
    display: flex;
    justify-content: center;
}

.ps-rail__mode {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-md);
    color: var(--color-ink-muted);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--easing-standard),
                color var(--duration-fast) var(--easing-standard);
}

.ps-rail__mode:hover {
    background-color: var(--color-border-subtle);
    color: var(--color-ink);
    text-decoration: none;
}

.ps-rail__mode:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-rail__mode.active {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
}

.ps-rail__mode-icon {
    font-size: 1.125rem;
    line-height: 1;
}

/* The label is announced to AT only — the rail is icon-only. */
.ps-rail__mode-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Positioning anchor so the pending-count badge can overlay the icon (#1183). */
.ps-rail__mode-wrap {
    position: relative;
    display: flex;
    justify-content: center;
}

/* Pending-count overlay pinned to the icon's top-right corner. */
.ps-rail__badge {
    position: absolute;
    top: -4px;
    right: -2px;
    pointer-events: none;
}

/* Positioning anchor so the pending-connections badge can overlay the
 * avatar trigger (#386), reusing the .ps-rail__badge overlay. */
.ps-rail__avatar-wrap {
    position: relative;
    display: inline-flex;
}

/* Settings menu row: label + trailing pending-connections badge (#386). */
.ps-avatar-menu__settings-label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-rail__avatar-trigger {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--color-border);
    background-color: var(--color-surface-raised);
    color: var(--color-ink-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--easing-standard),
                color var(--duration-fast) var(--easing-standard);
}

.ps-rail__avatar-trigger:hover {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
}

.ps-rail__avatar-trigger:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-rail__avatar-icon {
    font-size: 1.125rem;
    line-height: 1;
}

/* ──────────────────────────────────────────────────────────────────
 * Mode index landing pages (Phase 2.2a placeholder for /build,
 * /manage). Phase 2.2d / Phase 4 replace these with persona-specific
 * landing UX.
 * ────────────────────────────────────────────────────────────────── */
.ps-mode-index {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    margin-top: var(--space-4);
}

.ps-mode-index__section {
    margin: 0 0 var(--space-3);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--color-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ps-mode-index__links {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.ps-mode-index__links a {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    color: var(--color-ink);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--easing-standard);
}

.ps-mode-index__links a:hover {
    background-color: var(--color-border-subtle);
    text-decoration: none;
}

.ps-mode-index__links a:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}


/* ──────────────────────────────────────────────────────────────────
 * NavMenu (used by MainLayout drawer + SettingsLayout sidebar).
 * ────────────────────────────────────────────────────────────────── */
.ps-navmenu {
    display: flex;
    flex-direction: column;
    padding: var(--space-3);
    gap: var(--space-1);
}

.ps-navmenu__section {
    margin: var(--space-3) 0 var(--space-1) var(--space-1);
    color: var(--color-ink-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ps-navmenu__divider {
    border: 0;
    border-top: 1px solid var(--color-border-subtle);
    margin: var(--space-2) 0;
}

.ps-navmenu__link {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    color: var(--color-ink);
    text-decoration: none;
    font-size: var(--font-size-sm);
    cursor: pointer;
}

.ps-navmenu__link:hover {
    background-color: var(--color-surface-raised);
    color: var(--color-ink);
    text-decoration: none;
}

.ps-navmenu__link.active {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
}

.ps-navmenu__link--back {
    color: var(--color-ink-muted);
}

.ps-navmenu__icon {
    width: 1rem;
    flex-shrink: 0;
    text-align: center;
}


/* ──────────────────────────────────────────────────────────────────
 * SidenavPaneLayout (issue #773) — generic sidenav + body shell.
 *
 * Composition:
 *   .ps-sidenav-pane                        outer flex column
 *     .ps-sidenav-pane__top-tabs            optional top strip slot
 *     .ps-sidenav-pane__body-row            sidenav + body row
 *       .ps-sidenav-pane__sidenav           left-side section nav
 *         .ps-sidenav-pane__link            one row (<a> or <button>)
 *           .ps-sidenav-pane__icon          optional leading icon
 *           .ps-sidenav-pane__label         row text
 *       .ps-sidenav-pane__body              active section body slot
 *
 * Two callers today:
 *   - SettingsPageLayout (#773) — anchor-mode rows + a tab strip slot.
 *   - AppConfigEditorView (#773) — button-mode rows, no top tabs.
 *
 * The legacy .ps-settings-page__* class names are kept as combined
 * selectors below so AdminPageLayout / BuildPageLayout — which embed
 * the same pattern inline rather than composing SettingsPageLayout —
 * still pick up the styles. SettingsPageLayout itself was migrated to
 * the new primitive in #773.
 *
 * Section-page styling overrides (top-tab text-decoration, container
 * height behaviour) layer over this primitive via the
 * --settings-page / --appconfig-form variants below.
 * ────────────────────────────────────────────────────────────────── */
.ps-sidenav-pane,
.ps-settings-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    min-height: 100%;
}

.ps-sidenav-pane__body-row,
.ps-settings-page__body-row {
    display: grid;
    grid-template-columns: 15rem 1fr;
    gap: var(--space-6);
    flex: 1 1 auto;
    min-height: 0;
}

@media (max-width: 48rem) {
    .ps-sidenav-pane__body-row,
    .ps-settings-page__body-row {
        grid-template-columns: 1fr;
    }
}

/* Empty sidenav collapses so the body stretches across the full width. */
.ps-sidenav-pane__body-row:has(.ps-sidenav-pane__sidenav:empty),
.ps-settings-page__body-row:has(.ps-settings-page__sidenav:empty) {
    grid-template-columns: 1fr;
}

.ps-sidenav-pane__sidenav,
.ps-settings-page__sidenav {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-2) 0;
}

.ps-sidenav-pane__sidenav:empty,
.ps-settings-page__sidenav:empty {
    display: none;
}

.ps-sidenav-pane__link,
.ps-settings-page__subnav-link {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--easing-standard),
                color            var(--duration-fast) var(--easing-standard);
}

.ps-sidenav-pane__link {
    /* Reset button defaults so button-mode rows match anchor-mode rows. */
    background: transparent;
    border: 0;
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    width: 100%;
}

.ps-sidenav-pane__link:hover:not(.ps-sidenav-pane__link--active),
.ps-settings-page__subnav-link:hover:not(.ps-settings-page__subnav-link--active) {
    background-color: var(--color-surface-raised);
    color: var(--color-ink);
}

.ps-sidenav-pane__link:focus-visible,
.ps-settings-page__subnav-link:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-sidenav-pane__link--active,
.ps-settings-page__subnav-link--active {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
    font-weight: var(--font-weight-medium);
}

.ps-sidenav-pane__icon,
.ps-settings-page__subnav-icon {
    width: 1.1rem;
    text-align: center;
}

.ps-sidenav-pane__body,
.ps-settings-page__body {
    min-width: 0;
    min-height: 0;
}

/* Settings-specific overlay: the tab strip is the canonical
 * .ps-tabs.ps-tabs--underline composition, anchors not buttons, so
 * they need text-decoration: none. Both class systems carry the
 * override since AdminPageLayout / BuildPageLayout use the legacy
 * selector for the strip. */
.ps-sidenav-pane--settings-page .ps-sidenav-pane__top-tabs .ps-tabs__tab,
.ps-settings-page__tabs .ps-tabs__tab {
    text-decoration: none;
}

/* AppConfig-specific overlay: the designer host gives the form a
 * fixed-height container with its own scroll; suppress the
 * min-height: 100% from the primitive so the layout doesn't fight
 * the host's flex sizing. */
.ps-sidenav-pane--appconfig-form {
    min-height: 0;
    height: 100%;
    gap: 0;
}

.ps-sidenav-pane--appconfig-form .ps-sidenav-pane__body-row {
    height: 100%;
}

.ps-sidenav-pane--appconfig-form .ps-sidenav-pane__body {
    overflow: auto;
}

.ps-settings-page__placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 12rem;
}


/* ──────────────────────────────────────────────────────────────────
 * ImpersonationBanner — striped warning banner shown above the top
 * bar while an admin is impersonating. The pattern is intentionally
 * loud so the impersonating context is unmistakable.
 * ────────────────────────────────────────────────────────────────── */
.ps-impersonation-banner {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    color: var(--color-error-fg);
}

.ps-impersonation-banner--active {
    background: repeating-linear-gradient(
        45deg,
        var(--color-error-bg-soft),
        var(--color-error-bg-soft) 0.75rem,
        var(--color-pending-bg-soft) 0.75rem,
        var(--color-pending-bg-soft) 1.5rem);
}

.ps-impersonation-banner--terminal {
    background-color: var(--color-pending-bg-soft);
    color: var(--color-pending-fg);
}

.ps-impersonation-banner__icon {
    font-size: var(--font-size-lg);
}

.ps-impersonation-banner__title {
    flex-shrink: 0;
}

.ps-impersonation-banner__elapsed {
    color: inherit;
    opacity: 0.8;
    font-size: var(--font-size-sm);
}

.ps-impersonation-banner__actions {
    margin-left: auto;
}


/* ──────────────────────────────────────────────────────────────────
 * Performance monitor (admin tool).
 * ────────────────────────────────────────────────────────────────── */
.ps-perf-monitor {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-perf-monitor__hint {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-perf-monitor__summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-3);
}

.ps-perf-monitor__metric-label {
    margin: 0 0 var(--space-1) 0;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-perf-monitor__metric-value {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-tight);
}

.ps-perf-monitor__metric-value--brand   { color: var(--color-brand); }
.ps-perf-monitor__metric-value--success { color: var(--color-success-fg); }
.ps-perf-monitor__metric-value--error   { color: var(--color-error-fg); }

.ps-perf-monitor__metric-meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin: var(--space-2) 0 0 0;
}

.ps-perf-monitor__num-col {
    text-align: right;
}


/* ──────────────────────────────────────────────────────────────────
 * Chat message bubble (Components/Chat/ChatMessageBubble).
 * ────────────────────────────────────────────────────────────────── */
.ps-chat-msg {
    display: flex;
    margin-bottom: var(--space-3);
}

.ps-chat-msg--start  { justify-content: flex-start; }
.ps-chat-msg--end    { justify-content: flex-end; }
.ps-chat-msg--system { justify-content: center; }

.ps-chat-msg__system-text {
    color: var(--color-ink-muted);
    font-style: italic;
}

.ps-chat-msg__bubble {
    border-radius: var(--radius-md);
    padding: var(--space-3);
    word-wrap: break-word;
}

.ps-chat-msg__bubble--user {
    background-color: var(--color-brand);
    color: var(--color-on-brand);
    max-width: 90%;
}

.ps-chat-msg__bubble--agent {
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    max-width: 90%;
}

.ps-chat-msg__bubble--task {
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    max-width: 80%;
}

.ps-chat-msg__bubble--output {
    background-color: var(--color-success-bg-soft);
    border: 1px solid var(--color-border-subtle);
    max-width: 80%;
}

.ps-chat-msg__role {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-bottom: var(--space-1);
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
}

.ps-chat-msg__role-name {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

/* Timestamp sits to the right of the role label. font-variant-numeric
 * gives a tight, consistent figure width so adjacent bubbles don't
 * jitter as the relative time ticks (just now → 1m → 2m). */
.ps-chat-msg__time {
    margin-left: auto;
    font-size: var(--font-size-xs);
    opacity: 0.85;
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}

.ps-chat-msg__bubble--user .ps-chat-msg__role {
    color: rgba(255, 255, 255, 0.7);
}

.ps-chat-msg__content-pre {
    white-space: pre-wrap;
    margin-bottom: var(--space-2);
}

.ps-chat-msg__outputs {
    display: grid;
    grid-template-columns: minmax(7rem, max-content) 1fr;
    gap: var(--space-1) var(--space-3);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-chat-msg__outputs > dt {
    color: var(--color-ink-muted);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
}

.ps-chat-msg__outputs > dd {
    margin: 0;
    word-break: break-word;
}

.ps-chat-msg__attachments {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.ps-chat-msg__attachment-image-wrap {
    max-width: 12.5rem;
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.ps-chat-msg__attachment-image {
    max-height: 9.375rem;
    max-width: 100%;
    border-radius: var(--radius-sm);
    display: block;
}

.ps-chat-msg__attachment-name {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-top: var(--space-1);
}

.ps-chat-msg__bubble--user .ps-chat-msg__attachment-name {
    color: rgba(255, 255, 255, 0.7);
}

.ps-chat-msg__attachment-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
}

.ps-chat-msg__bubble--user .ps-chat-msg__attachment-chip {
    background-color: rgba(255, 255, 255, 0.15);
    border-color: rgba(255, 255, 255, 0.25);
    color: var(--color-on-brand);
}

.ps-chat-msg__attachment-link {
    color: inherit;
    text-decoration: none;
}

.ps-chat-msg__attachment-link:hover {
    text-decoration: underline;
}

.ps-chat-msg__attachment-size {
    color: var(--color-ink-muted);
}

.ps-chat-msg__bubble--user .ps-chat-msg__attachment-size {
    color: rgba(255, 255, 255, 0.7);
}

.ps-chat-msg__completed {
    margin-top: var(--space-1);
    color: var(--color-ink-muted);
    font-style: italic;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}


/* ──────────────────────────────────────────────────────────────────
 * Tasks/Index residue helpers.
 * ────────────────────────────────────────────────────────────────── */
.ps-tasks-list__check-col {
    width: 2.5rem;
}

.ps-tasks-list__alert {
    margin-bottom: var(--space-3);
}

.ps-files-list__check-col {
    width: 2.5rem;
}


/* ──────────────────────────────────────────────────────────────────
 * Files/Index stats strip.
 * ────────────────────────────────────────────────────────────────── */
.ps-files-stats {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.ps-files-stats__group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
}

.ps-files-stats__metric {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-files-stats__metric--warn {
    color: var(--color-pending-fg);
}


/* ──────────────────────────────────────────────────────────────────
 * Events/Index footer (load-more / count strip).
 * ────────────────────────────────────────────────────────────────── */
.ps-events-list__footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: var(--space-3);
}

.ps-events-list__count {
    color: var(--color-ink-muted);
}


/* ──────────────────────────────────────────────────────────────────
 * Standardized row-action wrapper for list pages — replaces ad-hoc
 * d-inline-flex gap-1 patterns around row IconButton groups.
 * ────────────────────────────────────────────────────────────────── */
.ps-row-actions {
    display: inline-flex;
    gap: var(--space-1);
}


/* ──────────────────────────────────────────────────────────────────
 * Generic v2 utility helpers — token-driven replacements for the
 * Bootstrap utilities we rely on at the call site (text colour,
 * weight, alignment, spacing). Authored conservatively: only the
 * patterns that recur across multiple pages get a helper.
 * ────────────────────────────────────────────────────────────────── */
.ps-text-muted   { color: var(--color-ink-muted); }
.ps-text-end     { text-align: right; }
.ps-text-center  { text-align: center; }
.ps-text-sm      { font-size: var(--font-size-sm); }
.ps-text-mono    { font-family: var(--font-family-mono); }

/* Date/time values rendered by <DateTimeDisplay> — pin white-space so
   yyyy-MM-dd never wraps mid-value in narrow table cells. */
.ps-datetime     { white-space: nowrap; }

.ps-mb-0 { margin-bottom: 0; }
.ps-mb-1 { margin-bottom: var(--space-1); }
.ps-mb-2 { margin-bottom: var(--space-2); }
.ps-mb-3 { margin-bottom: var(--space-3); }
.ps-mt-1 { margin-top: var(--space-1); }
.ps-mt-2 { margin-top: var(--space-2); }
.ps-mt-3 { margin-top: var(--space-3); }
.ps-ml-1 { margin-left: var(--space-1); }
.ps-ml-2 { margin-left: var(--space-2); }
.ps-mr-1 { margin-right: var(--space-1); }
.ps-mr-2 { margin-right: var(--space-2); }

/* Form grid helpers — quick column splits for forms inside Modals or
   Cards. Replaces the old Bootstrap row/col-md-* pattern with a clean
   CSS grid that collapses to a single column on narrow viewports. */
.ps-form-grid {
    display: grid;
    gap: var(--space-3);
}

.ps-form-grid--2col { grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }
.ps-form-grid--3col { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.ps-form-grid--4col { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); }

.ps-form-section-title {
    margin: var(--space-3) 0 var(--space-2) 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}


/* ──────────────────────────────────────────────────────────────────
 * Reset rules — used to live in Bootstrap's reboot. Now that the
 * Bootstrap stylesheet is gone, restate the small set we depend on.
 * ────────────────────────────────────────────────────────────────── */
*, *::before, *::after {
    box-sizing: border-box;
}

body {
    margin: 0;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

a {
    color: var(--color-brand);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

button {
    /* Native <button> defaults to its UA stylesheet (a beveled grey
       chip with serif font on some platforms). Reset to inherit so
       v2 .ps-btn / .ps-icon-btn drive the entire visual treatment. */
    font: inherit;
    color: inherit;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
}

img, svg {
    vertical-align: middle;
}

/* Default heading rhythm — Bootstrap's reboot zeroed top margin and
   set a small bottom margin on h1-h6. Replicate so our scoped CSS
   doesn't have to fight UA defaults. */
h1, h2, h3, h4, h5, h6 {
    margin-top: 0;
    margin-bottom: var(--space-2);
}

p {
    margin-top: 0;
    margin-bottom: var(--space-2);
}


/* ──────────────────────────────────────────────────────────────────
 * Versioning components: VersionsTab, DiffView, DraftEditor,
 * PromoteDraftModal body, UpgradeInstanceDialog body. Embedded
 * inside detail pages.
 * ────────────────────────────────────────────────────────────────── */
.ps-versions-tab {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-versions-tab__toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.ps-versions-tab__count {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-versions-tab__diff-picker {
    display: grid;
    grid-template-columns: 1fr 1fr auto;
    gap: var(--space-2);
    align-items: end;
}

.ps-versions-tab__diff-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.ps-versions-tab__version-cell {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-versions-tab__chev {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-versions-tab__muted {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-versions-tab__expand-header {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-versions-tab__hash-dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    column-gap: var(--space-4);
    row-gap: var(--space-1);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-versions-tab__hash-dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-versions-tab__hash-dl > dd {
    margin: 0;
}



.ps-diff-view {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-diff-view__empty {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-diff-view__header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.ps-diff-view__hash-row {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-diff-view__changes {
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
}

.ps-diff-view__changes > li {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
}

.ps-diff-view__changes > li + li {
    border-top: 1px solid var(--color-border-subtle);
}

.ps-diff-view__change-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.ps-diff-view__muted {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}


.ps-promote-draft {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}


.ps-upgrade-dialog {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-upgrade-dialog__current {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-upgrade-dialog__muted {
    color: var(--color-ink-muted);
}


.ps-draft-editor {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-draft-editor__conflict-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: calc(-1 * var(--space-2));
}

.ps-draft-editor__toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.ps-draft-editor__toolbar-left,
.ps-draft-editor__toolbar-right {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-draft-editor__label {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-draft-editor__textarea {
    display: block;
    width: 100%;
    padding: var(--space-3);
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-family: 'SFMono-Regular', Consolas, monospace;
    font-size: var(--font-size-sm);
    line-height: 1.4;
    resize: vertical;
}

.ps-draft-editor__textarea:focus {
    outline: none;
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px var(--color-focus-ring);
}


/* ──────────────────────────────────────────────────────────────────
 * Task forms (Approval / YesNo / TextInput / NumberInput / Selection
 * / Form / SystemTaskCard). Embedded by TaskFormDispatcher inside
 * Application Run + Instance pages.
 * ────────────────────────────────────────────────────────────────── */
.ps-task-form {
    margin-bottom: var(--space-3);
    border-left: 3px solid var(--color-border);
    border-radius: var(--radius-sm);
}

.ps-task-form--pending  { border-left-color: var(--color-pending-fg); }
.ps-task-form--info     { border-left-color: var(--color-running-fg); }
.ps-task-form--brand    { border-left-color: var(--color-brand); }
.ps-task-form--neutral  { border-left-color: var(--color-border-strong); }

.ps-task-form__header {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-task-form__icon {
    color: var(--color-ink-muted);
}

.ps-task-form__description {
    margin: 0 0 var(--space-3) 0;
}

.ps-task-form__field-row {
    margin-bottom: var(--space-3);
}

.ps-task-form__fields {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}

.ps-task-form__actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-task-form__result {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-ink-muted);
}

.ps-task-form__result-icon {
    color: var(--color-success-fg);
}

.ps-task-form__result-list {
    list-style: none;
    margin: var(--space-1) 0 0 var(--space-4);
    padding: 0;
    font-size: var(--font-size-sm);
}

.ps-task-form__waiting {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-ink-muted);
}


/* ──────────────────────────────────────────────────────────────────
 * Code-input field (JSON / XML editor inside InputParameterField).
 * ────────────────────────────────────────────────────────────────── */
.ps-code-input__textarea {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-family: 'SFMono-Regular', Consolas, monospace;
    font-size: var(--font-size-sm);
    line-height: 1.4;
    resize: vertical;
}

.ps-code-input__textarea:focus {
    outline: none;
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.ps-code-input__textarea--invalid {
    border-color: var(--color-error-fg);
}

.ps-code-input__error {
    margin-top: var(--space-1);
    color: var(--color-error-fg);
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Array-input field (variable-length list inside InputParameterField).
 * ────────────────────────────────────────────────────────────────── */
.ps-array-input {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-array-input__row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
}

.ps-array-input__field {
    flex: 1 1 auto;
}

.ps-array-input__error {
    margin-top: var(--space-1);
    color: var(--color-error-fg);
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * File-input fields (single + multi-file). Embedded in
 * InputParameterField; the surrounding <Field> owns the label.
 * ────────────────────────────────────────────────────────────────── */
.ps-file-input {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ps-file-input__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.ps-file-input__uploaded {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}


/* ──────────────────────────────────────────────────────────────────
 * Execution event row (linear event log).
 * ────────────────────────────────────────────────────────────────── */
.ps-event-row {
    display: flex;
    align-items: flex-start;
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--color-border-subtle);
    gap: var(--space-3);
}

.ps-event-row--error   { background-color: var(--color-error-bg-soft); }
.ps-event-row--success { background-color: var(--color-success-bg-soft); }

.ps-event-row__time {
    color: var(--color-ink-muted);
    min-width: 6.5rem;
    font-size: var(--font-size-sm);
}

.ps-event-row__type {
    min-width: 8rem;
}

.ps-event-row__details {
    flex: 1 1 auto;
    font-size: var(--font-size-sm);
}

.ps-event-row__name {
    font-weight: var(--font-weight-medium);
}

.ps-event-row__muted {
    color: var(--color-ink-muted);
    margin-left: var(--space-1);
}

.ps-event-row__output {
    margin: var(--space-1) 0 0 0;
    padding: var(--space-2);
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-sm);
    font-family: 'SFMono-Regular', Consolas, monospace;
    font-size: var(--font-size-sm);
    white-space: pre-wrap;
}

.ps-event-row__error {
    margin-top: var(--space-1);
}

.ps-event-row__error-msg {
    color: var(--color-error-fg);
    font-weight: var(--font-weight-semibold);
}

.ps-event-row__stack {
    margin: var(--space-1) 0 0 0;
    padding: var(--space-2);
    max-height: 200px;
    overflow: auto;
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-sm);
    color: var(--color-error-fg);
    font-family: 'SFMono-Regular', Consolas, monospace;
    font-size: var(--font-size-xs);
    white-space: pre-wrap;
}

.ps-event-row__duration {
    color: var(--color-ink-muted);
    min-width: 5rem;
    text-align: right;
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Execution tree (instance hierarchy with grouped node events).
 * ────────────────────────────────────────────────────────────────── */
.ps-tree-view__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.ps-tree-view__title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-tree-view__scroll {
    max-height: 700px;
    overflow-y: auto;
}

.ps-tree-node {
    display: flex;
    flex-direction: column;
}

.ps-tree-node--nested {
    border-left: 1px solid var(--color-border-subtle);
}

.ps-tree-node__header {
    padding: var(--space-2) var(--space-3);
    padding-left: calc(var(--space-3) + 1.5rem * var(--ps-tree-depth, 0));
    cursor: pointer;
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-tree-node__header:hover {
    background-color: var(--color-surface-sunken);
}

.ps-tree-node__row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-tree-node__chev {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    width: 0.75rem;
}

.ps-tree-node__name {
    font-weight: var(--font-weight-semibold);
}

.ps-tree-node__muted {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-tree-node__muted-icon {
    color: var(--color-ink-muted);
}

.ps-tree-node__success-icon { color: var(--color-success-fg); }
.ps-tree-node__error-icon   { color: var(--color-error-fg); }
.ps-tree-node__pending-icon { color: var(--color-pending-fg); }

.ps-tree-node__body {
    padding-left: calc(1.5rem * var(--ps-tree-depth, 0));
}

.ps-tree-node__empty {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-tree-node__flow {
    display: flex;
    flex-direction: column;
}

.ps-tree-node__event {
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-tree-node__node-header {
    display: grid;
    grid-template-columns: 70px 1fr auto auto 100px;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    padding-left: calc(2.5rem + 1.5rem * var(--ps-tree-depth, 0));
    font-size: var(--font-size-sm);
}

.ps-tree-node__node-header--error {
    background-color: var(--color-error-bg-soft);
}

.ps-tree-node__task-link {
    margin-left: var(--space-1);
    text-decoration: none;
}

.ps-tree-node__duration,
.ps-tree-node__time {
    text-align: right;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-tree-node__detail {
    padding: var(--space-1) var(--space-3);
    padding-left: calc(6.5rem + 1.5rem * var(--ps-tree-depth, 0));
    background-color: var(--color-surface-sunken);
    border-top: 1px solid var(--color-border-subtle);
    font-size: var(--font-size-sm);
}

.ps-tree-node__detail--error {
    background-color: var(--color-error-bg-soft);
}

.ps-tree-node__detail-tail {
    margin-left: var(--space-2);
}

.ps-tree-node__output-text {
    white-space: pre-wrap;
}

.ps-tree-node__error-text {
    color: var(--color-error-fg);
}


/* ──────────────────────────────────────────────────────────────────
 * Execution Trace (tabbed view with flow / console / inputs / outputs).
 * ────────────────────────────────────────────────────────────────── */
.ps-trace {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-trace__content {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-trace__flow {
    max-height: 500px;
    overflow-y: auto;
}

.ps-trace__event {
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-trace__node-header {
    display: grid;
    grid-template-columns: 70px 1fr auto auto 110px;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    font-size: var(--font-size-sm);
}

.ps-trace__node-header--error {
    background-color: var(--color-error-bg-soft);
}

.ps-trace__name {
    font-weight: var(--font-weight-semibold);
}

.ps-trace__name-link {
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
}

.ps-trace__task-link {
    margin-left: var(--space-1);
    text-decoration: none;
}

.ps-trace__muted {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-left: var(--space-1);
}

.ps-trace__muted-italic {
    color: var(--color-ink-muted);
    font-style: italic;
}

.ps-trace__muted-icon  { color: var(--color-ink-muted); }
.ps-trace__success-icon { color: var(--color-success-fg); }
.ps-trace__error-icon   { color: var(--color-error-fg); }
.ps-trace__pending-icon { color: var(--color-pending-fg); }

.ps-trace__error-text {
    color: var(--color-error-fg);
}

.ps-trace__required {
    color: var(--color-error-fg);
}

.ps-trace__duration,
.ps-trace__time {
    text-align: right;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-trace__detail {
    padding: var(--space-1) var(--space-3);
    padding-left: 6.125rem;
    background-color: var(--color-surface-sunken);
    border-top: 1px solid var(--color-border-subtle);
    font-size: var(--font-size-sm);
}

.ps-trace__detail--error {
    background-color: var(--color-error-bg-soft);
}

.ps-trace__detail-tail {
    margin-left: var(--space-2);
}

.ps-trace__stack {
    margin: var(--space-2) 0 0 0;
    padding: var(--space-2);
    max-height: 320px;
    overflow: auto;
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    color: var(--color-error-fg);
    font-family: 'SFMono-Regular', Consolas, monospace;
    font-size: var(--font-size-xs);
    white-space: pre-wrap;
    word-break: break-word;
}

.ps-trace__instance-event {
    display: grid;
    grid-template-columns: 70px 1fr auto 110px;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-border-subtle);
    font-size: var(--font-size-sm);
}

.ps-trace__console {
    max-height: 500px;
    overflow-y: auto;
    padding: var(--space-3);
    background-color: var(--color-surface-sunken);
    font-family: 'SFMono-Regular', Consolas, monospace;
    font-size: var(--font-size-sm);
}

.ps-trace__console-line {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.ps-trace__console-line--error   { color: var(--color-error-fg); }
.ps-trace__console-line--warning { color: var(--color-pending-fg); }
.ps-trace__console-line--muted   { color: var(--color-ink-muted); }
.ps-trace__console-line--default { color: inherit; }

.ps-trace__col-name { width: 25%; }
.ps-trace__col-type { width: 15%; }

.ps-schedule-detail__inputs {
    margin: 0;
    padding: var(--space-2);
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
    white-space: pre-wrap;
}


/* ──────────────────────────────────────────────────────────────────
 * Task detail page chrome. Only used by /tasks/{id}.
 * ────────────────────────────────────────────────────────────────── */
.ps-task-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-task-detail__dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-task-detail__dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-task-detail__dl > dd {
    margin: 0;
}

.ps-task-detail__muted {
    color: var(--color-ink-muted);
}

.ps-task-detail__assignments-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.ps-task-detail__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-task-detail__form-alert {
    margin-bottom: var(--space-3);
}

.ps-task-detail__comments {
    margin: 0;
    white-space: pre-wrap;
}

.ps-task-detail__toast {
    position: fixed;
    bottom: var(--space-3);
    right: var(--space-3);
    z-index: var(--z-index-toast, 1080);
    max-width: 28rem;
}


/* ──────────────────────────────────────────────────────────────────
 * Application detail page chrome. Only used by /applications/{id}.
 * ────────────────────────────────────────────────────────────────── */
.ps-application-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-application-detail__actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-application-detail__header-meta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: calc(-1 * var(--space-2));
    color: var(--color-ink-muted);
}

.ps-application-detail__muted {
    color: var(--color-ink-muted);
}

.ps-application-detail__input-tokens {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}

.ps-application-detail__history-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.ps-application-detail__file-row--clickable {
    cursor: pointer;
}

.ps-application-detail__file-cell {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-application-detail__col-size {
    text-align: right;
    width: 7.5rem;
}

.ps-application-detail__file-preview {
    border-top: 1px solid var(--color-border-subtle);
}

.ps-application-detail__file-content {
    margin: 0;
    padding: var(--space-3);
    max-height: 400px;
    overflow: auto;
    font-size: var(--font-size-sm);
    background-color: var(--color-surface-sunken);
    white-space: pre-wrap;
}

.ps-application-detail__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}



/* ──────────────────────────────────────────────────────────────────
 * Orchestration detail page chrome. Only used by /orchestrations/{id}.
 * ────────────────────────────────────────────────────────────────── */
.ps-orchestration-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-orchestration-detail__header-meta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: calc(-1 * var(--space-2));
}

.ps-orchestration-detail__dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-orchestration-detail__dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-orchestration-detail__dl > dd {
    margin: 0;
}

.ps-orchestration-detail__source-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.ps-orchestration-detail__source {
    margin: 0;
    padding: var(--space-3);
    max-height: 500px;
    overflow: auto;
    background-color: var(--color-surface-sunken);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
    white-space: pre-wrap;
}

.ps-orchestration-detail__executions-toolbar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}



/* ──────────────────────────────────────────────────────────────────
 * File reference chrome. Used by FileReferenceDisplay,
 * FileReferenceListDisplay, and FilePreviewCard under
 * Components/Common/Files/.
 * ────────────────────────────────────────────────────────────────── */
.ps-file-ref {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
}

.ps-file-ref__icon {
    color: var(--color-ink-muted);
    flex-shrink: 0;
}

.ps-file-ref__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 12.5rem;
}

.ps-file-ref__size {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    flex-shrink: 0;
}

.ps-file-ref--chip {
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    background-color: var(--color-surface-sunken);
}

.ps-file-ref-list__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.ps-file-ref-list__type,
.ps-file-ref-list__size-col {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-file-ref-list__size-col {
    text-align: right;
}

.ps-file-preview {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
}

.ps-file-preview__icon {
    font-size: var(--font-size-2xl);
    color: var(--color-ink-muted);
    flex-shrink: 0;
}

.ps-file-preview__body {
    flex: 1 1 auto;
    min-width: 0;
}

.ps-file-preview__title {
    margin: 0 0 var(--space-1) 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-file-preview__meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-bottom: var(--space-2);
}

.ps-file-preview__sep {
    margin: 0 var(--space-1);
}

.ps-file-preview__image-wrap {
    margin-bottom: var(--space-2);
}

.ps-file-preview__image {
    max-height: 18.75rem;
    max-width: 100%;
    border-radius: var(--radius-sm);
    display: block;
}

.ps-file-preview__actions {
    display: flex;
    gap: var(--space-2);
}


/* ──────────────────────────────────────────────────────────────────
 * Settings/Account page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-settings-account {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-settings-account__sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-settings-account__section-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-settings-account__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: var(--space-3);
}

.ps-settings-account__static {
    display: flex;
    align-items: center;
    min-height: 2rem;
    gap: var(--space-2);
}

.ps-settings-account__plan-name {
    font-weight: var(--font-weight-medium);
}

.ps-settings-account__empty {
    margin: 0;
    color: var(--color-ink-muted);
}

.ps-settings-account__tenant-name {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-settings-account__trial-alert {
    margin-top: var(--space-3);
}

.ps-settings-account__disclosure {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    background: none;
    border: 0;
    padding: 0;
    color: var(--color-ink-muted);
    cursor: pointer;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-settings-account__disclosure:hover {
    color: var(--color-ink);
}


/* ──────────────────────────────────────────────────────────────────
 * Settings/Appearance page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-settings-appearance {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-settings-appearance__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--space-3);
}

.ps-settings-appearance__section-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-settings-appearance__toast {
    position: fixed;
    bottom: var(--space-3);
    right: var(--space-3);
    z-index: var(--z-index-toast, 1080);
    max-width: 28rem;
}


/* ──────────────────────────────────────────────────────────────────
 * Settings/Passkeys page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-settings-passkeys {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-settings-passkeys__sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-settings-passkeys__section-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-settings-passkeys__status {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.ps-settings-passkeys__status-icon {
    font-size: 2rem;
    flex-shrink: 0;
}

.ps-settings-passkeys__status-icon--ok {
    color: var(--color-success-fg);
}

.ps-settings-passkeys__status-icon--warn {
    color: var(--color-pending-fg);
}

.ps-settings-passkeys__status-body {
    flex: 1 1 auto;
}

.ps-settings-passkeys__status-meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-settings-passkeys__actions {
    display: flex;
}

.ps-settings-passkeys__list {
    display: flex;
    flex-direction: column;
}

.ps-settings-passkeys__row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) 0;
}

.ps-settings-passkeys__row--bordered {
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-settings-passkeys__row-icon {
    font-size: 1.5rem;
    color: var(--color-ink-muted);
    margin-top: 0.125rem;
    flex-shrink: 0;
}

.ps-settings-passkeys__row-body {
    flex: 1 1 auto;
    min-width: 0;
}

.ps-settings-passkeys__row-meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    margin-top: var(--space-1);
}

.ps-settings-passkeys__row-meta-sep {
    margin: 0 var(--space-1);
}

.ps-settings-passkeys__row-actions {
    display: inline-flex;
    gap: var(--space-1);
    flex-shrink: 0;
}

.ps-settings-passkeys__rename {
    display: inline-flex;
    gap: var(--space-1);
    align-items: center;
    max-width: 18.75rem;
}

.ps-settings-passkeys__confirm-meta {
    color: var(--color-ink-muted);
    margin-bottom: 0;
}


/* ──────────────────────────────────────────────────────────────────
 * Settings/ApiKeyDetails page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-api-key-details {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-api-key-details__sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-api-key-details__section-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-api-key-details__permissions-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.ps-api-key-details__dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-sm);
}

.ps-api-key-details__dl > dt {
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-api-key-details__dl > dd {
    margin: 0;
}

.ps-api-key-details__alert-list {
    margin: var(--space-1) 0 0 0;
    padding-left: var(--space-4);
}


/* ──────────────────────────────────────────────────────────────────
 * Admin/Index page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-admin-index {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-admin-index__sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.ps-admin-index__stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-3);
}

.ps-admin-index__stat-label {
    margin: 0 0 var(--space-2) 0;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-ink-muted);
}

.ps-admin-index__stat-value {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-tight);
}

.ps-admin-index__stat-status {
    display: flex;
    align-items: center;
}

.ps-admin-index__actions-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--space-3);
}

.ps-admin-index__section-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-admin-index__action-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}


/* ──────────────────────────────────────────────────────────────────
 * Admin/Impersonate page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-impersonate {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-impersonate__sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-impersonate__confirm-header {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-pending-fg);
}

.ps-impersonate__confirm-icon {
    font-size: var(--font-size-lg);
}

.ps-impersonate__confirm-meta {
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}


/* ──────────────────────────────────────────────────────────────────
 * Admin/Query page chrome.
 * ────────────────────────────────────────────────────────────────── */
.ps-admin-query {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-admin-query__sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-admin-query__section-title {
    margin: 0;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.ps-admin-query__execute {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--space-3);
}


/* ──────────────────────────────────────────────────────────────────
 * Login page chrome. Standalone surface — full-viewport layout, no
 * shared MainLayout. Rendered statically (no Blazor circuit) by
 * Components/Pages/Login.razor via RazorComponentResult.
 * ────────────────────────────────────────────────────────────────── */
.ps-login-page {
    margin: 0;
    min-height: 100vh;
    background-color: var(--color-surface-sunken);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
}

.ps-login-page__container {
    width: 100%;
    max-width: 25rem;
}

.ps-login-page__card {
    width: 100%;
}

.ps-login-page__header {
    text-align: center;
}

.ps-login-page__title {
    margin: 0;
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-tight);
}

.ps-login-page__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ps-login-page__error,
.ps-login-page__sent {
    margin-bottom: var(--space-3);
}

.ps-login-page__retry {
    margin-top: var(--space-2);
}

.ps-login-page__divider {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-3) 0;
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

.ps-login-page__divider::before,
.ps-login-page__divider::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background-color: var(--color-border-subtle);
}

.ps-login-page__status {
    margin-top: var(--space-2);
    color: var(--color-error-fg);
    font-size: var(--font-size-sm);
    min-height: 1.25rem;
}


/* ──────────────────────────────────────────────────────────────────
 * Companion drawer (Phase 2.2c) — inline right-side panel that
 * lives next to @Body in AppShell. Two states: collapsed (36 px
 * vertical strip with a rotated label) and expanded (320 px panel
 * with a header + body + composer).
 *
 * Stable element types — toggle .ps-companion-drawer--expanded /
 * --collapsed on the same <aside>; never swap tag types.
 * ────────────────────────────────────────────────────────────────── */
.ps-companion-drawer {
    flex-shrink: 0;
    height: 100%;
    min-height: 0;
    border-left: 1px solid var(--color-border);
    background-color: var(--color-surface-raised);
    display: flex;
    flex-direction: column;
    transition: width var(--duration-base) var(--easing-standard);
}

.ps-companion-drawer--expanded {
    width: 320px;
}

.ps-companion-drawer--collapsed {
    width: 36px;
}

.ps-companion-drawer__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-border-subtle);
    min-height: 44px;
}

.ps-companion-drawer__title {
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-base);
    color: var(--color-ink);
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Right-align the header icon buttons (search + collapse) — title
 * fills remaining space, actions cluster on the right. */
.ps-companion-drawer__header-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    flex: 0 0 auto;
}

.ps-companion-drawer__toggle {
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--color-ink-muted);
    cursor: pointer;
    border-radius: var(--radius-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.ps-companion-drawer__toggle:hover {
    background-color: var(--color-border-subtle);
    color: var(--color-ink);
}

.ps-companion-drawer__toggle:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-companion-drawer__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--space-4);
    min-height: 0;
}

/* Drawer's message list is just a flex column — the bubbles themselves
 * are rendered by the shared <ChatMessageBubble> component using the
 * ps-chat-msg* styles (right-alignment for user, surface-sunken for
 * agent, markdown rendering, attachment chips, …). Keeping the layout
 * styles minimal here avoids a parallel implementation. */
.ps-companion-drawer__messages {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* Transient activity status under the messages (Thinking… / Using ToolName…).
 * Muted so it reads as status rather than as a message. */
.ps-companion-drawer__activity {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
    font-style: italic;
}

.ps-companion-drawer__composer {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--color-border-subtle);
}

.ps-companion-drawer__input {
    flex: 1 1 auto;
    min-width: 0;
}

/* Collapsed state: a single full-height button with a rotated
 * label so the affordance still reads as "open the concierge". */
.ps-companion-drawer__rail {
    flex: 1 1 auto;
    width: 100%;
    background-color: transparent;
    border: none;
    cursor: pointer;
    color: var(--color-ink-muted);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) 0;
}

.ps-companion-drawer__rail:hover {
    background-color: var(--color-border-subtle);
    color: var(--color-ink);
}

.ps-companion-drawer__rail:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: -2px;
}

.ps-companion-drawer__rail-icon {
    font-size: 0.875rem;
    line-height: 1;
}

.ps-companion-drawer__rail-label {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* ──────────────────────────────────────────────────────────────────────────
 * Chat-history-scalability Phase 6 (#423) — drawer scroll/search affordances.
 *
 * .ps-companion-drawer__icon-btn       shared header icon button
 * .ps-companion-drawer__search         search row container (stable element;
 *                                      --visible modifier toggles visibility)
 * .ps-companion-drawer__loading-older  prepend-in-flight sentinel at the
 *                                      top of the message list
 * .ps-companion-drawer__session-divider thin separator between bubbles whose
 *                                      Phase 2 SessionId differs
 * .ps-companion-drawer__msg-wrap       data-message-id anchor wrapper (used
 *                                      by the JS scroll-anchor helpers)
 * ────────────────────────────────────────────────────────────────────────── */

.ps-companion-drawer__icon-btn {
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--color-ink-muted);
    cursor: pointer;
    border-radius: var(--radius-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.ps-companion-drawer__icon-btn:hover {
    background-color: var(--color-border-subtle);
    color: var(--color-ink);
}

.ps-companion-drawer__icon-btn:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-companion-drawer__icon-btn[aria-pressed="true"] {
    color: var(--color-ink);
    background-color: var(--color-border-subtle);
}

.ps-companion-drawer__search {
    display: none;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-border-subtle);
}

.ps-companion-drawer__search--visible {
    display: flex;
}

.ps-companion-drawer__search-input {
    flex: 1 1 auto;
    min-width: 0;
}

.ps-companion-drawer__search-clear {
    flex: 0 0 auto;
}

.ps-companion-drawer__search-hits {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.ps-companion-drawer__search-hit {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.ps-companion-drawer__search-hit:hover {
    background-color: var(--color-surface-sunken);
}

.ps-companion-drawer__search-hit:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-companion-drawer__search-hit-meta {
    display: flex;
    justify-content: space-between;
    gap: var(--space-2);
    font-size: var(--font-size-xs);
    color: var(--color-ink-muted);
    text-transform: capitalize;
}

.ps-companion-drawer__search-hit-snippet {
    font-size: var(--text-sm);
    color: var(--color-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.ps-companion-drawer__search-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-6) var(--space-3);
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
}

/* Loading-older sentinel: stable container so the renderer doesn't
 * swap element types at this slot when toggling between idle and
 * in-flight (avoids the Blazor insertBefore-null crash). Visibility
 * is driven by the --visible modifier rather than @if/else. */
.ps-companion-drawer__loading-older {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 0;
    height: 0;
    overflow: hidden;
    opacity: 0;
    transition: height 120ms ease, opacity 120ms ease, padding 120ms ease;
    padding: 0;
}

.ps-companion-drawer__loading-older--visible {
    height: 32px;
    opacity: 1;
    padding: var(--space-1) 0;
}

.ps-companion-drawer__session-divider {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: var(--space-3) 0;
    border-top: 1px solid var(--color-border-subtle);
    position: relative;
}

.ps-companion-drawer__session-divider-label {
    position: relative;
    top: -0.55rem;
    padding: 0 var(--space-2);
    background-color: var(--color-surface);
    font-size: var(--font-size-xs);
    color: var(--color-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ps-companion-drawer__msg-wrap {
    display: contents;
}

.ps-companion-drawer__msg-wrap--highlight > * {
    animation: ps-companion-drawer-flash 1.4s ease-out;
}

@keyframes ps-companion-drawer-flash {
    0%   { box-shadow: 0 0 0 2px var(--color-focus-ring); }
    100% { box-shadow: 0 0 0 0 transparent; }
}


/* ──────────────────────────────────────────────────────────────────
 * ActionCard (Phase 4a, issue #447)
 *
 * Shared work-unit card used across the Manage tree detail pane,
 * Tasks Kanban, chat-composer palette items, and any future
 * actionable-entity surface. Tone moves the left-edge accent only;
 * the surface stays neutral so a board of mixed tones reads calmly.
 * ────────────────────────────────────────────────────────────────── */

.ps-action-card {
    /* Grid: optional select/icon column · body column. The accent
       is positioned absolutely so it can extend the full card height
       without disturbing the grid metrics. */
    position: relative;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-3);
    align-items: start;

    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    color: var(--color-ink);
    padding: var(--space-3) var(--space-4);
    padding-left: calc(var(--space-4) + 3px); /* room for accent strip */

    transition: border-color 0.12s ease,
                box-shadow 0.12s ease,
                background-color 0.12s ease;
}

.ps-action-card--compact {
    padding: var(--space-2) var(--space-3);
    padding-left: calc(var(--space-3) + 3px);
    gap: var(--space-2);
}

.ps-action-card--detailed {
    padding: var(--space-4);
    padding-left: calc(var(--space-4) + 3px);
}

.ps-action-card--clickable {
    cursor: pointer;
}

.ps-action-card--clickable:hover {
    border-color: var(--color-border-strong);
    background-color: var(--color-surface);
}

.ps-action-card--clickable:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.ps-action-card--selected {
    border-color: var(--color-brand);
    box-shadow: 0 0 0 1px var(--color-brand) inset;
}

.ps-action-card--disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* Tone accent — left-edge strip. Absolutely positioned so the card's
   own border radius clips it cleanly at the top/bottom corners. */
.ps-action-card__accent {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 3px;
    border-top-left-radius: var(--radius-md);
    border-bottom-left-radius: var(--radius-md);
    background-color: transparent;
}

.ps-action-card--neutral .ps-action-card__accent { background-color: var(--color-border-strong); }
.ps-action-card--info    .ps-action-card__accent { background-color: var(--color-running-fg); }
.ps-action-card--success .ps-action-card__accent { background-color: var(--color-success-fg); }
.ps-action-card--warning .ps-action-card__accent { background-color: var(--color-pending-fg); }
.ps-action-card--danger  .ps-action-card__accent { background-color: var(--color-error-fg); }
.ps-action-card--brand   .ps-action-card__accent { background-color: var(--color-brand); }

.ps-action-card__select {
    /* Aligns the checkbox vertically with the title baseline without
       reflowing on tag-row presence. */
    display: flex;
    align-items: center;
    padding-top: var(--space-1);
}

.ps-action-card__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    color: var(--color-ink-muted);
    font-size: var(--font-size-lg);
    padding-top: 2px;
}

.ps-action-card--compact .ps-action-card__icon {
    width: 1.25rem;
    height: 1.25rem;
    font-size: var(--font-size-base);
}

.ps-action-card__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}

.ps-action-card--compact .ps-action-card__body {
    gap: var(--space-1);
}

.ps-action-card__title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    min-width: 0;
}

.ps-action-card__title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--color-ink);
    line-height: var(--line-height-tight);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1 1 auto;
}

.ps-action-card--detailed .ps-action-card__title {
    font-size: var(--font-size-lg);
}

.ps-action-card__kebab {
    flex: 0 0 auto;
    display: inline-flex;
}

.ps-action-card__meta-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
}

.ps-action-card__meta {
    /* Truncation budget shared with tags via flex-shrink. */
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-action-card__tags {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    flex: 0 1 auto;
}

.ps-action-card__actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-1);
}

/* Compact density hides both meta and action rows so cards collapse
   to a single line; the host is expected to expose actions through
   the kebab when density is Compact. */
.ps-action-card--compact .ps-action-card__meta-row,
.ps-action-card--compact .ps-action-card__actions {
    display: none;
}


/* ──────────────────────────────────────────────────────────────────
 * Tree (#559 round-2)
 *
 * Hierarchical row list with disclosure chevrons, selection state,
 * keyboard nav, and ARIA tree-pattern roles. Driven by a data model
 * (consumer supplies KeySelector / ChildrenSelector / LabelSelector
 * / IconSelector + optional ItemTemplate). The primitive owns all
 * chrome — chevron rotation, indent guides, selection tint, focus
 * ring, hover.
 *
 * Variant chosen because trees are typically data-shaped (file
 * bundles, navigation maps) at unknown depth; declarative <TreeNode>
 * children would force consumers to expand recursion manually.
 * ──────────────────────────────────────────────────────────────── */

.ps-tree {
    display: flex;
    flex-direction: column;
    background: var(--color-surface);
    padding: var(--space-2) 0;
    font-family: var(--font-family-ui);
    color: var(--color-ink);
}

.ps-tree__empty {
    padding: var(--space-3) var(--space-4);
    color: var(--color-ink-muted);
    font-size: var(--font-size-sm);
}

/* A tree row is a flex line of disclosure + icon + label + trailing
   slot. Padding-left scales with --ps-tree-depth so indentation tracks
   nesting without nested <ul>s. Stable element type — every row is a
   <div role="treeitem"> regardless of selected / folder / leaf state
   (feedback_blazor_element_type_swaps). */
.ps-tree__row {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    /* Roomier vertical rhythm than the 6a hand-rolled tree. */
    padding: var(--space-2) var(--space-3);
    padding-left: calc(var(--space-3) + (var(--ps-tree-depth, 0) * 1rem));
    background: transparent;
    color: var(--color-ink);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
    text-align: left;
    cursor: pointer;
    border: 0;
    border-radius: var(--radius-sm);
    transition: background-color var(--duration-fast) var(--easing-standard),
                color var(--duration-fast) var(--easing-standard);
    user-select: none;
    outline: none;
}

/* Indent guide — a thin vertical line just to the left of the row's
   content column, drawn via a ::before pseudo-element. Only renders
   for rows at depth ≥ 1 (depth 0 has no parent — no guide needed).
   Position scales with --ps-tree-depth so guides line up with their
   parent folder's chevron column. */
.ps-tree__row::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--space-3) + (var(--ps-tree-depth, 0) * 1rem) - 0.5rem);
    width: 1px;
    background: var(--color-border-subtle);
    pointer-events: none;
}
/* Depth 0 — the line would land in the left padding area with no
   parent above it; suppress it for a clean root edge. */
.ps-tree__row[aria-level="1"]::before {
    display: none;
}

.ps-tree__row:hover {
    background-color: var(--color-surface-raised);
}

.ps-tree__row:focus-visible {
    box-shadow: 0 0 0 2px var(--color-focus-ring) inset;
}

.ps-tree__row--selected {
    background-color: var(--color-brand-soft);
    color: var(--color-brand);
    font-weight: var(--font-weight-medium);
}

.ps-tree__row--selected:hover {
    background-color: var(--color-brand-soft);
}

.ps-tree__row--selected .ps-tree__icon,
.ps-tree__row--selected .ps-tree__disclosure {
    color: var(--color-brand);
}

/* Disclosure column is fixed-width so leaves and folders align on
   the same icon baseline. */
.ps-tree__disclosure {
    flex: 0 0 auto;
    width: 0.875rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-ink-muted);
}

.ps-tree__chevron {
    font-size: 0.75rem;
    transition: transform var(--duration-fast) var(--easing-standard);
}

/* Leaves keep the chevron <i> in the DOM (stable element types — see
   Tree.razor comment) but render it invisible. */
.ps-tree__chevron--hidden {
    visibility: hidden;
}

/* Chevron rotation is the disclosure state — CSS transform, no
   icon-class swap (feedback_blazor_element_type_swaps). */
.ps-tree__row--open > .ps-tree__disclosure > .ps-tree__chevron {
    transform: rotate(90deg);
}

.ps-tree__icon {
    flex: 0 0 auto;
    width: 1rem;
    text-align: center;
    color: var(--color-ink-muted);
}

.ps-tree__label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ps-tree__content {
    flex: 1;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.ps-tree__trailing {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

/* Disabled state — only fired when a consumer encodes it via the
   trailing slot or a wrapping container, but we expose the class so
   future opt-in is straightforward without a re-style. */
.ps-tree__row[aria-disabled="true"] {
    cursor: not-allowed;
    opacity: 0.55;
    pointer-events: none;
}


