/* ===================================================================
   Sharp Architect — design system + app shell
   =================================================================== */
:root {
    --brand-darkest: #243d29;
    --brand-dark: #2f5236;
    --brand: #4f8a47;
    --brand-light: #7ba85f;
    --brand-pale: #eef4e9;
    --ink: #1c2620;
    --muted: #5f6b63;
    --surface: #ffffff;
    --surface-2: #f4f6f3;
    --border: #dde3dc;
    --danger: #b5341f;
    --warn: #8a6011;
    --warn-bg: #fbf1da;
    --warn-border: #ecd9a8;
    --warn-bg-soft: #fdf8ec;
    --ok: #2f7d32;
    --info: #2f6dac;
    --header-h: 56px;
    --radius: 8px;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .06);
    --shadow: 0 4px 14px rgba(0, 0, 0, .10);
}

* {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    height: 100%;
}

body {
    display: flex;
    flex-direction: column;
    font-family: 'Segoe UI', system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
    color: var(--ink);
    background: var(--surface-2);
    font-size: 14px;
    line-height: 1.5;
}

a {
    color: var(--brand-dark);
}

/* ---- Top header --------------------------------------------------- */
.app-header {
    flex: 0 0 var(--header-h);
    height: var(--header-h);
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0 1rem;
    background: linear-gradient(90deg, var(--brand-darkest), var(--brand-dark));
    color: #fff;
    box-shadow: var(--shadow-sm);
}

.app-brand {
    display: flex;
    align-items: center;
    gap: .6rem;
    text-decoration: none;
    color: #fff;
}

.app-logo {
    height: 34px;
    width: 34px;
    object-fit: contain;
}

.app-title {
    font-size: 1.15rem;
    font-weight: 600;
    letter-spacing: .2px;
}

.app-header-spacer {
    flex: 1 1 auto;
}

.app-header-actions {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.app-user {
    max-width: 16rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    opacity: .9;
}

.app-header .app-link {
    color: #e7efe2;
    text-decoration: none;
    font-size: .9rem;
}

.app-header .app-link:hover {
    text-decoration: underline;
}

.app-admin-btn {
    display: inline-flex;
    align-items: center;
    padding: .35rem .9rem;
    border: 1px solid rgba(255, 255, 255, .55);
    border-radius: 999px;
    color: #fff;
    text-decoration: none;
    font-weight: 600;
    font-size: .85rem;
    transition: background .12s ease;
}

.app-admin-btn:hover {
    background: rgba(255, 255, 255, .15);
}

/* Narrow viewports: the fixed 1rem gaps + the user-name span pushed the header past the edge
   at 390px. Drop the (title-attribute-preserved) user name first (≤640px), then the "Sign out"
   link (≤480px) — WO-025 inverts the old priority so the wordmark survives on phones. */
@media (max-width: 640px) {
    .app-header { gap: .5rem; padding: 0 .6rem; }
    .app-header-actions { gap: .5rem; }
    .app-user { display: none; }

    /* Mobile: let the header scroll away with the page instead of staying pinned.
       Desktop keeps its fixed-height, inner-pane-scroll shell (sticky header) untouched.
       The shell is normally a viewport-locked flex column (`html, body { height: 100% }`)
       with scrolling trapped inside `.hud-center`; on phones we relax that so the whole
       document scrolls and the un-pinned header flows off-screen with the content. */
    html, body { height: auto; }
    /* WO-014: the header un-pins and scrolls off with the page. WO-019: use `relative` instead of
       `static` — visually identical (still in normal flow, still scrolls away), but it lets the
       header carry a z-index so it paints ABOVE the off-canvas nav backdrop while on-screen, keeping
       it fully visible/un-dimmed when an overlay is open (see the WO-016/WO-019 off-canvas block). */
    .app-header { position: relative; z-index: 305; }
    .hud { height: auto; }
}
@media (max-width: 480px) {
    /* WO-025: keep the "Sharp Architect" wordmark visible on phones (this rule used to hide
       `.app-title`) and hide the header "Sign out" link instead — signing out is not a mobile
       flow, and the title reads as a proper title bar next to the logo. Scoped to /signout so
       the NotAuthorized "Sign in" link (same `.app-link` class) stays reachable on phones. */
    .app-header .app-link[href="/signout"] { display: none; }
}

.app-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
}

/* ---- Buttons (shared) --------------------------------------------- */
.sa-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    padding: .45rem .9rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
    font-weight: 500;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease, box-shadow .12s ease;
}

.sa-btn:hover:not(:disabled) {
    background: var(--surface-2);
    border-color: #c4ccc1;
}

.sa-btn:disabled {
    opacity: .5;
    cursor: not-allowed;
}

.sa-btn-primary {
    background: var(--brand);
    border-color: var(--brand);
    color: #fff;
}

.sa-btn-primary:hover:not(:disabled) {
    background: var(--brand-dark);
    border-color: var(--brand-dark);
}

.sa-btn-sm {
    padding: .3rem .6rem;
    font-size: .8rem;
}

.sa-btn-ghost {
    background: transparent;
    border-color: transparent;
}

.sa-btn-ghost:hover:not(:disabled) {
    background: var(--surface-2);
}

.sa-btn-danger {
    background: #b3261e;
    border-color: #b3261e;
    color: #fff;
}

.sa-btn-danger:hover:not(:disabled) {
    background: #8c1d18;
    border-color: #8c1d18;
}

/* ---- Form controls (shared) --------------------------------------- */
.sa-field {
    display: flex;
    flex-direction: column;
    gap: .3rem;
    margin-bottom: .9rem;
}

.sa-field > label {
    font-weight: 600;
    font-size: .82rem;
    color: var(--muted);
}

.sa-input, .sa-textarea, .sa-select {
    width: 100%;
    padding: .5rem .65rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    font: inherit;
    color: var(--ink);
}

.sa-input:focus, .sa-textarea:focus, .sa-select:focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 3px rgba(79, 138, 71, .15);
}

