/**
 * Mirror Ai — Persistent Workspace Shell (icon rail + contextual panel)
 * =====================================================================
 * TWO columns, two jobs — the model Slack, Discord, VS Code and Linear all use:
 *
 *   .ws-rail   (60px)  the ICON RAIL — brand, service icons (icon-only, tooltip on
 *                      hover, one bold active state), and a utility cluster at the
 *                      bottom (search/⌘K, collapse, avatar). It SELECTS the service.
 *   .ws-panel  (260px) the CONTEXTUAL PANEL — shows ONLY the active service's own
 *                      content (its header, CTA, search, list). It SHOWS the service.
 *
 * WHY: the previous rail stacked a grouped "service switcher" on top of the sidebar
 * content in one 280px column — two navigation systems and two search boxes fighting
 * for one column. Every product above explicitly moved away from that. The rail stays
 * short on purpose; the command palette (⌘K) holds the long tail, so a 13-service
 * product never grows a wall of items here.
 *
 * The two columns are a subtle 2-tone (rail = --ws-bg-deepest, panel = --ws-bg-deep)
 * so they read as rail + panel, the way Discord/Slack darken the server column.
 *
 * ARCHITECTURE (unchanged): the shell is position:fixed and a SIBLING of <main>, so
 * the transform:scale() immersive transitions on view wrappers cannot capture it.
 * Views are fixed/inset:0 and simply take `left: <shell width>` to make room.
 *
 * PRESERVED for JS: .service-tab[data-mode] (updateServiceTabs toggles .active),
 * #sidebar-collapse-btn, #back-to-dashboard-btn, #ws-palette-btn, every slot id.
 *
 * LOAD ORDER: workspace-tokens.css -> visibility-system.css -> workspace.css
 *             -> workspace-shell.css -> features -> workspace-shell-mobile.css
 */

/* ============================================================================
   1. THE SHELL — two-column container
   ============================================================================ */

.ws-sidebar {
    display: none;                 /* the dashboard has no rail */
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--ws-shell-w);      /* rail + panel */
    z-index: var(--z-workspace-rail, 450);
    flex-direction: row;           /* rail | panel */
    /* overflow VISIBLE so the icon-rail tooltips can escape the 60px column. The
       panel manages its own overflow; the list scrolls inside it. */
    overflow: visible;
    transition: width var(--ws-t-base);
}

/* Shown in every deep view. Enumerated per mode (not a blanket immersive rule).
   external-service (the embedded dashboards) is handled separately just below: it
   shows the icon RAIL but not the contextual panel, since the iframe brings its own
   content — so a user can still switch services without leaving the dashboard. */
body.immersive-mode.chat-mode .ws-sidebar,
body.immersive-mode.chat-history-mode .ws-sidebar,
body.immersive-mode.documents-mode .ws-sidebar,
body.immersive-mode.blog-mode .ws-sidebar {
    display: flex;
}

/* Make room for the shell. Views are already fixed/inset:0. */
body.immersive-mode.chat-mode .chat-view-wrapper,
body.immersive-mode.chat-history-mode .chat-view-wrapper,
body.immersive-mode.documents-mode #documents-view,
body.immersive-mode.blog-mode #blog-writer-view {
    left: var(--ws-shell-w);
    width: auto;
    transition: left var(--ws-t-base);
}

/* External-service (Market + the other embedded dashboards): icon RAIL only. The
   dashboard supplies its own content and navigation, so the 260px contextual panel
   would sit empty — we hide it and shrink the shell to the rail, exactly like the
   collapsed shell (section 5). The rail stays so a user can jump between services
   without leaving the dashboard. The view (fixed/inset:0) is offset by the rail width. */
body.immersive-mode[data-view="external-service"] .ws-sidebar {
    display: flex;
    width: var(--ws-rail-icon);
}

body.immersive-mode[data-view="external-service"] .ws-panel {
    display: none;
}

body.immersive-mode[data-view="external-service"] #external-service-view {
    left: var(--ws-rail-icon);
    width: auto;
    transition: left var(--ws-t-base);
}

/* ============================================================================
   2. COLUMN A — THE ICON RAIL
   ============================================================================ */

.ws-rail {
    flex: none;
    width: var(--ws-rail-icon);
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 12px 0 14px;
    background: var(--ws-bg-deepest);
    border-right: 1px solid var(--ws-glass-border);
    overflow: visible;             /* let tooltips out */
}

