/* ================================================
   NExtSEEK Theme — Scientific Publication
   Palette: Neutral off-whites · MIT Crimson · Deep ink
   Fonts: Inter throughout (Playfair retired)
   ================================================ */

:root {
    /* Brand — MIT crimson, preserved as the BMC accent */
    --ns-crimson:          #A31F34;
    --ns-crimson-hover:    #8a1a2b;
    --ns-crimson-soft:     #fdf0f2;
    --ns-crimson-border:   #f3c6ce;

    /* Clade accents — the dmac.clades colours, one language across catalogs + project pages */
    --ns-clade-source:     #A3D46F;   --ns-clade-source-tint:    #edf7e0;
    --ns-clade-processed:  #F4A45E;   --ns-clade-processed-tint: #fdefe1;
    --ns-clade-raw:        #A2C8F0;   --ns-clade-raw-tint:       #e9f2fc;
    --ns-clade-analyzed:   #6B8FDD;   --ns-clade-analyzed-tint:  #edf1fb;

    /* Neutrals — direction C: cool off-whites, no warm beige */
    --ns-bg:               #ffffff;
    --ns-white:            #ffffff;
    --ns-ink:              #1a1a2e;
    --ns-body:             #2d2d33;
    --ns-muted:            #6b7280;
    --ns-border:           #eaeaea;

    /* Surfaces */
    --ns-sidebar-bg:       #fafafa;
    --ns-sidebar-hover:    #f0f0f0;
    --ns-sidebar-active:   #fdf0f2;
    --ns-header-bg:        #ffffff;   /* unused now; kept for compat */
    --ns-footer-bg:        #fafafa;

    /* Typography — Inter throughout; Playfair retired */
    --ns-font-display:     'Inter', 'Source Sans 3', system-ui, sans-serif;
    --ns-font-body:        'Inter', 'Source Sans 3', system-ui, sans-serif;
    --ns-font-mono:        'JetBrains Mono', 'Fira Code', 'Courier New', monospace;

    /* Layout */
    --sidebar-width:       256px;
    --header-height:       0;        /* header removed */
    --footer-height:       50px;

    /* Elevation */
    --ns-shadow-xs:        0 1px 2px rgba(26,26,46,0.05);
    --ns-shadow-sm:        0 1px 4px rgba(26,26,46,0.07);
    --ns-shadow:           0 2px 10px rgba(26,26,46,0.10);
    --ns-shadow-md:        0 4px 24px rgba(26,26,46,0.14);

    /* Motion */
    --ns-ease:             cubic-bezier(0.4, 0, 0.2, 1);
    --ns-transition:       0.2s cubic-bezier(0.4, 0, 0.2, 1);

    /* Legacy compat aliases (preserved so existing pages keep working) */
    --primary-color:       #A31F34;
    --sidebar-bg:          #fafafa;
    --sidebar-text:        #2d2d33;
    --sidebar-hover:       #f0f0f0;
    --sidebar-active:      #A31F34;
    --content-bg:          #ffffff;
    --header-bg:           #ffffff;
    --footer-bg:           #fafafa;

    /* EasyUI compat — RETUNED in Task 6 */
    --easyui-bg:           #fafafa;
    --easyui-border:       #eaeaea;
    --easyui-hover:        #f0f0f0;
    --easyui-selected:     #fdf0f2;
    --easyui-active:       #A31F34;
    --easyui-text:         #2d2d33;
}

/* ================================================
   Base Reset
   ================================================ */

*, *::before, *::after { box-sizing: border-box; }

body.nextseek-app {
    margin: 0;
    padding: 0;
    font-family: var(--ns-font-body);
    font-size: 15px;
    line-height: 1.65;
    color: var(--ns-body);
    background-color: var(--ns-bg);
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--ns-font-display);
    color: var(--ns-ink);
    line-height: 1.25;
    font-weight: 600;
    letter-spacing: -0.015em;
}

/* ================================================
   Sidebar
   ================================================ */

.sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: var(--sidebar-width);
    height: 100vh;
    background-color: var(--ns-sidebar-bg);
    border-right: 1px solid var(--ns-border);
    z-index: 1000;
    display: flex;
    flex-direction: column;
    transition: transform var(--ns-transition);
    overflow: hidden;
}

/* Wordmark: NExtSEEK — N and S in crimson */
.sidebar-wordmark {
    font-family: var(--ns-font-display);
    font-size: 1.3rem;
    font-weight: 700;
    color: var(--ns-ink);
    text-decoration: none;
    letter-spacing: -0.02em;
    line-height: 1;
    display: inline-block;
    transition: opacity var(--ns-transition);
}
.sidebar-wordmark:hover { opacity: 0.85; color: var(--ns-ink); text-decoration: none; }
.sidebar-wordmark .wm-hi { color: var(--ns-crimson); }

/* Scrollable nav area */
.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 0.75rem 0 1rem;
    scrollbar-width: thin;
    scrollbar-color: var(--ns-border) transparent;
}
.sidebar-nav::-webkit-scrollbar { width: 3px; }
.sidebar-nav::-webkit-scrollbar-thumb { background: var(--ns-border); border-radius: 3px; }

.sidebar-nav .nav {
    padding: 0;
    list-style: none;
    margin: 0;
}
.sidebar-nav .nav-item { margin: 0; }

/* Collapsible section label (USEFUL INFO). A clickable variant of
   .sidebar-section-label; the chevron flips when the section collapses. */
.sidebar-section-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: calc(100% - 2rem);
    margin: 0 1rem;
    background: none;
    border: 0;
    cursor: pointer;
    text-decoration: none;
}
.sidebar-section-toggle .bi-chevron-down { transition: transform var(--ns-transition); font-size: 0.7rem; opacity: 0.6; }
.sidebar-section-toggle[aria-expanded="false"] .bi-chevron-down { transform: rotate(-90deg); }

/* Nav links */
.sidebar-nav .nav-link {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.52rem 1.25rem;
    color: var(--ns-body);
    text-decoration: none;
    font-size: 0.88rem;
    font-family: var(--ns-font-body);
    border-left: 2px solid transparent;
    transition: all var(--ns-transition);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border-radius: 0;
}
.sidebar-nav .nav-link:hover {
    background-color: var(--ns-sidebar-hover);
    color: var(--ns-ink);
    border-left-color: var(--ns-border);
    text-decoration: none;
}
.sidebar-nav .nav-link.active {
    background-color: var(--ns-sidebar-active);
    color: var(--ns-crimson);
    border-left-color: var(--ns-crimson);
    font-weight: 600;
}
.sidebar-nav .nav-link i {
    font-size: 0.9rem;
    width: 1rem;
    text-align: center;
    flex-shrink: 0;
    opacity: 0.65;
    transition: opacity var(--ns-transition);
}
.sidebar-nav .nav-link:hover i,
.sidebar-nav .nav-link.active i { opacity: 1; }

.sidebar-nav .nav-link .bi-chevron-down {
    font-size: 0.65rem;
    margin-left: auto;
    flex-shrink: 0;
    opacity: 0.45;
    transition: transform var(--ns-transition), opacity var(--ns-transition);
    width: auto;
}
.sidebar-nav .nav-link[aria-expanded="true"] .bi-chevron-down {
    transform: rotate(180deg);
    opacity: 0.7;
}

/* Submenus */
.sidebar-nav .submenu {
    list-style: none;
    padding: 0.2rem 0 0.2rem 1.6rem;
    margin: 0;
    border-left: 1px solid var(--ns-border);
    margin-left: 1.55rem;
}
.sidebar-nav .submenu .nav-link {
    padding: 0.4rem 0.85rem;
    font-size: 0.83rem;
    border-left: none;
    border-radius: 4px;
}
.sidebar-nav .submenu .nav-link:hover {
    background: var(--ns-sidebar-hover);
    border-left: none;
}
.sidebar-nav .submenu .nav-link.active {
    background: var(--ns-sidebar-active);
    color: var(--ns-crimson);
    border-left: none;
}

/* ================================================
   Main Wrapper
   ================================================ */

#main-wrapper {
    margin-left: var(--sidebar-width);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    transition: margin-left var(--ns-transition);
}

/* ================================================
   Main Content
   ================================================ */

.content {
    flex: 1;
    padding: 2rem 1.75rem;
    background: var(--ns-bg);
}

/* ================================================
   Footer
   ================================================ */

.footer {
    height: var(--footer-height);
    background-color: var(--ns-footer-bg);
    border-top: 1px solid var(--ns-border);
    display: flex;
    align-items: center;
    flex-shrink: 0;
}
.footer-content {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 0 1.75rem;
}
.footer-brand img {
    height: 20px;
    opacity: 0.65;
    transition: opacity var(--ns-transition);
}
.footer-brand img:hover { opacity: 0.9; }
.footer-text {
    font-size: 0.78rem;
    color: var(--ns-muted);
    font-family: var(--ns-font-body);
}

/* ================================================
   Bootstrap Component Overrides
   ================================================ */

.card {
    background: var(--ns-white);
    border: 1px solid var(--ns-border);
    border-radius: 8px;
    box-shadow: var(--ns-shadow-sm);
}
.card-body { padding: 1.5rem; }

.page-title {
    font-family: var(--ns-font-display);
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--ns-ink);
    margin-bottom: 1.5rem;
    letter-spacing: -0.02em;
}

/* Buttons */
.btn-primary {
    background: var(--ns-crimson);
    border-color: var(--ns-crimson);
    font-family: var(--ns-font-body);
    font-weight: 600;
    letter-spacing: 0.01em;
    transition: all var(--ns-transition);
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
    background: var(--ns-crimson-hover) !important;
    border-color: var(--ns-crimson-hover) !important;
    box-shadow: 0 0 0 3px rgba(163,31,52,0.18) !important;
}

.btn-outline-primary {
    color: var(--ns-crimson);
    border-color: var(--ns-crimson);
    font-family: var(--ns-font-body);
    font-weight: 600;
    transition: all var(--ns-transition);
}
.btn-outline-primary:hover {
    background: var(--ns-crimson);
    border-color: var(--ns-crimson);
    color: var(--ns-white);
}

/* Forms */
.form-control, .form-select {
    border-color: var(--ns-border);
    font-family: var(--ns-font-body);
    font-size: 0.9rem;
    color: var(--ns-body);
    border-radius: 6px;
    background: var(--ns-white);
    transition: border-color var(--ns-transition), box-shadow var(--ns-transition);
}
.form-control:focus, .form-select:focus {
    border-color: var(--ns-crimson);
    box-shadow: 0 0 0 3px rgba(163,31,52,0.1);
    outline: none;
}
.form-label {
    font-family: var(--ns-font-body);
    font-size: 0.83rem;
    font-weight: 600;
    color: var(--ns-ink);
    letter-spacing: 0.015em;
    margin-bottom: 0.35rem;
}