.sa-textarea {
    min-height: 7rem;
    /* Auto-grow (WO-019) sizes the box to its content; max-height caps it so a long
       entry scrolls internally instead of pushing the page off-screen. */
    max-height: 24rem;
    resize: none;
}

/* ---- Filter-select combobox (WO-008) ------------------------------ */
.filter-select {
    position: relative;
}

.filter-select-list {
    position: absolute;
    z-index: 20;
    left: 0;
    right: 0;
    top: calc(100% + 2px);
    margin: 0;
    padding: .25rem;
    list-style: none;
    max-height: 15rem;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
}

.filter-select-option {
    padding: .4rem .55rem;
    border-radius: var(--radius);
    cursor: pointer;
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.filter-select-option:hover,
.filter-select-option.highlight {
    background: var(--surface-2);
}

.filter-select-option.selected {
    font-weight: 600;
}

.filter-select-empty {
    padding: .4rem .55rem;
    color: var(--muted);
    font-size: .85rem;
}

/* ---- Badges / chips ----------------------------------------------- */
.sa-chip {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .12rem .5rem;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    font-size: .75rem;
    color: var(--muted);
    white-space: nowrap;
}

/* PBI-006: possible-duplicate merge prompt + merge panel */
.sa-chip-warn {
    cursor: pointer;
    color: var(--danger);
    background: #fae3df;
    border-color: #eebcb3;
}

.merge-panel {
    margin-top: .5rem;
    padding: .6rem .75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-2);
    display: flex;
    flex-direction: column;
    gap: .5rem;
    align-items: flex-start;
    max-width: 40rem;
}

.merge-note { margin: 0; font-size: .85rem; }

.merge-warn {
    display: block;
    margin-top: .25rem;
    color: var(--danger);
    font-weight: 600;
}

.sa-badge {
    display: inline-flex;
    align-items: center;
    padding: .12rem .55rem;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .3px;
    background: var(--surface-2);
    color: var(--muted);
}

.status-selected, .status-investigating, .status-working, .status-readyforreview {
    background: #e6f0fb;
    color: var(--info);
}

.status-awaitinginput {
    background: var(--warn-bg);
    color: var(--warn);
}

.status-done {
    background: #e4f3e5;
    color: var(--ok);
}

.status-failed, .status-cancelled {
    background: #fae3df;
    color: var(--danger);
}

.status-terminated {
    background: #ececec;
    color: var(--muted);
}

.screen-head-actions {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex: 0 0 auto;
}

.dot {
    width: .55rem;
    height: .55rem;
    border-radius: 50%;
    display: inline-block;
    background: #bcc4bb;
}

.dot.online {
    background: var(--ok);
}

.dot.offline {
    background: var(--danger);
}

/* ===================================================================
   HUD — three-panel shell
   =================================================================== */
.hud {
    display: flex;
    width: 100%;
    height: 100%;
    min-height: 0;
}

/* ---- Left panel (main menu) --------------------------------------- */
.hud-left {
    flex: 0 0 264px;
    width: 264px;
    background: var(--surface);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    min-height: 0;
    transition: flex-basis .15s ease, width .15s ease;
}

.hud-left.collapsed {
    flex-basis: 60px;
    width: 60px;
}

.hud-left-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: .6rem .75rem;
    border-bottom: 1px solid var(--border);
}

.hud-left-head .hud-left-caption {
    font-weight: 700;
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .5px;
    color: var(--muted);
}

.hud-left.collapsed .hud-left-caption {
    display: none;
}

.hud-collapse-btn {
    border: none;
    background: transparent;
    cursor: pointer;
    font-size: 1rem;
    color: var(--muted);
    padding: .25rem .4rem;
    border-radius: 6px;
}

.hud-collapse-btn:hover {
    background: var(--surface-2);
}

/* WO-016: phone-only hamburger + off-canvas backdrop for the FleetNav rail. Hidden above the
   phone breakpoint (see the max-width: 640px block below), so on desktop/tablet neither element
   is rendered visibly and the rail stays the in-flow .hud-left. Kept out of any @media here so
   the "not shown on desktop" default is explicit and any leftover open/backdrop state left over
   from a phone width collapses to display:none the moment the viewport crosses the breakpoint. */
.hud-hamburger { display: none; }
.hud-backdrop { display: none; }

.hud-menu {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: .5rem 0;
}

.menu-section-title {
    display: flex;
    align-items: center;
    gap: .45rem;
    padding: .5rem .85rem .25rem;
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .4px;
    color: var(--muted);
}

.menu-section-title .swatch {
    width: .7rem;
    height: .7rem;
    border-radius: 3px;
    flex: 0 0 auto;
}

.menu-section-title:not(:first-child) {
    margin-top: .75rem;
}

.hud-menu-note {
    padding: .75rem .85rem;
    color: var(--muted);
}

.hud-left.collapsed .menu-section-title .label {
    display: none;
}

.menu-item {
    display: flex;
    align-items: center;
    gap: .6rem;
    width: 100%;
    border: none;
    background: transparent;
    text-align: left;
    padding: .5rem .85rem;
    cursor: pointer;
    color: var(--ink);
    font: inherit;
    border-left: 3px solid transparent;
}

.menu-item:hover {
    background: var(--surface-2);
}

.menu-item.active {
    background: var(--brand-pale);
    border-left-color: var(--brand);
    font-weight: 600;
}

.menu-item-icon {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: 7px;
    background: var(--brand);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .8rem;
    font-weight: 700;
    position: relative;
}

.menu-item-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.menu-item-label small {
    display: block;
    color: var(--muted);
    font-weight: 400;
    font-size: .72rem;
}

.menu-badge {
    flex: 0 0 auto;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 .35rem;
    border-radius: 999px;
    background: var(--brand);
    color: #fff;
    font-size: .72rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* collapsed: hide labels, float the count onto the icon */
.hud-left.collapsed .menu-item-label,
.hud-left.collapsed .submenu {
    display: none;
}

.hud-left.collapsed .menu-item {
    justify-content: center;
    padding: .4rem 0;
}

.hud-left.collapsed .menu-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 1rem;
    height: 1rem;
    font-size: .62rem;
    border: 2px solid var(--surface);
}