/* ---- Brand mark = back to dashboard ---- */
.ws-rail-brand {
    flex: none;
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    padding: 0;
    margin-bottom: 12px;
    border: 0;
    border-radius: var(--ws-radius-md);
    background: transparent;
    cursor: pointer;
    transition: transform var(--ws-t-fast);
}

.ws-rail-brand:hover { transform: scale(1.06); }
.ws-rail-brand:active { transform: scale(0.96); }

.ws-rail-brand img {
    width: 34px;
    height: 34px;
    object-fit: contain;
    border-radius: var(--ws-radius-sm);
}

/* ---- The service switcher — icon-only, grouped by a thin divider ---- */
.ws-rail-nav {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    width: 100%;
    overflow: visible;
}

.ws-rail-group {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    width: 100%;
}

/* A short centred divider between groups — not a full-width rule. */
.ws-rail-group + .ws-rail-group {
    margin-top: 8px;
    padding-top: 12px;
    position: relative;
}
.ws-rail-group + .ws-rail-group::before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 24px;
    height: 1px;
    background: var(--ws-glass-border);
}

/* ---- The service icon button ---- */
.ws-rail .service-tab {
    position: relative;
    flex: none;
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--ws-radius-md);
    background: transparent;
    color: var(--ws-text-muted);
    cursor: pointer;
    transition: background var(--ws-t-fast), color var(--ws-t-fast);
}

.ws-rail .service-tab .service-tab-ic {
    font-size: 18px;
    transition: transform var(--ws-t-fast);
}

.ws-rail .service-tab:hover {
    background: var(--ws-glass-hover);
    color: var(--ws-text);
}

.ws-rail .service-tab.active {
    background: rgba(0, 242, 254, 0.1);
    color: var(--ws-accent);
}

/* The active indicator — a left-edge accent pill that grows when active
   (the Discord / VS Code treatment). Short on hover, tall on active. */
.ws-rail .service-tab::before {
    content: '';
    position: absolute;
    left: -12px;                   /* sits in the rail's edge gutter */
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
    height: 0;
    border-radius: 0 3px 3px 0;
    background: var(--ws-accent);
    transition: height var(--ws-t-fast);
}

.ws-rail .service-tab:hover::before { height: 16px; }
.ws-rail .service-tab.active::before { height: 26px; }

/* Guest-locked service (the /try upsell) — dimmed. The lock badge itself comes from
   guest-mode.css ([data-guest-locked]::after), which is the single source for that
   affordance across the whole guest workspace. */
.ws-rail .service-tab.is-locked { opacity: 0.5; }
.ws-rail .service-tab.is-locked:hover { opacity: 0.8; }

/* ---- Bottom utility cluster ---- */
.ws-rail-foot {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
}

.ws-rail-btn {
    position: relative;
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--ws-radius-md);
    background: transparent;
    color: var(--ws-text-muted);
    font-size: 15px;
    cursor: pointer;
    transition: background var(--ws-t-fast), color var(--ws-t-fast);
}

.ws-rail-btn:hover {
    background: var(--ws-glass-hover);
    color: var(--ws-text);
}

.ws-rail-avatar {
    position: relative;
    flex: none;
    width: 34px;
    height: 34px;
    margin-top: 2px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--ws-gradient-user);
    color: var(--ws-text);
    font-size: 0.82rem;
    font-weight: 700;
    cursor: pointer;
}

/* ---- Tooltips — how an icon-only rail stays legible ---- */
.ws-rail [data-tooltip] { position: relative; }

.ws-rail [data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    left: calc(100% + 12px);
    top: 50%;
    transform: translateY(-50%);
    padding: 6px 10px;
    border-radius: var(--ws-radius-sm);
    background: var(--ws-bg-elevated);
    border: 1px solid var(--ws-border-control);
    color: var(--ws-text);
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    box-shadow: var(--ws-shadow-md);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--ws-t-fast);
    z-index: 100;
}

.ws-rail [data-tooltip]:hover::after { opacity: 1; }

@media (hover: none) {
    /* No hover on touch — tooltips would be dead weight; the drawer shows labels. */
    .ws-rail [data-tooltip]::after { display: none; }
}

/* ============================================================================
   3. COLUMN B — THE CONTEXTUAL PANEL
   ============================================================================ */

.ws-panel {
    flex: none;
    width: var(--ws-panel-w);
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--ws-bg-deep);
    border-right: 1px solid var(--ws-glass-border);
    overflow: hidden;
}