/* Alerts */
.alert-danger {
    background: #fff0f2;
    border: 1px solid var(--ns-crimson-border);
    color: #8b1a2e;
    border-radius: 6px;
    font-size: 0.875rem;
}

/* ================================================
   Mobile / Responsive
   ================================================ */

@media (max-width: 991.98px) {
    .sidebar {
        transform: translateX(-100%);
        box-shadow: var(--ns-shadow-md);
    }
    body.sidebar-open .sidebar { transform: translateX(0); }
    #main-wrapper { margin-left: 0; }

    .content { padding: 1.25rem 1rem; }
}

/* Drawer scrim — promoted from body::before to a real element so the
   tap-to-close handler can listen on it. Display gated on .sidebar-open
   so it doesn't grab clicks on desktop. */
.sidebar-scrim {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(26,26,46,0.4);
    z-index: 999;
    cursor: pointer;
}
body.sidebar-open .sidebar-scrim { display: block; }

@media (max-width: 575.98px) {
    .content { padding: 1rem 0.875rem; }
}

/* ================================================
   EasyUI Theme Overrides — Updated for Option B
   ================================================ */

/* Tabs */
.tabs-header {
    background: var(--easyui-bg) !important;
    border-bottom: 2px solid var(--easyui-border) !important;
    padding-bottom: 0 !important;
}
.tabs .tabs-header .tabs-wrap { background: transparent !important; }
.tabs-header .tabs-scroller-left,
.tabs-header .tabs-scroller-right { background: var(--easyui-bg) !important; }

.tabs li a.tabs-inner {
    background: transparent !important;
    border: none !important;
    border-bottom: 2px solid transparent !important;
    color: var(--ns-muted) !important;
    font-family: var(--ns-font-body) !important;
    font-size: 0.875rem !important;
    padding: 10px 18px !important;
    margin-bottom: -2px !important;
    border-radius: 0 !important;
    transition: color 0.15s, border-color 0.15s !important;
}
.tabs li a.tabs-inner:hover {
    background: var(--easyui-hover) !important;
    color: var(--ns-body) !important;
}
.tabs li.tabs-selected a.tabs-inner {
    background: var(--ns-white) !important;
    color: var(--ns-crimson) !important;
    border-bottom: 2px solid var(--ns-crimson) !important;
    font-weight: 600 !important;
}

/* Panels */
.panel {
    border: 1px solid var(--easyui-border) !important;
    border-radius: 6px !important;
    box-shadow: var(--ns-shadow-sm) !important;
}
.panel-header {
    background: var(--easyui-bg) !important;
    border-bottom: 1px solid var(--easyui-border) !important;
    padding: 10px 14px !important;
    border-radius: 6px 6px 0 0 !important;
}
.panel-title {
    color: var(--ns-ink) !important;
    font-family: var(--ns-font-body) !important;
    font-weight: 600 !important;
    font-size: 0.875rem !important;
}
.panel-body { background: var(--ns-white) !important; padding: 14px !important; }

/* Datagrid */
.datagrid-wrap {
    border: 1px solid var(--easyui-border) !important;
    border-radius: 6px !important;
}
.datagrid-header {
    background: var(--easyui-bg) !important;
    border-bottom: 1px solid var(--easyui-border) !important;
}
.datagrid-header td { border-color: var(--easyui-border) !important; }
.datagrid-toolbar {
    background: var(--easyui-bg) !important;
    border-bottom: 1px solid var(--easyui-border) !important;
    padding: 7px 10px !important;
}
.datagrid-header td,
.datagrid-header th {
    font-family: var(--ns-font-body) !important;
    font-size: 0.8rem !important;
    font-weight: 700 !important;
    letter-spacing: 0.03em !important;
    color: var(--ns-muted) !important;
    text-transform: uppercase !important;
}
.datagrid-row { background: var(--ns-white) !important; }
.datagrid-row-alt { background: var(--ns-bg) !important; }
.datagrid-row-over { background: var(--easyui-hover) !important; }
/* Older rule retained for non-color properties; color is overridden by the direction-C block at end of file. */
.datagrid-row-selected {
    background: var(--easyui-selected) !important;
    color: var(--ns-body) !important;
}
.datagrid-cell {
    padding: 8px 10px !important;
    border-color: var(--easyui-border) !important;
    font-family: var(--ns-font-body) !important;
    font-size: 0.875rem !important;
}
.datagrid-pager {
    background: var(--easyui-bg) !important;
    border-top: 1px solid var(--easyui-border) !important;
    padding: 7px 10px !important;
}

/* Buttons */
.easyui-linkbutton,
.l-btn {
    background: var(--ns-white) !important;
    border: 1px solid var(--easyui-border) !important;
    border-radius: 5px !important;
    color: var(--ns-body) !important;
    font-family: var(--ns-font-body) !important;
    font-size: 0.85rem !important;
    padding: 5px 11px !important;
    transition: all 0.15s !important;
}
.easyui-linkbutton:hover,
.l-btn:hover {
    background: var(--easyui-hover) !important;
    border-color: #ccc !important;
}
.l-btn-plain { background: transparent !important; border: none !important; }
.l-btn-plain:hover { background: var(--easyui-hover) !important; }

/* Combobox / Textbox */
.textbox, .combo {
    border: 1px solid var(--easyui-border) !important;
    border-radius: 5px !important;
}
.textbox-text, .combo-text {
    padding: 7px 10px !important;
    font-family: var(--ns-font-body) !important;
    font-size: 0.875rem !important;
}
.textbox:focus-within, .combo:focus-within {
    border-color: var(--ns-crimson) !important;
    box-shadow: 0 0 0 3px rgba(163,31,52,0.08) !important;
}
.combo-panel {
    border: 1px solid var(--easyui-border) !important;
    border-radius: 6px !important;
    box-shadow: var(--ns-shadow-md) !important;
}
.combobox-item {
    padding: 7px 12px !important;
    font-family: var(--ns-font-body) !important;
    font-size: 0.875rem !important;
}
.combobox-item-hover { background: var(--easyui-hover) !important; }
.combobox-item-selected { background: var(--easyui-selected) !important; }

/* Layout */
.layout-panel { background: var(--ns-white) !important; }
.layout-split-proxy-h, .layout-split-proxy-v { background: var(--ns-crimson) !important; }

/* Accordion */
.accordion .accordion-header {
    background: var(--easyui-bg) !important;
    border-color: var(--easyui-border) !important;
    padding: 9px 14px !important;
    color: var(--ns-body) !important;
    font-family: var(--ns-font-body) !important;
}
.accordion .accordion-header-selected {
    background: var(--easyui-selected) !important;
    border-left: 3px solid var(--ns-crimson) !important;
}

/* Tree */
.tree-node-hover { background: var(--easyui-hover) !important; }
.tree-node-selected { background: var(--easyui-selected) !important; }

/* Window / Dialog */
.window, .dialog {
    border-radius: 8px !important;
    box-shadow: var(--ns-shadow-md) !important;
    border: 1px solid var(--ns-border) !important;
}
.window-header, .dialog-header {
    background: var(--easyui-bg) !important;
    border-bottom: 1px solid var(--easyui-border) !important;
    border-radius: 8px 8px 0 0 !important;
    padding: 11px 14px !important;
    font-family: var(--ns-font-body) !important;
    font-weight: 600 !important;
}

/* Toolbar / Slider / Progress / Messager */
.toolbar { background: var(--easyui-bg) !important; }
.slider-handle { background: var(--easyui-selected) !important; }
.progressbar-value { background: var(--ns-crimson) !important; border-radius: 4px !important; }
.messager-body { padding: 18px !important; font-family: var(--ns-font-body) !important; }

/* Filter row / Pagination */
.datagrid-filter-row input {
    border: 1px solid var(--easyui-border) !important;
    border-radius: 4px !important;
    padding: 3px 7px !important;
    font-family: var(--ns-font-body) !important;
}
.pagination-num {
    border: 1px solid var(--easyui-border) !important;
    border-radius: 4px !important;
    padding: 3px !important;
}

/* ================================================
   Utility
   ================================================ */

.well {
    background: #fafaf6 !important;
    border: 1px solid var(--ns-border) !important;
    border-radius: 8px !important;
    box-shadow: none !important;
    padding: 18px !important;
}

/* EasyUI pages on mobile: horizontal scroll + notice */
.easyui-page-wrapper {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

@media (max-width: 768px) {
    .easyui-mobile-notice {
        display: block !important;
    }
}
.easyui-mobile-notice {
    display: none;
    background: #fff8e1;
    border: 1px solid #f0c040;
    border-radius: 6px;
    padding: 0.6rem 1rem;
    font-size: 0.8rem;
    color: #78600a;
    margin-bottom: 0.75rem;
}

/* ================================================
   Getting Started / Tutorials page
   ================================================ */
.help-page {
    max-width: 760px;
    margin: 0 auto;
    padding: 3rem 1.5rem 4rem;
    font-family: var(--ns-font-body);
    color: var(--ns-body);
    line-height: 1.7;
}
.help-eyebrow {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--ns-muted);
    font-weight: 600;
    margin: 0 0 0.5rem;
}
.help-page h1 {
    font-size: 2.25rem;
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--ns-ink);
    margin: 0 0 0.75rem;
}
.help-lede {
    font-size: 1.05rem;
    color: var(--ns-body);
    margin: 0 0 2rem;
}
.help-toc {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.25rem;
    padding: 1rem 0;
    border-top: 1px solid var(--ns-border);
    border-bottom: 1px solid var(--ns-border);
    margin-bottom: 2.5rem;
}
.help-toc a {
    color: var(--ns-crimson);
    text-decoration: none;
    font-size: 0.92rem;
}
.help-toc a:hover { text-decoration: underline; }

.help-section {
    margin: 0 0 2.5rem;
}
.help-section h2 {
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.015em;
    color: var(--ns-ink);
    margin: 0 0 0.75rem;
}
.help-section h3 {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--ns-ink);
    margin: 1.5rem 0 0.5rem;
}
.help-section p,
.help-section ol,
.help-section ul {
    font-size: 0.98rem;
}
.help-section ol, .help-section ul {
    padding-left: 1.25rem;
}
.help-cta {
    background: var(--ns-crimson-soft);
    border: 1px solid var(--ns-crimson-border);
    border-radius: 10px;
    padding: 1.25rem 1.5rem;
}
.help-cta h2 { font-size: 1.15rem; margin-bottom: 0.5rem; }