.submenu {
    list-style: none;
    margin: 0;
    padding: 0 0 .25rem 0;
}

.submenu-item {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    border: none;
    background: transparent;
    text-align: left;
    padding: .35rem .85rem .35rem 3.1rem;
    cursor: pointer;
    color: var(--muted);
    font: inherit;
    font-size: .85rem;
}

.submenu-item:hover {
    background: var(--surface-2);
}

.submenu-item.active {
    color: var(--brand-dark);
    font-weight: 600;
}

.submenu-item .sub-dot {
    flex: 0 0 auto;
}

.submenu-item .sub-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---- Center panel ------------------------------------------------- */
.hud-center {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    padding: 1.25rem 1.5rem;
}

.hud-empty {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: var(--muted);
    gap: .5rem;
}

.hud-empty img {
    width: 72px;
    height: 72px;
    opacity: .85;
}

.screen-head {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin-bottom: .25rem;
}

.screen-head h2 {
    margin: 0;
    font-size: 1.35rem;
}

.screen-sub {
    color: var(--muted);
    margin: 0 0 1rem;
    font-size: .85rem;
}

/* ---- Tabs --------------------------------------------------------- */
.tabs {
    display: flex;
    gap: .25rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: 1.25rem;
}

.tab {
    border: none;
    background: transparent;
    padding: .6rem .9rem;
    cursor: pointer;
    color: var(--muted);
    font: inherit;
    font-weight: 600;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

.tab:hover {
    color: var(--ink);
}

.tab.active {
    color: var(--brand-dark);
    border-bottom-color: var(--brand);
}

/* ---- Cards / panels ----------------------------------------------- */
.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem 1.1rem;
    box-shadow: var(--shadow-sm);
    margin-bottom: 1rem;
}

.card-title {
    font-weight: 700;
    margin: 0 0 .75rem;
}

.actions-row {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: 1rem;
}

.actions-row .actions-label {
    width: 100%;
    font-size: .8rem;
    font-weight: 600;
    color: var(--muted);
    margin-bottom: .15rem;
}

/* ---- Definition list (device info) -------------------------------- */
.def-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: .55rem 1.25rem;
    align-items: baseline;
}

.def-grid dt {
    color: var(--muted);
    font-weight: 600;
    font-size: .82rem;
}

.def-grid dd {
    margin: 0;
}

/* ---- WO-037: Admin "Devices" list on phones ----------------------
   Each device <dt> in the Admin <dl class="def-grid device-grid"> packs a
   status dot, the display name, and up to four inline controls (the
   ⚠ duplicate chip, Rename, Merge, and Delete → Confirm delete + Cancel; or,
   in rename mode, the name input + Save/Cancel). The base `.def-grid` first
   column is `max-content`, so that row resolves well past a ~390px phone and
   spills out of its .card, forcing page-level horizontal scroll.

   At the SHARED Hub phone breakpoint (640px — the same threshold as the WO-014
   header scroll-away, the WO-016/019 off-canvas nav and the WO-027 cost
   tables; NOT a new value), collapse the grid to a single column and let each
   <dt> flex-wrap its controls onto following lines, so every device row stays
   inside the card. Scoped to the `.device-grid` modifier, so the other
   .def-grid consumers (Dashboard.razor, QueueOverview.razor) keep their
   desktop two-column layout unchanged at every width. Presentation-only. */
@media (max-width: 640px) {
    .def-grid.device-grid {
        grid-template-columns: 1fr;
        gap: .15rem 0;
    }
    .def-grid.device-grid dt {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: .35rem;
        margin-top: .75rem;
        overflow-wrap: anywhere;
    }
    .def-grid.device-grid dt:first-of-type {
        margin-top: 0;
    }
    /* The merge <select> lives in an inline <label>; a long device-name option
       gives it a wide intrinsic width that would overflow the card. Making the
       label full-width lets the select's own width:100% cap it to the column. */
    .def-grid.device-grid .merge-panel label {
        width: 100%;
    }
}

/* ---- WO-118: latest-agent header + per-device up-to-date/behind badge ----
   Inline text inside the device <dd>, so it wraps with the rest of the status
   line and never forces page-level horizontal scroll on the ≤900/640px phone
   layout. Observability only — no interactive affordance. */
.latest-agent {
    color: var(--muted);
    font-size: .82rem;
    margin: .2rem 0 .6rem;
}
.fleet-version-badge {
    font-weight: 600;
}
.fleet-version-badge.fleet-uptodate { color: var(--ok); }
.fleet-version-badge.fleet-behind { color: var(--warn); }

/* ---- History list ------------------------------------------------- */
.history-item {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .65rem .25rem;
    border-bottom: 1px solid var(--border);
    cursor: pointer;
}

.history-item:hover {
    background: var(--surface-2);
}

.history-item .hi-main {
    flex: 1 1 auto;
    min-width: 0;
}