.ws-body {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* Only the slot for the current mode is shown. Pure CSS — no JS, no .is-hidden. */
.ws-slot {
    display: none;
    flex: 1;
    min-height: 0;
    flex-direction: column;
    padding: 14px 12px 0;
    gap: 10px;
}

body.documents-mode    .ws-slot[data-ws-mode="documents"] { display: flex; }
body.blog-mode         .ws-slot[data-ws-mode="blog"]       { display: flex; }
body.chat-mode         .ws-slot[data-ws-mode="chat"]       { display: flex; }
body.chat-history-mode .ws-slot[data-ws-mode="chat"]       { display: flex; }

/* ---- Panel header — each slot names itself (no JS-driven header) ---- */
.ws-slot-head {
    flex: none;
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 2px 2px 10px;
    border-bottom: 1px solid var(--ws-glass-border);
    color: var(--ws-text);
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.ws-slot-head i { font-size: 0.85rem; color: var(--ws-accent); }

/* ============================================================================
   4. SLOT CONTENT — the panel internals (CTA, search, filters, list, meta)
   ============================================================================ */

/* ---- CTA — one primary action per service, identical shape everywhere ---- */
.ws-sidebar .ws-cta {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    height: 40px;
    padding: 0 14px;
    border: 0;
    border-radius: var(--ws-radius-md);
    background: var(--ws-gradient-cta);
    color: var(--ws-cta-ink);       /* dark ink on cyan — never white (contrast) */
    font-size: 0.9rem;
    font-weight: 700;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    transition: filter var(--ws-t-fast);
}

.ws-cta:hover { filter: brightness(1.06); }
.ws-cta:active { filter: brightness(0.98); }

/* ---- Search ---- */
.ws-search {
    flex: none;
    position: relative;
    display: flex;
    align-items: center;
}

.ws-search-ic {
    position: absolute;
    left: 11px;
    font-size: 0.78rem;
    color: var(--ws-text-subtle);
    pointer-events: none;
}

.ws-sidebar .ws-search-input {
    width: 100%;
    height: 36px;
    padding: 0 12px 0 32px;
    border-radius: var(--ws-radius-sm);
    border: 1px solid var(--ws-glass-border);
    background: var(--ws-glass-bg);
    color: var(--ws-text);
    font-size: 0.85rem;
    outline: none;
    transition: border-color var(--ws-t-fast), background var(--ws-t-fast);
}

.ws-search-input::placeholder { color: var(--ws-text-subtle); }

.ws-search-input:focus {
    border-color: var(--ws-glass-border-hover);
    background: var(--ws-glass-hover);
}

/* ---- Filter segments — only where a service actually has them (docs/blog).
        The chat slot uses the lighter .ws-scope--label variant (see §8). ---- */
.ws-sidebar .ws-scope {
    flex: none;
    display: flex;
    gap: 2px;
    padding: 3px;
    border-radius: var(--ws-radius-sm);
    background: var(--ws-glass-bg);
    border: 1px solid var(--ws-glass-border);
}

/* .doc-tab-btn is the class documents + blog already use; restyle, don't rename,
   so their JS (which queries .doc-tab-btn) is untouched. */
.ws-sidebar .ws-scope .doc-tab-btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 6px 8px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--ws-text-muted);
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--ws-t-fast), color var(--ws-t-fast);
}

.ws-sidebar .ws-scope .doc-tab-btn i { font-size: 0.72rem; }

.ws-sidebar .ws-scope .doc-tab-btn:hover {
    color: var(--ws-text);
    background: var(--ws-glass-hover);
}

.ws-sidebar .ws-scope .doc-tab-btn.active {
    background: rgba(0, 242, 254, 0.1);
    color: var(--ws-accent);
}

.ws-sidebar .ws-scope .trash-count-badge {
    min-width: 16px;
    padding: 0 4px;
    border-radius: var(--ws-radius-pill);
    background: var(--ws-glass-hover);
    color: var(--ws-text-muted);
    font-size: 0.66rem;
    line-height: 16px;
    text-align: center;
}

/* ---- The scrolling list ---- */
.ws-sidebar .ws-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    margin: 0 -4px;
    padding: 0 4px;
}