/* ================================================
   EasyUI datagrid retune — direction C
   Variables are set in :root; these rules apply them.
   ================================================ */

/* Datagrid surface */
.datagrid-wrap,
.datagrid-view,
.datagrid-body,
.datagrid-header-row,
.datagrid-row { background-color: var(--easyui-bg) !important; }

.datagrid-htable,
.datagrid-btable { color: var(--easyui-text); }

/* Header */
.datagrid-header,
.datagrid-header-inner {
    background: var(--easyui-bg) !important;
    border-bottom: 1px solid var(--easyui-border) !important;
}
.datagrid-cell,
.datagrid-header-cell { color: var(--easyui-text); }
.datagrid-cell-group,
.datagrid-header .datagrid-cell-group { color: var(--ns-muted); font-weight: 600; }

/* Borders */
.datagrid,
.datagrid-view1,
.datagrid-view2,
.datagrid-toolbar,
.datagrid-pager { border-color: var(--easyui-border) !important; }

/* Hover + selected */
.datagrid-row-over { background-color: var(--easyui-hover) !important; }
.datagrid-row-selected,
.datagrid-row-selected td { background-color: var(--easyui-selected) !important; color: var(--easyui-active) !important; }

/* Sort arrows */
.datagrid-sort-icon { color: var(--easyui-active); }

/* Pager */
.datagrid-pager .pagination-page-list,
.datagrid-pager .pagination-info { color: var(--ns-muted); }

/* Combobox + textbox inputs used inside EasyUI */
.textbox,
.combo {
    border-color: var(--easyui-border) !important;
    background: var(--ns-white) !important;
    border-radius: 6px;
}
.textbox:hover,
.combo:hover { border-color: var(--ns-crimson) !important; }
.textbox-focused,
.combo-focused {
    border-color: var(--ns-crimson) !important;
    box-shadow: 0 0 0 3px var(--ns-crimson-soft) !important;
}

/* Tabs (used on sample search page) */
.tabs-header,
.tabs-tool,
.tabs-scroller-left,
.tabs-scroller-right { background: var(--easyui-bg) !important; border-color: var(--easyui-border) !important; }
.tabs li a.tabs-inner { color: var(--ns-body); }
.tabs li.tabs-selected a.tabs-inner {
    background: white;
    color: var(--easyui-active);
    border-bottom-color: white;
    font-weight: 600;
}
.help-cta a { color: var(--ns-crimson); }

/* ================================================
   Sidebar user panel (bottom of sidebar)
   ================================================ */
.user-panel {
    position: relative;
    margin: 0.5rem 0.5rem 0.75rem;
    padding: 0.55rem 0.75rem;
    background: var(--ns-white);
    border: 1px solid var(--ns-border);
    border-radius: 9px;
    display: flex;
    align-items: center;
    gap: 0.625rem;
    transition: background var(--ns-transition);
    outline: none;
}
.user-panel:hover { background: #fcfcfc; }

.user-avatar {
    width: 30px; height: 30px;
    border-radius: 50%;
    background: var(--ns-crimson);
    color: white;
    font-size: 0.72rem;
    font-weight: 600;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    letter-spacing: 0.01em;
}
.user-meta { flex: 1; min-width: 0; }
.user-name {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--ns-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.user-role {
    font-size: 0.68rem;
    color: var(--ns-muted);
}
.user-menu-btn {
    background: transparent;
    border: 0;
    color: var(--ns-muted);
    padding: 4px 6px;
    border-radius: 5px;
    cursor: pointer;
}
.user-menu-btn:hover { background: var(--ns-sidebar-hover); color: var(--ns-ink); }

.user-menu {
    position: absolute;
    bottom: calc(100% + 4px);
    right: 0;
    min-width: 180px;
    background: var(--ns-white);
    border: 1px solid var(--ns-border);
    border-radius: 8px;
    box-shadow: var(--ns-shadow-md);
    padding: 0.35rem;
    display: none;
    z-index: 1000;
}
.user-menu.open { display: block; }
.user-menu a {
    display: flex; align-items: center; gap: 0.5rem;
    padding: 0.45rem 0.6rem;
    font-size: 0.86rem;
    color: var(--ns-body);
    text-decoration: none;
    border-radius: 5px;
}
.user-menu a:hover { background: var(--ns-sidebar-hover); color: var(--ns-ink); }

.user-panel--anon {
    justify-content: center;
}
.btn-signin {
    background: var(--ns-crimson);
    color: white;
    border: 0;
    padding: 0.4rem 0.85rem;
    font-size: 0.85rem;
    font-weight: 600;
    border-radius: 7px;
}
.btn-signin:hover { background: var(--ns-crimson-hover); color: white; }

/* ================================================
   Sidebar sections
   ================================================ */
.sidebar-section {
    padding: 0.875rem 0 0.25rem;
}
.sidebar-section:first-of-type { padding-top: 0.5rem; }

.sidebar-section-label {
    padding: 0 1rem 0.35rem;
    font-size: 0.62rem;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--ns-muted);
    font-weight: 600;
}

/* Override existing nav-link / submenu visuals to match direction C */
.sidebar-nav .nav-item .nav-link {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.4rem 0.65rem;
    margin: 0 0.5rem;
    border-radius: 7px;
    color: var(--ns-body);
    font-size: 0.88rem;
    font-weight: 500;
    text-decoration: none;
    transition: background var(--ns-transition);
}
.sidebar-nav .nav-item .nav-link:hover { background: var(--ns-sidebar-hover); }
.sidebar-nav .nav-item .nav-link.active,
.sidebar-nav .nav-item .nav-link[aria-current="page"] {
    background: var(--ns-sidebar-active);
    color: var(--ns-crimson);
    font-weight: 600;
}
.sidebar-nav .submenu {
    margin: 0.15rem 0 0.35rem;
    padding: 0;
    list-style: none;
}
.sidebar-nav .submenu .nav-link {
    padding-left: 2.25rem;
    font-size: 0.82rem;
    margin-left: 0.5rem;
    margin-right: 0.5rem;
}

/* ================================================
   Quick Access controls
   ================================================ */
.qa-input {
    position: relative;
    margin: 0.35rem 0.85rem;
}
.qa-input input {
    width: 100%;
    padding: 0.38rem 0.6rem 0.38rem 1.85rem;
    font-size: 0.82rem;
    background: var(--ns-white);
    border: 1px solid var(--ns-border);
    border-radius: 7px;
    color: var(--ns-ink);
    outline: none;
    font-family: inherit;
}
.qa-input input::placeholder { color: var(--ns-muted); }
.qa-input input:focus {
    border-color: var(--ns-crimson);
    box-shadow: 0 0 0 3px var(--ns-crimson-soft);
}
.qa-input .qa-ico {
    position: absolute;
    left: 0.55rem; top: 50%;
    transform: translateY(-50%);
    color: var(--ns-muted);
    font-size: 0.82rem;
    pointer-events: none;
}

.qa-cta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0.4rem 0.85rem 0.2rem;
    padding: 0.4rem 0.65rem;
    border: 1px dashed var(--ns-crimson-border);
    border-radius: 7px;
    background: transparent;
    color: var(--ns-crimson);
    font-size: 0.85rem;
    font-weight: 600;
    text-decoration: none;
    transition: background var(--ns-transition);
}
.qa-cta:hover {
    background: var(--ns-crimson-soft);
    color: var(--ns-crimson-hover);
}
.qa-cta .qa-plus { font-weight: 700; line-height: 1; }

/* ---------- Nessie promoted button (navbar + home) ---------- */
.nessie-btn {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin: 0.5rem;
    padding: 0.6rem 0.85rem;
    border-radius: 9px;
    background: var(--ns-crimson);
    color: var(--ns-white);
    text-decoration: none;
    font-weight: 600;
    font-size: 0.9rem;
    transition: background var(--ns-transition);
}
.nessie-btn:hover { background: var(--ns-crimson-hover); color: var(--ns-white); }
.nessie-btn__logo { width: 24px; height: 24px; flex: 0 0 auto; border-radius: 50%; }
.nessie-btn__label { line-height: 1; }

/* ---------- Home dashboard: clickable tiles, list header, project cards ---------- */
a.dash-tile { text-decoration: none; color: inherit; display: block; transition: background var(--ns-transition); }
a.dash-tile:hover { background: var(--ns-sidebar-hover); }
.dash-row-head { color: var(--ns-muted); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; }
.dash-row-head:hover { background: none; }
.dp-name { font-weight: 600; color: var(--ns-body); overflow-wrap: anywhere; }
.dp-avatar { width: 24px; height: 24px; border-radius: 5px; object-fit: cover; margin-right: 0.5rem; flex: 0 0 auto; }
.dp-logo a { gap: 0; }
.dash-projects-head { display: flex; align-items: center; justify-content: space-between; }

/* ---------- Clade accent helpers (one language, catalogs + project pages) ---------- */
.clade-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 0.4rem; vertical-align: middle; }
.clade-accent--source    { border-left: 3px solid var(--ns-clade-source); }
.clade-accent--processed { border-left: 3px solid var(--ns-clade-processed); }
.clade-accent--raw       { border-left: 3px solid var(--ns-clade-raw); }
.clade-accent--analyzed  { border-left: 3px solid var(--ns-clade-analyzed); }
.clade-accent--unassigned { border-left: 3px solid var(--ns-border); }
.clade-dot--source    { background: var(--ns-clade-source); }
.clade-dot--processed { background: var(--ns-clade-processed); }
.clade-dot--raw       { background: var(--ns-clade-raw); }
.clade-dot--analyzed  { background: var(--ns-clade-analyzed); }
.clade-dot--unassigned { background: var(--ns-muted); }