.history-item .hi-title {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.history-item .hi-meta {
    color: var(--muted);
    font-size: .78rem;
}

.load-more {
    margin-top: 1rem;
    text-align: center;
}

/* ---- WO-065: Sessions tab ---------------------------------------- */
.sessions-group-title {
    margin: 1rem 0 .35rem;
    font-weight: 700;
    font-size: .85rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .03em;
}

.sessions-group-title:first-child {
    margin-top: .25rem;
}

.sessions-reopen-note,
.sessions-offline {
    color: var(--muted);
    font-size: .8rem;
    margin: .1rem 0 .5rem;
}

.sessions-row .sessions-action {
    color: var(--accent, var(--muted));
    font-size: .8rem;
    font-weight: 600;
    white-space: nowrap;
}

.sa-badge.sessions-live {
    background: var(--ok-bg, rgba(46, 160, 67, .15));
    color: var(--ok, #2ea043);
}

.sessions-row.is-disabled {
    cursor: default;
    opacity: .6;
}

.sessions-row.is-disabled:hover {
    background: transparent;
}

/* WO-071: authoritative Sessions tab — provider notices, origin/availability badges, external-session snippet. */
.sessions-notices {
    margin: .1rem 0 .4rem;
}

.sessions-notice {
    font-size: .8rem;
    margin: .1rem 0;
}

.sa-badge.sessions-external {
    background: var(--surface-2, rgba(125, 125, 125, .15));
    color: var(--muted);
}

.sa-badge.sessions-unavailable {
    background: var(--warn-bg, rgba(210, 153, 34, .15));
    color: var(--warn, #d29922);
    margin-left: .35rem;
}

.sessions-snippet {
    color: var(--muted);
    font-size: .8rem;
    margin-top: .1rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 60ch;
}

/* WO-072: a row that can only be relaunched (a non-resumable provider, or a session the provider lost) explains
   the fresh start; a per-row open/resume refusal (e.g. an out-of-root resume) states its reason in the row. */
.sessions-relaunch-reason {
    color: var(--muted);
    font-size: .78rem;
    margin-top: .15rem;
}

/* WO-077 AC-3: a row whose folder is gone or out-of-root explains why it can't be re-opened, before the click. */
.sessions-directory-reason {
    color: var(--warn, #d29922);
    font-size: .78rem;
    margin-top: .15rem;
}

.sa-badge.sessions-deaddir,
.sa-badge.sessions-outofroot {
    background: var(--danger-bg, rgba(210, 90, 70, .15));
    color: var(--danger, #d2604a);
    margin-left: .35rem;
}

/* WO-077 AC-5: ended sessions grouped per known project / root folder. */
.sessions-project-group {
    margin: .2rem 0 .6rem;
}

.sessions-project-title {
    font-size: .82rem;
    font-weight: 600;
    color: var(--muted);
    margin: .35rem 0 .2rem;
}

.sessions-project-count {
    font-weight: 400;
    opacity: .8;
}

.sessions-row-error {
    margin-top: .35rem;
    font-size: .8rem;
}

/* ---- Task detail -------------------------------------------------- */
.progress {
    height: .5rem;
    border-radius: 999px;
    background: var(--surface-2);
    overflow: hidden;
    margin: .5rem 0 1rem;
}

.progress > span {
    display: block;
    height: 100%;
    background: var(--brand);
    transition: width .2s ease;
}

.steps {
    list-style: none;
    margin: 0;
    padding: 0;
}

.steps li {
    display: flex;
    gap: .6rem;
    padding: .4rem 0;
    border-left: 2px solid var(--border);
    padding-left: .9rem;
    margin-left: .35rem;
}

.steps .step-time {
    color: var(--muted);
    font-size: .75rem;
    flex: 0 0 auto;
    min-width: 7.5rem;
}

.steps .step-kind {
    font-weight: 600;
    flex: 0 0 auto;
    min-width: 6rem;
}

.proposal {
    border: 1px solid #e7d9b3;
    background: var(--warn-bg-soft);
    border-radius: var(--radius);
    padding: 1rem;
    margin-top: 1rem;
}

.proposal-body {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: .5rem .75rem;
    margin: .5rem 0;
    max-height: 22rem;
    overflow: auto;
}

.transcript pre {
    white-space: pre-wrap;
    max-height: 26rem;
    overflow: auto;
    background: #1c2620;
    color: #d8e2d4;
    padding: .85rem;
    border-radius: var(--radius);
    font-family: 'Cascadia Code', Consolas, monospace;
    font-size: .8rem;
}

/* ---- Chat thread (conversational follow-ups) ----------------------- */
.chat-thread {
    display: flex;
    flex-direction: column;
    gap: .6rem;
    max-height: 22rem;
    overflow: auto;
    margin-bottom: 1rem;
}

.chat-thread .turn {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .5rem .7rem;
    background: var(--surface-2);
}

.chat-thread .turn-user {
    background: var(--brand-pale);
    align-self: flex-end;
    max-width: 85%;
}

.chat-thread .turn-assistant, .chat-thread .turn-system {
    align-self: flex-start;
    max-width: 85%;
}

.chat-thread .who {
    display: block;
    font-weight: 600;
    font-size: .75rem;
    color: var(--muted);
    margin-bottom: .2rem;
}

.chat-thread .text {
    margin: 0;
    white-space: pre-wrap;
    font-family: inherit;
    font-size: .88rem;
}

.chat-input textarea {
    /* Auto-grow (WO-019): sized by content, capped so a long message scrolls internally. */
    resize: none;
    max-height: 16rem;
}

.chat-input .hint {
    color: var(--muted);
    font-size: .8rem;
    align-self: center;
}

/* ---- Chat-first task detail (two-column stream + side panel) ------- */
.task-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
    margin-top: 1rem;
}

@media (max-width: 1100px) {
    .task-grid { grid-template-columns: minmax(0, 1fr); }
}

.task-side { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }

.task-chat .chat-stream { max-height: 34rem; min-height: 10rem; }

.chat-md { font-size: .88rem; overflow-x: auto; max-height: 24rem; overflow-y: auto; }
.chat-md > :first-child { margin-top: 0; }
.chat-md > :last-child { margin-bottom: 0; }

.chat-qcard, .chat-pcard, .chat-ccard {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .6rem .8rem;
    background: var(--surface);
    align-self: stretch;
}

.chat-qcard { border-left: 4px solid var(--warn); background: var(--warn-bg-soft); }
.chat-pcard { border-left: 4px solid var(--info); }
.chat-ccard { border-left: 4px solid var(--ok); background: var(--brand-pale); }

.chat-card-head {
    font-weight: 600;
    font-size: .8rem;
    color: var(--muted);
    margin-bottom: .35rem;
}

.chat-card-live { box-shadow: var(--shadow); }
.chat-superseded { opacity: .6; }

.activity-acc summary {
    cursor: pointer;
    font-weight: 600;
    font-size: .85rem;
    color: var(--muted);
}

/* ---- Right panel -------------------------------------------------- */
.hud-right {
    flex: 0 0 340px;
    width: 340px;
    background: var(--surface);
    border-left: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.hud-right.hidden {
    display: none;
}

.hud-right-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: .6rem .85rem;
    border-bottom: 1px solid var(--border);
    font-weight: 700;
}

.hud-right-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 1rem;
    color: var(--muted);
}

/* floating tab to reveal the hidden right panel */
.hud-right-reveal {
    position: absolute;
    top: 50%;
    right: 0;
    transform: translateY(-50%);
    writing-mode: vertical-rl;
    border: 1px solid var(--border);
    border-right: none;
    border-radius: var(--radius) 0 0 var(--radius);
    background: var(--surface);
    color: var(--muted);
    padding: .75rem .3rem;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
}

.hud-center-wrap {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    position: relative;
    display: flex;
}

.banner-error {
    background: #fae3df;
    color: var(--danger);
    border: 1px solid #eebcb3;
    border-radius: var(--radius);
    padding: .6rem .85rem;
    margin-bottom: 1rem;
}

.banner-warn {
    background: var(--warn-bg);
    color: var(--warn);
    border: 1px solid var(--warn-border);
    border-radius: var(--radius);
    padding: .6rem .85rem;
    margin-bottom: 1rem;
}

.banner-error {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .6rem;
}

/* ===================================================================
   Admin page
   =================================================================== */
.admin {
    width: 100%;
    overflow-y: auto;
    padding: 1.5rem 2rem;
}

.admin h1 {
    margin-top: 0;
}

.admin-grid {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: center;
}

.install-link {
    margin: .5rem 0 1rem;
    display: flex;
    flex-direction: column;
    gap: .25rem;
    max-width: 40rem;
}

.install-link input {
    width: 100%;
    padding: .45rem .6rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font: inherit;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

/* ===== Mission Control: structured console ===== */
.console-card .console-stream {
    display: flex;
    flex-direction: column-reverse;   /* newest content stays anchored at the visual bottom */
    max-height: 420px;
    overflow-y: auto;
    gap: .4rem;
    font-size: .9rem;
}
.autopilot-console .console-stream {
    display: flex;
    flex-direction: column-reverse;   /* newest content stays anchored at the visual bottom */
    max-height: 420px;
    overflow-y: auto;
    gap: .4rem;
    font-size: .9rem;
}
/* WO-091: the session window replaces the old one-at-a-time picker. Every running session on the
   project is a tile that says what it is doing and for how long, so a stalled one is visible without
   opening it. Same visual language as the pills it replaces (running dot, brand-tinted selection). */
.autopilot-console .session-window { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .5rem; }
.autopilot-console .session-tile {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .25rem .5rem .25rem .6rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: transparent;
    min-width: 12rem;
}
.autopilot-console .session-tile.selected { border-color: var(--brand); background: var(--brand-pale); }
.autopilot-console .session-tile .tile-face {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .1rem;
    flex: 1 1 auto;
    border: 0;
    background: transparent;
    color: inherit;
    cursor: pointer;
    text-align: left;
    padding: 0;
}
.autopilot-console .session-tile .tile-label { font-size: .8rem; color: var(--ink); }
.autopilot-console .session-tile.running .tile-label::before { content: "● "; color: var(--ok); }
.autopilot-console .session-tile .tile-activity { font-size: .74rem; color: var(--muted); white-space: nowrap; }
/* Stuck is the one state that has to read WITHOUT being read: a warn-tinted border and a warn
   activity line, so it separates from its neighbours at a glance on a wall of tiles. It is not
   danger-red - nothing is broken and no work is lost; the session simply has not spoken for a while. */
.autopilot-console .session-tile.stuck { border-color: var(--warn); background: color-mix(in srgb, var(--warn) 8%, transparent); }
.autopilot-console .session-tile.stuck .tile-activity { color: var(--warn); }
.autopilot-console .session-tile.stuck .tile-label::before { content: "◐ "; color: var(--warn); }
/* Parked is waiting on the OWNER - the attention tint the parked panel already uses, never the
   stuck treatment, because the thing to do about it is answer it, not investigate it. */
.autopilot-console .session-tile.activity-parked .tile-activity { color: var(--warn); }
.autopilot-console .session-tile .tile-terminate { flex: 0 0 auto; }
.autopilot-console .window-summary {
    flex: 1 0 100%;
    font-size: .78rem;
    color: var(--warn);
}
/* WO-026 + WO-030: ended sessions collapse behind a toggle at the BOTTOM of the pane (last child of
   .autopilot-console, after the answer row). Disclosure state is component-owned (a plain button, not
   <details>), so it survives background re-bucketing. */
.autopilot-console .ended-sessions { margin-top: .6rem; margin-bottom: 0; }
.autopilot-console .ended-toggle {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .15rem .55rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    font-size: .78rem;
}
.autopilot-console .ended-toggle::before { content: "▸"; font-size: .7rem; }
.autopilot-console .ended-sessions.expanded .ended-toggle::before { content: "▾"; }
/* WO-030: a vertical list — one row per ended session — not the old wrapped row of pills. */
.autopilot-console .ended-list { display: flex; flex-direction: column; gap: .15rem; margin-top: .4rem; }
.autopilot-console .ended-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .6rem;
    width: 100%;
    padding: .2rem .5rem;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    font-size: .8rem;
    text-align: left;
}
.autopilot-console .ended-row:hover { background: var(--brand-pale); }
.autopilot-console .ended-row.selected { border-color: var(--brand); color: var(--ink); background: var(--brand-pale); }
.autopilot-console .ended-row-label::before { content: "○ "; color: var(--muted); }
.autopilot-console .ended-row-time { color: var(--muted); font-size: .74rem; white-space: nowrap; }
.autopilot-console .transcript-tools { margin-bottom: .5rem; }
.autopilot-console .transcript-tools input[type=search] { width: 100%; }
.autopilot-console .awaiting-banner {
    margin-bottom: .5rem;
    padding: .3rem .6rem;
    border-radius: 6px;
    background: var(--warn-bg);
    color: var(--warn);
    font-family: 'Cascadia Code', Consolas, monospace;
    font-size: .85rem;
}
.autopilot-console .answer { display: flex; gap: .5rem; margin-top: .6rem; align-items: flex-start; }
/* Auto-grow (WO-019): sized by content, capped so a long answer scrolls internally. */
.autopilot-console .answer textarea { flex: 1; min-height: 3rem; max-height: 16rem; resize: none; }
/* Terminate is sa-btn sa-btn-sm; this adds the danger emphasis on top. */
.autopilot-console button.danger { border-color: #eebcb3; color: var(--danger); }
.console-text { line-height: 1.45; overflow-wrap: anywhere; }
.console-text pre { overflow-x: auto; }
/* Per-block freshness stamp (WO-004): muted, right-gutter, secondary — never competes with the transcript. */
.console-ts {
    float: right;
    margin: 0 0 .1rem .6rem;
    color: var(--muted);
    font-family: 'Cascadia Code', Consolas, monospace;
    font-size: .7rem;
    opacity: .6;
    white-space: nowrap;
    cursor: default;
    user-select: none;
}
/* "last output X ago" chip; stale = a running session silent past the threshold. */
.autopilot-console .console-freshness {
    margin-bottom: .35rem;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    color: var(--muted);
    font-family: 'Cascadia Code', Consolas, monospace;
    font-size: .8rem;
}
.autopilot-console .console-freshness.stale { color: var(--warn); font-weight: 600; }
.autopilot-console .console-freshness .stale-dot {
    display: inline-block;
    width: .5rem; height: .5rem;
    border-radius: 50%;
    background: var(--warn);
    animation: sa-pulse 1.2s ease-in-out infinite;
}
.tool-chip {
    display: inline-block;
    padding: .1rem .55rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    font-family: 'Cascadia Code', Consolas, monospace;
    font-size: .8rem;
    color: var(--muted);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
}
.tool-result pre, .console-info pre {
    max-height: 200px;
    overflow: auto;
    font-size: .8rem;
    opacity: .85;
}
.console-info summary, .tool-result summary { cursor: pointer; color: var(--muted); font-size: .8rem; }
.console-turnbreak { border: none; border-top: 1px dashed var(--border); margin: .35rem 0; }
.console-activity {
    margin-top: .5rem;
    color: var(--muted);
    font-family: 'Cascadia Code', Consolas, monospace;
    font-size: .85rem;
}
.console-activity .pulse {
    display: inline-block;
    width: .55rem; height: .55rem;
    border-radius: 50%;
    background: var(--ok);
    animation: sa-pulse 1.2s ease-in-out infinite;
    vertical-align: middle;
    margin-right: .3rem;
}
@keyframes sa-pulse { 0%,100% { opacity: .35; } 50% { opacity: 1; } }

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ===== Mission Control: fleet grid ===== */
.fleet-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: .9rem;
    margin-top: 1rem;
}
.fleet-tile {
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: .8rem .95rem;
    cursor: pointer;
    transition: border-color .15s ease;
    display: flex;
    flex-direction: column;
    gap: .35rem;
    min-height: 150px;
}
.fleet-tile:hover { border-color: var(--brand); }
.fleet-tile-autopilot {
    border-left: 3px solid var(--brand);
    min-height: 0;
}
.badge-autopilot {
    background: var(--brand-pale);
    color: var(--brand-dark);
}
.fleet-tile-head { display: flex; justify-content: space-between; align-items: center; gap: .5rem; }
.fleet-device { color: var(--muted); font-size: .8rem; }
.fleet-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fleet-meta { color: var(--muted); font-size: .8rem; }
.fleet-tail {
    flex: 1 1 auto;
    font-family: ui-monospace, monospace;
    font-size: .78rem;
    color: var(--muted);
    overflow: hidden;
}
.fleet-tail-line { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fleet-attention { color: var(--warn); }

/* ===================================================================
   Modal scaffold (shared Modal.razor)
   =================================================================== */
.modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(28, 38, 32, .45);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.modal {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.25rem 1.4rem;
    width: 100%;
    max-width: 34rem;
    max-height: 85vh;
    overflow-y: auto;
    outline: none;
}

.modal > h2, .modal > h3 { margin-top: 0; }

.modal-close {
    position: absolute;
    top: .5rem;
    right: .6rem;
    border: none;
    background: transparent;
    color: var(--muted);
    font-size: 1.15rem;
    line-height: 1;
    padding: .2rem .45rem;
    border-radius: 6px;
    cursor: pointer;
}
.modal-close:hover { background: var(--surface-2); color: var(--ink); }

/* wider overlays */
.modal.ship-rules-modal { max-width: 44rem; }
.modal.wo-preview-modal { max-width: 46rem; }

/* Preview overlay text sections (Description, User story, Definition of done, Notes,
   Last session) render inside <pre>. Soft-wrap them so they don't force the modal to
   scroll horizontally on narrow/phone viewports, while keeping the markdown's intentional
   newlines/indentation. Long unbroken tokens (URLs, paths, hashes) break instead of
   overflowing. Scoped to this modal so console/other <pre> usages are unaffected. */
.modal.wo-preview-modal pre {
    white-space: pre-wrap;
    overflow-wrap: break-word;
    word-break: break-word;
}

/* WO-061: work-order dependency section (both directions + link/unlink picker). Uses the shared
   pill/sa-btn tokens so it sits consistently with the rest of the preview overlay. */
.wo-dependencies { margin-top: 1rem; }
.wo-dependencies .dep-group { margin: .5rem 0 .75rem; }
.wo-dependencies h5 {
    margin: 0 0 .35rem;
    font-size: .8rem;
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .02em;
}
.wo-dependencies h5 .dep-hint {
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--muted);
}
.wo-dependencies .dep-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .35rem;
}
.wo-dependencies .dep-chip {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    padding: .3rem .5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
}
/* A not-yet-Done blocker is what actually holds the work order back — tint it so it stands out from a
   satisfied (Done) one at a glance. */
.wo-dependencies .dep-chip.unsatisfied { border-color: #eebcb3; background: #fbeeeb; }
.wo-dependencies .dep-chip.satisfied { opacity: .75; }
.wo-dependencies .dep-chip .wo-num { font-variant-numeric: tabular-nums; color: var(--muted); font-size: .82rem; }
.wo-dependencies .dep-chip .dep-title { flex: 1 1 8rem; min-width: 0; overflow-wrap: break-word; }
.wo-dependencies .dep-chip .dep-holding { flex: 0 0 auto; }
.wo-dependencies .dep-chip .dep-remove { margin-left: auto; line-height: 1; padding: .1rem .4rem; }
.wo-dependencies .dep-add { margin-top: .4rem; max-width: 22rem; }
.wo-dependencies .dep-error { margin-top: .4rem; }

.modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
    margin-top: 1.1rem;
}

.modal .form-error { color: var(--danger); font-size: .85rem; margin: .5rem 0 0; }
.modal .warn { color: var(--warn); }
.modal .hint { color: var(--muted); font-size: .82rem; }

/* ===================================================================
   Autopilot shared pane states
   =================================================================== */
.pane-empty {
    padding: 1.5rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    color: var(--muted);
    text-align: center;
}

.pane-error {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .6rem;
    padding: .75rem .9rem;
    border: 1px solid #eebcb3;
    background: #fae3df;
    color: var(--danger);
    border-radius: var(--radius);
    margin-bottom: .75rem;
}
/* Also wins over `.modal .warn` (equal specificity, later in source) so pane errors inside a
   modal stay danger-colored. Keep this rule BELOW the modal scaffold block. */
.pane-error .warn { font-weight: 600; color: var(--danger); }

.pane-loading {
    display: flex;
    align-items: center;
    gap: .5rem;
    color: var(--muted);
    padding: .75rem .2rem;
}
.pane-loading-text { font-size: .88rem; }

.pane-spinner, .btn-spinner {
    display: inline-block;
    width: .9rem;
    height: .9rem;
    border: 2px solid var(--border);
    border-top-color: var(--brand);
    border-radius: 50%;
    animation: sa-spin .7s linear infinite;
    flex: 0 0 auto;
}
@keyframes sa-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .pane-spinner, .btn-spinner { animation: none; } }

/* ===================================================================
   Narrow screens — auto-collapse the HUD nav, let rows scroll
   =================================================================== */
@media (max-width: 900px) {
    .hud-left { flex-basis: 60px; width: 60px; }
    .hud-left .hud-left-caption,
    .hud-left .menu-item-label,
    .hud-left .submenu { display: none; }
    .hud-left .menu-item { position: relative; justify-content: center; padding: .4rem 0; }
    .hud-left .menu-badge {
        position: absolute;
        top: -4px;
        right: -4px;
        min-width: 1rem;
        height: 1rem;
        font-size: .62rem;
        border: 2px solid var(--surface);
    }
    .hud-center { padding: .85rem .75rem; }
    .activity-strip { flex-wrap: nowrap; overflow-x: auto; }
    .tabs { overflow-x: auto; }
    .screen-head { flex-wrap: wrap; }
    /* WO-035: on narrow viewports let the device-header action controls (usage chip, Loop
       switch, Ship rules, New work order) wrap onto their own full-width line and flow onto
       multiple rows. The base `.screen-head-actions { flex: 0 0 auto; }` sizes the block to its
       content's max-content width, so its own `flex-wrap: wrap` never triggers — overriding the
       flex here to a shrinkable `flex: 1 1 100%` drops the block below the title and lets its
       children wrap, so the row no longer overflows and forces a page scrollbar. */
    .screen-head-actions { flex: 1 1 100%; flex-wrap: wrap; }
    /* WO-034 / WO-018: hide the entire Autopilot project-detail sub-line (device name, synced
       time, state counts) on narrow viewports. The inline ship-defaults gear was removed in
       WO-018, so hiding the whole .screen-sub leaves no lone gear or empty sub-line behind. */
    .screen-sub { display: none; }
    /* WO-026 + WO-030: keep the ended-session toggle + its expanded list inside the narrow pane. The
       list is now a full-width vertical column (WO-030), so it can't overflow; the toggle wraps its
       own line if needed and each row lets its label/time wrap rather than push the pane wide. */
    .autopilot-console .ended-sessions { max-width: 100%; }
    .autopilot-console .ended-row { flex-wrap: wrap; }
    /* WO-061: let the dependency add-picker span the full modal width on narrow viewports. */
    .wo-dependencies .dep-add { max-width: 100%; }
}

/* ===================================================================
   WO-016 / WO-019 — Phone: the HUD nav rail becomes a hamburger-toggled
   slide-out overlay. SHARED by BOTH shells: the FleetNav rail (/ and
   /autopilot) and the Admin rail (/admin/*). Each marks its
   <aside class="hud-left …"> with the `hud-offcanvas` modifier, so these
   off-canvas rules are authored ONCE here and inherited by both (WO-019
   AC: "authored once so both shells inherit it").

   Breakpoint reconciliation:
     • 900px  — the HUD "narrow" rules above pin the in-flow rail to a 60px
                icon strip. KEPT for the 641–900px tablet band (unchanged,
                both shells).
     • 640px  — the phone breakpoint, matching the header's existing 640px
                phone rules (WO-014 header scroll-away). At <=640px the rail
                leaves the flow entirely and becomes an off-canvas overlay,
                so .hud-center spans the full viewport.
   The overlay is a bigger change than the 60px strip, so it gets the
   narrower, header-aligned threshold rather than the 900px one.

   Header offset (WO-019): the open overlay PANEL starts at the header's
   bottom edge (top: var(--header-h)) so the sticky .app-header stays visible
   above it rather than being covered.

   WO-014 reconciliation (header scroll-away): on phones .app-header is
   `position: relative` and scrolls OFF with the page — it is not pinned, and
   pure CSS cannot track its live on-screen offset. So rather than offset the
   BACKDROP by var(--header-h) (which would expose an undimmed strip the moment
   the header scrolls away), the backdrop covers the FULL viewport (inset: 0)
   and the header is layered ABOVE it (position: relative + z-index: 305, set in
   the 640px header block) so it stays fully visible/un-dimmed while on-screen.
   Net effect: at rest the overlay panel sits just below the visible header;
   once the header scrolls off, the backdrop still fills the whole viewport —
   no gap, no mispositioned overlay/backdrop — with only the panel kept
   var(--header-h) below the viewport top.

   z-order: backdrop 300 < header 305 < overlay panel 310 < hamburger 320. */
@media (max-width: 640px) {
    /* Floating toggle: fixed to the viewport so it stays reachable after the
       WO-014 header scrolls off-screen. Sits above the rail and backdrop so
       it doubles as the close (✕) affordance while the overlay is open. */
    .hud-hamburger {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        top: .5rem;
        left: .55rem;
        width: 38px;
        height: 38px;
        z-index: 320;
        border: 1px solid rgba(255, 255, 255, .45);
        border-radius: 9px;
        background: var(--brand-dark);
        color: #fff;
        font-size: 1.15rem;
        line-height: 1;
        cursor: pointer;
        box-shadow: var(--shadow-sm);
    }

    /* Keep the header wordmark/brand clear of the fixed toggle at the top. Both shells now render a
       hamburger (WO-019 added it to the admin shell), so :has() applies the padding on both. */
    .app-header:has(~ .app-body .hud-hamburger) { padding-left: calc(.6rem + 44px); }

    /* Dimmed backdrop behind the open overlay; tapping it closes (handled in
       FleetNav/AdminShell via @onclick="CloseNav"). Only rendered while open.
       Full-viewport (inset: 0) by design — see the WO-014 reconciliation above. */
    .hud-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 300;
        background: rgba(20, 28, 24, .5);
    }

    /* Take the rail out of flow and off-canvas. .hud-center-wrap (flex: 1) then
       fills the whole row, so content is full-width whenever the rail is closed.
       top: var(--header-h) + bottom: 0 fills from the header bottom to the viewport
       bottom; the inner .hud-menu (flex: 1; overflow-y: auto) scrolls if taller. */
    .hud-left.hud-offcanvas {
        position: fixed;
        top: var(--header-h);
        left: 0;
        bottom: 0;
        width: min(300px, 84vw);
        flex: 0 0 auto;
        z-index: 310;
        transform: translateX(-100%);
        transition: transform .22s ease;
        box-shadow: var(--shadow);
    }
    .hud-left.hud-offcanvas.open { transform: translateX(0); }

    /* The desktop «/» collapse toggle is meaningless in overlay mode — hide it;
       closing is via the hamburger, the backdrop, or selecting a destination. */
    .hud-left.hud-offcanvas .hud-collapse-btn { display: none; }

    /* Re-show the full labelled menu inside the overlay: the 900px block above
       hides labels/submenus/caption for the 60px strip, but the overlay rail is
       full width. Higher specificity (.hud-offcanvas) wins over those rules. */
    .hud-left.hud-offcanvas .hud-left-caption,
    .hud-left.hud-offcanvas .menu-item-label,
    .hud-left.hud-offcanvas .submenu { display: revert; }
    .hud-left.hud-offcanvas .menu-item { position: relative; justify-content: flex-start; padding: .4rem .75rem; }
    .hud-left.hud-offcanvas .menu-badge {
        position: static;
        top: auto;
        right: auto;
        border: none;
    }
}