.ws-list::-webkit-scrollbar { width: 6px; }
.ws-list::-webkit-scrollbar-track { background: transparent; }
.ws-list::-webkit-scrollbar-thumb {
    background: var(--ws-glass-border);
    border-radius: var(--ws-radius-pill);
}
.ws-list::-webkit-scrollbar-thumb:hover { background: var(--ws-glass-border-hover); }

/* ---- Slot meta line (e.g. "3 files") ---- */
.ws-meta {
    flex: none;
    padding: 8px 2px 10px;
    font-size: 0.72rem;
    color: var(--ws-text-subtle);
    white-space: nowrap;
    overflow: hidden;
}

/* ============================================================================
   5. COLLAPSED — hide the panel, keep the icon rail (the VS Code behaviour)
   ============================================================================
   `body.sidebar-collapsed` is owned by workspace.js. Collapsing does NOT shrink
   everything into a cramped 72px column any more — it simply hides the contextual
   panel and leaves the icon rail, which is what a two-column shell is for. The main
   view reclaims the panel's width.
   ============================================================================ */

body.sidebar-collapsed .ws-sidebar {
    width: var(--ws-rail-icon);
}

body.sidebar-collapsed .ws-panel {
    display: none;
}

body.immersive-mode.sidebar-collapsed.chat-mode .chat-view-wrapper,
body.immersive-mode.sidebar-collapsed.chat-history-mode .chat-view-wrapper,
body.immersive-mode.sidebar-collapsed.documents-mode #documents-view,
body.immersive-mode.sidebar-collapsed.blog-mode #blog-writer-view {
    left: var(--ws-rail-icon);
}

/* The collapse chevron flips to point right (expand). */
body.sidebar-collapsed .ws-rail-collapse i {
    transform: rotate(180deg);
}

/* ============================================================================
   6. MOBILE DRAWER BACKDROP (desktop: inert)
   ============================================================================ */

.ws-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-workspace-rail, 450) - 1);
    background: rgba(0, 0, 0, 0.55);
    opacity: 0;
    transition: opacity var(--ws-t-base);
}


/* ============================================================================
   7. CHAT SLOT EXTRAS
   The chat slot has no filter segments, so its scope row is a plain label +
   refresh action.
   ============================================================================ */

.ws-sidebar .ws-scope--label {
    justify-content: space-between;
    align-items: center;
    padding: 3px 8px 3px 10px;
    background: transparent;
    border-color: transparent;
}

.ws-sidebar .ws-scope-title {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ws-text-subtle);
}

.ws-sidebar .ws-scope-action {
    width: 24px;
    height: 24px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--ws-text-subtle);
    font-size: 0.72rem;
    cursor: pointer;
    transition: color var(--ws-t-fast), background var(--ws-t-fast);
}

.ws-sidebar .ws-scope-action:hover {
    color: var(--ws-text);
    background: var(--ws-glass-hover);
}

/* Clear-search button (chat) */
.ws-sidebar .ws-search-clear {
    position: absolute;
    right: 6px;
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: 5px;
    background: transparent;
    color: var(--ws-text-subtle);
    font-size: 0.72rem;
    cursor: pointer;
}

.ws-sidebar .ws-search-clear:hover {
    background: var(--ws-glass-hover);
    color: var(--ws-text);
}

/* "View all history" — sits under the session list, inside the scroll area. */
.ws-sidebar .sidebar-viewall {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    margin-top: 4px;
    padding: 8px 12px;
    border: 0;
    border-radius: var(--ws-radius-sm);
    background: transparent;
    color: var(--ws-text-muted);
    font-size: 0.82rem;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background var(--ws-t-fast), color var(--ws-t-fast);
}

.ws-sidebar .sidebar-viewall:hover {
    background: var(--ws-glass-hover);
    color: var(--ws-text);
}

.ws-sidebar .sidebar-viewall i { font-size: 0.76rem; }

/* Initial "Loading chats…" state (chat.js replaces it with rows or an empty state) */
.ws-sidebar .sidebar-sessions-loading {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px;
    color: var(--ws-text-subtle);
    font-size: 0.82rem;
}