/* ---------- Read-only catalog list table (sample types + assays) ---------- */
.cat-toolbar { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 1rem; }
.cat-search {
    width: 320px; max-width: 100%;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--ns-border); border-radius: 8px;
    font-size: 0.85rem; color: var(--ns-body); background: var(--ns-white);
}
.cat-search:focus { outline: none; border-color: var(--ns-crimson-border); box-shadow: 0 0 0 3px var(--ns-crimson-soft); }
.cat-count { color: var(--ns-muted); font-size: 0.8rem; }
.cat-table { width: 100%; max-width: 1100px; border-collapse: separate; border-spacing: 0; font-size: 0.85rem; }
.cat-table .cat-group-row th { text-align: left; padding: 0.4rem 0.6rem; }
.cat-table .cat-group-row th .cat-group-toggle { border-radius: 6px; }
/* Each clade section is banded in its dmac.clades colour (as it was before). */
.cat-table tbody.clade-accent--source    .cat-group-row th { background: var(--ns-clade-source-tint);    border-left: 4px solid var(--ns-clade-source); }
.cat-table tbody.clade-accent--processed .cat-group-row th { background: var(--ns-clade-processed-tint); border-left: 4px solid var(--ns-clade-processed); }
.cat-table tbody.clade-accent--raw       .cat-group-row th { background: var(--ns-clade-raw-tint);       border-left: 4px solid var(--ns-clade-raw); }
.cat-table tbody.clade-accent--analyzed  .cat-group-row th { background: var(--ns-clade-analyzed-tint);  border-left: 4px solid var(--ns-clade-analyzed); }
.cat-table tbody.clade-accent--unassigned .cat-group-row th { background: var(--ns-sidebar-bg); border-left: 4px solid var(--ns-border); }
.cat-group-toggle {
    background: none; border: 0; cursor: pointer; padding: 0.15rem 0.35rem 0.15rem 0.5rem;
    font-size: 0.8rem; font-weight: 700; color: var(--ns-ink); border-radius: 6px;
}
.cat-group-toggle:hover { background: var(--ns-sidebar-hover); }
.cat-group-n { color: var(--ns-muted); font-weight: 600; margin-left: 0.35rem; }
.cat-table .cat-head-row th {
    position: sticky; top: 0; z-index: 1; background: var(--ns-sidebar-bg);
    text-transform: uppercase; font-size: 0.68rem; letter-spacing: 0.06em;
    color: var(--ns-muted); text-align: left; padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--ns-border);
}
.cat-table .cat-row td { padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--ns-border); color: var(--ns-body); }
.cat-table .cat-row:hover td { background: #fbfbfc; }
.cat-table .cat-code { white-space: nowrap; }
.cat-table .cat-code a { text-decoration: none; }
.cat-table .cat-code code { font-family: var(--ns-font-mono); color: var(--ns-crimson); font-size: 0.82rem; }
.cat-table tbody.is-collapsed .cat-head-row,
.cat-table tbody.is-collapsed .cat-row { display: none; }
.cat-empty { color: var(--ns-muted); padding: 1rem 0; }

/* ---------- Read-only attribute-definitions table (sample type detail) ---------- */
.attrs-ro-table { width: 100%; max-width: 1100px; border-collapse: separate; border-spacing: 0; font-size: 0.85rem; }
.attrs-ro-table thead th {
    position: sticky; top: 0; z-index: 1; background: var(--ns-sidebar-bg);
    text-transform: uppercase; font-size: 0.68rem; letter-spacing: 0.06em;
    color: var(--ns-muted); text-align: left; padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--ns-border);
}
.attrs-ro-table td { padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--ns-border); color: var(--ns-body); vertical-align: top; }
.attrs-ro-table .ar-pos { font-family: var(--ns-font-mono); color: var(--ns-muted); width: 2.5rem; }
.attrs-ro-table .ar-name { font-weight: 600; }
.attrs-ro-table .ar-flag { text-align: center; width: 3rem; color: var(--ns-crimson); }
.attrs-ro-table .ar-muted { color: var(--ns-muted); }
.ar-badge {
    display: inline-block; font-size: 0.72rem; padding: 0.1rem 0.45rem; margin: 0.1rem 0.15rem 0.1rem 0;
    border-radius: 10px; border: 1px solid var(--ns-border); background: var(--ns-sidebar-bg); color: var(--ns-muted);
}
.attrs-ro-msg { color: var(--ns-muted); padding: 1rem 0.6rem; }

/* ---------- attrs-* shared components -------------------------------------
   Extracted verbatim from the inline <style> block of
   seek/templates/sampleAttributes.html so the sample-attributes editor and the
   association workbench (seek/templates/internal_assays.html,
   seek/templates/clades.html, static/js/custom/ns-vocab-workbench.js) share one
   definition instead of two drifting copies. The .attrs-ro-table family above
   is the precedent.

   Rules here are byte-identical to the ones deleted from that template (modulo
   the single added `.ns-wb-evidence dt` selector noted below). Only genuinely
   reusable component rules moved: anything bound to the attributes page's own
   table/tray/modal structure (.attrs-row*, .attrs-table*, .attrs-tray*,
   .attrs-detail-inner, .attrs-review-*, .attrs-diff*) stayed inline.

   CASCADE NOTE. This stylesheet is loaded from base.html and therefore comes
   BEFORE that page's inline <style>, so a moved rule now loses to any inline
   rule of EQUAL specificity that survived. Checked one by one; the only inline
   rules that touch these components are higher-specificity and so were already
   winning on specificity alone, order irrelevant:
     .attrs-tray .attrs-btn / --primary (0,2,0) > .attrs-btn (0,1,0)
     .attrs-row.is-open .attrs-chevron  (0,3,0) > .attrs-chevron (0,1,0)
     .attrs-detail-field .attrs-input   (0,2,0) — different property set
   No equal-specificity inline rule targets any moved selector, and nothing
   later in this stylesheet matches an .attrs-* class.
   -------------------------------------------------------------------------- */

.attrs-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.83rem;
    font-weight: 600;
    font-family: var(--ns-font-body);
    border-radius: 6px;
    border: 1px solid var(--ns-border);
    background: var(--ns-white);
    color: var(--ns-body);
    padding: 0.42rem 0.85rem;
    cursor: pointer;
    transition: all 0.15s var(--ns-ease);
    white-space: nowrap;
}
.attrs-btn:hover:not(:disabled) { border-color: var(--ns-crimson-border); color: var(--ns-crimson); }
.attrs-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.attrs-btn--primary {
    background: var(--ns-crimson);
    border-color: var(--ns-crimson);
    color: #fff;
}
.attrs-btn--primary:hover:not(:disabled) {
    background: var(--ns-crimson-hover);
    border-color: var(--ns-crimson-hover);
    color: #fff;
}
.attrs-btn--ghost { border-color: transparent; background: transparent; color: var(--ns-muted); }
.attrs-btn--ghost:hover:not(:disabled) { background: var(--ns-sidebar-hover); color: var(--ns-body); }
.attrs-chevron { transition: transform 0.15s var(--ns-ease); display: inline-block; }
.attrs-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    border-radius: 999px;
    padding: 0.08rem 0.45rem;
    border: 1px solid var(--ns-crimson-border);
    color: var(--ns-crimson);
    background: var(--ns-white);
    white-space: nowrap;
}
.attrs-chip--quiet { border-color: var(--ns-border); color: var(--ns-muted); }
.attrs-badges { display: flex; flex-wrap: wrap; gap: 0.25rem; }
.attrs-badge {
    font-size: 0.7rem;
    color: var(--ns-muted);
    background: var(--ns-bg);
    border: 1px solid var(--ns-border);
    border-radius: 999px;
    padding: 0.1rem 0.5rem;
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
}
.attrs-badge b { color: var(--ns-body); font-weight: 600; }
/* The one moved rule that is not byte-identical: `.ns-wb-evidence dt` is added
   to the selector list so the workbench's evidence panel wears the same field
   label as the attributes page's detail row instead of a second copy of these
   six declarations. The declarations are untouched, and the added selector has
   the same specificity (0,1,1) and cannot match anything on sampleAttributes.html
   (no .ns-wb-evidence there), so .attrs-detail-field label behaves exactly as
   before. */
.attrs-detail-field label,
.ns-wb-evidence dt {
    display: block;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ns-muted);
    margin-bottom: 0.25rem;
}
.attrs-count {
    font-size: 0.75rem;
    border: 1px solid var(--ns-border);
    border-radius: 999px;
    padding: 0.12rem 0.6rem;
    color: var(--ns-body);
    background: var(--ns-bg);
}
.attrs-count b { font-family: var(--ns-font-mono); }
.attrs-count--loud { border-color: var(--ns-crimson-border); background: var(--ns-crimson-soft); color: var(--ns-crimson); }
.attrs-muted { color: var(--ns-muted); font-size: 0.82rem; }

/* ---------- Sample type detail: lineage strip + tags ---------- */
.cat-lineage { display: flex; flex-direction: column; gap: 0.5rem; margin: 1rem 0 0.5rem; }
.lin-group { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem; }
.lin-label { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ns-muted); min-width: 6.5rem; }
.lin-code { font-family: var(--ns-font-mono); font-size: 0.8rem; color: var(--ns-crimson); text-decoration: none; padding: 0.05rem 0.4rem; border: 1px solid var(--ns-border); border-radius: 6px; }
.lin-code:hover { border-color: var(--ns-crimson-border); background: var(--ns-crimson-soft); }
.lin-assay { font-size: 0.82rem; color: var(--ns-body); text-decoration: none; padding: 0.05rem 0.4rem; border-bottom: 1px dotted var(--ns-muted); }
.lin-assay:hover { color: var(--ns-crimson); }
.cat-tags { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem; margin: 0.5rem 0 1rem; }
.cat-tag { font-size: 0.76rem; color: var(--ns-muted); padding: 0.05rem 0.45rem; border-radius: 10px; background: var(--ns-sidebar-bg); border: 1px solid var(--ns-border); }
.cat-subhead { font-size: 1rem; font-weight: 700; color: var(--ns-ink); margin: 1.5rem 0 0.6rem; }

/* ---------- Modal-over-route overlay ---------- */
.modal-route-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 2000; display: flex; align-items: center; justify-content: center; }
.modal-route-panel { position: relative; width: min(1200px, 95vw); height: 90vh; background: var(--ns-white); border-radius: 10px; overflow: auto; box-shadow: 0 12px 48px rgba(0,0,0,0.35); }
.modal-route-close { position: absolute; top: 8px; right: 12px; border: 0; background: none; font-size: 1.6rem; line-height: 1; cursor: pointer; color: var(--ns-muted); z-index: 1; }
.modal-route-close:hover { color: var(--ns-ink); }
.modal-route-body { padding: 1.5rem; min-height: 100%; }
.modal-route-iframe { width: 100%; height: 100%; border: 0; display: block; }
/* Iframe payloads (the connections diagram) fill the whole panel: give the body
   a definite height so the iframe's height:100% resolves, and stop the panel
   scrolling so it does not leave a white gutter below a short iframe. */