/* WO-016/WO-019: honor reduced-motion for the off-canvas slide (mirrors the
   existing prefers-reduced-motion rule for spinners). Applies to both shells. */
@media (prefers-reduced-motion: reduce) {
    .hud-left.hud-offcanvas { transition: none; }
}

/* ===================================================================
   WO-064 — Work Assist console-wall fullscreen.
   ConsoleWall.razor.js toggles `sa-console-fullscreen` on <body> when the
   wall enters its chrome-free mode. The wall itself promotes to a fixed,
   viewport-filling layer (scoped .console-wall-root.fullscreen); THESE rules
   reclaim every surrounding surface — the Hub header, the FleetNav rail, the
   phone hamburger trigger, and the release / manual-pause banners — so nothing
   but the wall's own controls is on screen. Applied by the body class so it
   holds for the CSS-only fallback where no real browser fullscreen exists
   (iOS Safari); in real browser fullscreen the chrome is off-screen anyway.
   Session-only: the class is never written to storage, so a reload — which
   builds a fresh <body> — is always back to the normal chrome. The ≤900px
   rules the wall needs (one full-height console per page) already come from
   ConsoleWall's own grid media query; here we only ensure the sticky header
   and the hamburger overlay trigger are gone (AC-7). */
body.sa-console-fullscreen .app-header,
body.sa-console-fullscreen .hud-left,
body.sa-console-fullscreen .hud-hamburger,
body.sa-console-fullscreen .hud-backdrop,
body.sa-console-fullscreen .release-banner {
    display: none !important;
}

/* Autopilot project picker (device selected, no project) */
/* WO-007: the picker is now a vertical stack (Touched grid, then the untouched show/hide block);
   the card grid itself lives in the .picker-grid rule (co-located ProjectPicker.razor.css). */
.project-picker {
    display: flex;
    flex-direction: column;
    gap: .75rem;
    margin-top: 1rem;
}

.project-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .35rem;
    padding: .85rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    text-align: left;
    font: inherit;
    transition: border-color .15s ease;
}

.project-card:hover {
    border-color: var(--brand);
}

.project-card-name {
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: .4rem;
}

.project-card-counts {
    font-size: .8rem;
    color: var(--muted);
}

.fav-star {
    color: var(--warn);
}