/* ============================================================================
   8. THE ONE CARD — .ws-item
   ----------------------------------------------------------------------------
   Every list row in the panel: a chat session, a document, a Hub post, a Hub
   asset. FOUR designs used to sit in this one sidebar:

     chat  .chat-item        transparent  r9px   pad 10/12/10/14  hover-ghost-icon
     docs  .document-item    white .03    r14px  pad 12/14        hover-icon + slide
                             + translateX(4px) lift + 2 shadows + shimmer sweep
                             + docSlideIn entrance with 11 stagger delays
     Hub   .blog-draft-item  navy .4      r8px   pad 12           ALWAYS-VISIBLE
                             + a visible cyan border at rest        text buttons
                             + a divider + a permanent 3rd row (2x taller)
     Hub   .blog-asset-item  no card at all — a table row with a bottom border

   Five radii, four backgrounds, three action patterns, three danger reds.
   Now: one of each. The action pattern is chat's — a ghost icon that fades in on
   hover — because it is the calmest and it keeps every row the same height.
   ============================================================================ */

.ws-sidebar .ws-item {
    position: relative;
    display: block;
    width: 100%;
    margin: 0 0 4px;
    padding: 10px 12px 10px 14px;
    border: 1px solid transparent;
    border-radius: var(--ws-radius-sm);
    background: transparent;
    text-align: left;
    cursor: pointer;
    overflow: hidden;
    transition: background var(--ws-t-fast), border-color var(--ws-t-fast);
}

.ws-sidebar .ws-item:hover {
    background: var(--ws-glass-hover);
}

.ws-sidebar .ws-item:focus-visible {
    outline: none;
    border-color: var(--ws-glass-border-hover);
}

.ws-sidebar .ws-item.is-active {
    background: rgba(0, 242, 254, 0.09);
}

/* The active marker — the same accent bar the rail uses. */
.ws-sidebar .ws-item.is-active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 10px;
    bottom: 10px;
    width: 2.5px;
    border-radius: 0 3px 3px 0;
    background: var(--ws-accent);
}