.modal-route-panel:has(.modal-route-iframe) { overflow: hidden; }
.modal-route-panel:has(.modal-route-iframe) .modal-route-body { padding: 0; height: 100%; }

/* ---------- Project sample-counts table (the /samples/ full view) ---------- */
.project-samples-table td.num, .project-samples-table th.num { text-align: right; white-space: nowrap; }
.project-samples-table .cat-total-row td { font-weight: 700; border-top: 1.5px solid var(--ns-border); }

/* ---------- Sample tree v2 fullscreen ---------- */
#tree_container { position: relative; }   /* anchors the button; overridden by .tree-fullscreen */
.tree-fullscreen-btn {
    position: absolute; top: 8px; right: 8px; z-index: 5;
    width: 32px; height: 32px; line-height: 1;
    border: 1px solid var(--ns-border); border-radius: 6px;
    background: var(--ns-white); color: var(--ns-body);
    font-size: 1rem; cursor: pointer;
}
.tree-fullscreen-btn:hover { border-color: var(--ns-crimson-border); color: var(--ns-crimson); background: var(--ns-crimson-soft); }
#tree_container.tree-fullscreen {
    position: fixed; inset: 0; z-index: 2000;
    background: var(--ns-sidebar-bg); padding: 8px;
    height: 100vh !important; width: 100vw;
}
#tree_container.tree-fullscreen #svg { height: 100% !important; }

/* ================================================
   Sidebar header with BMC lockup
   ================================================ */
.sidebar-header {
    padding: 0.85rem 1rem 0.85rem;
    border-bottom: 1px solid var(--ns-border);
    flex-shrink: 0;
}
.bmc-lockup {
    margin-bottom: 0.45rem;
}
.bmc-logo {
    width: 100%;
    max-width: 200px;
    height: auto;
    display: block;
    margin: 0 auto;
}
.sidebar-wordmark {
    font-family: var(--ns-font-display);
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: -0.015em;
    color: var(--ns-ink);
    text-decoration: none;
}
.sidebar-wordmark .wm-hi { color: var(--ns-crimson); }

.sidebar-foot {
    flex-shrink: 0;
    border-top: 1px solid var(--ns-border);
    padding-top: 0.25rem;
}

/* ================================================
   Mobile toggle (replaces the header hamburger)
   ================================================ */
.mobile-toggle {
    margin: 0.85rem 0 0 0.85rem;
    width: 36px; height: 36px;
    border: 1px solid var(--ns-border);
    border-radius: 7px;
    background: var(--ns-white);
    color: var(--ns-ink);
    cursor: pointer;
}
.mobile-toggle:hover { background: var(--ns-sidebar-hover); }
.mobile-toggle i { font-size: 1.05rem; }

/* Remove the now-unused header.height layout reservation */
.content { margin-top: 0; }

/* ================================================
   Footer — direction-C polish
   ================================================ */
.footer {
    background: var(--ns-footer-bg);
    border-top: 1px solid var(--ns-border);
    padding: 0.85rem 1.5rem;
    font-size: 0.8rem;
    color: var(--ns-muted);
}
.footer-content {
    display: flex;
    align-items: center;
    gap: 1rem;
    max-width: 1280px;
    margin: 0 auto;
}
.footer-brand img {
    max-height: 28px;
    width: auto;
    opacity: 0.85;
}

/* ================================================
   Home dashboard
   ================================================ */
.dashboard {
    max-width: 1100px;
    margin: 0 auto;
    padding: 2rem 1.75rem 3rem;
    font-family: var(--ns-font-body);
}

.dash-hero { margin-bottom: 1.75rem; }
.dash-eyebrow {
    font-size: 0.68rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ns-muted);
    font-weight: 600;
    margin: 0;
}
.dash-h1 {
    font-size: 1.95rem;
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--ns-ink);
    margin: 0.15rem 0 0.35rem;
}
.dash-sub {
    font-size: 0.95rem;
    color: var(--ns-body);
    margin: 0;
}

.dash-tiles {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.75rem;
    margin-bottom: 1.5rem;
}
.dash-tile {
    padding: 0.95rem 1.05rem;
    background: var(--ns-sidebar-bg);
    border: 1px solid var(--ns-border);
    border-radius: 10px;
}
.dash-tile .t-label {
    font-size: 0.7rem;
    color: var(--ns-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}
.dash-tile .t-num {
    font-size: 1.65rem;
    font-weight: 700;
    color: var(--ns-crimson);
    margin-top: 0.1rem;
}
.dash-tile .t-delta {
    font-size: 0.72rem;
    color: var(--ns-muted);
    margin-top: 0.1rem;
}

/* Projects box — partner-program logos pinned at the bottom of the dashboard.
   Crimson outline + sidebar-grey fill per user spec. */
.dash-projects {
    background: var(--ns-sidebar-bg);
    border: 1px solid var(--ns-crimson);
    border-radius: 10px;
    padding: 0.85rem 1.05rem 1rem;
    margin-top: 1.25rem;
}
.dash-projects-head {
    margin-bottom: 0.65rem;
}
.dash-projects .dp-label {
    font-size: 0.7rem;
    color: var(--ns-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}
.dp-grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 0.6rem;
}
.dp-logo a {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    min-height: 48px;
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--ns-border);
    border-radius: 8px;
    text-decoration: none;
    color: var(--ns-body);
    transition: background var(--ns-transition),
                border-color var(--ns-transition);
}
.dp-logo a:hover,
.dp-logo a:focus-visible {
    background: var(--ns-sidebar-hover);
    border-color: var(--ns-crimson-border);
    outline: none;
}
.dp-logo a:focus-visible {
    box-shadow: 0 0 0 2px var(--ns-crimson-border);
}
.dp-logo img {
    max-height: 40px;
    max-width: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
}
@media (max-width: 900px) {
    .dp-grid { gap: 0.5rem; }
}

.dash-row {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 1rem;
}
@media (max-width: 900px) {
    .dash-tiles { grid-template-columns: 1fr; }
    .dash-row { grid-template-columns: 1fr; }
}

.dash-card {
    border: 1px solid var(--ns-border);
    border-radius: 10px;
    overflow: hidden;
    background: var(--ns-white);
}
.dash-card-head {
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--ns-border);
    display: flex;
    align-items: center;
    background: var(--ns-white);
}
.dash-card-head h2 {
    margin: 0;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--ns-ink);
}
.dash-more {
    margin-left: auto;
    font-size: 0.78rem;
    color: var(--ns-crimson);
    text-decoration: none;
}
.dash-more:hover { text-decoration: underline; }
.dash-list { list-style: none; padding: 0; margin: 0; }
.dash-row-item {
    display: grid;
    grid-template-columns: 110px 1fr 120px;
    gap: 0.6rem;
    padding: 0.55rem 1rem;
    font-size: 0.85rem;
    border-bottom: 1px solid var(--ns-border);
}
.dash-row-item:last-child { border-bottom: 0; }
.dash-row-item .uid {
    color: var(--ns-muted);
    font-family: var(--ns-font-mono);
    font-size: 0.78rem;
}
.dash-row-item .name { color: var(--ns-ink); }
.dash-row-item .proj {
    color: var(--ns-muted);
    font-size: 0.8rem;
    text-align: right;
}
.dash-empty {
    padding: 1.5rem 1rem;
    text-align: center;
    color: var(--ns-muted);
    font-size: 0.88rem;
}

