/* Workspace area styles. Loaded globally from App.razor via the relative
   _content/Smoothin.PrivateModels.Workspace.Client/workspace.css path (base-href safe under tenant PathBase).
   Reuses the app's token set from app.css (--bg, --surface, --border, --text, --text-muted, --accent, ...). */

.workspace-page {
    /* width:100% matters: the parent .app-content is a column flexbox, and the auto margins would otherwise
       disable stretching and shrink-wrap this container to its content. */
    width: 100%;
    max-width: 1100px;
    margin: 0 auto;
    padding: 24px 16px;
}

.workspace-muted {
    color: var(--text-muted);
}

.workspace-hero h1 {
    margin: 0 0 0.35rem;
    font-size: 1.6rem;
    letter-spacing: -0.02em;
}

/* Turned-off panel — same shape as the Ask AI one. */
.workspace-disabled {
    max-width: 460px;
    margin: 12vh auto 0;
    text-align: center;
    color: var(--text-muted, #64748b);
}

.workspace-disabled h2 {
    color: var(--text, #0f172a);
    margin-bottom: 0.4rem;
}

/* Loading skeleton so an island never shows a blank pane while its first fetch is in flight. */
.workspace-skeleton {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    padding-top: 0.4rem;
}

.workspace-skeleton-bar {
    height: 16px;
    border-radius: 6px;
    background: linear-gradient(90deg, var(--surface-2, #f1f5f9) 25%, #e8edf3 50%, var(--surface-2, #f1f5f9) 75%);
    background-size: 200% 100%;
    animation: workspace-shimmer 1.2s ease-in-out infinite;
}

@keyframes workspace-shimmer {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}

/* ---- quick capture ---- */
.ws-capture {
    display: flex;
    gap: 0.6rem;
    margin: 0.75rem 0 1.5rem;
}

.ws-capture-input {
    flex: 1;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    font: inherit;
    background: var(--surface, #fff);
}

.ws-capture-input:focus {
    outline: none;
    border-color: var(--accent, #4f46e5);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #4f46e5) 15%, transparent);
}

/* The add rows now use the transcribe box; inside them it keeps the capture row's larger sizing. */
.ws-capture .ws-transcribe-box { flex: 1; }

.ws-capture .ws-transcribe-input {
    padding: 0.7rem 0.9rem;
    border-radius: 10px;
    font-size: inherit;
}

.ws-capture .ws-transcribe-input:focus {
    border-color: var(--accent, #4f46e5);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #4f46e5) 15%, transparent);
}

.ws-capture-btn { white-space: nowrap; }

.ws-mic { white-space: nowrap; }

/* ---- the transcribe text box (input + mic as ONE control) ---- */
/* align-items: stretch is the whole trick: the mic is exactly as tall as the box, whatever that is. */
.ws-transcribe-box { display: flex; gap: 0.4rem; align-items: stretch; min-width: 0; }

.ws-transcribe-input {
    flex: 1;
    min-width: 0;
    padding: 0.35rem 0.6rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 8px;
    font: inherit;
    font-size: 0.85rem;
    background: var(--surface, #fff);
}

.ws-transcribe-input:focus {
    outline: none;
    border-color: var(--accent, #4f46e5);
}

/* The buttons bring app-wide padding that would make them taller than the input; inside this control the
   input decides the height and the mic follows it. */
.ws-transcribe-box .ws-mic { padding: 0 0.7rem; font-size: 0.85rem; }

.ws-tree-search { flex: 1; }

.ws-mic-recording {
    animation: ws-pulse 1.2s ease-in-out infinite;
}

@keyframes ws-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.65; }
}

/* ---- sections / headings ---- */
.ws-section { margin-top: 1.75rem; }
.ws-section h2 { font-size: 1.05rem; margin-bottom: 0.6rem; }

.ws-count-pill {
    display: inline-block;
    min-width: 22px;
    text-align: center;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    font-size: 0.75rem;
    background: var(--surface-2, #f1f5f9);
    color: var(--text-muted, #64748b);
}

.ws-module-head { display: flex; align-items: center; gap: 0.8rem; margin-bottom: 0.5rem; }
.ws-module-head h1 { margin: 0; font-size: 1.45rem; letter-spacing: -0.02em; }

.ws-module-icon { margin-right: 0.25rem; }

.ws-archived-banner {
    padding: 0.2rem 0.7rem;
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 600;
    background: #fef3c7;
    color: #92400e;
}

/* ---- item lists ---- */
.ws-list {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 12px;
    background: var(--surface, #fff);
    overflow: hidden;
}

.ws-row {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.6rem 0.9rem;
    border-bottom: 1px solid var(--border, #eef1f4);
    text-decoration: none;
    color: var(--text, #0f172a);
    font-size: 0.92rem;
}

.ws-row:last-child { border-bottom: none; }
a.ws-row:hover, div.ws-row:hover { background: var(--surface-2, #f8fafc); }

.ws-row-header {
    background: var(--surface-2, #f8fafc);
    font-weight: 600;
    color: var(--text-muted, #64748b);
    font-size: 0.82rem;
    cursor: pointer;
}

.ws-row-icon { flex-shrink: 0; }

.ws-row-title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
}

.ws-done { text-decoration: line-through; color: var(--text-muted, #94a3b8); }

.ws-row-meta {
    flex-shrink: 0;
    color: var(--text-muted, #94a3b8);
    font-size: 0.8rem;
}

.ws-status-pill {
    flex-shrink: 0;
    padding: 0.12rem 0.55rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    background: var(--surface-2, #f1f5f9);
    color: var(--text-muted, #64748b);
}

.ws-row-action { max-width: 130px; }

.ws-row-delete {
    border: none;
    background: transparent;
    color: var(--text-muted, #94a3b8);
    cursor: pointer;
    font-size: 0.9rem;
    padding: 0.25rem 0.45rem;
    border-radius: 6px;
}

.ws-row-delete:hover { background: #fef2f2; color: var(--danger, #dc2626); }

/* ---- bulk action bar ---- */
.ws-bulkbar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.6rem;
    border: 1px solid color-mix(in srgb, var(--accent, #4f46e5) 30%, transparent);
    background: color-mix(in srgb, var(--accent, #4f46e5) 6%, transparent);
    border-radius: 10px;
    font-size: 0.88rem;
}

.ws-select {
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 8px;
    font: inherit;
    font-size: 0.85rem;
    background: var(--surface, #fff);
}

.ws-empty {
    padding: 2.2rem 1rem;
    text-align: center;
    color: var(--text-muted, #94a3b8);
    border: 1px dashed var(--border, #e6e8ec);
    border-radius: 12px;
}

/* ---- toast ---- */
.ws-toast {
    position: fixed;
    bottom: 1.2rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.6rem 1rem;
    border-radius: 10px;
    background: var(--text, #0f172a);
    color: #fff;
    font-size: 0.88rem;
    box-shadow: 0 10px 30px rgba(15, 23, 42, 0.25);
    z-index: 60;
    animation: ws-toast-in 0.18s ease;
}

.ws-toast a { color: #a5b4fc; font-weight: 600; text-decoration: none; }
.ws-toast-error { background: var(--danger, #dc2626); }

@keyframes ws-toast-in {
    from { opacity: 0; transform: translate(-50%, 8px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ---- the list column ----
   The 45/45 master-detail split is gone: the item pane is a modal now (see .ws-detail-modal), so the list
   keeps the full width whether or not something is open, and the page never re-lays-out to make room. */
.ws-split { display: flex; gap: 1.25rem; align-items: flex-start; }
.ws-split-list { flex: 1; min-width: 0; }

/* The list IS the page here, so it gets 90% of the screen (centred by the container's auto margins) rather
   than the 1100px reading column. */
.ws-page-wide { max-width: 90%; }

@media (max-width: 900px) {
    .ws-page-wide { max-width: none; }   /* every pixel counts — drop the 5% side margins */
}

.ws-detail {
    flex: 1;
    min-width: 0;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 14px;
    background: var(--surface, #fff);
    padding: 1rem 1.2rem 1.4rem;
}

.ws-detail-head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.6rem; }
.ws-detail-back { padding: 0.3rem 0.6rem; }
.ws-detail-close { flex-shrink: 0; }

.ws-detail-title {
    flex: 1;
    min-width: 0;
    font: inherit;
    font-size: 1.15rem;
    font-weight: 700;
    border: 1px solid transparent;
    border-radius: 8px;
    padding: 0.3rem 0.5rem;
    background: transparent;
}

.ws-detail-title:hover { border-color: var(--border, #e6e8ec); }
.ws-detail-title:focus { outline: none; border-color: var(--accent, #4f46e5); background: var(--surface, #fff); }

/* The task number (#42) in front of the title — reference text, not part of the editable name. */
.ws-detail-number {
    align-self: center;
    font-weight: 700;
    color: var(--text-muted, #94a3b8);
    white-space: nowrap;
}

/* The title is a transcribe box now; the wrapper takes the title's old place in the head row, and the
   input inside keeps the exact ws-detail-title look (that rule is later in this sheet, so it wins). */
.ws-detail-title-box { flex: 1; min-width: 0; }

.ws-detail-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem;
    align-items: end;
    padding-bottom: 0.7rem;
    border-bottom: 1px solid var(--border, #eef1f4);
}

.ws-detail-toolbar label {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text-muted, #94a3b8);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ws-detail-done { flex-direction: row !important; align-items: center; gap: 0.4rem !important; font-size: 0.9rem !important; text-transform: none !important; }

/* One control height across the toolbar: the selects (Status, Priority, Assignee, Kind) used to be visibly
   shorter than the Folder / Move buttons beside them. 42px is what .btn-secondary's own padding adds up to. */
.ws-detail-toolbar .ws-select,
.ws-detail-toolbar .btn-secondary { min-height: 42px; }

.ws-detail-meta { margin: 0.6rem 0 0.2rem; font-size: 0.8rem; color: var(--text-muted, #94a3b8); }
.ws-detail-error { color: var(--danger, #dc2626); font-size: 0.85rem; }

/* ---- markdown blocks ---- */
.ws-md-block { margin-top: 1.1rem; }
.ws-md-head { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 0.4rem; }
.ws-md-title { margin: 0; font-size: 0.95rem; flex: 1; }
.ws-md-title-input {
    flex: 1;
    font: inherit;
    font-weight: 600;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 8px;
}
.ws-md-actions { display: flex; align-items: center; gap: 0.45rem; margin-left: auto; }
.ws-md-empty { margin: 0.2rem 0 0; font-size: 0.85rem; }
.ws-dirty { color: #d97706; font-size: 0.78rem; font-weight: 600; }

.ws-icon-btn {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 8px;
    background: var(--surface, #fff);
    color: var(--text-muted, #64748b);
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}

.ws-icon-btn svg { width: 14px; height: 14px; }
.ws-icon-btn:hover { background: var(--surface-2, #f1f5f9); color: var(--accent, #4f46e5); }

.ws-md-view { cursor: text; }

/* ---- two-step delete confirm ---- */
.ws-confirm-wrap { position: relative; display: inline-flex; align-items: center; gap: 0.35rem; }

.ws-confirm-backdrop {
    position: fixed;
    inset: 0;
    z-index: 70;
    background: transparent;
}

.ws-delete-armed { opacity: 0.35; }

.ws-confirm-btn {
    position: relative;
    z-index: 71;
    display: grid;
    place-items: center;
    min-width: 28px;
    height: 28px;
    padding: 0 0.45rem;
    border: none;
    border-radius: 8px;
    background: #16a34a;
    color: #fff;
    font-weight: 700;
    cursor: pointer;
    animation: dropdown-in 0.12s ease;
}

.ws-confirm-btn:hover { background: #15803d; }

@keyframes dropdown-in {
    from { opacity: 0; transform: translateY(-3px); }
    to   { opacity: 1; transform: translateY(0); }
}
.ws-add-section { margin-top: 1rem; align-self: flex-start; }

/* The flex-column rules that used to pin "Add section" to the bottom of the full-height split pane went with
   the split: the modal scrolls its own content, so ordinary block flow is all this needs. */

.ws-files { margin-top: 1.3rem; }
.ws-file-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.35rem 0; font-size: 0.88rem; }

/* Linked knowledge notes (tasks/todos) — rows reuse .ws-file-row so the two sections read as one family. */
.ws-links { margin-top: 1.3rem; }

/* Sub-entries under "Prompts" in the settings menu (the menu chrome itself lives in app.css). */
.settings-menu-subitem { padding-left: 2.6rem; font-size: 0.88rem; }

/* The prompts settings page: the create block stacks title, textarea, button. */
.ws-prompt-create { margin-bottom: 0.5rem; }
.ws-prompt-panel { flex-direction: column; align-items: stretch; }
.ws-prompt-add { margin-top: 0.5rem; }

/* ---- the AI prompt section (tasks with Executor=AI) ---- */
.ws-prompt { margin-top: 1.3rem; }

/* The summary doubles as the section heading, styled like its .ws-md-title siblings. display:flex strips
   the browser's own disclosure triangle, so the chevron is drawn explicitly — the same ▸/▾ the trees use,
   which is what makes "this collapses" readable at a glance. */
.ws-prompt summary {
    cursor: pointer;
    font-size: 0.95rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    list-style: none;
}

.ws-prompt summary::-webkit-details-marker { display: none; }

.ws-prompt summary::before {
    content: "▸";
    font-size: 0.8rem;
    color: var(--text-muted, #94a3b8);
}

.ws-prompt[open] > summary::before { content: "▾"; }

/* The non-collapsible variant (Task Prompt): same heading row, minus chevron and toggle. */
.ws-prompt-head {
    font-size: 0.95rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.ws-prompt-empty-pill {
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--text-muted, #94a3b8);
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 999px;
    padding: 0.05rem 0.5rem;
}

.ws-prompt-body { margin-top: 0.5rem; }

/* Right end of the heading row: the 📚 picker in read mode, the state label + Done while editing. */
.ws-prompt-summary-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Plain text, shown plainly — pre-wrap keeps the author's line breaks without ever rendering markup. */
.ws-prompt-view {
    margin: 0;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    background: var(--surface, #fff);
    font: inherit;
    font-size: 0.88rem;
    white-space: pre-wrap;
    word-break: break-word;
    cursor: pointer;
}

/* Height comes from the rows attribute (base 8, task 5), so no min-height here. */
.ws-prompt-editor {
    width: 100%;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    font: inherit;
    font-size: 0.88rem;
    resize: vertical;
    background: var(--surface, #fff);
}

.ws-prompt-editor:focus { outline: none; border-color: var(--accent, #4f46e5); }

/* Picker rows: a title line with a one-line grey snippet under it. */
.ws-prompt-list { display: flex; flex-direction: column; gap: 0.25rem; }

.ws-prompt-row {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.15rem;
    width: 100%;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    background: var(--surface, #fff);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.ws-prompt-row:hover { border-color: var(--accent, #4f46e5); }

.ws-prompt-snippet {
    font-size: 0.78rem;
    color: var(--text-muted, #94a3b8);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

/* "＋ Add file(s)": a <label for> pointing at the hidden input, dressed as the button it acts like. */
.ws-attach-btn {
    display: inline-flex;
    align-items: center;
    width: fit-content;
    margin-top: 0.5rem;
    cursor: pointer;
    font-size: 0.85rem;
}

.ws-file-thumb {
    width: 48px;
    height: 48px;
    object-fit: cover;
    border-radius: 8px;
    border: 1px solid var(--border, #e6e8ec);
    flex-shrink: 0;
}

/* An attachment row opens what it points at. Same zoom-in cursor and accent hover the AI-result thumbnails
   wear (.ws-shot img), so the two galleries answer a click the same way and read as one gesture. */
.ws-file-zoom { cursor: zoom-in; }
.ws-file-zoom:hover { border-color: var(--accent, #6366f1); }

/* Icon + name as ONE target for the download-only kinds (PDF, Office, opaque binaries). */
.ws-file-link {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex: 1;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}

.ws-file-link:hover { color: var(--accent, #4f46e5); }
.ws-file-link:hover .ws-row-title { text-decoration: underline; }

/* ---- AI result screenshots ----
   Evidence attached to an agent's report, rendered inside the result block rather than in the Files strip:
   these are not files the user filed on the task. Thumbnails are cropped from the TOP LEFT, not centred —
   a screenshot's subject is almost always its heading, toolbar or first row, and centre-cropping a tall
   page reliably shows the one band of it that means nothing. */
.ws-shots {
    display: flex;
    flex-wrap: wrap;
    gap: 0.7rem;
    margin-top: 0.9rem;
}

.ws-shot { margin: 0; width: min(220px, 100%); }

.ws-shot img {
    display: block;
    width: 100%;
    height: 130px;
    object-fit: cover;
    object-position: top left;
    border-radius: 10px;
    border: 1px solid var(--border, #e6e8ec);
    background: var(--surface-2, #f1f5f9);
    cursor: zoom-in;
}

.ws-shot img:hover { border-color: var(--accent, #6366f1); }

.ws-shot figcaption {
    margin-top: 0.3rem;
    font-size: 0.76rem;
    line-height: 1.35;
    color: var(--text-muted, #64748b);
}

/* Its own layer above the other modals (80) and darker than them: an enlarged screenshot is read against
   the backdrop, not through it. */
.ws-lightbox-backdrop { z-index: 90; background: rgba(15, 23, 42, 0.82); }

.ws-lightbox {
    position: relative;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    max-width: min(1280px, 96vw);
}

.ws-lightbox img {
    max-width: 100%;
    max-height: 84vh;
    object-fit: contain;
    border-radius: 12px;
    background: #0f172a;
    cursor: zoom-out;
}

.ws-lightbox figcaption {
    text-align: center;
    font-size: 0.85rem;
    color: #e2e8f0;
    cursor: text;
}

.ws-lightbox-close {
    position: absolute;
    top: 0.45rem;
    right: 0.45rem;
    color: #fff;
    background: rgba(15, 23, 42, 0.65);
    border-radius: 8px;
}

/* On paper a cropped thumbnail is useless — nothing can be clicked to enlarge it. */
@media print {
    .ws-shot { width: 100%; }
    .ws-shot img { height: auto; max-height: none; object-fit: contain; cursor: default; }
}

.ws-audio { max-width: 260px; height: 32px; }

/* ---- notes / todo ---- */
.ws-notes-new { margin: 0; flex: 2; min-width: 260px; }
.ws-pager { display: flex; align-items: center; gap: 0.7rem; justify-content: center; margin-top: 0.8rem; font-size: 0.85rem; color: var(--text-muted, #64748b); }

.ws-move-btn {
    border: none;
    background: transparent;
    color: var(--text-muted, #94a3b8);
    cursor: pointer;
    padding: 0.15rem 0.3rem;
    border-radius: 6px;
    font-size: 0.85rem;
}
.ws-move-btn:hover:not(:disabled) { background: var(--surface-2, #f1f5f9); color: var(--text, #0f172a); }
.ws-move-btn:disabled { opacity: 0.3; cursor: default; }

.ws-todo-left { margin-top: 0.5rem; font-size: 0.82rem; }

/* ---- notes workbench: full width & height, tree pinned left ---- */
/* ---- the standalone task page ----
   A board card opened in its own tab (Ctrl-click / right-click). There is no app shell here, so the pane IS
   the document: it takes the tab's full width and height, inset by an even gap on every side. No reading
   column — this is a working surface, and the toolbar, the files strip and a wide code block all want the
   room. The gap is what keeps it a bordered card instead of content pressed against the window frame; the
   border and the 14px corners are .ws-detail's own, so it looks like the pane it is everywhere else.

   min-height, not height: a short task still reaches the bottom edge, and a long one grows past it and lets
   the page scroll rather than trapping the scroll inside the card. */
.ws-bare { min-height: 100vh; }

.ws-task-page { padding: var(--ws-task-gap, 1rem); }

.ws-task-page .ws-detail { min-height: calc(100vh - 2 * var(--ws-task-gap, 1rem)); }

@media (max-width: 640px) {
    .ws-task-page { --ws-task-gap: 0.6rem; }
}

.ws-page-full {
    max-width: none;
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

.ws-page-full .ws-module-head { flex-shrink: 0; }

.ws-notes-workbench {
    flex: 1;
    min-height: 0;
    display: flex;
    gap: 1rem;
    align-items: stretch;
    padding-bottom: 1rem;
}

.ws-tree-col {
    flex: 0 0 340px;
    min-width: 0;
    overflow-y: auto;
}

/* A note fills the pane: the detail column becomes a flex column whose body (the editor or the rendered
   page) takes every pixel the head and meta line leave behind. overflow-y moves INTO the body so the
   editor's own toolbar stays pinned while the text scrolls. */
.ws-notes-workbench .ws-detail {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.ws-notes-workbench .ws-detail-head,
.ws-notes-workbench .ws-detail-meta,
.ws-notes-workbench .ws-detail-error { flex-shrink: 0; }

/* THE note is the single scroller: the editor itself is height:auto (no scrollbar of its own), so a long
   note scrolls as one continuous page — while the action bar and the editor toolbar stay stuck to the top,
   floating above the text. */
.ws-note {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow-y: auto;

    /* The editor's toolbar strip (46px) plus the 1px frame border. Read mode reserves this much so the
       text does not jump when the editor opens; if a Toast UI upgrade changes the toolbar height, this
       is the single value to correct. */
    --ws-editor-chrome: 47px;

    /* Height of the action bar — what the sticky toolbar has to clear. */
    --ws-note-bar: 50px;
}

/* Read mode: the whole area is the click target, so "click the page to edit" is literally true. It also
   repeats the editor's own content padding (18px 25px + the 1px border) — together with the reserved
   chrome above, every line lands on the same pixel in both modes. */
.ws-note-view {
    flex: 1 0 auto;
    cursor: text;
    margin-top: var(--ws-editor-chrome);
    padding: 18px 26px 2rem;
}

.ws-note-empty { display: grid; place-items: start center; }

.ws-note-bar {
    position: sticky;
    top: 0;
    z-index: 31;   /* above the floating toolbar, which in turn clears Toast UI's z-index:20 content */
    display: flex;
    align-items: center;
    gap: 0.6rem;
    justify-content: flex-end;
    padding-bottom: 0.4rem;
    flex-shrink: 0;
    min-height: 32px;
    background: var(--surface, #fff);
}

.ws-note-state { font-size: 0.78rem; color: var(--text-muted, #94a3b8); }
.ws-note-state-error { color: var(--danger, #dc2626); font-weight: 600; }

.ws-note-editor { flex: 0 0 auto; }

/* The toolbar floats: it sticks under the action bar for as long as the editor is on screen, so the
   formatting controls are always reachable no matter how far down the note you are. Toast UI's own
   containers are all overflow:visible, which is what lets sticky escape the editor box.

   The z-index is NOT arbitrary: Toast UI stamps `z-index: 20` on .ProseMirror itself, so anything meant
   to float above the text has to outrank 20. The wrapper is also transparent (only its inner
   .toastui-editor-defaultUI-toolbar is painted), so it needs that same fill or the text scrolls
   through the gap around the buttons. */
.ws-note-editor .toastui-editor-toolbar {
    position: sticky;
    top: var(--ws-note-bar);
    z-index: 30;
    background: #f7f9fc;
}

/* Nothing inside the editor may scroll on its own — that scrollbar is exactly what we removed. */
.ws-note-editor .toastui-editor-ww-container,
.ws-note-editor .toastui-editor-md-container,
.ws-note-editor .toastui-editor-main { overflow: visible; }

/* Same reasoning for the Markdown/WYSIWYG switch: it is the last element of the document, so on a long
   note it would only be reachable at the very bottom. Stuck to the bottom edge it stays one click away. */
.ws-note-editor .toastui-editor-mode-switch {
    position: sticky;
    bottom: 0;
    z-index: 30;
    background: var(--surface, #fff);
}

.ws-note-files { margin-top: 0.6rem; flex-shrink: 0; font-size: 0.85rem; }
.ws-note-files summary { cursor: pointer; color: var(--text-muted, #64748b); }
.ws-file-icon { font-size: 1.2rem; width: 24px; text-align: center; }

/* The editor host itself: a plain block that fills whatever box it is given. */
.ws-editor { min-height: 0; }
.ws-editor .toastui-editor-defaultUI { border-radius: 10px; }
.ws-md-editor { height: 320px; }

.ws-detail-placeholder {
    display: grid;
    place-items: center;
}

@media (max-width: 640px) {
    .ws-notes-workbench { flex-direction: column; }
    .ws-tree-col { flex: 0 0 auto; max-height: 40vh; }
}

/* ---- notes tree ---- */
.ws-tree-head { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.7rem; flex-wrap: wrap; }

.ws-tree-nodes {
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 12px;
    background: var(--surface, #fff);
    padding: 0.35rem 0.4rem;
    /* Was hidden. A node's "add a sub-item" menu is positioned out of its row and hidden would clip it away
       entirely; the padding above already keeps row backgrounds clear of the rounded corner, so the clip was
       doing no visible work. */
    overflow: visible;
}

.ws-tree-node {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.28rem 0.5rem;
    border-radius: 8px;
    font-size: 0.92rem;
}

.ws-tree-node:hover { background: var(--surface-2, #f8fafc); }
.ws-tree-selected { background: color-mix(in srgb, var(--accent, #4f46e5) 10%, transparent); }
.ws-tree-node-hot { outline: 2px solid var(--accent, #4f46e5); outline-offset: -2px; }

.ws-tree-chevron {
    flex-shrink: 0;
    width: 20px;
    border: none;
    background: transparent;
    color: var(--text-muted, #94a3b8);
    cursor: pointer;
    font-size: 0.8rem;
    padding: 0;
    text-align: center;
}

.ws-tree-leaf { cursor: default; font-size: 0.6rem; }

.ws-tree-title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
    border: none;
    background: transparent;
    font: inherit;
    font-weight: 500;
    color: var(--text, #0f172a);
    cursor: pointer;
    padding: 0;
}

.ws-tree-title:hover { color: var(--accent, #4f46e5); }

.ws-tree-add {
    flex-shrink: 0;
    border: none;
    background: transparent;
    color: var(--text-muted, #cbd5e1);
    cursor: pointer;
    border-radius: 6px;
    padding: 0.05rem 0.35rem;
    visibility: hidden;
}

.ws-tree-node:hover .ws-tree-add { visibility: visible; }
.ws-tree-add:hover { background: var(--surface-2, #f1f5f9); color: var(--accent, #4f46e5); }

/* The per-kind glyph. Sized down and muted: it labels the row, it is not the row. */
.ws-tree-kind {
    flex-shrink: 0;
    font-size: 0.72rem;
    line-height: 1;
    opacity: 0.75;
}

.ws-tree-add-open { visibility: visible; background: var(--surface-2, #f1f5f9); color: var(--accent, #4f46e5); }

.ws-tree-line {
    height: 6px;
    border-radius: 3px;
    margin: -2px 0.4rem;
}

/* ---- the "add a sub-item" type menu ----
   Anchored to the ＋ that opened it. z-index sits above .ws-confirm-backdrop (70) so the backdrop catches
   clicks everywhere EXCEPT the menu, which is what closes it without a document listener. */
.ws-menu-anchor { position: relative; display: inline-flex; flex-shrink: 0; }

/* Anchors at a LEFT edge (the tree's "＋ New Root Item"): the menu grows rightward instead, because the
   default right-alignment below would push most of it off the page. */
.ws-menu-left .ws-menu { left: 0; right: auto; }

.ws-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 71;
    min-width: 220px;
    max-width: 260px;
    padding: 0.3rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    background: var(--surface, #fff);
    box-shadow: 0 10px 28px rgba(15, 23, 42, 0.16);
    animation: dropdown-in 0.12s ease;
    text-align: left;
    cursor: default;
}

.ws-menu-label {
    margin: 0.3rem 0.5rem 0.2rem;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted, #94a3b8);
}

.ws-menu-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.4rem 0.5rem;
    border: none;
    border-radius: 7px;
    background: transparent;
    font: inherit;
    font-size: 0.88rem;
    color: var(--text, #0f172a);
    text-align: left;
    cursor: pointer;
}

.ws-menu-item:hover,
.ws-menu-item:focus-visible {
    background: color-mix(in srgb, var(--accent, #4f46e5) 10%, transparent);
    outline: none;
}

.ws-menu-icon { width: 1.1rem; text-align: center; flex-shrink: 0; }

.ws-menu-hint {
    margin: 0.25rem 0.5rem 0.15rem;
    font-size: 0.7rem;
    line-height: 1.35;
    color: var(--text-muted, #94a3b8);
}

/* The file input the menu's <label for> points at: reachable and focusable, never drawn. display:none would
   make it unfocusable and, in some browsers, unclickable through the label. */
.ws-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.ws-tree-line-hot { background: color-mix(in srgb, var(--accent, #4f46e5) 45%, transparent); }

/* ---- folder tree (tree-enabled trackers) & its workbench ----
   The wrapper exists only to hold the tree column. WITHOUT a tree it is display:contents, so the split
   stays a direct child of .workspace-page for layout purposes and none of the pre-tree rules above change
   meaning — that is what keeps flat modules pixel-identical to how they were. */
.ws-layout-passthrough { display: contents; }

.ws-tracker-workbench {
    flex: 1;
    min-height: 0;
    display: flex;
    gap: 1rem;
    align-items: stretch;
    padding-bottom: 1rem;
}

/* The list+detail half behaves exactly as it does without a tree, just inside the leftover width. */
.ws-tracker-workbench > .ws-split {
    flex: 1;
    min-width: 0;
    min-height: 0;
    align-items: stretch;
}

.ws-tracker-workbench .ws-split-list,
.ws-tracker-workbench .ws-detail { overflow-y: auto; }

/* ---- the item pane as a modal ----
   It used to be a column in the split, which cost the board half its width whenever something was open. Now
   it floats over the page and the board keeps every pixel underneath it. Wider and taller than the generic
   .ws-modal (that one is for short confirmations; this holds a whole editor) and it scrolls inside itself. */
.ws-detail-backdrop { align-items: start; padding: 2vh 0.75rem; }

/* Sized to the CONTENT it holds — a task's description, prompts, files and linked notes — rather than to the
   confirmation dialogs .ws-modal was built for. It takes very nearly the whole window: everything in it is
   either long-form text or a wide toolbar, and the board behind it is not what you are reading.

   HEIGHT, not just max-height. A content-sized modal is a different size for every task, so the toolbar and
   the close button move under the pointer as you page through them; a fixed one stays where your hand
   already is, and the pane inside scrolls when the content outgrows it. */
.ws-detail-modal {
    width: min(1800px, 97vw);
    height: 95vh;
    max-height: 95vh;
    display: flex;
    overflow: hidden;
    border-radius: 14px;
    box-shadow: 0 24px 64px rgba(15, 23, 42, 0.3);
    animation: dropdown-in 0.12s ease;
}

/* The pane inside drops the border it wore as a column (the modal carries the frame now) and takes the
   scrolling, so the header stays put while a long description moves. */
.ws-detail-modal .ws-detail {
    flex: 1;
    min-width: 0;
    max-height: 100%;
    overflow-y: auto;
    border: none;
    border-radius: 14px;
}

@media (max-width: 900px) {
    .ws-detail-backdrop { padding: 0; }
    /* height as well as max-height, or the desktop rule's 95vh would leave a strip of board on show. */
    .ws-detail-modal { width: 100%; height: 100vh; max-height: 100vh; border-radius: 0; }
    .ws-detail-modal .ws-detail { border-radius: 0; }
}

@media (max-width: 900px) {
    /* Three columns cannot survive here: the tree becomes a short scroller above the list. */
    .ws-tracker-workbench { flex-direction: column; }
    .ws-tracker-workbench > .ws-split { flex-direction: column; }
    .ws-tracker-workbench .ws-split-list,
    .ws-tracker-workbench .ws-detail { overflow-y: visible; }
}

.ws-folder-tree .ws-tree-nodes { padding-bottom: 0.5rem; }
.ws-tree-empty { margin: 0.4rem 0.5rem; font-size: 0.85rem; }
.ws-tree-unfiled { margin-top: 0.2rem; border-top: 1px dashed var(--border, #e6e8ec); padding-top: 0.4rem; }
.ws-tree-actions { display: flex; flex-shrink: 0; }

/* Hovering a folder row swaps its numbers for its buttons. The ＋ ✎ ⓘ 🗑 appear in the same corner the count
   pills occupy, and with a pill per active status there is not room for both — the numbers are for reading,
   the buttons are for the row you have already reached for, so the buttons win while the pointer is there.
   :has() keeps it to rows that actually HAVE buttons: "All …", "Unfiled" and the search matches carry no
   actions, and their numbers must not blink out under the pointer for nothing. */
.ws-folder-tree .ws-tree-node:has(.ws-tree-actions):hover .ws-count-pill { display: none; }

.ws-tree-rename {
    flex: 1;
    min-width: 0;
    border: 1px solid var(--accent, #4f46e5);
    border-radius: 6px;
    padding: 0.1rem 0.35rem;
    font: inherit;
}

.ws-folder-confirm {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    padding: 0.35rem 0.5rem;
    font-size: 0.82rem;
    background: var(--surface-2, #f8fafc);
    border-radius: 8px;
    margin: 0.15rem 0.4rem;
}

/* Where a row is filed, shown only while the list spans more than one folder. */
.ws-folder-chip {
    flex-shrink: 1;
    min-width: 0;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.74rem;
    color: var(--text-muted, #64748b);
    background: var(--surface-2, #f1f5f9);
    border-radius: 999px;
    padding: 0.05rem 0.5rem;
}

.ws-folder-btn { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- move picker modal ---- */
.ws-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 80;   /* above the delete-confirm popover (70) — it can open on top of a row that has one */
    display: grid;
    place-items: center;
    padding: 1rem;
    background: rgba(15, 23, 42, 0.45);
    animation: dropdown-in 0.12s ease;
}

.ws-modal {
    width: min(560px, 100%);
    max-height: min(78vh, 720px);
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    padding: 1rem 1.1rem;
    border-radius: 14px;
    background: var(--surface, #fff);
    border: 1px solid var(--border, #e6e8ec);
    box-shadow: 0 18px 50px rgba(15, 23, 42, 0.25);
}

.ws-modal-head { display: flex; align-items: center; gap: 0.6rem; }
.ws-modal-head h2 { margin: 0; font-size: 1.05rem; flex: 1; }
.ws-modal-target { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; }
.ws-modal-target .ws-select { flex: 1; }

/* THE scroller: a deep tree scrolls inside the dialog, the header and footer never leave. */
.ws-modal-tree { flex: 1; min-height: 0; overflow-y: auto; }

.ws-modal-foot { display: flex; align-items: center; gap: 0.6rem; }

.ws-modal-destination {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.82rem;
    color: var(--text-muted, #64748b);
}

.ws-settings-hint { font-size: 0.82rem; margin: 0.4rem 0 0; }

/* ---- drag-and-drop reorder ---- */
.ws-grip {
    cursor: grab;
    color: var(--text-muted, #cbd5e1);
    font-size: 0.85rem;
    user-select: none;
    flex-shrink: 0;
}

/* A drag has to start from ANYWHERE on the row. Without this the browser sees a text selection under the
   pointer and drags that instead of the row, which is why dragging from a title worked only sometimes.
   Real inputs keep their own selection behaviour. */
.ws-drag { user-select: none; }
.ws-drag input, .ws-drag textarea { user-select: text; }

.ws-drag.ws-dragging { opacity: 0.4; }

.ws-drag.ws-drop-target {
    box-shadow: inset 0 2px 0 var(--accent, #4f46e5);
}

/* ---- settings ---- */
.ws-settings-row { border-bottom: 1px solid var(--border, #eef1f4); padding: 0.5rem 0.9rem; }
.ws-settings-row:last-child { border-bottom: none; }
.ws-settings-archived { opacity: 0.55; }

.ws-settings-main { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }

.ws-settings-icon {
    width: 52px;
    text-align: center;
    padding: 0.35rem 0.2rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 8px;
    font: inherit;
}

.ws-settings-name {
    padding: 0.35rem 0.55rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 8px;
    font: inherit;
    font-weight: 600;
    min-width: 140px;
}

.ws-type-pill {
    padding: 0.12rem 0.55rem;
    border-radius: 999px;
    font-size: 0.74rem;
    font-weight: 600;
    background: var(--surface-2, #f1f5f9);
    color: var(--text-muted, #64748b);
}

.ws-settings-actions { display: flex; align-items: center; gap: 0.35rem; margin-left: auto; }
.ws-btn-small { padding: 0.28rem 0.6rem; font-size: 0.8rem; }

.ws-settings-panel {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-top: 0.55rem;
    padding: 0.6rem 0.7rem;
    background: var(--surface-2, #f8fafc);
    border-radius: 10px;
    font-size: 0.88rem;
}

.ws-status-editor { flex-direction: column; align-items: stretch; }
.ws-status-row { display: flex; align-items: center; gap: 0.5rem; }
.ws-settings-panel-foot { display: flex; gap: 0.5rem; margin-top: 0.3rem; }

.ws-settings-create {
    display: flex;
    gap: 0.6rem;
    margin-top: 0.9rem;
    flex-wrap: wrap;
    align-items: center;
}

.ws-settings-purge { margin-top: 0.8rem; }

/* ---- tracker list ---- */
.ws-filters { display: flex; gap: 0.5rem; margin-bottom: 0.7rem; flex-wrap: wrap; }
/* The filter box is a TranscribeTextBox now (type or dictate), so this class lands on the WRAPPER and only
   has layout left to do: the padding/border/radius/font-size the bare <input> used to carry are already
   .ws-transcribe-input's own defaults, character for character. The min-width covers the mic beside it. */
.ws-filter-text { flex: 1; min-width: 180px; }

/* ---- the tracker filter bar (above the capture row) ----
   It heads the list column now, so it gets a card of its own: without one, a row of chips and dropdowns
   floating directly above the "New task title…" box reads as part of the add form. */
.ws-filters-top {
    align-items: center;
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 12px;
    background: var(--surface, #fff);
    margin-bottom: 0;
}

.ws-status-chips { display: flex; gap: 0.35rem; flex-wrap: wrap; align-items: center; }

/* The colours themselves are module data and arrive inline (see WorkspaceTaskFilterBar.ChipStyle); this
   only decides the shape. */
.ws-status-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.24rem 0.6rem;
    border: 1px solid transparent;
    border-radius: 999px;
    font: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.35;
    cursor: pointer;
    transition: filter 0.12s ease, box-shadow 0.12s ease;
}

.ws-status-chip:hover { filter: brightness(0.96); }
.ws-status-chip:focus-visible { outline: 2px solid var(--accent, #4f46e5); outline-offset: 1px; }

/* Lit chips carry their colour as a fill, so the badge has to sit on a wash of the chip rather than of the
   page — currentColor is white there and would vanish. */
.ws-status-chip-count {
    min-width: 1.1rem;
    text-align: center;
    padding: 0 0.25rem;
    border-radius: 999px;
    background: color-mix(in srgb, currentColor 14%, transparent);
    font-size: 0.7rem;
    font-weight: 700;
}

.ws-status-chip-on { box-shadow: 0 1px 3px rgba(15, 23, 42, 0.18); }
.ws-status-chip-on .ws-status-chip-count { background: rgba(255, 255, 255, 0.24); }

/* ---- multi-select filter dropdowns ---- */
.ws-multi-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    max-width: 190px;
    cursor: pointer;
    color: var(--text, #0f172a);
}

.ws-multi-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-multi-caret { color: var(--text-muted, #94a3b8); font-size: 0.7rem; }

/* A dimension that is actually narrowing the list says so — otherwise the only clue is a label you have to
   read against four others. */
.ws-multi-on {
    border-color: var(--accent, #4f46e5);
    background: color-mix(in srgb, var(--accent, #4f46e5) 10%, transparent);
    font-weight: 600;
}

.ws-multi-menu { min-width: 200px; max-height: 320px; overflow-y: auto; }
.ws-multi-opt { cursor: pointer; font-size: 0.85rem; }
.ws-multi-opt input { margin: 0; flex-shrink: 0; }
.ws-multi-opt-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.ws-multi-clear {
    margin-top: 0.2rem;
    border-top: 1px solid var(--border, #e6e8ec);
    border-radius: 0 0 7px 7px;
    color: var(--text-muted, #64748b);
    font-size: 0.8rem;
}

/* The y-axis mode is a VIEW switch, not a filter — it reads as one of the controls but never lights up the
   way an active filter does, because it hides nothing. */
.ws-axis-select { cursor: pointer; font-weight: 600; }

.ws-filters-clear { margin-left: auto; flex-shrink: 0; }

/* The tree's per-status pills sit shoulder to shoulder, so they lose the neutral pill's min-width — four of
   them at 22px each would push a folder name out of its row. */
.ws-count-pill-status {
    min-width: 0;
    padding: 0.05rem 0.35rem;
    font-weight: 700;
    background: var(--surface-2, #f1f5f9);
}

.ws-row-open {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
    border: none;
    background: transparent;
    font: inherit;
    font-weight: 500;
    color: var(--text, #0f172a);
    cursor: pointer;
    padding: 0;
}

.ws-row-open:hover { color: var(--accent, #4f46e5); }

/* Priority is the board's y-axis now, so these colour its ROW HEADS rather than a per-row dot. */
.ws-priority-low { color: #94a3b8; }
.ws-priority-normal { color: #64748b; }
.ws-priority-high { color: #d97706; }
.ws-priority-urgent { color: #dc2626; }

/* ---- the status × priority board ----
   The board scrolls sideways on its own rather than pushing the page wide: how many columns there are is
   the module's business, and the folder tree beside it has to keep its place. */
.ws-matrix-scroll { overflow-x: auto; padding-bottom: 0.4rem; }

.ws-matrix {
    display: grid;
    /* Column count is DATA (a module's statuses), so it arrives as --ws-matrix-cols on the element. The
       min of the minmax is what makes the board scroll instead of crushing the cells. */
    grid-template-columns: auto repeat(var(--ws-matrix-cols, 3), minmax(200px, 1fr));
    gap: 0.4rem;
    align-items: stretch;
}

/* Empty on purpose — the axes label themselves. It still holds the grid's top-left slot. */
.ws-matrix-corner { }

.ws-matrix-col-head,
.ws-matrix-row-head {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.6rem;
    font-size: 0.8rem;
    font-weight: 700;
}

/* The column head wears its status colour (inline, it is module data); the row head wears its priority's. */
.ws-matrix-col-head {
    border-bottom: 2px solid currentColor;
    justify-content: space-between;
}

.ws-matrix-row-head {
    justify-content: space-between;
    min-width: 92px;
    max-width: 180px;
    border-right: 2px solid currentColor;
}

/* Folder rows have no colour of their own (a folder is not a severity), so they take the muted default and
   lean on the name instead. Wider than a priority word, and still capped so the board keeps the width. */
.ws-matrix-row-folder { color: var(--text-muted, #64748b); }
.ws-matrix-row-unfiled { font-style: italic; }

.ws-matrix-head-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.ws-matrix-cell {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    gap: 0.4rem;
    min-height: 64px;
    padding: 0.4rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    background: var(--surface-2, #f8fafc);
}

/* The drop hint is on the CELL, not the block under the pointer: a drop here can change status AND
   priority, which is more than "this position". */
.ws-matrix-cell-drop {
    border-color: var(--accent, #4f46e5);
    border-style: dashed;
    background: color-mix(in srgb, var(--accent, #4f46e5) 7%, transparent);
}

.ws-matrix-cell-empty {
    align-self: center;
    margin: auto;
    font-size: 0.78rem;
    color: var(--accent, #4f46e5);
}

/* A block is a fixed-width card so the cells read as a grid of equal things and wrap predictably —
   flexible widths would make the same task a different size in every cell. */
.ws-matrix-block {
    flex: 0 0 auto;
    width: 168px;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 8px;
    background: var(--surface, #fff);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
    cursor: grab;
}

.ws-matrix-block:hover { border-color: color-mix(in srgb, var(--accent, #4f46e5) 45%, transparent); }
.ws-matrix-block.ws-dragging { cursor: grabbing; }

.ws-matrix-block-head { display: flex; align-items: center; gap: 0.35rem; }
.ws-matrix-block-head input { margin: 0; flex-shrink: 0; cursor: pointer; }

.ws-matrix-block-number {
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--text-muted, #94a3b8);
}

/* Executor, top right — margin-left auto is what pins it there whatever precedes it. AI is the one worth
   noticing (it changes who picks the task up), so Human stays deliberately quiet. */
.ws-matrix-block-executor {
    margin-left: auto;
    flex-shrink: 0;
    padding: 0.02rem 0.3rem;
    border-radius: 5px;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.ws-executor-human { color: var(--text-muted, #b6bfcc); }

.ws-executor-ai {
    color: var(--accent, #4f46e5);
    background: color-mix(in srgb, var(--accent, #4f46e5) 12%, transparent);
}

/* Two lines, then an ellipsis — the block is a card, and the full title is on the tooltip and in the pane
   it opens. line-clamp needs the -webkit- box even in browsers that have long since stopped needing it.
   No longer a button: the whole card is the target now, so this is plain text that inherits the card's
   pointer and hover. */
.ws-matrix-block-title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 0.82rem;
    font-weight: 500;
    line-height: 1.3;
    color: var(--text, #0f172a);
}

.ws-matrix-block:hover .ws-matrix-block-title { color: var(--accent, #4f46e5); }
.ws-matrix-block:focus-visible { outline: 2px solid var(--accent, #4f46e5); outline-offset: 1px; }

/* The foot holds the bottom-left stack (folder, then kind) and the bottom-right avatar. margin-top auto
   keeps the whole thing on the block's bottom edge however tall the title made it. */
.ws-matrix-block-foot {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin-top: auto;
    padding-top: 0.2rem;
    min-width: 0;
}

/* Where the task is filed. Light enough to sit under the title without competing with it, dark enough to
   actually read — this is the line people scan to answer "which project is this?". */
.ws-matrix-block-folder {
    font-size: 0.66rem;
    line-height: 1.25;
    color: var(--text-muted, #9aa4b2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ws-matrix-block-tags { display: flex; align-items: center; gap: 0.3rem; min-height: 20px; }

/* Kind, bottom left. */
.ws-matrix-block-kind {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 0.02rem 0.35rem;
    border-radius: 5px;
    background: var(--surface-2, #f1f5f9);
    color: var(--text-muted, #64748b);
    font-size: 0.65rem;
    font-weight: 600;
}

/* ...and the assignee bottom right, whether or not a kind shares the row. */
.ws-matrix-block-tags .ws-assignee { margin-left: auto; width: 20px; height: 20px; font-size: 0.56rem; }

/* The avatar as a menu bullet in the bulk-assign list. */
.ws-menu-avatar { width: 20px; height: 20px; font-size: 0.55rem; flex-shrink: 0; }
.ws-menu-unassign { border-top: 1px solid var(--border, #e6e8ec); border-radius: 0 0 7px 7px; margin-top: 0.2rem; }

/* One column per screen is unreadable; below this the board keeps its own horizontal scroll instead. */
@media (max-width: 900px) {
    .ws-matrix { grid-template-columns: auto repeat(var(--ws-matrix-cols, 3), minmax(170px, 1fr)); }
}

.ws-assignee {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--accent, #4f46e5), #7c3aed);
    color: #fff;
    font-size: 0.62rem;
    font-weight: 700;
}

/* Mobile: the modal already goes full-screen there (see .ws-detail-modal), so this only has to loosen the
   pane's own padding and let the Notes workbench's columns stack. */
@media (max-width: 640px) {
    .ws-split { flex-direction: column; }
    .ws-detail { padding: 0.8rem; }
}

/* ---- printing a note (this is also the PDF export: "Save as PDF" in the print dialog) ----

   Printing the note the app has ALREADY rendered is what makes a PDF that matches the screen — it is the
   same DOM, the same sanitized HTML, the same styles. So this block does only two things: hide the
   application chrome, and un-clip the scroll containers between the page and the text. That second half is
   the one that matters: .app-content, .ws-detail and .ws-note are each a scroller with a bounded height,
   and without releasing them the printout stops at the first screenful. */
.ws-print-title { display: none; }

@media print {
    /* Chrome, not content. The tree, the action bars and the icon buttons are navigation; the attachments
       drawer is a management affordance, not part of the document. */
    .app-header,
    .ws-module-head,
    .ws-tree-col,
    .ws-detail-head,
    .ws-note-bar,
    .ws-note-files,
    .ws-no-print,
    #pm-chat-widget,
    #blazor-error-ui { display: none !important; }

    /* Every bounded/clipping ancestor between <body> and the text, released. Also flattened to block: these
       are flex containers sized for a viewport, and paper has no viewport. */
    html, body,
    .app-shell,
    .app-content,
    .workspace-page,
    .ws-notes-workbench,
    .ws-detail,
    .ws-note {
        display: block !important;
        height: auto !important;
        min-height: 0 !important;
        max-height: none !important;
        overflow: visible !important;
    }

    .workspace-page { max-width: none !important; margin: 0 !important; padding: 0 !important; }
    .ws-notes-workbench { padding: 0 !important; gap: 0 !important; }

    /* The title comes back as a real heading — the on-screen one is an <input>, which would print as a
       bordered form field cropped to its column width. */
    .ws-print-title {
        display: block !important;
        margin: 0 0 0.2rem;
        font-size: 1.6rem;
    }

    .ws-detail-meta { margin: 0 0 1.2rem; color: #555 !important; font-size: 0.8rem; }

    /* On screen this reserves the editor toolbar's height so text does not jump when the editor opens.
       On paper there is no toolbar to clear. */
    .ws-note-view {
        margin-top: 0 !important;
        padding: 0 !important;
        cursor: auto;
    }

    /* Keep the units a reader thinks of as one thing on one page. */
    .ws-note-view pre,
    .ws-note-view blockquote,
    .ws-note-view table,
    .ws-note-view img,
    .ws-note-view li { break-inside: avoid; }

    .ws-note-view h1,
    .ws-note-view h2,
    .ws-note-view h3 { break-after: avoid; }

    .ws-note-view img { max-width: 100% !important; height: auto !important; }

    /* Links are useless as blue underlines on paper, but their targets are not — print the URL after
       external links only (in-app file links would print a meaningless relative path). */
    .ws-note-view a[href^="http"]::after {
        content: " (" attr(href) ")";
        font-size: 0.8em;
        color: #555;
        word-break: break-all;
    }
}

/* ---- Workspace settings shell: the settings-page/settings-menu chrome (app.css) hosts the module's own
   pages, so the pane inside it drops the standalone page's outer padding and max-width. ---- */
.settings-content .ws-settings-pane { margin: 0; padding: 0; max-width: none; }
.settings-content .ws-settings-pane .workspace-hero { padding-top: 0; }

/* ---- Workspace MCP settings page ---- */
.ws-mcp-callout {
    border: 1px solid var(--border);
    border-left-width: 4px;
    border-radius: 10px;
    padding: 0.8rem 1rem;
    background: var(--surface-2);
}

.ws-mcp-callout p { margin: 0.35rem 0 0; }
.ws-mcp-callout-warn { border-left-color: #d97706; }

.ws-mcp-spaced { margin-top: 1.1rem; }

.ws-mcp-bullets { margin: 0; padding-left: 1.2rem; line-height: 1.6; }
.ws-mcp-bullets li { margin: 0.35rem 0; }

/* ---- typed item bodies: code, Mermaid, uploaded files ----
   These reuse .ws-note's flex-column shell (bar on top, body scrolling beneath) so a code item and a note
   item put their action bar on the same pixel. Only what is BELOW the bar differs. */
.ws-source .ws-note-view { margin-top: 0; padding: 0.6rem 0 2rem; cursor: pointer; }

.ws-source-split {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.75rem;
}

/* Mermaid only: source on the left, drawing on the right. Typing a diagram blind is the thing this feature
   exists to avoid, so the preview is not an optional panel. */
.ws-source-split-preview { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }

@media (max-width: 900px) {
    .ws-source-split-preview { grid-template-columns: 1fr; grid-template-rows: auto auto; }
}

.ws-source-editor {
    width: 100%;
    min-height: 320px;
    resize: vertical;
    padding: 0.75rem 0.9rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    background: var(--surface, #fff);
    color: var(--text, #0f172a);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
    line-height: 1.55;
    tab-size: 4;
    white-space: pre;        /* code does not wrap; the horizontal scrollbar is the honest answer */
    overflow: auto;
}

.ws-source-preview {
    min-width: 0;
    padding: 0.75rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    background: var(--surface-2, #f8fafc);
    overflow: auto;
}

.ws-mermaid-wrap { min-width: 0; }

/* The SVG mermaid emits is sized to its content and can be far wider than the pane; scrolling it inside its
   own box is what keeps the page itself from scrolling sideways. */
.ws-mermaid { overflow: auto; }
.ws-mermaid svg { max-width: 100%; height: auto; }

.ws-code {
    margin: 0;
    padding: 0.85rem 1rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    background: var(--surface-2, #f8fafc);
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
    line-height: 1.55;
    tab-size: 4;
}

/* highlight.js paints its own background on .hljs; the <pre> above already provides one. */
.ws-code code.hljs { background: transparent; padding: 0; }

.ws-source-lang { display: flex; align-items: center; gap: 0.35rem; font-size: 0.78rem; color: var(--text-muted, #94a3b8); }
.ws-source-lang .ws-select { font-size: 0.78rem; padding: 0.15rem 0.3rem; }

.ws-source-note { margin: 0.5rem 0 0; font-size: 0.76rem; color: var(--text-muted, #94a3b8); }
.ws-source-error { margin: 0.5rem 0 0; font-size: 0.8rem; color: var(--danger, #dc2626); }

/* ---- file items ---- */
.ws-file-body { flex: 1; min-height: 0; overflow-y: auto; padding-top: 0.6rem; }

.ws-file-image { max-width: 100%; height: auto; border-radius: 10px; display: block; }
.ws-file-image-link { display: inline-block; max-width: 100%; }
.ws-file-caption { margin: 0.5rem 0 0; font-size: 0.78rem; color: var(--text-muted, #94a3b8); }

/* The universal fallback: identical for Word, Excel and an unrecognised binary, because the one thing this
   app can honestly do with all three is hand them back. */
.ws-file-card {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 12px;
    background: var(--surface-2, #f8fafc);
}

.ws-file-card-icon { font-size: 1.9rem; line-height: 1; flex-shrink: 0; }
.ws-file-card-text { flex: 1; min-width: 0; }
.ws-file-card-name {
    margin: 0;
    font-weight: 600;
    overflow-wrap: anywhere;   /* a long uploaded name must wrap, not widen the pane */
}
.ws-file-card-meta { margin: 0.15rem 0 0; font-size: 0.78rem; color: var(--text-muted, #94a3b8); }

/* ---- file preview (rendered markup / source / download) ---- */
.ws-file-bar {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    flex-wrap: wrap;
    margin-bottom: 0.6rem;
}

.ws-file-bar .ws-file-caption { flex: 1; min-width: 0; margin: 0; overflow-wrap: anywhere; }

.ws-file-tabs {
    display: inline-flex;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 8px;
    overflow: hidden;
}

.ws-file-tab {
    border: none;
    background: transparent;
    font: inherit;
    font-size: 0.8rem;
    padding: 0.3rem 0.7rem;
    color: var(--text-muted, #64748b);
    cursor: pointer;
}

.ws-file-tab-on { background: var(--accent, #4f46e5); color: #fff; font-weight: 600; }

/* The sandboxed preview frame. A white fill regardless of app theme: the document inside styles itself and
   almost always assumes a light page, so inheriting a dark surface would show its text on the wrong ground. */
.ws-file-frame {
    width: 100%;
    height: 70vh;
    min-height: 320px;
    border: 1px solid var(--border, #e6e8ec);
    border-radius: 10px;
    background: #fff;
}

.ws-file-source { max-height: 70vh; overflow: auto; white-space: pre; }

/* Footer row of a confirmation modal. Cancel sits left of the destructive action so the pointer does not
   land on "delete" by momentum after the dialog appears under it. */
.ws-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1rem;
}

/* Delete on a tree node. Hidden until the row is hovered, exactly like ＋ beside it, so a tree at rest shows
   titles and nothing else — the trigger carries .ws-tree-add, so it is that pair of rules doing the hiding.
   Nothing here may set visibility on the wrapper or the button: visibility is inherited, but a descendant
   that declares `visible` overrides a hidden ancestor, which is precisely how this icon stayed on screen. */
.ws-tree-del { flex-shrink: 0; display: inline-flex; }
.ws-tree-del .ws-tree-add:hover { background: #fef2f2; color: var(--danger, #dc2626); }

/* ---- item info modal ---- */
.ws-info-modal { max-width: 620px; }

.ws-info { margin: 0.6rem 0 0; display: flex; flex-direction: column; gap: 0.1rem; }

.ws-info-row {
    display: grid;
    grid-template-columns: 130px minmax(0, 1fr);
    gap: 0.6rem;
    align-items: baseline;
    padding: 0.28rem 0;
    border-bottom: 1px solid var(--border, #eef1f4);
}

.ws-info-row:last-child { border-bottom: none; }
.ws-info-row dt { color: var(--text-muted, #64748b); font-size: 0.8rem; }
.ws-info-row dd { margin: 0; display: flex; align-items: center; gap: 0.4rem; min-width: 0; font-size: 0.86rem; }

/* Ids and URLs wrap rather than widening the dialog — an absolute link is easily longer than the modal. */
.ws-info-mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8rem;
    overflow-wrap: anywhere;
}

.ws-copy-btn {
    flex-shrink: 0;
    border: 1px solid var(--border, #e6e8ec);
    background: var(--surface, #fff);
    border-radius: 6px;
    padding: 0.1rem 0.4rem;
    font-size: 0.78rem;
    line-height: 1.4;
    cursor: pointer;
    color: var(--text-muted, #64748b);
}

.ws-copy-btn:hover { border-color: var(--accent, #4f46e5); color: var(--accent, #4f46e5); }

.ws-info-hint {
    margin: 1rem 0 0;
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--text-muted, #64748b);
}

.ws-info-hint code {
    font-size: 0.76rem;
    padding: 0.05rem 0.3rem;
    border-radius: 4px;
    background: var(--surface-2, #f1f5f9);
    overflow-wrap: anywhere;
}