.ws-sidebar .ws-item__title {
    display: block;
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.35;
    color: var(--ws-text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    padding-right: 4px;
    transition: padding-right var(--ws-t-fast);
}

.ws-sidebar .ws-item.is-active .ws-item__title {
    color: var(--ws-text);
}

/* Make room for the actions as they fade in — never reflow the row. */
.ws-sidebar .ws-item:hover .ws-item__title {
    padding-right: 26px;
}

.ws-sidebar .ws-item__meta {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 4px;
    font-size: 11px;
    line-height: 1.4;
    color: var(--ws-text-subtle);
    font-variant-numeric: tabular-nums;
}

.ws-sidebar .ws-item__meta > * {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* A tag (the Hub's category) — the only coloured thing in the meta line. */
.ws-sidebar .ws-item__tag {
    color: var(--ws-accent);
}

/* ---- Actions: ONE pattern. Ghost icons, revealed on hover. ---- */

.ws-sidebar .ws-item__actions {
    position: absolute;
    top: 8px;
    right: 8px;
    display: flex;
    gap: 2px;
    opacity: 0;
    transition: opacity var(--ws-t-fast);
}

.ws-sidebar .ws-item:hover .ws-item__actions,
.ws-sidebar .ws-item:focus-within .ws-item__actions {
    opacity: 1;
}

.ws-sidebar .ws-item__btn {
    width: 24px;
    height: 24px;
    display: grid;
    place-items: center;
    border: 1px solid var(--ws-border-control);
    border-radius: 7px;
    background: var(--ws-glass-bg);
    color: var(--ws-text-subtle);
    font-size: 10px;
    cursor: pointer;
    transition: background var(--ws-t-fast), color var(--ws-t-fast);
}

.ws-sidebar .ws-item__btn:hover {
    background: rgba(0, 242, 254, 0.14);
    color: var(--ws-accent);
}

/* ONE danger red (there used to be three: #ff6b6b, #f72585, #ff5252). */
.ws-sidebar .ws-item__btn--danger:hover {
    background: rgba(239, 68, 68, 0.16);
    color: var(--ws-danger-text);
}

/* Touch devices have no hover — the actions must always be reachable. */
@media (hover: none) {
    .ws-sidebar .ws-item__actions { opacity: 1; }
    .ws-sidebar .ws-item__title   { padding-right: 26px; }
}

/* ---- --media modifier: the Hub's asset thumbnail. Same card, plus an image. */

.ws-sidebar .ws-item--media {
    display: flex;
    align-items: center;
    gap: 10px;
}

.ws-sidebar .ws-item__thumb {
    flex: none;
    width: 36px;
    height: 36px;
    border-radius: var(--ws-radius-sm);
    background-size: cover;
    background-position: center;
    background-color: var(--ws-glass-bg);
    border: 1px solid var(--ws-glass-border);
}

.ws-sidebar .ws-item--media .ws-item__body {
    flex: 1;
    min-width: 0;
}

/* ---- States the generation card needs (documents' task cards) ---- */

.ws-sidebar .ws-item.is-busy::before {
    content: '';
    position: absolute;
    left: 0;
    top: 10px;
    bottom: 10px;
    width: 2.5px;
    border-radius: 0 3px 3px 0;
    background: var(--ws-accent);
    opacity: 0.85;
}

.ws-sidebar .ws-item.is-done::before { background: var(--ws-success); }
.ws-sidebar .ws-item.is-failed::before { background: var(--ws-danger); }

/* ============================================================================
   9. THE ONE BUTTON — .ws-btn
   ----------------------------------------------------------------------------
   FOUR competing "primary" fills existed:

     --doc-gradient-button   #7b68ee -> #00d4ff   .doc-btn-primary (docs + Hub headers)
     --ws-gradient-cta       #00f2fe -> #58b9ff   .ws-cta (the rail)
     flat #00f2fe                                 #publish-blog-btn (Hub override)
     #8b5cf6 -> #7c3aed  (PURPLE)                 .btn-primary — declared in an
                                                  inline <style> in base.html, and
                                                  used by every workspace modal

   So generating a document walked you through a purple modal CTA, a purple->cyan
   header button, a flat-cyan Publish, and a cyan->blue rail CTA. Four primaries
   in one flow.

   #publish-blog-btn also inherited `color: #ffffff` from .doc-btn-primary while
   overriding only the background — white text on #00f2fe, a contrast failure.
   The .ws-cta token --ws-cta-ink exists precisely to prevent that, and .ws-btn
   uses it, so the bug cannot recur by construction.

   Now: ONE primary, ONE secondary, ONE ghost, ONE danger.
   ============================================================================ */

.ws-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 36px;
    padding: 0 14px;
    /* EVERY button carries a visible boundary at rest. A transparent border on a
       transparent fill is invisible: it reads as a text label, not a control. */
    border: 1px solid var(--ws-border-control);
    border-radius: var(--ws-radius-sm);
    background: var(--ws-glass-bg);
    color: var(--ws-text-secondary);
    font-family: inherit;
    font-size: 0.86rem;
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--ws-t-fast), border-color var(--ws-t-fast),
                color var(--ws-t-fast), filter var(--ws-t-fast);
}

.ws-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.ws-btn i { font-size: 0.82em; }

/* PRIMARY — the one call to action. Dark ink on cyan, never white. */
.ws-btn--primary {
    background: var(--ws-gradient-cta);
    color: var(--ws-cta-ink);
    border-color: transparent;
    font-weight: 700;
}
.ws-btn--primary:hover:not(:disabled)  { filter: brightness(1.06); }
.ws-btn--primary:active:not(:disabled) { filter: brightness(0.98); }

/* SECONDARY — glass. The default for anything that is not THE action. */
.ws-btn--secondary {
    background: var(--ws-glass-bg);
    border-color: var(--ws-border-control);
    color: var(--ws-text-secondary);
}
.ws-btn--secondary:hover:not(:disabled) {
    background: var(--ws-glass-hover);
    border-color: var(--ws-border-control-hover);
    color: var(--ws-text);
}

/* GHOST — the quietest variant, but still an obvious control: it keeps the box
   and just drops the fill. */
.ws-btn--ghost {
    background: transparent;
    border-color: var(--ws-border-control);
    color: var(--ws-text-muted);
}
.ws-btn--ghost:hover:not(:disabled) {
    background: var(--ws-glass-hover);
    border-color: var(--ws-border-control-hover);
    color: var(--ws-text);
}

/* DANGER — the ONLY place semantic colour survives. */
.ws-btn--danger {
    background: rgba(239, 68, 68, 0.12);
    border-color: rgba(239, 68, 68, 0.28);
    color: var(--ws-danger-text);
}
.ws-btn--danger:hover:not(:disabled) {
    background: rgba(239, 68, 68, 0.2);
    border-color: rgba(239, 68, 68, 0.45);
}

/* Icon-only */
.ws-btn--icon { width: 36px; padding: 0; }

/* On narrow headers the labels collapse and the icons carry the meaning. */
@media (max-width: 900px) {
    .ws-btn .btn-label { display: none; }
    .ws-btn { width: 36px; padding: 0; }
}