.dash-actions {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.6rem;
}
.dash-action {
    padding: 0.85rem 1rem;
    border: 1px solid var(--ns-border);
    border-radius: 10px;
    background: var(--ns-white);
    text-decoration: none;
    color: var(--ns-body);
    display: block;
    transition: background var(--ns-transition);
}
.dash-action:hover { background: var(--ns-sidebar-hover); color: var(--ns-body); }
.dash-action h3 {
    margin: 0 0 0.2rem;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--ns-ink);
}
.dash-action p {
    margin: 0;
    font-size: 0.8rem;
    color: var(--ns-muted);
}
.dash-action.accent {
    background: var(--ns-crimson-soft);
    border-color: var(--ns-crimson-border);
}
.dash-action.accent h3 { color: var(--ns-crimson); }
.dash-action.accent:hover { background: #f7d8de; }

/* ================================================
   Mobile view v1 — breakpoints 768 / 576 / 400
   Pairs with the dual-rendering `.d-only` / `.m-only`
   pattern used in seek/templates/searchAdvanced.html
   and seek/templates/projectPage.html.
   ================================================ */

.m-only { display: none; }
.d-only { display: block; }

@media (max-width: 767.98px) {
    .d-only { display: none; }
    .m-only { display: block; }

    /* iOS zoom-on-focus prevention — every text input gets >= 16px font */
    input[type="text"],
    input[type="search"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    textarea,
    select {
        font-size: 16px !important;
    }
}

/* ---------- Touch targets (>= 44px hit area) ---------- */
@media (max-width: 991.98px) {
    .mobile-toggle {
        width: 44px;
        height: 44px;
    }
    .sidebar-nav .nav-link {
        min-height: 44px;
        display: flex;
        align-items: center;
    }
    .user-panel .user-menu-btn {
        min-width: 44px;
        min-height: 44px;
    }
    .qa-input input {
        min-height: 44px;
    }
    .qa-cta {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
    }
}

/* ---------- Sample Search mobile results ---------- */
.m-results {
    list-style: none;
    padding: 0;
    margin: 0;
}
.m-result-row {
    border-bottom: 1px solid var(--ns-border);
    min-height: 44px;
    padding: 10px 0;
}
.m-result-row a {
    display: block;
    text-decoration: none;
    color: inherit;
}
.m-result-row .r-line1 {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 0.95rem;
}
.m-result-row .r-uid {
    color: var(--ns-crimson);
    font-weight: 600;
    flex-shrink: 0;
}
.m-result-row .r-title {
    color: var(--ns-ink);
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.m-result-row .r-line2 {
    font-size: 0.78rem;
    color: var(--ns-muted);
    margin-top: 3px;
}
.m-result-count {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--ns-ink);
    margin: 0 0 0.5rem;
}
.m-filter-form {
    margin-bottom: 1rem;
}
.m-filter-form summary {
    cursor: pointer;
    padding: 12px 14px;
    background: var(--ns-sidebar-bg);
    border: 1px solid var(--ns-border);
    border-radius: 8px;
    font-weight: 600;
    color: var(--ns-ink);
    list-style: none;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.m-filter-form summary::after { content: "▾"; color: var(--ns-crimson); }
.m-filter-form[open] summary::after { content: "▴"; }
.m-filter-form .m-filter-body {
    padding: 12px 0 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.m-filter-form .m-filter-body label {
    font-size: 0.78rem;
    color: var(--ns-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 600;
}
.m-filter-form .m-filter-body input,
.m-filter-form .m-filter-body select {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--ns-border);
    border-radius: 6px;
}
.m-filter-form .m-filter-submit {
    background: var(--ns-crimson);
    color: white;
    border: none;
    border-radius: 6px;
    padding: 12px;
    font-weight: 600;
    margin-top: 4px;
    min-height: 44px;
}

/* ---------- Project Detail mobile stats ---------- */
.m-stats-cards {
    /* The flex layout is turned on only in the mobile media query below, so this
       stack does not render on desktop alongside the .d-only table (the duplicate). */
    flex-direction: column;
    gap: 10px;
}
@media (max-width: 767.98px) {
    .m-stats-cards { display: flex; }
}
.m-stat-card {
    border: 1px solid var(--ns-border);
    border-radius: 8px;
    background: var(--ns-white);
    padding: 12px 14px;
}
.m-stat-card .group {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--ns-ink);
    margin-bottom: 8px;
}
.m-stat-card .row {
    display: flex;
    justify-content: space-between;
    font-size: 0.85rem;
    padding: 4px 0;
    border-top: 1px solid var(--ns-border);
}
.m-stat-card .row:first-of-type { border-top: none; }
.m-stat-card .row .label { color: var(--ns-muted); }
.m-stat-card .row .val { color: var(--ns-ink); font-weight: 500; }

/* ---------- Dashboard mobile refinements ---------- */
@media (max-width: 575.98px) {
    .dash-tiles { grid-template-columns: 1fr; }
    .dash-h1 { font-size: 1.5rem; }
    .dash-row-item {
        display: flex;
        flex-wrap: wrap;
        grid-template-columns: none;
        gap: 0.25rem 0.5rem;
    }
    .dash-row-item .name { flex: 1; }
    .dash-row-item .proj {
        flex-basis: 100%;
        margin-top: 2px;
        font-size: 0.78rem;
        text-align: left;
    }
    .dash-action { min-height: 44px; }
    .footer { padding: 0.75rem 1rem; }
}
@media (max-width: 400px) {
    .dash-h1 { font-size: 1.3rem; }
    .dash-action { padding: 0.75rem; }
}

/* ---------- Tutorials TOC mobile ---------- */
@media (max-width: 767.98px) {
    .help-toc a {
        display: block;
        padding: 12px 0;
        min-height: 44px;
        border-bottom: 1px solid var(--ns-border);
    }
}

/* ---------- Projects List touch targets ---------- */
@media (max-width: 767.98px) {
    .project-card .fairdata-link {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        padding: 0.4rem 0.75rem;
    }
    .project-card .project-chevron {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

/* ---------- EasyUI desktop-only notice ---------- */
/* (Notice display rule is already wired in the existing @media (max-width: 768px)
   block elsewhere in this file — Task 11 adds the markup to each desktop-only page.) */

/* === Sample search UI rework ========================================= */

/* Ellipsis + hover tooltip for long datagrid cells (paired with
   nsEllipsisFormatter). The datagrid cell already clips overflow; this adds
   the ellipsis affordance so users know there is more, revealed via title. */
.ns-ellip {
	display: block;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Search-form action buttons: a crimson primary Search + a neutral Reset,
   sized to sit level with the adjacent search inputs. */
.ns-search-actions {
	display: inline-flex;
	gap: 8px;
	align-items: flex-end;
}
.ns-btn-search,
.ns-btn-reset {
	height: 38px;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	box-sizing: border-box;
	padding: 0 18px;
	border-radius: 6px;
	font-family: var(--ns-font-body);
	font-size: 14px;
	font-weight: 600;
	letter-spacing: 0.01em;
	cursor: pointer;
	text-decoration: none;
}
.ns-btn-search {
	background: var(--ns-crimson);
	border: 1px solid var(--ns-crimson);
	color: var(--ns-white);
}
.ns-btn-search:hover,
.ns-btn-search:focus {
	background: var(--ns-crimson-hover);
	border-color: var(--ns-crimson-hover);
	box-shadow: 0 0 0 3px rgba(163, 31, 52, 0.18);
	color: var(--ns-white);
	text-decoration: none;
}
.ns-btn-reset {
	background: var(--ns-white);
	border: 1px solid var(--ns-border);
	color: var(--ns-body);
}
.ns-btn-reset:hover,
.ns-btn-reset:focus {
	background: var(--ns-sidebar-hover);
	border-color: #ccc;
	color: var(--ns-body);
	text-decoration: none;
}

/* Always-on filter row: a soft crimson-tinted band so it reads as an
   interactive filter strip, clearly distinct from the column headers.
   !important beats the base .datagrid-filter-row rule earlier in this file. */
.datagrid-filter-row {
	background: var(--ns-crimson-soft) !important;
	border-bottom: 1px solid var(--ns-crimson-border) !important;
}
.datagrid-filter-row .datagrid-filter input,
.datagrid-filter-row input {
	box-sizing: border-box !important;
	width: 100% !important;
	height: 26px !important;
	padding: 3px 7px !important;
	border: 1px solid var(--ns-crimson-border) !important;
	border-radius: 5px !important;
	background: var(--ns-white) !important;
	color: var(--ns-body) !important;
	font-size: 12px !important;
}
.datagrid-filter-row .datagrid-filter input::placeholder,
.datagrid-filter-row input::placeholder {
	color: #b07680;
	font-style: italic;
}
.datagrid-filter-row .datagrid-filter input:focus,
.datagrid-filter-row input:focus {
	outline: none !important;
	border-color: var(--ns-crimson) !important;
	box-shadow: 0 0 0 2px rgba(163, 31, 52, 0.15) !important;
}

/* --- Search tabs: flow the rigid EasyUI region layout so the criteria form
   and results grid size to their content, instead of sitting inside oversized
   fixed-height regions that left large empty gaps and clipped the button row.
   Scoped to #search_tab so no other EasyUI layout on the site is affected. --- */
#search_tab { height: auto !important; }
#search_tab .tabs-panels,
#search_tab .tabs-panels > .panel > .panel-body,
#search_tab > .panel > .panel-body { height: auto !important; position: static !important; }
#search_tab .layout,
#search_tab .layout-body { position: static !important; height: auto !important; width: auto !important; }
#search_tab .layout-panel { position: static !important; height: auto !important; width: auto !important; left: auto !important; top: auto !important; }
#search_tab .layout-panel-north,
#search_tab .layout-panel-center { height: auto !important; }
#search_tab .layout-split-proxy-v,
#search_tab .layout-expand,
#search_tab .layout-split-north,
#search_tab .layout-split-center { display: none !important; }
/* MUST stay after the rule above. EasyUI puts `layout-split-center` on the
   region PANEL itself, not on a separate splitter handle, whenever that region
   declares `split:true`. The Advanced Sample Search tab's centre region does
   (searchAdvanced.html), so the splitter-hiding rule above was hiding the whole
   results grid: the datagrid was built with all its rows and then display:none'd,
   which is why the tab looked empty while the search reported 5658 hits. Same
   specificity as that rule, so order is what decides it. The Sample Search tab
   is unaffected because its centre region has no split:true. */
#search_tab .layout-panel-center { display: block !important; }
/* Tighten the search-criteria panel so there is less blank space around it. */
#search_tab .well { padding: 12px 16px !important; margin-bottom: 10px !important; }
#search_tab .well .table { margin-bottom: 8px !important; }

/* --- Association workbench (see docs/superpowers/specs/2026-09-10-…-design.md)
   Dressed in the shared attrs-* components defined near the top of this file
   (.attrs-btn, .attrs-chip, .attrs-count, .attrs-chevron, .attrs-muted, and the
   `.attrs-detail-field label, .ns-wb-evidence dt` field label), so this page and
   seek/templates/sampleAttributes.html read as one design system rather than two.
   What remains below is only what those components cannot say: the tier
   semantics, and what the markup needs because it lives inside an easyui grid.
   ------------------------------------------------------------------------- */

/* Toolbar "N unmapped" readout. On seek/templates/internal_assays.html the span
   also carries .attrs-count, which supplies the bordered pill; these two
   declarations are the weight and the gap to the buttons that follow.
   seek/templates/clades.html has not been given .attrs-count yet, so its
   readout keeps exactly the plain bold text this rule produced on its own —
   i.e. adding the shared class there is the only change that page needs. */
.ns-wb-count { font-weight: 700; margin-right: 1em; }
/* updateCount() does not run until render(), which on a page with a
   suggestionsUrl waits for that fetch; without this the toolbar would show an
   empty .attrs-count pill for the length of the round trip. */
.ns-wb-count:empty { display: none; }

/* Tier group-header row: one flex line — caret, label, count pill, and the
   bulk-accept button pushed out to the right edge of the title column. */
.ns-wb-group {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ns-ink);
  cursor: pointer;
}
/* The caret itself is .attrs-chevron (shared: inline-block + the 0.15s
   transform transition). Only the turned state is workbench-specific, because
   the attributes page hangs its `is-open` off the table ROW
   (.attrs-row.is-open .attrs-chevron) and there is no such row here. Same
   glyph and same 90deg turn as .ns-workbench .datagrid-row-expander below, so
   the two expanders in this grid move alike. */
.ns-wb-caret { color: var(--ns-muted); font-size: 15px; line-height: 1; }
.ns-wb-caret.is-open { transform: rotate(90deg); }

/* Bulk accept. .attrs-btn supplies the whole shape; these four declarations are
   what an <a> inside a datagrid cell needs on top of it — Bootstrap's reboot
   underlines every bare anchor, the uppercase/letter-spacing inherited from
   .ns-wb-group has to be unwound for sentence-case button text, and
   margin-left:auto is what parks it at the far end of the flex line. */
.ns-wb-accept-tier {
  margin-left: auto;
  text-decoration: none;
  text-transform: none;
  letter-spacing: normal;
}

/* --- Tier semantics ---------------------------------------------------------
   The SHAPE comes from a shared component — .attrs-chip for the candidate
   badges in the suggestion column, .attrs-count for the counts in a group
   header — and these variants carry nothing but colour. Splitting it that way
   is what stops a tier drifting into a differently-shaped pill later.

   Exact = green, precedent/fuzzy = amber, conflict/none = the theme crimson
   (already this design language's error colour: see .attrs-status--error and
   .attrs-error in sampleAttributes.html), unavailable/mapped = the neutral grey
   pair .attrs-chip--quiet uses. Semantics unchanged from the workbench's first
   release; the green and amber hex values are literally the ones it shipped.

   Those two hues get no --ns-* token on purpose: the theme palette is crimson
   plus neutrals, and minting :root tokens for two workbench-only colours would
   push them at the whole app for the sake of three rules.

   These must stay AFTER the .attrs-chip / .attrs-count definitions above — same
   specificity (0,1,0), so source order is what lets a variant repaint the
   component's border-color and background. */
.ns-tier-exact       { color: #1a7f4b; border-color: #1a7f4b; background: rgba(26,127,75,0.07); }
.ns-tier-precedent   { color: #8a6100; border-color: #8a6100; background: rgba(138,97,0,0.07); }
.ns-tier-fuzzy       { color: #8a4b00; border-color: #8a4b00; background: rgba(138,75,0,0.07); }
.ns-tier-conflict,
.ns-tier-none        { color: var(--ns-crimson); border-color: var(--ns-crimson-border); background: var(--ns-crimson-soft); }
/* "Suggestions unavailable" and the "Already mapped" group header. Muted on
   purpose, and for two different reasons: one reports that the resolver was
   never reached, the other counts work already done, and neither must compete
   with the actionable tiers. Same two values as .attrs-chip--quiet. */
.ns-tier-unavailable,
.ns-tier-mapped      { color: var(--ns-muted); border-color: var(--ns-border); background: var(--ns-bg); }

/* Evidence panel under an expanded row. The geometry deliberately repeats
   .attrs-detail-inner's padding and .attrs-detail > td's background from
   sampleAttributes.html, so an expanded row here and an expanded row there are
   recognisably the same object. The <dt>s take their label typography from the
   shared `.attrs-detail-field label, .ns-wb-evidence dt` rule near the top of
   this file; only the spacing between successive pairs is set here. */
.ns-wb-evidence {
  padding: 0.9rem 1.1rem 1rem 3rem;
  background: var(--ns-sidebar-bg);
  font-size: 0.85rem;
  color: var(--ns-body);
}
.ns-wb-evidence dl { margin: 0; }
.ns-wb-evidence dt { margin-top: 0.6rem; }
.ns-wb-evidence dt:first-child { margin-top: 0; }
.ns-wb-evidence dd { margin: 0.15rem 0 0; color: var(--ns-body); }

/* The resolver's per-candidate reasoning: the mapped entities behind a
   precedent, the shared words and overlap score behind a fuzzy guess, the
   suffix and comparison key behind an exact match. A list, not prose, because
   the items are independent facts a curator scans rather than reads, and one
   of them is routinely a list of titles that would otherwise run together.

   No bullet glyph: these sit under a <dd> that is already indented under a
   field label, and a disc at this size read as noise against the rest of the
   pane. The hanging indent does the grouping instead. Entity titles come
   through in the curator's own spelling, so they are left in the body face --
   only the counts and scores around them are the resolver's words. */
.ns-wb-basis {
  list-style: none;
  margin: 0.3rem 0 0;
  padding: 0 0 0 0.9rem;
  border-left: 2px solid var(--ns-border);
}
.ns-wb-basis li {
  margin: 0.15rem 0 0;
  color: var(--ns-muted);
  font-size: 0.82rem;
  line-height: 1.45;
}
.ns-wb-basis li:first-child { margin-top: 0; }

/* Separator between the candidates of a conflict row. The span also carries
   .attrs-muted, which supplies the colour and size; this is the gap. */
.ns-wb-vs { margin: 0 0.35em; }

/* --- Workbench grid geometry ------------------------------------------------
   Vendored easyui (static/jquery-easyui-1.5.2/themes/default/easyui.css, frozen)
   gives every .datagrid-cell `height:18px; line-height:18px; white-space:nowrap;
   overflow:hidden`. The .datagrid-cell rule further up this file already raises
   the font to 0.875rem (14px) but leaves all four of those alone, so each cell
   is a fixed 18px non-wrapping box holding 14px text, and anything longer is cut
   off with no ellipsis. Long titles and the multi-candidate conflict rendering
   are the visible casualties.

   Scoped to .ns-workbench deliberately: every other datagrid in the app (search,
   samples, sops, datafiles, batch search, publish) keeps today's geometry
   exactly. The scoping works because easyui builds the grid *in the original
   <table>'s position* — jquery.easyui.min.js does
   `$('<div class="datagrid-wrap">…</div>').insertAfter(table)` and then
   `panel({cls:'datagrid'})`, whose own wrapper is
   `$('<div class="panel"></div>').insertBefore(target)` — so the generated
   panel is the table's sibling and stays inside this wrapper div. easyui also
   *moves* the element named by the grid's `toolbar:` option into that panel, so
   the toolbar is covered by the same scope.

   Horizontal padding is inherited unchanged from the .datagrid-cell rule above
   (10px), so column widths and header/body alignment are untouched; only the
   vertical box changes. Header cells use .datagrid-cell too, which is what keeps
   the header row on the same rhythm as the body rows.

   No `height` is declared here on purpose. Both workbench grids now set
   `nowrap:false, autoRowHeight:true` (seek/templates/internal_assays.html), so
   easyui writes `white-space:normal;height:auto;` *inline* on every body cell
   div itself — jquery.easyui.min.js's stock view renderRow and the vendored
   themes/NextSeek/static/js/easyui/datagrid-detailview.js:99-103 share that
   branch. Header cells already get `height:auto` from the vendored
   static/jquery-easyui-1.5.2/themes/default/easyui.css
   (`.datagrid-header .datagrid-cell{height:auto}`), as does an open editor
   (`.datagrid-row-editing .datagrid-cell{height:auto}`). An `!important` height
   here would sit on top of a property easyui now owns and writes during row
   equalisation, so it is gone; `min-height` is kept because it only raises the
   floor above the vendored 18px and cannot stop a row growing. */
.ns-workbench .datagrid-cell,
.ns-workbench .datagrid-cell-group {
  min-height: 22px !important;
  line-height: 1.45 !important;
  white-space: normal !important;
  /* Not `hidden`: with height:auto nothing would in fact be clipped, but making
     the no-silent-clipping property structural rather than a side effect of the
     height is the whole point of the change. The two wrap properties below are
     what stop a single unbreakable token spilling into the next column now that
     the overflow is visible — the column width itself is set by easyui on a
     generated .datagrid-cell-c…-<field> class and is not affected here. */
  overflow: visible !important;
  word-wrap: break-word !important;
  overflow-wrap: break-word !important;
}
/* The detailview expander column holds a caret, not text. It is declared as an
   ordinary (unfrozen) column by both consumer templates -- see the long note on
   the <th> in seek/templates/internal_assays.html -- specifically so that it is
   NOT frozen into a second <table> the way datagrid-detailview.js's
   onBeforeRender would otherwise arrange. Caret and row content are therefore
   cells of one <tr>, and stay level because the browser lays a table row out,
   not because easyui's fixRowHeight was able to keep two tables in step across
   every later re-wrap. Nothing here may reintroduce a fixed height, for the
   ordinary reason that the row's height belongs to its tallest cell.

   `height:auto` defeats the `height:16px` datagrid-detailview.js hardcodes
   inline on this one cell div (renderRow, the `col.expander` branch), which
   would otherwise clip the 22px caret. Vertical centring is the table cell's
   own `vertical-align`; horizontal centring comes from the `text-align:center`
   the plugin sets inline. */
.ns-workbench td[field="_expander"] {
  vertical-align: middle;
}
.ns-workbench td[field="_expander"] .datagrid-cell {
  min-height: 0 !important;
  height: auto !important;
}

/* --- Row expander: rotating caret ------------------------------------------
   Replaces the vendored plus/minus sprite (frozen —
   static/jquery-easyui-1.5.2/themes/default/datagrid.css:
   .datagrid-row-expand / .datagrid-row-collapse each paint a different
   offset of images/datagrid_icons.png) with one triangle that rotates 90deg
   on expand, matching the caret the tier-group header rows now use
   (.ns-wb-caret, an .attrs-chevron carrying the same &#9656; that
   titleFormatter in static/js/custom/ns-vocab-workbench.js renders, turned
   90deg by .ns-wb-caret.is-open) instead of a stock 2010 toolbar icon. Both
   expanders in this grid therefore show one glyph in two rotations; the
   earlier &#9654;/&#9660; character swap on the header rows is gone.

   Scoped to .ns-workbench: every other datagrid in the app (search, samples,
   sops, datafiles, batch search, publish) keeps the stock sprite untouched —
   see the scoping note above this block for why that selector cannot leak
   (.ns-workbench appears only on the wrapper divs in
   seek/templates/internal_assays.html and seek/templates/clades.html).

   The span itself (datagrid-detailview.js:118, frozen) carries an inline
   `style="display:inline-block;width:16px;height:16px;cursor:pointer;"`,
   which only `!important` can beat, so every property that must move off
   that baseline is declared `!important` below. */
.ns-workbench .datagrid-row-expander {
  background: none !important;   /* suppress the sprite in both states */
  display: inline-block !important;
  /* The plugin sets width/height inline on the span, hence the !important.
     The column is 32px wide (declared in both templates), so a 22px box clears
     it with 5px either side. The column width is now an ordinary column width
     and can be changed freely -- there is no second table to keep it in step
     with any more. */
  width: 22px !important;
  height: 22px !important;
  line-height: 22px;
  text-align: center;
  cursor: pointer;
}
.ns-workbench .datagrid-row-expander::before {
  /* Same glyph both states (collapsed default); .datagrid-row-collapse below
     only rotates it, so the motion reads as a turn, not a character swap. */
  content: '\25B8';
  display: inline-block;
  color: var(--ns-muted);
  /* Sized to sit just above the 14px body text rather than below it: at 11px
     the caret read as debris next to the row content it controls. */
  font-size: 15px;
  transform: rotate(0deg);
  transition: transform 150ms ease, color 150ms ease;
}
.ns-workbench .datagrid-row-expander:hover::before {
  color: var(--ns-body);   /* darker on hover; reuses the theme's body-text token */
}
.ns-workbench .datagrid-row-collapse::before {
  transform: rotate(90deg);   /* expanded: same ▸, turned to point down */
}

/* Tier group-header rows (row._nsHeader — see ns-vocab-workbench.js's
   layout()) open nothing when "expanded": there is no detail row content for
   a synthetic row. The rowStyler in both consumer templates puts this class
   on the row's <tr> in every one of the plugin's synchronized row instances,
   so hide the now-inert control rather than leave a control that visibly
   does nothing. visibility:hidden, not display:none: the plugin measures and
   moves these spans directly (getExpander(), and the footer masking a few
   lines below in datagrid-detailview.js uses this exact same
   `.css('visibility','hidden')` for the same "should not appear" case), and
   an empty cell of the declared width keeps the header row's columns aligned
   with every other row's. visibility:hidden also removes it from
   hit-testing, so the routing above (nsWbClickCell already returns early for
   row._nsHeader before this control would even be reached) is not relied on
   to keep it unclickable. */
.ns-workbench tr.ns-wb-header-row .datagrid-row-expander {
  visibility: hidden;
}

/* Per-row selection column, rendered by ns-vocab-workbench.js's
   selectFormatter() rather than by easyui's `checkbox: true` column (which
   cannot be suppressed for individual rows, so every synthetic tier-header row
   would grow one). */
.ns-wb-check { margin: 0; vertical-align: middle; cursor: pointer; }
/* Stands in for the checkbox on rows that must not be bulk-accepted: already
   mapped, conflicting candidates, or no candidate at all. A disabled checkbox
   would still read as "selectable, currently off". */
.ns-wb-nocheck { color: var(--ns-muted); cursor: default; }


/* --- Workbench page skin: easyui chrome, attrs-* clothes --------------------
   The workbench pages are easyui datagrids; seek/templates/sampleAttributes.html
   is hand-built markup and says so at the top of its <style> block ("No EasyUI
   on this page"). Sharing the small components was therefore never going to make
   the two pages look alike on its own -- what the eye reads first is the chrome
   around the table, and on these pages that chrome was still easyui's: a tab bar
   with one tab, a panel header, a grey gradient column header, dotted cell
   borders, zebra striping, an amber selection and 2010 sprite buttons.

   The templates dropped the tabs/layout/.well wrappers. This block dresses what
   is left -- the generated grid -- in the attributes page's own values, taken
   from its .attrs-table-wrap / .attrs-table / .attrs-row rules rather than
   invented here:
     container   1px var(--ns-border), radius 10px, white, --ns-shadow-xs
     head        --ns-sidebar-bg, 0.68rem/700/0.07em uppercase --ns-muted, left
     body        0.85rem --ns-font-body, 1px solid --ns-border under each row
     hover       #fbfbfc          selected  --ns-crimson-soft
     detail row  --ns-sidebar-bg

   SPECIFICITY, not order, is what makes these win. The app-wide easyui retune
   earlier in this file (".datagrid-header", ".datagrid-cell", ".l-btn" and the
   direction-C background block) declares everything !important at one class of
   specificity; every rule here is !important at two or more, so it takes
   precedence wherever it overlaps and this block could sit anywhere in the file.
   That is also why `!important` appears on declarations that would not otherwise
   need it: it is the price of entry against those rules, not emphasis.

   Scoped to .ns-workbench, which appears only on the single page wrapper in
   seek/templates/internal_assays.html and seek/templates/clades.html. Every
   other datagrid in the app (search, samples, sops, datafiles, batch search,
   publish) is untouched.
   -------------------------------------------------------------------------- */

/* Page column. sampleAttributes.html caps itself at the same width with its own
   page-local `.attrs-page { max-width: 1500px }`; that rule stayed inline with
   the rest of that page's structure, so the value is repeated here rather than
   the class borrowed for a shell these pages would otherwise get nothing from. */
.ns-workbench { max-width: 1500px; }

.ns-wb-section { margin-bottom: 2.25rem; }
.ns-wb-section:last-child { margin-bottom: 0; }
/* Sits a step below .ns-page-title (1.8rem) in the same display face, so the two
   sections read as parts of one page rather than as two stacked pages. */
.ns-wb-section-title {
  font-family: var(--ns-font-display);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ns-ink);
  margin: 0 0 0.25rem;
}
.ns-wb-section-note {
  font-size: 0.85rem;
  color: var(--ns-muted);
  margin: 0 0 0.75rem;
  max-width: 74ch;
}

/* --- the grid container ---------------------------------------------------- */
/* .attrs-table-wrap. overflow:hidden is what lets the radius actually clip the
   header and the last row; easyui scrolls inside .datagrid-body, so nothing
   here is what makes the grid scrollable and nothing is lost to the clip. */
.ns-workbench .datagrid-wrap {
  border: 1px solid var(--ns-border) !important;
  border-radius: 10px !important;
  overflow: hidden;
  background: var(--ns-white) !important;
  box-shadow: var(--ns-shadow-xs);
}
/* Both grids are built with `title:""`, so the panel header is an empty bar of
   easyui's blue gradient sitting above the table. */
.ns-workbench .panel-header { display: none !important; }
.ns-workbench .panel-body { border: 0 !important; padding: 0 !important; }
.ns-workbench .datagrid-view,
.ns-workbench .datagrid-body,
.ns-workbench .datagrid-row { background-color: var(--ns-white) !important; }

/* --- column header --------------------------------------------------------- */
.ns-workbench .datagrid-header {
  background: var(--ns-sidebar-bg) !important;
  border-width: 0 0 1px 0 !important;
  border-style: solid !important;
  border-color: var(--ns-border) !important;
}
.ns-workbench .datagrid-header-row { background-color: transparent !important; }
/* Vendored easyui draws every header and body cell with a DOTTED right and
   bottom border (themes/default/datagrid.css). The attributes page has no
   vertical rules at all and one hairline under each row. */
.ns-workbench .datagrid-header td {
  border-width: 0 !important;
  border-style: solid !important;
  border-color: transparent !important;
}
.ns-workbench .datagrid-header .datagrid-cell,
.ns-workbench .datagrid-header .datagrid-cell span,
.ns-workbench .datagrid-header td,
.ns-workbench .datagrid-header th {
  font-family: var(--ns-font-body) !important;
  font-size: 0.68rem !important;
  font-weight: 700 !important;
  letter-spacing: 0.07em !important;
  text-transform: uppercase !important;
  color: var(--ns-muted) !important;
  text-align: left !important;
}
.ns-workbench .datagrid-header .datagrid-cell {
  padding: 0.55rem 0.6rem !important;
  line-height: 1.3 !important;
}
/* The header cell of the expander column carries only a &nbsp;, and of the
   selection column only a &nbsp;; neither should reserve label height. */
.ns-workbench .datagrid-header td[field="_expander"] .datagrid-cell,
.ns-workbench .datagrid-header td[field="_nsSelect"] .datagrid-cell {
  padding-left: 0 !important;
  padding-right: 0 !important;
}

/* --- body rows ------------------------------------------------------------- */
.ns-workbench .datagrid-body td {
  border-width: 0 0 1px 0 !important;
  border-style: solid !important;
  border-color: var(--ns-border) !important;
}
.ns-workbench .datagrid-body .datagrid-cell {
  font-family: var(--ns-font-body) !important;
  font-size: 0.85rem !important;
  color: var(--ns-body) !important;
  padding: 0.45rem 0.6rem !important;
}
/* No zebra. Both templates now pass `striped:false`, so .datagrid-row-alt is not
   emitted at all; this is belt and braces for a grid rebuilt with striping on. */
.ns-workbench .datagrid-row-alt { background-color: var(--ns-white) !important; }
.ns-workbench .datagrid-row-over,
.ns-workbench .datagrid-header td.datagrid-header-over {
  background-color: #fbfbfc !important;
  color: var(--ns-body) !important;
}
/* easyui's stock selection is an amber (#ffe48d) band. .attrs-row's own dirty/
   selected states are all crimson tints. */
.ns-workbench .datagrid-row-selected {
  background-color: var(--ns-crimson-soft) !important;
  color: var(--ns-body) !important;
}

/* Expanded evidence row. The padding and background are .ns-wb-evidence's job
   (above); this only stops the cell it sits in drawing the row hairline twice
   and clears easyui's cell padding from around it. */
.ns-workbench .datagrid-body td:has(> .datagrid-row-detail) { padding: 0 !important; }
.ns-workbench .datagrid-row-detail { background: var(--ns-sidebar-bg); }

/* --- toolbar --------------------------------------------------------------- */
/* easyui moves the element named by the grid's `toolbar:` option into the panel
   above the header, so this is the bar holding the count pill and the actions. */
.ns-workbench .datagrid-toolbar {
  background: var(--ns-white) !important;
  border-width: 0 0 1px 0 !important;
  border-style: solid !important;
  border-color: var(--ns-border) !important;
  padding: 0.55rem 0.6rem !important;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
/* The toolbar buttons are easyui linkbuttons -- `<a class="l-btn l-btn-plain">`
   wrapping a text span and a 16px PNG sprite -- and are given .attrs-btn's box
   here rather than the class itself, because easyui rebuilds that markup and
   would not carry an added class through. Values are .attrs-btn's, verbatim. */
.ns-workbench .datagrid-toolbar .l-btn {
  display: inline-flex !important;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--ns-font-body) !important;
  font-size: 0.83rem !important;
  font-weight: 600 !important;
  border: 1px solid var(--ns-border) !important;
  border-radius: 6px !important;
  background: var(--ns-white) !important;
  color: var(--ns-body) !important;
  padding: 0.42rem 0.85rem !important;
  transition: all 0.15s var(--ns-ease) !important;
  white-space: nowrap;
  text-decoration: none !important;
}
.ns-workbench .datagrid-toolbar .l-btn:hover {
  background: var(--ns-white) !important;
  border-color: var(--ns-crimson-border) !important;
  color: var(--ns-crimson) !important;
  padding: 0.42rem 0.85rem !important;   /* beats `.l-btn-plain:hover{padding:0}` */
}
/* The sprite icons (icon-add / icon-save / icon-undo / icon-reload) are the most
   dated thing left on the page and have no counterpart on the attributes page,
   whose buttons are text plus a Font Awesome glyph. Hidden rather than replaced:
   the class that selects each sprite is written by easyui from `iconCls`. */
.ns-workbench .datagrid-toolbar .l-btn .l-btn-icon { display: none !important; }
.ns-workbench .datagrid-toolbar .l-btn .l-btn-text {
  margin: 0 !important;
  line-height: 1.3 !important;
  font-size: 0.83rem !important;
  color: inherit !important;
}
.ns-workbench .datagrid-toolbar .l-btn .l-btn-left { display: inline-flex; align-items: center; }

